1. I2C协议基础:物理层、协议层、时序图、寻址方式、读写操作

各位同学,咱们今天正式开讲I2C。说实话,我调试了十几年嵌入式系统,跟I2C打交道的次数比跟家人吃饭还多。这玩意儿看似简单,但坑是真不少。咱们先把地基打牢,后面调试时才不会抓瞎。

1.1 物理层:两条线怎么干活?

I2C总线就两条线:SCL(时钟线)和SDA(数据线)。嗯,你没看错,就两条。所有设备都挂在这两条线上,靠地址区分谁是谁。

核心特点:I2C是半双工通信,同一时刻只能一个方向传数据。别想着同时收发,它没这本事。

这两条线都是开漏输出结构。什么叫开漏?说白了,芯片只能把线拉低,不能主动拉高。那高电平谁给?上拉电阻。我见过不少新手把上拉电阻省了,结果总线死活拉不高,波形跟心电图似的。

参数 标准模式 (100kHz) 快速模式 (400kHz) 高速模式 (3.4MHz)
上拉电阻推荐值 4.7kΩ ~ 10kΩ 1.5kΩ ~ 4.7kΩ ~1kΩ
总线电容上限 400pF 400pF 100pF
最大从设备数量 理论127个 理论127个 理论127个

选电阻的小窍门:我个人习惯先按4.7kΩ起步。如果总线设备多、线长,换成2.2kΩ。曾经有个项目,传感器离主控20厘米,用10kΩ死活不通,换成3.3kΩ立马好了——上升沿太慢,时序不满足。

1.2 协议层:数据怎么在线上跑?

I2C的协议层,我总结成四个字:起、止、应、传。咱们一个一个说。

1.2.1 起始条件与停止条件

SCL高电平时,SDA从高变低 → 起始条件(Start Condition)
SCL高电平时,SDA从低变高 → 停止条件(Stop Condition)

你想想看,平时总线空闲时,SCL和SDA都是高电平。主设备想说话,先拉低SDA,再拉低SCL,告诉大家"我要开始通信了"。结束的时候反过来,先释放SCL,再释放SDA。

我曾经踩过的坑:有一次调试,从设备老是没响应。用逻辑分析仪一看,起始条件之后SDA又莫名其妙跳了一下。查了半天,是主控GPIO初始化时默认输出低电平,上电瞬间产生了一个伪起始条件。从那以后,我所有I2C引脚初始化都先配成开漏高电平。

1.2.2 数据有效性

I2C的数据采样规则很简单:SCL低电平时,SDA可以变化;SCL高电平时,SDA必须稳定。

为什么这样设计?因为从设备只在SCL高电平时采样SDA。你要是SCL高的时候乱变SDA,从设备就不知道你传的是0还是1了。这个规则贯穿整个I2C通信,你写驱动时一定要保证时序满足这一点。

1.2.3 应答机制

每传完一个字节(8位),接收方必须回一个应答位(ACK)。发送方在第9个时钟周期释放SDA,接收方拉低SDA表示"收到了"。

  • ACK(应答):SDA在第9个时钟被拉低 → 一切正常,继续
  • NACK(非应答):SDA在第9个时钟保持高 → 出问题了,或者传输结束

我刚开始学的时候觉得这机制挺啰嗦,后来发现它太有用了。调试时只要看到NACK,就知道问题出在哪一步——是地址不对?还是从设备忙?还是数据传错了?

1.3 时序图:用眼睛看通信

光说理论不够,咱们得会看时序图。这是I2C调试的基本功。

I2C 完整读写时序图 SCL SDA 起始 7位从设备地址 (0x3C) R/W ACK 寄存器地址 (0x00) ACK 数据字节 (0xA5) NACK 停止 时序要点解读: 1. 起始条件后,主设备发送7位地址 + 1位读写位(0=写,1=读) 2. 每个字节后跟一个ACK/NACK位,由接收方控制 3. 读操作时,主设备在最后一个字节发NACK表示"不要了",然后发停止条件

1.4 寻址方式:怎么找到你要的设备?

I2C用7位地址寻址(也有10位地址模式,但用得少)。主设备发完起始条件后,紧跟着发7位地址 + 1位读写位。

举个例子:你买了个温度传感器,数据手册说它的7位地址是0x3C。那主设备发送的地址字节就是:

0x3C << 1 | 0  = 0x78  (写操作,R/W=0)
0x3C << 1 | 1  = 0x79  (读操作,R/W=1)

注意,地址字节是8位,高7位是设备地址,最低位是读写标志。很多新手直接拿0x3C去发,结果从设备根本不搭理你——因为0x3C左移一位才是真正的地址字节。

查地址的小技巧:我习惯用逻辑分析仪抓一次成功的通信,看地址字节是多少,然后反推7位地址。这样比看数据手册快,还能确认硬件接线对不对。

1.5 读写操作:主设备怎么跟从设备说话?

I2C的读写操作,说白了就是主设备控制节奏,从设备跟着走。

1.5.1 写操作流程

  1. 主设备发起始条件
  2. 主设备发从设备地址 + 写标志(0)
  3. 从设备回ACK
  4. 主设备发寄存器地址(告诉从设备我要写哪个寄存器)
  5. 从设备回ACK
  6. 主设备发数据字节(可以连续发多个)
  7. 每发一个字节,从设备回一个ACK
  8. 主设备发停止条件

1.5.2 读操作流程

  1. 主设备发起始条件
  2. 主设备发从设备地址 + 写标志(0)(先假装要写)
  3. 从设备回ACK
  4. 主设备发寄存器地址(告诉从设备我要读哪个寄存器)
  5. 从设备回ACK
  6. 主设备发重复起始条件(Restart,不停止直接重新开始)
  7. 主设备发从设备地址 + 读标志(1)
  8. 从设备回ACK
  9. 从设备发数据字节(主设备变成接收方)
  10. 主设备每收一个字节,回ACK表示还要继续
  11. 主设备收完最后一个字节,回NACK表示够了
  12. 主设备发停止条件

重复起始条件是个坑:我曾经在一个项目里,读传感器数据时用了停止条件再重新开始,结果传感器每次都要多等几十毫秒才能响应。换成重复起始条件后,速度直接翻倍。记住:读操作一定要用Restart,别Stop再Start。

1.6 本章小结

I2C的基础就这些:两条线、开漏输出、上拉电阻、起止条件、7位地址、ACK/NACK、读写流程。这些东西看着简单,但每一个细节都可能成为调试时的拦路虎。