嵌入式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