1. UART驱动基础:硬件原理、波特率生成、数据帧格式与Linux串口子系统架构概览
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是嵌入式系统中最基础、最广泛使用的串行通信接口。理解其硬件原理、时序参数以及Linux内核中的软件抽象层,是后续优化中断处理函数和分析响应延迟的基石。本节将从物理层到驱动框架,系统梳理这些核心概念。
1.1 UART硬件原理与信号线
UART是一种全双工、异步、串行通信接口。所谓“异步”,是指通信双方不需要共享同一时钟信号,而是通过预先约定好的波特率(Baud Rate)来同步数据采样点。典型的UART硬件模块包含以下关键信号线:
| 信号线 | 名称 | 方向 | 功能描述 |
|---|---|---|---|
| TXD | Transmit Data | 输出 | 发送串行数据 |
| RXD | Receive Data | 输入 | 接收串行数据 |
| RTS | Request to Send | 输出 | 硬件流控:请求发送 |
| CTS | Clear to Send | 输入 | 硬件流控:允许发送 |
| GND | Ground | 公共 | 信号地参考 |
关键概念: 在嵌入式Linux驱动开发中,RTS/CTS硬件流控常用于高速传输场景(如115200 bps以上),可以有效防止接收缓冲区溢出导致的丢帧问题。中断处理函数中需要检查CTS状态变化,以决定是否暂停发送。
下图展示了UART模块内部的核心功能单元及其与外部设备的连接关系:
1.2 波特率生成原理
波特率决定了每秒传输的符号数(单位:Baud)。在UART中,1 Baud通常对应1 bit/s(当使用NRZ编码时)。波特率的生成依赖于系统时钟分频:
波特率 = 系统时钟频率 / (16 × 分频系数)
其中:
- 系统时钟频率:UART模块的输入时钟(如50 MHz)
- 分频系数:可编程的16位或32位寄存器值
- 16:过采样因子(oversampling factor),常见为16倍
实际案例: 某嵌入式平台UART时钟为50 MHz,目标波特率为115200 bps。计算分频系数:
分频系数 = 50,000,000 / (16 × 115200) ≈ 27.13 → 取整为27。
实际波特率 = 50,000,000 / (16 × 27) ≈ 115,740 bps,误差约0.47%,在可接受范围内(通常要求 < 2%)。
分频系数 = 50,000,000 / (16 × 115200) ≈ 27.13 → 取整为27。
实际波特率 = 50,000,000 / (16 × 27) ≈ 115,740 bps,误差约0.47%,在可接受范围内(通常要求 < 2%)。
下表列出了常用波特率及其对应的分频系数(假设系统时钟为50 MHz,过采样因子为16):
| 目标波特率 (bps) | 理论分频系数 | 取整分频系数 | 实际波特率 (bps) | 误差 (%) |
|---|---|---|---|---|
| 9600 | 325.52 | 326 | 9585 | -0.16 |
| 19200 | 162.76 | 163 | 19172 | -0.15 |
| 115200 | 27.13 | 27 | 115740 | +0.47 |
| 921600 | 3.39 | 3 | 1041667 | +13.02 |
注意: 当波特率较高(如921600 bps以上)时,分频系数较小,取整误差会显著增大。此时应考虑使用更高的系统时钟或支持小数分频的UART控制器,否则会导致通信误码率上升。
1.3 数据帧格式
UART数据帧由起始位、数据位、可选的奇偶校验位和停止位组成。通信双方必须配置完全相同的帧格式才能正确解析数据。
常见的帧格式配置选项:
- 数据位: 5、6、7、8位(常用8位)
- 奇偶校验: 无校验(N)、奇校验(O)、偶校验(E)、标记校验(M)、空格校验(S)
- 停止位: 1、1.5、2位
驱动开发要点: 在Linux串口驱动中,帧格式通过struct termios结构体配置。中断处理函数需要根据当前配置的帧长度计算接收超时阈值,避免在接收短帧时产生不必要的延迟。
1.4 Linux串口子系统架构概览
Linux内核的串口子系统采用分层架构设计,从底层的硬件操作到上层的系统调用接口,共分为四个层次:
各层次的核心职责与关键数据结构:
| 层次 | 核心数据结构 | 主要职责 |
|---|---|---|
| 用户空间 | 文件描述符 | 通过标准文件I/O接口操作串口设备 |
| 系统调用层 | struct file_operations | 将用户空间调用映射到TTY层操作 |
| TTY核心层 | struct tty_struct, struct tty_port | 线路规程处理、数据缓冲、流量控制 |
| UART驱动层 | struct uart_driver, struct uart_port | 硬件寄存器操作、中断处理、DMA传输 |
| 硬件层 | MMIO寄存器地址 | 物理UART控制器的读写操作 |
驱动开发关注点: 在UART驱动层,struct uart_port结构体中的irq字段指定了中断号,而ops结构体中的start_tx、stop_tx、startup、shutdown等回调函数是中断处理函数优化的主要切入点。后续章节将深入分析这些回调函数中的延迟来源。
1.5 中断处理在串口驱动中的位置
中断处理函数是UART驱动中连接硬件事件与软件协议栈的桥梁。当UART控制器接收到数据或完成发送时,会触发中断信号,CPU执行对应的中断服务程序(ISR)。典型的接收中断处理流程如下:
- 中断触发: UART接收FIFO达到预设阈值(如1字节或FIFO半满)
- 中断响应: CPU保存上下文,跳转到注册的中断处理函数
- 数据读取: ISR从UART接收FIFO读取数据,存入临时缓冲区
- 数据推送: 调用tty_insert_flip_char或tty_insert_flip_string将数据送入TTY层
- 唤醒等待: 调用tty_flip_buffer_push唤醒等待读取的用户进程
性能陷阱: 如果ISR中执行了过多的耗时操作(如复杂的协议解析、内存分配、锁竞争),会导致中断响应延迟增加,进而引发FIFO溢出丢帧。这是本课程后续重点优化的方向。
通过本节的学习,你已经掌握了UART的硬件基础、波特率计算、帧格式以及Linux串口子系统的分层架构。这些知识为后续深入分析中断处理函数的优化策略和响应延迟的测量方法奠定了坚实的基础。