ring_buffer.h
#ifndef __RING_BUFFER_H_
#define __RING_BUFFER_H_
// #define RINGBUF_IRQ_SAFE
#ifdef RINGBUF_IRQ_SAFE
#define INIT_CRITICAL() uint32_t priMask = __get_PRIMASK()//屏蔽中断/异常
#define ENTER_CRITICAL() __set_PRIMASK(1)//关闭总中断
#define LEAVE_CRITICAL() __set_PRIMASK(priMask)
#else
#define INIT_CRITICAL()
#define ENTER_CRITICAL()
#define LEAVE_CRITICAL()
#endif
typedef struct
{
uint8_t *pBuf;
uint32_t size;
uint32_t cnt;
uint32_t rNdx;//读取标号(始终指向头部)
uint32_t wNdx;//写入标号(始终指向尾端)
} ring_buffer_t, RINGBUFF_T;
#ifndef MIN
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#endif /* ifndef MIN */
extern int32_t RingBuf_Init(ring_buffer_t *pRB, uint8_t *pBuffer, uint32_t size);
extern int32_t RingBuf_Write(ring_buffer_t* pRB, const uint8_t *pcData, uint32_t dataBytes);
extern int32_t RingBuf_Write1Byte(ring_buffer_t* pRB, const uint8_t *pcData);
extern int32_t RingBuf_Read(ring_buffer_t* pRB, uint8_t *pData, uint32_t dataBytes);
extern int32_t RingBuf_Read1Byte(ring_buffer_t* pRB, uint8_t *pData);
extern int32_t RingBuf_Copy(ring_buffer_t* pRB, uint8_t *pData, uint32_t dataBytes);
extern int32_t RingBuf_Peek(ring_buffer_t* pRB, uint8_t **ppData);
extern int32_t RingBuf_Free(ring_buffer_t* pRB, uint32_t bytesToFree);
extern int32_t RingBuf_GetFreeBytes(ring_buffer_t* pRB);
extern int32_t RingBuf_GetUsedBytes(ring_buffer_t* pRB);
#define RingBuffer_Insert RingBuf_Write
#define RingBuffer_Pop RingBuf_Read
#endif
ring_buffer.c
#include "main.h"
#include "ring_buffer.h"
#include "string.h"
#define SUCCESS 1
#define ERROR 0
/**
* @brief 创建和初始化环形缓冲区
* @param pRB : 指向环形缓冲区的实例
* @param pBuffer: 指向用于填充环形缓冲区的数据
* @param size: 缓冲区数据的数目
* @return >=0:成功 ; <0:失败
*/
int32_t RingBuf_Init(ring_buffer_t* pRB, uint8_t* buffer, uint32_t size )
{
if(!pRB )return ERROR;
if(!size)return ERROR;
pRB->pBuf = (uint8_t*)buffer;
pRB->size = size;
pRB->rNdx = 0;
pRB->wNdx = 0;
pRB->cnt = 0;
return SUCCESS;
}
/**
* @brief 释放环形缓冲区
* @param pRB : 指向环形缓冲区的实例
* @return >=0:成功 ; <0:失败
*/
int32_t RingBuf_Deinit(ring_buffer_t* pRB )
{
if(!pRB )return ERROR;
pRB = pRB;
return SUCCESS;
}
/**
* @brief 获取环形缓冲区剩余数量
* @param pRB : 指向环形缓冲区的实例
* @return 环形缓冲区剩余数量
*/
int32_t RingBuf_GetFreeBytes(ring_buffer_t* pRB )
{
return pRB->size - pRB->cnt;
}
/**
* @brief 获取环形缓冲区已经用了的数量
* @param pRB : 指向环形缓冲区的实例
* @return 环形缓冲区已用数量
*/
int32_t RingBuf_GetUsedBytes(ring_buffer_t* pRB)
{
return pRB->cnt;
}
/**
* @brief 写新数据到缓冲区
* @param pRB : 指向环形缓冲区的实例
* @param pData: 指向要写入环形缓冲区的数据地址
* @param dataBytes: 要写入的字节数
* @return >=0:成功写入的字节 ; <0:写入失败
* @remark 此功能会更新环形缓冲区
*/
int32_t RingBuf_Write(ring_buffer_t* pRB, const uint8_t* pdata, uint32_t dataBytes)
{
if(!pRB )return ERROR;
uint32_t writeToEnd, bytesToCopy;
INIT_CRITICAL();
ENTER_CRITICAL();
/* 计算能写入的数量 */
writeToEnd = pRB->size - pRB->wNdx;//从数据尾部标号的位置到缓冲区末尾的剩余空间 例如:-----111--(10-8=2)
//要写入的数量和能写入的数量去最小的(实际能写入的数量)
bytesToCopy = MIN(dataBytes, pRB->size - pRB->cnt);
//容量和计数值相等时 bytesToCopy 为0,缓冲区已满。
if (bytesToCopy != 0)
{
/* 先向环形缓冲区空间少的位置写入数据 */
memcpy(&pRB->pBuf[pRB->wNdx], pdata, MIN(bytesToCopy, writeToEnd));
/* Check if we have more to copy to the front of the buffer */
if (writeToEnd < bytesToCopy)
{
memcpy(pRB->pBuf, pdata + writeToEnd, bytesToCopy - writeToEnd);
}
/* 更新数据标号 */
//更新尾指针,对缓冲区的大小取余数(当达到最大值size时又从0开始),防止计数超限
pRB->wNdx = (pRB->wNdx + bytesToCopy) % pRB->size;
//更新计数值
// pRB->cnt += dataBytes;
pRB->cnt += bytesToCopy;
}
LEAVE_CRITICAL();
return bytesToCopy;
}
/**
* @brief 写一个新数据到缓冲区
* @param pRB : 指向环形缓冲区的实例
* @param pcData: 指向要写入环形缓冲区的数据地址
* @return 1:成功; 其它失败
* @remark 此功能会更新环形缓冲区. 优先使用逐字节写入
*/
int32_t RingBuf_Write1Byte(ring_buffer_t* pRB, const uint8_t *pcData)
{
uint32_t ret = 0;
if(!pRB )return ERROR;
INIT_CRITICAL();
ENTER_CRITICAL();
if (pRB->cnt < pRB->size)
{
pRB->pBuf[pRB->wNdx] = pcData[0];
pRB->wNdx = (pRB->wNdx + 1) % pRB->size;
pRB->cnt++;
ret = 1;
}
LEAVE_CRITICAL();
return ret;
}
/**
* @brief 从缓冲区读取数据
* @param pRB : 指向环形缓冲区的实例
* @param pdata: 接收数据的地址
* @param dataBytes: 要读取的字节数
* @param isToFree: 1,读取并删除,0复制读取
* @return 读取的字节数
*/
static int32_t _prvRingBuf_Read(ring_buffer_t* pRB, uint8_t* pdata, uint32_t dataBytes, uint32_t isToFree)
{
uint32_t readToEnd, bytesToCopy;
if(!pRB )return ERROR;
INIT_CRITICAL();
ENTER_CRITICAL();
readToEnd = pRB->size - pRB->rNdx;//实际能读取的数量
bytesToCopy = MIN(dataBytes, pRB->cnt);//要读出的不能大于计数值
if (bytesToCopy != 0)
{
memcpy(pdata, &pRB->pBuf[pRB->rNdx], MIN(bytesToCopy, readToEnd));
if (readToEnd < bytesToCopy)//读取的多于能读取的数量,即读已经到了写的前面(需要过零点),111----111
//从0开始读取剩余的
memcpy(pdata + readToEnd, &pRB->pBuf[0], bytesToCopy - readToEnd);
if (isToFree)
{
/* 更新数据标号 */
pRB->rNdx = (pRB->rNdx + bytesToCopy) % pRB->size;
pRB->cnt -= bytesToCopy;
}
}
LEAVE_CRITICAL();
return bytesToCopy;
}
/**
* @brief 从缓冲区取出后并删除缓冲区原有的数值,会更新数据指针
* @param pRB : 指向环形缓冲区的实例
* @param pdata: 接收数据的地址
* @param dataBytes: 要读取的字节数
* @return 读取的字节数
*/
int32_t RingBuf_Read(ring_buffer_t* pRB, uint8_t* pdata, uint32_t dataBytes)
{
return _prvRingBuf_Read(pRB, pdata, dataBytes, 1);
}
/**
* @brief 从缓冲区取出后,缓冲区原有的数值不变,不会更新数据指针
* @param pRB : 指向环形缓冲区的实例
* @param pdata: 接收数据的地址
* @param dataBytes: 要读取的字节数
* @return 读取的字节数
*/
int32_t RingBuf_Copy(ring_buffer_t* pRB, uint8_t* pdata, uint32_t dataBytes)
{
return _prvRingBuf_Read(pRB, pdata, dataBytes, 0);
}
/**
* @brief 从缓冲区取1个字节,会更新数据指针
* @param pRB : 指向环形缓冲区的实例
* @param pData: 接收数据的地址
* @return 1:成功 ; 0:失败
* @remark 此功能会更新缓冲区,优先使用的逐字节读取方式
*/
int32_t RingBuf_Read1Byte(ring_buffer_t* pRB, uint8_t *pData)
{
uint32_t ret = 0;
if(!pRB )return ERROR;
INIT_CRITICAL();
ENTER_CRITICAL();
if (pRB->cnt != 0)
{
pData[0] = pRB->pBuf[pRB->rNdx];
pRB->rNdx = (pRB->rNdx + 1) % pRB->size;
pRB->cnt--;
ret = 1;
}
LEAVE_CRITICAL();
return ret;
}
/**
* @brief 获取环形缓冲区内最前面的数据地址
* @param pRB :环形缓冲区实例
* @param ppData : 接收环形缓冲区地址的指针
* @param contiguous_bytes: 记录能读取的数据下标
* @return >=0:连续可读数据的下标 ; <0:失败
* @remak 次功能只获取可连续读取数据的地址,性能优越,因为没有复制数据的操作。
* 使用此功能后可以联合 Use RingBuf_Free() 来释放数据
*/
int32_t RingBuf_Peek(ring_buffer_t* pRB, uint8_t **ppData)
{
uint32_t readToEnd = pRB->size - pRB->rNdx;
uint32_t contiguous_Bytes;
*ppData = &(pRB->pBuf[pRB->rNdx]);
contiguous_Bytes = MIN(readToEnd, (readToEnd + pRB->wNdx) % pRB->size);
return contiguous_Bytes;
}
/**
* @brief 释放环形缓冲区内数据
* @param pRB : 环形缓冲区实例
* @param bytesToFree : 释放的字节
* @remak 可以联合 RingBuf_Peek() 使用
更新计数值,其它不变,下次写入时继续从此位置。
*/
int32_t RingBuf_Free(ring_buffer_t* pRB, uint32_t bytesToFree)
{
INIT_CRITICAL();
ENTER_CRITICAL();
pRB->rNdx = (pRB->rNdx + bytesToFree) % pRB->size;
pRB->cnt -= bytesToFree;
LEAVE_CRITICAL();
return bytesToFree;
}
测试程序
ringbuffermain.h
#include "main.h"
#include "ring_buffer.h"
#define BUFSIZE 20
#define RING_BUFSIZE 10
uint8_t txBuf[BUFSIZE];
uint8_t rxBuf[BUFSIZE];
uint8_t testdat[30];
uint8_t* pBuf;
RINGBUFF_T txringBuff;
void Ring_buf_tsetMain()
{
pBuf=rxBuf;
for (size_t i = 0; i < BUFSIZE*2; i++)
testdat[i]=i+1;
RingBuf_Init(&txringBuff,txBuf,RING_BUFSIZE);
//向 txBuf 写入1,2,3
RingBuf_Write(&txringBuff,testdat,3);
uint8_t a=99;// 在后面写入99,txBuf 里面是1,2,3,99,计数是4
RingBuf_Write1Byte(&txringBuff,&a);//
/*
// 在后面再写入1,2,3,缓冲区计数值是7
写和读指针都是7
*/
RingBuf_Write(&txringBuff,testdat,3);
/*
//从 txringBuff 取4个,放进 rxbuf,
缓冲区计数值是3
读指针是4
写指针是7
*/
RingBuf_Read(&txringBuff,pBuf,4);
pBuf+=4;
/*从 txringBuff 再取1个,
缓冲区计数值是2
读指针是5
写指针是7
*/
RingBuf_Read1Byte(&txringBuff,pBuf);
pBuf+=1;
/*
从 txringBuff 再取4个,
其实只能读取2个,
缓冲区计数值是0
写和读指针都是7
*/
RingBuf_Read(&txringBuff,pBuf,4);
pBuf+=4;
/*
向环形缓冲区写入10个
缓冲区计数值是10
先从7开始写到9,再从0开始写到7,
写和读指针都是7,此时是满的
*/
RingBuf_Write(&txringBuff,testdat,10);
/*
再向环形缓冲区写入3个
因为是满的,数据保持不变
*/
RingBuf_Write(&txringBuff,testdat+10,3);
/*
释放10个数据位
只清除了计数值,其它不变,下次写入时继续从此位置。
*/
RingBuf_Free(&txringBuff,10);
/*
向环形缓冲区写入3个
使写指针到0
缓冲区计数值是3
*/
RingBuf_Write(&txringBuff,testdat+10,3);
/*
从环形缓冲区读取3个
使读指针也到0
写指针不变是0
缓冲区计数值是0
*/
RingBuf_Read(&txringBuff,pBuf,4);
pBuf+=4;
for (size_t i = 0; i < BUFSIZE; i++)
rxBuf[i]=0;
/*
向环形缓冲区写入15个,大于缓冲区容量
实际最大能写入10个
缓冲区计数值是15
写和读指针都是7
*/
RingBuf_Write(&txringBuff,testdat,15);
}