4. GPIO去抖处理按键输入

任务目标

机械按键在按下与释放瞬间会产生毫秒级抖动,导致GPIO中断被多次触发。本案例在Zephyr OS中实现软件去抖:按键触发GPIO中断后,延迟10ms再次读取引脚电平,确认状态稳定后才上报按键事件,消除抖动造成的误触发。

实现原理

去抖核心思路:首次检测到电平变化时立即禁用中断并启动定时器,定时器到期后重新使能中断并读取当前电平。若电平与触发时一致则判定为有效按键事件,否则视为抖动丢弃。该方案不阻塞主线程,适用于实时系统。

完整代码


/* 文件: src/main.c */
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h>

#define SW0_NODE    DT_ALIAS(sw0)
#define DEBOUNCE_MS 10

static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(SW0_NODE, gpios);
static struct gpio_callback button_cb_data;
static struct k_timer debounce_timer;
static volatile bool key_pressed;

/* 定时器到期回调:重新使能中断并确认最终电平 */
static void debounce_timeout(struct k_timer *timer)
{
    int val = gpio_pin_get_dt(&button);
    if (val >= 0) {
        key_pressed = (val == 1);
        printk("Key %s (debounced)\n", key_pressed ? "pressed" : "released");
    }
    gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_BOTH);
}

/* GPIO中断回调:禁用中断,启动去抖定时器 */
static void button_isr(const struct device *dev, struct gpio_callback *cb,
                       uint32_t pins)
{
    gpio_pin_interrupt_configure_dt(&button, GPIO_INT_DISABLE);
    k_timer_start(&debounce_timer, K_MSEC(DEBOUNCE_MS), K_NO_WAIT);
}

int main(void)
{
    int ret;

    if (!gpio_is_ready_dt(&button)) {
        printk("Button device not ready\n");
        return -1;
    }

    ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
    if (ret < 0) return ret;

    /* 初始电平作为释放状态 */
    key_pressed = false;

    gpio_init_callback(&button_cb_data, button_isr, BIT(button.pin));
    gpio_add_callback(button.port, &button_cb_data);
    gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_BOTH);

    k_timer_init(&debounce_timer, debounce_timeout, NULL);
    printk("Debounce example started. Press the button...\n");

    while (1) {
        k_sleep(K_FOREVER);
    }
    return 0;
}

工程配置


/* prj.conf */
CONFIG_GPIO=y

/* 设备树overlay(若板载按键未定义sw0别名) */
/ {
    aliases {
        sw0 = &button0;
    };
};

构建与运行

  1. 将代码保存至src/main.c,在prj.conf中启用GPIO。
  2. 若开发板已有sw0别名(如大多数Zephyr开发板),无需overlay;否则按需添加。
  3. 构建并烧录:

west build -b <your_board> .
west flash

验证方法

通过串口终端观察输出:

  • 快速按下并释放按键,应看到且仅看到一次Key pressed (debounced)和一次Key released (debounced)。
  • 用手轻触按键制造抖动,若去抖生效,不会出现连续多次pressed/released交替输出。
  • 若未去抖,每次抖动都会触发中断回调,串口会打印大量重复事件。

通过标准:每次物理按键操作最多产生一次按下事件和一次释放事件,无重复触发。

关键说明

  • 去抖时间10ms适用于常见机械按键;若按键抖动时间更长,可增大DEBOUNCE_MS。
  • 中断回调中仅做“禁用中断+启动定时器”操作,不执行耗时任务,符合中断上下文要求。
  • 定时器到期后重新读取引脚电平,确保状态稳定后才上报,避免抖动误判。