第4章 I2C驱动调试实战:从子系统到波形
I2C这玩意儿,说简单也简单,就两根线:SCL和SDA。但说复杂吧,我这些年遇到的I2C问题,十个手指头都数不过来。从设备树配错地址,到从机不回ACK,再到时钟延展卡死——每个坑我都踩过。
这一章,咱们就围绕RK3568平台,把I2C子系统扒开来看。我会结合i2c-tools和逻辑分析仪,带你一步步定位那些让人头疼的ACK/NACK问题。
4.1 I2C子系统架构概览
Linux内核的I2C子系统分三层,我习惯这么记:
- I2C Core(核心层):提供总线注册、设备枚举、驱动匹配等通用机制。你不需要动它。
- I2C Adapter(适配器层):对应SoC的I2C控制器硬件。RK3568有5个I2C控制器,每个都是一个adapter。
- I2C Client(设备层):挂载在总线上的从设备,比如温度传感器、音频Codec、触摸屏。
说白了,适配器负责发数据,设备层负责解析数据。驱动开发主要关注设备层,但调试时三层都可能出问题。
4.2 设备树配置——别小看这几行
RK3568的设备树里,I2C节点长这样。我见过太多人把地址写错,或者忘了配时钟频率。
&i2c1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c1m0_xfer>;
clock-frequency = <100000>; /* 100KHz标准模式 */
/* 挂载一个温度传感器 */
temperature_sensor: tmp102@48 {
compatible = "ti,tmp102";
reg = <0x48>;
#address-cells = <1>;
#size-cells = <0>;
};
};
这里有几个关键点:
- clock-frequency:RK3568支持100KHz、400KHz和1MHz。但实际能不能跑上去,取决于PCB走线和从设备能力。我建议先跑100KHz,稳定了再提频。
- pinctrl:必须选对复用功能。RK3568的I2C1有m0和m1两组引脚,配错了SCL/SDA就没波形。
- reg:7位从机地址,注意对齐方式。内核会帮你左移,所以设备树里写的就是7位地址本身。
4.3 通信时序分析——用逻辑分析仪看真相
软件层面调通了,不代表硬件没问题。我习惯先挂逻辑分析仪,抓一段波形看看。
标准的I2C写时序是这样的:
- START条件:SCL高电平时,SDA从高变低。
- 发送从机地址+写位:7位地址 + 0(写),等待ACK。
- 发送寄存器地址:告诉从机你要写哪个寄存器。
- 发送数据:一个字节一个字节发,每字节后等ACK。
- STOP条件:SCL高电平时,SDA从低变高。
为什么会NACK?我总结了几种常见情况:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 地址阶段NACK | 从机地址错误、从机未上电、SCL/SDA接反 | 核对设备树reg、测量VDD、检查原理图 |
| 数据阶段NACK | 从机忙、寄存器不存在、写保护 | 查看从机datasheet、降低频率重试 |
| 时钟延展卡死 | 从机拉低SCL、适配器超时设置过短 | 抓波形看SCL低电平时间、增大超时值 |
| 波形畸变 | 上拉电阻太小/太大、容性负载过重 | 检查PCB走线、调整上拉电阻(典型4.7KΩ) |
4.4 使用i2c-tools快速验证
在调试阶段,我几乎不用写测试程序。i2c-tools这套工具足够搞定90%的问题。
常用命令一览:
| 命令 | 作用 | 示例 |
|---|---|---|
i2cdetect -l |
列出所有I2C总线 | 查看RK3568的5个adapter |
i2cdetect -y 1 |
扫描总线1上的设备 | 看0x48位置有没有UU或数字 |
i2cget -y 1 0x48 0x00 |
读取设备0x48的寄存器0x00 | 读温度传感器ID寄存器 |
i2cset -y 1 0x48 0x01 0x3C |
向设备0x48的寄存器0x01写0x3C | 配置传感器工作模式 |
i2ctransfer -y 1 w2@0x48 0x00 0x01 |
发送2字节写数据 | 更灵活的传输方式 |
举个例子,我调试一个音频Codec时,用i2cdetect扫不到设备。但逻辑分析仪显示有波形,从机也回了ACK。后来发现是Codec的地址线A0接了高电平,实际地址是0x1B而不是默认的0x1A。用i2cdetect -y 2 0x1b指定地址扫描,一下就出来了。
i2cdetect -y扫所有总线。如果扫不到设备,别急着怀疑驱动,先拿示波器或逻辑分析仪看波形。硬件问题占I2C故障的60%以上。
4.5 实战:定位ACK/NACK问题
假设你遇到了一个典型的NACK问题。从机是MPU6050六轴传感器,地址0x68。你写驱动时发现i2c_master_send返回-5(即-EREMOTEIO,表示NACK)。
我的排查步骤:
- 确认设备树:检查reg = <0x68>是否正确。MPU6050的AD0引脚接地时地址是0x68,接VCC是0x69。
- 测量供电:用万用表量VDD和VLOGIC引脚,确保电压正常。
- 抓波形:逻辑分析仪挂SCL和SDA,触发条件设为START条件。看地址阶段有没有ACK。
- 分析波形:如果地址阶段SDA在第9个时钟周期被拉低,说明有ACK。否则就是NACK。
- 检查上拉:如果波形上升沿很缓,可能是上拉电阻太大。RK3568内部有弱上拉,但建议外部加4.7KΩ。
我曾经遇到一个奇葩问题:MPU6050在低温下偶尔NACK。抓波形发现时钟延展时间超过了内核默认的1ms超时。解决办法是在设备树里加i2c-scl-rising-time-ns和i2c-scl-falling-time-ns属性,或者增大adapter的超时值。
/* 在设备树中调整时序参数 */
&i2c1 {
clock-frequency = <100000>;
i2c-scl-rising-time-ns = <175>;
i2c-scl-falling-time-ns = <15>;
/* 这些值可以从逻辑分析仪测得的波形计算 */
};
4.6 内核调试开关
如果手头没有逻辑分析仪,可以打开内核的I2C调试日志。RK3568的内核默认编译了I2C动态调试支持。
# 打开I2C核心调试
echo 'file i2c-core.c +p' > /sys/kernel/debug/dynamic_debug/control
# 打开RK3X适配器调试
echo 'file i2c-rk3x.c +p' > /sys/kernel/debug/dynamic_debug/control
# 查看日志
dmesg | tail -50
日志会打印每次传输的细节,包括地址、数据、返回值。但说实话,这只能看到软件层面的信息。硬件波形问题,还是得上逻辑分析仪。
嗯,这一章的内容就到这里。I2C调试说难不难,说简单也不简单。关键是掌握方法:设备树配对、工具用好、波形看懂。下次遇到NACK,你应该知道从哪里下手了。