赞
踩
I2C通信协议控制,可以输出16路PWM(脉冲宽度调制)。
内部时钟是25MHz,要输出满足要求的频率需要进行设置转换。
以最常用的SG90舵机为例:
向信号端口发送20ms波长的信号,这个时候要用到一个频率的单位赫兹。
麦克斯韦理论上发现了电磁波的存在,赫兹通过实验证明了电磁波,然后用他的名字命名频率的单位。1s中一个震动周期就是1Hz,1s中1000个就是1KHz。
SG90的接收一个工作波的周期是20ms,1s = 1000ms / 20ms = 50, 就是 50个赫兹, 50Hz。
PCA9685的2个主要控制寄存器:
MODE1
MODE1 地址: 0x00
[ 7 ] 重新启动 0* 已禁用重新启动 / 1 已启用重新启动
[ 6 ] 外部时钟选择 0* 使用内部时钟 / 1 使用EXTCLK引脚时钟
[ 5 ] AI 0* 寄存器自动增量已禁用 / 1 启用寄存器自动增量
[ 4 ] 睡眠 0 正常模式 / 1* 低功率模式。
[ 3 ] SUB1 0* PCA9685不响应I2C总线子地址1 / 1 PCA9685响应I2C总线子地址1
[ 2 ] SUB2 0* PCA9685不响应I2C总线子地址2 / 1 PCA9685响应I2C总线子地址2。
[ 1 ] SUB3 0* PCA9685不响应I2C总线子地址3 / 1 PCA9685响应I2C总线子地址3。
[ 0 ] ALLCALL 0 PCA9685不响应 LED All Call I2C总线地址 / 1* PCA9685响应 LED All Call I2C总线地址
PRE_SCALE
PRE_SCALE 地址: 0xFE
设置工作频率,首先设置MODE1,设置成休眠状态,然后向PRE_SCALE写入频率。
PCA9685内部输出时钟是25MHz,需要通过计算公式换算出我们需要的频率。
prescale value = round ( osc_clock / (4096 * update_rate) ) - 1
预缩放值 = 整数 ( osc时钟 / (4096 * 更新速率) ) - 1
- // 设置PWM频率
- hi_u8 PCA9685_Set_PWM_Freq(hi_u8 freq)
- {
- hi_float prescale = 25000000 / 4096 / (freq * 0.95) - 1;
-
- // 向上取整
- hi_u8 val = (hi_u8)(prescale + 0.5);
-
- // 0x10 0001 0000 [7] = 0 已禁用重新启动 [4] = 1 休眠
- PCA9685_I2C_Write_Data(PCA9685_MODE1, 0x10);
-
- // 输入频率
- PCA9685_I2C_Write_Data(PCA9685_PRE_SCALE, val);
-
- // 0xa1 1010 0001 [7] = 1 已启用重新启动 [5] = 1 启用寄存器自动增量 [0] = 1 PCA9685响应 LED All Call I2C总线地址
- PCA9685_I2C_Write_Data(PCA9685_MODE1, 0xa1);
- hi_udelay(500);
- }
freq * 0.95 是为了防过冲
MODE1 [4] 位休眠位 置0一定要等待500us,否者就会出错,然后模块就像失灵了一样,不要担心没有坏,改一下 I2C的工作频率100KHz,试试能不能通,通了以后再恢复到400K。
然后就是控制舵机
SG90, 转动范围是180度,
0.5ms ---- 0 ° 0.5ms / 20ms = 0.025
1.0ms ---- 45 ° 1.0ms / 20ms = 0.050
1.5ms ---- 90 ° 1.5ms / 20ms = 0.075
2.0ms ---- 135 ° 2.0ms / 20ms = 0.100
2.5ms ---- 180 ° 2.5ms / 20ms = 0.125
PCA9685设置占空比, 通过4个1组,共16组LED0_ON_L,LED0_ON_H,LED0_OFF_L,LED0_OFF_H,寄存器设置。
模块处理的数据是16位整数,1个寄存器长度不够,所以由2个寄存器组成1个数值,LED0_ON_L,LED0_ON_H,是开始时间,LED0_OFF_L,LED0_OFF_H,是结束时间。这种设计是非常好的,如果高精度控制舵机非常有帮助,通常不需要高的精度,一般舵机也没有太高精度,所以开始时间就简单设置为0,直接设置结束时间LED0_OFF_L,LED0_OFF_H。
- // 设置PWM 占空比
- hi_void PCA9685_Set_PWM(hi_u8 num, hi_u16 on, hi_u16 off)
- {
- hi_u8 datas[5] = {0};
-
- datas[0] = PCA9685_LED0_ON_L + 4 * num;
- datas[1] = on & 0xff;
- datas[2] = (on >> 8) & 0xff;
- datas[3] = off & 0xff;
- datas[4] = (off >> 8) & 0xff;
-
- PCA9685_I2C_Write_Datas(datas, 5);
- hi_udelay(500);
- }
SG90, 0 ° 设置:
0 ° = 0.5ms 0.5/20 = 0.025 4096 * 0.025 = 102.4 - 1 = 101
0端口,开始时间0,结束时间101
PCA9685_Set_PWM(0, 0, 101)
180 = 2.5ms 2.5/20 = 0.125 4096 * 0.125 = 512.0 - 1 = 511
(511 - 101) / 180 = 2.28 前面已经向上取整,这里就不要再取正了。
这样 任意角度设置 = 101 + 角度 * 2.28
- // 设置 角度
- hi_void PCA9685_Angle(hi_u8 num, hi_u8 ang)
- {
- // (511 - 101) / 180 = 2.28 已经向上取整
- hi_u16 off = (hi_u16)(101 + ang * 2.28);
-
- printf(" ang = %d, off = %d \n", ang, off);
-
- PCA9685_Set_PWM(num, 0, off);
- }
最后结合sh1106 OLED,和 hx1838 红外遥控器 做个测试例子。
链接:https://pan.baidu.com/s/1RMh_MsFsD762RxWP8YcwbA?pwd=6ik4
提取码:6ik4
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。