标题: HAL库串口中断RX中的Overrun Error解决方案 [打印本页]

作者: 我是你的小号    时间: 2021-6-1 23:59
标题: HAL库串口中断RX中的Overrun Error解决方案
[首先简单研究一下什么时候会出现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);
  }
}
[
可以看出,分三种情况:
[其中,触发两次接收缓存溢出的时候,就会进入Overrun Error。
[比如说BUF长度为3,连续发两次长度为4的数据,就会进入ORE。或者说第一次发送长度为2的数据,然后接着发送两次长度为3的数据,也是会触发ORE的,后续的两次长度为3的数据因为有之前一条长度为2的铺垫,都是视作BUF溢出的数据。但是如果发送一次长度为4的数据之后,后续发长度一条长度为2的数据,就会进入正常状态,是有弹性的。弹性限度为BUF长度的两倍,也就是说发送一次长度为6的数据,会立即进入ORE。
[ORE状态下,串口进入ORE溢出中断,无法继续接收数据,但是可以发送数据,相当于接收数据不会触发中断了。
[对于ORE溢出常用解决方案有下:
[无论哪一种方法都要理解HAL_UART_RxCpltCallback的实现原理:
[从上到下,层层封装和调用。
  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的产生。






欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1