DEV Community

Hedy
Hedy

Posted on

How to Use printf() on STM32 via Serial (UART)?

Below is a practical, step-by-step guide to using printf() on an STM32 via a serial (UART) wire, written for real projects (CubeIDE / HAL style).
This is the most common and reliable debugging method on STM32.

1. Theory: How printf() Works on STM32

printf() does not magically know where to print.
On STM32, you must redirect the standard output (stdout) to a UART peripheral.

Data flow

printf()
  ↓
_putchar() / _write()
  ↓
UART TX register
  ↓
USB-UART adapter
  ↓
PC serial terminal
Enter fullscreen mode Exit fullscreen mode

So the core steps are:

  1. Initialize UART
  2. Retarget printf() to UART
  3. Connect UART TX to a PC

2. Hardware Setup (Serial Wire)
2.1 Required Hardware

  • STM32 board (e.g. Nucleo, Black Pill, Blue Pill)
  • USB-to-UART adapter (3.3 V logic!)
  • PC with serial terminal

2.2 Typical Wiring

USB-UART STM32
TX RX (e.g. PA10 for USART1)
RX TX (e.g. PA9 for USART1)
GND GND

Do NOT connect 5 V to STM32 UART pins

3. Configure UART (STM32CubeIDE / HAL)
Example: USART1 @ 115200 baud

  • Mode: Asynchronous
  • Word length: 8 bits
  • Parity: None
  • Stop bits: 1
  • Flow control: None

CubeIDE will generate:

UART_HandleTypeDef huart1;
Enter fullscreen mode Exit fullscreen mode

4. Retarget printf() to UART (HAL)
4.1 Add this code (GCC / CubeIDE)

In main.c or a separate retarget.c:

#include <stdio.h>
#include "stm32f4xx_hal.h"

extern UART_HandleTypeDef huart1;

int _write(int file, char *ptr, int len)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
    return len;
}
Enter fullscreen mode Exit fullscreen mode

This redirects all printf() output to USART1.

4.2 Use printf() normally

printf("Hello STM32!\r\n");
printf("ADC value = %d\r\n", adc_value);
printf("Voltage = %.2f V\r\n", voltage);
Enter fullscreen mode Exit fullscreen mode

5. PC Side: Serial Terminal

Use any terminal:

  • PuTTY
  • Tera Term
  • Arduino Serial Monitor
  • minicom (Linux)

Settings

  • Baud rate: 115200
  • Data bits: 8
  • Parity: None
  • Stop bits: 1
  • Line ending: CR+LF recommended

6. Common Problems & Fixes
Nothing prints

  • TX/RX swapped
  • Wrong UART instance
  • Baud rate mismatch
  • Forgot \r\n

Output is garbled

  • Wrong baud rate
  • Clock configuration incorrect
  • USB-UART is 5 V logic

printf() causes huge flash usage

printf() pulls in heavy formatting code.

Fix

  • Enable newlib-nano
  • Avoid floats if possible

CubeIDE:

Project → Properties → C/C++ Build → Settings
→ MCU Settings → Use newlib-nano
Enter fullscreen mode Exit fullscreen mode

7. Lightweight Alternatives (Recommended for Production)
7.1 sprintf() + HAL_UART_Transmit()

char buf[64];
sprintf(buf, "Temp=%d\r\n", temp);
HAL_UART_Transmit(&huart1, (uint8_t*)buf, strlen(buf), 100);
Enter fullscreen mode Exit fullscreen mode

7.2 puts() (very small footprint)

puts("System started\r\n");
Enter fullscreen mode Exit fullscreen mode

8. Performance Tips (Important!)

  • printf() is blocking
  • Avoid calling it in:
    • Interrupts
    • High-frequency loops
  • Use DMA for high-speed logging
  • Throttle output frequency

9. Example: STM32 Black Pill (STM32F401/F411)

UART

  • USART1
  • TX = PA9
  • RX = PA10

Code

printf("Black Pill ready\r\n");
Enter fullscreen mode Exit fullscreen mode

Terminal

  • 115200 baud
  • CRLF

Key Engineering Takeaway

On STM32, printf() is a debugging tool, not a logging system.
Use it wisely, retarget it properly, and never trust it inside interrupts.

Top comments (0)