3. Zephyr设备树配置TinyML模型输入输出张量

Zephyr设备树配置TinyML模型输入输出张量 学习卡片

任务目标

在Zephyr RTOS环境下,通过GPIO中断方式检测按键按下事件,每次按下按键时切换LED的亮灭状态。本案例聚焦GPIO输入引脚配置、中断回调机制以及中断与主线程间的数据传递。

硬件需求

  • 开发板:支持Zephyr的任意开发板(本案例以nRF52840DK为例)
  • 按键:连接到GPIO引脚(默认P0.11,低电平有效)
  • LED:连接到GPIO引脚(默认P0.13,高电平点亮)

实现原理

按键检测有两种方式:轮询和中断。本案例采用中断方式,按键按下时触发GPIO中断,在中断回调中通过Zephyr的原子变量或消息队列通知主线程,主线程负责翻转LED状态。中断回调运行在中断上下文,不能直接调用耗时操作,因此仅设置标志位,实际LED控制放在主线程中执行。

关键完整代码

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

/* 开发板引脚配置 - 根据实际硬件修改 */
#define LED_PIN   13
#define BTN_PIN   11
#define SW0_NODE  DT_ALIAS(sw0)
#define LED0_NODE DT_ALIAS(led0)

static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec btn = GPIO_DT_SPEC_GET(SW0_NODE, gpios);

static struct gpio_callback btn_cb_data;
static atomic_t btn_pressed;

/* 按键中断回调 - 运行在中断上下文 */
static void button_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
    atomic_set(&btn_pressed, 1);
}

void main(void)
{
    int ret;

    /* 配置LED引脚为输出 */
    if (!device_is_ready(led.port)) {
        printk("LED device not ready\n");
        return;
    }
    ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    if (ret < 0) {
        printk("LED config failed: %d\n", ret);
        return;
    }

    /* 配置按键引脚为输入,启用上拉电阻 */
    if (!device_is_ready(btn.port)) {
        printk("Button device not ready\n");
        return;
    }
    ret = gpio_pin_configure_dt(&btn, GPIO_INPUT | GPIO_PULL_UP);
    if (ret < 0) {
        printk("Button config failed: %d\n", ret);
        return;
    }

    /* 配置按键中断:下降沿触发(按下时从高到低) */
    ret = gpio_pin_interrupt_configure_dt(&btn, GPIO_INT_EDGE_TO_ACTIVE);
    if (ret < 0) {
        printk("Button interrupt config failed: %d\n", ret);
        return;
    }

    /* 注册中断回调 */
    gpio_init_callback(&btn_cb_data, button_isr, BIT(btn.pin));
    gpio_add_callback(btn.port, &btn_cb_data);

    printk("GPIO button interrupt demo started\n");

    while (1) {
        /* 等待中断标志 */
        if (atomic_get(&btn_pressed)) {
            atomic_set(&btn_pressed, 0);
            /* 翻转LED状态 */
            gpio_pin_toggle_dt(&led);
            printk("Button pressed, LED toggled\n");
        }
        k_sleep(K_MSEC(10));
    }
}

工程配置(prj.conf)

CONFIG_GPIO=y

设备树覆盖(boards/nrf52840dk_nrf52840.overlay)

/ {
    aliases {
        sw0 = &button0;
        led0 = &led0;
    };

    leds {
        compatible = "gpio-leds";
        led0: led_0 {
            gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
            label = "Green LED";
        };
    };

    buttons {
        compatible = "gpio-keys";
        button0: button_0 {
            gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
            label = "User Button";
        };
    };
};

构建与运行

# 在Zephyr工程目录下执行
west build -b nrf52840dk_nrf52840 -d build/button_led .
west flash -d build/button_led

验证方法

  1. 程序启动后,串口终端应输出GPIO button interrupt demo started。
  2. 按下开发板上的用户按键(P0.11),LED(P0.13)状态应翻转(亮变灭或灭变亮)。
  3. 串口终端每次按键后输出Button pressed, LED toggled。
  4. 连续快速按键多次,LED状态应每次都翻转,无丢失响应。

常见问题

  • 引脚不匹配:不同开发板按键和LED引脚不同,需根据硬件原理图修改overlay文件中的gpio引脚号和active电平。
  • 按键抖动:本案例未做软件消抖,若出现一次按键多次翻转,可在中断回调中增加延时消抖或使用Zephyr的debounce API。
  • 中断不触发:检查GPIO_INT_EDGE_TO_ACTIVE配置是否与按键实际电平匹配(低有效按键应配置为下降沿或TO_ACTIVE)。