I2C 总线调试最让人头疼的地方在于:它不像串口那样只要波特率对上就能出数据,也不像 SPI 那样四根线各司其职、逻辑清晰。I2C 只有两根线,所有设备挂在同一对 SDA/SCL 上,任何一处时序偏差、上拉电阻选错、地址冲突,都会让整条总线陷入沉默。更麻烦的是,很多时候设备"看起来在工作"——电源正常、芯片发烫、代码也没报错,但读回来的数据就是全 0 或者全 FF。
我这些年调过的 I2C 问题,从简单的上拉电阻漏焊,到从机时钟拉伸导致的偶发丢包,再到多主竞争下的仲裁丢失,几乎每一类都踩过。这篇就把我实际排查 I2C 信号的完整流程梳理一遍:从最基础的万用表静态检查,到示波器抓波形看时序,再到 ACK 位的判定逻辑,每一步该看什么、怎么判断、常见误判在哪,都讲清楚。不管你是刚上手 STM32 的 HAL 库,还是在调 GT911 触摸屏、SSD1306 OLED 这类典型 I2C 外设,这套流程都能直接套用。
1. 先搞清楚 I2C 到底在测什么
很多人一上来就抓波形,结果示波器上看到一堆毛刺,反而更懵。在动手测之前,得先明确 I2C 通信的本质:它是主从架构的半双工同步串行总线,主机产生时钟 SCL,数据在 SDA 上双向传输,每个字节后面必须跟一个 ACK 位。所以"测 I2C"这件事,本质上是在验证三件事:电气层是否正常、时序层是否合规、协议层是否收到应答。
1.1 电气层、时序层、协议层的三层排查模型
我把 I2C 排查分成三层,从下往上逐层排除,这样不会乱:
- 电气层:SDA/SCL 的静态电平、上拉电阻阻值、电源电压、地是否共地。这一层用万用表就能查大半。
- 时序层:SCL 频率、上升沿/下降沿时间、建立时间/保持时间、时钟拉伸。这一层必须上示波器或逻辑分析仪。
- 协议层:起始条件、地址帧、读写位、ACK/NACK、数据帧、停止条件。这一层看的是波形背后的协议含义。
为什么要分层?因为如果电气层就有问题(比如上拉电阻没焊),你抓再多波形也是徒劳。我见过太多人直接跳到协议层分析,结果发现根本是 SDA 线被拉死在低电平。从下往上排查,是效率最高的路径。
1.2 为什么 I2C 比 SPI、UART 更容易"沉默"
UART 只要 TX/RX 接对、波特率一致,基本就能通。SPI 有独立的 CS 片选,一个从机不响应不影响其他。但 I2C 不一样:
- 所有设备共享 SDA/SCL,一根线被拉死,整条总线瘫痪。
- 从机通过地址区分,地址冲突或地址错误,主机收不到 ACK。
- I2C 是开漏输出,必须依赖上拉电阻,没有上拉就没有高电平。
- 从机可以时钟拉伸(拉低 SCL 拖延时间),主机如果不支持就会出错。
这四点决定了 I2C 的排查必须更系统。下面这张表是我总结的三层模型对照,先建立整体认知:
| 层级 | 检查内容 | 推荐工具 | 典型故障 |
|---|---|---|---|
| 电气层 | 电平、上拉、供电、共地 | 万用表 | 上拉漏焊、电平不匹配 |
| 时序层 | 频率、边沿、建立保持时间 | 示波器/逻辑分析仪 | 时钟拉伸、边沿过缓 |
| 协议层 | 起始、地址、ACK、数据 | 逻辑分析仪 | 地址错误、无 ACK |
2. 万用表能查到的那些"低级但致命"的问题
别小看万用表。我统计过自己遇到的 I2C 故障,至少三成用万用表就能定位,根本不需要示波器。万用表查的是静态特性,虽然看不到动态波形,但恰恰是这些静态问题最容易致命。
2.1 静态电平测量:SDA/SCL 应该是高电平
I2C 总线空闲时,SDA 和 SCL 都应该是高电平(被上拉电阻拉到 VCC)。测量方法很简单:
- 系统上电,但不要发起任何通信(让总线处于空闲态)。
- 万用表打到直流电压档,黑表笔接地,红表笔分别点 SDA 和 SCL。
- 正常读数应该接近 VCC。比如 3.3V 系统,读数应该在 3.2V 以上。
如果测出来是 0V 或者很低,说明这条线被拉死了。常见原因:
- 上拉电阻没焊或虚焊:这是最常见的。我遇到过一批板子,上拉电阻的焊盘设计太窄,回流焊后虚焊,导致整批 I2C 不通。
- 从机把线拉死:某个从机芯片损坏或进入异常状态,持续拉低 SDA。
- 短路:SDA 对地短路,或者 SDA 和 SCL 短接。
提示:测量时一定要确保总线空闲。如果主机正在通信,SDA 会动态变化,万用表读数会跳动,没有参考价值。
2.2 上拉电阻阻值怎么算、怎么量
上拉电阻是 I2C 的命脉。阻值选大了,上升沿太缓,高速通信时数据还没拉高就被采样,直接出错;选小了,灌电流太大,可能超过器件的驱动能力。
理论计算:上拉电阻的最大值由总线电容和上升时间决定。公式是:
Rp(max) = tr / (0.8473 × Cb)其中 tr 是允许的最大上升时间(标准模式 1000ns,快速模式 300ns),Cb 是总线总电容(包括走线、引脚、器件电容,一般估算 100~400pF)。
举个例子:快速模式 400kHz,tr 取 300ns,Cb 估算 200pF,那么:
Rp(max) = 300ns / (0.8473 × 200pF) ≈ 1.77kΩ所以快速模式下上拉电阻一般选1.5kΩ ~ 4.7kΩ。标准模式 100kHz 可以放宽到4.7kΩ ~ 10kΩ。
实测方法:断电,万用表打到电阻档,测 SDA 对 VCC 的电阻,读数就是上拉电阻值(前提是这条线上没有其他并联到 VCC 的路径)。如果读出来是无穷大,说明上拉电阻没焊或断了。
| 通信模式 | 速率 | 推荐上拉阻值 | 上升时间上限 |
|---|---|---|---|
| 标准模式 | 100kHz | 4.7k~10k | 1000ns |
| 快速模式 | 400kHz | 1.5k~4.7k | 300ns |
| 快速模式+ | 1MHz | 1k~2k | 120ns |
2.3 供电与共地:最容易被忽略的坑
我踩过最冤的一次坑:两块板子各自供电,I2C 死活不通。查了半天,发现两块板子没有共地。I2C 的电平是相对于各自的地来判断的,地不共,电平参考就乱了。
所以万用表还要查两件事:
- 从机供电是否正常:直接量从机 VCC 引脚对地的电压。有些从机芯片供电范围窄,比如 1.8V 器件接到 3.3V 上,直接烧了或者不工作。
- 主从是否共地:量主机地和从机地之间的电压,正常应该是 0V。如果有压差,说明没共地或者地线阻抗太大。
注意:电平不匹配也是常见问题。3.3V 主机接 5V 从机,或者反过来,都可能出问题。这时候需要电平转换电路,不能直接连。
3. 示波器抓波形:时序问题的照妖镜
万用表查完静态没问题,但通信还是不通,这时候就得上示波器了。示波器能看到动态波形,是排查时序问题的核心工具。但很多人抓了波形却不会看,或者被毛刺误导。这一节讲怎么抓、怎么看。
3.1 双通道抓 SDA 和 SCL 的正确姿势
I2C 必须同时抓 SDA 和 SCL,因为协议是靠两根线的配合来定义的。单看一根线没有意义。
接线方法:
- 示波器 CH1 接 SCL,CH2 接 SDA,两个通道的地都接系统 GND。
- 探头用10:1 无源探头,输入电容小,对总线影响小。
- 触发方式选SCL 下降沿触发(或者 SDA 下降沿,因为起始条件是 SCL 高时 SDA 下降)。
时基设置:如果通信速率是 100kHz,一个时钟周期是 10us,建议时基设成10us/div ~ 50us/div,这样一屏能看到几个字节。如果速率是 400kHz,时基设成2us/div ~ 10us/div。
触发电压设成 VCC 的一半左右,比如 3.3V 系统设 1.65V。
3.2 从波形上读出起始、地址、ACK、停止
抓到波形后,怎么读?I2C 的协议规则是:
- 起始条件(Start):SCL 为高时,SDA 从高变低。
- 停止条件(Stop):SCL 为高时,SDA 从低变高。
- 数据位:SCL 为低时 SDA 变化,SCL 为高时 SDA 稳定(数据有效)。
- ACK 位:每 8 个数据位后,第 9 个时钟周期,从机把 SDA 拉低表示应答。
看波形时,先找起始条件(SDA 在 SCL 高时下降),然后数 8 个时钟,看第 9 个时钟 SDA 是否被拉低。如果第 9 个时钟 SDA 保持高,就是 NACK。
我一般会这样逐段核对:
- 找到起始条件的位置。
- 数接下来 8 个 SCL 周期,读出地址字节(7 位地址 + 1 位读写位)。
- 看第 9 个周期 SDA 电平,判断 ACK/NACK。
- 继续看数据字节和后续 ACK。
- 最后找停止条件。
3.3 上升沿过缓、时钟拉伸、毛刺的识别
示波器上最常见的三种异常波形:
上升沿过缓:SDA/SCL 从低到高的过程太慢,波形不是陡峭的方波,而是缓慢爬升的斜坡。这说明上拉电阻太大或者总线电容太大。在高速通信时,还没爬到高电平阈值就被采样,导致误判。解决办法是减小上拉电阻。
时钟拉伸(Clock Stretching):从机在需要更多时间处理数据时,会主动拉低 SCL,让主机等待。波形上表现为 SCL 的高电平时间被拉长,出现一个异常宽的低电平。如果主机不支持时钟拉伸,就会误以为通信出错。这个现象在 EEPROM 写入、传感器转换时很常见。
毛刺(Glitch):波形上出现窄的尖峰,可能是走线反射、串扰或者探头接地不良导致的。如果毛刺出现在 SCL 高电平期间,可能被误认为是数据跳变。排查方法是改善探头接地(用弹簧地线而不是长鳄鱼夹),缩短走线。
| 异常波形 | 现象 | 根因 | 解决方向 |
|---|---|---|---|
| 上升沿过缓 | 斜坡爬升 | 上拉过大/电容过大 | 减小上拉电阻 |
| 时钟拉伸 | SCL 低电平异常宽 | 从机需要处理时间 | 主机支持拉伸 |
| 毛刺 | 窄尖峰 | 反射/串扰/接地差 | 改善接地与走线 |
4. ACK 位:I2C 通信成败的判定核心
ACK 是 I2C 协议里最关键的一个信号。主机发完地址或数据后,从机必须在一个时钟周期内把 SDA 拉低,表示"我收到了"。如果主机收不到 ACK,就会认为通信失败。所以排查 I2C,本质上很大一部分是在排查 ACK。
4.1 ACK 和 NACK 的电气表现与协议含义
- ACK(应答):第 9 个 SCL 周期,SDA 被拉低(低电平)。
- NACK(非应答):第 9 个 SCL 周期,SDA 保持高电平。
NACK 出现的几种情况:
- 主机发送的地址没有从机匹配(地址错误或从机不在线)。
- 从机忙,暂时无法响应。
- 主机读取数据时,最后一个字节故意发 NACK,表示"我读完了"。
- 从机内部错误,无法应答。
所以看到 NACK 不要慌,先判断是哪种场景。如果是地址阶段的 NACK,基本就是地址错了或者从机没工作。如果是数据阶段的 NACK,可能是从机忙或者写保护。
4.2 用示波器定位"第 9 个时钟"的技巧
在示波器上定位 ACK 位,关键是数时钟。我的做法是:
- 先找到起始条件。
- 用示波器的光标(Cursor)功能,标记起始条件的位置。
- 数 8 个 SCL 下降沿(每个下降沿对应一位数据)。
- 第 9 个 SCL 周期就是 ACK 位,看这个周期内 SDA 的电平。
如果示波器有协议解码功能(很多中高端示波器都支持 I2C 解码),直接开启解码,屏幕上会直接标出地址、数据、ACK/NACK,省去手动数的麻烦。力科、鼎阳、普源这些品牌的示波器基本都有 I2C 解码选件,配置好 SDA/SCL 通道和阈值电压就能用。
提示:协议解码的阈值电压要设对。3.3V 系统设 1.65V,5V 系统设 2.5V。阈值设错,解码结果全乱。
4.3 地址不匹配导致 NACK 的排查链路
地址 NACK 是最常见的故障。排查链路是这样的:
- 确认从机地址:查数据手册,确认 7 位地址是多少。注意有些手册给的是 8 位地址(含读写位),要右移一位才是 7 位地址。
- 确认读写位:地址字节的最低位是读写位,0 表示写,1 表示读。写地址和读地址差 1。
- 示波器解码确认主机实际发出的地址:看解码结果里主机发的地址和手册是否一致。
- 确认从机是否上电、复位是否正常:有些从机需要复位引脚拉高才能工作。
- 确认地址引脚配置:很多 I2C 从机有地址选择引脚(A0/A1/A2),接高或接低决定地址。如果这些引脚悬空或接错,地址就变了。
我遇到过一次 GT911 触摸屏 I2C 通信失败,查了半天发现是地址引脚在上电时的电平不对,导致芯片锁定了错误的地址。后来按照手册要求,在复位时序里正确控制了地址引脚,问题解决。
5. 逻辑分析仪与协议解码:把波形翻译成人话
示波器看的是模拟波形,逻辑分析仪看的是数字逻辑。对于纯协议排查,逻辑分析仪更直观,因为它直接输出解码后的地址、数据、ACK。而且逻辑分析仪通道多,可以同时抓多路信号。
5.1 逻辑分析仪和示波器的分工
两者不是替代关系,而是互补:
- 示波器:看电气特性(电平、边沿、毛刺、时钟拉伸),适合排查信号质量问题。
- 逻辑分析仪:看协议内容(地址、数据、ACK),适合排查协议层问题。
我的习惯是:先用示波器确认电气和时序没问题,再用逻辑分析仪看协议内容。如果电气层就有问题,逻辑分析仪的解码结果也不可信。
5.2 采样率、阈值电压、通道配置的关键参数
逻辑分析仪配置有几个关键参数:
- 采样率:至少要是被测信号频率的10 倍以上。I2C 400kHz,采样率至少 4MHz,建议 10MHz 以上。采样率不够会漏掉窄脉冲。
- 阈值电压:设成 VCC 的一半。3.3V 系统设 1.65V。
- 通道分配:SCL 和 SDA 各占一个通道,地线要接。
- 解码协议:选择 I2C,配置好 SDA/SCL 对应的通道。
配置好后,抓一段通信,软件会自动解析出类似这样的结果:
Start | Address: 0x3C W | ACK | Data: 0x00 | ACK | Data: 0xAE | ACK | Stop一眼就能看出地址对不对、有没有 ACK、数据是什么。
5.3 解码结果与代码逻辑对不上的排查思路
有时候解码结果和代码里写的对不上,比如代码里写的是往寄存器 0x10 写数据,解码出来却是 0x00。这种情况排查思路:
- 确认代码里的地址和寄存器地址是否正确:特别是 7 位/8 位地址的换算。
- 确认字节序:有些器件是高位在前,有些是低位在前。
- 确认是否有中间层:比如用了 HAL 库或者驱动框架,可能中间做了地址转换。
- 确认时序:有些器件要求先写寄存器地址,再写数据,中间不能有停止条件(即所谓的"复合格式")。
我调 SSD1306 OLED 时遇到过类似问题,代码里发的命令和数据顺序不对,导致屏幕不亮。用逻辑分析仪一看,发现控制字节(0x00 表示命令,0x40 表示数据)没发对,修正后正常。
6. 那些年我踩过的 I2C 坑与实战心得
前面讲的都是方法论,这一节讲几个具体的坑,都是我在实际项目里踩过的,希望能帮你少走弯路。
6.1 上拉电阻漏焊导致的整批故障
有一次做了一批板子,I2C 全部不通。用万用表一量,SDA 和 SCL 空闲时都是 0V。查原理图,上拉电阻是有的。最后发现是 PCB 封装画错了,上拉电阻的焊盘间距和实际元件不匹配,回流焊后全部虚焊。这个坑的教训是:PCB 封装一定要和实际元件核对,尤其是小批量试产时,先手工焊一块验证。
6.2 时钟拉伸引发的偶发丢包
有个项目用 I2C 读传感器,大部分时候正常,偶尔丢包。用示波器抓波形,发现偶尔 SCL 的低电平时间异常长,是从机在做时钟拉伸。而主机用的是硬件 I2C,配置里没使能时钟拉伸支持,导致偶尔出错。解决办法是在主机 I2C 配置里使能时钟拉伸(很多 MCU 的 I2C 外设都支持),或者降低通信速率给从机更多时间。
6.3 多设备地址冲突的定位方法
I2C 总线上挂了多个设备,如果两个设备地址相同,就会冲突。定位方法是:逐个断开设备,看通信是否恢复。或者用逻辑分析仪看地址阶段的 ACK,如果某个地址有多个设备响应,SDA 的电平可能异常(多个设备同时拉低或一个拉低一个释放,导致电平不确定)。
6.4 软件 I2C 和硬件 I2C 的调试差异
软件 I2C 用 GPIO 模拟时序,灵活性高但容易出时序问题(比如延时不够)。硬件 I2C 由外设产生时序,稳定但配置复杂。调试时:
- 软件 I2C:重点查 GPIO 配置(开漏输出)、延时函数、时序是否符合协议。
- 硬件 I2C:重点查外设配置(速率、时钟拉伸、地址模式)、中断/DMA 配置。
我调 ESP32 的 I2C 时,遇到过休眠后 I2C 复位的问题,需要在唤醒后重新初始化 I2C 外设。这类问题在低功耗场景很常见,要特别注意。
7. 一套可复用的 I2C 排查清单
最后把我常用的排查清单整理出来,遇到 I2C 问题可以按这个顺序过一遍:
- 万用表查静态:SDA/SCL 空闲电平是否为高,上拉电阻是否存在,供电是否正常,是否共地。
- 示波器查时序:SCL 频率是否正确,上升沿是否过缓,是否有时钟拉伸,是否有毛刺。
- 逻辑分析仪查协议:起始条件、地址、读写位、ACK、数据、停止条件是否完整正确。
- 核对地址:7 位/8 位换算,地址引脚配置,读写位。
- 核对代码:寄存器地址、字节序、命令/数据控制字节。
- 隔离排查:逐个断开设备,排除地址冲突和单设备故障。
这套流程覆盖了从电气到协议的全链路,按顺序走一遍,绝大多数 I2C 问题都能定位。我个人在实际操作中的体会是:不要跳过任何一层,哪怕你觉得问题肯定在协议层,也先用万用表花两分钟确认电气层没问题,这两分钟往往能省下两小时。