STM32实战:用TB6612驱动模块控制直流电机(附完整代码)
在智能小车、机械臂等嵌入式开发项目中,直流电机控制是最基础也最关键的环节之一。对于刚接触STM32的开发者来说,如何通过单片机精准控制电机转速和转向,往往成为项目推进的第一个技术门槛。本文将手把手带你完成从硬件连接到代码编写的全流程实战,使用TB6612这款高性价比驱动模块,配合STM32的PWM功能,实现直流电机的双向调速控制。
1. 硬件选型与电路设计
1.1 TB6612模块核心特性
TB6612FNG是东芝推出的双通道H桥驱动芯片,相比传统的L298N具有明显优势:
| 参数 | TB6612FNG | L298N |
|---|---|---|
| 工作电流 | 1.2A | 1A |
| 峰值电流 | 3.2A | 2A |
| 待机电流 | 0μA | 2mA |
| 工作电压 | 2.5-10V | 4.8-46V |
| 效率 | 92% | 60% |
关键引脚说明:
- VM:电机供电(建议4.5-10V)
- VCC:逻辑电平输入(2.7-5.5V)
- AIN1/AIN2:电机A控制输入
- PWMA:电机A PWM输入
- STBY:使能引脚(高电平工作)
1.2 典型接线方案
// STM32与TB6612连接示例 TB6612_STBY -> PA0 // 使能控制 TB6612_AIN1 -> PA1 // 方向控制1 TB6612_AIN2 -> PA2 // 方向控制2 TB6612_PWMA -> PA3 // PWM速度控制 TB6612_VM -> 7.4V锂电池 TB6612_GND -> 共地注意:电机供电与逻辑供电必须共地,PWM频率建议在1kHz-10kHz之间。大电流场景下,务必在VM端并联100μF以上电容。
2. STM32 PWM配置详解
2.1 定时器基础配置
使用TIM3的通道1生成PWM波,关键参数计算:
// 时钟树配置示例(72MHz主频) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseInitTypeDef TIM_InitStruct; TIM_InitStruct.TIM_Period = 999; // ARR值 TIM_InitStruct.TIM_Prescaler = 71; // 预分频 // 输出PWM频率 = 72MHz / (71+1) / (999+1) = 1kHz2.2 PWM输出配置技巧
TIM_OCInitTypeDef PWM_OCInit; PWM_OCInit.TIM_OCMode = TIM_OCMode_PWM1; PWM_OCInit.TIM_OutputState = TIM_OutputState_Enable; PWM_OCInit.TIM_Pulse = 500; // 初始占空比50% TIM_OC1Init(TIM3, &PWM_OCInit); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);常见问题排查:
- 无PWM输出:检查GPIO是否配置为复用推挽输出
- 波形畸变:降低预分频系数提高计数器时钟
- 控制不灵敏:调整ARR值改变分辨率
3. 电机驱动层实现
3.1 方向控制逻辑
通过AIN1/AIN2的组合决定电机转向:
| AIN1 | AIN2 | 电机状态 |
|---|---|---|
| 0 | 0 | 刹车 |
| 0 | 1 | 反转 |
| 1 | 0 | 正转 |
| 1 | 1 | 刹车 |
3.2 完整驱动代码
// motor.h typedef enum { MOTOR_STOP, MOTOR_CW, // 顺时针 MOTOR_CCW // 逆时针 } MotorDir; void Motor_Init(void); void Motor_Set(MotorDir dir, uint8_t speed);// motor.c #include "stm32f10x_gpio.h" #include "stm32f10x_tim.h" #define MOTOR_AIN1_PIN GPIO_Pin_1 #define MOTOR_AIN2_PIN GPIO_Pin_2 #define MOTOR_PWMA_PIN GPIO_Pin_3 void Motor_Init() { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 方向控制引脚 GPIO_InitStruct.GPIO_Pin = MOTOR_AIN1_PIN | MOTOR_AIN2_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruct); // PWM初始化(略) } void Motor_Set(MotorDir dir, uint8_t speed) { switch(dir) { case MOTOR_CW: GPIO_SetBits(GPIOA, MOTOR_AIN1_PIN); GPIO_ResetBits(GPIOA, MOTOR_AIN2_PIN); break; case MOTOR_CCW: GPIO_ResetBits(GPIOA, MOTOR_AIN1_PIN); GPIO_SetBits(GPIOA, MOTOR_AIN2_PIN); break; default: GPIO_ResetBits(GPIOA, MOTOR_AIN1_PIN); GPIO_ResetBits(GPIOA, MOTOR_AIN2_PIN); } TIM_SetCompare1(TIM3, speed); // 更新PWM占空比 }4. 应用层开发实例
4.1 按键调速实现
// main.c #include "motor.h" #include "key.h" int main() { uint8_t speed = 0; Motor_Init(); Key_Init(); while(1) { if(Key_Pressed(KEY_UP)) { speed = (speed < 100) ? speed + 10 : 100; Motor_Set(MOTOR_CW, speed); } else if(Key_Pressed(KEY_DOWN)) { speed = (speed > 0) ? speed - 10 : 0; Motor_Set(speed ? MOTOR_CW : MOTOR_STOP, speed); } Delay_ms(100); } }4.2 PID速度闭环控制
对于需要精确调速的场景,可以引入编码器反馈:
// pid.c typedef struct { float Kp, Ki, Kd; float error, lastError; float integral; } PID; float PID_Update(PID* pid, float setpoint, float feedback) { pid->error = setpoint - feedback; pid->integral += pid->error; float output = pid->Kp * pid->error + pid->Ki * pid->integral + pid->Kd * (pid->error - pid->lastError); pid->lastError = pid->error; return output; }实际调试中发现,当电机负载突变时,单纯的比例控制会出现明显抖动。加入微分项后,系统响应更平稳,但需要注意避免高频噪声带来的干扰。