3. I2C 寻址机制:7 位寻址与 10 位寻址、广播地址、保留地址
I2C 总线上的每个从设备都有一个唯一的地址,主设备通过发送地址来选择要通信的从设备。理解 I2C 的寻址机制是驱动开发的基础,它决定了设备如何被识别、如何避免冲突以及如何处理特殊总线事件。
3.1 7 位寻址:标准模式与地址格式
7 位寻址是 I2C 最常用的寻址方式,地址范围从 0x00 到 0x7F(共 128 个地址),但其中 16 个地址被保留用于特殊用途,实际可用的从设备地址为 112 个。
7 位地址的传输格式:
- 主设备在 START 条件后发送第一个字节,高 7 位为从设备地址,最低位(LSB)为读写标志位(R/W)。
- R/W = 0:主设备写数据到从设备。
- R/W = 1:主设备从从设备读数据。
- 从设备在第 9 个 SCL 周期发送 ACK 应答。
驱动开发要点: 在 Linux 内核中,7 位地址存储在
i2c_client 结构体的 addr 字段中,驱动通过 i2c_add_driver 注册时需指定 id_table 匹配地址。
常见误区: 设备数据手册中给出的地址通常是 7 位地址(如 0x48),但在发送时需左移 1 位并加上 R/W 位。例如,地址 0x48 写操作实际发送字节为 0x90(0x48 << 1 | 0)。
3.2 10 位寻址:扩展地址空间
当系统中从设备数量超过 112 个时,I2C 规范定义了 10 位寻址模式。10 位地址范围从 0x000 到 0x3FF(共 1024 个地址),与 7 位地址兼容。
10 位地址的传输格式:
- 第一个字节: 高 5 位固定为
11110(保留标识),接着是地址的高 2 位(A9-A8),最低位为 R/W。 - 第二个字节: 地址的低 8 位(A7-A0)。
- 从设备在第一个字节和第二个字节后都需要发送 ACK。
| 字节序号 | 位 7-3 | 位 2-1 | 位 0 | 说明 |
|---|---|---|---|---|
| 字节 1 | 11110 | A9-A8 | R/W | 10 位地址标识 + 高 2 位地址 |
| 字节 2 | A7 | A6-A0 | X | 低 8 位地址(A7-A0) |
驱动兼容性注意: 并非所有 I2C 控制器硬件都支持 10 位寻址。在 Linux 内核中,通过
i2c_client 的 flags 字段设置 I2C_CLIENT_TEN 标志来启用 10 位地址模式。驱动开发时需检查控制器 quirks 标志。
3.3 广播地址与通用呼叫
广播地址(General Call Address)是 I2C 总线上的特殊地址 0x00(7 位),用于主设备向总线上所有从设备发送消息。
广播地址的两种模式:
- 模式 1(最低位为 0): 主设备发送广播地址后跟数据字节,所有支持广播的从设备必须 ACK 并接收数据。常用于系统复位、状态更新等全局操作。
- 模式 2(最低位为 1): 主设备发送广播地址后跟硬件通用呼叫(Hardware General Call),用于从设备报告自身地址。
驱动开发场景: 在 Linux 内核中,广播地址通常由
i2c-core 处理,驱动一般不需要主动处理广播消息。但某些电源管理芯片或传感器阵列会使用广播地址进行批量配置,此时驱动需注册 i2c_slave 接口监听广播。
3.4 保留地址:I2C 规范中的特殊地址
I2C 规范保留了 16 个 7 位地址(0x00 - 0x0F)用于特殊总线功能。驱动开发时必须避免将这些地址分配给普通从设备。
| 7 位地址 | 保留用途 | 说明 |
|---|---|---|
| 0x00 | 广播地址 / 通用呼叫 | 主设备向所有从设备发送消息 |
| 0x01 | 起始字节 | 用于慢速 MCU 的起始字节协议 |
| 0x02 | CBUS 地址 | 保留给 CBUS 兼容设备 |
| 0x03 | 保留 | 未来扩展用途 |
| 0x04 - 0x07 | 高速模式(Hs-mode) | 高速模式主设备代码 |
| 0x08 - 0x0B | 10 位地址标识 | 10 位地址的第一个字节前缀 |
| 0x0C - 0x0F | 保留 | 未来扩展用途 |
地址冲突风险: 某些廉价传感器芯片可能错误地使用了保留地址范围。驱动开发时,务必通过
i2cdetect 工具扫描总线,确认设备地址不在保留范围内。若发现冲突,需通过硬件地址引脚或软件配置更改设备地址。
3.5 地址分配与驱动开发实践
在 Linux 内核 I2C 驱动开发中,地址管理遵循以下最佳实践:
- 设备树(Device Tree)中指定地址: 在
dts文件中通过reg属性指定 7 位或 10 位地址。 - 地址匹配: 驱动通过
i2c_device_id或of_match_table匹配设备地址。 - 地址探测: 使用
i2c_smbus_read_byte_data等函数探测设备是否存在,避免使用i2c_probe函数(已被弃用)。 - 动态地址分配: 某些总线管理器(如 PCA954x 多路复用器)支持动态地址分配,驱动需处理
i2c_new_device或i2c_new_probed_device。
// Linux 内核 I2C 驱动地址匹配示例
static const struct i2c_device_id sensor_id[] = {
{ "temperature_sensor", 0x48 }, // 7 位地址 0x48
{ }
};
static const struct of_device_id sensor_of_match[] = {
{ .compatible = "vendor,temperature-sensor", .data = (void *)0x48 },
{ }
};
MODULE_DEVICE_TABLE(i2c, sensor_id);
MODULE_DEVICE_TABLE(of, sensor_of_match);
static int sensor_probe(struct i2c_client *client)
{
// 检查地址是否在有效范围内
if (client->addr < 0x08 || client->addr > 0x77) {
dev_err(&client->dev, "Invalid I2C address 0x%02x\n", client->addr);
return -EINVAL;
}
// 驱动初始化代码
return 0;
}
调试技巧: 使用
i2cdetect -y <bus_number> 扫描总线上的设备地址。若发现地址 0x00 或 0x01 有响应,说明总线可能存在短路或设备地址配置错误。使用 i2cget 和 i2cset 工具可以手动验证地址通信。
3.6 地址冲突检测与故障恢复
当总线上两个从设备使用相同地址时,会发生地址冲突,导致通信失败。驱动开发中需实现以下检测与恢复策略:
- 启动时地址扫描: 驱动初始化时遍历可能地址范围,检测是否有多个设备响应同一地址。
- 唯一标识读取: 通过读取设备 ID 寄存器(如
WHO_AM_I)确认设备身份,避免误匹配。 - 动态地址重映射: 某些设备支持通过 GPIO 或软件命令更改地址,驱动可在检测到冲突后重新配置。
- 总线复位: 当地址冲突导致总线锁死时,驱动需触发 9 个 SCL 时钟脉冲的复位序列。
内核实现参考: Linux 内核的
i2c-core 提供了 i2c_check_addr_busy 函数检查地址是否已被占用。驱动开发时,建议在 probe 函数中调用此函数进行地址冲突检测。