环形数组,(循环队列,环形队列,环形缓冲,circular buffer ,ring buffer),能够很好的接收不定长数据。
# define BUFFERMAX 256 /* 1*/
static uint8_t UsartBuffer[BUFFERMAX]; /* 2 */
static uint8_t BufferWptr =0; /* 3*/
static uint8_t BufferRptr =0; /* 4 */
/*static volatile uint8_t BufferWptr = 0; // 声明为volatile以避免编译器优化导致的问题
static volatile uint8_t BufferRptr = 0; */
/*函数名 :BufferWrite
*函数描述:缓冲区写函数(由通信接口接收中断服务调用)
*输入参数:无
*输出结果:无
*返回值 :无
*/
void BufferWrite(void){
if(BufferWptr==(BufferRptr - 1)) /* 5 */
/*
if(BufferWptr == (BufferPtr - 1))
:检查是否即将发生缓冲区溢出。这里需要注意,(BufferPtr - 1)
需要处理BufferPtr
为0的情况,否则当BufferPtr
为0时,(BufferPtr - 1)
会变为BUFFERMAX - 1
,可能导致错误的溢出判断。- 正确的溢出判断应该检查
BufferWptr
的下一位是否等于BufferPtr
,而不是BufferWptr
是否等于BufferPtr - 1
。
*/
return; /* 6 */
UsartBuffer[BufferWptr]=USART_ReceiveData(USART1); /* 7*/
BufferWptr++ ; /* 8 */
BufferWptr = BufferWptr % BUFFERMAX; /* 9*/
}
/*
void BufferWrite(void) {
uint8_t tempWptr;
// 获取当前写指针,并准备增加
tempWptr = BufferWptr;
tempWptr++;
if (tempWptr >= BUFFERMAX) {
tempWptr = 0; // 循环回到缓冲区开始
}
// 检查是否即将发生缓冲区溢出
if (tempWptr != BufferRptr) {
// 没有溢出,可以安全写入
UsartBuffer[BufferWptr] = USART_ReceiveData(USART1);
BufferWptr = tempWptr; // 更新写指针
}
// 否则,忽略这次写入,因为缓冲区已满
}
*/
/*函数名 :BufferRead
*函数描述:缓冲区读函数
*输人参数:data,待存放读出数据的内存空间地址
*输出结果:无
*返回值 :0一无数据,1一有数据
*/
uint8_t BufferRead(uint8_t *data)
{
if(BufferRptr == BufferWptr) /*10 */
return 0; /* 11 */
*data= UsartBuffer[BufferRptr]; /* 12 */
BufferRptr++; /* 13 */
BufferRptr = BufferRptr %BUFFERMAX; /* 14 */
return 1; /* 15 */
}
/*
uint8_t BufferRead(uint8_t *data) {
uint8_t tempRptr;
// 获取当前读指针,并准备增加
tempRptr = BufferRptr;
tempRptr++;
if (tempRptr >= BUFFERMAX) {
tempRptr = 0; // 循环回到缓冲区开始
}
// 检查缓冲区是否为空
if (tempRptr != BufferWptr) {
// 非空,可以安全读取
*data = UsartBuffer[BufferPtr];
BufferPtr = tempRptr; // 更新读指针
return 1; // 有数据可读
}
return 0; // 无数据可读
}
*/
//对程序清单6.6.1进行逐一解释。首先是程序定义部分。
//第1句:定义将要开辟的缓冲区的大小,在此定义为256(字节)。
//第2句:开辟一片大小为256字节的缓冲区,供串口设备接收数据暂存使用。
//第3句:定义一个字节变量作为缓冲区写指针。
//第4句:定义一个字节变量作为缓冲区读指针。
//(2)然后是缓冲区写函数BufferWrite。
//第5句:此句的功能是判断缓冲区是否处于“已写满”的状态,下文会给出解释。
//第6句:缓冲区已写满,函数返回。
//第7句:缓冲区未满,读出串口设备接收到的数据,并存放在缓冲区里,存放位置由缓冲区写指针 BufferWptr确定。
//第8句:更新缓冲区写指针BufferWptr。
//第9句:将缓冲区写指针的BufferWptr最大值和最小值对接,形成环形,即该写指针在[0:255]范围内依次循环变化。
//③之后是缓冲区读函数BufferRead。
//第10句:判断缓冲区是否有数据。
//第11句:缓冲区无数据,返回0(表示无数据)。
//第12句:缓冲区有数据,将缓冲区数据读出,读出位置由缓冲区读指针确定BufferRptr。
//第13句:更新缓冲区读指针BufferRptr。
//第14句:将缓冲区读指针BufferRptr的最大值与最小值对接,形成环形,即该写指针也在[0:255]范围内依次循环变化。
//第15句:返回1(表示已有数据读出)。
//可以对程序清单6.6.1的几个重点配合程序的运行过程进行分析。
//首先当然要有数据写进缓冲区才会有数据能被读出来,因此率先对缓冲区进行存取的是BufferWrite函数。
//该函数主要功能为把串口接收到的数据写入缓冲区中,并更新写指针。
//而BufferRead函数则是读出缓冲区的数据,并更新读指针。
//缓冲区主要通过读/写指针的变化来指示缓冲区当前的读/写位置,并且由读/写指针的最大、最小值对接而形成了环形缓冲区。
//!!!不难理解,读指针总是处在一种“追赶”写指针的状态。
//但由于缓冲区被设计成环形,当缓冲区写速度比读速度快时,随着时间的推移,
//最终会出现写指针比读指针整整快了一圈的情况(想象环形跑道上赛跑运动中运动员的套圈情形)。
//因此当这种读指针正好被写指针“套圈”时,就意为着缓冲区已被写满了,无法再写人新的数据。
//Buffer Write函数的第5、6句正是针对这种情况而设的。
//同时这也说明了,读指针却不总是小于写指针的。
//!!!因此判断缓冲区是否有数据,必须使用第10句的写法,而不能写成if(BufferRptr< BufferWptr){…},
//!!!这是一个比较隐蔽的容易忽略的地方。
//但同时读者也应该想到,缓冲区保持在写满的状态,则不会将新接收到的数据保存,这样也是一种数据丢失的情况。
阅读记录,版权归原书作者所有