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

基于STM32串口中断之缓存区溢出卡死经验分享

[复制链接]
攻城狮Melo 发布时间:2024-5-29 19:53
一个关于STM32串口的问题。他想实现接收无限制字符数量的串口信息。

但是他遇到了一个问题,他在主函数中发送循环发送内容(500ms的延时)串口一中断回调函数中如果收到了串口内容,就利用串口发送。

但是他的串口一旦接收到了信息就会导致主循环中的串口发送极快。他的主代码如下
  1. unsigned char RecieveBuffer;
  2. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  3. {
  4.   UNUSED(huart);
  5.   HAL_UART_Transmit(&huart1,&RecieveBuffer,8 ,100); // 将接收到的数据再通过串口发送出去
  6.   HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8); //使能接收中断,RecieveBuffer的类型是uint8_t
  7. }
复制代码

看了他的代码,就这么短短的7行代码,让我汗流浃背。

他的发送和接收中有一个参数是8,这意味着当串口接收的寄存器中有8个数据时就会触发中断回调。

我们需要注意的是,串口寄存器的数据会被存储到缓存区中,这里的缓存区相当于一个队列先进先出,当我们调用HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8);

会将RecieveBuffer作为缓存区,将串口接收的信息存入缓存区。

那么让我们来试一下这段代码。

微信图片_20240529195314.png

可以看到,代码死住了。其实不难理解,我们发送数据的时候,由于缓存区只有一个字符的空间,我们发送八个字符的时候会导致缓存区溢出。所以我朋友的代码可以“正常运行”也是一件非常奇怪的事情。

所以正确的做法应该是:设置和缓存区一样长的读取字符。

  1. unsigned char RecieveBuffer[8];
  2. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  3. {
  4.   UNUSED(huart);
  5.   HAL_UART_Transmit(&huart1,&RecieveBuffer,8 ,100); // 将接收到的数据再通过串口发送出去
  6.   HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8); //使能接收中断,RecieveBuffer的类型是uint8_t
  7. }
复制代码

这样子就可以实现每接收8个字符就调用中断一次

微信图片_20240529195311.png

可以看到,我们正常的发送了8个字符。

但是这样子有一个非常非常致命的缺点!

由于我们的代码每8个才会接收一次,所以当我们的发送的数量小于8时,必须发送多次才能会触发一次调用。

注意,这里的每次我们都是先发送再接收,因为初始化的时候是启用了接收。

微信图片_20240529195307.png

当我们发送3次“123”时才能让接收的字符>=8,我们把前八个发送了出去,此时缓存区剩下了一个字符是“3”

微信图片_20240529195305.png

第二次我们发送了三次“123123123”,再次让字符数量>=8,这时候把缓存区的八个“31231231”发送出去再接收后面的“23”,这时候缓存区的数据为:"23 "。

微信图片_20240529195302.png

最后我们第三次发送“123”的时候,由于23 + 123 + 123 这时候,虽然这里按理来说正好八个,但是实际测下来,当这时候接收的时候系统就会卡死。

所以,实际上这种方法必须保证发送端的数据是完完整整的八个八个发送,多一个少一个都不行。


解法
事实上最标准的做法是单个字符单个字符处理。定义一个大的缓存区,用来存储接收到的字符并且确立一个结束符来确定数据流的结束。
  1. if((USART_RX_STA&0x8000)==0)//接收未完成
  2. {
  3.   if(USART_RX_STA&0x4000)//接收到了0x0d
  4.   {
  5.     if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
  6.     else USART_RX_STA|=0x8000;  //接收完成了
  7.   }
  8.   else //还没收到0X0D
  9.   {  
  10.     if(Res==0x0d)USART_RX_STA|=0x4000;
  11.     else
  12.     {
  13.       USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
  14.       USART_RX_STA++;
  15.       if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收   
  16.     }     
  17.   }
  18. }
复制代码



上述是正点原子的官方例程,其中USART_RX_STA是接收到的字符,即是确定一个结束符\r\n来作为一串字符的结尾并且检测长度是否超出长度。


这样子的代码容错率非常高,也确定了一个规范,并且避免了缓存区溢出的情况。


转载自:电路小白
如有侵权请联系删除

收藏 评论0 发布时间:2024-5-29 19:53

举报

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