cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_UART_Transmit_IT blocks until end of transmission

BBB.1
Associate II

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.

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

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".

View solution in original post

6 REPLIES 6
TDK
Guru

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
Associate II

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
Guru

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
Associate II

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.

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
Associate II

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!