🧩 1. Why Use UART Interrupts?
In the previous article, we used:
HAL_UART_Receive(&huart2, &byte, 1, HAL_MAX_DELAY);
The problem is:
-
Waiting for data → The program gets stuck
-
The MCU cannot perform other tasks (like controlling LEDs/sensors)
-
It can only “wait idly”, which is very inefficient
This is like:
You sit at the door waiting for a delivery, doing nothing, just staring at the door.
Very wasteful.
Interrupt mode is much smarter:
You do your thing in the living room, and when the delivery arrives, the doorbell “dings” to notify you.
The program continues doing its tasks, a byte arrives at the serial port → it notifies you to handle it → then continues with other tasks.
This is UART interrupt.
🟦 2. The Interrupt Mode is an “Automatic Notification” Mechanism
We tell the STM32:
“As soon as you receive a byte, please notify me (just once).”
When the STM32 receives data:
-
It automatically triggers an “event”
-
It runs to your “interrupt handler function”
-
It places the received byte in the variable you set up
You only need to write:
-
The variable you want to receive
-
What you want to do after receiving
-
Continue to set up for the next reception
The interrupt mechanism is that simple.
🟦 3. Three Steps of Interrupt Mode
✔ Step One: Prepare a “Variable to Receive Bytes”
uint8_t rx_byte;
✔ Step Two: Start Interrupt Reception in main (only once)
HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
This means:
“Come 1 byte, notify me, and place it in rx_byte.”
✔ Step Three: Implement the Callback Function (Automatically executed upon receiving data)
The HAL framework will automatically call when a byte is received:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
We write it like this:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2) // Ensure it is USART2
{
// Echo: send back the received byte as is
HAL_UART_Transmit(&huart2, &rx_byte, 1, 10);
// Restart interrupt reception (must be done)
HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
}
}
Just these three steps, and the serial port interrupt reception is ready to go.
🟦 4. The “Echo Experiment” You Can Verify Immediately
On the PC side, using a serial assistant → input a letter, for example, <span>A</span> STM32 will automatically receive it → automatically call the callback → automatically send back <span>A</span> → the serial assistant displays <span>A</span>
You will see:
-
Whatever you input, STM32 echoes back
-
Completely non-blocking to the main loop
-
The MCU can continue doing other tasks
This is the most intuitive effect of interrupts working.
🟦 5. Why Must You Reattach HAL_UART_Receive_IT?
This is the point that confuses many beginners.
Many people write:
HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
Placed in main and written only once → can only receive once, and then it will never come again.
The reason is very simple:
HAL’s interrupt reception is a “one-time task” After receiving the specified number of bytes (1), it considers the task complete and will not automatically receive again.
So you must reset it in the callback:
HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
It’s like commanding a delivery platform:
“After delivering this order, please continue to notify me for the next one.”
Otherwise, you won’t be notified for the next order.
🟦 6. What Can Interrupt Reception Do?
Common uses:
-
Single-byte commands (e.g., control an LED)
-
Serial command line (CLI/Shell)
-
Keyboard input echo
-
Interaction between the host computer and STM32
-
Small commands to control robots/peripheral devices
However, interrupt mode can only “receive one byte at a time”, suitable for low-speed commands.
If you want to:
-
Receive a large amount of data
-
Variable length
-
High speed
-
Have protocol frames (header, length, CRC)
Then you must upgrade to an engineering-level solution:
DMA + IDLE (next article)
🟦 7. Common Troubleshooting
❗ 1. “I only receive one interrupt”
Reason: You did not execute again in the callback:
HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
❗ 2. “The received character is garbled”
Reason:
-
The baud rate of the PC does not match that of STM32
-
Incorrect clock configuration in CubeMX
Ensure the PC serial assistant is set to:
115200 8N1
❗ 3. “The interrupt function does not trigger at all”
Check the order:
-
Is TX/RX reversed?
-
Is GND connected?
-
Is USART2 RX checked in CubeMX?
-
Is the NVIC interrupt enabled (automatically handled by CubeMX)?
-
Is the USB-TTL 3.3V?
-
Is STM32 really running (test with an LED)?
🟩 8. Complete Minimal Project Template
#include "main.h"
#include <string.h>
uint8_t rx_byte;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
// Start the first interrupt reception
HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
while (1)
{
// Main loop doing its own thing
HAL_Delay(10);
}
}
// Interrupt callback function: will automatically execute upon receiving a byte
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
HAL_UART_Transmit(&huart2, &rx_byte, 1, 10);
// Restart reception
HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
}
}
Next Section Preview:Register Special Edition (In-depth on Serial Interrupts)
👉 “Article 2.5: How UART Interrupts Work (TDR / RDR / Status Bits – Super Simple Model)”
In this way, you will see: The interrupt behavior learned earlier is actually implemented using a few registers.