1. UART基础与协议概述
UART,全称是Universal Asynchronous Receiver/Transmitter,通用异步收发器。说实话,这可能是嵌入式工程师打交道最多的外设之一了。我刚开始做嵌入式那会儿,调试信息全靠它打印,跟传感器通信也靠它,甚至两个单片机之间聊天还是靠它。你想想看,一个没有UART的板子,就像一个人没了嘴巴和耳朵,憋屈得很。
1.1 UART的历史:从电传到串口
UART的历史其实挺有意思的。它的老祖宗可以追溯到电报时代的电传打字机,那时候就已经在用串行方式传输数据了。后来计算机出现了,需要一种简单可靠的方式连接外设,比如调制解调器、打印机,于是RS-232标准在1960年代诞生了。
我记得第一次看到RS-232的DB9接口时,心里还在嘀咕:这玩意儿怎么这么大?但你别看它个头大,它可是定义了UART通信的电气基础。后来随着微控制器的发展,UART被集成到了芯片内部,变成了我们今天用的这种形式。
核心要点:UART从独立芯片演变为MCU内置外设,但通信的基本原理——异步串行——几乎没有变过。这就像汽车的发动机,技术迭代了无数次,但烧油做功的原理还是那个原理。
1.2 异步串行通信原理
什么叫异步?说白了,就是收发双方没有独立的时钟线。你想想看,两个人打电话,如果一方说话另一方必须同时听,那就需要同步。但UART不是这样,它只靠一根发送线(TX)和一根接收线(RX)就能干活。
那问题来了:没有时钟线,接收方怎么知道什么时候该采样数据?
答案是:双方事先约定好速率。这个速率就是波特率。接收方用自己的时钟去采样RX线上的电平变化,只要双方时钟偏差在容忍范围内,就能正确通信。
我在项目中遇到过一个问题:两块板子用UART通信,一块用16MHz晶振,另一块用8MHz晶振,波特率都配成了115200。结果数据总是偶尔出错。后来一查,是晶振精度不够,导致波特率偏差超过了2%。从那以后,我养成了一个习惯:高速通信时一定用带小数分频的MCU,或者干脆用外部高精度晶振。
1.3 波特率:通信的节拍器
波特率,单位是bps(bits per second),表示每秒传输的比特数。常见的波特率有:9600、19200、38400、115200、921600等。
| 波特率 (bps) | 每比特时间 (μs) | 典型应用场景 |
|---|---|---|
| 9600 | 104.17 | 调试终端、低速传感器 |
| 38400 | 26.04 | GPS模块、蓝牙模块 |
| 115200 | 8.68 | 常用调试、WiFi模块 |
| 921600 | 1.09 | 高速数据传输、固件升级 |
我的经验:调试阶段用115200最舒服,速度够快,稳定性也还行。量产产品如果通信距离超过1米,我建议降到38400或更低,抗干扰能力会好很多。
1.4 数据帧格式:一帧数据的结构
UART的数据是一帧一帧传输的。每一帧包含以下几个部分:
- 起始位(Start Bit):1位,逻辑0。告诉接收方“我要开始发数据了”。
- 数据位(Data Bits):5~9位,常用8位。就是你要传的实际数据。
- 校验位(Parity Bit):可选,0或1位。用于简单的错误检测。
- 停止位(Stop Bit):1、1.5或2位,逻辑1。表示这一帧结束了。
我画了一张图,帮你直观理解一帧数据的结构:
1.5 常见数据帧格式
实际项目中,最常用的数据帧格式是8N1,也就是8位数据位、无校验、1位停止位。为什么是8N1?因为8位刚好是一个字节,无校验省去了计算开销,1位停止位效率最高。
当然,也有其他格式:
| 格式名称 | 数据位 | 校验位 | 停止位 | 典型场景 |
|---|---|---|---|---|
| 8N1 | 8 | 无 | 1 | 通用调试、传感器 |
| 8E1 | 8 | 偶校验 | 1 | 工业通信(Modbus) |
| 8O1 | 8 | 奇校验 | 1 | 老式设备兼容 |
| 9N1 | 9 | 无 | 1 | 特殊协议(如DMX512) |
| 7E2 | 7 | 偶校验 | 2 | 老式电传打字机 |
注意:我曾经在一个项目中,设备手册写的是8N1,结果死活通信不上。折腾了半天才发现,手册写错了,实际是8E1。所以拿到一个新设备,第一件事就是确认它的真实帧格式,别信手册,信逻辑分析仪。
1.6 校验位:简单的错误检测
校验位分三种:无校验(N)、奇校验(O)、偶校验(E)。
- 无校验(N):不发送校验位,帧长度最短,效率最高。
- 奇校验(O):保证数据位+校验位中1的个数为奇数。
- 偶校验(E):保证数据位+校验位中1的个数为偶数。
举个例子:你要发送0x55(二进制01010101),里面有4个1。
- 如果选偶校验,校验位就是0,因为4已经是偶数了。
- 如果选奇校验,校验位就是1,让1的总数变成5(奇数)。
接收方收到数据后,会重新计算1的个数,跟校验位对比。如果不一致,就说明传输过程中有比特翻转了。
我的建议:校验位只能检测奇数个比特错误,检测不了偶数个错误。如果你对数据可靠性要求高,别指望校验位,上CRC或者更高级的协议层校验才是正道。
1.7 停止位:帧与帧之间的间隔
停止位的作用是给接收方一个缓冲时间,让它处理完当前帧,准备好接收下一帧。停止位可以是1位、1.5位或2位。
什么时候用2位停止位?
- 接收方的时钟精度比较差的时候。
- 通信距离比较长,信号有反射的时候。
- 老式设备,处理速度慢的时候。
我一般默认用1位停止位。只有在遇到通信不稳定、偶尔丢帧的情况下,才会尝试改成2位。嗯,这里要注意:改成2位停止位会降低有效数据传输速率,因为每帧多花了1个比特的时间。
1.8 本章小结
UART的基础知识就这些了。说白了,它就是一套约定:双方说好波特率、数据位、校验位、停止位,然后一根线发、一根线收,没有时钟线也能干活。这个设计从1960年代用到现在,足以证明它的经典和实用。