4. 按键消抖处理

任务说明

机械按键在按下与释放瞬间,触点会因物理弹性产生多次快速通断(通常持续5~20ms),若直接读取GPIO电平,一次按键可能被识别为多次触发。本案例实现基于延时采样的软件消抖:检测到电平变化后,等待10ms再次确认电平状态,仅当两次采样一致时才判定按键状态有效,从而避免误触发。

硬件连接

树莓派 GPIO GPIO17 3.3V GND 按键 1 2 3 4 GPIO17 GND 内部上拉已启用 按下/释放时产生5~20ms抖动

按键一端接GPIO17,另一端接GND。启用GPIO内部上拉电阻,未按下时读入高电平(1),按下时读入低电平(0)。

代码实现


import RPi.GPIO as GPIO
import time

BUTTON_PIN = 17
DEBOUNCE_MS = 10  # 消抖延时(毫秒)

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

def read_debounced(pin, delay_ms=DEBOUNCE_MS):
    """读取按键状态,带软件消抖。
    返回:0=按下,1=释放"""
    first = GPIO.input(pin)
    time.sleep(delay_ms / 1000.0)
    second = GPIO.input(pin)
    # 两次采样一致才返回,否则返回上一次稳定状态
    if first == second:
        return first
    else:
        # 抖动中,返回当前实际电平(保守处理)
        return GPIO.input(pin)

try:
    last_state = 1  # 初始为释放状态
    while True:
        current = read_debounced(BUTTON_PIN)
        if current != last_state:
            if current == 0:
                print("按键按下(已消抖)")
            else:
                print("按键释放(已消抖)")
            last_state = current
        time.sleep(0.01)  # 主循环间隔10ms
except KeyboardInterrupt:
    pass
finally:
    GPIO.cleanup()

代码说明

read_debounced()函数是消抖核心:首次读取电平后等待10ms,再读取一次。若两次结果一致,说明电平已稳定,返回该值;若不一致,说明正处于抖动过程,返回当前实际电平。主循环中,仅当消抖后的状态发生变化时才打印事件,从而确保一次物理按键只产生一次“按下”和一次“释放”事件。

运行与验证


sudo python3 debounce.py

运行程序后,快速按下并释放按键多次。判定标准:

  • 每次物理按下,终端只打印一行按键按下(已消抖);
  • 每次物理释放,终端只打印一行按键释放(已消抖);
  • 快速连续按键时,不会出现一次按下对应多行输出,也不会出现无操作时的随机触发。

若观察到一次按键产生多次输出,可适当增大DEBOUNCE_MS至15或20ms;若按键响应明显迟钝,可减小至5ms。