一.问题描述
STM32的串口应用有一种场景:串口数据传输时,接收数据长度未知,频率未知,不要求实时处理的场景。如果采用上述方案,接收一帧数据立即处理,那么在处理的时候来的数据包就“丢失”了。这个时候就需要环形缓冲区来解决这个问题。
二.环形缓冲区简介
环形缓冲区就是一个带“头指针”和“尾指针”的数组。“头指针”指向环形缓冲区中可读的数据,“尾指针”指向环形缓冲区中可写的缓冲空间。通过移动“头指针”和“尾指针”就可以实现缓冲区的数据读取和写入。
在通常情况下,应用程序读取环形缓冲区的数据仅仅会影响“头指针”,而串口接收数据仅仅会影响“尾指针”。当串口接收到新的数组,则将数组保存到环形缓冲区中,同时将“尾指针”加1,以保存下一个数据;应用程序在读取数据时,“头指针”加1,以读取下一个数据。当“尾指针”超过数组大小,则“尾指针”重新指向数组的首元素,从而形成“环形缓冲区”!,有效数据区域在“头指针”和“尾指针”之间。如下图:

三.代码实现
RingBuffer.h
#ifndef __RINGBUFFER_H__
#define __RINGBUFFER_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include "usart.h"
#define MAX_BUFFER 128 /*缓冲区大小*/
/*缓冲区结构体*/
typedef struct
{
uint16_t head;
uint16_t tail;
uint16_t length;
uint8_t ringBuffer[MAX_BUFFER];
}Type_RingBuffer_Uchar;
/*读写缓冲区函数声明*/
void Ringbuf_wirte_float(float data, Type_RingBuffer_float* pRingBuffer);
uint8_t Ringbuf_read_float(float *pdata, Type_RingBuffer_float* pRingBuffer);
#endif
如果串口传输的数据量过大,我们会发现环形缓冲区被“冲爆”,也就是缓冲区满了,但是还有待缓冲的数据时,只需要修改MAX_BUFFER的宏定义,增大缓冲区间即可。
RingBuffer.c
/**
* @brief 往环形缓冲写入数据
* @param uint8_t data 待写入的数据 ;Type_RingBuffer_Uchar* pRingBuffer 要写入的缓冲区
* @note 往环形缓冲区写入uint8_t类型的数据
*/
void Ringbuf_wirte_Uchar(uint8_t data, Type_RingBuffer_Uchar* pRingBuffer)
{
pRingBuffer->ringBuffer[pRingBuffer->tail] = data;
if(++pRingBuffer->tail >= MAX_BUFFER) /*判断缓冲区是否已满*/
{
pRingBuffer->tail = 0;
}
if(pRingBuffer->tail == pRingBuffer->head)
{
if(++pRingBuffer->head >= MAX_BUFFER)/*防止越界*/
{
pRingBuffer->head = 0;
}
}
pRingBuffer->length++;
}
/**
* @brief 从环形缓冲区读取数据
* @param uint8_t* pdata 存放读取的数据的地址 ;Type_RingBuffer_Uchar* pRingBuffer 要读取的缓冲区
* @return 1:环形缓冲区为空,读取失败;0:读取成功
* @note 从环形缓冲区读取uint8_t类型的数据
*/
uint8_t Ringbuf_read_Uchar(uint8_t *pdata, Type_RingBuffer_Uchar* pRingBuffer)
{
if(pRingBuffer->head == pRingBuffer->tail) /*判断缓冲区是否为空*/
{
return 1;
}
else
{
*pdata = pRingBuffer->ringBuffer[pRingBuffer->head];
if(++pRingBuffer->head >= MAX_BUFFER) /*防止越界*/
{
pRingBuffer->head = 0;
}
pRingBuffer->length--;
return 0;
}
}