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%。留出足够的余量,防止在信号传播延迟期间发生溢出。
4.3 何时发送 / 何时停止:信号电平逻辑
硬件流控使用负逻辑或正逻辑取决于接口标准。以下以 TTL 电平(正逻辑)为例:
| 接收方状态 | RTS 电平 | 发送方行为 | 说明 |
|---|---|---|---|
| 缓冲区充足(低于高水位) | 低电平(有效) | 继续发送数据 | 正常传输状态 |
| 缓冲区达到高水位 | 高电平(无效) | 立即停止发送 | 发送方检测到 CTS 无效后,应在发送完当前字节后暂停 |
| 缓冲区降至低水位 | 低电平(有效) | 恢复发送数据 | 接收方已读取足够数据,释放空间 |
硬件流控的潜在陷阱: 发送方在检测到 CTS 无效后,可能已经将下一个字节移入发送移位寄存器。因此,接收方的高水位必须预留至少 1~2 字节的余量,避免在信号传播期间发生溢出。
4.4 解决接收缓冲区溢出的核心机制
接收缓冲区溢出(RX Overrun)是指 UART 硬件在接收 FIFO 已满时,仍然接收到新的起始位,导致数据丢失。硬件流控通过以下方式防止溢出:
- 实时反馈: 接收方通过 RTS 信号实时告知发送方缓冲区状态,延迟仅为信号线传播时间(纳秒级)。
- 自动暂停: 发送方硬件在 CTS 无效时自动暂停发送,无需 CPU 中断处理,适合高速或大数据量场景。
- 水位线保护: 高水位线设置在 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 信号未连接,启用硬件流控可能导致发送方永久阻塞。