3、I2C寻址机制:7位寻址与10位寻址、广播地址与保留地址
说到I2C的寻址,我刚开始接触时也觉得挺绕的。说白了,就是主设备怎么找到从设备,然后跟它说话。I2C总线上可以挂很多设备,每个设备都得有个唯一的“门牌号”,不然数据就乱套了。
3.1 7位寻址:最常用的方式
7位寻址是I2C最基础、最常用的方式。你想想看,7位二进制能表示0到127,一共128个地址。但实际能用的没那么多,因为有些地址被保留做特殊用途了。
在发送数据前,主设备会先发一个起始条件,然后紧跟着发送7位地址 + 1位读写位。这8位数据组合起来,就是第一个字节。我习惯把这个字节叫做“地址字节”。
地址字节格式:
Bit[7:1] = 7位从设备地址
Bit[0] = 读写位(0表示写,1表示读)
举个例子,假设你有一个温度传感器,它的7位地址是0x48(二进制1001000)。如果你想读取温度数据,主设备发送的地址字节就是:
0x48 << 1 | 0x01 = 0x91
这里左移一位是为了给读写位腾位置。嗯,这里要注意:很多新手会搞混,以为地址字节就是0x48,其实不是的,地址字节是0x90(写)或0x91(读)。
我在项目中遇到过好几次这样的问题:明明从设备地址写对了,但就是通信不上。后来一查,原来是地址字节没左移。说白了,就是忘了给读写位留位置。
3.2 10位寻址:当7位不够用时
7位地址只有128个,去掉保留地址,实际能用的也就100多个。随着I2C设备越来越多,这显然不够用了。于是就有了10位寻址。
10位寻址的地址范围是0到1023,共1024个地址。但它的传输方式比7位复杂一些。我刚开始用10位寻址时,也觉得挺别扭的。
10位寻址的地址字节分两部分发送:
- 第一个字节:高5位固定为11110,接着是10位地址的高2位,最后是读写位
- 第二个字节:10位地址的低8位
我个人习惯:除非你的项目真的需要挂很多I2C设备,否则尽量用7位寻址。10位寻址兼容性不太好,有些老设备不支持。而且代码写起来也麻烦一些。
举个例子,假设一个从设备的10位地址是0x2A3(二进制10 1010 0011):
第一个字节:11110 | 10 (地址高2位) | R/W = 1111 0100 (写) 或 1111 0101 (读)
第二个字节:1010 0011 (地址低8位)
你看,10位寻址需要两个字节才能把地址传完,而7位寻址只需要一个字节。这就是为什么10位寻址效率低一些的原因。
3.3 广播地址:一呼百应
广播地址是0x00。当主设备发送地址0x00时,所有支持广播的从设备都会响应。这有点像老师在教室里喊“全体注意”,所有学生都得听。
广播地址一般用在什么场景呢?我遇到过的情况是:系统初始化时,主设备想通知所有从设备“准备复位”或“进入低功耗模式”。这时候用广播地址,一条指令就能搞定所有设备,省事多了。
注意:不是所有从设备都支持广播地址。有些设备会忽略广播,有些设备只响应特定的广播命令。用之前一定要看数据手册。
广播地址的格式是这样的:
起始条件 + 0x00 (地址字节,写操作) + 命令字节 + ... + 停止条件
我曾经在一个项目中,想用广播地址让所有传感器同时开始采集数据。结果发现有个传感器根本不响应广播,搞得我排查了半天。后来查手册才知道,那个传感器需要单独配置才能启用广播响应。
3.4 保留地址:这些地址别乱用
I2C协议规定了一些保留地址,这些地址有特殊用途,不能分配给普通设备。我整理了一个表格,方便你查阅:
| 地址(7位) | 用途 | 说明 |
|---|---|---|
| 0x00 | 广播地址 | 所有设备响应 |
| 0x01 | 起始字节 | 用于慢速设备 |
| 0x02 | CBUS地址 | 保留给CBUS兼容设备 |
| 0x03 | 保留 | 未来扩展用 |
| 0x04 - 0x07 | 高速模式 | Hs模式主设备地址 |
| 0x78 - 0x7F | 10位寻址 | 10位地址的标识 |
你看,0x78到0x7F这8个地址是留给10位寻址用的。如果你用7位寻址,千万别把设备地址设在这个范围内,否则会跟10位寻址的设备冲突。
避坑指南:我曾经在一个项目里,把两个传感器的地址都设成了0x48。结果总线上数据乱飞,两个设备同时响应,根本没法正常工作。后来我学乖了,每次画板子前都会列一个地址分配表,确保每个设备都有唯一的地址。
3.5 地址冲突怎么办?
实际项目中,地址冲突是个常见问题。尤其是现在I2C设备这么多,很多芯片的默认地址都一样。比如常见的EEPROM芯片,默认地址经常是0x50或0x51。
解决地址冲突有几种办法:
- 硬件改地址:很多芯片有地址选择引脚(A0、A1、A2),通过拉高或拉低可以改变地址。我习惯在设计阶段就预留这些引脚的上拉/下拉电阻位置。
- 使用多路复用器:比如PCA9548这类I2C多路复用器,可以把总线分成多个通道,每个通道挂不同的设备。
- 改用10位寻址:如果设备支持,可以试试10位寻址,地址空间大得多。
嗯,这里要提醒一下:有些芯片的地址选择引脚是悬空不接的,这时候它的默认地址可能跟数据手册上写的不一样。我遇到过好几次,芯片明明设了地址,但就是不对。后来用万用表一量,发现地址引脚是浮空的。
3.6 实际调试中的地址问题
调试I2C时,地址问题是最常见的坑之一。我总结了几条经验:
- 用逻辑分析仪抓波形:别光看代码,波形不会骗人。看看起始条件后发的第一个字节是不是对的。
- 确认读写位:很多新手把读地址和写地址搞混。记住:写地址 = 设备地址 << 1,读地址 = 设备地址 << 1 | 0x01。
- 检查ACK信号:如果从设备没回应ACK,说明地址不对或者设备没上电。
- 看数据手册:有些芯片的地址是7位,但数据手册里写的是8位(已经包含了读写位)。这时候要仔细看,别直接复制。
我的小技巧:调试时先用一个简单的I2C扫描程序,把总线上所有能响应的设备地址都扫出来。这样就能确认设备是不是在线,地址对不对。我每次调试新板子,第一件事就是跑扫描程序。
好了,关于I2C寻址机制就讲这么多。说白了,7位寻址够用就用7位,不够用再考虑10位。广播地址和保留地址要记住,别踩坑。下一章我们聊聊I2C的时序和速率控制,那才是真正考验功底的地方。