1. 从“轮询”到“中断”:为什么你的按键响应总是不灵敏?
很多刚开始玩 MicroPython 开发板(比如 Raspberry Pi Pico 或者 ESP32)的朋友,在尝试用按键控制 LED 时,大概率会写出下面这样的代码:
from machine import Pin
import time
button = Pin(2, Pin.IN, Pin.PULL_UP)
led = Pin(25, Pin.OUT)
while True:
if button.value() == 0: # 检测按键是否被按下(低电平)
led.value(1) # 点亮 LED
else:
led.value(0) # 熄灭 LED
time.sleep(0.01) # 稍微延时一下,避免 CPU 跑满
这段代码看起来逻辑清晰,运行起来似乎也没问题——按下按键,灯亮;松开按键,灯灭。但如果你真的用它来做项目,很快就会遇到一个头疼的情况:你的单片机好像“变笨”了。当它全神贯注地在那个 while True 循环里一遍遍检查按键状态时,它就很难再分心去做其他事情,比如同时读取传感器数据、刷新屏幕,或者处理网络请求。这种不断主动询问“按键按下了吗?”的方式,在编程里叫做 轮询(Polling)。
轮询就像是你每隔一秒就拿起手机看一眼有没有新消息,效率很低,而且会占用你大量的精力(也就是 CPU 时间)。在嵌入式开发中,轮询最大的问题就是 阻塞。你的主程序被这个死循环“绑住”了,其他任务得不到及时执行。想象一下,你的智能家居开关因为要一直检测按键,导致无法及时响应手机 App 的远程控制命令,这显然是不可接受的。
那么,有没有一种更聪明的方法,让按键自己“主动报告”状态变化,而不是让 CPU 不停地去问呢?这就是 中断(Interrupt) 机制大显身手的时候了。中断就像是手机的消息推送通知。当有新消息时,手机会“打断”你当前正在做的事情(比如看视频),用铃声或震动提醒你。处理完消息后,你再回到刚才的视频继续观看。在 MicroPython 中,GPIO 中断允许你在引脚的电平发生特定变化(比如从高变低)时,立即暂停主程序,转而去执行一个你预先设定好的函数(中断服务程序),执行完毕后再自动回到主程序继续运行。
这样一来,CPU 绝大部分时间都可以自由地处理其他任务,只有在按键真正被按下或释放的瞬间,才被“中断”一下去处理按键事件。这种事件驱动的模型,极大地提高了系统的响应效率和整体性能。我刚开始接触中断时,感觉就像给程序装上了“耳朵”,它不再需要傻傻地不停张望,而是学会了聆听外界的信号。
2. 深入理解 MicroPython 的 GPIO 中断机制
2.1 中断的触发条件:不只是按下那么简单
在 MicroPython 的 machine.Pin 类中,配置中断的核心方法是 irq()。其中,trigger 参数决定了在什么情况下会触发中断。新手最容易混淆的就是这几个触发条件,它们直接决定了你的按键行为是否被正确捕捉。
Pin.IRQ_FALLING(下降沿触发):这是检测按键按下最常用的方式。当引脚电平从 高(1)变为低(0) 的瞬间,触发中断。通常,我们将按键一端接 GPIO 引脚,并启用内部上拉电阻(Pin.PULL_UP),另一端接地(GND)。平时引脚被拉高为1,按下按键时,引脚被拉到 GND 变成0,产生一个下降沿。Pin.IRQ_RISING(上升沿触发):与下降沿相反,当引脚电平从 低(0)变为高(1) 的瞬间触发。这常用于检测按键释放。Pin.IRQ_LOW_LEVEL(低电平触发):只要引脚电平为低(0),就会持续触发中断。这非常危险!如果你的按键一直按着,中断就会像连发枪一样不停地触发,瞬间塞满中断队列,导致系统卡死。除非有特殊需求,否则一般避免使用。Pin.IRQ_HIGH_LEVEL(高电平触发):只要引脚为高(1),就持续触发。同样需要谨慎使用。
更灵活的是,你可以使用“或”运算(|)来组合多个条件。例如,trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING 表示无论是按下还是释放按键,都会触发中断。这在需要精确检测一次完整按键动作(按下并释放)的场景中非常有用。
2.2 中断服务程序:短平快是关键
irq() 方法的 handler 参数,就是中断发生时你要调用的函数,我们称之为 中断服务程序(ISR)。编写 ISR 有一条黄金法则:快进快出。
中断会打断主程序的正常执行流,因此 ISR 应该尽可能短小精悍,只完成最必要、最快速的操作,比如设置一个标志位、翻转一个引脚电平,或者记录一个时间戳。绝对要避免在 ISR 中进行复杂的计算、分配内存(如创建列表、字典)、或执行耗时的操作(如网络通信、文件读写)。
一个典型的中断服务程序结构如下:
from machine import Pin
import utime
# 定义一个全局变量作为“事件标志”
button_pressed = False
last_press_time = 0
# 初始化硬件
button = Pin(2, Pin.IN, Pin.PULL_

684

被折叠的 条评论
为什么被折叠?



