3、I2C调试实战:逻辑分析仪抓取波形、Linux下i2c-tools使用、常见I2C故障排查

I2C这个协议,说简单也简单,就两根线:SCL和SDA。但说复杂也复杂,我在联发科平台上前前后后调了不下几十个I2C设备,从简单的温度传感器到复杂的摄像头模组,踩过的坑真不少。这一章,咱们就聊聊怎么用逻辑分析仪抓波形、怎么用Linux下的i2c-tools工具,以及那些最常见的I2C故障怎么排查。

3.1 逻辑分析仪抓取I2C波形

说实话,调试I2C最直观的方法就是看波形。你代码写得再漂亮,不如亲眼看看SCL和SDA线上到底发生了什么。我个人习惯,只要I2C通信出问题,第一件事就是上逻辑分析仪。

3.1.1 硬件连接

连接其实很简单。把逻辑分析仪的通道0接到SCL,通道1接到SDA,然后共地。嗯,这里要注意:一定要共地。我曾经有一次忘了接地,抓出来的波形全是毛刺,折腾了半天才发现是地线没接。

警告:逻辑分析仪的采样率建议设置为I2C时钟频率的4倍以上。比如400kHz的快速模式,采样率至少设到2MHz。太低了你抓不到细节。

3.1.2 波形解读

抓到了波形,怎么看?我一般按这个顺序来:

  • 起始条件:SCL高电平时,SDA从高变低。这是通信开始的标志。
  • 设备地址:7位或10位地址,后面跟着读写位(0表示写,1表示读)。
  • 应答位:第9个时钟周期,SDA被从设备拉低,表示ACK。如果SDA保持高电平,那就是NACK。
  • 数据字节:每个字节8位,MSB先发。后面跟着应答位。
  • 停止条件:SCL高电平时,SDA从低变高。

举个例子,你抓到一个波形,地址是0x3C,写操作,然后发了0x00。如果从设备在第9个时钟没有拉低SDA,那就是NACK。说白了,从设备没理你。

小技巧:大部分逻辑分析仪软件都支持I2C协议解码。你只要设置好SCL和SDA的通道,软件会自动帮你解析出地址、数据、ACK/NACK。省心不少。

3.2 Linux下i2c-tools使用

在联发科平台上调试I2C,i2c-tools是必备工具。它包含一组命令行工具,可以让你直接和I2C设备交互,不用写代码。我个人觉得,这是排查I2C问题最快的方式。

3.2.1 安装与基本命令

在Linux终端里,先确认i2c-tools有没有装:

sudo apt-get install i2c-tools  # Debian/Ubuntu
sudo yum install i2c-tools      # CentOS/RHEL

装好之后,最常用的几个命令:

  • i2cdetect -l:列出所有I2C总线。
  • i2cdetect -y <总线号>:扫描总线上有哪些设备地址。
  • i2cget -y <总线号> <设备地址> <寄存器地址>:读取某个寄存器的值。
  • i2cset -y <总线号> <设备地址> <寄存器地址> <值>:写入某个寄存器。
  • i2cdump -y <总线号> <设备地址>:dump整个设备的寄存器。

3.2.2 实战:检测设备是否存在

假设你的设备挂在I2C总线2上,地址是0x50。先扫描一下:

i2cdetect -y 2

输出大概是这样:

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- --

看到0x50位置显示"50",说明设备响应了。如果全是"--",那设备可能没上电、地址不对、或者硬件连接有问题。

重点:i2cdetect扫描时,会向每个地址发送一个读请求。如果设备返回ACK,就认为该地址有设备。但有些设备在扫描时可能会异常,比如复位或进入错误状态。所以,扫描前最好确认设备处于空闲状态。

3.2.3 实战:读写寄存器

读设备0x50的寄存器0x00:

i2cget -y 2 0x50 0x00

返回一个字节,比如0xAB。写寄存器:

i2cset -y 2 0x50 0x00 0xCD

把0xCD写入寄存器0x00。写完之后再读回来确认:

i2cget -y 2 0x50 0x00

如果返回0xCD,说明读写正常。如果返回的还是0xAB,那可能寄存器是只读的,或者写操作没成功。

避坑指南:我曾经遇到一个设备,写寄存器后需要等待几毫秒才能读回正确值。如果你读回来不对,试试加个延时再读。可以用shell的sleep命令,或者写个小脚本循环读。

3.3 常见I2C故障排查

调试多了,你会发现I2C的问题就那么几类。下面是我总结的常见故障和排查方法。

3.3.1 设备无应答(NACK)

现象:i2cdetect扫不到设备,或者读写时返回NACK。

可能原因:

  • 地址错误:7位地址和8位地址搞混了。I2C设备地址通常是7位,但有些文档写的是8位(左移了一位)。你想想看,0x50和0xA0其实是同一个设备。
  • 设备未上电:用万用表量一下设备电源引脚,确认电压正常。
  • 上拉电阻问题:SCL和SDA需要上拉电阻到VCC。如果没上拉,或者阻值太大,信号上升沿会变慢,导致通信失败。一般4.7kΩ比较常用。
  • 总线冲突:总线上有其他设备在拉低时钟或数据。用逻辑分析仪看看波形,SCL和SDA是不是一直低电平。
注意:有些设备在复位后需要一段时间才能响应I2C请求。如果你刚给设备上电就去扫描,可能会得到NACK。等个几百毫秒再试。

3.3.2 时钟拉伸(Clock Stretching)

现象:通信时SCL被从设备拉低,主设备一直等待。

原因:从设备处理速度慢,需要拉低SCL来让主设备等待。这在一些慢速传感器上很常见。

排查方法:用逻辑分析仪抓波形,看SCL是不是在某个位置被拉低很长时间。如果是,说明从设备在时钟拉伸。主设备需要支持时钟拉伸,否则会超时报错。

经验之谈:联发科平台的I2C控制器默认支持时钟拉伸,但有些第三方驱动可能没配置好。如果你遇到通信超时,检查一下驱动里时钟拉伸相关的配置。

3.3.3 数据错位或乱码

现象:读回来的数据不对,或者写进去的值和读出来的不一样。

可能原因:

  • 位序错误:I2C是MSB先发,但有些设备文档可能写反了。用逻辑分析仪看波形,对比数据手册确认。
  • 寄存器地址自增:连续读写时,寄存器地址会自动递增。如果你没处理好,可能会读到错误的位置。
  • 干扰:SCL或SDA线上有噪声。检查布线,SCL和SDA不要长距离平行走线,最好加地线隔离。

3.3.4 总线死锁(Bus Lock)

现象:SCL或SDA一直为低电平,无法通信。

原因:某个设备异常,一直拉低总线。或者主设备在传输过程中异常退出,没有发送停止条件。

解决方法:

  • 硬件复位:给设备断电再上电。
  • 软件复位:有些I2C控制器支持软件复位,发送9个时钟脉冲来释放总线。
  • 手动释放:如果SDA被拉低,可以尝试连续发送时钟脉冲,直到SDA变高。
避坑指南:我曾经在一个项目里,设备在掉电时没有释放I2C总线,导致系统重启后总线一直死锁。后来我在驱动初始化时加了一段代码,先发送9个时钟脉冲释放总线,问题就解决了。

3.4 总结

I2C调试其实不难,关键是方法要对。我个人习惯的排查流程是:

  1. 先用逻辑分析仪看波形,确认起始条件、地址、数据、停止条件是否正常。
  2. 再用i2c-tools扫描设备,确认设备是否响应。
  3. 如果设备响应,读写寄存器验证功能。
  4. 如果不行,检查硬件连接、上拉电阻、电源。
  5. 最后,看看驱动配置,特别是时钟频率、时钟拉伸、超时时间这些参数。

说白了,I2C就是个串行通信协议,只要时序对了,数据就能传对。遇到问题别慌,一步一步排查,总能找到原因。

下一章,咱们聊聊SPI调试。SPI比I2C快,但线也多,调试起来又是另一番风景了。