How to redirect the printf function to a UART for debug messages
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Email to a Friend
- Printer Friendly Page
- Report Inappropriate Content
2021-11-16 2:08 AM - edited 2024-09-23 9:28 AM
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 ST-LINKs 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 ST-LINK pins used for the virtual COM port.
In this article I will show you how to redirect the printf output to the STM32 UART peripheral that is connected to the UART pins on the embedded ST-Link that will be transmitted to the host computer and displayed via a windows terminal program, Tera Term.
1. Prerequisites
Hardware
- Micro USB cable: in order to power and program the board
- Nucleo-G070RB

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 will be redirected to use PA2 and PA3. We will 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
- Open STM32CubeIDE
- Create a new project using the NUCLEO-G070RB board
- Give a name to the project
- For this example, the project will be named, “Printf”.
- Initialize all peripherals with their default settings:
- To do this, click on “Yes”.
- Make sure 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 USART2 is activated as shown here:
- It should be configured in Asynchronous mode with the following settings as shown below in the red rectangles:
- Make sure the USART2 alternate functions have been mapped to PA2 and P3 which are connected to the TX and RX pins (respectively) of the ST-LINK on the Nucleo board.
- Generate Code
- Saving the project will generate the code.
- Add code for printf:
- This code will redirect the output of the printf function to the USART2. 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 */
- Build the project, enter debug mode and run the code
- First enter Debug session:
- Then Execute the code:
- Now open a Terminal application like TeraTerm
- Configure the serial port connection to the ST-LINK Virtual COM port.
- Select the following settings:
- You will now see the printf message, “Hello World”, being displayed every second.
4. Related links
STM32G0 - Datasheet
STM32G0x0 advanced Arm®-based 32-bit MCUs - Reference manual
STM32CubeIDE - Integrated Development Environment for STM32 - STMicroelectronics
NUCLEO-G070RB - STM32 Nucleo-64 development board with STM32G070RB MCU, supports Arduino and ST morpho connectivity - STMicroelectronics
Tera Term
5. Notes
- 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
}
For the main function -
int main(){
setbuf(stdin,NULL); //TO HANDLE INPUT BUFFER WHEN USING SCANF/COUT
printf("\rHello World\n\r");
while(1);
}
- Line-buffering:
The default (well, GCC's default) is that stdout is line-buffered; i.e., the output is not sent until a complete line has been formed.
This has the effect that output doesn't actually appear until a newline is sent; e.g.,
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"; e.g.,
fprintf( stderr, "Hello, " ); // output *does* appear here ...
fprintf( stderr, "World!" ); // ... and here ...
fprintf( stderr, "\n" ); // and here!
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
I'm not getting the expected result from this article.
I don't see anything printed on the terminal. To test the virtual com port, I use the code:
/* USER CODE BEGIN WHILE */
char data[] = {"Hey Universe\n\r"};
while (1)
{
//printf("Hello World\n\r");
HAL_Delay(1000);
HAL_UART_Transmit(&huart2, (uint8_t*)&data, 20, 20);
/* USER CODE END WHILE */
This works fine. But if I uncomment the printf-statement, then nothing gets printed to the terminal !
That is, the printf-statement seems to mess up the code.
BTW, I'm using "RealTerm", with 115200 baudrate and 7-bit data-width.
Any ideas ?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
Yes, i also have the same problem. I'm also looking for answers.
Use printf will print nothing.
#include "main.h"
#include "stdio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t buffer[10] = {'0','1','2','3','4','5','6','7','8','9'};
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* USER CODE BEGIN PFP */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
/* USER CODE END PFP */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/*HAL_UART_Transmit(&huart2,buffer,10,100);*/
printf("Hello World\n\r");
HAL_Delay(1000);
//printf
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
/**
* @brief Retargets the C library printf function to the USART.
* @param 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 */
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
Thanks Laura, that's very useful for someone like me who's used to this type of debugging with Arduino.
Although, I think it could be helpful to indicate that this work with any Nucleo board, and that USART1 can be used, you just need to provide the proper handler in the PUTCHAR_PROTOTYPE. A newbie like me could be discouraged for not having the exact same board or not USART2 as in the example.
But one thing is weird : in the Pinout & Config section regarding USART1's Parameter Settings, I can only specify Word Length of 8 or 9 bits.
I tried with 8 bits and it worked.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
Can I configure the USART in an already created/existing project and generate code as said in this article? If so how to do in STM32F4 Discovery board?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
Hi,
The ST-LINK in STM32F4 Discovery supports only SWD and not Virtual COM port.
BR
Nico.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
But in the product link as given below
https://www.st.com/en/evaluation-tools/stm32f4discovery.html
It is showing as Virtual COM port is supported..
Please look that and confirm.
And what is SWD. Please explain.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
OK I see, there are 2 models for the STM32F4 Disco, if you are sure yours support virtual com port then yes you can use it. check what UART and I/Os form the STM32F4 are connected to RX and TX of the ST-LINK and use those when configuring your project.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
please refer to the user manual and schematics to see what IOs are connected the ST-LINK on your discovery kit.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
I have followed the wiring connection recommended in the user manual of stm32f4 discovery board. I am getting junk characters in the terminal. What are the possible reasons for that. In the .ioc file for GPIO settings I have used "No Pull Up and No Pull down" option. will that be a problem?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
I am able to print the desired characters now, but with the following bit-wise manipulation. Before passing the character to the function
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
I had to do this
~(ch<<1);
and use the resulted value to pass to the function above.
Arrived at this solution when I studied the pattern of difference between desired character and the character that was printed in the console.
But don't know the reason for this. Can anybody say what is the reason? I am using STM32F4 Discover board.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
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.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
Is there any remaining issue with this article that needs my attention?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
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.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
Correct
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
can I use usb to ttl converter having builtin PC-PL2303HX Chip. will there be any problems?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
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.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
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.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
Hello @Community member both solution should actually work.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
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;
}
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
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.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
A terminal application is not necessary, it can be done inside STM32CubeIDE:
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
Awesome!
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
Before trying to get printf working, be sure that the basic UART transmission is working - see:
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Email to a Friend
- Report Inappropriate Content
it worked perfectly on the Nucle-f411re.
Now the STM32 world opens up to great challenges.