3. I2C协议深度解析:从时序到仲裁
I2C协议,说白了就是两根线(SCL时钟、SDA数据)搞定多个设备通信。很多新手觉得它简单,但实际项目中翻车最多的恰恰是这些“基础”细节。我做了十几年嵌入式,见过太多因为时序踩坑的案例——嗯,今天咱们就把I2C的底裤扒干净。
3.1 总线时序:SCL与SDA的舞蹈
I2C是同步串行协议,SCL提供时钟,SDA传输数据。标准模式下最高100kHz,快速模式400kHz,高速模式能到3.4MHz。但注意——速度越快,抗干扰能力越差。我在工业现场项目中,经常被迫降速到50kHz以下。
| 参数 | 标准模式 | 快速模式 | 说明 |
|---|---|---|---|
| SCL时钟频率 | 100 kHz | 400 kHz | 实际可用更低 |
| 上升时间 tr | ≤ 1000 ns | ≤ 300 ns | 受总线电容影响 |
| 下降时间 tf | ≤ 300 ns | ≤ 300 ns | 开漏输出决定 |
| 数据建立时间 tsu;DAT | ≥ 250 ns | ≥ 100 ns | SDA在SCL低变高前稳定 |
| 数据保持时间 thd;DAT | ≥ 0 ns | ≥ 0 ns | SCL低变高后保持 |
我曾经在一个项目中,因为PCB走线过长导致总线电容过大,上升时间超标。从示波器上看,SCL的上升沿变成了圆弧——从机根本识别不了时钟。最后只能降速+加驱动芯片解决。
3.2 起始条件与停止条件
这两个条件是I2C通信的“开关”。
- 起始条件(S):SCL高电平时,SDA从高变低。表示总线开始传输。
- 停止条件(P):SCL高电平时,SDA从低变高。表示总线释放。
3.3 数据有效性:时钟的“眼睛”
I2C的数据采样规则很简单:SCL高电平时读取SDA,SCL低电平时SDA可以变化。但就是这个简单的规则,在长距离传输时容易出问题。
你想想看,如果SCL线上有毛刺,从机可能在错误时刻采样SDA。我建议在硬件上做两点:
- SCL和SDA各加一个RC低通滤波器(R=100Ω,C=100pF),滤除高频噪声。
- 软件上做“三次采样”逻辑——连续读到三个相同电平才确认有效。
3.4 应答机制:ACK与NACK
每传输完一个字节(8位),接收方必须在第9个SCL周期拉低SDA表示应答(ACK),否则表示非应答(NACK)。
这里有个常见的坑:主控在发送完数据后,必须释放SDA线(设为输入),等待从机拉低。我见过有人把SDA一直设为推挽输出,结果从机根本拉不动——通信直接挂掉。
3.5 时钟同步与仲裁
这两个机制是I2C多主机通信的核心。虽然大部分项目只用单主机,但理解它们对排查总线冲突很有帮助。
时钟同步
多个主机同时驱动SCL时,谁先拉低SCL,谁就“赢”了。所有主机都会同步到最慢的那个时钟。说白了,就是“慢者通吃”。
仲裁
多个主机同时发送数据时,谁先输出低电平,谁就赢得总线。仲裁发生在SDA线上,且不会丢失数据。
我记得有个项目需要两个MCU通过I2C交换数据,用了多主机模式。第一次上电时,两个主机同时发送,结果总线一直处于低电平——死锁了。后来发现是仲裁逻辑没处理好,其中一个主机在失去仲裁后没有及时释放总线。
3.6 实战中的抗干扰设计要点
基于以上协议特性,我在实际项目中总结了几条抗干扰设计原则:
- 上拉电阻选择:标准模式用4.7kΩ,快速模式用2.2kΩ。总线电容大时用更小的电阻,但注意功耗。
- 加TVS管:在SCL和SDA对地各加一个5V的TVS管,防止静电和浪涌。
- 软件滤波:在I2C中断服务函数中,对SCL和SDA做去抖处理——连续采样3次一致才确认电平变化。
- 超时复位:如果SCL低电平超过10ms,强制复位I2C外设。这是防止总线死锁的最后防线。