• 环形缓冲区-----适合在通信中接收数据(例如uart)


    为什么要用环形缓冲区
    当有大量数据的时候,我们不能存储所有的数据,那么计算机处理数据的时候,只能先处理先来的,处理之后就会把数据释放掉,再处理下一个。那么已经处理的数据的内存就会被浪费掉。因为后来的数据只能往后排队,如果要将剩余的数据都往前移动一次,那么效率就会低下了,肯定不现实,所以,环形队列就出现了。

    环形缓冲区是一项很好的技术,不用频繁的分配内存,而且在大多数情况下,内存的反复使用也使得我们能用更少的内存块做更多的事。

    例如在串口数据接受中,外设某次发送的报文最大是100个字节,如果使用普通接收方式则需要申请一个100字节的数组。而外设并不是每次都是发送100个字节,当发送50个字节的时候,剩余的50个字节就会被浪费掉。因此引入环形缓冲区,mcu接收到一个数据在串口中断往环形缓冲区里面写一个数据,另一个线程就读一个数据,互相追逐,使用这种方式接收100字节的报文或许只需要20个字节的环形缓冲区,大大节省了内存。

    1. typedef struct {
    2. volatile unsigned int pW; //写位置
    3. volatile unsigned int pR; //读位置
    4. volatile unsigned char buff[RING_BUFF_SIZE];//缓冲区
    5. }RingBuff;

    环形缓冲区实现原理

    环形缓冲区通常有一个表示读位置的变量和写位置的变量。读位置指向环形缓冲区可读的数据,写位置只想环形缓冲区可写的数据。我们只需要移动读写位置就可以获实现缓冲区的读写。初始化时读写位置都为0。

    1. void RingBuffInitial(RingBuff *dst_buff)
    2. {
    3. dst_buff->pW = 0;
    4. dst_buff->pR = 0;
    5. }

    写数据

    往环形缓冲区里面写数据数据时要考虑缓冲区是否已经满了,如果满了就放弃这次的数据。每写入成功一个数据pW位置都要更新W

    判断环形缓冲区满的条件是pR ==(pW +1)%RING_BUFF_SIZE则为满

    1. void RingBuffWrite(RingBuff *dst_buff, unsigned char dat)
    2. {
    3. int i;
    4. i = (dst_buff->pW + 1)%RING_BUFF_SIZE;
    5. if(i != dst_buff->pR)
    6. {
    7. dst_buff->buff[dst_buff->pW] = dat;
    8. dst_buff->pW = i; //更新pW位置
    9. }
    10. }

     

    读数据

    读取数据要考虑环形缓冲区是否为空。判断条件是pW == pR则为空,读取成功返回0 失败返回-1

    每读取成功一个数据pR位置都要更新

    1. int RingBuffRead(RingBuff *dst_buff, unsigned char *dat)
    2. {
    3. if(dst_buff->pW == dst_buff->pR)
    4. {
    5. return -1;
    6. }
    7. else
    8. {
    9. *dat = dst_buff->buff[dst_buff->pR];
    10. dst_buff->pR = (dst_buff->pR+1)%RING_BUFF_SIZE; 更新pW位置
    11. return 0;
    12. }
    13. }

     

     

    可以看出来,读取位置和写位置一直在互相追逐,当读位置追上写位置的时候,表示环形缓冲区数据为空。反过来则表示为满。

    完整代码如下

    1. #include
    2. #define RING_BUFF_SIZE 10
    3. typedef struct {
    4. volatile unsigned int pW;
    5. volatile unsigned int pR;
    6. volatile unsigned char buff[RING_BUFF_SIZE];
    7. }RingBuff;
    8. void RingBuffInitial(RingBuff *dst_buff)
    9. {
    10. dst_buff->pW = 0;
    11. dst_buff->pR = 0;
    12. }
    13. void RingBuffWrite(RingBuff *dst_buff, unsigned char dat)
    14. {
    15. int i;
    16. i = (dst_buff->pW + 1)%RING_BUFF_SIZE;
    17. if(i != dst_buff->pR)
    18. {
    19. dst_buff->buff[dst_buff->pW] = dat;
    20. dst_buff->pW = i;
    21. }
    22. }
    23. int RingBuffRead(RingBuff *dst_buff, unsigned char *dat)
    24. {
    25. if(dst_buff->pW == dst_buff->pR)
    26. {
    27. return -1;
    28. }
    29. else
    30. {
    31. *dat = dst_buff->buff[dst_buff->pR];
    32. dst_buff->pR = (dst_buff->pR+1)%RING_BUFF_SIZE; 更新pW位置
    33. return 0;
    34. }
    35. }
    36. int main()
    37. {
    38. int i=0;
    39. int ret;
    40. unsigned char read_dat;
    41. RingBuff RecvBuff;
    42. RingBuffInitial(&RecvBuff);
    43. printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
    44. for(i = 0; i < 5; i++)
    45. {
    46. RingBuffWrite(&RecvBuff,'A'+i);
    47. printf("RecvBuff.pW = %d ,RecvBuff.pR = %d ,RecvBuff.buff[%d] = %c\n",\
    48. RecvBuff.pW,RecvBuff.pR,i,RecvBuff.buff[i]);
    49. }
    50. printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
    51. for(i = 0; i < 5; i++)
    52. {
    53. ret = RingBuffRead(&RecvBuff,&read_dat);
    54. if(!ret)
    55. printf("read_dat = %c\n",read_dat);
    56. else
    57. printf("read err\n");
    58. }
    59. printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
    60. for(i = 0; i < 5; i++)
    61. {
    62. ret = RingBuffRead(&RecvBuff,&read_dat);
    63. if(!ret)
    64. printf("read_dat = %c\n",read_dat);
    65. else
    66. printf("read err\n");
    67. }
    68. printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
    69. for(i = 5; i < 10; i++)
    70. {
    71. RingBuffWrite(&RecvBuff,'A'+i);
    72. printf("RecvBuff.pW = %d ,RecvBuff.pR = %d ,RecvBuff.buff[%d] = %c\n",\
    73. RecvBuff.pW,RecvBuff.pR,i,RecvBuff.buff[i]);
    74. }
    75. printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
    76. for(i = 0; i < 5; i++)
    77. {
    78. ret = RingBuffRead(&RecvBuff,&read_dat);
    79. if(!ret)
    80. printf("read_dat = %c\n",read_dat);
    81. else
    82. printf("read err\n");
    83. }
    84. printf("当前位置RecvBuff.pW = %d, RecvBuff.pR = %d\n",RecvBuff.pW,RecvBuff.pR);
    85. return 0;
    86. }

    代码结果分析

     

     

  • 相关阅读:
    连续信号与系统的频域分析之傅里叶级数
    【剑指Offer】整数(二)二进制 - 二进制加法 - JavaScript
    B站-后台开发岗
    【深度学习推荐系统 工程篇】三、浅析FastTransFormer看 GPU推理优化 思路
    Django项目引入NPM和gulp管理前端资源
    【vue,unapi】UniApp引入全局js实现全局方法,全局变量
    java计算机毕业设计口红专卖网站源码+mysql数据库+系统+lw文档+部署
    对象的解构赋值(基本用法2)
    [BJDCTF2020]EzPHP
    密码学系列之:使用openssl检测网站是否支持ocsp
  • 原文地址:https://blog.csdn.net/qq_34991245/article/details/127808208