理解

环形缓冲区就是一个带“头指针”和“尾指针”的数组。“头指针”指向环形缓冲区中可读的数据,“尾指针”指向环形缓冲区中可写的缓冲空间。通过移动“头指针”和“尾指针”就可以实现缓冲区的数据读取和写入。在通常情况下,应用程序读取环形缓冲区的数据仅仅会影响“头指针”,而串口接收数据仅仅会影响“尾指针”。当串口接收到新的数组,则将数组保存到环形缓冲区中,同时将“尾指针”加1,以保存下一个数据;应用程序在读取数据时,“头指针”加1,以读取下一个数据。当“尾指针”超过数组大小,则“尾指针”重新指向数组的首元素,从而形成“环形缓冲区”!,有效数据区域在“头指针”和“尾指针”之间。
image.png
如上面说的,环形缓冲区其实就是一个数组,将其“剪开”,然后“拉直”后如下图
image.png

环形缓冲区的特性

  • 先进新出。
  • 当缓冲区被使用完,且又有新的数据需要存储时,丢掉历史最久的数据,保存最新数据。

    代码实现

    创建环形缓冲区

    首先需要创建一个环形缓冲区
    1. #define RINGBUFF_LEN (500) //定义最大接收字节数 500
    2. #define RINGBUFF_OK 1
    3. #define RINGBUFF_ERR 0
    4. typedef struct
    5. {
    6. uint16_t Head;
    7. uint16_t Tail;
    8. uint16_t Lenght;
    9. uint8_t Ring_data[RINGBUFF_LEN];
    10. }RingBuff_t;
    11. RingBuff_t DATA_ringBuff;//创建一个ringBuff的缓冲区
    当我们发现环形缓冲区被“冲爆”时,也就是缓冲区满了,但是还有待缓冲的数据时,只需要修改RINGBUFF_LEN的宏定义,增大缓冲区间即可。

    环形缓冲区的初始化

    1. void RingBuff_Init(RingBuff_t *ringBuff)
    2. {
    3. //初始化相关信息
    4. ringBuff->Head = 0;
    5. ringBuff->Tail = 0;
    6. ringBuff->Lenght = 0;
    7. }
    主要是将环形缓冲区的头,尾和长度清零,表示没有任何数据存入。

    环形缓冲区的写入

    ```c //写入一个char unsigned char Write_Single_RingBuff(RingBuff_t *ringBuff, unsigned char data) { if(ringBuff->Lenght >= RINGBUFF_LEN) //判断缓冲区是否已满 { return RINGBUFF_ERR; } ringBuff->Ring_data[ringBuff->Tail]=data; ringBuff->Tail = (ringBuff->Tail+1)%RINGBUFF_LEN;//防止越界非法访问 ringBuff->Lenght++; return RINGBUFF_OK; }

//写入多个char unsigned char Write_RingBuff(RingBuff_t ringBuff, uint8_t data, rt_uint32_t len) { for(int i = 0;iLenght >= RINGBUFF_LEN) //判断缓冲区是否已满 { return RINGBUFF_ERR; } ringBuff->Ring_data[ringBuff->Tail]=data[i]; ringBuff->Tail = (ringBuff->Tail+1)%RINGBUFF_LEN;//防止越界非法访问 ringBuff->Lenght++; } return RINGBUFF_OK; }

  1. 这个接口是写入一个字节到环形缓冲区。这里注意:大家可以根据自己的实际应用修改为一次缓冲多个字节。并且这个做了缓冲区满时报错且防止非法越界的处理,大家可以自行修改为缓冲区满时覆盖最早的数据。
  2. <a name="TBDGK"></a>
  3. ## 环形缓冲区的读取
  4. ```c
  5. unsigned char Read_RingBuff(RingBuff_t *ringBuff, unsigned char *rData)
  6. {
  7. if(ringBuff->Lenght == 0)//判断非空
  8. {
  9. return RINGBUFF_ERR;
  10. }
  11. *rData = ringBuff->Ring_data[ringBuff->Head];//先进先出FIFO,从缓冲区头出
  12. ringBuff->Head = (ringBuff->Head+1)%RINGBUFF_LEN;//防止越界非法访问
  13. ringBuff->Lenght--;
  14. return RINGBUFF_OK;
  15. }

使用

  1. RingBuff_t DATA_ringBuff;
  2. RingBuff_Init(&DATA_ringBuff);//初始化环形队列
  3. unsigned char data;
  4. if(Read_RingBuff(&DATA_ringBuff, &data)) //从环形缓冲区中读取数据
  5. {
  6. Command_ReceiveAndCheck(data);
  7. }

串口接收中断

  1. void EXCHANGE_USART_IRQHandler(void)
  2. {
  3. if(USART_GetITStatus( EXCHANGE_USART,USART_IT_RXNE)!=RESET)
  4. {
  5. Write_RingBuff(&DATA_ringBuff, USART_ReceiveData(EXCHANGE_USART));
  6. USART_ClearITPendingBit(EXCHANGE_USART, USART_IT_RXNE);
  7. }
  8. }

RTT使用
RTT里面自带Ringbuffer
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device-ipc/ringbuffer/ringbuffer