Zephyr串口驱动开发及构建完全指南

Zephyr串口驱动开发及构建完全指南

引言

串口(UART)是嵌入式系统中最常用的通信接口之一,用于调试、数据传输和设备间通信。Zephyr提供了一套完整的串口驱动框架,支持多种硬件平台和通信模式。

本文将详细介绍Zephyr下串口驱动的开发流程,包括驱动架构、设备树配置、Kconfig配置、API使用以及完整的项目构建过程。


一、Zephyr串口驱动架构

1.1 架构概述

#mermaid-svg-MDa2DujOs5Eyrsrb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MDa2DujOs5Eyrsrb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MDa2DujOs5Eyrsrb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MDa2DujOs5Eyrsrb .error-icon{fill:#552222;}#mermaid-svg-MDa2DujOs5Eyrsrb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MDa2DujOs5Eyrsrb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MDa2DujOs5Eyrsrb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MDa2DujOs5Eyrsrb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MDa2DujOs5Eyrsrb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MDa2DujOs5Eyrsrb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MDa2DujOs5Eyrsrb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MDa2DujOs5Eyrsrb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MDa2DujOs5Eyrsrb .marker.cross{stroke:#333333;}#mermaid-svg-MDa2DujOs5Eyrsrb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MDa2DujOs5Eyrsrb p{margin:0;}#mermaid-svg-MDa2DujOs5Eyrsrb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MDa2DujOs5Eyrsrb .cluster-label text{fill:#333;}#mermaid-svg-MDa2DujOs5Eyrsrb .cluster-label span{color:#333;}#mermaid-svg-MDa2DujOs5Eyrsrb .cluster-label span p{background-color:transparent;}#mermaid-svg-MDa2DujOs5Eyrsrb .label text,#mermaid-svg-MDa2DujOs5Eyrsrb span{fill:#333;color:#333;}#mermaid-svg-MDa2DujOs5Eyrsrb .node rect,#mermaid-svg-MDa2DujOs5Eyrsrb .node circle,#mermaid-svg-MDa2DujOs5Eyrsrb .node ellipse,#mermaid-svg-MDa2DujOs5Eyrsrb .node polygon,#mermaid-svg-MDa2DujOs5Eyrsrb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MDa2DujOs5Eyrsrb .rough-node .label text,#mermaid-svg-MDa2DujOs5Eyrsrb .node .label text,#mermaid-svg-MDa2DujOs5Eyrsrb .image-shape .label,#mermaid-svg-MDa2DujOs5Eyrsrb .icon-shape .label{text-anchor:middle;}#mermaid-svg-MDa2DujOs5Eyrsrb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MDa2DujOs5Eyrsrb .rough-node .label,#mermaid-svg-MDa2DujOs5Eyrsrb .node .label,#mermaid-svg-MDa2DujOs5Eyrsrb .image-shape .label,#mermaid-svg-MDa2DujOs5Eyrsrb .icon-shape .label{text-align:center;}#mermaid-svg-MDa2DujOs5Eyrsrb .node.clickable{cursor:pointer;}#mermaid-svg-MDa2DujOs5Eyrsrb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MDa2DujOs5Eyrsrb .arrowheadPath{fill:#333333;}#mermaid-svg-MDa2DujOs5Eyrsrb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MDa2DujOs5Eyrsrb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MDa2DujOs5Eyrsrb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MDa2DujOs5Eyrsrb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MDa2DujOs5Eyrsrb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MDa2DujOs5Eyrsrb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MDa2DujOs5Eyrsrb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MDa2DujOs5Eyrsrb .cluster text{fill:#333;}#mermaid-svg-MDa2DujOs5Eyrsrb .cluster span{color:#333;}#mermaid-svg-MDa2DujOs5Eyrsrb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MDa2DujOs5Eyrsrb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MDa2DujOs5Eyrsrb rect.text{fill:none;stroke-width:0;}#mermaid-svg-MDa2DujOs5Eyrsrb .icon-shape,#mermaid-svg-MDa2DujOs5Eyrsrb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MDa2DujOs5Eyrsrb .icon-shape p,#mermaid-svg-MDa2DujOs5Eyrsrb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MDa2DujOs5Eyrsrb .icon-shape .label rect,#mermaid-svg-MDa2DujOs5Eyrsrb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MDa2DujOs5Eyrsrb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MDa2DujOs5Eyrsrb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MDa2DujOs5Eyrsrb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用层
串口API
串口驱动层
硬件抽象层
硬件寄存器
uart_write
uart_read
uart_irq_callback
nrf-uart
stm32-uart
x86-uart
通用UART接口

1.2 驱动层次

层次 说明 文件位置
应用层 用户应用代码 src/main.c
API层 通用串口API include/zephyr/drivers/uart.h
驱动层 平台特定驱动 drivers/uart/
HAL层 硬件抽象层 drivers/uart/*/uart_*.c

1.3 串口驱动目录结构

复制代码
zephyr/drivers/uart/
├── uart.c              # 通用串口API实现
├── uart.h              # 串口API头文件
├── Kconfig             # Kconfig配置
├── nrf/                # Nordic UART驱动
│   ├── uart_nrf.c
│   └── uart_nrf.h
├── stm32/              # STM32 UART驱动
│   ├── uart_stm32.c
│   └── uart_stm32.h
├── x86/                # x86 UART驱动
│   ├── uart_pc.c
│   └── uart_pc.h
└── ...                 # 其他平台驱动

二、设备树配置

2.1 串口节点定义

dts 复制代码
/* 设备树中的串口节点 */

uart0: uart@40002000 {
    compatible = "nordic,nrf-uart";
    reg = <0x40002000 0x1000>;
    interrupts = <2 1>;
    status = "okay";
    current-speed = <115200>;
    label = "UART_0";
    pinctrl-0 = <&uart0_default>;
    pinctrl-1 = <&uart0_sleep>;
    pinctrl-names = "default", "sleep";
};

2.2 设备树属性说明

属性 说明 示例
compatible 兼容的驱动 "nordic,nrf-uart"
reg 寄存器地址和大小 <0x40002000 0x1000>
interrupts 中断号和优先级 <2 1>
status 设备状态 "okay""disabled"
current-speed 默认波特率 <115200>
label 设备标签 "UART_0"
pinctrl-0 默认引脚配置 <&uart0_default>

2.3 别名定义

dts 复制代码
/* 在aliases节点中定义别名 */

aliases {
    uart0 = &uart0;
    uart1 = &uart1;
    console = &uart0;
    shell-uart = &uart0;
};

2.4 使用设备树获取串口

c 复制代码
#include <zephyr/devicetree.h>
#include <zephyr/drivers/uart.h>

/* 使用别名获取串口设备 */
#define UART_NODE DT_ALIAS(uart0)
const struct device *uart_dev = DEVICE_DT_GET(UART_NODE);

/* 使用路径获取串口设备 */
#define UART_NODE DT_PATH(soc, uart_40002000)

/* 使用标签获取串口设备 */
#define UART_NODE DT_NODELABEL(uart0)

三、Kconfig配置

3.1 基本配置

kconfig 复制代码
# prj.conf - 串口配置

# 启用串口驱动
CONFIG_UART=y

# 启用控制台串口
CONFIG_UART_CONSOLE=y

# 启用串口输入
CONFIG_CONSOLE_INPUT=y

# 启用控制台处理器
CONFIG_CONSOLE_HANDLER=y

# 启用串行驱动
CONFIG_SERIAL=y

3.2 高级配置

kconfig 复制代码
# 缓冲区配置
CONFIG_UART_RX_BUF_SIZE=256          # 接收缓冲区大小
CONFIG_UART_TX_BUF_SIZE=256          # 发送缓冲区大小

# 流控制配置
CONFIG_UART_HW_FLOW_CONTROL=n         # 硬件流控制
CONFIG_UART_SW_FLOW_CONTROL=n         # 软件流控制

# 中断配置
CONFIG_UART_INTERRUPT_DRIVEN=y        # 中断驱动模式

# 调试配置
CONFIG_UART_DEBUG=n                   # 串口调试
CONFIG_UART_DEBUG_LOG=n               # 串口调试日志

# 驱动特定配置
CONFIG_UART_NRF=y                     # Nordic UART驱动
CONFIG_UART_STM32=y                   # STM32 UART驱动
CONFIG_UART_PC=y                      # x86 UART驱动

3.3 配置项说明

配置项 说明 默认值
CONFIG_UART 启用串口驱动 y
CONFIG_UART_CONSOLE 串口作为控制台 y
CONFIG_CONSOLE_INPUT 控制台输入 n
CONFIG_UART_INTERRUPT_DRIVEN 中断驱动模式 y
CONFIG_UART_RX_BUF_SIZE 接收缓冲区大小 256
CONFIG_UART_TX_BUF_SIZE 发送缓冲区大小 256
CONFIG_UART_HW_FLOW_CONTROL 硬件流控制 n

四、串口API详解

4.1 基本API

c 复制代码
#include <zephyr/drivers/uart.h>

/* 获取设备 */
const struct device *uart_dev = DEVICE_DT_GET(DT_ALIAS(uart0));

/* 检查设备是否就绪 */
if (!device_is_ready(uart_dev)) {
    printk("UART device not ready\n");
    return -1;
}

/* 写入数据 */
int uart_write(const struct device *dev, const uint8_t *buf, size_t len);

/* 读取数据(阻塞) */
int uart_read(const struct device *dev, uint8_t *buf, size_t len);

/* 读取数据(非阻塞) */
int uart_fifo_read(const struct device *dev, uint8_t *buf, size_t len);

/* 写入数据(非阻塞) */
int uart_fifo_write(const struct device *dev, const uint8_t *buf, size_t len);

/* 获取接收缓冲区中可用字节数 */
int uart_rx_buf_rpos(const struct device *dev, const uint8_t **buf);

/* 获取发送缓冲区中可用空间 */
int uart_tx_buf_nfree(const struct device *dev);

4.2 中断驱动API

c 复制代码
#include <zephyr/drivers/uart.h>

/* 中断回调函数 */
void uart_callback(const struct device *dev, void *user_data)
{
    uint8_t c;
    
    /* 更新中断状态 */
    if (uart_irq_update(dev)) {
        /* 检查接收中断 */
        if (uart_irq_rx_ready(dev)) {
            /* 读取一个字节 */
            uart_fifo_read(dev, &c, 1);
            /* 处理接收到的数据 */
            process_byte(c);
        }
        
        /* 检查发送中断 */
        if (uart_irq_tx_ready(dev)) {
            /* 发送更多数据 */
            send_next_byte();
        }
    }
}

/* 设置中断回调 */
uart_irq_callback_set(uart_dev, uart_callback);

/* 启用接收中断 */
uart_irq_rx_enable(uart_dev);

/* 启用发送中断 */
uart_irq_tx_enable(uart_dev);

/* 禁用接收中断 */
uart_irq_rx_disable(uart_dev);

/* 禁用发送中断 */
uart_irq_tx_disable(uart_dev);

4.3 串口配置API

c 复制代码
#include <zephyr/drivers/uart.h>

/* 串口配置结构体 */
struct uart_config {
    uint32_t baudrate;        /* 波特率 */
    uint8_t parity;           /* 校验位 */
    uint8_t stop_bits;        /* 停止位 */
    uint8_t data_bits;        /* 数据位 */
    uint8_t flow_ctrl;        /* 流控制 */
};

/* 配置串口 */
int uart_configure(const struct device *dev, const struct uart_config *cfg);

/* 获取当前配置 */
int uart_config_get(const struct device *dev, struct uart_config *cfg);

/* 设置波特率 */
int uart_set_baudrate(const struct device *dev, uint32_t baudrate);

/* 获取波特率 */
int uart_get_baudrate(const struct device *dev, uint32_t *baudrate);

4.4 配置选项说明

选项 说明
parity UART_CFG_PARITY_NONE 无校验
UART_CFG_PARITY_EVEN 偶校验
UART_CFG_PARITY_ODD 奇校验
stop_bits UART_CFG_STOP_BITS_1 1个停止位
UART_CFG_STOP_BITS_2 2个停止位
data_bits UART_CFG_DATA_BITS_7 7位数据
UART_CFG_DATA_BITS_8 8位数据
flow_ctrl UART_CFG_FLOW_CTRL_NONE 无流控制
UART_CFG_FLOW_CTRL_RTS_CTS RTS/CTS流控制

五、完整串口驱动开发示例

5.1 项目结构

复制代码
uart_example/
├── CMakeLists.txt
├── prj.conf
├── src/
│   └── main.c
└── boards/
    └── qemu_x86.overlay

5.2 CMakeLists.txt

cmake 复制代码
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

project(uart_example)

target_sources(app PRIVATE src/main.c)

5.3 prj.conf

kconfig 复制代码
CONFIG_KERNEL=y
CONFIG_INIT_STACKS=y

CONFIG_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_UART=y
CONFIG_UART_CONSOLE=y
CONFIG_CONSOLE_INPUT=y

CONFIG_DEBUG=y
CONFIG_LOG=y
CONFIG_LOG_LEVEL_DBG=y

5.4 src/main.c - 阻塞模式

c 复制代码
#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/printk.h>

#define UART_NODE DT_ALIAS(uart0)
const struct device *uart_dev = DEVICE_DT_GET(UART_NODE);

void main(void)
{
    uint8_t tx_buf[] = "Hello from Zephyr UART!\n";
    uint8_t rx_buf[64];
    int ret;

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

    printk("UART example started\n");

    /* 发送数据 */
    ret = uart_write(uart_dev, tx_buf, sizeof(tx_buf) - 1);
    if (ret < 0) {
        printk("UART write failed: %d\n", ret);
        return;
    }

    printk("Data sent, waiting for response...\n");

    /* 等待接收数据(阻塞模式) */
    ret = uart_read(uart_dev, rx_buf, sizeof(rx_buf) - 1);
    if (ret < 0) {
        printk("UART read failed: %d\n", ret);
        return;
    }

    /* 确保字符串以NULL结尾 */
    rx_buf[ret] = '\0';
    printk("Received: %s\n", rx_buf);
}

5.5 src/main.c - 中断驱动模式

c 复制代码
#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/printk.h>

#define UART_NODE DT_ALIAS(uart0)
const struct device *uart_dev = DEVICE_DT_GET(UART_NODE);

#define RX_BUF_SIZE 128
static uint8_t rx_buf[RX_BUF_SIZE];
static size_t rx_buf_pos = 0;

#define TX_BUF_SIZE 128
static uint8_t tx_buf[TX_BUF_SIZE];
static size_t tx_buf_pos = 0;
static size_t tx_buf_len = 0;

/* 串口中断回调函数 */
void uart_callback(const struct device *dev, void *user_data)
{
    uint8_t c;
    int ret;

    if (!uart_irq_update(dev)) {
        return;
    }

    /* 处理接收中断 */
    if (uart_irq_rx_ready(dev)) {
        while (uart_fifo_read(dev, &c, 1) == 1) {
            if (rx_buf_pos < RX_BUF_SIZE - 1) {
                rx_buf[rx_buf_pos++] = c;
                
                /* 检查是否收到换行符 */
                if (c == '\n' || c == '\r') {
                    rx_buf[rx_buf_pos] = '\0';
                    printk("Received: %s", rx_buf);
                    rx_buf_pos = 0;
                    
                    /* 回显数据 */
                    tx_buf_len = rx_buf_pos;
                    memcpy(tx_buf, rx_buf, tx_buf_len);
                    tx_buf_pos = 0;
                    uart_irq_tx_enable(dev);
                }
            } else {
                /* 缓冲区溢出 */
                rx_buf_pos = 0;
                printk("RX buffer overflow!\n");
            }
        }
    }

    /* 处理发送中断 */
    if (uart_irq_tx_ready(dev)) {
        while (tx_buf_pos < tx_buf_len) {
            ret = uart_fifo_write(dev, &tx_buf[tx_buf_pos], 1);
            if (ret == 1) {
                tx_buf_pos++;
            } else {
                break;
            }
        }
        
        /* 发送完成 */
        if (tx_buf_pos >= tx_buf_len) {
            tx_buf_pos = 0;
            tx_buf_len = 0;
            uart_irq_tx_disable(dev);
        }
    }
}

void main(void)
{
    uint8_t welcome_msg[] = "Zephyr UART Interrupt Example\n";
    int ret;

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

    /* 设置中断回调 */
    uart_irq_callback_set(uart_dev, uart_callback);

    /* 启用接收中断 */
    uart_irq_rx_enable(uart_dev);

    /* 发送欢迎消息 */
    tx_buf_len = sizeof(welcome_msg) - 1;
    memcpy(tx_buf, welcome_msg, tx_buf_len);
    tx_buf_pos = 0;
    uart_irq_tx_enable(uart_dev);

    printk("UART interrupt example started\n");

    while (1) {
        k_msleep(100);
    }
}

六、构建过程

6.1 编译命令

bash 复制代码
# 进入项目目录
cd uart_example

# 编译(QEMU x86平台)
west build -b qemu_x86 .

# 编译并清理旧构建
west build -b qemu_x86 . --clean

# 指定构建目录
west build -b qemu_x86 . -d build_qemu

# 使用多核编译
west build -b qemu_x86 . -j $(nproc)

# 编译到硬件平台
west build -b nrf52840dk_nrf52840 .

6.2 编译流程

编译器 Kconfig CMake West 用户 编译器 Kconfig CMake West 用户 #mermaid-svg-fEIdv8Fr84Gn0f6A{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fEIdv8Fr84Gn0f6A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fEIdv8Fr84Gn0f6A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fEIdv8Fr84Gn0f6A .error-icon{fill:#552222;}#mermaid-svg-fEIdv8Fr84Gn0f6A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fEIdv8Fr84Gn0f6A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fEIdv8Fr84Gn0f6A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fEIdv8Fr84Gn0f6A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fEIdv8Fr84Gn0f6A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fEIdv8Fr84Gn0f6A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fEIdv8Fr84Gn0f6A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fEIdv8Fr84Gn0f6A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fEIdv8Fr84Gn0f6A .marker.cross{stroke:#333333;}#mermaid-svg-fEIdv8Fr84Gn0f6A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fEIdv8Fr84Gn0f6A p{margin:0;}#mermaid-svg-fEIdv8Fr84Gn0f6A .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fEIdv8Fr84Gn0f6A text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-fEIdv8Fr84Gn0f6A .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-fEIdv8Fr84Gn0f6A .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-fEIdv8Fr84Gn0f6A .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-fEIdv8Fr84Gn0f6A .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-fEIdv8Fr84Gn0f6A #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-fEIdv8Fr84Gn0f6A .sequenceNumber{fill:white;}#mermaid-svg-fEIdv8Fr84Gn0f6A #sequencenumber{fill:#333;}#mermaid-svg-fEIdv8Fr84Gn0f6A #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-fEIdv8Fr84Gn0f6A .messageText{fill:#333;stroke:none;}#mermaid-svg-fEIdv8Fr84Gn0f6A .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fEIdv8Fr84Gn0f6A .labelText,#mermaid-svg-fEIdv8Fr84Gn0f6A .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-fEIdv8Fr84Gn0f6A .loopText,#mermaid-svg-fEIdv8Fr84Gn0f6A .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-fEIdv8Fr84Gn0f6A .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-fEIdv8Fr84Gn0f6A .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-fEIdv8Fr84Gn0f6A .noteText,#mermaid-svg-fEIdv8Fr84Gn0f6A .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-fEIdv8Fr84Gn0f6A .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fEIdv8Fr84Gn0f6A .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fEIdv8Fr84Gn0f6A .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fEIdv8Fr84Gn0f6A .actorPopupMenu{position:absolute;}#mermaid-svg-fEIdv8Fr84Gn0f6A .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-fEIdv8Fr84Gn0f6A .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fEIdv8Fr84Gn0f6A .actor-man circle,#mermaid-svg-fEIdv8Fr84Gn0f6A line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-fEIdv8Fr84Gn0f6A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} west build -b qemu_x86 . 解析prj.conf 生成autoconf.h 运行CMake 加载Zephyr模块 解析设备树 生成构建文件 编译源代码 编译main.c 编译uart驱动 链接目标文件 生成zephyr.elf 构建完成

6.3 编译输出

bash 复制代码
$ west build -b qemu_x86 .
-- west build: making build dir /home/user/zephyrproject/uart_example/build
-- west build: sourcing /home/user/zephyrproject/zephyr/zephyr-env.sh
-- Zephyr version: 3.6.0 (/home/user/zephyrproject/zephyr)
-- Board: qemu_x86
-- Found host-tools: zephyr-sdk-0.16.4 (/home/user/zephyr-sdk-0.16.4)
-- Found toolchain: zephyr (/home/user/zephyr-sdk-0.16.4/x86_64-zephyr-elf)
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/zephyrproject/uart_example/build
[1/234] Building C object zephyr/CMakeFiles/zephyr.dir/drivers/uart/uart.c.obj
[2/234] Building C object zephyr/CMakeFiles/zephyr.dir/drivers/uart/x86/uart_pc.c.obj
...
[234/234] Linking C executable zephyr/zephyr.elf
Memory region         Used Size  Region Size  %age Used
             FLASH:       2048 B       256 KB      0.78%
               RAM:        256 B        64 KB      0.39%

七、运行与测试

7.1 在QEMU中运行

bash 复制代码
# 运行
west build -t run

# 输出示例:
# [1/1] To exit from QEMU enter: 'CTRL+a, x'
# QEMU 7.0.0 monitor - type 'help' for more information
# (qemu)
# SeaBIOS (version rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org)
# Booting from ROM..
# UART example started
# Hello from Zephyr UART!

7.2 在硬件开发板上运行

步骤1:连接开发板

bash 复制代码
# 查看串口设备
ls /dev/ttyACM*
ls /dev/ttyUSB*

步骤2:编译目标平台

bash 复制代码
west build -b nrf52840dk_nrf52840 .

步骤3:烧录固件

bash 复制代码
west flash

步骤4:查看串口输出

bash 复制代码
# 使用minicom
minicom -D /dev/ttyACM0 -b 115200

# 使用picocom
picocom /dev/ttyACM0 -b 115200

7.3 测试串口通信

发送测试

bash 复制代码
# 在串口终端中输入数据
Hello Zephyr!

预期输出

bash 复制代码
# 回显数据
Hello Zephyr!

八、串口驱动开发进阶

8.1 流控制配置

c 复制代码
#include <zephyr/drivers/uart.h>

struct uart_config cfg = {
    .baudrate = 115200,
    .parity = UART_CFG_PARITY_NONE,
    .stop_bits = UART_CFG_STOP_BITS_1,
    .data_bits = UART_CFG_DATA_BITS_8,
    .flow_ctrl = UART_CFG_FLOW_CTRL_RTS_CTS,  /* 启用RTS/CTS流控制 */
};

int ret = uart_configure(uart_dev, &cfg);
if (ret < 0) {
    printk("UART configure failed: %d\n", ret);
}

8.2 多串口管理

c 复制代码
#include <zephyr/drivers/uart.h>

/* 定义多个串口 */
const struct device *uart0 = DEVICE_DT_GET(DT_ALIAS(uart0));
const struct device *uart1 = DEVICE_DT_GET(DT_ALIAS(uart1));

/* 配置不同波特率 */
uart_set_baudrate(uart0, 115200);
uart_set_baudrate(uart1, 9600);

/* 发送数据到不同串口 */
uart_write(uart0, "Data to uart0\n", 14);
uart_write(uart1, "Data to uart1\n", 14);

8.3 DMA模式

c 复制代码
#include <zephyr/drivers/uart.h>

/* DMA配置(如果硬件支持) */
#if defined(CONFIG_UART_DMA)
#include <zephyr/drivers/dma.h>

static struct dma_block_config dma_tx_block = {
    .source_address = NULL,
    .dest_address = (uint32_t)UART_TX_REG,
    .block_size = 0,
    .source_addr_adj = DMA_ADDR_ADJ_INCREMENT,
    .dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE,
    .source_reload_en = 0,
};

/* 使用DMA发送 */
dma_configure(dma_dev, dma_channel, &dma_tx_block);
dma_start(dma_dev, dma_channel);
#endif

8.4 串口轮询模式

c 复制代码
#include <zephyr/drivers/uart.h>

/* 轮询发送 */
void uart_poll_send(const struct device *dev, const uint8_t *buf, size_t len)
{
    for (size_t i = 0; i < len; i++) {
        /* 等待发送缓冲区为空 */
        while (!uart_irq_tx_ready(dev)) {
            k_msleep(1);
        }
        
        /* 发送一个字节 */
        uart_fifo_write(dev, &buf[i], 1);
    }
}

/* 轮询接收 */
int uart_poll_receive(const struct device *dev, uint8_t *buf, size_t len)
{
    size_t received = 0;
    
    while (received < len) {
        /* 检查是否有数据可读 */
        if (uart_irq_rx_ready(dev)) {
            int ret = uart_fifo_read(dev, &buf[received], 1);
            if (ret == 1) {
                received++;
            }
        } else {
            k_msleep(1);
        }
    }
    
    return received;
}

九、常见问题与解决方案

9.1 编译错误

问题1:找不到串口设备

bash 复制代码
# 错误信息:
# error: 'DT_ALIAS_uart0' undeclared

# 解决:检查设备树配置
# 确保设备树中定义了uart0节点
# 在aliases中添加uart0别名

问题2:串口驱动未启用

bash 复制代码
# 错误信息:
# undefined reference to `uart_write'

# 解决:在prj.conf中添加
CONFIG_UART=y
CONFIG_SERIAL=y

9.2 运行错误

问题1:串口无输出

bash 复制代码
# 解决:检查配置
# 确保启用了控制台
CONFIG_UART_CONSOLE=y

# 检查设备树
status = "okay"

# 检查波特率配置
current-speed = <115200>

问题2:串口输出乱码

bash 复制代码
# 解决:确保波特率一致
# 开发板和终端使用相同波特率
minicom -D /dev/ttyACM0 -b 115200

问题3:接收数据丢失

bash 复制代码
# 解决:增大缓冲区
CONFIG_UART_RX_BUF_SIZE=512
CONFIG_UART_TX_BUF_SIZE=512

# 或使用中断驱动模式
CONFIG_UART_INTERRUPT_DRIVEN=y

9.3 硬件问题

问题1:串口设备权限

bash 复制代码
# 错误信息:
# Can't open /dev/ttyACM0: Permission denied

# 解决:添加用户到dialout组
sudo usermod -aG dialout $USER

问题2:串口不识别

bash 复制代码
# 解决:检查硬件连接
lsusb

# 检查驱动是否加载
dmesg | grep tty

十、最佳实践

10.1 代码结构规范

c 复制代码
/* 推荐的串口代码结构 */

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

/* 使用设备树获取设备 */
#define UART_NODE DT_ALIAS(uart0)
static const struct device *const uart_dev = DEVICE_DT_GET(UART_NODE);

/* 定义缓冲区 */
#define BUF_SIZE 256
static uint8_t rx_buf[BUF_SIZE];
static uint8_t tx_buf[BUF_SIZE];

/* 串口初始化 */
static int uart_init(void)
{
    if (!device_is_ready(uart_dev)) {
        printk("UART device not ready\n");
        return -1;
    }
    
    /* 配置串口 */
    struct uart_config cfg = {
        .baudrate = 115200,
        .parity = UART_CFG_PARITY_NONE,
        .stop_bits = UART_CFG_STOP_BITS_1,
        .data_bits = UART_CFG_DATA_BITS_8,
        .flow_ctrl = UART_CFG_FLOW_CTRL_NONE,
    };
    
    return uart_configure(uart_dev, &cfg);
}

/* 发送字符串 */
static int uart_send_string(const char *str)
{
    return uart_write(uart_dev, (const uint8_t *)str, strlen(str));
}

/* 主函数 */
void main(void)
{
    uart_init();
    uart_send_string("Hello World!\n");
}

10.2 中断驱动模式最佳实践

#mermaid-svg-Ih4Gdysb4QP2YWS3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .error-icon{fill:#552222;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .marker.cross{stroke:#333333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ih4Gdysb4QP2YWS3 p{margin:0;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .cluster-label text{fill:#333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .cluster-label span{color:#333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .cluster-label span p{background-color:transparent;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .label text,#mermaid-svg-Ih4Gdysb4QP2YWS3 span{fill:#333;color:#333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .node rect,#mermaid-svg-Ih4Gdysb4QP2YWS3 .node circle,#mermaid-svg-Ih4Gdysb4QP2YWS3 .node ellipse,#mermaid-svg-Ih4Gdysb4QP2YWS3 .node polygon,#mermaid-svg-Ih4Gdysb4QP2YWS3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .rough-node .label text,#mermaid-svg-Ih4Gdysb4QP2YWS3 .node .label text,#mermaid-svg-Ih4Gdysb4QP2YWS3 .image-shape .label,#mermaid-svg-Ih4Gdysb4QP2YWS3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .rough-node .label,#mermaid-svg-Ih4Gdysb4QP2YWS3 .node .label,#mermaid-svg-Ih4Gdysb4QP2YWS3 .image-shape .label,#mermaid-svg-Ih4Gdysb4QP2YWS3 .icon-shape .label{text-align:center;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .node.clickable{cursor:pointer;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .arrowheadPath{fill:#333333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ih4Gdysb4QP2YWS3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ih4Gdysb4QP2YWS3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ih4Gdysb4QP2YWS3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .cluster text{fill:#333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .cluster span{color:#333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ih4Gdysb4QP2YWS3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .icon-shape,#mermaid-svg-Ih4Gdysb4QP2YWS3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .icon-shape p,#mermaid-svg-Ih4Gdysb4QP2YWS3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .icon-shape .label rect,#mermaid-svg-Ih4Gdysb4QP2YWS3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ih4Gdysb4QP2YWS3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ih4Gdysb4QP2YWS3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ih4Gdysb4QP2YWS3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 中断驱动模式
初始化
中断处理
数据处理
获取设备
设置回调
启用中断
接收中断
发送中断
缓冲区管理
数据解析

10.3 性能优化建议

优化项 建议
缓冲区大小 根据数据量调整,避免频繁中断
中断优先级 高优先级数据使用高优先级中断
DMA 大量数据传输使用DMA
批量操作 合并多次小数据传输

结束语

通过本文的详细介绍,相信您已经掌握了Zephyr串口驱动开发的完整流程:

知识点 内容
驱动架构 应用层→API层→驱动层→HAL层
设备树 定义串口节点、配置属性、别名
Kconfig 启用串口驱动、配置缓冲区、流控制
API使用 uart_write、uart_read、中断回调
构建过程 west build编译、QEMU运行、硬件烧录

串口开发要点

  1. 设备树配置:正确定义串口节点和属性
  2. Kconfig配置:启用必要的配置选项
  3. API选择:根据场景选择阻塞/中断/DMA模式
  4. 缓冲区管理:合理设置缓冲区大小
  5. 错误处理:检查返回值和设备状态

下一步学习

  1. 实现串口数据解析协议
  2. 添加流控制支持
  3. 使用DMA进行高速数据传输
  4. 实现多串口通信

参考资料

相关推荐
阿扬ABCD7 小时前
ipmitool的Windows版本编译
windows·驱动开发
fitpolo1 天前
详解Zephyr设备树与设备驱动模型
zephyr
编程圈子1 天前
电机驱动开发学习19. 霍尔 BLDC 三段式 FOC 启动算法
驱动开发·学习·算法
咖啡星人k3 天前
MonkeyCode 架构深度解析:SDD 规范驱动开发的工程哲学
驱动开发·架构·monkeycode
小此方3 天前
Re:Linux系统篇(四十六)信号篇·四:一文串联操作系统底层:时钟中断、内核态切换、系统调用与 Linux 信号处理全解析
linux·驱动开发·信号处理
Industio_触觉智能4 天前
RK3576开发板UART驱动实战教程(下),基于触觉智能开发板演示
嵌入式硬件·硬件架构·串口·uart·智能硬件·rs232·rs485
Echo_cy_4 天前
基于ZYNQ-7000的Ethernet驱动配置
linux·驱动开发·嵌入式硬件·fpga开发·ethernet·zynq
乐鑫科技 Espressif5 天前
用 AI 开发 Zephyr-IoT 应用
人工智能·物联网·esp32·乐鑫科技·zephyr
sukalot6 天前
windows网络适配器驱动开发-为多个 Windows 版本生成 WiFiCx 驱动程序
windows·驱动开发
TE-茶叶蛋7 天前
Spec-Driven Development(规范驱动开发)
驱动开发