【組件】通用環形緩衝區模塊

寫在前面

環形緩衝區是嵌入式應用(醫療電子、消費電子、工控)中常用的數據結構模型,如音視頻流、通信總線數據收發等。一些操作系統、第三方庫都提供現成的環形緩衝區接口API,性能和安全性都有保障,多線程訪問互斥機制,空內存訪問保護等。

1.基本原則

先進先出(FIFO),重複使用。

2.實現原理

環形緩衝區,顧名思義就是一段循環使用的一段內存。通過寫指針向“空白內存”(未寫入過或者已經被讀出的內存)寫入數據並記錄,讀指針從已寫的內存讀取數據並記錄,當指針訪問到內存最後一個位置時,再折回第一個內存位置,達到循環效果。

 

3.注意事項

1)環形緩衝區本質是“生產者消費者”模型,必須先有生產(寫)後有消費(讀),不可提前消費(讀);

2)注意程序效率問題,如果讀效率過低,導致“生產”過剩,從而覆蓋未讀出的數據,導致出錯,此時需增加環形緩衝區內存大小或者優化代碼效率;

3)單一線程訪問時是安全的,多線程訪問時,爲保證數據安全性必須加互斥鎖機制;

4.應用場合

處理串口(RS232)接收數據命令:

串口中斷--->數據接收--->寫入環形緩衝區--->數據處理線程讀取環形緩衝區--->處理有效數據。

5.實現方式

頭文件


#ifndef _FIFO_H_
#define _FIFO_H_

#include <stdbool.h>
#include <stdint.h>

typedef struct
{
	uint8_t *buf;      	/* 緩衝區 */
	uint32_t buf_size;    	/* 緩衝區大小 */
	uint8_t *pwrite;      	/* 寫指針 */
	uint8_t *pread;       	/* 讀指針 */
	uint8_t	write_cover;    /* 讀覆蓋標識 */
	uint8_t read_cover;	/* 寫覆蓋標識 */
	void (*lock)(void);	/* 互斥上鎖 */
	void (*unlock)(void);	/* 互斥解鎖 */
}_fifo_t;


extern void fifo_register(_fifo_t *pfifo, uint8_t *pfifo_buf, uint32_t size);
extern void fifo_release(_fifo_t *pfifo);
extern uint32_t fifo_write(_fifo_t *pfifo, const uint8_t *pbuf, uint32_t size);
extern uint32_t fifo_read(_fifo_t *pfifo, uint8_t *pbuf, uint32_t size);
extern uint32_t fifo_get_total_size(_fifo_t *pfifo);
extern uint32_t fifo_get_free_size(_fifo_t *pfifo);
extern uint32_t fifo_get_occupy_size(_fifo_t *pfifo);

#endif

源文件

#include <stddef.h>
#include "fifo.h"


/**
  * @brief  註冊一個fifo
  * @param  pfifo: fifo結構體指針
		    pfifo_buf: fifo內存塊
		    size: 長度
  * @retval none
*/
void fifo_register(_fifo_t *pfifo, uint8_t *pfifo_buf, uint32_t size)
{
	pfifo->buf_size = size;
	pfifo->buf 	= pfifo_buf;
	pfifo->pwrite = pfifo->buf;
	pfifo->pread  = pfifo->buf;
	pfifo->write_cover = 0;
	pfifo->read_cover  = 0;
}

/**
  * @brief  釋放fifo
  * @param  pfifo: fifo結構體指針
  * @retval none
*/
void fifo_release(_fifo_t *pfifo)
{
	pfifo->buf_size = 0;
	pfifo->buf 	= NULL;
	pfifo->pwrite = 0;
	pfifo->pread  = 0;
	pfifo->write_cover = 0;
	pfifo->read_cover  = 0;
}

/**
  * @brief  往fifo寫數據
  * @param  pfifo: fifo結構體指針
		    pbuf: 待寫數據
		    size: 待寫數據大小
  * @retval 實際寫大小
*/
uint32_t fifo_write(_fifo_t *pfifo, const uint8_t *pbuf, uint32_t size)
{
	uint32_t w_size= 0,free_size = 0;
	
	if ((size==0) || (pfifo==NULL) || (pbuf==NULL))
	{
		return 0;
	}

        free_size = fifo_get_free_size(pfifo);
        if(free_size == 0)
        {
            return 0;
        }

        if(free_size < size)
        {
            size = free_size;
        }

	w_size = size;
	while(w_size-- > 0)
	{
		*pfifo->pwrite++ = *pbuf++;
		if (pfifo->pwrite >= &(pfifo->buf[pfifo->buf_size])) 
		{
			pfifo->pwrite = pfifo->buf;
			pfifo->write_cover = ~pfifo->write_cover;
		}
	}
	
	return size;
}

/**
  * @brief  從fifo讀數據
  * @param  pfifo: fifo結構體指針
		    pbuf: 待讀數據緩存
		    size: 待讀數據大小
  * @retval 實際讀大小
*/
uint32_t fifo_read(_fifo_t *pfifo, uint8_t *pbuf, uint32_t size)
{
	uint32_t r_size = 0,occupy_size = 0;
	
	if ((size==0) || (pfifo==NULL) || (pbuf==NULL))
	{
		return 0;
	}
    
        occupy_size = fifo_get_occupy_size(pfifo);
        if(occupy_size == 0)
        {
            return 0;
        }
    
        if(occupy_size < size)
        {
            size = occupy_size;
        }

	r_size = size;
	while(r_size-- > 0)
	{
		*pbuf++ = *pfifo->pread++;
		if (pfifo->pread >= &(pfifo->buf[pfifo->buf_size]))
		{
			pfifo->pread = pfifo->buf;
			pfifo->read_cover = ~pfifo->read_cover;
		}
	}
	
	return size;
}

/**
  * @brief  獲取fifo空間大小
  * @param  pfifo: fifo結構體指針
  * @retval fifo大小
*/
uint32_t fifo_get_total_size(_fifo_t *pfifo)
{
	if (pfifo==NULL)
		return 0;
	
	return pfifo->buf_size;
}

/**
  * @brief  獲取fifo空閒空間大小
  * @param  pfifo: fifo結構體指針
  * @retval 空閒空間大小
*/
uint32_t fifo_get_free_size(_fifo_t *pfifo)
{
	uint32_t size;

	if (pfifo==NULL)
		return 0;
	
	if (pfifo->pwrite == pfifo->pread)
	{
		if(pfifo->write_cover == pfifo->read_cover)
			size = pfifo->buf_size;
		else
			size = 0;
	}
	else
	{
		if (pfifo->pwrite > pfifo->pread)
			size = (uint32_t)&(pfifo->buf[pfifo->buf_size]) - (uint32_t)&(pfifo->buf[0]) - (uint32_t)(pfifo->pwrite) + (uint32_t)(pfifo->pread);
		else
			size = pfifo->pread - pfifo->pwrite;
	}
	return size;
}

/**
  * @brief  獲取fifo已用空間大小
  * @param  pfifo: fifo結構體指針
  * @retval fifo已用大小
*/
uint32_t fifo_get_occupy_size(_fifo_t *pfifo)
{
	uint32_t size;

	if (pfifo==NULL)
		return 0;
	
	size = pfifo->buf_size - fifo_get_free_size(pfifo);
	return size;
}

其中,注意點是環形緩衝區“滿”與“空”狀態處理,這裏使用了讀、寫指針的“翻轉”標識,即當讀、寫指針訪問到緩衝區最後地址時,訪問地址折回初始地址,此時做一個翻轉標識;當讀地址和寫地址相等時,如果讀、寫翻轉標識相等,說明此時緩衝區爲“空狀態”,否則緩衝區爲“滿”狀態。

5.應用例子

註冊一個512字節大小環形緩衝區,然後往緩衝區寫入數據,接着讀出來。

#include "fifo.h"

static uint8_t	buf_0[512];
static _fifo_t  fifo_0;

int main(void)
{
	uint8_t w_data[] = {0x01,0x02,0x03};
	uint8_t	r_data[10];
	
	fifo_register(&fifo_0, buf_0, sizeof(buf_0));
	fifo_write(&fifo_0, (const uint8_t*)w_data, sizeof(data));
	fifo_read(&fifo_0, r_data, 1);
	fifo_read(&fifo_0, &r_data[1], 2);

	return 0;
}

 

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