STM32F4 I2C返回HAL_OK但无法驱动PCA9685电机驱动板
STM32 HAL库控制PCA9685电机驱动板无反应问题解决
核心问题分析
你的代码存在两个致命问题:未初始化PCA9685的工作模式(芯片上电默认处于睡眠状态,不响应任何输出控制),以及I2C传输的时序配置与调用方式错误,同时寄存器数据的逻辑也需要核对。
1. 补全PCA9685的强制初始化步骤
PCA9685上电后默认处于睡眠模式,必须先唤醒并配置核心寄存器才能工作,这是Arduino官方代码隐含的关键步骤,你转译时遗漏了。在MX_I2C_Init()后添加以下初始化代码:
#define MODE1 0x00 #define MODE2 0x01 #define PRE_SCALE 0xFE void PCA9685_Init(void) { uint8_t data; // 唤醒芯片:启用自动递增(BIT5),清除睡眠位(BIT4) data = 0x20; // 00100000,AI=1,SLEEP=0 HAL_I2C_Mem_Write(&hi2c1, driver_address, MODE1, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY); HAL_Delay(10); // 等待内部振荡器稳定 // 配置输出模式:推挽输出,低电平有效(与Arduino默认逻辑一致) data = 0x04; // 00000100,OUTDRV=1,INVRT=0 HAL_I2C_Mem_Write(&hi2c1, driver_address, MODE2, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY); // 设置PWM频率为1000Hz(公式:PRE_SCALE = (25000000 / 4096 / Fpwm) - 1,Fosc默认25MHz) data = 0x06; // 25000000 / 4096 / 1000 -1 ≈ 5.07,取整为6 HAL_I2C_Mem_Write(&hi2c1, driver_address, PRE_SCALE, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY); }
2. 修正I2C时序异常问题
针对HAL_I2C_Master_Transmit字节间额外高电平
- 检查I2C初始化配置:确保
MX_I2C_Init()中的ClockSpeed设置为100kHz(标准模式)或400kHz(快速模式),与Arduino代码一致(Arduino默认用100kHz)。 - 禁用时钟拉伸禁止位:在I2C的MSP初始化函数(
HAL_I2C_MspInit())中添加:__HAL_I2C_DISABLE_NO_STRETCH(&hi2c1); - 调整传输超时:将
HAL_I2C_Master_Transmit的超时参数从10改为HAL_MAX_DELAY,避免因超时中断导致的时序断裂。
针对HAL_I2C_Mem_Write的SCL无间隙问题
使用HAL_I2C_Mem_Write时必须传入正确参数,该函数专门用于设备寄存器写入,会自动处理时序逻辑,正确调用示例:
// 向M1IN1寄存器写入4字节PWM控制数据 HAL_I2C_Mem_Write(&hi2c1, driver_address, M1IN1, I2C_MEMADD_SIZE_8BIT, TX_Buffer_1, 4, HAL_MAX_DELAY);
3. 修正PWM控制逻辑
PCA9685的12位PWM计数器范围是0~4095,对应输出逻辑:
- 持续高电平:
ON=0,OFF=4096(OFF_H的BIT4置1,值为0x10) - 持续低电平:
ON=4096(ON_H的BIT4置1,值为0x10),OFF=0 - 100% PWM:设置
ON=0,OFF=4096(输出持续高,对应电机满速)
修正你的缓冲区数据:
// M1IN1持续高电平 uint8_t TX_Buffer_1[4] = {0, 0, 0x00, 0x10}; // M1IN2持续低电平 uint8_t TX_Buffer_2[4] = {0x00, 0x10, 0, 0}; // M1PWM满占空比输出 uint8_t TX_Buffer_3[4] = {0, 0, 0x00, 0x10};
修正后的完整主函数示例
#define driver_address 0x40<<1 #define LED0_ON_L 0x06 #define M1IN1 LED0_ON_L + 4 * 0 #define M1IN2 LED0_ON_L + 4 * 1 #define M1PWM LED0_ON_L + 4 * 2 #define MODE1 0x00 #define MODE2 0x01 #define PRE_SCALE 0xFE uint8_t TX_Buffer_1[4] = {0, 0, 0x00, 0x10}; // M1IN1 高电平 uint8_t TX_Buffer_2[4] = {0x00, 0x10, 0, 0}; // M1IN2 低电平 uint8_t TX_Buffer_3[4] = {0, 0, 0x00, 0x10}; // M1PWM 100% PWM void PCA9685_Init(void) { uint8_t data; data = 0x20; HAL_I2C_Mem_Write(&hi2c1, driver_address, MODE1, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY); HAL_Delay(10); data = 0x04; HAL_I2C_Mem_Write(&hi2c1, driver_address, MODE2, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY); data = 0x06; HAL_I2C_Mem_Write(&hi2c1, driver_address, PRE_SCALE, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY); } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C_Init(); // 检查设备是否存在 uint8_t device_exist = HAL_I2C_IsDeviceReady(&hi2c1, driver_address, 10, HAL_MAX_DELAY); if(device_exist != HAL_OK) { // 设备不存在的报错逻辑,比如点亮LED提示 while(1); } // 初始化PCA9685 PCA9685_Init(); while(1) { // 控制寄存器只需初始化时写入一次,无需循环重复写入 HAL_I2C_Mem_Write(&hi2c1, driver_address, M1IN1, I2C_MEMADD_SIZE_8BIT, TX_Buffer_1, 4, HAL_MAX_DELAY); HAL_I2C_Mem_Write(&hi2c1, driver_address, M1IN2, I2C_MEMADD_SIZE_8BIT, TX_Buffer_2, 4, HAL_MAX_DELAY); HAL_I2C_Mem_Write(&hi2c1, driver_address, M1PWM, I2C_MEMADD_SIZE_8BIT, TX_Buffer_3, 4, HAL_MAX_DELAY); HAL_Delay(1000); // 示例延时,实际可移除 } }
额外注意事项
- 确保I2C引脚的上拉电阻正常:PCA9685需要外部上拉,STM32内部上拉可能不足,建议添加4.7kΩ上拉电阻到SDA/SCL引脚。
- 逻辑分析仪采样时,设置采样率不低于10MHz,确保准确捕获I2C的每个时钟边沿。
内容的提问来源于stack exchange,提问作者jingjingccc
相关产品推荐
相关产品推荐

