1. 程式人生 > >STM32上使用的環形FIFO佇列,用於快取待發送資料

STM32上使用的環形FIFO佇列,用於快取待發送資料

C語言實現的環形FIFO佇列,用於執行緒非同步通訊,資料傳送非常方便,比如GPRS傳送資料,一個執行緒將資料寫入到FIFO,傳送執行緒不停的從FIFO中讀取資料,然後傳送,又比如上位機中,資料接收執行緒不停的接收資料,寫入到FIFO,另一個非同步的處理執行緒不停的讀取資料,進行處理。

  1. /*************************************************************************************************************
  2. * 檔名: SendDataFIFO.c
  3. * 功能: 實時資料傳送緩衝區
  4. * 作者:
    [email protected]
  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. if(pHandle == NULL) return FALSE;
  21. if(isExtSRAM == TRUE) //是否使用外部緩衝區
  22. {
  23. pHandle->pFifoBuff = (u8 *)mymalloc(SRAMEX, OneSize*MaxCnt); //緩衝區指標,申請記憶體
  24. pHandle->pByteCntBuff = (u16 *)mymalloc(SRAMEX, sizeof(u16)*MaxCnt); //資料大小記錄緩衝區,申請記憶體
  25. }
  26. else
  27. {
  28. pHandle->pFifoBuff = (u8 *)mymalloc(SRAMIN, OneSize*MaxCnt); //緩衝區指標,申請記憶體
  29. pHandle->pByteCntBuff = (u16 *)mymalloc(SRAMIN, sizeof(u16)*MaxCnt); //資料大小記錄緩衝區,申請記憶體
  30. }
  31. //uart_printf("pHandle->pFifoBuff=0x%X\r\n", (u32)pHandle->pFifoBuff);
  32. //uart_printf("pHandle->pByteCntBuff=0x%X\r\n", (u32)pHandle->pByteCntBuff);
  33. if(pHandle->pFifoBuff==NULL)
  34. {
  35. DEBUG("pHandle->pFifoBuff申請記憶體出錯\r\n");
  36. }
  37. if(pHandle->pByteCntBuff==NULL)
  38. {
  39. DEBUG("pHandle->pByteCntBuff申請記憶體出錯\r\n");
  40. }
  41. pHandle->InitStatus = 0; //初始化成功狀態無效
  42. if((pHandle->pFifoBuff==NULL)||(pHandle->pByteCntBuff==NULL)) return FALSE;
  43. pHandle->OneSize = OneSize; //單條資料大小
  44. pHandle->Cnt = MaxCnt; //緩衝區總資料容量(條數)
  45. pHandle->ReadCnt = 0; //讀取位置
  46. pHandle->WriteCnt = 0; //寫位置
  47. pHandle->NotReadCnt = 0; //未讀取數量
  48. pHandle->FullCallBack = FullCallBack; //溢位回撥函式
  49. pHandle->InitStatus = FIFO_INIT_STATUS_ID; //初始化成功狀態有效
  50. return TRUE;
  51. }
  52. //寫入一條資料
  53. //當緩衝區滿了後,快取最新的資料,丟掉最早的資料
  54. bool FIFO_Write(FIFO_HANDLE *pHandle,u8 *pBuff, u16 ByteCnt)
  55. {
  56. u16 cnt;
  57. #ifdef _UCOS_II_
  58. OS_CPU_SR cpu_sr;
  59. #endif
  60. if(pHandle == NULL) return FALSE;
  61. if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return FALSE; //沒有初始化
  62. if (pHandle->NotReadCnt >= pHandle->Cnt) //傳送溢位
  63. {
  64. cnt = pHandle->WriteCnt; //先將寫指標後移,佔位,防止多執行緒寫衝突
  65. if(pHandle->FullCallBack!=NULL) pHandle->FullCallBack(&pHandle->pFifoBuff[cnt * pHandle->OneSize]);
  66. if (ByteCnt > pHandle->OneSize) ByteCnt = pHandle->OneSize; //限制單條資料大小
  67. pHandle->WriteCnt++;
  68. if (pHandle->WriteCnt >= pHandle->Cnt) pHandle->WriteCnt = 0; //環形FIFO
  69. pHandle->ReadCnt++; //讀取數量增加
  70. if (pHandle->ReadCnt >= pHandle->Cnt) pHandle->ReadCnt = 0; //環形FIFO,把讀寫指標都增加,但是剩餘資料數量不變
  71. pHandle->pByteCntBuff[cnt] = ByteCnt; //記錄資料大小
  72. memcpy(&pHandle->pFifoBuff[cnt * pHandle->OneSize], pBuff, ByteCnt); //拷貝資料到緩衝區
  73. return FALSE; //資料已經滿了
  74. }
  75. else
  76. {
  77. if (ByteCnt > pHandle->OneSize) ByteCnt = pHandle->OneSize; //限制單條資料大小
  78. //先將寫指標後移,佔位,防止多執行緒寫衝突
  79. cnt = pHandle->WriteCnt;
  80. pHandle->WriteCnt++;
  81. if (pHandle->WriteCnt >= pHandle->Cnt) pHandle->WriteCnt = 0; //環形FIFO
  82. pHandle->pByteCntBuff[cnt] = ByteCnt; //記錄資料大小
  83. memcpy(&pHandle->pFifoBuff[cnt * pHandle->OneSize], pBuff, ByteCnt); //拷貝資料到緩衝區
  84. /*{
  85. u16 i;
  86. printf("\r\n寫入的資料測試[讀%d/寫:%d]:\r\n",pHandle->ReadCnt,pHandle->WriteCnt);
  87. for(i = 0;i < ByteCnt;i ++)
  88. {
  89. pHandle->pFifoBuff[cnt * pHandle->OneSize+i] = pBuff[i];
  90. printf("%02X\t",pBuff[i]);
  91. if(pHandle->pFifoBuff[cnt * pHandle->OneSize+i] != pBuff[i])
  92. {
  93. printf("拷貝檢測錯誤,資料丟失了\r\n");
  94. }
  95. }
  96. printf("\r\n檢測寫入的資料測試:\r\n");
  97. for(i = 0;i < ByteCnt;i ++)
  98. {
  99. printf("%02X\t",pHandle->pFifoBuff[cnt * pHandle->OneSize+i]);
  100. }
  101. printf("\r\n");
  102. }*/
  103. #ifdef _UCOS_II_
  104. OS_ENTER_CRITICAL(); //關閉系統中斷
  105. #endif
  106. pHandle->NotReadCnt ++; //沒有讀取的數量增加
  107. #ifdef _UCOS_II_
  108. OS_EXIT_CRITICAL(); //開啟系統中斷
  109. #endif
  110. return TRUE;
  111. }
  112. }
  113. //讀取一條資料,返回指標,無需複製資料
  114. bool FIFO_ReadNotCopy(FIFO_HANDLE *pHandle,u8 **pBuff, u16 *pByteCnt)
  115. {
  116. u16 cnt;
  117. printf("\r\n讀取資料[讀%d/寫:%d]:\r\n",pHandle->ReadCnt,pHandle->WriteCnt);
  118. if(pHandle == NULL) return FALSE;
  119. if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return FALSE; //沒有初始化
  120. if (pHandle->NotReadCnt == 0) return FALSE; //資料為空
  121. cnt = pHandle->pByteCntBuff[pHandle->ReadCnt]; //獲取資料大小
  122. if (cnt > pHandle->OneSize) cnt = pHandle->OneSize; //限制單條資料大小
  123. *pBuff = &pHandle->pFifoBuff[pHandle->ReadCnt * pHandle->OneSize]; //資料緩衝區指標
  124. *pByteCnt = cnt;
  125. return TRUE;
  126. }
  127. //未讀取資料減少一次,用於讀取資料返回指標後呼叫
  128. u16 FIFO_ReduceOne(FIFO_HANDLE *pHandle)
  129. {
  130. #ifdef _UCOS_II_
  131. OS_CPU_SR cpu_sr;
  132. #endif
  133. if(pHandle == NULL) return FALSE;
  134. if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return FALSE; //沒有初始化
  135. if (pHandle->NotReadCnt == 0) return FALSE;
  136. pHandle->ReadCnt++; //讀取數量增加
  137. if (pHandle->ReadCnt >= pHandle->Cnt) pHandle->ReadCnt = 0; //環形FIFO
  138. #ifdef _UCOS_II_
  139. OS_ENTER_CRITICAL(); //關閉系統中斷
  140. #endif
  141. pHandle->NotReadCnt--; //沒有讀取的數量減少
  142. #ifdef _UCOS_II_
  143. OS_EXIT_CRITICAL(); //開啟系統中斷
  144. #endif
  145. return pHandle->NotReadCnt; //返回沒有讀取的資料數量
  146. }
  147. //清除FIFO
  148. bool FIFO_Clear(FIFO_HANDLE *pHandle)
  149. {
  150. if(pHandle == NULL) return FALSE;
  151. if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return FALSE; //沒有初始化
  152. pHandle->ReadCnt = 0; //FIFO讀取位置
  153. pHandle->WriteCnt = 0; //FIFO寫入位置
  154. pHandle->NotReadCnt = 0; //FIFO內沒有讀取的資料數量為0
  155. return TRUE;
  156. }
  157. //獲取FIFO中資料數量
  158. u16 FIFO_GetDataNumber(FIFO_HANDLE *pHandle)
  159. {
  160. if(pHandle == NULL) return 0;
  161. if(pHandle->InitStatus != FIFO_INIT_STATUS_ID) return 0; //沒有初始化
  162. return pHandle->NotReadCnt; //FIFO內沒有讀取的資料數量
  163. }
  1. /*************************************************************************************************************
  2. * 檔名: SendDataFIFO.h
  3. * 功能: 實時資料傳送緩衝區
  4. * 作者: [email protected]
  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__

示例,比如定時傳送資料,將資料定時拷貝到FIFO中,另一個執行緒定時啟動,傳送資料:

  1. //拷貝實時資料到傳送緩衝區中,進行立即傳送資料(注意功能碼必須為8位)
  2. void CopyTempDataToFIFO(u8 Fun)
  3. {
  4. u8 i;
  5. //寫入需要傳送的資料的功能碼
  6. g_TempRealData.Fun = Fun; //功能碼
  7. g_TempRealData.SerialNumber = 0; //流水號設定為0,自動分配
  8. g_TempRealData.Time[0] = timer.w_year-2000; //拷貝實時時間
  9. g_TempRealData.Time[1] = timer.w_month; //拷貝實時時間
  10. g_TempRealData.Time[2] = timer.w_date; //拷貝實時時間
  11. g_TempRealData.Time[3] = timer.hour; //拷貝實時時間
  12. g_TempRealData.Time[4] = timer.min; //拷貝實時時間
  13. g_TempRealData.Time[5] = 0; //拷貝實時時間
  14. //迴圈將資料寫入到4箇中心站的傳送FIFO中(如果中心站埠為0,則認為中心站無效,不進行傳送)
  15. for(i=0;i<4;i++)
  16. {
  17. //伺服器配置有效才寫入資料到緩衝區
  18. if(g_SYS_Config.ServerPort[i] > 0)
  19. {
  20. FIFO_Write(&g_SendFifoBuff[i], (u8 *)&g_TempRealData, sizeof(REAL_DATA)); //寫入資料到傳送緩衝區
  21. }
  22. else
  23. {
  24. FIFO_Clear(&g_SendFifoBuff[i]); //不用傳送的站點,清除緩衝區
  25. FIFO_Clear(&g_SendCacheFifoBuff[i]); //不用傳送的站點,清除Cache緩衝區
  26. }
  27. }
  28. }

傳送資料,從FIFO中讀取

  1. isFifoStatus = FIFO_ReadNotCopy(pFifoHandle, (u8 **)&pTempRealData, &ByteLen);
  2. if(isFifoStatus==TRUE) //有資料要傳送 //實時緩衝區中沒有資料,檢查Cache中是否有資料
  3. {....../*傳送資料邏輯*/ FIFO_ReduceOne(pFifoHandle);//傳送結束後,清除當前讀取過的資料
  4. }