2. I2C协议详解:起始条件、停止条件、数据有效性、字节格式与应答机制(ACK/NACK)

I2C协议看起来简单,就两根线——SCL(时钟)和SDA(数据)。但真要在示波器上抓波形、定位问题,你得把每个细节都吃透。这一章,咱们就把协议最核心的几个动作掰开揉碎了讲。

2.1 起始条件(S)与停止条件(P)

总线空闲时,SCL和SDA都是高电平。这是背景,记住它。

起始条件(Start Condition):
SCL保持高电平,SDA从高电平切换到低电平。

停止条件(Stop Condition):
SCL保持高电平,SDA从低电平切换到高电平。

说白了,就是在时钟高电平期间,数据线跳变一次。这个跳变方向决定了是开始还是结束。

关键点:起始和停止条件都是由主机(Master)产生的。从机(Slave)永远不能主动发起或终止传输。

我个人习惯在调试时,先用示波器抓一下起始条件附近的波形。为什么?因为很多通信失败,根源就是起始条件没发出来——可能是主机GPIO配置错了,也可能是上拉电阻没焊好,SDA拉不下去。

实战技巧:如果你看到SCL在正常翻转,但SDA一直高,先查起始条件。我曾经遇到过一颗MCU的I2C外设,初始化顺序不对,导致起始条件永远发不出来。折腾了两天,最后发现是时钟使能位没先开。

2.2 数据有效性:时钟高电平采样,低电平变化

I2C的数据传输有个铁律:数据在SCL高电平时被采样,在SCL低电平时才能变化。

你想想看,如果数据在时钟高电平期间变了,接收方就不知道你传的是0还是1。所以协议规定:

  • SCL为高电平时,SDA必须保持稳定。
  • SCL为低电平时,SDA可以变化。

唯一的例外就是前面讲的起始和停止条件——那是在高电平跳变,属于特殊标记。

避坑指南:我曾经调试一个温湿度传感器,读出来的数据偶尔跳变。抓波形发现,从机在SCL低电平时改变数据没问题,但主机采样时刻刚好卡在上升沿附近,由于线缆电容导致SCL上升沿变缓,数据还没稳定就被采了。解决办法是降低I2C速率,从400kHz降到100kHz。

2.3 字节格式:8位数据 + 1位应答

I2C传输的基本单位是字节。每个字节固定为8位,MSB(最高位)在前。发送完8位数据后,紧接着跟一个应答位(ACK/NACK)。

所以一个完整的数据帧是:8位数据 + 1位应答 = 9个SCL时钟周期。

时钟周期 SDA行为 说明
1-8 主机发送8位数据(MSB first) 数据在SCL低电平时变化
9 从机拉低SDA(ACK)或保持高(NACK) 主机释放SDA,从机控制

这里有个容易忽略的点:发送方在数据位期间控制SDA,应答位期间释放SDA,由接收方控制。如果接收方没拉低,SDA就被上拉电阻拉高,变成NACK。

2.4 应答机制:ACK与NACK

应答位是I2C协议的“握手信号”。每次传输完一个字节,接收方必须给出回应:

  • ACK(Acknowledge):接收方在第9个时钟周期将SDA拉低,表示“收到,请继续”。
  • NACK(Not Acknowledge):接收方在第9个时钟周期保持SDA高,表示“没收到”或“不想收了”。

NACK的出现通常意味着以下几种情况:

  1. 从机地址不匹配:总线上没有设备响应这个地址。
  2. 从机忙:正在处理内部操作,无法接收新数据。
  3. 传输结束:从机告诉主机“数据够了,别再发了”。
  4. 从机故障:芯片坏了或I2C接口死锁。

调试口诀:看到NACK,先查地址对不对,再查从机有没有上电,最后查从机是不是在忙。

我记得有一次调试一个EEPROM,写数据时总是NACK。查了半天,发现是EEPROM在写内部Flash时需要时间(tWR),这段时间内它不响应任何I2C命令。解决办法是在每次写操作后加一个延时,或者轮询直到收到ACK为止。

2.5 完整传输流程:从起始到停止

把上面几个知识点串起来,一次完整的I2C传输是这样的:

  1. 主机发送起始条件(S)。
  2. 主机发送7位从机地址 + 1位读写位(R/W)。
  3. 对应地址的从机回复ACK。
  4. 主机发送寄存器地址或命令字节(可选,取决于设备)。
  5. 从机回复ACK。
  6. 主机发送或接收数据字节,每字节后跟ACK。
  7. 主机发送停止条件(P)。

嗯,这里要注意:如果是读操作,在最后一个数据字节后,主机要回复NACK,然后发停止条件。这是告诉从机“别再发了”。

2.6 知识结构图:I2C协议核心要素

下面这张图把本章的核心概念串起来了。你可以把它当作调试时的“速查地图”。

I2C协议核心要素结构图 I2C协议核心要素 总线空闲状态 SCL=高, SDA=高 起始条件 (S) SCL高时SDA下降沿 停止条件 (P) SCL高时SDA上升沿 数据有效性 SCL高电平时SDA稳定 SCL低电平时SDA变化 字节格式 8位数据 + 1位应答 MSB在前,9个时钟周期 应答机制 ACK: SDA拉低 NACK: SDA保持高 完整传输流程 S 地址+R/W ACK 数据字节 ACK/NACK P 调试要点 • 起始条件:检查SDA下降沿是否在SCL高电平期间 • 应答位:第9个时钟周期SDA是否被从机拉低

2.7 常见问题与排查思路

最后,我把调试中遇到最多的几个问题列出来,你碰到类似情况可以直接对照:

现象 可能原因 排查方法
无起始条件 GPIO配置错误、上拉电阻缺失 检查SCL/SDA电平,确认主机是否拉低SDA
地址后NACK 地址错误、从机未上电、总线冲突 核对地址(7位还是8位),测量从机VDD
数据后NACK 从机忙、不支持该命令、写保护 查看数据手册的tWR时间,检查WP引脚
SCL无时钟 主机时钟未使能、从机时钟拉伸卡死 检查时钟配置,测量SCL是否被从机拉低
数据错位 速率不匹配、上升沿过缓 降低速率,检查总线电容和上拉电阻值

嗯,这一章的内容就到这里。协议细节是调试的基础,你把这些动作刻在脑子里,后面抓波形的时候一眼就能看出问题出在哪一步。