3. I2C地址与寻址:7位地址格式、10位地址格式、广播地址、保留地址、地址冲突检测
I2C总线采用从设备地址来区分挂在同一条总线上的多个器件。主设备在每次通信前必须先发送目标从设备的地址,并附带读写标志位。理解地址格式、特殊地址以及冲突检测机制,是编写正确I2C设备驱动的基石。
3.1 7位地址格式
7位地址是I2C最常用的地址模式,地址范围从 0x00 到 0x7F(共128个地址),但其中一部分被保留用于特殊用途,实际可用地址约为112个。
7位地址传输时序:主设备在起始条件(S)后,先发送地址字节的高7位(A6~A0),最低位为读写标志位(R/W)。
关键点: 7位地址在总线上实际占用1个字节(8位),其中高7位为地址,最低位为方向位。0表示写,1表示读。
驱动开发提示: 在Linux内核中,
struct i2c_client 的 addr 字段存储的是7位地址(左对齐,范围0~127)。设备树中通常使用 reg = <0x48>; 直接指定7位地址值。
3.2 10位地址格式
10位地址格式扩展了地址空间,允许挂接更多从设备。它使用两个字节来传输地址,第一个字节的高5位固定为 11110,作为10位地址的标志。
| 字节 | 位 | 含义 |
|---|---|---|
| 第1字节 (高8位) | Bit[7:3] = 11110 Bit[2] = A9 Bit[1] = A8 Bit[0] = R/W |
10位地址标志 + 地址高2位 + 读写方向 |
| 第2字节 (低8位) | Bit[7:0] = A7~A0 | 地址低8位 |
兼容性注意: 并非所有I2C从设备都支持10位地址。在驱动开发中,如果从设备不支持10位地址,主设备发送
11110xx 头字节时,从设备不会应答,导致通信失败。Linux内核通过 I2C_CLIENT_TEN 标志来标识10位地址设备。
3.3 广播地址与保留地址
I2C规范定义了一些特殊地址,用于总线管理和通用调用。这些地址不能分配给具体的从设备。
| 地址 (7位) | 名称 | 用途 | 驱动注意事项 |
|---|---|---|---|
0x00 |
广播地址 / 通用调用地址 | 主设备向所有从设备发送广播消息。从设备可选择性应答。 | 驱动中通常忽略广播;若需响应,需在 probe 中特殊处理。 |
0x01 |
起始字节地址 | 用于低速MCU检测总线起始条件。 | 现代I2C控制器很少使用,驱动无需处理。 |
0x02 |
CBUS地址 | 保留给CBUS兼容设备。 | Linux I2C子系统不支持CBUS协议。 |
0x03 |
保留 | 未来扩展用途。 | 驱动不应使用。 |
0x04 ~ 0x07 |
高速模式主设备代码 | 用于高速模式(Hs-mode)的地址分配。 | 标准驱动无需关注,由I2C控制器硬件处理。 |
0x78 ~ 0x7F |
保留地址 | 10位地址格式的预留范围。 | 7位模式下不可用。 |
驱动开发关键: 在注册
i2c_board_info 或设备树节点时,必须确保 reg 属性不落在保留地址范围内。Linux内核在 i2c-core-base.c 中会校验地址合法性,非法地址会导致 probe 失败。
3.4 地址冲突检测
当同一条I2C总线上挂接了多个相同地址的从设备时,会发生地址冲突。这会导致主设备无法正确寻址目标设备,通信行为不可预测。
3.4.1 冲突场景
- 硬件设计冲突: 两个相同型号的传感器使用了相同的地址引脚配置(如ADDR引脚都接GND)。
- 地址复用: 不同厂商的设备恰好使用了相同的7位地址(如某些温度传感器和加速度计都默认使用
0x48)。 - 总线扩展: 通过I2C多路复用器(如PCA9548)切换子总线时,子总线上的地址重叠。
3.4.2 检测方法
| 方法 | 操作 | 驱动实现 |
|---|---|---|
| 总线扫描 | 主设备依次发送每个地址,检查是否有ACK应答。 | 使用 i2c_smbus_read_byte() 探测;Linux提供 i2cdetect 工具。 |
| 设备ID读取 | 向疑似地址发送读ID命令,比对返回的设备ID寄存器值。 | 在 probe 函数中调用 i2c_smbus_read_word_data() 读取ID。 |
| 时序冲突检测 | 监测总线上的SCL/SDA电平异常(如多设备同时拉低SDA)。 | 由I2C控制器硬件检测,驱动通过 -EAGAIN 或 -EREMOTEIO 错误码感知。 |
驱动开发最佳实践: 在
probe 函数中,不要仅依赖地址匹配就认为设备存在。应通过读取设备ID寄存器或执行特定握手命令来二次确认设备身份,避免因地址冲突导致驱动绑定到错误的设备上。
3.5 Linux内核中的地址处理
在Linux I2C驱动框架中,地址处理涉及以下几个关键点:
/* i2c_client 结构体中的地址字段 */
struct i2c_client {
unsigned short addr; /* 7位地址 (0~127) 或 10位地址 */
unsigned short flags; /* 包含 I2C_CLIENT_TEN 等标志 */
// ... 其他字段
};
/* 设备树中指定地址示例 */
&i2c1 {
status = "okay";
clock-frequency = <100000>;
temperature_sensor: tmp102@48 {
compatible = "ti,tmp102";
reg = <0x48>; /* 7位地址 0x48 */
};
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>; /* 7位地址 0x50 */
pagesize = <8>;
};
};
/* 内核地址合法性检查 (i2c-core-base.c) */
int i2c_check_addr_validity(unsigned int addr, unsigned short flags)
{
if (flags & I2C_CLIENT_TEN) {
/* 10位地址范围: 0 ~ 1023 */
if (addr > 0x3FF)
return -EINVAL;
return 0;
}
/* 7位地址范围: 0x08 ~ 0x77 (排除保留地址) */
if (addr < 0x08 || addr > 0x77)
return -EINVAL;
return 0;
}
常见驱动错误: 初学者容易将设备树中的
reg 值写为8位地址(如 0x90 代替 0x48)。内核期望的是7位地址,而不是左移后的8位地址字节。错误配置会导致 i2c_check_addr_validity 返回 -EINVAL,设备无法被正确绑定。
3.6 本章小结
- 7位地址是最常用的格式,范围
0x08~0x77,驱动中直接使用7位值。 - 10位地址通过两个字节传输,第一个字节高5位固定为
11110,需要设置I2C_CLIENT_TEN标志。 - 广播地址 (0x00) 和 保留地址 (0x01~0x07, 0x78~0x7F) 不能分配给具体设备。
- 地址冲突会导致通信失败,驱动应通过读取设备ID等方式进行二次确认。
- Linux内核提供
i2c_check_addr_validity()进行地址合法性校验,设备树中的reg必须使用7位地址。