【组件】通用环形缓冲区模块

写在前面

环形缓冲区是嵌入式应用(医疗电子、消费电子、工控)中常用的数据结构模型,如音视频流、通信总线数据收发等。一些操作系统、第三方库都提供现成的环形缓冲区接口API,性能和安全性都有保障,多线程访问互斥机制,空内存访问保护等。

1.基本原则

先进先出(FIFO),重复使用。

2.实现原理

环形缓冲区,顾名思义就是一段循环使用的一段内存。通过写指针向“空白内存”(未写入过或者已经被读出的内存)写入数据并记录,读指针从已写的内存读取数据并记录,当指针访问到内存最后一个位置时,再折回第一个内存位置,达到循环效果。

 

3.注意事项

1)环形缓冲区本质是“生产者消费者”模型,必须先有生产(写)后有消费(读),不可提前消费(读);

2)注意程序效率问题,如果读效率过低,导致“生产”过剩,从而覆盖未读出的数据,导致出错,此时需增加环形缓冲区内存大小或者优化代码效率;

3)单一线程访问时是安全的,多线程访问时,为保证数据安全性必须加互斥锁机制;

4.应用场合

处理串口(RS232)接收数据命令:

串口中断--->数据接收--->写入环形缓冲区--->数据处理线程读取环形缓冲区--->处理有效数据。

5.实现方式

头文件


#ifndef _FIFO_H_
#define _FIFO_H_

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

typedef void (*lock_fun)(void);

typedef struct
{
    uint8_t *buf;      	  /* 缓冲区 */
    uint32_t buf_size;    /* 缓冲区大小 */
    uint32_t occupy_size; /* 有效数据大小 */
    uint8_t *pwrite;      /* 写指针 */
    uint8_t *pread;       /* 读指针 */
    void (*lock)(void);	  /* 互斥上锁 */
    void (*unlock)(void); /* 互斥解锁 */
}_fifo_t;
 
 
extern void fifo_register(_fifo_t *pfifo, uint8_t *pfifo_buf, uint32_t size,
                            lock_fun lock, lock_fun unlock);
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,
                   lock_fun lock, lock_fun unlock)
{
    pfifo->buf_size = size;
    pfifo->buf 	= pfifo_buf;
    pfifo->pwrite = pfifo->buf;
    pfifo->pread  = pfifo->buf;
    pfifo->occupy_size = 0;
    pfifo->lock = lock;
    pfifo->unlock = unlock;
}
 
/**
  * @brief  释放fifo
  * @param  pfifo: fifo结构体指针
  * @retval none
*/
void fifo_release(_fifo_t *pfifo)
{
    pfifo->buf_size = 0;
    pfifo->occupy_size = 0;
    pfifo->buf 	= NULL;
    pfifo->pwrite = 0;
    pfifo->pread  = 0;
    pfifo->lock = NULL;
    pfifo->unlock = NULL; 
}
 
/**
  * @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;
    if (pfifo->lock != NULL)
        pfifo->lock();
    while(w_size-- > 0)
    {
        *pfifo->pwrite++ = *pbuf++;
	if (pfifo->pwrite >= (pfifo->buf + pfifo->buf_size)) 
	{
	    pfifo->pwrite = pfifo->buf;
	}
        pfifo->occupy_size++;
    }
    if (pfifo->unlock != NULL)
        pfifo->unlock();
    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;
    }
    if (pfifo->lock != NULL)
        pfifo->lock();
    r_size = size;
    while(r_size-- > 0)
    {
	*pbuf++ = *pfifo->pread++;
	if (pfifo->pread >= (pfifo->buf + pfifo->buf_size)) 
	{
	    pfifo->pread = pfifo->buf;
	}
        pfifo->occupy_size--;
    }
    if (pfifo->unlock != NULL)
        pfifo->unlock();
    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;
	
    size = pfifo->buf_size - fifo_get_occupy_size(pfifo);

    return size;
}
 
/**
  * @brief  获取fifo已用空间大小
  * @param  pfifo: fifo结构体指针
  * @retval fifo已用大小
*/
uint32_t fifo_get_occupy_size(_fifo_t *pfifo)
{
    if (pfifo==NULL)
        return 0;
    
    return  pfifo->occupy_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), 0, 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;
}

 

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Acuity.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值