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硬件模块的内部结构以及数据流路径:
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数据帧在串行总线上的时序波形:
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驱动的完整分层架构以及数据流路径:
驱动开发注意事项:
- UART驱动层的中断处理函数必须在
startup()中注册,并在shutdown()中释放。 - 波特率设置通过
set_termios()回调实现,内部需计算分频系数并写入硬件寄存器。 - FIFO的使用可以显著降低中断频率,但需要根据硬件规格配置触发阈值。
- 流控(硬件RTS/CTS或软件XON/XOFF)通常在
set_termios()中配置。