UART 硬件流控 RTS/CTS:为什么 FIFO 再深也需要握手
更新时间:2026-09-19。前置:UART 波特率、过采样与 FIFO 溢出、UART 自动波特率。FIFO 只能吸收短期突发,发送端长期比接收端处理快时必然溢出;硬件流控让接收方在将满前直接「踩刹车」。
本文要回答的问题
- RTS 和 CTS 各自由谁驱动、指示什么?
- 接收 FIFO 水位怎么转成 RTS,发送方何时停?
- 硬件流控和 XON/XOFF、和 RS485 的 DE 有何不同?
- 接线、触发电平和 CTS 响应延迟有哪些坑?
一、两根额外的线
带硬件流控的 UART 在 TX/RX/GND 之外增加:
| 信号 | 全称 | 谁驱动 | 含义(现代常用约定) |
|---|---|---|---|
| RTS# | Request To Send | 接收方驱动 | 「我准备好接收了」(低有效);将满时拉高=停发 |
| CTS# | Clear To Send | 发送方的对端驱动 | 发送方发数据前检测,低=允许发 |
关键:这是交叉连接的一对——A 的 RTS# 接 B 的 CTS#,B 的 RTS# 接 A 的 CTS#。本质上「我(接收者)能不能再收」由我的 RTS# 告诉你,你把它当作你的 CTS#。历史上调制解调器的 RTS/CTS 语义略有不同(请求/允许发送经过 DCE),现代芯片上 UART 流控普遍采用上述硬件握手流控语义。
二、FIFO 水位 → RTS 的闭环
接收方: 数据进 RX FIFO
FIFO 占用 ≥ 高水位(如 3/4 满) → RTS# 拉高(无效) → 通知对方停
对方停发后,软件/DMA 取走数据
FIFO 占用 ≤ 低水位(如 1/4) → RTS# 拉低(有效) → 恢复
发送方: 发送移位前每帧检查 CTS#
CTS# 无效 → 暂停发送(已在移位中的一字节发完,不再启动新字节)用高/低两个水位(滞回)而非单点,避免在阈值附近反复抖动(与去抖迟滞同理)。水位选择要考虑「从 RTS# 生效到对方真正停发」期间还会到达的字节数:对方检测 CTS 有延迟、且已经在线路上/发送移位器里的那一字节无法收回,所以高水位必须给这些在途字节留空间,否则仍溢出。
三、为什么 FIFO 不够、软件流控不行
- FIFO 有限:只是弹性缓冲。若 CPU 被高优先级中断长时间占用、或 DMA 描述符未及时 replenish(见 DMA 描述符环),FIFO 再深也会满;流控提供端到端反压(思想同 valid/ready)。
- XON/XOFF 软件流控:接收方在数据流里插入 0x13(XOFF)/0x11(XON) 字符让发送方停/续。省两根线,但:控制字符会和二进制数据冲突(需转义)、要靠软件响应延迟大、且对纯二进制传输不透明。硬件 RTS/CTS 不占数据、延迟低、对数据透明,高速场景首选。
四、CTS 与发送使能的硬件细节
- UART 通常有 AUTO-CTS 硬件模式:发送逻辑在每个起始位前自动检查 CTS#,无需软件介入,保证响应在一帧以内。若用软件轮询 CTS 再发,响应慢且可能在两字节间来不及停。
- 对应 AUTO-RTS:硬件按 RX FIFO 水位自动驱动 RTS#。
- 流控只解决「接收端处理不过来」,不解决波特率不匹配;波特率错仍帧错(见 自动波特率)。
五、别和 RS485 的 DE/RE 混淆
半双工 RS485 也常用 UART 的一根 RTS 引脚,但用途完全不同:那里它驱动收发器的 DE(驱动器使能)/RE#(接收器使能)做方向切换(发送时使能驱动、发完立刻释放以接收/避免总线冲突),不是流量握手。设计时要分清「流控 RTS」和「RS485 方向 RTS」,很多 UART 有专门的 RS485 模式自动在最后一位后撤 DE。
六、常见的坑
- RTS/CTS 直连不交叉:A.RTS 接 B.RTS,刹车信号送错地方,形同没接。
- 只开一端流控:A 使能 RTS/CTS、B 不使能,A 的 RTS# 悬空或 B 无视 CTS,要么误停要么溢出。
- 悬空的 CTS#:无流控时 CTS# 必须拉到有效电平,否则发送方永久不发。
- 有效电平搞反:多数低有效,极性配反导致要么不停要么永远停。
- 高水位留得太满:没给在途字节(CTS 响应延迟+移位中字节)留空间,还是 overrun。
- 单阈值抖动:在一个水位上反复 toggle,应高低水位滞回。
- 用软件轮询替代 auto-CTS:高波特率下响应不过来,停发太晚。
- 把 RS485 DE 当流控:半双工方向控制与流量握手混用,总线冲突或丢数据。
- 二进制数据用 XON/XOFF:0x11/0x13 出现在 payload 里被误判停发。
- 低功耗下 RTS 不随唤醒:sleep 时流控脚状态错,对端持续发送而本端未醒(见 低功耗)。
- 电平不匹配:3.3V MCU 与 5V/RS232 电平直连(RS232 是 ±电平,需收发器,见 ESD/IO)。
- DMA 模式未联动流控:DMA 一次搬一大块,CTS 撤销期间仍在喂发送保持寄存器,要确保硬件 auto-CTS 能在帧边界停。
相关与延伸
FIFO/溢出机制见 UART 过采样与 FIFO;自动波特率见 ABD;反压思想见 valid/ready、Skid Buffer。
总结
RTS/CTS 硬件流控是由接收方按 RX FIFO 高低水位自动驱动 RTS#(低有效=可收、将满拉高=停发)、交叉接到发送方的 CTS#,发送方用 auto-CTS 在每帧起始前检查、在移位完当前字节后立刻停发的端到端反压闭环;高水位必须为「CTS 响应延迟+线路与移位器中不可收回的在途字节」预留空间并用双水位滞回防抖,它比 XON/XOFF 软件流控更快、对二进制透明,也完全不同于 RS485 里用 RTS 做半双工 DE 方向切换——接线不交叉、只开一端、CTS 悬空、高水位太满和把 DE 当流控是五大常见坑。