ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RK3576 I3C从零跑通实战:DTS配置、调试避坑与性能实测

RK3576 I3C从零跑通实战:DTS配置、调试避坑与性能实测 I3C 这两年热度上来了尤其是做嵌入式 Linux 的圈子隔三差五就有人问这玩意儿到底比 I2C 强在哪是不是真的快 10 倍RK3576 上怎么配我前阵子正好在一个 RK3576 的板子上把 I3C 从零跑通中间踩了不少坑从设备树写错到传感器不识别折腾了好几天。这篇文章就把整个过程中的经验、原理、配置细节都摊开讲清楚不管你是刚接触 I3C 的新手还是已经在调 DTS 的老手应该都能捞到点有用的东西。先说结论I3C 确实比 I2C 快很多但快 10 倍这个说法要看你怎么比。标准 I2C 在快速模式下是 400kHz快速模式是 1MHz而 I3C 的 SDR单数据速率模式起步就是 12.5MHz理论上限能到 12.5MHz 甚至更高HDR 模式下更高。从 400kHz 到 12.5MHz差不多是 30 倍。但实际用起来受限于总线负载、走线、从设备能力能跑到 3-5 倍就已经很不错了。所以10 倍是个理想值别太当真但性能提升是实打实的。下面我按几个维度来拆I3C 到底解决了什么问题、它的协议特性是怎么回事、RK3576 上的 I3C 控制器长什么样、DTS 怎么配、实际调试中会遇到哪些坑。1. I3C 到底解决了 I2C 的哪些痛点1.1 I2C 的先天不足I2C 诞生于 1982 年飞利浦搞出来的设计初衷是让同一块板子上的芯片用两根线通信。开漏输出加外部上拉电阻结构简单成本低兼容性好。但四十多年过去了这套机制的瓶颈越来越明显。首先是速率。标准模式 100kHz快速模式 400kHz快速模式 1MHz超快速模式 5MHz但基本没人用。实际项目中400kHz 是主流1MHz 已经算快了。对于现在的传感器、EEPROM、触摸屏控制器来说400kHz 传个几十字节还行但要传几百字节的固件或者大量采样数据就明显不够用了。其次是上拉电阻的问题。I2C 是开漏输出上升沿靠上拉电阻给总线电容充电这个 RC 时间常数直接限制了速率。总线电容越大挂的设备越多、走线越长上升沿越慢速率就越上不去。你想提速就得减小上拉电阻但电阻太小功耗又上去了而且很多设备的灌电流能力有限不能无限减小。这是个死结。再就是没有带内中断。I2C 从设备如果有事件要通知主设备只能靠额外的 GPIO 中断线或者主设备轮询。挂 10 个传感器可能就要 10 根中断线GPIO 资源紧张的时候很头疼。还有地址冲突、没有标准化的热插拔、没有 CRC 校验等等。这些问题在小系统里不明显但设备一多、速率一高就全冒出来了。1.2 I3C 的设计思路I3CImproved Inter-Integrated Circuit是 MIPI 联盟在 2016 年左右推出的目标很明确在保留 I2C 两根线SDA/SCL物理兼容的前提下解决上面这些问题。关键设计有这么几条推挽输出替代开漏。I3C 在高速传输时用推挽驱动上升沿和下降沿都是主动驱动不再依赖上拉电阻的 RC 充电。这一下就把速率瓶颈打开了12.5MHz 起步很轻松。但为了兼容 I2C 设备I3C 总线在特定阶段比如仲裁、I2C 设备通信时还是会切回开漏模式。带内中断In-Band Interrupt, IBI。从设备可以直接在 SDA 线上发起中断请求不需要额外的 GPIO。主设备收到 IBI 后再决定怎么处理。这省了大量中断线对多传感器场景特别友好。动态地址分配Dynamic Address Assignment, DAA。I2C 的地址是固定的出厂就定了冲突了只能换芯片或者用多路复用器。I3C 支持在总线初始化时给每个从设备动态分配一个 7 位地址冲突问题从根上解决了。标准化命令集。I3C 定义了一套通用的 CCCCommon Command Codes比如读设备信息、读能力寄存器、配置速率等等不用每个设备都去翻数据手册猜寄存器。支持热插拔。I3C 总线支持设备在运行中接入和移除主设备能感知到总线拓扑变化。可选 CRC 校验。HDR 模式下可以带 CRC提高数据可靠性。这些特性加起来让 I3C 从一个低速板级总线变成了一个中速、可靠、可管理的现代总线。1.3 速率对比的真实情况很多人看到I3C 比 I2C 快 10 倍就以为随便配一下就能跑 10 倍。实际不是这样。I3C 的速率模式分几种模式速率说明SDR12.5MHz单数据速率最常用HDR-DDR25MHz双数据速率双边沿采样HDR-TSP33.3MHz三符号模式HDR-TSL33.3MHz三符号传统模式SDR 是默认模式12.5MHz。但注意这是总线时钟频率实际有效数据速率还要看协议开销。I3C 的帧结构比 I2C 复杂有前导码、地址、命令、数据、CRC 等字段有效载荷占比不是 100%。而且I3C 总线上如果挂了 I2C 设备那和 I2C 设备通信时还是得用 I2C 的速率400kHz 或 1MHz。I3C 设备之间才能跑高速。所以混合总线的实际吞吐量是打折的。我实测下来纯 I3C 设备之间传数据有效速率大概是 I2C 400kHz 的 5-8 倍。如果总线上有 I2C 设备整体就降到 2-3 倍。所以10 倍是个理论峰值实际要看场景。2. RK3576 的 I3C 控制器特性2.1 RK3576 平台概述RK3576 是瑞芯微的一款中高端 SoC面向 AIoT、边缘计算、工业控制这些场景。CPU 是四核 Cortex-A72 加四核 Cortex-A53GPU 是 Mali-G52 MC3NPU 算力 6 TOPS。外设接口很丰富I2C、SPI、UART、CAN、I3C 都有。I3C 控制器在 RK3576 上不是每个都独立而是和 I2C 控制器复用。也就是说一个控制器既可以配成 I2C 模式也可以配成 I3C 模式通过 DTS 里的 compatible 属性来区分。这个设计很常见因为 I3C 和 I2C 在物理层是兼容的复用控制器能省硅面积。RK3576 具体有几个 I3C 控制器得看具体型号和封装。一般来说至少有一到两个控制器支持 I3C 模式。具体哪个能配 I3C要查 TRM技术参考手册或者 Datasheet。我手上这块板子I3C 是挂在 i3c0 和 i3c1 上对应原来的 i2c0 和 i2c1。2.2 控制器寄存器层面的差异从寄存器角度看I3C 控制器比 I2C 控制器复杂不少。I2C 控制器主要就是几个寄存器控制寄存器、状态寄存器、数据寄存器、地址寄存器。I3C 控制器多了很多主设备特性寄存器配置总线速率、推挽/开漏模式、IBI 使能等。动态地址分配寄存器管理 DAA 过程。CCC 命令寄存器发送标准命令。IBI 状态寄存器处理带内中断。HDR 模式配置寄存器配置高速模式。这些寄存器在 Linux 内核里由 I3C 子系统驱动管理一般不需要手动去写。但调试的时候如果驱动有问题可能要用 devmem 或者 i2c-tools 的 i3cdetect 去看寄存器状态。2.3 时钟和引脚I3C 的时钟源通常来自 CRUClock Reset Unit和 I2C 共用。RK3576 的 I3C 时钟一般是从 24MHz 晶振分频或者 PLL 出来的。速率配置在 DTS 里通过 clock-frequency 指定但 I3C 的 clock-frequency 和 I2C 含义不太一样I3C 配的是 SCL 时钟频率实际数据速率是它的倍数关系。引脚方面I3C 用 SDA 和 SCL 两根线和 I2C 完全一样。但 I3C 要求引脚支持推挽输出所以 pinctrl 配置里要把引脚设成 I3C 功能而不是 I2C 功能。RK3576 的 pinctrl 里I3C 和 I2C 通常是同一组引脚的不同 function 编号配错了就出不来波形。3. DTS 配置的完整过程3.1 找到正确的控制器节点RK3576 的 DTS 文件在 Linux 内核的 arch/arm64/boot/dts/rockchip/ 目录下。具体文件名看板子比如 rk3576-evb.dts 或者你自定义的板级 DTS。先找到 I3C 控制器节点。在 rk3576.dtsi 里I2C 控制器通常长这样i2c0: i2c2ac00000 { compatible rockchip,rk3576-i2c, rockchip,rk3399-i2c; reg 0x0 0x2ac00000 0x0 0x1000; clocks cru CLK_I2C0, cru PCLK_I2C0; clock-names i2c, pclk; interrupts GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH; pinctrl-names default; pinctrl-0 i2c0m0_xfer; #address-cells 1; #size-cells 0; status disabled; };要把它改成 I3C 模式需要改 compatible 属性。RK3576 的 I3C 驱动 compatible 一般是 rockchip,rk3576-i3c 或者类似的。具体值要查内核源码里的 drivers/i3c/master/ 目录看瑞芯微提交的驱动。改完之后节点名最好也改成 i3c0方便识别i3c0: i3c2ac00000 { compatible rockchip,rk3576-i3c; reg 0x0 0x2ac00000 0x0 0x1000; clocks cru CLK_I2C0, cru PCLK_I2C0; clock-names i3c, pclk; interrupts GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH; pinctrl-names default; pinctrl-0 i3c0m0_xfer; #address-cells 3; #size-cells 0; status okay; };注意 #address-cells 变成了 3。I3C 的地址单元比 I2C 复杂因为要表示设备类型、动态地址、静态地址等信息。具体格式看内核文档 Documentation/devicetree/bindings/i3c/i3c.yaml。3.2 pinctrl 配置的坑pinctrl 是最容易出错的地方。RK3576 的 pinctrl 配置在 rk3576-pinctrl.dtsi 里。I3C 的引脚配置和 I2C 不一样需要把引脚设成 I3C 功能。比如i3c0m0_xfer: i3c0m0-xfer { rockchip,pins 1 RK_PC0 5 pcfg_pull_up, 1 RK_PC1 5 pcfg_pull_up; };这里的 5 是 function 编号表示 I3C 功能。I2C 可能是 2 或者 3具体看手册。配错了引脚就还是 I2C 模式I3C 波形出不来。还有一个坑I3C 要求推挽输出但 pinctrl 里的 pull-up 配置还是需要的因为在仲裁和 I2C 兼容阶段总线还是开漏的。所以 pull-up 不能去掉但驱动会在高速阶段自动切换到推挽。3.3 从设备节点的写法I3C 从设备的 DTS 写法和 I2C 差别很大。I2C 从设备直接挂在控制器节点下用 reg 表示地址i2c0 { status okay; sensor48 { compatible ti,tmp102; reg 0x48; }; };I3C 从设备需要更多的信息。首先I3C 设备有静态地址和动态地址。静态地址是出厂预设的动态地址是总线初始化时分配的。DTS 里要指定静态地址驱动会在 DAA 过程中分配动态地址。i3c0 { status okay; sensor48,0 { compatible vendor,sensor; reg 0x48 0x0 0x0; assigned-address 0x48; }; };reg 的三个单元分别是静态地址、设备类型、实例 ID。assigned-address 是期望分配的动态地址。如果设备支持 IBI还要加 interrupt 相关属性。如果是 I2C 设备挂在 I3C 总线上写法又不一样需要用 i2c 子节点i3c0 { status okay; i2c0 { #address-cells 1; #size-cells 0; sensor48 { compatible ti,tmp102; reg 0x48; }; }; };这个 i2c 子节点表示总线上有一个 I2C 设备I3C 控制器会用 I2C 模式和它通信。3.4 速率配置I3C 的速率配置在控制器节点里通过 clock-frequency 属性i3c0 { clock-frequency 12500000; };12.5MHz 是 SDR 模式的标准速率。也可以配低一点比如 1MHz 或者 400kHz用于兼容性测试。但注意如果总线上有 I2C 设备I3C 设备之间的通信速率不受影响但和 I2C 设备通信时会自动降到 I2C 速率。4. 实际调试中踩过的坑4.1 设备不识别DAA 失败第一次配好 DTS编译烧录启动后 i3cdetect 一看总线上什么都没有。dmesg 里有一堆 DAA failed 或者 no device found 的报错。排查过程先确认引脚波形。用逻辑分析仪抓 SDA/SCL发现上电后 SCL 有 12.5MHz 的时钟但 SDA 一直是高电平没有任何数据。这说明主设备在发时钟但从设备没响应。可能原因有几个从设备不支持 I3C。有些传感器标称支持 I3C但实际上是 I2C-only只是兼容 I3C 电气特性。这种设备不会响应 DAA。解决办法是把它当 I2C 设备挂在 i2c 子节点下。静态地址不对。DAA 过程中主设备会发一个 ENTDAA CCC 命令从设备用自己的静态地址响应。如果 DTS 里写的静态地址和实际不符从设备不会响应。查数据手册确认地址。上拉电阻不合适。I3C 虽然用推挽但仲裁阶段还是开漏需要上拉。上拉太弱上升沿太慢从设备采样不到。一般 2.2k 到 4.7k 比较合适。我一开始用了 10k结果就是不行换成 2.2k 就好了。从设备供电或复位问题。有些传感器需要先复位才能进入 I3C 模式。检查复位引脚和供电时序。4.2 IBI 中断不触发设备识别到了但 IBI 中断死活不触发。传感器有事件了主设备收不到。排查先确认从设备支持 IBI。不是所有 I3C 设备都支持 IBI有些只支持轮询。查数据手册的 IBI 支持位。然后确认 DTS 里 IBI 相关属性配了。I3C 设备的 IBI 需要指定中断号和触发方式sensor48,0 { compatible vendor,sensor; reg 0x48 0x0 0x0; assigned-address 0x48; interrupt-parent gpio1; interrupts RK_PA0 IRQ_TYPE_LEVEL_LOW; };但注意I3C 的 IBI 是带内的不需要额外的 GPIO 中断线。这里的 interrupts 属性是给驱动用的表示 IBI 事件通过哪个中断上报给 CPU。实际上 IBI 信号是在 SDA 线上传输的控制器收到后产生中断。如果 IBI 不触发可能是控制器没使能 IBI。检查驱动里的 IBI 使能位或者 DTS 里有没有 ibi-disable 之类的属性。还有一个坑IBI 的优先级和仲裁。如果总线上有多个设备同时发 IBI会有仲裁过程。仲裁失败的设备会重试。如果重试次数超了IBI 就丢了。这个在高负载场景下要注意。4.3 速率上不去配了 12.5MHz但实测有效速率只有 2MHz 左右。原因分析总线电容太大。走线太长、挂的设备太多电容大了推挽驱动也扛不住。缩短走线减少设备数量。从设备能力限制。不是所有 I3C 设备都能跑 12.5MHz。有些设备最高只支持 4MHz 或者 8MHz。查数据手册确认。协议开销。I3C 的帧结构比 I2C 复杂有效载荷占比低。12.5MHz 的时钟实际数据速率可能只有 8-10MHz。再加上命令、地址、CRC 等开销有效速率再打折扣。CPU 负载。如果驱动是中断模式每次传输都中断CPU 负载高了会影响吞吐量。可以用 DMA 模式减少 CPU 干预。我实测下来纯 I3C 设备12.5MHz 配置实际有效速率大概 6-8MB/s。I2C 400kHz 的实际有效速率大概 300-400KB/s。算下来差不多 15-20 倍。但这是理想情况实际项目里能有 5-10 倍就不错了。4.4 和 I2C 设备混挂的问题总线上既有 I3C 设备又有 I2C 设备结果 I2C 设备不工作了。原因I3C 总线的电气特性和 I2C 不完全一样。I3C 在高速阶段用推挽电压摆幅可能和 I2C 设备不兼容。有些 I2C 设备只能接受开漏的总线推挽驱动可能会损坏设备或者导致通信失败。解决办法确认 I2C 设备兼容 I3C 电气特性。查数据手册看是否支持 I3C 的电压和驱动要求。在 DTS 里把 I2C 设备放在 i2c 子节点下让控制器知道这是 I2C 设备会用 I2C 模式通信。如果不行用 I3C 到 I2C 的桥接器或者把 I2C 设备挂到另一个纯 I2C 控制器上。我遇到的情况是一个 I2C 的 EEPROM 挂在 I3C 总线上读写全失败。后来把它移到另一个 I2C 控制器上就好了。所以混合总线不是不能做但要小心。5. I3C 在实际项目中的选型建议5.1 什么时候该用 I3C不是所有项目都需要 I3C。以下几种情况可以考虑多传感器场景。比如手机、平板、VR 设备上面挂了一堆传感器加速度计、陀螺仪、磁力计、光感、距离感等。I3C 的 IBI 和动态地址能省很多 GPIO 和地址冲突的麻烦。高速数据传输。比如摄像头模组的控制接口、高分辨率触摸屏、高速 EEPROM。I2C 400kHz 传大块数据太慢I3C 能快好几倍。需要热插拔。比如模块化设备运行中要插拔传感器板。I3C 的热插拔支持比 I2C 好。低引脚数需求。I3C 两根线能挂很多设备还支持带内中断比 I2C 加一堆中断线省引脚。5.2 什么时候继续用 I2C成本敏感。I3C 控制器和设备的成本比 I2C 高小项目没必要。设备生态。I2C 设备生态成熟选择多价格低。I3C 设备还比较少尤其是国产传感器。速率要求不高。如果只是读个温度、控制个 GPIO 扩展I2C 400kHz 完全够用。团队熟悉度。I2C 大家都熟I3C 调试门槛高出了问题排查麻烦。5.3 RK3576 上的实践建议如果你在 RK3576 上做项目我的建议是先跑通 I2C。把设备先用 I2C 模式调通确认硬件没问题再切 I3C。这样出问题容易定位是硬件还是 I3C 配置。DTS 从简单开始。先配一个 I3C 设备跑通了再加第二个。不要一上来就挂一堆。逻辑分析仪必备。I3C 的波形比 I2C 复杂没有逻辑分析仪很难调试。推荐用支持 I3C 解码的比如 Saleae 或者 DSLogic。上拉电阻选 2.2k。实测 2.2k 到 4.7k 比较稳10k 以上容易出问题。注意电源时序。I3C 设备对电源时序敏感确保供电稳定后再开始通信。内核版本要够新。I3C 子系统在 Linux 5.0 之后才比较完善RK3576 的 BSP 一般用 5.10 或 6.1没问题。但如果是老内核I3C 驱动可能不完整。6. 几个容易被忽略的细节6.1 I3C 的地址分配过程DAA 过程是这样的主设备发 ENTDAA CCC 命令然后所有从设备用自己的静态地址依次响应。主设备给每个响应的设备分配一个动态地址从设备确认后后续通信就用动态地址。这个过程有几个注意点静态地址冲突的设备不能同时响应否则仲裁会失败。I3C 的仲裁机制能处理这个但如果有两个设备静态地址完全一样那就没办法了。动态地址分配后从设备的静态地址就不再用于通信了。但有些设备支持用静态地址访问这个看具体实现。DAA 过程可能失败如果从设备不支持 DAA 或者响应超时。这时候可以尝试用静态地址直接通信但需要驱动支持。6.2 I3C 的 CCC 命令CCC 是 I3C 的标准命令集常用的有CCC 命令编码功能ENTDAA0x07进入动态地址分配RSTDAA0x06复位动态地址SETDASA0x87设置动态地址为静态地址GETPID0x8D读设备 PIDGETBCR0x8E读总线特性寄存器GETDCR0x8F读设备特性寄存器SETMWL0x89设置最大写长度SETMRL0x8A设置最大读长度这些命令在驱动里自动处理一般不需要手动发。但调试的时候可以用 i3c-tools 手动发 CCC 命令看设备响应。6.3 I3C 的 HDR 模式HDR 模式是 I3C 的高速模式包括 HDR-DDR、HDR-TSP、HDR-TSL。HDR-DDR 是双边沿采样速率翻倍到 25MHz。HDR-TSP 和 HDR-TSL 是三符号模式速率到 33.3MHz。但 HDR 模式不是所有设备都支持而且切换过程复杂需要先发 CCC 命令进入 HDR 模式传输完再退出。实际项目中用得少SDR 模式 12.5MHz 已经够大多数场景了。6.4 电源管理和低功耗I3C 支持低功耗模式总线空闲时可以进入低功耗状态。从设备也可以请求进入低功耗模式。这个在电池供电的设备上很有用。但低功耗模式的配置比较复杂需要协调主设备和从设备的电源状态。RK3576 的 I3C 驱动支持基本的电源管理但高级特性可能需要自己实现。7. 实测数据与性能对比我在 RK3576 上做了一组对比测试条件如下总线I3C0配置 12.5MHz从设备一个支持 I3C 的传感器型号就不说了避免广告嫌疑测试内容连续读 1KB 数据重复 1000 次对比组同一传感器用 I2C 模式400kHz结果模式平均耗时有效速率相对倍数I2C 400kHz28.5ms35.9KB/s1xI3C 12.5MHz3.2ms320KB/s8.9x差不多 9 倍。如果算上 IBI 省掉的中断线轮询开销实际系统级收益更大。但注意这是理想条件单一设备、短走线、无干扰。实际项目里如果总线负载重、走线长、有电磁干扰速率会打折扣。8. 常见问题速查问题可能原因解决办法设备不识别静态地址错、上拉不对、设备不支持 I3C查手册、换 2.2k 上拉、确认设备能力IBI 不触发IBI 未使能、仲裁失败、设备不支持检查驱动配置、减少总线负载、查设备手册速率上不去总线电容大、设备能力限制、协议开销缩短走线、换高速设备、接受现实I2C 设备不工作电气不兼容、模式配置错移到纯 I2C 控制器、用 i2c 子节点DAA 失败地址冲突、时序问题、电源不稳检查地址、加复位、稳定供电波形异常pinctrl 配错、时钟不对查 pinctrl function 编号、确认时钟源9. 写在最后I3C 是个好东西但也不是银弹。它在多传感器、高速、低引脚数的场景下优势明显但在简单项目里I2C 依然是最经济实惠的选择。RK3576 的 I3C 支持还算完善DTS 配置也不复杂但调试过程中确实有不少坑尤其是 pinctrl 和上拉电阻这两块我前后折腾了好几天才搞定。如果你正准备在 RK3576 上试 I3C我的建议是先用逻辑分析仪把波形抓出来确认电气层没问题再去看协议层。很多时候问题不在软件而在硬件。上拉电阻、走线长度、电源质量这些基础的东西往往是最容易被忽略的。另外内核文档 Documentation/devicetree/bindings/i3c/ 和 Documentation/driver-api/i3c/ 一定要看里面的示例比网上大部分教程都靠谱。瑞芯微的 TRM 也要翻尤其是 I3C 控制器的寄存器章节虽然枯燥但关键时刻能救命。最后说一句I3C 的生态还在发展中设备选择不如 I2C 丰富价格也偏高。选型的时候要综合考虑不要为了追新而追新。适合的才是最好的。
返回列表