1. UART驱动基础:UART硬件原理、串口通信协议、Linux内核中UART驱动框架概述

1.1 UART硬件原理

UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是一种用于串行通信的硬件接口。它负责将并行数据转换为串行比特流进行发送,并将接收到的串行比特流转换为并行数据。UART是嵌入式系统中使用最广泛的通信接口之一,常用于与传感器、GPS模块、蓝牙模块以及调试控制台进行数据交换。

核心硬件组成:
  • 波特率发生器(Baud Rate Generator): 通过分频系统时钟产生精确的比特率时钟,决定数据传输速率。
  • 发送移位寄存器(Transmit Shift Register, TSR): 将并行数据逐位移出到TX引脚。
  • 接收移位寄存器(Receive Shift Register, RSR): 从RX引脚逐位移入数据,组合成并行字节。
  • 发送保持寄存器(Transmit Holding Register, THR): CPU写入待发送数据的缓冲寄存器。
  • 接收缓冲寄存器(Receive Buffer Register, RBR): CPU读取已接收数据的缓冲寄存器。
  • 控制与状态寄存器: 配置数据格式、中断使能、查询线路状态(如溢出、帧错误、奇偶校验错误)。

下图展示了UART硬件模块的内部结构以及数据流路径:

UART硬件内部结构图 UART 硬件内部结构图 CPU 总线接口 控制/状态寄存器 发送保持寄存器 (THR) 接收缓冲寄存器 (RBR) 发送移位寄存器 (TSR) 接收移位寄存器 (RSR) 波特率发生器 TX 引脚 RX 引脚 数据流方向: 发送:CPU → THR → TSR → TX 引脚 | 接收:RX 引脚 → RSR → RBR → CPU

1.2 串口通信协议

UART通信协议定义了数据在串行链路上的传输格式。通信双方必须配置相同的参数才能正确收发数据。一个标准的UART数据帧包含以下部分:

字段 长度 说明
起始位 1 bit 逻辑低电平,标志数据帧开始,同步接收器时钟。
数据位 5 ~ 9 bits 实际传输的数据内容,通常为8 bits(一个字节)。LSB先发送。
奇偶校验位 0 / 1 bit 可选,用于简单的错误检测(奇校验/偶校验/无校验)。
停止位 1 / 1.5 / 2 bits 逻辑高电平,标志数据帧结束,为下一帧起始位提供间隔。
常见配置组合: 嵌入式Linux调试串口通常使用 115200 8N1(波特率115200,8数据位,无校验,1停止位)。这是最广泛使用的标准配置。

下图展示了UART数据帧在串行总线上的时序波形:

UART数据帧时序图 UART 数据帧时序图(8N1 格式) 空闲 起始位 D0 (LSB) D1 D2 D3 D4 D5 D6 D7 (MSB) 停止位 下一帧 注意:数据位 D0 为最低有效位(LSB),D7 为最高有效位(MSB),先发送 LSB。

1.3 Linux内核中UART驱动框架概述

Linux内核为串口设备提供了高度抽象化的驱动框架,主要分为三个层次:TTY核心层、线路规程层和UART驱动层。这种分层设计使得驱动开发者只需关注底层硬件操作,而无需重复实现复杂的TTY语义。

层次 文件/模块 职责
TTY核心层 drivers/tty/tty_io.c 提供字符设备接口(open/read/write/ioctl),管理会话、进程组、行编辑缓冲。
线路规程层 drivers/tty/n_tty.c 实现数据加工(如回车换行转换、信号字符处理),默认规程为 N_TTY。
UART驱动层 drivers/tty/serial/ 直接操作UART硬件寄存器,实现中断处理、数据收发、波特率设置、流控管理。

UART驱动层是本章的核心关注点。该层通过注册 struct uart_driver 和 struct uart_port 来与TTY核心交互。关键数据结构如下:

// 定义在 include/linux/serial_core.h
struct uart_driver {
    struct module           *owner;
    const char              *driver_name;   // 驱动名称,如 "serial"
    const char              *dev_name;      // 设备名称,如 "ttyS"
    int                      major;         // 主设备号
    int                      minor;         // 起始次设备号
    int                      nr;            // 支持的端口数量
    struct uart_state       *state;         // 端口状态数组
};

struct uart_port {
    spinlock_t               lock;          // 端口自旋锁
    unsigned long            iobase;        // IO基地址
    unsigned char __iomem   *membase;       // 内存映射基地址
    unsigned int             irq;           // 中断号
    unsigned int             uartclk;       // UART输入时钟频率
    unsigned int             fifosize;      // FIFO大小
    struct device           *dev;           // 设备结构体
    const struct uart_ops   *ops;           // 硬件操作函数集
};
uart_ops 核心回调函数:
  • start_tx():启动发送引擎,使能发送中断或DMA。
  • stop_tx():停止发送引擎。
  • start_rx():启动接收引擎,使能接收中断。
  • set_termios():配置波特率、数据位、校验、停止位等参数。
  • startup():初始化硬件,申请中断,使能端口。
  • shutdown():关闭端口,释放中断。

下图展示了Linux内核中UART驱动的完整分层架构以及数据流路径:

Linux UART驱动分层架构图 Linux UART 驱动分层架构图 用户空间 (User Space) 应用程序:open("/dev/ttyS0", ...) / read() / write() TTY 核心层 (tty_io.c) 字符设备接口、会话管理、ioctl 分发 线路规程层 (n_tty.c) 数据加工、行缓冲、信号字符处理 UART 驱动层 (serial_core.c + 具体驱动) uart_ops:start_tx / stop_tx / set_termios / startup 硬件层 (UART 外设) 寄存器操作、中断控制器、DMA 引擎 关键文件 tty_io.c tty_port.c 关键文件 n_tty.c ldisc.c 关键文件 serial_core.c 8250_core.c
驱动开发注意事项:
  • UART驱动层的中断处理函数必须在 startup() 中注册,并在 shutdown() 中释放。
  • 波特率设置通过 set_termios() 回调实现,内部需计算分频系数并写入硬件寄存器。
  • FIFO的使用可以显著降低中断频率,但需要根据硬件规格配置触发阈值。
  • 流控(硬件RTS/CTS或软件XON/XOFF)通常在 set_termios() 中配置。