3、UART协议基础:UART工作原理、帧格式(起始位、数据位、校验位、停止位)、波特率与误差容限、全双工与半双工
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是MCU系统中最古老、最基础、也最广泛使用的串行通信接口之一。与I2C和SPI不同,UART不依赖时钟线,而是通过双方预先约定的波特率(Baud Rate)在一条数据线上逐位传输数据。理解UART的工作原理、帧格式、波特率误差容限以及双工模式,是进行MCU调试、传感器数据采集、蓝牙/Wi-Fi模块通信、GPS/GPRS模块对接等工程实践的前提。本章将从物理层信号机制出发,逐步拆解UART的完整通信链路。
3.1 UART工作原理:异步串行通信的核心机制
UART是一种异步串行通信协议,其核心特征在于不需要独立的时钟线。通信双方各自使用自己的内部时钟(通常由晶振或PLL产生),通过约定相同的波特率来保证位同步。发送端将并行数据(如MCU内部总线上的8位数据)转换为串行比特流,逐位发送到TX引脚;接收端在RX引脚上采样电平,将串行比特流重新组合为并行数据。
UART的物理层非常简单,通常只需要三根线:TX(发送)、RX(接收)、GND(公共地)。发送端的TX连接接收端的RX,发送端的RX连接接收端的TX,双方共地。由于没有时钟线,接收端必须依靠起始位来检测一帧数据的开始,并在每个位周期内进行多次采样(通常为16倍波特率采样)以确定每个位的电平状态。
UART的发送过程可以概括为以下步骤:
- 空闲状态:TX线保持高电平(逻辑1),表示线路空闲。
- 发送起始位:TX线拉低一个位时间(逻辑0),通知接收端“数据即将开始”。
- 发送数据位:按照约定的数据位长度(通常为5~8位),从LSB(最低有效位)开始逐位发送。
- 发送校验位(可选):根据校验模式(无校验、奇校验、偶校验)计算并发送校验位。
- 发送停止位:TX线拉高至少一个位时间(逻辑1),表示一帧数据结束。
接收端的工作流程与之对应:检测到下降沿(高→低跳变)后,确认起始位;然后以波特率周期采样数据位、校验位和停止位;最后将数据位组合成字节存入接收寄存器,并置位接收标志位(如RXNE)供MCU读取。
3.2 UART帧格式详解:起始位、数据位、校验位、停止位
UART的帧格式是通信双方必须严格约定的参数组合。最常见的配置是8N1(8个数据位、无校验、1个停止位),此外还有7E1、8O2、9N1等变体。帧格式的每个字段都有明确的时序要求。
| 字段 | 电平状态 | 位宽 | 作用 | 注意事项 |
|---|---|---|---|---|
| 空闲态 | 高电平(1) | 任意 | 线路空闲标识 | 接收端持续监测下降沿 |
| 起始位 | 低电平(0) | 1 bit | 同步信号,标志帧开始 | 必须为下降沿触发 |
| 数据位 | 0或1 | 5~8 bits(常用8) | 承载有效数据 | 先发LSB,后发MSB |
| 校验位 | 0或1 | 0或1 bit | 错误检测(可选) | 奇校验/偶校验/无校验 |
| 停止位 | 高电平(1) | 1~2 bits(常用1) | 帧结束标志 | 保证下一帧起始位有下降沿 |
起始位是UART帧同步的关键。接收端在空闲状态下持续采样RX线,一旦检测到高→低的跳变,即认为收到起始位,并以此为时间基准开始采样后续数据位。起始位的下降沿是接收端进行位同步的锚点。
数据位的长度决定了单帧传输的有效数据量。8位数据是最常用的配置,因为一个字节正好是8位。发送顺序固定为LSB first(最低有效位在前),例如发送十六进制0x53(二进制0101 0011),实际线上发送顺序为:1, 1, 0, 0, 1, 0, 1, 0。
校验位用于简单的错误检测。奇校验要求数据位和校验位中“1”的总数为奇数;偶校验要求总数为偶数。校验位只能检测奇数个位错误,无法纠正错误,也无法检测偶数个位错误。在噪声较大的工业环境中,建议使用更可靠的CRC校验或协议层重传机制。
停止位必须为高电平,其作用是给接收端一个“帧结束”的明确信号,同时保证线路回到空闲状态。停止位长度可以是1、1.5或2个位时间。1.5位停止位主要用于某些老式设备,2位停止位常用于低速设备或需要额外处理时间的场景。
3.3 波特率与误差容限:通信可靠性的关键参数
波特率(Baud Rate)定义为单位时间内传输的码元(符号)数量,对于UART而言,每个码元就是一个比特,因此波特率等于比特率(bps)。常见的波特率有9600、19200、38400、57600、115200等。波特率决定了每个位的持续时间:位时间 = 1 / 波特率。例如,115200bps的位时间约为8.68微秒。
波特率的误差容限是UART通信中极易被忽视的问题。由于收发双方使用独立的时钟源,实际波特率与标称值必然存在偏差。误差来源包括:
- 晶振精度:普通晶振精度为±20~±50ppm,温补晶振(TCXO)可达±2ppm。
- 分频误差:MCU内部UART外设通过分频器从系统时钟产生波特率,分频系数可能无法整除,产生量化误差。
- 温度漂移:晶振频率随温度变化,导致波特率漂移。
接收端通常以16倍波特率进行过采样,即在每个位时间内采样16次,取中间3次(第7、8、9次采样)的多数表决结果作为该位的电平值。这种过采样机制可以容忍一定程度的相位偏差,但误差累积不能超过半个位时间。
N × 16 个过采样时钟周期。如果收发双方的波特率误差为ε,则第N位的累积误差为 N × 16 × ε。为保证采样点落在位中心附近(允许±1个过采样周期偏差),要求 10 × 16 × ε < 8,即 ε < 5%。实际工程中建议控制在±2%以内。
在实际MCU配置中,波特率寄存器的值通过以下公式计算:
// 以STM32 USART为例
// BRR = fck / (16 × 目标波特率)
// 假设 fck = 72MHz,目标波特率 = 115200
// BRR = 72000000 / (16 × 115200) = 39.0625
// 取整为39,实际波特率 = 72000000 / (16 × 39) = 115384.6
// 误差 = (115384.6 - 115200) / 115200 = 0.16% → 在容限内
当计算出的BRR值不是整数时,应四舍五入取整,并验证实际波特率误差是否在容限内。对于高速通信(如921600bps以上),建议使用精度更高的晶振,并仔细计算分频误差。
3.4 全双工与半双工:UART的双工模式
UART天然支持全双工(Full-Duplex)通信,因为发送和接收使用独立的物理线路(TX和RX),数据可以同时双向传输。全双工模式下,MCU可以在发送数据的同时接收数据,无需切换方向,适合实时性要求高的场景,如调试日志输出与命令接收并行。
半双工(Half-Duplex)模式则要求通信双方共享同一条数据线,同一时刻只能有一个方向的数据传输。UART的半双工模式通常通过以下方式实现:
- 单线UART模式:部分MCU(如STM32的HALF_DUPLEX模式)支持将TX和RX引脚在内部短接,外部仅使用一根数据线。
- RS-485总线:通过收发器(如MAX485)将UART信号转换为差分信号,使用DE/RE引脚控制方向。
- 软件模拟:通过GPIO控制方向切换,但需要处理总线冲突问题。
| 特性 | 全双工 | 半双工 |
|---|---|---|
| 物理线路 | TX + RX 独立两根线 | 单根数据线(或差分对) |
| 同时收发 | 支持 | 不支持,需切换方向 |
| 典型应用 | MCU调试、蓝牙模块、GPS模块 | RS-485工业总线、单总线设备 |
| 冲突风险 | 无 | 需协议层避免冲突 |
| 传输距离 | 较短(通常<15m) | RS-485可达1200m |
在MCU开发中,最常见的UART应用是全双工模式。例如,MCU通过UART连接ESP8266 Wi-Fi模块,MCU的TX发送AT指令,RX接收模块响应,两者同时进行,互不干扰。而在RS-485工业现场总线中,则必须使用半双工模式,通过主站轮询或令牌机制避免冲突。
3.5 实战案例:MCU与GPS模块的UART通信调试
下面通过一个完整的工程案例,展示UART协议在实际项目中的配置、调试与排障过程。
项目背景:某物联网终端设备使用STM32F103 MCU(系统时钟72MHz)通过UART2连接u-blox NEO-6M GPS模块,要求以9600bps波特率接收NMEA 0183格式的定位数据(如$GPRMC语句)。
初始现象:设备上电后,MCU无法解析到有效的GPS定位数据,串口调试助手显示乱码或完全无数据。
关键参数与日志:
// STM32 UART2 初始化代码(部分)
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
// 调试串口输出(UART1,115200bps)
printf("GPS UART2 init done, waiting for data...\r\n");
分析判断过程:
- 检查硬件连接:使用万用表测量GPS模块的TX引脚(PPS/ TXD)与STM32的PA3(USART2_RX)之间的电压,确认GPS模块供电正常(3.3V),TX线在空闲时约为3.3V高电平。
- 检查波特率匹配:GPS模块默认波特率为9600,与MCU配置一致。但使用逻辑分析仪抓取GPS模块TX引脚波形,发现实际波特率约为9580bps(误差-0.2%),在容限内。
- 检查帧格式:GPS模块输出8N1格式,与MCU配置一致。但逻辑分析仪显示数据位顺序为LSB first,与预期相符。
- 检查MCU接收中断:在中断服务函数中添加调试计数器,发现接收中断从未触发。进一步检查发现USART2的时钟未使能(RCC_APB1PeriphClockCmd),导致外设未工作。
实际操作步骤:
// 修复:使能USART2时钟和GPIO时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置PA2为TX(复用推挽),PA3为RX(浮空输入)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
结果对比:修复时钟使能后,MCU成功接收到GPS模块的NMEA数据,串口调试助手输出正常的$GPRMC,023456.00,A,3153.1234,N,12123.4567,E,...语句。整个排障过程耗时约30分钟,主要时间花费在逻辑分析仪波形分析和时钟配置检查上。
风险与代价:如果未使用逻辑分析仪,仅凭肉眼观察串口输出,很难区分是硬件连接问题、波特率问题还是外设配置问题。建议在UART调试初期,优先使用逻辑分析仪或示波器确认物理层波形。
复盘经验:
- UART调试的第一步永远是确认物理层波形,而不是修改软件配置。
- 时钟使能是MCU外设配置中最容易被遗漏的步骤,务必检查RCC寄存器。
- GPS模块的波特率可以通过UART-Center等工具修改,但默认9600bps是通用选择。
- 在工业环境中,建议在UART的TX/RX线上串联33Ω~100Ω电阻,并添加TVS管进行ESD保护。
3.6 常见误区与总结复盘
UART协议虽然简单,但在实际工程中仍存在许多容易混淆的细节。以下是常见误区及应对策略:
| 误区 | 错误理解 | 正确理解 |
|---|---|---|
| 波特率=比特率 | 两者完全等价 | UART中每个码元=1比特,所以数值相等;但其他调制方式(如QPSK)中波特率≠比特率 |
| 停止位=空闲态 | 停止位后线路高电平就是空闲 | 停止位是帧的一部分,有固定宽度;空闲态是帧间任意长度高电平 |
| 校验位能纠错 | 校验位可以纠正错误数据 | 校验位只能检测奇数个位错误,无法纠正,需配合重传机制 |
| 全双工一定比半双工好 | 全双工适用于所有场景 | 半双工(如RS-485)在长距离、多节点场景下更可靠 |
| 波特率误差小于5%即可 | 5%误差是安全线 | 5%是理论极限,实际建议≤2%,且需考虑温度漂移和分频量化误差 |
总结:UART协议的核心在于“异步”二字——没有时钟线,依靠起始位同步、波特率约定和过采样机制保证通信可靠性。帧格式的每个字段都有明确的时序要求,任何参数不匹配都会导致通信失败。全双工模式适合短距离双向实时通信,半双工模式适合长距离多节点总线。在实际开发中,建议遵循以下流程:
- 确认硬件连接(TX↔RX交叉,GND共地)。
- 使用逻辑分析仪验证物理层波形。
- 核对帧格式参数(数据位、校验位、停止位)。
- 计算并验证波特率误差。
- 检查MCU外设时钟和GPIO复用配置。
- 添加错误统计和超时处理机制。
掌握UART协议不仅是学会配置寄存器,更重要的是理解其信号时序和误差模型,