本文目标:掌握FIFO缓冲区设计、环形缓冲区实现和硬件/软件流控机制
### 目录
- [@TOC(目录)](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [硬件FIFO配置](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [FIFO深度与触发级别](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [软件环形缓冲区](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [完整实现](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [线程安全设计](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [硬件流控(RTS/CTS)](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [工作原理](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [配置代码](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [软件流控(XON/XOFF)](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [协议定义](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [缓冲区大小计算](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [公式推导](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [场景化配置](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [流控失效问题排查](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [常见原因](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
- [实际案例:大文件传输](#目录 @TOC 硬件FIFO配置 FIFO深度与触发级别 软件环形缓冲区 完整实现 线程安全设计 硬件流控(RTS/CTS) 工作原理 配置代码 软件流控(XON/XOFF) 协议定义 缓冲区大小计算 公式推导 场景化配置 流控失效问题排查 常见原因 实际案例:大文件传输)
硬件FIFO配置
FIFO深度与触发级别
典型UART控制器的FIFO深度:
- 基础型:16字节(16550兼容)
- 增强型:64/128字节(STM32、NXP)
- 高性能:256/512字节(部分工业级芯片)
c
// STM32 UART FIFO配置
void uart_fifo_config(void) {
// 使能FIFO
UART->CR1 |= USART_CR1_FIFOEN;
// 配置接收FIFO触发级别
UART->CR3 &= ~USART_CR3_RXFTCFG;
UART->CR3 |= USART_CR3_RXFTCFG_1_8; // 1/8满触发(2字节)
// 选项:1/8, 1/4, 1/2, 3/4, 7/8, Full
// 配置发送FIFO触发级别
UART->CR3 &= ~USART_CR3_TXFTCFG;
UART->CR3 |= USART_CR3_TXFTCFG_1_2; // 1/2空触发
// 使能FIFO阈值中断
UART->CR3 |= USART_CR3_RXFTIE;
}
触发级别选择原则:
- 接收:低阈值(1/8-1/4)→ 低延迟,防止溢出
- 发送:中等阈值(1/2)→ 平衡中断频率和响应速度
- 高速传输:高阈值(3/4-7/8)→ 减少中断次数
软件环形缓冲区
完整实现
c
#define RING_BUF_SIZE 256 // 必须是2的幂(优化取模运算)
typedef struct {
uint8_t buffer[RING_BUF_SIZE];
volatile uint16_t head; // 写指针
volatile uint16_t tail; // 读指针
} ring_buffer_t;
// 初始化
void ring_buf_init(ring_buffer_t *rb) {
rb->head = 0;
rb->tail = 0;
}
// 写入单字节(中断安全)
bool ring_buf_put(ring_buffer_t *rb, uint8_t data) {
uint16_t next = (rb->head + 1) & (RING_BUF_SIZE - 1);
if (next == rb->tail) {
return false; // 缓冲区满
}
rb->buffer[rb->head] = data;
// 内存屏障:确保数据写入在指针更新之前完成
__DMB(); // ARM Cortex-M
rb->head = next;
return true;
}
// 读取单字节
bool ring_buf_get(ring_buffer_t *rb, uint8_t *data) {
if (rb->head == rb->tail) {
return false; // 缓冲区空
}
*data = rb->buffer[rb->tail];
__DMB();
rb->tail = (rb->tail + 1) & (RING_BUF_SIZE - 1);
return true;
}
// 批量写入
uint16_t ring_buf_write(ring_buffer_t *rb, const uint8_t *data, uint16_t len) {
uint16_t written = 0;
for (uint16_t i = 0; i < len; i++) {
if (!ring_buf_put(rb, data[i])) {
break;
}
written++;
}
return written;
}
// 获取可用空间
uint16_t ring_buf_free(ring_buffer_t *rb) {
return (rb->tail - rb->head - 1) & (RING_BUF_SIZE - 1);
}
// 获取已存储数据量
uint16_t ring_buf_available(ring_buffer_t *rb) {
return (rb->head - rb->tail) & (RING_BUF_SIZE - 1);
}
// 清空缓冲区
void ring_buf_flush(ring_buffer_t *rb) {
rb->tail = rb->head;
}
线程安全设计
关键点:
- 单生产者-单消费者:无需加锁(中断写+线程读)
- volatile修饰符:防止编译器优化
- 内存屏障:确保多核/乱序执行正确性
- 原子操作:head/tail更新必须原子
c
// 多生产者环境(需要互斥锁)
typedef struct {
uint8_t buffer[RING_BUF_SIZE];
volatile uint16_t head;
volatile uint16_t tail;
mutex_t lock; // 多生产者保护
} mp_ring_buffer_t;
bool mp_ring_buf_put(mp_ring_buffer_t *rb, uint8_t data) {
mutex_lock(&rb->lock);
uint16_t next = (rb->head + 1) & (RING_BUF_SIZE - 1);
if (next == rb->tail) {
mutex_unlock(&rb->lock);
return false;
}
rb->buffer[rb->head] = data;
rb->head = next;
mutex_unlock(&rb->lock);
return true;
}
硬件流控(RTS/CTS)
工作原理
设备A 设备B
TX ------------------> RX
RX <------------------ TX
RTS ------------------> CTS (A请求发送,询问B是否准备好)
CTS <------------------ RTS (B允许A发送)
流程:
- 设备A置RTS为低电平(请求发送)
- 设备B接收缓冲区有空间,置CTS为低电平(允许发送)
- 设备A开始发送数据
- 设备B缓冲区将满,置CTS为高电平(停止发送)
- 设备A停止发送,等待CTS恢复低电平
配置代码
c
void uart_rts_cts_init(void) {
// 使能RTS/CTS硬件流控
UART->CR3 |= USART_CR3_RTSE | USART_CR3_CTSE;
// 配置RTS阈值(接收缓冲区剩余空间)
#define RTS_THRESHOLD (RING_BUF_SIZE / 4)
// GPIO配置
GPIO_InitTypeDef gpio = {
.Pin = RTS_PIN | CTS_PIN,
.Mode = GPIO_MODE_AF_PP,
.Pull = GPIO_NOPULL,
.Speed = GPIO_SPEED_FREQ_HIGH,
.Alternate = GPIO_AF7_USART1
};
HAL_GPIO_Init(GPIOA, &gpio);
}
// 软件控制RTS(手动管理)
void uart_check_rts(ring_buffer_t *rb) {
if (ring_buf_free(rb) < RTS_THRESHOLD) {
GPIO_SetBits(RTS_PORT, RTS_PIN); // 停止对方发送
} else {
GPIO_ResetBits(RTS_PORT, RTS_PIN); // 允许对方发送
}
}
软件流控(XON/XOFF)
协议定义
c
#define XON 0x11 // Ctrl+Q,恢复发送
#define XOFF 0x13 // Ctrl+S,暂停发送
static volatile bool tx_paused = false;
// 接收到流控字符
void uart_process_flow_control(uint8_t ch) {
if (ch == XOFF) {
tx_paused = true;
} else if (ch == XON) {
tx_paused = false;
uart_resume_tx(); // 恢复发送
}
}
// 发送前检查
void uart_tx_with_flow_control(uint8_t data) {
while (tx_paused) {
// 等待XON或超时
}
uart_send(data);
}
// 接收缓冲区管理
void uart_rx_check_buffer(ring_buffer_t *rb) {
uint16_t free = ring_buf_free(rb);
if (free < 16 && !xoff_sent) {
uart_send(XOFF);
xoff_sent = true;
} else if (free > 64 && xoff_sent) {
uart_send(XON);
xoff_sent = false;
}
}
注意事项:
- XON/XOFF占用数据通道,不适合二进制传输
- 需要协议层识别流控字符
- 存在延迟(发送XOFF后对方可能还发几个字节)
缓冲区大小计算
公式推导
最小缓冲区 = 数据速率 × 最大处理延迟
例:波特率115200,最大延迟50ms
数据速率 = 115200 / 10 = 11520 字节/秒
最小缓冲区 = 11520 × 0.05 = 576 字节
建议:实际值 × 2(安全裕量)→ 1024字节
场景化配置
c
// 低速命令接收(9600 bps)
#define CMD_RX_BUF_SIZE 64
// 高速数据传输(921600 bps)
#define DATA_RX_BUF_SIZE 2048
// GPS NMEA语句(4800 bps,最长82字节)
#define GPS_RX_BUF_SIZE 128
// 日志输出(异步缓冲)
#define LOG_TX_BUF_SIZE 4096
流控失效问题排查
常见原因
- CTS信号未连接
c
// 检测CTS状态
if (UART->ISR & USART_ISR_CTS) {
// CTS有效,可以发送
}
- 流控阈值设置不当
c
// 错误:阈值过高
#define RTS_THRESHOLD (RING_BUF_SIZE - 4) // 仅剩4字节才停止
// 正确:预留足够缓冲
#define RTS_THRESHOLD (RING_BUF_SIZE / 4)
- 中断延迟导致溢出
c
// 使能DMA减少中断延迟
UART->CR3 |= USART_CR3_DMAR;
实际案例:大文件传输
需求:通过串口传输1MB固件,115200bps,确保无丢失
方案设计:
c
#define FILE_RX_BUF_SIZE 4096 // 4KB缓冲区
#define FLOW_LOW_WATER 1024 // 剩余1KB发送XOFF
#define FLOW_HIGH_WATER 3072 // 恢复到3KB发送XON
typedef struct {
ring_buffer_t rb;
uint32_t total_bytes;
uint32_t lost_bytes;
bool flow_stopped;
} file_transfer_t;
void file_transfer_task(file_transfer_t *ft) {
uint8_t data[512];
while (1) {
// 检查缓冲区并发送流控
uint16_t available = ring_buf_available(&ft->rb);
if (available > FLOW_LOW_WATER && !ft->flow_stopped) {
uart_send(XOFF);
ft->flow_stopped = true;
} else if (available < FLOW_HIGH_WATER && ft->flow_stopped) {
uart_send(XON);
ft->flow_stopped = false;
}
// 读取并写入Flash
uint16_t len = ring_buf_read(&ft->rb, data, sizeof(data));
if (len > 0) {
flash_write(ft->total_bytes, data, len);
ft->total_bytes += len;
}
osDelay(10);
}
}
实测结果:
- 传输速度:11.5 KB/s
- 传输时间:约90秒
- 丢包率:0%
- CPU占用:<5%
延伸阅读:\[08-中断驱动vs轮询vs DMA]、\[10-低功耗与唤醒机制]
上一篇 :\[08-中断驱动vs轮询vs DMA]
下一篇:\[10-低功耗与唤醒机制]
✨ 感谢你的阅读 ✨
技术之路漫漫,分享是为了更好地交流。
如果本文的内容对你有启发,希望能得到你的支持:
👍 点赞 | ⭐ 收藏 | 👀 关注
如果你在调试过程中遇到了其他问题,
欢迎在 评论区 💬 留言,我们一起探讨。
关注我,一起交流底层开发的那些事儿!🚀