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
So the core steps are:
- Initialize UART
- Retarget printf() to UART
- 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;
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;
}
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);
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
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);
7.2 puts() (very small footprint)
puts("System started\r\n");
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");
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)