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;
};
};
构建与运行
- 将代码保存至
src/main.c,在prj.conf中启用GPIO。 - 若开发板已有
sw0别名(如大多数Zephyr开发板),无需overlay;否则按需添加。 - 构建并烧录:
west build -b <your_board> .
west flash
验证方法
通过串口终端观察输出:
- 快速按下并释放按键,应看到且仅看到一次
Key pressed (debounced)和一次Key released (debounced)。 - 用手轻触按键制造抖动,若去抖生效,不会出现连续多次
pressed/released交替输出。 - 若未去抖,每次抖动都会触发中断回调,串口会打印大量重复事件。
通过标准:每次物理按键操作最多产生一次按下事件和一次释放事件,无重复触发。
关键说明
- 去抖时间10ms适用于常见机械按键;若按键抖动时间更长,可增大
DEBOUNCE_MS。 - 中断回调中仅做“禁用中断+启动定时器”操作,不执行耗时任务,符合中断上下文要求。
- 定时器到期后重新读取引脚电平,确保状态稳定后才上报,避免抖动误判。