当前位置: 首页 > news >正文

手把手教你用STM32F103C8T6和TB6612驱动直流电机(附HAL库代码)

从零开始玩转STM32与TB6612直流电机驱动

第一次拿到STM32开发板和TB6612电机驱动模块时,我盯着桌上那台小直流电机发愁——作为一个电子爱好者,想实现电机的正反转和调速控制,却不知从何下手。如果你也面临同样的困惑,这篇教程将带你一步步完成这个看似复杂实则有趣的项目。我们不需要昂贵的编码器电机,用最基础的STM32F103C8T6最小系统板、TB6612模块和普通直流电机就能实现核心功能。

1. 硬件准备与基础原理

在开始编程前,我们需要先理解硬件的工作原理。TB6612是一款双通道H桥驱动芯片,能同时控制两个直流电机。与常见的L298N相比,它的效率更高、发热更少,特别适合电池供电的小型项目。

必备硬件清单

  • STM32F103C8T6最小系统板(蓝色药丸板)
  • TB6612电机驱动模块
  • 5V-12V直流电机(建议先使用普通减速电机练习)
  • 杜邦线若干
  • 稳压电源或锂电池组(建议7.4V-12V)

TB6612模块上有几个关键接口需要特别注意:

引脚标识功能说明连接目标
VM电机电源接7-12V电源正极
VCC逻辑电源接STM32的3.3V
GND共地接电源和STM32的GND
AIN1/AIN2电机A控制信号接STM32 GPIO
PWMA电机A PWM输入接STM32 PWM输出
AO1/AO2电机A输出接电机两端

注意:一定要确保STM32和TB6612的GND相连,这是很多初学者容易忽略的关键点,否则控制信号无法正常传递。

H桥的工作原理就像四个开关组合控制电流方向。当AIN1=1、AIN2=0时,电机正转;AIN1=0、AIN2=1时反转;两者同为1或0时刹车。PWMA则通过PWM占空比调节电机转速。

2. CubeMX工程配置

现在打开STM32CubeMX开始配置工程。选择STM32F103C8系列芯片,首先配置时钟源:

  1. 在RCC配置中启用外部高速晶振(HSE)
  2. 时钟树配置为72MHz主频
  3. 在SYS中勾选Serial Wire调试接口

接下来配置PWM输出:

  1. 找到TIM3(或其他支持PWM的定时器)
  2. 将Channel1设置为PWM Generation CH1
  3. 预分频(Prescaler)设为71,计数周期(Counter Period)设为999,这样得到的PWM频率为:
    PWM频率 = 72MHz / (71+1) / (999+1) = 1kHz

然后配置两个GPIO控制电机方向:

  1. 选择两个普通IO口(如PA4和PA5)
  2. 设置为输出模式,初始电平为低
  3. 重命名为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. 硬件连接与测试

按照以下步骤连接硬件:

  1. 将STM32的3.3V连接到TB6612的VCC
  2. STM32的GND连接到TB6612的GND
  3. 配置好的PWM引脚(如PA6)接PWMA
  4. 方向控制引脚(PA4、PA5)分别接AIN1和AIN2
  5. 电机电源(VM)接7-12V电源正极
  6. 电机两端接在AO1和AO2上

常见接线错误排查

  • 电机不转:先检查所有电源连接,特别是共地
  • 电机只震动不转:PWM频率可能过高,尝试调整到1-5kHz
  • 模块发热严重:立即断电,检查是否短路或电机堵转

上电前建议先用万用表检查:

  1. VM和GND之间电阻(防止电源短路)
  2. 电机两端电阻(通常几欧姆到几十欧姆)
  3. 确认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.cmotor.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 i=0; i<=1000; i+=10) { Motor_SetSpeed(&motor, i); HAL_Delay(20); } // 减速到停止 for(int i=1000; i>=0; 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. 项目扩展思路

基础功能实现后,可以考虑以下扩展方向:

  1. 串口控制:通过USB转TTL模块,用电脑串口助手发送指令控制电机
  2. 电位器调速:外接电位器,用ADC读取电压值映射为电机转速
  3. 无线控制:添加蓝牙或2.4G模块,用手机或遥控器控制
  4. 多电机同步:利用STM32的多个定时器同时控制2-4个电机
  5. 速度闭环:虽然本教程用开环控制,但可以尝试添加编码器实现简单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"之间的数字,就能实时控制电机转速和方向。

http://www.jsqmd.com/news/562803/

相关文章:

  • I2C协议详解:从基础原理到工程实践
  • 从60+犬种数据集中,我总结出训练目标检测模型的3个关键避坑点
  • 鱼鱼刘怀旧手游|永恒岛高清重置版:4K 焕新归来,重走彩虹青春路
  • 用OpenMV和STM32F765VI做个追球小车:从硬件接线到PID调参的保姆级避坑指南
  • Matrix Color Sensor嵌入式RGBW色彩传感驱动设计
  • I2C总线信号特性与上拉电阻设计详解
  • 【Java工业互联网协议解析实战指南】:覆盖OPC UA、MQTT、Modbus TCP等7大协议的高可用解析框架设计与源码级拆解
  • 深入解析Infineon BTS54040-LBF高边芯片的SPI控制与汽车电子应用
  • Claude 4.7多模态Agent深度测评:实时视频推理能力到底提升了多少?
  • 孤能子视角:数字时代,“社会生产关系“[4],具身虚拟身份,耦合强度追责
  • 从Lending Club数据看机器学习在金融风控中的实战应用
  • 2026年硝酸钠公司权威推荐:粒硝/钠硝石/土硝/火硝/盐硝/粉硝/钾硝/农业级硝酸钾/工业级硝酸钾/硝石/选择指南 - 优质品牌商家
  • 等式方程的可满足性
  • 【电力系统】机会约束置信度参数以及安全裕量系数在综合能源系统调度中的应用研究(Matlab代码实现)
  • 3个信号预示你的应用不适合虚拟线程:IO密集型误判率高达79%,附自动检测工具Jar包下载
  • Linux下C程序编译全流程详解与实战
  • 虚拟线程CPU飙升、GC暴增、调度失序全复现,3大反模式避坑指南,附可复用监控脚本
  • 基于SpringBoot的老年人食堂系统
  • 基于中点电位平衡的光伏NPC三电平逆变器并网仿真研究:额定功率100kW、直流电压750V的M...
  • FinalBurn Neo终极指南:如何免费重温经典街机游戏体验
  • Node.js 25性能优化秘籍:单线程瓶颈突破的5个核心方案
  • 别再手动排版了!用LaTeX + TikZ 5分钟搞定高中数学试卷里的立体几何图
  • 消费很难幸福感和检测工具
  • AI软件开发✅企业必看!告别传统开发内耗,自动编码+智能测试,降本50%+、落地零门槛,电商/制造/金融全行业定制,免费领需求评估,省时省力提效[特殊字符]
  • 教育心理学教程资源合集
  • C语言程序结构怎么认识?一个简单例子带你入门
  • 2026缓释阻垢剂供应商评测深度解析:反渗透絮凝剂/反渗透药剂/反渗透还原剂/反渗透阻垢剂/选择指南 - 优质品牌商家
  • 从三相到两相:手把手带你用Clark和Park变换搞定PMSM电压方程(附MATLAB验证)
  • 如何高效使用Ryujinx:开源Switch模拟器完整实战指南
  • 如何快速使用Diablo Edit2:暗黑破坏神II角色编辑完整指南