Skip to main content
BBB.1
Associate
January 10, 2021
Solved

HAL_UART_Transmit_IT blocks until end of transmission

  • January 10, 2021
  • 6 replies
  • 5740 views

I am using STM32CubeIDE + STM32F103C8T6 MCU with USART1. I am transmitting data by calling this function:

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size)

The function is generated by CubeIDE. It turns out that this call blocks until the last character is sent out. I found that what is blocking is the following line in this function:

 __HAL_UART_ENABLE_IT(huart, UART_IT_TXE);

HAL_UART_TxCpltCallback is called some microseconds before HAL_UART_Transmit_IT returns.

Any ideas why this happens? I would expect HAL_UART_Transmit_IT to return immediately, while transmitting the data should be done in the background.

This topic has been closed for replies.
Best answer by TDK

Your baud rate is high. You have about 10us between characters, which may not be enough for the IRQ handler to finish, although it should be enough if your clock settings are maxed. Slow down your baud rate by an order of magnitude. If that still blocks, instrument HAL_UART_IRQHandler in the same manner to see how long it takes and where it's getting held up and/or to confirm that's what is actually blocking execution.

The small delay between the "G" and "H" characters suggests your IRQ handler is barely keeping up with feeding the peripheral data.

6 replies

TDK
January 10, 2021

If characters are being sent out correctly, it means the ISR is getting called. Perhaps the system is overwhelmed with calls and can't make progress in the main loop.

It's not possible for that call to really block. There's no logic within it which would cause that. See for yourself:

https://github.com/STMicroelectronics/STM32CubeF1/blob/003dfc9e6c2e424c19a25df3934afaf7fce660d6/Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h#L516

"If you feel a post has answered your question, please click ""Accept as Solution""."
BBB.1
BBB.1Author
Associate
January 10, 2021

Thanks for your reply. I have already seen the syntax of __HAL_UART_ENABLE_IT and totally agree that there is no logic for this to block. However, let me give more details.

Main loop:

while (1) {
 DIAGN_Print("ABCDEFGHIJKLM\n");
 HAL_GPIO_TogglePin(nLED_GPIO_Port, nLED_Pin);
 HAL_IWDG_Refresh(&hiwdg);
 HAL_Delay(50);
 }

Called functions:

#define DIAGN_HUART huart1
 
#define DIAGN_BUFFER_SIZE 128
static char diagn_buffer[DIAGN_BUFFER_SIZE];
 
static void Print(const char *data_ptr, size_t size) {
 HAL_GPIO_WritePin(OUT_1_GPIO_Port, OUT_1_Pin, 1);
 
 while ((&DIAGN_HUART)->gState != HAL_UART_STATE_READY) {
 }
 HAL_GPIO_WritePin(OUT_3_GPIO_Port, OUT_3_Pin, 1);
 
 if (HAL_UART_Transmit_IT(&DIAGN_HUART, (uint8_t*) data_ptr, size) != HAL_OK) {
 Error_Handler();
 }
 
 HAL_GPIO_WritePin(OUT_1_GPIO_Port, OUT_1_Pin, 0);
}
 
void DIAGN_Print(const char *ptr) {
 HAL_GPIO_WritePin(OUT_0_GPIO_Port, OUT_0_Pin, 1);
 Print(ptr, strlen(ptr));
 HAL_GPIO_WritePin(OUT_0_GPIO_Port, OUT_0_Pin, 0);
}
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
 HAL_GPIO_WritePin(OUT_3_GPIO_Port, OUT_3_Pin, 0);
}

Instrumented HAL_UART_Transmit_IT (the only difference is added HAL_GPIO_WritePin before and after __HAL_UART_ENABLE_IT):

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
 /* Check that a Tx process is not already ongoing */
 if (huart->gState == HAL_UART_STATE_READY)
 {
 if ((pData == NULL) || (Size == 0U))
 {
 return HAL_ERROR;
 }
 
 /* Process Locked */
 __HAL_LOCK(huart);
 
 huart->pTxBuffPtr = pData;
 huart->TxXferSize = Size;
 huart->TxXferCount = Size;
 
 huart->ErrorCode = HAL_UART_ERROR_NONE;
 huart->gState = HAL_UART_STATE_BUSY_TX;
 
 /* Process Unlocked */
 __HAL_UNLOCK(huart);
 
 /* Enable the UART Transmit data register empty Interrupt */
 HAL_GPIO_WritePin(OUT_2_GPIO_Port, OUT_2_Pin, 1);
 __HAL_UART_ENABLE_IT(huart, UART_IT_TXE);
 HAL_GPIO_WritePin(OUT_2_GPIO_Port, OUT_2_Pin, 0);
 
 return HAL_OK;
 }
 else
 {
 return HAL_BUSY;
 }
}

Result (D0...3 is the state of OUT_0...3; D4 is the Tx pin):

0693W000006I7HTQA0.png

TDK
TDKBest answer
January 10, 2021

Your baud rate is high. You have about 10us between characters, which may not be enough for the IRQ handler to finish, although it should be enough if your clock settings are maxed. Slow down your baud rate by an order of magnitude. If that still blocks, instrument HAL_UART_IRQHandler in the same manner to see how long it takes and where it's getting held up and/or to confirm that's what is actually blocking execution.

The small delay between the "G" and "H" characters suggests your IRQ handler is barely keeping up with feeding the peripheral data.

"If you feel a post has answered your question, please click ""Accept as Solution""."
BBB.1
BBB.1Author
Associate
January 10, 2021

Thanks TDK, this was a super fast solution! I wasn't aware of the fact that HAL_UART_IRQHandler gets called after every character transmission as well, I was erroneously supposing it only gets called once at the end of packet transmission.

HAL_UART_IRQHandler takes ~11us @ 16MHz so this explains why I was stuck with 921.6kbps.

TDK
January 10, 2021
For _IT calls, an ISR is called every time something needs done, such as putting a new character into the outgoing data register.
For _DMA, the ISR is only called on certain conditions, like half transfer complete or full transfer complete or an error.
"If you feel a post has answered your question, please click ""Accept as Solution""."
BBB.1
BBB.1Author
Associate
January 10, 2021

Great, I switched to transmission in DMA mode which finally allows me to keep the high baud rate and non-blocking call of DIAGN_Print().

Thanks for explanation!