2025-10-14 5:51 AM - last edited on 2025-10-14 6:48 AM by Andrew Neil
We have a battery-powered device using:
Our communication pattern:
This means we re-enable/disable UART Idle Line interrupt on every transaction (potentially hundreds of times per day).
#define RING_BUFFER_SIZE 2048
#define ISR_BUFFER_SIZE 1024
typedef struct {
lwrb_t rb; // lwrb ring buffer
volatile bool rx_data_ready_flag; // Flag set by ISR
uint8_t rb_buffer[RING_BUFFER_SIZE]; // Ring buffer storage
uint8_t isr_buffer[ISR_BUFFER_SIZE]; // Intermediate buffer for Idle Line ISR
} uart_rb_t;
uart_rb_t modem_rb;
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if (huart == &MODEM_UART) {
// Save data from ISR buffer to ring buffer
lwrb_write(&modem_rb.rb, modem_rb.isr_buffer, Size);
modem_rb.rx_data_ready_flag = true;
// Re-enable Idle Line interrupt
int retries = 10;
do {
if (HAL_UARTEx_ReceiveToIdle_IT(&MODEM_UART,
modem_rb.isr_buffer,
sizeof(modem_rb.isr_buffer)) == HAL_OK) {
break;
}
retries--;
} while (retries > 0);
}
}
After ~60 days of continuous operation, the device completely froze with no recovery.
<<EndParse <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>StartParse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
"AT+QISTATE=1,1" --> [response received OK]
<<EndParse <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>StartParse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
"AT+QISTATE=1,1" --> [response received OK]
<<EndParse <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>StartParse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
"AT+QISTATE=1,1" --> [response received OK]
<<EndParse <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>StartParse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[SYSTEM FROZE HERE - no further output]Notice: The command string was not printed in the last call, suggesting the code hung before that point.
uint32_t tick = HAL_GetTick();
do {
return_code = modem_send_command_wait_parse_result(
"AT+QISTATE=1,1",
"+QISTATE:",
/* parsing params */,
300 /* timeout ms */
);
if (condition_met) break;
HAL_Delay(100);
} while (HAL_GetTick() - tick < 15000); // 15 second outer timeout
int modem_send_command_wait_parse_result(..., int timeout, ...) {
// Local buffers
char formatted_command[1024] = {0};
char buffer_final[2048] = {0};
unsigned int total_bytes = 0;
printf(">>StartParse >>>>\n");
if (command_to_send != NULL) {
printf("\"%s\" --> ", command_to_send);
}
// Enable UART RX Interrupt with Idle-line detection
int retries = 10;
do {
if (HAL_UARTEx_ReceiveToIdle_IT(&MODEM_UART,
modem_rb.isr_buffer,
sizeof(modem_rb.isr_buffer)) == HAL_OK) {
break;
}
retries--;
} while (retries > 0);
if (retries == 0) {
return_code = -1;
}
uint32_t tick = HAL_GetTick();
// Main receive loop with timeout
while (return_code > 0 && ((HAL_GetTick() - tick) < timeout)) {
// Check flag and read from ring buffer
if (modem_rb.rx_data_ready_flag) {
modem_rb.rx_data_ready_flag = false;
int bytes = lwrb_read(&modem_rb.rb,
&buffer_final[total_bytes],
sizeof(buffer_final) - total_bytes);
total_bytes += bytes;
}
// Parse response, check for expected strings, etc.
// ...
}
// Cleanup
HAL_UART_Abort_IT(&MODEM_UART);
lwrb_reset(&modem_rb.rb);
return return_code;
}
// Main loop (non-atomic):
if (modem_rb.rx_data_ready_flag) { // Read
modem_rb.rx_data_ready_flag = false; // Write - ISR could interrupt here!
}2025-11-05 12:24 AM - edited 2025-11-05 1:14 AM
@TDK @Andrew Neil I used my program in debug mode and I saw that the program when called HAL_UARTEx_ReceiveToIdle_IT stopped inside this function in the stm32l4xx_hal_uart.c
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t isrflags = READ_REG(huart->Instance->ISR);
uint32_t cr1its = READ_REG(huart->Instance->CR1); // Stoped here
uint32_t cr3its = READ_REG(huart->Instance->CR3);
uint32_t errorflags;
uint32_t errorcode;
.....
}The call stack in debug mode is the following:
HAL_UART_IRQHandler() ->Stopped here at the line that mentioned above.
USART1_IRQHandler() -> HAL_UART_IRQHandler(&huart1);
UART_Start_Receive_IT() ->
/* Computation of UART mask to apply to RDR register */
UART_MASK_COMPUTATION(huart);
HAL_UARTEx_ReceiveToIdle_IT()
modem_send_command_wait_parse_result()
modem_open_connection()
udp_FSM_open_socket()
I saw the UART1 ISR register and the only bits that are asserted are:
IDLE
RXNE
TC
TXE
EOBF
From the register side:
sp = 0x20016f60
lr = 134363963
Disassembly shows this:
2294 uint32_t cr1its = READ_REG(huart->Instance->CR1);
0802b8f6: ldr r3, [r7, #4]
0802b8f8: ldr r3, [r3, #0] Mentioning this
0802b8fa: ldr r3, [r3, #0]
0802b8fc: str.w r3, [r7, #224] @ 0xe0
I can't understand why it doesn't print the command in the
modem_send_command_wait_parse_result
The debug mode does not showing anything wrong like the command not normally passed inside this function, I can see it but I didn't see it in the logs. Furthermore, I can't understand why it stack and left my MCU idle it does not doing anything stack there for hours.
When I hit run code it continue correctly. I saw that in low level function modem_send_command_wait_parse_result() the receive to idle fails in the first try and went to enable it in the second try and stopped there. But when I hit the play button it continue.