Skip to main content
sincoon
Associate III
June 5, 2015
Question

CubeMX - UART receive complete interrupt

  • June 5, 2015
  • 5 replies
  • 4959 views

Posted on June 05, 2015 at 17:08

I'm a little bit confusing with question of UART. So, I need to receive data through UART from PC. Ok, as said in stm32f4xx_hal_uart.c file:

*** Interrupt mode IO operation ***

===================================

[..]

(+) Send an amount of data in non blocking mode using HAL_UART_Transmit_IT()

(+) At transmission end of transfer HAL_UART_TxCpltCallback is executed and user can

add his own code by customization of function pointer HAL_UART_TxCpltCallback

(+) Receive an amount of data in non blocking mode using HAL_UART_Receive_IT()

(+) At reception end of transfer HAL_UART_RxCpltCallback is executed and user can

add his own code by customization of function pointer HAL_UART_RxCpltCallback

(+) In case of transfer Error, HAL_UART_ErrorCallback() function is executed and user can

add his own code by customization of function pointer HAL_UART_ErrorCallback

this way is also used in examples (UART_Hyperterminal_IT, etc).

Does it means that there are no way to get interrupt on data ready in UART buffer?

Ok, another question. Code in hyperterminal example:

int main(void)
{ 
/*hardware configuration*/
if(HAL_UART_Init(&UartHandle) != HAL_OK)
{
/* Turn LED3 on: in case of Initialization Error */
BSP_LED_On(LED3);
while(1)
{
}
}
/*##-2- Start the transmission process #####################################*/ 
/* While the UART in reception process, user can transmit data through 
''aTxBuffer'' buffer */
if(HAL_UART_Transmit_IT(&UartHandle, (uint8_t*)aTxStartMessage, TXSTARTMESSAGESIZE)!= HAL_OK)
{
/* Turn LED3 on: Transfer error in transmission process */
BSP_LED_On(LED3);
while(1)
{
} 
}
/*##-3- Put UART peripheral in reception process ###########################*/ 
/* Any data received will be stored ''aRxBuffer'' buffer : the number max of 
data received is 10 */
if(HAL_UART_Receive_IT(&UartHandle, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)
{
/* Turn LED3 on: Transfer error in reception process */
BSP_LED_On(LED3);
while(1)
{
} 
}
/*##-4- Wait for the end of the transfer ###################################*/ 
/* Before starting a new communication transfer, you need to check the current 
state of the peripheral; if it’s busy you need to wait for the end of current
transfer before starting a new one.
For simplicity reasons, this example is just waiting till the end of the 
transfer, but application may perform other tasks while transfer operation
is ongoing. */ 
while (HAL_UART_GetState(&UartHandle) != HAL_UART_STATE_READY)
{
} 
/*##-5- Send the received Buffer ###########################################*/ 
if(HAL_UART_Transmit_IT(&UartHandle, (uint8_t*)aRxBuffer, RXBUFFERSIZE)!= HAL_OK)
{
/* Turn LED3 on: Transfer error in transmission process */
BSP_LED_On(LED3);
while(1)
{
} 
}
/*##-6- Wait for the end of the transfer ###################################*/ 
while (HAL_UART_GetState(&UartHandle) != HAL_UART_STATE_READY)
{
}
/*##-7- Send the End Message ###############################################*/ 
if(HAL_UART_Transmit_IT(&UartHandle, (uint8_t*)aTxEndMessage, TXENDMESSAGESIZE)!= HAL_OK)
{
/* Turn LED3 on: Transfer error in transmission process */
BSP_LED_On(LED3);
while(1)
{
} 
}
/*##-8- Wait for the end of the transfer ###################################*/ 
while (HAL_UART_GetState(&UartHandle) != HAL_UART_STATE_READY)
{
}
/* Infinite loop */ 
while (1)
{
}
}

Why all this stuff, like HAL_UART_Receive_IT,

HAL_UART_Transmit_IT, etc. are NOT in last endless while loop? Does this means that I'll receive data only once? If not, does this means that I need call HAL_UART_Receive_IT in loop?

All this question is because previously, in SPL, there was clear interrupt, which invokes at UART events and I could check, if this interrupt is due to receiving.

This topic has been closed for replies.

5 replies

markb
Associate III
June 5, 2015

Posted on June 05, 2015 at 22:54

Hi, this is my second attempt to write a reply, the first got trashed by this ****** forum system.

OK, what I was trying to say is that the UART API is a broken as the other APIs (e.g. CAN) in that you can't reliably post a request for another

read from within the receive complete callback function due to the fact that if at the time you call HAL_UART_Receive_IT() a call to

HAL_UART_Transmit() is in progress which has locked the UART API and so the receive doesn't happen. So, whereas it would be nice to have something like:

uint8_t rxChar;
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
 /* here we consume the current value of rxChar */
 ...
 
 /* set up to receive another char */
 HAL_UART_Receive_IT(&huart3, &rxChar, 1); 
}

That won't work reliably so, instead, I take the important bit out of HAL_UART_Receive_IT() and put it into my own function which I can then call after processing the latest received character and also after an error (PE, FE, OV, etc.) has occurred because if you don't

repost the receive, then you get no more characters received.

 void UART_RxAgain(UART_HandleTypeDef *huart) { 
 
 // use the code from HAL_UART_Receive_IT() to repost interrupt 
 
 huart->pRxBuffPtr = &wifiRxChar; 
 
 huart->RxXferSize = 1; 
 
 huart->RxXferCount = 1; 
 
 huart->ErrorCode = HAL_UART_ERROR_NONE; 
 
 /* Check if a transmit process is ongoing or not */ 
 
 if(huart->State == HAL_UART_STATE_BUSY_TX) 
 
 { 
 
 huart->State = HAL_UART_STATE_BUSY_TX_RX; 
 
 } 
 
 else 
 
 { 
 
 huart->State = HAL_UART_STATE_BUSY_RX; 
 
 } 
 
 
 
 /* Enable the UART Parity Error Interrupt */ 
 
 __HAL_UART_ENABLE_IT(huart, UART_IT_PE); 
 
 
 
 /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */ 
 
 __HAL_UART_ENABLE_IT(huart, UART_IT_ERR); 
 
 /* Process Unlocked */ 
 
 //__HAL_UNLOCK(huart); 
 
 /* Enable the UART Data Register not empty Interrupt */ 
 
 __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); 
 
 } 
 
 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { 
 
 /* consume the received character */ 
 
 UART_RxAgain(huart); 
 
 } 
 
 void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { 
 
 UART_RxAgain(huart); 
 
 } 

Sorry about the terrible formatting, the forum software screwed up again!!! I hope you get the idea of what I am on about.

Vito Marolda
Associate III
April 20, 2017
Posted on April 20, 2017 at 14:37

Same problem here: HAL_UART_Receive_IT cannot be called reliably during interrupt, because it risks to find __HAL_LOCK'ed by an ongoing Transmit.

Anyway, this is not the only potential glitch.

Also in the workaround you proposed, the HAL_UART_Receive_IT (fragment) still manipulates interrupt flags (and reception end also does it before invoking callback) in a manner that I suppose it is not safe (disassembly reveals standard read-modify-write on interrupt registers, without any atomic countermeasure). So, if a similar manipulation was occurring in the background (for starting a transmission with interrupts for example), this can lead to erroneous update of interrupt registers.

My solution is as follows:

  • Call HAL_UART_Receive_IT only once, when initializing the uart.
  • Then, modify the interrupt handler (in stm32xxx_it.c as follows:

void USART2_IRQHandler(void)

{

/* USER CODE BEGIN USART2_IRQn 0 */

 extern void HAL_UART_RxProgressCallback(UART_HandleTypeDef *huart);

++huart2.RxXferCount; //so, it will never stop

/* USER CODE END USART2_IRQn 0 */

HAL_UART_IRQHandler(&huart2);

/* USER CODE BEGIN USART2_IRQn 1 */

--huart2.pRxBuffPtr; //write always the same location

HAL_UART_RxProgressCallback(&huart2);

/* USER CODE END USART2_IRQn 1 */

}
  • in your routine 

    HAL_UART_RxProgressCallback

    you can extract the received character from the location of the buffer you passed in at the initial (and only) 

    HAL_UART_Receive_IT.

In this manner, no messing with interrupt registers occurs during interrupts.

Anyway my opinion is that this HAL USART driver is missing an important use-case, not implementing a specific API for continuous reception during intermittent transmission in full-duplex.

Moreover, it seems to me that if main program starts a Transmit_IT while USART IRQ occurs, and irq enable manipulation also happens during IRQ, erroneous irq enable register update may occur.

m8r-xuulk51
Associate
May 18, 2017

Posted on May 18, 2017 at 08:20

Edit: 2017-July-14: Fixed pointer incrementing.

Hi,

I ran in into the same problem and improved upon the solution here a little bit. I solved the problem to get a byte-wise reception of serial UART data by interrupt in the following way.

DISCLAIMER:

Only tested for RX only. I did not test, if you can use the normal HAL functions forsending TX data. There might be problems.

1st step: Create a new customUart.c file. To this file copy the following functions from stm32f7xx_hal_uart.c and rename them as follows.

HAL_UART_RxCpltCallback --> customUart_HAL_UART_RxByteCallback
UART_Receive_IT --> customUart_UART_Receive_IT�?
HAL_UART_IRQHandler --> customUart_HAL_UART_IRQHandler
//just copy, including the static keyword, no rename necessary for:
UART_DMAAbortOnError
UART_EndTransmit_IT
UART_EndRxTransfer
UART_Transmit_IT�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Then edit the customUart_HAL_UART_IRQHandler()-function to call the the customUart_Receive_IT() function, instead of the original UART_Receive_IT(huart):

//in emCustomUart_HAL_UART_IRQHandler(...)
//replace:
//old: UART_Receive_IT(huart);
//new: emCustomUart_UART_Receive_IT(huart);�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Then modify the customUart_Receive_IT() to look like this.

HAL_StatusTypeDef customUart_UART_Receive_IT(UART_HandleTypeDef *huart)
{
 uint16_t* tmp;
 uint16_t uhMask = huart->Mask;
 /* Check that a Rx process is ongoing */
 if(huart->RxState == HAL_UART_STATE_BUSY_RX)
 {
 if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
 {
 tmp = (uint16_t*) huart->pRxBuffPtr ;
 *tmp = (uint16_t)(huart->Instance->RDR & uhMask);
 }
 else
 {
 //here we remove the buffer incrementing
 *huart->pRxBuffPtr = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
 }
 //Removed check for completion, and instead call our custom callback
 customUart_HAL_UART_RxByteCallback(huart);
 /* Clear RXNE interrupt flag */
 __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
 return HAL_OK;
 }
 else
 {
 /* Clear RXNE interrupt flag */
 __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
 return HAL_BUSY;
 }
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

2nd step: create a customUart.h header file, add an #include 'stm32f7xx_hal_def.h', and export the customUart_** functions.

//emCustomUart.h
#ifndef CUSTOMUART_H_
#define CUSTOMUART_H_
#include 'stm32f7xx_hal_def.h'
void customUart_HAL_UART_RxProgressCallback(UART_HandleTypeDef *huart);
HAL_StatusTypeDef customUart_UART_Receive_IT(UART_HandleTypeDef *huart);
void customUart_HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
#endif /* CUSTOMUART_H_ */�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

3rd step: Modify the file stm32f7xx_it.c at the appropriate interrupt handler. Here example for USART6:

void USART6_IRQHandler(void)
{
 /* USER CODE BEGIN USART6_IRQn 0 */
 
 customUart_HAL_UART_IRQHandler(&huart6);
 //return now, so we never call the original interrupt handler
 return;
 /* USER CODE END USART6_IRQn 0 */
 HAL_UART_IRQHandler(&huart6);
 /* USER CODE BEGIN USART6_IRQn 1 */
 /* USER CODE END USART6_IRQn 1 */
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

4th step: start reception by using the normal way:

volatile uint8_t rxBuffer[1]; // single byte buffer
HAL_UART_Receive_IT(&huart6, (uint8_t *)&rxBuffer[0], 1);�?�?�?�?�?�?�?�?�?�?�?�?�?

The advantage over the solutionfrom @

is that for me, it does not generate false reception events after the MCU is reset, due to the proper error handling of the UART unit.

If someone from ST's STM32CubeMx-team is reading this, please add a USER CODE block in the following location, so this whole mess could be made a lot easier. This would allow to skip the check for completion, call a custom call-back function and return early from the function!

static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
 ...
 if (...) {...}
 else
 {
 *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
 }
 
 /* USER CODE BEGIN 1 */
 //we need to undo the buffer-incrementing here
 /* USER CODE END 1 */
 
 if(--huart->RxXferCount == 0)
 { ... }
 return HAL_OK;
 }
 else
 { ... }
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Marcus J
Visitor II
July 19, 2017
Posted on July 19, 2017 at 08:30

@ross.mike Hey buddy,

i have the same problem like you guys, but i am not able to build this modifications. May someone can upload the ready files, that i can have a look how the source code have to look like. Or can someone upload the full source code?

I have problems to understand the instructions, especially

//just copy, including the static keyword, no rename necessary for:
UART_DMAAbortOnError
UART_EndTransmit_IT
UART_EndRxTransfer
UART_Transmit_IT

and

'and export the customUart_** functions.'

Thank you guys! Youre Great!

m8r-xuulk51
Associate
July 28, 2017
Posted on July 28, 2017 at 11:00

Hello Marcus,

This means, that you should copy these 4 functions from stm32f7xx_hal_uart.c to customUart.c.

For your second part of the question: I included the source for emCustomUart.h in my post.

Konami
Senior
October 5, 2017
Posted on October 05, 2017 at 21:32

Is this still the best solution to buffering UART RX data one byte at a time? Has anyone tried using the older libraries instead that didn't have these issues?

Tesla DeLorean
Guru
October 5, 2017
Posted on October 05, 2017 at 21:44

The SPL and the HARDWARE interrupt on every byte, the USART has no FIFO, just a single byte holding buffer.

One can also trivially build an RX FIFO using circular DMA of desired depth. Think data rate vs consumption rate/periodicity.

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
Tamas Novak
Associate III
October 6, 2017
Posted on October 06, 2017 at 23:00

I think I've found a better solution. I modified  UART_Receive_IT (without leading HAL_!! It is at the end of stm32f0xx_hal_uart.c, line 2755 at my version. This file is copied to my project subdir, so I may modify it.)

The following code part would decrement the amount remaining and turn IT off when received amount of data is reached:

if(--huart->RxXferCount == 0U)

    {

      // Disable the UART Parity Error Interrupt and RXNE interrupt

      CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));

      // Disable the UART Error Interrupt: (Frame error, noise error, overrun error)

      CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);

      // Rx process is completed, restore huart->RxState to Ready

      huart->RxState = HAL_UART_STATE_READY;

      HAL_UART_RxCpltCallback(huart);

      return HAL_OK;

    }

I commented the above lines out, and inserted a custom callback instead:

HAL_UART_RxByteCallback(huart);

This callback gets the single received byte in rx buffer. 

Take care, because huart->pRxBuffPtr  is still incremented. Remove '++' from the line

      *huart->pRxBuffPtr++ = (uint8_t)(uhdata & (uint8_t)uhMask);

to prevent pointer incrementing  when using a single byte buffer only.

Harvinder Singh
Visitor II
June 28, 2018
Posted on June 28, 2018 at 15:34

Actually  i want to read data from Rx pin of the uart using HAL libraries .I am using HAL_UART_RECIEVE _IT function but not able to get any data .So can anyone please let me know how to implelent it

thank you

harvinder singh

Tesla DeLorean
Guru
June 28, 2018
Posted on June 28, 2018 at 18:27

See

STM32Cube_FW_F4_V1.21.0\Projects\STM32446E_EVAL\Applications\USB_Device\CDC_Standalone\Src\usbd_cdc_interface.c

STM32Cube_FW_F4_V1.21.0\Projects\STM32446E_EVAL\Examples\UART\UART_HyperTerminal_IT\Src\main.c

STM32Cube_FW_F4_V1.21.0\Projects\STM32F4-Discovery\Examples\UART\UART_TwoBoards_ComIT\Src\main.c

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..