cancel
Showing results for 
Search instead for 
Did you mean: 

STM34L4 freeze during UART with Interrupt Idle line

path321
Associate III

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:
debug.png 

 

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 ?

 

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Super User

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.

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

2 REPLIES 2
TDK
Super User

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.

If you feel a post has answered your question, please click "Accept as Solution".
path321
Associate III

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