ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RK3576平台I3C实战:比I2C快10倍背后的原理与DTS配置

RK3576平台I3C实战:比I2C快10倍背后的原理与DTS配置 “I3C 比 I2C 快 10 倍”这句话第一次在 RK3576 资料里看到时我盯着规格书里的控制器列表看了半天同样两根线同样叫 SDA/SCL怎么就能从 400kHz 跳到 12.5MHz后来把 MIPI I3C 规范和 Linux 内核里的 I3C 框架都捋了一遍又在一块 RK3576 板子上把老款 I2C 触摸屏和一颗原生 I3C 传感器挂到同一路控制器上调通才有底气写这篇东西。这篇文章不打算复读规格书而是从“快 10 倍到底怎么算的”“RK3576 上 I3C 硬件要注意什么”“DTS 里 i3c-scl-hz 和 i2c-scl-hz 该怎么配”这几个角度展开最后把调试中踩过的坑整理成速查表。硬件工程师、BSP 工程师以及正在评估 RK3576 方案的朋友应该都能找到自己需要的那一段。1. I3C为什么比I2C快10倍这个说法到底怎么来的1.1 I2C的瓶颈不只在频率上聊 I3C 之前得先把 I2C 的底账翻出来。很多人一说 I2C 慢第一反应就是“频率低”标准模式 100kHz快速模式 400kHz快速模式 1MHz高速模式 3.4MHz。但实际上 I2C 的瓶颈不是单纯频率数字而是它“天生开漏”的电气结构。开漏输出意味着 SCL 和 SDA 的高电平完全靠外部上拉电阻把线路拉起来上拉电阻和总线寄生电容组成 RC 电路电平从低到高的爬升时间会非常长频率一高上升沿还没到位下一个时钟沿就来了时序直接就乱掉。另外 I2C 每一帧都有固定套路起始位、7 位地址、读/写位、从机 ACK、数据字节、停止位。传输 1 个字节实际要占用 9 个时钟8 位数据加 1 个 ACK如果算上地址和重复起始位开销更大。假如我用 400kHz 的 I2C 总线读一颗传感器地址阶段加寄存器阶段加数据阶段往往超过 30 个时钟才拿到一个字节的数据有效吞吐率可能连 30KB/s 都不到。还有一个经常被忽略的问题I2C 从机没有任何主动发声的能力传感器要上报事件只能靠一根额外的 INT 中断脚拉 GPIO设计上非常像“合作生意必须自己打电话通知对方”。地址空间也很局促7 位地址去掉保留位才 112 个可用地址同型号传感器一多就得换地址或者上加 PCA9548 这类扩展器。用生活里的场景来类比I2C 像是条限速 40 的双向单车道路关键路口还没有红绿灯所有车都要停下来插卡缴费再重新起步交费这个动作就是上拉电阻那一下漫长的上升沿。1.2 I3C关键设计推挽、DDR与带内中断MIPI I3C 的设计思路本质上是“保留两根线推翻 I2C 的老规矩”。I3C 在 SDRSingle Data Rate模式下SCL 最高可以到 12.5MHz这个模式专门为了兼容老式 I2C 设备帧格式、地址、ACK 都和 I2C 差不多。但 SDR 只是 I3C 的及格线真正拉速度的是 HDR 模式其中 HDR-DDR 可以在时钟的双边沿都采样数据相当于 12.5MHz 的时钟跑出 25Mbps 的带宽再往上还有更高阶的模式只是对控制器和从机的要求也更高。为什么 I3C 能在同样两根线上跑这么高关键在于电气特性改了。I3C 总线不是纯开漏SDR 模式下主机要用推挽方式驱动时钟数据线在部分阶段也允许推挽输出这就绕开了 I2C 那套“上拉电阻慢慢充电”的死穴。推挽驱动的边沿快信号完整度好频率自然就上去了。当然总线上可能还挂着老 I2C 设备I3C 规范专门设计了动态上拉机制控制器会在合适的时间窗口切换上拉强度既能保护兼容设备又不牺牲高速传输。更重要的变化在协议层面。I3C 引入了带内中断IBIIn-Band Interrupt从机想上报事件时不需要拉外部 GPIO 了它可以在总线空闲时主动发起一个中断请求主机响应后再进行数据交换。这对系统设计的冲击很大省掉一根 INT 线意味着连接器能少一个引脚、PCB 能少一根走线、驱动里少一个 gpio 中断资源。另外 I3C 还有热加入Hot-Join机制新设备可以在系统运行中挂上总线主机通过公共命令码 CCC 动态分配地址绕开了 I2C 静态地址冲突的尴尬。公共命令码还能做广播、组寻址一批传感器同一时刻统一初始化效率远高于 I2C 逐个访问。“I2C 从机主动更新主机寄存器”这类需求在 I2C 时代非常别扭必须从机自己摸到主机寄存器地址然后假装发起通信或者靠额外的信号线。I3C 的 IBI 把这件事变成了标准能力从机可以随时把数据“推”给主机这是 I3C 相比 I2C 在交互模型上最本质的差别。1.3 10倍是营销话术还是工程实算现在回头算算“快 10 倍”这个数字。I2C 最常见的工作点是 400kHz 快速模式即便它跑满1 字节占 9 个时钟位后理论有效吞吐大约也就是 44KB/s。I3C SDR 模式跑 12.5MHz同样按 9 个位算有效吞吐约 1.39MB/s这已经是 31 倍。哪怕拿 1MHz 的 I2C 来比12.5MHz SDR 也是 12.5 倍。如果再算 HDR-DDR那就更夸张了。所以“快 10 倍”不是精确计算而是工程上的保守说法按实际业务吞吐、总线仲裁、应答等待折算下来说“大约快一个数量级”是完全站得住的。但这里必须泼一盆冷水这 10 倍指的是 I3C 总线的能力上限不是你板上所有外设都能吃到。如果你的传感器只是一颗普通 I2C 从机就算把控制器换成 I3C通信还是会落到 SDR 模式里的 I2C 兼容帧频率也由传感器支持的上限决定通常只有 400kHz 或 1MHz这时候换控制器对速度一点帮助都没有。1.4 为什么你手上的I2C设备用不上这个速度I3C 控制器最大的隐藏价值在于它能同时管两类设备原生 I3C 设备和老式 I2C 设备。原生 I3C 设备可以享受 12.5MHz SDR、25MHz HDR、带内中断、动态地址这些新特性老式 I2C 设备则挂在同一根总线上控制器会以降速的兼容模式去访问它们。对系统集成来说这意味着一条总线既能接新一代传感器又能继续用存量 I2C 芯片减少控制器数量、少占用引脚。很多人在评估时容易把“I3C 快”理解成“我把 GT911 触摸屏挂到 I3C 控制器上触摸响应就变快了”这个预期是错的。GT911 是 I2C 设备内部接口决定了它只能按 I2C 时序响应I3C 控制器访问它时频率最多也就 1MHz 左右触摸延迟不会有任何改善。真正受益的是原生 I3C 传感器比如一些新出的环境光传感器、IMU它们本身就支持 12.5MHz再加上 IBI 中断一上来就能感受到吞吐和中断模型的代差。2. RK3576上的I3C控制器与硬件设计注意事项2.1 先搞清楚SoC里到底有几个I3C控制器RK3576 相比我手上另一块 RK3588 板子I3C 资源的布局差异很大。RK3588 日常用到的还是 I2C 为主RK3576 的 TRM 和官方设备树里能明显看到 I3C 控制器被单独列出来具体几路、引脚复用关系以你拿到的型号和原理图为准。我在 RK3576 的芯片手册里初步数了一下I3C 控制器和传统 I2C 控制器是并存的它们很可能复用同一组物理引脚也就是说你做了 I3C 的 PCB 走线往往就意味着放弃了这组引脚的 I2C 功能二选一。选择 I3C 还是 I2C我的建议是“以新设备为驱动”。如果板上有一两颗原生 I3C 传感器值得把总线切到 I3C 控制器上如果板上全是存量 I2C 芯片那老老实实用 I2C 控制器没必要为了 I3C 而 I3C。硬件设计阶段就要看 RK3576 的引脚复用表确认 I3C_SCL、I3C_SDA 具体是哪个 bank 哪两个 pinM0、M1、M2 三种复用可能对应不同引脚组选一组跟 PCB 走线、连接器位置匹配的。另外要留意内核里 I3C 控制器的 compatible 字符串RK3576 用的可能是一段类似rockchip,rk3576-i3c的字符串也可能是第三方 IP 核的名字具体以 dtsi 为准。板级工程师不需要去改 dtsi 里已经写好的 reg、interrupts、clocks但一定要知道它们存在排查问题时这些是“控制器的地基”。2.2 引脚复用、上拉电阻与电平域RK3576 的 I3C 引脚设计上仍然允许外接上拉电阻这件事我一开始也纠结过I3C 不是推挽为主吗为什么还要上拉其实理解成“兼容模式下必须保留上拉能力”就顺了。总线上有老 I2C 设备时它们的开漏输出还是需要上拉电阻来提供高电平I3C 控制器自己的动态上拉机制不等于板上可以完全不拉。我在第一版 PCB 上把上拉电阻设计成 4.7k测试 SDR 模式 12.5MHz 时发现上升沿有点肉换上 2.2k 后波形明显利落但也注意别用太小的电阻比如 470 欧推挽驱动和动态上拉配合时会有过冲严重时反射造成误采样。电阻值选择没有绝对公式跟总线长度、挂载设备数量、寄生电容都有关系。工程上我习惯的做法是先用 4.7k 起跑用示波器看上升沿如果边沿时间超过 SDR 周期的 20%就往低换 2.2k如果看到振铃再往高或加源端匹配。调试时务必用靠近 SoC 的测试点抓波形测试点离太远看到的都是线缆反射而不是总线真实情况。电平域同样不能马虎。RK3576 的 I3C 引脚一般属于某个 IO 电源域如果传感器是 1.8V而控制器的引脚电源域配成了 3.3V轻则通信异常重则烧器件。I2C 时代很多人习惯“反正都是开漏上拉接 3.3V 就行”到了 I3C 时代这一套行不通因为推挽输出要求电平域严格匹配不能靠上拉电阻“拉”到一个高一档的电压域。2.3 时钟、复位与中断为什么探查失败先看这里I3C 控制器不是“给个 GPIO 就能跑”的东西它内部有时钟分频器、有状态机还有和 CPU 之间的中断控制器。RK3576 的 dtsi 里通常会给 I3C 控制器准备两组时钟一组是控制器的工作时钟决定 SCL 分频基准另一组是 APB 接口时钟负责寄存器读写。板级 DTS 里如果没有显式配置assigned-clock-rates默认父时钟可能不够高导致i3c-scl-hz配了 12.5MHz 但实际分频算出来达不到总线照跑但速度上不去。排查这类问题先看 CRUClock and Reset Unit寄存器或者内核的 clk_summary 接口确认 I3C 父时钟频率再倒推分频系数是否合理。复位同样是“隐形杀手”。有些板级 DTS 为了省电会把用不到的外设放进power-domains如果 I3C 控制器的电源域没有正确打开probe 阶段会直接失败。常见表象是内核 log 里 I3C master 注册失败或者超时这时候要检查 powers 属性、复位属性别一上来就怀疑外设坏了。中断资源我单独提醒一句I3C 控制器的中断号在 dtsi 里已经定义好IBI、错误、DMA 完成这些事件通常共享一个中断号。如果你在板级 DTS 里为了调试把 interrupts 属性覆盖掉很可能把控制器和中断控制器之间的连接弄断系统起来后一访问总线就整段崩。我在调试时吃过这个亏后来凡是 dtsi 里已有的内容一律不覆盖只改 status、clock-frequency、pinctrl 和子节点。3. DTS配置I3C主控和I2C旧设备混用3.1 设备树里I3C节点的基本框架Linux 内核的 I3C 子系统在设备树上的表达和 I2C 相似但多了两个关键属性i3c-scl-hz和i2c-scl-hz。这两个属性在文档里有明确分工前者是原生 I3C 设备通信时 SDR 模式的 SCL 频率后者是控制器和普通 I2C 设备通信时采用的总线频率。刚接触的人很容易只配一个比如只写了i3c-scl-hz 12500000结果老 I2C 设备挂不上或者只写了i2c-scl-hz原生 I3C 设备又只能跑低速率两头都不讨好。正确姿势是同时配好i3c-scl-hz 12500000I3C SDR 模式时钟一般可以冲到 12.5MHzi2c-scl-hz 1000000老 I2C 设备的上限保守一点先定 1MHz#address-cells 1、#size-cells 0总线寻址基本配置status okay别忘了打开控制器。子节点方面I3C 原生设备可以用reg指定静态地址也可以不写地址等控制器通过动态地址分配扫描到。老 I2C 设备则必须写reg因为 I2C 没有热加入机制内核只能靠设备树里的静态信息去访问它。我习惯把所有兼容 I2C 从机都显式写在设备树里哪怕地址可能被动态分配覆盖也一样写至少系统起来后能在/sys/bus/i3c/devices/下看到完整拓扑。3.2 一个GT911触摸屏加一颗原生I3C传感器的完整配置拿我实际调过的配置举例脱敏简化后是这样i3c0 { status okay; pinctrl-names default; pinctrl-0 i3c0_xfer; i3c-scl-hz 12500000; i2c-scl-hz 1000000; #address-cells 1; #size-cells 0; /* 老式 I2C 触摸屏走兼容模式 */ touch5d { compatible goodix,gt911; reg 0x5d; interrupt-parent gpio1; interrupts RK_PA3 IRQ_TYPE_LEVEL_LOW; reset-gpios gpio1 RK_PB2 GPIO_ACTIVE_LOW; }; /* 原生 I3C 传感器地址由总线动态分配 */ light-sensor { compatible vendor,light-sensor-i3c; }; };这里面有两个容易被新手误解的细节。第一个是 GT911 的地址reg 0x5d用的是 7 位地址写法GT911 的数据手册里可能写 0xBA那是把读写位算进去的 8 位地址DTS 里千万别直接抄 0xBA否则内核访问的地址是 0x5d 1总线完全对不上。第二个是原生 I3C 传感器节点不写reg不代表 Linux 找不到它控制器会在初始化时发起动态地址分配流程传感器收到后返回自己的设备信息。但这种“不写 reg”的做法依赖驱动框架支持热探测我建议还是尽量在 DTS 里给 I3C 设备留一个静态地址字段把动态分配当作兜底而不是主路径稳定性会好很多。中断方面GT911 还是走传统的外部 gpio 中断这是因为它本身不是 I3C 设备没有 IBI 能力。那颗原生 I3C 传感器则完全可以不接 INT 线靠 IBI 上报事件硬件上能省掉一根线驱动里也不用注册 gpio 中断。3.3 内核配置项与启动流程设备树配好了内核还得分编译选项。I3C 子系统相关的配置项需要打开用 menuconfig 搜 I3C 关键字把核心框架和 RK3576 对应的控制器驱动选上如果你还要调试把 I3C 的 debugfs 选项也打开。老 I2C 设备的 I2C 配置项当然也要保留因为板上其他 I2C 控制器可能还在用I3C 的兼容模式不依赖内核的 I2C 子系统它是在 I3C 框架内模拟 I2C 时序所以不会和 I2C 驱动抢占资源。启动流程上I3C 控制器一般在 Linux 启动早期完成注册然后按设备树里的静态子节点逐一轮询。原生 I3C 设备支持热加入时还可以在系统运行中后插总线会扫描到新设备并动态分配地址。日志里如果看到类似 i3c master 注册成功、DAA 完成之类的输出说明控制器层面已经正常。如果外设没有出现在 sysfs排查优先级是先看 pinctrl 是否切到了 I3C再看电源域和复位然后看外设地址、中断属性这些板级小细节。4. 实测与调试从uboot到内核验证I3C是否真的转起来4.1 上电第一件事扫总线RK3576 板子第一次上电先别急着跑业务代码用最笨的办法确认 I3C 总线还活着。我习惯在系统起来后直接看 debugfs 和 sysfs/sys/bus/i3c/devices/下面会列出当前总线上识别到的设备。如果原生 I3C 传感器已经出现说明动态地址分配流程是通的如果只有静态声明的 GT911说明 I3C 设备那边没有正确响应 CCC 命令。有条件的话建议搞一套 i3c-tools就是类似 i2c-tools 的那组命令行工具可以直接枚举总线、读写寄存器比反复改驱动打印效率高很多。具体命令不同版本有差异i3cdump、i3cset这类工具在 I3C 设备上就是雷打不动的三板斧。没有现成工具也没关系Linux 内核 I3C 驱动通常会提供 ioctl 接口写个小测试程序 open 控制器直接发私有命令读外部设备寄存器同样能验证通路。第一次扫总线时如果什么都看不到我最常犯的错误是地址搞错。静态 I2C 从机地址的位数问题之前说过还有一个是总线上设备地址冲突如果 I3C 动态分配出来的地址和静态 I2C 设备地址撞了设备之间互相踩脚表现为两个设备都间歇性失败。解决方法是给原生 I3C 设备一个静态地址区间避开板上的 I2C 地址。4.2 用示波器确认物理层真的是I3C时序软件扫通了不代表物理层没问题我用逻辑分析仪抓 SDR 波形时发现I3C 的 SCL 高电平和 I2C 不太一样。I2C 的高电平靠上拉慢慢爬所以上升沿是弧线I3C 推挽驱动的高电平是直上直下的方波。如果你在板子上测到 SCL 上升沿明显存在缓慢爬升的斜坡说明总线其实还是以开漏模式工作推挽机制没有真正启用这通常是控制器配置或者 pinctrl 里电气属性没配对。抓波形时采样率要够至少 50Mbps 以上别拿 1MHz 的破逻辑分析仪去测 12.5MHz 的 I3C采出来全是锯齿没法判断。示波器探头要接地短探头本身的寄生电容也会影响高频信号尽量用差分探头或者原厂测试方法。我在 RK3576 上实测 SDR 12.5MHz 时SCL 上升沿可以做到 5ns 以内整个波形很干净而同样接法的 I2C 400kHz 上升沿大概在 100ns 的量级两者的物理差异一眼就能看出来。HDR 模式的波形是另一个量级普通 8 通道逻辑分析仪基本没法解析最好用带 I3C 协议解码的示波器或者厂商调试工具。如果项目只用到 SDR 模式其实不太需要深入抓 HDR但若要跑原生 I3C 传感器的高吞吐数据HDR 时序验证不能跳。4.3 uboot与内核切换的边界RK3576 这类平台上uboot 阶段处理 I3C 的优先级通常很低。很多工程把 LCD、触摸屏、PMIC 都挂在 I2C 上uboot 驱动也按 I2C 初始化。到了内核阶段设备树把同一组引脚复用切换成 I3C 控制器这时候如果 uboot 那边还在用 I2C 驱动访问设备内核切换 pinctrl 时可能出现“总线正被占用”的假象甚至把总线上设备的内部状态搞乱。我踩过的一个坑是GT911 触摸屏在 uboot 阶段已经被初始化过一次内核的 I3C 控制器接管后触摸屏还保持着 uboot 里的寄存器配置但中断线已经重新映射到不同驱动导致触摸事件打不到内核。解决办法有两个方向一是在内核里把触摸屏的 reset 引脚拉一拉、重新初始化二是让 uboot 阶段不要初始化 GT911交给内核完全接管。具体取舍看项目需求的领域是“显示菜单要能触控”还是“进系统才用触控”。另外要注意 I3C 和 I2C 控制器的 pinctrl 不能同时是 okay 状态。RK3576 的引脚复用寄存器是全局的两个控制器 claim 同一组 pin后加载的驱动会把先加载的复用配置覆盖掉出现“上午测 I2C 好的、下午配 I3C 后发现 I2C 外设全部掉线”的灵异现象。排查时看/sys/kernel/debug/pinctrl/下的 pin 占用记录确认同一时刻只有一个外设占用这组引脚。5. 常见问题与排查技巧实录5.1 问题速查表现象可能原因排查方法I3C master 注册失败电源域没开、时钟没配、dtsi 被裁剪查 clk_summary、power-domain、reset 状态老 I2C 设备挂不上reg 地址写错、i2c-scl-hz 太高、中断不匹配用 i3c-tools 逐字节探测先降到 400kHz原生 I3C 设备扫描不到从机不支持 DAA、CCC 命令被总线上的 I2C 设备干扰只挂一颗 I3C 设备重扫确认驱动 compatible总线跑着跑着卡死某从机拉死 SDA、没有超时处理示波器看 SDA 是否常低复位对应外设速度到不了 12.5MHz上拉电阻偏大、PCB 走线过长、父时钟不够改 2.2k 电阻、测上升沿、核对时钟树DMA 偶尔传错数据buffer 对齐、DMA 通道冲突、电源域电压不稳关 DMA 用 PIO 对比检查 dts dma 属性触摸中断丢失uboot 已初始化、中断类型配置不符内核重新 reset 触摸屏或 uboot 跳过初始化引脚复用冲突I2C 和 I3C 控制器同时 okay查 pinctrl debugfs确保同一组 pin 只有一个 owner这张表基本涵盖了我调试 I3C 时遇到的大部分问题。每一条背后都对应一次“看波形、看日志、翻手册”的折腾过程。尤其是“总线卡死”这个问题I3C 总线上混合挂载 I2C 设备后如果某颗 I2C 从机在应答位时拉低 SDA 不放控制器会一直等 ACK 超时表现就是整条总线挂住。I2C 时代有专门的总线恢复机制I3C 控制器同样需要可靠超时但前提是设备树里没把超时寄存器配成 0否则全等。5.2 两个容易踩的深坑第一个坑是 pinctrl 与 I2C 控制器的隐性冲突。RK3576 的 I3C 引脚和 I2C 引脚经常复用同一组物理 pin但 dtsi 里 I2C 控制器和 I3C 控制器各自都有默认 pinctrl 子节点。如果板级 DTS 只改了 I3C 的 status把 I2C 的 status 也忘了同时改成 disabled内核会先注册 I2C 控制器占用 pinctrl后注册的 I3C 控制器再把 pinctrl 切过来看起来都能注册实际上一旦反复切换就出问题。我遇到的现场是 I2C 外设偶尔掉、I3C 外设也偶尔掉最后才发现两个控制器都在 claim 同一组引脚。修法是只保留一个外设的 status okay另一个强制 disabled。第二个坑是 IBI 与外部 GPIO 中断的关系。原生 I3C 传感器支持 IBI但很多现成驱动仍然按 I2C 时代习惯申请一个 gpio 中断。这时候如果硬件上没接 INT 线驱动申请失败就会直接禁用设备如果接了 INT 线又会出现 IBI 和 GPIO 中断两边都在报事件中断处理函数互相打架。稳妥做法是确认传感器驱动支持 I3C 的 IBI 模式把驱动里对传统中断脚的依赖删掉。如果驱动不支持那就老老实实接 INT 线并让 DTS 里同一事件只允许一条中断路径生效。5.3 调试路径建议最后给一条我的调试路径供参考。顺序是硬件确认、控制器确认、总线上设备确认、速率往上拉。先确认 RK3576 的 I3C 引脚电平域、上拉电阻、电源域没问题再用 i3c-tools 或最小驱动程序只挂一颗原生 I3C 设备跑通 DAA然后加入老 I2C 设备验证兼容模式最后再把i3c-scl-hz从 1MHz 往上加每次观察波形和丢包情况。这样一点点加条件比一上来就混合所有设备要容易定位问题得多。我个人在实际操作中的体会是I3C 最吸引人的不是那个“10 倍”的数字而是它让两条线的总线既能跑高速原生设备又能兼容存量 I2C 设备顺便还把从机主动上报这个老大难问题解决了。最后再分享一个小技巧在 RK3576 板级 DTS 里i3c-scl-hz和i2c-scl-hz两个值不需要一开始就顶着上限配先把i3c-scl-hz设成 1MHz、i2c-scl-hz设成 400kHz系统稳定后通过设备树的 overlay 或者直接改 dts 逐步升频。这样即使某颗从机时钟上限不够也能快速确定是总线问题还是设备问题比对着 12.5MHz 的规格书盲目调参省时间。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进