Skip to main content
Associate
July 27, 2023
Question

NVIC and/or USART appears to hold pending requests stuck (Cortex-M0 in STM32L071)

  • July 27, 2023
  • 12 replies
  • 3372 views

With STM32L071, I've been fighting a problem of losing UART RX characters. The RX path uses HAL_UART_ReceiveIT to queue a single character buffer, and then re-queues it from HAL_UART_RxCpltCallback. The received character is then stored in a software FIFO. The main loop reads from the software FIFO and echoes it back. The TX path uses DMA (TX is used for high volume debug output as well). Simple action of pasting a line to the terminal on UART5 (with echoing back) could cause it to drop a few characters. But one thing helped. After initiating the next RX operation (for a single byte buffer), check RXNE and set the interrupt pending:

 

 

 

// If RXNE is already set, request an immediate interrupt,
 // otherwise it may be lost and cause RX overrun
 if (READ_REG(huart->Instance->ISR) & USART_ISR_RXNE)
 {
 if (huart->Instance == USART4
 || huart->Instance == USART5)
 {
 NVIC_SetPendingIRQ(USART4_5_IRQn);
 }
 else if (huart->Instance == USART1)
 {
 NVIC_SetPendingIRQ(USART1_IRQn);
 }
 else if (huart->Instance == USART2)
 {
 NVIC_SetPendingIRQ(USART2_IRQn);
 }
 }

 

 

 

I then wrote additional test code to echo the data to another UART, get the TX looped back to its RX, and echoing back. Same as in previous example, The RX callback would queue the data, and then the main loop would read the data and transmit it. This was based on a different codebase, and TX DMA was NOT used. The main loop was checking if NVIC interrupt was pending, and/or RXNE bit was set in the UARTs. Surprisingly, once in a few tries, the test code managed to catch either RXNE or an interrupt pending bit set in NVIC. Some of those were caught for UART URQ, but also for SysTick IRQ and for RTC wakeup event IRQ. And it's not like the test loop was tight, it was doing some other stuff and turned about 1000 times a second. And by sampling NVIC pending register just 1000 times a second, it read interrupt pending.

WHAT'S HAPPENING?

One would have thought that code running with interrupts enabled would never see a pending interrupt or RXNE bit set (with RXNEIE also set), because these are supposed to cause an immediate interrupt to clear them. Yet here we are, catching them by sampling just 1000 times per second.

Yes, I've read the NVIC description. And honestly, when I encountered the phrase "Cortex-M0+ interrupts are both level-sensitive and pulse-sensitive. Pulse interrupts are also described as edge-triggered interrupts.", my first thought was "Uh oh, they probably screwed it up". And apparently they did.

This topic has been closed for replies.

12 replies

Pavel A.
July 27, 2023

UARTs of low-end STM32s don't have FIFO and so are more vulnerable to receiver overrun.

 After initiating the next RX operation (for a single byte buffer)

Interrupt-driven RX of size 1 using the ST "HAL" library is definitely NOT the way to go. Read the code and understand that the RX routine enables the interrupt before, and disables it after receive (of one byte). This has huge overhead and creates time window of disabled interrupt, when the RX overflow can occur. 

Bottom line: for good result, use cyclic DMA or write solid, fast, robust interrupt handlers. Do code reviews.  Be happy.

 

Associate
July 31, 2023

I'm afraid these replies are missing the point.

The character loss would not happen if NVIC+USART would function as you'd expect: invoke the interrupt as soon as RXNE+RXNEIE are set and the MCU is running in interrupt-able state. In this case, the main loop would never be picking RXNE set and/or a request being pending in NVIC, because these conditions would immediately cause an interrupt handler being invoked and clearing them. But the main loop sees these conditions (while sampling them less than 1000 times a second), which means the interrupts are not happening in timely manner, but are being held pending for some reason.

One option is that me (along with writers of CubeMX STM HAL code) don't understand some aspect of USART and NVIC design; another option is that NVIC/USART is broken. I'm trying to find which is that.

TDK
August 2, 2023

It's pretty unlikely that there is some fundamental flaw in the USART/NVIC hardware. Note that these devices are extremely popular and used by many people, and L0 isn't exactly a new device. Unlikely a critical flaw exists in the USART peripheral (but possible, check the errata, it's certainly been spotted if it does exist).

Could be a HAL flaw, but software isn't going to cause NVIC requests to be held. Likely there's a deeper misunderstanding here that would be explained by seeing the code and how these functions are used.

"If you feel a post has answered your question, please click ""Accept as Solution""."
KnarfB
Super User
July 27, 2023

As @Pavel A. said, keeping up with single char RX interrupts at higher baud rates may not always work. In the early days (16550A UART), loops were used in the IRQ handler to catch all bytes/events once the handler was running.

Consider using HAL_UARTEx_ReceiveToIdle_DMA or the cyclic DMA routines by Tilen Majerle https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx/tree/main

hth

KnarfB

Karl Yamashita
Principal
July 28, 2023

Show your code so we can see how you're exactly saving the data and transmitting it.

If a reply has proven helpful, click on Accept as Solution so that it'll show at top of the post.CAN Jammer an open source CAN bus hacking toolCANableV3 Open Source
Associate
July 31, 2023

I'll make a minimal version of my code and post it.

 

waclawek.jan
Super User
July 28, 2023

> As @Pavel A. said, keeping up with single char RX interrupts at higher baud rates may not always work.

That's not what he said. Rather, he said, it's a bad idea to use Cube/HAL functions to do this.

Granted, interrupt-based UART Rx has its limitations; however, under usual circumstances, it's okay. Just write your ISRs yourself, and in reasonable way.

JW

Pavel A.
August 1, 2023

Yes this makes sense. Do you have a well known STM32L071 board to repro? Or maybe a C0?

Associate
August 1, 2023

I'm using one of my company's boards.

Did you use to frequent OSR forums? Those were the times. I'm not doing Windows kernel these days.

Pavel A.
August 1, 2023

Me same. this is a small world.

Karl Yamashita
Principal
August 2, 2023

Again, post your code. More than 95% of people posting that have issues with missing characters don't use HAL_UART_ReceiveIT  properly.

If a reply has proven helpful, click on Accept as Solution so that it'll show at top of the post.CAN Jammer an open source CAN bus hacking toolCANableV3 Open Source
KnarfB
Super User
August 4, 2023

Think those observations are interesting, but would need more code/explanations.

The following shall hold:

  1. if the baud rate is high enough and a string is received, the IRQ handler is continuously executed (provided no higher prio interrupt interferes), and not a single instruction of the main loop will be executed in between
  2. if the string is long enough, an overrun error may happen. HAL does not include error recovery and anything can happen afterward

hth

KnarfB