3. I2C 地址与寻址:7位地址与10位地址格式、广播地址、保留地址
I2C 总线采用从设备地址来区分挂在同一条总线上的多个器件。主设备在每次通信开始时,都会先发送一个地址字节(或两个字节),指定要通信的从设备。理解地址格式、保留地址和广播地址,是编写正确 I2C 控制器驱动的关键前提。
3.1 7位地址格式
标准 I2C 规范使用 7 位地址,这是最广泛使用的寻址模式。地址字节的高 7 位(D7~D1)为从设备地址,最低位(D0)为读写方向位(R/W)。
- 地址范围:0x00 ~ 0x7F(共 128 个地址),但可用地址约为 112 个(保留地址除外)。
- 传输顺序:先传 MSB(最高位),后传 LSB(最低位)。
- 方向位:0 表示主设备写(发送),1 表示主设备读(接收)。
驱动要点:在 Linux I2C 核心层,7 位地址存储在
i2c_client.addr 字段中,范围 0x00~0x7F。驱动开发时需注意地址左移一位后放入地址字节的高 7 位。
常见误区:许多数据手册将 7 位地址写为 0x3C(7 位),但实际发送的地址字节是 0x78(左移 1 位 + 方向位 0)。务必区分“器件地址”与“总线字节”。
3.2 10位地址格式
为了解决 7 位地址空间不足的问题,I2C 规范引入了 10 位地址模式。10 位地址使用两个字节来传输地址信息,兼容 7 位地址总线。
| 字节 | 位域 | 说明 |
|---|---|---|
| 第1字节(高5位) | 1111 0XX R/W | 前 5 位固定为 11110,标识 10 位地址模式;XX 为地址的 A9~A8;R/W 为方向位 |
| 第2字节(低8位) | A7~A0 | 10 位地址的低 8 位 |
- 保留标识:第1字节高 5 位
11110是 10 位地址的保留前缀,不与 7 位地址冲突。 - 兼容性:10 位地址从设备可以挂在标准 7 位地址总线上,但主设备必须支持 10 位地址模式。
- Linux 内核支持:通过
I2C_CLIENT_TEN标志位来标识 10 位地址,地址值直接存储 10 位数值(0~1023)。
驱动开发注意:10 位地址的从设备在发送地址时,主设备需要发送两个字节,中间不能插入 STOP 条件。Linux I2C 核心层会自动处理地址格式转换,但控制器驱动必须正确设置
I2C_M_TEN 消息标志。
3.3 广播地址与通用呼叫
广播地址(General Call Address)是 I2C 总线中一个特殊的保留地址 0x00(7 位),用于主设备向总线上所有从设备发送消息。
| 地址 | 类型 | 行为 |
|---|---|---|
| 0x00(7位) | 广播地址 | 所有支持广播的从设备必须应答,并接收后续数据 |
| 0x04(7位) | 保留 | 用于 Hs-mode 主设备标识 |
| 0x7C(7位) | 保留 | 10 位地址前缀标识 |
- 广播数据格式:第1字节为 0x00(地址)+ 写方向,后续字节为广播数据。
- 从设备响应:从设备可以忽略广播,或根据广播数据执行复位、状态更新等操作。
- 驱动处理:Linux I2C 核心层默认不发送广播地址,驱动需要显式构造
i2c_msg并设置地址为 0。
内核实现:在
i2c-core-base.c 中,广播地址的处理通过 i2c_general_call() 函数实现。控制器驱动无需特殊处理,只需正常发送 START + 地址字节即可。
3.4 保留地址汇总
I2C 规范保留了一些特殊地址,用于总线管理、高速模式标识和未来扩展。驱动开发时禁止将这些地址分配给普通从设备。
| 7位地址 | 用途 | 说明 |
|---|---|---|
| 0x00 | 广播地址 | 通用呼叫,所有从设备可选应答 |
| 0x01 | 起始字节 | 用于慢速 MCU 检测总线起始 |
| 0x02 | CBUS 地址 | 保留给 CBUS 兼容设备 |
| 0x03~0x07 | 保留 | 用于不同总线模式标识 |
| 0x78~0x7B | 10位地址前缀 | 11110XX 格式,XX 为 A9~A8 |
| 0x7C~0x7F | 保留 | 未来扩展用途 |
驱动开发红线:在设备树或板级代码中配置从设备地址时,必须避开上述保留地址。如果硬件设计使用了保留地址,需要确认该器件是否遵循 I2C 规范的特殊协议,否则可能导致总线冲突或无法识别。
3.5 Linux 内核地址处理流程
在 Linux I2C 子系统中,地址处理分为三个层次:
- 设备层:
i2c_client.addr存储原始地址(7 位或 10 位),i2c_client.flags中的I2C_CLIENT_TEN标识地址类型。 - 消息层:
i2c_msg.addr存储地址,i2c_msg.flags中的I2C_M_TEN和I2C_M_RD控制传输方向。 - 控制器层:
master_xfer()回调函数接收i2c_msg数组,驱动需要根据地址格式构造正确的总线时序。
/* Linux I2C 消息结构体定义 */
struct i2c_msg {
__u16 addr; /* 从设备地址(7位或10位) */
__u16 flags; /* I2C_M_TEN: 10位地址标志
I2C_M_RD: 读标志
I2C_M_STOP: 传输结束后发送STOP */
__u16 len; /* 数据长度 */
__u8 *buf; /* 数据缓冲区 */
};
最佳实践:在控制器驱动的
master_xfer() 中,应通过 msg->flags & I2C_M_TEN 判断地址类型,并据此决定发送 1 字节还是 2 字节地址。对于 10 位地址,注意在发送完第1字节后不要插入重复起始条件(Sr),应连续发送第2字节。
3.6 地址冲突与调试
在实际驱动开发中,地址冲突是常见问题。以下排查步骤可帮助快速定位:
- 检查设备树:确认
reg属性中的地址值是否与数据手册一致(注意 7 位 vs 8 位表示法)。 - 使用 i2cdetect:通过
i2cdetect -y <bus>扫描总线,查看哪些地址有应答。 - 逻辑分析仪抓取:观察 START 条件后的地址字节,确认发送的地址值是否正确。
- 内核日志:开启
CONFIG_I2C_DEBUG_CORE和CONFIG_I2C_DEBUG_ALGO,查看地址传输细节。
总结:I2C 地址是总线通信的“门牌号”。7 位地址适用于大多数场景,10 位地址用于地址空间扩展,广播地址和保留地址用于特殊总线管理。在控制器驱动中,正确解析
i2c_msg 的地址标志并构造对应的总线时序,是驱动稳定性的基础。