一 本章简介
本章介绍 MicroPython 中 GPIO(通用输入输出)的基础操作,包括数字输出、数字输入、开漏模式,以及如何查询引脚的复用功能。这是后续所有外设驱动的基础。
1.1 学习目标
| 序号 | 学习目标 | 重要程度 |
|---|---|---|
| 1 | 掌握 pyb.Pin 和 machine.Pin 的基本用法 | ⭐⭐⭐⭐⭐ |
| 2 | 能够配置引脚为数字输出并控制高低电平 | ⭐⭐⭐⭐⭐ |
| 3 | 能够配置引脚为数字输入并读取电平状态 | ⭐⭐⭐⭐⭐ |
| 4 | 了解推挽输出与开漏输出的区别和应用场景 | ⭐⭐⭐⭐ |
| 5 | 掌握 af_list() 查询引脚复用功能的方法 | ⭐⭐⭐ |
1.2 重点提示
- STM32F407 的 GPIO 挂载在 AHB1 总线上,使用前必须使能对应端口的时钟。MicroPython 会在你创建
Pin对象时自动使能时钟,无需手动操作。 - 天空星引出了 PA0-PA15、PB0-PB15、PC0-PC15、PD0-PD15、PE0-PE15 共 80 个 GPIO 引脚,但其中部分引脚已被板载外设占用,使用前请确认引脚是否空闲。
- STM32F407 的 GPIO 输出电流建议控制在 5~8mA 以下,单个 IO 绝对最大值为 25mA,但长期工作在极限状态会影响稳定性。
pyb.Pin和machine.Pin都可以控制 GPIO,pyb.Pin是 STM32 专用 API,功能更丰富;machine.Pin是跨平台 API,代码可移植性更好。
1.3 基础概念与术语
- GPIO(General-Purpose Input/Output,通用输入输出口):可以由软件配置为输入或输出的数字引脚,是单片机与外部世界交互最基本的方式。
- 推挽输出(Push-Pull,PP):输出高电平时内部 PMOS 导通(推),输出低电平时内部 NMOS 导通(拉),能主动输出高低两种电平,驱动能力强。
- 开漏输出(Open-Drain,OD):内部只有 NMOS,只能主动拉低,输出高电平时需要外部上拉电阻。常用于 I2C 总线或多设备共享信号线的场景。
- 上拉(Pull-Up):将引脚通过电阻连接到 VCC,使引脚在悬空时保持高电平。
- 下拉(Pull-Down):将引脚通过电阻连接到 GND,使引脚在悬空时保持低电平。
- 复用功能(Alternate Function,AF):GPIO 引脚除了普通输入输出外,还可以被内部硬件外设(UART、SPI、I2C、TIM 等)接管,实现特定的硬件功能。
二 GPIO 工作模式
STM32F407 的每个 GPIO 引脚支持以下几种工作模式:
| 模式 | MicroPython 常量 | 说明 |
|---|---|---|
| 输入 | Pin.IN | 高阻抗数字输入,读取外部电平 |
| 推挽输出 | Pin.OUT_PP | 能主动输出高低电平,驱动能力强 |
| 开漏输出 | Pin.OUT_OD | 只能主动拉低,高电平需外部上拉 |
| 复用推挽 | Pin.AF_PP | 引脚交给硬件外设控制,推挽模式 |
| 复用开漏 | Pin.AF_OD | 引脚交给硬件外设控制,开漏模式(如硬件 I2C) |
| 模拟 | Pin.ANALOG | 用于 ADC/DAC,禁用数字缓冲区 |
上拉/下拉选项:
| 选项 | MicroPython 常量 | 说明 |
|---|---|---|
| 无上下拉 | Pin.PULL_NONE | 引脚浮空,适合外部已有上下拉的场景 |
| 内部上拉 | Pin.PULL_UP | 激活内部上拉电阻(约 30~50kΩ) |
| 内部下拉 | Pin.PULL_DOWN | 激活内部下拉电阻(约 30~50kΩ) |
三 数字输出
3.1 基本用法
from pyb import Pin
# 配置 PB2 为推挽输出模式
p = Pin('PB2', Pin.OUT_PP)
# 输出高电平
p.high()
# 等效写法:
p.value(1)
# 输出低电平
p.low()
# 等效写法:
p.value(0)
# 翻转电平
p.high()
p.low()2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
也可以使用跨平台的 machine.Pin:
from machine import Pin
# 配置 PB2 为输出,初始电平为低
p = Pin('BP2', Pin.OUT, value=0)
p.on() # 输出高电平
p.off() # 输出低电平
p.toggle() # 翻转2
3
4
5
6
7
8
3.2 读取输出状态
from pyb import Pin
p = Pin('PA0', Pin.OUT_PP)
p.high()
# 读取当前输出状态
print(p.value()) # 输出 12
3
4
5
6
7
3.3 批量初始化多个输出引脚
from pyb import Pin
# 批量配置多个引脚为输出
output_pins = [Pin(name, Pin.OUT_PP) for name in ('PE0', 'PE1', 'PE2', 'PE3')]
# 全部拉高
for p in output_pins:
p.high()
# 全部拉低
for p in output_pins:
p.low()2
3
4
5
6
7
8
9
10
11
12
四 数字输入
4.1 基本用法
from pyb import Pin
# 配置 PE1 为输入,内部上拉
p = Pin('PE1', Pin.IN, Pin.PULL_UP)
# 读取引脚电平(返回 0 或 1)
print(p.value())
# 配置为下拉输入
p2 = Pin('PE2', Pin.IN, Pin.PULL_DOWN)
print(p2.value())
# 浮空输入(外部已有上下拉电阻时使用)
p3 = Pin('PE3', Pin.IN, Pin.PULL_NONE)
print(p3.value())2
3
4
5
6
7
8
9
10
11
12
13
14
15
4.2 轮询检测电平变化
from pyb import Pin, delay
# 配置 PA0 为下拉输入(天空星用户按键)
btn = Pin('PA0', Pin.IN, Pin.PULL_DOWN)
print("等待引脚变高...")
while True:
if btn.value() == 1:
print("检测到高电平!")
break
delay(10)2
3
4
5
6
7
8
9
10
11
4.3 外部中断(IRQ)
轮询方式需要 CPU 不断检查引脚状态,效率较低。对于按键等事件,更好的方式是使用外部中断:
from pyb import Pin, ExtInt
# 定义中断回调函数
# 注意:中断回调函数必须简短快速,不能有耗时操作
flag = False
def pin_handler(line):
global flag
flag = True # 只设置标志位,在主循环中处理
# 配置 PA0 上升沿触发中断,内部下拉
ext = ExtInt('PA0', ExtInt.IRQ_RISING, Pin.PULL_DOWN, pin_handler)
# 主循环中检查标志位
while True:
if flag:
flag = False
print("检测到上升沿!")2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
WARNING
中断回调函数在中断上下文中执行,有严格限制:
- 禁止:
print()、time.sleep()、内存分配(创建列表/字典/字符串)、浮点运算 - 允许:修改全局变量、置标志位、直接操作寄存器
违反这些规则可能导致系统崩溃或行为异常。
五 开漏输出
开漏输出常用于以下场景:
- I2C 总线:SCL 和 SDA 都是开漏模式,允许多个设备共享总线
- 线与逻辑:多个设备共享一根信号线,任意一个拉低则整体为低
- 电平转换:配合不同电压的上拉电阻,实现 3.3V 与 5V 之间的电平转换
from pyb import Pin
# 配置 PE3 为开漏输出,内部上拉
p = Pin('PE3', Pin.OUT_OD, Pin.PULL_UP)
p.low() # 主动拉低(NMOS 导通,引脚接 GND)
p.high() # 释放(NMOS 截止,由上拉电阻将引脚拉高)2
3
4
5
6
7
NOTE
开漏输出调用 p.high() 并不是主动输出高电平,而是让引脚"悬空",由外部上拉电阻决定最终电平。如果没有上拉电阻,引脚会处于浮空状态,电平不确定。
六 查询引脚复用功能
STM32 的每个引脚都可以复用为多种外设功能。在 MicroPython 中,可以直接在 REPL 里查询任意引脚支持哪些复用功能,不用翻数据手册。
6.1 查询单个引脚
from pyb import Pin
# 查询 PB8 支持的所有复用功能
p = Pin('PB8')
print(p.af_list())
# 输出类似: [Pin.AF2_TIM4, Pin.AF3_TIM10, Pin.AF4_I2C1]2
3
4
5
6
6.2 筛选特定外设
from pyb import Pin
# 找出 PA1 能用哪些定时器
p = Pin('PA1')
for af in p.af_list():
if 'TIM' in str(af):
print(af)
# 输出:
# Pin.AF1_TIM2
# Pin.AF2_TIM5
# 说明 PA1 可以复用为 TIM2 或 TIM5 的通道2
3
4
5
6
7
8
9
10
11
6.3 批量扫描所有引脚的复用功能
from pyb import Pin
ports = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
print("{:<6} {}".format("GPIO", "AF list"))
print("-" * 80)
for port in ports:
for num in range(16):
name = 'P{}{}'.format(port, num)
try:
p = Pin(name)
afs = p.af_list()
if afs:
af_str = ", ".join(str(af) for af in afs)
else:
af_str = "-"
print("{:<6} {}".format(name, af_str))
except (ValueError, OSError, TypeError):
pass2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
6.4 查看引脚当前状态
from pyb import Pin
p = Pin('PB2')
print(p) # 打印引脚当前模式、速度、上下拉等完整信息
print(p.name()) # 'PB2'
print(p.pin()) # 2(引脚编号)
print(p.port()) # 1(端口编号,0=A, 1=B, 2=C...)2
3
4
5
6
7
TIP
af_list() 返回的是该引脚在芯片硬件层面支持的所有复用功能。但并不是所有功能都在固件中启用了——比如天空星只配置了 SPI2,即使某个引脚的 af_list() 里有 SPI1,也需要先在板级配置中启用 SPI1 才能使用。
七 GPIO 速度设置(进阶)
STM32F407 允许为每个 GPIO 引脚设置输出速度(边沿翻转速率),影响信号的上升/下降时间和 EMI 辐射。
| 速度等级 | 最高频率 | 适用场景 |
|---|---|---|
| 低速(DRIVE_0) | 2MHz | 普通 IO,EMI 最小 |
| 中速(DRIVE_1) | 25MHz | 一般通信信号 |
| 快速(DRIVE_2) | 50MHz | 较高速 SPI、I2C |
| 高速(DRIVE_3) | 100MHz | 高速 SPI(>40MHz)、USB |
from pyb import Pin
import stm
# 通过 stm 模块直接操作寄存器设置 GPIO 速度
# OSPEEDR 寄存器:00=低速 01=中速 10=快速 11=高速
# 设置 PE0 为高速输出
p = Pin('PE0', Pin.OUT_PP)
# PE0 对应 OSPEEDR 的 bit[1:0]
stm.mem32[stm.GPIOE + stm.GPIO_OSPEEDR] |= (3 << 0) # 11 = 高速2
3
4
5
6
7
8
9
NOTE
在大多数应用场景下,不需要手动设置 GPIO 速度,MicroPython 默认配置已经足够。只有在驱动高速 SPI 或需要精确控制信号边沿时,才需要关注这个参数。
八 工程实践要点
8.1 驱动能力限制
STM32F407 的 GPIO 输出电流建议在 5~8mA 以下使用。虽然数据手册标注单个 IO 最大可以输出 25mA,但这是绝对极限值,长期工作在极限状态会:
- 导致芯片发热,影响稳定性
- 可能损坏 IO 口
- 影响信号质量
工程建议:
- 驱动 LED 时串联限流电阻,控制电流在 5mA 左右
- 驱动大电流负载(电机、继电器)必须使用驱动芯片
- 多个 IO 同时输出时,总电流不超过 120mA
8.2 5V 容忍引脚
STM32F407 的大部分 GPIO 引脚支持 5V 容忍输入(FT,Five-volt Tolerant),即输入端可以接受最高 5V 的信号,不会损坏芯片。但输出仍然是 3.3V 电平。
CAUTION
并非所有引脚都支持 5V 容忍!在连接 5V 外设之前,请查阅 STM32F407 数据手册确认引脚是否标注了 "FT"。将 5V 信号接入非 FT 引脚会损坏芯片。
8.3 避免引脚冲突
天空星核心板上部分引脚已被板载外设占用(如 PB2 连接 LED、PA0 连接按键、PB6/PB7 连接 I2C 等)。在使用这些引脚做其他用途时,需要注意可能的冲突。
九 本节参考文档
- STM32F407 数据手册(含 GPIO 电气特性):
- STM32F4 参考手册 RM0090(GPIO 章节与寄存器):
- MicroPython pyb.Pin 文档:
- MicroPython machine.Pin 文档: