轉自| Mculover666
今天給大家分享一個開源的嵌入式通用FIFO環形緩沖區實現庫:ringbuff 地址:
https://github.com/MaJerle/ringbuff
1. 關于ringbuff
開源項目ringbuff ,是一款通用FIFO環形緩沖區實現的開源庫,作者MaJerle,遵循 MIT 開源許可協議。
目前 ringbuff 的特點有:
使用C99語法編寫,并且沒有平臺相關代碼;
沒有動態內存分配;
使用更優的內存復制而不是循環從內存讀取數據/向內存寫入數據;
2. 移植ringbuff
2.1. 移植思路
在移植過程中主要參考兩個資料:項目的readme文檔和demo工程。
對于這些開源項目,其實移植起來也就兩步:
① 添加源碼到裸機工程中;
② 實現需要的接口即可;
2.2. 準備裸機工程
本文中我使用的是小熊派IoT開發套件,主控芯片為STM32L431RCT6:
移植之前需要準備一份裸機工程,我使用STM32CubeMX生成,需要初始化以下配置:
配置一個串口,中斷方式接收數據,查詢方式發送數據;
printf重定向;
2.3. 添加ringbuff 到工程中
① 復制 ringbuff 源碼到工程中:
② 在keil中添加 ringbuff 組件的源碼文件:
③ 添加 ringbuff 的頭文件路徑:
2.4. 配置ringbuff
ringbuff中默認volatile關鍵詞沒有定義,需要手動配置一下,在ringbuff.h中:
至此,ringbuff移植修改完成,可以愉快的使用ringbuff啦~
3. 使用ringbuff
3.1. 為什么使用ringbuff
緩沖區一般用于解決設備接收數據的速度和設備處理速度不匹配的情況下,防止丟包,通俗的來說就是:收到數據先存進緩沖區,等到CPU來處理的時候一次性取出處理。
緩沖區有兩種形式,一種是數組,一種就是本文所介紹的環形緩沖區ringbuff。
相較于數組,環形緩沖區對整段內存的利用達到最大,并且使用非常方便,如下:
① 寫入的時候不用手動維護下標,直接寫入即可(由緩沖區的實現維護);
② 讀取的時候不用判斷從哪里讀,直接讀取即可(有緩沖區的實現維護)
本文設計的一個簡單的不定長串口協議如下:
數據類型:比如0x3F表示這是通道1的數據,0x4E表示通道2的數據;
數據長度:表示后面跟著有效數據的長度;
有效數據:有效字節數;
校驗數據:省略;
接下來演示如何用環形緩沖區做到不丟包解析。
3.2. 計算緩沖區大小
假定數據每200ms處理一次,而數據10ms接收一次,每次接收的數據包長度為7個字節。
要想做到不丟包,就需要將200ms內接收到的所有數據包都存進緩沖區,所以緩沖區大小至少為:200/10*7 = 140 個字節。
保險起見,可以將緩沖區適當的擴大一下,設置為150個字節。
3.3. 初始化緩沖區
使用時包含頭文件:
#include"ringbuff/ringbuff.h"
接著初始化緩沖區:
uint8_tringbuff_init(RINGBUFF_VOLATILEringbuff_t*buff,void*buffdata,size_tsize);
該 API 用來初始化一個ringbuff句柄(指向ringbuff結構體的指針),其中傳入的參數分別為:
buff:ringbuff句柄;
buffdata:緩沖區地址;
size:緩沖區大小;
首先創建一個緩沖區句柄,開辟一塊緩沖區:
/*Privateusercode---------------------------------------------------------*/ /*USERCODEBEGIN0*/ //用于串口接收 uint8_trecv_data=0; //用于存儲從緩沖區讀取出的數據 uint8_tread_data=0; //用于串口1的ringbuff句柄 ringbuff_tusart1_ringbuff; //開辟一塊內存用于緩沖區 #defineUSART1_BUFFDATA_SIZE150 uint8_tusart1_buffdata[USART1_BUFFDATA_SIZE]; /*USERCODEEND0*/
然后在main函數中初始化ringbuff:
/*USERCODEBEGIN2*/ printf("ringbuffPortByMculover666 "); //初始化ringbuff句柄 if(1!=ringbuff_init(&usart1_ringbuff,(uint8_t*)usart1_buffdata,USART1_BUFFDATA_SIZE)) { printf("usart1ringbuffinitfail. "); } //使能串口中斷接收 HAL_UART_Receive_IT(&huart1,(uint8_t*)&recv_data,1); /*USERCODEEND2*/
3.4. 數據接收
接收到一個字節數據后,話不多說,直接往緩沖區扔:
/*USERCODEBEGIN4*/ /*中斷回調函數*/ voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart) { /*判斷是哪個串口觸發的中斷*/ if(huart->Instance==USART1) { /*將接收到的數據寫入緩沖區*/ ringbuff_write(&usart1_ringbuff,&recv_data,1); //重新使能串口接收中斷 HAL_UART_Receive_IT(huart,(uint8_t*)&recv_data,1); } } /*USERCODEEND4*/
3.5. 數據處理
數據處理在while(1)中進行,每隔200ms將緩沖區數據全部讀出進行處理:
/*USERCODEBEGINWHILE*/ while(1) { /*USERCODEENDWHILE*/ /*USERCODEBEGIN3*/ while((len=ringbuff_read(&usart1_ringbuff,(uint8_t*)&read_data,sizeof(read_data)))>0) { /*捕獲起始標志*/ if(read_data==0x3F) { //讀取數據字節數,最大支持0xFF if((len=ringbuff_read(&usart1_ringbuff,(uint8_t*)&read_data,sizeof(read_data)))>0) { data_len=read_data; printf("yourdatahas%dbyte(s): ",data_len); } //提取data_len個數據 for(i=0;i0) { printf("[0x%02x]",read_data); } } printf("over "); } } HAL_Delay(200); } /*USERCODEEND3*/
編譯下載測試,實驗結果如下,可以做到不丟包解析:
3.6. 丟包測試
經過3.2節的計算,不丟包的最小緩沖區大小是140個字節,接下里我們將緩沖區大小修改為100個字節,測試一下是否產生丟包:
//開辟一塊內存用于緩沖區 #defineUSART1_BUFFDATA_SIZE100//會發生丟包 //#defineUSART1_BUFFDATA_SIZE150//10ms接收7byte的協議包時不丟包 uint8_tusart1_buffdata[USART1_BUFFDATA_SIZE];
再次編譯下載,查看串口輸出:
4. 設計思想解讀
關于環形緩沖區背后的設計實現,請閱讀這篇文章,寫的非常棒:
STM32進階之串口環形緩沖區實現
-
緩沖區
+關注
關注
0文章
33瀏覽量
9093 -
嵌入式
+關注
關注
5068文章
19014瀏覽量
303229 -
fifo
+關注
關注
3文章
387瀏覽量
43547 -
串口
+關注
關注
14文章
1543瀏覽量
76185 -
開源
+關注
關注
3文章
3245瀏覽量
42396
原文標題:分享一個嵌入式通用FIFO環形緩沖區實現庫
文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論