ZZZ-14-串口通讯UART

如果说 GPIO 和 PWM 是单片机的“手脚”,定时器是“脉搏”,那么 串口通信(UART) 就是单片机的“嘴巴”和“耳朵”。有了它,单片机就能把内部的运行数据(比如传感器的数值、调试信息)发送给电脑显示出来,也能接收电脑发来的指令。

1. 什么是 UART?(通俗理解)

UART (Universal Asynchronous Receiver/Transmitter),即通用异步收发传输器。

  • 异步 (Asynchronous):意思是通信双方没有共同的时钟线(不像 SPI 或 I2C 有根时钟线牵着)。
  • 收发 (Receiver/Transmitter):它有两根核心信号线:
    • TX (Transmit):发送数据(嘴巴)。
    • RX (Receive):接收数据(耳朵)。
  • 全双工:因为有两根线,单片机可以一边说一边听,互不干扰。

2. UART 的“接头暗号”(通信参数)

既然没有时钟线同步,通信双方必须提前约定好“说话的速度”和“说话的格式”,否则就是鸡同鸭讲,显示乱码。

参数名称 常用设置 形象比喻
波特率 (Baud Rate) 115200 / 9600 说话的速度(每秒钟发送多少个比特位)。
停止位 (Stop Bits) 1 句号(一段话结束的标志)。
数据位 (Data Bits) 8 bits 单词长度(每次发 8 位,刚好一个字节)。
奇偶校验 (Parity) None (无) 逻辑纠错(检查话有没有传错)。

3. 硬件连接:必须“交叉”

这是初学者最容易犯错的地方。单片机和电脑(通过 USB-TTL 模块)连接时:

  • 单片机的 TX(PA9) $\rightarrow$ USB-TTL 的 RX(我的嘴对着你的耳朵)
  • 单片机的 RX(PA10) $\leftarrow$ USB-TTL 的 TX(你的嘴对着我的耳朵)
  • GND $\leftrightarrow$ GND(必须共地,参考电压一致)
graph LR subgraph STM32_MCU [STM32 开发板] TX1[PA9 / TX] RX1[PA10 / RX] GND1[GND] end subgraph USB_TTL [USB-TTL 模块] RX2[RXD] TX2[TXD] GND2[GND] end TX1 --- RX2 RX1 --- TX2 GND1 --- GND2

4. STM32CubeIDE 配置步骤

我们通常使用 USART1(它是单片机里最高速、最常用的串口)。

  1. 开启串口:在 .ioc 文件左侧找到 Connectivity -> USART1
  2. 设置模式:将 Mode 设为 Asynchronous(异步)。
    • 你会发现引脚图上 PA9 (TX)PA10 (RX) 变绿了。
  3. 配置参数:
    • Baud Rate: 115200
    • Word Length: 8 bits
    • Stop Bits: 1
  4. 生成代码:按 Ctrl + S

5. 核心代码实现

A. 发送数据(让单片机说话)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
while (1)
{
char *test = "Running...\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)test, strlen(test), 100);
HAL_Delay(1000); // 每秒发一次

/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}

![[Pasted image 20251221092640.png]]

B. 接收数据(让单片机听话)

在串口通信中,让单片机接收数据(听话)通常有两种方法:

  1. 轮询接收(Polling):单片机死等电脑发消息,不来就不干别的。
  2. 中断接收(Interrupt):单片机边干活边等,电脑一发消息就立刻停下活去处理。

在实际开发中,中断接收才是最专业、最常用的,这里单独开一篇学习—>[[ZZZ-15-串口通讯中断接收]]

6. 一个进阶的小技巧:重定向 printf

如果你想像在电脑上写 C 语言一样使用 printf("数值是: %d", value);,你需要做一个“重定向”。

第一步:包含头文件 在 main.c 最顶部的 USER CODE BEGIN Includes 区域:

1
2
3
4
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

第二步:重定向(告诉编译器 printf 往串口跑)main.c 里的 MX_USART1_UART_Init() 函数之后,或者专门的 USER CODE BEGIN 0 区域:

1
2
3
4
5
6
7
8
/* USER CODE BEGIN 0 */
int __io_putchar(int ch)
{
    // 核心代码:通过串口1发送一个字符
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
}
/* USER CODE END 0 */

这样,你之后所有的 printf 都会自动通过串口 1 发送到电脑上。

常见避坑指南

  1. 波特率不匹配:如果电脑串口助手设置的是 9600,单片机是 115200,你会看到满屏乱码。
  2. 未接 GND:如果不接地线,信号没有参考平面,也会产生乱码或无法接收。
  3. 阻塞问题:HAL_UART_Receive 在没收到数据前会一直卡在那(死等)。在正式项目中,我们通常会配合中断DMA 来接收数据。
Licensed under CC BY-NC-SA 4.0