[其他ST产品] STM32上使用的环形FIFO队列,用于缓存待发送数据

[复制链接]
547|5
 楼主| 雨果喝水 发表于 2023-1-31 23:59 | 显示全部楼层 |阅读模式
C语言实现的环形FIFO队列,用于线程异步通信,数据发送非常方便,比如GPRS发送数据,一个线程将数据写入到FIFO,发送线程不停的从FIFO中读取数据,然后发送,又比如上位机中,数据接收线程不停的接收数据,写入到FIFO,另一个异步的处理线程不停的读取数据,进行处理。
 楼主| 雨果喝水 发表于 2023-1-31 23:59 | 显示全部楼层
  1. /*************************************************************************************************************
  2. * 文件名:                        SendDataFIFO.c
  3. * 功能:                        实时数据发送缓冲区
  4. * 作者:                        cp1300@139.com
  5. * 创建时间:                2015-08-09
  6. * 最后修改时间:        2017-08-26
  7. * 详细:                        用于数据发送缓冲区
  8.                                         2017-08-26:增加溢出回调函数,可以对溢出的数据进行处理,用于FIFO嵌套
  9. *************************************************************************************************************/
  10. #include "system.h"
  11. #include "usart.h"
  12. #include "led.h"
  13. #include "main.h"
  14. #include "SendDataFIFO.h"
  15. #include "SYSMalloc.h"

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




  17. //初始化缓冲区
  18. bool FIFO_Init(FIFO_HANDLE *pHandle,u16 OneSize, u16 MaxCnt, void (*FullCallBack)(void *pData), bool isExtSRAM)
  19. {
  20.        
  21.         if(pHandle == NULL) return FALSE;
  22.         if(isExtSRAM == TRUE)        //是否使用外部缓冲区
  23.         {
  24.                 pHandle->pFifoBuff = (u8 *)mymalloc(SRAMEX, OneSize*MaxCnt);                        //缓冲区指针,申请内存
  25.                 pHandle->pByteCntBuff = (u16 *)mymalloc(SRAMEX, sizeof(u16)*MaxCnt);        //数据大小记录缓冲区,申请内存
  26.         }
  27.         else
  28.         {
  29.                 pHandle->pFifoBuff = (u8 *)mymalloc(SRAMIN, OneSize*MaxCnt);                        //缓冲区指针,申请内存
  30.                 pHandle->pByteCntBuff = (u16 *)mymalloc(SRAMIN, sizeof(u16)*MaxCnt);        //数据大小记录缓冲区,申请内存
  31.         }
  32.        
  33.         //uart_printf("pHandle->pFifoBuff=0x%X\r\n", (u32)pHandle->pFifoBuff);
  34.         //uart_printf("pHandle->pByteCntBuff=0x%X\r\n", (u32)pHandle->pByteCntBuff);
  35.         if(pHandle->pFifoBuff==NULL)
  36.         {
  37.                 DEBUG("pHandle->pFifoBuff申请内存出错\r\n");
  38.         }
  39.         if(pHandle->pByteCntBuff==NULL)
  40.         {
  41.                 DEBUG("pHandle->pByteCntBuff申请内存出错\r\n");
  42.         }
  43.         pHandle->InitStatus = 0;                //初始化成功状态无效
  44.         if((pHandle->pFifoBuff==NULL)||(pHandle->pByteCntBuff==NULL)) return FALSE;
  45.         pHandle->OneSize = OneSize;                //单条数据大小
  46.         pHandle->Cnt = MaxCnt;                        //缓冲区总数据容量(条数)
  47.         pHandle->ReadCnt = 0;                        //读取位置
  48.         pHandle->WriteCnt = 0;                        //写位置
  49.         pHandle->NotReadCnt = 0;                //未读取数量
  50.         pHandle->FullCallBack = FullCallBack;                        //溢出回调函数
  51.         pHandle->InitStatus = FIFO_INIT_STATUS_ID;                //初始化成功状态有效
  52.        
  53.        
  54.         return TRUE;
  55. }


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

  64.         if(pHandle == NULL) return FALSE;
  65.         if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return FALSE;                                                //没有初始化
  66.         if (pHandle->NotReadCnt >= pHandle->Cnt)                                                                                        //发送溢出                                                                               
  67.         {
  68.                 cnt = pHandle->WriteCnt;                                                                                                                //先将写指针后移,占位,防止多线程写冲突
  69.                
  70.                 if(pHandle->FullCallBack!=NULL) pHandle->FullCallBack(&pHandle->pFifoBuff[cnt * pHandle->OneSize]);
  71.                 if (ByteCnt > pHandle->OneSize) ByteCnt = pHandle->OneSize;                                                //限制单条数据大小
  72.                
  73.                 pHandle->WriteCnt++;
  74.                 if (pHandle->WriteCnt >= pHandle->Cnt) pHandle->WriteCnt = 0;                                        //环形FIFO
  75.                 pHandle->ReadCnt++;                                                                                                                                //读取数量增加
  76.                 if (pHandle->ReadCnt >= pHandle->Cnt) pHandle->ReadCnt = 0;                                                //环形FIFO,把读写指针都增加,但是剩余数据数量不变
  77.                 pHandle->pByteCntBuff[cnt] = ByteCnt;                                                                                        //记录数据大小
  78.                 memcpy(&pHandle->pFifoBuff[cnt * pHandle->OneSize], pBuff, ByteCnt);                        //拷贝数据到缓冲区
  79.                
  80.                 return FALSE;                                                                                                                                        //数据已经满了
  81.         }
  82.         else
  83.         {
  84.                 if (ByteCnt > pHandle->OneSize) ByteCnt = pHandle->OneSize;                                                //限制单条数据大小
  85.                 //先将写指针后移,占位,防止多线程写冲突
  86.                 cnt = pHandle->WriteCnt;
  87.                 pHandle->WriteCnt++;
  88.                 if (pHandle->WriteCnt >= pHandle->Cnt) pHandle->WriteCnt = 0;                                        //环形FIFO
  89.                 pHandle->pByteCntBuff[cnt] = ByteCnt;                                                                                        //记录数据大小
  90.                
  91.                 memcpy(&pHandle->pFifoBuff[cnt * pHandle->OneSize], pBuff, ByteCnt);                        //拷贝数据到缓冲区
  92.                 /*{
  93.                         u16 i;
  94.                         printf("\r\n写入的数据测试[读%d/写:%d]:\r\n",pHandle->ReadCnt,pHandle->WriteCnt);
  95.                         for(i = 0;i < ByteCnt;i ++)
  96.                         {
  97.                                 pHandle->pFifoBuff[cnt * pHandle->OneSize+i] = pBuff[i];
  98.                                 printf("%02X\t",pBuff[i]);
  99.                                 if(pHandle->pFifoBuff[cnt * pHandle->OneSize+i] != pBuff[i])
  100.                                 {
  101.                                         printf("拷贝检测错误,数据丢失了\r\n");
  102.                                 }
  103.                         }
  104.                         printf("\r\n检测写入的数据测试:\r\n");
  105.                         for(i = 0;i < ByteCnt;i ++)
  106.                         {
  107.                                 printf("%02X\t",pHandle->pFifoBuff[cnt * pHandle->OneSize+i]);
  108.                         }
  109.                         printf("\r\n");
  110.                 }*/

  111. #ifdef _UCOS_II_
  112.                 OS_ENTER_CRITICAL();                                                //关闭系统中断
  113. #endif
  114.                
  115.                 pHandle->NotReadCnt ++;                                                                                                                        //没有读取的数量增加
  116.                
  117. #ifdef _UCOS_II_
  118.                 OS_EXIT_CRITICAL();                                                         //开启系统中断
  119. #endif               
  120.                
  121.                 return TRUE;
  122.         }
  123.        

  124.        
  125. }




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

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


  156. #ifdef _UCOS_II_
  157.         OS_ENTER_CRITICAL();                                                //关闭系统中断
  158. #endif
  159.                
  160.         pHandle->NotReadCnt--;                                                                                                                        //没有读取的数量减少
  161. #ifdef _UCOS_II_
  162.         OS_EXIT_CRITICAL();                                                         //开启系统中断
  163. #endif       
  164.        
  165.        

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


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


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

  183.         return pHandle->NotReadCnt;                                                //FIFO内没有读取的数据数量
  184. }
 楼主| 雨果喝水 发表于 2023-1-31 23:59 | 显示全部楼层
  1. /*************************************************************************************************************
  2. * 文件名:                        SendDataFIFO.h
  3. * 功能:                        实时数据发送缓冲区
  4. * 作者:                        cp1300@139.com
  5. * 创建时间:                2015-08-09
  6. * 最后修改时间:        2017-08-26
  7. * 详细:                        用于数据发送缓冲区
  8.                                         2017-08-26:增加溢出回调函数,可以对溢出的数据进行处理
  9. *************************************************************************************************************/
  10. #ifndef __SEND_DATA_FIFO_H__
  11. #define __SEND_DATA_FIFO_H__

  12. #include "system.h"


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



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

  31. #endif        //__SEND_DATA_FIFO_H__
 楼主| 雨果喝水 发表于 2023-1-31 23:59 | 显示全部楼层
示例,比如定时发送数据,将数据定时拷贝到FIFO中,另一个线程定时启动,发送数据:
 楼主| 雨果喝水 发表于 2023-1-31 23:59 | 显示全部楼层
  1. //拷贝实时数据到发送缓冲区中,进行立即发送数据(注意功能码必须为8位)
  2. void CopyTempDataToFIFO(u8 Fun)
  3. {
  4.         u8 i;
  5.        
  6.         //写入需要发送的数据的功能码
  7.         g_TempRealData.Fun = Fun;                                                //功能码
  8.         g_TempRealData.SerialNumber = 0;                                //流水号设置为0,自动分配
  9.         g_TempRealData.Time[0] = timer.w_year-2000;                //拷贝实时时间
  10.         g_TempRealData.Time[1] = timer.w_month;                        //拷贝实时时间
  11.         g_TempRealData.Time[2] = timer.w_date;                        //拷贝实时时间
  12.         g_TempRealData.Time[3] = timer.hour;                        //拷贝实时时间
  13.         g_TempRealData.Time[4] = timer.min;                                //拷贝实时时间
  14.         g_TempRealData.Time[5] = 0;                                                //拷贝实时时间
  15.        
  16.         //循环将数据写入到4个中心站的发送FIFO中(如果中心站端口为0,则认为中心站无效,不进行发送)
  17.         for(i=0;i<4;i++)
  18.         {
  19.                 //服务器配置有效才写入数据到缓冲区
  20.                 if(g_SYS_Config.ServerPort[i] > 0)
  21.                 {
  22.                         FIFO_Write(&g_SendFifoBuff[i], (u8 *)&g_TempRealData, sizeof(REAL_DATA));        //写入数据到发送缓冲区
  23.                 }
  24.                 else
  25.                 {
  26.                         FIFO_Clear(&g_SendFifoBuff[i]);                                //不用发送的站点,清除缓冲区
  27.                         FIFO_Clear(&g_SendCacheFifoBuff[i]);                //不用发送的站点,清除Cache缓冲区
  28.                 }
  29.         }
  30. }
 楼主| 雨果喝水 发表于 2023-2-1 00:00 | 显示全部楼层
发送数据,从FIFO中读取
  1. isFifoStatus = FIFO_ReadNotCopy(pFifoHandle, (u8 **)&pTempRealData, &ByteLen);
  2.                 if(isFifoStatus==TRUE)        //有数据要发送                                                                                                        //实时缓冲区中没有数据,检查Cache中是否有数据
  3.                 {....../*发送数据逻辑*/ FIFO_ReduceOne(pFifoHandle);//发送结束后,清除当前读取过的数据
  4.                 }
您需要登录后才可以回帖 登录 | 注册

本版积分规则

90

主题

1213

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部