3. 动态地址分配原理:I3C 动态地址分配协议详解
说到 I3C 的核心玩法,动态地址分配是绕不开的关键一环。
我个人习惯把动态地址分配比作一场“设备点名大会”。
主设备挨个叫名字(或者说,让从设备自己报名字),然后分配一个临时的、唯一的 7 位地址。省去了传统 I2C 里那些硬件的地址引脚,也避免了地址冲突的烦恼。
但坑也不少。我当年第一次调通 ENTDAA 流程时,就因为没注意时序细节,导致一个从设备死活不响应。查了好几天的波形,最后发现是 TCAS 时间设置不够。
咱们今天就聊聊这个分配环节的细节。
3.1 三个核心命令:ENTDAA、SETDASA、SETNEWDA
I3C 的地址分配,说白了就是靠几个专用命令完成的。咱们先看一个对比表,心里有个底。
| 命令(缩写) | 全称 | 核心用途 | 适用场景 |
|---|---|---|---|
| ENTDAA | Enter Dynamic Address Assignment | 启动动态地址分配流程,从设备主动提交自己的地址 | 系统启动、总线枚举 |
| SETDASA | Set Dynamic Address from Static Address | 给有固定 I2C 地址的从设备直接设置一个动态地址 | 兼容 I2C 设备、快速配置 |
| SETNEWDA | Set New Dynamic Address | 为已分配地址的设备更换一个新的动态地址 | 热插拔、地址冲突处理 |
你可能会问:这三条命令到底怎么选?嗯,这取决于你的从设备是什么“出身”。
关键区分点:
- 如果从设备是纯 I3C 设备(没有固定 I2C 地址),只能用 ENTDAA。
- 如果从设备有出厂时的固定 I2C 地址(比如 0x50),用 SETDASA 更直接。
- 如果运行时需要改地址(比如地址冲突或热插拔),那就用 SETNEWDA。
3.2 ENTDAA 流程深度解析
这个命令是 I3C 的招牌操作。我习惯把它比作“全自动点名”。主设备只发出一个命令,剩下的工作由从设备自己“抢答”。
步骤拆解:
- 主设备发起。 主设备在总线上发送 ENTDAA(0x07 编码)的 CCC(Common Command Code)。
- 从设备响应。 所有未分配地址的 I3C 从设备,收到命令后会进入“侦察模式”。它们把自己的 48-bit Provisional ID 放在 SDA 线上。
- 总线仲裁(关键!)。 多个从设备同时上拉数据,利用 I3C 的开漏模式做位仲裁。谁的 Provisional ID 更小(按位比较,低电平胜出),谁就继续响应。
- 唯一胜出者产生。 仲裁失败的从设备会释放总线,等待下一轮。胜出者会收到主设备分配的 新的 7 位动态地址。
- 循环。 重复上述过程,直到所有从设备都分配完地址。
我曾经踩过的坑:
我做一个小批量产品时,用了三颗不同厂家的 I3C 传感器。它们的 Provisional ID 结构不同,其中一颗在 EntDAA 流程中因为 ID 的某一位处理不当,导致它在仲裁阶段误判。结果是,这颗传感器“假装”自己获胜,但后面又响应了错误的地址。后来我用逻辑分析仪抓了总线,才发现问题出在 ID 的 BIT47 上——有一家的芯片把那一位留空了(当作 Reserved),而另一家把它用作厂商版本号。版本号高,仲裁时先输出 1,导致它“输”给了正确的从设备,最后乱套。所以,千万别相信所有芯片的 Provisional ID 都按同一套标准来。
你想想看,这个过程是不是很像“抢答器”?而且 I3C 的仲裁停得很快,几乎不浪费总线时间。
3.3 SETDASA:给“老设备”一个“新身份”
如果你的从设备是 I2C 转 I3C 的混合设备,或者有固定的静态地址(比如老式的 EEPROM),那用 SETDASA 更合适。
操作方式:
- 主设备直接发送 SETDASA CCC,后面带上目标设备的静态 I2C 地址。
- 拥有该静态地址的从设备,会被主设备分配一个新的动态地址。
- 整个过程不需要仲裁,因为静态地址是唯一的。
这里有讲究:
SETDASA 适合那种“我明确知道谁在总线上”的场景。比如,你在 BOM 里只有一颗温度芯片和一颗光感芯片,且它们的静态地址分别是 0x48 和 0x50。那么,直接调用两次 SETDASA,比跑一遍完整的 ENTDAA 要快得多。
小技巧:
我在做复杂系统时,通常先尝试 SETDASA,如果失败(比如从设备没响应),再回退到 ENTDAA。这样能兼顾速度和兼容性。
3.4 SETNEWDA:运行时“换马甲”
这个命令的使用频率相对低一些,但关键时刻很有用。
典型场景:
- 热插拔:一个从设备被拔出又插回,它之前分配的动态地址可能冲突,需要换成新的。
- 地址冲突:两个从设备因为某些奇怪原因(比如硬件 Bug)分配了相同的动态地址,需要用 SETNEWDA 把其中一个踢出去。
操作方式:
主设备先知道从设备的“旧动态地址”,然后发送 SETNEWDA,后面跟上新的地址。
说白了,就是给设备换一个数字标签。
3.5 核心流程图
说了这么多,不如画个图直观。下面是我这章的核心逻辑:I3C 动态地址分配的完整决策路径。
流程图解读:
- 左侧(紫色、蓝色):ENTDAA 流程,适用于纯 I3C 设备,依靠 ID 仲裁逐步分配。
- 中间(绿色):SETDASA 流程,适用于有固定 I2C 地址的设备,快速分配。
- 右侧(青色):SETNEWDA 流程,适用于运行时的地址变更操作。
- 最终所有路径汇聚到红色终点:分配完成。
3.6 代码示例:ENTDAA 流程的时序描述
光看概念还不够,咱们看看实际在总线上跑的时候长什么样。下面这段“伪代码”更像是一段波形描述,方便你理解主从交互的节奏。
// I3C 动态地址分配 (ENTDAA) 流程示例
// 假设总线上有 3 个从设备,Provisional ID 分别为:
// DevA: 0x12_34_56_78_9A_BC
// DevB: 0x0A_00_00_00_00_01
// DevC: 0x12_34_56_78_9A_BB
// Step 1: 主设备发送 ENTDAA (0x07)
START -> SCL 时钟 -> 发送 7'b000_0111 + W (0x0E) -> 从设备 ACK
// 此时,所有未分配地址的从设备开始准备
// Step 2: 从设备发送 Provisional ID (逐位仲裁)
// 总线上出现 48-bit 数据流,所有从设备同时驱动
// 仲裁结果:最低的 ID 胜出 (DevB: 0x0A_00_00_00_00_01 胜出)
SDA: 0x0A_00_00_00_00_01 (从设备 B 赢得仲裁)
// 其他从设备释放总线,进入待命状态
// Step 3: 主设备分配动态地址 (例如 0x01)
主设备发送:0x01 (7-bit 地址) + R/W bit (0)
从设备 B ACK
// Step 4: 主设备发送 ENTDAA (0x07) 重复流程
// 剩下 DevA 与 DevC 继续仲裁
// DevC (0x12_34_56_78_9A_BB) < DevA (0x12_34_56_78_9A_BC)
// DevC 赢得第二轮仲裁
主设备分配地址:0x02
// Step 5: 第三次 ENTDAA
// 只剩下 DevA,无仲裁直接分配
主设备分配地址:0x03
// 主设备发送 ENTDAA,无人响应 (NACK) -> 分配完成
实战建议:
我一般在代码里给 ENTDAA 循环加一个“超时”机制。如果连续三次尝试都没有任何从设备响应(主设备收到 NACK),就认为总线枚举结束。否则,可能会陷入死循环。
3.7 避坑指南
说了这么多优点,也该提提我踩过的雷了。
- Provisional ID 的完整性:不是所有厂商的 ID 格式都一致。有的厂商会留空某些位,有的会用作厂家版本号。如果你家总线上混用不同品牌的芯片,务必查清每颗芯片的 ID 定义。不然仲裁会出幺蛾子。
- TCAS 时间:ENTDAA 过程中,从设备从“命令解码”切换到“数据发送”的过渡时间很短。如果主设备的 SCL 频率太高,从设备可能来不及准备数据。我建议刚开始做开发时,先用 400kHz 跑通,再试着调高。
- 地址 0x7E 是保留地址:动态地址不能分配成 0x7E,这个地址是作为广播或特殊操作用的。不少新手在调试时手动配了个 0x7E,结果通讯全乱套了。
- SETDASA 不支持 I2C 从设备:这一点很容易搞混。SETDASA 虽然用到 I2C 的静态地址,但操作的必须是 I3C 从设备(支持 I3C 协议)。纯 I2C 设备是无法响应 SETDASA 命令的。
特别警告:
我曾经为客户排查一个棘手的 EMI 问题。现象是:I3C 总线在运行 ENTDAA 时,会突然产生巨大的辐射干扰。我抓了波形,发现主设备在发送 ENTDAA 后的那个“总线空闲”窗口,SCL 和 SDA 上有明显的振铃。原因?主设备的驱动电流太强,信号边沿太陡。我调高了驱动能力的配置寄存器,把上升沿从 3ns 拉到 8ns,辐射噪声瞬间下降了 6dB。所以,ENTDAA 流程中的信号边沿控制,是 EMI 整改的重点之一。
好了,关于动态地址分配的核心机制,咱们聊到这儿。理解这三条命令(ENTDAA、SETDASA、SETNEWDA),就算是抓住 I3C 地址管理的命脉了。