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

地址,是I2C通信的起点。没有地址,主设备就不知道跟谁说话。我刚开始接触I2C时,觉得地址就是个简单的编号,后来踩了几个坑才明白——地址格式、保留地址、广播地址,这些细节搞不清楚,设备就是死活不通。

这一节,咱们把I2C地址的底裤扒干净。

3.1 7位地址格式:最常用的寻址方式

绝大多数I2C设备都使用7位地址。为什么?因为够用,而且省字节。

7位地址,取值范围是0x00到0x7F,一共128个地址。但别高兴太早——其中16个是保留地址,真正能用的只有112个。

地址范围 用途 说明
0x00 广播地址 所有从设备都会响应
0x01 - 0x07 保留 用于特殊总线协议
0x08 - 0x77 可用地址 普通设备地址,共112个
0x78 - 0x7F 保留 10位地址扩展、特殊用途

7位地址在总线上的传输格式是这样的:

起始位 | 7位地址 | 读写位 | 应答位 | 数据...

注意,地址字节是8位——高7位是地址,最低位是读写方向。读为1,写为0。

我的习惯:看数据手册时,先确认设备地址是7位还是10位。很多芯片标注的是"7位地址+读写位"的完整字节,比如0xA0,实际地址是0x50。我刚开始就犯过这个错,把0xA0当成了地址,结果怎么都匹配不上。

3.2 10位地址格式:什么时候用?

7位地址只有112个可用,对于复杂系统来说,不够用。比如一个大型服务器主板上,可能有几十个I2C设备,地址冲突是常有的事。

10位地址,取值范围0x000到0x3FF,共1024个地址。它通过两个字节来传输:

第一字节: 1111 0XX R/W   (XX是地址高2位)
第二字节: AAAAAAAA        (地址低8位)

第一字节的高5位固定为11110,这是10位地址的标志。后面跟着地址的高2位,然后是读写位。第二字节就是地址的低8位。

我遇到过这样一个场景:一个系统里挂了8个相同的温度传感器,每个传感器只有3个地址引脚,最多支持8个不同地址。但系统里还有其他I2C设备,地址冲突了。最后我把温度传感器换成10位地址版本,才解决了问题。

注意:不是所有I2C控制器都支持10位地址。Linux内核里,i2c_board_info结构体需要显式设置I2C_CLIENT_TEN标志。我曾经在移植驱动时忘了加这个标志,结果设备注册失败,查了两天才找到原因。

3.3 广播地址:一呼百应

广播地址是0x00。主设备发送广播地址时,所有从设备都应该应答——当然,前提是从设备支持广播。

广播地址的用途:

  • 系统复位:主设备发送广播,让所有从设备复位到初始状态
  • 固件升级:批量更新设备固件时,先广播进入升级模式
  • 状态同步:某些系统需要所有设备同时执行某个操作

但要注意,广播地址不是所有设备都支持。很多传感器芯片压根不实现广播响应。我在项目中用过一次广播复位,结果发现某个从设备没反应,后来查手册才知道它不支持广播。

关键点:使用广播地址前,务必确认所有从设备都支持。否则,不支持广播的设备会忽略这个地址,导致总线状态不一致。

3.4 保留地址:哪些不能用?

I2C规范定义了一些保留地址,这些地址不能分配给普通设备。我整理了一个表格,方便你查阅:

地址 用途 说明
0x00 广播地址 通用呼叫地址
0x01 起始字节 用于慢速设备
0x02 CBUS地址 保留给CBUS协议
0x03 保留 未来扩展
0x04 - 0x07 高速模式 Hs模式主设备地址
0x78 - 0x7F 10位地址 10位地址扩展标志

嗯,这里要注意:0x78到0x7F这8个地址,是10位地址的保留区域。如果你用7位地址的设备,千万别把地址设在这个范围内,否则会跟10位地址设备冲突。

3.5 地址冲突:实战中的坑

地址冲突是我在项目中遇到最多的问题。两个设备用了同一个地址,总线就乱了。

常见的解决方案:

  • 地址引脚:很多芯片有ADDR引脚,通过拉高拉低改变地址
  • 地址复用:使用I2C多路复用器(如PCA9548)
  • 地址转换:使用地址转换芯片(如LTC4316)
  • 更换设备:实在不行就换一个地址不同的型号

我曾经在一个项目里,用了两个相同型号的ADC芯片,地址引脚都接地了,结果地址冲突。后来我加了一个I2C多路复用器,才把问题解决。那段时间,我天天盯着逻辑分析仪看波形,眼睛都快瞎了。

避坑指南:设计电路时,提前规划好所有I2C设备的地址。画个表格,把每个设备的地址、地址引脚配置、是否支持广播都列出来。这个习惯帮我省了不少事。

3.6 地址与寻址模式知识结构

下面这张图,把I2C地址体系的核心逻辑串起来了:

I2C地址与寻址模式知识结构图 I2C地址与寻址模式 7位地址 10位地址 特殊地址 范围: 0x00 - 0x7F 可用: 112个地址 格式: 7位地址 + R/W位 范围: 0x000 - 0x3FF 可用: 1024个地址 格式: 11110XX + 8位地址 广播地址: 0x00 保留地址: 0x01-0x07 10位标志: 0x78-0x7F 核心要点 7位地址最常用,10位地址用于大型系统 广播地址和保留地址不能分配给普通设备

这张图把I2C地址体系分成了三个分支:7位地址、10位地址、特殊地址。每个分支都列出了核心参数。你写驱动时,对照这张图就能快速定位地址问题。

3.7 Linux内核中的地址处理

在Linux内核里,I2C地址的处理有固定的套路。我写驱动时,一般这样配置:

// 7位地址设备
static struct i2c_board_info my_i2c_devices[] = {
    {
        I2C_BOARD_INFO("temp_sensor", 0x48),
        .platform_data = &temp_data,
    },
};

// 10位地址设备
static struct i2c_board_info my_i2c_devices_10bit[] = {
    {
        I2C_BOARD_INFO("adc_10bit", 0x290),
        .flags = I2C_CLIENT_TEN,
        .platform_data = &adc_data,
    },
};

注意10位地址设备的flags字段,必须设置I2C_CLIENT_TEN。否则内核会按7位地址处理,导致地址不匹配。

我曾经踩过的坑:在设备树里配置10位地址时,reg属性要写两个字节。比如地址0x290,要写成reg = <0x0290>。我一开始只写了0x90,结果内核解析错误,设备死活不认。