Skip to main content
Associate III
Sep 22, 2025 6:21 AM UTC
Question

printf and puts function not working in release build firmware

Hi everyone,

I’m working on an STM32 firmware project (HAL + CubeIDE). I’m trying to output debug messages to Tera Term via UART using printf and puts.

Here’s the situation:

  • Debug build: Both printf and puts work perfectly.

  • Release build: Calling printf or puts causes the MCU to reset immediately. Maybe due to watchdog reset and these function call cause the MCU freezing.

I also retarget the printf function by implemented the following: 

int __io_putchar(int ch) {
 while (!(huart1.Instance->ISR & USART_ISR_TXE));
 huart1.Instance->TDR = (uint16_t)ch;
 return ch;
}

int _write(int file, char *ptr, int len) {
 for (int i = 0; i < len; i++) {
 __io_putchar(ptr[i]);
 }
 return len;
}

My questions:

  1. Why do printf and puts crash/reset in Release but work in Debug?

  2. Are there known issues with _write or __io_putchar in Release builds?

  3. Any recommended approach to safely use printf/puts in Release firmware for UART output?

Thanks in advance!

 

This topic has been closed for replies.

12 replies

Ozone
Principal
Sep 22, 2025 6:59 AM UTC

Compare the build settings for debug and release.
Normally you have semihosting enabled for debug, which includes additional code and these outputs are routed via JTAG/SWD or an UART line.

Relase build setting do usually not.
This is why you have debug and release builds in the first place - you don't do debug printf()s in release versions.


I'm not using CubeIDE, so I don't know why it crashes.
With my toolchain, the  _write and __io_putchar calls are empty functions in the release version, and thus printf()s in the release build amount to nothing.

chai2145Author
Associate III
Sep 22, 2025 8:25 AM UTC

The thing is my application support CLI function, accept commands from console like tera term and response accordingly.

Ozone
Principal
Sep 22, 2025 8:47 AM UTC

Then don't use the semihosting interface for that, but a separate UART.

Or extend the release build settings accordingly.

Andrew Neil
Super User
Sep 22, 2025 12:45 PM UTC

You can still run the Release version in the debugger to see what's happening.

 


@chai2145 wrote:
  • Maybe due to watchdog reset and these function call cause the MCU freezing


Maybe instrument your code so that you can see if that actually is the case ...

 

As @Ozone said, check what's different between the builds - Optimisation level is another common one.

When increasing the optimisation level breaks your code, that usually indicates flaws in the code.

eg, failure to use 'volatile' where required...

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
chai2145Author
Associate III
Sep 23, 2025 3:07 AM UTC

Hi Andrew, 

 

I'd tried to debug the code, found that it actually stuck at while loop. 

while (!(huart1.Instance->ISR & USART_ISR_TXE)); 

and cause the watchdog reset.

int __io_putchar(int ch) {
	while (!(huart1.Instance->ISR & USART_ISR_TXE));
	huart1.Instance->TDR = (uint16_t) ch;
	return ch;
}

 

Ozone
Principal
Sep 23, 2025 7:01 AM UTC

To wich pins is UART1 routed, and what are these pins connected to externally ?

Is UART1 initialized anywhere in your release code ?
I suppose not.