嵌入式C语言_环形存储(Ring/Circular Buffer)

嵌入式C语言_环形存储(Ring /Circular Buffer)

基本原理

在嵌入式开发的串口收发数据处理时,常常使用环形存储结构,将中断接收数据进行缓存,以防止在数据处理过程中可能产生的数据覆盖,造成数据丢失。环形存储是我们定义的一段首位相连的数据存储方式,通过头和尾索引数据,进行数据的写入和读取。因而,环形存储结构的关键要素包含:数据存储buffer,buffer的大小,头和尾。

如下图所示,定义一个buffer,大小为12,初始化状态,头和尾的索引都在0号存储单元,当写入数据时,头指针进行偏移,读取数据时,尾指针进行偏移,在读取数据量不超过写入数据量时,读写一直能够进行。

在这里插入图片描述

代码实现:

#define RING_BUFFER_SIZE 128
typedef unsigned char U8;
#define RING_BUFFER_MASK (RING_BUFFER_SIZE-1)
typedef	struct {
  /** Buffer memory. */
  char buffer[RING_BUFFER_SIZE];
  /** Index of tail. */
  U8 tail_index;
  /** Index of head. */
  U8 head_index;
}ring_buffer_t;

首先,对环形存储结构进行初始化:

void ring_buffer_init(ring_buffer_t *buffer) {
  buffer->tail_index = 0;
  buffer->head_index = 0;
}

判断buffer是否存储满了,存储满了,返回1:

inline U8 ring_buffer_is_full(ring_buffer_t *buffer) {
  return ((buffer->head_index - buffer->tail_index) & RING_BUFFER_MASK) == RING_BUFFER_MASK;
}

判断buffer是否为空:

inline U8 ring_buffer_is_empty(ring_buffer_t *buffer) {
  return (buffer->head_index == buffer->tail_index);
}

返回buffer中存储的数据个数:

inline U8 ring_buffer_num_items(ring_buffer_t *buffer) {
  return ((buffer->head_index - buffer->tail_index) & RING_BUFFER_MASK);
}

以上三个函数是程序中频繁调用的,做成内联函数。接下来就是数据的存储,从存储一个字节开始。如果存储区域满了,则把尾指针后移一位,否则直接把数组存储在头指针指向的内存,头指针后移一位:

void ring_buffer_queue(ring_buffer_t *buffer, char data) {
  /* Is buffer full? */
  if(ring_buffer_is_full(buffer)) {
    /* Is going to overwrite the oldest byte */
    /* Increase tail index */
    buffer->tail_index = ((buffer->tail_index + 1) & RING_BUFFER_MASK);
  }

  /* Place data in buffer */
  buffer->buffer[buffer->head_index] = data;
  buffer->head_index = ((buffer->head_index + 1) & RING_BUFFER_MASK);
}

数组存储,传入待存储数据data的指针,逐个字节存储即可:

void ring_buffer_queue_arr(ring_buffer_t *buffer, const char *data, U8 size) {
  /* Add bytes; one by one */
  U8 i;
  for(i = 0; i < size; i++) {
    ring_buffer_queue(buffer, data[i]);
  }
}

单字节取数据,存储区不空的情况下,取一个数据,将尾指针后移一位:

U8 ring_buffer_dequeue(ring_buffer_t *buffer, char *data) {
  if(ring_buffer_is_empty(buffer)) {
    /* No items */
    return 0;
  }
  
  *data = buffer->buffer[buffer->tail_index];
  buffer->tail_index = ((buffer->tail_index + 1) & RING_BUFFER_MASK);
  return 1;
}

读取一个数组:

U8 ring_buffer_dequeue_arr(ring_buffer_t *buffer, char *data, U8 len) {
  if(ring_buffer_is_empty(buffer)) {
    /* No items */
    return 0;
  }

  char *data_ptr = data;
  U8 cnt = 0;
  while((cnt < len) && ring_buffer_dequeue(buffer, data_ptr)) {
    cnt++;
    data_ptr++;
  }
  return cnt;
}

除了写入和读取之外,倘若想查看存储区的一个数据,而不进行指针偏移呐,传入索引号和缓存数据指针:

U8 ring_buffer_peek(ring_buffer_t *buffer, char *data, U8 index) {
  if(index >= ring_buffer_num_items(buffer)) {
    /* No items at index */
    return 0;
  }
  
  /* Add index to pointer */
  U8 data_index = ((buffer->tail_index + index) & RING_BUFFER_MASK);
  *data = buffer->buffer[data_index];
  return 1;
}

通过以上操作就可以完成环形存储区数据的读写和查看。下面是一段测试程序:

#include <assert.h>
#include <stdio.h>
#include "ringbuffer.h"
int main(void) {
	int i, cnt;
	char buf;
	char buf_arr[50];

	/* Create and initialize ring buffer */
	ring_buffer_t ring_buffer;
	ring_buffer_init(&ring_buffer);

	/* Add elements to buffer; one at a time */
	for (i = 0; i < 100; i++) {
		ring_buffer_queue(&ring_buffer, i);
	}

	/* Verify size */
	assert(ring_buffer_num_items(&ring_buffer) == 100);
	
	/* Dequeue all elements */
	for (cnt = 0; ring_buffer_dequeue(&ring_buffer, &buf) > 0; cnt++) {
		/* Do something with buf... */
		assert(buf == cnt);
		printf("Read: %d\n", buf);
	}
	printf("\n===============\n");

	/* Add array */
	ring_buffer_queue_arr(&ring_buffer, "Hello, Ring Buffer!", 20);

	/* Is buffer empty? */
	assert(!ring_buffer_is_empty(&ring_buffer));

	/* Dequeue all elements */
	while (ring_buffer_dequeue(&ring_buffer, &buf) > 0) {
		/* Print contents */
		printf("Read: %c\n", buf);
	}

	/* Add new array */
	ring_buffer_queue_arr(&ring_buffer, "Hello again, Ring Buffer!", 26);

	/* Dequeue array in two parts */
	printf("Read:\n");
	cnt = ring_buffer_dequeue_arr(&ring_buffer, buf_arr, 13);
	printf("%d\n", cnt);
	assert(cnt == 13);
	/* Add \0 termination before printing */
	buf_arr[13] = '\0';
	printf("%s\n", buf_arr);
	/* Dequeue remaining */
	cnt = ring_buffer_dequeue_arr(&ring_buffer, buf_arr, 13);
	assert(cnt == 13);
	printf("%s", buf_arr);

	return 0;
}

reference:

https://github.com/AndersKaloer/Ring-Buffer

  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值