第4章:UART串口通信协议详解与电平标准

做嵌入式开发,串口是咱们打交道最多的外设之一。说实话,我见过不少工程师,代码写得飞起,但一遇到串口乱码、通信不稳定,就开始抓瞎。根本原因是什么?就是对UART协议本身的理解不够扎实。

这一章,咱们就把UART的底裤扒干净。从波特率、数据位、停止位、校验位这些基础参数,到RS232和TTL电平的区别,我都会结合自己踩过的坑来讲。你想想看,搞懂了这些,后面调试AT指令才能有的放矢。

核心观点:UART通信的可靠性,90%取决于收发双方的参数配置是否完全一致。剩下的10%,是电平匹配问题。

4.1 UART帧结构——一个字节是怎么传出去的?

UART是异步串行通信,没有时钟线。收发双方靠什么同步?靠约定的波特率,以及帧结构里的起始位和停止位。

一个完整的UART数据帧,长这样:

空闲状态(高电平) → 起始位(低电平) → 数据位(LSB先发) → 校验位(可选) → 停止位(高电平) → 空闲状态(高电平)

我刚开始学的时候,总觉得“LSB先发”这个事无所谓。直到有一次用逻辑分析仪抓波形,发现数据对不上,才意识到:如果你按MSB顺序去解析,整个字节都是反的。

字段 长度 电平 说明
空闲位 无限制 高电平(逻辑1) 总线空闲状态,线路上保持高
起始位 1位 低电平(逻辑0) 通知接收方:数据要来了
数据位 5~9位(常用8位) 高/低电平 先发最低位(LSB),后发最高位(MSB)
校验位 0或1位 高/低电平 奇校验/偶校验/无校验
停止位 1/1.5/2位 高电平(逻辑1) 帧结束标志,给接收方处理时间
我的习惯:调试阶段,我一般把停止位设成2位。虽然带宽浪费一点,但能给接收中断留出更充裕的处理时间,减少丢帧。量产时再改回1位。

4.2 波特率——通信的节拍器

波特率,说白了就是每秒传输多少个码元。对于UART来说,一个码元就是一个bit。所以波特率 = 比特率。

常用值:9600、19200、38400、115200、921600。

这里有个关键点:收发双方的波特率误差必须在±2%以内。超过这个范围,就会出现间歇性乱码。

为什么会这样?因为UART接收方是用内部时钟去采样数据线的。假设波特率是9600,每个bit的时长约104μs。接收方会在每个bit的中间位置采样。如果时钟偏差太大,采样点就会偏移,最终采到错误的电平。

我曾经踩过的坑:用STM32的HSE外部晶振是8MHz,但PLL配置有误差,导致USART的波特率实际是9642,而不是9600。对方设备是严格的9600,误差0.44%,按理说没问题。但那个设备对时序要求特别苛刻,最终我换了一个精确的晶振分频才解决。所以,不要迷信理论误差值,实际测试才是王道。

4.3 数据位、停止位、校验位——细节决定成败

4.3.1 数据位

常用的是8位数据位。为什么?因为一个字节就是8位,方便。7位数据位在老的ASCII通信中还有,现在很少见了。9位数据位用在一些特殊协议里,比如多机通信的地址帧识别。

我个人建议:除非有特殊需求,一律用8位数据位。少给自己找麻烦。

4.3.2 停止位

停止位不是“停止传输”,而是给接收方一个处理窗口。1位停止位是最常见的。1.5位和2位用在慢速设备或长线传输场景。

你想想看,如果接收方的中断服务函数执行时间比较长,停止位太短,下一帧的起始位就来了,容易造成数据覆盖。所以,如果你的MCU主频不高,或者中断里有复杂处理,用2位停止位更稳妥。

4.3.3 校验位

校验位分三种:无校验(None)、奇校验(Odd)、偶校验(Even)。

校验位只覆盖数据位,不覆盖起始位和停止位。它的原理很简单:

  • 偶校验:数据位中1的个数 + 校验位 = 偶数
  • 奇校验:数据位中1的个数 + 校验位 = 奇数

但说实话,单bit校验的检错能力非常有限。如果两个bit同时翻转,校验位反而正确。我在实际项目中,如果通信环境比较恶劣(比如电机驱动附近),我会在应用层加CRC校验,而不是依赖UART的硬件校验位。

一句话总结参数配置:8N1(8数据位、无校验、1停止位)是事实上的工业标准。除非对方设备有特殊要求,否则就用8N1。

4.4 RS232 vs TTL电平——别把芯片烧了

这是新手最容易犯的错误。我见过不止一次,有人直接把RS232的DB9接口接到STM32的GPIO上,然后问“为什么芯片冒烟了”。

咱们直接看对比表:

项目 TTL电平 RS232电平
逻辑1(高电平) 2.0V ~ 5.0V -15V ~ -3V
逻辑0(低电平) 0V ~ 0.8V +3V ~ +15V
电压摆幅 0~5V(或3.3V) ±12V左右
传输距离 几米以内 可达15米以上
抗干扰能力 弱 强(差分传输)
常用接口 杜邦线、排针 DB9、端子排

看到没?RS232的逻辑1是负电压,逻辑0是正电压。这和TTL正好相反。而且RS232的电压摆幅高达±12V,直接进STM32的GPIO,必烧。

所以,TTL设备和RS232设备通信,必须加电平转换芯片。常用的有MAX232、SP3232等。这些芯片内部有电荷泵,能把TTL的0~5V转换成RS232的±12V。

我的调试小技巧:如果你手头没有逻辑分析仪,可以用一个LED串1kΩ电阻,接到TX或RX线上。TTL电平下,LED在空闲时微亮(高电平),发送数据时会闪烁。虽然不能看具体数据,但至少能判断有没有信号。RS232电平就别这么干了,电压太高。

4.5 知识结构总览

下面这张图,把本章的核心知识点串起来了。你可以把它当成一个快速参考:

UART串口通信知识结构图 UART串口通信知识结构 UART通信协议 帧结构 起始位 + 数据位(LSB先发) 校验位(可选) + 停止位 参数配置 波特率(误差<2%) 数据位/停止位/校验位 电平标准 TTL: 0~5V, 逻辑1=高 RS232: ±12V, 逻辑1=负 核心原则:收发参数一致 + 电平匹配 = 可靠通信 调试建议 1. 先用逻辑分析仪抓波形,确认波特率和帧结构是否正确 2. 检查电平转换电路,确保TTL侧和RS232侧电压正常 3. 应用层加超时和重传机制,不要依赖硬件校验位

4.6 实战中的电平转换方案

最后,我分享一下实际项目中常用的几种电平转换方案:

  1. STM32 ↔ PC(USB转TTL):直接用CH340或CP2102模块,3.3V供电,TX/RX交叉连接。最简单。
  2. STM32 ↔ 老式设备(RS232):必须加MAX232或SP3232。注意MAX232需要外接4个电解电容(1μF左右),别漏了。
  3. STM32 ↔ 长距离传输:用RS485,半双工,差分信号,抗干扰强。但协议要自己处理收发切换。
  4. 3.3V ↔ 5V TTL互连:如果STM32是3.3V,对方是5V TTL,TX方向可以直接连(3.3V被识别为高),RX方向需要加电阻分压或用电平转换芯片。
注意:STM32的GPIO耐压最高3.6V(部分型号是4.0V)。5V TTL的高电平是5V,直接输入会损坏IO口。我见过有人用两个电阻分压,但这种方法在高速通信时波形会变差。建议用TXS0108或74LVC4245这类专用电平转换芯片。