UART 串口协议详解与 STM32 实战实现

在嵌入式开发领域,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) { ... }
    }
}

四、调试技巧与常见问题解决

  1. 通信失败:优先检查双方波特率、数据位 / 校验位 / 停止位是否一致(STM32 配置需与外设参数完全匹配);
  2. 接收乱码:波特率计算错误(确认 STM32 的 APB 时钟频率,如 F103 需确保 APB2 为 72MHz,库函数才会正确计算 BRR 寄存器);
  3. 引脚无输出 :GPIO 模式配置错误(TX 需为GPIO_Mode_AF_PP,而非普通推挽输出,否则无法映射 USART 功能);
  4. 中断不触发 :NVIC 优先级配置错误(抢占优先级需低于系统其他高优先级中断)或未使能 USART 中断(USART_ITConfig未调用);
  5. 数据丢失:查询式接收阻塞主程序导致漏接,建议改用中断式或 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 的结合,核心是 "协议参数一致性" 与 "硬件配置匹配":

  1. 理解 UART 的串行异步特性、数据帧结构,是 STM32 配置的基础;
  2. STM32 代码实现需围绕 "时钟使能→GPIO 配置→USART 参数设置→收发逻辑(查询 / 中断)" 展开;
  3. 调试时需紧扣 "参数匹配" 和 "硬件连接",优先排查波特率、引脚模式、中断配置等关键节点。

作者​​:趙小贞

​​声明​​:本文基于个人学习经验总结,如有错误欢迎指正!

​​版权​​:转载请注明出处,禁止商业用途。

AI声明:本文代码注释借助AI详细补全,整体框架和内容优化借助CSDN文章AI助手润色!

整体内容原创,用于复习总结以及分享经验,欢迎大家指点!

相关推荐
不惑_3 小时前
如何在 CentOS 9 Stream 服务器上安装 MySQL?
1024程序员节
今天背单词了吗9803 小时前
Spring Boot+RabbitMQ 实战:4 种交换机模式(Work/Fanout/Direct/Topic)保姆级实现
java·spring·中间件·rabbitmq·1024程序员节
xyx-3v3 小时前
STM32F1和STM32F4在配置硬件SPI1时有什么不同?
stm32·单片机·嵌入式硬件
jojo是只猫3 小时前
vscode中好用的插件
1024程序员节
CodeCraft Studio3 小时前
FastReport .NET 2026.1 全新发布: 统一Demo中心、全新Ribbon界面、Excel公式导出、Word图像质量设置等重磅升级!
1024程序员节·fastreport .net·fastreport·报表设计器·报表开发工具·ribbon ui
猫头虎3 小时前
大模型训练中的关键技术与挑战:数据采集、微调与资源优化
人工智能·爬虫·数据挖掘·数据分析·网络爬虫·aigc·1024程序员节
m0_516484673 小时前
博途DWORD中包含word ,字节,位的关系
1024程序员节
itachi-uchiha3 小时前
bash的“进程替换 + 重定向”和“传统管道”
1024程序员节
Aevget3 小时前
DevExpress WPF中文教程:Data Grid - 如何使用虚拟源?(三)
wpf·界面控件·devexpress·ui开发·1024程序员节