2025-11-07 5:43 AM
Hi,
We are building an IoT device, using STM32L476RET6 as main core. It communicates with an LPWAN modem with AT-commands via UART. We have redirected UART2 into external pins for "serial output" into Nucleo RX pins.
During testing, we noticed initially that some devices where seemingly "stuck" in some state - no printing in Nucleo RX, not responding to external interrupts - while communicating with LPWAN modem ( it stayed in idle ON state).
No hard fault or similar (we have put checks inside repsective callbacks). IWDG can reset the device, but it is a "last resort" method.
After putting device in debug mode, catching the situation and pausing the code, it stopped here :
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t isrflags = READ_REG(huart->Instance->ISR);
uint32_t cr1its = READ_REG(huart->Instance->CR1); <<<-------Looking at USART1 ISR register, we found ORE error flag.
Resuming the code in debug mode makes the MCU continue, but in run mode the MCU is frozen , which is strange.
For UART TX, we are using basic transmit command such as :
uint8_t res = HAL_UART_Transmit(&MODEM_UART, (uint8_t*) command_to_send, strlen(command_to_send), 1000);For UART RX, we are using Idle Iine interrupt for receiving in combination with ring buffer (using lwrb library) :
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if (huart == &huart1) {
// write rx result into ring buffer
lwrb_write(&rb, isr_buffer, Size);
data_ready_flag = true;
// Enable UART RX Interrupt with Idle-line detection
int retries = 10;
do {
if (HAL_UARTEx_ReceiveToIdle_IT(&huart1, isr_buffer, sizeof(isr_buffer)) == HAL_OK) {
break;
}
retries--;
} while (retries > 0);
}
}
Our function for AT commands communication looks like this :
static int send_rcv_at_command(const char *command_to_send, const char *exp_res, int timeout) {
uint32_t tick = HAL_GetTick();
bool command_expected_to_be_sent = true;
char buffer_final[1024] = {0};
int total_num_of_bytes = 0;
printf("Send command %s\r\n", command_to_send);
// Enable UART RX Interrupt with Idle-line detection, if not already enabled
if (((huart1.Instance->CR1 & USART_CR1_IDLEIE_Msk) >> USART_CR1_IDLEIE_Pos) == 0) {
int retries = 10; // retry if function fail
do {
if (HAL_UARTEx_ReceiveToIdle_IT(&huart1, isr_buffer, sizeof(isr_buffer)) == HAL_OK) {
break;
}
retries--;
} while (retries > 0);
if (retries == 0) {
return -1;
}
}
while (return_code > 0 && ((HAL_GetTick() - tick) < timeout)) {
if (command_expected_to_be_sent == true) {
if ((command_to_send != NULL) && (strlen(command_to_send) > 0)) {
//send command
uint8_t res = HAL_UART_Transmit(&MODEM_UART, (uint8_t*) command_to_send, strlen(command_to_send), 1000);
if (res == HAL_OK) { // Tx successfull
command_expected_to_be_sent = false;
} else { //Tx fail, try again
continue;
}
}
}
if (data_ready_flag) { // data available at UART1
data_ready_flag = false;
// Receive characters from ring buffer
int numOfBytes = lwrb_read(&.rb, &buffer_final[total_num_of_bytes], sizeof(buffer_final) - total_num_of_bytes);
total_num_of_bytes = total_num_of_bytes + numOfBytes;
}
// ...
// further parsing...
// ...
}
HAL_UART_Abort_IT(&huart1);
lwrb_reset(&rb);
return 0;
}
Screenshot from callstack of debug mode:
1) If it is a UART error, why does the MCU freezes and not just fail to receive and continue ?
2) How can MCU recover from a fail at UART communication ?
Solved! Go to Solution.
2025-11-07 6:05 AM
You should implement the error callback and clear the ORE flag. Or, you should rework the code so that you don't get overruns in the first place. An overrun happens when data is received but your code isn't ready for it (i.e. the previous HAL_UARTEx_ReceiveToIdle_IT has completed and has not been called again).
No need to call HAL_UARTEx_ReceiveToIdle_IT 10x. If it doesn't work the first time, stop, and understand why. Probably it is still busy with the previous transaction.
2025-11-07 6:05 AM
You should implement the error callback and clear the ORE flag. Or, you should rework the code so that you don't get overruns in the first place. An overrun happens when data is received but your code isn't ready for it (i.e. the previous HAL_UARTEx_ReceiveToIdle_IT has completed and has not been called again).
No need to call HAL_UARTEx_ReceiveToIdle_IT 10x. If it doesn't work the first time, stop, and understand why. Probably it is still busy with the previous transaction.
2025-11-07 6:29 AM
Thanks TDK, I hadn't checked uart error callback
1) is an implementation like the following ok ?
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
if (huart == &huart1) {
uart_error = true; //flag
//Exit ongoing RX transfer and clear error flags
HAL_UART_Abort_IT(&huart1)
}
}
void HAL_UART_AbortCpltCallback(UART_HandleTypeDef *huart) { //make sure that UART IT has aborted successfuly
if (huart == &huart1) {
if (uart_error == true) { // UART Aborted from error callback, not by direct call
uart_error = false;
HAL_UARTEx_ReceiveToIdle_IT(&huart1, isr_buffer, sizeof(isr_buffer); //re-enable uart interrupt
}
uart_rx_set = false;
}
}
2) How should we handle not HAL_OK return value from HAL_UARTEx_ReceiveToIdle_IT() & HAL_UART_Abort_IT() ? I didn't want to put any delay inside callback function