3. I2C地址与寻址:7位地址与10位地址格式、广播地址、保留地址、地址分配规则
I2C总线采用地址-响应机制实现多设备通信。每个从设备在总线上拥有唯一地址,主设备通过发送目标地址来选择通信对象。本章深入解析7位与10位地址格式、特殊地址(广播/保留)以及地址分配规则,帮助你在驱动开发中正确配置设备地址。
3.1 7位地址格式
标准I2C总线使用7位地址,这是最广泛使用的寻址模式。地址在数据传输的起始条件后立即发送,占用第一个字节的高7位,最低位(LSB)为读写方向位(R/W)。
关键结构: 第一个字节 = [A6 A5 A4 A3 A2 A1 A0] + [R/W]
R/W = 0 表示主设备写(发送数据),R/W = 1 表示主设备读(接收数据)。
R/W = 0 表示主设备写(发送数据),R/W = 1 表示主设备读(接收数据)。
驱动提示: 在Linux内核中,7位地址通常以左对齐方式存储(即地址值左移1位,最低位为0)。例如,设备地址0x48在驱动中常表示为0x90(左移1位)。使用
i2c_client 结构体时,addr 字段存储的是7位原始地址(0x48),内核会自动处理移位。
3.2 10位地址格式
当总线上设备数量超过112个(7位地址上限)时,I2C规范定义了10位地址扩展模式。10位地址占用两个字节传输,第一个字节的前5位固定为 11110 作为标识,后2位为地址的高2位;第二个字节为地址的低8位。
| 字节 | 位域 | 值/含义 |
|---|---|---|
| 字节1(高5位) | Bit[7:3] | 11110 — 10位地址标识 |
| 字节1(高2位地址) | Bit[2:1] | 地址位 A9 ~ A8 |
| 字节1(最低位) | Bit[0] | R/W 读写方向位 |
| 字节2 | Bit[7:0] | 地址位 A7 ~ A0(低8位) |
兼容性注意: 并非所有I2C从设备都支持10位地址。在Linux内核中,使用10位地址需要设置
I2C_CLIENT_TEN 标志。驱动开发时,务必查阅设备数据手册确认地址模式。
3.3 广播地址与通用呼叫
I2C规范定义了一个广播地址(General Call Address):0x00(7位)。当主设备发送地址 0x00 且R/W位为0(写操作)时,所有支持广播的从设备都会响应并接收后续数据。
- 广播地址值: 7位
0x00,第一个字节为0x00(R/W=0)。 - 用途: 主设备向所有从设备发送全局命令,如系统复位、状态查询等。
- 从设备响应: 从设备可以选择性地响应广播地址,非强制要求。
- 第二个字节含义: 广播后的第一个数据字节决定命令类型:
0x04— 从设备复位(通过硬件复位引脚)0x06— 从设备复位(通过软件命令)0x00— 不执行操作(空呼叫)
3.4 保留地址
I2C规范保留了一些特殊地址用于协议控制。这些地址不允许分配给普通从设备。以下是7位地址空间中的保留地址表:
| 7位地址 | R/W位 | 用途 | 说明 |
|---|---|---|---|
| 0x00 | 0 | 通用呼叫(广播地址) | 所有从设备可选响应 |
| 0x01 | — | 起始字节 | 用于慢速设备同步 |
| 0x02 | — | CBUS地址 | 保留给CBUS兼容设备 |
| 0x03 | — | 保留 | 未来扩展用途 |
| 0x04 ~ 0x07 | — | 高速模式(Hs-mode) | 主设备代码,用于高速模式仲裁 |
| 0x78 ~ 0x7B | — | 10位地址标识 | 高5位为11110,用于10位地址扩展 |
| 0x7C ~ 0x7F | — | 保留 | 未来扩展用途 |
地址分配红线: 普通从设备可用的7位地址范围为
0x08 ~ 0x77(共112个地址)。10位地址模式下,保留区 0x78~0x7B 被复用为扩展标识,因此10位地址不会与7位地址冲突。
3.5 地址分配规则与Linux内核实践
I2C设备地址分配遵循以下原则,驱动开发时必须严格遵守:
- 唯一性: 同一I2C总线上每个从设备地址必须唯一,地址冲突会导致通信失败。
- 避免保留区: 不得使用
0x00~0x07和0x78~0x7F范围内的地址。 - 硬件可配置: 多数I2C芯片通过引脚(如A0、A1、A2)设置地址,驱动需读取硬件配置。
- 动态检测: 内核提供
i2c_detect机制,通过发送地址并检测ACK来枚举设备。
在Linux内核中,地址配置通常在设备树(Device Tree)或板级文件中完成:
/* 设备树示例:7位地址0x48 */
&i2c1 {
status = "okay";
clock-frequency = <100000>;
temperature_sensor: tmp102@48 {
compatible = "ti,tmp102";
reg = <0x48>; /* 7位I2C地址 */
};
};
/* 内核驱动中获取地址 */
static int my_probe(struct i2c_client *client)
{
dev_info(&client->dev, "device addr: 0x%02x\n", client->addr);
/* client->addr 存储原始7位地址,无需移位 */
return 0;
}
调试技巧: 使用
i2cdetect -y <bus_number> 命令扫描总线上的设备。该命令会向所有有效地址发送读请求,有ACK响应的地址即为已连接的设备。注意:扫描可能干扰某些敏感设备,生产环境中慎用。
3.6 地址冲突与解决方案
当多个设备使用相同地址时,总线会出现地址冲突。常见场景及解决方案如下:
| 场景 | 原因 | 解决方案 |
|---|---|---|
| 同型号传感器并联 | 芯片默认地址相同 | 使用地址选择引脚(A0/A1/A2)配置不同地址 |
| 不同设备地址重叠 | 厂商未遵循地址分配规范 | 更换设备型号或使用I2C多路复用器(如PCA9548) |
| 地址被保留区占用 | 误用0x00~0x07或0x78~0x7F | 修改设备地址或更换兼容芯片 |
| 10位地址与7位地址混淆 | 驱动未正确设置地址模式标志 | 在 i2c_client 中设置 I2C_CLIENT_TEN 标志 |
驱动开发常见错误: 在
i2c_board_info 或设备树中填写地址时,容易将7位地址错误地左移1位。内核期望的是原始7位地址(如0x48),而不是左移后的0x90。如果地址错误,设备将无法被正确匹配,probe函数也不会被调用。