1. I2C协议基础:物理层与协议层核心特性

各位同学好,我是老周。做嵌入式调试这么多年,I2C和SPI是我打交道最多的两个串行协议。今天咱们先聊I2C——这个协议看起来简单,但坑其实不少。我见过太多工程师在I2C上栽跟头,原因往往就是对物理层和协议层的基本特性理解不够透彻。

说白了,I2C就是一个两线制的串行总线。一条数据线SDA,一条时钟线SCL。就这么两根线,却能挂载几十个设备。你想想看,这背后肯定有它的设计智慧。

1.1 物理层特性:开漏输出与上拉电阻

I2C物理层最核心的特征,就是SDA和SCL都是开漏(Open-Drain)输出。什么叫开漏?简单说,就是芯片的引脚只能拉低电平,不能主动拉高。要拉高怎么办?靠外部上拉电阻。

关键理解:开漏结构让I2C总线具备了“线与”能力。任何一个设备都可以把总线拉低,多个设备同时操作也不会短路。这是I2C多主通信的基础。

我在项目中遇到过这样一个问题:一块板子上挂了5个I2C设备,通信时有时无,波形乱七八糟。查了半天,发现是上拉电阻选得太大了——4.7kΩ用在400kHz高速模式下,上升沿太慢,时序不满足要求。

上拉电阻的选择,其实是个平衡问题:

参数 影响 经验值
电阻太小 功耗大,低电平可能拉不下来 标准模式:4.7kΩ~10kΩ
电阻太大 上升沿变缓,高速通信受限 快速模式:1.5kΩ~4.7kΩ
总线电容大 需要更小的上拉电阻 高速模式:1kΩ~2.2kΩ

我的习惯:调试初期先用4.7kΩ,波形稳定后再根据实际总线电容和速率微调。别一上来就追求极致,先把通信调通再说。

另外要注意,I2C总线的最大电容负载是400pF(标准模式)。总线越长、挂的设备越多,电容越大。超过这个值,上升沿会变得很慢,通信就容易出错。

1.2 协议层特性:起始条件与停止条件

I2C协议层的第一步,就是学会“开始”和“结束”。

起始条件(Start Condition):SCL为高电平时,SDA从高电平切换到低电平。
停止条件(Stop Condition):SCL为高电平时,SDA从低电平切换到高电平。

这两个条件由主机(Master)产生。总线上所有从机(Slave)都在监听这两个信号——听到起始条件,就知道“要开始干活了”;听到停止条件,就知道“本次通信结束”。

我曾经踩过的坑:有一次调试,发现从机偶尔不响应。用示波器抓波形,发现主机发送的起始条件不完整——SDA下降沿还没稳定,SCL就开始跳变了。从机检测不到有效的起始条件,自然就不理你。后来在代码里加了延时,确保SDA先稳定再操作SCL,问题解决。

1.3 数据有效性:SCL与SDA的时序关系

I2C的数据传输,遵循一个简单规则:

  • SCL为低电平时,SDA可以变化(准备下一个数据位)
  • SCL为高电平时,SDA必须保持稳定(数据被采样)

说白了,SCL的高电平就是“采样窗口”。在这个窗口内,SDA不能乱动,否则数据就错了。

每次传输8位数据,高位在前(MSB first)。每传完一个字节,接收方要给出应答信号。

1.4 应答机制:ACK与NACK

应答机制是I2C协议里非常巧妙的设计。每传完8位数据,第9个SCL时钟周期就是应答位:

  • ACK(应答):接收方在第9个时钟周期拉低SDA,表示“收到,请继续”
  • NACK(非应答):接收方在第9个时钟周期保持SDA高电平,表示“收完了/不想收了/出错了”

这个机制让主机能实时知道从机的状态。比如:

  • 从机正在处理内部操作,没准备好接收新数据 → 发NACK
  • 从机接收完最后一个字节,不想再收了 → 发NACK
  • 主机读数据时,读到最后一个字节 → 主机主动发NACK,然后发停止条件

调试要点:如果I2C通信卡在某一步不动了,先看应答位。从机没发ACK,主机就会一直等。这时候要么是地址不对,要么是从机没准备好,要么是总线被拉死了。

下面这张图把I2C协议的核心流程串起来了:

I2C协议核心流程 起始条件 SCL高→SDA下降沿 地址+读写位 7位地址 + R/W 应答位 ACK 从机拉低SDA 数据字节 8位数据 MSB first 应答位 ACK 每字节后都有 可重复多个数据字节 停止条件 SCL高→SDA上升沿 可发起下一次通信 💡 关键点: 起始条件→地址→应答→数据→应答→...→停止条件。每个字节后都有应答,NACK表示传输结束或出错。

1.5 完整的数据传输流程

把上面这些要素串起来,一次完整的I2C通信流程是这样的:

  1. 主机发送起始条件:告诉所有从机“我要开始通信了”
  2. 主机发送从机地址+读写位:7位地址 + 1位读写标志(0=写,1=读)
  3. 匹配的从机发ACK:拉低SDA表示“我在,请继续”
  4. 主机发送/接收数据字节:每字节8位,高位在前
  5. 每字节后接收方发ACK:确认收到
  6. 主机发送停止条件:告诉所有从机“通信结束”

我的调试习惯:遇到I2C通信问题,第一步永远是抓波形。看起始条件是否完整,看地址是否正确,看应答位有没有。很多时候,问题在波形上一目了然,比看代码快多了。

嗯,I2C协议的基础就这些。物理层的开漏结构和上拉电阻决定了总线的电气特性,协议层的起始/停止条件、数据有效性和应答机制构成了通信的骨架。把这些搞清楚了,后面调试I2C设备时你就能快速定位问题。