☰
I2C通信排查实战:万用表、示波器与i2cdetect分层定位指南
2026/9/28 19:49:27 网站建设 项目流程

1. 从一根不亮的 OLED 说起:I2C 排查到底难在哪

I2C 总线只有两根线,SDA 和 SCL,看起来比 SPI、UART 都简单,但真正在项目里调起来,它反而是最容易让人抓狂的一种。我见过太多人,板子焊好、代码烧进去,屏幕就是不亮,然后开始怀疑人生:是地址错了?是上拉电阻没焊?是时序不对?还是从机根本没响应?更麻烦的是,I2C 是双向开漏总线,主机发完一个字节之后要释放 SDA 去读从机的 ACK,这个"释放再采样"的动作,用普通万用表几乎看不出来,用示波器又容易因为触发设置不对而抓不到关键帧。

所以这篇东西,我想把 I2C 从"怀疑它坏了"到"确认它到底哪一步坏了"的完整排查链路讲清楚。核心工具就三样:万用表、示波器、以及 Linux 下的i2cdetect这类软件工具。目标读者是嵌入式软件工程师、硬件调试人员、以及刚接触 I2C 协议、手里拿着逻辑分析仪却不知道怎么下手的同学。不管你是调 SSD1306 OLED、GT911 触摸屏、BH1750 光照传感器,还是读写 EEPROM,这套流程都能套用。

我先把结论摆在这:I2C 排查的本质,是分层定位。电气层(电压、上拉、短路)用万用表;协议层(起始、地址、ACK、数据)用示波器或逻辑分析仪;软件层(地址扫描、驱动加载)用 i2cdetect 和内核日志。三层从下往上排,哪一层出问题就在哪一层解决,不要一上来就改代码。下面我按这个思路,一层一层拆。

2. 先搞懂 I2C 的电气本质,再谈测量

2.1 为什么 I2C 必须用开漏加外部上拉

很多人测 I2C 时第一个困惑是:为什么空闲时 SDA 和 SCL 都是高电平?这跟 I2C 的电气结构直接相关。I2C 的每个引脚都是开漏(Open-Drain)输出,器件只能把线拉低,不能主动拉高。线要变高,靠的是外部上拉电阻把线"拽"回 VCC。这就好比一根绳子,所有人都只能往下拽,没人能往上推,那要让它回到上面,就得在顶端挂一根弹簧——这个弹簧就是上拉电阻。

这个结构带来两个直接后果。第一,总线上任何一个器件把线拉低,整条线就是低,这就是 I2C 支持多主多从、还能做时钟同步和仲裁的物理基础。第二,上拉电阻的阻值直接决定上升沿的陡峭程度。阻值太大,上升沿变缓,高速通信时波形还没爬到高电平阈值就被下一个时钟拉低了,通信直接失败;阻值太小,灌电流过大,器件可能扛不住。常见取值是 4.7kΩ,快速模式(400kHz)下有时用 2.2kΩ 甚至 1.5kΩ,具体要看总线电容。

提示:如果你测到 SDA 或 SCL 空闲时不是稳定的高电平,而是中间某个电压(比如 1.8V 而不是 3.3V),八成是上拉电阻没焊、焊错阻值,或者总线上有器件把线半拉着。这种情况万用表一测就露馅。

2.2 万用表能测什么,不能测什么

万用表在 I2C 排查里的定位很明确:它测静态,不测动态。具体来说,它能干这几件事:

  • 测 SDA、SCL 对地的直流电压,判断空闲电平是否正常(应该是接近 VCC);
  • 测上拉电阻的实际阻值,确认有没有虚焊、错件;
  • 测 SDA 和 SCL 之间、以及各自对 VCC/GND 之间有没有短路;
  • 断电测通断,确认走线有没有断。

但它测不了 ACK。原因很简单:ACK 是主机发完 8 个数据位后,从机在第 9 个时钟周期把 SDA 拉低的一个短暂动作,持续时间可能只有几个微秒甚至更短。万用表的采样率根本跟不上,你看到的永远是平均值或者跳动。所以凡是涉及"从机到底有没有应答"的问题,万用表直接出局,必须上示波器或逻辑分析仪。

我自己的习惯是:拿到一块新板子,先不上电,用万用表蜂鸣档把 SDA、SCL 对 VCC、对 GND 都量一遍,确认没有短路;再量上拉电阻阻值;然后上电,量空闲电压。这三步做完,电气层的低级问题基本就排除了,能省掉后面一大堆瞎折腾。

2.3 上电前的静态检查清单

我把上电前的检查整理成一张表,照着做就行:

检查项工具正常表现异常含义
SDA/SCL 对 GND 通断万用表蜂鸣档不通(或高阻)短路,查焊接
SDA/SCL 对 VCC 通断万用表蜂鸣档不通短路,查焊接
上拉电阻阻值万用表电阻档标称值附近虚焊/错件
空闲电平万用表电压档接近 VCC上拉缺失或器件拉死
器件供电万用表电压档标称电压供电异常

这张表看着简单,但我踩过的坑里,至少三成的问题在第一步就能发现。有一次一块板子 SCL 死活不动,最后发现是上拉电阻焊成了 0Ω,等于把 SCL 直接短到 VCC,从机根本拉不动。万用表一量就出来了,可惜当时我直接上示波器折腾了半小时。

3. 示波器抓 I2C:触发设置才是成败关键

3.1 为什么很多人抓不到完整的 I2C 帧

用示波器测 I2C,最常见的失败不是探头接错,而是触发没设对。I2C 是突发通信,主机想发就发,如果你用自动触发或者边沿触发随便设一个,屏幕上要么是一堆乱七八糟的毛刺,要么波形一直在跑根本稳不住。正确的做法是用协议触发或者脉宽/欠幅触发去锁定起始条件。

起始条件(Start)的定义是:SCL 保持高电平时,SDA 从高变低。这是 I2C 一帧数据的开头,也是最好的触发点。如果你的示波器支持 I2C 协议解码(现在很多中端示波器都带,比如鼎阳、普源的部分型号),直接开协议触发,设成"Start"或者"Address"触发,一抓一个准。如果示波器不支持协议触发,就用 SDA 的下降沿触发,同时把 SCL 也接上,靠双通道看时序关系。

注意:测 I2C 一定要用两个通道,CH1 接 SCL,CH2 接 SDA,别只接一根。只接 SDA 你根本分不清哪段是数据、哪段是 ACK,因为 ACK 也是 SDA 上的电平变化,没有 SCL 做参照就是天书。

3.2 探头接法和地线处理

探头接法这块,很多人忽略一个细节:地线要短。示波器探头那根鳄鱼夹地线如果拉得很长,会形成一个天线环路,测高速信号时引入大量振铃和噪声,本来干净的 I2C 波形看起来像被狗啃过。正确做法是用探头自带的弹簧地针,直接怼在器件 GND 引脚附近,地线环路越小越好。

另外,探头要设在10X 档,不要用 1X。1X 档输入电容大,会加重总线负载,本来上升沿就缓,再被探头电容一拖,波形更难看。10X 档输入电容小,对总线影响小,代价是信号幅度衰减 10 倍,示波器里记得把探头倍率设成 10X 补偿回来。

3.3 从波形上读出起始、地址、ACK

抓到波形之后,怎么读?我按一帧典型写操作的顺序说:

  1. 起始条件:SCL 高,SDA 下降沿。这是帧头。
  2. 7 位地址 + 读写位:紧接着起始条件,主机在 SCL 的每个时钟周期送一位,共 8 位。前 7 位是从机地址,第 8 位是 R/W 位(0 写,1 读)。
  3. ACK/NACK:第 9 个时钟周期,主机释放 SDA,从机如果在线且地址匹配,就把 SDA 拉低,这就是 ACK。如果从机没反应,SDA 保持高,就是 NACK。
  4. 数据字节 + ACK:之后每 8 位数据后面跟一个 ACK,直到停止条件。

判断 ACK 的关键,就是看第 9 个 SCL 高电平期间,SDA 是不是被拉低了。如果 SDA 在这段时间一直是高,说明从机没应答。这一步用示波器看非常直观,但前提是你得把时基调对,让一个字节的宽度占屏幕合适比例。一般 100kHz 的 I2C,一个时钟周期 10μs,一个字节加 ACK 是 9 个周期 90μs,时基设成 20μs/div 左右比较舒服。

3.4 用双通道测量判断"谁在拉低"

有时候你会看到 SDA 一直是低,分不清是主机在发数据还是从机把线拉死了。这时候双通道配合就有用了:看 SCL 有没有在动。如果 SCL 完全不动、SDA 恒低,很可能是某个从机把 SDA 拉死了(比如器件上电异常、地址冲突、或者器件损坏)。如果 SCL 在动、SDA 跟着 SCL 有规律变化,那就是正常通信,只是你可能没触发对。

还有一种情况:SDA 和 SCL 都被拉低,总线彻底死锁。这通常发生在主机复位时从机还在传输中途,从机占着 SDA 不放。解决办法是主机发 9 个时钟脉冲,把从机状态机走完,让它释放总线。这个技巧在调 GT911 这类触摸芯片时特别有用,它们偶尔会卡在传输中途。

4. 软件层排查:i2cdetect 和内核日志怎么用

4.1 i2cdetect 扫不到设备,先别急着骂驱动

在 Linux 平台上,i2cdetect是排查 I2C 的第一把刀。用法很简单:

i2cdetect -l # 列出所有 I2C 总线 i2cdetect -y 1 # 扫描 1 号总线上的所有地址

正常的话,你会看到一张表,从机地址位置显示对应的十六进制地址。如果全是--,说明总线上一个设备都没应答。这时候很多人第一反应是驱动没加载,其实更可能是电气问题或者地址问题。排查顺序应该是:

  1. 确认总线号对不对(i2cdetect -l看名字);
  2. 确认设备供电正常(万用表量);
  3. 确认上拉电阻在(万用表量);
  4. 确认设备地址(查数据手册,注意 7 位地址和 8 位地址的区别);
  5. 最后才怀疑驱动。

提示:i2cdetect默认只扫 0x03 到 0x77 这段地址。有些设备的地址在这个范围之外,或者被内核驱动占用了(显示成UU),这时候要加参数或者先卸载驱动再扫。

4.2 地址的 7 位与 8 位之争

这是 I2C 新手最容易栽的坑。数据手册上写的地址,有的是 7 位,有的是 8 位(把读写位也算进去了)。比如一个器件手册写地址 0x50,这是 7 位地址;如果写 0xA0,那其实是 8 位写法,右移一位才是 7 位地址 0x50。i2cdetect显示的是 7 位地址,所以如果你拿 8 位地址去对,永远对不上。

我一般这么记:7 位地址左移一位,最低位补 0 是写,补 1 是读。比如 SSD1306 的 7 位地址是 0x3C,写操作就是 0x78,读操作就是 0x79。示波器上抓到的地址字节,如果是 0x78,那对应的 7 位地址就是 0x3C。这个换算在对照波形和代码时特别重要。

4.3 内核日志里的 I2C 线索

Linux 下dmesg是排查 I2C 的另一个宝库。设备树里配了 I2C 设备但驱动没起来,dmesg里通常会有线索:

dmesg | grep -i i2c dmesg | grep -i "0x3c"

常见的报错有i2c i2c-1: sendbytes: NAK bailout(从机没应答)、timeout waiting for bus ready(总线被占死)、probe failed(驱动探测失败)。看到 NAK,就往电气和地址方向查;看到 timeout,就往总线死锁方向查。这些日志比盲目改代码有用得多。

5. 完整排查流程:从现象到根因

5.1 一张流程图式的排查顺序

我把整个排查过程整理成一条线,遇到问题按这个顺序走,基本不会绕远路:

  1. 现象确认:设备完全不工作,还是偶尔工作?完全不动优先查电气,偶尔出错优先查时序和干扰。
  2. 静态检查:万用表量短路、上拉、供电、空闲电平。
  3. 软件扫描:i2cdetect 扫地址,确认从机是否应答。
  4. 波形抓取:示波器抓起始、地址、ACK,确认协议层是否正常。
  5. 根因定位:根据前面三步的结果,锁定是电气、协议还是软件问题。

这个顺序的核心逻辑是从便宜到贵、从简单到复杂。万用表两分钟能做的事,不要一上来就架示波器;i2cdetect 一条命令能确认的事,不要先去改驱动。

5.2 典型故障对照表

现象可能原因排查工具解决方向
i2cdetect 全--供电/上拉/地址错万用表 + i2cdetect查电气和地址
有地址但读写失败时序/速率不匹配示波器降速或查时序
波形上升沿很缓上拉阻值过大示波器减小上拉
SDA 恒低从机拉死/短路万用表 + 示波器查器件/发时钟脉冲
ACK 缺失地址错/从机异常示波器核对地址和器件
偶发 NAK干扰/总线电容大示波器缩短走线/减小上拉

5.3 几个我踩过的真实坑

第一个坑:上拉电阻接到了错误的电源域。有一次板子上 I2C 器件是 1.8V 供电,但上拉电阻接到了 3.3V,结果空闲电平是 3.3V,超过了从机 IO 的耐压,从机直接不响应。万用表一量空闲电平 3.3V,而器件供电是 1.8V,问题立刻暴露。所以量空闲电平时,一定要跟器件供电电压对比,不能只看"是不是高"。

第二个坑:总线电容过大导致上升沿太缓。一块板子上挂了 6 个 I2C 器件,走线又长,用 4.7kΩ 上拉时,400kHz 下波形上升沿爬不到阈值,通信时好时坏。后来把上拉改成 2.2kΩ,问题解决。这个用示波器看上升时间一目了然,万用表完全看不出来。

第三个坑:地址冲突。两个器件地址一样,i2cdetect 能扫到地址,但读写数据总是错乱。这种情况示波器上能看到 ACK 是有的,但数据内容不对。解决办法是查每个器件的数据手册,确认地址引脚配置,必要时改硬件地址。

6. 进阶:逻辑分析仪和协议解码的配合

6.1 逻辑分析仪和示波器怎么分工

示波器看的是模拟波形,能看出上升沿、过冲、噪声这些电气细节;逻辑分析仪看的是数字电平,能直接解码出地址、数据、ACK,适合看协议层。两者不是替代关系,而是互补。我的习惯是:电气问题用示波器,协议问题用逻辑分析仪。如果只有示波器,那就开协议解码功能,现在很多示波器都带。

逻辑分析仪接 I2C 时,采样率要足够高。100kHz 的 I2C,采样率至少 1MHz 以上才能准确还原,一般设 4MHz 或 10MHz 比较稳。采样深度要够,否则一帧还没抓完就满了。触发条件设成 SDA 下降沿(起始条件),或者直接设地址触发。

6.2 协议解码结果怎么读

逻辑分析仪解码出来的结果,通常长这样:

Start Address: 0x3C (Write) ACK Data: 0x00 ACK Data: 0xAE ACK Stop

读这个结果,重点看三处:地址对不对、ACK 有没有、数据是不是你期望的。如果地址后面跟的是 NACK,说明从机没应答;如果 ACK 都有但数据不对,说明时序或者寄存器配置有问题。这个解码结果比示波器上数格子直观太多,强烈建议有条件就上一个。

6.3 自由数据模式下的观察技巧

有些 I2C 器件支持"自由数据模式"或者连续读,主机发一次地址后连续读多个字节。这种模式下,示波器上会看到一长串时钟,中间没有停止条件。观察这种波形时,重点看每个字节后的 ACK 是否连续,以及最后一个字节主机是否回 NACK(表示读够了要停止)。如果中间某个 ACK 丢了,说明从机在那一拍出了问题,可能是内部缓冲满了或者时序跟不上。

7. 一些容易被忽略的细节

7.1 时钟拉伸(Clock Stretching)

I2C 允许从机把 SCL 拉低来"拖住"主机,这叫时钟拉伸。从机处理不过来时就会这么干。示波器上表现为 SCL 高电平时间被拉长。如果你的主机不支持时钟拉伸,或者超时设置太短,就会误判为通信失败。排查时如果看到 SCL 高电平异常长,先别怀疑主机,看看是不是从机在拉伸时钟。

7.2 上电顺序和复位

有些 I2C 器件对上下电顺序敏感,主机先上电、从机后上电,可能导致从机在初始化时把总线拉死。解决办法是确保从机先上电稳定,或者主机在初始化前先发几个时钟脉冲清总线。这个细节在数据手册里经常一笔带过,但实际调试中很要命。

7.3 长走线和干扰

I2C 走线长了,容易受干扰,尤其是和开关电源、电机驱动这些噪声源靠近时。示波器上会看到波形上有毛刺,严重时毛刺被误判成时钟边沿,导致多读或少读数据。解决办法是缩短走线、远离噪声源、必要时加屏蔽或者用 I2C 缓冲器/多路复用器。测纹波和噪声时,示波器探头要用弹簧地针,别用长地线,否则测到的噪声里有一半是探头自己引入的。

8. 我个人在实际操作中的体会

调 I2C 这么多年,我最大的体会是:别急着改代码,先看波形。代码逻辑再对,电气层不对照样不通;反过来,电气层对了,代码问题反而好定位。万用表、示波器、i2cdetect 这三样工具,对应电气、协议、软件三层,按顺序用,绝大多数问题都能在半小时内定位。

还有一点,记录每次测量的结果。我习惯在调试笔记里记下每次量的电压、抓的波形、i2cdetect 的输出,这样问题复现或者换人接手时,不用从头再来。I2C 这种总线,问题往往藏在细节里,今天量到的 1.8V 空闲电平,可能就是明天定位问题的关键线索。

最后分享一个小技巧:如果手头没有逻辑分析仪,又需要快速确认 ACK,可以用示波器的单次触发,触发条件设成 SDA 下降沿,时基设成 50μs/div,抓一帧完整的写操作,然后放大看第 9 个时钟。这个土办法我用了很多年,虽然不如协议解码直观,但足够判断从机有没有应答。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询