衡山派开发板I2C扩展16路舵机控制:PCA9685模块驱动移植与RT-Thread实战
衡山派开发板I2C扩展16路舵机控制PCA9685模块驱动移植与RT-Thread实战最近在做一个机械臂项目用衡山派开发板做主控发现一个很头疼的问题板子上的PWM引脚不够用了。舵机控制需要PWM信号一个舵机就要占一个引脚想做多自由度机器人引脚资源立马捉襟见肘。相信很多朋友在做机器人、机械臂或者多自由度项目时都遇到过类似问题。今天我就来分享一个非常实用的解决方案——通过I2C总线扩展16路PWM输出使用PCA9685舵机驱动模块。这个模块特别适合衡山派开发板只需要两个GPIO引脚I2C的SCL和SDA就能控制多达16个舵机而且还能级联最多可以控制992路PWM下面我就手把手教大家如何在RT-Thread操作系统下把PCA9685的驱动移植到衡山派开发板上实现精确的舵机角度控制。1. PCA9685模块你的多路PWM救星1.1 模块简介与工作原理PCA9685是一个I2C接口的16通道PWM控制器芯片。简单来说它就像一个PWM扩展器——你的主控芯片通过I2C告诉PCA9685第0通道输出1ms高电平19ms低电平PCA9685就会忠实地执行完全不需要主控芯片一直占用CPU资源去维持PWM波形。这和传统的TLC5940系列有很大不同。传统方案需要主控不断发送数据来维持PWM输出而PCA9685内置了时钟和PWM驱动器你只需要设置一次它就能自己维持输出大大减轻了主控的负担。模块关键特性16路独立PWM输出每路12位分辨率4096级I2C接口只需要SCL和SDA两根线可调频率约1.6kHz可调舵机常用50Hz宽电压兼容3.3V~5V供电可驱动5V舵机级联能力通过地址选择引脚最多可挂62个模块992路PWM输出使能引脚可快速禁用所有输出1.2 硬件连接与引脚定义模块的接线非常简单只需要4根线引脚说明连接衡山派开发板VCC电源正极3.3V~5V3.3V或5V电源GND电源地GNDSCLI2C时钟线PE.14SDAI2C数据线PE.12注意模块上已经内置了上拉电阻所以不需要外接上拉电阻。如果你发现通信不稳定可以尝试在SCL和SDA线上各加一个4.7kΩ的上拉电阻到VCC。模块的舵机接口是标准的3针接口信号、电源、地可以直接插接舵机。每个通道最大输出电流约15mA驱动舵机时需要外接电源。2. 驱动移植让PCA9685在RT-Thread上跑起来2.1 获取驱动代码首先需要下载驱动代码。驱动代码已经打包好你可以在衡山派开发板的资料下载中心找到下载路径资料下载中心 → 模块移植资料下载 → 找到16路舵机驱动模块的压缩包下载后解压把整个驱动文件夹复制到你的工程目录下\luban-lite\application\rt-thread\helloworld\user-bsp\提示如果你没有user-bsp这个文件夹说明你还没有进行模块移植的前置配置。需要先按照官方文档完成必要的配置操作。2.2 配置Kconfig文件接下来需要修改Kconfig文件让menuconfig能够识别我们的驱动模块。用VSCode或其他编辑器打开application\rt-thread\helloworld\Kconfig在文件的最后#endif语句前面添加以下内容# 16路舵机驱动模块 source application/rt-thread/helloworld/user-bsp/16ch-servo-drive-module/Kconfig2.3 使用menuconfig启用模块现在进入env工具配置环境打开env工具双击luban-lite文件夹下的win_env.bat应用默认配置输入以下命令选择衡山派开发板的默认配置scons --apply-defd13x_JLC_rt-thread_helloworld_defconfig进入menuconfigscons --menuconfig启用模块进入Porting code using the LCKFB module菜单找到Using 16-channel servo driver module选项按Y键选中前面会出现*号按左右方向键选择Save保存配置然后退出2.4 编译与烧录保存配置后开始编译工程scons -j16这里的-j16表示使用16个线程并行编译编译速度更快。你可以根据自己电脑的CPU核心数调整这个数字。编译完成后在以下路径会生成镜像文件\luban-lite\output\d13x_JLC_rt-thread_helloworld\images\d13x_JLC_v1.0.0.img用烧录工具将这个镜像烧录到开发板即可。3. 代码深度解析理解PCA9685的驱动原理3.1 I2C地址配置PCA9685是一个I2C从设备每个设备都有一个唯一的地址。地址由A0、A1、A2三个引脚的电平决定A2A1A0地址7位写地址读地址0000x400x800x810010x410x820x830100x420x840x85..................我们使用的模块默认A0、A1、A2都接地所以地址是0x40。在I2C通信时写地址是0x80地址左移1位最低位写0读地址是0x81地址左移1位最低位读1。在代码中这个地址定义在bsp_pca9685.c文件开头#define PCA_Addr 0x80 // IIC写地址3.2 PWM频率设置为什么是50Hz舵机控制需要20ms的PWM周期也就是50Hz的频率。PCA9685的输出频率通过一个预分频器prescale来设置。计算公式来自数据手册prescale round(25,000,000 / (4096 × freq)) - 1其中25,000,000是PCA9685的内部时钟频率25MHz4096是12位计数器的最大值0-4095freq是我们要设置的PWM频率对于50Hz的舵机控制频率prescale round(25,000,000 / (4096 × 50)) - 1 round(25,000,000 / 204,800) - 1 round(122.07) - 1 122 - 1 121但在实际代码中我们用的是这个公式prescaleval 25000000; prescaleval / 4096; prescaleval / freq; prescaleval - 1; prescale floor(prescaleval 0.5f);这里有个重要的注意事项修改频率必须在芯片休眠状态下进行。所以设置频率的步骤是读取MODE1寄存器的当前值设置SLEEP位让芯片进入休眠写入预分频值清除SLEEP位唤醒芯片等待5ms让芯片稳定对应的代码实现void PCA9685_setFreq(float freq) { uint8_t prescale, oldmode, newmode; double prescaleval; // 计算预分频值 prescaleval 25000000; prescaleval / 4096; prescaleval / freq; prescaleval - 1; prescale floor(prescaleval 0.5f); // 保存当前模式设置休眠位 oldmode PCA9685_Read(PCA_Model); newmode (oldmode 0x7F) | 0x10; // 设置SLEEP位 PCA9685_Write(PCA_Model, newmode); // 进入休眠 // 写入频率设置 PCA9685_Write(PCA_Pre, prescale); // 0xFE是预分频寄存器地址 // 恢复模式唤醒芯片 PCA9685_Write(PCA_Model, oldmode); delay_ms(5); // 设置自动递增模式 PCA9685_Write(PCA_Model, oldmode | 0xa1); }3.3 舵机角度控制脉宽计算舵机的控制原理是通过PWM脉冲的宽度来控制角度。标准舵机的控制信号是0.5ms脉宽 → 0度1.5ms脉宽 → 90度2.5ms脉宽 → 180度在20ms周期50Hz下这些脉宽对应的占空比是0.5ms / 20ms 2.5%1.5ms / 20ms 7.5%2.5ms / 20ms 12.5%PCA9685使用12位计数器0-4095所以我们需要把时间转换成计数值计数值 脉宽(秒) × 频率(Hz) × 4096对于50Hz频率0.5ms脉宽0.0005 × 50 × 4096 102.4 ≈ 1021.5ms脉宽0.0015 × 50 × 4096 307.2 ≈ 3072.5ms脉宽0.0025 × 50 × 4096 512但在实际代码中作者使用了经验公式。在PCA9685_SetAngle函数中int PCA9685_SetAngle(uint8_t Num, uint8_t Angle) { uint32_t off 0; off (uint32_t)(158 Angle * 2.2); PCA9685_SetPWM(Num, 0, off); return RT_EOK; }这个公式158 Angle * 2.2是经过实测校准的。我们来验证一下0度时158 0 × 2.2 15890度时158 90 × 2.2 158 198 356180度时158 180 × 2.2 158 396 554对应的脉宽158对应约0.77ms158/4096×20ms356对应约1.74ms554对应约2.70ms这个范围覆盖了大部分舵机的可调范围。如果你用的舵机不同可能需要调整这个公式。3.4 关键寄存器说明PCA9685有几个重要的寄存器需要了解寄存器地址名称说明0x00MODE1模式寄存器1控制芯片工作模式0x06LED0_ON_L通道0开启时间低字节0x07LED0_ON_H通道0开启时间高字节0x08LED0_OFF_L通道0关闭时间低字节0x09LED0_OFF_H通道0关闭时间高字节0xFEPRE_SCALE预分频寄存器设置PWM频率每个通道有4个寄存器ON_L、ON_H、OFF_L、OFF_H16个通道就是64个寄存器。寄存器地址是连续的所以通道n的寄存器地址是LEDn_ON_L 0x06 4×nLEDn_ON_H 0x07 4×nLEDn_OFF_L 0x08 4×nLEDn_OFF_H 0x09 4×n4. 实战应用创建舵机控制线程4.1 测试代码解析驱动包中提供了一个测试程序test_16ch_servo_drive_module.c这个文件创建了一个线程来控制舵机。我们来看看关键部分// 线程入口函数 static void pca9685_thread_entry(void *param) { int ret 0; int i 0; int while_count 1; /* PCA9685初始化设置频率60Hz初始角度0度 */ ret PCA9685_Init(60, 0); if(ret ! RT_EOK) { LOG_E(failed to PCA9685_Init !!); return; } rt_thread_mdelay(1000); // 等待1秒让舵机稳定 rt_kprintf(Start Loop !!\n); while(while_count) { i (i 1) % 180; // 角度从0循环到179 PCA9685_SetAngle(0, i); // 控制第0通道 if(while_count 5000) { while_count 1; rt_kprintf(\n输入[test_exit_pca9685_driver_module]命令退出\n); rt_thread_mdelay(2000); } rt_thread_mdelay(50); // 每50ms改变一次角度 } }这个线程做了几件事初始化PCA9685设置PWM频率为60Hz注意不是50Hz所有舵机初始角度为0度进入循环让第0通道的舵机从0度逐步增加到179度然后回到0度重新开始每5000次循环提示用户如何退出4.2 命令导出与使用代码最后导出了两个命令// 启动PCA9685线程 MSH_CMD_EXPORT(test_pca9685_driver_module, run PCA9685 16 ch driver module); // 退出PCA9685线程 MSH_CMD_EXPORT(test_exit_pca9685_driver_module, quit PCA9685);在RT-Thread的Finsh命令行中你可以输入test_pca9685_driver_module启动舵机控制输入test_exit_pca9685_driver_module停止控制提示输入命令时按Tab键可以自动补全命令名。4.3 串口调试验证将开发板通过USB转TTL模块连接到电脑使用串口调试工具如Putty、SecureCRT等连接波特率设置为115200。上电后在串口终端输入test_pca9685_driver_module你会看到舵机开始缓慢转动从0度转到180度然后回到0度重新开始。同时终端会输出Start Loop !!的提示信息。5. 实际项目中的使用技巧5.1 多舵机同时控制在实际项目中我们往往需要同时控制多个舵机。修改测试代码让多个舵机协同工作// 控制多个舵机做不同动作 for(int channel 0; channel 16; channel) { // 每个舵机从不同角度开始 int angle (channel * 10) % 180; PCA9685_SetAngle(channel, angle); }5.2 角度平滑移动直接设置角度会让舵机跳到目标位置不够平滑。我们可以实现一个平滑移动函数void smooth_move(uint8_t channel, uint8_t target_angle, uint8_t speed) { uint8_t current_angle get_current_angle(channel); // 需要自己记录当前角度 if(current_angle target_angle) { for(uint8_t i current_angle; i target_angle; i speed) { PCA9685_SetAngle(channel, i); rt_thread_mdelay(20); // 每20ms移动一小步 } } else { for(uint8_t i current_angle; i target_angle; i - speed) { PCA9685_SetAngle(channel, i); rt_thread_mdelay(20); } } // 最后确保到达目标位置 PCA9685_SetAngle(channel, target_angle); }5.3 处理不同品牌的舵机不同品牌、不同型号的舵机脉宽范围可能不同。常见的几种标准舵机0.5ms~2.5ms0-180度数码舵机0.5ms~2.5ms但中间位置可能是1.5ms特殊舵机有些是0.9ms~2.1ms或者0.6ms~2.4ms你可以在PCA9685_SetAngle函数中修改计算公式// 对于0.9ms~2.1ms的舵机 off (uint32_t)(184 Angle * 1.33); // 0.9ms~2.1ms // 对于0.6ms~2.4ms的舵机 off (uint32_t)(123 Angle * 2.4); // 0.6ms~2.4ms5.4 电源注意事项舵机在转动时电流很大特别是多个舵机同时转动时。务必注意独立供电不要用开发板的3.3V或5V直接给多个舵机供电电源滤波舵机电源端要加大的电解电容如1000μF滤波共地舵机电源地和开发板电源地一定要连接在一起电流估算小型舵机空载约100-200mA带负载可能达到500mA-1A我一般会用单独的5V/3A以上的开关电源给舵机供电开发板只提供控制信号。6. 常见问题与调试技巧6.1 舵机不转动或抖动可能原因1电源不足现象舵机不转或者转到某个位置就卡住解决用万用表测量舵机供电电压转动时电压不应低于4.8V可能原因2脉宽范围不对现象舵机只在一个小范围内转动解决调整PCA9685_SetAngle函数中的计算公式可能原因3频率设置错误现象舵机发热但不转动解决确认PWM频率设置为50Hz舵机标准或60Hz代码中用的6.2 I2C通信失败可能原因1地址错误现象完全没反应解决用逻辑分析仪或示波器抓取I2C波形确认地址是否正确可能原因2上拉电阻问题现象偶尔通信成功大部分时间失败解决在SCL和SDA线上各加4.7kΩ上拉电阻到3.3V可能原因3时序问题现象在特定角度通信失败解决调整I2C时序中的延时特别是delay_us(5)这些地方6.3 多个模块级联如果需要控制超过16个舵机可以级联多个PCA9685模块。每个模块的A0、A1、A2引脚设置不同的电平对应不同的I2C地址。接线时注意所有模块的VCC、GND、SCL、SDA并联每个模块的A0、A1、A2设置不同在代码中根据地址分别初始化每个模块// 初始化多个模块 PCA9685_Init_Addr(0x80, 60, 0); // 地址0x40的模块 PCA9685_Init_Addr(0x82, 60, 0); // 地址0x41的模块 PCA9685_Init_Addr(0x84, 60, 0); // 地址0x42的模块你需要修改驱动增加带地址参数的初始化函数。通过PCA9685模块我们成功解决了衡山派开发板PWM引脚不足的问题。现在只需要两个GPIO引脚就能控制16个甚至更多的舵机这对于机器人、机械臂、云台等需要多自由度控制的项目来说简直是神器。在实际使用中我建议先把单个舵机调通理解脉宽和角度的对应关系然后再扩展到多个舵机。电源问题一定要重视很多奇怪的问题都是电源引起的。如果遇到问题先用逻辑分析仪抓一下I2C波形往往能快速定位问题所在。这个方案我在好几个机器人项目中都用过稳定性很好。希望这篇教程能帮你快速上手PCA9685让你的多舵机项目顺利进行。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2421702.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!