You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 01:27:20