从零开始玩转STM32与TB6612直流电机驱动第一次拿到STM32开发板和TB6612电机驱动模块时我盯着桌上那台小直流电机发愁——作为一个电子爱好者想实现电机的正反转和调速控制却不知从何下手。如果你也面临同样的困惑这篇教程将带你一步步完成这个看似复杂实则有趣的项目。我们不需要昂贵的编码器电机用最基础的STM32F103C8T6最小系统板、TB6612模块和普通直流电机就能实现核心功能。1. 硬件准备与基础原理在开始编程前我们需要先理解硬件的工作原理。TB6612是一款双通道H桥驱动芯片能同时控制两个直流电机。与常见的L298N相比它的效率更高、发热更少特别适合电池供电的小型项目。必备硬件清单STM32F103C8T6最小系统板蓝色药丸板TB6612电机驱动模块5V-12V直流电机建议先使用普通减速电机练习杜邦线若干稳压电源或锂电池组建议7.4V-12VTB6612模块上有几个关键接口需要特别注意引脚标识功能说明连接目标VM电机电源接7-12V电源正极VCC逻辑电源接STM32的3.3VGND共地接电源和STM32的GNDAIN1/AIN2电机A控制信号接STM32 GPIOPWMA电机A PWM输入接STM32 PWM输出AO1/AO2电机A输出接电机两端注意一定要确保STM32和TB6612的GND相连这是很多初学者容易忽略的关键点否则控制信号无法正常传递。H桥的工作原理就像四个开关组合控制电流方向。当AIN11、AIN20时电机正转AIN10、AIN21时反转两者同为1或0时刹车。PWMA则通过PWM占空比调节电机转速。2. CubeMX工程配置现在打开STM32CubeMX开始配置工程。选择STM32F103C8系列芯片首先配置时钟源在RCC配置中启用外部高速晶振(HSE)时钟树配置为72MHz主频在SYS中勾选Serial Wire调试接口接下来配置PWM输出找到TIM3或其他支持PWM的定时器将Channel1设置为PWM Generation CH1预分频(Prescaler)设为71计数周期(Counter Period)设为999这样得到的PWM频率为PWM频率 72MHz / (711) / (9991) 1kHz然后配置两个GPIO控制电机方向选择两个普通IO口如PA4和PA5设置为输出模式初始电平为低重命名为AIN1和AIN2方便代码阅读最后生成代码前记得设置工程名称和路径选择Toolchain为MDK-ARM如果用Keil勾选Generate peripheral initialization as a pair of .c/.h files// 生成的PWM初始化关键代码片段 htim3.Instance TIM3; htim3.Init.Prescaler 71; htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 999; htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim3); TIM_OC_InitTypeDef sConfigOC; sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; // 初始占空比为0 sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1);3. 硬件连接与测试按照以下步骤连接硬件将STM32的3.3V连接到TB6612的VCCSTM32的GND连接到TB6612的GND配置好的PWM引脚如PA6接PWMA方向控制引脚PA4、PA5分别接AIN1和AIN2电机电源VM接7-12V电源正极电机两端接在AO1和AO2上常见接线错误排查电机不转先检查所有电源连接特别是共地电机只震动不转PWM频率可能过高尝试调整到1-5kHz模块发热严重立即断电检查是否短路或电机堵转上电前建议先用万用表检查VM和GND之间电阻防止电源短路电机两端电阻通常几欧姆到几十欧姆确认3.3V电源正常测试代码可以先用简单的逻辑验证// 在main.c的while(1)循环中添加测试代码 HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, 500); // 50%占空比 // 正转 HAL_GPIO_WritePin(GPIOA, AIN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, AIN2_Pin, GPIO_PIN_RESET); HAL_Delay(2000); // 反转 HAL_GPIO_WritePin(GPIOA, AIN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, AIN2_Pin, GPIO_PIN_SET); HAL_Delay(2000); // 刹车 HAL_GPIO_WritePin(GPIOA, AIN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, AIN2_Pin, GPIO_PIN_SET); HAL_Delay(1000);4. 完善电机控制函数为了代码更好的复用性我们可以将电机控制封装成独立函数。新建一个motor.c和motor.h文件// motor.h #ifndef __MOTOR_H #define __MOTOR_H #include stm32f1xx_hal.h typedef struct { GPIO_TypeDef* IN1_Port; uint16_t IN1_Pin; GPIO_TypeDef* IN2_Port; uint16_t IN2_Pin; TIM_HandleTypeDef* PWM_Timer; uint32_t PWM_Channel; } Motor_TypeDef; void Motor_Init(Motor_TypeDef* motor, GPIO_TypeDef* IN1_Port, uint16_t IN1_Pin, GPIO_TypeDef* IN2_Port, uint16_t IN2_Pin, TIM_HandleTypeDef* PWM_Timer, uint32_t PWM_Channel); void Motor_SetSpeed(Motor_TypeDef* motor, int16_t speed); #endif// motor.c #include motor.h void Motor_Init(Motor_TypeDef* motor, GPIO_TypeDef* IN1_Port, uint16_t IN1_Pin, GPIO_TypeDef* IN2_Port, uint16_t IN2_Pin, TIM_HandleTypeDef* PWM_Timer, uint32_t PWM_Channel) { motor-IN1_Port IN1_Port; motor-IN1_Pin IN1_Pin; motor-IN2_Port IN2_Port; motor-IN2_Pin IN2_Pin; motor-PWM_Timer PWM_Timer; motor-PWM_Channel PWM_Channel; HAL_TIM_PWM_Start(motor-PWM_Timer, motor-PWM_Channel); } void Motor_SetSpeed(Motor_TypeDef* motor, int16_t speed) { speed (speed 1000) ? 1000 : speed; speed (speed -1000) ? -1000 : speed; if(speed 0) { // 正转 HAL_GPIO_WritePin(motor-IN1_Port, motor-IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(motor-IN2_Port, motor-IN2_Pin, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(motor-PWM_Timer, motor-PWM_Channel, speed); } else if(speed 0) { // 反转 HAL_GPIO_WritePin(motor-IN1_Port, motor-IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(motor-IN2_Port, motor-IN2_Pin, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(motor-PWM_Timer, motor-PWM_Channel, -speed); } else { // 刹车 HAL_GPIO_WritePin(motor-IN1_Port, motor-IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(motor-IN2_Port, motor-IN2_Pin, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(motor-PWM_Timer, motor-PWM_Channel, 0); } }使用这个电机驱动库时只需要几行代码就能实现复杂控制Motor_TypeDef motor; int main(void) { // ...初始化代码... Motor_Init(motor, GPIOA, GPIO_PIN_4, GPIOA, GPIO_PIN_5, htim3, TIM_CHANNEL_1); while(1) { // 加速正转 for(int i0; i1000; i10) { Motor_SetSpeed(motor, i); HAL_Delay(20); } // 减速到停止 for(int i1000; i0; i-10) { Motor_SetSpeed(motor, i); HAL_Delay(20); } // 同样方式反转 // ... } }5. 进阶调试技巧当基本功能实现后你可能遇到一些需要优化的情况PWM频率选择1-5kHz通用范围电机运行平稳但可能有轻微啸叫10-20kHz超声波范围噪音小但驱动芯片可能发热增加20kHz适合对噪音敏感场合但效率可能降低死区时间设置 在CubeMX的TIMx配置中可以设置Dead Time防止H桥上下管直通。对于TB6612这种集成驱动通常不需要设置但如果你用分立元件搭建H桥建议设置500ns-1us的死区。电源去耦 在VM和GND之间靠近驱动芯片的位置添加一个100uF电解电容并联0.1uF陶瓷电容能有效抑制电机启动时的电压波动。电流检测 TB6612的AO1和AO2之间可以串联小电阻0.1-0.5欧姆通过测量电压降估算电流。当电流过大时及时切断输出保护电机和驱动芯片。// 简单的电流保护示例 #define CURRENT_THRESHOLD 2000 // 2A对应的ADC值 void Motor_SafetyCheck(Motor_TypeDef* motor) { uint32_t adcValue ReadMotorCurrentADC(); // 需要实现ADC读取函数 if(adcValue CURRENT_THRESHOLD) { Motor_SetSpeed(motor, 0); // 紧急刹车 Error_Handler(); // 进入错误处理 } }6. 项目扩展思路基础功能实现后可以考虑以下扩展方向串口控制通过USB转TTL模块用电脑串口助手发送指令控制电机电位器调速外接电位器用ADC读取电压值映射为电机转速无线控制添加蓝牙或2.4G模块用手机或遥控器控制多电机同步利用STM32的多个定时器同时控制2-4个电机速度闭环虽然本教程用开环控制但可以尝试添加编码器实现简单PID一个实用的串口控制协议示例// 在main.c中添加串口回调处理 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart huart1) { static char cmd[10]; static uint8_t idx 0; if(rxData \n) { // 接收到完整命令 cmd[idx] \0; int speed atoi(cmd); Motor_SetSpeed(motor, speed); idx 0; } else { cmd[idx] rxData; } HAL_UART_Receive_IT(huart1, rxData, 1); // 重新启用接收中断 } }使用时通过串口发送-500到500之间的数字就能实时控制电机转速和方向。
手把手教你用STM32F103C8T6和TB6612驱动直流电机(附HAL库代码)
发布时间:2026/5/24 14:41:37
从零开始玩转STM32与TB6612直流电机驱动第一次拿到STM32开发板和TB6612电机驱动模块时我盯着桌上那台小直流电机发愁——作为一个电子爱好者想实现电机的正反转和调速控制却不知从何下手。如果你也面临同样的困惑这篇教程将带你一步步完成这个看似复杂实则有趣的项目。我们不需要昂贵的编码器电机用最基础的STM32F103C8T6最小系统板、TB6612模块和普通直流电机就能实现核心功能。1. 硬件准备与基础原理在开始编程前我们需要先理解硬件的工作原理。TB6612是一款双通道H桥驱动芯片能同时控制两个直流电机。与常见的L298N相比它的效率更高、发热更少特别适合电池供电的小型项目。必备硬件清单STM32F103C8T6最小系统板蓝色药丸板TB6612电机驱动模块5V-12V直流电机建议先使用普通减速电机练习杜邦线若干稳压电源或锂电池组建议7.4V-12VTB6612模块上有几个关键接口需要特别注意引脚标识功能说明连接目标VM电机电源接7-12V电源正极VCC逻辑电源接STM32的3.3VGND共地接电源和STM32的GNDAIN1/AIN2电机A控制信号接STM32 GPIOPWMA电机A PWM输入接STM32 PWM输出AO1/AO2电机A输出接电机两端注意一定要确保STM32和TB6612的GND相连这是很多初学者容易忽略的关键点否则控制信号无法正常传递。H桥的工作原理就像四个开关组合控制电流方向。当AIN11、AIN20时电机正转AIN10、AIN21时反转两者同为1或0时刹车。PWMA则通过PWM占空比调节电机转速。2. CubeMX工程配置现在打开STM32CubeMX开始配置工程。选择STM32F103C8系列芯片首先配置时钟源在RCC配置中启用外部高速晶振(HSE)时钟树配置为72MHz主频在SYS中勾选Serial Wire调试接口接下来配置PWM输出找到TIM3或其他支持PWM的定时器将Channel1设置为PWM Generation CH1预分频(Prescaler)设为71计数周期(Counter Period)设为999这样得到的PWM频率为PWM频率 72MHz / (711) / (9991) 1kHz然后配置两个GPIO控制电机方向选择两个普通IO口如PA4和PA5设置为输出模式初始电平为低重命名为AIN1和AIN2方便代码阅读最后生成代码前记得设置工程名称和路径选择Toolchain为MDK-ARM如果用Keil勾选Generate peripheral initialization as a pair of .c/.h files// 生成的PWM初始化关键代码片段 htim3.Instance TIM3; htim3.Init.Prescaler 71; htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 999; htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim3); TIM_OC_InitTypeDef sConfigOC; sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; // 初始占空比为0 sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1);3. 硬件连接与测试按照以下步骤连接硬件将STM32的3.3V连接到TB6612的VCCSTM32的GND连接到TB6612的GND配置好的PWM引脚如PA6接PWMA方向控制引脚PA4、PA5分别接AIN1和AIN2电机电源VM接7-12V电源正极电机两端接在AO1和AO2上常见接线错误排查电机不转先检查所有电源连接特别是共地电机只震动不转PWM频率可能过高尝试调整到1-5kHz模块发热严重立即断电检查是否短路或电机堵转上电前建议先用万用表检查VM和GND之间电阻防止电源短路电机两端电阻通常几欧姆到几十欧姆确认3.3V电源正常测试代码可以先用简单的逻辑验证// 在main.c的while(1)循环中添加测试代码 HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, 500); // 50%占空比 // 正转 HAL_GPIO_WritePin(GPIOA, AIN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, AIN2_Pin, GPIO_PIN_RESET); HAL_Delay(2000); // 反转 HAL_GPIO_WritePin(GPIOA, AIN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, AIN2_Pin, GPIO_PIN_SET); HAL_Delay(2000); // 刹车 HAL_GPIO_WritePin(GPIOA, AIN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, AIN2_Pin, GPIO_PIN_SET); HAL_Delay(1000);4. 完善电机控制函数为了代码更好的复用性我们可以将电机控制封装成独立函数。新建一个motor.c和motor.h文件// motor.h #ifndef __MOTOR_H #define __MOTOR_H #include stm32f1xx_hal.h typedef struct { GPIO_TypeDef* IN1_Port; uint16_t IN1_Pin; GPIO_TypeDef* IN2_Port; uint16_t IN2_Pin; TIM_HandleTypeDef* PWM_Timer; uint32_t PWM_Channel; } Motor_TypeDef; void Motor_Init(Motor_TypeDef* motor, GPIO_TypeDef* IN1_Port, uint16_t IN1_Pin, GPIO_TypeDef* IN2_Port, uint16_t IN2_Pin, TIM_HandleTypeDef* PWM_Timer, uint32_t PWM_Channel); void Motor_SetSpeed(Motor_TypeDef* motor, int16_t speed); #endif// motor.c #include motor.h void Motor_Init(Motor_TypeDef* motor, GPIO_TypeDef* IN1_Port, uint16_t IN1_Pin, GPIO_TypeDef* IN2_Port, uint16_t IN2_Pin, TIM_HandleTypeDef* PWM_Timer, uint32_t PWM_Channel) { motor-IN1_Port IN1_Port; motor-IN1_Pin IN1_Pin; motor-IN2_Port IN2_Port; motor-IN2_Pin IN2_Pin; motor-PWM_Timer PWM_Timer; motor-PWM_Channel PWM_Channel; HAL_TIM_PWM_Start(motor-PWM_Timer, motor-PWM_Channel); } void Motor_SetSpeed(Motor_TypeDef* motor, int16_t speed) { speed (speed 1000) ? 1000 : speed; speed (speed -1000) ? -1000 : speed; if(speed 0) { // 正转 HAL_GPIO_WritePin(motor-IN1_Port, motor-IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(motor-IN2_Port, motor-IN2_Pin, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(motor-PWM_Timer, motor-PWM_Channel, speed); } else if(speed 0) { // 反转 HAL_GPIO_WritePin(motor-IN1_Port, motor-IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(motor-IN2_Port, motor-IN2_Pin, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(motor-PWM_Timer, motor-PWM_Channel, -speed); } else { // 刹车 HAL_GPIO_WritePin(motor-IN1_Port, motor-IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(motor-IN2_Port, motor-IN2_Pin, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(motor-PWM_Timer, motor-PWM_Channel, 0); } }使用这个电机驱动库时只需要几行代码就能实现复杂控制Motor_TypeDef motor; int main(void) { // ...初始化代码... Motor_Init(motor, GPIOA, GPIO_PIN_4, GPIOA, GPIO_PIN_5, htim3, TIM_CHANNEL_1); while(1) { // 加速正转 for(int i0; i1000; i10) { Motor_SetSpeed(motor, i); HAL_Delay(20); } // 减速到停止 for(int i1000; i0; i-10) { Motor_SetSpeed(motor, i); HAL_Delay(20); } // 同样方式反转 // ... } }5. 进阶调试技巧当基本功能实现后你可能遇到一些需要优化的情况PWM频率选择1-5kHz通用范围电机运行平稳但可能有轻微啸叫10-20kHz超声波范围噪音小但驱动芯片可能发热增加20kHz适合对噪音敏感场合但效率可能降低死区时间设置 在CubeMX的TIMx配置中可以设置Dead Time防止H桥上下管直通。对于TB6612这种集成驱动通常不需要设置但如果你用分立元件搭建H桥建议设置500ns-1us的死区。电源去耦 在VM和GND之间靠近驱动芯片的位置添加一个100uF电解电容并联0.1uF陶瓷电容能有效抑制电机启动时的电压波动。电流检测 TB6612的AO1和AO2之间可以串联小电阻0.1-0.5欧姆通过测量电压降估算电流。当电流过大时及时切断输出保护电机和驱动芯片。// 简单的电流保护示例 #define CURRENT_THRESHOLD 2000 // 2A对应的ADC值 void Motor_SafetyCheck(Motor_TypeDef* motor) { uint32_t adcValue ReadMotorCurrentADC(); // 需要实现ADC读取函数 if(adcValue CURRENT_THRESHOLD) { Motor_SetSpeed(motor, 0); // 紧急刹车 Error_Handler(); // 进入错误处理 } }6. 项目扩展思路基础功能实现后可以考虑以下扩展方向串口控制通过USB转TTL模块用电脑串口助手发送指令控制电机电位器调速外接电位器用ADC读取电压值映射为电机转速无线控制添加蓝牙或2.4G模块用手机或遥控器控制多电机同步利用STM32的多个定时器同时控制2-4个电机速度闭环虽然本教程用开环控制但可以尝试添加编码器实现简单PID一个实用的串口控制协议示例// 在main.c中添加串口回调处理 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart huart1) { static char cmd[10]; static uint8_t idx 0; if(rxData \n) { // 接收到完整命令 cmd[idx] \0; int speed atoi(cmd); Motor_SetSpeed(motor, speed); idx 0; } else { cmd[idx] rxData; } HAL_UART_Receive_IT(huart1, rxData, 1); // 重新启用接收中断 } }使用时通过串口发送-500到500之间的数字就能实时控制电机转速和方向。