为什么要用环形缓冲区
当有大量数据的时候,我们不能存储所有的数据,那么计算机处理数据的时候,只能先处理先来的,处理之后就会把数据释放掉,再处理下一个。那么已经处理的数据的内存就会被浪费掉。因为后来的数据只能往后排队,如果要将剩余的数据都往前移动一次,那么效率就会低下了,肯定不现实,所以,环形队列就出现了。
环形缓冲区是一项很好的技术,不用频繁的分配内存,而且在大多数情况下,内存的反复使用也使得我们能用更少的内存块做更多的事。
例如在串口数据接受中,外设某次发送的报文最大是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
-
- #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;
- }