3、I3C协议基础:帧格式、命令码、起始/停止条件归纳总结

协议玩了这么多年,每次看到新总线协议,我第一反应就是去翻它的帧格式和命令码。

为什么?因为帧格式决定了你怎么把数据塞进去,命令码决定了对方怎么理解你的意图。

I3C的帧结构其实并不复杂,但和I2C有不少差别。你想想看,一个总线既要向后兼容I2C,又要跑出更高效率,必然得在帧头尾做文章。下面我把这块掰开揉碎了讲。

3.1 起始条件与停止条件

先说最基础的——总线状态的切换。

I3C的起始条件和停止条件,形式上跟I2C一模一样:

  • 起始条件(Start):SCL为高电平时,SDA从高电平跳变为低电平。
  • 停止条件(Stop):SCL为高电平时,SDA从低电平跳变为高电平。

但这里有个坑——I3C的多主场景下,仲裁逻辑比I2C更复杂。

我曾经在一款传感器项目中,因为起始条件建立时间不够,导致主机切换时总线卡死。后来查协议规范才发现,I3C对起始条件的保持时间有更严格的要求,特别是从I2C切换到I3C模式的瞬间。

注意:I3C允许在同一个传输序列中连续发送多个起始条件(重复起始),但不允许中途插入停止条件后再重新启动。如果你要做多命令合并,务必用重复起始,不要发Stop。

3.2 I3C帧格式解析

帧格式是协议的核心。I3C的帧结构分为两种:私有数据帧和公共命令帧(CCC)。先看图,再细讲。

I3C基本帧结构(私有数据帧格式) 起始 地址+R/W Ack 数据字节 数据字节 … 停止 关键说明 • 起始后紧接7位地址 + 1位R/W,和I2C一致,但I3C地址是动态分配的。 • 每个字节后都需要一个应答位(Ack/Nak),I3C中从机可以用Nak拒绝命令。 • 数据字节个数由命令决定,CCC帧则包含命令码和可选参数。 • 停止条件标记传输结束,主机也可以选择不发停止,直接接下一个起始。 • 我个人习惯:调试时先用逻辑分析仪抓起始/停止沿,确认时序没越界再往下查。

上面这幅图展示的是最基本的私有数据帧。如果是CCC帧(公共命令帧),结构会稍有不同:地址阶段后紧接着一个命令码字节,而不是数据字节。命令码由I3C协议规范统一规定。

3.3 命令码汇总

命令码(CCC)是I3C区别于I2C的核心。说白了,I2C里你想让从机做什么,得自定义寄存器;I3C则通过公共命令码让总线做标准操作。

我个人习惯把常用CCC分成三类:地址分配类、配置控制类、状态读取类。下面这张表列出了我在项目中反复用到的几个:

命令码(十六进制) 名称 用途 方向
0x07 ENTDAA 进入动态地址分配模式 主机→所有从机
0x15 SETDASA 设置静态地址 主机→指定从机
0x00 ENEC 启用事件检测(中断) 主机→所有从机
0x01 DISEC 禁用事件检测 主机→所有从机
0x02 ENTAS0 进入挂起模式(低功耗) 主机→所有从机
0x06 RSTDAA 重置动态地址 主机→所有从机
0x08 GETMXDS 读取最大数据速率 主机→指定从机

实战提醒:ENTDAA是地址分配的第一步。你用逻辑分析仪抓CCC时,注意看从机回复的Ack。如果从机没出现,很大概率是地址冲突或时序不满足。我记得第一次调I3C,愣是花了一个下午才发现是停止条件后的总线空闲时间不够。

3.4 避坑总结

最后,把我在I3C协议基础这块遇到过的坑攒一攒,送给你:

  1. 起始条件前必须保证总线空闲——I3C要求停止条件后至少等待一个tBUF时间才能发起始。我曾经用I2C那套习惯,连续传输间只留了几个时钟,结果从机直接罢工。
  2. 地址7位还是8位?I3C地址是7位,但和I2C一样,发送时左移一位,低位放R/W。别搞错了,否则地址对不上。
  3. CCC帧的应答响应:有些命令(如ENTDAA)需要所有从机同时应答,此时如果某个从机没准备好,总线上的应答位可能是多个从机信号叠加——你看到的可能是“奇怪的电平”。
  4. 停止条件后SDA释放:主机必须释放SDA为高阻,否则从机无法拉高。我见过硬件设计把SDA上拉电阻选太小,导致停止后SDA拉不到高电平,总线直接死锁。

建议新手从最简单的私有写帧开始调:发起始→地址+写→数据字节→停止。先把这条路径跑通,再玩CCC命令。你想想看,如果连最简单的数据都传不对,哪还有心思去管动态地址分配呢?

好了,帧格式、起始停止、命令码这三块就是I3C协议的基础骨架。把这个骨架搭稳了,后面做地址分配、事件检测、高速模式都会顺很多。