5

I'm trying to understand how the STM32F091VB manages the send of data via serial protocol with the function HAL_UART_Transmit_IT()

At the moment I've a function called in the main() that creates the packet and send it via serial; it is something like this:

tx1[0] = STX;    
tx1[1] = 0xFF;
tx1[2] = 0x80;
tx1[3] = 0x80;

DE_TAST_HIGH;
HAL_UART_Transmit_IT(&huart3, tx1, 8);

Now, the data I'm sending is quite small so the code run pretty fast and I'm trying to understand what's going to happen if I try to send a huge packet via serial protocol.

For istance, if my tx1[] is 100byte the HAL_UART_Transmit_IT() function block the CPU waiting while the full packet is sent to the serial port or it works more like a separate process where I tell the micro to send that packet and, while sending it it also process the remaining part of my code/main function?

I've tried to search on the micro datasheet to see if there was something about this process but I had no luck. I've read the stm32f0xx_hal_uart.c and it confirms that it is sent via interrupt in a non blocking mode but I would like to have some more in depth documentation about it

6
  • 1
    What more information would you need? It's non-blocking interrupt based. It will send a byte when it's ready, otherwise will execute you main loop. For detailed description of the interrupt registers etc. have a look at the "Reference Manual" (what ST calls them) their "Datasheet" just says "yup, this has a UART interface". For software handling of those interrupts, you need to look at the HAL manual or the code itself. I'd suggest single stepping through the ISR and checking out when things get sent and where you end up afterwards. Commented Apr 28, 2022 at 10:26
  • @Max so it is not some kind of "multi thread/multi processor"?! When the micro is ready to trasmit jump from the main to the actual sending section of the code and back when it's over? Commented Apr 28, 2022 at 10:35
  • 1
    It's a small ST MC, there is no multithreding. And yes, that is how interrupts work. There should also be a blocking UART_Transmit() (without the _IT at the end). Have a look at this to see the difference between blocking and non-blocking send. Commented Apr 28, 2022 at 10:43
  • 1
    @NicoCaldo multithreading is not magic, if you are programming bare metal than there is no multithreading/parallelism by default and everything you do run within the same thread. Interrupts are the base for context switch but that's the maximum you can get. Commented Apr 28, 2022 at 10:44
  • 1
    @NicoCaldo by the way, I discourage using the HAL library to understand how microcontrollers work, as it is unnecessary convoluted. You can use the micro specific ARM libraries and learn from there. HAL have surely their reasons to exist, but learning isn't one of them. Commented Apr 28, 2022 at 11:16

2 Answers 2

8

First of all you need to understand how the HAL_UART_Transmit_IT is meant to be used. We can get some help from STM FAQ.

interrupt mode.

The function is "non blocking" because when you call it it will do some configuration of the interrupts and then return. The buffer will not be transmitted during the call to your function, instead the heavy lifting is deferred to a later stage.

We can further have a look at the source code, to get a proof from what I said (note I kept only the juicy parts).

Blocking

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint16_t* tmp;
  uint32_t tickstart = 0U;
  
    // [ ... ]

    huart->TxXferSize = Size;
    huart->TxXferCount = Size;
    while(huart->TxXferCount > 0U)
    {

        // [ ... ]
        // This is were the actual HW regs are accessed, starting the transfer
        huart->Instance->DR = (*pData++ & (uint8_t)0xFF);
      } 
    }
    
    // [ ... ]

  return HAL_OK
}

Non Blocking

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

    
    huart->pTxBuffPtr = pData;
    huart->TxXferSize = Size;
    huart->TxXferCount = Size;

    /* Enable the UART Transmit data register empty Interrupt */
    // This is the only part were HW regs are accessed. What is happening here??
    SET_BIT(huart->Instance->CR1, USART_CR1_TXEIE);
    
    return HAL_OK;

}

The _IT function only activates one interrupt, based also on the datasheet:

enter image description here

enter image description here

This means we will receive an interrupt whenever the TX buffer is free. Who is actually sending the data then?

With the help of the FAQs and reading the source code, we find that void HAL_UART_IRQHandler(UART_HandleTypeDef *huart) does something like this:

/* UART in mode Transmitter ------------------------------------------------*/
   if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
   {
     UART_Transmit_IT(huart);
     return;
   }

Which in turn calls the UART_Transmit_IT

 static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
 {
   uint16_t* tmp;
   
   /* Check that a Tx process is ongoing */
   if(huart->gState == HAL_UART_STATE_BUSY_TX)
   {

       huart->Instance->DR = (uint8_t)(*huart->pTxBuffPtr++ & (uint8_t)0x00FF);
 
     if(--huart->TxXferCount == 0U)
     {
       /* Disable the UART Transmit Complete Interrupt */
       CLEAR_BIT(huart->Instance->CR1, USART_CR1_TXEIE);
 
       /* Enable the UART Transmit Complete Interrupt */    
       SET_BIT(huart->Instance->CR1, USART_CR1_TCIE);
     }
     return HAL_OK;
   }
   else
   {
     return HAL_BUSY;
   }
 }

This function transmits only one byte! It then decrements the counter for the transmission (remember, all the information was set into the uart handler) and if it reaches 0, finally the complete interrupt is called.

Interrupts

Note that StmCube does the peripheral initialization and interrupt linking for you, but if you program from scratch you need to remember to write and register UART_IRQ_Handler

You can find here the code navigator to review my snippets and investigate further.

Sign up to request clarification or add additional context in comments.

Comments

0

From my experience of HAL code so far whoever wrote some of it should be shot.

Take for instance

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

I would expect this to transmit any multi byte buffer perfectly from start to finish once kicked off - but no!

YOU have to make sure that pData is pointing to a STATIC buffer, (and you can't change the contents of that static buffer until the transmission has completed), i.e. YOU have to wait until you get the Tx Complete callback invoked before you can either reuse the buffer, or before the buffer goes out of scope on your stack\heap.

In other words if you want proper interrupt based buffer transmission YOU have to implement ring buffering yourself on top of the HAL code provided by ST. The ST code will only reliably work sending single bytes at a time which rather renders pointless using interrupt driven comms in the first place.

Same applies to how they implemented DMA Idle buffer handling... a real mess when it could have been done properly using industry standard ring buffering patterns.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.