I3C 协议栈简介
做 I3C 开发和调试,第一件事就是搞清楚它的协议栈长什么样。说白了,协议栈就是一套分层规则,告诉你底下怎么传比特,中间怎么分帧,上面怎么发命令。我个人习惯把 I3C 协议栈分为四层:物理层、数据链路层、地址分配层、应用层。每一层都有自己该管的事,也都有坑。
核心要点:I3C 协议栈跟传统 I2C 最大的区别,就是它把地址分配提到了数据链路层之上,单独作为一个逻辑层次来处理。这意味着什么?意味着地址冲突不再靠硬件比对,而是靠协议协商。
物理层
物理层决定总线怎么走线、电平怎么摆、驱动谁来负责。I3C 物理层复用 I2C 的 SCL 和 SDA 两根线,但驱动模式改成了推挽(Push-Pull,简称 PP)结合开漏(Open-Drain,简称 OD)的混合模式。
我记得第一次调 I3C 波形时,示波器上看到上升沿一塌糊涂。后来发现是 OD 模式下没有配好上拉电阻,导致总线被拉死的假象。I3C 的物理层其实对 PCB 布局的要求比 I2C 高不少,尤其是高频通信时,推挽模式的瞬态电流很容易产生过冲。
| 模式 | 速度 | 驱动方式 | 典型用途 |
|---|---|---|---|
| 开漏 (OD) | ~1 MHz | 外部上拉电阻 | 仲裁、广播、旧设备兼容 |
| 推挽 (PP) | 12.5 MHz | 内部驱动 | 高速数据传输、动态地址分配 |
数据链路层
这一层负责把字节流打包成帧,处理起始条件(START)、重复起始(Sr)、停止条件(STOP),以及确认位(ACK/NACK)。I3C 的数据链路层继承了很多 I2C 的帧结构,但去掉了我一直觉得多余的 10 位地址模式——这在 I3C 里完全不再使用。
I3C 数据链路层跟 I2C 还有一个重大区别:多主支持。传统 I2C 多主靠仲裁,谁发低谁赢;I3C 则靠动态角色切换(Dynamic Role Switch),任何一个设备都能在适当的时候主动申请当主。
地址分配层
这是 I3C 协议栈最核心、也最容易出问题的一层。地址分配层做的事很简单:给总线上的每个设备分一个唯一的 7 位动态地址(Dynamic Address,简称 DA)。为什么要分动态地址?因为 I3C 保留了 I2C 的静态地址机制用于向后兼容,但静态地址容易冲突。我见过一个产品,两个不同厂商的传感器都用了 0x48,总线一拉起来就打架。
动态地址分配协议(Dynamic Address Assignment,简称 DAA)就派上了用场。DAA 过程完全是软件驱动的:主发一个广播 ENTDAA(Enter Dynamic Address Assignment),从机依次响应,主读取其临时 UID(唯一标识符),分配新 DA。
应用层
应用层负责解释命令和数据。I3C 定义了一组通用命令代码(Common Command Code,简称 CCC),比如 GETPID、GETBCR、SETDASA 等。这些 CCC 是标准化的,所有兼容设备都必须实现。我通常把 CCC 分成三类:
- 广播命令:主发给所有从机,比如 ENTDAA、RSTDAA(重置动态地址)
- 定向命令:主发给指定从机,比如 SETAASA(设置静态地址别名)、GETSTATUS
- 私有命令:供应商自定义,不在标准 CCC 列表内
应用层里还有 I3C 的 IBI(带内中断)和 HDR(高数据速率)模式。IBI 允许从机在无时钟仲裁的情况下主动拉低 SDA 发起中断,这个机制在传感器场景下特别好用。
协议栈与地址分配的关系
你可能会问:为什么地址分配要单独拉一层?其实原因很简单——I3C 没有专用的地址线。传统 I2C 中,地址冲突是硬件级的:谁的地址线被拉低了,谁就赢。但 I3C 放弃了这种打法,它在数据链路层之上做了一个仲裁点,通过协议解决冲突。
我在项目里见过很多人把 I3C 的地址分配当成 I2C 那样硬编码,结果挂两个同型号传感器就触发冲突。嗯,这里要注意,I3C 的地址分配层是支持动态重分配的,你可以先用广播命令枚举所有从机,再根据 UID 依次分配 DA。这样即使两个从机的静态地址一样,上了总线也能正常工作。
经验之谈:做好地址分配层的关键是理解 DAA 的回退机制。当多个从机同时响应 ENTDAA 时,只有一个能成功,失败的会被要求等待下一轮广播。我早期做多主系统时,漏了这一步,导致总线上的从机永远只有 1/3 能拿到地址。
| 协议层 | 与地址分配的关系 | 常见问题 |
|---|---|---|
| 物理层 | 推挽模式提供高速状态切换,支撑 ENTDAA 时序 | OD 模式下速度不足导致 DAA 超时 |
| 数据链路层 | 帧结构承载地址命令(广播/定向) | ACK/NACK 窗口不匹配导致地址分配失败 |
| 地址分配层 | 直接管理动态地址的申请、分配、释放 | 多主并发分配时的冲突检测不完善 |
| 应用层 | 通过 CCC 命令触发与检查地址分配状态 | GETACCCRC 返回不匹配,说明地址写入时序有误 |
最后说一句,I3C 协议栈的设计思路,其实是一个“软件定义总线”的缩影。物理层拼命做减法(只留两根线),地址分配层则做加法(靠协议代替仲裁)。这种取舍听起来很美,但落地时对调试者的要求确实高了一个台阶。我个人觉得,搞懂协议栈,就是搞懂 I3C 的七寸。