2. I2C时序分析:起始条件、停止条件、数据有效性、字节格式与应答机制
时序,是I2C协议的命脉。很多工程师调不通I2C,说白了就是时序没吃透。我刚开始接触I2C时也吃过亏——波形看着对,从机就是不响应。后来才发现,是起始条件保持时间差了那么几十纳秒。
这一节,我们把I2C时序拆开揉碎。你搞懂了这些,后面写驱动、调bug都会顺手很多。
2.1 起始条件(S)与停止条件(P)
I2C总线空闲时,SCL和SDA都是高电平。这是前提。
起始条件(Start Condition):SCL为高电平时,SDA从高电平切换到低电平。
停止条件(Stop Condition):SCL为高电平时,SDA从低电平切换到高电平。
记住这个口诀:“高切低是起始,低切高是停止”。
关键时序参数(标准模式100kHz)
- tHD;STA(起始条件保持时间):最小4.0µs
- tSU;STA(起始条件建立时间):最小4.7µs
- tSU;STO(停止条件建立时间):最小4.0µs
- tBUF(停止与起始间总线空闲时间):最小4.7µs
我曾经遇到一个案子:从机偶尔不响应,抓波形发现起始条件保持时间只有3.2µs。手册要求4.0µs,差了0.8µs。把I2C时钟从400kHz降到100kHz,问题消失。嗯,这就是时序裕量不够的典型表现。
2.2 数据有效性
I2C的数据采样规则很简单:
- SCL高电平期间,SDA数据必须稳定
- SCL低电平期间,SDA允许变化
换句话说,从机只在SCL高电平时采样SDA。你在SCL低电平时随便折腾SDA都没事,但SCL一拉高,SDA就得稳住。
避坑指南:我曾经在STM32上用GPIO模拟I2C,SCL拉高后立刻读SDA,结果偶尔读到错误数据。后来发现是SDA电平还没稳定。加了个空指令延时(约100ns),问题解决。硬件上,SDA的上拉电阻和总线电容会形成RC延迟,SCL上升沿后需要等一小会儿再采样。
2.3 字节格式与数据传输
I2C每次传输一个字节(8位),高位在前(MSB first)。这是铁律。
一个完整的数据帧结构如下:
| 阶段 | 位数 | 说明 |
|---|---|---|
| 起始条件 | 1 | S |
| 从机地址 | 7 或 10 | 7位地址模式最常见 |
| 读写位 | 1 | 0=写,1=读 |
| 应答位 | 1 | ACK/NACK |
| 数据字节 | 8 × N | N个字节,每个后跟应答 |
| 停止条件 | 1 | P |
举个例子,主机向地址0x50的从机写一个字节0xA5:
S 0x50 0 ACK 0xA5 ACK P
其中:
S= 起始条件0x50= 7位地址左移1位(实际发送0xA0)0= 写操作ACK= 从机应答0xA5= 数据字节P= 停止条件
2.4 应答机制(ACK/NACK)
应答是I2C协议的“握手信号”。每发送完一个字节(8位),第9个SCL时钟周期用于应答。
- ACK(应答):接收方在第9个SCL时钟拉低SDA。表示“收到了,继续发”。
- NACK(非应答):接收方在第9个SCL时钟保持SDA高电平。表示“没收到/不想收/发完了”。
我个人的经验:调试I2C时,第一个要看的信号就是应答位。如果地址发送后没有ACK,90%是地址错了或者从机没上电。
实用技巧:用逻辑分析仪抓I2C波形时,把光标放在第9个SCL时钟的下降沿,看SDA电平。低=ACK,高=NACK。我习惯在代码里加个调试打印,把每次传输的ACK状态打出来,这样不用每次都接仪器。
2.5 时序综合流程图
下面这张图把I2C单字节写操作的完整时序串起来了。你对照着看,每个阶段在波形上长什么样,心里就有数了。
2.6 常见时序问题与排查
调试I2C时,我总结了一套“三板斧”排查法:
- 看起始条件:SDA下降沿是否在SCL高电平时发生?保持时间够不够?
- 看地址+ACK:地址对不对?从机有没有拉低SDA应答?
- 看停止条件:SDA上升沿是否在SCL高电平时发生?总线是否恢复空闲?
我曾经踩过的坑:用GPIO模拟I2C时,起始条件后忘了释放SDA为输入模式,导致从机无法拉低SDA应答。折腾了两小时才发现是GPIO方向没切换。嗯,细节决定成败。
还有一个常见问题:时钟拉伸(Clock Stretching)。有些从机处理数据慢,会在应答位后拉低SCL,迫使主机等待。如果你的主机不支持时钟拉伸,通信就会失败。我建议:写驱动时默认支持时钟拉伸,兼容性会好很多。
2.7 小结
这一节的内容,是I2C协议的“语法”。起始条件、停止条件、数据有效性、字节格式、应答机制——这五个概念构成了I2C通信的底层规则。你写驱动也好,调硬件也罢,最终都是在跟这五个东西打交道。