串行总线协议

本页目录

读取同一个传感器寄存器,用 UART 可能先发一段厂商命令,用 I²C 要先定位寄存器再切换读方向,用 SPI 则可能连续发送命令、地址和空时钟。协议名称只说明一部分传输规则,真正能读到数据还取决于设备命令格式、时序和电气连接。本篇沿一次传输展开三种接口,再计算有效吞吐。

接口同步方式与常见信号数据边界由谁定义常被忽略的问题
UART异步,TX/RX起始、数据、校验、停止位构成字符;消息另定时钟误差、帧边界、接收溢出
I²C同步,SCL/SDA地址、应答、START/STOP地址表示、重复起始、拉伸与上升时间
SPI同步,SCK、数据线、CS设备规定的片选和时钟序列模式、首位建立、dummy cycles、MISO 释放

表中不计电源、参考地和额外中断/流控引脚。串行描述按时间传位,不等于所有接口只有一根导线;SPI 有多根不同方向信号,QSPI 有多条数据线,高速串行也可使用多 lane。并行接口同样仍有用途,不能说现代高速接口全是串行。

UART:双方各自计时,在每个起始位重新对齐

常见非反相 UART 空闲为高,起始位拉低,随后按约定波特率发送数据位。8N1 表示 8 个数据位、无校验、1 个停止位;停止位为高。发送和接收方向可以独立工作,但单线或外接半双工收发器的实现不一定能同时收发。

Start0D01D10D21D30D40D51D60D71Stop1UART 8N1 发送 0xA5:数据低位先行,每字节占 10 个位时间115200 bit/s:每位约 8.681 μs;每字节约 86.806 μs高电平表示 UART 引脚侧逻辑;RS-232 线缆侧电压与极性需经过收发器转换。

0xA5 的二进制按高位书写是 10100101,UART 通常先发 D0,所以数据位顺序为 1、0、1、0、0、1、0、1。这里完整字符包括起始和停止,共 10 位。115200 bit/s 连续传输时理论有效数据率为 115200/10=11520 byte/s;100 字节需要约 8.681 ms,还没计消息头、校验、字节间隔和设备等待。

波特率误差是累计的,不能只看设置值相同

接收器识别起始位后,用本地时钟估计每个位中心。若接收与发送时钟相差 2%,到起始后约 9.5 个位时间处,理想化累计偏移约 0.19 位;再叠加起始检测误差、抖动与边沿畸变,可用窗口进一步缩小。两端都配置 115200,并不保证晶振和分频结果完全相同。

允许误差取决于数据/校验长度、过采样方式和具体接收器,不宜写成所有 UART 都适用的固定 ±2%。应把两端时钟精度、温漂与分频误差组合起来,查目标硬件的容差表。Microchip:异步接收工作范围

波形正确也可能丢数据:接收 FIFO 满而软件/DMA 未及时读取会 overrun;停止位位置不是有效高电平可能产生 framing error;奇偶校验只能检测部分错误,不能代替消息 CRC。UART 本身没有统一应用消息格式,可采用明确长度、分隔与转义、校验及超时;不能假设一次 read 就对应一条完整命令。

RTS/CTS 可为设备间提供硬件流控,但要考虑对方停止发送前仍在途的字符和 FIFO 容量。TX FIFO 空也不一定意味着最后停止位已从引脚发完,关闭发送器或撤销 RS-485 DE 应使用真正的发送完成条件。UART 可经 RS-485 等电气层参与多点系统,不能笼统限定为“一对一、低于 1 Mbit/s”。

采样点怎样逐位偏离中心?

正误差表示接收时钟更快,采样点向左漂;负误差向右漂。改变发送字节可看到:采样已偏离理想位中心,也未必马上产生错误,因为相邻位可能相同。因此单个字节成功不能证明整个误差预算合格。

正在呈现知识画面
采样点怎样逐位偏离中心?

字符内的时钟误差逐位累计;同样的起始对齐不代表后面的采样仍在中心。

I²C:总线控制、字节应答与设备寄存器是三层概念

在常见 Standard/Fast/Fast-mode Plus 双向模式中,SDA、SCL 使用开漏/开集形式和上拉。控制器安排时钟,但总线最终电平由所有连接设备决定;目标设备若支持时钟拉伸,可以在控制器释放 SCL 后继续拉低。控制器需观察真实 SCL,且驱动与设备都要支持所用特性。

普通数据位在 SCL 高期间保持稳定,SDA 在 SCL 高时下降定义 START,上升定义 STOP。每个 8 位字节之后有第 9 个应答时钟:发送方释放 SDA,由接收方拉低表示 ACK,保持高表示 NACK。NACK 不必然是故障,控制器读取最后一个字节后通常主动 NACK,表示不再接收后续字节。NXP:I²C 总线规范

连续读一次,先明确谁在驱动 SDA

假设一个教学设备使用 7 位地址 0x50、8 位寄存器地址,寄存器 0x10 当前值为 0x5A,要求用重复起始读出:

S起始0xA0地址+WACK目标确认0x10寄存器ACK目标确认Sr重复起始0xA1地址+RACK目标确认0x5A目标发数据NACK / P控制器结束地址为 7 位 0x50;0xA0 / 0xA1 是上线路径中的地址字节一次寄存器读取:先写寄存器指针,再重复起始切换为读S、Sr、P 不占一个完整数据字节;每个地址/数据字节均占 8 位加第 9 个应答时钟。

控制器先发送地址字节 (0x50<<1)|0=0xA0,目标 ACK;再发送寄存器指针 0x10,目标 ACK。控制器不释放总线,发送 repeated START,然后发读地址字节 0xA1。目标 ACK 后成为数据发送方,控制器读到 0x5A,在第 9 个时钟 NACK,最后 STOP。

这里有四个地址/数据字节,每个占 9 个时钟,共 36 个 SCL 周期。400 kHz 下仅这些时钟需 90 μs,此外还有 START/STOP、总线空闲、可能的拉伸和软件间隙。连续读 N 字节、设备支持地址自动递增时,可用 9(N+3) 个时钟摊薄前导开销;前 N−1 个数据字节由控制器 ACK,最后一个 NACK。

7 位设备地址 0x50 与上线路径中的 0xA0/0xA1 是两种表示。多数 API 接收 7 位地址,但也有库要求左移后的值,应查 API,不要连续左移两次。10 位寻址有专门头部格式,不能把 10 位值直接塞入一个字节。地址通常由器件固定或地址脚配置,并非天然全局唯一;同地址冲突需改地址、分段或使用多路复用器。

ACK 不是业务成功,仲裁不是软件锁

地址 NACK 可能表示无设备、地址错误、掉电或设备暂时不响应;数据 ACK 只表示本次字节被应答,并不一定表示 EEPROM 已完成内部编程。写后等待、忙状态或 ACK polling 必须按器件协议安排,不能凭总线 ACK 就断言非易失数据已落盘。

多控制器场景利用开漏总线逐位仲裁:发送逻辑 1 是释放 SDA,若实际读到 0,说明另一方正在发送优先的低电平,当前控制器应按规范退出。硬件是否完整支持多控制器、拉伸和仲裁,要看具体控制器与驱动;“总线规范允许”不等于每个外设实现都允许。

SDA 卡低时,不应无限阻塞系统。先超时确认故障,再按控制器/设备和总线状态执行恢复;规范提供 SDA 卡低时尝试时钟脉冲的 bus clear 方法,但 SCL 被持续拉低、设备掉电或硬件短路不能靠盲发九个时钟保证恢复。恢复后还应确认设备内部事务状态,避免把中断的写入当成完整成功。

上升时间按模式查,不能把频率变成固定电阻

常见模式最高 SCL 频率上升时间最大值,30%~70%
Standard-mode100 kHz1000 ns
Fast-mode400 kHz300 ns
Fast-mode Plus1 MHz120 ns

高速度模式还有不同的进入流程和电气约束;上表并非 I²C 所有模式的全集。上升时间上限应与工作模式对应,不能只按时钟周期推算。Rp,max≈tr/(0.8473Cb),还要用 Rp,min≈(Vpullup−VOL,max)/IOL 限制灌电流。NXP:UM10204 时序表与上拉设计

例如 200 pF 总线用 4.7 kΩ,上升约 796 ns,可落在 Standard-mode 的上升时间要求内,却不满足 Fast-mode。400 kHz 时,单按上升时间算上限约 1.77 kΩ;还必须与灌电流下限形成可行区间。板上多个模块各自带上拉时,等效值是并联,电容也会增加,不应只看一块板的电阻。

SPI:共享时钟,仍必须给数据留出建立时间

典型四线 SPI 中,控制器输出 SCK、MOSI 和 CS_n,外设输出 MISO。共享总线时通常用独立 CS 选择外设,被取消选择的外设需按手册释放 MISO。也有三线半双工、只写、无独立片选、Dual/Quad 等变体,因此“四根线”只是常见结构。

ModeCPOL:空闲电平CPHA采样边沿通常更新数据的边沿
000上升下降
101下降上升
210下降上升
311上升下降

CPHA=0 在第一个活动边沿采样,首位必须提前准备;CPHA=1 在第二个边沿采样,第一个边沿用于推出首位。CPOL 决定第一个活动边沿是上升还是下降。不能只记“上升沿读”而漏掉空闲极性和片选时序。Analog Devices:SPI 接口

采样 b7采样 b6采样 b5采样 b4SCK数据首位提前有效SPI Mode 0:上升沿采样,下降沿准备下一位模式図前 4 位为 1010,CS_n 在整个片段保持有效;真实数据需满足采样沿两侧的建立/保持时间。

假设 Mode 0 的半周期给外设输出提供 20 ns 预算,而外设时钟到 MISO 延迟为 12 ns、线路和板内传播共 4 ns、控制器建立要求 5 ns,总需求 21 ns,仍然不够。总时钟频率、采样延迟选项、负载和布线都要参与预算,不能因为双方标注支持同一 MHz 就忽略具体模式与时序条件。

有时钟就移位,但移进来的不一定是有效数据

假设设备定义一次读操作为 8 位命令、24 位地址、8 个 dummy 时钟,再返回 16 字节,整个事务需要 8+24+8+16×8=168 个时钟。10 MHz 时需 16.8 μs,有效载荷约 7.62 Mbit/s;即使没有 I²C 式地址阶段,SPI 设备自己的命令、地址和等待依然是开销。

读数据时控制器通常仍须发送占位字节以产生时钟;命令阶段同时收到的 MISO 位可能无效。要按设备定义决定何时丢弃、何时存入接收缓冲。CS_n 是否需跨所有字节持续为低、字节间是否允许暂停、末尾保持多久,均属于事务格式,不能简单把每个字节当成独立访问。

菊花链只有设备支持串接移位语义时才成立,不能把任意 SPI 外设的 MISO 接到下一块 MOSI。基础 SPI 没有统一 ACK 或流控,不代表设备不能提供忙位、READY 引脚、CRC 或重试规则。以太网芯片也要区分通过 SPI 访问的控制器与使用 MDIO/MDC 管理的 PHY,不能统称“以太网 PHY 都用 SPI”。

如何读一次异常传输

观察优先核对进一步定位
UART 字符稳定但整体乱码波特率、位数、校验、反相原始字节与编码、消息格式
UART 偶发丢段overrun、DMA/FIFO、流控阻塞时间与消息校验
I²C 地址后 NACK7 位/8 位地址、电源、地址脚设备忙、上升沿、总线段
I²C 永久等待SCL 拉伸或线路卡低超时、恢复、器件状态
SPI 数据整体移一位CPOL/CPHA、首位建立CS 时序、采样沿和传播延迟
SPI 多设备一起接才失败MISO 高阻与片选上拉、负载、冲突驱动

逻辑分析仪可解释位序和事务,但门限附近的慢沿、过冲、地弹和争用还需要模拟波形。解码器显示 ACK 或一个十六进制值,并不证明电气裕量充足。

接口选择最终取决于设备支持、吞吐、引脚预算、拓扑和功耗状态。I²C 空闲时上拉通常不耗持续直流,但总线低电平、设备静态功耗和通信占空比会改变能耗;不能把 I²C 直接列为所有电池设备的最低功耗方案。长线差分可选择合适物理层,但 RS-485 不限定只能承载 UART 字符格式。

复算帧与吞吐

from math import isclose

value = 0xA5
bits = [(value >> i) & 1 for i in range(8)]
assert bits == [1, 0, 1, 0, 0, 1, 0, 1]
assert sum(bit << i for i, bit in enumerate(bits)) == value
assert isclose(100*10/115200, 0.008680555555555556)
assert (0x50 << 1, (0x50 << 1) | 1) == (0xA0, 0xA1)
assert isclose(36/400000, 90e-6)
assert 0.8473*4700*200e-12 < 1000e-9
assert 0.8473*4700*200e-12 > 300e-9
assert 12+4+5 > 20
cycles = 8+24+8+16*8
assert cycles == 168
assert isclose(cycles/10e6, 16.8e-6)
assert isclose(128/(cycles/10e6), 7.619047619e6, rel_tol=1e-9)
print('UART 位序、I²C 地址/时钟及 SPI 事务吞吐检查通过')

这些检查复算教学事务,不代表已连接真实 UART、传感器或 SPI Flash。

继续查阅