2. I2C协议核心:起始条件、停止条件、数据有效性、ACK/NACK机制

I2C协议说简单也简单,说复杂也复杂。它的核心就四个东西:起始条件、停止条件、数据有效性、ACK/NACK机制。把这四个搞明白,I2C通信你就掌握了八成。

我个人习惯把I2C比作一个微型公交系统。SCL是时钟节拍,SDA是数据车道。起始条件就是公交车进站,停止条件就是公交车离站。数据有效性就是乘客必须在节拍内上下车。ACK/NACK就是乘客回应“收到”或“没收到”。

好,咱们一个一个拆开讲。

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

这两个条件是I2C通信的“开关”。没有它们,总线上的设备不知道什么时候开始说话,什么时候结束。

起始条件(S):SCL为高电平时,SDA从高电平切换到低电平。

停止条件(P):SCL为高电平时,SDA从低电平切换到高电平。

你想想看,这个设计很巧妙。平时总线空闲时,SCL和SDA都是高电平(通过上拉电阻)。起始条件打破了这种空闲状态,告诉所有设备:“注意,要开始通信了!”

关键点:起始条件和停止条件都是由主机(Master)产生的。从机(Slave)只能被动响应,不能主动发起或终止通信。

我在项目中遇到过一个问题:某次调试时,示波器上看到SDA线上有数据跳变,但所有从机都不响应。折腾了半天,发现是起始条件没满足——SCL高电平时SDA的下降沿不够陡,从机没识别出来。嗯,这里要注意,起始条件的时序是有严格要求的。

条件 SCL状态 SDA变化 作用
起始条件(S) 高电平 高 → 低 通知所有设备:通信开始
停止条件(P) 高电平 低 → 高 通知所有设备:通信结束
重复起始条件(Sr) 高电平 高 → 低 不释放总线,直接开始新传输

实战技巧:调试时,我习惯先抓起始条件和停止条件的波形。如果这两个波形不对,后面的数据都不用看。起始条件没建立好,整个通信就是空中楼阁。

2.2 数据有效性:SCL高电平时采样

数据有效性是I2C协议里最容易理解也最容易出错的地方。

规则很简单:

  • SDA上的数据必须在SCL为低电平时改变
  • SDA上的数据必须在SCL为高电平时被采样(读取)

说白了,就是SCL高电平的时候,SDA必须稳定。SCL低电平的时候,SDA可以变化。

为什么会这样?因为从机是在SCL的上升沿或高电平期间采样SDA的。如果SDA在SCL高电平时还在跳变,从机采到的数据就是错的。

避坑指南:我曾经在一个项目中,MCU的I2C时钟频率设得太高,导致SCL高电平时间太短。从机还没来得及采样,SCL就变低了。结果就是数据总是对不上。后来我把时钟频率降下来,问题就解决了。所以,时钟频率不是越高越好,要考虑到从机的采样时间。

数据有效性的时序要求,用一张图就能看明白:

I2C数据有效性时序图 SCL SDA 数据变化区 数据采样区 数据变化区 SCL高电平时SDA必须稳定(采样区) SCL低电平时SDA可以变化(数据变化区)

2.3 ACK/NACK机制:从机的回应

ACK/NACK是I2C协议里最有人情味的部分。它让主机知道从机到底有没有收到数据。

工作机制:

  • 每传输完一个字节(8位数据),主机释放SDA线(拉高)
  • 在第9个SCL时钟周期,从机拉低SDA表示ACK(确认收到)
  • 如果从机不拉低SDA(保持高电平),就是NACK(未确认)

说白了,ACK就是从机说“收到,请继续”。NACK就是从机说“我没收到”或者“我不想再收了”。

常见NACK场景:

  • 地址不匹配:总线上没有这个地址的从机
  • 从机忙:从机正在处理内部事务,没空理你
  • 传输结束:从机告诉主机“数据够了,别再发了”
  • 从机故障:从机挂了或者初始化没完成

我记得有一次调试一个温度传感器,主机发送地址后一直收到NACK。我以为是地址写错了,查了半天手册。后来用示波器一看,发现从机的电源纹波太大,导致从机复位了。嗯,这里要注意,NACK不一定都是协议问题,也可能是电源问题。

信号 SDA状态(第9个SCL) 含义 后续操作
ACK 低电平 从机确认收到 主机继续发送下一个字节
NACK 高电平 从机未确认 主机发送停止条件或重复起始条件

调试技巧:我调试I2C时,第一个看的永远是ACK位。如果ACK位正常,说明从机在线且正常工作。如果连续NACK,先检查地址对不对,再检查从机电源和复位。

2.4 完整的数据传输流程

把上面四个核心机制串起来,一个完整的I2C数据传输流程是这样的:

  1. 起始条件(S):主机拉低SDA,通知所有从机“我要开始通信了”
  2. 发送从机地址(7位或10位):主机在SCL的驱动下,逐位发送从机地址
  3. 发送读写位(R/W):0表示写,1表示读
  4. 等待ACK:从机拉低SDA表示“我在,请继续”
  5. 发送/接收数据字节:每个字节后跟一个ACK位
  6. 停止条件(P):主机拉高SDA,通知所有从机“通信结束”

我刚开始学I2C时,总觉得这个流程很繁琐。后来做多了才发现,这种设计保证了通信的可靠性。每个字节都有确认,每个步骤都有反馈,出问题了能立刻知道。

避坑指南:我曾经在一个项目中,主机发送停止条件后,SDA线没有完全释放(被拉低)。结果下一个起始条件无法产生,因为SDA已经是低电平了。后来我在停止条件后加了一个小延时,确保SDA恢复到高电平。这个问题就解决了。

2.5 重复起始条件(Sr)

重复起始条件是个很实用的功能。它允许主机在不释放总线的情况下,直接开始新的传输。

典型应用场景:

  • 读寄存器:先写寄存器地址(写操作),再读寄存器数据(读操作)
  • 多字节读写:连续读写多个寄存器,不需要重新仲裁

重复起始条件的时序和起始条件完全一样:SCL高电平时,SDA从高切换到低。区别在于,它前面没有停止条件。

说白了,重复起始条件就是“我还没说完,别走”。

I2C完整传输流程(含重复起始条件) 起始条件 地址+R/W ACK 数据字节 ACK 停止条件 重复起始 NACK时使用 重复起始条件:不释放总线,直接开始新传输(常用于读寄存器操作)

好了,I2C协议的四个核心机制就讲完了。起始条件、停止条件、数据有效性、ACK/NACK,这四个东西是I2C通信的基石。你把这四个搞明白了,I2C协议就算入门了。剩下的就是各种细节和变种,但万变不离其宗。