有两个 Button 示例,展示了不同的按钮检测方法:
static const struct led_dt_spec led0 = LED_DT_SPEC_GET_OR(DT_ALIAS(led0), {0});
static void button_input_cb(struct input_event *evt, void *user_data)
{
if (evt->sync == 0) return; // 等待同步事件
printk("Button %d %s at %u\n",
evt->code,
evt->value ? "pressed" : "released",
k_cycle_get_32());
if (led0.dev != NULL) {
led_set_brightness_dt(&led0, evt->value ? 100 : 0);
}
}
INPUT_CALLBACK_DEFINE(NULL, button_input_cb, NULL);
┌────────────────────────────────────────────────────┐
│ 物理按钮按下 │
│ ↓ │
│ Input 子系统 (GPIO/按键矩阵驱动) │
│ ↓ │
│ 去抖动处理 │
│ ↓ │
│ 触发 input_event (类型/代码/值/时间戳) │
│ ↓ │
│ 调用 button_input_cb 回调 │
└────────────────────────────────────────────────────┘
struct input_event {
uint32_t type; // 事件类型 (EV_KEY)
uint32_t code; // 按键代码
int32_t value; // 0=released, 1=pressed
uint32_t sync; // 1=同步点,表示事件包完成
};
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
static struct gpio_callback button_cb_data;
void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
printk("Button pressed at %u\n", k_cycle_get_32());
}
int main(void)
{
// 1. 配置 GPIO 为输入
ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
// 2. 配置中断(上升沿触发)
ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
// 3. 初始化回调结构
gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
// 4. 注册回调
gpio_add_callback(button.port, &button_cb_data);
}
┌────────────────────────────────────────────────────┐
│ 物理按钮按下 │
│ ↓ │
│ GPIO 引脚电平变化 │
│ ↓ │
│ GPIO 中断触发 │
│ ↓ │
│ button_pressed 回调执行 │
└────────────────────────────────────────────────────┘
GPIO_INT_EDGE_TO_ACTIVE // 上升沿(高电平有效)
GPIO_INT_EDGE_TO_INACTIVE // 下降沿
GPIO_INT_EDGE_BOTH // 双边沿
GPIO_INT_LEVEL_HIGH // 高电平
GPIO_INT_LEVEL_LOW // 低电平
// 示例:简单去抖动
static uint32_t last_press_time = 0;
void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
uint32_t now = k_cycle_get_32();
if ((now - last_press_time) < k_ms_to_cyc_ceil32(50)) { // 50ms 去抖动
return; // 忽略抖动
}
last_press_time = now;
printk("Button pressed!\n");
}
CONFIG_GPIO=y
CONFIG_INPUT=y # 启用 Input 子系统
CONFIG_LED=y
CONFIG_GPIO=y # 只需 GPIO 支持
/ {
aliases {
sw0 = &button0;
led0 = &led0;
};
buttons {
button0: button_0 {
gpios = <&gpio0 23 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "Push button switch 0";
};
};
};
/ {
gpio-keys {
compatible = "gpio-keys";
button0: button_0 {
gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;
label = "Push button switch 0";
zephyr,code = <INPUT_BTN_0>; // 映射到标准输入码
};
};
};
| 特性 | Input 子系统 | GPIO 中断 |
|---|---|---|
| 抽象层级 | 高(硬件无关) | 低(直接 GPIO) |
| 代码量 | 更少 | 更多 |
| 去抖动 | 自动处理 | 需软件实现 |
| 多平台移植 | ✅ 好 | ⚠️ 需适配 |
| 复杂度 | 简单 | 中等 |
| 适用场景 | 通用输入 | 精确控制 GPIO |
INPUT_CALLBACK_DEFINE(device, callback, user_data); // 注册回调
struct input_event { type, code, value, sync }; // 事件结构
gpio_pin_configure_dt(&spec, GPIO_INPUT); // 配置输入
gpio_pin_interrupt_configure_dt(&spec, GPIO_INT_EDGE_TO_ACTIVE); // 配置中断
gpio_init_callback(&cb, callback, pins); // 初始化回调
gpio_add_callback(port, &cb); // 注册回调
| 问题 | 答案 |
|---|---|
| 推荐使用哪种方式? | Input 子系统(basic/button),更抽象、可移植 |
| 按钮按下检测? | evt->value == 1 (Input) 或边沿中断 (GPIO) |
| 去抖动怎么处理? | Input 自动处理;GPIO 需软件实现 |
| 如何获取按钮状态? | gpio_pin_get_dt() |
| 支持长按检测? | Input 支持;GPIO 需自己计时 |
// Input 方式 - 推荐
INPUT_CALLBACK_DEFINE(NULL, button_cb, NULL);
static void button_cb(struct input_event *evt, void *user_data)
{
if (evt->code == INPUT_BTN_0 && evt->value) {
// 按钮按下
}
}
// GPIO 方式
ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_BOTH);
gpio_init_callback(&cb_data, my_callback, BIT(button.pin));
gpio_add_callback(button.port, &cb_data);
总结:四个示例都已完成学习笔记!
| 示例 | 文件 |
|---|---|
| UART echo_bot | notes/UART_echo_bot_Learning_Notes.md |
| BLE peripheral_uart | notes/BLE_peripheral_uart_Learning_Notes.md |
| SPI spi_flash | notes/SPI_spi_flash_Learning_Notes.md |
| Button GPIO | notes/Button_GPIO_Learning_Notes.md |
小白 🤖 2026-03-14