串口是 STM32 调试的生命线:打印日志、看传感器数据、跟上位机联调都靠它。这篇把最常用的两招讲透:printf 重定向输出,中断方式接收。
1. CubeMX 配置要点
Connectivity → USART1,Mode 选 Asynchronous
波特率 115200,8 数据位、无校验、1 停止位(8N1)
NVIC Settings 里勾选 USART1 global interrupt(中断接收才需要)
PA9(TX)、PA10(RX)接到 USB 转串口模块,注意 TX 接 RX、RX 接 TX,交叉接
2. printf 重定向
C 库的 printf 最终调 fputc,重写它把字符送进串口:
#include <stdio.h>
int fputc(int ch, FILE *f) {
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
// main 里直接用
printf("system clock: %lu Hz\r\n", HAL_RCC_GetSysClockFreq());
注意两点:
printf 发 \n 不会自动回车,串口助手里看到满屏阶梯状就改成 \r\n
HAL_MAX_DELAY 是阻塞等待,放在中断里调 printf 会卡死,中断里只做标记
3. 中断接收(推荐做法)
轮询接收会堵住主循环,用中断 + 一字节缓冲:
uint8_t rx_byte;
uint8_t rx_buf[128];
uint8_t rx_len = 0;
void start_uart_rx(void) {
HAL_UART_Receive_IT(&huart1, &rx_byte, 1); // 启动一次
}
// 每收到 1 字节进一次回调,记得重新挂起下一次接收
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
if (rx_byte == '\n' || rx_len >= sizeof(rx_buf) - 1) {
rx_buf[rx_len] = '\0';
// 在这里处理一行命令,不要做耗时操作
rx_len = 0;
} else if (rx_byte != '\r') {
rx_buf[rx_len++] = rx_byte;
}
HAL_UART_Receive_IT(&huart1, &rx_byte, 1); // 关键:重新挂起
}
}
4. 排障清单
现象
先查
串口无输出
波特率两边是否一致;TX/RX 是否交叉接;GND 有没有共地
输出乱码
波特率/时钟配置,检查 HSE 是否起振
只能收一次
回调里有没有重新调 HAL_UART_Receive_IT
printf 卡死
是否在中断上下文里调用;串口外设时钟开了没
下一篇计划写定时器 PWM 驱动舵机/电机,连载目录见 /series。