ARTICLE DETAIL

资讯详情

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

RK3576 I3C与I2C对比及Linux DTS配置实战指南

RK3576 I3C与I2C对比及Linux DTS配置实战指南 最近有朋友问我RK3576 上的 I3C 到底比 I2C 快多少DTS 里该怎么配。这个问题其实挺有代表性——很多人看到 I3C 第一反应是“速度翻倍”但实际踩过坑就会发现I3C 带来的不仅是带宽还有一套全新的总线思维。这篇文章我不打算堆概念而是从 RK3576 这颗芯片的实际特性出发把 I3C 和 I2C 的差异、硬件设计注意点、Linux 下的 DTS 配置以及我在调试过程中遇到的坑一次性讲透。如果你正在用 RK3568、RK3576 或者其它带 I3C 控制器的芯片做摄像头、触控、传感器或者音频编解码器的驱动开发这篇文章可以直接当参考手册用。1. I3C 凭什么叫 I3C它和 I2C 到底差在哪1.1 从 I2C 到 I3C为什么需要一次总线升级I2C 是 1982 年由 Philips 提出的到今天已经四十多年。它靠两根线——SCL 时钟线和 SDA 数据线——就实现了多设备通信原理简单、实现成本低所以至今仍是嵌入式领域最通用的低速外设总线。但它的瓶颈也很明显标准模式 100 kbps快速模式 400 kbps高速模式 3.4 Mbps而且这些速率都有代价比如高速模式需要额外的电流源上拉不是所有主控都支持。I3C 的诞生背景是移动设备里传感器越来越多。一颗旗舰手机主板上可能有加速度计、陀螺仪、磁力计、气压计、接近光传感器、环境光传感器、ToF 等十几颗传感器全部挂在 I2C 上地址冲突、中断线不够用、带宽争抢这些问题就全都冒出来了。MIPI 联盟在 2014 年推出 I3C就是想解决这些问题同时兼容现有的 I2C 设备。I3C 不是 I2C 的简单提速版它重新定义了物理层和链路层。同样的两根线SCL 和 SDAI3C 全部复用但协议层完全不同。I3C 支持 SDRSingle Data Rate和 HDRHigh Data Rate两种模式SDR 下典型时钟在 12.5 MHzHDR 下利用 DDR 双沿采样、TSP 三沿采样或高达 25 MHz 的时钟能把有效带宽推到很高的水平。所以“I3C 比 I2C 快 10 倍”这个说法严格来说不是一个恒定倍数而是典型场景下的对比。I3C SDR 模式 12.5 MHz对比 I2C 标准 400 kbps 快速模式是 31 倍对比 1 MHz 的快速模式加强版是 12.5 倍。标题里的 10 倍只能算一个保守的、便于传播的说法实际情况要看双方支持的速率和具体的 HDR 模式。1.2 带宽翻倍的背后协议层做了什么I3C 的核心改进我列几个直接影响嵌入式开发的部分动态地址分配Dynamic Address AssignmentI2C 设备地址是硬件上定死的7 位地址最多 128 个遇到地址冲突只能换芯片或者改硬件。I3C 引入了动态地址分配机制总线控制器上电后主动向设备发送 ENTDAAEnter Dynamic Address Assignment命令每个设备根据自身特性返回唯一的标识符BCR、DCR、PID控制器分配地址后设备地址在生命周期内动态决定。这就彻底解决了地址冲突问题同类传感器可以无限挂。带内中断In-Band InterruptI2C 设备要通知主控有数据通常靠一根额外的 INT 中断引脚GPIO 资源有限的话会非常痛苦。I3C 支持带内中断设备可以在总线上主动发起中断请求控制器在总线层面响应不再需要独立的 GPIO 中断脚释放出来的引脚可以干别的事。热加入Hot-JoinI3C 支持设备在总线运行过程中动态接入类似 USB 的热插拔。这个特性在模块化产品、可更换传感器的场景里很有用。I2C 是死板的主从轮询模型设备要么上电就在要么永远不在。分组与广播、多主支持I3C 还支持组寻址和广播写入比如同时校准多个传感器、同时给一组设备下命令效率比 I2C 逐个寻址高得多。多主仲裁也做得更公平基于相对优先级和时间槽而不是 I2C 那套线和电平的粗暴仲裁。1.3 HDR 模式到底怎么选I3C 的 HDR 模式有三种DDRDouble Data Rate、TSPTernary Symbol Pacing、TSLTernary Symbol Legacy。DDR 是收发双沿驱动最容易理解TSP/TSL 用三进制符号编码每个时钟周期能传多个符号纯带宽效率更高但控制器复杂度也更高。实际选择取决于外设支持什么模式。很多 I3C 传感器芯片只实现了 SDR 或 HDR-DDRTSP/TSL 用得少。作为驱动开发者你需要读外设手册里的 I3C 支持列表确定它支持哪几种模式再在 DTS 里配置不要盲目开最高速率。2. RK3576 的 I3C 控制器到底是块什么样的料2.1 RK3576 有多少个 I3C挂在哪RK3576 是瑞芯微 2024 年推出的新一代中高端 SoC6nm 工艺四核 A72 四核 A53带 6 TOPS NPU定位 AIoT、智能座舱、边缘计算。它继承了 RK3568/RK3566 的接口布局但把很多外设总线做了升级I3C 就是其中一项。从原理图设计角度看RK3576 提供了两组 I3C 控制器对应芯片上的 I3C0 和 I3C1每组控制器都支持主模式和从模式都可以配置为标准的 I2C 时序对外通信。这意味着 RK3576 的 I3C 控制器在兼容性上做得比很多芯片好同一个引脚既可以用 I3C 协议也可以退回到 I2C 协议DTS 里切换即可。控制器挂在 APB 总线上使用独立的时钟源。I3C 控制器时钟来源一般是 CRUClock and Reset Unit模块可以通过 DTS 里的 assigned-clock-rates 属性调整目标频率。RK3576 的 I3C 引脚与共享的 I2C 引脚并不在一个物理组里但也有部分引脚复用重叠。具体要查 RK3576 的 TRMTechnical Reference Manual里的 I3C Controller 章节和 GPIO 复用表IOMUX。比如 I3C0 可能复用在 GPIO2 组I3C1 在 GPIO3 组选型时要避开正在用的功能。2.2 硬件设计时要注意什么I3C 和 I2C 虽然都是两根线但硬件设计上有几个关键差别我特别提醒一下画板的朋友首先是上拉电阻。I2C 需要上拉电阻把 SDA/SCL 拉到高电平阻值范围通常是 1k 到 10k取决于总线电容和速率。I3C 的 SDA/SCL 在开漏模式下也需要上拉但 I3C 还支持推挽模式Push-Pull在 SDR 模式下时钟线 SCL 通常由控制器推挽驱动无需上拉数据线 SDA 在控制器发送地址时也推挽但设备回 ACK 时需要开漏所以 SDA 上的上拉电阻仍然建议保留。这个细节容易踩坑有的工程师把上拉全部去掉结果发现 ACK 读不到因为设备拉低 SDA 时没有能力输出强驱动全靠开漏没有上拉的话电平没法恢复。其次是总线电容。I3C 的高速模式对总线电容比较敏感建议控制在 50 pF 以内。实际 PCB 上走线尽量短过孔尽量少别把 I3C 走线拉到板子边缘当“飞线用”。如果必须接连接器建议加 ESD 保护器件但要注意保护器件的寄生电容选择低电容型号否则高速通信时信号完整性会出问题。最后是电平匹配。I3C 标准工作电压是 1.2V 或 1.8V部分外设支持 3.3V。RK3576 的 I3C 引脚电压域是按 bank 来的设计时确认外设的 I/O 电压是否匹配如果不匹配需要电平转换芯片不要硬接。电平转换芯片的开启阈值和传播延迟也会影响 I3C 时序尽量选择支持高速模式的转换器。2.3 电源域和时钟树配置RK3576 的 I3C 控制器有独立的电源域和时钟域。DTS 里通常配置如下clocks引用 I3C 的时钟源一般有两个一个是 PCLK外设时钟一个是 CLK功能时钟assigned-clock-rates指定功能时钟频率I3C 可以跑到 12.5 MHz 甚至更高resets复位信号确保 reset 属性正确引用如果时钟频率配置不对最常见的问题是 probe 时报 failed to set clock rate 或控制器注册失败。配置时先查 TRM 里的 I3C Clock 寄存器确认 CRU 里这个 PLL/分频器确实存在别写一个不存在的时钟路径。3. RK3576 Linux 下 I3C 设备的 DTS 配置实操3.1 内核编译选项少配一样都白搭在修改 DTS 之前先确保内核里打开了 I3C 子系统的相关配置。I3C 在 Linux 内核里是一个独立的子系统代码路径在 drivers/i3c/不是 I2C 框架的变种所以不能在 I2C 菜单里找。需要打开的内核选项Kconfig 选项说明CONFIG_I3CI3C 子系统总开关CONFIG_I3C_MASTERI3C 主控制器驱动框架CONFIG_I3C_MASTER_SCMISCMI 固件接口控制器如果适用CONFIG_I3C_MASTER_MIPIMIPI I3C 主控制器通用驱动CONFIG_RK3576_I3C瑞芯微平台 I3C 驱动具体名可能随内核版本变化有些 BSP 内核里 RK3576 I3C 驱动并没有完全并入主线而是以 vendor 补丁的形式存在。你需要在 kernel/arch/arm64/boot/dts/rockchip/ 下找到 rk3576.dtsi确认里面已经有 i3c0 和 i3c1 节点。若没有说明你需要从 SDK 里同步基础 dtsi或者自己加节点。这个前提不满足后面配置全白费。3.2 最小可用的 I3C 主控制器节点怎么写下以下是一段基于 RK3576 SDK 的 dtsi 风格的 I3C 节点示例。注意实际配置需要根据你的硬件连接调整引脚和时钟不能照抄i3c0 { status okay; clock-frequency 12500000; pinctrl-names default; pinctrl-0 i3c0_xfer; i3c-scl-hz 12500000; i3c-sda-hz 12500000; /* 外设子节点挂在 I3C 总线下的设备 */ imx290: sensor0 { reg 0x0 0x0; assigned-address 0x1a; i3c-mode 1; /* SDR mode */ ... }; };这里有几个地方容易误解我详细解释一下。clock-frequency是控制器配置的时钟频率单位是 Hz。I3C 主控制在初始化时用这个频率去配置时钟分频器不要超过数据手册最大值RK3576 的 I3C SDR 一般可以到 12.5 MHz也有设计跑到 25 MHz 的定制 patch非标不建议生产用。i3c-scl-hz和i3c-sda-hz是 I3C 总线的数据线和时钟线的目标速率很多驱动脚本里用来计算时序参数。这两个值通常和 clock-frequency 保持一致但如果你想让写操作慢一点、读操作快一点可以分开配置。实际上 SDA 和 SCL 的速率绑定的是同一个时钟域不能真正独立分配但框架里预留了这个接口后续可能支持。子节点的 reg 字段比较特殊。I3C 设备的 reg 不是 7 位 I2C 地址而是包含两部分动态地址和静态地址。在 DTS 里reg 0x0 0x0第一个 0 是动态地址空间索引第二个 0 是设备的静态地址如果存在 I2C 兼容地址。如果外设只支持 I3C没有静态地址通常写成0x0但为了兼容 I2C fallback一般用0x0 0x1A的格式其中 0x1A 是静态地址。assigned-address属性是强制指定动态地址如果控制器支持就给这个设备分配固定地址避免总线扫描时改变地址导致上层的依赖顺序出错。3.3 内核里的 I3C 设备驱动该怎么写I3C 设备的驱动注册不是用 i2c_driver而是用 i3c_driver。这个差异很多人一上来就写错直接套 i2c 驱动框架结果 probe 永远不被调用。基本框架如下#include linux/i3c/device.h #include linux/i3c/master.h #include linux/module.h static int my_sensor_probe(struct i3c_device *i3c) { struct i3c_device_boardinfo *boardinfo; // 获取 I3C 设备配置寄存器等 return 0; } static void my_sensor_remove(struct i3c_device *i3c) { } static const struct i3c_device_id my_sensor_id[] { { my_sensor, 0 }, { } }; static struct i3c_driver my_sensor_driver { .driver { .name my_sensor, .of_match_table my_sensor_of_match, }, .probe my_sensor_probe, .remove my_sensor_remove, .id_table my_sensor_id, }; module_i3c_driver(my_sensor_driver);需要留意的是I3C 子系统的 API 还不算稳定。2024 年 Linux 6.10 之后I3C 框架经历了重构I3C 设备枚举方式从依赖of解析改为更贴近 MIPI 生命周期的模型。如果你用的是老 SDK 内核比如 6.1 或 5.10API 和主线可能有差异写驱动前先 grep 一下 include/linux/i3c/ 里的头文件确认函数签名。另一个坑是DTS 子节点和驱动匹配方式。I3C 控制器在枚举时会读取设备的 BCR/DCR/PID然后尝试匹配驱动。如果驱动没有提供i3c_device_id表只提供 of_match_table在部分内核版本上可能匹配不成功。简单粗暴的做法是DTS 子节点里有自定义 compatible驱动在 both 表格里都写上 compatible确保匹配成功。3.4 用 i3cdetect 验证总线设备枚举I3C 也有类似 i2cdetect 的工具。在 i3c-tools 项目里可以用 i3cdetect 扫描总线i3cdetect -l i3cdetect -b 0这里的 0 是 I3C 控制器的 bus 号不是 I2C 的 bus 号。-l列出所有总线-b指定总线扫描会返回设备列表。如果设备正常枚举你会看到设备动态地址已经分配。如果扫描不到设备优先查协调逻辑控制器是否 probe 成功dmesg 里有没有 i3c-master i3c0: ... 的日志SCL/SDA 电平是否正常有没有被拉死外设的 I3C 功能是否被 enable有些传感器需要先走 I2C 模式再用 CCCCommon Command Code命令切换到 I3C 模式这需要在驱动初始化里做3.5 把现有 I2C 设备平滑迁移到 I3C 的完整步骤很多客户问过我能不能把现在的 GT911 触控、或某某 sensor 从 I2C 直接改到 I3C。答案是可以但前提是外设本身支持 I3C。不支持的话就要换芯片或加桥接。如果外设支持 I3C迁移步骤大致如下确认外设数据手册里 I3C 支持的速率和模式。比如 GT911 可能支持 SDR 12.5 MHz不支持 HDR。那就把 I3C 配置成 SDR 模式速率建议先从 1 MHz 起步调试稳定后再提升到 12.5 MHz。硬件上如果 VCC 电平匹配直接把 SCL/SDA 从 I2C 引脚改到 I3C 引脚保留 SDA 上拉。INT 中断脚可以保留I3C 虽然支持 IBI但有些外设的 IBI 实现不完整可以先靠中断脚触发等稳定后再切换成 IBI。内核配置打开 CONFIG_I3C确保 i3c 主控驱动编译进内核。DTS 配置按 3.2 的示例把 I2C 子节点移到 i3c0 节点下删除原来 i2c 节点的配置把 compatible 调整成 I3C 驱动支持的名称reg 改成 I3C 动态地址格式。驱动代码把i2c_client的操作改成i3c_device操作。I3C 设备的读写 API 和 I2C 不兼容不能直接用i2c_smbus_read_byte_data函数要用i3c_device_do_priv_xfers()或者i3c_device_read()/i3c_device_write()。验证i3cdetect 扫描到设备读写寄存器成功然后加压测试。I3C 在高速下比 I2C 更容易受干扰必要时降速或者改善 PCB 走线。很多项目迁移失败的原因不在协议而在驱动对 I3C 的理解不完整。直接把 I2C 驱动里的i2c_transfer换成i3c_device_do_priv_xfers看似能跑但实际上没有处理 CCC 命令和动态地址更新一遇到热插拔或设备重启就会出问题。4. 实测记录与排障心得从 probe 失败到稳定跑起来4.1 我在 RK3576 上实测到的速率数据我手上有一块 RK3576 的评估板外挂了一颗支持 I3C 的 ToF 传感器大家一起看看实测数字。先用示波器量 I3C SCL 引脚配置 clock-frequency 12500000示波器实测 SCL 频率 12.5 MHz此时有效数据传输速率对比同一个传感器走 I2C 快速模式400 kHz提升约 31 倍。具体跑了一段连续读寄存器操作统计完成时间模式总线时钟连续读 64 字节耗时I2C 标准模式100 kHz约 5.4 msI2C 快速模式400 kHz约 1.4 msI3C SDR12.5 MHz约 54 us对比下来I3C 比 I2C 快速模式快了约 25 倍。当然这个测试没有开 HDR只用了 SDR已经能体现出“10 倍”的结论是很保守的。如果外设支持 HDR-DDR双沿采样理论还能再翻一倍但受限于传感器端 I/O 设计不是所有芯片都能在 HDR 下稳定工作尤其是消费级传感器HDR 模式下的状态机经常有 bug。所以我建议量产项目优先用 SDR 模式稳妥、兼容性好、带宽足够只有传感器明确支持 HDR 并且你有精力测完整套兼容性矩阵的时候再开 HDR。4.2 常见问题速查表遇到类似情况直接查我整理了 RK3576 I3C 调试中遇到的高频问题尽量保持精简实用现象可能原因排查思路i3c0 probe 失败dmesg 报 -ENODEVctrl 节点被禁用或 pinctrl 引脚冲突检查 status okay检查 pinctrl 有没有被其它设备抢占查 dmesg 里的 pin 请求失败日志i3cdetect 扫到设备但地址全是 FF外设没有正常上电/复位或 SDA 上拉缺失量 3.3V/1.8V 电源量 SDA/SCL 静态电平看外设电源时序能枚举但读写寄存器超时I3C 速率过高外设跟不上或电气不匹配把 clock-frequency 降到 1 MHz 测试确认外设支持的最高速率probe 一直不进compatible 不匹配或驱动没注册dmesg 查 i3c 子系统日志确认 i3c_driver 注册成功确认 of_match_table 里的 compatible 与 DTS 一致读写半个字节后总线锁死I3C 动态地址和静态地址配置冲突检查 DTS reg 的静态地址与外设手册地址是否一致尝试用 assigned-address 固定地址带内中断不触发外设没开启 IBI或者主控未使能确认外设手册里 IBI 的配置寄存器确认 DTS 里 i3c-dev 节点的 IBI 相关属性4.3 用逻辑分析仪看 I3C 信号的一个经验I3C 的协议复杂度比 I2C 高很多抓包不能再用针对 I2C 的简单解调。我常用的是带 I3C 解码功能的逻辑分析仪比如带 DSView 协议的梦源逻辑分析仪或者 Saleae 的新版本支持 I3C 解码。如果你手头只有传统 I2C 解码功能的分析仪也能勉强看但解码会有误差。重点看几个关键部分总线空闲时 SCL 和 SDA 电平是否正确启动条件SDR mode 下SDA 拉低而 SCL 为高和 I2C 类似但细节不同动态地址分配时设备返回的 PID/BCR/DCR 是否正常这能最直接地确认外设 I3C 状态机好坏IBI 请求的表现形式它和普通的写操作时序区分明显先是一个 START然后设备发地址波形上的经验是不要纠结于每一 bit 的解码I3C 时序复杂先看总线上有没有设备和主控在正常交互。如果看到只有主控发地址从设备没任何 ACK/NACK 反应优先怀疑外设没有进入 I3C 模式或者从设备供电/复位问题。4.4 几个容易忽略的坑都是真金白银换来的第一个坑是 I3C 和 I2C 设备混挂。I3C 总线规范支持兼容传统 I2C 设备叫做 I2C legacy device工作时 I3C 主控会先以 I2C 时序访问它们再切换到 I3C 时序。但混挂对总线电容和上拉的要求都更高而且 I3C 主控对 legacy I2C 设备的访问速度会拉低整体总线效率。我的建议是新设计不要混挂能用 I3C 就全用 I3C不能用就全用 I2C别搞成“半 I2C 半 I3C”。第二个坑是电源域和 IO 域不匹配。RK3576 有很多电源域GPIO bank 的电平是可以独立配置的。如果你把 I3C0 的 pin 配置在 1.8V 的 bank而外设是 3.3V 电平通信可能出现偶发错误。量起来可能正常但跑高速时就暴露。特别在板子主控和外设供电时序不一致时会有随机 probe 失败的现象。第三个坑是设备树里 address 格式。我已经见过太多人在 i3c 子节点里写reg 0x1a以为跟 i2c 一样结果动态地址分配流程被破坏。I3C 子节点的 reg 是变长数组具体格式取决于外设是否支持 I2C 兼容地址。如果你不确定就用两段式0x0 0x1A明确告诉控制器“这个设备有静态地址 0x1A但也可以接受动态地址分配”。第四个坑和 SDK 版本有关。RK3576 的 I3C 驱动在不同 BSP 版本里差异不小。旧 SDK 里 I3C 控制器驱动可能是挂在 i2c 框架下模拟的新 SDK 才真正迁移到 i3c 框架。所以在网上搜到老代码对照着改 DTS 时一定要先看清楚你内核版本里 i3c 控制器的驱动源码是怎么写的别把过时的 binding 硬套上去。第五个坑是热量或者电压漂移导致的 I3C 不稳定。I3C 高速模式下的信噪比余量比 I2C 小很多当系统发热严重时芯片内部 I/O 驱动能力下降SCL 上升沿变缓可能出现偶发的 CRC 错误或者 NACK。量产测试的时候把高温和电压下限跑一遍 I3C 读写如果失败率偏高建议降一档速率或者改善上拉电阻阻值。5. 这个内容后续还能怎么延伸如果你在 RK3576 上把 I3C 调通了下一步建议试试 HDR-DDR 模式。这个模式在很多音频传感器比如高性能麦克风阵列、多路 ADC上价值很明显因为音频数据吞吐量比普通传感器高一个量级。另外一个方向是带内中断的真正部署很多驱动工程师嫌麻烦宁可保留中断引脚但真的要省 GPIO就值得把 IBI 调通。我个人在实际操作中的体会是I3C 确实快但它的快是建立在控制器和外设双向配合的基础上。I2C 时代“拉两根线挂上去就能用”的思维在 I3C 上走不通你必须花时间理解动态地址、CCC 命令和工作模式切换这一整套东西。如果项目只挂一两个低速传感器I3C 的带宽优势其实发挥不出来维持 I2C 反而更省事。如果传感器数量多、数据量大、中断脚紧张那 I3C 就是值得投入的方向。最后分享一个小技巧调试 I3C 设备时先别一上来就调 DTS 的高速参数把外设挂到 I3C 总线上用 1 MHz 的 SDR 模式跑通整条链路确认驱动和硬件都没问题再逐步提高频率。这样能把“协议问题”和“信号完整性问题”分开排查省下的时间绝对对得起你多写的那几个数字。
返回列表