在嵌入式开发中离不开设备通信,而在通信中稳定性最高的莫过于环形缓冲区算法,当读取速度大于写入速度时,在环形缓冲区的支持下不会丢掉任何一个字节(硬件问题除外)。下面我分享一段由我原创的Ringbuffer代码。
C文件:
- /**
- ******************************************************************************
- * @file cx_ringbuff.c
- * @author CX
- * @version V1.0.0.2
- * @date 2016-07-13
- * @brief 1.0.0.2 期望帧逻辑优化
- 修改匹配期望帧任务的条件
- 增加匹配期望帧函数
- 1.0.0.1 优化耦合性
- 优化读取逻辑
- 增加多缓冲区支持
- 1.0.0.0 主体架构搭建,完成读写环形结构化
- ******************************************************************************
- * @attention
- *
- * 项目 :None
- * 官网 : None
- * 实验室 :None
- *
- ******************************************************************************
- */
- #include "cx_ringbuff.h"
- /* 除了必须的缓冲区外没有向外部模块公开一个变量,实现高内聚,低耦合 */
- RingBuff_Typedef RingBuffStruct;
- /**
- * @brief 缓冲区实例化
- * @param rb_ptr 缓冲区结构体地址
- * @retval None
- * @notice None
- */
- void RingBuff_New(RingBuff_Typedef* rb_ptr)
- {
- rb_ptr->Readpos = rb_ptr->RxBuff;
- rb_ptr->Writepos = rb_ptr->RxBuff;
- rb_ptr->ReadRetStateStruct.Readstate = Fail;
- rb_ptr->ReadRetStateStruct.ptr = NULL;
- rb_ptr->RingbufCount = 0;
- memset(rb_ptr->RxBuff, 0, MaxBuffSize);
- }
- /**
- * @brief 定位函数
- * @param rb_ptr 缓冲区结构体地址,addr 当前读写地址
- * @retval addr+1 or 0
- * @notice None
- */
- unsigned char* NextDataAddrHandle(RingBuff_Typedef* rb_ptr, unsigned char* addr)
- {
- return (addr + 1) == (rb_ptr->RxBuff + MaxBuffSize) ? rb_ptr->RxBuff : (addr + 1);
- }
- /**
- * @brief 读字节函数
- * @param rb_ptr 缓冲区结构体地址
- * @retval data
- * @notice None
- */
- ReadRetState_Typedef* ReadDataFromRingbuff(RingBuff_Typedef* rb_ptr)
- {
- if (rb_ptr->RingbufCount > 0)
- {
- rb_ptr->ReadRetStateStruct.ptr = rb_ptr->Readpos;
- rb_ptr->ReadRetStateStruct.Readstate = Success;
- rb_ptr->Readpos = NextDataAddrHandle(rb_ptr, rb_ptr->Readpos);
- rb_ptr->RingbufCount--;
- return &rb_ptr->ReadRetStateStruct;
- }
- rb_ptr->ReadRetStateStruct.Readstate = Fail;
- rb_ptr->ReadRetStateStruct.ptr = NULL;
- return &rb_ptr->ReadRetStateStruct;
- }
- /**
- * @brief 写字节函数
- * @param rb_ptr 缓冲区结构体地址,data 写入数据
- * @retval None
- * @notice None
- */
- void WriteDataToRingbuff(RingBuff_Typedef* rb_ptr, unsigned char data)
- {
- *(rb_ptr->Writepos) = data;
- rb_ptr->Writepos = NextDataAddrHandle(rb_ptr,rb_ptr->Writepos);
- rb_ptr->RingbufCount++;
- }
- /**
- * @brief 读指定包长函数
- * @param rb_ptr 缓冲区结构体地址,head 包头,length 包长
- * @retval 读取状态及数据包地址
- * @notice length为整个数据包长即头置尾
- */
- ReadRetState_Typedef* ReadEfectiveFrameFixLength(RingBuff_Typedef* rb_ptr, unsigned char head, unsigned char length)
- {
- unsigned char count = rb_ptr->RingbufCount; //标记触发前有效数据大小
- if (count < length)
- {
- rb_ptr->ReadRetStateStruct.Readstate = Fail;
- rb_ptr->ReadRetStateStruct.ptr = NULL;
- return &rb_ptr->ReadRetStateStruct;
- }
- while (count >= length)
- {
- rb_ptr->ReadRetStateStruct = *ReadDataFromRingbuff(rb_ptr);
- count--;
- if (rb_ptr->ReadRetStateStruct.Readstate == Success && *rb_ptr->ReadRetStateStruct.ptr == head)
- {
- return &rb_ptr->ReadRetStateStruct;
- }
- }
- rb_ptr->ReadRetStateStruct.Readstate = Fail;
- rb_ptr->ReadRetStateStruct.ptr = NULL;
- return &rb_ptr->ReadRetStateStruct;
- }
- /**
- * @brief 读期望帧
- * @param rb_ptr 缓冲区结构体地址,str 期望帧
- * @retval 读取状态
- * @notice None
- */
- ReadRetState_Enum ReadEfectiveFrame(RingBuff_Typedef* rb_ptr, const char* str)
- {
- unsigned char count = rb_ptr->RingbufCount; //标记触发前有效数据大小
- unsigned char length = strlen(str);
- unsigned char i = 0;
- if (count < length)
- {
- return Fail;
- }
- while (count >= length)
- {
- rb_ptr->ReadRetStateStruct = *ReadDataFromRingbuff(rb_ptr);
- count--;
- if (rb_ptr->ReadRetStateStruct.Readstate == Success && *rb_ptr->ReadRetStateStruct.ptr == *(str + i))
- {
- count++;
- i++;
- }
- else
- {
- i = 0;
- }
- if (i == length)
- {
- return Success;
- }
- }
- return Fail;
- }
- /**
- * @brief 匹配期望帧
- * @param rb_ptr 缓冲区结构体地址,str 期望帧所在注册表地址, ExpectFrameCount 期望帧注册表的个数
- * @retval 读取状态
- * @notice None
- */
- ReadRetState_Typedef* MatchExpectFrame(RingBuff_Typedef* rb_ptr, const char** str, unsigned char ExpectFrameCount)
- {
- unsigned char Retcount = rb_ptr->RingbufCount;
- unsigned char* RetPos = rb_ptr->Readpos;
- unsigned char i = 0;
- for (i = 0; i < ExpectFrameCount; i++)
- {
- if (ReadEfectiveFrame(rb_ptr, *(str + i)) == Success)
- {
- rb_ptr->ReadRetStateStruct.pos = i;
- rb_ptr->ReadRetStateStruct.Readstate = Success;
- return &rb_ptr->ReadRetStateStruct;
- }
- else
- {
- rb_ptr->RingbufCount = Retcount;
- rb_ptr->Readpos = RetPos;
- }
- }
- rb_ptr->ReadRetStateStruct.Readstate = Fail;
- return &rb_ptr->ReadRetStateStruct;
- }
- /******************** (C)COPYRIGHT(2016) YFTC END OF FILE **********************/
- #ifndef __CX_RINGBUFF_H
- #define __CX_RINGBUFF_H
- #include "stdlib.h"
- #include "string.h"
- #define MaxBuffSize 256 //缓冲区大小可任意设置,但最好为2^X
- typedef enum
- {
- Success = 1,
- Fail = !Success,
- }ReadRetState_Enum;
- typedef struct
- {
- ReadRetState_Enum Readstate; //读状态
- unsigned char* ptr; //读取字节地址
- unsigned char pos; //期望帧在注册表中位置
- }ReadRetState_Typedef;
- typedef struct
- {
- unsigned char* Writepos; //写入地址
- unsigned char* Readpos; //读取地址
- unsigned char RingbufCount; //有效未读数据大小
- ReadRetState_Typedef ReadRetStateStruct; //读缓冲相关
- unsigned char RxBuff[MaxBuffSize]; //数据缓存区
- }RingBuff_Typedef;
- void RingBuff_New(RingBuff_Typedef* rb_ptr);
- unsigned char* NextDataAddrHandle(RingBuff_Typedef* rb_ptr,unsigned char* addr);
- ReadRetState_Typedef* ReadDataFromRingbuff(RingBuff_Typedef* rb_ptr);
- void WriteDataToRingbuff(RingBuff_Typedef* rb_ptr,unsigned char data);
- ReadRetState_Typedef* ReadEfectiveFrameFixLength(RingBuff_Typedef* rb_ptr, unsigned char head, unsigned char length);
- ReadRetState_Enum ReadEfectiveFrame(RingBuff_Typedef* rb_ptr, const char* str);
- ReadRetState_Typedef* MatchExpectFrame(RingBuff_Typedef* rb_ptr, const char** str, unsigned char ExpectFrameCount);
- #endif
以上就是我所编写的Ringbuffer源码。
从.H文件中可看到Ringbuff的数据结构,当中有读写地址以及有效未读数据大小,以及读状态等。
向外部提供的API接口就是常规的读数据包,读期望帧以及匹配期望帧,整段代码可用于普通的通信数据帧读取,也可以在单片机中用于shell组件的实现。还是希望广大网友能对我的代码进行功能补充,BUG修复等。