3. I2C通信流程:起始条件、停止条件、数据有效性归纳总结

好了,咱们聊聊I2C最基础的那些事儿。很多做嵌入式的人,调了一两年I2C,遇到锁死还是傻眼。说白了,就是没把通信流程的底层逻辑吃透。我个人习惯,拿到一个新芯片的数据手册,第一件事不是看寄存器,而是先画它的通信流程图——起始条件在哪、停止条件在哪、数据什么时候才算有效。这步搞明白了,后面调驱动就是填空。

3.1 起始条件(Start Condition)

起始条件长什么样?记住一句话:SCL为高电平时,SDA从高跳变到低。就这一个边沿,总线上的所有从机就知道:主人要开工了。

为什么这样设计?因为SCL高电平期间,SDA上的数据是锁定的(我们马上会讲数据有效性),所以此时跳变不会被误解为数据位。嗯,这里要注意,很多锁死故障就是起始条件没发干净导致的。

⚠️ 我曾经踩过的坑: 有一次调试一个气压传感器,板子一上电总线就挂死。用示波器抓了半天,发现主机的起始条件不是等SCL拉高后才拉低SDA的,而是在SCL上升沿过程中就变了。从机根本识别不了,直接进入不确定状态。从那以后我写启动函数,一定会先加个delay_us确保SCL稳定在高电平。

起始条件的关键参数:

  • SDA低电平保持时间:至少大于SCL低电平周期?不,标准规定tHD:STA最小是4.7μs(100kHz模式)。我建议你按最坏情况留余量。
  • 重复起始条件(Repeated Start):有时候你不想放弃总线,可以再发一次起始条件,代替停止条件。这个操作在复合读写时很常用,但也是锁死的重灾区——重复起始没发好,从机会以为你要停了。

3.2 停止条件(Stop Condition)

停止条件是起始条件的镜像:SCL为高电平时,SDA从低跳变到高。一旦出现这个边沿,所有从机都知道本次通信结束了,释放总线,准备迎接下一次。

你想想看,如果停止条件没发完整,从机一直以为总线还在占用状态,那它就不会释放SDA。这就造成了最经典的锁死场景:SDA被从机一直拉低,主机没办法再发起起始条件。

💡 我的判断法则: 看到SDA持续低电平超过一个时钟周期,而且SCL还在跳,99%是停止条件丢失了。先用示波器看停止条件有无——SCL高、SDA上升沿。如果看不到这个上升沿,或者上升沿发生在SCL低电平期间,那就属于无效停止。

停止条件的时间参数同样重要:

参数100kHz模式400kHz模式说明
tSU:STO4.0μs0.6μs停止条件建立时间(SCL高后,SDA变为高前)
tBUF4.7μs1.3μs停止到下一次起始的最小空闲时间

我曾经在一个项目里遇到奇怪现象:同一批板子,有的能工作有的不能。后来发现是停止条件的tBUF留短了,主机连续操作时,从机还没来得及复位,新的起始条件就来了。结果呢?从机认错数据,产生总线冲突,最后锁死。

3.3 数据有效性(Data Validity)

这是很多工程师容易忽略的点。I2C有个硬性规定:SCL高电平时,SDA必须保持稳定;数据采样就在SCL的上升沿、或者高电平期间。只有在SCL低电平时,SDA才能变化。

说白了,你传输一个字节的8个位,每个位都是在SCL高电平时被采样的。如果SDA在SCL高电平期间抖了一下,哪怕就几纳秒的毛刺,接收方就会采样到一个错误值,严重时会导致地址错配、ACK混乱、然后锁死。

📌 我建议: 写驱动时,在字节发送函数里,强制检查SDA在SCL高电平前后的稳定时间。可以用一个变量记录上一次SDA电平,如果发现变化,就报一个“时序违例”错误。这个习惯帮我抓到了不少因中断抢占导致的电平抖动。

数据有效性的流程可以总结如下:

  1. SCL低电平 → 发送器改变SDA电平(允许变化)
  2. SCL上升沿 → 接收器采样SDA(必须稳定)
  3. SCL高电平保持 → 继续保持SDA不变(避免第二个边沿采样)
  4. SCL下降沿 → 准备下一个位

注意,ACK位也遵循相同的规则:主机在第9个SCL时钟释放SDA(拉高),从机如果准备应答,会在SCL高电平时拉低SDA。如果从机不拉低(NACK),SDA保持高电平——此时主机可以发停止或重复起始。

3.4 三要素归纳对比

我把它们放在一起对比,方便你记忆:

要素SCL电平SDA动作发生时刻故障后果(常见)
起始条件高下降沿通信开始前SDA被持续拉低,锁死
停止条件高上升沿通信结束时从机不释放总线,锁死
数据有效性高(采样)必须不变每个时钟高电平期间数据错位、ACK混乱、锁死
⚠️ 特别提醒: 很多MCU的硬件I2C外设在出现总线锁死后,可以通过产生9个SCL时钟脉冲来让从机复位。但前提是你要先确认锁死的根因。如果是因为停止条件缺失,光发9个时钟没用——你必须产生一个正确的停止条件(GND脉冲模拟?不,是SCL高时SDA跳高)。我早期就因为这个浪费了一周时间。

3.5 通信流程全景图

下面我用一张SVG图,把起始、数据传送、停止的完整动线画出来。图中深色节点代表关键步骤,箭头指示流程方向,右侧说明框补充了每个节点的时序条件。

I2C 通信流程总图(起始 → 数据 → 停止) 起始条件 发送地址+读写位 等待ACK 数据传输 停止条件 (重复起始时回到起始条件) 起始条件 • SCL=H, SDA 下降沿 • tHD:STA ≥ 4.7μs (100kHz) • 总线从空闲变为忙碌 数据有效性 • SCL=H 时 SDA 不可变 • 在 SCL 高电平期间采样 • 每字节后跟 ACK/NACK 停止条件 • SCL=H, SDA 上升沿 • tSU:STO ≥ 4.0μs (100kHz) • 从机释放总线,回到空闲

这个图很好地概括了整个流程:起始条件发起后,主机逐字节发送地址和读写位,每个字节后从机必须给出ACK,然后继续数据传输,最后主机发出停止条件结束会话。如果中途需要重新开始(比如从写切换为读),就重复起始条件,不走停止。

在实际调试中,我习惯用逻辑分析仪同时观察SCL和SDA,重点检查起始和停止条件是否满足“SCL高、SDA跳变”的时序。只要是锁死故障,十有八九是这俩条件没发对,或者数据有效性被违反——比如SCL高时SDA发生了不该有的毛刺。把这些基础功练扎实了,后面讲锁死故障的解码技巧,你才能上手就用。