一、TB6612 简介

TB6612FNG 是东芝的双路 H 桥电机驱动芯片,比 L298N 更高效(MOSFET 而非 BJT)。

特性 TB6612 L298N
驱动类型 MOSFET BJT
压降 ~0.5V ~2V
单路连续电流 1.2A 2A
峰值电流 3.2A 3A
PWM 频率 最高 100KHz 最高 25KHz
控制方式 IN1/IN2 + PWM IN1/IN2 + ENA
体积 大(带散热片)

二、引脚功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

┌─────────────────┐
GND ────┤1 24├──── VM (电机电源 2.5~13.8V)
VCC ────┤2 23├──── PWMB
AIN1 ────┤3 22├──── BIN2
AIN2 ────┤4 21├──── BIN1
PWMA ────┤5 TB6612FNG 20├──── GND
AOUT1───┤6 19├──── GND
GND ────┤7 18├──── BOUT1
GND ────┤8 17├──── BOUT2
AOUT2───┤9 16├──── VM
VM ────┤10 15├──── GND
VM ────┤11 14├──── VM
GND ────┤12 13├──── GND
└─────────────────┘
引脚组 引脚 功能
电源 VM(10/11/14/16/24) 电机电源 2.5~13.8V
电源 VCC(2) 逻辑电源 2.7~5.5V
电源 GND(1/7/8/12/13/15/19/20) 全部接地
A路控制 AIN1(3), AIN2(4) A路方向控制
A路控制 PWMA(5) A路速度 PWM
A路输出 AOUT1(6), AOUT2(9) 接电机 A
B路控制 BIN1(21), BIN2(22) B路方向控制
B路控制 PWMB(23) B路速度 PWM
B路输出 BOUT1(18), BOUT2(17) 接电机 B
待机 STBY(未标注) 高电平=工作,低=待机

三、控制逻辑

IN1 IN2 PWM 电机状态
0 0 X 刹车(短路制动)
0 1 PWM 反转,PWM 调速
1 0 PWM 正转,PWM 调速
1 1 X 刹车(短路制动)

注意:刹车和待机不同。刹车时 H 桥下管导通,电机惯性旋转产生反向电动势被短路→快速停止。待机时所有 MOS 管关断→自由滑行。

四、硬件连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

STM32F103C8T6 TB6612
PA0 ──── PWM ──── PWMA (A路速度)
PA1 ──── IN1 ──── AIN1 (A路方向)
PA2 ──── IN2 ──── AIN2 (A路方向)
PA3 ──── PWM ──── PWMB (B路速度)
PA4 ──── IN1 ──── BIN1 (B路方向)
PA5 ──── IN2 ──── BIN2 (B路方向)
PB0 ──── STBY ──── STBY (待机,高有效)

3.3V ──── VCC (逻辑电源)
12V ──── VM (电机电源,按电机额定电压)
GND ──── GND (共地!)

┌── AOUT1 ── 电机A(+)
TB6612 ──────────┤
└── AOUT2 ── 电机A(-)

五、CubeMX 配置

外设 引脚 配置
TIM2_CH1 PA0 PWM Generation, 20KHz
TIM2_CH2 PA1 PWM Generation, 20KHz
GPIO PA2, PA3, PA4, PA5, PB0 Output, Push-Pull

PWM 周期:72MHz / 72 / 1000 = 1KHz,实际常用 10K~20KHz。

六、代码实现

电机驱动封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* motor.h */
#ifndef __MOTOR_H
#define __MOTOR_H

#include "stm32f1xx_hal.h"

/* 电机方向 */
typedef enum {
MOTOR_STOP = 0,
MOTOR_FORWARD,
MOTOR_BACKWARD,
} MotorDir_t;

/* TB6612 引脚结构 */
typedef struct {
GPIO_TypeDef *IN1_Port; uint16_t IN1_Pin;
GPIO_TypeDef *IN2_Port; uint16_t IN2_Pin;
TIM_HandleTypeDef *htim; uint32_t PWM_Channel;
GPIO_TypeDef *STBY_Port; uint16_t STBY_Pin;
} Motor_t;

void Motor_Init(Motor_t *m);
void Motor_Run(Motor_t *m, MotorDir_t dir, uint16_t speed); // speed: 0~999
void Motor_Stop(Motor_t *m); // 刹车
void Motor_Coast(Motor_t *m); // 惰行

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/* motor.c */
#include "motor.h"

static Motor_t *motor_A = NULL, *motor_B = NULL;
static uint8_t standby = 0;

void Motor_Init(Motor_t *m) {
HAL_TIM_PWM_Start(m->htim, m->PWM_Channel);
HAL_GPIO_WritePin(m->IN1_Port, m->IN1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(m->IN2_Port, m->IN2_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(m->STBY_Port, m->STBY_Pin, GPIO_PIN_SET);
}

/*
* 驱动电机
* dir: 方向 (正转/反转/停止)
* speed: 0~999 (对应 0~100% 占空比)
*/
void Motor_Run(Motor_t *m, MotorDir_t dir, uint16_t speed) {
if (speed > 999) speed = 999;

switch (dir) {
case MOTOR_FORWARD:
HAL_GPIO_WritePin(m->IN1_Port, m->IN1_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(m->IN2_Port, m->IN2_Pin, GPIO_PIN_RESET);
break;
case MOTOR_BACKWARD:
HAL_GPIO_WritePin(m->IN1_Port, m->IN1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(m->IN2_Port, m->IN2_Pin, GPIO_PIN_SET);
break;
default:
speed = 0;
HAL_GPIO_WritePin(m->IN1_Port, m->IN1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(m->IN2_Port, m->IN2_Pin, GPIO_PIN_RESET);
break;
}

__HAL_TIM_SET_COMPARE(m->htim, m->PWM_Channel, speed);
}

/* 刹车:IN1=IN2=0 → 短路制动 */
void Motor_Stop(Motor_t *m) {
Motor_Run(m, MOTOR_STOP, 0);
}

/* 惰行:关 PWM + 待机 */
void Motor_Coast(Motor_t *m) {
__HAL_TIM_SET_COMPARE(m->htim, m->PWM_Channel, 0);
HAL_GPIO_WritePin(m->STBY_Port, m->STBY_Pin, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(m->STBY_Port, m->STBY_Pin, GPIO_PIN_SET);
}

主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int main(void) {
HAL_Init();
SystemClock_Config();
MX_TIM2_Init();
MX_GPIO_Init();

Motor_t motorA = {
.IN1_Port = GPIOA, .IN1_Pin = GPIO_PIN_2,
.IN2_Port = GPIOA, .IN2_Pin = GPIO_PIN_3,
.htim = &htim2, .PWM_Channel = TIM_CHANNEL_1,
.STBY_Port = GPIOB, .STBY_Pin = GPIO_PIN_0,
};
Motor_Init(&motorA);

while (1) {
// 加速正转 0→80%
for (int s = 0; s <= 800; s += 10) {
Motor_Run(&motorA, MOTOR_FORWARD, s);
HAL_Delay(20);
}
HAL_Delay(1000);

// 刹车
Motor_Stop(&motorA);
HAL_Delay(500);

// 反转 50%
Motor_Run(&motorA, MOTOR_BACKWARD, 500);
HAL_Delay(2000);

// 惰行
Motor_Coast(&motorA);
HAL_Delay(2000);
}
}

七、双路控制(小车差速)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Motor_t motorL = { /* PA0(TIM2_CH1), PA2, PA3 */ };
Motor_t motorR = { /* PA1(TIM2_CH2), PA4, PA5 */ };

/* 前进:两轮同向 */
Motor_Run(&motorL, MOTOR_FORWARD, 600);
Motor_Run(&motorR, MOTOR_FORWARD, 600);

/* 左转:左轮慢,右轮快(差速) */
Motor_Run(&motorL, MOTOR_FORWARD, 200);
Motor_Run(&motorR, MOTOR_FORWARD, 800);

/* 原地右转:左轮正转,右轮反转 */
Motor_Run(&motorL, MOTOR_FORWARD, 500);
Motor_Run(&motorR, MOTOR_BACKWARD, 500);

/* 停车 */
Motor_Stop(&motorL);
Motor_Stop(&motorR);

八、麦克纳姆轮解算

麦轮(Mecanum Wheel)辊子与轮毂成 45°,通过四轮差速组合实现全向移动。

1
2
3
4
5
6
7
8
9
10
11
   ┌─── 前方 ───┐
│ │
┌──┴──┐ ┌──┴──┐
│ ① │ │ ② │ 轮①: 左前 (↗)
└──┬──┘ └──┬──┘ 轮②: 右前 (↖)
│ │
│ 车身 │ 轮③: 左后 (↖)
│ │ 轮④: 右后 (↗)
┌──┴──┐ ┌──┴──┐
│ ③ │ │ ④ │ 辊子方向: ④↗ ②↖
└─────┘ └─────┘ ①↖ ③↗

运动学模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
轮速度 = 运动学矩阵 × 期望速度

┌──────┐ ┌─────────────────────┐ ┌────┐
│ ω₁ │ │ 1 -1 -(Lx+Ly) │ │ Vx │
│ ω₂ │ │ 1 1 (Lx+Ly) │ │ Vy │
│ ω₃ │ = │ 1 1 -(Lx+Ly) │ │ ω │
│ ω₄ │ │ 1 -1 (Lx+Ly) │ └────┘
└──────┘ └─────────────────────┘

Vx: 横向速度 (右为正)
Vy: 纵向速度 (前为正)
ω : 旋转角速度 (逆时针为正)
Lx: 轮距的一半 (左右轮间距/2)
Ly: 轴距的一半 (前后轮间距/2)

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <math.h>

/* 麦轮底盘参数 */
#define WHEEL_BASE_X 0.15f // 左右轮距的一半 (m)
#define WHEEL_BASE_Y 0.12f // 前后轴距的一半 (m)
#define WHEEL_RADIUS 0.032f // 轮半径 (m)
#define MAX_PWM 999 // PWM 最大值

typedef struct {
float vx; // 横向速度 m/s
float vy; // 纵向速度 m/s
float w; // 角速度 rad/s
} RobotVel_t;

/*
* 麦轮逆运动学解算
* 输入: 期望机器人速度 (vx, vy, w)
* 输出: 四轮 PWM [-MAX_PWM, MAX_PWM]
* 正值=正转, 负值=反转
*/
void Mecanum_Kinematics(RobotVel_t *vel, int16_t pwm[4]) {
float L_sum = WHEEL_BASE_X + WHEEL_BASE_Y;

// 逆运动学矩阵
float w1 = vel->vx - vel->vy - vel->w * L_sum;
float w2 = vel->vx + vel->vy + vel->w * L_sum;
float w3 = vel->vx + vel->vy - vel->w * L_sum;
float w4 = vel->vx - vel->vy + vel->w * L_sum;

// 速度 → PWM (线性映射)
float speed[4] = {w1, w2, w3, w4};

// 归一化: 如果任一值超过最大速度,等比缩放
float max_speed = 0;
for (int i = 0; i < 4; i++) {
if (fabsf(speed[i]) > max_speed) max_speed = fabsf(speed[i]);
}
if (max_speed > MAX_PWM) {
float scale = MAX_PWM / max_speed;
for (int i = 0; i < 4; i++) speed[i] *= scale;
}

for (int i = 0; i < 4; i++) pwm[i] = (int16_t)speed[i];
}

/*
* 将解算结果写入四路电机
*/
void Mecanum_Drive(Motor_t *m[4], RobotVel_t *vel) {
int16_t pwm[4];
Mecanum_Kinematics(vel, pwm);

for (int i = 0; i < 4; i++) {
MotorDir_t dir = (pwm[i] >= 0) ? MOTOR_FORWARD : MOTOR_BACKWARD;
uint16_t speed = (uint16_t)(pwm[i] >= 0 ? pwm[i] : -pwm[i]);
if (speed > MAX_PWM) speed = MAX_PWM;
Motor_Run(m[i], dir, speed);
}
}

典型运动指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
RobotVel_t cmd;

// 前进 (Vy=300 对应约 0.3m/s)
cmd = (RobotVel_t){0, 300, 0}; Mecanum_Drive(motors, &cmd);

// 后退
cmd = (RobotVel_t){0, -300, 0}; Mecanum_Drive(motors, &cmd);

// 右移 (纯横向)
cmd = (RobotVel_t){ 300, 0, 0}; Mecanum_Drive(motors, &cmd);

// 左移
cmd = (RobotVel_t){-300, 0, 0}; Mecanum_Drive(motors, &cmd);

// 右前 45° 斜移
cmd = (RobotVel_t){ 300, 300, 0}; Mecanum_Drive(motors, &cmd);

// 原地逆时针旋转
cmd = (RobotVel_t){0, 0, 200}; Mecanum_Drive(motors, &cmd);

// 右移 + 同时旋转
cmd = (RobotVel_t){ 200, 0, 100}; Mecanum_Drive(motors, &cmd);

// 停车
cmd = (RobotVel_t){0, 0, 0}; Mecanum_Drive(motors, &cmd);

公式速查(轮转速 → 机器人速度)

1
2
3
Vx = (ω₁ + ω₂ + ω₃ + ω₄) / 4
Vy = (-ω₁ + ω₂ + ω₃ - ω₄) / 4
ω = (-ω₁ + ω₂ - ω₃ + ω₄) / (4 × (Lx + Ly))

九、PWM 频率选择

频率 效果 适用
< 100Hz 电机抖动,尖锐啸叫 ❌ 不用
1KHz 可听见轻微啸叫 勉强可用
10K~20KHz 安静平滑,推荐 ✅ 通用
> 20KHz 超出人耳范围,完全无声 ✅ 精密控制

TB6612 PWM 最高 100KHz。超过 20KHz 会增大 MOSFET 开关损耗,通常选 10K~20KHz。

十、常见问题

现象 原因 解决
电机不转 STBY 未拉高 HAL_GPIO_WritePin(STBY, SET)
电机只朝一个方向转 IN1/IN2 接反 交换 IN1/IN2 或交换电机线
低速抖动 PWM 频率太低 提高到 10KHz 以上
芯片发烫 PWM 频率太高 / 电流过大 降到 20KHz,检查电机堵转
调速不线性 死区太大 低于 10% 占空比可能带不动,加死区补偿
VM 和 VCC 短接 接错线 VM=电机电压(3~13.8V),VCC=逻辑电压(3.3V/5V)

十一、刹车 vs 惰行

1
2
3
4
5
6
7
8
9
10

刹车(STOP):
H 桥下管导通,电机两端短路
→ 电机旋转产生的反电动势被短接→形成制动扭矩
→ 快速停车,适合精确控制

惰行(COAST):
STBY=0,所有 MOS 管截止
→ 电机两端开路,无电流回路
→ 靠摩擦慢慢停下,适合省电场景