3. I2C地址机制:7位地址与10位地址格式、广播地址、保留地址

地址机制是I2C通信的基石。说白了,主机要跟哪个从机说话,全靠地址来区分。很多初学者觉得地址就是个编号,随便填就行——嗯,我以前也这么想,直到在一个多从机项目里踩了坑。

这一节我们就把地址机制彻底讲透。包括7位地址怎么用、10位地址什么时候需要、广播地址和保留地址又是什么角色。

3.1 7位地址格式

标准I2C设备最常用的就是7位地址。地址占用第一个字节的高7位,最低位是读写标志位(0表示写,1表示读)。

7位地址帧结构:

起始条件 | 7位地址 | R/W位 | 应答位 | 数据... | 停止条件

地址范围:0x00 ~ 0x7F(共128个地址,但可用地址只有112个)

为什么只有112个?因为地址0x00和0x01被用作特殊用途,还有一部分地址被保留。我习惯把7位地址称为“原生地址”,因为这是I2C规范最初定义的格式。

举个例子,一个常见的温度传感器LM75,它的7位地址是0x48(如果AD0引脚接地)。在代码里你这样写:

// 7位地址 0x48,写操作
uint8_t addr = 0x48 << 1;  // 左移1位,最低位填0
// 实际发送的字节:0x90

注意这个左移操作。很多新手直接传0x48,结果发现设备不响应。因为I2C控制器期望的是左移后的8位字节,而不是原始7位地址。

3.2 10位地址格式

7位地址最多支持112个设备。这在复杂系统里不够用。于是I2C规范引入了10位地址。

10位地址的格式稍微复杂一点。它用两个字节来传输地址:

字节 位[7:5] 位[4:0] 说明
第一字节 1111 0 地址[9:8] 前5位固定为11110,后2位是地址高2位
第二字节 地址[7:0] — 地址低8位

第一字节的高5位是11110,这是10位地址的标志。然后紧跟地址的高2位(A9和A8),最后是R/W位。第二字节就是地址的低8位。

我的经验:10位地址在实际项目中用得不多。大多数消费级传感器、EEPROM、ADC都只用7位地址。我只有在做服务器主板的管理系统时,才遇到过需要10位地址的场景——板卡上挂了十几个I2C设备,7位地址根本不够分。

10位地址的读写流程是这样的:

  1. 主机发送起始条件
  2. 发送第一字节:11110 + A9A8 + R/W
  3. 从机应答
  4. 主机发送第二字节:A7~A0
  5. 从机再次应答
  6. 开始数据传输

注意,10位地址的从机在收到第一字节后就要应答,而不是等到第二字节。我曾经调试过一个10位地址的EEPROM,发现它第一字节不应答,查了半天手册才发现是地址高两位配置错了。

3.3 广播地址

广播地址是0x00。当主机发送地址0x00时,总线上的所有从机都应该响应。

广播地址的用途很明确:

  • 通用呼叫:主机想同时通知所有从机做某件事
  • 系统复位:发送特定数据让所有从机复位
  • 地址分配:某些可编程地址的设备通过广播来分配地址

但要注意,不是所有从机都支持广播地址。很多从机手册里会明确写“不支持通用呼叫”。我建议你在选型时就确认这一点,否则广播发出去没人理,主机就卡在等待应答的状态。

避坑指南:我曾经在一个项目里用广播地址复位所有传感器,结果有一个传感器不支持广播,它直接NACK了。主机检测到NACK后终止了传输,导致其他传感器也没复位成功。后来我改成逐个发送复位命令,虽然慢了点,但可靠多了。

3.4 保留地址

I2C规范保留了一些地址用于特殊功能。这些地址你不能分配给从机设备。

地址 用途 说明
0x00 广播/通用呼叫 所有从机都应响应
0x01 起始字节 用于慢速MCU检测总线
0x02 CBUS地址 保留给CBUS兼容设备
0x03~0x07 保留 未来扩展用途
0x78~0x7B 10位地址标志 用于10位地址的第一字节
0x7C~0x7F 保留 未来扩展用途

你看,0x78到0x7B这四个地址就是10位地址的标志区。如果你用7位地址的设备,千万别把地址设在这个范围,否则会跟10位地址设备冲突。

还有一个地址0x01叫“起始字节”,这个比较冷门。它用于一些慢速MCU,它们需要检测总线上的起始条件,但自己的时钟太慢跟不上。通过发送0x01作为起始字节,这些MCU有足够时间准备接收后续数据。说实话,我在实际项目中从没用过这个功能。

3.5 地址冲突与调试

地址冲突是I2C调试中最常见的问题之一。两个从机用了同一个地址,总线就乱套了。

我总结了几条经验:

  • 选型时留余地:尽量选有多个地址引脚(AD0、AD1、AD2)的设备,这样你可以通过硬件配置来避免冲突
  • 用I2C扫描工具:调试阶段先跑一遍地址扫描,看看总线上有哪些设备在响应
  • 注意地址偏移:很多库函数传参时用的是原始7位地址,但硬件寄存器里存的是左移后的值。搞混了就会找不到设备

我的习惯:在代码里统一用原始7位地址(0x00~0x7F),只在发送前做左移。这样代码可读性好,也不容易出错。我会在头文件里写清楚:

// 传感器地址定义(原始7位地址)
#define LM75_ADDR     0x48
#define EEPROM_ADDR   0x50
// 发送时左移
i2c_write(LM75_ADDR << 1, data, len);

3.6 地址机制知识图谱

下面这张图把I2C地址机制的核心脉络梳理清楚了。你可以看到7位地址和10位地址的格式差异,以及保留地址和广播地址在整个体系中的位置。

I2C地址机制知识图谱 I2C地址机制知识图谱 I2C地址机制 7位地址格式 10位地址格式 特殊地址 地址范围 0x00~0x7F 112个可用地址 R/W位占最低位 第一字节标志 11110 1024个可用地址 双字节传输 广播地址 0x00 保留地址 起始字节 0x01 核心要点 7位地址最常用,10位地址用于大规模系统,广播地址和保留地址不可分配给从机 地址选择建议 • 大多数传感器/EEPROM:7位地址足够 • 多从机复杂系统:考虑10位地址或I2C多路复用器 • 调试阶段:先用地址扫描确认总线设备

这张图把三种地址类型的关系和子节点都画出来了。你可以看到7位地址和10位地址是并列关系,而广播地址和保留地址属于特殊地址类别。底部还给出了实际选型建议。