Article 2: UART Interrupt Reception

🧩 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:

  1. Is TX/RX reversed?

  2. Is GND connected?

  3. Is USART2 RX checked in CubeMX?

  4. Is the NVIC interrupt enabled (automatically handled by CubeMX)?

  5. Is the USB-TTL 3.3V?

  6. 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.

Leave a Comment