当前位置: 首页 > news >正文

关于循环缓冲区

循环缓冲区

说明

后面写不下,回到开头去写。
在这里插入图片描述

一般情况

先来看一般的情况。

有数据时

情况1

XXX123456XXX
readPointerwritePointer

情况2

789XXX123456
writePointerreadPointer

注意

不允许将整个数组全部使用,否则无法区分当前是没有数据,还是数据已满!

没有数据

XXXXXXXXXXXX
writePointer
readPointer

数据充满整个数组

111212345678910
writepointer
readPointer

当然,这也可以通过加入一个Flag变量去标记,目前到底有没有数据。如果这样的话,每次读写后都应当检查writePointer是否等于readPointer,若相等则根据情况更新Flag变量。

读写时的情况

直接写入的情况

返回开头写入的情况

写不下需要返回开头!

以写四个为例

XXXXXXX123XX
readPointerwritePointer
67XXXXX12345
writePointerreadPointer

直接读取的情况

回到开头读取的情况

以读取5个数据为例

34567XXXXX12
writePointerreadPointer
XXX67XXXXXXX
readPointerwritePointer

实现代码

我这里以C实现它

CircularBuffer.h

#pragma once#ifdef __cplusplus
extern "C" {
#endif#include <stdint.h>
#include <string.h>
#include <stdbool.h>#define CIRCULAR_BUFFER_SIZE 64 //缓冲区大小 根据需要修改typedef struct {uint8_t buffer[CIRCULAR_BUFFER_SIZE];uint8_t* writePointer;uint8_t* readPointer;
}CircularBuffer;/*** @brief 初始化一个循环缓冲区* @param cb 缓冲区指针*/
void circularBufferInit(CircularBuffer* cb);/*** @brief 得到允许读取的数据长度* @param cb 循环缓冲区指针* @return 允许读取的数据长度* @note 也就是未处理的数据长度*/
uint16_t circularBufferReadAllow(CircularBuffer* cb);/*** @brief 得到允许写入的数据长度* @param cb 循环缓冲区指针* @return 允许写入的数据长度*/
uint16_t circularBufferWriteAllow(CircularBuffer* cb);/*** @brief 向缓冲区写入数据* @param cb 循环缓冲区指针* @param data 要写入的数据指针* @param length 要写入的数据的长度* @return true 成功* @return false 失败*/
bool circularBufferWrite(CircularBuffer* cb, uint8_t* data, uint16_t length);/*** @brief 看看有没有要处理的数据* @param cb 循环缓冲区指针* @return true 有* @return false 没有*/
bool circularBufferNeedProcessed(CircularBuffer* cb);/*** @brief 从缓冲区中读出数据* @param cb 循环缓冲区指针* @param buffer 读取到的缓冲区* @param length 要求读取的长度* @return true 成功* @return false 失败*/
bool circularBufferRead(CircularBuffer* cb, uint8_t* buffer, uint16_t length);#ifdef __cplusplus
}
#endif

CircularBuffer.c

#include "CircularBuffer.h"void circularBufferInit(CircularBuffer* cb)
{memset(cb, 0x00, CIRCULAR_BUFFER_SIZE);cb->writePointer = cb->buffer;cb->readPointer = cb->buffer;
}uint16_t circularBufferReadAllow(CircularBuffer* cb)
{//if (cb->writePointer == cb->readPointer)//{//	cb->readAllow = 0;//二者相等时没有数据//}//else if (cb->writePointer > cb->readPointer)//{//	cb->readAllow = cb->writePointer - cb->readPointer;//}//else//{//	cb->readAllow = CIRCULAR_BUFFER_SIZE - (cb->readPointer - cb->writePointer);//}//与上面的代码等价uint16_t readAllow = (cb->writePointer + CIRCULAR_BUFFER_SIZE - cb->readPointer) % CIRCULAR_BUFFER_SIZE;return readAllow;
}uint16_t circularBufferWriteAllow(CircularBuffer* cb)
{//uint16_t writeAllow = 0;//if (cb->writePointer == cb->readPointer)//{//	writeAllow = CIRCULAR_BUFFER_SIZE - 1;//不允许整个数组都存入数据//}		//else if (cb->writePointer > cb->readPointer)//{//	writeAllow = CIRCULAR_BUFFER_SIZE - (cb->writePointer - cb->readPointer) - 1;//}//else//{//	writeAllow = cb->readPointer - cb->writePointer - 1;//}//与上面的代码等价//不允许整个数组都存入数据uint16_t writeAllow = CIRCULAR_BUFFER_SIZE - (cb->writePointer + CIRCULAR_BUFFER_SIZE - cb->readPointer) % CIRCULAR_BUFFER_SIZE - 1;return writeAllow;
}bool circularBufferWrite(CircularBuffer* cb, uint8_t* data, uint16_t length)
{if (length > circularBufferWriteAllow(cb))return false;if (cb->writePointer + length < cb->buffer + CIRCULAR_BUFFER_SIZE)//不需要返回开头的情况{memcpy(cb->writePointer, data, length);cb->writePointer += length;}else//需要返回开头的情况{uint16_t endLength = cb->buffer + CIRCULAR_BUFFER_SIZE - cb->writePointer;		memcpy(cb->writePointer, data, endLength);//拷贝到后面uint16_t beginLength = length - endLength;memcpy(cb->buffer, data + endLength, beginLength);//拷贝到前面cb->writePointer = cb->buffer + beginLength;}return true;
}bool circularBufferNeedProcessed(CircularBuffer* cb)
{return !(cb->writePointer == cb->readPointer);
}bool circularBufferRead(CircularBuffer* cb, uint8_t* buffer, uint16_t length)
{if (length > circularBufferReadAllow(cb))return false;if (cb->readPointer + length < cb->buffer + CIRCULAR_BUFFER_SIZE)//不需要返回开头的情况{memcpy(buffer, cb->readPointer, length);cb->readPointer += length;}else//需要返回开头的情况{uint16_t endLength = cb->buffer + CIRCULAR_BUFFER_SIZE - cb->readPointer;memcpy(buffer, cb->readPointer, endLength);//拷贝后面的uint16_t beginLength = length - endLength;memcpy(buffer+endLength, cb->buffer, beginLength);//拷贝前面的cb->readPointer = cb->buffer + beginLength;}return true;
}

测试代码

main.c

#include <stdio.h>#include <time.h>#include "CircularBuffer.h"void test1()
{	CircularBuffer cb;uint8_t writeData[CIRCULAR_BUFFER_SIZE * 2]; // 写入数据uint8_t readData[CIRCULAR_BUFFER_SIZE * 2];  // 读取数据// 初始化缓冲区circularBufferInit(&cb);// 准备测试数据for (uint16_t i = 0; i < CIRCULAR_BUFFER_SIZE * 2; i++) {writeData[i] = i % 256; // 数据范围 0x00 到 0xFF}printf("=== Testing Circular Buffer ===\n");// 测试写入超出允许长度的情况printf("\n--- Test 1: Write more than allowed ---\n");if (!circularBufferWrite(&cb, writeData, CIRCULAR_BUFFER_SIZE + 1)) {printf("Write failed as expected.\n");}// 测试写入正常数据printf("\n--- Test 2: Write normal data ---\n");if (circularBufferWrite(&cb, writeData, CIRCULAR_BUFFER_SIZE / 2)) {printf("Wrote %d bytes successfully.\n", CIRCULAR_BUFFER_SIZE / 2);}else {printf("Write failed unexpectedly.\n");}// 检查允许读取的数据长度printf("\n--- Test 3: Check read allowance ---\n");uint16_t readAllow = circularBufferReadAllow(&cb);printf("Allowed to read: %d bytes\n", readAllow);// 测试读取部分数据printf("\n--- Test 4: Read partial data ---\n");if (circularBufferRead(&cb, readData, readAllow)) {printf("Read %d bytes successfully.\n", readAllow);printf("Read data: ");for (uint16_t i = 0; i < readAllow; i++) {printf("%02X ", readData[i]);}printf("\n");}else {printf("Read failed unexpectedly.\n");}// 测试是否需要处理数据printf("\n--- Test 5: Check if data needs processing ---\n");if (circularBufferNeedProcessed(&cb)) {printf("There is still data to process.\n");}else {printf("No data to process.\n");}// 测试再次写入数据(模拟环形覆盖)printf("\n--- Test 6: Write more data to test wrap-around ---\n");if (circularBufferWrite(&cb, writeData + CIRCULAR_BUFFER_SIZE * 7 / 8, CIRCULAR_BUFFER_SIZE * 7 / 8)){printf("Wrote %d bytes successfully.\n", CIRCULAR_BUFFER_SIZE * 7 / 8);}else{printf("Write failed unexpectedly.\n");}// 再次测试是否需要处理数据printf("\n--- Test 7: Check if data needs processing ---\n");if (circularBufferNeedProcessed(&cb)) {printf("There is still data to process.\n");}else{printf("No data to process.\n");}// 测试读取所有剩余数据printf("\n--- Test 8: Read all remaining data ---\n");readAllow = circularBufferReadAllow(&cb);if (circularBufferRead(&cb, readData, readAllow)) {printf("Read %d bytes successfully.\n", readAllow);printf("Read data: ");for (uint16_t i = 0; i < readAllow; i++) {printf("%02X ", readData[i]);}printf("\n");}else {printf("Read failed unexpectedly.\n");}// 最终检查是否还有未处理数据printf("\n--- Final Check: Any data left? ---\n");if (circularBufferNeedProcessed(&cb)) {printf("There is still data to process.\n");}else {printf("No data to process.\n");}}int main(void)
{test1();return 0;
}

后记

如果嵌入式项目中涉及大量数据结构的问题,请使用ETL库

ETL(Embedded Template Library) 是一个专为嵌入式系统和资源受限环境设计的轻量级 C++ 模板库,提供了类似 STL(标准模板库)的容器和算法,但更注重 确定性内存管理零动态内存分配低开销

etl::circular_buffer实现了循环缓冲区

参考

嵌入式模板库(ETL):为嵌入式系统量身定制的C++模板库-CSDN博客

参考资料

什么是循环缓冲区?-CSDN博客

https://docs.keysking.com/docs/stm32/example/UART_COMMAND

https://www.bilibili.com/video/BV1p75yzSEt9/?spm_id_from=333.337.search-card.all.click&vd_source=a56c06558fd43f4777ff9ae6c06c3e71

http://www.xdnf.cn/news/133543.html

相关文章:

  • 【java源码】AI智能导诊系统,基于H5、小程序、app等多端,引导患者自助就诊挂号,实现科学就诊
  • 4G卡的DTU固件TCP通讯
  • 【Rust】Rust中的枚举与模式匹配,原理解析与应用实战
  • 秒级到毫秒:BFD的速度革命
  • Swift闭包(Closure)深入解析与底层原理
  • SAM 2 (Segment Anything ):图像与视频通用分割模型
  • Vue里面elementUi-aside 和el-main不垂直排列
  • 知识蒸馏和迁移学习的区别
  • 在项目中使用 Sonar:提升代码质量的利器
  • 深入理解机器学习:人工智能的核心驱动力
  • AI之FastAPI+ollama调用嵌入模型OllamaBgeEmbeddings
  • SQL笛卡尔积运用-为每个用户初始化数据
  • [Windows] 卡巴斯基Kaspersky 21.21.7.384 免费版
  • 基于Axure的动态甘特图设计:实现任务增删改与时间拖拽交互
  • 打工人必看:Word中姓名对齐的高效方法
  • 计算器(WEB)
  • PWNOS:2.0(vulnhub靶机)
  • Java知识日常巩固(五)
  • 在GNS3中安装Kali Linux
  • 【深度好文】2、深入浅出 Milvus 数据库管理:从创建到删除的完整指南
  • spark-standalone模式
  • 设置Rocky Linux盒盖不休眠的3个简单步骤
  • 常见的几种分块策略,每种策略都有适用场景和优缺点
  • 题目 3320: 蓝桥杯2025年第十六届省赛真题-产值调整
  • 【爬虫】DrissionPage-获取douyim用户下的视频
  • 论文阅读:2024 NeurIPS Group Robust Preference Optimization in Reward-free RLHF
  • 幻读是什么项目中是怎么保证不会出现幻读
  • 高级电影感户外街拍人像摄影后期Lr调色教程,手机滤镜PS+Lightroom预设下载!
  • Gartner魔力象限(Gartner Magic Quadrant)
  • 你怎么通过postman或者fidder或者JMeter来获取到token,然后后面的请求怎么使用token