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