摘要
本文简要介绍如何通过STM32CubeMX2开启并配置UART(universal asynchronous receiver transmitter,通用异步收发器)。本教程基于NUCLEO-C562RE开发板,覆盖以下UART功能:
polling mode(轮询模式)
中断
DMA(direct memory access,直接存储器访问)
简介
USART/UART(universal synchronous / asynchronous receiver transmitter,通用同步/异步收发器)可与外部设备进行全双工数据交互,采用业界标准NRZ(non-return-to-zero,不归零)异步串行数据格式。
USART通过可编程波特率发生器支持多种波特率;同时支持同步单向通信、半双工单线通信以及多处理器通信。
其他支持功能包括:本地互联网络(local interconnect network,LIN)、智能卡协议、红外SIR编解码、调制解调器硬件流控(CTS/RTS)。借助DMA多缓冲区配置可以实现高速数据传输,UART也可配合中断方式工作。
前置准备
-
最新版STM32C5 HAL2驱动
本教程使用硬件:NUCLEO-C562RE开发板。
1. STM32CubeMX2工程创建与基础配置
按照以下步骤创建NUCLEO-C562RE应用工程,完成一个简单USART示例程序。
++1.1 创建工程++
打开STM32CubeMX2,在Home页点击MCU新建工程。

在MCU名称搜索框输入STM32C562RE,选中芯片,点击Continue。

填写工程名称与保存路径,点击Automatically Download,Install \& Create Project完成工程创建。

点击Launch Project打开工程。

++1.2 引脚与外设配置++
在STM32CubeMX2 界面进入Peripherals外设区域,选择Connectivity连接外设,选择异步模式使能UART。

按图示配置USART2外设:

NUCLEO板载VCOM虚拟串口桥接电路使用USART2_TX、USART2_RX,对应引脚PA2、PA3,但软件默认选中引脚不一定是这一组。可以在下方滚动修改TX/RX对应的GPIO引脚。

切换到Pinout视图,添加用户LED。右键PA5,配置为GPIO引脚

再点击齿轮图标,在GPIO配置页,标签设置为USER_LED,引脚模式配置为推挽输出。

++1.3 生成代码++
生成代码操作:
- 点击Project settings工程设置图标,选择目标IDE。
- 点击Generate按钮生成工程代码。

2. 在 Visual Studio Code中配置工程
打开VS Code并打开工程文件夹。弹出配置选择框则直接选择;未弹出则按下Ctrl+Shift+P,输入CMake:Select Configure Preset,选择调试配置。

编译工程确认环境无误,之后进行代码开发。

++2.1 代码编辑++
VS Code打开生成的工程后修改main.c。该示例将C库printf、getchar重定向到UART外设,使用HALUART轮询收发接口,在串口终端工具(Tera Term或者VS Code内置终端)实现回显功能。
/** ****************************************************************************** * file :main.c * brief :Main program body * Calls target system initialization then loop in main. ****************************************************************************** * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** *//* Includes ------------------------------------------------------------------*/#include "main.h"#include <stdio.h> /* C library input/output functions */#include <string.h> /* importing memcmp and memset functions */
/* Private typedef -----------------------------------------------------------*//* Private define ------------------------------------------------------------*/#define RX_TIMEOUT 10000U#define TX_TIMEOUT 1000U#define MAX_BUFFER_SIZE 26U
/* Private macro -------------------------------------------------------------*//* Private variables ---------------------------------------------------------*/uint8_t RxBuffer[MAX_BUFFER_SIZE]; /* Reception buffer *//* Private functions prototype -----------------------------------------------*//** ----------------------------------------------------------- * Definitions of C library functions re-targeted to the UART. * ----------------------------------------------------------- */
int __io_getchar(void) { uint8_t ch = 0; HAL_UART_Receive(mx_usart2_uart_gethandle(), (uint8_t *)&ch, 1, 10000); return ch;}
int __io_putchar(int ch) { HAL_UART_Transmit(mx_usart2_uart_gethandle(), (uint8_t *)&ch, 1, 100); return ch;}
/** * brief: The application entry point. * retval: none but we specify int to comply with C99 standard */int main(void) { /** System Init: this code placed in targets folder initializes your system. * It calls the initialization (and sets the initial configuration) of the * peripherals. You can use STM32CubeMX to generate and call this code or not * in this project. It also contains the HAL initialization and the initial * clock configuration. */ if (mx_system_init() != SYSTEM_OK) { return (-1); } else { /* * You can start your application code here */ printf("USART2 echo example\r\n"); /** System Init: this generated code placed in targets folder initializes * your system. It calls the initialization (and sets the initial * configuration) of the peripherals. You can use STM32CubeMX to generate * and call this code or not in this project. It also contains the HAL * initialization and the initial clock configuration. */setvbuf(stdin, NULL, _IONBF, 0); while (1) { int idx = 0; int ch; printf("Input: "); // prompt before typing // Receive chars until '\n', '\r' or '\b' while (1) { ch = getchar(); // uses HAL_UART_Receive in polling if (ch == '\n' || ch == '\r' || ch == '\b') { break; } if (idx < (MAX_BUFFER_SIZE - 1)) { RxBuffer[idx++] = (char)ch; } }RxBuffer[idx] = '\0'; // Echo received data printf("\n Echo: %s\r\n", RxBuffer); for (idx = 0; idx < MAX_BUFFER_SIZE; idx++) { RxBuffer[idx] = 0; } } }} /* end main */
++2.2 功能验证++
编译完成后,点击Run and Debug图标,调试会话选择STM32Cube: STLINK GDB Server。

按下Ctrl+Shift+P,输入Open Serial>COMXY(STMicroelectronics)> 115200

3. UART中断方式使用
本固件示例替换getchar函数,使用硬件中断实现响应式回显,无数据处理时CPU可以保持空闲。系统初始化完成打印欢迎信息后,程序进入空死循环;通信采用事件驱动:每收到一个字符,硬件触发回调函数,将收到字符回显至终端;检测到回车键时追加换行符,最后重新开启中断接收。
在STM32CubeMX2的USART2配置页面向下找到System系统选项,或者快捷菜单点击NVIC。

重新生成工程。
提示:可以通过高级设置指定工程重新生成策略,例如备份上一版示例工程。

下面是中断示例完整main.c代码:
/** ****************************************************************************** * file :main.c * brief : Main program body * Calls target system initialization then loop in main. ****************************************************************************** * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** *//* Includes ------------------------------------------------------------------*/#include "main.h"#include <stdio.h> /* C library input/output functions */#include <string.h> /* importing memcmp and memset functions */
/* Private typedef -----------------------------------------------------------*//* Private define ------------------------------------------------------------*/#define RX_TIMEOUT 10000U#define TX_TIMEOUT 1000U#define MAX_BUFFER_SIZE 2U
/* Private macro -------------------------------------------------------------*//* Private variables ---------------------------------------------------------*/uint8_t RxBuffer[MAX_BUFFER_SIZE]; /* Reception buffer */
/* Private functions prototype -----------------------------------------------*//** ----------------------------------------------------------- * Definitions of C library functions re-targeted to the UART. * ----------------------------------------------------------- */ int __io_putchar(int ch) { HAL_UART_Transmit(mx_usart2_uart_gethandle(), (uint8_t *)&ch, 1, 100); return ch;}
void HAL_UART_RxCpltCallback(hal_uart_handle_t *huart, uint32_t size_byte,hal_uart_rx_event_types_t rx_event) { HAL_UART_Transmit(mx_usart2_uart_gethandle(), RxBuffer, 1, 10); if (RxBuffer[0] == '\r') { uint8_t newline = '\n'; HAL_UART_Transmit(mx_usart2_uart_gethandle(), &newline, 1, 10); } HAL_UART_Receive_IT(mx_usart2_uart_gethandle(), RxBuffer, 1);}
/** * brief: The application entry point. * retval: none but we specify int to comply with C99 standard */int main(void) { /** System Init: this code placed in targets folder initializes your system. * It calls the initialization (and sets the initial configuration) of the * peripherals. You can use STM32CubeMX to generate and call this code or not * in this project. It also contains the HAL initialization and the initial * clock configuration. */ if (mx_system_init() != SYSTEM_OK) { return (-1); } else { /* * You can start your application code here */ printf("USART2 RX IT echo example\r\n"); HAL_UART_Receive_IT(mx_usart2_uart_gethandle(), RxBuffer, 1); while (1) { } }} /* end main */
编译工程,完成调试配置后,按下Ctrl+Shift+P,输入Open Serial,选择COM40(STMicroelectronics),波特率115200。
此时程序正常运行,终端保持本地回显。

4. UART DMA方式使用
该示例不再使用中断,改用DMA传输预设数据;等待收到10字节数据块后触发接收回调,无数据处理时CPU保持空闲。
进入STM32CubeMX2的USART2配置页,向下找到System系统选项,或者快捷菜单点击DMA。

开启DMA发送与DMA接收,展开下拉选项完成配置。

重新生成工程,在IDE打开项目。
本示例配置STM32使用DMA异步处理UART通信,CPU可以并行处理其他任务。上电后后台立刻开启接收器等待接收数据。
主循环每10秒发送一次ASCII字符串"ABCDEFGHIJ";当收到恰好10字节数据时触发回调函数。回调内部自动重新使能接收,保证开发板可以接收下一组数据,不会打断周期性发送。
main.c代码片段:
/** ****************************************************************************** * file :main.c * brief : Main program body * Calls target system initialization then loop in main. ****************************************************************************** * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** *//* Includes ------------------------------------------------------------------*/#include "main.h"#include <stdio.h> /* C library input/output functions */#include <string.h> /* importing memcmp and memset functions *//* Private typedef -----------------------------------------------------------*//* Private define ------------------------------------------------------------*/#define RX_TIMEOUT 10000U#define TX_TIMEOUT 1000U#define MAX_BUFFER_SIZE 10U
/* Private macro -------------------------------------------------------------*//* Private variables ---------------------------------------------------------*/uint8_t RxBuffer[MAX_BUFFER_SIZE]; /* Reception buffer *//* Buffer used for transmission */uint8_t TxBuffer[]={65,66,67,68,69,70,71,72,73,74}; //ABCDEFGHIJ in ASCII code/* Private functions prototype -----------------------------------------------*//** ----------------------------------------------------------- * Definitions of C library functions re-targeted to the UART. * ----------------------------------------------------------- */int __io_putchar(int ch) { HAL_UART_Transmit(mx_usart2_uart_gethandle(), (uint8_t *)&ch, 1, 100); return ch;}
void HAL_UART_RxCpltCallback(hal_uart_handle_t *huart, uint32_t size_byte, hal_uart_rx_event_types_t rx_event) { HAL_UART_Transmit_DMA(mx_usart2_uart_gethandle(), RxBuffer, MAX_BUFFER_SIZE); HAL_UART_Receive_DMA(mx_usart2_uart_gethandle(), RxBuffer, MAX_BUFFER_SIZE);}
/** * brief: The application entry point. * retval: none but we specify int to comply with C99 standard */
int main(void) { /** System Init: this code placed in targets folder initializes your system. * It calls the initialization (and sets the initial configuration) of the * peripherals. You can use STM32CubeMX to generate and call this code or not * in this project. It also contains the HAL initialization and the initial * clock configuration. */ if (mx_system_init() != SYSTEM_OK) { return (-1); } else { /* * You can start your application code here */ printf("USART2 RX IT echo example\r\n"); HAL_UART_Receive_DMA(mx_usart2_uart_gethandle(), RxBuffer, MAX_BUFFER_SIZE); while (1) { HAL_UART_Transmit_DMA(mx_usart2_uart_gethandle(), TxBuffer, MAX_BUFFER_SIZE); HAL_Delay(10000); } }} /* end main */
编译工程,调试步骤完成后,按下Ctrl+Shift+P,输入Open Serial,选择COM40(STMicroelectronics),波特率115200。
程序正常运行:粉色下划线代表DMA传输,蓝色为输入本地回显,绿色为DMA回发的接收数据。

总结
本文介绍基于STM32CubeMX2 + VS Code在NUCLEO-C562RE上搭建UART通信,详细说明了HAL2硬件抽象层配置、工程结构。分别演示轮询、中断、DMA三种通信模式,展现STM32C5系列UART外设的灵活性。开发者可以从简单阻塞式调试控制台,逐步升级到高效率非阻塞数据传输方案。