1. I2C协议基础:物理层、协议层、时序图、寻址方式、读写操作
各位同学,咱们今天正式开讲I2C。说实话,我调试了十几年嵌入式系统,跟I2C打交道的次数比跟家人吃饭还多。这玩意儿看似简单,但坑是真不少。咱们先把地基打牢,后面调试时才不会抓瞎。
1.1 物理层:两条线怎么干活?
I2C总线就两条线:SCL(时钟线)和SDA(数据线)。嗯,你没看错,就两条。所有设备都挂在这两条线上,靠地址区分谁是谁。
核心特点:I2C是半双工通信,同一时刻只能一个方向传数据。别想着同时收发,它没这本事。
这两条线都是开漏输出结构。什么叫开漏?说白了,芯片只能把线拉低,不能主动拉高。那高电平谁给?上拉电阻。我见过不少新手把上拉电阻省了,结果总线死活拉不高,波形跟心电图似的。
| 参数 | 标准模式 (100kHz) | 快速模式 (400kHz) | 高速模式 (3.4MHz) |
|---|---|---|---|
| 上拉电阻推荐值 | 4.7kΩ ~ 10kΩ | 1.5kΩ ~ 4.7kΩ | ~1kΩ |
| 总线电容上限 | 400pF | 400pF | 100pF |
| 最大从设备数量 | 理论127个 | 理论127个 | 理论127个 |
选电阻的小窍门:我个人习惯先按4.7kΩ起步。如果总线设备多、线长,换成2.2kΩ。曾经有个项目,传感器离主控20厘米,用10kΩ死活不通,换成3.3kΩ立马好了——上升沿太慢,时序不满足。
1.2 协议层:数据怎么在线上跑?
I2C的协议层,我总结成四个字:起、止、应、传。咱们一个一个说。
1.2.1 起始条件与停止条件
SCL高电平时,SDA从高变低 → 起始条件(Start Condition)
SCL高电平时,SDA从低变高 → 停止条件(Stop Condition)
你想想看,平时总线空闲时,SCL和SDA都是高电平。主设备想说话,先拉低SDA,再拉低SCL,告诉大家"我要开始通信了"。结束的时候反过来,先释放SCL,再释放SDA。
我曾经踩过的坑:有一次调试,从设备老是没响应。用逻辑分析仪一看,起始条件之后SDA又莫名其妙跳了一下。查了半天,是主控GPIO初始化时默认输出低电平,上电瞬间产生了一个伪起始条件。从那以后,我所有I2C引脚初始化都先配成开漏高电平。
1.2.2 数据有效性
I2C的数据采样规则很简单:SCL低电平时,SDA可以变化;SCL高电平时,SDA必须稳定。
为什么这样设计?因为从设备只在SCL高电平时采样SDA。你要是SCL高的时候乱变SDA,从设备就不知道你传的是0还是1了。这个规则贯穿整个I2C通信,你写驱动时一定要保证时序满足这一点。
1.2.3 应答机制
每传完一个字节(8位),接收方必须回一个应答位(ACK)。发送方在第9个时钟周期释放SDA,接收方拉低SDA表示"收到了"。
- ACK(应答):SDA在第9个时钟被拉低 → 一切正常,继续
- NACK(非应答):SDA在第9个时钟保持高 → 出问题了,或者传输结束
我刚开始学的时候觉得这机制挺啰嗦,后来发现它太有用了。调试时只要看到NACK,就知道问题出在哪一步——是地址不对?还是从设备忙?还是数据传错了?
1.3 时序图:用眼睛看通信
光说理论不够,咱们得会看时序图。这是I2C调试的基本功。
1.4 寻址方式:怎么找到你要的设备?
I2C用7位地址寻址(也有10位地址模式,但用得少)。主设备发完起始条件后,紧跟着发7位地址 + 1位读写位。
举个例子:你买了个温度传感器,数据手册说它的7位地址是0x3C。那主设备发送的地址字节就是:
0x3C << 1 | 0 = 0x78 (写操作,R/W=0)
0x3C << 1 | 1 = 0x79 (读操作,R/W=1)
注意,地址字节是8位,高7位是设备地址,最低位是读写标志。很多新手直接拿0x3C去发,结果从设备根本不搭理你——因为0x3C左移一位才是真正的地址字节。
查地址的小技巧:我习惯用逻辑分析仪抓一次成功的通信,看地址字节是多少,然后反推7位地址。这样比看数据手册快,还能确认硬件接线对不对。
1.5 读写操作:主设备怎么跟从设备说话?
I2C的读写操作,说白了就是主设备控制节奏,从设备跟着走。
1.5.1 写操作流程
- 主设备发起始条件
- 主设备发从设备地址 + 写标志(0)
- 从设备回ACK
- 主设备发寄存器地址(告诉从设备我要写哪个寄存器)
- 从设备回ACK
- 主设备发数据字节(可以连续发多个)
- 每发一个字节,从设备回一个ACK
- 主设备发停止条件
1.5.2 读操作流程
- 主设备发起始条件
- 主设备发从设备地址 + 写标志(0)(先假装要写)
- 从设备回ACK
- 主设备发寄存器地址(告诉从设备我要读哪个寄存器)
- 从设备回ACK
- 主设备发重复起始条件(Restart,不停止直接重新开始)
- 主设备发从设备地址 + 读标志(1)
- 从设备回ACK
- 从设备发数据字节(主设备变成接收方)
- 主设备每收一个字节,回ACK表示还要继续
- 主设备收完最后一个字节,回NACK表示够了
- 主设备发停止条件
重复起始条件是个坑:我曾经在一个项目里,读传感器数据时用了停止条件再重新开始,结果传感器每次都要多等几十毫秒才能响应。换成重复起始条件后,速度直接翻倍。记住:读操作一定要用Restart,别Stop再Start。
1.6 本章小结
I2C的基础就这些:两条线、开漏输出、上拉电阻、起止条件、7位地址、ACK/NACK、读写流程。这些东西看着简单,但每一个细节都可能成为调试时的拦路虎。