STM32平衡小车电机驱动实战:JGB-520与TB6612选型、接线与PID控制基础

📅 2026/8/6 7:29:48
STM32平衡小车电机驱动实战:JGB-520与TB6612选型、接线与PID控制基础
1. 项目概述从电机驱动开始搭建你的第一台平衡小车搞过嵌入式开发的尤其是玩过STM32的估计没人能绕开“平衡小车”这个经典项目。它就像一道坎跨过去了你对PID控制、传感器融合、实时系统这些概念的理解会深一个层次。但很多新手一上来就被卡在了第一步让两个轮子听话地转起来。你可能已经搜遍了资料知道要用直流减速电机要用电机驱动模块但面对JGB-520和TB6612这一对组合具体怎么接线、怎么编程、怎么测试心里还是没底。这篇文章我就从一个老电子爱好者的角度带你彻底搞定平衡小车的动力核心——基于STM32的JGB-520减速电机与TB6612驱动模块的驱动与测试。这不是一个简单的模块说明而是包含了我从选型、接线、调试到写出稳定测试代码的全过程实录以及那些数据手册上不会写的“坑点”。简单说我们要做的事很明确用一块STM32开发板比如常见的STM32F103C8T6核心板通过TB6612电机驱动模块精准控制两个JGB-520减速电机的正转、反转、停止和调速。这是你小车能立起来、跑起来的基础。无论你后续是打算用MPU6050做姿态解算还是上各种高级算法这套动力系统都是你必须先搭建并验证无误的。我会附上完整的、带有详细注释的测试代码你可以直接复制到你的工程里快速验证你的硬件连接是否正确。2. 核心器件选型与原理深度解析2.1 为什么是JGB-520减速电机在淘宝上搜“直流减速电机”JGB-520系列绝对是爆款。它不是一个型号而是一个规格系列核心参数是电机直径37mm减速箱长度26mm。选择它绝不是因为它名字好听而是基于平衡小车项目的几个硬性需求扭矩与速度的平衡平衡小车需要电机有足够的扭矩来快速响应车体的倾斜以产生恢复力。普通直流电机比如玩具小车上的130电机转速太高、扭矩太小不加减速箱的话一点轻微的负载变化就会导致转速剧烈波动根本无法实现平衡。JGB-520内部集成了金属齿轮减速箱通常减速比在1:30到1:200之间常见。我推荐使用1:48或1:120的版本。1:48的减速比能提供不错的扭矩和反应速度适合初学者调试1:120的扭矩更大低速线性度更好但最高速度慢更适合追求稳定性的方案。尺寸与安装的便利性37mm的直径和紧凑的结构使其能非常方便地安装在常见的平衡小车底盘上。电机轴通常是D型轴俗称“菊花轴”配合联轴器或车轮套件连接非常牢固避免了打滑。供电电压的匹配常见的JGB-520电机工作电压范围在3V到12V。我们的平衡小车通常使用单节或两节锂电池供电标称电压3.7V或7.4V这个电压范围正好匹配。直接连接3.3V也能转但扭矩会非常小。注意购买时一定要问清楚卖家电机的“空载转速”和“减速比”。一个标称6V减速比1:48的电机空载转速大概在100-150 RPM转/分钟左右。这个数据对你后续估算PWM频率和PID参数范围有参考价值。2.2 TB6612驱动模块为何优于经典的L298N十年前L298N是电机驱动的代名词。但现在做平衡小车TB6612几乎是更优的选择。这背后的原因是性能、效率和体积的全方位提升效率与发热L298N是双H桥芯片但其内部是双极型晶体管结构压降大典型值2-3V功耗高发热严重。在小车上宝贵的电池能量被白白浪费在驱动芯片的发热上。而TB6612采用MOSFET构建H桥导通内阻小压降低可低至0.5V以下效率极高发热量微乎其微这意味着你可以用更小的散热片甚至不用散热片。驱动能力与逻辑电压TB6612单通道连续输出电流可达1.2A峰值3.2A驱动JGB-520这种工作电流通常在几百毫安的小电机绰绰有余。更重要的是它的控制逻辑电压VCC引脚支持宽范围2.7V-5.5V这意味着你可以直接用STM32的3.3V GPIO口直接驱动无需任何电平转换电路。而L298N的逻辑部分需要5V供电STM32的3.3V输出在高电平时可能达不到L298N的输入高电平阈值导致控制不可靠需要额外加电平转换或上拉电阻。集成度与保护TB6612模块通常集成了必要的滤波电容和稳压电路并且芯片本身带有热关断和欠压锁定保护安全性更好。模块上的PWMA、PWMB、AIN1、AIN2、BIN1、BIN2等控制引脚排列规整方便接线。核心控制逻辑TB6612每个通道A或B控制一个电机需要三个信号两个方向控制引脚如AIN1/AIN2决定电机转向。其真值表如下AIN1AIN2电机状态00停止刹车10正转01反转11停止刹车一个PWM引脚PWMA输入PWM脉冲信号用于调速。PWM占空比越高电机平均电压越高转速越快。2.3 STM32的PWM生成定时器的正确打开方式STM32的PWM输出功能非常强大但也让初学者困惑。我们通常使用通用定时器如TIM2, TIM3, TIM4的某个通道来生成PWM。关键参数设定与计算时钟源通常使用系统时钟SYSCLK经过APB总线预分频后得到定时器时钟如TIM_CLK。预分频器PSC用于降低计数频率。定时器计数频率 TIM_CLK / (PSC 1)。自动重装载值ARR决定了PWM的周期。PWM频率 定时器计数频率 / (ARR 1)。捕获/比较寄存器CCRx设置占空比。占空比 CCRx / (ARR 1)。对于直流电机PWM频率如何选择频率太低如几十Hz电机会听到明显的“滋滋”声转动不连续有顿挫感且电机电刷磨损加快。频率太高如几十kHzMOSFET开关损耗增加且可能超出电机电感线圈的响应速度控制效率下降。推荐范围1kHz 到 20kHz之间是一个较好的范围。我个人的经验是使用5kHz 或 10kHz。这个频率人耳几乎听不到噪音电机运行平稳TB6612也能高效工作。举例计算假设系统时钟72MHzTIM_CLK也是72MHz。我们要生成10kHz的PWM。目标PWM频率 10kHz 10,000 Hz。定时器计数频率应为PWM_Freq * (ARR1)。我们先设定ARR719则计数频率需要10,000 * (7191) 7,200,000 Hz 7.2MHz。计算预分频值PSCPSC TIM_CLK / 计数频率 - 1 72,000,000 / 7,200,000 - 1 10 - 1 9。验证计数频率 72MHz / (91) 7.2MHz。PWM周期 (ARR1) / 计数频率 720 / 7,200,000 0.0001秒 10kHz。匹配成功。3. 硬件连接与电路设计要点3.1 接线图与引脚分配这是最容易出错的一步。请务必对照你的模块和开发板仔细核对。TB6612模块引脚说明以常见模块为例VM电机驱动电压输入接电池正极如7.4V。务必与单片机的供电隔离避免电机干扰导致单片机复位。VCC逻辑电压输入接STM32的3.3V或5V。给TB6612内部逻辑电路供电。GND电源地。必须与STM32的GND共地这是信号参考的基础。STBY待机控制引脚。高电平使能芯片低电平进入待机模式所有输出关闭。我们通常直接接VCC3.3V使其一直使能。AIN1, AIN2, PWMA控制电机A。BIN1, BIN2, PWMB控制电机B。AO1, AO2电机A输出接电机A两根线。BO1, BO2电机B输出接电机B两根线。STM32引脚分配示例以STM32F103C8T6控制两个电机为例电机A方向AIN1 - PA1, AIN2 - PA2电机A调速PWMA - PA3 (TIM2_CH4)电机B方向BIN1 - PA6, BIN2 - PA7电机B调速PWMB - PA0 (TIM2_CH1)STBY接 3.3VVM接 7.4V 锂电池正极VCC接 3.3VGND接 电源地实操心得强烈建议使用彩色杜邦线并按功能分组如电源用红黑控制信号用黄绿蓝。接线完成后先不要接电机用万用表电压档测量VM和GND之间是否为电池电压VCC和GND之间是否为3.3V。确保电源无误后再进行下一步。3.2 电源系统的设计与避坑指南平衡小车的“心脏病”就是电源。设计不好轻则电机无力、单片机重启重则烧毁芯片。双电源系统这是必须的。一套给电机VM一套给控制电路单片机、传感器、驱动模块逻辑部分。通常用一块2S锂电池7.4V作为总电源。电机电源电池正负极直接接到TB6612的VM和GND。这条路径上电流可能瞬间达到2A以上导线要粗接触要牢。控制电源需要通过一个DC-DC降压模块如LM2596、MP1584将7.4V降压到稳定的5V或3.3V。我推荐先降到5V然后一部分给TB6612的VCC它支持5V逻辑另一部分再通过AMS1117等LDO降到3.3V给STM32和传感器。这样隔离效果更好。地线的连接电机驱动的大电流地和控制系统的信号地最终必须在电池负极处单点共地。不要在多个地方随意连接否则大电流在地线上产生的压降会干扰敏感的模拟信号如MPU6050的读数。滤波电容在TB6612的VM引脚附近尽量靠近引脚焊接一个100uF以上的电解电容和一个0.1uF的陶瓷电容用于吸收电机启停时产生的瞬间大电流和电压尖峰。这是保护驱动芯片、稳定系统电压的关键措施很多廉价模块省略了这部分需要你自己加上。4. 软件驱动与测试代码实现4.1 工程初始化与PWM配置基于标准库这里以STM32F103标准库为例展示核心的初始化代码。我们使用TIM2的CH1和CH4通道。// motor.h #ifndef __MOTOR_H #define __MOTOR_H #include sys.h // 电机编号定义 #define MOTOR_A 0 #define MOTOR_B 1 // 方向定义 #define MOTOR_FORWARD 0 #define MOTOR_BACKWARD 1 #define MOTOR_STOP 2 // 引脚定义 - 根据你的实际接线修改 // 电机A #define AIN1_PIN GPIO_Pin_1 #define AIN1_PORT GPIOA #define AIN2_PIN GPIO_Pin_2 #define AIN2_PORT GPIOA // 电机B #define BIN1_PIN GPIO_Pin_6 #define BIN1_PORT GPIOA #define BIN2_PIN GPIO_Pin_7 #define BIN2_PORT GPIOA // 函数声明 void Motor_Init(void); // 电机初始化 void Motor_SetSpeed(u8 motor, s16 speed); // 设置电机速度和方向speed范围-1000 ~ 1000 #endif// motor.c #include motor.h #include stm32f10x_tim.h // 电机初始化函数 void Motor_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 1. 开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 2. 初始化方向控制引脚为推挽输出 GPIO_InitStructure.GPIO_Pin AIN1_PIN | AIN2_PIN | BIN1_PIN | BIN2_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 初始化默认状态停止 GPIO_ResetBits(AIN1_PORT, AIN1_PIN); GPIO_ResetBits(AIN2_PORT, AIN2_PIN); GPIO_ResetBits(BIN1_PORT, BIN1_PIN); GPIO_ResetBits(BIN2_PORT, BIN2_PIN); // 3. 初始化PWM引脚 (PA0-TIM2_CH1, PA3-TIM2_CH4) 为复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 4. 配置定时器TIM2基础参数 (生成10kHz PWM) // 定时器时钟为72MHz TIM_TimeBaseStructure.TIM_Period 719; // ARR值决定周期 TIM_TimeBaseStructure.TIM_Prescaler 9; // PSC值72M/(91)7.2M TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; // 向上计数模式 TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); // 计算PWM频率 7.2M / (7191) 10,000 Hz 10kHz // 5. 配置TIM2通道1和通道4为PWM模式1 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; // PWM模式1 TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; // 使能输出 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; // 输出极性高 TIM_OCInitStructure.TIM_Pulse 0; // 初始占空比为0电机停止 TIM_OC1Init(TIM2, TIM_OCInitStructure); // 初始化通道1 TIM_OC4Init(TIM2, TIM_OCInitStructure); // 初始化通道4 // 6. 使能TIM2的预装载寄存器ARR和CCRx TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM2, ENABLE); // 7. 启动定时器TIM2 TIM_Cmd(TIM2, ENABLE); } // 设置电机速度和方向 // motor: MOTOR_A 或 MOTOR_B // speed: -1000 ~ 1000负值反转正值正转绝对值大小代表PWM占空比 void Motor_SetSpeed(u8 motor, s16 speed) { u16 pwm_val 0; if(speed 1000) speed 1000; if(speed -1000) speed -1000; pwm_val (u16)abs(speed) * 720 / 1000; // 将-1000~1000映射到0~720的CCRx值 if(motor MOTOR_A) { if(speed 0) { // 正转 GPIO_SetBits(AIN1_PORT, AIN1_PIN); GPIO_ResetBits(AIN2_PORT, AIN2_PIN); } else if(speed 0) { // 反转 GPIO_ResetBits(AIN1_PORT, AIN1_PIN); GPIO_SetBits(AIN2_PORT, AIN2_PIN); } else { // 停止 GPIO_ResetBits(AIN1_PORT, AIN1_PIN); GPIO_ResetBits(AIN2_PORT, AIN2_PIN); } TIM_SetCompare4(TIM2, pwm_val); // 设置TIM2_CH4 (PA3) 的占空比 } else if(motor MOTOR_B) { if(speed 0) { // 正转 GPIO_SetBits(BIN1_PORT, BIN1_PIN); GPIO_ResetBits(BIN2_PORT, BIN2_PIN); } else if(speed 0) { // 反转 GPIO_ResetBits(BIN1_PORT, BIN1_PIN); GPIO_SetBits(BIN2_PORT, BIN2_PIN); } else { // 停止 GPIO_ResetBits(BIN1_PORT, BIN1_PIN); GPIO_ResetBits(BIN2_PORT, BIN2_PIN); } TIM_SetCompare1(TIM2, pwm_val); // 设置TIM2_CH1 (PA0) 的占空比 } }4.2 系统化测试流程与代码硬件和基础驱动准备好了不要急着让小车跑。分步骤测试是保证成功的关键。测试步骤静态电平测试不接电机上电。用万用表或逻辑分析仪测量AIN1/AIN2/BIN1/BIN2的电平并调用Motor_SetSpeed(MOTOR_A, 500)等函数观察电平变化是否符合真值表。同时测量PWM引脚PA0 PA3应有方波输出。单电机空载测试接上一个电机先不装轮子。在main函数中编写一个简单的测试序列。// main.c 测试部分 #include motor.h #include delay.h // 需要一个简单的延时函数 int main(void) { delay_init(); // 初始化延时函数 Motor_Init(); // 初始化电机驱动 while(1) { // 测试电机A正转50%速度 Motor_SetSpeed(MOTOR_A, 500); delay_ms(2000); // 测试电机A停止 Motor_SetSpeed(MOTOR_A, 0); delay_ms(1000); // 测试电机A反转30%速度 Motor_SetSpeed(MOTOR_A, -300); delay_ms(2000); Motor_SetSpeed(MOTOR_A, 0); delay_ms(1000); // 同样的逻辑测试电机B... // Motor_SetSpeed(MOTOR_B, 400); // ... } }观察电机是否按照指令正转、停、反转。用手轻轻捏住电机轴感受不同PWM值下的扭矩变化。你应该能感觉到即使PWM值很小比如50电机也能输出一个恒定的扭矩而不是抖动这说明你的PWM频率是合适的。双电机同步性测试同时控制两个电机正转、反转。观察它们启动和停止是否同步。由于电机特性、轮子摩擦等差异完全同步很难但大致应该一致。这个测试是为了排查某个通道接线或驱动有问题。加载测试给电机装上轮子让小车架空可以用东西架起来让轮子悬空。重复测试序列观察轮子转动是否顺畅有无异响。5. 调试过程中常见问题与解决方案即使按照教程一步步来你也可能会遇到下面这些问题。这里是我和很多爱好者踩过的坑。问题现象可能原因排查步骤与解决方案电机完全不转无反应1. 电源未接通或接反。2. STBY引脚未接高电平。3. 控制引脚电平错误全低或全高。4. PWM占空比为0。1. 用万用表测量VM、VCC对GND电压。2. 检查STBY是否连接至VCC3.3V/5V。3. 用逻辑分析仪或示波器检查AIN1/AIN2/PWMA电平序列。4. 检查代码中TIM_SetComparex的值是否大于0。电机抖动或嗡嗡响但不连续转动1.PWM频率过低典型低于100Hz。2. 电机供电电压不足。3. 电机损坏或卡死。1.这是最常见原因检查定时器配置确保PWM频率在1kHz以上建议5-10kHz。2. 测量电机运行时VM端的电压看是否被拉低太多。3. 断开电机直接给电机加额定电压看是否正常转动。一个电机转另一个不转1. 不转的电机通道接线错误或虚焊。2. 对应的STM32 IO口配置错误或损坏。3. TB6612芯片局部损坏。1. 交换两个电机的接线如果问题跟随电机走则是电机问题如果问题跟随驱动通道走则是驱动或控制信号问题。2. 用万用表测量不转通道的控制引脚在程序运行时的电平变化。3. 交换两个通道的PWM输出引脚定义测试。电机只能单向转方向控制引脚AIN1/AIN2设置逻辑错误始终为一高一低或同一种状态。仔细检查Motor_SetSpeed函数中正转和反转时对方向引脚的电平设置代码对照TB6612真值表。电机转速与PWM值不成比例或低速时不动1. 电机存在“死区”。2. 电池电量不足。3. PWM分辨率不够ARR值太小。1. 直流电机有启动电压阈值PWM占空比低于一定值如10%时扭矩无法克服静摩擦力。在软件中可以做映射将低速区的PWM值适当提高。2. 给电池充电或更换电池。3. 增大ARR值如从719改为3599PWM频率会降低但分辨率从0-720提高到0-3600低速控制更细腻。需权衡频率与分辨率。单片机频繁复位或程序跑飞电机干扰通过电源串入控制系统这是最经典的电源问题。1.确保电机电源VM与控制电源VCC是分开的通过DC-DC模块隔离。2. 在TB6612的VM和GND之间紧贴引脚并联大电容100uF以上和小电容0.1uF。3. 检查所有地线是否在一点共地且导线足够粗。上电后TB6612模块发热严重1. 输出端AO1/AO2短路。2. 电机线圈短路或电流过大。3. 控制逻辑错误导致H桥上下管直通虽然TB6612有防直通逻辑但极端情况可能发生。1.立即断电用万用表蜂鸣档测量电机两线间电阻正常应为几欧姆到几十欧姆如果接近0欧姆则短路。2. 检查电机是否被堵转堵转电流极大。3. 检查代码确保不会出现AIN1和AIN2同时为高或同时为低虽然都是停止但同时为高时功耗略大的情况过长尤其是在PWM占空比100%时。独家避坑技巧在正式组装小车前强烈建议在面包板或洞洞板上搭建整个驱动电路进行测试。使用可调稳压电源给VM供电并设置电流限流比如1A这样即使短路或接错也不会烧坏元件。用串口助手发送简单指令如‘A500’控制电机比反复下载程序测试要高效得多。当你完成了以上所有测试两个电机都能精准、平稳、有力地响应你的速度指令时恭喜你你的平衡小车项目已经完成了最坚实的第一步。这套驱动系统将成为你后续植入“大脑”控制算法的强健“双腿”。记住硬件是骨骼软件是灵魂而稳定可靠的驱动是连接骨骼与灵魂的神经。在接下来的系列文章中我们会将MPU6050姿态传感器接入并开始探讨PID控制算法如何让这双“腿”学会自己站立。