1. 为什么选择TB6612?从L298N到高效驱动的升级之路
很多朋友刚开始玩单片机控制直流电机的时候,用的第一个驱动模块大概率是L298N。我也不例外,当年用它做过不少小车和机械臂。但用久了,槽点就来了:发热量感人,夏天摸上去烫手;体积不小,占地方;效率也一般,电池消耗快。后来接触到TB6612,感觉就像从“老爷车”换到了“新能源”,体验提升了一大截。
简单来说,TB6612是一个双通道的H桥驱动芯片,能同时驱动两个直流电机。它最大的优点就是效率高、发热小、体积紧凑。这背后的原理是它采用了MOSFET作为功率开关器件,导通内阻小,所以自身损耗的功率就少,大部分电能都实实在在地用在了驱动电机上,自然就不那么烫了。对于用电池供电的移动小车、机器人项目来说,这意味着更长的续航。而且它的逻辑电压(VCC)支持3.3V和5V,可以直接和STM32这类3.3V逻辑的单片机对接,省去了电平转换的麻烦,接线也清爽很多。
所以,如果你正在为你的STM32智能小车、机械臂或者任何需要精准控制电机转速和方向的项目选型,TB6612绝对是一个比L298N更“香”的选择。它特别适合那些对空间、功耗和发热有要求,同时又希望控制逻辑简单直接的场景。接下来,我就带你从零开始,手把手搞定STM32和TB6612的搭配,实现平滑的PWM调速。
2. 硬件连接:给STM32和TB6612“牵线搭桥”
硬件连接是第一步,也是确保后续一切顺利的基础。这一步看似简单,但接错线轻则电机不转,重则可能损坏模块或单片机,所以咱们得仔细捋清楚。
2.1 认识TB6612的“手脚”(引脚功能)
拿到TB6612模块(市面上常见的是搭载TB6612FNG芯片的蓝色小板子),我们先来认一遍引脚,这就像认识新朋友得先知道名字一样。
- 电源部分:
VM:这是电机的“主食”电源口。接你的直流电机工作电压,范围很宽,从2.5V到13.5V都行,但模块一般标称最大15V。我常用的12V减速电机就用12V电源接这里。VCC:这是驱动芯片的“大脑”工作电源,也就是逻辑电压。接3.3V或5V。当你的STM32是3.3V逻辑电平时,这里就接3.3V,完美匹配。GND:地线。模块上通常有好几个GND引脚,我的经验是,把它们全部接在一起,并且同时连接到电源的GND和STM32的GND。这样可以建立一个稳固的“共同参考点”,避免因电势差引入噪声,让信号更稳定。
- 控制信号部分:
STBY(Standby):待机控制引脚。这个引脚必须接高电平(VCC)芯片才能正常工作。接低电平时,芯片进入待机模式,所有输出关闭,电机停转。我们可以用一个STM32的GPIO来控制它,实现电机的总开关功能,但初期测试可以直接接到VCC上让它一直使能。AIN1/AIN2,BIN1/BIN2:这两组是控制电机方向的逻辑输入引脚,分别对应A通道和B通道电机。它们接收来自STM32 GPIO的数字信号(高/低电平)。PWMA/PWMB:这是控制电机速度的PWM信号输入引脚,分别对应A通道和B通道。我们需要把STM32定时器产生的PWM波接到这里。AO1/AO2,BO1/BO2:这两组是驱动模块的功率输出,直接连接到你电机的两根线上。
2.2 实战接线图与避坑指南
假设我们只驱动一个电机(A通道),STM32使用常见的STM32F103C8T6(蓝色pill板)。下面是一个清晰的接线表格:
| TB6612引脚 | 连接目标 | 说明与注意事项 |
|---|---|---|
| VM | 12V直流电源正极 | 根据你的电机额定电压选择,注意电源功率要足够。 |
| VCC | STM32的 3.3V 引脚 | 关键点:务必确认是3.3V,不是5V,除非你的单片机是5V逻辑。 |
| GND | 12V电源地 & STM32的 GND | 多个GND引脚用杜邦线并联起来,确保共地可靠。 |
| STBY | STM32的某个GPIO(如PA0)或直接接 VCC | 测试时可接VCC,实际应用建议用GPIO控制,更灵活。 |
| AIN1 | STM32的GPIO(如PA1) | 用于控制电机方向。 |
| AIN2 | STM32的GPIO(如PA2) | 用于控制电机方向。 |
| PWMA | STM32的 PWM输出引脚(如PA8) | 核心:必须连接到有定时器PWM输出功能的引脚上。 |
| AO1, AO2 | 直流电机的线1,线2 | 不分正负,交换接线只会改变我们定义的正反转方向。 |
我踩过的坑:有一次电机抖动得厉害,转速也不稳,排查了半天发现是STM32的GND和电机电源的GND只通过一根细长的杜邦线连接,接触还有点虚焊。后来我把所有GND用粗导线拧在一起焊牢,问题立刻消失。所以,“共地无小事”,一定要扎实。
接好线后,先别急着写PWM代码。我们可以做一个简单的“静态测试”:把STBY接VCC,AIN1接VCC,AIN2接GND,PWMA也接VCC(模拟100%占空比)。此时电机应该以一个方向全速转动。如果转了,恭喜你,硬件连接基本正确!如果不转,立刻断电,检查电源、接地和电机本身。
3. STM32的PWM配置:让定时器“唱出”精准的脉冲波
电机能转了,接下来就是重头戏——调速。调速的本质就是改变平均电压,而PWM(脉冲宽度调制)就是通过高速开关,用数字方法模拟出不同平均电压的神奇技术。占空比(高电平时间占整个周期的比例)越大,平均电压越高,电机转速就越快。
在STM32里,PWM是由定时器(TIM)的外设功能产生的。它非常灵活,但也让不少新手头疼。别怕,我们一步步来。
3.1 定时器基础概念:ARR和PSC是什么?
配置PWM时,你会反复遇到两个参数:ARR(自动重装载寄存器)和PSC(预分频器)。你可以把它们想象成共同决定PWM“心跳”频率的搭档。
PSC(预分频器):假设STM32的主频是72MHz,这个时钟太快了。PSC就像一个“减速齿轮”,把72MHz先分频。计算公式是:定时器时钟 = 系统时钟 / (PSC + 1)。如果PSC设为71,那么定时器时钟就是 72MHz / (71+1) = 1MHz。ARR(自动重装载值):这个值决定了定时器计数多少次溢出一次,形成一个PWM周期。计数器从0数到ARR,然后归零重来。PWM的频率就是:PWM频率 = 定时器时钟 / (ARR + 1)。
所以,如果我们想要一个10kHz的PWM频率(这个频率对于电机驱动来说很常用,既高于人耳听觉范围避免噪音,又能保证较好的响应速度),假设系统时钟72MHz,PSC设为71得到1MHz定时器时钟,那么ARR就应该设置为 (1,000,000 / 10,000) - 1 = 99。
3.2 手把手配置代码与函数解读
我们以STM32标准外设库为例,配置定时器TIM3的通道1(对应引脚PA6)产生PWM。代码我会加上详细注释,你甚至可以直接复制到你的工程里修改使用。
#include "stm32f10x.h"
void TIM3_PWM_Init(u16 arr, u16 psc) {
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 1. 开启时钟:定时器和对应GPIO的时钟必须打开
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // TIM3挂载在APB1总线
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // GPIOA挂载在APB2
// 2. 配置GPIO为复用推挽输出模式
// PA6是TIM3_CH1的复用功能输出引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出,这是关键!
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 输出速度设高一些
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 初始化定时器时基单元:设定ARR和PSC,决定PWM频率
TIM_TimeBaseStructure.TIM_Period = arr; // 自动重装载值ARR
TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频器PSC
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分频,通常为0
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 4. 初始化PWM输出比较模式:决定PWM的生成方式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // 模式2:CNT<CCR时输出有效电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
TIM_OCInitStructure.TIM_Pulse = 0; // 初始脉冲宽度(占空比),可先设为0,后面动态改
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性高:有效电平为高
TIM_OC1Init(TIM3, &TIM_OCInitStructure); // 初始化通道1
// 5. 使能预装载和定时器
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能通道1预装载寄存器
TIM_ARRPreloadConfig(TIM3, ENABLE); // 使能ARR预装载,避免更新时产生毛刺
TIM_Cmd(TIM3, ENABLE); // 最后,启动定时器TIM3!
}
在main函数初始化部分调用这个函数:TIM3_PWM_Init(99, 71); 这样我们就得到了一个10kHz的PWM波,初始占空比为0(电机不转)。
关键点解析:
GPIO_Mode_AF_PP(复用推挽输出):这个模式是PWM输出所必需的,意味着这个引脚的控制权已经交给了内部的定时器外设,而不是普通的GPIO输出寄存器。TIM_OCMode_PWM2:STM32有两种PWM模式。简单理解,模式1是“计数小于比较值时输出有效电平”,模式2是“计数小于比较值时输出无效电平”。我们通常用模式2,并设置极性为高,这样TIM_Pulse(也就是CCR寄存器值)直接对应高电平的宽度,更直观。TIM_OCPolarity_High:有效电平为高。这意味着当PWM处于“有效”阶段时,引脚输出高电平(3.3V)。这个设置必须和TB6612的PWMA输入要求匹配,TB6612的PWM输入默认就是高电平有效。
4. 动态调速与方向控制:让你的电机“听指挥”
硬件和PWM基础都有了,现在让我们把电机真正控制起来,实现随心所欲的变速和换向。
4.1 方向控制逻辑:AIN1和AIN2的“舞蹈”
TB6612控制电机方向非常直观,它内部是一个H桥电路,通过AIN1和AIN2的电平组合来切换电流流经电机的方向。逻辑表如下:
| AIN1 | AIN2 | 电机状态 (A通道) |
|---|---|---|
| 0 | 0 | 停止/刹车(短路刹车) |
| 1 | 0 | 正转 |
| 0 | 1 | 反转 |
| 1 | 1 | 停止/刹车(短路刹车) |
我们可以用两个普通的GPIO输出引脚来控制它们。例如,我们之前把AIN1接PA1,AIN2接PA2。
// 简单的方向控制函数示例
void Motor_SetDirection(uint8_t dir) {
switch(dir) {
case 0: // 停止
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_RESET);
break;
case 1: // 正转
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_RESET);
break;
case 2: // 反转
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_SET);
break;
default:
// 默认停止
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_RESET);
}
}
4.2 动态改变PWM占空比:调速的核心魔法
配置好PWM后,调速就变成了简单地修改一个寄存器的值——捕获/比较寄存器(CCR)。在标准库中,有专门的函数来安全地修改它。
// 设置电机速度,speed范围建议为0-ARR值(我们之前设置的99)
void Motor_SetSpeed(uint16_t speed) {
if(speed > 99) speed = 99; // 防止设置值超过ARR
TIM_SetCompare1(TIM3, speed); // 修改TIM3通道1的CCR值
}
这个TIM_SetCompare1函数,就是整个PWM调速的灵魂。你传入的speed值,直接决定了在一个PWM周期内,高电平持续的时间比例。比如ARR=99,你设置speed=30,那么占空比就是 (30 / (99+1)) = 30%。电机就会以大约30%的最大速度运转。
实测中的小技巧:电机的启动通常需要克服静摩擦力,所以你会发现,占空比低于某个值(比如10%)时,电机可能不转,只是嗡嗡响。这个值被称为“死区”。在实际编程中,我们可以做一个简单的映射函数,当输入速度为0时,输出0;当输入速度大于0时,输出值=死区值+按比例缩放的值,这样能让低速控制更线性。
4.3 整合测试:让电机转起来
现在,我们把所有功能整合到一个简单的测试程序里:
int main(void) {
// 系统时钟初始化等...
Delay_Init(); // 初始化延时函数
TIM3_PWM_Init(99, 71); // 初始化10kHz PWM
// 初始化控制方向的GPIO PA1, PA2为推挽输出...
// 初始化STBY控制引脚(如果需要)...
Motor_SetDirection(1); // 设置正转方向
Motor_SetSpeed(50); // 设置50%左右的速度(CCR=50)
while(1) {
// 可以在这里加入按键扫描或串口指令,动态改变速度和方向
// 例如:
// if(Key_Pressed) {
// Motor_SetSpeed(new_speed);
// }
Delay_Ms(10);
}
}
烧录程序,上电,你的电机应该就能按照设定的速度和方向平稳运行了!试着修改Motor_SetSpeed的参数,听听电机转速的变化,感受PWM调速的直观效果。
5. 进阶优化与实战经验分享
基础功能跑通后,我们可以玩点更高级的,让系统更稳定、更智能。
5.1 频率与占空比分辨率的选择艺术
PWM频率不是随便选的。太高了(比如上百kHz),开关损耗会增加,TB6612可能会轻微发热,而且对STM32的定时器配置要求也高。太低了(比如几十Hz),电机会有明显的“咔咔”声,转动不连续,而且可能进入人耳可听的音频范围,产生噪音。
- 对于普通直流有刷电机:1kHz到20kHz是一个比较理想的区间。我个人最常用的是10kHz,这是一个很好的平衡点。上面的例程就是10kHz。
- 占空比分辨率:这由ARR的值决定。ARR=99时,分辨率是1%(1/100)。如果你需要更精细的速度控制,比如做一台非常平稳的摄像头云台,可以增大ARR值。例如,设置PSC=719,ARR=999,这样PWM频率是 72MHz / (719+1) / (999+1) = 100Hz。虽然频率低了点,但分辨率达到了0.1%。你需要根据对速度平滑度和响应速度的要求来权衡。
5.2 加入“软启动”与“刹车”功能
直接给电机全速的PWM信号,有时冲击电流会比较大。我们可以实现一个软启动函数,让速度在几百毫秒内线性增加。
void Motor_SoftStart(uint16_t target_speed, uint16_t duration_ms) {
uint16_t step = target_speed / (duration_ms / 10); // 假设每10ms增加一次
uint16_t current_speed = 0;
while(current_speed < target_speed) {
current_speed += step;
if(current_speed > target_speed) current_speed = target_speed;
Motor_SetSpeed(current_speed);
Delay_Ms(10); // 延时10ms
}
}
刹车功能也很重要。除了前面提到的将AIN1和AIN2同时置为低电平(短路刹车,刹车快但有冲击),还可以采用“缓刹车”,即快速将PWM占空比减小到0。
5.3 应对电机干扰:电源与滤波
电机,尤其是碳刷电机,是一个巨大的干扰源。它在启动、停止、换向时会产生强烈的电压尖峰和电磁噪声,可能会通过电源线或空间辐射干扰STM32,导致程序跑飞或ADC采样不准。
我的抗干扰三板斧:
- 电源隔离与滤波:在TB6612的VM电源入口处,紧贴模块焊接一个100uF的电解电容和一个0.1uF的瓷片电容,分别滤除低频和高频噪声。如果条件允许,给STM32的供电最好和电机驱动电源分开(比如用两个独立的稳压模块),或者使用DCDC隔离模块。
- 信号线保护:连接STM32和TB6612的PWM、方向控制线,如果距离较长(超过10cm),可以考虑在STM32输出端串联一个几十欧姆的小电阻(如22Ω-100Ω),可以削弱尖峰电流。
- 软件看门狗:务必在STM32程序中开启独立看门狗(IWDG),这样即使程序因干扰暂时跑飞,也能自动复位恢复,不至于完全“死机”。这是提高产品可靠性的必备措施。
最后,别忘了TB6612的底部有一个大的散热焊盘,如果你驱动的电机电流较大(比如持续1A以上),建议在PCB设计时,给这个焊盘铺设大面积铜皮并打过孔,帮助散热。虽然它比L298N凉快很多,但良好的散热设计总是没错的。
1119



被折叠的 条评论
为什么被折叠?



