在使用树莓派控制舵机的时候,由于树莓派自带的硬件PWM资源少,稳定性差,所以买了块PCA9685模块,芯片本身是比较简单的,但是网上教程混乱,互相抄袭,看的云里雾里,无奈只好自己啃了一下datasheet,有些值得注意的地方,在这里记录一下,只挑我自己用的上来写,有些没有详细验证,可能理解有出入。
以上PCA9685的寄存器大致分几个类型(模式选择、通道配置、频率设置)。
MODE1、MODE2是模式选择,LED0_ON_L,LED0_ON_H,LED0_OFF_L,LED0_OFF_H控制LED0这个通道的开、关时间,其他LEDn通道都一样。那ALL_LED_ON(OFF)_L(H)顾名思义就是同时控制所有通道了,PRE_SCALE控制频率。
需要注意的地方:向PRE_SCALE写数据的时候,MODE1的SLEEP位需要置1,也就是在休眠状态下修改频率才有效。
MODE1寄存器里面的套路比较多,仔细看一下:
首先是RESTART位,默认为0,为1的时候是重启状态,但是清空该状态位需要向该位写“1”而不是写“0”。
EXTCLK是是否使用外部时钟源,我用不上。
AI是寄存器寻址自增设置,批量读写寄存器时用,打开后,读写PWM寄存器时,一次读写两个字节,比较方便。
睡眠位(SLEEP):
SLEEP是睡眠模式(低功耗模式),1为睡眠状态,0位工作状态。默认为1,就是说上电、开机后都是睡眠模式了,需要注意的是,在使用内部时钟的时候,从睡眠模式切换到正常模式,最多需要大概500us时间,其实就是启用内部晶振大概需要这么多时间,但使用外部时钟源就不需要等待。还有就是休眠状态下,PWM无法使用,有些单片机在休眠状态下,外设可以独立工作,这个就不一样。还有就是休眠状态下,各LEDn通道输出状态都无法改变。
重启位(RESTART):
如果PCA9685正在工作,用户在没有关闭PWM通道的情况下,决定让芯片进入SLEEP状态,那么在最后一个PWM周期结束后,RESTART位才会被置1,时钟停止后,各PWM寄存器(就是通道控制寄存器,每路4个)的内容会被保留。
再切换到正常模式时,为了重启之前保留的PWM设置,可以按下面几步进行:
1.读取MODE1寄存器内容。
2.检查第7位(RESTART)是否是1.如果是,清空第4位(SLEEP)(写0),等待500us使时钟稳定。
3.写MODE1的第7位写1。所有的PWM通道将会重新工作,且RESTART位会清空。
备注:在向RESTART位写1之前,SLEEP位一定要至少持续500us为0。
其他可以清空RESTART位的情况:
1.上电。
2.I2C软件重启命令。
3.如果MODE2 OCH位为0,向任何PWM寄存器写数据后,I2C总线产生STOP。
4.如果MODE2 OCH位为1,向任何通道的所有4个PWM寄存器都写一次数据。
同时,如果用户在设置SLEEP位之前,人为的关闭所有PWM通道(有两个方法,最快的方法是向ALL_LED_OFF_H寄存器的4位写1,另一个是向所有通道的LEDn_OFF_H寄存器的4位写1),RESTART位也会被清空。如果这样做,所有的PWM寄存器的内容都会作废,在重新启用之前需要设置。
一个使用RESTART位的例子是将客户笔记本电脑显示器背光亮度从待机模式恢复到正常水平。
MODE2寄存器包括设置反转输出电平,输出生效模式,输出推挽/开漏模式等等。
LED通道输出和PWM控制
LEDn_ON和LEDn_OFF寄存器控制PWM占空比。简单来说,在一个PWM周期内,LEDn_ON控制的是什么时候开,LEDn_OFF控制的是什么时候关。两者的取值范围都是从0到4095,这两个寄存器在工作时会和一个持续计数(0到4095)的计数器进行比较。
例1:假设使用的是LED0通道,且(延时时间)+(PWM占空比)≤100%
延时=10%;PWM占空比=20%(即LED on 时间=20%;LED off 时间=80%)。
延时=10%=409.6个计数, 约等于410=0x19A
由于计数器是从0开始到4095结束,我们要在上面的值上减1,所以延时=0x199。
所以:
LED0_ON_H=0X01;LED0_ON_L=0x99(LED在从409个计数开始处于开启状态)
LED开启时间=20%=819.2个计数,约等于819.
所以LED关闭时间=410+819-1=1228=0x4CC
所以LED0_OFF_H=0x04;LED0_OFF_L=0xCC(LED从1228个计数开始处于关闭状态)
这个还是比较容易理解的,简单来说,就是设置LED0通道的4个PWM控制寄存器嘛,开和关各两个寄存器(因为一个寄存器只有8位,而最大值4095需要12位,所以需要弄两个寄存器拼凑在一下才装的下),因为开启时间和关闭时间都可编程,那么移相就变的很简单,上面的延时的作用就是改变相位。
那么放在树莓派中,如果我们要使用I2CTOOL设置LED0这个通道的占空比,可按下面几步进行:
先查看datasheet,查找LED0通道PWM寄存器是哪几个:
可知LED0通道对应的寄存器编号为6、7、8、9.
使用i2cset依次写入上面计算得出的hex值,我的模块是默认地址,0x40:
pi@raspberrypi:~ $ i2cset -y 1 0x40 0x06 0x99
pi@raspberrypi:~ $ i2cset -y 1 0x40 0x07 0x01
pi@raspberrypi:~ $ i2cset -y 1 0x40 0x08 0xcc
pi@raspberrypi:~ $ i2cset -y 1 0x40 0x09 0x04
这样就可以了,就这么简单。
查看一下所有寄存器的情况:
pi@raspberrypi:~ $ i2cdump -y 1 0x40
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 01 04 e2 e4 e8 e0 99 01 cc 04 00 00 00 10 00 00 ??????????...?..
10: 00 10 00 00 00 10 00 00 00 10 00 00 00 10 00 00 .?...?...?...?..
20: 00 10 00 00 00 10 00 00 00 10 00 00 00 10 00 00 .?...?...?...?..
30: 00 10 00 00 00 10 00 00 00 10 00 00 00 10 00 00 .?...?...?...?..
40: 00 10 00 00 00 10 XX XX XX XX XX XX XX XX XX XX .?...?XXXXXXXXXX
50: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
80: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
90: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
a0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
b0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
c0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
d0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
e0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
f0: XX XX XX XX XX XX XX XX XX XX 00 00 00 00 1e 00 XXXXXXXXXX....?.
6、7、8、9四个寄存器的值,和我们写入的是一致的,这时我接的LED灯已经点亮了(LED0 PWM引脚经限流电阻接LED灯珠,到GND),随意更改这4个寄存器中的哪个数值,LED亮度将发生变化。