1. UART基础:从协议到实战
UART,全称是Universal Asynchronous Receiver/Transmitter。说白了,就是通用异步收发器。这是串行通信里最经典、最基础的一种方式。我刚开始接触FPGA时,第一个实现的通信协议就是UART。为什么?因为它简单、直观,而且几乎所有单片机、传感器、GPS模块都在用。
你想想看,一根发送线、一根接收线,就能让两个设备聊天。是不是很神奇?
1.1 UART协议概述
UART是一种异步串行通信协议。所谓异步,就是收发双方不需要共享时钟信号。那它们怎么同步呢?靠的是双方约定好的波特率。
UART的物理层很简单:
- TXD:发送数据线
- RXD:接收数据线
- GND:地线(共地参考)
全双工通信,意味着可以同时发送和接收。我在项目中遇到过一个问题:两个设备明明波特率设置一样,但通信就是乱码。后来发现,是晶振精度不够,导致两边的时间基准有偏差。嗯,这里要注意,晶振精度很关键。
核心要点:UART不需要时钟线,靠波特率同步。这是它最大的优点,也是它容易出问题的根源。
1.2 UART帧结构
UART的数据是以帧为单位传输的。一帧数据长什么样?我画个图给你看。
一帧UART数据包含以下几个部分:
- 起始位(Start Bit):1位,逻辑低电平。告诉接收方「我要开始发数据了」。
- 数据位(Data Bits):5~9位,常见的是8位。先发LSB(最低有效位)。
- 校验位(Parity Bit):可选。奇校验或偶校验,用于简单的错误检测。
- 停止位(Stop Bit):1位、1.5位或2位,逻辑高电平。表示一帧结束。
个人经验:我习惯用8位数据位、无校验、1位停止位,也就是常说的8N1。这是最通用的配置,兼容性最好。除非有特殊要求,否则别折腾。
1.3 波特率的概念与计算
波特率,就是每秒传输的符号数。对于UART来说,一个符号就是一比特。所以波特率就等于比特率。
常见的波特率有:9600、19200、38400、115200、921600等。
怎么计算呢?举个例子:
假设波特率是115200,传输一帧8N1的数据(1起始 + 8数据 + 1停止 = 10位)。
那么每秒能传输的字节数 = 115200 / 10 = 11520 字节/秒。
传输一个字节需要的时间 = 1 / 11520 ≈ 86.8 微秒。
| 波特率 | 每比特时间 | 每字节时间(8N1) | 每秒字节数 |
|---|---|---|---|
| 9600 | 104.17 μs | 1.04 ms | 960 |
| 38400 | 26.04 μs | 260.4 μs | 3840 |
| 115200 | 8.68 μs | 86.8 μs | 11520 |
| 921600 | 1.09 μs | 10.9 μs | 92160 |
避坑指南:我曾经在一个项目里,把波特率设成了115200,但FPGA的时钟是50MHz。算了一下,分频系数是50,000,000 / 115200 ≈ 434.03。取整434后,实际波特率变成了50,000,000 / 434 ≈ 115,207。误差只有0.006%,没问题。但如果时钟是27MHz,分频系数是27,000,000 / 115200 ≈ 234.375,取整后误差就大了。记住,误差要控制在2%以内,否则通信会丢数据。
1.4 UART的优缺点
聊了这么多,咱们客观评价一下UART。
优点:
- 硬件简单:只需要两根数据线加一根地线,成本极低。
- 无需时钟线:省了一根线,布线方便。
- 全双工:可以同时收发,效率高。
- 协议成熟:几乎所有MCU都内置UART外设,软件库也丰富。
- 远距离:配合RS232或RS485驱动,能传几十米甚至上千米。
缺点:
- 速度慢:最高也就几Mbps,跟SPI动辄几十Mbps没法比。
- 效率低:每传8位数据,要搭上2~3位的开销(起始+停止+校验)。
- 距离受限:TTL电平的UART,超过1米就容易出错。
- 多设备麻烦:点对点通信,不能像I2C那样挂多个从机。
- 无流控:没有硬件流控的话,接收方来不及处理数据就会丢包。
我的看法:UART就像一把瑞士军刀——不是最锋利的,但足够实用。在调试、日志输出、低速传感器通信这些场景里,它依然是首选。别因为它简单就轻视它,很多复杂的通信问题,最后都是UART的基础知识没搞明白。
好了,这一章的内容就到这里。UART看似简单,但它是理解更复杂协议(比如SPI、I2C、PCIe)的基石。下一章,我们会用FPGA亲手实现一个UART发送模块,到时候你就知道理论怎么落地了。
公众号:蓝海资料掘金营,微信deep3321