3. I2C 寻址模式:7位寻址与10位寻址,保留地址详解
说到 I2C 的寻址,很多人第一反应就是“7位地址嘛,简单”。嗯,确实简单。但你真的搞懂过那 112 个可用地址是怎么算出来的吗?10 位寻址又是怎么和 7 位共存的?还有那些“保留地址”——我敢说,至少有一半的工程师在调试时被这些保留地址坑过。
这一节,咱们就把寻址这件事彻底掰扯清楚。
3.1 7位寻址:基础中的基础
I2C 总线上的每个从设备,都有一个唯一的地址。标准模式下,这个地址是 7 位的。但注意——这 7 位并不是全部都能用。
我们来看地址的构成:
- 地址位:7 位,范围 0x00 ~ 0x7F
- 读写位:第 8 位,0 表示写,1 表示读
- 实际可用地址:112 个(0x08 ~ 0x77)
为什么是 112 个?因为 0x00 ~ 0x07 和 0x78 ~ 0x7F 被保留用于特殊用途。我刚开始做 I2C 时,就犯过一个低级错误——给设备配了 0x00 地址,结果总线死活没响应。查了半天规格书才发现,0x00 是通用呼叫地址,不能用作普通从机地址。
7位地址帧格式:
起始条件 → 7位从机地址 → 读写位(R/W) → 从机应答位(ACK) → 数据... → 停止条件
发送地址时,主机会把 7 位地址左移一位,最低位填 R/W。比如从机地址是 0x32(二进制 011 0010),写操作时发送 0x64(0110 0100),读操作时发送 0x65(0110 0101)。
这个细节,示波器上看得最清楚。我习惯在抓波形时,先看第一个字节的高 7 位是不是我预期的地址,再看最低位是 0 还是 1。很多新手抓了波形不会看,其实就是没搞懂这个移位关系。
3.2 10位寻址:扩展的艺术
7 位地址只有 112 个可用,这在复杂系统里显然不够。所以 I2C 规范引入了 10 位寻址。你可能会问:“10 位寻址和 7 位怎么区分?”
答案就在第一个字节里。
10 位寻址的地址帧分两阶段发送:
- 第一个字节:11110XX + R/W,其中 XX 是 10 位地址的高 2 位
- 第二个字节:10 位地址的低 8 位
关键点来了:11110 这个前缀,正好落在保留地址 0x78 ~ 0x7B 的范围内。也就是说,10 位寻址“借用”了 7 位寻址的保留地址空间来做标识。
| 地址类型 | 第一个字节 | 说明 |
|---|---|---|
| 7位寻址 | 0x00 ~ 0x7F | 正常地址范围 |
| 10位寻址(写) | 11110XX + 0 | XX 为地址高 2 位 |
| 10位寻址(读) | 11110XX + 1 | XX 为地址高 2 位 |
| 保留地址 | 0x00 ~ 0x07, 0x78 ~ 0x7F | 特殊用途 |
我在一个项目里用过 10 位寻址——系统里挂了 16 个同样的传感器,7 位地址根本不够用。当时查了不少资料才搞清楚这个前缀机制。说实话,日常调试中 10 位寻址用得不多,但一旦遇到,你必须知道怎么处理。
实战技巧:用逻辑分析仪抓 10 位寻址时,别只看第一个字节。很多分析仪默认只显示 7 位地址,你得手动设置成 10 位模式,否则地址会解析错误。
3.3 保留地址详解
保留地址是 I2C 协议里最容易踩坑的地方。我见过有人把 0x00 配给从机,也见过有人用 0x7F 做设备地址——结果都是通信失败。
下面这张表,建议你收藏:
| 地址(7位) | 用途 | 说明 |
|---|---|---|
| 0x00 | 通用呼叫地址 | 广播到所有从机 |
| 0x01 | 起始字节 | 用于慢速设备 |
| 0x02 | CBUS 地址 | 保留给其他总线 |
| 0x03 ~ 0x07 | 保留 | 未来扩展 |
| 0x78 ~ 0x7B | 10位寻址前缀 | 11110XX |
| 0x7C ~ 0x7F | 保留 | 未来扩展 |
这里重点说一下 通用呼叫地址(0x00)。主机发送 0x00 时,所有支持通用呼叫的从机都会响应。这个功能常用于系统同步或软件复位。但注意——不是所有从机都支持通用呼叫,而且即使支持,你也可以通过配置关闭它。
我曾经在一个项目里,因为从机芯片默认开启了通用呼叫响应,导致主机每次发送 0x00 做其他用途时,所有从机都乱响应。排查了两天才找到原因。嗯,从那以后,我每次选型 I2C 芯片,都会仔细看 datasheet 里关于通用呼叫的说明。
避坑指南:设计 PCB 时,如果多个从机地址冲突,别想着用保留地址来“绕过去”。保留地址是协议层面的硬限制,硬件上改不了。老老实实用地址转换器或者换不同地址的芯片型号。
3.4 寻址模式对比与选择
7 位还是 10 位?这不是一个选择题,而是一个场景题。
- 7位寻址:简单、兼容性好、大多数芯片都支持。如果你的系统里从机数量不超过 10 个,用 7 位就够了。
- 10位寻址:地址空间大,但实现复杂。需要主从双方都支持,而且通信效率略低(多传一个字节)。
我个人习惯是:能用 7 位绝不用 10 位。原因很简单——调试工具对 10 位寻址的支持参差不齐。有些逻辑分析仪解析 10 位地址时会有 bug,你抓到的波形看起来是对的,但软件解析出来的地址就是不对。
下面这张图,帮你理清寻址的整体逻辑:
3.5 调试中的地址问题
最后,分享几个我在调试中遇到的地址相关的问题:
- 地址冲突:两个从机用了同一个地址。解决办法——检查 datasheet,看有没有地址引脚可以配置。如果没有,就得用 I2C 多路复用器(如 PCA9548)。
- 地址移位搞错:写驱动时,有人直接把 7 位地址塞进第一个字节,忘了左移。结果总线上的地址全错了。我习惯在代码里用
(addr << 1) | rw这种写法,一目了然。 - 保留地址误用:这个前面说过了,0x00 和 0x78 ~ 0x7F 千万别碰。
快速检查方法:用示波器抓 SDA 线上的第一个字节,手动算一下地址。比如抓到 0x64,右移一位得 0x32,最低位是 0 表示写操作。这个方法虽然土,但最可靠。
寻址模式这块,说白了就是一张表、一个移位、几个保留地址。搞懂了这些,I2C 通信的第一步就算走稳了。