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 引脚号 */