数据结构:环形缓冲区
当有大量数据的时候,我们不能存储所有的数据,那么计算机处理数据的时候,只能先处理先来的,那么处理完后呢,就会把数据释放掉,再处理下一个。那么,已经处理的数据的内存就会被浪费掉。因为后来的数据只能往后排队,如过要将剩余的数据都往前移动一次,那么效率就会低下了,肯定不现实,所以,环形队列就出现了。环形缓冲区是一项很好的技术,不用频繁的分配内存,而且在大多数情况下,内存的反复使用也使得我们能用更少的内存块做更多的事。
头文件
//ringbuffer.h
#ifndef _RINGBUFFER_H_
#define _RINGBUFFER_H_
#include "string.h"
#include "stdint.h"
/*
*声明缓冲区结构体
*param 1:数据缓存地址
*param 2:掩码
*param 3:读指针、写指针
*/
struct Ringbuffer {
uint8_t *data;
uint16_t mask;
uint16_t put_ptr, get_ptr;
};
//初始化缓冲区,绑定内存地址
void Ringbuffer_init(struct Ringbuffer *r, uint8_t *a,uint16_t size);
//清空缓冲区
void Ringbuffer_clear(struct Ringbuffer *r);
//向缓冲区插入数据,写指针+1
uint8_t Ringbuffer_push(struct Ringbuffer *r, uint8_t c);
//从缓冲区获取数据,读指针+1
uint8_t Ringbuffer_pop(struct Ringbuffer *r);
//返回缓冲区总长度
uint16_t Ringbuffer_size(struct Ringbuffer *r);
//返回缓冲区当前存储的元素的个数
uint16_t Ringbuffer_elements(struct Ringbuffer *r);
//返回当前读指针后第n个位置的一个字节,读指针不移动
uint8_t Ringbuffer_get_n(struct Ringbuffer *r,uint16_t n);
//获取当前读指针后长度为n的字符串,读指针移动相应长度
uint8_t Ringbuffer_get_str(struct Ringbuffer *r,uint8_t* dst,uint16_t length);
#endif
源文件
//ringbuffer.c
#include "ringbuffer.h"
//绑定内存位置,将读、写指针设为0,掩码设置为内存长度-1
//注意:内存长度需要为2的指数
void Ringbuffer_init(struct Ringbuffer *r, uint8_t *dataptr, uint16_t size)
{
r->data = dataptr;
r->mask = size - 1;
r->put_ptr = 0;
r->get_ptr = 0;
}
//清空环形缓冲区
void Ringbuffer_clear(struct Ringbuffer *r)
{
r->put_ptr = 0;
r->get_ptr = 0;
memset(r->data,0,r->mask);
}
//向缓冲区插入数据,写指针+1
uint8_t Ringbuffer_push(struct Ringbuffer *r, uint8_t c)
{
//检查当前内存是否有足够的位置插入新的数据,仅if判断为真时读写指针处在同一位置,
//此时缓冲区没有足够空间写入新的数据
if(((r->put_ptr - r->get_ptr) & r->mask) == r->mask) {
return 0;
}
//将数据存储到写指针位置
r->data[r->put_ptr] = c;
//写指针+1,如果写指针移动到内存末尾,则将写指针移动回0位置
r->put_ptr = (r->put_ptr + 1) & r->mask;
return 1;
}
uint8_t Ringbuffer_pop(struct Ringbuffer *r)
{
uint8_t c;
//检查是否有可读取的内容
if(((r->put_ptr - r->get_ptr) & r->mask) > 0) {
c = r->data[r->get_ptr];
//读指针+1,如果读指针移动到内存末尾,则将读指针移动回0位置
r->get_ptr = (r->get_ptr + 1) & r->mask;
return c;
}
else
{
return -1;
}
}
//返回缓冲区总长度
uint16_t Ringbuffer_size(struct Ringbuffer *r)
{
return r->mask + 1;
}
//返回缓冲区当前存储的元素的个数
uint16_t Ringbuffer_elements(struct Ringbuffer *r)
{
return (r->put_ptr - r->get_ptr) & r->mask;
}
//返回当前读指针后第n个位置的一个字节,读指针不移动
uint8_t Ringbuffer_get_n(struct Ringbuffer *r,uint16_t n)
{
//检查是否有可读取的内容以及读取内容长度是否满足
if(n<Ringbuffer_elements(r))
return r->data[r->get_ptr+n-1];
else
return 0;
}
//获取当前读指针后长度为n的字符串,读指针移动相应长度
uint8_t Ringbuffer_get_str(struct Ringbuffer *r,uint8_t* dst,uint16_t length)
{
if(Ringbuffer_elements(r)>length)
{
int i;
for ( i= 0; i < length; i++)
{
dst[i]=Ringbuffer_pop(r);
}
return 1;
}
else
{
return 0;
}
}
使用例程:
该例程假设在数据接收端使用环形缓冲区接收usart中断的数据,在定时器中断中循环检测缓冲区收到的内容
//1声明结构体
struct Ringbuffer RX_Ringbuffer;
//2申请内存,长度为RX_Buffer_Size
uint8_t RX_Buffer[RX_Buffer_Size];
//3初始化
Ringbuffer_init(&RX_Ringbuffer, RX_Buffer, RX_Buffer_Size);
//4串口中断函数中将数据放入环形缓冲区
Ringbuffer_push(&RX_Ringbuffer,data);
//5定时器中断函数中分析获取到的数据
uint8_t count=0;
uint8_t data[4]={0};
bool dataGotFlag = false;
while(!dataGotFlag)
{
count++;
//数据包以0x02开头, 以0x03开头结尾,长度为12
if(Ringbuffer_pop(&Ultrasonic_RX_Ringbuffer)==0x02)
{
if(Ringbuffer_get_n(&_RX_Ringbuffer,12)==0x03)
{
//将获取到的字段放入变量保存
Ringbuffer_get_str(&RX_Ringbuffer,data[0],4);
dataGotFlag=true;
}
else
{
if(count>=RX_Buffer_Size)
memset(data,0,4);
break;;
}
}
else
{
//循环次数超过缓冲区大小则返回
if(count>=RX_Buffer_Size)
memset(data,0,4);
break;;
}
}