1. I2C协议基础回顾:物理层、协议层、起始/停止条件、数据有效性、应答机制

各位同学,咱们正式开始这门课之前,先把I2C协议的老底翻出来晒一晒。你可能会说:“I2C基础谁不会啊?” 嗯,我当年也是这么想的。直到有一次在多主模式下总线死锁,示波器抓了一整天波形,最后发现是起始条件时序不满足从机要求——从那以后,我每次调试多主系统,都会老老实实回头检查物理层和协议层的基础。

说白了,I2C就两条线:SCL(时钟线)和SDA(数据线)。但就是这两条线,玩出了花。咱们这一章把物理层、协议层、起始/停止条件、数据有效性、应答机制这五个核心点彻底讲透。

本章核心脉络:物理层决定电气特性 → 协议层决定通信规则 → 起始/停止条件决定帧边界 → 数据有效性决定采样窗口 → 应答机制决定传输可靠性。这五层缺一不可,多主模式下的所有坑,几乎都跟这五个点有关。

I2C协议基础五层结构 物理层 协议层 起始/停止条件 数据有效性 应答机制 物理层:开漏输出 + 上拉电阻 → 线与逻辑 协议层:地址 + 读写位 + 数据字节 + 应答位 → 帧结构 多主模式关键 仲裁:SDA线与逻辑 时钟同步:SCL线与逻辑 调试关注点 上升沿/下降沿时序 ACK/NACK 超时 常见故障 总线死锁 时钟拉伸异常

1.1 物理层:两条线的大学问

I2C的物理层,说白了就是开漏输出加上拉电阻。SCL和SDA都是双向的,任何设备都可以拉低它们,但没人能主动拉高——只能靠上拉电阻把电平拉回高。

这就引出了一个关键特性:线与逻辑。只要有一个设备拉低总线,总线就是低电平。所有设备都释放了,总线才回到高。你想想看,这个特性就是多主仲裁的基础。

实战经验:上拉电阻的取值很讲究。我见过太多工程师直接抄参考设计用4.7kΩ,结果在400kHz高速模式下波形惨不忍睹。标准模式(100kHz)用4.7kΩ~10kΩ没问题,快速模式(400kHz)建议用2.2kΩ~4.7kΩ,高速模式(3.4MHz)得上1kΩ左右。具体算一下总线电容和上升时间,别偷懒。

参数 标准模式 (100kHz) 快速模式 (400kHz) 高速模式 (3.4MHz)
上拉电阻典型值 4.7kΩ ~ 10kΩ 2.2kΩ ~ 4.7kΩ 1kΩ ~ 2.2kΩ
最大上升时间 1000ns 300ns 120ns
总线电容上限 400pF 400pF 100pF

注意:多主模式下,每个主机的SCL和SDA引脚必须是开漏输出,绝对不能推挽输出。我曾经遇到一个同事把GPIO配成了推挽,结果两个主机同时发送时直接短路,芯片冒烟了。嗯,这个教训挺深刻的。

1.2 协议层:帧结构是通信的骨架

I2C的协议层,核心就是帧结构。每一帧包含:起始条件 → 7位从机地址 + 读写位 → 应答位 → 数据字节 + 应答位(重复) → 停止条件。

这里有个细节:地址是7位,但实际发送时是8位——高7位是地址,最低位是读写方向(0=写,1=读)。所以你在数据手册里看到的0x50其实是7位地址左移1位后的结果,别搞混了。

帧结构速记:S + [7位地址 + R/W] + ACK + [8位数据 + ACK]×N + P

S = 起始条件,P = 停止条件,ACK = 应答位(低电平有效)

1.3 起始/停止条件:总线的“发令枪”和“终止符”

起始条件(S):SCL为高时,SDA从高切换到低。
停止条件(P):SCL为高时,SDA从低切换到高。

这两个条件有个共同点:SCL必须是高电平。为什么?因为数据采样也是在SCL高电平时进行的,起始/停止条件利用SCL高电平时的SDA变化来标识帧边界,这样就不会和数据传输混淆。

我调试时经常用示波器抓这两个条件。有一次发现从机不响应,仔细一看,起始条件中SCL还没完全变高SDA就拉低了,时序差了那么几十纳秒——从机不认。所以,起始条件的建立时间(tSU:STA)和保持时间(tHD:STA)必须严格满足。

时序参数 标准模式 快速模式
tHD:STA(起始保持时间) 4.0μs 0.6μs
tSU:STA(起始建立时间) 4.7μs 0.6μs
tSU:STO(停止建立时间) 4.0μs 0.6μs
tBUF(总线空闲时间) 4.7μs 1.3μs

1.4 数据有效性:SCL高电平时采样

I2C的数据有效性规则很简单:SCL为高电平时,SDA必须保持稳定;SCL为低电平时,SDA允许变化。

为什么这样设计?因为数据是在SCL的上升沿或高电平期间被采样的。如果SDA在SCL高电平时变化,采样值就不确定了。你想想看,如果SCL高电平时SDA抖动了一下,接收方可能采到错误位——这就是噪声导致误码的根源。

我个人的习惯是:在PCB布局时,SCL和SDA尽量走短、走平行,并且远离高频开关信号。如果总线长度超过10cm,建议加串联电阻(22Ω~33Ω)抑制过冲和振铃。

1.5 应答机制:通信的“握手”

应答机制是I2C协议中最容易被忽视但最容易出问题的环节。每个字节(9位:8位数据 + 1位应答)传输完成后,接收方必须在第9个SCL周期拉低SDA表示ACK,或者保持高表示NACK。

应答位的位置:发送方在第8个SCL周期结束后释放SDA,接收方在第9个SCL周期拉低SDA(ACK)或保持高(NACK)。

避坑指南:我曾经在一个项目中,从机因为内部忙,在应答位时没来得及拉低SDA,主机检测到NACK后直接发了停止条件。但问题是,主机没有重试机制,整个通信就断了。后来我加了一个重试逻辑:收到NACK后等待1ms再重发,最多重试3次。这个改动解决了现场80%的通信故障。

常见的应答场景:

  • 主机发送地址后:从机ACK表示存在,NACK表示无此设备或忙
  • 主机发送数据后:从机ACK表示接收成功,NACK表示不能接收更多数据
  • 主机接收数据后:主机ACK表示还要继续读,NACK表示够了,准备停止

多主模式特别提醒:仲裁过程中,失去仲裁的主机必须释放SDA和SCL,并且不能产生应答位。如果失去仲裁的主机还试图拉低SDA做ACK,就会破坏总线上其他主机的通信。我调试时遇到过这种情况,示波器上看到SDA波形异常,最后发现是软件里仲裁退出逻辑没处理好。