在嵌入式开发领域,UART(通用串行异步收发器)是设备间数据交互的核心协议之一,尤其在 STM32 开发中,其通过 USART 外设(通用同步异步收发器)实现异步通信,广泛应用于调试日志输出、传感器数据接收、模块指令交互等场景。本文整合 UART 核心协议概念与 STM32 实战代码,从原理到落地,系统梳理关键知识点,兼顾复习与工程应用。
一、UART 核心概念:从定义到特性
UART 的名称蕴含 "通用、串行、异步、收发器" 四大核心属性,也是理解其工作逻辑的基础,同时需结合 STM32 的硬件实现关联认知。
1. 核心属性解析
- 通用:不依赖特定场景,STM32 中通过 USART1/2/3 等多外设支持,覆盖工控、电力、消费电子等领域(如 STM32F103 的 USART1 可用于调试,USART2 连接蓝牙模块)。
- 串行 :处理器与外设仅通过 1 根数据线传输数据,按 "低位优先" 逐 bit 发送;对比并行通信(多根数据线,一次传多字节),STM32 中 UART 因抗干扰强、传输距离远(优于并行),更适合板间短距通信。
| 特性 | 串行通信(UART) | 并行通信 |
|---|---|---|
| 数据线数量 | 1 根 | 8/16/32 根 |
| 速度 | 较慢(如 115200bps) | 较快 |
| 抗干扰 | 强(单线路串扰少) | 弱(多线路干扰多) |
| 传输距离 | 较远 | 较近 |
- 异步:无需时钟线同步,依赖 "波特率" 和 "数据帧协议" 实现双方对齐(STM32 中通过配置 USART 参数约定,区别于 SPI 的同步时钟模式)。
- 收发器:硬件单元负责数据发送 / 接收,角色随流向变化(STM32 作为 CPU 时,给外设发数据则 STM32 是发送器,接收外设数据则是接收器)。
2. 电气特性与 STM32 硬件
- 电平类型:STM32 引脚输出 TTL 电平(高≈3.3V,低≈0V),外部串口线常用 EIA 电平(高 - 3~-15V,低 3~15V),需通过电平转换芯片(如 MAX232)兼容。
- 硬件连接:STM32 UART 通信至少需 3 根线 ------TX(发送,如 USART1 的 PA9)、RX(接收,如 USART1 的 PA10)、GND(共地,保证电平参考一致),且需 "交叉连接"(STM32 的 TX 接外设的 RX,STM32 的 RX 接外设的 TX)。
- 工作方式:STM32 UART 默认支持全双工(数据可同时双向传输,如调试时 PC 发指令、STM32 回传数据),也可配置为半双工或单工(极少用)。
二、UART 协议核心:数据帧与关键参数(附 STM32 配置映射)
UART 通过 "数据帧" 规范数据传输格式,STM32 的 USART 外设需配置与帧结构匹配的参数,双方参数一致才能正常通信。
1. 数据帧结构
标准数据帧格式为:{空闲位 + 起始位 + 数据位 +(校验位)+ 停止位},各字段定义与 STM32 配置对应关系如下:
| 字段 | 位数 | 电平 / 作用 | STM32 配置参数(USART_InitTypeDef) |
|---|---|---|---|
| 空闲位 | 1bit | 高电平(无数据时的默认状态) | 硬件自动生成,无需配置 |
| 起始位 | 1bit | 低电平(标志传输开始) | 硬件自动生成,无需配置 |
| 数据位 | 5/6/7/8bit | 高低电平表示有效数据 | USART_WordLength_8b(常用,对应 1 字节数据) |
| 校验位 | 0/1bit | 检测传输错误 | USART_Parity_No(无校验)/Even(偶)/Odd(奇) |
| 停止位 | 1/2bit | 高电平(标志传输结束) | USART_StopBits_1(常用) |
2. 关键协议参数
- 波特率 :每秒传输的 bit 数(单位 bps),STM32 常用 115200bps(调试首选,速度快)和 9600bps(稳定性高,适合弱信号场景),配置通过
USART_InitStruct.USART_BaudRate设置,由库函数自动计算 BRR 寄存器值(基于 STM32 的 APB 时钟,如 F103 的 APB2 时钟为 72MHz)。 - 校验位逻辑 :STM32 硬件自动计算校验位,无需软件干预。例如传输
0x95(二进制10010101,含 4 个 1):- 奇校验:硬件自动添加 1bit 高电平(4+1=5,奇数);
- 偶校验:硬件自动添加 1bit 低电平(4+0=4,偶数)。
三、STM32 UART 实战代码:从初始化到收发
以 STM32F103 固件库为例,实现 USART1 的初始化、查询式收发、中断式接收,覆盖主流应用场景。
1. 基础初始化(GPIO+USART)
需先使能时钟,再配置 GPIO 引脚模式和 USART 参数:
cpp
#include "stm32f10x.h"
// USART1初始化:115200bps,8位数据,无校验,1位停止位,全双工
void USART1_Init(void) {
// 1. 使能时钟(GPIOA和USART1均挂载APB2总线)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 2. 配置GPIO引脚
GPIO_InitTypeDef GPIO_InitStruct;
// TX引脚:PA9(复用推挽输出,需将USART1_TX功能映射到引脚)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// RX引脚:PA10(浮空输入,避免外部干扰)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 3. 配置USART参数
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 115200; // 波特率115200bps
USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 8位数据位
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1位停止位
USART_InitStruct.USART_Parity = USART_Parity_No; // 无校验
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 收发双模式
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
USART_Init(USART1, &USART_InitStruct);
// 4. 使能USART1外设
USART_Cmd(USART1, ENABLE);
}
2. 查询式收发(适合简单场景)
通过查询 USART 状态寄存器(SR)的TXE(发送空)和RXNE(接收非空)标志位,实现阻塞式收发:
cpp
// 发送1字节数据(查询TXE标志,确保发送寄存器空)
void USART1_SendByte(uint8_t data) {
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待发送空
USART_SendData(USART1, data); // 写入数据寄存器,硬件自动发送
}
// 发送字符串(逐字节调用SendByte)
void USART1_SendString(char* str) {
while (*str != '\0') {
USART1_SendByte(*(str++));
}
}
// 接收1字节数据(查询RXNE标志,阻塞等待数据)
uint8_t USART1_ReceiveByte(void) {
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); // 等待接收非空
return USART_ReceiveData(USART1); // 读取数据寄存器
}
3. 中断式接收(适合非阻塞 / 大数据场景)
查询式接收会阻塞主程序,中断式接收可在数据到来时触发中断,提升程序效率,需配置 NVIC(嵌套向量中断控制器):
cpp
#include "misc.h" // 包含NVIC相关定义
uint8_t g_rx_data; // 全局变量存储接收数据
// NVIC初始化:配置USART1中断优先级
void USART1_NVIC_Init(void) {
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; // USART1中断通道
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级(需根据系统调整)
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 子优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_Init(&NVIC_InitStruct);
// 使能USART1接收中断(RXNE触发)
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
// USART1中断服务函数:处理接收中断
void USART1_IRQHandler(void) {
// 判断是否为接收中断(RXNE标志置位)
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {
g_rx_data = USART_ReceiveData(USART1); // 读取接收数据
USART_ClearITPendingBit(USART1, USART_IT_RXNE); // 清除中断标志(可选,读取DR后自动清除)
}
}
4. 主函数调用示例
cpp
int main(void) {
USART1_Init(); // 初始化USART1
USART1_NVIC_Init(); // 初始化中断(若用中断接收)
USART1_SendString("STM32 UART Test Start!\r\n"); // 发送测试字符串
while (1) {
// 方式1:查询式接收并回发
// uint8_t recv = USART1_ReceiveByte();
// USART1_SendByte(recv);
// 方式2:中断接收后处理(此处仅示例,可在中断中直接处理)
// if (g_rx_data != 0) { ... }
}
}
四、调试技巧与常见问题解决
- 通信失败:优先检查双方波特率、数据位 / 校验位 / 停止位是否一致(STM32 配置需与外设参数完全匹配);
- 接收乱码:波特率计算错误(确认 STM32 的 APB 时钟频率,如 F103 需确保 APB2 为 72MHz,库函数才会正确计算 BRR 寄存器);
- 引脚无输出 :GPIO 模式配置错误(TX 需为
GPIO_Mode_AF_PP,而非普通推挽输出,否则无法映射 USART 功能); - 中断不触发 :NVIC 优先级配置错误(抢占优先级需低于系统其他高优先级中断)或未使能 USART 中断(
USART_ITConfig未调用); - 数据丢失:查询式接收阻塞主程序导致漏接,建议改用中断式或 DMA 方式(STM32 USART 支持 DMA,适合大数据量传输)。
五、相关结构体和库函数详解
cpp
typedef struct{
uint32_t USART_BaudRate; //波特率,例如:115200 uint16_t USART_WordLength; //字长
uint16_t USART_StopBits; //停止位
uint16_t USART_Parity; //校验位
uint16_t USART_Mode; //USART模式
uint16_t USART_HardwareFlowControl; //硬件流控制
} USART_InitTypeDef;
USART_BaudRate:
波特率,如 9600、115200
USART_WordLength:
数据位长度:USART_WordLength_8b(8位)、USART_WordLength_9b(9位)
USART_StopBits:
停止位:USART_StopBits_1(1位)、USART_StopBits_2(2位)
USART_Parity:
校验:USART_Parity_No(无)、USART_Parity_Even(偶)USART_Parity_Odd(奇)
USART_Mode:
模式:USART_Mode_Rx(接收)、USART_Mode_Tx(发送),可组合USART_Mode_Rx|USART_Mode_Tx
USART_HardwareFlowControl:
硬件流控:None、RTS、CTS、RTS_CTS USART_HardwareFlowControl_None
硬件流控制常用的有RTS/CTSQ流控制和DTR/DSR(数据终端就绪/数据设置就绪)流控制。
RTS(RequireToSend,发送请求)为输出信号,用于指示本设备准备好可接收数据,低电平有效,低电平说明本设备可以接收数据。
CTS(ClearToSend,发送允许)为输入信号,用于判断是否可以向对方发送数据,低电平有效,低电平说明本设备可以向对方发送数据。
-----------------------------------------------------------------------------------------
USART 寄存器结构,USART_TypeDeff,在文件"stm32f10x_map.h"中定义如下:
typedef struct {
vu16 SR;
u16 RESERVED1;
vu16 DR;
u16 RESERVED2;
vu16 BRR;
u16 RESERVED3;
vu16 CR1;
u16 RESERVED4;
vu16 CR2;
u16 RESERVED5;
vu16 CR3;
u16 RESERVED6;
vu16 GTPR;
u16 RESERVED7;
} USART_TypeDef;
寄存器 描述
SR USART 状态寄存器
DR USART 数据寄存器
BRR USART 波特率寄存器
CR1 USART 控制寄存器 1
CR2 USART 控制寄存器 2
CR3 USART 控制寄存器 3
GTPR USART 保护时间和预分频寄存器
----------------------------------------------------------------------------------------
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
功能: 根据 USART_InitStruct 中指定的参数初始化外设 USARTx 寄存器
参数 1 USARTx:x 可以是 1,2 或者 3,来选择 USART 外设
参数 2 USART_InitStruct:指向结构 USART_InitTypeDef 的指针,包含了外设 USART 的配置信息。
----------------------------------------------------------------------------------------
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能 使能或者失能 USART 外设
参数 1 USARTx:x 可以是 1,2 或者 3,来选择 USART 外设
参数 2 NewState: 外设 USARTx 的新状态 这个参数可以取:ENABLE 或者 DISABLE
----------------------------------------------------------------------------------------
void USART_SendData(USART_TypeDef* USARTx, u8 Data)
功能 通过外设 USARTx 发送单个数据
参数 1 USARTx:x 可以是 1,2 或者 3,来选择 USART 外设
参数 2 Data: 待发送的数据
----------------------------------------------------------------------------------------
u8 USART_ReceiveData(USART_TypeDef* USARTx)
功能 返回 USARTx 最近接收到的数据
参数 USARTx:x 可以是 1,2 或者 3,来选择 USART 外设
返回值 接收到的字
----------------------------------------------------------------------------------------
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, u16 USART_FLAG)
功能 检查指定的 USART 标志位设置与否
参数 1 USARTx:x 可以是 1,2 或者 3,来选择 USART 外设
参数 2 USART_FLAG:待检查的 USART 标志位
USART_FLAG :
USART_FLAG_CTS CTS 标志位
USART_FLAG_LBD LIN 中断检测标志位
USART_FLAG_TXE 发送数据寄存器空标志位
√ USART_FLAG_TC 发送完成标志位
√ USART_FLAG_RXNE 接收数据寄存器非空标志位
USART_FLAG_IDLE 空闲总线标志位 检测一包数据接收完成
USART_FLAG_ORE 溢出错误标志位
USART_FLAG_NE 噪声错误标志位
USART_FLAG_FE 帧错误标志位
USART_FLAG_PE 奇偶错误标志位
返回值 USART_FLAG 的新状态(SET 或者 RESET)
------------------------------------------
USART_IT_IDLE中断,是串口收到一帧数据后,发生的中断。也可以叫做一包数据
USART_IT_IDLE和USART_IT_RXNE区别
当接收到1个字节,会产生USART_IT_RXNE中断
当接收到一帧数据,就会产生USART_IT_IDLE中断
清中断方法
USART_IT_RXNE
USART_ClearITPendingBit(USART1, USART_IT_RXNE); 或者USART1->DR
USART_IT_IDLE
先读SR寄存器 USART1->SR ,再读DR寄存器USART1->DR。
----------------------------------------------------------------------------------------
void USART_ITConfig(USART_TypeDef* USARTx, u16 USART_IT, FunctionalState NewState)
功能 使能或者失能指定的 USART 中断
参数 1 USARTx:x 可以是 1,2 或者 3,来选择 USART 外设
参数 2 USART_IT:待使能或者失能的 USART 中断源
参数 3 NewState:USARTx 中断的新状态 可以取:ENABLE 或者 DISABLE
USART_IT
USART_IT_PE 奇偶错误中断
USART_IT_TXE 发送中断
发送寄存器 DR 为空
USART_IT_TC 传输完成中断
数据帧(含停止位)发送完
USART_IT_RXNE 接收中断
当DR寄存器从外设接收了一个字符,触发一次RXNE中断
USART_IT_IDLE 空闲总线中断
检测一包数据接收完成
USART_IT_LBD LIN 中断检测中断
LIN 协议相关(极少用)
USART_IT_CTS CTS 中断
硬件流控制信号变化
USART_IT_ERR 错误中断
帧错误 / 溢出 / 噪声等
----------------------------------------------------------------------------------------
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, u16 USART_IT)
功能 检查指定的 USART 中断发生与否
参数 1 USARTx:x 可以是 1,2 或者 3,来选择 USART 外设
参数 2 USART_IT:待检查的 USART 中断源
返回值 USART_IT 的新状态 (SET 或者 RESET)
----------------------------------------------------------------------------------------
void USART_ClearITPendingBit(USART_TypeDef* USARTx, u16 USART_IT)
功能 清除 USARTx 的中断待处理位
参数 1 USARTx:x 可以是 1,2 或者 3,来选择 USART 外设
参数 2 USART_IT:待检查的 USART 中断源
----------------------------------------------------------------------------------------
时序图示例:

六、总结
UART 协议与 STM32 的结合,核心是 "协议参数一致性" 与 "硬件配置匹配":
- 理解 UART 的串行异步特性、数据帧结构,是 STM32 配置的基础;
- STM32 代码实现需围绕 "时钟使能→GPIO 配置→USART 参数设置→收发逻辑(查询 / 中断)" 展开;
- 调试时需紧扣 "参数匹配" 和 "硬件连接",优先排查波特率、引脚模式、中断配置等关键节点。
作者:趙小贞
声明:本文基于个人学习经验总结,如有错误欢迎指正!
版权:转载请注明出处,禁止商业用途。
AI声明:本文代码注释借助AI详细补全,整体框架和内容优化借助CSDN文章AI助手润色!
整体内容原创,用于复习总结以及分享经验,欢迎大家指点!