cancel
Showing results for 
Search instead for 
Did you mean: 

Is the uart overrun unavoidable?

JLee.32
Associate III

MCU: STM32F072, clock: 48M

UART BR: 115200, clock: 48M

application: receive 20 bytes per second

issue: overrun happens sometimes, maybe after 3 hours or 1 day.

I think the clock is fast enough to handle the slow application, but overrun happens sometimes for my project. I'm wondering if the uart overrun is unavoidable?? If not, is there any way to 100% avoid the uart overrun??

7 REPLIES 7
KnarfB
Principal III

How do you receive the data? Polling? interrupt? DMA? HAL? register level? Is the receiver 100% of the time "on"? Some code would also help.

Thanks for your reply. I use interrupt. Here is my code. Only USART1 and USART3 will be used. rb_write is for copying data to ring buffer.

Is the receiver 100% of the time "on"? Sorry, I'm not sure what you mean. Are you asking the received timing of 20 bytes in a second?

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

  HAL_StatusTypeDef r;

  

  char port;

  

  if      (huart->Instance == USART1) port = 0;

  else if (huart->Instance == USART2) port = 1;

  else if (huart->Instance == USART3) port = 2;

  else if (huart->Instance == USART4) port = 3;

  else return;

    if (m_uart[port].rb_rx == NULL)

    {

      printf("port: %d, %s: rb is NULL\n", port, __func__);

    }

    else if (rb_is_full(*m_uart[port].rb_rx))

    {

      printf("port: %d, %s: rb_is_full\n", port, __func__);

    }

    else

    {

      rb_write((*m_uart[port].rb_rx), m_uart[port].ch);

    }

  

    r = HAL_UART_Receive_IT(huart, &m_uart[port].ch, 1);

}

#define rb_write(rb, c)              \

    do                                \

    {                                 \

        rb.data[rb.w] = c;            \

        rb.w = (rb.w + 1U) % rb.size; \

    } while (0)

KnarfB
Principal III

> 100% of the time "on"

If you receive one char per interrupt using HAL, there is some time from the beginning of the interrupt handler until your call of HAL_UART_Receive_IT for the next char. If this time is not short enough, overrun will happen. If this happens rarely (as in you case) the interrupt priority might not be high enough such that other interrupts (like SysTick) delay the UART interrupt handler.

Remedies:

  • check interrupt prios or other obstacles.
  • try HAL_UARTEx_ReceiveToIdle_IT which receives until a max. number (e.g. 20) of chars has arrived or the uart line is idle
  • use HAL_UART_Receive_DMA with cyclic DMA to fill a ring buffer without interrupt interaction
  • do not use HAL but some clever register level programming
  • If possible use a scope or LA to measure the timing

For the DMA approch, check Tilen Majerle tutorials.

Pavel A.
Evangelist III

> r = HAL_UART_Receive_IT(huart, &m_uart[port].ch, 1);

As recently mentioned in other thread, HAL_UART_Receive_IT is not good for receiving by one byte - exactly because it enables RX interrupt only while in the function.

Between calls, the interrupt is disabled and overruns are expected.

UART of STM32F0 even does not have a FIFO.

So - roll your own "driver" where you keep the RX interrupt always enabled, or use DMA.

-- pa

The principles used in Tilen's example are usable also for interrupt mode, he just didn't implement it.

https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx

Even on L4 and F7 the USART peripheral doesn't have FIFO. It's a pretty new feature for STM32 MCUs. That said, it's a nice feature, but not a critical one.

Looks like LL driver solves my issue. HAL driver disables interrupt, but LL driver keeps interrupt enable. Is there any reason HAL has to disable interrupt? In other words, is there side effect when LL driver keeps interrupt enable?