4. I2C协议基础:7位地址与10位地址、通用呼叫地址

I2C协议,说白了就是一套“谁跟谁说话、怎么说话”的规矩。你想想看,总线上挂着好几个设备,主控器怎么知道要找谁?从设备又怎么知道主控在叫自己?答案就在地址里。

我个人习惯把I2C地址比作门牌号。7位地址就像小区里的普通门牌,10位地址则是带单元号的复杂门牌。而通用呼叫地址,相当于广播喇叭喊“所有人注意”。

4.1 7位地址结构

7位地址是I2C最常用的地址格式。主控发送起始条件后,紧跟着的第一个字节就是地址字节。这个字节的高7位是地址,最低位是读写方向位。

位 7 (MSB) 6 5 4 3 2 1 0 (LSB)
含义 7位从机地址 R/W
说明 A6 ~ A0 0=写,1=读

举个例子:你常用的温度传感器LM75,它的7位地址是0x48(二进制1001000)。主控要写数据时,发送的地址字节就是0x90(10010000);要读数据时,发送0x91(10010001)。

我的习惯:写驱动时,我通常把7位地址左移1位,再手动加上读写位。这样代码更清晰,不容易搞混。比如:addr_7bit = 0x48; write_byte = (addr_7bit << 1) | 0;

4.2 10位地址格式

7位地址最多支持112个设备(保留地址不算)。但有些场景下设备太多,7位不够用。I2C规范在1992年引入了10位地址。

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

  1. 第一个字节:高5位固定为11110,接着是10位地址的高2位,最后是R/W位。
  2. 第二个字节:10位地址的低8位。
阶段 字节内容 说明
第1字节 11110 A9 A8 R/W 11110是10位地址标识,A9-A8是地址高位
第2字节 A7 A6 A5 A4 A3 A2 A1 A0 地址低位,共8位
我曾经踩过的坑:10位地址的从机在应答时,主控需要发送两次地址才能完成寻址。有些MCU的I2C外设不支持10位地址模式,必须用GPIO模拟。我当年在某个项目里,芯片手册说支持10位地址,结果实际测试发现硬件有bug,最后只能切到模拟I2C。所以,用10位地址前,务必确认硬件是否真的支持。

4.3 通用呼叫地址

通用呼叫地址是0x00(7位全0)。它的作用是让主控一次性给所有从机发消息。从机可以选择响应,也可以忽略。

通用呼叫的典型应用场景:

  • 系统复位:主控发通用呼叫,让所有从机复位到初始状态。
  • 广播数据:比如更新所有从机的配置参数。
  • 设备发现:某些系统在初始化时,用通用呼叫来探测总线上有哪些设备。

通用呼叫地址字节是0x00(写方向)。第二个字节是命令字节,从机根据命令字节决定是否响应。

第二个字节 含义
0x06 复位所有从机
0x04 写入可编程地址(部分从机支持)
其他值 由具体从机定义
实际项目经验:我在做多传感器采集板时,用通用呼叫地址做批量复位。每次系统启动,先发0x00+0x06,确保所有传感器处于已知状态。这比逐个复位快得多,尤其当总线上挂了8个以上的传感器时。

4.4 保留地址与特殊地址

I2C规范保留了一些特殊地址,普通设备不能用。这些地址有特殊用途:

7位地址 用途
0x00 通用呼叫地址
0x01 起始字节(用于慢速设备)
0x02 ~ 0x03 保留
0x04 ~ 0x07 高速模式主控代码
0x78 ~ 0x7F 10位地址扩展标识

你设计系统时,选从机地址要避开这些保留地址。我见过有人不小心把设备地址设成0x00,结果总线上所有设备都响应,数据乱成一锅粥。

4.5 地址冲突与解决

实际项目中,多个同型号设备挂在同一条I2C总线上很常见。比如两个LM75温度传感器,默认地址都是0x48,这就冲突了。

解决办法通常有几种:

  • 地址引脚:很多传感器有ADDR引脚,通过拉高或拉低来改变地址。LM75就有3个地址引脚,可以配置8个不同地址。
  • I2C多路复用器:用PCA9548这类芯片,把总线分成多路,每路挂一个设备。
  • 软件地址重映射:部分设备支持通过命令修改自身地址。
我的建议:画原理图时,就把地址引脚的上拉/下拉电阻画清楚。我吃过一次亏,板子打回来才发现地址引脚忘了接,结果两个传感器地址一样,只能飞线改。那叫一个狼狈。

4.6 地址与读写操作流程

理解了地址格式,我们看看完整的读写流程。下面这张图展示了7位地址的写操作过程:

7位地址写操作流程 主控发START 地址字节(0x90) 从机ACK 数据字节 STOP 流程说明: 1. 主控拉低SCL和SDA,产生起始条件 2. 发送地址字节0x90(7位地址0x48 + 写位0) 3. 从机在第九个时钟周期拉低SDA,表示应答 4. 主控发送数据字节,从机每字节后应答 5. 主控产生停止条件,释放总线

读操作流程类似,只是地址字节的R/W位为1。但要注意,读操作前通常要先写寄存器地址,所以实际流程是:START + 写地址 + 寄存器地址 + 重复起始 + 读地址 + 读数据 + STOP。

关键点:重复起始条件(Repeated START)是I2C协议里很实用的技巧。它允许主控在不释放总线的情况下切换读写方向。我写驱动时经常用这个,比发STOP再发START要快,而且能防止其他主控抢占总线。

4.7 地址选择实战建议

最后,给你几个我在项目中总结的地址选择原则:

  • 优先用7位地址:除非设备数量超过112个,否则别碰10位地址。7位地址兼容性好,几乎所有MCU和传感器都支持。
  • 避开保留地址:选地址前,对照I2C规范检查一遍。0x00~0x07、0x78~0x7F这些别用。
  • 考虑地址冲突:如果总线上有多个同型号设备,提前规划地址引脚配置。最好在原理图阶段就定好。
  • 通用呼叫慎用:通用呼叫会影响所有从机。除非你确定所有设备都支持且行为可控,否则别乱发。