3 I3C数据链路层:帧格式、起始条件、重复起始、停止条件

数据链路层,说白了就是规定“怎么把一串比特流组织成一个对通信双方都有意义的包”。我刚开始接触I3C时,最头疼的就是这一层——它和I2C长得像,但细节上完全不同。其实你只要抓住几个核心概念:起始条件、重复起始、停止条件,再配上一张正确的帧格式图,基本就能看懂总线上的每次对话。

3.1 起始条件(START Condition)

任何一帧I3C通信,都以起始条件开始。SDA线先由高变低,然后SCL线变低——注意顺序!这和I2C正好相反:I2C是SCL高电平时SDA降下去。I3C保留了这一时序,但意义不同。

关键时序:

  • SCL为高时,SDA从高到低跃迁,即为START。
  • START之后的第一个SCL上升沿,从机必须采样地址位。
  • 整个START和地址段的总线驱动由主机(通常是Current Master)负责。

我个人习惯在逻辑分析仪上同时抓取SCL和SDA,然后找这个下降沿。记得有一次,我发现板子上的I3C总线总是卡死在起始阶段,反复排查才发现是SMA线太长导致上升沿变缓,MCU检测不到合格的START。嗯,这里要注意:起始条件的建立时间(tSU;STA)在I3C规范里定义为最小26ns,比I2C宽松得多,但如果你用长线+高电容负载,还是会出问题。

3.2 帧格式(Frame Format)

一个标准的I3C数据帧包括以下部分:起始条件 → 7位地址 + 读写位 → ACK/NACK → 数据字节 + 奇偶校验 → 停止条件。其中动态地址分配过程中,广播地址(0x7E)或者目的地址会出现在第二个字段。

我把它整理成一张表,方便你对照看:

字段 位数 说明 谁驱动
START 1 bit(条件) SDA下降沿,SCL为高 主机
7-bit Address 7 bits 从机地址(高7位) 主机
R/W 1 bit 0=写,1=读 主机
ACK/NACK 1 bit 从机拉低SDA为ACK 从机
Data(若干) 8 bits per byte 数据负载,每次传输一个字节 读写方向决定
Parity 1 bit 奇校验(数据+地址位的奇校验?实际为每个字节后的奇校验) 数据发送方
STOP 1 bit(条件) SDA上升沿,SCL为高 主机

注意:I3C的每个数据字节后面跟的不是ACK,而是一个奇校验位(Parity)。这是我的踩坑点——之前以为和I2C一样用ACK,结果从机一直不回数据。后来查手册才发现,I3C数据段用奇校验代替了ACK机制。只有地址段和广播码后面才用传统ACK。

3.3 重复起始(Repeated START)

你想想看,如果主机想换个方向读写数据,或者想切换到另一个从机,但又不想放掉总线控制权,怎么办?答案是:不产生STOP,而是再发一次START——这就是重复起始(Sr)。

在动态地址分配过程中,重复起始用得特别多。举个例子:主机先发一个广播寻址帧(地址0x7E + 写),然后立即发出重复起始,再发一个目标设备的静态地址进行后续读写。如果中间放了STOP,总线就会被其他主机抢走。所以,重复起始是用来保持“当前主机”身份的关键手段。

我曾经调试一个多主机系统,发现某个从机在动态地址分配中总是丢失状态,最后用示波器发现主机在广播帧后面误发了STOP,导致另一个主机插进来发了同一个从机的地址——总线冲突了。从那以后,我每次检查逻辑分析仪波形时,都会特意确认重复起始的SCL低电平长度是否满足最小165ns的保持时间(tHD;STA)。

3.4 停止条件(STOP Condition)

停止条件正好是起始条件的逆操作:在SCL为高时,SDA线从低变高。但有一点容易忽略——I3C规范要求,停止条件之后必须有一段总线空闲时间(tBUF),至少26ns,之后才能发起新的起始条件。这个空闲时间是为了让总线释放干净。

避坑提醒:

我曾经在DMA传输完成后,软件没有等待tBUF就立刻拉低SDA发下一个START,导致从机误操作。后来加上一个微小的延迟(大约3个SCL周期)就好了。切记:停止条件不是终点,它只是告诉总线“我这轮占线结束了”,其他主机或下一轮对话需要等待。

3.5 完整帧流程与SVG图示

上面说了这么多,不如画一张图把整个链路层的帧序列串起来。下面这张SVG展示了一次典型的I3C写帧:从START开始,经过7位地址+R/W、从机ACK、数据字节+奇校验位,最后以STOP结束。其中虚线部分表示可以插入重复起始(Sr)来切换到新的地址或方向。

I3C 数据链路层帧结构 START 7-bit Addr + R/W ACK (从机拉低) Data[7:0] + Parity(奇校验) STOP 重复起始 (Sr) 可在此处插入,不释放总线 可在此处重复起始 时间约束要点 • START建立时间 t_SU;STA ≥ 26ns | SCL高时SDA下降 • 重复起始保持时间 t_HD;STA ≥ 165ns | SCL低时SDA第二次下降 • STOP后空闲时间 t_BUF ≥ 26ns | SCL高时SDA上升

3.6 关于动态地址分配中的链路层细节

动态地址分配(DAA)消息也是用上面这个帧格式发送的,但它有一个特殊开头——主机先发一个广播地址0x7E(7'b1111110),然后带R/W=0(写)。之后通过GETACCCCCR、SETDASA等子命令完成分配。注意在这些子命令的连续字节之间,主机可以选择用重复起始来保持总线占有,而不是每次都发STOP。

我个人建议:在做DAA实现时,固件里尽量写一个“帧发送状态机”,明确区分START、Sr和STOP三种状态,别把它们混在一起。我见过新手工程师把所有条件和字节都揉成一个函数,结果逻辑分析仪上波形乱成一锅粥。

3.7 常见问题与避坑

我曾踩过的两个坑:

  • 奇校验搞反了方向: 对数据字节而言,奇校验是数据发送方计算并驱动;对地址段而言,ACK由从机驱动。千万不要把ACK和奇校验混淆——地址段之后是ACK,数据字节之后是奇校验。
  • 重复起始的SCL低电平时间不够: 有些MCU的I3C外设自动产生Sr,但内部时序可能不满足165ns保持时间。我后来手动加了一条NOP指令才解决。

你可能会问:为什么I3C要这么设计,用奇校验代替ACK?我理解是ACK需要从机主动驱动,而I3C希望简化从机侧的驱动逻辑——奇校验只要求发送方按位计算,接收方只需检查。这在高频模式下(比如12.5MHz)很有用,因为ACK等待窗口太长会拖慢速度。当然,这是我个人的理解,你可以去查一下I3C Spec的“Why Parity”部分。