一 本章简介
本章介绍如何使用 MicroPython 的 PWM 功能驱动筑基学习板上的两路直流电机。筑基学习板板载了 2 颗 AT8236 H 桥电机驱动芯片,每路最大输出电流约 2.2A,可以驱动常见的小型直流减速电机,实现正转、反转、调速和刹车等功能。
CAUTION
直流电机驱动需要外部直流电源供电(8V~24V),单纯靠 TYPE-C 供来的 5V 是无法驱动电机的。请根据你所使用的直流电机的额定电压,通过筑基学习板的 DC 头或接线端子接入对应电压的直流电源。例如,使用 12V 额定电压的电机,就需要接入 12V 电源适配器。
1.1 学习目标
| 序号 | 学习目标 | 重要程度 |
|---|---|---|
| 1 | 了解 H 桥电机驱动的工作原理 | ⭐⭐⭐⭐⭐ |
| 2 | 掌握使用 PWM 控制直流电机正转、反转和调速的方法 | ⭐⭐⭐⭐⭐ |
| 3 | 理解两路电机的引脚分配和拨码开关配置 | ⭐⭐⭐⭐⭐ |
| 4 | 能够实现加减速、刹车等实用功能 | ⭐⭐⭐⭐ |
1.2 重点提示
- 必须外接直流电源。电机驱动的供电直接来自 DC 输入(8V~24V),TYPE-C 的 5V 只能给单片机和传感器供电,无法驱动电机。不接外部电源时,电机不会转动。
- 电机 1 使用 TIM9(PE5/PE6),不受拨码开关影响,可以直接使用。
- 电机 2 使用 TIM12(PB14/PB15),与舵机接口共用引脚。使用电机 2 时,拨码开关 BIT5 必须拨到 ON 位置(向上),否则 PWM 信号会被路由到舵机接口。
- AT8236 的控制逻辑:一路 PWM + 一路低电平 = 正转;一路低电平 + 一路 PWM = 反转;两路都低 = 停止(滑行);两路都高 = 刹车。
- 每路驱动芯片内置过流保护,保护电流约 2.2A。如果电机堵转时间过长,驱动芯片会发热,请注意散热。
1.3 基础概念与术语
- H 桥(H-Bridge):由 4 个开关元件(MOS 管)组成的电路拓扑,形状像字母 H。通过控制不同开关的通断组合,可以改变流过电机的电流方向,从而实现正转和反转。
- PWM 调速:通过改变 PWM 占空比来控制电机的等效电压,占空比越高,电机转速越快。
- 刹车(Brake):H 桥两侧同时导通高电平,电机两端短路,产生制动力矩,电机快速停止。
- 滑行(Coast):H 桥两侧同时断开(低电平),电机靠惯性自由减速停止。
二 硬件说明
在这个接口可以接两个直流电机

2.1 电机驱动原理
AT8236 是一颗集成 H 桥的直流电机驱动芯片,每颗芯片有两个输入引脚(IN1、IN2)控制一路电机:

| IN1 | IN2 | 电机状态 |
|---|---|---|
| PWM | 低电平 | 正转(速度由 PWM 占空比决定) |
| 低电平 | PWM | 反转(速度由 PWM 占空比决定) |
| 低电平 | 低电平 | 停止(滑行) |
| 高电平 | 高电平 | 刹车 |
2.2 电机引脚分配
| 参数 | 电机 1 | 电机 2 |
|---|---|---|
| IN1 引脚 | PE5(TIM9_CH1) | PB14(TIM12_CH1) |
| IN2 引脚 | PE6(TIM9_CH2) | PB15(TIM12_CH2) |
| 定时器 | TIM9 | TIM12 |
| 拨码开关 | 无需设置 | BIT5 = ON(向上) |
| 驱动芯片 | AT8236(U54) | AT8236(U55) |
| 最大电流 | ~2.2A | ~2.2A |
| 对外接口 | 3.81mm 可插拔接口 | 3.81mm 可插拔接口 |
IMPORTANT
使用电机 2 时,拨码开关 BIT5 必须拨到 ON 位置(向上)。BIT5 默认是 OFF(舵机接口),如果不切换,PB14/PB15 的 PWM 信号会被路由到舵机接口,电机 2 不会转动。
电机 1(PE5/PE6)不受任何拨码开关影响,可以直接使用。
2.3 供电说明
CAUTION
电机驱动必须通过 DC 头或接线端子接入外部直流电源(8V~24V)。
电机驱动芯片的供电直接来自 DC 输入,不经过 5V 降压。TYPE-C 供电只能给单片机和板载传感器使用,无法驱动电机。
请根据你的电机额定电压选择合适的电源:
- 12V 电机 → 使用 12V 电源适配器
- 24V 电机 → 使用 24V 电源适配器
供电后,将筑基学习板的电源拨动开关拨到对应位置(DC 头向下,接线端子向上)。
三 软件设计
3.1 基础部分
3.1.1 电机 1 正转与反转
# 电机 1 基本控制(PE5/PE6,TIM9)
from pyb import Pin, Timer
import time
# PE5 -> TIM9_CH1 (IN1)
# PE6 -> TIM9_CH2 (IN2)
pin_in1 = Pin('PE5', Pin.AF_PP, af=Pin.AF3_TIM9)
pin_in2 = Pin('PE6', Pin.AF_PP, af=Pin.AF3_TIM9)
tim = Timer(9, freq=20000) # 20kHz PWM,超出人耳范围,电机运行安静
ch1 = tim.channel(1, Timer.PWM, pin=pin_in1)
ch2 = tim.channel(2, Timer.PWM, pin=pin_in2)
# 停止
ch1.pulse_width_percent(0)
ch2.pulse_width_percent(0)
# 正转 50% 速度
print("电机 1 正转")
ch1.pulse_width_percent(50)
ch2.pulse_width_percent(0)
time.sleep(2)
# 停止(滑行)
print("停止")
ch1.pulse_width_percent(0)
ch2.pulse_width_percent(0)
time.sleep(1)
# 反转 50% 速度
print("电机 1 反转")
ch1.pulse_width_percent(0)
ch2.pulse_width_percent(50)
time.sleep(2)
# 刹车
print("刹车")
ch1.pulse_width_percent(100)
ch2.pulse_width_percent(100)
time.sleep(1)
# 停止
ch1.pulse_width_percent(0)
ch2.pulse_width_percent(0)
print("完成")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
3.1.2 电机 2 正转与反转
WARNING
运行此代码前,请确认:
- 拨码开关 BIT5 已拨到 ON(向上)
- 已通过 DC 头或接线端子接入外部直流电源
# 电机 2 基本控制(PB14/PB15,TIM12)
# 注意:BIT5 必须拨到 ON!
from pyb import Pin, Timer
import time
# PB14 -> TIM12_CH1 (IN1)
# PB15 -> TIM12_CH2 (IN2)
pin_in1 = Pin('PB14', Pin.AF_PP, af=Pin.AF9_TIM12)
pin_in2 = Pin('PB15', Pin.AF_PP, af=Pin.AF9_TIM12)
tim = Timer(12, freq=20000)
ch1 = tim.channel(1, Timer.PWM, pin=pin_in1)
ch2 = tim.channel(2, Timer.PWM, pin=pin_in2)
# 停止
ch1.pulse_width_percent(0)
ch2.pulse_width_percent(0)
# 正转
print("电机 2 正转")
ch1.pulse_width_percent(50)
ch2.pulse_width_percent(0)
time.sleep(2)
# 反转
print("电机 2 反转")
ch1.pulse_width_percent(0)
ch2.pulse_width_percent(50)
time.sleep(2)
# 停止
ch1.pulse_width_percent(0)
ch2.pulse_width_percent(0)
print("完成")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
3.1.3 加速与减速
# 电机 1 加速与减速
from pyb import Pin, Timer
import time
pin_in1 = Pin('PE5', Pin.AF_PP, af=Pin.AF3_TIM9)
pin_in2 = Pin('PE6', Pin.AF_PP, af=Pin.AF3_TIM9)
tim = Timer(9, freq=20000)
ch1 = tim.channel(1, Timer.PWM, pin=pin_in1)
ch2 = tim.channel(2, Timer.PWM, pin=pin_in2)
ch2.pulse_width_percent(0) # IN2 保持低电平,正转方向
print("加速...")
for speed in range(0, 101, 5):
ch1.pulse_width_percent(speed)
print(" 速度: {}%".format(speed))
time.sleep_ms(200)
print("减速...")
for speed in range(100, -1, -5):
ch1.pulse_width_percent(speed)
print(" 速度: {}%".format(speed))
time.sleep_ms(200)
ch1.pulse_width_percent(0)
print("完成")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
3.2 进阶部分
3.2.1 电机驱动类封装
# 直流电机驱动类
from pyb import Pin, Timer
import time
class DCMotor:
"""
直流电机驱动类(AT8236 H桥)
"""
def __init__(self, pin1_name, pin2_name, tim_id, ch1_id, ch2_id, af, freq=20000):
self.pin1 = Pin(pin1_name, Pin.AF_PP, af=af)
self.pin2 = Pin(pin2_name, Pin.AF_PP, af=af)
self.tim = Timer(tim_id, freq=freq)
self.ch1 = self.tim.channel(ch1_id, Timer.PWM, pin=self.pin1)
self.ch2 = self.tim.channel(ch2_id, Timer.PWM, pin=self.pin2)
self.stop()
def forward(self, speed=50):
"""正转,speed: 0~100"""
speed = min(100, max(0, speed))
self.ch1.pulse_width_percent(speed)
self.ch2.pulse_width_percent(0)
def backward(self, speed=50):
"""反转,speed: 0~100"""
speed = min(100, max(0, speed))
self.ch1.pulse_width_percent(0)
self.ch2.pulse_width_percent(speed)
def stop(self):
"""停止(滑行)"""
self.ch1.pulse_width_percent(0)
self.ch2.pulse_width_percent(0)
def brake(self):
"""刹车"""
self.ch1.pulse_width_percent(100)
self.ch2.pulse_width_percent(100)
def ramp(self, target_speed, step=5, delay_ms=100):
"""
平滑加减速到目标速度
target_speed: 正数=正转,负数=反转,0=停止
"""
# 简化实现:先停止再加速到目标
self.stop()
time.sleep_ms(50)
if target_speed > 0:
for s in range(0, target_speed + 1, step):
self.forward(s)
time.sleep_ms(delay_ms)
elif target_speed < 0:
for s in range(0, abs(target_speed) + 1, step):
self.backward(s)
time.sleep_ms(delay_ms)
# 电机 1:PE5/PE6, TIM9, AF3
motor1 = DCMotor('PE5', 'PE6', 9, 1, 2, Pin.AF3_TIM9)
# 电机 2:PB14/PB15, TIM12, AF9(需要 BIT5=ON)
# motor2 = DCMotor('PB14', 'PB15', 12, 1, 2, Pin.AF9_TIM12)
# 使用示例
motor1.forward(60)
time.sleep(2)
motor1.backward(40)
time.sleep(2)
motor1.brake()
time.sleep(1)
motor1.stop()
print("完成")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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
3.2.2 双电机同时控制
注意!下面所说的正反转其实也是相对的,你可以在给直流电机接线的时候接切换一下电机的正负供电。
# 双电机同时控制
# 注意:BIT5 必须拨到 ON!
from pyb import Pin, Timer
import time
class DCMotor:
def __init__(self, pin1_name, pin2_name, tim_id, ch1_id, ch2_id, af, freq=20000):
self.pin1 = Pin(pin1_name, Pin.AF_PP, af=af)
self.pin2 = Pin(pin2_name, Pin.AF_PP, af=af)
self.tim = Timer(tim_id, freq=freq)
self.ch1 = self.tim.channel(ch1_id, Timer.PWM, pin=self.pin1)
self.ch2 = self.tim.channel(ch2_id, Timer.PWM, pin=self.pin2)
self.stop()
def forward(self, speed=50):
speed = min(100, max(0, speed))
self.ch1.pulse_width_percent(speed)
self.ch2.pulse_width_percent(0)
def backward(self, speed=50):
speed = min(100, max(0, speed))
self.ch1.pulse_width_percent(0)
self.ch2.pulse_width_percent(speed)
def stop(self):
self.ch1.pulse_width_percent(0)
self.ch2.pulse_width_percent(0)
def brake(self):
self.ch1.pulse_width_percent(100)
self.ch2.pulse_width_percent(100)
motor1 = DCMotor('PE5', 'PE6', 9, 1, 2, Pin.AF3_TIM9)
motor2 = DCMotor('PB14', 'PB15', 12, 1, 2, Pin.AF9_TIM12)
print("双电机演示")
print("注意:BIT5 必须拨到 ON!")
try:
# 两个电机同时正转
print("同时正转")
motor1.forward(60)
motor2.forward(60)
time.sleep(2)
# 两个电机同时反转
print("同时反转")
motor1.backward(60)
motor2.backward(60)
time.sleep(2)
# 差速转弯(左转:电机1慢,电机2快)
print("差速左转")
motor1.forward(30)
motor2.forward(70)
time.sleep(2)
# 原地旋转(两个电机方向相反)
print("原地旋转")
motor1.forward(50)
motor2.backward(50)
time.sleep(2)
except KeyboardInterrupt:
pass
finally:
motor1.stop()
motor2.stop()
print("已停止")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
60
61
62
63
64
65
66
67
68
69
3.2.3 按键控制电机
# 按键控制电机(PA0=正转,PE8=反转,松开=停止)
from pyb import Pin, Timer
import pyb
pin_in1 = Pin('PE5', Pin.AF_PP, af=Pin.AF3_TIM9)
pin_in2 = Pin('PE6', Pin.AF_PP, af=Pin.AF3_TIM9)
tim = Timer(9, freq=20000)
ch1 = tim.channel(1, Timer.PWM, pin=pin_in1)
ch2 = tim.channel(2, Timer.PWM, pin=pin_in2)
btn_fwd = Pin('PA0', Pin.IN, Pin.PULL_DOWN)
btn_rev = Pin('PE8', Pin.IN, Pin.PULL_DOWN)
SPEED = 60 # 运行速度
print("PA0=正转 PE8=反转 松开=停止")
while True:
if btn_fwd.value() == 1:
ch1.pulse_width_percent(SPEED)
ch2.pulse_width_percent(0)
elif btn_rev.value() == 1:
ch1.pulse_width_percent(0)
ch2.pulse_width_percent(SPEED)
else:
ch1.pulse_width_percent(0)
ch2.pulse_width_percent(0)
pyb.delay(20)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
四 常见问题
Q: 电机完全不转?
- 检查供电:这是最常见的原因。电机驱动需要通过 DC 头或接线端子接入 8V~24V 外部直流电源,TYPE-C 的 5V 无法驱动电机。
- 如果是电机 2,检查拨码开关 BIT5 是否拨到了 ON(向上)。
- 确认电机已正确插入 3.81mm 可插拔接口。
- 在 REPL 中手动测试:给 IN1 100% 占空比,IN2 0%,看电机是否转动。
Q: 电机只能朝一个方向转?
检查代码中 IN1 和 IN2 的占空比设置是否正确。正转是 IN1=PWM + IN2=0,反转是 IN1=0 + IN2=PWM。如果两路都给了 PWM,电机会处于刹车状态。
Q: 电机转动时有很大的噪音?
PWM 频率太低会导致电机发出可听见的啸叫声。建议将 PWM 频率设置为 20kHz(超出人耳范围),代码中 Timer(9, freq=20000) 已经是 20kHz。
Q: 电机堵转后驱动芯片很烫?
AT8236 内置过流保护(约 2.2A),但长时间堵转仍会导致芯片发热。请避免长时间堵转,必要时加装散热片。
Q: 可以同时使用电机 2 和舵机吗?
不可以。电机 2 和舵机接口共用 PB14/PB15,通过 BIT5 二选一。电机 1(PE5/PE6)不受影响。
五 本节参考文档
- MicroPython pyb.Timer 文档:
- MicroPython pyb.Pin 文档: