4、硬件流控原理:RTS/CTS 硬件流控的工作机制

硬件流控(Hardware Flow Control)是 UART 通信中防止接收缓冲区溢出的第一道防线。它利用专用的 RTS(Request to Send)和 CTS(Clear to Send)信号线,在物理层实现“发送/停止”的实时握手,无需软件干预,响应延迟极低。

4.1 RTS/CTS 信号定义与连接

在标准 RS-232 或 TTL 串口中,RTS 和 CTS 是独立于 TXD/RXD 的控制信号线。其连接方式为交叉握手:

信号 方向(以设备 A 视角) 功能描述
RTS 输出 设备 A 通知设备 B:“我准备好接收数据,请发送”
CTS 输入 设备 A 检测设备 B 的应答:“你可以发送,我已准备好接收”

典型连接方式:设备 A 的 RTS 接设备 B 的 CTS,设备 A 的 CTS 接设备 B 的 RTS,形成双向握手回路。

关键区别: 软件流控(XON/XOFF)在数据线上传输特殊字符,可能被误判为数据;硬件流控使用独立信号线,不受数据内容干扰,可靠性更高。

4.2 硬件流控工作流程

RTS/CTS 硬件流控的核心逻辑基于接收缓冲区的“水位线”(Watermark)阈值。当接收 FIFO 或缓冲区中的数据量达到预设的高水位线时,接收方拉高 RTS 信号(无效状态),通知发送方暂停发送;当数据被读取、缓冲区降至低水位线时,接收方拉低 RTS(有效状态),允许发送方继续传输。

水位线设计原则: 高水位线通常设为 FIFO 深度的 75%~90%,低水位线设为 25%~50%。留出足够的余量,防止在信号传播延迟期间发生溢出。
RTS/CTS 硬件流控状态机与水位线触发逻辑 硬件流控状态机:基于接收缓冲区水位线的自动切换 空闲/发送状态 RTS=低(有效) 允许发送方传输 暂停发送状态 RTS=高(无效) 禁止发送方传输 恢复发送状态 RTS=低(有效) 允许继续传输 FIFO ≥ 高水位 FIFO ≤ 低水位 继续发送,回到正常状态 接收 FIFO 水位线示意图 当前数据量:80% 高水位 (90%) 低水位 (30%) 溢出风险区

4.3 何时发送 / 何时停止:信号电平逻辑

硬件流控使用负逻辑或正逻辑取决于接口标准。以下以 TTL 电平(正逻辑)为例:

接收方状态 RTS 电平 发送方行为 说明
缓冲区充足(低于高水位) 低电平(有效) 继续发送数据 正常传输状态
缓冲区达到高水位 高电平(无效) 立即停止发送 发送方检测到 CTS 无效后,应在发送完当前字节后暂停
缓冲区降至低水位 低电平(有效) 恢复发送数据 接收方已读取足够数据,释放空间
硬件流控的潜在陷阱: 发送方在检测到 CTS 无效后,可能已经将下一个字节移入发送移位寄存器。因此,接收方的高水位必须预留至少 1~2 字节的余量,避免在信号传播期间发生溢出。

4.4 解决接收缓冲区溢出的核心机制

接收缓冲区溢出(RX Overrun)是指 UART 硬件在接收 FIFO 已满时,仍然接收到新的起始位,导致数据丢失。硬件流控通过以下方式防止溢出:

  1. 实时反馈: 接收方通过 RTS 信号实时告知发送方缓冲区状态,延迟仅为信号线传播时间(纳秒级)。
  2. 自动暂停: 发送方硬件在 CTS 无效时自动暂停发送,无需 CPU 中断处理,适合高速或大数据量场景。
  3. 水位线保护: 高水位线设置在 FIFO 满之前,留出余量吸收信号延迟期间的残余数据。
硬件流控防止接收缓冲区溢出时序图 硬件流控防止溢出时序图 t0 t1 t2 t3 RTS 低(允许发送) 高(暂停) 低(恢复) CTS TXD 数据包 1 暂停 数据包 2 RX FIFO 低水位 高水位 → 暂停 低水位

4.5 硬件流控的适用场景与局限性

场景 推荐使用硬件流控? 原因
高速传输(≥1 Mbps) ✅ 强烈推荐 软件流控无法及时响应,硬件流控延迟最低
长距离通信(>10 米) ⚠️ 需谨慎 信号线增加,易受干扰;RS-485 更常用
多设备总线拓扑 ❌ 不适用 RTS/CTS 是点对点信号,无法在总线上共享
低功耗嵌入式设备 ✅ 推荐 减少 CPU 中断唤醒次数,降低功耗
已有软件流控的系统 ⚠️ 需评估 硬件流控需要额外引脚和线缆,改造成本高
工程建议: 在驱动开发中,优先启用硬件流控(如果硬件支持且引脚可用)。对于不支持硬件流控的场景,必须实现软件超时重传机制作为后备方案(后续章节展开)。

4.6 驱动开发中的配置示例

在 Linux 内核串口驱动中,通过 termios 结构体启用硬件流控:

#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int uart_set_hw_flowctrl(int fd) {
    struct termios options;
    
    // 获取当前串口参数
    tcgetattr(fd, &options);
    
    // 启用 RTS/CTS 硬件流控(CRTSCTS 标志)
    options.c_cflag |= CRTSCTS;
    
    // 设置接收 FIFO 触发阈值(部分硬件支持)
    // 例如:设置为 FIFO 深度的 3/4
    // ioctl(fd, TIOCSFIFOTHRESH, &threshold);
    
    // 应用设置
    if (tcsetattr(fd, TCSANOW, &options) != 0) {
        perror("tcsetattr failed");
        return -1;
    }
    
    return 0;
}
调试检查: 使用 stty -F /dev/ttyS0 -a 命令查看当前串口参数,确认 crtscts 标志已生效。如果 CTS 信号未连接,启用硬件流控可能导致发送方永久阻塞。