Linux UART 驱动开发全解析:从原理到实战

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 驱动调试方法

  1. 确认串口是否注册成功:

    bash 复制代码
    dmesg | grep tty
    ls /dev/ttyS*
  2. 查看设备树配置是否正确:

    bash 复制代码
    cat /proc/device-tree/soc/serial@xxxx/compatible
  3. 使用 minicom 或 picocom 测试通信:

    bash 复制代码
    picocom -b 115200 /dev/ttyS0
  4. 查看中断是否触发:

    bash 复制代码
    cat /proc/interrupts | grep uart
  5. 打印寄存器状态排查问题:

    在驱动中加入 readl/writel 打印,检查波特率、TX/RX FIFO 状态等。


七、总结与扩展

本文完整解析了 UART 的通信原理、SoC 控制器结构、驱动架构与实现方式,帮助你从零理解并实现一个 Linux 串口驱动。

推荐扩展学习方向:

  • DMA 模式串口传输
  • RS485/Modbus 协议支持
  • 异常处理与功耗管理
  • Console earlyprintk 支持(内核早期调试)
相关推荐
mennekes1 小时前
临时用电线路标识:插头颜色、钟面标识配套规范
运维·网络·科技·安全·制造
程序猿老A3 小时前
轻量云服务器总是卡,内存占比高?
运维·服务器
一只鹿鹿鹿4 小时前
智算算力、AI-芯片与产品解析(PPT文件)
大数据·运维·网络·人工智能·物联网·1024程序员节
沫璃染墨4 小时前
从零入门计算机网络系列(十一):HTTP协议进阶——从重定向原理到GET/POST与Fiddler抓包
linux·服务器·开发语言·网络·c++·tcp/ip·html
Kobebryant-Manba5 小时前
C/S和B/S结构
运维·服务器
AI_Lab016 小时前
宝塔用了两年,有没有必要换成轻量方案?
linux·运维·服务器
众壹新能源科技6 小时前
逆变器直流开关柜温度监测与预防:红外巡检发现3个120度热点
运维·人工智能·自动化
by209996 小时前
Linux GDB 调试与 CGDB 使用
linux·运维·服务器·经验分享
小Ti客栈6 小时前
Netty 零拷贝原理与实战
运维·服务器·网络
ai_coder_ai7 小时前
AIOps 智能运维架构设计及其应用
运维