I2C 寻址机制:7 位地址、10 位扩展与保留地址

说到 I2C 的寻址,我刚开始做嵌入式时总觉得这东西死板。7 位地址够用吗?广播地址到底什么时候用?10 位地址又是怎么扩展的?今天咱们把这几个概念彻底理清。

核心要点:I2C 总线上每个从设备必须拥有唯一地址。主设备通过地址选中目标设备,其他设备保持静默。

7 位地址格式——最常见也最基础

标准 I2C 使用 7 位地址。主设备在起始条件后先发送 7 位地址 + 1 位读写方向位。这 8 位叫“地址字节”。

位序 功能 说明
Bit 7~1 7 位设备地址 高位先发,范围 0x00 ~ 0x7F
Bit 0 (R/W) 读写方向 0 = 写,1 = 读

举个例子。假设从机地址是 0x48(7 位二进制 1001000),主设备要写入数据,那发送的第一个字节就是 0x48 << 1 | 0 = 0x90。如果想读,就发 0x48 << 1 | 1 = 0x91。

我个人习惯:写代码时直接用 addr << 1 表示写地址,addr << 1 | 1 表示读地址。别在脑子里转二进制,容易晕。

我曾经踩过这个坑:在一个传感器项目里,我直接把 7 位地址当作 8 位发送,结果设备没应答。查了半天才发现问题——少左移一位。那滋味,啧,不想再尝。

10 位地址扩展——什么时候用?怎么用?

7 位地址只支持 128 个地址,其中还有 16 个保留地址。实际可用也就 112 个。有些大型系统需要挂载几十个同类芯片,这时就要用到 10 位地址。

10 位地址的发送分两个阶段:

  1. 第一个字节:高 5 位固定为 11110,接着是 10 位地址的高 2 位,最后 1 位是 R/W。格式:11110 A9 A8 R/W
  2. 第二个字节:剩下的 8 位地址 A7~A0。

主设备先发第一个字节(包含高两位地址),然后发第二个字节。如果 R/W=0(写操作),从设备会在每个字节后应答。

// 10 位地址 0x2B0 的写操作示例
// 10 位地址 = 00 1010 1101 0000? 等一下,我算一下。
// 0x2B0 = 二进制 10 1011 0000,高两位 A9A8 = 10,低 8 位 A7~A0 = 1011 0000
// 第一个字节: 1111 0 10 0 = 0xF4 (写)
// 第二个字节: 1011 0000 = 0xB0
// 所以发送序列: START + 0xF4 + 0xB0 + 数据字节...
小提示:10 位地址和 7 位地址可以混用在同一总线上。但从设备支持 10 位的话,一定要看数据手册确认。我遇到过芯片手册写错了地址位数,结果花了半天调试。

广播地址与通用调用

广播地址就是 0x00(7 位全零)。任何从设备只要支持广播功能,都会响应这个地址。通常用于系统级命令,比如软件复位、地址分配等。

广播地址的 R/W 位必须是 0(写模式),因为主设备要发送命令字节。从设备只接收,不应答数据中的内容。

地址值 含义 典型使用
0x00 通用调用(广播) 复位所有设备、地址分配
0x01 起始字节 慢速设备检测总线
0x02~0x03 CBUS 地址 已废弃
0x04~0x07 保留 future use
避坑指南:别随便发广播地址!有一次我在生产线上用广播复位所有传感器,结果一个不支持的芯片直接锁死了总线。从此我发广播前都会先检查从机手册。

保留地址一览

I2C 规范保留了 16 个地址(0x00 ~ 0x0F)用于特殊用途。实际软件开发里最常用的是 0x00 广播地址,其他地址基本碰不到。

7 位地址 用途 备注
0x00 通用调用 广播
0x01 起始字节 用于慢速 MCU
0x02 CBUS 地址 旧协议
0x03 保留 未来扩展
0x04 ~ 0x07 高速模式主码 Hs 模式
0x08 ~ 0x0F 保留 未来标准

你想想看,如果某个从设备地址恰好落在保留地址里,那它就不能用于标准 I2C 总线了。市面上正规芯片都会避开这些地址,但山寨货不好说。

I2C 寻址机制总览 7 位地址 10 位地址 保留 / 广播地址 地址字节 = 7位地址 + R/W 可用地址 0x08~0x77(112个) 两阶段发送 可支持1024个地址 0x00广播 / 0x01起始字节 0x04~0x07高速主码等 地址冲突风险 多个同地址设备需复用 第一字节高位 11110 兼容7位地址规范 通用调用命令 复位/初始化/地址分配 硬件设计建议: 优先使用7位地址(0x08~0x77),避开保留区。若从机超过112个再考虑10位地址。

实际项目中的地址分配策略

我做过一个 12 通道温度采集板,每个通道用一片 LM75。LM75 的 7 位地址由三个引脚决定,只能配置 8 个不同地址。怎么办?只能上 10 位地址吗?不用——我外挂了 I2C 多路复用器 TCA9548A。把 12 个传感器分成两组,每组用同一个地址,通过复用器切换通道。你看,灵活应用才是关键。

实战注意事项:
  • 同一总线上地址冲突是灾难性的,系统可能随机无应答。上电先扫描一遍所有地址。
  • 10 位地址比较老旧,新设计尽量用 7 位地址 + 复用器方案,代码移植性好。
  • 广播地址发完后,必须等所有从机处理完(一般延时几微秒),别立刻发下一帧。
我的调试习惯:写一个地址扫描函数,把总线上所有应答的设备地址都列出来。系统初始化时跑一遍,确认没有撞地址。稳定之后再关掉这个扫描,节省开机时间。