STM32上使用的环形FIFO队列,用于缓存待发送数据

C语言实现的环形FIFO队列,用于线程异步通信,数据发送非常方便,比如GPRS发送数据,一个线程将数据写入到FIFO,发送线程不停的从FIFO中读取数据,然后发送,又比如上位机中,数据接收线程不停的接收数据,写入到FIFO,另一个异步的处理线程不停的读取数据,进行处理。

/*************************************************************************************************************
 * 文件名:			SendDataFIFO.c
 * 功能:			实时数据发送缓冲区
 * 作者:			[email protected]
 * 创建时间:		2015-08-09
 * 最后修改时间:	2017-08-26
 * 详细:			用于数据发送缓冲区
					2017-08-26:增加溢出回调函数,可以对溢出的数据进行处理,用于FIFO嵌套
*************************************************************************************************************/
#include "system.h"
#include "usart.h"
#include "led.h"
#include "main.h"
#include "SendDataFIFO.h"
#include "SYSMalloc.h"

#define FIFO_INIT_STATUS_ID		0x354789d	//用于标示是否初始化




//初始化缓冲区
bool FIFO_Init(FIFO_HANDLE *pHandle,u16 OneSize, u16 MaxCnt, void (*FullCallBack)(void *pData), bool isExtSRAM)
{
	
	if(pHandle == NULL) return FALSE;
	if(isExtSRAM == TRUE)	//是否使用外部缓冲区
	{
		pHandle->pFifoBuff = (u8 *)mymalloc(SRAMEX, OneSize*MaxCnt);			//缓冲区指针,申请内存
		pHandle->pByteCntBuff = (u16 *)mymalloc(SRAMEX, sizeof(u16)*MaxCnt);	//数据大小记录缓冲区,申请内存
	}
	else
	{
		pHandle->pFifoBuff = (u8 *)mymalloc(SRAMIN, OneSize*MaxCnt);			//缓冲区指针,申请内存
		pHandle->pByteCntBuff = (u16 *)mymalloc(SRAMIN, sizeof(u16)*MaxCnt);	//数据大小记录缓冲区,申请内存
	}
	
	//uart_printf("pHandle->pFifoBuff=0x%X\r\n", (u32)pHandle->pFifoBuff);
	//uart_printf("pHandle->pByteCntBuff=0x%X\r\n", (u32)pHandle->pByteCntBuff);
	if(pHandle->pFifoBuff==NULL)
	{
		DEBUG("pHandle->pFifoBuff申请内存出错\r\n");
	}
	if(pHandle->pByteCntBuff==NULL)
	{
		DEBUG("pHandle->pByteCntBuff申请内存出错\r\n");
	}
	pHandle->InitStatus = 0;		//初始化成功状态无效
	if((pHandle->pFifoBuff==NULL)||(pHandle->pByteCntBuff==NULL)) return FALSE;
	pHandle->OneSize = OneSize;		//单条数据大小
	pHandle->Cnt = MaxCnt;			//缓冲区总数据容量(条数)
	pHandle->ReadCnt = 0;			//读取位置
	pHandle->WriteCnt = 0;			//写位置
	pHandle->NotReadCnt = 0;		//未读取数量
	pHandle->FullCallBack = FullCallBack;			//溢出回调函数
	pHandle->InitStatus = FIFO_INIT_STATUS_ID;		//初始化成功状态有效
	
	
	return TRUE;
}


//写入一条数据
//当缓冲区满了后,缓存最新的数据,丢掉最早的数据
bool FIFO_Write(FIFO_HANDLE *pHandle,u8 *pBuff, u16 ByteCnt)
{
	u16 cnt;
	#ifdef _UCOS_II_
	OS_CPU_SR  cpu_sr;
	#endif

	if(pHandle == NULL) return FALSE;
	if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return FALSE;						//没有初始化
	if (pHandle->NotReadCnt >= pHandle->Cnt)											//发送溢出										
	{
		cnt = pHandle->WriteCnt;														//先将写指针后移,占位,防止多线程写冲突
		
		if(pHandle->FullCallBack!=NULL) pHandle->FullCallBack(&pHandle->pFifoBuff[cnt * pHandle->OneSize]);
		if (ByteCnt > pHandle->OneSize) ByteCnt = pHandle->OneSize;						//限制单条数据大小
		
		pHandle->WriteCnt++;
		if (pHandle->WriteCnt >= pHandle->Cnt) pHandle->WriteCnt = 0;					//环形FIFO
		pHandle->ReadCnt++;																//读取数量增加
		if (pHandle->ReadCnt >= pHandle->Cnt) pHandle->ReadCnt = 0;						//环形FIFO,把读写指针都增加,但是剩余数据数量不变
		pHandle->pByteCntBuff[cnt] = ByteCnt;											//记录数据大小
		memcpy(&pHandle->pFifoBuff[cnt * pHandle->OneSize], pBuff, ByteCnt);			//拷贝数据到缓冲区
		
		return FALSE;																	//数据已经满了
	}
	else
	{
		if (ByteCnt > pHandle->OneSize) ByteCnt = pHandle->OneSize;						//限制单条数据大小
		//先将写指针后移,占位,防止多线程写冲突
		cnt = pHandle->WriteCnt;
		pHandle->WriteCnt++;
		if (pHandle->WriteCnt >= pHandle->Cnt) pHandle->WriteCnt = 0;					//环形FIFO
		pHandle->pByteCntBuff[cnt] = ByteCnt;											//记录数据大小
		
		memcpy(&pHandle->pFifoBuff[cnt * pHandle->OneSize], pBuff, ByteCnt);			//拷贝数据到缓冲区
		/*{
			u16 i;
			printf("\r\n写入的数据测试[读%d/写:%d]:\r\n",pHandle->ReadCnt,pHandle->WriteCnt);
			for(i = 0;i < ByteCnt;i ++)
			{
				pHandle->pFifoBuff[cnt * pHandle->OneSize+i] = pBuff[i];
				printf("%02X\t",pBuff[i]);
				if(pHandle->pFifoBuff[cnt * pHandle->OneSize+i] != pBuff[i])
				{
					printf("拷贝检测错误,数据丢失了\r\n");
				}
			}
			printf("\r\n检测写入的数据测试:\r\n");
			for(i = 0;i < ByteCnt;i ++)
			{
				printf("%02X\t",pHandle->pFifoBuff[cnt * pHandle->OneSize+i]);
			}
			printf("\r\n");
		}*/

#ifdef _UCOS_II_
		OS_ENTER_CRITICAL();						//关闭系统中断
#endif
		
		pHandle->NotReadCnt ++;															//没有读取的数量增加
		
#ifdef _UCOS_II_
		OS_EXIT_CRITICAL();	 						//开启系统中断
#endif		
		
		return TRUE;
	}
	

	
}




//读取一条数据,返回指针,无需复制数据
bool FIFO_ReadNotCopy(FIFO_HANDLE *pHandle,u8 **pBuff, u16 *pByteCnt)
{
	u16 cnt;
	
	printf("\r\n读取数据[读%d/写:%d]:\r\n",pHandle->ReadCnt,pHandle->WriteCnt);
	if(pHandle == NULL) return FALSE;
	if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return FALSE;					//没有初始化
	if (pHandle->NotReadCnt == 0)  return FALSE;									//数据为空
	cnt = pHandle->pByteCntBuff[pHandle->ReadCnt];									//获取数据大小
	if (cnt > pHandle->OneSize) cnt = pHandle->OneSize;								//限制单条数据大小
	*pBuff = &pHandle->pFifoBuff[pHandle->ReadCnt * pHandle->OneSize];				//数据缓冲区指针
	*pByteCnt = cnt;
	
	
	return TRUE;
}

//未读取数据减少一次,用于读取数据返回指针后调用
u16 FIFO_ReduceOne(FIFO_HANDLE *pHandle)
{
#ifdef _UCOS_II_
	OS_CPU_SR  cpu_sr;
#endif
	
	if(pHandle == NULL) return FALSE;
	if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return FALSE;					//没有初始化
	if (pHandle->NotReadCnt == 0) return FALSE;
	pHandle->ReadCnt++;																//读取数量增加
	if (pHandle->ReadCnt >= pHandle->Cnt) pHandle->ReadCnt = 0;						//环形FIFO
	


#ifdef _UCOS_II_
	OS_ENTER_CRITICAL();						//关闭系统中断
#endif
		
	pHandle->NotReadCnt--;															//没有读取的数量减少
#ifdef _UCOS_II_
	OS_EXIT_CRITICAL();	 						//开启系统中断
#endif	
	
	

	return pHandle->NotReadCnt;														//返回没有读取的数据数量
}


//清除FIFO
bool FIFO_Clear(FIFO_HANDLE *pHandle)
{
	if(pHandle == NULL) return FALSE;
	if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return FALSE;					//没有初始化
	pHandle->ReadCnt = 0;						//FIFO读取位置
	pHandle->WriteCnt = 0;						//FIFO写入位置
	pHandle->NotReadCnt = 0;					//FIFO内没有读取的数据数量为0
	return TRUE;
}


//获取FIFO中数据数量
u16 FIFO_GetDataNumber(FIFO_HANDLE *pHandle)
{
	if(pHandle == NULL) return 0;
	if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return 0;	//没有初始化

	return pHandle->NotReadCnt;						//FIFO内没有读取的数据数量
}
/*************************************************************************************************************
 * 文件名:			SendDataFIFO.h
 * 功能:			实时数据发送缓冲区
 * 作者:			[email protected]
 * 创建时间:		2015-08-09
 * 最后修改时间:	2017-08-26
 * 详细:			用于数据发送缓冲区
					2017-08-26:增加溢出回调函数,可以对溢出的数据进行处理
*************************************************************************************************************/
#ifndef __SEND_DATA_FIFO_H__
#define __SEND_DATA_FIFO_H__

#include "system.h"


typedef struct
{
	u8 *pFifoBuff;						//缓冲区指针
	u16 *pByteCntBuff;					//记录数据大小的缓冲区
	u16 OneSize;						//单条数据大小
	u16 Cnt;							//缓冲区总数据容量(条数)
	u16 ReadCnt;						//读取位置
	u16 WriteCnt;						//写位置
	u16 NotReadCnt;						//未读取数量
	u32 InitStatus;						//初始化状态
	void (*FullCallBack)(void *pData);	//缓冲区满回调函数
}FIFO_HANDLE;



bool FIFO_Init(FIFO_HANDLE *pHandle,u16 OneSize, u16 MaxCnt, void (*FullCallBack)(void *pData), bool isExtSRAM);			//初始化缓冲区
bool FIFO_Write(FIFO_HANDLE *pHandle,u8 *pBuff, u16 ByteCnt);			//写入一条数据
bool FIFO_ReadNotCopy(FIFO_HANDLE *pHandle,u8 **pBuff, u16 *pByteCnt);	//读取一条数据,返回指针,无需复制数据
u16 FIFO_ReduceOne(FIFO_HANDLE *pHandle);								//未读取数据减少一次,用于读取数据返回指针后调用
bool FIFO_Clear(FIFO_HANDLE *pHandle);									//清除FIFO
u16 FIFO_GetDataNumber(FIFO_HANDLE *pHandle);							//获取FIFO中数据数量

#endif	//__SEND_DATA_FIFO_H__


示例,比如定时发送数据,将数据定时拷贝到FIFO中,另一个线程定时启动,发送数据:

//拷贝实时数据到发送缓冲区中,进行立即发送数据(注意功能码必须为8位)
void CopyTempDataToFIFO(u8 Fun)
{
	u8 i;
	
	//写入需要发送的数据的功能码
	g_TempRealData.Fun = Fun;						//功能码
	g_TempRealData.SerialNumber = 0;				//流水号设置为0,自动分配
	g_TempRealData.Time[0] = timer.w_year-2000;		//拷贝实时时间
	g_TempRealData.Time[1] = timer.w_month;			//拷贝实时时间
	g_TempRealData.Time[2] = timer.w_date;			//拷贝实时时间
	g_TempRealData.Time[3] = timer.hour;			//拷贝实时时间
	g_TempRealData.Time[4] = timer.min;				//拷贝实时时间
	g_TempRealData.Time[5] = 0;						//拷贝实时时间
	
	//循环将数据写入到4个中心站的发送FIFO中(如果中心站端口为0,则认为中心站无效,不进行发送)
	for(i=0;i<4;i++)
	{
		//服务器配置有效才写入数据到缓冲区
		if(g_SYS_Config.ServerPort[i] > 0)
		{
			FIFO_Write(&g_SendFifoBuff[i], (u8 *)&g_TempRealData, sizeof(REAL_DATA));	//写入数据到发送缓冲区
		}
		else
		{
			FIFO_Clear(&g_SendFifoBuff[i]);				//不用发送的站点,清除缓冲区
			FIFO_Clear(&g_SendCacheFifoBuff[i]);		//不用发送的站点,清除Cache缓冲区
		}
	}
}

发送数据,从FIFO中读取

isFifoStatus = FIFO_ReadNotCopy(pFifoHandle, (u8 **)&pTempRealData, &ByteLen);
		if(isFifoStatus==TRUE)	//有数据要发送													//实时缓冲区中没有数据,检查Cache中是否有数据
		{....../*发送数据逻辑*/ FIFO_ReduceOne(pFifoHandle);//发送结束后,清除当前读取过的数据
		}


你可能感兴趣的:(CortexM3(STM32))