环形数据缓冲区的实现 --C语言版本

一、引言

在嵌入式编程中,我们可能经常需要用到缓冲区来对数据进行短暂的保存,大部分的嵌入式编程本人的做法都是将数据先放入缓冲区而不对数据进行实时的处理,而是将数据的处理放在另一个地方进行处理,这源于在嵌入式的编程中我们的数据来源大部分都是来自中断或者是回调中接收数据流,对于在中断中我只做数据的接收从而达到快速的离开中断的目的,对于数据的处理可以通过消息的机制通知处理机去处理。

如果觉得上面的想法不对,就当废话!这里也推荐大家这种做法,不喜忽喷,本人目前也是只菜鸟,欢迎大家对我所说的做法以及代码中的错误在评论区评论,我也将跟大家共同进步!

二、环形数据缓冲区的原理

环形缓冲区的实现原理

环形缓冲区通常有一个读指针和一个写指针。读指针指向环形缓冲区中可读的数据,写指针指向环形缓冲区中可写的缓冲区。通过移动读指针和写指针就可以实现缓冲区的数据读取和写入。在通常情况下,环形缓冲区的读用户仅仅会影响读指针,而写用户仅仅会影响写指针。如果仅仅有一个读用户和一个写用户,那么不需要添加互斥保护机制就可以保证数据的正确性。如果有多个读写用户访问环形缓冲区,那么必须添加互斥保护机制来确保多个用户互斥访问环形缓冲区。
这里在网上盗张图方便大家理解:
在这里插入图片描述

代码通过struct data_ringbuffer对数据缓冲区的进行管理,write_index为写指针,read_index为读指针,这里存在一个问题就是当读指针和写指针相等时缓冲区为空还是满的问题,这就通过 read_mirror和write_mirror来区别,当read_mirror==write_mirror时缓冲区为空,当read_mirror != write_mirror时缓冲区为满。

/* ring buffer */
struct data_ringbuffer
{
    
    
    uint8 *buffer_ptr;
    uint16 read_mirror : 1;
    uint16 read_index : 15;
    uint16 write_mirror : 1;
    uint16 write_index : 15;
    uint16 buffer_size;
};

这里只简单介绍一下,对于想具体理解的原理可以去网上搜索其他关于缓冲区原理的帖子了解,小伙伴也可以自己阅读接下来代码,里面的注释也写明了做法,没啥难懂的地方。

三、源码

前面的东西可以说很废话,直接贴上代码!

  • data_ringbuffer.h
/**
  **********************************************************************************
  * @file    data_ringbuffer.h
  * @author  czw
  * @version 
  * @date    2021-01-23
  * @brief
	**********************************************************************************  
  * @attention 
  *
  */

#ifndef __DATA_RINGBUFFER_H
#define __DATA_RINGBUFFER_H

typedef unsigned char           uint8;
typedef signed char             int8;
typedef unsigned short          wchar;
typedef unsigned short int      uint16;
typedef signed short int        int16;
typedef unsigned int            uint32;
typedef signed int              int32;

typedef signed char 			int8_t;
typedef signed short int 		int16_t;
typedef signed int 				int32_t;
typedef unsigned char 			uint8_t;
typedef unsigned short int 		uint16_t;
typedef unsigned int 			uint32_t;

/**
 * @ingroup BasicDef
 *
 * @def DATA_ALIGN_DOWN(size, align)
 * Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4)
 * would return 12.
 */
#define DATA_ALIGN_DOWN(size, align)      ((size) & ~((align) - 1))

/* DATA_ALIGN_SIZE*/
#define DATA_ALIGN_SIZE	4


/* ring buffer */
struct data_ringbuffer
{
    
    
    uint8 *buffer_ptr;
    uint16 read_mirror : 1;
    uint16 read_index : 15;
    uint16 write_mirror : 1;
    uint16 write_index : 15;
    /* as we use msb of index as mirror bit, the size should be signed and
     * could only be positive. */
    uint16 buffer_size;
};

enum data_ringbuffer_state
{
    
    
    DATA_RINGBUFFER_EMPTY,
    DATA_RINGBUFFER_FULL,
    DATA_RINGBUFFER_HALFFULL, /* half full is neither full nor empty */
};

/* 缓冲区初始化 */
void data_ringbuffer_init(struct data_ringbuffer *rb,uint8 *pool,uint16 size);
/* 缓冲区中填充指定数据长度的数据(当缓冲区空间小于待写入的数据长度事不覆盖缓冲区原有数据) */
uint16 data_ringbuffer_put(struct data_ringbuffer *rb,const uint8 *ptr,uint16 length);
/* 缓冲区中填充指定数据长度的数据(当缓冲区空间小于待写入的数据长度事覆盖缓冲区原有数据) */
uint16 data_ringbuffer_put_force(struct data_ringbuffer *rb,const uint8 *ptr,uint16 length);
/* 缓冲区中填充一个数据(当缓冲区空间小于待写入的数据长度事不覆盖缓冲区原有数据) */
uint16 data_ringbuffer_putchar(struct data_ringbuffer *rb, const uint8 ch);
/* 缓冲区中填充一个数据(当缓冲区空间小于待写入的数据长度事覆盖缓冲区原有数据) */
uint16 data_ringbuffer_putchar_force(struct data_ringbuffer *rb, const uint8 ch);
/* 缓冲区中获取指定长度的数据(返回实际获取数据的长度) */
uint16 data_ringbuffer_get(struct data_ringbuffer *rb,uint8 *ptr,uint16 length);
/* 缓冲区中获取一个数据(返回实际获取数据的长度) */
uint16 data_ringbuffer_getchar(struct data_ringbuffer *rb, uint8 *ch);

#endif

  • data_ringbuffer.c
/**
  **********************************************************************************
  * @file    data_ringbuffer.c
  
  * @author  czw
  * @version 
  * @date    2020-01-23
  * @brief
	**********************************************************************************  
  * @attention 
  *
  */
#include "data_ringbuffer.h"


/** return the size of empty space in rb */
#define data_ringbuffer_space_len(rb) ((rb)->buffer_size - data_ringbuffer_data_len(rb))


 /**
 ****************************************************************************************
 * @brief Initialization of the data buffer.
 *
 * @param[in] rb 	Pointer to a variable that manages the data buffer.
 * @param[in] pool 	A pointer to a data buffer.
 * @param[in] size 	Size of the data buffer.
 * @return 
 ****************************************************************************************
 */ 
void data_ringbuffer_init(struct data_ringbuffer *rb,
						  uint8           	   *pool,
                          uint16            	size)
{
    
    

    /* initialize read and write index */
    rb->read_mirror = rb->read_index = 0;
    rb->write_mirror = rb->write_index = 0;

    /* set buffer pool and size */
    rb->buffer_ptr = pool;
    rb->buffer_size = DATA_ALIGN_DOWN(size, DATA_ALIGN_SIZE);
}


/**
 ****************************************************************************************
 * @brief Get the data buffer state.
 *
 * @param[in] rb Pointer to a variable that manages the data buffer.
 * @return Returns the data buffer storage state.
 ****************************************************************************************
 */
enum data_ringbuffer_state data_ringbuffer_status(struct data_ringbuffer *rb)
{
    
    
    if (rb->read_index == rb->write_index)
    {
    
    
        if (rb->read_mirror == rb->write_mirror)
            return DATA_RINGBUFFER_EMPTY;
        else
            return DATA_RINGBUFFER_FULL;
    }
    return DATA_RINGBUFFER_HALFFULL;
}

 /**
 ****************************************************************************************
 * @brief Gets the size of the existing data in the data buffer.
 *
 * @param[in] Pointer to a variable that manages the data buffer.
 * @return Returns the size of the existing data in the data buffer (in bytes).
 ****************************************************************************************
 */ 
uint16 data_ringbuffer_data_len(struct data_ringbuffer *rb)
{
    
    
    switch (data_ringbuffer_status(rb))
    {
    
    
    case DATA_RINGBUFFER_EMPTY:
        return 0;
    case DATA_RINGBUFFER_FULL:
        return rb->buffer_size;
    case DATA_RINGBUFFER_HALFFULL:
    default:
        if (rb->write_index > rb->read_index)
            return rb->write_index - rb->read_index;
        else
            return rb->buffer_size - (rb->read_index - rb->write_index);
    };
}


 /**
 ****************************************************************************************
 * @brief  Put a block of data into ring buffer.
 *
 * @param[in] rb 	 Pointer to a variable that manages the data buffer.
 * @param[in] ptr 	 Pointer to the data to be written.
 * @param[in] length The length of the written data.
 * @return The length of the data actually written.
 ****************************************************************************************
 */ 
uint16 data_ringbuffer_put(struct data_ringbuffer *rb,
                           const uint8       	  *ptr,
                           uint16                 length)
{
    
    
    uint16 size = 0;

    /* whether has enough space */
    size = data_ringbuffer_space_len(rb);

    /* no space */
    if (size == 0)
        return 0;

    /* drop some data */
    if (size < length)
        length = size;
	/* One-time write */
    if (rb->buffer_size - rb->write_index > length)
    {
    
    
        /* read_index - write_index = empty space */
        memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
        /* this should not cause overflow because there is enough space for
         * length of data in current mirror */
        rb->write_index += length;
        return length;
    }
	/* two-time write */
    memcpy(&rb->buffer_ptr[rb->write_index],
           &ptr[0],
           rb->buffer_size - rb->write_index);
    memcpy(&rb->buffer_ptr[0],
           &ptr[rb->buffer_size - rb->write_index],
           length - (rb->buffer_size - rb->write_index));

    /* we are going into the other side of the mirror */
    rb->write_mirror = ~rb->write_mirror;
    rb->write_index = length - (rb->buffer_size - rb->write_index);

    return length;
}

 /**
 ****************************************************************************************
 * @brief  put a block of data into ring buffer. 
		   When the buffer is full,it will discard the old data.
 *
 * @param[in] rb 	 Pointer to a variable that manages the data buffer.
 * @param[in] ptr 	 Pointer to the data to be written.
 * @param[in] length The length of the written data.
 * @return The length of the data actually written.
 ****************************************************************************************
 */ 
uint16 data_ringbuffer_put_force(struct data_ringbuffer *rb,
								 const uint8     		*ptr,
								 uint16           		length)
{
    
    
    uint16 space_length = 0;

    space_length = data_ringbuffer_space_len(rb);

    if (length > space_length)
        length = rb->buffer_size;

    if (rb->buffer_size - rb->write_index > length)
    {
    
    
        /* read_index - write_index = empty space */
        memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
        /* this should not cause overflow because there is enough space for
         * length of data in current mirror */
        rb->write_index += length;

        if (length > space_length)
            rb->read_index = rb->write_index;

        return length;
    }

    memcpy(&rb->buffer_ptr[rb->write_index],
           &ptr[0],
           rb->buffer_size - rb->write_index);
    memcpy(&rb->buffer_ptr[0],
           &ptr[rb->buffer_size - rb->write_index],
           length - (rb->buffer_size - rb->write_index));

    /* we are going into the other side of the mirror */
    rb->write_mirror = ~rb->write_mirror;
    rb->write_index = length - (rb->buffer_size - rb->write_index);

    if (length > space_length)
    {
    
    
        rb->read_mirror = ~rb->read_mirror;
        rb->read_index = rb->write_index;
    }

    return length;
}

 /**
 ****************************************************************************************
 * @brief  put a character into ring buffer. 
 *
 * @param[in] rb 	 Pointer to a variable that manages the data buffer.
 * @param[in] ch 	 Written data.
 * @return The length of the data actually written.
 ****************************************************************************************
 */ 
uint16 data_ringbuffer_putchar(struct data_ringbuffer *rb, const uint8 ch)
{
    
    
    /* whether has enough space */
    if (!data_ringbuffer_space_len(rb))
        return 0;

    rb->buffer_ptr[rb->write_index] = ch;

    /* flip mirror */
    if (rb->write_index == rb->buffer_size-1)
    {
    
    
        rb->write_mirror = ~rb->write_mirror;
        rb->write_index = 0;
    }
    else
    {
    
    
        rb->write_index++;
    }

    return 1;
}

 /**
 ****************************************************************************************
 * @brief  put a character into ring buffer. 
 *		   When the buffer is full, it will discard one old data.
 * @param[in] rb 	 Pointer to a variable that manages the data buffer.
 * @param[in] ch 	 Written data.
 * @return The length of the data actually written.
 ****************************************************************************************
 */ 
uint16 data_ringbuffer_putchar_force(struct data_ringbuffer *rb, const uint8 ch)
{
    
    
    enum data_ringbuffer_state old_state;

    old_state = data_ringbuffer_status(rb);

    rb->buffer_ptr[rb->write_index] = ch;

    /* flip mirror */
    if (rb->write_index == rb->buffer_size-1)
    {
    
    
        rb->write_mirror = ~rb->write_mirror;
        rb->write_index = 0;
        if (old_state == DATA_RINGBUFFER_FULL)
        {
    
    
            rb->read_mirror = ~rb->read_mirror;
            rb->read_index = rb->write_index;
        }
    }
    else
    {
    
    
        rb->write_index++;
        if (old_state == DATA_RINGBUFFER_FULL)
            rb->read_index = rb->write_index;
    }

    return 1;
}

 /**
 ****************************************************************************************
 * @brief  Get data from ring buffer. 
 *
 * @param[in] rb 	 Pointer to a variable that manages the data buffer.
 * @param[in] ptr 	 Pointer to the read storage location.
 * @param[in] length The length of the Read data.
 * @return Get the length of the actual data.
 ****************************************************************************************
 */ 
 uint16 data_ringbuffer_get(struct data_ringbuffer *rb,
                            uint8           	   *ptr,
                            uint16           	   length)
{
    
    
    uint16 size = 0;

    /* The length of the existing data in the buffer */
    size = data_ringbuffer_data_len(rb);

    /* no data */
    if (size == 0)
        return 0;

    /* less data */
    if (size < length)
        length = size;

    if (rb->buffer_size - rb->read_index > length)
    {
    
    
        /* copy all of data */
        memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
        /* this should not cause overflow because there is enough space for
         * length of data in current mirror */
        rb->read_index += length;
        return length;
    }

    memcpy(&ptr[0],
           &rb->buffer_ptr[rb->read_index],
           rb->buffer_size - rb->read_index);
    memcpy(&ptr[rb->buffer_size - rb->read_index],
           &rb->buffer_ptr[0],
           length - (rb->buffer_size - rb->read_index));

    /* we are going into the other side of the mirror */
    rb->read_mirror = ~rb->read_mirror;
    rb->read_index = length - (rb->buffer_size - rb->read_index);

    return length;
}

 /**
 ****************************************************************************************
 * @brief  get a character from a ringbuffer. 
 *
 * @param[in] rb 	 Pointer to a variable that manages the data buffer.
 * @param[in] ch 	 Pointer to the read storage location.
 * @return Get the length of the actual data.
 ****************************************************************************************
 */ 
uint16 data_ringbuffer_getchar(struct data_ringbuffer *rb, uint8 *ch)
{
    
    

    /* ringbuffer is empty */
    if (!data_ringbuffer_data_len(rb))
        return 0;

    /* put character */
    *ch = rb->buffer_ptr[rb->read_index];

    if (rb->read_index == rb->buffer_size-1)
    {
    
    
        rb->read_mirror = ~rb->read_mirror;
        rb->read_index = 0;
    }
    else
    {
    
    
        rb->read_index++;
    }

    return 1;
}

四、总结

上面的代码怎么用?
第一步就是初始化啦

struct data_ringbuffer DATA_RINGBUFF;
uint8       Receive_buf[256];
data_ringbuffer_init(&DATA_RINGBUFF,Receive_buf,sizeof(Receive_buf));

接着就是调入写数据以及取数据函数

/* 缓冲区中填充指定数据长度的数据(当缓冲区空间小于待写入的数据长度事不覆盖缓冲区原有数据) */
uint16 data_ringbuffer_put(struct data_ringbuffer *rb,const uint8 *ptr,uint16 length);
/* 缓冲区中获取指定长度的数据(返回实际获取数据的长度) */
uint16 data_ringbuffer_get(struct data_ringbuffer *rb,uint8 *ptr,uint16 length);

另外还提供了对于写数据和读数据的其他函数具体可以查看头文件对函数的说明。
数据接收有了,对于数据的解析处理如状态机,流处理等笔者学习完,下次有时间再写一篇出来,欢迎关注,交流学习,一枚小白的卑微请求!

小伙伴觉得可以的话就直接将代码加入进你们的工程中吧!也顺便麻烦点个赞收藏起来,拒绝伸手党好吧嘻嘻!

猜你喜欢

转载自blog.csdn.net/qq_41290252/article/details/113058051