3. I2C 寻址机制:7位寻址与10位寻址、广播地址、保留地址与通用呼叫
好,咱们今天聊聊 I2C 的寻址。说白了,就是主设备怎么在总线上找到它想对话的那个从设备。这就像在一个大办公室里喊人,你不能光喊“喂”,得喊名字。I2C 总线上挂着一堆芯片,每个都得有个唯一的“名字”——也就是地址。
我刚开始用 I2C 的时候,总觉得寻址这事儿挺简单的,不就是发个地址嘛。后来踩了几个坑才发现,这里面的门道还真不少。尤其是当你需要挂很多设备,或者跟一些“不太规矩”的芯片打交道时,寻址机制的理解就变得至关重要了。
3.1 7位寻址:最常用的方案
7位寻址是 I2C 最基础、最常用的模式。你想想看,7个 bit,能表示 0 到 127,一共 128 个地址。但别高兴太早,这 128 个地址里,有 16 个是保留的,不能随便用。所以实际能用的,是 112 个。
在通信的时候,主设备会先发一个起始条件,然后紧跟着发送 7 位地址 + 1 位读写标志位。这 8 个 bit 组成了第一个字节。读写位是 0 表示主设备要写,是 1 表示要读。
核心要点: 7位地址 + 读写位 = 第一个字节。从设备收到后,如果地址匹配,会在第9个时钟周期拉低 SDA 线,发出应答信号 (ACK)。
我记得有一次项目,选了一个 I2C 的传感器,它的地址是 0x48。我心想这多简单,直接写地址不就完了?结果死活通信不上。后来一查数据手册,发现这个芯片的地址是 7 位的,但数据手册里给的 0x48 是左移了一位之后的!也就是说,真正的 7 位地址是 0x24,左移一位加上读写位才是 0x48 或 0x49。这个坑,我估计不少人都踩过。
我的习惯: 拿到一个新芯片,第一件事就是看数据手册里的地址说明。确认它是 7 位还是 10 位,以及数据手册里给出的地址值是否已经包含了读写位。很多厂商喜欢直接给你左移后的值,你得自己心里有数。
3.2 10位寻址:当 112 个地址不够用的时候
7位寻址虽然有 112 个可用地址,但现在的系统越来越复杂,挂十几个、二十几个 I2C 设备很常见。万一地址冲突了怎么办?这时候,10位寻址就派上用场了。
10位寻址并不是完全替代 7 位寻址,而是在同一个总线上混合使用的。它的原理是:用两个字节来传输地址。第一个字节的前 5 位是固定的,11110xx,后两位是 10 位地址的高两位。第二个字节则是剩下的 8 位地址。
你可能会问,那从设备怎么知道主设备发的是 7 位地址还是 10 位地址?答案就在第一个字节的前 5 位里。如果前 5 位是 11110,那就是 10 位地址;否则就是 7 位地址。这个设计很巧妙,对吧?
| 地址类型 | 第一个字节 (高7位) | 第一个字节 (最低位) | 后续字节 |
|---|---|---|---|
| 7位地址 | A6 A5 A4 A3 A2 A1 A0 | R/W | 无 |
| 10位地址 (写) | 1111 0 A9 A8 | 0 (写) | A7 A6 A5 A4 A3 A2 A1 A0 |
| 10位地址 (读) | 1111 0 A9 A8 | 0 (写,先发地址) | 发送完整10位地址后,再发一个起始条件 + 1111 0 A9 A8 + 1 (读) |
我个人在实际项目中,很少用到 10 位寻址。因为大部分常用的传感器、存储器、IO 扩展芯片,都提供了多个可配置的地址引脚,通过硬件拉高或拉低,就能在几个不同的地址之间切换。比如一个芯片有 3 个地址引脚,就能支持 8 个不同的地址,这通常够用了。但如果你在做复杂的系统,比如服务器主板、基站设备,那 10 位寻址就是必备技能了。
3.3 广播地址与通用呼叫
广播地址,也叫通用呼叫地址,它的值是 0x00。当主设备往这个地址发数据时,理论上所有支持通用呼叫的从设备都应该响应。
通用呼叫的用途主要有两个:
- 软件复位: 主设备发送 0x00 地址 + 写标志,然后发送一个特定的数据字节 (比如 0x06),让所有从设备执行软件复位。这在系统初始化时非常有用,可以确保所有设备都处于一个已知的起始状态。
- 硬件地址编程: 某些特殊的从设备,可以通过通用呼叫来分配一个动态地址。不过这个用法比较少见,我至今没在普通项目里遇到过。
注意: 并不是所有 I2C 设备都支持通用呼叫。很多廉价或简单的芯片,压根就不理会 0x00 地址。所以,千万别指望发一个广播复位就能搞定所有设备。我曾经在一个项目里,想用广播复位来统一初始化三个不同厂家的传感器,结果两个复位了,一个没反应,搞得我排查了半天。最后只能一个一个单独发复位命令。
3.4 保留地址:这些地址你不能碰
I2C 协议规范里,定义了一些保留地址。这些地址有特殊用途,你不能把它们分配给普通的从设备。如果你不小心用了,轻则通信异常,重则总线死锁。
下面这个表,列出了主要的保留地址:
| 地址 (7位) | 用途 | 说明 |
|---|---|---|
| 0x00 | 通用呼叫地址 | 用于广播,上面刚讲过 |
| 0x01 | 起始字节 | 用于慢速设备,很少用 |
| 0x02 - 0x07 | 保留 | CBUS 地址等,普通设备别碰 |
| 0x78 - 0x7B | 10位寻址 | 这4个地址用于10位寻址的“头” |
| 0x7C - 0x7F | 保留 | 未来扩展用,目前别用 |
你看,0x78 到 0x7B 这 4 个地址,就是专门留给 10 位寻址的。它们的二进制形式是 11110xx,正好是 10 位地址第一个字节的前 5 位。所以,你在设计系统时,一定要避开这些地址。
嗯,这里要特别提醒一下。很多初学者在选型时,看到芯片的地址是 0x7E,觉得挺顺眼,就用上了。结果发现总线老是莫名其妙地出错。为什么?因为 0x7E 属于保留地址范围 (0x7C - 0x7F),有些主控制器或者分析仪会把它当成特殊命令来处理。所以,选地址的时候,最好选 0x08 到 0x77 之间的,这是最安全的区域。
3.5 寻址机制知识图谱
为了让你更直观地理解整个寻址体系,我画了一张图。你可以把它当成一个决策树,看看主设备是怎么一步步找到目标的。
这张图把整个流程串起来了。从起始条件开始,主设备发送第一个字节。如果前5位是11110,那就走10位地址的路径,再发一个字节的地址。如果不是,那就是标准的7位地址。无论哪种路径,最终都要等待从设备的应答,然后才开始真正的数据传输。
好了,关于 I2C 的寻址机制,咱们就聊到这儿。记住,7位寻址是主流,10位寻址是扩展,广播地址和保留地址是雷区。下次你设计电路或者写驱动的时候,多留个心眼,看看地址有没有冲突,有没有踩到保留地址。这样,你的 I2C 总线才能跑得稳、跑得快。