免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

RK3576 I3C调试笔记:从I2C迁移到高速总线的DTS配置与踩坑

RK3576 I3C调试笔记:从I2C迁移到高速总线的DTS配置与踩坑 I3C 和 I2C 之间的话题最近因为 RK3576 这类新平台又热了起来。网上到处是“I3C 比 I2C 快 10 倍”的说法可真拿到开发板很多人连 DTS 里的节点都配不出来外设不识别、速率上不去甚至整条总线卡死。这篇文章就从接口特性出发把手头 RK3576 项目里调 I3C 的笔记整理一遍先说清楚快 10 倍到底怎么来的再讲 I3C 和 I2C 的机制差异最后给出设备树 DTS 配置和一组排查经验。适合正在做 sensor hub、传感器透传、多摄配置或者单纯想把 I2C 总线换掉的朋友参考。1. I3C 比 I2C 快 10 倍先看协议层面的答案1.1 “快 10 倍”是怎么算出来的I2C 的速率档位大家都熟标准模式 100kbps快速模式 400kbps快速增强模式 Fm 也就 1Mbps。再往上走开漏输出加外部上拉的结构很难把信号边沿做陡因为上拉电阻和总线电容构成的 RC 时间常数就摆在那想快只能把上拉电阻往小里调可电阻一小静态功耗和灌电流又扛不住。所以 I2C 在物理层上基本到头了。I3C 的 SDR 模式标称可以跑到 12.5MHzHDR-DDR 模式在双沿采样下等效 25Mbps 左右。简单算个账12.5MHz 除以 400kHz 是 31.25 倍除以 1MHz 是 12.5 倍HDR 模式相对 400kHz 就是 62.5 倍。所以“I3C 比 I2C 快 10 倍”这个说法不夸张甚至属于保守宣传。厂商喜欢拿它当卖点就是因为数字够直观。但实际项目里别指望随手就能享受这个倍率。I2C 传输有 START、STOP、地址、ACK 这些固定开销I3C 同样有地址、CCC 命令、动态地址分配等协议开销还会受从设备能力、总线混接情况影响。我在 RK3576 板上用一批数据连续读实测从 400kHz 的 I2C 切到 12.5MHz 的 I3C SDR有效吞吐大概提升了 5 到 8 倍和理论值差一截但体感已经非常明显。把所有外设都堆在一条总线上、混着不支持 I3C 的老设备那能跑到 2 倍都算不错。1.2 除了速率I3C 还动了哪些“老规矩”很多人把 I3C 理解成“I2C 加了个高速档位”其实机制变化比速率大得多。I2C 是开漏结构所有设备靠外部上拉把电平拉高设备要输出低电平就主动拉低所以天然支持线与仲裁I3C 的 SDR 模式大部分时间用推挽输出边沿快、驱动能力强只有特定阶段才切回开漏。这就是速率能上去的物理基础。I2C 的从机地址是硬件固定死的7 位地址要避让、要查表一根总线上挂十几个设备就很拥挤。I3C 引入了动态地址分配 DAA上电后主机发出 ENTDAA 命令每个从机用自己的临时身份响应由主机统一分配动态地址。这个机制还带来了两个 I2C 想都不敢想的功能一是热加入设备可以在系统运行中插到总线上主机发个广播就能把新设备纳入管理二是带内中断 IBI从机想报告事件时直接拉总线发起中断不用额外拉一根 INT 引脚。再说兼容性。I3C 控制器通常能跟老 I2C 从设备通信这是设计上保留的 legacy 能力。但代价是只要总线上有一个纯 I2C 设备主机访问它的那段时间就必须退回到 I2C 时序I3C 的优势就得打折。我见过有人把 SSD1306 OLED、EEPROM、温湿度传感器全挂在一个 I3C 控制器下结果 OLED 和 EEPROM 只能跑 400kHz主机频繁切换模式高速传感器的吞吐也跟着遭殃。I3C 不是简单地把 I2C 外设搬到高速总线上就完事总线规划比之前更重要。1.3 什么时候值得换 I3C我的判断很简单外设数量多、中断需求多、配置数据量大这三条占一条以上才值得折腾 I3C。典型场景是 sensor hub一颗 IMU、一颗气压计、一颗磁力计挂在同一根总线上如果都用 I2C 加硬件中断脚三颗传感器就是三根 GPIO板子布局非常痛苦用 I3C 的 IBI 功能中断直接走总线GPIO 全省了。摄像头 sensor 的控制通道也类似配置寄存器一般有几十上百个寄存器分辨率越高配置数据越大I3C 高带宽能明显缩短上电出图时间。反过来如果板上主要是一堆 OLED、RTC、EEPROM、音频 codec这些外设要么只给几字节状态要么配置一次就基本不动I2C 的 400kHz 完全够用。硬上 I3C 反而要面对驱动不成熟、从设备不支持、DTS 配置复杂一堆问题。另外一个现实是市面上真正支持 I3C 的从设备还不太多选型时不要只看宣传页写了“I3C”就下单要确认它支持的是 SDR 还是 HDR支不支持 DAA 和 IBI。只有 SDR 也够快但和 12.5MHz 的宣传上限还是有差距。2. RK3576 上的 I3C 接口先看资源和引脚2.1 RK3576 的 I3C 控制器与 Linux 驱动栈RK3576 在瑞芯微的产品线里算中高端外设资源比 RK3568 这一代丰富不少。手册里 I3C 控制器不止一个具体编号和引脚复用关系要翻对应 TRM不同封装、不同开发板引出来的接口不一样。有些 RK3576 开发板把其中一组 I3C 引脚复用成了普通 I2C你光看原理图以为能用 I3C实际 bootloader 里已经把 pinmux 切走了这是最开始容易踩的坑。Linux 这边的软件栈已经比较成熟I3C 控制器驱动通常在drivers/i3c/master/下面设备树里通过 compatible 匹配到对应驱动。I3C framework 和 I2C framework 是两套东西不是简单的“把 compatible 改成 i3c 就能用”。I3C master 在上电后要先发起动态地址分配把总线上的 I3C 从设备都拉进系统这个过程发生在驱动 probe 阶段。传统 I2C 设备靠固定地址注册I3C 设备则是先探测后分配所以 dmesg 里看到的日志风格也不一样别拿老经验硬套。这里有个容易忽略的点I3C 控制器驱动如果注册成了 i3c master它和 i2c subsystem 的协作方式取决于内核版本和 vendor 实现。有的实现会把 legacy I2C 设备直接挂到 i3c 总线节点下通过 I3C framework 访问有的则要求你额外注册一个 I2C adapter两类设备各走各的路径。所以配 DTS 之前先去看 SDK 里有没有现成的 I3C 设备 demo 或者文档比照葫芦画瓢要靠谱得多。2.2 硬件连接与上拉电阻选择I2C 时代大家习惯直接上 4.7k 上拉电阻这一套在 I3C 下不一定行得通。I3C SDR 模式大量使用推挽输出上升沿靠主控的驱动能力推上去外部上拉只是辅助但总线进入开漏相位时比如仲裁或 legacy I2C 访问又需要上拉电阻把电平拉高。上拉电阻太大边沿变缓高速下容易采错上拉电阻太小开漏低电平时的灌电流太大可能把从设备的驱动管搞发热甚至损坏。我调试时一般从 2.2k 起步再根据波形调整到 1k 左右具体以信号完整性和从设备手册为准。电平匹配也要格外小心。RK3576 的 IO 电压由对应电源域决定常见的是 1.8V 或 3.3V 配置。新版 I3C 传感器很多是 1.2V/1.8V 电平而那些老 I2C 外设比如 0.9 寸 OLED 模块普遍是 3.3V混接在一条总线上轻则电平不识别重则反向漏电把 IO 搞坏。我建议先把同一组总线上的设备按工作电压分类电压不一致就拆成两条物理总线或者加电平转换芯片不要指望 DTS 能解决硬件不匹配的问题。调试工具方面普通逻辑分析仪的 I2C 解码器不能直接拿来解 I3C因为 I3C 的广播地址、CCC 命令、HDR 帧格式和 I2C 完全不一样。我手边用的是一台带宽足够的示波器先抓裸波形再配合 I3C 协议分析功能慢慢数。有些高端逻辑分析仪已经支持 I3C 协议解码能省不少事但几十块钱的玩具分析仪基本只能看个电平别浪费太多时间在上面。2.3 如何识别“真 I3C”从设备选型时不能只看“兼容 I3C”“支持 I3C”这种模糊表述。真正支持 I3C 的从设备datasheet 里一定会提到 MIPI I3C、DAA、IBI、CCC、HDR-DDR 这些关键词并且会给出 I3C 模式下的寄存器初始化序列。如果全文只写“I2C compatible”那大概率只是普通 I2C 设备放在 I3C 总线上只能走 legacy 路径。还有一个实用的判断方法看 Linux 内核驱动源码。支持 I3C 的设备驱动通常会注册到 i3c 子系统的 device ID 表里或者至少包含针对 I3C 特性的初始化函数。只改了 compatible 字符串、内部逻辑还是 I2C 那套那不叫支持 I3C。我习惯的做法是先按 I2C 模式把设备点亮确认功能和寄存器映射没问题再切到 I3C 模式验证 DAA 和 HDR。这样一旦 I3C 模式下有问题至少能确定不是器件本身坏掉。3. RK3576 的 DTS 配置实战从 I2C 平滑迁移到 I3C3.1 先搭一个最简 I3C 节点在动手改 DTS 之前我先把结论说在前面RK3576 的 I3C 节点写法不要直接从网上抄因为不同 SDK 的 compatible、pinctrl 命名甚至属性名都可能不一样。你要先去内核源码的Documentation/devicetree/bindings/i3c/目录下看 binding 文档对照 vendor 驱动确认细节。下面这个是最简骨架逻辑上能跑但名字要按你手里的 SDK 改i3c0 { status okay; pinctrl-names default; pinctrl-0 i3c0_xfer_pins; clock-frequency 12500000; };status okay负责使能节点pinctrl-names和pinctrl-0负责把引脚复用到 I3C 功能。这里的i3c0_xfer_pins只是示意名具体到 RK3576 的 dtsi 里可能叫i3c0_xfer、i3c0_pins或者直接复用i2c0_xfer打开 dtsi 搜索同名宏就能找到。clock-frequency是 I3C 控制器期望的外设时钟频率不是所有外设都能吃下 12.5MHz如果从设备不支持 HDR 或者只支持 SDR就把这个值降下来。挂从设备时I3C 子节点的写法比 I2C 要讲究。很多新手的错误是把 I2C 的从节点直接复制进 I3C 节点结果内核根本识别不了。下面是一个示意i3c0 { status okay; clock-frequency 12500000; #address-cells 1; #size-cells 0; pressure1e { compatible vendor,barometer-i3c; reg 0x1e; /* 某些内核绑定里 reg 可能有三个 cell静态地址、动态地址、BCR/DCR */ }; };注意#address-cells和reg的格式在不同内核版本里差异很大。有些绑定要求#address-cells 3有些直接用#size-cells 0然后 reg 写旧 I2C 地址。最好的办法是看内核里已有 I3C 设备的示例节点或者看 bindings 文档里的Example部分。不要看到别人的 RK3568 配置能用就全盘复制两代芯片的控制器驱动可能完全不一样。如果你在同一个 I3C 控制器下挂的是老 I2C 设备节点一般还是按照 I2C 子设备的方式声明让驱动把它当 legacy 设备处理。但这里有个前提你的 I3C 控制器驱动必须实现了 legacy I2C 设备访问路径否则节点写了也白写从设备永远不会 probe。所以降级方案往往更实用下面单独说。3.2 几个 DTS 属性必须弄清楚DTS 里 I3C 节点常见的属性就这么几个但含义容易搞混。clock-frequency刚才说了是控制器外设时钟决定 I3C 能跑多快它不等于总线实际速率总线速率还受从设备能力和协议模式限制。i3c-scl-hz、i2c-scl-hz这类属性在部分内核版本里分别控制 I3C 模式和 legacy I2C 模式的 SCL 频率命名没有完全统一一定以你手里的驱动源码为准。我见过有人只调clock-frequency忘了 legacy I2C 速率还是默认值结果 I3C 设备正常、OLED 反而时序不对。pinctrl配置是另一个高频翻车点。RK3576 的 I3C 引脚和普通 I2C、UART、SPI 是复用关系如果 bootloader 阶段已经被复用成了别的功能Linux 里 DTS 配得再对也没用因为引脚方向已经被占住。排查办法是看 u-boot 的 dts 或rk3576-board.dts里有没有冲突节点再配合 I3C 对应 IO 的电压域配置。只改 kernel dts 不动 bootloader很多时候根本切不过去。I3C 特有的地址属性也要留意。I2C 设备地址是固定的I3C 从设备虽然有一个静态地址或者说旧 I2C 地址但真正工作用的是 DAA 分配出来的动态地址。DTS 里写 reg 主要用来标识设备、给驱动做匹配不代表驱动运行后总线上的地址一定不变。如果你的系统做了休眠唤醒从设备断电再上电后动态地址可能会变主机必须重新走 DAA否则还在用旧地址访问必然失败。这和 ESP32 休眠唤醒后 I2C 外设不复位导致总线挂死的现象有相似之处底层都是状态不一致。3.3 降级方案把 I3C 节点当 I2C 用不管 I3C 吹得多好实际项目里总有那么一批外设不支持 I3C或者 vendor 的 I3C 驱动还不稳定。这时候最稳妥的降级方案就是让 RK3576 的控制器工作在 I2C 兼容模式整个系统先跑起来。具体做法通常是在设备树里把该节点注册为 I2C 控制器而不是 I3C 控制器并配成 400kHz 或 1MHz。示意如下i3c0 { status okay; pinctrl-names default; pinctrl-0 i2c0_xfer_pins; clock-frequency 400000; };注意这个写法只是为了说明降级思路实际 compatible 要不要改、节点该叫i3c0还是i2c0都要看 SDK 里有没有对应的 I2C controller 驱动注册。有些瑞芯微平台是把同一份硬件 IP 做成两个驱动入口由设备树 compatible 决定走哪条路有些则是固定只能按 I3C controller 注册需要内核配置项把控制器切到 legacy 模式。先翻arch/arm64/boot/dts/rockchip/里同一系列板子的 dts看看有没有现成的 I2C 节点复用同一组引脚直接参考比写新节点省事得多。我更推荐的进阶路线是分阶段迁移。第一阶段全部外设按 I2C 兼容方式跑确保硬件没问题第二阶段把一个真正支持 I3C 的高频传感器接入DTS 切到 I3C 模式其他外设继续走 legacy第三阶段等所有设备都确认支持后再统一切高速。这种渐进式做法能把排障范围缩小比一步到位然后整条总线黑屏要舒服很多。4. 调试实录速率上不去、SDA 拉死、OLED 不亮4.1 总线 SDA 被拉死的几个阶段I3C 调试里最常见的严重故障就是 SDA 被拉死整条总线一上电就卡在低电平。原因通常有三类一是 DAA 动态地址分配时序不对从机上电后没有正确响应 ENTDAA主从状态机错乱二是有纯 I2C 设备挂在总线上但它不理解 I3C 的帧格式在 I3C 主机切换模式时误动作把 SDA 抢住三是上拉电阻或电平配置不对推挽阶段推不上去开漏阶段拉不下来信号持续处于未定义电平。遇到 SDA 拉死我一般按这个顺序排查先断电用万用表量 SDA 对地阻抗排除焊接短路和芯片损坏然后把除了一个 I3C 从机之外的所有设备都摘掉用最慢的 I2C 模式尝试通信确认主控本身没问题最后再用逻辑分析仪抓上电后的第一段波形看 I3C 主机有没有正确发送广播地址、从机有没有 ACK。如果 DAA 阶段就失败直接把clock-frequency降到 1MHz 以下再试先排除速率问题。休眠唤醒也是一个容易让 SDA 锁死的场景。系统休眠后如果 I3C 控制器断电而外设还挂着唤醒时主机和从机状态不一致外设还记着旧动态地址主机直接拿新地址去访问总线就可能一直 NACK 然后卡住。处理办法是在 driver 的 resume 回调里强制重新 DAA或者干脆给外设做一个硬件复位引脚。这一点和 ESP32 休眠后 I2C 外设不复位的经典问题很像很多人只在 I2C 上踩过到了 I3C 上又踩一遍。4.2 速率上不去实际吞吐差很多人在 RK3576 上把 DTS 配成clock-frequency 12500000结果抓波形发现 SCL 频率只有几百 kHz或者频率上去了但传输效率极低。原因多半出在三个地方从设备只支持 SDR 不支持 HDR主机会根据从设备能力自动降低速率总线上混接了 legacy I2C 设备主机每次访问完老设备都要重新切换模式总线占用时间一大半浪费在切换上还有可能就是内核没有启用 DMACPU 中断频繁软件开销把带宽吃掉了。优化方向首先是确认从设备的 I3C 能力。DAA 过程中从机会上报自己的 BCR/DCR 信息驱动会记录它是否支持 HDR。如果从设备只支持 SDR你把它放在 12.5MHz 的 DTS 配置下总线也可能只能跑到 SDR 上限再高就 NACK。其次看访问模式大量小字节随机读写比批量突发传输吃亏得多I3C 的 HDR 模式适合连续 burst不适合一次一个 byte 的寄存器操作。最后检查 dmesg 里有没有 DMA 相关的报错DMA 没使能的时候高速 I3C 的收益会被 CPU 中断吃掉大半。现象可能原因建议处理SCL 最高只有 1MHz从设备不支持 HDR 或 SDR 能力较低检查芯片手册降低期望值时钟 12.5MHz 但有效吞吐低总线上混接 I2C 设备频繁切换模式拆分物理总线I3C 单独走高速设备传输偶尔 NACK上拉电阻不合适或电平不匹配换 1k~2.2k 上拉确认电压域一次读一个字节速率很慢软件访问粒度太小改成 burst 读配合 DMA 传输这个表我调试时贴在手边出现类似问题先对号入座比自己瞎改 DTS 快得多。4.3 0.9 寸 OLED 这类纯 I2C 外设怎么共存0.9 寸 OLED 模块用的是 SSD1306 或 SSD1315 驱动芯片典型 I2C 地址 0x3C只支持 I2C 协议没有任何 I3C 能力。把它直接挂到 I3C 控制器下如果 DTS 节点按 I3C 设备声明驱动会尝试用 I3C 方式访问OLED 自然不响应如果按 legacy I2C 设备声明又要看 I3C 驱动有没有把 legacy 路径暴露成标准的 i2c-adapter。我实测过几种平台有的内核版本兼容做得很好有的就卡在 probe 阶段OLED 点不亮。最省心的做法是物理隔离I3C 控制器只挂真正支持 I3C 的设备OLED、EEPROM、RTC 这类老设备留在普通 I2C 控制器上。RK3576 的 I2C 控制器数量并不少拿出一组给低速外设完全够用。这样 I3C 总线能冲到 12.5MHzI2C 总线继续 400kHz 跑它的显示刷新两边互不干扰。就算你手头只有一组引脚可用也尽量用软件把 I3C 节点配成兼容模式再挂 OLED别强行让 OLED 参与 DAA它没有这个能力。这里还有个地址规划的问题。0.9 寸 OLED 的 0x3C 是固定地址如果 I3C 总线上的某个高速传感器恰好也把静态地址标成 0x3C两个设备就冲突了。I2C 时代遇到这种情况只能牺牲一个设备但 I3C 的动态地址机制原则上可以避开前提是主机正确识别出两个设备的不同身份。DTS 里两个子节点 reg 一定要区分开不能都写 0x3C否则内核会把后一个设备当成重复节点丢掉。4.4 资源不足、中断冲突从 ACPI 到 DTS顺带说一个在很多论坛里刷屏的问题Windows 下 I2C HID 设备报“设备找不到足够资源可以使用代码 12”表面看是资源分配失败本质常常是中断号、地址空间或者 GPIO 被其他设备占用了。嵌入式 Linux 下也有类似的“玄学”就是failed to claim resource、irq xxx already assigned这类 probe 错误。原因大同小异都是设备树里 reg、interrupt、pinctrl 和别的节点冲突。排查手段很直接dmesg看 I3C 和 I2C 相关报错cat /proc/interrupts看中断号占用情况/sys/kernel/debug/gpio看 GPIO 是否被重复请求。如果某个外设用了独立 INT 脚检查该 GPIO 有没有被另一个节点通过gpio-request或者interrupt属性占用。I3C 的 IBI 功能虽然能省物理中断引脚但它同样要占一条中断线主控驱动要先向中断子系统申请资源再接收从机带内中断没有正确实现的话多个 I3C 从机的 IBI 会互相干扰表现为设备 probe 成功但一进中断就崩溃。我个人对 IBI 的态度是产品上用当然好能省 GPIO 空间但开发调试阶段宁可先给每个传感器留一根普通 INT 脚。等 I3C 总线协议、DAA、速率都稳定了再逐个把 INT 功能切到 IBI 上验证。这样即使 IBI 出问题也可以立刻切回独立中断脚继续跑业务不会让整块板子瘫在总线上。我在 RK3576 上折腾 I3C 最大的体会是别一上来就追求 12.5MHz先把 I2C 兼容模式跑通再开 I3C 模式验证 DAA最后才考虑 HDR 高速传输。这个顺序能帮你把硬件问题、协议问题和驱动问题一层层隔离开省下大量加班时间。另外一个小技巧是PCB 设计时给 I3C 的上拉电阻位置留两三个可选焊盘把 4.7k、2.2k、1k 都备好调试时直接换电阻看波形比在 dmesg 里猜半天快得多。总线高速这种事很多问题其实不是软件配错而是边沿和电平没伺候好先把物理层弄扎实了剩下的都好说。
返回列表