如何解决Raspberry Pi Pico W的MicroPython定时器超时后无法响应键盘中断问题?
解决Raspberry Pi Pico W定时器阻塞KeyboardInterrupt问题
问题场景
在Raspberry Pi Pico W上使用MicroPython,通过定时器周期性读取多个超声波传感器,读取耗时随目标距离变化(距离越远耗时越长)。当传感器读取函数的执行时间超过定时器触发间隔时,程序完全无法响应KeyboardInterrupt,尝试Ctrl-C、Ctrl-D、VSCode插件硬重置、Pico物理重置都无效,仅能通过重置闪存恢复。
尝试过用布尔标志+try-catch-finally的方案,但中断被完全忽略,测试代码如下:
import time from machine import Timer running = True timer = Timer() def some_function(): if running: time.sleep(0.05) print('other timed function') return timer.deinit() def main(): timer.init(period=50, mode=Timer.PERIODIC, callback=lambda t: some_function()) while running: print('running') time.sleep(1) try: main() except: print('ctrl c') running = False finally: print('finally')
运行时输出示例:
... other timed function running other timed function Traceback (most recent call last): File "main.py", line 30, in <lambda> File "main.py", line 21, in some_other KeyboardInterrupt: other timed function ...
问题根源
MicroPython中,定时器回调函数运行在中断上下文中,此时KeyboardInterrupt无法打断正在执行的回调逻辑。当回调执行时间超过定时器周期时,定时器会持续触发新的回调请求,导致中断上下文长期占用CPU,主循环的异常捕获代码根本没有机会处理中断信号,甚至物理重置都可能因为回调持续占用MCU而失效。
解决方案
方案1:将耗时操作移至主循环(推荐)
把超声波读取这类耗时逻辑从定时器回调中移出,仅用定时器设置触发标志,主循环检测到标志后再执行读取操作:
import time from machine import Timer timer_trigger = False running = True def timer_callback(t): global timer_trigger timer_trigger = True def read_ultrasonic(): # 替换为实际的超声波读取逻辑 time.sleep(0.05) # 模拟耗时操作 print("完成超声波数据读取") def main(): timer = Timer() timer.init(period=50, mode=Timer.PERIODIC, callback=timer_callback) global timer_trigger while running: if timer_trigger: timer_trigger = False read_ultrasonic() time.sleep(0.01) # 给中断处理预留时间 try: main() except KeyboardInterrupt: running = False print("收到中断,停止程序") finally: Timer().deinit() print("资源释放完成")
方案2:拆分回调中的耗时逻辑
如果必须在回调中处理操作,将耗时逻辑拆分为多个小块,每执行一块后主动检测中断状态:
import time from machine import Timer running = True def some_function(t): global running if not running: t.deinit() return # 拆分耗时操作,避免长时间占用中断上下文 for _ in range(5): time.sleep(0.01) # 主动检测中断 if machine.disable_irq(): machine.enable_irq() if not running: t.deinit() return print('other timed function') def main(): timer = Timer() timer.init(period=50, mode=Timer.PERIODIC, callback=some_function) while running: print('running') time.sleep(1) try: main() except KeyboardInterrupt: running = False print('ctrl c') finally: print('finally')
方案3:自定义中断处理函数
利用MicroPython的machine.sigint()自定义中断处理逻辑,直接在中断信号触发时停用定时器:
import time from machine import Timer timer = None def sigint_handler(_): global timer if timer: timer.deinit() print("收到中断,停止定时器") raise SystemExit def some_function(t): time.sleep(0.05) print('other timed function') def main(): global timer machine.sigint(sigint_handler) timer = Timer() timer.init(period=50, mode=Timer.PERIODIC, callback=some_function) while True: print('running') time.sleep(1) main()
关键注意事项
- 定时器回调必须尽可能短小,绝对避免在中断上下文执行耗时操作
- 所有可能阻塞CPU的逻辑(如传感器读取、延迟)都要放到主循环中处理
- 物理重置失效是因为定时器回调持续占用CPU,导致MCU无法进入重置流程,重置闪存是彻底清除异常程序的终极方法
内容的提问来源于stack exchange,提问作者ningelsohn
相关产品推荐
相关产品推荐

