一、串口(UART)是什么?
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)是一种异步、全双工、串行的通信协议。
拆解这几个关键词:
-
异步:通信双方没有统一的时钟信号,依靠预先约定好的波特率(Baud Rate) 来同步数据。就像两个人用摩斯电码交流,需要以相同的速度敲击和聆听。
-
全双工:数据可以同时在两个方向上传输(A->B 和 B->A 同时进行),因为它有独立的发送(TX)和接收(RX)线路。
-
串行:数据是一位一位(bit)地依次传输,相对于一次传输多位(如8位)的“并行”通信,节省了引脚但速度较慢。
在STM32中,USART(Universal Synchronous/Asynchronous Receiver/Transmitter)是UART的增强版,它既支持异步模式(UART),也支持同步模式(如需要时钟线的SPI)。通常我们默认使用其异步模式,所以可以粗略地将USART和UART等同看待。
二、串口通信的数据格式
一帧完整的UART数据,从起始位开始,到停止位结束,中间是有效数据,其结构如下所示:
下载
[空闲状态为高电平] | | | | --- --- | | | [起始位] [数据位0] [数据位1] ... [数据位7] [奇偶校验位] [停止位] ... --- --- | | | |
-
空闲状态(Idle):通信线路在空闲时保持高电平(逻辑1)。
-
起始位(Start Bit):总是1位的低电平(逻辑0)。它标志着一帧数据的开始,接收方检测到这个下降沿,就知道数据要来了。
-
数据位(Data Bits):紧跟在起始位之后,可以是5、6、7、8或9位(最常用的是8位)。传输时是低位(LSB)在前,高位(MSB)在后。
-
奇偶校验位(Parity Bit):可选。用于简单的错误检测,可以是奇校验(Odd)或偶校验(Even)。通过计算数据位中“1”的个数,使总个数(数据位+校验位)为奇数或偶数。
-
停止位(Stop Bit):1位、1.5位或2位的高电平(逻辑1)。它标志着一帧数据的结束,并让信号回到空闲状态,为下一帧数据的起始位做准备。
波特率(Baud Rate):表示每秒传输的符号数(即位数)。常见的波特率有9600, 19200, 115200等。波特率决定了每一位的持续时间(Bit Time = 1 / Baud Rate)。通信双方必须设置为相同的波特率,否则数据会错乱。
三、STM32中的串口(USART/UART)
STM32芯片内部集成了多个USART/UART外设(如USART1, USART2, UART4等)。它们的功能非常强大,除了基本的数据收发,还支持:
-
硬件流控制(CTS/RTS):用于防止数据丢失,在高速通信中常用。
-
多处理器通信。
-
多种中断/DMA请求。
核心工作流程
-
发送(TX):
-
程序将数据写入发送数据寄存器(TDR)。
-
USART外设自动将TDR中的数据转移到发送移位寄存器。
-
发送移位寄存器按照设定的数据格式(波特率、数据位、停止位等),从TX引脚一位一位地将数据发送出去。
-
当数据从TDR转移到移位寄存器后,会产生发送完成或发送数据寄存器空等中断/标志,通知CPU可以发送下一个数据了。
-
-
接收(RX):
-
RX引脚持续监测线路电平。
-
检测到起始位(下降沿)后,接收移位寄存器开始以设定的波特率一位一位地采样RX引脚的电平。
-
接收完一帧数据后,将移位寄存器中的数据转移到接收数据寄存器(RDR)。
-
并产生接收完成中断/标志,通知CPU来读取RDR中的数据。
-
四、如何在STM32上使用串口?(基于HAL库)
以最常见的查询方式发送和中断方式接收为例。
1. 硬件连接
STM32 引脚 | 外部设备 | 说明 |
---|---|---|
PA9 (USART1_TX) | RX | STM32的发送端接对方的接收端 |
PA10 (USART1_RX) | TX | STM32的接收端接对方的发送端 |
GND | GND | 共地!非常重要! |
2. 软件配置(使用STM32CubeMX)
-
选择USART1,模式设置为 Asynchronous(异步)。
-
参数设置:
-
Baud Rate: 115200
-
Word Length: 8 Bits(数据位)
-
Parity: None(无校验)
-
Stop Bits: 1(停止位)
-
-
GPIO设置:CubeMX会自动将PA9和PA10配置为复用功能模式。
-
NVIC设置:使能USART1全局中断(如果要用中断接收)。
3. 代码实现
c
// 1. 发送数据(查询方式,阻塞式)
char hello[] = "Hello World!\r\n"; // \r\n是换行符
HAL_UART_Transmit(&huart1, (uint8_t*)hello, strlen(hello), HAL_MAX_DELAY);
// 2. 接收数据(中断方式,非阻塞)
// 首先在main函数初始化后,启动一次接收中断
uint8_t rx_data; // 用于存放接收到的单个字节
HAL_UART_Receive_IT(&huart1, &rx_data, 1);
// 然后,当USART1真正接收到一个字节后,会自动进入中断服务函数,最终会调用到HAL_UART_RxCpltCallback回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) { // 判断是哪个串口触发的中断
// 处理接收到的数据 rx_data
if (rx_data == 'A') {
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 收到'A',翻转LED
}
// 处理完以后,必须再次启动中断接收,否则只会接收一次
HAL_UART_Receive_IT(&huart1, &rx_data, 1);
}
}
// 3. printf重定向(可选,极度方便调试)
// 在代码中添加以下代码,就可以使用printf函数通过串口输出
#include <stdio.h>
int _write(int fd, char *ptr, int len) {
HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, HAL_MAX_DELAY);
return len;
}
// 之后就可以这样用了
printf("System Booted! Time: %d ms\r\n", HAL_GetTick());
五、应用场景
-
调试打印(最常用):通过
printf
将程序运行状态、变量值、调试信息打印到电脑的串口助手,是嵌入式调试的神器。 -
与PC通信:通过USB转TTL模块,与电脑上的串口助手、自定义的上位机软件进行数据交换。
-
与模块通信:很多传感器、模块(如GPS、蓝牙、Wi-Fi、LoRa)都使用串口作为控制和数据接口,遵循特定的AT指令或自定义协议。
-
单片机之间通信:两个STM32之间可以通过串口交换数据。
总结
特性 | 描述 |
---|---|
协议类型 | 异步、串行、全双工 |
关键参数 | 波特率、数据位、停止位、校验位 |
硬件接口 | TX(发送)、RX(接收)、GND(共地) |
STM32实现 | 使用USART外设,配合HAL库函数(Transmit , Receive_IT 等) |
核心优势 | 硬件简单(仅需两根线),软件成熟,用途广泛,是调试和通信的基石 |
掌握STM32的串口通信,是打开嵌入式世界大门的关键一步。
为串口初始化IO引脚