一、UART协议深度解析:从历史到实战
各位同学,咱们今天聊UART。说实话,这可能是嵌入式开发里最基础、也最容易被忽视的协议之一。我做了十几年嵌入式,见过太多工程师在UART上翻车——波特率算错、帧格式配反、中断处理不当……其实这些坑,只要把协议底层搞明白,都能避免。
UART全称是Universal Asynchronous Receiver/Transmitter,通用异步收发器。它诞生于上世纪60年代,最初是为了解决计算机与电传打字机之间的通信问题。你想想看,那时候的通信设备多贵啊,能有一套标准化的串行通信方案,简直是救星。
核心要点:UART是一种异步串行通信协议,不需要时钟线,靠双方约定好的波特率和帧格式来传输数据。
1.1 异步串行通信原理
什么叫异步?说白了,就是收发双方没有独立的时钟线来同步。每个设备用自己的时钟,只要大家跑得一样快就行。这跟I2C、SPI那种同步协议完全不同——它们有专门的时钟线,主机负责打拍子。
我刚开始学的时候,总觉得异步不靠谱:万一两边时钟有偏差怎么办?嗯,这里要注意,UART确实对时钟精度有要求,一般允许的误差在±2%以内。超过这个范围,数据就可能错位。
串行通信的意思更简单:数据一位一位地传。不像并行通信一次传8位、16位,串行就是一根线,按顺序来。好处是节省引脚,坏处是速度相对慢。但在大多数嵌入式场景下,115200bps已经够用了。
个人经验:我在一个工业控制项目里,曾经用9600bps的UART连接PLC和传感器,跑了三年没出过问题。别盲目追求高速,稳定才是王道。
1.2 帧格式详解
UART的帧格式,说白了就是数据包的包装方式。每一帧包含以下几个部分:
| 字段 | 长度 | 说明 |
|---|---|---|
| 起始位 | 1位 | 逻辑低电平,标志数据传输开始 |
| 数据位 | 5~9位 | 实际传输的数据,常用8位 |
| 校验位 | 0或1位 | 奇偶校验,可选 |
| 停止位 | 1、1.5或2位 | 逻辑高电平,标志帧结束 |
起始位:这是关键。线路空闲时是高电平,起始位拉低,接收端检测到这个下降沿就知道数据要来了。我曾经遇到过一个bug,就是因为线路噪声导致误触发起始位,整个数据流全乱了。后来加了硬件滤波才解决。
数据位:最常用的是8位,正好一个字节。也有用7位的,比如传输ASCII字符时。数据位从最低有效位(LSB)开始发送,这是UART的惯例。
校验位:分奇校验和偶校验。奇校验保证数据位+校验位里1的个数是奇数,偶校验则是偶数。说实话,现在很多应用已经不用校验位了,因为CRC或者上层协议更可靠。但如果你在噪声环境里跑UART,校验位还是能挡掉一部分错误。
停止位:至少1位高电平。为什么要有停止位?给接收端一点时间处理刚收到的数据。1.5位和2位停止位用在低速设备或机械式设备上,比如老式电传打字机。
避坑指南:我曾经在一个项目里,把停止位设成了2位,结果接收端死活收不到完整数据。查了半天才发现,对方只支持1位停止位。记住:收发双方的帧格式必须完全一致,否则通信失败。
1.3 波特率概念与计算
波特率,单位是bps(bits per second),表示每秒传输的位数。注意,是位数,不是字节数。一个字节通常需要10位来传输(1起始+8数据+1停止),所以115200bps的理论最大字节速率是11520字节/秒。
波特率的计算公式很简单:
波特率 = 系统时钟频率 / (16 × 分频系数)
这里的16是UART模块内部采样倍数,大多数UART用16倍过采样。分频系数就是你要配置的寄存器值。
举个例子,假设系统时钟是16MHz,你想要115200bps:
分频系数 = 16,000,000 / (16 × 115200) ≈ 8.68
取整后,实际波特率 = 16,000,000 / (16 × 9) ≈ 111,111 bps
误差 = (115200 - 111111) / 115200 ≈ 3.55%
这个误差已经超过±2%了,通信可能不稳定。怎么办?换一个能整除的时钟频率,或者用更高的系统时钟。
关键结论:波特率误差必须控制在±2%以内,否则数据会出错。我建议你在配置时,先算一下实际误差,别想当然。
常见的标准波特率有:9600、19200、38400、57600、115200、230400、460800、921600。其中115200是嵌入式开发中最常用的,兼顾速度和稳定性。
1.4 UART通信流程与知识体系
下面这张图,是我自己总结的UART知识体系。你把它记在脑子里,后面写驱动的时候就不会迷路。
这张图把UART协议分成三层:物理层管电平标准和波特率,数据链路层管帧格式和校验,应用层管数据怎么用。后面写驱动的时候,你就是在跟数据链路层打交道——配置寄存器、设置帧格式、处理收发缓冲。
1.5 常见问题与避坑
- 波特率误差过大:一定要算实际误差,别只看标称值。我见过有人用8MHz时钟配115200,误差超过5%,数据全是乱码。
- 帧格式不匹配:收发双方的数据位、校验位、停止位必须一致。调试时先用逻辑分析仪抓波形,一眼就能看出问题。
- 起始位误触发:线路噪声可能导致接收端误判起始位。加硬件滤波或在软件里做去抖处理。
- 缓冲区溢出:接收端处理速度跟不上发送端,数据就丢了。要么用流控(RTS/CTS),要么用DMA。
调试技巧:我习惯在调试UART时,先用示波器或逻辑分析仪看波形。起始位的下降沿、数据位的电平、停止位的高电平,一眼就能判断通信是否正常。比盯着串口助手看乱码强多了。
好了,UART协议的基础就讲到这里。记住:协议是死的,但应用是活的。把帧格式和波特率吃透,后面写驱动、调DMA,你才能游刃有余。