你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32串口开发之环形缓冲区

[复制链接]
STMCU小助手 发布时间:2021-7-19 14:17
01、缓冲区
缓冲区看名字就知道,是缓冲数据用的。实现缓冲区最简单的办法时,定义多个数组,接收一包数据到数组A,就把接收数据的地址换成数组B,每个数据有个标记字节用于表示这个数组是否收到数据,收到数据是否处理完成。


上述方案是完全可行的,但有缺点:


①缓冲数据组数一定,且有多变量,代码结构不太清晰。


②接收数据长度可能大于数组大小,也可能小于数组大小。不灵活,需要接收数据很长时容易出错,且内存利用率低。


解决这个问题的好办法是:环形缓冲区。


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


2.png


环形缓冲区的特性


1、先进新出。


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


02、代码实现
环形缓冲区的实现很简单,只需要简单的几个接口即可。


首先需要创建一个环形缓冲区
  1. <font face="微软雅黑" size="3">#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 ringBuff;//创建一个ringBuff的缓冲区
  12. </font>
复制代码
当我们发现环形缓冲区被“冲爆”时,也就是缓冲区满了,但是还有待缓冲的数据时,只需要修改RINGBUFF_LEN的宏定义,增大缓冲区间即可。


环形缓冲区的初始化
  1. <font face="微软雅黑" size="3">/**
  2. * @brief  RingBuff_Init
  3. * @param  void
  4. * @return void
  5. * @note   初始化环形缓冲区
  6. */
  7. void RingBuff_Init(void)
  8. {
  9.   //初始化相关信息
  10.   ringBuff.Head = 0;
  11.   ringBuff.Tail = 0;
  12.   ringBuff.Lenght = 0;
  13. }
  14. </font>
复制代码
主要是将环形缓冲区的头,尾和长度清零,表示没有任何数据存入。


环形缓冲区的写入

  1. <font face="微软雅黑" size="3">/**
  2. * @brief  Write_RingBuff
  3. * @param  uint8_t data
  4. * @return FLASE:环形缓冲区已满,写入失败;TRUE:写入成功
  5. * @note   往环形缓冲区写入uint8_t类型的数据
  6. */
  7. uint8_t Write_RingBuff(uint8_t data)
  8. {
  9.   if(ringBuff.Lenght >= RINGBUFF_LEN) //判断缓冲区是否已满
  10.   {
  11.     return RINGBUFF_ERR;
  12.   }
  13.   ringBuff.Ring_data[ringBuff.Tail]=data;
  14.   ringBuff.Tail = (ringBuff.Tail+1)%RINGBUFF_LEN;//防止越界非法访问
  15.   ringBuff.Lenght++;
  16.   return RINGBUFF_OK;
  17. }</font>
复制代码
这个接口是写入一个字节到环形缓冲区。这里注意:大家可以根据自己的实际应用修改为一次缓冲多个字节。并且这个做了缓冲区满时报错且防止非法越界的处理,大家可以自行修改为缓冲区满时覆盖最早的数据。


环形缓冲区的读取

  1. <font face="微软雅黑" size="3">
  2. /**
  3. * @brief  Read_RingBuff
  4. * @param  uint8_t *rData,用于保存读取的数据
  5. * @return FLASE:环形缓冲区没有数据,读取失败;TRUE:读取成功
  6. * @note   从环形缓冲区读取一个u8类型的数据
  7. */
  8. uint8_t Read_RingBuff(uint8_t *rData)
  9. {
  10.   if(ringBuff.Lenght == 0)//判断非空
  11.   {
  12.     return RINGBUFF_ERR;
  13.   }
  14.   *rData = ringBuff.Ring_data[ringBuff.Head];//先进先出FIFO,从缓冲区头出
  15.   ringBuff.Head = (ringBuff.Head+1)%RINGBUFF_LEN;//防止越界非法访问
  16.   ringBuff.Lenght--;
  17.   return RINGBUFF_OK;
  18. }
  19. </font>
复制代码
读取的话也很简单,同样是读取一个字节,大家可以自行修改为读取多个字节。


02、验证
光说不练假把式,下面我们就来验证上面的代码可行性。


串口中断函数中缓冲数据

  1. <font face="微软雅黑" size="3">void USART1_IRQHandler(void)
  2. {
  3.   if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE))
  4.   {
  5.     Write_RingBuff(USART_ReceiveData(USART1));
  6.     USART_ClearFlag(USART1, USART_FLAG_RXNE);
  7.   }
  8. }</font>
复制代码
在主循环中,读取缓冲区的数据,然后发送出去,因为是简单的demo,添加了延时模拟CPU处理其他任务。
  1. <font face="微软雅黑" size="3">
  2. while (1)
  3.   {
  4.     if(Read_RingBuff(&data))            //从环形缓冲区中读取数据
  5.     {
  6.       USART_SendData(USART1, data);
  7.     }
  8.     SysCtlDelay(1*(SystemCoreClock/3000));
  9.   }
  10. </font>
复制代码
验证,间隔100ms发送数据。





结果显示没有出现丢包问题。如果你的应用场景串口通信速率快,数据量大或处理速度慢导致丢包,建议增大RINGBUFF_LEN的宏定义,增大缓冲区间即可。


Keil和IAR的工程文件下载地址:
[url=https://github.com/strongercjd/STM32F207VCT6]https://github.com/strongercjd/STM32F207VCT6
[/url]


收藏 评论0 发布时间:2021-7-19 14:17

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版