最近好几个朋友拿着某芯片公众号的标题来问我:I3C 比 I2C 快 10 倍,到底是真的还是噱头?如果用的是 RK3576,DTS 里又该怎么把这个接口配起来?说实话,这个说法在宣传话术里不算离谱,但真上手你会发现,协议、控制器、设备树、从机支持每一层都有坑。这篇我就用 RK3576 当例子,把 I3C 的速度账、协议特性、DTS 配置和实测里的坑一次讲透,给准备评估或者已经在调 I3C 的朋友做个参考。
1. 先算一笔速度账:I3C 的“10 倍”到底是怎么来的
1.1 从 I2C 的速率档位说起
I2C 大家都不陌生,经典的双线通信,SCL 负责时钟,SDA 负责数据。它最麻烦的地方不在于协议本身,而在于物理层用的是开漏输出加外部上拉电阻。高电平是靠上拉电阻把总线慢慢拉上去的,总线电容一大,上升沿就拖沓,速度根本上不去。所以 I2C 的速率被划分成了几个很保守的档位:
- 标准模式 Standard Mode:100 kbit/s;
- 快速模式 Fast Mode:400 kbit/s;
- 快速模式+ Fast Mode Plus:1 Mbit/s;
- 高速模式 High Speed Mode:3.4 Mbit/s;
- 超快速模式 Ultra Fast Mode:5 Mbit/s,但是单向传输,现实中很少见。
绝大部分嵌入式产品真正用的还是 100k 和 400k,板卡设计好点的能稳定跑 1M。3.4M 那个档位对器件要求很苛刻,普通传感器和线缆几乎跑不动,所以大家平时对 I2C 的印象就是“慢、稳、够用”。
I3C 的 SDR 单数据率模式,标准里给的上限是 12.5 Mbit/s。拿它和几个常用档位对比一下:
| 总线模式 | 典型速率 | 相对 I2C 快速模式的倍数 |
|---|---|---|
| I2C Standard | 100 kbit/s | 0.25 倍 |
| I2C Fast | 400 kbit/s | 1 倍基准 |
| I2C Fast Plus | 1 Mbit/s | 2.5 倍 |
| I2C High Speed | 3.4 Mbit/s | 8.5 倍 |
| I3C SDR | 12.5 Mbit/s | 约 31 倍 |
| I3C HDR-DDR | 等效 25 Mbit/s | 约 62 倍 |
这么看,说“I3C 比 I2C 快 10 倍”其实是保守说法。如果你是在和 100k 的老式 I2C 比,那差距是 125 倍。但注意,这里比的是总线速率上限,不是实际应用层带宽。
1.2 物理层变了,才是速度上得去的根本原因
I3C 能把速率提到 12.5M,表面上是协议升级,本质上是物理层从“开漏+上拉”换成了“推挽输出”。开漏模式就像一群人只能用一只手拉绳子,放开绳子后得靠外界弹簧把绳子拉回去,弹簧紧了伤手,松了又回得慢。I3C 的推挽输出则是高电平由驱动器主动推上去,上升沿很陡,不需要依赖上拉电阻,所以速率才有质的提升。
这还带来一个额外好处:I2C 在高速时上拉电阻选小了,静态功耗会变大;I3C 在数据阶段没有持续的灌电流,功耗反而更低。这也是传感器厂愿意往 I3C 迁移的原因之一,对电池供电设备很友好。
1.3 实际应用中能快多少:总线开销和从机响应不能忽略
账面速率很漂亮,但真测起来,I3C 的传输效率还要扣掉以下开销:
- I3C 保留了类似 I2C 的 START/STOP 时序,每次传输都要花周期;
- 动态地址分配 DAA 在枚举阶段要额外消耗时间,不过只在启动时发生一次;
- 从机的响应速度、内部准备时间也会吃掉一部分;
- 如果你访问的是慢速传感器,比如温度、气压这类,转换时间动辄几十毫秒,总线再快也救不回来。
我实测过一个典型场景:从一颗传感器读 32 字节数据,I2C 400k 模式下总线时间大概要 250us 左右;I3C SDR 12.5M 模式下,不算从机准备时间,总线时间能压到 10us 上下。整体访问周期因为中间等待,实际体验大约是 5-10 倍提升。所以“快 10 倍”这个说法,在大批量连续读写的场景下站得住脚,但在小数据量、慢从机的场景里,你会觉得也就那样。
2. RK3576 的 I3C 控制器:先弄清芯片给了我什么
2.1 控制器资源和引脚复用关系
RK3576 是瑞芯微面向 AIoT、边缘设备推出的一款 SoC,和 RK3588 属于同一代设计思路,外设资源很丰富。它上面是有 I3C 控制器的,具体能引出几路、Pin 脚复用在哪一组,需要以芯片 TRM 的 IO List 为准,不同封装和 SKU 会略有差异。
有一点很重要:RK3576 的 I3C 控制器和 I2C 控制器是引脚复用关系。比如某个节点叫 i3c0,它对应的那组引脚可能也能配成 I2C0 功能。你在 dtsi 里看到既有 i2c0 又有 i3c0,两者抢占的是同一组 pad。所以一个 pad 在一个系统里只能选一种功能,不能同时开。
这也是新手最容易踩的第一个坑:看到 dts 里 i2c0 和 i3c0 都开着,以为没问题,实际上 iomux 会打架,有时内核启动时就报 pin conflict。
2.2 内核的 I3C 驱动框架怎么组织
Linux 从 4.20 开始引入了独立的 I3C 子系统,目录在drivers/i3c/下面,分为 core 和 master 驱动两层。Rockchip 不同型号的 I3C 控制器 IP 不完全一样,有使用 Cadence IP 的,也有使用 Synopsys DesignWare IP 的,对应的驱动名字分别是cdns-i3c-master和dw-i3c-master。
你在配置 RK3576 时,最稳妥的做法是打开 SDK 自带的rk3576.dtsi,搜一下 i3c 节点,看它的 compatible 到底是什么。比如:
i3c0: i3c@fec90000 { compatible = "snps,dw-i3c-master-1.00a"; reg = <0x0 0xfec90000 0x0 0x10000>; interrupts = <GIC_SPI 246 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru CLK_I3C0>, <&cru PCLK_I3C0>; clock-names = "pclk", "fast_clk"; resets = <&cru SRST_I3C0>; pinctrl-names = "default"; pinctrl-0 = <&i3c0_xfer>; status = "disabled"; };注意上面节点地址和中断号只是示意,不同 SDK 里可能是另一个地址。你在文章里看到任何 RK3576 I3C 节点,别直接抄地址,先跟你手上的 dtsi 比对。reg 对应的是控制器寄存器基地址,这个和芯片流片版本强相关。
2.3 内核配置项最容易漏
I3C 子系统和 master 驱动在大部分 SDK 里默认其实是关闭的。很多人把设备树改得明明白白,结果启动后 dmesg 里根本没有 i3c master 注册,一查才发现内核的 CONFIG 压根没开。
需要确认这几个配置项:
CONFIG_I3C,I3C core;CONFIG_I3C_MASTER_CDNS或CONFIG_I3C_MASTER_DW,按你芯片实际用的 IP 选;- 如果要从用户态调试,可能还要看有没有
CONFIG_I3C_TOOLS对应的支持。
配置路径一般在Device Drivers -> I3C drivers下面。有的 SDK 把它们编成模块,建议直接编进内核,省得启动时还要处理模块加载顺序。
3. 速度之外,I3C 真正解决的老问题
如果说 I3C 只是换了个名字提速,那还不值得这么大动静。它真正值钱的是把 I2C 年代几个老大难问题从协议层面解决了。
3.1 动态地址分配:同型号传感器终于能挂同一根总线了
用过 I2C 的人都知道,两个相同型号的芯片如果硬件地址一样,是不能同时挂同一条总线的。比如两个同样的温湿度传感器,芯片厂把地址引脚做死或者只有一两个可选位,你的板子上想放两颗,就得开两条 I2C 控制器,或者用 TCA9548 这类 I2C 多路开关。
I3C 的动态地址分配 DAA 从根本上解决了这个问题。上电后主控制器会发起动态地址分配流程,每个 I3C 从设备用自己的 48 位临时 ID 参与仲裁,主控制器逐个为它们分配 1-0x7D 范围内的动态地址。所以两块完全相同的芯片,可以在同一条 I3C 总线上各自拿到不同地址。
DAA 的具体过程简单说就是:主控制器发送 ENTDAA 命令,从设备按临时 ID 的位序在总线上做仲裁,赢家先被分配地址,然后继续第二轮,直到所有设备分配完毕。这个过程在内核启动时自动完成,对应用层透明。DTS 里你可以给从设备指定一个 preferred address,也可以完全交给控制器自动分。这个特性对多摄模组、多 IMU、多麦克风矩阵这类场景非常香。
3.2 带内中断:省掉那一根 INT 引脚
传统 I2C 从设备要主动通知主控,一般只能靠额外拉一根 GPIO 中断线。每个传感器一根 INT,四个传感器就是四根线,PCB 走线烦,SoC 的 GPIO 资源也紧张。
I3C 支持带内中断 IBI,从设备需要上报事件时,直接抓总线发起一个中断请求,主控制器解析到对应地址就能知道是谁来了。硬件上不再需要额外中断引脚,对减小模组尺寸、简化走线很有意义。调试上也有好处:少一根线就少一个对地短路和信号串扰的点。
3.3 热加入、多主机和广播寻址
I3C 还支持热加入 Hot-Join,设备可以在总线运行的过程中插入并被动态分配地址。产品形态上有点像 USB 的即插即用,比如可插拔的传感器子板、扩展板,主控不用重启就能识别到新设备。
多主机方面,I3C 允许总线上存在 Secondary Master,可以做角色切换或协同管理,不像 I2C 多主机那么麻烦。另外 I3C 还支持广播地址和组地址,一条命令同时发给多个从设备,典型用法是让多个传感器同时开始采样。这在惯性传感阵列里很实用,能最大化采样同步性。
3.4 和传统 I2C 设备的兼容逻辑
I3C 规范在设计时就考虑了向下兼容,总线上可以同时挂 I3C 设备和传统 I2C 设备。控制器在做 DAA 时,不支持 I3C 的设备不会响应,控制器就会把它们识别为 legacy I2C 设备,后续按 I2C 时序访问。
不过我要泼盆冷水:兼容归兼容,实际用起来要看主控制器驱动支持得怎么样。某些内核版本对 I3C 和 I2C 设备混挂的支持并不完善,我遇到过挂 I2C EEPROM 后枚举流程卡住的情况。如果你不需要混挂,尽量把传统 I2C 设备挪到单独的 I2C 控制器上,把 I3C 总线留给纯 I3C 设备,省得被驱动里的兼容逻辑折磨。
4. 手把手配置 RK3576 的 I3C 节点:DTS 逐行拆解
4.1 控制器节点的完整例子
下面是一份示意性质完整的 RK3576 I3C 配置,注意节点地址和中断号必须以你自己的 SDK 为准。我故意不用网上抄来的固定地址,免得你照抄踩坑。
&i3c0 { pinctrl-0 = <&i3c0_xfer>; status = "okay"; i3c-scl-hz = <12500000>; /* 一颗支持 I3C 的传感器,地址由 DAA 动态分配 */ pressure_sensor: pressure@20 { compatible = "example,pressure-i3c"; reg = <0x20>; assigned-address = <0x20>; lsm6dso-pull-addr; /* 示意属性,实际按 binding 文档写 */ }; /* 一个传统 I2C EEPROM,仍可以挂在 I3C 总线上 */ eeprom@50 { compatible = "atmel,24c02"; reg = <0x50>; pagesize = <8>; }; };第一眼看上去和 I2C 设备树很像,但 I3C 从设备的节点语义有差别。compatible是驱动匹配用的,reg在这个上下文里表示设备在总线上的地址,而assigned-address是告诉控制器:如果可能,请优先给这个设备分配这个地址。如果 DAA 过程中发现地址冲突,控制器会自动重新分配。
4.2 从设备地址怎么定:assigned-address 与 DAA 的关系
很多刚从 I2C 转过来的人会问:reg 已经是 0x20,assigned-address 为什么还要写一遍?这里的规矩是这样:
- 如果从设备只写
reg = <0x0>,表示完全交给 DAA 动态分配,控制器枚举后会分配一个可用地址,你在系统里看到的地址不一定和 datasheet 里默认地址一致; - 如果写
reg = <0x20>且assigned-address = <0x20>,表示这是 preferred address,控制器会尽量按这个地址分,但被占用时另说; - 传统 I2C 设备写
reg = <0x50>是正常的 7 位地址,和 I3C 动态地址分配无关。
这里要提醒一点:I3C 从设备在芯片出厂时也有一个默认静态地址或者临时 ID,但这跟你在 DTS 里写的地址不是一回事。有些传感器可以从 I2C 模式切换到 I3C 模式,切换方式各不相同,有的靠引脚,有的靠 OTP 配置,有的第一次上电必须先用默认地址发命令。你在调试前先确认从设备是不是真的处于 I3C 模式,不然 DAA 永远等不到它响应。
4.3 频率和 HDR 模式怎么选
i3c-scl-hz控制的是 SDR 模式下的时钟频率,理论上限是 12.5M,但我建议你做板子时先按 5M 或者 10M 跑通功能,再往上提。原因是 I3C 对信号完整性要求比 I2C 高,推挽快沿对走线长度、过孔、端接都更敏感。RK3576 这种 BGA 封装的 SoC 到传感器之间走线如果绕了多层板,12.5M 直接翻车并不稀奇。
HDR 模式在 DTS 里通过hdr-mode属性指定,可选项有 hdr-ddr、hdr-tsl、hdr-tsp,具体要看控制器驱动和从设备支持情况。我的实际看法是:如果不是做大量数据吞吐的场景,不要一上来就开 HDR。HDR 的出错的链路比 SDR 多,调试工具支持也差,很多从设备压根没实现 HDR。先把 SDR 跑稳,收益已经很大了。
4.4 内核配置和设备树编译的整套流程
DTS 改完不是改个文件就完事,完整流程应该是:
- 确认
rk3576.dtsi里 i3c 节点存在且 compatible 能被内核识别; - 打开对应板级 dts,把 i3c0 的
status改成"okay",按需添加 pinctrl; - 检查 pinctrl 里
i3c0_xfer有没有和 I2C 的使用冲突,如果有其他节点占用了同一组 pad,先把它 disable; - 确认内核开启了 I3C core 和对应 master 驱动;
- 重新编译内核和设备树,烧录后看 dmesg。
编译时如果你用的是独立 dtb,改完 dts 记得重新生成 dtb 再烧录,别只烧内核。很多“我改了怎么没生效”的案例,最后发现是 dtb 没更新。
4.5 混挂传统 I2C 设备时特别要注意的坑
前面说过,I3C 总线可以带 I2C 旧设备,但 DTS 写法上有讲究。传统 I2C 设备节点就是普通 I2C 设备树格式:
eeprom@50 { compatible = "atmel,24c02"; reg = <0x50>; };但有些内核版本的 I3C master 驱动对 legacy I2C 设备的处理依赖于子系统的 probing 流程,不是所有版本都稳定。我的建议是:如果你一定要混挂,先在比较新的内核版本上验证。若发现枚举异常,先尝试把所有传统 I2C 设备从 I3C 总线上移除,确认纯 I3C 设备工作正常,再逐个加回来定位是哪个设备拖垮了流程。
5. 上板实测:波形、时序和常见的第一块绊脚石
5.1 怎么确认控制器已经正常起来
配置完成后,启动阶段内核应该有类似这样的输出:
i3c-master i3c@fec90000: registered master同时/sys/bus/i3c/devices/下面会出现已分配地址的从设备目录。比如地址 0x20 的 I3C 设备,可能显示为0-0020,传统 I2C 设备可能显示成0-0050之类的形式。如果你的 SDK 里有 i3c-tools,也能用它查看总线上的设备列表;没有的话,看 sysfs 一样能判断。
如果 dmesg 里连 master registered 都没看到,问题大概率不在设备树,而在内核配置或者驱动加载顺序上。先把 CONFIG_I3C 相关配置项确认了,再往下查。
5.2 用逻辑分析仪抓 I3C 信号的两个建议
I3C 时序和 I2C 有相似之处,也有关键差异。普通 I2C 解码器在 I3C 的 DAA 阶段经常会解出一些“乱七八糟”的东西,因为推挽驱动模式下信号边沿快,且 DAA 阶段的仲裁时序和 I2C 不一样。
我的建议是:
- 采样率至少开到 25M,最好是 50M 以上,否则抓不到 SDR 12.5M 的波形细节;
- 如果逻辑分析仪软件带 I3C 解码器,直接用;不带的话,先抓 DAA 启动过程,找到 START 后第一个地址字节,确认是不是 0x7E 这个广播地址,这能快速判断控制器有没有在尝试枚举从设备。
抓波形时还要注意探头的接地,必须用最短的接地夹,否则探头本身的寄生电容会直接让 I3C 波形恶化,甚至导致原本正常的系统被你一接探头就出错。这种“仪器引起的故障”我踩过不止一次。
5.3 从设备不响应 DAA 怎么排查
这是 I3C 调试里最常遇到的问题。现象是 dmesg 里没有从设备被枚举出来,或者i3c-scl-hz降得再低也还是 timeout。排查顺序建议是:
- 第一查电源:从设备供电是否正常,复位引脚有没有被拉死;
- 第二查引脚:确认 pinctrl 配置的是 i3c 而不是 I2C,用万用表量 SCL/SDA 电压域是否和 SoC IO 域一致;
- 第三查电平:I3C 是推挽输出,空闲时 SCL/SDA 应该是高电平,如果被拉低,说明有设备占住了总线;
- 第四查模式:从设备是不是真的处于 I3C 模式,很多传感器默认是 I2C 模式,需要按 datasheet 先切过去。
把以上四项检查完,大部分“不响应 DAA”的问题都能定位。剩下的再考虑是不是控制器驱动版本的兼容问题。
5.4 总线设计layout上的经验教训
最后聊点硬件上的事。I3C 虽然不需要上拉电阻也能跑,但如果总线上挂了传统 I2C 设备,你得保留合适的上拉。上拉阻值太大会拖慢电平恢复,太小会增加功耗并和推挽输出抢电流。我一般习惯在纯 I3C 总线上不放上拉或者只放一个 4.7k 左右的电阻兜底;混挂 I2C 设备时按 I2C 的标准重新算,通常 1k 到 2.2k 之间。
走线方面,I3C 的 SCL 和 SDA 要尽量短、等长,少打过孔。如果必须跨层,最好在相邻层做完整的地平面回流。RK3576 这类芯片 IO 密度高,pad 间距小,走线自然绕远,这时候宁可把 I3C 速率降到 5M,也不要为了 12.5M 去挑战糟糕的走线。稳定压倒一切。
我个人用了两三块板子才把 I3C 从“能用”调到“好用”,最大的体会是:I3C 的价值从来不只在“比 I2C 快”,动态地址分配和带内中断才是真正解决生产痛点的东西。如果你只是想把现有 I2C 传感器换个高一点的速率跑,那 I2C 的 Fast Mode Plus 也许够用;但如果你在做多设备总线、想省 GPIO、想支持热插拔,老老实实花时间把 I3C 的 DAA 和 DTS 机制啃下来,回报是值得的。