cancel
Showing results for 
Search instead for 
Did you mean: 

CubeMX - UART receive complete interrupt

sincoon
Associate II

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.

17 REPLIES 17
markb
Associate II

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.

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.

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
 { ... }
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Posted on July 10, 2017 at 18:23

This a great discussion!!  

I found this thread searching for Receive_IT issues when dealing with stopped receive processes during full-duplex testing.  (Full-duplex command-line terminal with one-character echo hammered with a script).  What happens for me is after many thousands of entries, or just a few, there's an all stop on the uart being exercised due to the interrupt not being reset, making it so the next incoming character fails to call the RxCplt callback.  With a debugger, I cause the call to Receive_IT again - this allows resumption of test.  The upshot is it's likely Vito's claim above about the library being locked up due to improper interrupt register handling (__HAL_LOCKED, etc.) is likely on the money.  

With a UART intensive application, this sort of stuff simply cannot happen!!  How will such conditions be detected if the developers don't exercise the heck out of every application of the HAL libraries??  On one hand, it should be standard practice, but the errors are too easily revealed.  

There should be no reason to have to second-guess the HAL libraries at every corner - they should function properly and be vetted by the ST team.  I find these failures disturbing.

Posted on July 12, 2017 at 06:10

Thanks for the very detailed description, but I think your solution doesn't reset the receive buffer so you'll very quickly have a buffer overrun, right?  You also need to edit customUart_UART_Receive_IT such that it doesn't increment 

huart->

pRxBuffPtr in any case.  The buffer will only ever contain the last received byte (or word), but that's ok because the callback function is now invoked after every individual receive, not just when the buffer is full.

Posted on July 13, 2017 at 09:04

Hi,

I'm having some trouble with the solution by @m8r-xuulk51​ . I have made the changes he has suggested. I only call

HAL_UART_Receive_IT() once on startup, passing in a one byte buffer.customUart_HAL_UART_RxByteCallback() triggers as expected. However, when I read the buffer when in the RX callback, I only ever see the very first byte I received.

The problem appears to be the line

in the file function

customUart_UART_Receive_IT().

The pointer pRxBuffPtr is never re-initialised, which would be done by re-calling

HAL_UART_Receive_IT()

.

I can see I'm receiving my bytes, but they are being stored in memory offset from the original buffer. i.e. The first byte I receive is located inpRxBuffPtr[0], the second inpRxBuffPtr

[1], etc.

However, my buffer is only one byte long.

So, do I need to be callingHAL_UART_Receive_IT() at the end ofcustomUart_HAL_UART_RxByteCallback()? Or have I missed something?

I am using STM32CubeMX 4.0 and FW_F4 V1.1. Is it possible the recommended changes are not quite valid with the version of FW i'm using?

Regards, Matt.

*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007FU);�?

Posted on July 14, 2017 at 08:31

You are right @matthewgkerr​ 

In my original post, I forgot to say to remove the pointer incrementing.

This approach is intended to receive byte-by-byte, so the buffer is only 1 byte long.

I updated my original post accordingly.

//original:*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);//customUart_UART_Receive_IT:*huart->pRxBuffPtr = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Posted on July 18, 2017 at 06:53

Thanks for the update Mike. The Rx interrupt is now working as expected. Now to put it through its paces.

Another issue I've spotted is if HAL_UART_ErrorCallback is called, HAL_UART_Receive_IT would have reset huart->ErrorCode back to HAL_UART_ERROR_NONE. I assume I need to now do this in

HAL_UART_ErrorCallback.

Marcus J
Associate
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!