简介本资源是一份面向嵌入式初学者与STM32实践者的电机控制实战代码包聚焦于使用STM32F103C8T6微控制器驱动TB6612FNG双H桥电机模块解决直流电机PWM调速、正反转及小车运动状态封装控制等核心问题。压缩包共16个文件含5个头文件.h定义寄存器与函数接口、4个源文件.c实现定时器PWM配置、GPIO方向控制及运动状态函数库如前进/转向/停止另含README.md说明文档、Makefile构建脚本、链接脚本.ld与启动文件.s等完整工程要素总大小仅24KB轻量易导入。已有126人学习下载适合课程设计、智能小车入门开发或嵌入式外设驱动专项训练。读者可直接编译运行快速掌握STM32标准库下电机驱动的硬件连接逻辑、定时器中断调速实现及模块化运动控制函数的设计思路。1. STM32控制TB6612驱动电机不是接上线就能转而是搞懂PWM相位、死区与电流反馈闭环的实操起点你手头有一块STM32F103C8T6最小系统板、一块TB6612FNG双H桥驱动模块、两个直流减速电机还下载了一份标着“STM32控制TB6612驱动电机[源码]”的压缩包——但上电后电机只抖一下就停或者一上电就发热冒烟。这不是硬件坏了而是你还没跨过那道隐性门槛TB6612不是L298N那种“喂PWM就转”的黑匣子它对PWM极性、IN1/IN2逻辑时序、待机引脚STBY使能时机、甚至电源退耦电容位置都极其敏感。这份源码包的价值不在于它“能跑”而在于它把STM32标准外设库或HAL下TB6612的初始化链路、占空比映射关系、方向切换防短路机制、以及最关键的——如何用ADC采样电机端电压反推堵转状态——全摊开在.c和.h文件里。适合正在做智能小车底盘、机械臂关节驱动、或毕业设计中需要稳定双电机差速控制的嵌入式初学者也适合被“电机乱转”“上电烧MOS”问题卡住两周的老手——因为里面藏着三处教科书从不提、但现场必踩的寄存器配置陷阱。2. TB6612与STM32的硬件连接与电气约束为什么必须用100nF10μF并联退耦且STBY不能直连VCCTB6612FNG虽是双H桥但它的电气特性与常见驱动芯片有本质差异内部MOSFET体二极管压降低典型0.45V、开关速度极快上升/下降时间100ns这既是优势也是雷区。若PCB布局不当或电源滤波不足高频开关噪声会直接耦合进STM32的ADC参考电压或复位引脚导致电机失控或MCU反复复位。这份源码包配套的原理图见/hardware/schematic.pdf明确标注了四点硬性约束我逐条拆解2.1 电源路径必须分轨VM与VCC物理隔离TB6612的VM引脚接电机电源7–15VVCC引脚接逻辑电源3.3V或5V。关键点在于VCC必须由STM32的3.3V稳压输出如AMS1117-3.3单独供电严禁从VM经LDO降压后共用原因当电机启动瞬间电流突变可达2A峰值VM端电压跌落会导致LDO输出不稳VCC波动超过STM32的复位阈值通常2.7VMCU硬复位。源码中tb6612_init()函数第一行就检查HAL_GPIO_ReadPin(GPIOx, STBY_PIN) GPIO_PIN_SET实则是用软件确认STBY已拉高——而STBY若由不稳的VCC驱动此检查永远失败。2.2 PWM信号线必须加100Ω串联电阻与100nF对地电容STM32的GPIO推挽输出上升沿极陡10ns直接驱动TB6612的PWM输入引脚如PWMA会激发PCB走线的LC谐振在引脚上产生2V的过冲电压。TB6612手册明确要求PWM引脚电压绝对最大额定值为7V过冲易击穿输入保护二极管。源码包中/core/stm32f1xx_hal_conf.h第87行启用了HAL_MODULE_ENABLED并在main.c的MX_GPIO_Init()里将PWM引脚模式设为GPIO_MODE_AF_PP复用推挽但真正起作用的是原理图中标注的R12100Ω、C15100nF组成的RC吸收网络。实测数据未加此网络时示波器测PWMA引脚过冲达3.8V加后降至0.3V以内。2.3 STBY引脚必须软件可控且上电时序严格TB6612的STBYStandby引脚是全局使能端低电平强制所有H桥关断高电平才允许IN1/IN2/PWM生效。致命陷阱在于STBY必须在VM电源稳定后≥10ms再拉高且拉高前需确保IN1/IN2均为低电平。源码中tb6612_init()函数执行顺序为HAL_GPIO_WritePin(STBY_GPIO_Port, STBY_Pin, GPIO_PIN_RESET);// 先拉低HAL_Delay(15);// 等待VM稳定HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_RESET);HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, GPIO_PIN_RESET);// 清零方向HAL_GPIO_WritePin(STBY_GPIO_Port, STBY_Pin, GPIO_PIN_SET);// 最后使能若跳过步骤3–4直接拉高STBYIN1/IN2可能处于上电默认高阻态被内部上拉拉至高电平导致H桥直通短路——这就是“一上电就冒烟”的根源。提示TB6612的VM电源建议使用带过流保护的DC-DC模块如LM2596而非线性稳压器。实测电机堵转时VM电流瞬时达3.2A线性稳压器温升超100℃必然热关断。3. 源码核心逻辑解析从HAL_TIM_PWM_Start到电机转向防抖的12行关键代码这份源码包采用HAL库非标准外设库主控芯片为STM32F103C8T6使用TIM2_CH1PA0和TIM2_CH2PA1生成两路互补PWM驱动左右电机。其核心不在“怎么输出PWM”而在“如何让PWM与方向信号严格同步避免H桥上下管同时导通”。我们聚焦/src/tb6612_driver.c中tb6612_set_speed()函数3.1 PWM占空比与电机转速的非线性映射TB6612的输出电压并非简单等于VM × 占空比因内部MOSFET存在导通压降Ron≈0.5Ω及体二极管续流损耗。实测发现当占空比15%时电机几乎不转20%–40%区间转速增长缓慢60%后趋于线性。源码中SPEED_MAP[]数组第42行做了分段线性补偿const uint16_t SPEED_MAP[101] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0–9%: 强制为0消除静摩擦死区 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, // 10–19% 320, 360, 400, 440, 480, 520, 560, 600, 640, 680, // 20–29% // ... 后续每10%增加40直至100%对应1000 };调用tb6612_set_speed(LEFT_MOTOR, 35)时实际写入TIM的CCR1值为SPEED_MAP[35] 440TIM2计数周期为1000。注意此映射需根据你的电机型号实测校准——同一份源码换用12V/100rpm减速电机时35%占空比可能对应60rpm而换用24V/300rpm电机时可能仅20rpm。3.2 方向切换的“先停后切”原子操作TB6612要求改变转向时必须先将PWM占空比置0等待至少100μs手册Table 7.2再改变IN1/IN2电平最后恢复PWM。源码中tb6612_set_direction()函数第89行实现如下void tb6612_set_direction(uint8_t motor, uint8_t dir) { if (motor LEFT_MOTOR) { __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 0); // 立即停PWM HAL_Delay(1); // 软件延时确保100μs实际约1ms留足余量 if (dir FORWARD) { HAL_GPIO_WritePin(IN1_LEFT_GPIO_Port, IN1_LEFT_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(IN2_LEFT_GPIO_Port, IN2_LEFT_Pin, GPIO_PIN_RESET); } else { HAL_GPIO_WritePin(IN1_LEFT_GPIO_Port, IN1_LEFT_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN2_LEFT_GPIO_Port, IN2_LEFT_Pin, GPIO_PIN_SET); } __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, speed_left); // 恢复原速 } }关键点HAL_Delay(1)不可替换为HAL_Delay(0)或空循环因为HAL_Delay()底层调用SysTick精度为1ms而空循环受编译器优化影响可能被优化掉。曾有A同学将此处改为for(volatile int i0; i100; i);结果在-O2优化下循环被完全剔除导致方向切换时H桥直通炸毁MOS。3.3 双电机差速控制的时序锁当同时控制左右电机如小车转弯时若左右PWM更新不同步会产生瞬时扭矩差导致车身歪斜。源码中tb6612_update_dual()函数第135行使用TIM2的主从模式// 配置TIM2为主定时器TIM3为从定时器触发源为TIM2更新事件 __HAL_TIM_ENABLE(htim2); // 先启主定时器 HAL_Delay(1); __HAL_TIM_ENABLE(htim3); // 再启从定时器确保相位一致这样左右PWM的上升沿偏差被控制在100ns内实测小车直线行走偏移量从±8cm/米降至±0.5cm/米。4. 常见问题排查三类“电机不转”现象背后的寄存器级真相这份源码包在某高校电子设计竞赛中被23支队伍采用累计暴露57次现场故障。以下是最典型的三类问题按“现象→原因→解决”结构整理全部来自真实调试日志4.1 现象电机完全不转但万用表测PWMA引脚有方波IN1/IN2电平正确→原因TB6612的VM电源未接入或VM电压低于4.5V芯片欠压锁定。TB6612FNG内部有UVLO电路当VM4.5V时自动关闭所有H桥且不报任何错误标志。→解决用示波器测VM引脚对地电压确认稳定在7V以上若用电池供电需检查电池接触电阻——曾有队伍因电池弹片氧化导致接触电阻2Ω满载时VM跌至3.8V电机不转。4.2 现象电机间歇性抖动频率与PWM频率一致如1kHz抖一次→原因STM32的VDDA模拟电源未与VDD数字电源通过磁珠隔离或ADC参考电压VREF未加10μF钽电容。抖动源于PWM开关噪声耦合进ADC导致tb6612_read_current()函数误判堵转而强制停机。→解决在PCB上VDDA与VDD之间串接10Ω磁珠并在VREF与GND间补焊10μF钽电容非电解电容若无硬件修改条件临时方案是在main.c中注释掉HAL_ADC_Start_IT(hadc1)及相关中断服务函数。4.3 现象正转正常反转时电机发出“咔哒”声但不转→原因IN1与IN2逻辑电平配置错误。TB6612规定正转需IN1H/IN2L反转需IN1L/IN2H若接反为IN1H/IN2H则进入刹车模式两桥臂同时导通电机轴被锁死。→解决用逻辑分析仪抓取IN1/IN2/PWMA三路信号确认反转时IN1为低、IN2为高若硬件已焊接无法更改可在软件中交换IN1_LEFT_Pin与IN2_LEFT_Pin的宏定义/Inc/tb6612_config.h第22–23行。注意所有排查务必在断电状态下进行曾有开发者带电插拔TB6612模块静电击穿STM32的PA0引脚导致TIM2_CH1永久失效。5. 进阶技巧用ADC采样电机端电压实现堵转检测与PID闭环调速这份源码包最被低估的价值是它实现了基于端电压反馈的简易闭环控制。TB6612本身无电流检测引脚但电机堵转时反电动势Back-EMF消失端电压会骤降至接近0V——这正是堵转的黄金特征量。源码中/src/tb6612_adc.c利用STM32F103的ADC1通道6PA6采样电机正端电压配合软件滤波实现毫秒级堵转响应。5.1 端电压采样的硬件连接与校准PA6引脚需通过电阻分压网络接入电机正端电机正端 → R110kΩ → PA6PA6 → R210kΩ → GND此分压比为1:1PA6电压电机端电压。但需注意ADC参考电压为3.3V故电机端电压不得超过3.3V。若VM12V必须改用R133kΩ/R210kΩ分压比4.3:1此时PA6电压电机端电压/4.3。校准方法用万用表测电机正端电压V_m再用示波器测PA6电压V_pa6计算实际分压比KV_m/V_pa6写入adc_config.h的#define VOLTAGE_DIV_RATIO 4.3f。5.2 堵转判断算法与PID参数整定tb6612_check_stall()函数第68行采用滑动窗口均值动态阈值#define ADC_WINDOW_SIZE 16 uint16_t adc_buffer[ADC_WINDOW_SIZE]; float voltage_avg; // 每10ms采集一次存入环形缓冲区 voltage_avg 0; for(int i0; iADC_WINDOW_SIZE; i) { voltage_avg (float)adc_buffer[i] * 3.3f / 4095.0f / VOLTAGE_DIV_RATIO; } voltage_avg / ADC_WINDOW_SIZE; // 动态阈值空载时电压≈VM*0.95堵转时VM*0.3 if(voltage_avg (vm_voltage * 0.3f)) { stall_flag 1; __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 0); // 立即停机 }PID调速的关键参数/Inc/pid_config.h参数初始值调试逻辑Kp0.8增大则响应快但易超调观察电机启动时是否“冲过头”Ki0.02增大可消除稳态误差但过大会引起低频振荡电机嗡嗡响Kd0.05抑制超调但过大会放大噪声导致电机微抖实测经验对12V/100rpm减速电机最终参数为Kp1.2, Ki0.03, Kd0.08调参口诀“先调Kp看响应再加Ki消误差最后Kd压抖动”。5.3 从开环到闭环的代码改造清单若你当前使用的是纯开环源码只需修改三处在main.c的MX_ADC_Init()中将hadc1.Init.ContinuousConvMode ENABLE;启用连续转换在stm32f1xx_it.c的ADC_IRQHandler中添加HAL_ADC_Stop_DMA(hadc1);后调用tb6612_check_stall();在tb6612_set_speed()末尾加入pid_output pid_calculate(target_speed, actual_speed);并用__HAL_TIM_SET_COMPARE()更新CCR值从那以后我每次调试电机驱动都强制走一遍“断电测VM→上电测STBY电平→示波器抓PWMA/IN1/IN2三路时序→加载ADC采样验证端电压”的四步流程。少走一步就可能多烧一块TB6612——这钱花得不冤但时间耗不起。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?