11 - 电机控制应用
本章节介绍 Pico-G1 扩展板上的直流电机控制应用示例 —— motor。该应用演示了如何使用 GPIO 和 PWM 接口驱动 TB6612 电机驱动模块,实现直流电机的正反转、速度调节和停止控制,并在 TFT 屏幕上显示电机状态。这是学习电机控制和 PWM 调速的实用示例。
应用源码位于 SDK 目录 source/app/11_motor/,提供了完整的 TB6612 驱动实现。
1 应用概述
1.1 功能特性
- 电机驱动控制:通过 GPIO 控制 TB6612 电机驱动模块
- PWM 调速:支持 PWM 速度调节(占空比 0~100%)
- 正反转控制:支持电机正转、反转、停止和刹车
- 实时状态显示:在 TFT 屏幕上显示电机方向、速度和运行状态
- 多种运行模式:支持连续运行、定时运行、脉冲运行等模式
1.2 技术参数
| 参数 | 值 |
|---|---|
驱动芯片 | TB6612FNG |
电机电压 | 2.5V~13.5V |
逻辑电压 | 2.7V~5.5V |
最大电流 | 1.2A 连续 / 3.2A 峰值 |
PWM 频率 | 1kHz~20kHz |
速度调节 | 0~100% 占空比 |
控制接口 | GPIO + PWM |
1.3 测试用例列表
| index | 名称 | 测试指令 | 预期现象(成功) | 失败可能原因 |
|---|---|---|---|---|
| 1 | 正转测试 | ./motor | 电机A正向旋转,TFT显示FORWARD状态 | GPIO配置错误、供电不足 |
| 2 | 反转测试 | 发送反转命令 | 电机A反向旋转,TFT显示BACKWARD状态 | 方向控制信号错误 |
| 3 | 调速测试 | 发送速度命令 | 电机转速变化,TFT显示速度百分比 | PWM配置错误 |
| 4 | 刹车测试 | 发送刹车命令 | 电机快速停止,TFT显示BRAKE状态 | 刹车逻辑错误 |
1.4 目录结构
source/app/11_motor/
├── Makefile # 构建脚本
├── main.c # 主程序
├── tb6612.c # TB6612 驱动实现
├── tb6612.h # TB6612 驱动头文件
├── pwm_hal.c # PWM HAL 层实现
├── pwm_hal.h # PWM HAL 层头文件
├── gpio_hal.c # GPIO HAL 层实现
├── gpio_hal.h # GPIO HAL 层头文件
├── spi_hal.c # SPI HAL 层实现
├── spi_hal.h # SPI HAL 层头文件
├── st7789.c # ST7789 驱动实现
├── st7789.h # ST7789 驱动头文件
├── font8x16.h # 8×16 ASCII 点阵字库
└── README.md # 说明文档2 硬件连接说明
2.1 引脚定义
| 信号 | 板上 GPIO | 说明 |
|---|---|---|
| AIN1 | GPIO5_2 | 电机 A 方向控制1 |
| AIN2 | GPIO5_3 | 电机 A 方向控制2 |
| PWMA | GPIO5_4 | 电机 A 速度控制(PWM) |
| BIN1 | GPIO5_5 | 电机 B 方向控制1 |
| BIN2 | GPIO5_6 | 电机 B 方向控制2 |
| PWMB | GPIO5_7 | 电机 B 速度控制(PWM) |
| VCC | 3.3V | 逻辑供电 |
| VM | 电池电压 | 电机供电(2.5V~13.5V) |
| GND | GND | 地线 |
2.2 硬件电路
TB6612 接线图:
Pico-G1 TB6612 Module
┌───────────┐ ┌──────────────┐
│ │ │ │
│ GPIO5_2 ──┼────── AIN1 ───┤ AIN1 │
│ │ │ │
│ GPIO5_3 ──┼────── AIN2 ───┤ AIN2 │
│ │ │ │
│ GPIO5_4 ──┼────── PWMA ───┤ PWMA │
│ │ │ │
│ GPIO5_5 ──┼────── BIN1 ───┤ BIN1 │
│ │ │ │
│ GPIO5_6 ──┼────── BIN2 ───┤ BIN2 │
│ │ │ │
│ GPIO5_7 ──┼────── PWMB ───┤ PWMB │
│ │ │ │
│ 3.3V ───┼─────────────┤ VCC │
│ 电池电压 ──┼─────────────┤ VM │
│ GND ───┼─────────────┤ GND │
└───────────┘ └──────────────┘
│ AO1 ───── 电机A+
│ AO2 ───── 电机A-
│ BO1 ───── 电机B+
│ BO2 ───── 电机B-2.3 控制逻辑
TB6612 控制真值表:
| AIN1 | AIN2 | PWMA | 电机状态 |
|---|---|---|---|
| 0 | 0 | X | 停止(高阻态) |
| 0 | 1 | PWM | 正转(CCW) |
| 1 | 0 | PWM | 反转(CW) |
| 1 | 1 | X | 刹车(低阻态) |
3 编译与部署
3.1 编译应用
export PATH=$PATH:<SDK>/tools/linux/toolchains/arm-gcc12.2.0-linux-uclibceabi/bin
cd <SDK>/source/app/11_motor
make3.2 运行应用
scp motor root@<板端IP>:/usr/bin/
ssh root@<板端IP} '/usr/bin/motor'3.3 预期输出
控制台输出
/mnt # ./motor
[motor] 初始化 SPI 屏...
[spi] pad 0x100C0028 -> 0x00001004
[spi] pad 0x100C002C -> 0x00001004
[spi] pad 0x100C0030 -> 0x00001000
[spi] pad 0x100C0020 -> 0x00001005
[spi] pad 0x100C001C -> 0x00001005
[spi] opening /dev/spidev2.0 ...
[spi] spidev opened, fd=3
[spi] spidev mode/bits/speed set (MODE3/8b/24MHz)
[spi] chardev request DC @ /dev/gpiochip4 line 5 ...
[spi] chardev request RES @ /dev/gpiochip4 line 4 ...
[spi] chardev request CS @ /dev/gpiochip5 line 1 ...
[spi] chardev-verify: DC=0(expect0) RES=1(expect1) CS=1(expect1) ==> OK(chardev 真驱动了引脚)
[spi] spi_hal_init done
[tft] init: SLPOUT
[tft] init: SLPOUT +120ms ok
[tft] init: config cmds ok
[tft] init: DISPON
[tft] init: DISPON ok
[tft] init: clear-flush start
[tft] flush #1 start
[tft] flush #1 done
[tft] init: clear-flush done
[motor] 初始化 TB6612(7 路 GPIO + 软件 PWM 线程)...
[tb6612] pad 0x100C003C -> 0x00001000
[tb6612] pad 0x100C007C -> 0x00001000
[tb6612] pad 0x100C0074 -> 0x00001000
[tb6612] pad 0x100C0034 -> 0x00001000
[tb6612] pad 0x100C0060 -> 0x00001000
[tb6612] pad 0x100C0064 -> 0x00001000
[tb6612] pad 0x100C0040 -> 0x00001000
[tb6612] PWMA ok (chip=/dev/gpiochip5 line=4)
[tb6612] AIN2 ok (chip=/dev/gpiochip7 line=4)
[tb6612] AIN1 ok (chip=/dev/gpiochip7 line=2)
[tb6612] STBY ok (chip=/dev/gpiochip5 line=2)
[tb6612] BIN1 ok (chip=/dev/gpiochip6 line=5)
[tb6612] BIN2 ok (chip=/dev/gpiochip6 line=6)
[tb6612] PWMB ok (chip=/dev/gpiochip5 line=5)
[tb6612] init ok, 两路 1kHz 软件 PWM 已启动
[tft] flush #2 start
[tft] flush #2 done
[motor] 就绪:方向=COAST 速度=0%。敲命令(回车确认):
> w 60
[motor] 方向=FWD 速度=0%
[tft] flush #3 start
[tft] flush #3 done
> 50
[motor] 方向=FWD 速度=50%TFT 屏幕显示

4 PWM 调制原理
4.1 PWM 基础概念
PWM(脉冲宽度调制)通过改变脉冲的占空比来控制平均电压:
- 占空比:高电平时间占整个周期的比例
- 频率:PWM 信号的周期性(建议 10kHz)
- 分辨率:PWM 可调节的精度等级
4.2 PWM 配置
int pwm_export(int pin)
{
char path[64];
snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip0/export");
FILE *fp = fopen(path, "w");
if (!fp) return -1;
fprintf(fp, "%d", pin);
fclose(fp);
return 0;
}
int pwm_set_config(int pin, int period_ns, int duty_ns)
{
char path[64];
// 设置周期
snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip0/pwm%d/period", pin);
FILE *fp = fopen(path, "w");
if (fp) {
fprintf(fp, "%d", period_ns);
fclose(fp);
}
// 设置占空比
snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip0/pwm%d/duty_cycle", pin);
fp = fopen(path, "w");
if (fp) {
fprintf(fp, "%d", duty_ns);
fclose(fp);
}
return 0;
}4.3 速度调节
占空比与速度关系:
void motor_set_speed(int motor_id, int speed_percent)
{
// 速度百分比转换为占空比
int period_ns = 100000; // 10kHz 周期
int duty_ns = period_ns * speed_percent / 100;
// 设置 PWM 占空比
if (motor_id == 0) {
pwm_set_config(GPIO5_4, period_ns, duty_ns); // 电机A
} else {
pwm_set_config(GPIO5_7, period_ns, duty_ns); // 电机B
}
}5 TB6612 控制详解
5.1 基础控制函数
// 电机正转
void motor_forward(int motor_id, int speed_percent)
{
if (motor_id == 0) {
gpio_set_value(GPIO5_2, 0); // AIN1 = 0
gpio_set_value(GPIO5_3, 1); // AIN2 = 1
motor_set_speed(motor_id, speed_percent);
}
}
// 电机反转
void motor_backward(int motor_id, int speed_percent)
{
if (motor_id == 0) {
gpio_set_value(GPIO5_2, 1); // AIN1 = 1
gpio_set_value(GPIO5_3, 0); // AIN2 = 0
motor_set_speed(motor_id, speed_percent);
}
}
// 电机停止
void motor_stop(int motor_id)
{
if (motor_id == 0) {
gpio_set_value(GPIO5_2, 0); // AIN1 = 0
gpio_set_value(GPIO5_3, 0); // AIN2 = 0
motor_set_speed(motor_id, 0);
}
}
// 电机刹车
void motor_brake(int motor_id)
{
if (motor_id == 0) {
gpio_set_value(GPIO5_2, 1); // AIN1 = 1
gpio_set_value(GPIO5_3, 1); // AIN2 = 1
motor_set_speed(motor_id, 0);
}
}5.2 双电机控制
typedef enum {
MOTOR_STOP = 0,
MOTOR_FORWARD,
MOTOR_BACKWARD,
MOTOR_BRAKE
} motor_direction_t;
void motor_control(int motor_id, motor_direction_t direction, int speed_percent)
{
switch (direction) {
case MOTOR_FORWARD:
motor_forward(motor_id, speed_percent);
break;
case MOTOR_BACKWARD:
motor_backward(motor_id, speed_percent);
break;
case MOTOR_STOP:
motor_stop(motor_id);
break;
case MOTOR_BRAKE:
motor_brake(motor_id);
break;
}
}6 关键编程要点
6.1 GPIO 初始化
int gpio_export(int pin)
{
char buffer[64];
snprintf(buffer, sizeof(buffer), "/sys/class/gpio/export");
FILE *fp = fopen(buffer, "w");
if (!fp) return -1;
fprintf(fp, "%d", pin);
fclose(fp);
return 0;
}
int gpio_set_direction(int pin, const char *direction)
{
char path[64];
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin);
FILE *fp = fopen(path, "w");
if (!fp) return -1;
fprintf(fp, "%s", direction);
fclose(fp);
return 0;
}6.2 PWM 初始化
int pwm_enable(int pin)
{
char path[64];
snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip0/pwm%d/enable", pin);
FILE *fp = fopen(path, "w");
if (!fp) return -1;
fprintf(fp, "1");
fclose(fp);
return 0;
}6.3 速度平滑调节
void motor_smooth_speed(int motor_id, int target_speed, int step_delay)
{
int current_speed = get_current_speed(motor_id);
while (current_speed != target_speed) {
if (current_speed < target_speed) {
current_speed++;
} else {
current_speed--;
}
motor_set_speed(motor_id, current_speed);
usleep(step_delay * 1000);
}
}7 常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 电机不转 | 控制信号错误、供电不足 | 检查 GPIO 配置、确认 VM 电压 |
| 只能单向 | 方向控制信号错误 | 检查 AIN1/AIN2 状态 |
| 速度无法调节 | PWM 信号异常 | 检查 PWM 配置和频率设置 |
| 电机抖动 | PWM 频率过低 | 提高 PWM 频率到 10kHz 以上 |
| 电机发热 | 长时间大负载运行 | 降低负载、增加散热 |
| 刹车效果差 | 刹车逻辑错误 | 检查 AIN1/AIN2 都为高电平 |
电机使用建议
- 电源选择:使用独立电池供电,避免从板端取电
- PWM 频率:建议使用 10kHz,兼顾噪声和响应速度
- 启动方式:使用平滑启动,避免电机冲击电流
- 散热设计:长时间运行注意电机和驱动芯片散热
8 进阶功能
8.1 加减速控制
void motor_ramp_speed(int motor_id, int start_speed, int end_speed, int ramp_time)
{
int steps = abs(end_speed - start_speed);
int delay = ramp_time * 1000 / steps;
for (int i = 0; i <= steps; i++) {
int speed = start_speed + (end_speed - start_speed) * i / steps;
motor_set_speed(motor_id, speed);
usleep(delay);
}
}8.2 位置控制
void motor_move_steps(int motor_id, int steps, int speed_percent)
{
int step_delay = 1000000 / (speed_percent * 10); // 简化计算
for (int i = 0; i < steps; i++) {
motor_forward(motor_id, speed_percent);
usleep(step_delay);
}
motor_stop(motor_id);
}8.3 电流保护
bool motor_check_current(int motor_id, float max_current)
{
float current = read_motor_current(motor_id);
if (current > max_current) {
printf("[Motor] 电流过载: %.2fA\n", current);
motor_brake(motor_id);
return false;
}
return true;
}