I2C 寻址与读写操作:7位/10位地址、读写位、数据帧结构
咱们做嵌入式开发,天天跟I2C打交道。地址搞错了,总线就哑火。今天我就把这块掰开了揉碎了讲清楚。
一、7位地址:最常用的寻址方式
市面上95%的I2C器件都用7位地址。说白了,就是主机在总线上喊一声:“谁是0x3C?” 从机听到是自己的号,就回一声“到!”。
7位地址的范围是0x00到0x7F。但实际能用多少?嗯,这得看器件手册。比如常用的AT24C02 EEPROM,它的地址是0x50(7位)。但我记得有一次调试一个温湿度传感器,手册上写地址0x40,结果逻辑分析仪抓出来是0x80——少了左移一位!这是个经典坑。
| 7位地址值 | 左移1位(8位写地址) | 左移1位+1(8位读地址) |
|---|---|---|
| 0x50 | 0xA0 | 0xA1 |
| 0x3C | 0x78 | 0x79 |
| 0x40 | 0x80 | 0x81 |
二、10位地址:扩展的玩法
7位地址最多挂128个设备,够不够?项目中我遇到过挂几十个传感器的情况,地址冲突那是家常便饭。这时候10位地址就派上用场了。
10位地址怎么发?其实它由两个阶段组成:
- 第一个字节:11110xx + R/W,其中xx是10位地址的高2位。
- 第二个字节:剩下的8位地址。
举个例子:10位地址0x2A6(二进制:10 1010 0110)
- 高2位是10,所以第一个字节是 1111 0100 + R/W(写为0)= 0xF4
- 低8位是 1010 0110 = 0xA6
逻辑分析仪上抓到的是:0xF4 0xA6 这样一个两字节的头。你想想看,普通7位地址只用1个字节头,10位地址需要2个,时序上自然慢一些。
三、读写位:方向的决定者
地址字节的最低位就是读写位。0表示写操作,1表示读操作。这个再简单不过了,但有件事我必须提醒:很多初学者在逻辑分析仪上看到地址0xA0,以为是0xA0号设备,其实人家是0x50(7位)+ 写位0!
四、数据帧结构:一帧一帧看透它
I2C通信就是一串帧的串联。每个完整的读写操作包含:
| 步骤 | 描述 | SDA/SCL状态 |
|---|---|---|
| Start | SCL高时SDA从高变低 | 始发信号 |
| 从机地址 | 7位或10位地址 | 主机驱动SDA |
| R/W位 | 0写 1读 | 主机驱动 |
| ACK位 | 从机拉低SDA表示应答 | 从机驱动 |
| 数据字节 | 8位数据,MSB在前 | 主机或从机驱动 |
| ACK位 | 每字节后跟一个应答 | 从机或主机驱动 |
| Stop | SCL高时SDA从低变高 | 终止信号 |
用逻辑分析仪看,其实就几根线在那跳来跳去。抓一个完整的写操作,你会看到 Start → 地址+R/W → ACK → 数据 → ACK → Stop 这样一个流程。
// 伪代码:逻辑分析仪解析出的写操作波形
// 起始条件:SCL高时SDA下降沿
// 地址:0x50 (7位) + 写位0 = 0xA0
// 从机应答:SDA被拉低1个时钟
// 数据:0x00 (寄存器地址) + ACK
// 数据:0xFF (写数据) + ACK
// 停止条件:SCL高时SDA上升沿
核心要点: 每个字节后必跟一个ACK/NACK。如果从机不拉低SDA(即SDA保持高),就是NACK。这是排查应答失败的关键信号。
五、知识结构总览
下面这张图帮你理清I2C寻址与读写的数据流向:
看到没?从Start到Stop是一条完整的链路。我一般在逻辑分析仪上就是按这个顺序逐帧检查的。哪一帧没ACK,问题就出在哪。
六、避坑指南
- 地址左移的坑: 我曾经一个项目里,新手直接把7位地址0x3C当8位用。结果主机发的0x3C对应7位地址的0x1E+写位。总线不认是必然的。
- 10位地址格式: 别把第一个字节的高位搞错。11110是固定前缀,后面跟地址高2位。我见过有人在代码里写0xF0作为保留字节,结果地址对不上。
- 读写位混淆: 写操作发0xA0,读操作发0xA1。很多人把0xA0当成了设备地址,忘了最后一位是方向。逻辑分析仪上一看,地址没问题,但方向不对。
- 多字节读写: 连续读写时,每字节后都有ACK。我记得有一次项目里,从机在连续写时突然返回NACK,查了半天才发现是地址指针超出范围了。
讲到这里,I2C的地址和数据帧结构你应该清楚了。下次用逻辑分析仪抓总线,对着这个结构一帧一帧对,保证问题一秒定位。