1. I2C协议基础:从一根线到一套规矩

各位同学,咱们今天聊聊I2C协议的基础。说实话,我刚开始接触嵌入式时,觉得I2C就是两根线拉来拉去,没啥了不起。直到有一次调一块OLED屏,死活不出画面,示波器一抓才发现——嗯,起始条件没做对。从那以后,我再也不敢小看这两根线了。

1.1 I2C总线的起源

I2C,全称Inter-Integrated Circuit,是飞利浦(现在的NXP)在1982年搞出来的。当时他们想解决一个很实际的问题:板子上芯片越来越多,每个芯片都拉一堆并行总线,PCB都快成蜘蛛网了。于是他们想——能不能只用两根线,把所有外设串起来?

结果你也看到了。到今天,几乎所有MCU、传感器、存储器、LCD驱动芯片都支持I2C。我个人习惯把I2C比作一条“公共走廊”:主设备是走廊里的管理员,从设备是各个房间,大家共用一条通道,但谁说话、什么时候说,得按规矩来。

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

I2C物理层只有两根线:SDA(串行数据线)和SCL(串行时钟线)。但这两根线的电气特性,跟普通GPIO不太一样。

1.2.1 开漏输出

SDA和SCL都是开漏输出。什么意思?说白了,芯片只能把线拉低(输出0),不能主动拉高(输出1)。想拉高?靠外部上拉电阻。

为什么这么设计?因为I2C支持多主机、多从机。如果每个芯片都能主动拉高,两个芯片同时输出不同电平,那就短路了。开漏结构保证:只要有一个芯片拉低,整条线就是低电平。这叫“线与”。

关键点:开漏输出 + 线与机制 = 多设备共享总线不打架。这是I2C物理层的核心设计思想。

1.2.2 上拉电阻

上拉电阻的取值很讲究。我见过不少新手随便焊个10kΩ上去,结果400kHz模式下波形圆得像馒头。为什么?

  • 电阻太大(比如10kΩ以上):上升沿变缓,高速通信容易出错。我在一个项目中用过47kΩ,100kHz下勉强能用,但400kHz直接废了。
  • 电阻太小(比如1kΩ以下):功耗大,而且芯片拉低时电流太大,可能损坏IO口。

一般经验值:

总线电容 标准模式(100kHz) 快速模式(400kHz)
< 100pF 4.7kΩ ~ 10kΩ 2.2kΩ ~ 4.7kΩ
100pF ~ 400pF 2.2kΩ ~ 4.7kΩ 1kΩ ~ 2.2kΩ

我的习惯:不确定时先焊4.7kΩ。示波器看上升沿,如果太缓就换2.2kΩ。别一上来就追求最小电阻,功耗和信号质量要平衡。

1.3 协议层概述:四个基本动作

I2C协议层其实不复杂。你想想看,两个人要在一根线上对话,至少需要:怎么开始说话、怎么结束、说话时怎么让对方听清、对方听没听懂。I2C用四个基本动作解决这些问题。

1.3.1 起始条件(START Condition)

SCL为高电平时,SDA从高电平跳变到低电平。这就是起始条件。

为什么要在SCL高时跳变?因为数据采样也是在SCL高时进行的。起始条件故意在SCL高时改变SDA,就是为了跟普通数据传输区分开。这是一个“特殊信号”。

// 起始条件的时序描述
SCL:  ████████████___________
SDA:  ████████___            // 在SCL高时,SDA从高变低
       ↑ 起始条件

1.3.2 停止条件(STOP Condition)

SCL为高电平时,SDA从低电平跳变到高电平。跟起始条件正好相反。

我曾经遇到一个故障:代码里漏了停止条件,结果总线一直处于“占用”状态,后续通信全部失败。示波器上一看,SDA一直低着,其他设备根本没法发起通信。

注意:起始条件和停止条件都是由主机产生的。从机不能主动发起或终止通信。

1.3.3 数据有效性

I2C的数据采样规则很简单:

  • SCL高电平时,SDA上的数据必须稳定(不能变化)。
  • SCL低电平时,SDA可以变化(准备下一个bit)。

说白了,SCL高时读数据,SCL低时换数据。每个时钟周期传输1个bit,8个时钟周期传1个字节。先传高位(MSB),再传低位(LSB)。

// 一个字节的传输过程(以0x55为例)
SCL:  ██__██__██__██__██__██__██__██__
SDA:  ██__██____████____████____████__
       ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑
       B7  B6  B5  B4  B3  B2  B1  B0
       (0) (1) (0) (1) (0) (1) (0) (1)

1.3.4 应答机制(ACK/NACK)

每传完一个字节(8个bit),接收方要在第9个时钟周期拉低SDA,表示“我收到了”。这就是应答(ACK)。如果接收方不拉低(保持高电平),就是非应答(NACK)。

应答机制是I2C的“握手信号”。我调试时经常先看ACK:如果地址发出去没收到ACK,八成是地址错了或者从机没上电。如果数据发出去没收到ACK,可能是从机忙或者不支持该操作。

信号 SDA状态(第9个SCL) 含义
ACK 低电平 接收成功,可以继续
NACK 高电平 接收失败,或传输结束

避坑指南:我曾经在写LCD驱动时,忘了在读取数据后发NACK。结果从机以为我还要继续读,一直发数据,总线乱成一团。记住:读操作最后一个字节,主机要发NACK,然后发停止条件。

1.4 知识体系总览

下面这张图把I2C协议基础的核心知识点串起来了。你可以把它当作本章的“地图”。

I2C协议基础知识体系 I2C协议基础 · 知识体系 I2C总线基础 起源与背景 物理层特性 协议层概述 应用场景 SDA/SCL 开漏输出 上拉电阻 线与机制 起始条件 停止条件 数据有效性 应答机制 LCD驱动芯片 传感器/存储器 核心:两根线(SDA/SCL)+ 四个动作(起始/停止/数据/应答) 物理层保证“不打架”,协议层保证“说得清”

这张图把I2C协议基础拆成了四个维度:起源、物理层、协议层、应用场景。物理层解决“怎么连”,协议层解决“怎么传”,应用场景告诉你“用在哪”。后面几章我们会逐个深入,尤其是用示波器抓波形时,你会反复看到这些基础元素的影子。

学习建议:别急着背时序图。先理解“为什么这么设计”。比如开漏输出是为了多设备共享,起始条件是为了区分数据。理解了动机,时序图自然就记住了。