4、串口通信基础:UART协议帧格式与波特率
串口通信,说白了就是让两个设备之间用一根线“聊天”。在FPGA的世界里,UART(通用异步收发传输器)是最常用的串口协议之一。我刚开始接触FPGA时,第一个实战项目就是通过串口让开发板和电脑“对话”。那时候觉得这东西挺神奇的——一根线就能传数据?后来搞明白了,其实核心就两个东西:帧格式和波特率。
4.1 UART协议帧格式
UART的帧格式,你可以想象成快递包裹。每个包裹都有固定的包装方式,接收方按这个包装拆包,就能拿到里面的数据。一个标准的UART帧包含以下几个部分:
- 起始位:告诉接收方“我要开始发数据了”。通常是1位,逻辑0。
- 数据位:真正要传的数据。可以是5到8位,最常见的是8位。
- 校验位:可选,用于简单的错误检测。有奇校验、偶校验、无校验三种。
- 停止位:告诉接收方“这包数据发完了”。通常是1位或2位,逻辑1。
我习惯把帧格式画成下面这张图,一目了然:
你看,空闲时总线保持高电平(逻辑1)。当发送方把电平拉低(逻辑0),接收方就知道“来了来了,数据要来了”。这就是起始位的作用。
4.2 数据位与传输顺序
数据位是帧的核心。这里有个细节我当年踩过坑——UART传输数据时,是先传低位(LSB),再传高位(MSB)。举个例子,你要发送0x55(二进制01010101),实际在线上看到的顺序是:
起始位(0) → 1 → 0 → 1 → 0 → 1 → 0 → 1 → 0 → 校验位 → 停止位(1)
嗯,这里要注意:如果你用逻辑分析仪抓波形,别按习惯从左到右读成高位到低位,否则数据全反了。我有个同事就因为这个bug调了一整天,最后发现是字节序搞反了。
4.3 校验位:奇校验与偶校验
校验位是用来检测传输过程中有没有发生错误的。它的原理很简单:
- 奇校验:数据位中1的个数 + 校验位 = 奇数
- 偶校验:数据位中1的个数 + 校验位 = 偶数
举个例子,数据位是0x55(二进制01010101),里面有4个1(偶数)。如果选奇校验,校验位就得是1,让总1的个数变成5(奇数)。如果选偶校验,校验位就是0,保持4个1(偶数)。
4.4 停止位
停止位就是给接收方一点时间“消化”刚收到的数据。常见的有1位、1.5位、2位三种。我个人习惯用1位停止位,除非对方芯片手册明确要求用2位。
为什么会有1.5位这种奇怪的存在?其实是因为早期的一些UART芯片,时钟精度不够,需要更长的停止位来保证下一帧起始位能被正确识别。现在FPGA的时钟精度很高,1位停止位完全够用。
4.5 波特率:通信的“节拍器”
波特率,说白了就是每秒能传多少位数据。单位是bps(bits per second)。常见的波特率有:
| 波特率 (bps) | 每位时间 (μs) | 常见用途 |
|---|---|---|
| 9600 | 104.17 | 调试、低速设备 |
| 19200 | 52.08 | 一般通信 |
| 38400 | 26.04 | 较高速通信 |
| 115200 | 8.68 | FPGA调试常用 |
| 921600 | 1.09 | 高速数据传输 |
波特率怎么算?很简单:每位的时间 = 1 / 波特率。比如115200bps,每位就是1/115200 ≈ 8.68微秒。在FPGA里,你需要用系统时钟去“数”这个时间。假设系统时钟是50MHz(周期20ns),那么每位需要数 8.68μs / 20ns ≈ 434个时钟周期。
核心公式:
波特率时钟分频值 = 系统时钟频率 / 目标波特率
例如:50MHz / 115200 ≈ 434
4.6 波特率误差与容限
这里有个坑,我必须要说——波特率不可能完全精确。因为分频值通常是整数,而计算结果往往是小数。比如50MHz分频到115200,434.0278...取整434,实际波特率是50MHz/434 ≈ 115207,误差只有0.006%,完全没问题。
但如果你用27MHz的晶振分频到115200呢?27MHz/115200 = 234.375,取整234,实际波特率是27MHz/234 ≈ 115385,误差0.16%。还在可接受范围内(一般要求误差小于2%)。
4.7 异步通信的关键:没有时钟线
UART叫“异步”收发,意思是发送方和接收方没有单独的时钟线。那它们怎么同步呢?靠的是双方约定好相同的波特率,然后接收方在起始位的下降沿“对齐”自己的采样时钟。
具体来说,接收方会用一个比波特率高16倍(或8倍、64倍)的时钟去采样。为什么是16倍?因为这样可以在一个位时间内采样16次,取中间3次做多数表决,能有效避免毛刺干扰。我习惯用16倍过采样,可靠性更高。
你想想看,如果发送方和接收方的波特率有误差,每个位时间的采样点就会逐渐偏移。误差越大,偏移越快。当偏移超过半个位时间时,就会采到错误的电平。这就是为什么波特率误差要控制在2%以内——保证一帧数据(最多10位)不会偏移太多。
4.8 实战中的参数选择
在实际项目中,我一般这样选参数:
- 数据位:8位,因为一个字节正好8位,方便处理
- 校验位:无,除非环境恶劣
- 停止位:1位
- 波特率:调试用115200,量产用9600或19200(更稳定)
这样一帧就是1 + 8 + 0 + 1 = 10位。在115200波特率下,每秒能传115200/10 = 11520字节,约11.25KB/s。对于大多数调试场景,这个速度足够了。
好了,UART协议的基础就这些。说白了就是“约定好速度,按固定格式打包,一根线来回传”。下一节我们会用Verilog把接收逻辑写出来,到时候你就知道这些理论知识怎么落地了。