rk3288 使用i2ctools调试pca9685
一、PCA9695的I2C地址
二、PCA9685的寄存器学习
- 寄存器模式:MODE1、MODE2
- 脉宽(占空比)设置寄存器:LED0_ON_L、LED0_ON_H、LED0_OFF_L、LED0_OFF_L
- 周期(频率)设置寄存器:PRE_SCALE
1.1 Mode1寄存器
bit7:0重启关闭,1重启开启
bit6:外部时钟设置引脚,设置时序:1.设置MODE1为SLEEP模式,会关闭内部晶振。2.同时设置MODE1的SLEEP和EXTCLK为1。只能被断电或者软件复位清零。
bit5:地址自动增加是能
bit4:设置为1时为sleep模式,设置为0时是正常模式
bit3:不管
bit2:不管
bit1:不管
bit0:不管
1.1.1 Mode1的Restart mode
如果想要在使用PWM时进入slepp模式(设置Mode1 bit4),那么当pwm周期结束时会将MODE1 bit7设置为1。
简单的通过i2c总线,重启所有之前活动的PWM通道可以使用下面的步骤:
- 读取MODE1寄存器
- 确认bit7是高电平,如果是,就把bit4清零(在睡眠模式),然后等待晶振稳定(500us)
- 写1到Mode1的bit7,所有的PWM通道将会被重启,RESTART位会被清除。
注意sleep位清零必须保持500us,在写RESATRT位前
其他行为会清除RESART位:
- 断电
- I2C软件复位命令
- 如果MODE2 OCH位为0,写任何PWM寄存器会引起I2C-bus停止
- 如果MODE2 OCH位为1,写所有四个PWM寄存器在任何PWM通道。
1.2 Mode2寄存器
bit7~bit5:保留
bit4:输出逻辑反向
bit3:0输出修改在STOP命令,1输出修改在ACK
bit2:0开漏配置,1推挽
bit1~bit0:看不懂
1.3 LED输出和PWM控制
对LEDn_ON和LEDn_OFF寄存器可以独立控制PWM。
每个LED输出,有连个12bit寄存器,这些寄存器都是从0~4096。一个控制On的时间,一个控制OFF的时间。
有个Counter寄存器,回去对比On和Off的值,从而做出对应的输出。
这样就可以控制pwm的高低电平了,每个相位就是1/4096。
设置的例子:
一个周期总共是4095,所以LED0On的位置在409,409=199h,所以LED0_ON_H = 1h LED0_ON_L=99h。
然后led off低电平的位置在1228,1228=4cch,所以LED0_OFF_H = 4h LED0_OFF_L = cch。
1.3 PWM frequecy PRE_SCALE
PRE_SCALE寄存器强制的硬件最小值是3,PRE_SCALE决定了输出的调制频率公式如下。
如果osc_clock是25MHz,输出的频率是200Hz那么就是:
如果PRE_SCALE是0x03h,那么最大频率是1526Hz
如果PRE_SCALE是0ffh,那么最大频率是24Hz
只有当MODE1 的sleep为设置为1时,PRE_SCALE才能被设置。
三、PCA9685的操作逻辑梳理
例子1:假设LED0输出,并且格式是((delay time)+(PWM duty cycle) <= 100%)
延迟时间(delay time) 是10%个周期,PWM工作周期是20%个周期(高电平20%, 低电平80%)
一个周期分为4096(0xFFFh)个单位,延迟时间(delay time): = 10% = 4096 * 10% = 409.6 = 401 = 0x19Ah
所以在寄存器LED0_ON_H=1H,LED0_ON_l =0x99H(因为计数是从0开始的,0x19Ah - 1 = 0x199h)
LED高电平的时间是20% = 4096*20%=819.2 = 819
LED低电平的时间的位置 是0x4CCh(410+819 - 1 = 1228)
所以LED0_OFF_H = 4h LED0_OFF_l = CCh
四、PCA9685的i2c-tools使用
参考资料:https://www.adafruit.com/product/815
4.1 参考例程
void Adafruit_PWMServoDriver::begin(void) {WIRE.begin();reset();
}void Adafruit_PWMServoDriver::reset(void) {write8(PCA9685_MODE1, 0x0);
}void Adafruit_PWMServoDriver::setPWMFreq(float freq) {//Serial.print("Attempting to set freq ");//Serial.println(freq);freq *= 0.9; // Correct for overshoot in the frequency setting (see issue #11).float prescaleval = 25000000;prescaleval /= 4096;prescaleval /= freq;prescaleval -= 1;if (ENABLE_DEBUG_OUTPUT) {Serial.print("Estimated pre-scale: "); Serial.println(prescaleval);}uint8_t prescale = floor(prescaleval + 0.5);if (ENABLE_DEBUG_OUTPUT) {Serial.print("Final pre-scale: "); Serial.println(prescale);}uint8_t oldmode = read8(PCA9685_MODE1);uint8_t newmode = (oldmode&0x7F) | 0x10; // sleepwrite8(PCA9685_MODE1, newmode); // go to sleepwrite8(PCA9685_PRESCALE, prescale); // set the prescalerwrite8(PCA9685_MODE1, oldmode);delay(5);write8(PCA9685_MODE1, oldmode | 0xa1); // This sets the MODE1 register to turn on auto increment.// This is why the beginTransmission below was not working.// Serial.print("Mode now 0x"); Serial.println(read8(PCA9685_MODE1), HEX);
}void Adafruit_PWMServoDriver::setPWM(uint8_t num, uint16_t on, uint16_t off) {//Serial.print("Setting PWM "); Serial.print(num); Serial.print(": "); Serial.print(on); Serial.print("->"); Serial.println(off);WIRE.beginTransmission(_i2caddr);WIRE.write(LED0_ON_L+4*num);WIRE.write(on);WIRE.write(on>>8);WIRE.write(off);WIRE.write(off>>8);WIRE.endTransmission();
}
4.2 使用i2c-tools调试PCA9685
# reset
i2cset -f -y I2CBUS CHIP-ADDRESS 0x00 0x0 b
# setPWMFreq
i2cget -f -y I2CBUS CHIP-ADDRESS 0x00 b
#get oldmode
i2cset -f -y I2CBUS CHIP-ADDRESS 0x00 ((oldmode&0x7f)|
0x10) b
# freq
# freq *= 0.9 // prescaleval = 25000000/4096/freq; prescaleval -=1;
# uint8_t prescale = floor(prescaleval + 0.5);
i2cset -f -y I2CBUS CHIP-ADDRESS 0xFE prescale b
i2cset -f -y I2CBUS CHIP-ADDRESS 0x00 oldmode b
i2cset -f -y I2CBUS CHIP-ADDRESS 0x00 (oldmode|0xa1) b# setPWM(num on, off)
i2cset -f I2CBUS CHIP-ADDRESS LED0_ON_L+4*num on w
i2cset -f I2CBUS CHIP-ADDRESS LED0_ON_L+4*num off w
4.3 调试结果
[root@firefly-rk3288:~]# i2cdetect -y -a 40 1 2 3 4 5 6 7 8 9 a b c d e f
00: 00 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
[root@firefly-rk3288:~]# i2cget -f -y 4 0x40 0x00 b
0x11
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x00 0x0 b
[root@firefly-rk3288:~]# i2cget -f -y 4 0x40 0x00 b
0x00
#所以oldmode是0x00,((oldmode&0x7f)|0x10) --> 0x10
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x00 0x10 b
[root@firefly-rk3288:~]# i2cget -f -y 4 0x40 0x00 b
0x10
# 简单一点prescale直接pdf上的值 0x30
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0xfe 0x30 b
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x00 0x00 b
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x00 0xa1 b
# setPWM(num on, off), num=0, on=0xff off=0x00
# 全高的话on=0x0000 off=0x0fff
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x06 0x00 b
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x07 0x00 b
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x08 0xff b
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x09 0x0f b
# 结果是输出了1v[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x06 0x00 b
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x07 0x00 b
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x08 0x00 b
[root@firefly-rk3288:~]# i2cset -f -y 4 0x40 0x09 0x00 b
# 结果是输出了0v
不知道什么原因,最大只有1v