1. 认识RZ7886电机驱动芯片
第一次接触RZ7886是在去年做一个智能小车项目时,当时需要找一个既能控制正反转又能PWM调速的驱动芯片。对比了几款常见驱动IC后,最终选择了这款性价比超高的国产芯片。RZ7886最大的特点是内置了H桥电路和PWM控制逻辑,单芯片就能搞定直流电机的双向驱动,省去了外接MOS管的麻烦。
实测下来,这颗芯片有几个让我惊喜的地方:首先是驱动能力,虽然标称持续电流7A,但散热做好时短时间可以扛到13A;其次是待机电流特别小,只有几个微安,这对电池供电的设备很友好;最关键是内置了反向电动势吸收二极管,不用再外接续流二极管,PCB布局能简洁不少。
说到引脚定义,标准的DIP8封装用起来很顺手:
- VM接电机电源(建议6-24V)
- GND接地
- IN1和IN2接控制信号
- OUT1和OUT2接电机两端
- VCC接逻辑电源(3.3V/5V都行)
注意:实际布线时建议在VM引脚就近放置100uF以上的电解电容,我在第一次测试时就因为电容太小导致芯片重启。
2. STM32硬件连接方案
我常用的搭配是STM32F103C8T6最小系统板+RZ7886模块。具体接线时,TIM3的CH1和CH2正好可以复用为PWM输出引脚,对应到开发板上的PB4和PB5。这里有个小技巧:通过GPIO重映射功能,可以把TIM3通道1映射到PB4,通道2映射到PB5,这样布线走线最顺。
硬件连接示意图:
STM32F103C8T6 RZ7886 PB4(TIM3_CH1) ----> IN1 PB5(TIM3_CH2) ----> IN2 GND ----> GND 3.3V ----> VCC电机电源建议单独供电,我测试时用12V/2A的开关电源带775电机完全没问题。如果要用电池供电,记得在电源输入端加个开关,不然RZ7886的待机电流虽然小,长时间放着也会耗光电量。
遇到过的一个坑是:刚开始没加光耦隔离,电机启动时偶尔会导致STM32复位。后来在PWM信号线上加了PC817光耦就再没出过问题。如果空间允许,建议在信号线上串个100欧姆电阻做限流保护。
3. PWM调速原理与实现
PWM调速的本质是通过调节占空比来控制平均电压。比如12V电源用50%占空比,等效输出电压就是6V。在STM32上配置PWM其实很简单,主要是三步:
- 初始化定时器基准
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_TimeBaseStruct.TIM_Prescaler = 71; // 72MHz/(71+1)=1MHz TIM_TimeBaseStruct.TIM_Period = 999; // 1MHz/1000=1kHz PWM频率 TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStruct);- 配置PWM模式
TIM_OCInitTypeDef TIM_OCStruct; TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM2; TIM_OCStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCStruct.TIM_Pulse = 500; // 初始占空比50% TIM_OCStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM3, &TIM_OCStruct); // 通道1- 使能预装载和定时器
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_Cmd(TIM3, ENABLE);实际调试时发现,PWM频率选1kHz-5kHz比较合适。太低会听到电机啸叫,太高会导致开关损耗增大。占空比分辨率建议至少1000级,这样低速时也能平稳运转。
4. 正反转控制逻辑
RZ7886的控制逻辑非常直观:
- IN1=0, IN2=PWM → 正转
- IN1=PWM, IN2=0 → 反转
- IN1=IN2=0 → 刹车
- IN1=IN2=1 → 自由停止
对应的代码实现:
void Motor_Forward(uint16_t speed) { GPIO_ResetBits(GPIOB, GPIO_Pin_4); // IN1=0 TIM_SetCompare2(TIM3, speed); // IN2=PWM } void Motor_Backward(uint16_t speed) { GPIO_ResetBits(GPIOB, GPIO_Pin_5); // IN2=0 TIM_SetCompare1(TIM3, speed); // IN1=PWM }这里有个重要细节:切换方向时最好先刹车再反转。直接反向容易产生大电流冲击。我的做法是加个50ms的延时:
void Motor_ChangeDirection(uint16_t speed) { Motor_Brake(); // 先刹车 delay_ms(50); Motor_Backward(speed); // 再反转 }5. 完整代码解析
结合前面的模块,完整的控制程序可以分为三层架构:
- 硬件抽象层(HAL)
- timer.c:处理PWM初始化
- gpio.c:配置控制引脚
- delay.c:提供毫秒延时
- 驱动层
- motor.c:封装正反转接口
- rz7886.c:芯片特定控制逻辑
- 应用层
- main.c:实现业务逻辑
关键函数调用流程:
main() ├─ HAL_Init() ├─ MX_TIM3_Init() ├─ MX_GPIO_Init() └─ while(1) ├─ Motor_Forward(500) ├─ delay_ms(1000) ├─ Motor_Brake() └─ Motor_Backward(300)实测中发现,启动时逐渐增大PWM值可以避免电流冲击。我通常用线性加速算法:
for(int i=0; i<1000; i+=10) { TIM_SetCompare2(TIM3, i); delay_ms(1); }6. 常见问题排查
- 电机不转
- 检查VM电压是否正常
- 用万用表测IN1/IN2是否有PWM信号
- 尝试短接OUT1和OUT2看电机是否自由转动
- 电机单向转动
- 交换OUT1和OUT2接线测试
- 检查控制信号极性是否正确
- 测量两个PWM通道输出是否正常
- 电机抖动或异响
- 适当提高PWM频率
- 检查电源是否功率不足
- 尝试减小占空比步进值
- 芯片发热严重
- 确认散热片安装良好
- 检查电机电流是否超限
- 测量VM电压是否过高
最近一个项目中出现过诡异的现象:电机偶尔会自发轻微转动。后来发现是PCB布局问题,PWM信号线太长引入了干扰。重新布线后问题解决。建议信号线尽量短,必要时加屏蔽层。
7. 进阶应用技巧
- 速度闭环控制 通过编码器反馈可以实现精准调速。我用的是M法测速:
uint16_t Get_Speed(void) { static uint32_t last_count = 0; uint32_t current_count = TIM_GetCounter(TIM2); // 编码器计数器 uint16_t speed = (current_count - last_count) * 1000 / SAMPLE_TIME; last_count = current_count; return speed; }- 电流保护 在电源线上加个0.1欧姆采样电阻,通过运放放大后送ADC检测:
if(ADC_Value > 2500) { // 超过2.5A Motor_Brake(); Error_Handler(); }- 缓启动设计 用查表法实现S曲线加速:
const uint16_t accel_table[] = {0,50,180,380,500,680,800,900,950,1000}; for(int i=0; i<10; i++) { TIM_SetCompare2(TIM3, accel_table[i]); delay_ms(100); }最后分享一个实用技巧:调试时可以用串口实时输出PWM值和电机状态。我通常用printf重定向到USART1,配合串口助手监控:
printf("PWM:%d DIR:%s\n", pwm_val, dir?"FWD":"REV");