回复了 Alawn 创建的主题 问与答 / 树莓派与PCA9685通信
2023-05-08

@店长大人 你看这个什么情况

回复了 Alawn 创建的主题 问与答 / 树莓派与PCA9685通信
2023-05-08

单片机:


#define PCA9685_adrr 0x80// 1+A5+A4+A3+A2+A1+A0+w/r
//片选地址,将焊接点置1可改变地址,
//当IIC总线上有多片PCA9685或相同地址时才需焊接
#define PCA9685_SUBADR1 0x02
#define PCA9685_SUBADR2 0x03
#define PCA9685_SUBADR3 0x04


#define PCA9685_MODE1 0x00
#define PCA9685_PRESCALE 0xFE


#define LED0_ON_L 0x06
#define LED0_ON_H 0x07
#define LED0_OFF_L 0x08
#define LED0_OFF_H 0x09


#define LED1_ON_L 0x0A
#define LED1_ON_H 0x0B
#define LED1_OFF_L 0x0C
#define LED1_OFF_H 0x0D

#define LED2_ON_L 0x0E
#define LED2_ON_H 0x0F
#define LED2_OFF_L 0x10
#define LED2_OFF_H 0x11

#define LED3_ON_L 0x12
#define LED3_ON_H 0x13
#define LED3_OFF_L 0x14
#define LED3_OFF_H 0x15

#define LED4_ON_L 0x16
#define LED4_ON_H 0x17
#define LED4_OFF_L 0x18
#define LED4_OFF_H 0x19

#define LED5_ON_L 0x1A
#define LED5_ON_H 0x1B
#define LED5_OFF_L 0x1C
#define LED5_OFF_H 0x1D

#define LED6_ON_L 0x1E
#define LED6_ON_H 0x1F
#define LED6_OFF_L 0x20
#define LED6_OFF_H 0x21

#define LED7_ON_L 0x22
#define LED7_ON_H 0x23
#define LED7_OFF_L 0x24
#define LED7_OFF_H 0x25

#define LED8_ON_L 0x26
#define LED8_ON_H 0x27
#define LED8_OFF_L 0x28
#define LED8_OFF_H 0x29

#define LED9_ON_L 0x2A
#define LED9_ON_H 0x2B
#define LED9_OFF_L 0x2C
#define LED9_OFF_H 0x2D

#define LED10_ON_L 0x2E
#define LED10_ON_H 0x2F
#define LED10_OFF_L 0x30
#define LED10_OFF_H 0x31

#define LED11_ON_L 0x32
#define LED11_ON_H 0x33
#define LED11_OFF_L 0x34
#define LED11_OFF_H 0x35

#define LED12_ON_L 0x36
#define LED12_ON_H 0x37
#define LED12_OFF_L 0x38
#define LED12_OFF_H 0x39

#define LED13_ON_L 0x3A
#define LED13_ON_H 0x3B
#define LED13_OFF_L 0x3C
#define LED13_OFF_H 0x3D

#define LED14_ON_L 0x3E
#define LED14_ON_H 0x3F
#define LED14_OFF_L 0x40
#define LED14_OFF_H 0x41

#define LED15_ON_L 0x42
#define LED15_ON_H 0x43
#define LED15_OFF_L 0x44
#define LED15_OFF_H 0x45



/*#define ALLLED_ON_L 0xFA
#define ALLLED_ON_H 0xFB
#define ALLLED_OFF_L 0xFC
#define ALLLED_OFF_H 0xFD*/


#define SERVOMIN 115 // this is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 590 // this is the 'maximum' pulse length count (out of 4096)
#define SERVO000 130 //0度对应4096的脉宽计数值
#define SERVO180 520 //180度对应4096的脉宽计算值,四个值可根据不同舵机修改

IIC总线初始化函数
----------------------------------------------------------------*/
void init()
{
sda=1; //sda scl使用前总是被拉高
delayus();
scl=1;
delayus();
}
/*---------------------------------------------------------------
IIC总线启动信号函数
----------------------------------------------------------------*/
void start()
{
sda=1;
delayus();
scl=1; //scl拉高时 sda突然来个低电平 就启动了IIC总线
delayus();
sda=0;
delayus();
scl=0;
delayus();
}
/*---------------------------------------------------------------
IIC总线停止信号函数
----------------------------------------------------------------*/
void stop()
{
sda=0;
delayus();
scl=1; //scl拉高时 sda突然来个高电平 就停止了IIC总线
delayus();
sda=1;
delayus();
}
/*---------------------------------------------------------------
IIC总线应答信号函数
----------------------------------------------------------------*/
void ACK()
{
uchar i;
scl=1;
delayus();
while((sda==1)&&(i<255))
i++;
scl=0;
delayus();
}
/*---------------------------------------------------------------
写一个字节,无返回值,需输入一个字节值
----------------------------------------------------------------*/
void write_byte(uchar byte)
{
uchar i,temp;
temp=byte;
for(i=0;i<8;i++)
{
temp=temp<<1;
scl=0;
delayus();
sda=CY;
delayus();
scl=1;
delayus();
}
scl=0;
delayus();
sda=1;
delayus();
}
/*---------------------------------------------------------------
读一个字节函数,有返回值
----------------------------------------------------------------*/
uchar read_byte()
{
uchar i,j,k;
scl=0;
delayus();
sda=1;
delayus();
for(i=0;i<8;i++)
{
delayus();
scl=1;
delayus();
if(sda==1)
{
j=1;
}
else j=0;
k=(k<< 1)|j;
scl=0;
}
delayus();
return k;
}
/*---------------------------------------------------------------
有关PCA9685模块的函数
----------------------------------------------------------------*/
/*---------------------------------------------------------------
向PCA9685里写地址,数据
----------------------------------------------------------------*/
void PCA9685_write(uchar address,uchar date)
{
start();
write_byte(PCA9685_adrr); //PCA9685的片选地址
ACK();
write_byte(address); //写地址控制字节
ACK();
write_byte(date); //写数据
ACK();
stop();
}
/*---------------------------------------------------------------
从PCA9685里的地址值中读数据(有返回值)
----------------------------------------------------------------*/
uchar PCA9685_read(uchar address)
{
uchar date;
start();
write_byte(PCA9685_adrr); //PCA9685的片选地址
ACK();
write_byte(address);
ACK();
start();
write_byte(PCA9685_adrr|0x01); //地址的第八位控制数据流方向,就是写或读
ACK();
date=read_byte();
stop();
return date;
}
/*---------------------------------------------------------------
PCA9685复位
----------------------------------------------------------------*/
void reset(void)
{
PCA9685_write(PCA9685_MODE1,0x0);
}


void begin(void)
{
reset();
}
/*---------------------------------------------------------------
PCA9685修改频率函数
----------------------------------------------------------------*/
void setPWMFreq(uint freq)
{
uint prescale,oldmode,newmode;
float prescaleval;
freq *= 0.92; // Correct for overshoot in the frequency setting
prescaleval = 25000000;
prescaleval /= 4096;
prescaleval /= freq;
prescaleval -= 1;
prescale = floor(prescaleval + 0.5);

oldmode = PCA9685_read(PCA9685_MODE1); //选择模式
newmode = (oldmode&0x7F) | 0x10; // sleep
PCA9685_write(PCA9685_MODE1, newmode); // go to sleep
PCA9685_write(PCA9685_PRESCALE,prescale); // set the prescaler
PCA9685_write(PCA9685_MODE1, oldmode);
delayms(2);
PCA9685_write(PCA9685_MODE1, oldmode | 0xa1);
}
/*---------------------------------------------------------------
PCA9685修改角度函数
num:舵机PWM输出引脚0~15,on:PWM上升计数值0~4096,off:PWM下降计数值0~4096
一个PWM周期分成4096份,由0开始+1计数,计到on时跳变为高电平,继续计数到off时
跳变为低电平,直到计满4096重新开始。所以当on不等于0时可作延时,当on等于0时,
off/4096的值就是PWM的占空比。
----------------------------------------------------------------*/
/*void setPWM(uint num, uint on, uint off)
{
PCA9685_write(LED0_ON_L+4*num,on);
PCA9685_write(LED0_ON_H+4*num,on>>8);
PCA9685_write(LED0_OFF_L+4*num,off);
PCA9685_write(LED0_OFF_H+4*num,off>>8);
}


---------------------------------------------------------------*/
void PWM(uint num,float rate)
{
if(rate<=1&&rate>=0)
{
int off;
off=rate*4096-1;
PCA9685_write(LED0_OFF_L+8*num,off);
PCA9685_write(LED0_OFF_H+8*num,off>>8);
off+=2;
if (off>4095)
off=4095;
PCA9685_write(LED0_ON_L+8*num+4,off);
PCA9685_write(LED0_ON_H+8*num+4,off>>8);
}
}

回复了 Alawn 创建的主题 问与答 / 树莓派与PCA9685通信
2023-05-08

这个是树莓派的:
import time
import math
import smbus

# ============================================================================
# Raspi PCA9685 16-Channel PWM Servo Driver
# ============================================================================

class PCA9685:

# Registers/etc.
__SUBADR1 = 0x02
__SUBADR2 = 0x03
__SUBADR3 = 0x04
__MODE1 = 0x00
__PRESCALE = 0xFE
__LED0_ON_L = 0x06
__LED0_ON_H = 0x07
__LED0_OFF_L = 0x08
__LED0_OFF_H = 0x09
__ALLLED_ON_L = 0xFA
__ALLLED_ON_H = 0xFB
__ALLLED_OFF_L = 0xFC
__ALLLED_OFF_H = 0xFD

def __init__(self, address=0x40, debug=False):
self.bus = smbus.SMBus(1)
self.address = address
self.debug = debug
if (self.debug):
print("Reseting PCA9685")
self.write(self.__MODE1, 0x00)
# self.write(self.__PRE_SCALE, 0x03)

def write(self, reg, value):
"Writes an 8-bit value to the specified register/address"
self.bus.write_byte_data(self.address, reg, value)
if (self.debug):
print("I2C: Write 0x%02X to register 0x%02X" % (value, reg))

def read(self, reg):
"Read an unsigned byte from the I2C device"
result = self.bus.read_byte_data(self.address, reg)
if (self.debug):
print("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % (self.address, result & 0xFF, reg))
return result

def setPWMFreq(self, freq):
"Sets the PWM frequency"
prescaleval = 25000000.0 # 25MHz
prescaleval /= 4096.0 # 12-bit
prescaleval /= float(freq)
prescaleval -= 1.0
if (self.debug):
print("Setting PWM frequency to %d Hz" % freq)
print("Estimated pre-scale: %d" % prescaleval)
prescale = math.floor(prescaleval + 0.5)
if (self.debug):
print("Final pre-scale: %d" % prescale)

oldmode = self.read(self.__MODE1);
newmode = (oldmode & 0x7F) | 0x10 # sleep
self.write(self.__MODE1, newmode) # go to sleep
self.write(self.__PRESCALE, int(math.floor(prescale)))
self.write(self.__MODE1, oldmode)
time.sleep(0.005)
self.write(self.__MODE1, oldmode | 0x80)

def setPWM(self, channel, on, off):
"Sets a single PWM channel"
self.write(self.__LED0_ON_L+4*channel, on & 0xFF)
self.write(self.__LED0_ON_H+4*channel, on >> 8)
self.write(self.__LED0_OFF_L+4*channel, off & 0xFF)
self.write(self.__LED0_OFF_H+4*channel, off >> 8)
if (self.debug):
print("channel: %d LED_ON: %d LED_OFF: %d" % (channel,on,off))

def setServoPulse(self, channel, pulse):
"Sets the Servo Pulse,The PWM frequency must be 50HZ"
pulse = int(pulse*4096/100) #PWM frequency is 100%
self.setPWM(channel, 0, pulse)

回复了 Alawn 创建的主题 问与答 / 树莓派与PCA9685通信
2023-05-07

@店长大人 树莓派控制PCA9684时,单片机并没有发送指令,也冲突吗?

回复了 Alawn 创建的主题 问与答 / 树莓派与PCA9685通信
2023-05-07

@店长大人 这种情况下应该怎么办呢?