为什么要用环形缓冲区
当有大量数据的时候,我们不能存储所有的数据,那么计算机处理数据的时候,只能先处理先来的,处理之后就会把数据释放掉,再处理下一个。那么已经处理的数据的内存就会被浪费掉。因为后来的数据只能往后排队,如果要将剩余的数据都往前移动一次,那么效率就会低下了,肯定不现实,所以,环形队列就出现了。
环形缓冲区是一项很好的技术,不用频繁的分配内存,而且在大多数情况下,内存的反复使用也使得我们能用更少的内存块做更多的事。
例如在串口数据接受中,外设某次发送的报文最大是100个字节,如果使用普通接收方式则需要申请一个100字节的数组。而外设并不是每次都是发送100个字节,当发送50个字节的时候,剩余的50个字节就会被浪费掉。因此引入环形缓冲区,mcu接收到一个数据在串口中断往环形缓冲区里面写一个数据,另一个线程就读一个数据,互相追逐,使用这种方式接收100字节的报文或许只需要20个字节的环形缓冲区,大大节省了内存。
typedef struct {
volatile unsigned int pW; //写位置
volatile unsigned int pR; //读位置
volatile unsigned char buff[RING_BUFF_SIZE];//缓冲区
}RingBuff;
环形缓冲区实现原理
环形缓冲区通常有一个表示读位置的变量和写位置的变量。读位置指向环形缓冲区可读的数据,写位置只想环形缓冲区可写的数据。我们只需要移动读写位置就可以获实现缓冲区的读写。初始化时读写位置都为0。
void RingBuffInitial(RingBuff *dst_buff)
{
dst_buff->pW = 0;
dst_buff->pR = 0;
}
写数据
往环形缓冲区里面写数据数据时要考虑缓冲区是否已经满了,如果满了就放弃这次的数据。每写入成功一个数据pW位置都要更新W
判断环形缓冲区满的条件是pR ==(pW +1)%RING_BUFF_SIZE则为满
void RingBuffWrite(RingBuff *dst_buff, unsigned char dat)
{
int i;
i = (dst_buff->pW + 1)%RING_BUFF_SIZE;
if(i != dst_buff->pR)
{
dst_buff->buff[dst_buff->pW] = dat;
dst_buff->pW = i; //更新pW位置
}
}
读数据
读取数据要考虑环形缓冲区是否为空。判断条件是pW == pR则为空,读取成功返回0 失败返回-1
每读取成功一个数据pR位置都要更新
int RingBuffRead(RingBuff *dst_buff, unsigned char *dat)
{
if(dst_buff->pW == dst_buff->pR)
{
return -1;
}
else
{
*dat = dst_buff->buff[dst_buff->pR];
dst_buff->pR = (dst_buff->pR+1)%RING_BUFF_SIZE; 更新pW位置
return 0;
}
}
可以看出来,读取位置和写位置一直在互相追逐,当读位置追上写位置的时候,表示环形缓冲区数据为空。反过来则表示为满。
完整代码如下
#include <stdio.h>
#define RING_BUFF_SIZE 10
typedef struct {
volatile unsigned int pW;
volatile unsigned int pR;
volatile unsigned char buff[RING_BUFF_SIZE];
}RingBuff;
void RingBuffInitial(RingBuff *dst_buff)
{
dst_buff->pW = 0;
dst_buff->pR = 0;
}
void RingBuffWrite(RingBuff *dst_buff, unsigned char dat)
{
int i;
i = (dst_buff->pW + 1)%RING_BUFF_SIZE;
if(i != dst_buff->pR)
{
dst_buff->buff[dst_buff->pW] = dat;
dst_buff->pW = i;
}
}
int RingBuffRead(RingBuff *dst_buff, unsigned char *dat)
{
if(dst_buff->pW == dst_buff->pR)
{
return -1;
}
else
{
*dat = dst_buff->buff[dst_buff->pR];
dst_buff->pR = (dst_buff->pR+1)%RING_BUFF_SIZE; 更新pW位置
return 0;
}
}
int main()
{
int i=0;
int ret;
unsigned char read_dat;
RingBuff RecvBuff;
RingBuffInitial(&RecvBuff);
printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
for(i = 0; i < 5; i++)
{
RingBuffWrite(&RecvBuff,'A'+i);
printf("RecvBuff.pW = %d ,RecvBuff.pR = %d ,RecvBuff.buff[%d] = %c\n",\
RecvBuff.pW,RecvBuff.pR,i,RecvBuff.buff[i]);
}
printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
for(i = 0; i < 5; i++)
{
ret = RingBuffRead(&RecvBuff,&read_dat);
if(!ret)
printf("read_dat = %c\n",read_dat);
else
printf("read err\n");
}
printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
for(i = 0; i < 5; i++)
{
ret = RingBuffRead(&RecvBuff,&read_dat);
if(!ret)
printf("read_dat = %c\n",read_dat);
else
printf("read err\n");
}
printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
for(i = 5; i < 10; i++)
{
RingBuffWrite(&RecvBuff,'A'+i);
printf("RecvBuff.pW = %d ,RecvBuff.pR = %d ,RecvBuff.buff[%d] = %c\n",\
RecvBuff.pW,RecvBuff.pR,i,RecvBuff.buff[i]);
}
printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
for(i = 0; i < 5; i++)
{
ret = RingBuffRead(&RecvBuff,&read_dat);
if(!ret)
printf("read_dat = %c\n",read_dat);
else
printf("read err\n");
}
printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
return 0;
}
原文连接