Skip to content

Zephyr 驱动开发

Zephyr 驱动模型

Zephyr 拥有类似 Linux 的统一驱动框架,基于设备树和 Kconfig:

应用层
  device_get_binding("UART_0")
  uart_poll_in() / uart_poll_out()


驱动 API 层(struct uart_driver_api)


具体驱动实现(uart_stm32.c)


硬件寄存器

设备定义(设备树 + Kconfig)

c
/* boards/arm/myboard/myboard.dts */
/ {
    soc {
        usart1: serial@40011000 {
            compatible = "st,stm32-uart";
            reg = <0x40011000 0x400>;
            clocks = <&rcc STM32_CLOCK_BUS_APB2 0x00000010>;
            interrupts = <37 0>;
            status = "okay";
            current-speed = <115200>;
        };
    };
};
text
# drivers/serial/Kconfig
config UART_STM32
    bool "STM32 UART driver"
    depends on SOC_FAMILY_STM32
    select SERIAL_HAS_DRIVER
    help
      Enable STM32 UART driver.

编写 Zephyr 驱动

c
/* drivers/serial/uart_mydev.c */
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/devicetree.h>

/* 从设备树获取配置 */
#define DT_DRV_COMPAT myvendor_my_uart

struct my_uart_config {
    uint32_t base_addr;
    uint32_t baud_rate;
    const struct device *clock_dev;
};

struct my_uart_data {
    struct k_sem     tx_sem;
    struct k_fifo    rx_fifo;
    uart_irq_callback_user_data_t callback;
    void            *callback_data;
};

/* 实现 UART API */
static int my_uart_poll_in(const struct device *dev, unsigned char *c)
{
    const struct my_uart_config *cfg = dev->config;
    uint32_t status = sys_read32(cfg->base_addr + UART_SR);

    if (!(status & UART_SR_RXNE))
        return -1;   /* 无数据 */

    *c = sys_read32(cfg->base_addr + UART_DR) & 0xFF;
    return 0;
}

static void my_uart_poll_out(const struct device *dev, unsigned char c)
{
    const struct my_uart_config *cfg = dev->config;

    /* 等待发送缓冲区空 */
    while (!(sys_read32(cfg->base_addr + UART_SR) & UART_SR_TXE))
        ;

    sys_write32(c, cfg->base_addr + UART_DR);
}

static int my_uart_init(const struct device *dev)
{
    const struct my_uart_config *cfg = dev->config;
    struct my_uart_data *data = dev->data;

    k_sem_init(&data->tx_sem, 1, 1);

    /* 配置波特率 */
    configure_baud_rate(cfg->base_addr, cfg->baud_rate);

    /* 配置中断 */
    IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
                my_uart_isr, DEVICE_DT_INST_GET(0), 0);
    irq_enable(DT_INST_IRQN(0));

    return 0;
}

static const struct uart_driver_api my_uart_api = {
    .poll_in  = my_uart_poll_in,
    .poll_out = my_uart_poll_out,
    .irq_callback_set = my_uart_irq_callback_set,
    .irq_rx_enable    = my_uart_irq_rx_enable,
    .irq_tx_enable    = my_uart_irq_tx_enable,
};

/* 为每个设备树实例生成驱动实例 */
#define MY_UART_INIT(n)                                          \
    static const struct my_uart_config my_uart_cfg_##n = {      \
        .base_addr = DT_INST_REG_ADDR(n),                       \
        .baud_rate = DT_INST_PROP(n, current_speed),            \
    };                                                           \
    static struct my_uart_data my_uart_data_##n;                 \
    DEVICE_DT_INST_DEFINE(n,                                     \
        my_uart_init, NULL,                                      \
        &my_uart_data_##n, &my_uart_cfg_##n,                     \
        PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY,               \
        &my_uart_api);

DT_INST_FOREACH_STATUS_OKAY(MY_UART_INIT)

应用层使用

c
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>

void main(void)
{
    /* 通过设备树标签获取设备 */
    const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(usart1));

    if (!device_is_ready(uart)) {
        printk("UART device not ready\n");
        return;
    }

    /* 轮询发送 */
    const char *msg = "Hello Zephyr!\r\n";
    for (int i = 0; msg[i]; i++)
        uart_poll_out(uart, msg[i]);

    /* 中断接收 */
    uart_irq_callback_user_data_set(uart, uart_cb, NULL);
    uart_irq_rx_enable(uart);
}

static void uart_cb(const struct device *dev, void *user_data)
{
    uint8_t c;
    while (uart_irq_update(dev) && uart_irq_rx_ready(dev)) {
        uart_fifo_read(dev, &c, 1);
        printk("Received: %c\n", c);
    }
}

Zephyr 内核同步原语

c
/* 信号量 */
K_SEM_DEFINE(my_sem, 0, 1);
k_sem_give(&my_sem);
k_sem_take(&my_sem, K_MSEC(1000));

/* 互斥锁 */
K_MUTEX_DEFINE(my_mutex);
k_mutex_lock(&my_mutex, K_FOREVER);
k_mutex_unlock(&my_mutex);

/* 消息队列 */
K_MSGQ_DEFINE(my_msgq, sizeof(my_msg_t), 10, 4);
k_msgq_put(&my_msgq, &msg, K_NO_WAIT);
k_msgq_get(&my_msgq, &msg, K_MSEC(100));

/* 工作队列(类似 Linux workqueue) */
static struct k_work my_work;
static void my_work_handler(struct k_work *work) { /* ... */ }
k_work_init(&my_work, my_work_handler);
k_work_submit(&my_work);

Zephyr 设备树宏速查

c
/* 获取节点属性 */
DT_INST_REG_ADDR(n)              /* reg 地址 */
DT_INST_REG_SIZE(n)              /* reg 大小 */
DT_INST_IRQN(n)                  /* 中断号 */
DT_INST_IRQ(n, priority)         /* 中断优先级 */
DT_INST_PROP(n, current_speed)   /* 自定义属性 */
DT_INST_GPIO_CTLR(n, reset_gpios)/* GPIO 控制器 */
DT_INST_GPIO_PIN(n, reset_gpios) /* GPIO 引脚号 */

褚成志的笔记