Skip to main content
Associate
July 16, 2026
Question

STM32C542 Development (3) — UART Configuration and printf Redirection

  • July 16, 2026
  • 1 reply
  • 49 views

Overview

In traditional STM32 development, we typically use STM32CubeMX to configure the USART peripheral and redirect printf() to the serial port for debugging output. With the STM32C5 series, ST introduces the new STM32CubeMX2 configuration tool and the HAL2 driver framework. Compared with the legacy HAL, the project structure and some API names have changed, so the UART configuration process and application code require slight modifications accordingly.

 

Hardware Preparation

First, prepare a development board. In this tutorial, I use a custom-designed development board. If you are interested in obtaining one, you can apply for a sample.

The main controller is the STM32C542CCT6.

 

 

Reference Project

The reference project is available on GitHub:

https://github.com/CoreMaker-lab/STM32C542_SENSOR

 

Creating a Project with STM32CubeMX2

Use STM32CubeMX2 to generate the example project. The MCU used in this tutorial is the STM32C542CCT6.

  1. Open STM32CubeMX2 and go to the Home page.

  2. Click MCU to create a new project based on a specific device.

 

 

Enter STM32C542CCT6 in the MCU name field. Select the corresponding STM32C5 device from the list, then click Continue to proceed to the project configuration page.

 

After entering the project name and selecting the project directory, click Automatically Download, Install & Create Project. STM32CubeMX2 will automatically download the required software packages and generate the project.

 

 

After STM32CubeMX2 displays the Project Successfully Created message, click Launch Project in the lower-right corner to open the project configuration interface.

Clock Configuration

  1. In the left-side panel, click Peripherals to open the peripheral configuration page.

  2. Under the System category, select RCC to configure the system clock source.

  3. Set HSE Source to Crystal/Ceramic Resonator to enable the external high-speed oscillator (HSE).

  4. Set LSE Source to Crystal/Ceramic Resonator to enable the external low-speed oscillator (LSE).

 

  1. Click the Clock icon in the left-side panel to open the Clock Configuration page.

  2. Set HSE OSC to 24 MHz to match the external high-speed crystal frequency.

  3. Configure PSI Mux / PLL to select the PLL clock source and generate the desired system clock frequency.

  4. Set System Mux to use the PLL as the system clock source. In this example, the system clock is configured to 144 MHz.

 

 

 

DEBUG Configuration

In the Peripherals panel, navigate to Cortex → DEBUG, then set Mode to Single-wire trace asynchronous. This enables program downloading, on-chip debugging, and Trace debugging in subsequent development.

 

 

 

UART Configuration

Refer to the schematic and note that PA9 and PA10 are connected to the UART interface on the development board.

 

 

 

 

  1. In the Peripherals panel, navigate to Connectivity → USART1.

  2. Set Mode to Async to configure USART1 for asynchronous communication.

  3. Verify that Function used by the component is displayed as UART, indicating that USART1 uses the UART HAL driver in asynchronous mode.

  4. Configure the UART parameters as follows:

    • Baud Rate: 115200

    • Word Length: 8 Bits

    • Parity: None

    • Stop Bits: 1

    • Mode: TX and RX

 

  1. For GPIO Tx, assign USART1_TX to PA9.

  2. For GPIO Rx, assign USART1_RX to PA10.

  3. Configure both PA9 and PA10 in Alternate Function mode.

  4. Set Pull to No pull-up and no pull-down, Output type to Push-pull, and Speed to Low.

 

Generate the Project

  1. After modifying the configuration, a Click to save prompt appears in the lower-left corner. Save the current project configuration first.

  2. Click Project settings in the left-side panel to open the project generation settings.

  3. Under IDE Project Generation, select the desired project format and toolchain. In this example, choose CMake + GCC, then click Generate IDE project to generate the project.

 

Import the Project into STM32CubeIDE

  1. Open STM32CubeIDE and click File from the menu bar.

  2. Select Import... to import the CMake project generated by STM32CubeMX2.

 

 

 

  1. In the Import dialog, expand Import STM32 Project.

  2. Select STM32 CMake Project.

  3. Click Next to proceed to the CMake project location selection page.

 

 

 

  1. Project name: Enter the name that will be displayed for the project in STM32CubeIDE.

  2. Source directory: Select the CMake project directory generated by STM32CubeMX2.

  3. Click Next to continue the import process.

 

  1. Toolchain: Select MCU ARM GCC to use the ARM GCC toolchain for building the project.

  2. MCU: Verify that the selected device is STM32C542CCTx, matching the MCU configured in STM32CubeMX2.

  3. CPU/Core: Confirm that the CPU is Cortex-M33 and the Core is Core 0.

  4. Click Finish to complete the import of the CMake project.

 

 

 

Set the Project Encoding

  1. In the Project Explorer, select the current project.

  2. Click Project from the menu bar.

  3. Select Properties to open the project properties dialog.

 

 

 

 

  1. In the Project Properties dialog, select Resource.

  2. Under Text file encoding, choose Other.

  3. Set the encoding to GBK.

  4. Click Apply and Close to save the settings.

 

 

Add Header Files

Add the required header files to main.c.

 

#include "mx_usart1.h"
#include <stdio.h>
#include <string.h>

 

 

printf() Redirection

To redirect the output of printf() to USART1, you need to override the _write() function.

In GCC-based projects, printf() ultimately calls _write() to output characters. Therefore, by calling HAL_UART_Transmit() inside the _write() function, all data printed by printf() can be transmitted through the UART.

 

 

int _write(int file, char *ptr, int len)
{
hal_uart_handle_t *huart1 = mx_usart1_uart_gethandle();

if (huart1 != NULL)
{
HAL_UART_Transmit(huart1, ptr, len, 1000);
}

return len;
}

 

 

UART Print Test

After mx_system_init() has completed, you can directly call printf() to verify that UART output is working correctly.

 

	hal_uart_handle_t *huart1 = mx_usart1_uart_gethandle();

printf("Hello STM32C5 UART printf\r\n");
printf("STM32C5 串口打印测试\r\n");
char msg[] = "Hello STM32C5 UART\r\n";

HAL_UART_Transmit(huart1, msg, strlen(msg), 1000);

 

Demonstration

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1 reply

Andrew Neil
Super User
July 16, 2026

Before adding the complications of printf, I would still recommend that you just get a simple, direct HAL_UART_Transmit working first.

Use this to prove basic connectivity first, then move on to adding printf.

 

UART Configuration

Refer to the schematic and note that PA9 and PA10 are connected to the UART interface on the development board.

This, of course, will depend on the board being used.

Note that the ST-Link VCP on the NUCLEO-C542RC and NUCLEO-C562RE use PA2 and PA3:

https://www.st.com/resource/en/user_manual/um3615-stm32c5-nucleo64-board-mb2213-stmicroelectronics.pdf#page=27

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.
Hank XiaoAuthor
Associate
July 17, 2026

Thank you for your suggestion.

This example is based on my custom STM32C542CCT6 development board. On this board, USART1 on PA9/PA10 is connected to the onboard CH340 USB-to-UART interface.

I agree that testing HAL_UART_Transmit() directly before redirecting printf() is a good way to verify the basic UART configuration and hardware connection.

For the NUCLEO-C542RC and NUCLEO-C562RE boards, the onboard ST-Link VCP uses USART2 on PA2/PA3, as you mentioned.

I also plan to write a dedicated tutorial about UART configuration and printf redirection, and publish the example project on GitHub later.
https://github.com/CoreMaker-lab/STM32C542_SENSOR

Andrew Neil
Super User
July 17, 2026

This example is based on my custom STM32C542CCT6 development board.

But you’re the only one who has that - so it’s not generally useful.

Would be more useful to a wider audience to use a widely-known and readily-available board like the Nucleo

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.