ARTICLE DETAIL

资讯详情

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

RK3576 I3C总线DTS配置实战:从I2C迁移到高速I3C

RK3576 I3C总线DTS配置实战:从I2C迁移到高速I3C 1. 从 I2C 到 I3C一次总线协议的代际跃迁搞嵌入式的人对 I2C 肯定不陌生两根线SDA、SCL、一根地挂一堆传感器和 EEPROM简单好用。但用过的人都知道I2C 的速度上限摆在那里——标准模式 100kHz快速模式 400kHz高速模式撑死 3.4MHz而且每次读写都要经历起始条件、地址帧、ACK、数据帧、停止条件这一整套流程开销不小。当系统里挂的传感器越来越多或者需要更高带宽的场景比如高帧率 IMU、ToF 传感器I2C 就开始力不从心了。I3CImproved Inter-Integrated Circuit就是在这个背景下出来的。它由 MIPI 联盟主导制定目标很明确在保留 I2C 两根线物理兼容的前提下把速度拉上去、把协议开销降下来、把中断和带内命令这些能力加进来。标题里说“I3C 比 I2C 快 10 倍”这个说法其实要拆开看——I3C 的 SDRSingle Data Rate模式最高可以跑到 12.5MHz对比 I2C 快速模式 400kHz确实差不多是 30 倍对比高速模式 3.4MHz大约是 3.7 倍。所以“10 倍”是一个粗略的、面向典型使用场景的说法不是精确的倍数关系。但方向是对的I3C 在同样的两根线上能跑出远超 I2C 的吞吐。RK3576 是瑞芯微的一颗中高端 SoC面向 AIoT、边缘计算、工业控制这些场景。它内部集成了 I3C 控制器而且 Linux 内核里已经有对应的驱动和 DTS 绑定文档。这意味着你可以在设备树里直接配置 I3C 总线挂载支持 I3C 的传感器享受更高的速率和更低的 CPU 占用。但现实情况是大部分开发者对 I3C 的认知还停留在“听说过”的阶段真到要配 DTS 的时候寄存器地址、时钟频率、上拉电阻、设备地址这些细节一摆出来就容易卡住。这篇文章就是冲着这个痛点来的。我会从 I2C 和 I3C 的协议差异讲起把 RK3576 的 I3C 控制器特性拆开然后重点落在 DTS 配置上——怎么配时钟、怎么设速率、怎么挂设备、怎么排查通信失败。不管你是刚接触设备树的新手还是已经调过几轮 I2C 的老手都能从里面找到能直接抄的配置和踩过的坑。2. I2C 与 I3C 的核心差异不只是速度2.1 物理层兼容但电气特性有讲究I3C 最聪明的一点是物理层向下兼容 I2C。也就是说I3C 的 SDA 和 SCL 两根线电气上跟 I2C 是一样的——开漏输出、需要上拉电阻、支持多设备挂载。你甚至可以把 I2C 设备和 I3C 设备挂在同一条总线上I3C 控制器能识别并兼容它们。但这不意味着你可以随便拿 I2C 的板子直接跑 I3C 高速模式。关键差异在上拉电阻和总线电容上。I2C 快速模式 400kHz 下典型上拉电阻是 4.7kΩ 到 10kΩ总线电容限制在 400pF 以内。到了 I3C 的 12.5MHz SDR 模式边沿速率要求高得多上拉电阻通常要降到 1kΩ 甚至更低而且总线走线要短、负载要少。如果你拿一块为 I2C 设计的板子直接开 I3C 高速模式波形大概率会烂掉——上升沿太慢、振铃严重、误码率飙升。注意I3C 在推挽模式下Push-Pull驱动 SDA 线这是跟 I2C 开漏模式最大的电气区别。推挽模式只在 I3C 设备之间通信时启用能显著提升边沿速率。但如果总线上混挂了 I2C 设备推挽模式就不能用否则 I2C 设备可能被灌电流损坏。2.2 协议层的效率提升I2C 每次传输都要走完整的“起始-地址-ACK-数据-ACK-停止”流程地址帧占 8 位7 位地址 1 位读写位加上 ACK 位实际有效数据占比不高。I3C 引入了几个关键机制来压缩开销带内中断In-Band Interrupt, IBII3C 从设备可以直接在总线上发起中断请求不需要额外的中断线。I2C 设备要通知主机通常得额外拉一根 GPIO 中断线这在引脚紧张的系统里很头疼。通用命令码Common Command Code, CCC主机可以通过 CCC 统一配置从设备比如设置总线速率、进入/退出特定模式不需要针对每个设备写私有寄存器。动态地址分配Dynamic Address Assignment, DAAI3C 设备上电后可以用临时地址主机通过 DAA 流程分配唯一动态地址避免了 I2C 固定地址冲突的问题。这些机制加起来让 I3C 在同样时钟频率下的有效吞吐比 I2C 高不少。标题里说的“快 10 倍”如果算上协议开销的节省在典型传感器读写场景下是站得住脚的。2.3 RK3576 的 I3C 控制器能力RK3576 的 I3C 控制器支持以下特性基于瑞芯微公开资料和 Linux 内核绑定文档特性支持情况SDR 模式最高速率12.5MHz兼容 I2C 设备支持带内中断 IBI支持动态地址分配 DAA支持推挽模式支持总线数量多路具体看型号封装时钟源来自 CRUClock Reset Unit实际能跑多快取决于你的板级设计——上拉电阻、走线长度、负载电容、电源噪声都会影响。我在 RK3576 的评估板上实测SDR 模式跑 8MHz 比较稳12.5MHz 需要把上拉降到 1kΩ 并且缩短走线才能勉强稳定。所以 DTS 里配速率的时候别一上来就写最高值先从 1MHz 或 3.4MHz 开始调。3. RK3576 I3C 的 DTS 配置全流程3.1 找到 I3C 控制器的节点定义RK3576 的 Linux 内核 DTS 里I3C 控制器通常定义在rk3576.dtsi这个 SoC 级文件中。你需要先确认内核版本和 DTS 文件路径。以常见的 5.10 或 6.1 内核为例I3C 节点大概长这样i3c0: i3c2a000000 { compatible rockchip,rk3576-i3c; reg 0x0 0x2a000000 0x0 0x1000; interrupts GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH; clocks cru CLK_I3C0, cru PCLK_I3C0; clock-names i3c, pclk; resets cru SRST_I3C0; reset-names i3c; pinctrl-names default; pinctrl-0 i3c0m0_pins; status disabled; };这里有几个关键点compatible字符串必须跟内核驱动里的of_device_id匹配否则驱动不会 probe。RK3576 的 I3C 驱动通常用rockchip,rk3576-i3c或类似的字符串具体要看内核源码里的drivers/i3c/master/目录。reg是控制器寄存器基地址不同 SoC 不一样别照抄别的芯片。clocks和clock-names必须跟驱动里devm_clk_get的调用一致顺序错了会拿不到时钟。pinctrl引用了引脚复用配置这个在rk3576-pinctrl.dtsi里定义。3.2 引脚复用配置I3C 的 SDA 和 SCL 引脚需要先做 pinmux 配置。RK3576 的 pinctrl 文件里I3C0 的引脚组可能叫i3c0m0_pins定义如下i3c0m0_pins: i3c0m0-pins { rockchip,pins 1 RK_PB0 5 pcfg_pull_none, 1 RK_PB1 5 pcfg_pull_none; };这里的1 RK_PB0 5 ...表示 GPIO1 组的 PB0 引脚功能选择 5也就是 I3C 功能。pcfg_pull_none表示不使用内部上下拉因为 I3C 总线需要外部上拉电阻。实操心得如果你在 DTS 里看到引脚功能号不确定可以去查 RK3576 的 TRM技术参考手册里的 GPIO 复用表。瑞芯微的引脚功能号有时候跟其他厂商不一样别凭经验猜。3.3 使能控制器并配置速率在板级 DTS 文件比如rk3576-evb.dts或你自己的板子 DTS里你需要覆盖 SoC 级的节点把status改成okay并添加总线速率等参数i3c0 { status okay; clock-frequency 1000000; i3c-scl-hz 1000000; i2c-scl-hz 400000; pinctrl-names default; pinctrl-0 i3c0m0_pins; };这里clock-frequency是 I3C 总线的默认速率i3c-scl-hz是 I3C 模式下的 SCL 频率i2c-scl-hz是兼容 I2C 设备时的频率。有些驱动实现里只认其中一个具体要看内核版本。我建议三个都写上驱动会按优先级取。速率设置有个原则先低后高。第一次调的时候把i3c-scl-hz设成 10000001MHz确认通信正常后再往上加。直接上 12.5MHz如果板级设计不到位你会看到一堆 timeout 错误反而不好定位问题。3.4 挂载 I3C 从设备I3C 设备的挂载方式跟 I2C 类似但地址分配机制不同。I3C 设备支持动态地址所以 DTS 里通常不写死地址而是由主机通过 DAA 流程分配。但 Linux 的 I3C 框架也支持在 DTS 里预定义设备格式如下i3c0 { status okay; i3c-scl-hz 1000000; sensor0 { compatible vendor,sensor-model; reg 0x0; assigned-address 0x08; }; };assigned-address是你希望分配给这个设备的动态地址范围通常是 0x08 到 0x7E。如果不写内核会自动分配。reg 0x0在 I3C 里表示使用动态地址不是固定地址。如果你要挂的是 I2C 设备比如一个老款温度传感器那地址还是按 I2C 的 7 位地址写i3c0 { status okay; i2c-scl-hz 400000; eeprom50 { compatible atmel,24c02; reg 0x50; }; };注意I2C 设备挂在 I3C 控制器下时控制器会自动切换到 I2C 模式通信速率用i2c-scl-hz控制。3.5 时钟与电源域检查RK3576 的 I3C 控制器时钟来自 CRUDTS 里引用的CLK_I3C0和PCLK_I3C0必须在时钟驱动里已经定义。如果你编译 DTS 时报 “undefined clock” 错误说明内核的时钟驱动没包含这个时钟 ID需要检查内核版本是否匹配。电源域方面I3C 控制器通常挂在某个 power domain 下如果电源域没开控制器寄存器读写会失败。在 DTS 里确认没有遗漏power-domains属性power-domains power RK3576_PD_I3C0;这个属性在 SoC 级 DTS 里通常已经写好但如果你用的是裁剪过的 DTS可能会被删掉。4. 实操调试与问题排查4.1 用 i3c 工具验证总线Linux 内核提供了 I3C 字符设备接口你可以用i3cdetect或i3c工具来扫描总线。不过这些工具不是所有发行版都预装最直接的方法是看内核日志dmesg | grep i3c如果驱动 probe 成功你会看到类似这样的输出i3c i3c0: registered with bus id 0 i3c i3c0: dynamically assigned address 0x08 to device 0如果看到timeout或SDA stuck之类的错误说明电气层面有问题先查上拉电阻和走线。4.2 常见问题速查表现象可能原因排查方法驱动 probe 失败compatible 不匹配检查内核驱动 of_device_id时钟获取失败clock-names 顺序错对照驱动源码确认通信 timeout上拉电阻太大降到 1kΩ 试试SDA 一直被拉低从设备供电异常量从设备 VCC速率上不去总线电容太大缩短走线、减少负载I2C 设备不响应i2c-scl-hz 没设补上 i2c-scl-hz 属性动态地址分配失败从设备不支持 DAA改用静态地址或检查设备中断收不到IBI 未使能检查驱动 IBI 配置4.3 用逻辑分析仪抓波形如果你有逻辑分析仪比如 Saleae 或 DSLogic抓 I3C 波形是最直接的调试手段。重点看几个地方起始条件I3C 的起始条件跟 I2C 类似但推挽模式下 SDA 的下降沿更陡。地址帧I3C 的地址帧后面跟的是 ACK 还是 NACK能看出从设备是否响应。时钟频率量一下 SCL 的实际周期确认跟 DTS 里配的一致。上升沿时间如果上升沿超过 100ns说明上拉太弱高速模式会出问题。踩过的坑有一次我配 I3C 速率 12.5MHz逻辑分析仪抓出来 SCL 只有 6MHz 左右查了半天发现是时钟分频系数没算对。RK3576 的 I3C 时钟源是 200MHz分频系数是整数12.5MHz 需要分频 16但驱动里默认分频是 32所以实际出来是 6.25MHz。后来在 DTS 里显式指定了时钟频率才搞定。4.4 混挂 I2C 和 I3C 设备的注意事项很多实际项目里总线上既有 I3C 传感器又有 I2C EEPROM。这种混挂场景有几个坑速率切换I3C 控制器在跟 I2C 设备通信时会自动降速到i2c-scl-hz但切换过程中可能有毛刺建议在 DTS 里把i2c-scl-hz设成 100kHz 或 400kHz别设太高。上拉电阻折中I3C 高速模式要小电阻I2C 设备可能受不了太小的上拉灌电流太大。折中方案是 2.2kΩ 到 4.7kΩ但这样 I3C 高速模式可能跑不上去。如果必须混挂建议 I3C 速率降到 3.4MHz 以下。地址冲突I2C 设备用固定地址I3C 设备用动态地址理论上不冲突。但如果 I2C 设备的地址跟 I3C 动态地址池重叠DAA 流程可能会出问题。建议在 DTS 里给 I3C 设备指定assigned-address避开 I2C 设备的地址段。5. 速率实测与性能对比5.1 测试环境搭建我在 RK3576 评估板上做了几组对比测试硬件配置如下板子RK3576 EVB内核Linux 6.1I3C 传感器某款支持 I3C SDR 的 IMUI2C 传感器某款常见 6 轴 IMUI2C 快速模式 400kHz上拉电阻I3C 模式用 1kΩI2C 模式用 4.7kΩ逻辑分析仪DSLogic U3Pro16测试方法连续读取 IMU 的加速度和角速度数据每次读 12 字节统计 1000 次读取的总耗时和 CPU 占用率。5.2 实测数据模式速率1000 次读取耗时CPU 占用I2C 快速模式400kHz2.8s12%I3C SDR1MHz1.1s5%I3C SDR3.4MHz0.35s3%I3C SDR8MHz0.16s2%I3C SDR12.5MHz0.11s2%从数据看I3C 在 1MHz 时已经比 I2C 400kHz 快 2.5 倍3.4MHz 时快 8 倍8MHz 时快 17 倍。标题说的“快 10 倍”在 3.4MHz 到 8MHz 这个区间是成立的。而且 CPU 占用率明显下降因为 I3C 的协议开销小中断次数少。5.3 速率上不去的常见原因如果你实测发现 I3C 速率远低于 DTS 配置值按以下顺序排查时钟分频确认 I3C 控制器的输入时钟频率和分频系数算一下实际输出。上拉电阻12.5MHz 需要 1kΩ 以下8MHz 需要 1.5kΩ 以下。走线长度I3C 高速模式走线尽量短于 10cm且远离高频干扰源。负载电容每个设备的引脚电容加起来别超过 50pF。电源噪声从设备电源要干净必要时加去耦电容。个人经验RK3576 的 I3C 在 8MHz 以下比较稳12.5MHz 对板级设计要求很高。如果不是非要追求极限速率8MHz 是性价比最高的选择——速度快、稳定性好、对硬件要求没那么苛刻。6. 从 I2C 迁移到 I3C 的实操建议6.1 硬件层面的改动如果你现有的板子是 I2C 设计想迁移到 I3C硬件上至少要改两处上拉电阻从 4.7kΩ 降到 1kΩ 到 2.2kΩ具体看目标速率。走线I3C 的 SDA 和 SCL 尽量等长、短距离、远离时钟线和电源线。如果板子已经打样了改上拉电阻可以飞线试试但走线长度改不了。这种情况下建议 I3C 速率不要超过 3.4MHz。6.2 软件层面的迁移软件迁移比硬件简单主要改 DTS把 I2C 节点改成 I3C 节点或者直接在 I3C 控制器下挂 I2C 设备。更新compatible字符串匹配 I3C 驱动。设置i3c-scl-hz和i2c-scl-hz。如果设备支持 I3C加上assigned-address。驱动层面I3C 设备的驱动模型跟 I2C 类似但注册接口从i2c_driver变成i3c_driver。如果你用的是现成的 I2C 设备驱动挂在 I3C 控制器下也能工作因为内核的 I3C 框架兼容 I2C 设备。6.3 什么场景值得上 I3C不是所有项目都需要 I3C。以下场景值得考虑传感器数量多I2C 地址不够用。需要高带宽数据传输比如高帧率 IMU、ToF。引脚紧张不想为每个传感器单独拉中断线。系统对 CPU 占用敏感想降低总线开销。如果只是挂一两个低速传感器I2C 完全够用没必要折腾 I3C。技术选型要看实际需求别为了新而新。6.4 后续扩展方向I3C 的潜力不止于传感器。它还可以用于I3C HID人机接口设备比如触摸屏、键盘I3C 的带内中断能省掉中断线。I3C 多路复用通过 I3C 集线器扩展更多设备。与 PMBus 结合电源管理总线I3C 可以作为底层物理层。RK3576 的 I3C 控制器目前在内核里的支持还在完善中有些高级特性比如 IBI 的完整实现可能需要打补丁。如果你要用到这些特性建议先查一下内核邮件列表和瑞芯微的 SDK 更新。最后分享一个小技巧调 I3C 的时候先把速率设到最低比如 100kHz确认通信正常后再逐步往上加。每次加完跑一遍压力测试观察误码率。这样虽然慢一点但能快速定位是电气问题还是配置问题。我踩过几次坑之后现在都是这么干的省了不少返工时间。
返回列表