GPIO 驱动模型与中断接口

本页依据 文档基线与后续更新 中记录的 Zephyr main 快照更新。

GPIO 公共接口位于 <zephyr/drivers/gpio.h>。应用通常在 Devicetree 中使用 gpio-leds 或 gpio-keys 绑定描述引脚与极性,并在 C 代码中通过 struct gpio_dt_spec 和 *_dt 系列函数访问引脚。

配置与设备树描述

启用 CONFIG_GPIO=y 以及目标平台对应的控制器驱动(在 QEMU 仿真环境中可启用 CONFIG_GPIO_EMUL=y 并使用 zephyr,gpio-emul)。

在设备树或 app.overlay 中定义 LED 与按键节点:

#include <zephyr/dt-bindings/gpio/gpio.h>

/ {
    aliases {
        led0 = &status_led;
        sw0 = &user_key;
    };

    leds {
        compatible = "gpio-leds";
        status_led: led_0 {
            gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
        };
    };

    buttons {
        compatible = "gpio-keys";
        user_key: button_0 {
            gpios = <&gpio0 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
        };
    };
};

GPIO_ACTIVE_LOW 与 GPIO_ACTIVE_HIGH 定义引脚的**逻辑有效电平(Active Level)**。 使用 gpio_pin_set_dt(&spec, 1) 时,1 始终表示“设为逻辑有效状态”,驱动内部会根据 dt_flags 自动转换为对应的物理高低电平。

输出控制与输入读取

#include <zephyr/drivers/gpio.h>
#include <errno.h>

static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
static const struct gpio_dt_spec key = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios);

int io_init_and_poll(void)
{
    int ret;

    if (!gpio_is_ready_dt(&led) || !gpio_is_ready_dt(&key)) {
        return -ENODEV;
    }

    ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
    if (ret != 0) {
        return ret;
    }

    ret = gpio_pin_configure_dt(&key, GPIO_INPUT);
    if (ret != 0) {
        return ret;
    }

    /* 读取按键逻辑状态:1 表示按下(有效),0 表示未按下 */
    int pressed = gpio_pin_get_dt(&key);
    if (pressed > 0) {
        gpio_pin_toggle_dt(&led);
    }
    return 0;
}

中断回调配置

GPIO 中断通过 struct gpio_callback 注册。该结构体必须在中断启用期间保持有效(通常声明为 static 或全局变量,不能是栈上临时变量):

static struct gpio_callback key_cb_data;

static void key_isr(const struct device *port, struct gpio_callback *cb,
                    gpio_port_pins_t pins)
{
    ARG_UNUSED(port);
    ARG_UNUSED(cb);
    ARG_UNUSED(pins);
    /* 运行在 ISR 上下文:仅执行非阻塞操作或提交 k_work */
}

int key_interrupt_enable(void)
{
    int ret = gpio_pin_interrupt_configure_dt(&key, GPIO_INT_EDGE_TO_ACTIVE);
    if (ret != 0) {
        return ret;
    }

    gpio_init_callback(&key_cb_data, key_isr, BIT(key.pin));
    return gpio_add_callback_dt(&key, &key_cb_data);
}

上游依据