UART(Universal Asynchronous Receiver/Transmitter)串口通信接口是嵌入式系统中最基础、最常用的外设之一,广泛用于系统调试、终端通信、外设互联等场景。本文将从通信原理、SoC 内部控制器结构、Linux 驱动架构、设备树配置、驱动实现与调试等多个方面,全面解析 UART 驱动的开发流程。

一、UART 通信原理简介
UART 是一种异步串行通信协议,通过 TX(发送)和 RX(接收)两根数据线进行数据传输。常见通信参数包括:
- 波特率(Baud Rate):如 9600、115200 等,代表每秒传输的符号数;
- 数据位(Data Bits):常为 8 位;
- 校验位(Parity Bit):可选,支持奇偶校验;
- 停止位(Stop Bit):通常为 1 位或 2 位。
通信时序如下:
| 起始位(Start Bit) | 数据位(8位) | 校验位 | 停止位 |
| 0 | 10101010 | 1 | 1 |
UART 通信无需时钟同步,适用于点对点、低速通信,具有电路简单、开销低的特点。
二、SoC 中的 UART 控制器原理
UART 控制器是串口通信的核心硬件模块。在 SoC(System on Chip)内部,通常集成多个 UART 控制器,每个控制器通过片上总线(如 AMBA APB/AHB)与 CPU 通信,驱动程序通过映射的 MMIO 寄存器操作控制器。
1. UART 控制器结构组成
一个标准的 UART 控制器包含以下子模块:
模块 | 功能说明 |
---|---|
波特率发生器(Baud Rate Generator) | 通过分频器将主频转换为目标波特率 |
发送 FIFO(TX FIFO) | 发送缓冲区,一般为 16~64 字节 |
接收 FIFO(RX FIFO) | 接收缓冲区 |
中断控制器 | 支持 TX 空、RX 满等中断 |
状态管理逻辑 | 表示忙碌、错误等状态 |
配置/控制寄存器接口 | 供驱动程序控制和读取状态 |
2. 常见寄存器接口(以通用 UART 控制器为例)
寄存器名 | 功能说明 |
---|---|
DR |
Data Register,读写数据 |
FR |
Flag Register,状态标志,如 FIFO 满/空 |
IBRD |
整数分频设置(波特率) |
FBRD |
小数分频设置(波特率) |
CR |
Control Register,启用 TX/RX、使能串口 |
IMSC |
Interrupt Mask,配置中断源 |
3. 数据传输流程
发送过程:
CPU --> DR寄存器 --> TX FIFO --> 串口发送逻辑 --> TX 引脚输出
接收过程:
RX 引脚输入 --> 接收逻辑 --> RX FIFO --> DR寄存器 --> CPU读取
支持中断方式和 DMA 模式。
三、Linux 串口驱动框架分析
在 Linux 内核中,UART 驱动属于字符设备驱动,其框架包括以下几个层级:
应用层程序(终端/串口调试器)
↓
TTY 子系统(ttyS、ttyAMA、ttyPS 等)
↓
串口驱动(serial_core + UART 控制器驱动)
↓
硬件 UART 控制器(SoC 内部 IP)
1. 串口驱动层次结构
层级 | 说明 |
---|---|
应用层 | 使用标准 I/O 接口打开 /dev/ttyS0 |
TTY 层 | 提供标准串口 API,如 open , read , write , ioctl |
serial_core | Linux 内核串口核心框架,统一管理 UART 驱动 |
UART 驱动 | 硬件控制器的具体实现,提供寄存器读写、FIFO 操作等 |
硬件层 | SoC 内部 UART 控制器,接收 CPU 操作并驱动 TX/RX 引脚 |
2. 串口驱动关键结构体
struct uart_driver
:注册给 serial_core,表示一个驱动;struct uart_ops
:操作函数集,如start_tx
,stop_tx
,startup
,shutdown
;struct uart_port
:描述一个 UART 控制器的所有状态,如地址、波特率、中断号等。
四、设备树配置 UART 控制器
现代 ARM 平台采用设备树描述硬件 UART 控制器。例如:
dts
&uart2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart2>;
status = "okay";
};
设备树会通过 compatible 属性匹配驱动,驱动通过 of_match_table
自动加载。
五、UART 驱动代码实现(简化版)
以下为简化后的 UART 驱动骨架:
c
static struct uart_ops my_uart_ops = {
.startup = my_uart_startup,
.shutdown = my_uart_shutdown,
.tx_empty = my_uart_tx_empty,
.start_tx = my_uart_start_tx,
.stop_tx = my_uart_stop_tx,
.set_termios = my_uart_set_termios,
// ...
};
static struct uart_driver my_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "my_uart",
.dev_name = "ttyMY",
.major = 204,
.minor = 64,
.nr = 1,
};
static int __init my_uart_init(void)
{
uart_register_driver(&my_uart_driver);
// 注册 port,调用 uart_add_one_port()
}
注意:完整驱动需实现中断处理函数、FIFO 操作逻辑、寄存器读写等。
六、UART 驱动调试方法
-
确认串口是否注册成功:
bashdmesg | grep tty ls /dev/ttyS*
-
查看设备树配置是否正确:
bashcat /proc/device-tree/soc/serial@xxxx/compatible
-
使用 minicom 或 picocom 测试通信:
bashpicocom -b 115200 /dev/ttyS0
-
查看中断是否触发:
bashcat /proc/interrupts | grep uart
-
打印寄存器状态排查问题:
在驱动中加入
readl
/writel
打印,检查波特率、TX/RX FIFO 状态等。
七、总结与扩展
本文完整解析了 UART 的通信原理、SoC 控制器结构、驱动架构与实现方式,帮助你从零理解并实现一个 Linux 串口驱动。
推荐扩展学习方向:
- DMA 模式串口传输
- RS485/Modbus 协议支持
- 异常处理与功耗管理
- Console earlyprintk 支持(内核早期调试)