I2C协议核心:起始/停止条件、数据有效性、应答机制、7位与10位地址模式

I2C协议,说白了就是一套设备之间聊天的规矩。谁先开口,谁先闭嘴,数据怎么才算有效,对方听没听懂——这些都得有个约定。我刚开始调I2C的时候,总觉得这东西简单,结果被一个应答信号坑了一整天。嗯,咱们今天就把这些核心机制掰开揉碎了讲清楚。

起始条件与停止条件

I2C总线上,SCL(时钟线)和SDA(数据线)平时都是高电平。什么时候开始传输?什么时候结束?全靠这两个特殊信号来标记。

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

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

这两个信号是唯一允许SDA在SCL高电平时变化的情况。其他时候,SDA只能在SCL低电平时变化——这是硬规矩。

我个人习惯在逻辑分析仪上先看这两个边沿。如果起始条件没抓到,后面全是废数据。我记得有一次,从机地址一直读不到,折腾了半天,最后发现是起始条件保持时间不够,从机根本没识别到。

信号 SCL状态 SDA变化 时序要求
起始条件 高电平 高→低 tHD;STA ≥ 4μs(100kHz)
停止条件 高电平 低→高 tSU;STO ≥ 4μs(100kHz)

小技巧:连续传输时,可以用重复起始条件(Repeated START)代替停止+起始。这样总线不会被释放,适合多字节连续读写。我在驱动传感器时经常用这个,能省不少时间。

数据有效性:什么时候读数据才算数?

数据有效性的规则其实就一句话:SCL高电平时,SDA上的数据必须稳定。

为什么?因为接收方只在SCL的上升沿或高电平期间采样SDA。如果SDA在SCL高电平时乱跳,采样结果就是错的。

具体来说:

  • SCL低电平时,发送方可以改变SDA电平
  • SCL高电平时,SDA必须保持稳定
  • 每个SCL时钟周期传输1位数据
  • 数据以MSB(最高位)优先发送

我曾经在项目中遇到过一个问题:从机返回的数据总是错位。查了半天,发现是主机的SCL时钟占空比不对,高电平时间太短,从机来不及采样。说白了,时序参数没配好。

应答机制:对方听懂了吗?

I2C的应答机制,是它可靠性的核心。每发送完一个字节(8位),发送方会释放SDA,接收方在第9个时钟周期拉低SDA表示应答。

ACK(应答):第9个SCL周期,SDA被接收方拉低。

NACK(非应答):第9个SCL周期,SDA保持高电平。

什么时候会出现NACK?我总结了几种常见情况:

  1. 地址不匹配:总线上没有设备响应这个地址
  2. 从机忙:从机正在处理内部操作,无法接收新数据
  3. 读操作结束:主机发送NACK告诉从机“别再发了”
  4. 非法命令:从机不支持当前操作

避坑指南:我曾经在驱动一个EEPROM时,写操作后没有等内部写入完成就直接发下一个命令,结果从机一直回NACK。后来加了轮询等待才解决。记住:NACK不一定是错误,也可能是从机在说“等一下”。

7位地址模式

7位地址是I2C最常用的模式。主机发送起始条件后,紧接着发送一个字节:高7位是设备地址,最低位是读写方向位(0=写,1=读)。

举个例子:

起始条件 → 0xA0 (1010 0000) → ACK → 数据字节 → ACK → ... → 停止条件
// 0xA0 = 7位地址0x50 + 写方向位0
// 0xA1 = 7位地址0x50 + 读方向位1

7位地址模式下,理论上最多挂127个设备(地址0x00保留)。但实际中,很多设备会占用多个地址,或者有固定地址位。我常用的做法是:先查数据手册,确认设备的7位地址和可配置的地址引脚。

地址范围 用途 说明
0x00 通用呼叫地址 保留,用于广播
0x01-0x07 保留 特殊用途
0x08-0x77 普通设备地址 可用范围
0x78-0x7F 10位地址前缀 用于扩展地址

10位地址模式

7位地址不够用怎么办?I2C规范定义了10位地址模式。它用两个字节来传输地址:

  • 第一个字节:高5位固定为11110,接着是高2位地址,最后是读写位
  • 第二个字节:低8位地址

你想想看,10位地址能挂多少设备?1024个。实际中虽然用不了这么多,但在一些复杂的系统中,比如服务器主板、基站设备里,确实会遇到地址冲突的问题。

我记得有一次调试一个多路I2C交换机,上面挂了十几个传感器,7位地址根本不够分。最后改用10位地址模式才搞定。不过要注意,不是所有从机都支持10位地址,用之前一定要确认。

经验之谈:10位地址的读写流程和7位略有不同。写操作时,第一个字节的读写位为0,然后发第二个字节地址;读操作时,需要先发一个写操作来传输地址,再发重复起始条件和读操作。我建议你在驱动里单独封装10位地址的处理函数,别和7位混在一起。

核心机制关系图

下面这张图把I2C协议的核心机制串起来了。从起始条件开始,到地址传输、数据读写、应答判断,最后到停止条件——整个流程一目了然。

I2C协议核心机制流程图 I2C协议核心机制流程图 起始条件 (START) 发送7位/10位地址 + R/W 等待从机应答 (ACK/NACK) NACK:错误处理/重试 ACK:继续传输数据 发送/接收数据字节 停止条件 (STOP) 数据有效性规则 SCL高电平时SDA必须稳定 SCL低电平时SDA可以变化

写在最后

起始条件、停止条件、数据有效性、应答机制、地址模式——这五个东西构成了I2C协议的骨架。你写驱动的时候,底层操作无非就是这几件事的组合。我个人建议,刚开始学的时候别急着写代码,先在逻辑分析仪上把每个信号都看一遍,心里有数了再动手。

嗯,这些基础打牢了,后面讲驱动分层设计的时候,你才能理解为什么要把协议层和设备层分开。咱们下回见。