cancel
Showing results for 
Search instead for 
Did you mean: 

Best way to use HAL UART Receiver IT Function?

rajpaged
Associate II
Posted on July 18, 2014 at 01:52

I'm trying to understand how the interrupt handlers works for the HAL UART module. I have the UART1 set with global interrupts using stm32cubeMX generated code, and am able to send and receive data into buffers.

Using the HAL_UART_Receive_IT() function, I see the application enter the HAL_UART_RxCpltCallback function once the buffer is full. How do I ready the module for another receive interrupt when I'm done in this callback handler? If I issue the HAL_UART_Receive_IT() function again at the end of the handler, my application gets stuck after a few receive interrupts.

I essentially want to use the UART to receive various console like commands (each different in length) so plan to use the callback handler to house a table of possible commands and act on them. As the interrupt only triggers on a full buffer, would I need to read single characters at a time, or is there a better way of doing this?

#stm32-hal-uart #hal #stm32f4 #uart #discovery
14 REPLIES 14
Posted on August 06, 2014 at 15:04

Hih.hammer,

Let's shed some light onhow the interrupt handlers work for the HAL UART module:
  • When the HAL_UART_RxCpltCallback() API is called the UART module is ready for another receive, so user can start the next receive process under the call back API.
  • Before starting the next receive operation, make sure the DR register is empty (no data received on the UART interface after the last DR register read) to avoid UART overrun error: you can use this macro to flush the DR register: __HAL_UART_FLUSH_DRREGISTER()
Concerning the use of the UART to receive various console like commands (each different in length), it depends on application constratins:
  • For blocking mode, you can use the polling process by configuring the parameter size as max commands size and the parameter Timeout as max commands timeout and check the process returned status: if the status is HAL_TIMEOUT but the data is correctly received in this case you can consider that the transfer is done with success then you can start the second receive process.

UARTStatus = HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, MaxCmdSize, MaxCmdTimeout); 

/* MaxCmdSize and MaxCmdTimeout depend on user application */
While(UARTStatus != HAL_OK) || (UARTStatus != HAL_TIMEOUT))
{
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, MaxCmdSize, MaxCmdTimeout); 
}

  • For non-blocking mode (IT or DMA), it should be better to do this in two receive operations instead of single byte at a time: you can start the first receive processto ger the command opcode (I think that the opcode size is fix) and when the opcode is received under the callback you can decode the data and start the second receive process to receive that command body.

Thank you for your interest in our STM32Cube solution.

With regards.
william
Associate
Posted on November 28, 2014 at 07:52

Heisenberg,

I have question along same lines. 

A little background will help you understand my questions:

I have an application written for an NXP ARM7 I'd like to port to STM32F4. The ARM7 is a component in a system that includes a host PC running high level control code.

The ARM7 has a 16 byte receive buffer in the UART that is completely hands free, transparent to the user. User code has a long (500us) high priority (time sensitive) scheduled routine that runs every mS, that doesn't(can't) get interrupted so the host PC never sends a string longer than the 16 byte UART buffer. The buffer allows saving characters during the scheduled routine without causing a timing deviation in that routine. The ARM7 code uses polling (blocking) when it's ready to see what next command is, and the command is always terminated with a CR so it pulls one byte at a time from the buffer until it finds the CR. If the scheduled routine starts during the polling process, the polling of course resumes once the routine is complete. The length of the command is variable.

For the STM 

HAL_UART_Receive() command I have a couple questions to see if this blocking command can work in my situation.

1 - once the 

HAL_UART_Receive() starts, if the high priority (uninterruptable) scheduled routine is called, will overruns occur if characters are received while scheduled routine is using processor? - I guess I'm asking if this blocking routine is using DMA in the background and can buffer data without the using the processor.

2 - for the timeout that is part of the 

HAL_UART_Receive(), does the timeout start at the moment the API is called, or does it start when the 1st byte arrives at the UART? Since I don't know the length of command, I can use a timeout to return from API but only if the timeout doesn't start until the 1st character arrives at the UART. The UART runs at 200,400 baud and some of the commands take less than a mS to complete, but the host computer can take anywhere from 1mS to 10mS to respond to a request for next command. Excessive timeout waits will add-up and cause unacceptable throughput loss.

With the command length unknown, is there a documented way to use DMA for this task?

Thanks in advance for any help you can provide.

-Billy

simon239955_stm1
Associate
Posted on December 08, 2014 at 16:15

Billy, you can scroll  the STM32 reference manual to see how the DMA works, there is some dedicated channels for USART and UART, it's not that hard to use. You just have to choose the trigger, the function ( hardware -> memory ) and set the adresse where you want your data.

I hope I was able to help you a bit.

herrjohan
Associate
Posted on December 23, 2014 at 13:14

To that I added to the file ''stm32f2xx_hal.�?''

/ *********************************************** /

 __weak void HAL_UART_REC_BYTE(UART_HandleTypeDef *huart, uint16_t byte)

{

  /* NOTE: This function Should not be modified, when the callback is needed,

           the HAL_UART_REC_BYTE could be implemented in the user file

   */ 

}

void HAL_UART_IRQHandler_ALWAYS_RECIEVE(UART_HandleTypeDef *huart){

  uint32_t tmp1 = 0, tmp2 = 0;

  tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_PE);

  tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_PE);  

  /* UART parity error interrupt occurred ------------------------------------*/

  if((tmp1 != RESET) && (tmp2 != RESET))  { 

    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_PE);

    huart->ErrorCode |= HAL_UART_ERROR_PE;

  }

  

  tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_FE);

  tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);

  /* UART frame error interrupt occurred -------------------------------------*/

  if((tmp1 != RESET) && (tmp2 != RESET))  { 

    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_FE);

    huart->ErrorCode |= HAL_UART_ERROR_FE;

  }

  

  tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_NE);

  tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);

  /* UART noise error interrupt occurred -------------------------------------*/

  if((tmp1 != RESET) && (tmp2 != RESET))

  { 

    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_NE);

    

    huart->ErrorCode |= HAL_UART_ERROR_NE;

  }

  

  tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_ORE);

  tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);

  /* UART Over-Run interrupt occurred ----------------------------------------*/

  if((tmp1 != RESET) && (tmp2 != RESET))

  { 

    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE);

    

    huart->ErrorCode |= HAL_UART_ERROR_ORE;

  }

  

  tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);

  /* UART in mode Receiver ---------------------------------------------------*/

  if(tmp2 != RESET)  { 

uint16_t tmp;

//uint8_t byte;

    if(huart->Init.WordLength == UART_WORDLENGTH_9B)    {

      //tmp = (uint16_t*) huart->pRxBuffPtr;

      if(huart->Init.Parity == UART_PARITY_NONE)      {

        tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);

        //huart->pRxBuffPtr += 2;

      } else {

        tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);

        //huart->pRxBuffPtr += 1;

      }

    } else {

      if(huart->Init.Parity == UART_PARITY_NONE)      {

        tmp = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);

      } else {

        tmp = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);

      }

    }

    HAL_UART_REC_BYTE(huart, tmp);

    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_RXNE);

  }

  

  tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_TC);

  tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TC);

  /* UART in mode Transmitter ------------------------------------------------*/

  if((tmp1 != RESET) && (tmp2 != RESET))

  {

    UART_Transmit_IT(huart);

    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_TC);

  }

  

  if(huart->ErrorCode != HAL_UART_ERROR_NONE)

  {

    /* Set the UART state ready to be able to start again the process */

    huart->State = HAL_UART_STATE_READY;

    

    HAL_UART_ErrorCallback(huart);

  }  

}

/ ************************************************* /

The file ''stm32f2xx h.�?'' replaced the interrupt function(sample)

/*****************************************************/

void UART5_IRQHandler(void){

  HAL_NVIC_ClearPendingIRQ(UART5_IRQn);

  //HAL_UART_IRQHandler(&huart5);

HAL_UART_IRQHandler_ALWAYS_RECIEVE(&huart5);

}

/*****************************************************/

and made the implementation of the function:

(here it is necessary to insert the algorithm receiving bytes)

/************************************/

void HAL_UART_REC_BYTE(UART_HandleTypeDef *huart, uint16_t byte)

{

switch((uint32_t)huart->Instance){

case (uint32_t)USART1:

break;

case (uint32_t)USART2:

break;

case (uint32_t)USART3:

break;

case (uint32_t)UART4:

break;

case (uint32_t)UART5:

break;

case (uint32_t)USART6:

break;

}

}

/************************************/

I am sorry for bad english

childresss
Associate II
Posted on January 06, 2015 at 08:28

I am not quite following the code posted. I am guessing that it replaces the interrupt service routine (ISR) in the standard HAL.

The standard HAL for UART receive is unusable for most applications where the incoming data is not structured and a length can be determined.

What is sorely needed is the CONVENTIONAL I/O read, where one can make an API call to learn how many data items (bytes) have been received. Then call an API again to read that many bytes from the buffer, adjust the pointer and count.

It seems the major mistake in the HAL for UART receive is that there is no ordinary ring buffer where, for reading UART, there are head and tail pointers and an ever-present read enabled, filling the ring buffer. If the ring buffer fills, further incoming data is discarded and an overrun is declared. This only happens if the application is too slow in reading from the ring buffer.

This logic and ring buffer is used in every UART ISR I've ever used.

What say ye at ST?

marco239955_stm1_st
Associate II
Posted on January 13, 2015 at 10:41

I second Steve's opinion, such a ring buffer based implementation is widely needed. In the meanwhile I'll write myself something similar to Ilya's functions.

sunphoenixt
Associate II
Posted on February 12, 2015 at 14:26

yeah,I agree to steve too.

when I test uart on stm32f4-nucleo board use HAL library, it is really ''fool'',for it can only receive dedicatate bytes,more or less will cause timeout or discard data error .

I have decide to use standard library for my current project.

PS: There are many useless or dumy code in HAL library,which i think some ST programmer leaved it for further modify.

Mikk Leini
Senior
Posted on February 22, 2015 at 12:52

I decided to migrate from standard peripheral library to cube (HAL) and faced this same problem with UART. I really like the I2C read-write DMA/IT functions because 99% of cases you know how long is the data and these functions make implementation 10x faster, but on UART case this HAL approach is not completely suitable - in fact it even makes implementation difficult.

I could agree that on transmission you know the length and you can use these functions, but on reception not all protocols contain length at packet header. For example - NMEA for GPS. To make that work i'd need to check for delimiter at the end of packet. Implementing such reception with 1-byte receive functions calls is an overkill. Doing it in larger chunks with timeouts means that i'd need to implement another top layer data glueing logic (e.g. FIFO). This goes too crazy.

Anyway, i decided to keep my original simple implementation and do interrupt based reception/transmission on my own, but this is not possible anymore! There are no primitive functions nor macros to access the data register. Of course i could access it directly with UART handle->Instance->DR, but this does not look nice - this is a hack. So as i see, the very least which ST could do is to implement a macro or function for DR access. But the nicest thing to have would of course be IT or DMA based ring buffers on RX and TX 🙂

I'm using STM32CubeF4 V1.4.0 btw.

kristaps
Associate
Posted on May 29, 2015 at 17:20

How does one flush the DR register with the HAL V1.2.1 ?

There is no macro like that...

It's just that it calls the RxCpltCallback callback function only the first time I enter something in the serial terminal... What could be the solution? I'm using the STM32F072RBT6 (STM32F072-Nucleo board) TY!