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. 实现多串口通信

参考资料

相关推荐
董厂长4 小时前
Loop Engineering:停止手动提示,开始设计自动提示的系统
大数据·人工智能·驱动开发·llm
ScilogyHunter5 小时前
Zephyr Hello World应用开发构建完全指南
zephyr·hello world
Saniffer_SH1 天前
【高清视频】Gen6 服务器还没到,Gen6 SSD 怎么测?Emily 现场演示三种测试环境
人工智能·驱动开发·测试工具·缓存·fpga开发·计算机外设·压力测试
暮云星影1 天前
全志linux开发屏幕适配(二)`HDMI`驱动适配说明
linux·arm开发·驱动开发
charlie1145141911 天前
嵌入式Linux驱动开发——从轮询到中断
linux·开发语言·驱动开发·嵌入式
暮云星影1 天前
瑞芯微rk3566开发FIT Secure Boot
linux·arm开发·驱动开发·安全
ScilogyHunter1 天前
west init 命令详解
init·zephyr·west
ScilogyHunter1 天前
使用Kconfig配置Zephyr工程完全指南
kconfig·zephyr
暮云星影1 天前
全志linux开发 USB接口设置
linux·arm开发·驱动开发