1. UART通信基础:从协议到实战
各位同学好,我是你们的老朋友。今天咱们正式开始《单片机UART硬件驱动开发与寄存器编程实战》的第一章。
UART这个东西,说白了就是让两个设备之间能“说上话”。我做了十几年嵌入式,接触过的通信协议少说也有几十种,但UART始终是用的最多的那个。为什么?因为它简单、可靠、几乎每个单片机都自带。
这一章,咱们先把UART通信的底子打牢。你只有搞懂了数据是怎么在线上跑的,后面写驱动、调寄存器才不会发懵。
1.1 什么是UART协议?
UART的全称是Universal Asynchronous Receiver/Transmitter,通用异步收发器。它定义了一套规则,让两个设备通过一根发送线(TX)和一根接收线(RX)进行串行通信。
注意,UART本身是硬件模块,它负责把并行数据转成串行比特流发出去,再把收到的串行比特流转回并行数据。而协议,就是约定好这些比特怎么排列、什么时候开始、什么时候结束。
核心特点:
- 异步:没有独立的时钟线,靠双方约定的波特率同步
- 全双工:发送和接收可以同时进行
- 点对点:通常只连接两个设备
- 串行:数据逐位传输,节省引脚
我刚开始做项目时,总觉得异步通信不靠谱——没有时钟线,两边怎么对齐?后来调通了第一个UART驱动,看到示波器上波形严丝合缝,才明白这个设计有多巧妙。
1.2 异步串行通信原理
异步通信的核心思想是:双方各自用自己的时钟,但约定好相同的速率。
发送方在TX线上逐位发送数据,接收方在RX线上采样。没有时钟线,那接收方怎么知道什么时候该采样?答案是:靠起始位和波特率。
我画了一张图,帮你理解这个过程:
你看,发送方和接收方各自有独立的时钟,但都配置成9600波特率。发送方按这个速率逐位发送,接收方也按这个速率采样。只要两边时钟误差在允许范围内(通常±2%以内),通信就能正常进行。
我的经验:实际项目中,晶振精度和温度漂移都会影响时钟。我曾经在一个户外项目中,白天40度、晚上零下10度,UART通信时好时坏。后来发现是晶振温漂导致波特率偏差过大。从那以后,我只要做宽温产品,都会选用±25ppm以内的晶振。
1.3 数据帧格式详解
UART的数据帧,就是一次完整传输的基本单位。它由几个固定部分组成:
| 字段 | 长度 | 电平状态 | 作用 |
|---|---|---|---|
| 空闲位 | 不定 | 高电平(1) | 线路空闲时的默认状态 |
| 起始位 | 1 bit | 低电平(0) | 通知接收方“准备接收数据” |
| 数据位 | 5~9 bits | 高/低电平 | 实际传输的数据,通常LSB先发 |
| 校验位 | 0/1 bit | 奇/偶校验 | 可选,用于检错 |
| 停止位 | 1/1.5/2 bits | 高电平(1) | 表示一帧结束,给接收方处理时间 |
咱们逐个拆开看:
起始位
线路空闲时是高电平。发送方把电平拉低,这就是起始位。接收方检测到这个下降沿,就知道“数据要来了”。
我习惯在示波器上看起始位——那个下降沿特别明显。如果波形上找不到下降沿,说明两边根本没对上。
数据位
起始位之后,就是真正的数据。通常是5到9位,最常用的是8位(正好一个字节)。数据按低位在前(LSB First)的顺序发送。
举个例子,你要发送0x55(二进制01010101),实际线上发送的顺序是:1→0→1→0→1→0→1→0。为什么?因为LSB是第0位,先发。
注意:有些老设备或特殊协议会使用MSB First,但绝大多数UART都是LSB First。如果你发现通信数据全是反的,先检查一下位顺序配置。
校验位
校验位是可选的,用于检测传输错误。有两种模式:
- 奇校验(Odd):数据位中1的个数 + 校验位 = 奇数
- 偶校验(Even):数据位中1的个数 + 校验位 = 偶数
比如数据是0x55(二进制01010101,有4个1),如果选偶校验,校验位就是0(4+0=4,偶数);如果选奇校验,校验位就是1(4+1=5,奇数)。
说实话,现在大多数应用都不用校验位了。因为UART本身误码率很低,而且上层协议通常有更可靠的校验机制(比如CRC)。我一般只在长距离传输或噪声环境恶劣时才启用校验位。
停止位
数据位(和校验位)发完后,发送方把电平拉高并保持至少1个比特时间。这就是停止位。它给接收方留出时间处理刚收到的数据,准备接收下一帧。
停止位可以是1位、1.5位或2位。1.5位比较少见,我几乎没用过。2位停止位常用于低速设备或机械式设备,给它们多一点处理时间。
1.4 波特率概念
波特率(Baud Rate)就是每秒传输的符号数。在UART中,一个符号就是一个比特,所以波特率就等于比特率(bps)。
常见的波特率有:
| 波特率(bps) | 每比特时间 | 典型应用场景 |
|---|---|---|
| 2400 | 416.7 μs | 低速设备、老旧设备 |
| 9600 | 104.2 μs | 最常用,兼容性好 |
| 115200 | 8.68 μs | 高速通信,调试输出 |
| 921600 | 1.09 μs | 高速数据传输,短距离 |
为什么是这些数字?它们都是从系统时钟分频得来的。比如一个16MHz的单片机,要得到9600波特率,需要分频系数 = 16,000,000 / (16 × 9600) ≈ 104.17。取整后就是104,实际波特率 = 16,000,000 / (16 × 104) ≈ 9615,误差只有0.16%,完全可用。
波特率误差公式:
实际波特率 = 系统时钟频率 / (16 × 分频系数)
误差 = |实际波特率 - 目标波特率| / 目标波特率 × 100%
一般要求误差 < 2%,否则可能丢数据。
我建议新手从9600开始。这个速率足够慢,示波器上波形清晰,调试方便。等调通了再逐步提高速率。我曾经见过有人一上来就设921600,结果死活调不通,最后发现是线太长信号衰减了。
1.5 常见配置组合
UART的配置通常写成“波特率-数据位-校验位-停止位”的格式,比如:
- 9600-8-N-1:9600波特率,8位数据,无校验,1位停止位(最常用)
- 115200-8-E-1:115200波特率,8位数据,偶校验,1位停止位
- 4800-7-O-2:4800波特率,7位数据,奇校验,2位停止位
我90%的项目都用9600-8-N-1。简单、可靠、兼容性好。除非有特殊需求,否则别搞花里胡哨的配置。
避坑指南:我曾经在一个项目里,设备A用9600-8-N-1,设备B用9600-8-E-1。两边波特率一样,但校验位不匹配。结果数据能收到,但全是乱码。我查了整整两天才找到原因。所以,通信前一定要确认两边配置完全一致。
1.6 本章小结
这一章咱们把UART通信的基础过了一遍。你知道了:
- UART是异步串行通信,没有时钟线
- 数据帧由起始位、数据位、校验位、停止位组成
- 波特率决定了传输速度,两边必须一致
- 最常用的配置是9600-8-N-1
这些概念看起来简单,但它们是后面所有内容的基础。下一章,咱们会深入UART硬件模块的内部结构,看看那些寄存器到底是怎么工作的。
记住,搞嵌入式,基础不牢,地动山摇。把这些概念吃透了,后面写驱动就是水到渠成的事。