Skip to main content
ST AME Support NF
ST Employee
November 16, 2021

How to redirect the printf function to a UART for debug messages

  • November 16, 2021
  • 27 replies
  • 140597 views

Introduction

It can be especially useful during code development to display messages in a terminal window about various system parameters to help with debugging. An easy way to do that is to use the printf function and redirect the output to a UART for display in a terminal window.

The STLINK embedded on ST Nucleo boards have a virtual COM port feature, and we can easily get debugging information on a terminal using printf redirected to the UART of the STM32 connected to the STLINK pins used for the virtual COM port.
In this article, I show you how to redirect the printf output to the STM32 UART peripheral that connects to the UART pins on the embedded STLINK. It transmits to the host computer and displays via a Windows terminal program, Tera Term.
 

1. Prerequisites

Hardware

  • Micro USB cable: in order to power and program the board
  • NUCLEO-G070RB

1891.png
 
Software

2. Theory

On the NUCLEO-G070RB board, the embedded ST-LINK/V2 is connected to PA2 (UART TX) and PA3 (UART RX) of the target STM32G070 device. 

These port pins have USART2 alternate functions. Printf is redirected to use PA2 and PA3. We then use a terminal connected to the virtual COM port of the ST-LINK of the Nucleo board to display the printf messages.
 

3. Steps

  1. Open STM32CubeIDE
  2. Create a new project using the NUCLEO-G070RB board


3. steps 1.png
 

  1. Give a name to the project
  • For this example, the project name is “Printf”.

3. steps 2.png
 

  1. Initialize all peripherals with their default settings:
  • To do this, click on [Yes].

3. steps 3.png
 

  1. Make sure that USART2 has been selected, configured, and mapped to PA2 and PA3 as shown below:
  • By default, this was configured when starting the project from the Nucleo board that was selected.
  • First make sure that USART2 is activated as shown here:


3. steps 4.png

 

  • It should be configured in Asynchronous mode with the following settings as shown below in the red rectangles:

 

3. steps 5.png

 

  • Make sure the USART2 alternate functions have been mapped to PA2 and P3 that are connected to the TX and RX pins (respectively) of the ST-LINK on the Nucleo board.

 

3. steps 6.png

  1. Generate the code
  • Saving the project generates the code.
  1. Add code for printf:
  • This code redirects the output of the printf function to the USART2. The printf function is calling fputc to transmit the output via the USART.
  • In main.c, add the following code:
/* USER CODE BEGIN PFP */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
/* USER CODE END PFP */
…
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 printf("Hello World\n\r");
 HAL_Delay(1000);
 /* USER CODE END WHILE */

…
/* USER CODE BEGIN 4 */
/**
 * @brief Retargets the C library printf function to the USART.
 * None
 * @retval None
 */
PUTCHAR_PROTOTYPE
{
 /* Place your implementation of fputc here */
 /* e.g. write a character to the USART1 and Loop until the end of transmission */
 HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);

 return ch;
}

/* USER CODE END 4 */
  1. Build the project, enter debug mode, and run the code.

3. steps 7.png

To execute the code:

  • Enter the debug session.

3. steps 8.png

 

  • Execute the code.

 

3. steps 9.png

 

  • Open a terminal application like Tera Term.
  • Configure the serial port connection to the STLINK virtual COM port.

 

3. steps 10.png

 

  • Select the following settings.

 

1902.png

 

  • You now see the printf message, “Hello World”, being displayed every second.

 

1903.png

 

4. Related links

5. Notes

5.1 Tips when using C++

We need to update the _write and _read functions,

namespace std{
#ifdef __cplusplus
extern "C"{
#endif
int _write(int fd, char *ptr, int len){
 (void)fd;
 int i;
 for(i=0;i<len;i++){
 uart_write(*ptr++);
 }
 return len;
}

size_t _read(int fd, char *ptr, size_t len){
 (void)fd;
 size_t i;
 for(i=0;i<len;i++){
 *ptr++ = uart_read();
 uart_write(*ptr++); //For Terminal Echo
 }
 return i;
}

#ifdef __cpluscplus
}
#endif
}

And the main function.

int main(){
 setbuf(stdin,NULL); //TO HANDLE INPUT BUFFER WHEN USING SCANF/COUT
 printf("\rHello World\n\r");
 while(1);
}

5.2. Line-buffering

The default (well, GCC's default) is that stdout is line-buffered, that is, the output is not sent until a complete line has been formed.

This affects that the output does not actually appear until a new line is sent, for example:

printf( "Hello, " ); // No output here ...
printf( "World!" ); // ... still no output ...
printf( "\n" ); // Now the output appears!

This is a common cause of confusion, as many people expect that the output should appear immediately.

It is also worth noting that, by default, stderr is not line-buffered - so output does appear "immediately," for example:

fprintf( stderr, "Hello, " ); // output *does* appear here ...
fprintf( stderr, "World!" ); // ... and here ...
fprintf( stderr, "\n" ); // and here!
 

27 replies

ST AME Support NF
ST Employee
October 28, 2022

Glad to see you are making progress with your printf redirection to UART. Personally I have not seen this issue on my side. May be someone from the community have seen this issue and can help. Glad to see this is working now.

ST AME Support NF
ST Employee
October 28, 2022

Is there any remaining issue with this article that needs my attention?

AMuth.3
Associate III
November 22, 2022

Hi @ST AME Support NF​ 

I am using USB to serial DB9 converter to connect my Stm32f4 discovery board with laptop's minicom console. Is it true that I have to use only USB to TTL serial, and not RS232 converter? Please confirm and if so how to select that correct adapter? Thanks.

ST AME Support NF
ST Employee
November 22, 2022

Correct

AMuth.3
Associate III
November 24, 2022

can I use usb to ttl converter having builtin PC-PL2303HX Chip. will there be any problems?

ST AME Support NF
ST Employee
November 29, 2022

Hello, I reviewed the spec and looks like VDD is 5V for this model. This is not going to work or is not adapted to the STM32 board where VDD is 3.3V instead. Try to find a version that works with VDD=3.3V.

Associate III
May 23, 2023

Shouldn't line endings on Windows be "\r\n" instead of "\n\r"? Sure, TeraTerm may be rendering either sequence correctly, but there are no operating systems I know of (not Linux, not Mac) that use "\n\r" for line termination.

ST AME Support NF
ST Employee
May 26, 2023

Hello @Community member​ both solution should actually work.

Visitor II
September 28, 2023

On some systems it is not enough to overwrite __io_putchar, if the syscalls.c file is missing or not implemented.

In this case also overwrite _write:

int _write(int file, char *ptr, int len)
{
	int DataIdx;

	for (DataIdx = 0; DataIdx < len; DataIdx++)
	{
		__io_putchar(*ptr++);
	}
	return len;
}

or more efficient (take care of the right UART):

int _write(int fd, char * ptr, int len)
{
 HAL_UART_Transmit(&huart2, (uint8_t *) ptr, len, HAL_MAX_DELAY);
 return len;
}

 

Explorer
April 17, 2024

I'm using NUCLEO-G474RE, and I followed the instructions above (using LPUART1). However, I initially got garbled characters from Tera Term. Later, after changing the word length settings for UART to 8 bits in both the project and Tera Term, I got the correct display.