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 支持(内核早期调试)
相关推荐
@郭小茶28 分钟前
docker-compose方式部署docker项目
运维·docker·容器
GalaxyPokemon34 分钟前
Muduo网络库实现 [十] - EventLoopThreadPool模块
linux·服务器·网络·c++
自由鬼40 分钟前
开源虚拟化管理平台Proxmox VE部署超融合
linux·运维·服务器·开源·虚拟化·pve
瞌睡不来1 小时前
(学习总结32)Linux 基础 IO
linux·学习·io
inquisiter1 小时前
UEFI镜像结构布局
linux·spring
Linux运维老纪2 小时前
运维之 Centos7 防火墙(CentOS 7 Firewall for Operations and Maintenance)
linux·安全·centos·云计算·运维开发·火绒
斯普信专业组2 小时前
Ceph异地数据同步之-RBD异地同步复制(下)
linux·服务器·ceph
counsellor2 小时前
CentOS 7安装hyperscan
linux·centos·hyperscan
电星托马斯2 小时前
Linux系统CentOS 6.3安装图文详解
linux·运维·服务器·程序人生·centos
啞謎专家2 小时前
CentOS中挂载新盘LVM指南:轻松扩展存储空间,解决磁盘容量不足问题
linux·运维·服务器