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

各位同学好,我是老周。咱们这门课叫《I2C协议与SPI外设驱动CRC校验调试与故障定位实战》,第一讲,得先把I2C的底子打牢。

I2C协议,说白了就是两根线搞定一堆设备通信。我刚开始做嵌入式那会儿,觉得这东西简单,结果调了三天没调通——后来发现是上拉电阻没焊。嗯,这种坑,咱们今天先讲清楚原理,后面再慢慢聊实战。

1.1 物理层:就两根线,但别小看它

I2C总线只有两条线:

  • SCL(串行时钟线)——由主设备驱动,控制通信节奏
  • SDA(串行数据线)——双向传输数据

这两根线都是开漏输出结构。什么叫开漏?就是芯片只能把线拉低,不能主动拉高。高电平靠外部上拉电阻实现。

关键参数速查表

参数标准模式快速模式高速模式
最大时钟频率100 kHz400 kHz3.4 MHz
上拉电阻典型值4.7 kΩ2.2 kΩ1.0 kΩ
总线电容上限400 pF400 pF100 pF
最大从设备数量127(7位地址)127127

个人经验:我习惯在调试板上预留两个上拉电阻位置,一个4.7kΩ,一个2.2kΩ。总线长了换小的,总线短了换大的。省得改板子。

1.2 协议层:谁说话,谁听话

I2C总线上有两种角色:

  • 主设备(Master)——发起通信、产生时钟、决定何时开始何时结束
  • 从设备(Slave)——监听总线、响应主设备的请求

每个从设备都有一个唯一的地址。7位地址模式最多挂127个设备,10位地址模式可以更多,但实际项目中7位地址够用了。

我记得有一次,一个同事把两个传感器的地址设成一样的,结果总线上一片混乱。嗯,地址冲突这种低级错误,排查起来真能让人抓狂。

1.3 起始条件与停止条件

这是I2C通信的“开关”。没有起始条件,总线上的设备都在睡觉;没有停止条件,总线不知道这次通信结束了。

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

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

避坑指南:我曾经遇到过一个问题——MCU复位后,SDA线被拉死了。查了半天,发现是从设备在通信中途掉电,总线停在中间状态,没有收到停止条件。解决办法:主设备在初始化时先发一个伪停止条件,把总线“复位”一下。

// 伪停止条件示例:强制释放总线
void i2c_bus_reset(void) {
    // 先让SCL和SDA都变成高
    SCL_HIGH();
    SDA_HIGH();
    delay_us(5);
    // 模拟一个停止条件
    SDA_LOW();
    delay_us(5);
    SCL_LOW();
    delay_us(5);
    SCL_HIGH();
    delay_us(5);
    SDA_HIGH();
    delay_us(5);
}

1.4 数据有效性:时钟低时变,时钟高时读

这个规则很简单,但很多人一开始会搞反:

  • SCL为低电平时,SDA上的数据可以变化
  • SCL为高电平时,SDA上的数据必须稳定,此时接收方采样数据

你想想看,如果SCL高的时候SDA还在跳变,那接收方采到的数据到底是0还是1?没法确定。所以这个规则是I2C通信的基石。

数据有效性规则图解

I2C数据有效性时序 SCL SDA 数据变化区 数据采样区 SCL低→SDA可变化 SCL高→SDA必须稳定

1.5 应答机制:每发一个字节,都要听回音

I2C的应答机制是它可靠性的关键。每传输完一个字节(8位),接收方必须回复一个应答位(ACK)或非应答位(NACK)。

应答流程:

  1. 发送方释放SDA总线(拉高)
  2. 发送方产生第9个SCL时钟脉冲
  3. 接收方在SCL高电平期间拉低SDA(ACK)或保持高电平(NACK)

我个人的习惯:写驱动时,每次发送完地址字节后,一定要检查ACK。如果没收到ACK,直接报错返回,不要继续发数据。我曾经见过一个驱动,没检查ACK就往下发,结果从设备根本没在线,总线一直卡死。

ACK vs NACK 的含义:

场景应答位含义
主设备发送地址后ACK从设备存在且准备好通信
主设备发送地址后NACK从设备不存在或忙
主设备发送数据后ACK从设备已接收,可以继续发
主设备发送数据后NACK从设备不想收了(比如缓冲区满)
从设备发送数据后ACK主设备还想继续读
从设备发送数据后NACK主设备说够了,停止读

曾经踩过的坑:有一次调试温湿度传感器,读出来的数据全是0xFF。用逻辑分析仪一看,从设备发了NACK,但主设备没处理,继续读下一个字节。结果读到的都是总线上的高电平(0xFF)。从那以后,我写驱动必加ACK超时检测。

1.6 完整通信流程:一个字节的旅程

把上面这些知识点串起来,一次完整的I2C写操作是这样的:

  1. 主设备发送起始条件
  2. 主设备发送7位从设备地址 + 写位(0)
  3. 等待从设备回复ACK
  4. 主设备发送寄存器地址(1字节)
  5. 等待从设备回复ACK
  6. 主设备发送数据字节
  7. 等待从设备回复ACK
  8. 主设备发送停止条件

读操作稍微复杂一点,需要先写寄存器地址,再重新发起始条件,然后读数据。这个咱们后面讲实战的时候再细说。

本章知识结构

I2C协议基础知识结构 I2C协议基础 物理层(2线+上拉) 协议层(主/从) 起始/停止条件 数据有效性 SCL/SDA开漏 7位/10位地址 SDA边沿触发 SCL高采样 应答机制:每字节后ACK/NACK