是什么
一个首尾相连的「数据圈」,像跑步的环形赛道。数据在固定大小的数组中循环存储,满了就从头开始覆盖。
核心原理
- 两个「指针」控制:
- 📝写指针:下一个要存数据的位置
- 📖读指针:下一个要取数据的位置
- 判断状态:
- ✅空:两个指针重合
- ❌满:写指针差一步追上读指针
串口中的实际使用
- 接收数据时:
串口收到数据 → 立刻存入环形缓冲区 → 主程序慢慢处理 - 发送数据时:
主程序准备数据 → 存入缓冲区 → 串口自动按顺序发送
三大优点
- 🚀 内存固定,不动态申请
- ⚡ 存取速度快(直接跳转指针)
- 🔄 自动循环利用空间
#include <stdint.h>
#include <stdbool.h>
// 环形缓冲区配置
#define BUFF_SIZE 128 // 建议设置为2的幂次(如128=2^7),便于位运算优化
typedef struct {
uint8_t data[BUFF_SIZE]; // 数据存储区
volatile uint16_t wr_pos; // 写位置(volatile保证中断安全)
volatile uint16_t rd_pos; // 读位置
} CircularBuffer;
/* 初始化缓冲区 */
void buff_init(CircularBuffer *cb) {
cb->wr_pos = 0;
cb->rd_pos = 0;
}
/* 判断空缓冲区 */
bool buff_is_empty(CircularBuffer *cb) {
return cb->wr_pos == cb->rd_pos;
}
/* 判断满缓冲区(牺牲一个单元区分空/满状态) */
bool buff_is_full(CircularBuffer *cb) {
return ((cb->wr_pos + 1) % BUFF_SIZE) == cb->rd_pos;
}
/* 写入数据(成功返回true) */
bool buff_write(CircularBuffer *cb, uint8_t byte) {
if (buff_is_full(cb)) return false;
cb->data[cb->wr_pos] = byte;
cb->wr_pos = (cb->wr_pos + 1) % BUFF_SIZE;
return true;
}
/* 读取数据(成功返回true) */
bool buff_read(CircularBuffer *cb, uint8_t *byte) {
if (buff_is_empty(cb)) return false;
*byte = cb->data[cb->rd_pos];
cb->rd_pos = (cb->rd_pos + 1) % BUFF_SIZE;
return true;
}
/****************** 串口应用示例 ******************/
// 声明全局缓冲区
CircularBuffer rx_buff; // 接收缓冲区
CircularBuffer tx_buff; // 发送缓冲区
// 串口接收中断服务函数
void USART_IRQHandler(void) {
if (/* 接收到数据 */) {
uint8_t byte = USART->DR; // 读取接收数据
buff_write(&rx_buff, byte); // 存入接收缓冲区
}
}
// 主程序处理数据
void process_data(void) {
uint8_t byte;
while (buff_read(&rx_buff, &byte)) { // 持续读取直到缓冲区空
// 数据处理逻辑
printf("Received: 0x%02X\n", byte);
}
}