4. I2C总线协议进阶:7位地址和10位地址、读写操作、时钟 stretching

前面我们已经聊了I2C的基础时序,说白了就是Start、Stop和字节传输那套。但实际项目里,光懂这些是不够的。我调试过不下百种I2C设备,从几毛钱的温度传感器到复杂的摄像头模组,真正让你卡住的往往是地址冲突、时序不匹配这些进阶问题。这一章,咱们就深入进去,看看地址怎么玩、读写怎么区分,还有那个让人又爱又恨的时钟拉伸。

4.1 7位地址,你看到的可能只是表象

绝大多数I2C设备用的是7位地址。但很多人被数据手册里的“0x68”这种写法搞晕了。你想想看,这个“0x68”到底是不是总线上传输的那个字节?

不是。 数据手册给的通常是7位地址,但总线上你得左移一位,最后补上读写位。

概念 举例 说明
7位器件地址 0x34 数据手册给出的常用格式
写操作地址字节 0x68 左移一位,最低位为0 (0x34 << 1 | 0)
读操作地址字节 0x69 左移一位,最低位为1 (0x34 << 1 | 1)
我的习惯: 写代码时,我从来不在宏定义里直接写移位后的地址。我会定义成7位原始地址,比如 #define I2C_ADDR 0x34,然后在读写函数里临时左移。这样代码可读性高,别人一看就知道你用的是哪个器件。

我曾经在一个项目中,接手别人的代码,发现宏定义写着 #define ADDR 0xD0。我查了半天数据手册,器件地址明明是0x68。后来才明白,他把写地址直接写死了。这种做法很危险,万一哪天要读寄存器,还得记着再或一个1,很容易出错。

4.2 10位地址:什么时候用?怎么用?

7位地址只能挂127个设备(0x00保留做通用呼叫),不够用了怎么办?这就轮到10位地址上场了。说实话,我在消费类电子产品里用到10位地址的场景不多,但在一些大型的工控背板上,挂几十个传感器时就会遇到。

10位地址的传输分两个阶段:

  1. 第一个字节: 高5位固定是 11110,接着是10位地址的高2位,最后是读写位。
  2. 第二个字节: 剩下的8位地址。

用逻辑分析仪抓出来的波形,你是能直接看出来的。第一个字节的前5位就是 0xF0 到 0xF7 的范围。你想想看,如果看到地址字节是 0xF0 或 0xF2,那肯定不是普通7位地址的设备。

为什么容易出错? 很多MCU的I2C外设原生不支持10位地址。我建议你在选型时就确认好,如果要用10位地址,要么用GPIO模拟I2C,要么选一个明确支持10位地址模式的硬件I2C控制器。

4.3 读写操作,逻辑分析仪上的一目了然

I2C的读写其实非常直观。总线上读写操作的区别,不在于数据怎么传,而在于那个最低位的控制。

  • 写操作: 主机发送地址字节(读写位=0),然后连续发送数据字节。每个字节后从机都会回复ACK。
  • 读操作: 主机发送地址字节(读写位=1),然后主机变成接收者,从机开始发送数据。注意,这里的ACK/NACK是由主机控制的!

我刚开始学的时候,总有一个困惑:读操作时最后一拍到底怎么结束?

标准做法是: 读最后一个字节时,主机不要回复ACK,而是回复一个NACK,然后发Stop信号。这样从机就知道数据发送完了,停止驱动SCL和SDA。

你看这个表:

操作类型 主机角色 从机角色 最后一个字节应答
写 发送方 接收方 从机回复ACK
读 接收方 发送方 主机回复NACK
我曾经 犯过一个低级错误:读数据时,最后一个字节也回了ACK。结果从机以为还要继续发,一直拉着SCL不放,整个总线就卡死了。用逻辑分析仪一看,波形停在第九个时钟的ACK位置上,一直不动。所以记着,读操作的最后一拍,必须是NACK。

4.4 时钟拉伸(Clock Stretching):从机的“缓兵之计”

这是I2C协议里一个很特别的地方。标准I2C是主设备控制时钟,但时钟拉伸允许从机把SCL线拉低,强制主机等待。

为什么要干这事?你想想看,从机可能正在处理内部数据,比如刚刚完成一次AD转换,数据还没准备好。如果主机不管三七二十一地读,从机只能返回垃圾数据。这时从机就会拉低SCL,说:“等等,我还没准备好。”

逻辑分析仪上看时钟拉伸,最明显的就是:SCL的低电平时间被延长了,比正常的长很多。正常的SCL高低电平时间是对称的,拉伸时你会看到低电平占了很大一部分。

我遇到过一种情况:一个温湿度传感器在每次采样后,会拉伸时钟长达10ms。如果主机没有超时处理机制,就以为总线死锁了。其实不是,是传感器在忙。

时钟拉伸的两个关键点:

  • 主机必须支持: 如果主机硬件不支持时钟拉伸,就会误判为总线错误。很多便宜的MCU的硬件I2C外设是不支持自动处理拉伸的,需要你用GPIO模拟。
  • 必须有超时机制: 不能无限等下去。我个人的经验是,把超时设在10-20ms之间。超过这个时间还没释放,就认为从机挂死了,进行总线恢复。

4.5 一张图看懂本章核心

I2C协议进阶核心逻辑 7位地址 10位地址 读写操作 左移1位+读写位 范围0x00-0x7F 首字节11110+高2位 次字节低8位地址 写:主机发+从机ACK 读:从机发+主机NACK 时钟拉伸 (Clock Stretching) 从机拉低SCL,主机等待,需超时机制

用逻辑分析仪抓波形时,我习惯开启协议解码功能。设置好是否支持时钟拉伸,很多时候你看到地址不对,其实是因为数据手册和逻辑分析仪的设置,一个用了7位格式,一个用了8位格式。统一一下就好了。

4.6 避坑指南

这一章讲的内容,在实际调试中会反复出现。我总结几个坑:

  1. 地址冲突: 同一个I2C总线上挂了两个相同地址的器件。解决办法:看器件有没有地址引脚,有的话改一个;没有的话,只能加I2C多路复用器。
  2. 读写位搞反: 我见过有人发送了读地址,但后续操作全是主机发数据。逻辑分析仪上一看,从机根本没响应,因为从机以为主机要读,自己正等着发数据呢。
  3. 忘记NACK: 读操作最后一拍回了ACK,总线卡死。这个前面说过了,一定注意。
  4. 时钟拉伸没超时: 一些从机启动时要几百ms,但正常工作后拉伸只有几ms。如果超时设得太短,上电时就会报错。

嗯,我个人经验是,调试I2C最忌讳“凭感觉”。用逻辑分析仪一帧一帧地看,地址对不对、ACK有没有、时序是否符合规格书上写的最小时序要求。这一章的内容,说白了就是让你拿到数据手册能看懂,抓到波形能分析。