找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4064|回复: 0
收起左侧

HAL库串口中断RX中的Overrun Error解决方案

[复制链接]
ID:432932 发表于 2021-6-1 23:59 | 显示全部楼层 |阅读模式
[首先简单研究一下什么时候会出现overrun的问题,配置正常的HAL串口中断接收如下
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
   /* judge interrupt source */
   if(huart ->Instance == USART2)
  {
       //send data to huart1 from huart2
       HAL_UART_Transmit(&huart1, (uint8_t*)recv_buf, 3,0xFFFF);
       //enable RX IT
       HAL_UART_Receive_IT(huart, (uint8_t*)recv_buf, 3);
  }
}
[ image-20210530135826312.png
可以看出,分三种情况:
  • 发送数据=接收缓存,正常
  • 发送数据<接收缓存,BUF未满,要等待下一次数据到来填满缓存才会触发中断
  • 发送数据>接收缓存,BUF已满,多余部分被截断,和下一次数据一起触发中断

[其中,触发两次接收缓存溢出的时候,就会进入Overrun Error。
[比如说BUF长度为3,连续发两次长度为4的数据,就会进入ORE。或者说第一次发送长度为2的数据,然后接着发送两次长度为3的数据,也是会触发ORE的,后续的两次长度为3的数据因为有之前一条长度为2的铺垫,都是视作BUF溢出的数据。但是如果发送一次长度为4的数据之后,后续发长度一条长度为2的数据,就会进入正常状态,是有弹性的。弹性限度为BUF长度的两倍,也就是说发送一次长度为6的数据,会立即进入ORE。
[ORE状态下,串口进入ORE溢出中断,无法继续接收数据,但是可以发送数据,相当于接收数据不会触发中断了。
[对于ORE溢出常用解决方案有下:
  • 关闭ORE中断
  • 采取双重缓存

[无论哪一种方法都要理解HAL_UART_RxCpltCallback的实现原理:
  • HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中断处理函数)  
  • UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数)
  • HAL_UART_RxCpltCallback(huart)(中断回调函数)

[从上到下,层层封装和调用。
  1. void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
  2. {
  3. uint32_t isrflags = READ_REG(huart->Instance->SR);
  4. uint32_t cr1its = READ_REG(huart->Instance->CR1);
  5. uint32_t cr3its = READ_REG(huart->Instance->CR3);
  6. uint32_t errorflags = 0x00U;
  7. uint32_t dmarequest = 0x00U;
  8. /* If no error occurs */
  9. errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
  10. //close ORE check
  11. //errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_NE));
  12. if (errorflags == RESET)
  13. {
  14. /* UART in mode Receiver -------------------------------------------------*/
  15. if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
  16. {
  17. UART_Receive_IT(huart);
  18. return;
  19. }
  20. }

复制代码
[        想要关闭ORE中断,将errorflags中的USART_SR_ORE 这一位去掉即可,相当于对ORE错误不予检查,经过实际的测试是可行的,实际的表现就是出现ORE溢出之后,串口只接受固定BUF长度的数据,对于溢出的数据直接丢弃,但是还需要考虑ORE溢出之后,那个时刻中BUF肯定是有数据,但是由于没有清除BUF,后续的数据直接进来会导致错位,因此想要真正实现完美的溢出数据丢弃处理且不影响后续的数据,是要在程序中加入清除BUF的指令的。但是这些方法都会影响HAL库的底层代码,包括去掉USART_SR_ORE 这一位,这也是STM32IDE的一点的不好的地方,底层文件是没有用户代码入口的,修改IOC文件之后,一切都会回到最初的起点。
[          其次就是双重缓存,具体的就是将BUF长度设置为1,这样子每一次收到一个字节的话都会调用回调函数,为什么长度长度大于1的时候会发生ORE,而1的时候可以接受很长的数据而不ORE呢,我猜是因为1的时候将某些位置位了,间接关闭了ORE,然后用户自己定义BUF区,一个一个去读取写入,这个方法还可以实现不定长的接收,设置数据结束帧位就可以了。缺点也很明显,当数据很长的时候,中断要进入很多次,有点占资源了。
[用户回调函数HAL_UART_RxCpltCallback只有接收到设定的BUF的长度才会调用一次,而不是有字节就调用,实现的方法在UART_Receive_IT函数中:
  1.   if (--huart->RxXferCount == 0U)
  2.   {
  3.      /* Disable the UART Data Register not empty Interrupt */
  4.      __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);

  5.      /* Disable the UART Parity Error Interrupt */
  6.      __HAL_UART_DISABLE_IT(huart, UART_IT_PE);

  7.      /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  8.      __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);

  9.      /* Rx process is completed, restore huart->RxState to Ready */
  10.      huart->RxState = HAL_UART_STATE_READY;

  11. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  12.      /*Call registered Rx complete callback*/
  13.      huart->RxCpltCallback(huart);
  14. #else
  15.      /*Call legacy weak Rx complete callback*/
  16.      HAL_UART_RxCpltCallback(huart);
  17. #endif /* USE_HAL_UART_REGISTER_CALLBACKS */
复制代码

[if (--huart->RxXferCount == 0U)这一句,每次字节来都会调用UART_Receive_IT,然后RxXferCount减1,这个变量就是一开始设置的BUF接收长度,只有等这个到零了,才会调用HAL_UART_RxCpltCallback(huart)
[最终,因为在项目中串口是需要来高频率接收固定长度的测量数据的,但是在之前要用串口发送上电指令,这个指令会返回一个和测量数据长度不同的ASK信号,处理方法就是在上电之后,再开启串口接收,这样子就可以设置BUF长度为固定长度,同时稍微降低测量的频率,让串口接收有足够的时间,避免ORE的产生。

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表