2. 从机地址的构成:7位地址与10位地址格式、读写位(R/W)的拼接
好,咱们进入正题。I2C 通信的第一步是什么?说白了,就是主机要找到它想跟谁说话。这个“谁”,就是从机地址。
很多新手拿到 I2C 器件,第一件事就是看数据手册里的地址。但地址到底怎么构成的?7 位和 10 位有什么区别?读写位又是怎么塞进去的?这些细节搞不清楚,调试的时候就会一脸懵。
我记得刚入行那会儿,有一次调一个温度传感器,死活读不到数据。折腾了半天,才发现是我把地址左移了一位没处理对。嗯,这种坑,咱们今天一次性讲清楚。
2.1 7位地址格式
标准 I2C 总线使用 7 位地址。这 7 位由器件制造商设定,通常前几位是固定的,后几位由引脚电平决定。
举个例子,一个常见的 EEPROM 芯片,它的 7 位地址可能是 1010 A2 A1 A0。其中 1010 是固定部分,A2 A1 A0 由硬件引脚决定。你想想看,这就意味着你可以挂最多 8 个同样的芯片在同一条总线上。
我个人的习惯是,拿到一个新器件,先看数据手册里地址那一页。通常会有个表格,清清楚楚写着固定位和可配置位。
2.2 10位地址格式
7 位地址最多挂 112 个设备(去掉保留地址)。这在复杂系统里不够用。所以 I2C 规范引入了 10 位地址。
10 位地址怎么工作的?它用了两个字节来传输地址。第一个字节的前 5 位是 11110,这是 10 位地址的标志。后面跟着地址的高 2 位,再加上读写位。第二个字节是地址的低 8 位。
| 字节 | 位 7 | 位 6 | 位 5 | 位 4 | 位 3 | 位 2 | 位 1 | 位 0 |
|---|---|---|---|---|---|---|---|---|
| 第1字节 | 1 | 1 | 1 | 1 | 0 | A9 | A8 | R/W |
| 第2字节 | A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 |
你看,第一个字节的高 5 位固定是 11110,这就是 10 位地址的“身份证”。从机收到这个标志,就知道后面还有 8 位要收。
11110xx 范围是 0x78 ~ 0x7B,这些地址在 7 位模式下是保留的,所以不会冲突。
2.3 读写位(R/W)的拼接
这是最容易出错的地方。I2C 协议里,地址字节的最低位是读写位。0 表示主机要写,1 表示主机要读。
但问题来了:数据手册里给的地址,通常是 7 位地址。比如手册说地址是 0x50,这是 7 位的。但实际发送时,你要把它左移一位,然后把读写位塞到最低位。
举个例子:
- 手册地址:
0x50(二进制1010000) - 写操作:左移一位 + 0 =
0xA0(二进制10100000) - 读操作:左移一位 + 1 =
0xA1(二进制10100001)
我曾经见过一个同事,直接把手册上的地址当发送字节用,结果通信死活不通。你想想看,0x50 和 0xA0 差了一倍呢。
2.4 地址格式对比
| 特性 | 7位地址 | 10位地址 |
|---|---|---|
| 地址位数 | 7 位 | 10 位 |
| 最大设备数 | 112 (去掉保留地址) | 1024 |
| 传输字节数 | 1 字节 | 2 字节 |
| 兼容性 | 所有 I2C 设备 | 需从机支持 |
| 常见场景 | 传感器、EEPROM、ADC | 复杂系统、多设备总线 |
2.5 地址格式与读写位拼接流程图
下面这张图把整个流程串起来了。从拿到手册地址,到最终发送到总线上的数据,每一步都清清楚楚。
2.6 代码示例:地址拼接
下面这段代码展示了如何在 C 语言里处理地址拼接。我习惯用宏定义把 7 位地址和 8 位发送地址都写清楚,这样代码可读性高,也不容易出错。
/* 手册给出的 7 位从机地址 */
#define DEVICE_ADDR_7BIT 0x50
/* 左移 1 位后的基地址 */
#define DEVICE_ADDR_BASE (DEVICE_ADDR_7BIT << 1)
/* 写操作地址 */
#define DEVICE_ADDR_WRITE (DEVICE_ADDR_BASE | 0x00)
/* 读操作地址 */
#define DEVICE_ADDR_READ (DEVICE_ADDR_BASE | 0x01)
/* 发送写地址 */
I2C_Start();
I2C_SendByte(DEVICE_ADDR_WRITE); /* 发送 0xA0 */
if (I2C_GetAck() == ACK) {
/* 从机应答,继续发送数据 */
}
/* 发送读地址 */
I2C_Start();
I2C_SendByte(DEVICE_ADDR_READ); /* 发送 0xA1 */
if (I2C_GetAck() == ACK) {
/* 从机应答,开始接收数据 */
}
2.7 常见问题与避坑
- 地址冲突: 两个从机用了相同的地址。解决办法是检查硬件引脚配置,或者用地址转换器。
- 左移遗漏: 直接把 7 位地址当发送字节用。这是新手最常见的错误。
- 读写位搞反: 写操作发了读地址,读操作发了写地址。从机不会应答,或者应答了但数据不对。
- 10 位地址兼容性: 不是所有从机都支持 10 位地址。用之前一定要看数据手册。
好了,地址格式和读写位拼接就讲到这里。这些是 I2C 通信的基础,搞清楚了,后面的调试才能顺风顺水。