STM32 HAL USART receive by interrupt - Stack Overflow
microcontroller - Clearing USART (UART) interrupt flags in an STM32? - Electrical Engineering Stack Exchange
How to properly handle 6-8 UART interrupts (STM32)
c - USART receive interrupt stm32 - Stack Overflow
Can I use this interrupt method for multiple UART peripherals?
Can UART interrupts and DMA be used together?
How can I handle UART errors when using DMA or interrupt mode?
HAL_UART_ErrorCallback() function in your code to manage these issues gracefully.Videos
What are the best practices for handling received packets using UART in interrupt mode?
The way I am currently working, I have a global receive buffer and then perform all processing of this buffer inside my RxCpltCallback function and although it works, it doesn't seem right to me.
I can think of two ways I could improve on this but I would like it if someone could weigh in for me:
-
Use a secondary processing buffer: I can create a secondary global "processing buffer" and copy the received data into this buffer in the interrupt callback function, and then process this second buffer in the main body of my code. This will allow me to begin receiving again immediately in the callback, but there is the obvious problem of the secondary buffer being overwritten in the middle of processing if more data comes in.
-
Raise a flag upon completion: In the interrupt callback, I could raise a flag and then process the received packet inside the main body. After processing, I would then begin trying to receive data again. This eliminates the need for two buffers (I could even have a single local buffer but I would still require a global flag) and if I attempt to send data before I start trying to receive again, I believe it will just sit in a transmission buffer on the transmitting device until it can be received. Please correct me if my understanding here is wrong.
I appreciate any and all input or if you have ideas for better/nicer ways of handling things.
HAL_UART_Receive_IT() is not meant to be called from an interrupt handler that way, but to initiate receiving a fixed number of bytes via interrupt.
A possible workaround is to check your input buffer after HAL_UART_IRQHandler() completes, i.e., in the /* USER CODE BEGIN USART1_IRQn 1 */ section. When a command is processed, you can reset pRxBuffPtr and RxXferCount in the handle structure to their original values to start from the start of the buffer again.
Another horrible possible workaround would be to call HAL_UART_Receive_IT() with a buffer size of 1, and set up a HAL_UART_RxCpltCallback() handler that checks the received byte each time, and calls HAL_UART_Receive_IT() again when necessary.
Of course you could do it without HAL, as PeterJ and others (always) suggest.
- You've already implemented pin and interrupt setup. Leave them unchanged at first.
- Calculate the
UART->BRRvalue according to the reference manual, or copy the relevant code from HAL. - set
UART->CR1 = USART_CR1_RE | USART_CR1_TE | USART_CR1_UE |USART_CR1_RXNEIE;Now, you are getting interrupts. - In the interrupt function, read
UART->SRinto a temporary variable, and examine it. - Read
UART->DRwhen there is a received byte waiting, and do the error handling otherwise (later). - Get rid of the rest of the HAL calls when the above is working.
Interrupt response and processing time is often critical in embedded applications, and the HAL just wastes a lot of that.
The normal HAL library is not useful for continuous reception or commands with different length.
If you have the complete HAL package installed, you could look at the examples for the Low-Layer (LL) interface.
Projects\STM32F411RE-Nucleo\Examples_LL\USART\USART_Communication_Rx_IT_Continuous
The main thing is to set your USART to continuous reception:
void Configure_USART(void) {
/* (1) Enable GPIO clock and configures the USART pins *********************/
/* Enable the peripheral clock of GPIO Port */
USARTx_GPIO_CLK_ENABLE();
/* Configure Tx Pin as: Alternate function, High Speed, Push pull, Pull up */
LL_GPIO_SetPinMode(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_MODE_ALTERNATE);
USARTx_SET_TX_GPIO_AF();
LL_GPIO_SetPinSpeed(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinOutputType(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_PULL_UP);
/* Configure Rx Pin as: Alternate function, High Speed, Push pull, Pull up */
LL_GPIO_SetPinMode(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_MODE_ALTERNATE);
USARTx_SET_RX_GPIO_AF();
LL_GPIO_SetPinSpeed(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinOutputType(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_PULL_UP);
/* (2) NVIC Configuration for USART interrupts */
/* - Set priority for USARTx_IRQn */
/* - Enable USARTx_IRQn */
NVIC_SetPriority(USARTx_IRQn, 0);
NVIC_EnableIRQ(USARTx_IRQn);
/* (3) Enable USART peripheral clock and clock source ***********************/
USARTx_CLK_ENABLE();
/* (4) Configure USART functional parameters ********************************/
/* TX/RX direction */
LL_USART_SetTransferDirection(USARTx_INSTANCE, LL_USART_DIRECTION_TX_RX);
/* 8 data bit, 1 start bit, 1 stop bit, no parity */
LL_USART_ConfigCharacter(USARTx_INSTANCE, LL_USART_DATAWIDTH_8B, LL_USART_PARITY_NONE, LL_USART_STOPBITS_1);
/* No Hardware Flow control */
/* Reset value is LL_USART_HWCONTROL_NONE */
// LL_USART_SetHWFlowCtrl(USARTx_INSTANCE, LL_USART_HWCONTROL_NONE);
/* Oversampling by 16 */
/* Reset value is LL_USART_OVERSAMPLING_16 */
// LL_USART_SetOverSampling(USARTx_INSTANCE, LL_USART_OVERSAMPLING_16);
/* Set baud rate to 115200 using APB frequency set to 100000000/APB_Div Hz */
/* Frequency available for USART peripheral can also be calculated through LL RCC macro */
/* Example:
Periphclk = LL_RCC_GetUSARTClockFreq(Instance); or
LL_RCC_GetUARTClockFreq(Instance); depending on USART/UART instance
In this example, Peripheral Clock is expected to be equal to
100000000/APB_Div Hz => equal to SystemCoreClock/APB_Div
*/
LL_USART_SetBaudRate(USARTx_INSTANCE, SystemCoreClock/APB_Div, LL_USART_OVERSAMPLING_16, 115200);
/* (5) Enable USART *********************************************************/
LL_USART_Enable(USARTx_INSTANCE);
}
The USART IT Handler should look like
void USARTx_IRQHandler(void)
{
/* Check RXNE flag value in SR register */
if(LL_USART_IsActiveFlag_RXNE(USARTx_INSTANCE) && LL_USART_IsEnabledIT_RXNE(USARTx_INSTANCE))
{
/* RXNE flag will be cleared by reading of DR register (done in call) */
/* Call function in charge of handling Character reception */
USART_CharReception_Callback();
}
else
{
/* Call Error function */
Error_Callback();
}
}
The last thing to set up is the Callback
void USART_CharReception_Callback(void);
Where you could put the bytes into an buffer and handle it in the main loop or where you want.
Generally, you only need to handle the interrupt flags which you have specifically enabled with USART_ITConfig().
However, if you enable the RXNE interrupt (USART_ITConfig(USARTx, USART_IT_RXNE)) then this also enables the Overrun interrupt! So you must handle both of those.

The USART flags can be confusing. There are separate status flags and interrupt flags and they share similar names. For example: USART_IT_RXNE and USART_FLAG_RXNE.
In addition, there are various methods to clear these flags. For example, the USART_ClearITPendingBit() function only works for four (of the ten) possible flags.
Here is a summary of the interrupt flags and how to use them. These are specific for the STM32F105, but are representative:
USART_IT_TXE - "Transmit Data register empty"
- It is cleared automatically when calling
USART_SendData()
USART_IT_RXNE - "Receive Data register not empty"
It is cleared automatically when calling
USART_ReceiveData(USARTx)It can be cleared manually by calling
USART_ClearITPendingBit(USARTx, USART_IT_RXNE)
USART_IT_TC - "Transmission complete"
It is cleared automatically by:
USART_GetITStatus(USARTx, USART_IT_TC)followed byUSART_SendData()
It can be also be cleared manually by calling
USART_ClearITPendingBit(USARTx, USART_IT_TC)
USART_IT_CTS - "CTS change"
- Cleared by calling
USART_ClearITPendingBit(USARTx, USART_IT_CTS)
USART_IT_LBD - "LIN Break detected"
- Cleared by calling
USART_ClearITPendingBit(USARTx, USART_IT_LBD)
USART_IT_PE - "Parity error"
- Cleared by:
USART_GetITStatus(USARTx, USART_IT_PE)followed byUSART_ReceiveData(USARTx)
USART_IT_NE - "Noise error"
- Cleared by:
USART_GetITStatus(USARTx, USART_IT_NE)followed byUSART_ReceiveData(USARTx)
USART_IT_ORE - "Overrun error"
- Cleared by:
USART_GetITStatus(USARTx, USART_IT_ORE)followed byUSART_ReceiveData(USARTx)()
USART_IT_IDLE - "Idle line detected"
- Cleared by:
USART_GetITStatus(USARTx, USART_IT_IDLE)followed byUSART_ReceiveData(USARTx)()
Just want to add some my experience on this problem, I follow the instructions:
USART_IT_ORE - "Overrun error"
Cleared by: USART_GetITStatus(USARTx, USART_IT_ORE) followed by USART_ReceiveData(USARTx)()
Is seems not work, and the following command work for me instead:
USART_GetFlagStatus(USARTx, USART_IT_ORE) followed by USART_ReceiveData(USARTx)
If you look into the functions:
USART_GetFlagStatus() and USART_ReceiveData()
You will find what exactly Bitsmack wrote before... "First read the USARTx_SR register, then read the USARTx_DR register."
Hopefully it work for you and save lot more time on this issue.=)
I will need to handle around 6-8 UARTs in my project. What I'm wondering is, what the best way is to handle them? I've used the callback function with if-statements for 2 UARTs before, but this seems not ideal for a larger number. Any suggestions?
Such a question is difficult to answer without knowing which specific processor you are using, which board you are using, and/or which compiler you are using. But in an attempt to be helpful, here's my code.
Here's my GPIO and NVIC initialization code using Sourcery CodeBench Lite with an STM32F4 processor mounted on a custom board.
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// Enable the USART RX Interrupt
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
Of course your settings will vary depending on your processor, board and interrupt priority.
Here's my interrupt handler code. In my development environment, this handler is declared in my startup assembly file as a weak reference to Default_Handler...
Default_Handler:
b .
/* ... */
.word USART3_IRQHandler
/* ... */
.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler
... so as long as I provide a new declaration and implementation of this interrupt handler, the weak reference will be replaced. Here's what my code looks like.
//Interrupt handler declaration
void USART3_IRQHandler();
If you are using C++ you will need to declare it as follows:
//Interrupt handler declaration in C/C++
#ifdef __cplusplus
extern "C" {
#endif
void USART3_IRQHandler();
#ifdef __cplusplus
}
#endif
And here's the interrupt handler implemenation.
//Interrupt handler implementation
void USART3_IRQHandler()
{
//handle interrupt
}
Here's short and simple code to configure STM32 USART (USART3) and Interrupt Handler.
Configure and Init
void Init_USART3()
{
// PB.11 = RX Floating Input
// PB.10 = TX Alternate function output Push-pull 50 MHz
RCC->APB2ENR = RCC->APB2ENR | (RCC_APB2ENR_IOPBEN);
RCC->APB1ENR |= RCC_APB1ENR_USART3EN; // enable clock for USART3.
GPIOB->CRH = GPIOB->CRH & 0xFFFF00FF;
GPIOB->CRH = GPIOB->CRH | 0x00004B00;
USART3->BRR =72000000/9600; // set baudrate.
USART3->CR1 |= (USART_CR1_RE | USART_CR1_TE); // RX, TX enable.
USART3->CR1 |= USART_CR1_UE; // USART3 enable.
USART3->CR1 |= USART_CR1_RXNEIE; // UART3 Receive Interrupt Enable.
// Enable interrupt fromUSART1(NVIC level)
NVIC_EnableIRQ(USART3_IRQn);
}
Handle Receive Interrupt
void USART3_IRQHandler()
{
if(USART3->SR & USART_SR_RXNE)
{
// Do Something
}
}