STM32控制TB6612驱动电机:硬件选型到源码实现详解

📅 发布时间:2026/9/1 11:09:44
STM32控制TB6612驱动电机:硬件选型到源码实现详解 简介面向STM32初学者的电机驱动控制源码包基于STM32F103C8T6与TB6612FNG模块完整实现双直流电机的PWM调速、正反转及停止控制适合智能小车、机器人等场景参考。压缩包共16个文件包含5个H头文件、4个C源文件以及Makefile、链接脚本、启动文件、README说明等代码基于标准库编写硬件层聚焦定时器PWM输出与GPIO方向控制软件层则提供模块化的电机控制接口。现有123人学习下载。源码中封装了电机状态控制函数可直接调用实现前进、后退、左转、右转与停止并给出关键引脚定义、接线原理图和实验现象说明便于复现与调试。通过学习该工程可掌握定时器输出比较配置、PWM占空比调节、H桥驱动逻辑及工程构建方法为后续自主扩展速度闭环或遥控功能打下基础。 做电机控制这块的兄弟应该都体会过选驱动模块比调代码还纠结。市面上的驱动方案看着挺多真上手后各种问题发热严重、逻辑电平不匹配、PWM频率不对导致电机啸叫……我这几年折腾STM32和各类电机驱动最后在TB6612上稳定下来这个模块性价比确实高。这篇东西是基于“STM32控制TB6612驱动电机”这个项目整理的完整记录从硬件选型、接线原理、定时器PWM配置到源码实现和踩坑排查全部是我实际跑过的方案。适合刚入手STM32做小车、机械臂、云台之类项目的朋友也适合那些用L298N被发热和体积折磨到想换方案的人。代码部分基于STM32F103C8T6和HAL库编写逻辑清晰移植到其他型号也就改改引脚的事。1. 方案选型为什么是TB6612而不是L298N1.1 TB6612和L298N的对比很多教程上来就让你用L298N不是说它不能用而是它的设计太老了。L298N内部是双极性晶体管组成的H桥导通时饱和压降通常在1.5V到2V左右。想象一下电源电压12V经过驱动模块到电机两端实际可能只剩10V出头这还不算它在2A电流下的发热量。不加散热片的话跑两分钟就烫得不敢摸加了散热片体积又大得离谱。TB6612FNG是东芝出的MOSFET型H桥驱动芯片内部是N沟道和P沟道MOSFET的组合导通阻抗低得多压降小所以芯片本身的发热量很可控。连续驱动电流1.2A峰值3.2A带内置续流二极管。这意味着电机在停止、反转时产生的反电动势不需要你额外搭续流电路芯片内部就处理掉了。逻辑电压范围2.7V到5.5V完美匹配STM32的3.3V电平不需要额外的电平转换电路。1.2 用在小车项目上的实际体验我实际做过一轮对比测试同一台电机、同一个电源、同一个STM32开发板分别接L298N和TB6612。L298N跑空载电机时芯片温度十几分钟就明显烫手TB6612跑同样的工况几乎只是微温。体积上L298N模块比TB6612模块大了一圈对做小车、无人机这类对空间敏感的项目来说差距非常明显。当然TB6612也不是万能的。如果项目需要用大功率电机比如额定电流超过1.2A的直流电机或者需要12V以上电压驱动那就要换大功率驱动方案了比如BTS7960或者直接上MOSFET分立方案。TB6612更精准的定位是小功率直流电机、两路独立控制、对体积和发热有要求的场景。大多数入门小车、云台、智能家居项目它都是最优解。2. 硬件接线与原理把TB6612吃透2.1 TB6612引脚功能详解TB6612模块虽然小引脚功能却一点都不少。先把每个关键引脚的作用理清楚后面接线才不会一团乱麻。引脚功能说明VM电机电源正极建议接2.7V-12V实际项目常用5V-12VVCC逻辑电源正极接3.3V或5V与MCU电平匹配GND公共地必须与STM32共地PWMAA路电机PWM输入接STM32定时器PWM输出引脚AIN1/AIN2A路电机方向控制组合电平决定正转/反转/刹车AO1/AO2A路电机接线端接电机的两根线PWMBB路电机PWM输入接另一个定时器PWM输出引脚BIN1/BIN2B路电机方向控制组合电平决定正转/反转/刹车BO1/BO2B路电机接线端接另一个电机的两根线STBY待机控制低电平进入待机高电平正常工作有个细节容易被忽略AO1和AO2接电机两端时其实没有正负之分因为直流电机的正反转是靠改变电流方向实现的。接反了也没关系只是正转和反转的逻辑对调而已代码里调整方向控制位就能纠正。2.2 STBY引脚为什么必须拉高STBY是TB6612的待机控制引脚。当STBY为低电平时整个芯片进入待机模式H桥输出全部关闭此时不管PWMA、AIN1、AIN2怎么输入电机都不会转。只有当STBY为高电平时芯片才处于正常工作状态。这个引脚很容易被忽略因为网上很多原理图里它默认接到了VCC。如果你用的是现成的TB6612模块有些模块已经把STBY通过上拉电阻接到了VCC那就不用管。但如果你是自己画板子或者用的模块没有默认拉高一定要在初始化代码里把STBY置高否则电机就是不转而且很难排查。我习惯把STBY接在STM32的一个普通GPIO上这样既能控制芯片开关还能在电机异常时通过软件快速切断输出。初始化时先输出低电平等所有GPIO和PWM配置完成后再拉高避免上电瞬间产生误动作。这个做法在后面调稳定性的环节会非常有用。2.3 与STM32和电机的完整接线表这里以STM32F103C8T6最小系统板为例列出我实测的接线方式。引脚不是唯一选择但逻辑必须一致每路电机需要一个PWM引脚加两个方向控制引脚。STM32引脚TB6612引脚说明PA0PWMATIM2_CH1A路PWMPA1PWMBTIM2_CH2B路PWMPC0AIN1A路方向控制1PC1AIN2A路方向控制2PC2BIN1B路方向控制1PC3BIN2B路方向控制2PC4STBY待机控制3.3VVCC逻辑电源外部5V/12VVM电机电源GNDGND共地这里的PWMA和PWMB都接在TIM2上原因是让两路PWM共用一个定时器频率和计数周期天然同步做双电机差速控制时避免出现频率不一致导致的问题。如果只控制单电机PWMB也可以选别的定时器的引脚比如TIM3的CH2。注意一个容易踩的坑VM接的电源要能提供足够电流。小电机的启动电流一般是额定电流的2到3倍。如果VM用开发板的5V引脚供电开发板上的稳压芯片很可能扛不住导致电压跌落电机没劲甚至STM32直接复位。正确的做法是单独准备一个电源给VM供电比如一节锂电池或者一个5V/2A的电源模块然后把GND和STM32的GND接到一起。3. 软件架构与定时器PWM配置3.1 为什么用定时器PWM而不是软件延时控制电机转速最直接的方式就是PWM占空比。有人偷懒用GPIO翻转加延时模拟PWM这种方式在小功率、不要求精度的场合能凑合但有两个硬伤一是占用CPU时间主循环干不了别的二是频率不稳定延时的抖动会直接体现在电机转速上。STM32的定时器PWM输出是硬件行为配置好之后CPU只需要改比较寄存器的值脉冲宽度就变了完全不需要CPU干预。以STM32F103为例定时器时钟是72MHz通过预分频和自动重装载值可以精确设定PWM频率和分辨率。这才是嵌入式控制该有的做法。3.2 频率的选择和计算电机PWM频率的选择是个需要认真考虑的点。频率太低电机能听到明显的“吱吱”声这是绕组在脉冲电流下产生的机械振动频率太高虽然安静了但MOSFET的开关损耗增加驱动芯片发热会偏高。我实测下来10kHz到20kHz是直流电机驱动比较合适的区间既听不到明显的啸叫芯片发热也正常。以TIM2为例计算一下10kHz频率下的参数。时钟源72MHz预分频PSC设置71则定时器计数频率为72MHz / (711) 1MHz也就是每个计数周期1微秒。自动重装载值ARR设置999那么PWM频率为1MHz / (9991) 1kHz。呃算到这里发现频率不对重新来要得到10kHzARR应该设99这样1MHz / (991) 10kHz。但ARR为99时占空比分辨率只有0-99精度够用但不富余。如果希望更高的占空比分辨率可以降低预分频比如PSC设为7计数频率为72MHz / 8 9MHzARR设为899PWM频率为9MHz / 900 10kHz占空比分辨率0-899控制起来细腻很多。两种方案我都试过对一般电机来说第一种PSC71、ARR99已经够用但第二种对精密速度控制更好看项目需求选择。3.3 HAL库定时器配置用STM32CubeMX配置的话选择TIM2通道1和通道2配置为PWM Generation CH1和CH2设置Prescaler71Counter Period99Pulse0。注意Pulse初始值设为0否则上电瞬间电机可能突然转一下。需要提醒的是如果使用CubeMX生成代码后不小心改了系统时钟频率必须同步检查PSC和ARR的值否则实际PWM频率会和预期差很远。我就犯过这种错误时钟树里把系统时钟改成48MHz但定时器参数还是按72MHz算的结果电机啸叫特别响排查了半天才发现是时钟源变了。初始化完成后使用HAL库的修改占空比接口__HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 50); // A路占空比50% __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_2, 50); // B路占空比50%这里填的值是0-99之间的数对应占空比0%-100%。每次改完占空比定时器硬件会自动完成波形输出更新无需其他操作。4. 源码实现从GPIO初始化到电机控制4.1 GPIO和控制引脚的初始化完整的工程文件建议用STM32CubeMX生成基础代码这里重点展示电机控制相关的初始化逻辑。方向控制引脚AIN1、AIN2、BIN1、BIN2和STBY全部配置为推挽输出模式。// 电机控制引脚初始化 void Motor_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitStruct.Pin GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, GPIO_InitStruct); // 初始化使能引脚为低电平待机状态 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET); // 初始化所有方向控制引脚为低电平 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3, GPIO_PIN_RESET); }速度配置用LOW就行因为GPIO翻转速度对电机控制来说完全够用设置成HIGH反而可能引入额外的电磁干扰这个细节在EMC敏感的场景下会有点影响。4.2 电机控制函数设计电机控制的核心是一个带方向的速度函数。输入参数speed的取值范围我定义为-100到100正数表示正转负数表示反转0表示停止。这样上层业务逻辑只需要算出一个目标速度值不用关心方向引脚的具体逻辑非常直观。// 电机A控制函数 // speed: -100 ~ 100正数正转负数反转0停止 void MotorA_SetSpeed(int16_t speed) { uint16_t duty 0; // 死区处理避免微小的PWM值导致电机堵转发热 if (speed -5 speed 5) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 0); return; } if (speed 0) { // 正转AIN11, AIN20 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET); duty speed * 99 / 100; } else { // 反转AIN10, AIN21 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET); duty (-speed) * 99 / 100; } __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, duty); }这里特意加了一个死区判断当speed在-5到5之间时直接输出停止状态。原因是我实际测试中发现占空比低于5%时电机由于启动扭矩不足根本不转但绕组还在通电时间长了会发热白白浪费电。做死区处理既保护电机也避免控制不灵敏的问题。方向控制逻辑需要和硬件接线配合理解。当AIN11、AIN20时H桥的电流方向是AO1流向AO2电机正转反之时电流方向反转电机反转。如果实际接线方向和你预期相反不需要改代码逻辑把AO1和AO2的两根电机线对调就行或者直接把代码里的SET和RESET换一下。B路电机的控制函数结构完全一样只是对应的引脚换成PC2、PC3PWM通道换成TIM_CHANNEL_2。4.3 主循环测试程序写一个简单的测试程序验证驱动逻辑是否正常电机正转1.5秒停止0.5秒反转1.5秒再停止0.5秒循环运行。int main(void) { HAL_Init(); SystemClock_Config(); Motor_GPIO_Init(); MX_TIM2_Init(); // 使能PWM输出 HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_2); // 使能TB6612 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_SET); while (1) { MotorA_SetSpeed(60); MotorB_SetSpeed(60); HAL_Delay(1500); MotorA_SetSpeed(0); MotorB_SetSpeed(0); HAL_Delay(500); MotorA_SetSpeed(-60); MotorB_SetSpeed(-60); HAL_Delay(1500); MotorA_SetSpeed(0); MotorB_SetSpeed(0); HAL_Delay(500); } }这里有个细节PWM输出使能要在所有初始化完成之后STBY拉高之前。顺序反过来的话上电瞬间芯片已经处于工作状态而定时器PWM引脚可能还没有正确初始化会导致电机抖动一下虽然不会损坏硬件但小车放在地上会突然蹿一下挺吓人的。5. 常见问题与调试心得5.1 电机不转但芯片发热先查STBY和VM电机完全没动静芯片却有点温这个现象基本指向两种情况第一种是STBY没有拉高芯片一直在待机状态此时芯片有微弱功耗但不驱动电机第二种是VM没接对逻辑电源VCC给3.3V能工作但电机电源VM没接或接错H桥自然无法输出电流。排查时先量一下芯片各引脚的电压。STBY如果接近0V直接查代码里有没有执行拉高操作以及接线有没有断开。VM如果有电压但电机还是不转继续量AO1和AO2之间的电压如果始终是0V说明PWM或者方向引脚配置有问题再往上查。5.2 电机啸叫或者转速异常PWM频率和电源供应问题电机发出尖锐的“吱吱”声大概率是PWM频率设置得太低。我之前把ARR设成999PSC设成71一算频率只有1kHz电机一通电那个声音简直没法听。把这个组合改成PSC71、ARR99频率变成10kHz声音立刻消失电机运行顺滑很多。还有一种情况是转速忽快忽慢但代码里占空比是固定的。这时候不要纠结代码去查电源。用示波器看VM引脚的电压波形电机转动时如果电压跌落明显说明电源带载能力不足。换个电流更大的电源或者在VM和GND之间并联一个470uF的电解电容能给电机启停瞬间的电流冲击提供缓冲这个小技巧实测很有效。5.3 刹车和停止有什么区别TB6612的方向控制引脚组合除了正转和反转还有一种特殊的组合AIN10、AIN20时H桥两路输出都是低电平电机依靠惯性自由滑行AIN11、AIN21时H桥两个输出端直接短接到电源端相当于电机制动短路转速会迅速下降也就是主动刹车。这个区别在项目里特别重要。比如做小车循迹转弯后需要快速停下来用滑行模式会发现车子过了弯道线还没停住这时候就要用刹车模式。我在代码里预留了刹车接口void MotorA_Brake(void) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 0); }需要注意刹车模式会瞬间产生较大的反向电流虽然TB6612内置了续流二极管但频繁的急刹还是会让芯片温度上升得快一点。项目中如果需要频繁启停在硬件设计上尽量保证芯片背面有散热铜箔或者贴一小块散热片。5.4 关于共地的严格性STM32的GND、TB6612的GND、外部电源的GND必须全部接在同一个参考点上。如果不共地逻辑信号的高电平参考点和芯片内部不一致会导致PWM信号识别异常电机要么不转要么转速不稳定。我实际遇到过一次很奇怪的问题电机无负载时转得好好的一装上轮子开始带负载就抖动排查了半天才发现是给VM供电的电源模块GND和STM32的GND没有直接相连只靠USB线共地而USB线有一些不确定的阻抗。把两个GND用粗导线直接短接后问题彻底消失。从此我养成了习惯任何模块接进来第一件事就是检查共地。6. 实测调参心得调速度闭环的时候会用到PID这时PWM频率的选择就更关键。我一开始用5kHz的PWM跑速度环发现码盘反馈的数据毛刺很大速度波形看起来像锯齿。把PWM频率提到15kHz后波形明显平滑PID参数也更容易收敛。原理其实不复杂PWM频率越高电机绕组电流的纹波越小转速波动也就越小反馈到的码盘数据自然更稳定。不过频率也不是越高越好。超过30kHz后我实测驱动芯片的温度会比15kHz时明显升高而且电机的电感滤波效果已经足够平滑了继续升频率换来的改善可以忽略。折中下来15kHz是我个人比较推荐的工作频率兼顾噪音、发热和控制的平稳性。关于加速和减速不建议直接用阶跃的占空比变化。比如小车从静止直接给80%占空比轮子会打滑对电机和驱动芯片也是冲击。我习惯写一个简单的斜坡函数目标速度和当前速度之差每次中断最多改变2%到3%的占空比让电机平滑过渡。这样不仅保护硬件得到的运动轨迹也更可控。最后再分享一个实际调试时踩过的坑。新焊好的板子通电后电机正转正常反转却一直抖动。用万用表量BIN1和BIN2的电压发现反转时BIN1引脚电压只有1.8V而不是预期的3.3V。检查后发现是上一版板子预留的上拉电阻焊错了位置把信号电平拉到了半高。这是硬件层面的问题但如果你也遇到类似的情况方向引脚电平异常可以先查外部上下拉电阻然后再怀疑代码。TB6612配合STM32这套方案我用在好几代小车和云台上稳定性和可维护性都让人放心。遇到问题的兄弟们按照文章里的排查顺序捋一遍大部分坑都能避开。本文还有配套的精品资源点击获取