2. GPIO输入读取按键状态

任务目标

在Zephyr OS中配置一个GPIO引脚为输入模式,读取按键电平状态,并在按键按下/释放时通过串口打印状态变化。本案例使用开发板自带的用户按键(若硬件无按键,可按文末说明外接),重点理解GPIO输入驱动的设备树配置与API调用流程。

硬件与设备树配置

以常见的nRF52840DK开发板为例,其板载用户按键BUTTON1连接到P0.11引脚(低电平有效,内部上拉)。若使用其他开发板,请查阅对应设备树文件确认按键引脚与有效电平。

在app.overlay中为按键添加设备树节点(若板级设备树已定义button节点则无需重复添加):

/ {
    buttons {
        compatible = "gpio-keys";
        user_button: button_0 {
            gpios = <&gpio0 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
            label = "User button";
        };
    };
};

完整实现代码

创建src/main.c,实现GPIO输入读取与中断回调打印:

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h>

/* 设备树节点标识 */
#define BUTTON_NODE DT_NODELABEL(user_button)

static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(BUTTON_NODE, gpios);
static struct gpio_callback button_cb_data;

/* 按键中断回调:打印当前电平状态 */
void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
    int val = gpio_pin_get_dt(&button);
    if (val < 0) {
        printk("Error reading button: %d\n", val);
        return;
    }
    printk("Button state: %s\n", val ? "RELEASED" : "PRESSED");
}

int main(void)
{
    int ret;

    if (!device_is_ready(button.port)) {
        printk("GPIO device not ready\n");
        return -ENODEV;
    }

    /* 配置为输入,启用设备树指定的上拉 */
    ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
    if (ret < 0) {
        printk("Failed to configure button pin: %d\n", ret);
        return ret;
    }

    /* 配置中断:下降沿(按下)与上升沿(释放)均触发 */
    ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_BOTH);
    if (ret < 0) {
        printk("Failed to configure interrupt: %d\n", ret);
        return ret;
    }

    gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
    gpio_add_callback(button.port, &button_cb_data);

    printk("GPIO input button example started. Press the button...\n");

    /* 主线程空闲,实际工作由中断回调完成 */
    while (1) {
        k_sleep(K_MSEC(1000));
    }
    return 0;
}

构建与运行

  1. 将上述app.overlay与src/main.c放入Zephyr应用工程目录(含CMakeLists.txt与prj.conf,本案例无需额外Kconfig配置)。
  2. 在应用目录下执行构建(以nRF52840DK为例):
west build -b nrf52840dk/nrf52840 .
  1. 烧录并打开串口终端(如screen /dev/ttyACM0 115200):
west flash

验证方法与判定标准

  • 程序启动后串口输出GPIO input button example started...,说明GPIO设备初始化成功。
  • 按下开发板BUTTON1,串口立即输出Button state: PRESSED;松开后输出Button state: RELEASED。
  • 若按键未按下时串口无输出,且按下/释放均有对应打印,则判定案例通过。

常见问题

  • 编译报错“node not found”:确认app.overlay中user_button标签与代码中DT_NODELABEL(user_button)一致,且overlay文件已被构建系统包含。
  • 无按键响应:检查按键有效电平。若按键为高电平有效,需将设备树中GPIO_ACTIVE_LOW改为GPIO_ACTIVE_HIGH,并移除GPIO_PULL_UP改用GPIO_PULL_DOWN。
  • 外接按键场景:将按键一端接GPIO引脚,另一端接GND(低有效)或VCC(高有效),按上述有效电平调整设备树即可。
按键输入电路示意(低电平有效) VCC 10kΩ KEY GPIO GND 按键未按下:GPIO读到高电平(1) 按键按下:GPIO读到低电平(0) 中断触发:上升沿/下降沿 回调中读取电平并打印