ARTICLE DETAIL

资讯详情

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

RK3576平台I3C总线配置实战:从I2C迁移到I3C的DTS指南

RK3576平台I3C总线配置实战:从I2C迁移到I3C的DTS指南 1. 先把“快10倍”这件事说清楚做嵌入式的人对 I2C 都有一种又爱又恨的感觉爱它简单可靠、只占两根线恨它速度上不去、地址很容易撞车做中断还得额外拉一根 GPIO。I3C 这个名字出现之后几乎所有介绍第一句都是“I3C 比 I2C 快 10 倍”。我在 RK3576 的项目里第一次认真把 I3C 用起来从接口特性一路摸到 DTS 配置踩了不少坑这里把整个过程整理出来给同样准备从 I2C 迁移到 I3C 的朋友做个参考。先说结论这个“快 10 倍”不是营销噱头但也不是一个固定不变的倍数。它是有物理基础的只是很多人没搞明白到底比的是什么拿到板子一测发现没差那么多就以为资料骗人。其实只要把 I2C 的几种速率档和 I3C 的传输模式摆在一起账立刻就算清楚了。1.1 I2C 为什么快不起来I2C 不是只有一个速度。标准模式 100 kHz快速模式 400 kHz快速加强版 FM 可以到 1 MHz高速模式 HS 可以到 3.4 MHz。但在 RK 板子上大家最常用的还是 100 kHz 和 400 kHz偶尔有传感器规格书写支持 1 MHz真正敢跑满的也不多。速度上不去的原因主要有两个层面。第一个是协议层面I2C 每个字节后面都有 ACK/NACK每笔传输要 START 和 STOP读操作还经常要 Repeated Start 先写寄存器地址再读数据这些控制开销占了大量总线时间。第二个是电气层面I2C 是开漏输出加外部上拉设备只能把总线拉低拉高完全靠上拉电阻对线上寄生电容充电。RC 时间常数摆在那里频率一高上升沿就变成一条缓慢的弧线接收端采样很容易误判。这就是为什么 I2C 明明标称到 3.4 MHz实际产品里跑 400 kHz 才是主流。1.2 速度账I3C 的 10 倍从哪来I3C 的 SDR 单数据率模式理论最高 12.5 MHz。注意单位直接差了一级I2C 我们习惯说 kHzI3C 一上来就是 MHz。拿 12.5 MHz 和最常见的 400 kHz 比是 31 倍就算拿 FM 的 1 MHz 来比也有 12.5 倍。所以资料里那句“快 10 倍”本质是把 I3C 的 SDR 模式和 I2C 最常用的 fast mode 放到一起比较得出的量级结论。I3C 还有 HDR 系列高速模式比如 HDR-DDR 的理论带宽能到 25 Mbps 以上比 SDR 又翻了一倍。不过从实际项目看HDR 用得真不多RK3576 上挂传感器、触摸屏这类场景SDR 就已经绰绰有余了。后面讲 DTS 配置时主要也是围绕 SDR 模式展开先把最基本的一条路走通再研究高速模式不迟。总线模式最高速率典型应用I2C标准/快速100 kHz / 400 kHz温湿度、EEPROM、简单传感器I2CFM1 MHz高帧率传感器I3CSDR12.5 MHz中高速传感器、触摸屏、多设备总线I3CHDR-DDR25 Mbps 以上需要大批量传输数据的场景1.3 电气模型的改变才是根本I3C 能跑上去关键不在协议书写得多花哨而是电气模型变了。SDR 模式下主控和从机的输出级大部分时间用推挽结构拉高不再靠上拉电阻慢慢充电上升沿陡很多只有需要兼容老 I2C 设备时才切回开漏。信号边沿一陡同样两根线能承受的时钟频率就完全不同。代价是有的。推挽输出意味着总线上不能随便挂一大堆设备因为每个设备的输入电容都会叠加到线上频率越高对走线长度、过孔数量、上拉参数越敏感。这也是为什么我后面反复强调别一上来就把 DTS 里的 clock-frequency 写成最大值信号质量不达标时速度越快越容易出怪问题。2. I3C 不只是快动态地址、带内中断与热接入如果 I3C 只是把 I2C 的频率调高那它根本不配叫“新一代总线”。真正让我觉得这个接口值钱的是下面几件事它们解决的都是 I2C 用了二十年都没解决的痛点。2.1 动态地址分配解决撞地址的老毛病I2C 的 7 位地址是硬编码的同型号传感器用在一条总线上十有八九要撞。I3C 引入了动态地址分配机制业内一般叫 DAA。总线上电后主控发送 ENTDAA 公共命令从机先上报自己的 Provisional ID主控再给每个设备分配一个不冲突的 7 位动态地址之后的通信全部用动态地址进行。这个机制对应的工程经验是DTS 里子节点的动态地址最好让控制器自动分配也就是填 0。手动指定动态地址虽然也合法但你要自己保证它不和总线上任何设备冲突还要考虑后续加设备时的维护成本。我做过几个项目结论都是“自己指定地址”完全不划算除非你的产品拓扑永远不变。2.2 IBI 带内中断省掉一根 GPIO用 I2C 传感器做中断通知常规做法是给传感器预留一根 INT 脚接到 SoC 的 GPIO。I3C 提供了带内中断 In-Band Interrupt设备可以直接在 SDA 线上发起中断请求主控在总线层面就能响应。少一根线、少占一个引脚、少配一组 pinctrl对 BGA 封装的芯片来说每个引脚都很值钱。带内中断不是免费的。主控需要能从正常工作状态迅速切入接收 IBI 的状态从机发起 IBI 请求也要遵守仲裁规则。这些底层东西控制器硬件已经处理了但驱动层必须注册相应的中断回调。DTS 里设备的 interrupt 配置写法和 I2C client 有些差异直接从老模板复制过来常常不生效后面第 4 节会提到。2.3 Hot-Join 热接入工作状态下动态挂设备I3C 支持设备在总线工作过程中动态接入这就是 Hot-Join。比如扩展板比主控晚一步上电或者用户在产品运行中插入一个从设备从机可以通过热接入请求让主控给它分配地址并开始通信。I2C 完全没有这个能力加设备必须断电重扫。对扩展类产品来说这个特性很实用代价是驱动里要处理热插拔事件比固定拓扑的代码多一层状态管理。2.4 CCC 公共命令与 I2C 兼容性I3C 定义了一套公共命令码 CCC用来做地址分配、地址重分配、设备状态查询、总线模式切换这些维护动作。I2C 里这些东西全都靠软件层自己协商各写各的I3C 直接把它固化在协议里规范双方只要按标准实现软件层就不用重复造轮子。CCC 的处理大多由控制器驱动自动完成不需要应用层操心但排查问题时会在 dmesg 里看到相关日志至少要知道它是什么别误判为异常。I3C 还有一个经常被忽略的能力兼容老 I2C 设备。I3C 主控可以在总线上以 legacy 模式跟传统 I2C 设备通信所以理论上一条总线上可以混挂 I3C 从机和 I2C 从机。这个功能对存量硬件很友好但工程上我不推荐混挂原因后面 DTS 部分会展开讲。3. RK3576 上的 I3C 控制器与内核现状RK3576 是一颗定位在边缘计算、平板、智能座舱这类场景的芯片4 个 A72 大核加 4 个 A53 小核算力中上外设给得也比较全。I3C 控制器在它上面不是试验品而是正经可用的外设。我手上 SDK 里看到的 I3C 主控实际是 Cadence 的 IPcompatible 里带 cdns,i3c-master 的 fallback 字符串不同 BSP 版本可能略有差异但整体结构一致。3.1 和 I2C 共用引脚只能二选一RK3576 上的 I3C 和 I2C 往往是同一组引脚复用出来的比如 i3c0 和 i2c0 可能共用同一对 SCL/SDA。这带来两个直接结论一是同一时间只能启用一个控制器i2c0 和 i3c0 都设成 okay 会引发 pinctrl 冲突二是从硬件改款角度看原来画 I2C 传感器的地方只要传感器换成支持 I3C 的型号原理图基本不用动改软件配置就行这对老产品升级非常友好。内核侧对应的驱动在 drivers/i3c 子系统里。Linux 主线有独立的 I3C frameworkmaster driver 挂在下面Cadence 对应 cdns-i3c-master.c。编译前确认 CONFIG_I3C 和相关 master driver 是打开的Rockchip 的 BSP 有时会维护自己的 vendor 版本名字不一定一样先在 menuconfig 里搜 i3c 关键字。3.2 电气特性别想当然I3C 速度快对板级参数的要求显著变高。I2C 用 4.7kΩ 甚至 10kΩ 上拉很常见I3C 总线则希望上升沿更陡上拉电阻过大、走线太长、过孔太多波形边沿会迅速劣化。我在实际调试中遇到过把总线频率从 10 MHz 降到 3 MHz原来频繁丢 ACK 的设备马上就正常的情况最后排查出来就是一段 10cm 左右的飞线加高阻上拉惹的祸。还有一个容易踩的点是时钟延展。I2C 从机可以随时拉低 SCL让主机等它准备好这是 I2C 的保命机制。I3C 规范在某些阶段不允许时钟延展老 I2C 设备混挂在 I3C 总线上时它的时钟行为可能直接违反规范。所以混挂老设备不能只看地址对得上就完事时序合规性才是真正要担心的问题。3.3 从 I2C 切到 I3C 的迁移成本如果只算软件成本迁移本身不高确认板子对应的 I3C 引脚和 pinctrl把 dts 节点从 i2c 换成 i3c子节点地址写法按 I3C 规则调整驱动如果本来就有 i3c device 支持就能直接挂上。更稳妥的路线是“I2C 打底、I3C 提速”先用 I2C 模式把传感器点亮验证硬件通路再切 I3C 模式验证高速和动态地址风险逐级释放。我是建议所有从 I2C 迁移过来的项目都走一遍这条过渡路线不要在第一次点亮时就同时引入新协议和新硬件变量否则出了问题很难定位。4. 手把手配置 RK3576 的 I3C DTS进入正题。DTS 配置是 I3C 和 I2C 差异最大的地方因为 I3C 子设备节点的 reg 格式和 I2C 完全不同不把这一点弄清楚写出来的节点要么编译不出来要么设备在总线上压根不会被枚举。4.1 先看一个最小可用的 i3c0 节点下面是我在 RK3576 上调通的写法骨架寄存器地址、中断号和 pinctrl 名字以你拿到的 SDK 为准不同版本会有偏移i3c0 { status okay; pinctrl-names default; pinctrl-0 i3c0_xfer; clock-frequency 4000000; #address-cells 3; #size-cells 0; /* I3C 传感器静态地址 0x49动态地址交给控制器自动分配 */ gyro49 { compatible vendor,gyro-i3c; reg 0x49 0x0 0x0; }; };如果 SDK 里 i3c0 默认是 disabled还要确认和它共用引脚的 i2c0 已经关闭否则 pinctrl 会打架i2c0 { status disabled; };4.2 关键属性逐个解释把上面节点里的关键字段拆开讲一遍知其然也知其所以然compatible控制器节点的 compatible 给框架匹配 master driver 用一般写 Rockchip 自己的前缀加 cdns,i3c-master fallback。子节点的 compatible 给传感器驱动匹配用按你自己的驱动填写。pinctrl-0这里选的是 i3c0_xfer 这个 pinmux 组而不是 i2c0_xfer。两个功能组可能共用同一批引脚选错的话外设内存映射都正常但总线上就是没有信号。调不通时第一件事就查这里。clock-frequencyI3C SDR 模式的期望总线频率。硬件上限 12.5 MHz但我不建议直接写 12500000。板级走线、器件负载都会影响上限从低频往上加比一上来跑满速好排查得多。方案验证阶段我习惯先写 3 MHz 到 4 MHz。#address-cells 和 #size-cellsI3C master 节点必须声明的地址格式。子节点 reg 的 cell 数量和 I2C 完全不同写错整个 bus 的 device 都注册不上。status从 disabled 改成 okay。很多 SDK 默认外设是关的这一步漏掉后面全白搭。RK 系列的 I3C 控制器一般有两个时钟一个叫 baud一个叫 pclkDTS 里 clock-names 必须写对驱动才能分别拿到参考时钟和外设时钟。如果时钟树配置不对你 DTS 里写 4 MHz实际总线上可能只有几百 kHz。验证时千万别只看配置要拿逻辑分析仪抓真实波形。4.3 子节点 reg 的三个 cell 怎么填I3C 子节点的地址信息比 I2C 多得多除了静态地址还有动态地址和 Provisional ID。静态地址是设备出厂时的固定地址动态地址是 DAA 流程里主控分配的工作地址Provisional ID 则用来在 DAA 期间识别设备身份。因此在 Linux 的 I3C 绑定里master 的 #address-cells 通常为 3子节点 reg 的第一个 cell 是静态地址第二个是动态地址第三个是 Provisional ID。需要特别提醒的是不同内核版本对 Provisional ID 的 cell 拆分方式可能有差异有些实现里整个 reg 会变成 4 个 cell动手前一定先翻内核源码里的 Documentation/devicetree/bindings/i3c/i3c.txt 或对应的 yaml 文档别拿网上搜来的老例子盲抄。从 I2C 迁移过来最容易犯的错就是把子节点 reg 写成 0x49 一个 cell。这样写编译器不会报错但内核会把设备当成 legacy I2C 设备处理永远不会走 I3C 的 DAA 流程。如果你确实想让它以 I3C 方式工作就必须按 3 个 cell 的格式把静态地址放在第一格动态地址填 0 表示自动分配Provisional ID 暂时填 0gyro49 { compatible vendor,gyro-i3c; reg 0x49 0x0 0x0; };如果总线上要挂一个不支持 I3C 的老 EEPROM则按 I2C 写法i3c0 { eeprom50 { compatible atmel,24c08; reg 0x50; pagesize 16; }; };不过我要泼一盆冷水I3C 总线上混挂 I2C legacy 设备协议上虽然支持工程上我一般尽量避免。一是 I3C 主控要定期向总线发 CCC 广播老 I2C 设备对非 I2C 帧的理解可能出问题二是 legacy 设备会把总线拖回低速、开漏模式影响整条总线的吞吐违背了上 I3C 的初衷。产品里真的有一堆低速器件时宁可让它们单独走一路 I2C 控制器也别硬塞到 I3C 总线上。4.4 从零把 I3C 跑起来的步骤清单整理一份可照着做的流程按顺序执行基本不会漏看原理图确认传感器接的 SCL/SDA 对应哪组 I3C 引脚以及 pinctrl 里有没有预留对应的 i3c_xfer 配置。在内核配置里打开 CONFIG_I3C 和对应的 master driver再把传感器驱动本身打开。修改 dts启用 i3c0配置 pinctrl、clock-frequency、address-cells 和子节点 reg。确认共用引脚的 i2c0 已 disabled避免 pinctrl 冲突。编译烧录开机后执行ls /sys/bus/i3c/devices/正常情况下能看到 i3c-0 和子设备。用用户态工具验证读写。社区维护的 i3c-tools 里有 i3ctransfer可以对 /dev/i3c-x 直接发起读写如果设备被内核注册成了 legacy I2C 模式也可以用 i2c-tools 里的 i2cget 访问但要清楚这走的是兼容路径不是 I3C 的 SDR 路径。5. 踩坑记录与排查套路这部分是我最想写的。I3C 的坑基本都属于“看起来一切正常但就是不工作”的类型没有经验的话排查效率会非常低。下面把高频问题整理成速查表再挑两个我印象最深的展开说说。5.1 常见问题速查表现象常见原因排查方向子设备根本没注册reg 写错 cell 数量或静态地址不符核对 I3C 子节点 reg 格式总线上没有波形pinctrl 选错或 i2c0 还开着检查 pinmux 和节点 statusDAA 分配不成功静态地址被占用或设备不支持 I3Cdmesg 里看 CCC/DAA 日志高速必挂、低频正常上拉阻值、走线电容过大降频验证再改板级参数ACK 偶发丢失从机时钟延展行为不合规换支持 I3C 规范的从机验证设备注册成功但无中断IBI 回调没注册或 DTS 中断写法错误检查驱动 interrupt 配置用 i2cget 读 I3C 设备失败设备是纯 I3C 模式不支持 legacy 访问改用 i3ctransfer 走 I3C 路径整个排查思路的核心是“先总线、后设备、再驱动”。逻辑分析仪抓波形确认总线在动、地址对得上再怀疑设备端设备枚举成功了但没有中断再往驱动回调方向查。顺序反了很容易在原地打转。5.2 两个印象深刻的坑第一个坑是 pinctrl 冲突。第一次调 I3C我只开了 i3c0完全没注意 SDK 里 i2c0 默认就是 okay。烧录后设备死活枚举不出来dmesg 也没有明确报错因为 pinctrl 子系统只是把后申请的一方拒了。折腾半天才发现 i2c0 还占着同一组引脚。后来我把 i2c0 显式 disabled一切恢复正常。教训是Rockchip 这类 SoC 的 I2C/I3C 引脚复用关系一定要先查清楚两个功能节点宁可显式关掉一个也不要留默认状态冒险。第二个坑是频率从高往低调的教训。最初我在 DTS 里直接写 12.5 MHz传感器在 3 MHz 和 10 MHz 都能正常唯独 12 MHz 以上开始偶尔丢 ACK。用逻辑分析仪看波形大体正常但边沿明显变缓没有满足从机的最小建立时间。后来这个项目量产时也没用满速而是定在 6 MHz留足裕量。很多人以为接口支持 12.5 MHz 就必须跑满其实总线速度和信号质量之间必须留工程余量满速跑基本是实验室里才能做到的事。5.3 关于是否升级 I3C 的个人体会做了几个 I3C 项目后我的判断标准很简单要不要从 I2C 切到 I3C看的是你缺不缺带宽和引脚而不是新接口听起来很酷。如果场景只是读温湿度、读陀螺仪姿态I2C 400 kHz 根本用不完切 I3C 只是给自己增加维护成本。但如果传感器数量多、中断脚不够用、又需要高频批量读取I3C 的动态地址、IBI 和速度优势会立刻体现出来。最后分享一个我固定的调试习惯新板子第一次点亮 I3C永远先用低频起步确认 pinctrl 和地址都没问题再把频率往上拉。哪怕浪费半天跑低频也比直接跑高速抓瞎一整天强很多。逻辑分析仪的钱不要省I3C 的帧格式和 I2C 差别不小直接用 I2C 解码模板去套 I3C 波形很容易被误导。这个习惯帮我省下来的排查时间早就够买好几台分析仪了。
返回列表