1. UART协议基础:异步串行通信原理
UART,全称是Universal Asynchronous Receiver/Transmitter,通用异步收发器。说白了,它就是让两个设备之间用一根线(或两根线)就能聊天的接口。我刚开始做嵌入式那会儿,觉得这东西太简单了,不就是发数据收数据嘛。后来踩了几个坑才发现,里面的门道真不少。
咱们先搞清楚一个核心概念:异步。什么叫异步?就是收发双方没有独立的时钟线。你想想看,没有时钟线,接收方怎么知道对方什么时候开始发数据、什么时候发完?嗯,这就是UART协议要解决的根本问题。
1.1 异步串行通信原理
串行通信,就是数据一位一位地按顺序传输。和并行通信(比如8根线同时传8位)相比,串行省线、省成本,适合长距离传输。UART就是串行通信的典型代表。
异步的关键在于:双方约定好相同的通信参数,然后靠数据帧本身的起始位和停止位来同步。我打个比方——就像两个人约定好每隔1秒说一个字,说话的人先喊一声“开始”,然后一个字一个字往外蹦,最后喊一声“结束”。听的人就知道什么时候该听、什么时候该停了。
核心要点:UART通信不需要时钟线,但收发双方必须使用相同的波特率、数据位、校验位和停止位配置。否则就是鸡同鸭讲。
1.2 数据帧结构详解
一个完整的UART数据帧,由以下几个部分组成。我在项目中调试过不下几十种UART设备,帧结构基本都一样,但细节上偶尔会有厂商自己搞的小花样。
| 字段 | 长度 | 说明 |
|---|---|---|
| 起始位 | 1位 | 固定为逻辑0(低电平),标志数据开始 |
| 数据位 | 5~9位(常用8位) | 实际传输的数据,LSB先发 |
| 校验位 | 0或1位 | 可选,用于简单检错 |
| 停止位 | 1、1.5或2位 | 固定为逻辑1(高电平),标志数据结束 |
起始位:这是整个帧的“发令枪”。空闲时总线是高电平,起始位拉低,接收方检测到这个下降沿就知道“要开始了”。我记得有一次调试GPS模块,发现数据偶尔丢帧,最后查出来是起始位检测的阈值设得太严了。
数据位:通常是8位,也就是一个字节。注意顺序——LSB(最低有效位)先发。比如你要发0x55(二进制01010101),实际线上先发的是最低位的1,然后依次是0、1、0、1、0、1、0。这个顺序搞反了,数据就全乱了。
校验位:可选,分奇校验和偶校验。奇校验就是让数据位+校验位里1的个数为奇数;偶校验就是让1的个数为偶数。说实话,这个校验只能检测奇数个位的翻转错误,能力有限。我一般只在调试阶段用,正式产品里更依赖CRC之类的强校验。
停止位:固定高电平,给接收方一点时间处理收到的数据。停止位可以是1位、1.5位或2位。1.5位比较少见,我就在一些老式工控设备上见过。2位停止位常用于低速、噪声大的环境,相当于多给点缓冲时间。
我的习惯:大多数场景用8数据位 + 无校验 + 1停止位(8N1),这是最通用的配置。如果环境噪声大,我会改成8E1(偶校验)或8O1(奇校验)。
1.3 波特率的概念与计算
波特率,就是每秒传输的符号数。在UART里,一个符号就是一位,所以波特率就等于比特率(bps)。常见的波特率有9600、19200、38400、115200等。
为什么是这些数字?不是随便定的。它们都是从一个基准时钟(通常是1.8432MHz或3.6864MHz)分频得来的。比如1.8432MHz ÷ 16 ÷ 9600 = 12,刚好能整除。这样分频出来的时钟误差最小。
咱们算一下传输一个字节需要多长时间。以115200bps、8N1为例:
一帧总位数 = 1(起始位)+ 8(数据位)+ 0(校验位)+ 1(停止位)= 10位
每帧时间 = 10 / 115200 ≈ 86.8微秒
每秒可传帧数 = 115200 / 10 = 11520帧/秒
也就是说,115200波特率下,理论上每秒能传11520个字节。但实际应用中,因为协议开销、中断处理、缓冲区管理等,实际吞吐量会打折扣。我做过一个测试,115200波特率下,纯数据吞吐量大概能到理论值的85%~90%。
避坑指南:我曾经在一个项目里,收发双方标称都是115200,但实际通信就是乱码。查了半天,发现一方的晶振是12MHz,另一方是11.0592MHz,分频出来的实际波特率差了3%左右。UART的波特率误差一般要求不超过±2%,超过这个范围就容易丢数据。所以,选晶振的时候尽量选那些能精确分频出标准波特率的型号。
1.4 波特率误差的影响
波特率误差是怎么影响通信的?咱们来算一笔账。
接收方在每个数据位的中间采样。如果波特率有误差,采样点就会逐渐偏移。假设误差是2%,传输10位后,采样点偏移了10 × 2% = 20%的位宽。也就是说,本来应该在位中间采样的,现在偏到了位边缘,很容易采错。
| 波特率误差 | 10位后偏移量 | 影响 |
|---|---|---|
| ±1% | ±10%位宽 | 安全,采样点仍在位中间区域 |
| ±2% | ±20%位宽 | 临界,可能偶尔出错 |
| ±3% | ±30%位宽 | 大概率出错,数据不可靠 |
| ±5% | ±50%位宽 | 完全无法通信 |
所以,设计UART通信时,一定要算好双方的波特率误差。我一般要求误差控制在±1%以内,这样即使温度变化导致晶振漂移,也能留出余量。
1.5 UART帧结构示意图
下面这张图展示了UART一帧数据的完整结构。我画的时候特意把每个字段的时序关系标清楚了。
1.6 常见配置与适用场景
不同的应用场景,UART配置也不一样。我整理了一个对照表,方便你快速选择。
| 应用场景 | 常用配置 | 波特率 | 说明 |
|---|---|---|---|
| 调试串口 | 8N1 | 115200 | 通用,速度快,适合打印日志 |
| GPS模块 | 8N1 | 9600/115200 | NMEA协议标准,老模块多9600 |
| 蓝牙模块 | 8N1 | 9600~115200 | HC-05/HC-06默认9600 |
| 工控设备 | 8E1/8O1 | 9600~38400 | 工业环境噪声大,加校验位 |
| 低功耗无线 | 8N1 | 2400~19200 | 低速省电,距离远 |
| 老旧设备 | 7E1 | 1200~9600 | 7位数据+偶校验,历史遗留 |
我的建议:新项目一律用115200/8N1。如果通信距离超过10米或者环境有电机、继电器等干扰源,降到38400或19200,并考虑加校验位。别为了省那点时间用最高波特率,稳定比速度重要。
1.7 本章小结
UART协议看起来简单,但每个细节都影响通信的可靠性。起始位、数据位、校验位、停止位,这四个字段构成了UART数据帧的骨架。波特率决定了通信速度,但误差控制才是关键。
我个人习惯,拿到一个新板子,第一件事就是用示波器看UART波形。起始位的下降沿清不清晰?停止位有没有被拉低?数据位的电平稳不稳定?这些一眼就能看出问题。纸上谈兵不如动手实测,后面几章咱们会一步步深入,从环回测试到数据帧校验,把UART通信吃透。