You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 15:02:53