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

如何实现树莓派GPIO输入按键的异步运行?

嘿,我来帮你搞定树莓派GPIO按键的异步响应问题!你的同步代码里,subprocess.call()会阻塞主线程直到命令执行完毕——这就是为什么播放音频时没法响应其他按键的原因。下面给你几个实用的解决方案,按推荐程度排序:

方案1:用RPi.GPIO的边缘事件检测(最优雅高效)

RPi.GPIO原生支持边缘检测,不用写死循环轮询GPIO状态,而是让系统在按键状态变化时自动触发回调函数,完美实现异步响应。

修改后的代码如下:

import RPi.GPIO as GPIO
import subprocess
import threading

# 定义全局变量,防止重复触发同一命令(比如多次按播放键启动多个音频进程)
is_playing = False
is_scanning = False

def scan_button_callback(channel):
    global is_scanning
    if not is_scanning:
        is_scanning = True
        print('Scan Button Pressed')
        # 用Popen启动子进程,不阻塞主线程
        subprocess.Popen(['./ocr.sh'])
        is_scanning = False  # 若ocr.sh执行耗时久,可改用线程等待进程结束后再重置

def play_button_callback(channel):
    global is_playing
    if not is_playing:
        is_playing = True
        print('Read Button Pressed')
        # 启动音频播放进程,不阻塞
        proc = subprocess.Popen(['aplay','temp1.wav'])
        # 用线程等待音频播放完毕,重置标志位避免重复播放
        def reset_play_flag():
            proc.wait()
            global is_playing
            is_playing = False
        threading.Thread(target=reset_play_flag, daemon=True).start()

# GPIO初始化
GPIO.setmode(GPIO.BCM)
# 设置上拉电阻,按键按下时输入为低电平
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# 添加边缘检测:检测FALLING边缘(从高到低,即按键按下),bouncetime消除按键抖动
GPIO.add_event_detect(18, GPIO.FALLING, callback=scan_button_callback, bouncetime=300)
GPIO.add_event_detect(17, GPIO.FALLING, callback=play_button_callback, bouncetime=300)

# 保持主线程运行
try:
    while True:
        pass  # 主线程可在此执行其他逻辑,或仅挂起
except KeyboardInterrupt:
    GPIO.cleanup()  # 退出前清理GPIO资源
    print('程序已退出')

关键说明:

  • GPIO.add_event_detect:注册按键状态变化的回调,bouncetime参数用于消除机械按键的抖动,避免一次按下触发多次回调。
  • subprocess.Popen:替代call(),启动子进程后立即返回,不会阻塞主线程。
  • 全局标志位:防止同一命令被重复触发(比如音频还在播放时再次按播放键),用线程等待进程结束后重置标志位。

方案2:替换subprocess.call为Popen(最小改动)

如果你不想大幅修改原有代码,只需要把阻塞式的subprocess.call()换成非阻塞的subprocess.Popen(),主线程就能继续轮询按键状态:

import RPi.GPIO as GPIO
import time
import subprocess
import threading

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# 标志位防止重复触发
playing = False
scanning = False

while True:
    input_state = GPIO.input(18)
    input_state1 = GPIO.input(17)
    
    if input_state == False and not scanning:
        scanning = True
        print('Scan Button Pressed')
        subprocess.Popen(['./ocr.sh'])
        scanning = False  # 若ocr.sh执行耗时久,可改用线程等待后重置
    
    if input_state1 == False and not playing:
        playing = True
        print('Read Button Pressed')
        proc = subprocess.Popen(['aplay','temp1.wav'])
        # 线程等待音频播放完毕,重置标志位
        def wait_complete():
            proc.wait()
            global playing
            playing = False
        threading.Thread(target=wait_complete, daemon=True).start()
    
    time.sleep(0.1)  # 降低轮询频率,减少CPU占用

注意:

  • 这个方案仍使用轮询机制,CPU占用比边缘检测略高,但代码改动最小。
  • 必须添加标志位,否则多次按键会启动多个相同进程(比如同时播放多段音频)。

方案3:用Python线程封装处理逻辑

把每个按键的处理逻辑放到独立线程中,主线程检测到按键按下时启动对应线程:

import RPi.GPIO as GPIO
import time
import subprocess
import threading

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def handle_scan():
    print('Scan Button Pressed')
    subprocess.call(['./ocr.sh'])  # 此处用call也无妨,因为在子线程中执行,不阻塞主线程

def handle_play():
    print('Read Button Pressed')
    subprocess.call(['aplay','temp1.wav'])

while True:
    input_state = GPIO.input(18)
    input_state1 = GPIO.input(17)
    
    if input_state == False:
        threading.Thread(target=handle_scan, daemon=True).start()
        time.sleep(0.3)  # 消除按键抖动,避免一次按下触发多次线程
    
    if input_state1 == False:
        threading.Thread(target=handle_play, daemon=True).start()
        time.sleep(0.3)
    
    time.sleep(0.1)

说明:

  • 每个处理逻辑在独立线程中执行,主线程可继续检测其他按键。
  • daemon=True确保线程随主线程一起退出,避免程序残留后台进程。
  • time.sleep(0.3)用于消除机械按键的抖动问题,防止一次按下触发多次线程。

内容的提问来源于stack exchange,提问作者Dusayanta Prasad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:58:33