11 - DC Motor Control Application
This chapter describes the DC motor control application example — motor — on the Pico-G1 expansion board. The application demonstrates how to drive a TB6612 motor driver module through GPIO and PWM interfaces to control the forward/reverse rotation, speed, and stop/brake states of a DC motor, and how to display the motor status on a TFT screen. It is a practical example for learning motor control and PWM speed regulation.
The application source code is located in the SDK directory source/app/11_motor/ and provides a complete TB6612 driver implementation.
1 Application Overview
1.1 Features
- Motor driver control: drives a TB6612 motor driver module through GPIO
- PWM speed regulation: supports PWM speed adjustment (duty cycle 0~100%)
- Forward/reverse control: supports forward, reverse, stop, and brake
- Real-time status display: shows the motor direction, speed, and running state on the TFT screen
- Multiple running modes: supports continuous, timed, and pulsed operation
1.2 Technical Specifications
| Parameter | Value |
|---|---|
Driver chip | TB6612FNG |
Motor voltage | 2.5V~13.5V |
Logic voltage | 2.7V~5.5V |
Max current | 1.2A continuous / 3.2A peak |
PWM frequency | 1 kHz~20 kHz |
Speed regulation | 0~100% duty cycle |
Control interface | GPIO + PWM |
1.3 Test Case List
| index | Name | Test command | Expected result (success) | Possible causes of failure |
|---|---|---|---|---|
| 1 | Forward test | ./motor | Motor A rotates forward, TFT shows FORWARD | Wrong GPIO config, insufficient supply |
| 2 | Reverse test | Send a reverse command | Motor A rotates backward, TFT shows BACKWARD | Wrong direction control signal |
| 3 | Speed test | Send a speed command | Motor speed changes, TFT shows the speed percentage | Wrong PWM config |
| 4 | Brake test | Send a brake command | Motor stops quickly, TFT shows BRAKE | Wrong brake logic |
1.4 Directory Structure
source/app/11_motor/
├── Makefile # Build script
├── main.c # Main program
├── tb6612.c # TB6612 driver implementation
├── tb6612.h # TB6612 driver header
├── pwm_hal.c # PWM HAL layer implementation
├── pwm_hal.h # PWM HAL layer header
├── gpio_hal.c # GPIO HAL layer implementation
├── gpio_hal.h # GPIO HAL layer header
├── spi_hal.c # SPI HAL layer implementation
├── spi_hal.h # SPI HAL layer header
├── st7789.c # ST7789 driver implementation
├── st7789.h # ST7789 driver header
├── font8x16.h # 8×16 ASCII bitmap font
└── README.md # Documentation2 Hardware Connection
2.1 Pin Definitions
| Signal | On-board GPIO | Description |
|---|---|---|
| AIN1 | GPIO5_2 | Motor A direction control 1 |
| AIN2 | GPIO5_3 | Motor A direction control 2 |
| PWMA | GPIO5_4 | Motor A speed control (PWM) |
| BIN1 | GPIO5_5 | Motor B direction control 1 |
| BIN2 | GPIO5_6 | Motor B direction control 2 |
| PWMB | GPIO5_7 | Motor B speed control (PWM) |
| VCC | 3.3V | Logic supply |
| VM | Battery voltage | Motor supply (2.5V~13.5V) |
| GND | GND | Ground |
2.2 Hardware Circuit
TB6612 wiring diagram:
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 │
│ Battery voltage ──┼─────────────┤ VM │
│ GND ───┼─────────────┤ GND │
└───────────┘ └──────────────┘
│ AO1 ───── Motor A+
│ AO2 ───── Motor A-
│ BO1 ───── Motor B+
│ BO2 ───── Motor B-2.3 Control Logic
TB6612 control truth table:
| AIN1 | AIN2 | PWMA | Motor state |
|---|---|---|---|
| 0 | 0 | X | Stop (high impedance) |
| 0 | 1 | PWM | Forward (CCW) |
| 1 | 0 | PWM | Reverse (CW) |
| 1 | 1 | X | Brake (low impedance) |
3 Build and Deployment
3.1 Build the Application
export PATH=$PATH:<SDK>/tools/linux/toolchains/arm-gcc12.2.0-linux-uclibceabi/bin
cd <SDK>/source/app/11_motor
make3.2 Run the Application
scp motor root@<board_ip>:/usr/bin/
ssh root@<board_ip> '/usr/bin/motor'3.3 Expected Output
Console output
/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 screen display

4 PWM Modulation Principles
4.1 PWM Basics
PWM (pulse-width modulation) controls the average voltage by changing the duty cycle of the pulses:
- Duty cycle: the proportion of the high-level time within a full period
- Frequency: the periodicity of the PWM signal (10 kHz recommended)
- Resolution: the number of adjustable steps of the PWM
4.2 PWM Configuration
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];
// Set the period
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);
}
// Set the duty cycle
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 Speed Regulation
Duty cycle vs. speed:
void motor_set_speed(int motor_id, int speed_percent)
{
// Convert the speed percentage to a duty cycle
int period_ns = 100000; // 10 kHz period
int duty_ns = period_ns * speed_percent / 100;
// Set the PWM duty cycle
if (motor_id == 0) {
pwm_set_config(GPIO5_4, period_ns, duty_ns); // Motor A
} else {
pwm_set_config(GPIO5_7, period_ns, duty_ns); // Motor B
}
}5 TB6612 Control in Detail
5.1 Basic Control Functions
// Motor forward
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);
}
}
// Motor reverse
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);
}
}
// Motor stop
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);
}
}
// Motor brake
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 Dual-Motor Control
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 Key Programming Points
6.1 GPIO Initialization
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 Initialization
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 Smooth Speed Adjustment
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 Troubleshooting
| Problem | Possible cause | Solution |
|---|---|---|
| Motor does not turn | Wrong control signal, insufficient supply | Check the GPIO config, verify the VM voltage |
| Only one direction | Wrong direction control signal | Check the AIN1/AIN2 states |
| Speed not adjustable | Abnormal PWM signal | Check the PWM configuration and frequency |
| Motor jitter | PWM frequency too low | Raise the PWM frequency above 10 kHz |
| Motor overheating | Prolonged heavy-load operation | Reduce the load, improve cooling |
| Weak braking | Wrong brake logic | Check that both AIN1/AIN2 are high |
Motor usage tips
- Power choice: use a separate battery supply; avoid powering the motor from the board
- PWM frequency: 10 kHz is recommended, balancing noise and response speed
- Start-up: use a smooth (ramped) start to avoid inrush current
- Thermal design: watch the motor and driver chip temperature during long runs
8 Advanced Features
8.1 Acceleration/Deceleration Control
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 Position Control
void motor_move_steps(int motor_id, int steps, int speed_percent)
{
int step_delay = 1000000 / (speed_percent * 10); // Simplified calculation
for (int i = 0; i < steps; i++) {
motor_forward(motor_id, speed_percent);
usleep(step_delay);
}
motor_stop(motor_id);
}8.3 Current Protection
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;
}