4. I3C 命令码与 CCC:通用命令码、设备角色、组地址

4.1 CCC 命令码——I3C 的"通用语言"

CCC(Common Command Code)是 I3C 总线里最核心的控制机制。说白了,它就是主控器用来给所有设备或某个设备下指令的一套标准"口令"。你想想看,I2C 里要配置一个从机,往往得写一堆寄存器地址和数据,而 I3C 直接用一条 CCC 命令就能完成,效率高得多。

我刚开始接触 I3C 时,总觉得 CCC 就是一堆命令码表格,背下来就行了。后来才发现,真正重要的是理解它的两种传递方式:广播命令和定向命令。广播命令是所有设备都会响应,定向命令只发给某个特定的目标设备。这个区别在地址分配场景里特别关键——ENTDAA 就是广播命令,而 SETNEWDA 是定向命令。

核心要点:CCC 命令码是 I3C 协议的控制层,所有地址分配、角色切换、配置操作都通过它完成。

4.2 通用命令码分类与速查

标准 CCC 命令码分为三类:必选命令、可选命令和厂商自定义命令。我习惯把跟地址分配相关的几条命令单独拎出来记,因为它们在 DMA 场景下出现频率最高。

命令码 缩写 类型 功能简述
0x07 ENTDAA 广播 进入动态地址分配流程
0x08 SETNEWDA 定向 为指定设备设置新动态地址
0x09 SETGRP 定向 为设备分配组地址
0x0A GETGRP 定向 读取设备的组地址配置
0x0B RSTDAA 广播 重置所有设备的动态地址
0x0C SETDASA 定向 设置设备的静态地址作为动态地址

为什么我特别强调这几条?因为 DMA 做批量地址分配时,依赖的就是 ENTDAA 启动流程、SETNEWDA 逐个写入地址、RSTDAA 做异常恢复。有一次我在项目里发现 DMA 传输老是中断,查了半天,最后发现是 SETNEWDA 的响应超时处理没做好——嗯,从那以后我给每条命令都加了超时计数器。

小技巧:我建议你把 0x07–0x0C 这几条命令的时序图打印出来贴在工位上,调试时一眼就能定位问题。

4.3 设备角色:谁在发号施令?

I3C 总线上有三种角色:Primary Controller、Secondary Controller 和 Target。它们的关系有点像团队里的"主负责人、副手和成员"。

  • Primary Controller:总线的"老大",负责发起 CCC 命令、分配地址、管理时钟。DMA 场景下,它通常是 SoC 的主控核。
  • Secondary Controller:可以理解为"备份老大"。当 Primary 让出总线时,它能接管控制权。我遇到过主控器宕机、Secondary 接管后地址表不同步的坑——解决方法是在切换后重新发起 ENTDAA。
  • Target:就是普通的从设备,接收命令、响应地址分配。DMA 的目标设备基本都是 Target 角色。

你可能会问:角色之间怎么切换?靠的就是 CCC 命令。比如 SETDASA 能把一个 Target 提升为 Secondary Controller,但需要先验证它的能力。我在项目中就见过一个传感器说自己支持 Secondary 模式,结果发过去命令根本没响应……

注意:角色切换不是即时的,Primary 必须在发出切换命令后等待至少一次 SCL 周期的延迟,才能确认切换完成。别问我怎么知道的——看波形看到的。
I3C 命令码、设备角色与组地址关系图 I3C 命令码 · 设备角色 · 组地址 关系 CCC 命令码 广播命令 (Broadcast) 定向命令 (Directed) ENTDAA · RSTDAA · 组广播 SETNEWDA · SETGRP · GETGRP 设备角色 Primary Controller Secondary Controller Target Device 发起 CCC · 管理地址 接管控制 · 备份角色 响应命令 · 被分配地址 组地址机制 DMA 地址分配 🔹 命令码 → 设备角色 → 组地址 → DMA 广播用于群体操作,定向用于精准配置

4.4 组地址:一对多通信的钥匙

组地址(Group Address)是 I3C 里一个很巧妙的设计。它允许 Primary Controller 把多个 Target 设备编成一个"组",然后通过一个地址同时给组内所有设备发命令。你想想看,如果不用组地址,要对 8 个设备做同样的操作就得发 8 次定向命令——有了组地址,一次搞定。

在 DMA 地址分配场景里,组地址的价值体现在"批量同步"上。比如你要同时给一组传感器分配动态地址,传统做法是逐个发 SETNEWDA,耗时随设备数量线性增长。而先通过 SETGRP 把设备编组,再用一次广播命令触发组内地址分配,效率直接翻倍。

我曾经在组地址分配上踩过一个坑:SETGRP 命令的参数里包含了组地址值和成员列表,我当时把成员列表的起始地址算错了,结果组里少了两个设备。调试了整整两天,最后用 GETGRP 回读才发现——嗯,从那以后我每次发完 SETGRP 都会立即读回来校验。

最佳实践:在 DMA 批量地址分配前,先用 SETGRP 把需要分配的设备组成一个组,然后通过一次 ENTDAA 广播触发组内所有设备的地址分配。这样能大幅减少 CCC 命令的发送次数,提升 DMA 的整体吞吐量。

4.5 设备角色与组地址的配合

不同角色在使用组地址时的权限不一样:

  • Primary Controller 可以发起 SETGRP 和 GETGRP,管理所有组的配置。
  • Secondary Controller 只能读取组地址配置(GETGRP),不能修改。如果需要修改,必须先切换为 Primary 角色。
  • Target 只能被动接受组地址分配,并在收到组广播命令时响应。

这个权限分级在 DMA 场景下很重要。我记得有个项目,Secondary Controller 试图在接管总线后直接发 SETGRP,结果所有 Target 都不响应——因为角色还没切换完成。正确的做法是先发 SETDASA 完成角色切换,再执行组地址操作。

调试建议:用逻辑分析仪抓 CCC 命令时,重点关注 ENTDAA 之后的地址确认阶段。如果某个 Target 没有回应,大概率是它的组地址配置与预期不符。

4.6 实战:结合 DMA 的组地址分配流程

总结一下,在 DMA 地址分配场景中,命令码、角色和组地址是这样协同的:

  1. Primary Controller 通过广播 RSTDAA 重置所有设备的动态地址。
  2. Primary 向目标设备逐个发送 SETGRP,将它们编入同一个组。
  3. Primary 发送广播 ENTDAA,组内所有设备同时进入动态地址分配流程。
  4. DMA 控制器接管总线,批量写入每个设备的动态地址(通过 SETNEWDA)。
  5. Primary 发送 GETGRP 校验组地址配置是否完整。

这个流程我测试过多次,在 8 个设备以下时,DMA 的地址分配耗时比逐条发送 CCC 命令减少了 60% 以上。核心就在于利用了组地址的"一次广播、全员响应"特性。

再次提醒:组地址分配完成后,记得检查每个设备的响应状态。我曾经因为一个设备掉电没响应,导致整个组地址分配流程卡死,DMA 一直在等它的 ACK……加一个超时重试机制就能避免这个问题。