3.10. GPIO 输入¶
从 GPIO 引脚读取一个开关(或任何数字信号)同样要经过 machine.Pin,只不过这次配置为输入。其接口与输出对称:先配置模式,然后要么在主循环中轮询引脚的值,要么注册一个在值发生变化时触发的中断处理函数。
3.10.1. 配置输入¶
向构造函数传入 Pin.IN,并可选地传入一个上拉/下拉电阻:
from machine import Pin
button = Pin("P0", Pin.IN, Pin.PULL_UP)
这会把 P0 配置为一个启用了 MCU 内部上拉电阻的输入。当引脚上没有连接任何东西时,上拉会把它保持为高电平;从引脚到地闭合一个开关会把它拉为低电平。
3.10.2. 读取值¶
不带参数的 value() 返回当前状态——0 表示低电平,1 表示高电平:
if button.value() == 0:
print("button pressed")
else:
print("button released")
在主循环模式下,轮询看起来像这样:
while True:
if button.value() == 0:
do_action()
time.sleep_ms(50)
50 ms 的休眠可以防止循环以满 CPU 占用率运行。
3.10.3. 中断驱动的输入¶
轮询是可行的,但主循环的每一次迭代都会消耗 CPU 时间。对于很少变化的输入——每分钟按一次的按钮、一个报警信号——irq() 会注册一个仅在引脚变化时才运行的处理函数。
处理函数运行在中断上下文中,这限制了它能做的事情:
不能分配内存。创建新对象——列表、字符串、异常、格式化字符串——在 ISR 内部可能失败,因为堆被锁定。请在模块作用域预先分配处理函数所需的任何缓冲区。
不能执行长时间运行的工作。处理函数应当把工作交出去然后返回。在 ISR 内部花费实际时间会延误其他一切(其他中断、主循环、USB 流量)。
不能在紧凑的 ISR 中打印。
print()会分配内存、在 UART 上阻塞,是处理函数能做的最昂贵的事情之一。
标准的做法是让 ISR 通过 micropython.schedule() 调度真正的工作,它会把一个函数排入队列,在下一个安全点回到主上下文中运行:
import micropython
def handle_press(pin):
print("button pressed")
def on_press(pin):
micropython.schedule(handle_press, pin)
button.irq(handler=on_press, trigger=Pin.IRQ_FALLING)
ISR 只有一行:把回调入队然后返回。handle_press 随后在普通上下文中运行,那里内存分配、print() 和慢速 I/O 又都安全了。
trigger 参数选择由哪个边沿触发:
Pin.IRQ_FALLING—— 1 变 0。Pin.IRQ_RISING—— 0 变 1。Pin.IRQ_FALLING|Pin.IRQ_RISING—— 两个边沿。