打印
[其他ST产品]

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

[复制链接]
405|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
雨果喝水|  楼主 | 2023-1-31 23:59 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
C语言实现的环形FIFO队列,用于线程异步通信,数据发送非常方便,比如GPRS发送数据,一个线程将数据写入到FIFO,发送线程不停的从FIFO中读取数据,然后发送,又比如上位机中,数据接收线程不停的接收数据,写入到FIFO,另一个异步的处理线程不停的读取数据,进行处理。

使用特权

评论回复
沙发
雨果喝水|  楼主 | 2023-1-31 23:59 | 只看该作者
/*************************************************************************************************************
* 文件名:                        SendDataFIFO.c
* 功能:                        实时数据发送缓冲区
* 作者:                        cp1300@139.com
* 创建时间:                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内没有读取的数据数量
}

使用特权

评论回复
板凳
雨果喝水|  楼主 | 2023-1-31 23:59 | 只看该作者
/*************************************************************************************************************
* 文件名:                        SendDataFIFO.h
* 功能:                        实时数据发送缓冲区
* 作者:                        cp1300@139.com
* 创建时间:                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__

使用特权

评论回复
地板
雨果喝水|  楼主 | 2023-1-31 23:59 | 只看该作者
示例,比如定时发送数据,将数据定时拷贝到FIFO中,另一个线程定时启动,发送数据:

使用特权

评论回复
5
雨果喝水|  楼主 | 2023-1-31 23:59 | 只看该作者
//拷贝实时数据到发送缓冲区中,进行立即发送数据(注意功能码必须为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缓冲区
                }
        }
}

使用特权

评论回复
6
雨果喝水|  楼主 | 2023-2-1 00:00 | 只看该作者
发送数据,从FIFO中读取
isFifoStatus = FIFO_ReadNotCopy(pFifoHandle, (u8 **)&pTempRealData, &ByteLen);
                if(isFifoStatus==TRUE)        //有数据要发送                                                                                                        //实时缓冲区中没有数据,检查Cache中是否有数据
                {....../*发送数据逻辑*/ FIFO_ReduceOne(pFifoHandle);//发送结束后,清除当前读取过的数据
                }

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

86

主题

1165

帖子

0

粉丝