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):决定电机转向。其真值表如下:
AIN1 AIN2 电机状态 0 0 停止(刹车) 1 0 正转 0 1 反转 1 1 停止(刹车) - 一个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):电机会听到明显的“滋滋”声,转动不连续,有顿挫感,且电机电刷磨损加快。
- 频率太高(如几十kHz):MOSFET开关损耗增加,且可能超出电机电感线圈的响应速度,控制效率下降。
- 推荐范围:1kHz 到 20kHz之间是一个较好的范围。我个人的经验是使用5kHz 或 10kHz。这个频率人耳几乎听不到噪音,电机运行平稳,TB6612也能高效工作。
举例计算:假设系统时钟72MHz,TIM_CLK也是72MHz。我们要生成10kHz的PWM。
- 目标:PWM频率 = 10kHz = 10,000 Hz。
- 定时器计数频率应为:
PWM_Freq * (ARR+1)。我们先设定ARR=719,则计数频率需要10,000 * (719+1) = 7,200,000 Hz = 7.2MHz。 - 计算预分频值PSC:
PSC = TIM_CLK / 计数频率 - 1 = 72,000,000 / 7,200,000 - 1 = 10 - 1 = 9。 - 验证:
计数频率 = 72MHz / (9+1) = 7.2MHz。PWM周期 = (ARR+1) / 计数频率 = 720 / 7,200,000 = 0.0001秒 = 10kHz。匹配成功。
3. 硬件连接与电路设计要点
3.1 接线图与引脚分配
这是最容易出错的一步。请务必对照你的模块和开发板,仔细核对。
TB6612模块引脚说明(以常见模块为例):
- VM:电机驱动电压输入(接电池正极,如7.4V)。务必与单片机的供电隔离,避免电机干扰导致单片机复位。
- VCC:逻辑电压输入(接STM32的3.3V或5V)。给TB6612内部逻辑电路供电。
- GND:电源地。必须与STM32的GND共地,这是信号参考的基础。
- STBY:待机控制引脚。高电平使能芯片,低电平进入待机模式(所有输出关闭)。我们通常直接接VCC(3.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.3V
- VM:接 7.4V 锂电池正极
- VCC:接 3.3V
- GND:接 电源地
实操心得:强烈建议使用彩色杜邦线,并按功能分组(如电源用红黑,控制信号用黄绿蓝)。接线完成后,先不要接电机,用万用表电压档测量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/(9+1)=7.2M TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 计算:PWM频率 = 7.2M / (719+1) = 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是否连接至VCC(3.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改为3599),PWM频率会降低,但分辨率从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),这样即使短路或接错,也不会烧坏元件。用串口助手发送简单指令(如‘A+500’)控制电机,比反复下载程序测试要高效得多。
当你完成了以上所有测试,两个电机都能精准、平稳、有力地响应你的速度指令时,恭喜你,你的平衡小车项目已经完成了最坚实的第一步。这套驱动系统将成为你后续植入“大脑”(控制算法)的强健“双腿”。记住,硬件是骨骼,软件是灵魂,而稳定可靠的驱动,是连接骨骼与灵魂的神经。在接下来的系列文章中,我们会将MPU6050姿态传感器接入,并开始探讨PID控制算法如何让这双“腿”学会自己站立。