树莓派Python开发:按下GPIO按钮时如何立即退出运行循环?
问题根源
你当前代码按键响应延迟的核心原因是使用了阻塞式的sleep(3):程序执行到sleep语句时会完全暂停当前线程的逻辑运行,必须等3秒等待结束后才会走到按键状态判断的代码段,自然无法做到按键按下即时响应。
gpiozero库的Button组件本身自带硬件中断式的事件监听能力,不需要在循环里轮询按键状态,配合非阻塞等待逻辑即可实现毫秒级的按键响应,立刻打断当前运行的循环流程。
可直接运行的修改版代码
import pygame from gpiozero import Button, Motor from time import sleep # 硬件初始化,添加50ms消抖避免机械按键误触发 btn1 = Button(20, bounce_time=0.05) btn2 = Button(21, bounce_time=0.05) motor = Motor(4, 14) pygame.init() # 全局状态标记,记录触发的按键,None为无按键按下状态 pressed_btn = None def trigger_btn1(): global pressed_btn pressed_btn = 1 def trigger_btn2(): global pressed_btn pressed_btn = 2 # 绑定按键按下回调,gpiozero会自动启动后台线程监听引脚电平变化,无需手动轮询 btn1.when_pressed = trigger_btn1 btn2.when_pressed = trigger_btn2 def wait_with_interrupt(duration): """替代原生sleep的非阻塞等待函数,检测到按键触发立刻终止等待""" global pressed_btn time_passed = 0 check_step = 0.05 # 每50ms检测一次按键状态,最高延迟50ms人无感知 while time_passed < duration: if pressed_btn is not None: return sleep(check_step) time_passed += check_step # 主逻辑循环 while True: pressed_btn = None # 默认运行循环,检测到按键立刻跳出 while pressed_btn is None: motor.stop() pygame.mixer.music.load('sounds/sound1.mp3') pygame.mixer.music.play() wait_with_interrupt(3) if pressed_btn is not None: break motor.forward() wait_with_interrupt(3) if pressed_btn is not None: break # 按键触发后的分支逻辑 pygame.mixer.music.stop() motor.stop() print(f'button {pressed_btn} pressed') if pressed_btn == 1: pygame.mixer.music.load('sounds/sound2.mp3') else: pygame.mixer.music.load('sounds/sound3.mp3') pygame.mixer.music.play() wait_with_interrupt(3) motor.forward() wait_with_interrupt(3)
关键改动说明
- 用gpiozero自带的
when_pressed事件回调实现按键监听,该机制基于硬件中断实现,运行在独立后台线程,不会被主线程的sleep阻塞,按键按下时会立刻修改状态标记 - 替换所有长阻塞的原生
sleep(3)为分段短等待实现的非阻塞等待函数,每50ms检测一次按键状态,响应延迟控制在人无法感知的50ms以内,同时CPU占用几乎可以忽略 - 给按键添加了50ms的硬件消抖配置,避免机械按键接触抖动导致的重复触发问题
- 移除了原代码中未使用的
RPi.GPIO导入,减少不必要的资源占用
内容的提问来源于stack exchange,提问作者joaomcs
相关产品推荐
相关产品推荐

