4. UART 通信模式:单工、半双工、全双工对比与应用场景分析

UART 作为最基础的串行通信接口,其通信模式决定了数据在设备间的流动方式。理解单工、半双工、全双工三种模式的区别,是设计高效 UART 驱动的关键前提。本章将从物理层信号流向、驱动实现差异、典型应用场景三个维度进行系统对比。

💡 核心概念: 通信模式由数据线数量与方向控制逻辑共同决定。UART 硬件本身支持全双工(独立的 TX/RX 线),但上层协议或应用场景可能将其限制为半双工或单工模式。
UART 三种通信模式对比 设备 A (发送) TX 设备 B (接收) RX 单向 单工 设备 A TX/RX 设备 B TX/RX 分时双向 半双工 设备 A TX / RX 设备 B RX / TX 同时双向 全双工

4.1 单工模式 (Simplex)

单工模式下,数据只能沿一个固定方向传输,接收端无法向发送端回传任何数据。UART 硬件只需连接 TX → RX 一条数据线,GND 共地。

4.1.1 技术特征

  • 数据线数量: 1 根(TX → RX)+ GND
  • 方向控制: 无,硬件固定
  • 驱动实现: 发送端只需 TX FIFO 写操作,接收端只需 RX FIFO 读操作
  • 流控: 不需要硬件流控(RTS/CTS)

4.1.2 典型应用场景

场景 说明 驱动关注点
传感器数据上报 温度/湿度传感器只向主机发送数据 接收端中断或 DMA 读取
广播式日志输出 嵌入式设备通过 UART 输出调试日志 发送端轮询或 DMA 写入
单向 LED 控制 主机发送命令控制 LED 灯板 无需接收处理
🔧 驱动提示: 单工模式下,接收端驱动无需考虑发送逻辑,可简化中断处理函数。但需注意接收 FIFO 溢出问题——如果主机读取不及时,数据会被覆盖。

4.2 半双工模式 (Half-Duplex)

半双工模式下,通信双方可以互相发送数据,但同一时刻只能有一方发送。典型实现是使用单根数据线(TX/RX 复用),或通过软件控制方向切换。

4.2.1 技术特征

  • 数据线数量: 1 根(TX/RX 共用)+ GND,或 2 根但软件控制方向
  • 方向控制: 需要 GPIO 或协议层切换(如 RS-485 的 DE/RE 引脚)
  • 驱动实现: 发送前需将 UART 模式切换为 TX,发送完成后切换回 RX
  • 流控: 通常需要软件协议避免冲突(如 Modbus 的主从轮询)

4.2.2 方向切换时序

/* 半双工模式切换示例(RS-485 驱动) */
void uart_halfduplex_tx_start(struct uart_port *port)
{
    /* 1. 等待当前接收完成 */
    while (!(readl(port->membase + UART_LSR) & LSR_TEMT));
    
    /* 2. 切换 GPIO 为发送模式(使能 DE) */
    gpiod_set_value(port->rs485->de_gpio, 1);
    
    /* 3. 使能发送器,禁用接收器 */
    uart_update_mctrl(port, TIOCM_RTS, 0);
}

void uart_halfduplex_tx_stop(struct uart_port *port)
{
    /* 1. 等待发送移位寄存器空 */
    while (!(readl(port->membase + UART_LSR) & LSR_TEMT));
    
    /* 2. 切换 GPIO 为接收模式(使能 RE) */
    gpiod_set_value(port->rs485->de_gpio, 0);
    
    /* 3. 使能接收器 */
    uart_update_mctrl(port, TIOCM_RTS, 1);
}

4.2.3 典型应用场景

场景 说明 驱动关注点
RS-485 工业总线 Modbus RTU 协议,主从轮询通信 方向切换时序、发送完成等待
单线 UART(1-Wire 模拟) 调试接口或低成本传感器网络 开漏输出、上拉电阻、位时序
红外通信 (IrDA) 半双工红外收发 编码/解码、载波调制
⚠️ 常见陷阱: 半双工驱动中最容易出错的是方向切换的“死锁”——发送完成后未及时切回接收模式,导致对方发送的数据丢失。建议在发送完成中断中立即切换方向,并使用定时器做超时保护。

4.3 全双工模式 (Full-Duplex)

全双工模式下,通信双方可以同时发送和接收数据。UART 硬件原生支持此模式,因为 TX 和 RX 是独立的物理线路和 FIFO。

4.3.1 技术特征

  • 数据线数量: 2 根(TX ↔ RX 交叉连接)+ GND
  • 方向控制: 无,硬件同时支持收发
  • 驱动实现: 发送和接收完全独立,可并行处理
  • 流控: 需要硬件流控(RTS/CTS)防止 FIFO 溢出

4.3.2 全双工驱动架构

全双工 UART 驱动架构 用户空间 (User Space) 内核空间 (Kernel Space) — 串行核心层 发送 FIFO / DMA 接收 FIFO / DMA UART 硬件 (TX/RX 独立 FIFO + 移位寄存器) RTS/CTS 流控

4.3.3 典型应用场景

场景 说明 驱动关注点
标准 UART 调试串口 Linux 内核早期启动日志 + 交互式 shell 中断处理、环形缓冲区
蓝牙 HCI UART 传输 蓝牙芯片与主机间的 HCI 命令/事件/数据 DMA 传输、硬件流控
GPS 模块数据接收 GPS 持续发送 NMEA 语句,主机可发送配置命令 高波特率、DMA 接收
高速串口屏通信 MCU 发送绘图命令,屏幕返回触摸事件 双缓冲、DMA 乒乓操作

4.4 三种模式综合对比

对比维度 单工 半双工 全双工
数据流向 单向固定 双向分时 双向同时
物理线数 1 根 + GND 1 根 + GND 或 2 根 2 根 + GND
硬件流控 不需要 可选(RS-485 需 DE/RE) 推荐(RTS/CTS)
驱动复杂度 低 中(方向切换逻辑) 高(并发收发 + 流控)
吞吐量 低(单向) 中(需切换时间) 高(同时双向)
典型波特率 9600 ~ 115200 9600 ~ 115200 115200 ~ 3Mbps+
Linux 驱动支持 标准 UART 驱动 需 RS-485 半双工扩展 标准 UART 驱动 + 流控

4.5 驱动开发中的模式选择策略

📋 决策指南:
  1. 优先选择全双工 — 如果硬件支持且线数允许,全双工驱动最简单(无需方向切换),且吞吐量最高。
  2. 半双工用于长距离/多节点 — RS-485 总线必须使用半双工,驱动需重点处理方向切换时序和总线冲突检测。
  3. 单工用于纯数据采集 — 如果设备只发不收(如传感器),单工模式可节省 CPU 中断开销。
  4. 流控匹配 — 全双工高波特率必须启用硬件流控,否则 FIFO 溢出会导致数据丢失。

4.5.1 模式切换的驱动接口

在 Linux 内核中,通过 termios 结构体或 TIOCSRS485 ioctl 配置通信模式:

/* 设置半双工 RS-485 模式 */
struct serial_rs485 rs485conf;

rs485conf.flags = SER_RS485_ENABLED;         /* 启用半双工 */
rs485conf.flags |= SER_RS485_RTS_ON_SEND;    /* 发送时 RTS 高 */
rs485conf.flags |= SER_RS485_RTS_AFTER_SEND; /* 发送后 RTS 低 */
rs485conf.delay_rts_before_send = 1;         /* 发送前延迟 1ms */
rs485conf.delay_rts_after_send = 1;          /* 发送后延迟 1ms */

ioctl(fd, TIOCSRS485, &rs485conf);

/* 设置全双工模式(默认) */
struct termios2 tio;
ioctl(fd, TCGETS2, &tio);
tio.c_cflag &= ~CMSPAR;  /* 清除半双工特殊标志 */
ioctl(fd, TCSETS2, &tio);
💡 经验之谈: 在嵌入式 Linux 中,UART 驱动默认工作在全双工模式。如果需要半双工 RS-485,务必在设备树中配置 rs485-mode 属性,并在驱动中实现 uart_ops->rs485_config() 回调函数。

4.6 本章小结

  • 单工:最简单,适合单向数据流场景,驱动只需处理单方向 FIFO。
  • 半双工:需要方向切换逻辑,驱动复杂度中等,常见于 RS-485 工业总线。
  • 全双工:UART 原生模式,吞吐量最高,驱动需处理并发收发和硬件流控。
  • 选择依据:根据物理层标准(RS-232/RS-485)、线数限制、吞吐量需求和协议要求综合决定。