C语言环形队列缓冲-FIFO_RingBuffer

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);
	

}

你可能感兴趣的:(单片机C语言,C语言,c语言,开发语言,stm32,单片机)