ringbuff | 通用FIFO環形緩衝區實現庫

嵌入式開源項目精選專欄

本專欄由Mculover666創建,主要內容爲尋找嵌入式領域內的優質開源項目,一是幫助開發者使用開源項目實現更多的功能,二是通過這些開源項目,學習大佬的代碼及背後的實現思想,提升自己的代碼水平,和其它專欄相比,本專欄的優勢在於:

不會單純的介紹分享項目,還會包含作者親自實踐的過程分享,甚至還會有對它背後的設計思想解讀

目前本專欄包含的開源項目有:

如果您自己編寫或者發現的開源項目不錯,歡迎留言或者私信投稿到本專欄,分享獲得雙倍的快樂!

1. ringbuff

本期給大家帶來的開源項目是 ringbuff ,一款通用FIFO環形緩衝區實現的開源庫,作者MaJerle,目前收穫 79 個 star,遵循 MIT 開源許可協議。

目前 ringbuff 的特點有:

  • 使用C99語法編寫,並且沒有平臺相關代碼;
  • 沒有動態內存分配;
  • 使用更優的內存複製而不是循環從內存讀取數據/向內存寫入數據;

項目地址:https://github.com/MaJerle/ringbuff

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_t	ringbuff_init(RINGBUFF_VOLATILE ringbuff_t* buff, void* buffdata, size_t size);

該 API 用來初始化一個ringbuff句柄(指向ringbuff結構體的指針),其中傳入的參數分別爲:

  • buff:ringbuff句柄;
  • buffdata:緩衝區地址;
  • size:緩衝區大小;

首先創建一個緩衝區句柄,開闢一塊緩衝區:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//用於串口接收
uint8_t recv_data = 0;

//用於存儲從緩衝區讀取出的數據
uint8_t read_data = 0;

//用於串口1的ringbuff句柄
ringbuff_t	usart1_ringbuff;

//開闢一塊內存用於緩衝區
#define USART1_BUFFDATA_SIZE	150
uint8_t usart1_buffdata[USART1_BUFFDATA_SIZE];

/* USER CODE END 0 */

然後在main函數中初始化ringbuff:

/* USER CODE BEGIN 2 */
printf("ringbuff Port By Mculover666\r\n");

//初始化ringbuff句柄
if(1 != ringbuff_init(&usart1_ringbuff, (uint8_t*)usart1_buffdata, USART1_BUFFDATA_SIZE))
{
	printf("usart1 ringbuff init fail.\r\n");
}

//使能串口中斷接收
HAL_UART_Receive_IT(&huart1, (uint8_t*)&recv_data, 1);

/* USER CODE END 2 */

3.4. 數據接收

接收到一個字節數據後,話不多說,直接往緩衝區扔:

/* USER CODE BEGIN 4 */
/* 中斷回調函數 */
void HAL_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);
    }
}
/* USER CODE END 4 */

3.5. 數據處理

數據處理在while(1)中進行,每隔200ms將緩衝區數據全部讀出進行處理:

 /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
	  

    /* USER CODE BEGIN 3 */
	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("your data has %d byte(s):\r\n\t", data_len);
			}
			
			//提取data_len個數據
			for(i = 0; i < data_len; i++)
			{
				if((len = ringbuff_read(&usart1_ringbuff, (uint8_t*)&read_data, sizeof(read_data))) > 0)
				{
					printf("[0x%02x] ", read_data); 
				}
			}
			printf("over\r\n");
		}
	}
	HAL_Delay(200);
	  
  }
  /* USER CODE END 3 */

編譯下載測試,實驗結果如下,可以做到不丟包解析:

3.6. 丟包測試

經過3.2節的測試,不丟包的最小緩衝區大小是140個字節,接下里我們將緩衝區大小修改爲100個字節,測試一下是否產生丟包:

//開闢一塊內存用於緩衝區
#define USART1_BUFFDATA_SIZE	100		//會發生丟包
//#define USART1_BUFFDATA_SIZE	150		//10ms接收7byte的協議包時不丟包
uint8_t usart1_buffdata[USART1_BUFFDATA_SIZE];

再次編譯下載,查看串口輸出:

4. 設計思想解讀

關於環形緩衝區背後的設計實現,請閱讀這篇文章,寫的非常棒:

5. 項目工程源碼獲取和問題交流

目前我將ringbuff源碼、我移植到小熊派STM32L431RCT6開發板的工程源碼上傳到了QQ羣裏(包含好幾份HAL庫,QQ相對速度快點),可以在QQ羣裏下載,有問題也可以在羣裏交流,當然也歡迎大家分享出來自己移植的工程到QQ羣裏

放上QQ羣二維碼:

接收更多精彩文章及資源推送,歡迎訂閱我的微信公衆號:『mculover666』。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章