3. I2C设备地址:7位地址与10位地址格式、广播地址、保留地址、地址冲突与解决策略

地址,是I2C总线上每个设备的“身份证”。

我刚开始学I2C时,总觉得地址就是个编号,随便给一个就行。直到有一次,我把两个型号一样传感器挂到同一条总线上,结果数据全乱了。嗯,从那以后我才认真研究起地址这回事。

这一节,我们就来把I2C地址的底细彻底搞清楚。

3.1 7位地址格式

标准I2C设备使用7位地址。这7位地址跟在起始条件之后,由主机发送。

具体格式是这样的:

起始条件 | 7位地址(高7位) | 读写位(1位) | 应答位

举个例子,假设一个设备的7位地址是 0x48(二进制 1001000),主机要读取数据:

主机发送:0x48 << 1 | 1 = 0x91  (10010001)
设备应答:ACK

这里有个细节:7位地址左移1位,最低位填读写位。所以你在数据手册里看到的地址,通常是7位值,但实际发送时要左移。

我的习惯:在代码里直接定义7位地址,发送时用 (addr << 1) | rw 拼装。这样可读性最好,也不容易出错。

3.2 10位地址格式

7位地址最多挂128个设备,实际可用的大约112个。对于复杂系统,这不够用。

10位地址格式应运而生。它用两个字节来传输地址:

字节 位[7:0] 说明
第1字节 1111 0 XX 高5位固定11110,后2位是10位地址的最高2位
第2字节 XXXXXXXX 10位地址的低8位

10位地址的保留标识是 11110XX,其中XX是地址的最高两位。主机先发这个标识字节,再发低8位地址。

注意:不是所有I2C控制器都支持10位地址。选型时一定要确认MCU的I2C外设是否支持。我曾在STM32F103上踩过这个坑——它的I2C外设不支持10位地址模式。

3.3 广播地址与保留地址

I2C协议定义了一些特殊地址,不能分配给普通设备。

地址(7位) 用途 说明
0x00 广播地址/通用呼叫 所有设备都应应答,用于主机向所有设备发送命令
0x01 起始字节 用于慢速设备检测总线
0x04-0x07 HS模式主码 高速模式标识
0x78-0x7B 10位地址标识 11110XX格式
0x7C-0x7F 保留 未来扩展用

广播地址(0x00)比较特殊。主机发送广播地址时,所有从机都应该应答。我曾在项目中用广播地址实现“一键复位所有传感器”——主机发一个广播命令,所有设备同时复位,省去了逐个操作的麻烦。

关键点:普通设备的可用7位地址范围是 0x08 ~ 0x77(共112个)。设计系统时,要避开保留地址段。

3.4 地址冲突与解决策略

地址冲突,说白了就是两个设备用了同一个地址。这在I2C系统中很常见,尤其是使用相同型号的传感器时。

我遇到过最典型的情况:一块板子上用了两个相同的温度传感器,地址都是 0x48。结果读温度时,两个传感器的数据混在一起,根本分不清谁是谁。

解决地址冲突,有几种常用策略:

  1. 地址引脚配置——很多I2C芯片有ADDR引脚,通过拉高或拉低来改变地址。比如AT24C02的A0/A1/A2引脚,可以组合出8个不同地址。
  2. 使用多路复用器——比如PCA9548A,可以分时选通不同的I2C通道,让同一地址的设备在不同时间工作。
  3. 使能引脚控制——有些芯片有EN或SHDN引脚,通过GPIO控制哪个设备在线。
  4. 更换地址范围——如果设备支持软件配置地址(如某些数字电位器),可以在初始化时写入不同地址。
我的建议:设计阶段就规划好地址分配表。把每个设备的地址、引脚配置、复用器通道都列出来。别等到焊好板子才发现冲突,那时候改起来就麻烦了。

3.5 地址分配实战流程

下面是我个人习惯的地址分配流程,供你参考:

  1. 列出所有I2C设备,查数据手册确认可用地址范围
  2. 排除保留地址(0x00-0x07, 0x78-0x7F)
  3. 优先使用地址引脚配置,让相同型号设备错开地址
  4. 如果地址不够,引入多路复用器
  5. 在代码中定义地址宏,方便后期修改
// 地址分配示例
#define TEMP_SENSOR_1_ADDR    0x48  // ADDR引脚接GND
#define TEMP_SENSOR_2_ADDR    0x49  // ADDR引脚接VCC
#define EEPROM_ADDR           0x50  // A0/A1/A2全接GND
#define MUX_ADDR              0x70  // 多路复用器地址

3.6 知识结构图

下面这张图总结了I2C地址体系的核心内容:

I2C地址体系结构图 I2C地址体系结构 I2C地址体系 7位地址格式 10位地址格式 特殊地址 范围:0x08 ~ 0x77(112个) 发送时左移1位 + 读写位 标识:11110XX + 低8位 需MCU硬件支持 广播地址:0x00 保留地址:0x78~0x7F 地址冲突解决策略 地址引脚配置 多路复用器 使能引脚控制 软件配置地址

3.7 避坑指南

最后,分享几个我踩过的坑:

  • 地址左移的坑:有些库函数已经帮你左移了,你再左移一次就错了。一定要看文档确认。
  • 保留地址的坑:别把设备地址设成0x00或0x78附近,否则总线会异常。
  • 多设备共存的坑:同一总线上设备太多,总线电容会增大,影响信号质量。一般建议不超过8个设备。
  • 地址冲突的坑:如果两个设备地址相同且都应答,总线上的数据会打架,产生总线冲突。
我曾经:在一个项目里用了两个相同地址的传感器,没加复用器,结果读数据时总线一直报错。查了两天才发现是地址冲突。从那以后,我每次画原理图前都会先做地址分配表。