STM32
登录
直播中
王浩
7年用户
1315经验值
私信
关注
[问答]
STM32环形缓冲区怎么实现?
开启该帖子的消息推送
STM32
STM32
环形缓冲区怎么实现?
回帖
(1)
郑广荣
2021-12-7 14:21:06
01、简介
在之前的文章《
stm32 串口详解
》中,我们讲解了串口的基本应用,使用串口中断接收数据,串口中断发送回包(一般可以使用非中断形式发送回包,在数据接收不频繁的应用中。串口接收中断保证串口数据及时响应,使用非中断方式发送回包即可)。
后面的文章《
STM32使用DMA接收串口数据
》和《
STM32使用DMA发送串口数据
》讲解了如何使用DMA辅助串口收发数据,使用DMA的好处在于不用CPU即可完成串口收发数据,减轻CPU负担,在串口通信频繁且不想频繁中断的应用中非常有用。
除了上述两种场景,还有一种应用场景:串口接收数据长度位置,频率未知,不要求实时处理的场景。如果采用上述方案,接收一帧数据立即处理,那么在处理的时候来的数据包就“丢失”了。这个时候就需要缓冲队列来解决这个问题。
0
2、缓冲区
缓冲区看名字就知道,是缓冲数据用的。实现缓冲区最简单的办法时,定义多个数组,接收一包数据到数组A,就把接收数据的地址换成数组B,每个数据有个标记字节用于表示这个数组是否收到数据,收到数据是否处理完成。
上述方案是完全可行的,但有缺点:
①缓冲数据组数一定,且有多变量,代码结构不太清晰。
②接收数据长度可能大于数组大小,也可能小于数组大小。不灵活,需要接收数据很长时容易出错,且内存利用率低。
解决这个问题的好办法是:环形缓冲区。
环形缓冲区就是一个带“头指针”和“尾指针”的数组。“头指针”指向环形缓冲区中可读的数据,“尾指针”指向环形缓冲区中可写的缓冲空间。通过移动“头指针”和“尾指针”就可以实现缓冲区的数据读取和写入。在通常情况下,应用程序读取环形缓冲区的数据仅仅会影响“头指针”,而串口接收数据仅仅会影响“尾指针”。当串口接收到新的数组,则将数组保存到环形缓冲区中,同时将“尾指针”加1,以保存下一个数据;应用程序在读取数据时,“头指针”加1,以读取下一个数据。当“尾指针”超过数组大小,则“尾指针”重新指向数组的首元素,从而形成“环形缓冲区”!,有效数据区域在“头指针”和“尾指针”之间。如下图
如上面说的,环形缓冲区其实就是一个数组,将其“剪开”,然后“拉直”后如下图
环形缓冲区的特性
1、先进新出。
2、当缓冲区被使用完,且又有新的数据需要存储时,丢掉历史最久的数据,保存最新数据。
03、代码实现
环形缓冲区的实现很简单,只需要简单的几个接口即可。
首先需要创建一个环形缓冲区
#define RINGBUFF_LEN (500) //定义最大接收字节数 500#define RINGBUFF_OK 1 #define RINGBUFF_ERR 0 typedef struct{ uint16_t Head; uint16_t Tail; uint16_t Lenght; uint8_t Ring_data[RINGBUFF_LEN];}RingBuff_t;RingBuff_t ringBuff;//创建一个ringBuff的缓冲区
当我们发现环形缓冲区被“冲爆”时,也就是缓冲区满了,但是还有待缓冲的数据时,只需要修改RINGBUFF_LEN的宏定义,增大缓冲区间即可。
环形缓冲区的初始化
/*** @brief RingBuff_Init* @param void* @return void* @note 初始化环形缓冲区*/void RingBuff_Init(void){ //初始化相关信息 ringBuff.Head = 0; ringBuff.Tail = 0; ringBuff.Lenght = 0;}
主要是将环形缓冲区的头,尾和长度清零,表示没有任何数据存入。
环形缓冲区的写入
/*** @brief Write_RingBuff* @param uint8_t data* @return FLASE:环形缓冲区已满,写入失败;TRUE:写入成功* @note 往环形缓冲区写入uint8_t类型的数据*/uint8_t Write_RingBuff(uint8_t 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;}
这个接口是写入一个字节到环形缓冲区。这里注意:大家可以根据自己的实际应用修改为一次缓冲多个字节。并且这个做了缓冲区满时报错且防止非法越界的处理,大家可以自行修改为缓冲区满时覆盖最早的数据。
环形缓冲区的读取
/*** @brief Read_RingBuff* @param uint8_t *rData,用于保存读取的数据* @return FLASE:环形缓冲区没有数据,读取失败;TRUE:读取成功* @note 从环形缓冲区读取一个u8类型的数据*/uint8_t Read_RingBuff(uint8_t *rData){ if(ringBuff.Lenght == 0)//判断非空 { return RINGBUFF_ERR; } *rData = ringBuff.Ring_data[ringBuff.Head];//先进先出FIFO,从缓冲区头出 ringBuff.Head = (ringBuff.Head+1)%RINGBUFF_LEN;//防止越界非法访问 ringBuff.Lenght--; return RINGBUFF_OK;}
读取的话也很简单,同样是读取一个字节,大家可以自行修改为读取多个字节。
04、验证
光说不练假把式,下面我们就来验证上面的代码可行性。
串口中断函数中缓冲数据
void USART1_IRQHandler(void){ if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)) { Write_RingBuff(USART_ReceiveData(USART1)); USART_ClearFlag(USART1, USART_FLAG_RXNE); }}
在主循环中,读取缓冲区的数据,然后发送出去,因为是简单的demo,添加了延时模拟CPU处理其他任务。
while (1) { if(Read_RingBuff(&data)) //从环形缓冲区中读取数据 { USART_SendData(USART1, data); } SysCtlDelay(1*(SystemCoreClock/3000)); }
结果显示没有出现丢包问题。如果你的应用场景串口通信速率快,数据量大或处理速度慢导致丢包,建议增大RINGBUFF_LEN的宏定义,增大缓冲区间即可。
01、简介
在之前的文章《
stm32 串口详解
》中,我们讲解了串口的基本应用,使用串口中断接收数据,串口中断发送回包(一般可以使用非中断形式发送回包,在数据接收不频繁的应用中。串口接收中断保证串口数据及时响应,使用非中断方式发送回包即可)。
后面的文章《
STM32使用DMA接收串口数据
》和《
STM32使用DMA发送串口数据
》讲解了如何使用DMA辅助串口收发数据,使用DMA的好处在于不用CPU即可完成串口收发数据,减轻CPU负担,在串口通信频繁且不想频繁中断的应用中非常有用。
除了上述两种场景,还有一种应用场景:串口接收数据长度位置,频率未知,不要求实时处理的场景。如果采用上述方案,接收一帧数据立即处理,那么在处理的时候来的数据包就“丢失”了。这个时候就需要缓冲队列来解决这个问题。
0
2、缓冲区
缓冲区看名字就知道,是缓冲数据用的。实现缓冲区最简单的办法时,定义多个数组,接收一包数据到数组A,就把接收数据的地址换成数组B,每个数据有个标记字节用于表示这个数组是否收到数据,收到数据是否处理完成。
上述方案是完全可行的,但有缺点:
①缓冲数据组数一定,且有多变量,代码结构不太清晰。
②接收数据长度可能大于数组大小,也可能小于数组大小。不灵活,需要接收数据很长时容易出错,且内存利用率低。
解决这个问题的好办法是:环形缓冲区。
环形缓冲区就是一个带“头指针”和“尾指针”的数组。“头指针”指向环形缓冲区中可读的数据,“尾指针”指向环形缓冲区中可写的缓冲空间。通过移动“头指针”和“尾指针”就可以实现缓冲区的数据读取和写入。在通常情况下,应用程序读取环形缓冲区的数据仅仅会影响“头指针”,而串口接收数据仅仅会影响“尾指针”。当串口接收到新的数组,则将数组保存到环形缓冲区中,同时将“尾指针”加1,以保存下一个数据;应用程序在读取数据时,“头指针”加1,以读取下一个数据。当“尾指针”超过数组大小,则“尾指针”重新指向数组的首元素,从而形成“环形缓冲区”!,有效数据区域在“头指针”和“尾指针”之间。如下图
如上面说的,环形缓冲区其实就是一个数组,将其“剪开”,然后“拉直”后如下图
环形缓冲区的特性
1、先进新出。
2、当缓冲区被使用完,且又有新的数据需要存储时,丢掉历史最久的数据,保存最新数据。
03、代码实现
环形缓冲区的实现很简单,只需要简单的几个接口即可。
首先需要创建一个环形缓冲区
#define RINGBUFF_LEN (500) //定义最大接收字节数 500#define RINGBUFF_OK 1 #define RINGBUFF_ERR 0 typedef struct{ uint16_t Head; uint16_t Tail; uint16_t Lenght; uint8_t Ring_data[RINGBUFF_LEN];}RingBuff_t;RingBuff_t ringBuff;//创建一个ringBuff的缓冲区
当我们发现环形缓冲区被“冲爆”时,也就是缓冲区满了,但是还有待缓冲的数据时,只需要修改RINGBUFF_LEN的宏定义,增大缓冲区间即可。
环形缓冲区的初始化
/*** @brief RingBuff_Init* @param void* @return void* @note 初始化环形缓冲区*/void RingBuff_Init(void){ //初始化相关信息 ringBuff.Head = 0; ringBuff.Tail = 0; ringBuff.Lenght = 0;}
主要是将环形缓冲区的头,尾和长度清零,表示没有任何数据存入。
环形缓冲区的写入
/*** @brief Write_RingBuff* @param uint8_t data* @return FLASE:环形缓冲区已满,写入失败;TRUE:写入成功* @note 往环形缓冲区写入uint8_t类型的数据*/uint8_t Write_RingBuff(uint8_t 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;}
这个接口是写入一个字节到环形缓冲区。这里注意:大家可以根据自己的实际应用修改为一次缓冲多个字节。并且这个做了缓冲区满时报错且防止非法越界的处理,大家可以自行修改为缓冲区满时覆盖最早的数据。
环形缓冲区的读取
/*** @brief Read_RingBuff* @param uint8_t *rData,用于保存读取的数据* @return FLASE:环形缓冲区没有数据,读取失败;TRUE:读取成功* @note 从环形缓冲区读取一个u8类型的数据*/uint8_t Read_RingBuff(uint8_t *rData){ if(ringBuff.Lenght == 0)//判断非空 { return RINGBUFF_ERR; } *rData = ringBuff.Ring_data[ringBuff.Head];//先进先出FIFO,从缓冲区头出 ringBuff.Head = (ringBuff.Head+1)%RINGBUFF_LEN;//防止越界非法访问 ringBuff.Lenght--; return RINGBUFF_OK;}
读取的话也很简单,同样是读取一个字节,大家可以自行修改为读取多个字节。
04、验证
光说不练假把式,下面我们就来验证上面的代码可行性。
串口中断函数中缓冲数据
void USART1_IRQHandler(void){ if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)) { Write_RingBuff(USART_ReceiveData(USART1)); USART_ClearFlag(USART1, USART_FLAG_RXNE); }}
在主循环中,读取缓冲区的数据,然后发送出去,因为是简单的demo,添加了延时模拟CPU处理其他任务。
while (1) { if(Read_RingBuff(&data)) //从环形缓冲区中读取数据 { USART_SendData(USART1, data); } SysCtlDelay(1*(SystemCoreClock/3000)); }
结果显示没有出现丢包问题。如果你的应用场景串口通信速率快,数据量大或处理速度慢导致丢包,建议增大RINGBUFF_LEN的宏定义,增大缓冲区间即可。
举报
更多回帖
rotate(-90deg);
回复
相关问答
STM32
什么是
STM32
环形
缓冲区
?
2021-11-18
775
STM32
串口
环形
缓冲区
的
实现
方法
2020-12-24
1837
怎么
实现
串口
环形
缓冲区
?
2021-12-06
1328
如何
实现
STM32
串口
环形
缓冲区
?
2021-12-08
1541
请问怎么
实现
串口
环形
缓冲区
FIFO?
2021-12-06
981
stm32
是怎样使用
环形
缓冲区
形式去接收数据的
2021-12-07
1224
请问串口的DMA接收
缓冲区
是不是
环形
缓冲区
2022-08-30
1212
SPI在通信的过程中怎么
实现
环形
缓冲区
读取?
2023-10-11
229
请问
stm32
环形
队列里面怎么存数据包到队列里
2018-12-18
3268
rtt的
环形
缓冲区
读完就丢弃了?
2023-04-17
767
发帖
登录/注册
20万+
工程师都在用,
免费
PCB检查工具
无需安装、支持浏览器和手机在线查看、实时共享
查看
点击登录
登录更多精彩功能!
英国威廉希尔公司网站
william hill官网 版块
小组
免费开发板试用
ebook
直播
搜索
登录
×
20
完善资料,
赚取积分