I2C总线基础:协议概述与信号线

I2C总线,说白了就是一根数据线加一根时钟线,让多个设备能互相聊天。我第一次接触I2C时觉得它挺神奇的——就两根线,怎么就能挂那么多设备呢?后来做项目多了才发现,这玩意儿虽然简单,但坑也不少。

I2C是飞利浦(现在的NXP)在80年代搞出来的,全称叫Inter-Integrated Circuit。它的设计初衷就是让微控制器和外围芯片之间能用最少的线通信。嗯,这个目标确实实现了。

SDA与SCL线

I2C总线就两条线:

  • SDA(Serial Data Line)—— 数据线,负责传数据
  • SCL(Serial Clock Line)—— 时钟线,负责同步节奏

这两条线都是开漏输出结构,需要外接上拉电阻。我见过不少新手在这上面栽跟头——忘了加上拉电阻,结果总线死活拉不高电平。

经验之谈:上拉电阻一般选4.7kΩ,但具体要看总线电容和通信速率。高速模式(400kHz以上)建议用2.2kΩ甚至1kΩ。我习惯在板子上预留不同阻值的焊盘,调试时换着试。

开漏结构的好处是:多个设备可以共用总线,谁想说话就把线拉低,不想说话就释放(高阻态)。这就避免了多个设备同时输出高低电平打架的问题。

起始条件与停止条件

I2C通信的开始和结束,靠的是SDA和SCL的特定时序组合:

条件 SDA状态 SCL状态 说明
起始条件(START) 高→低 高 SCL为高时,SDA从高变低
停止条件(STOP) 低→高 高 SCL为高时,SDA从低变高

我刚开始学的时候老记混,后来总结了个口诀:「起始先拉数据,停止先放时钟」。其实你想想看,起始条件是SDA先变,停止条件也是SDA先变,只不过方向相反。

关键点:起始和停止条件都是由主机(Master)产生的。从机(Slave)只能被动响应,不能主动发起或终止通信。

7位地址模式

这是最常用的地址模式。每个从设备有一个7位的地址,主机通过这7位来选择跟谁说话。

7位地址能挂多少个设备?理论上2^7=128个,但实际要留一些保留地址,所以最多112个左右。我在一个项目里挂过20多个传感器,总线负载已经很大了,再往上加就得小心信号质量。

地址的发送格式是这样的:

主机发送:起始条件 + 7位地址 + R/W位
从机响应:ACK(拉低SDA表示应答)

R/W位:
  0 = 主机写(发送数据给从机)
  1 = 主机读(从从机读取数据)

举个例子,假设一个温度传感器的7位地址是0x48(二进制1001000),主机要读取它的数据:

START + 0x48 + 1(读) → 从机ACK → 从机发送数据 → 主机NACK → STOP
注意:7位地址在发送时实际上占8位——左对齐,最低位补R/W位。所以你在数据手册里看到的地址通常是8位的,左移了一位。比如0x48实际发送时是0x90(读)或0x90(写)。我当年就被这个坑过,调了两天才发现地址写错了。

10位地址模式

7位地址不够用怎么办?I2C规范后来扩展了10位地址模式。说实话,我在实际项目中用到10位地址的情况不多,但大系统里确实有需求。

10位地址的发送分两步:

  1. 第一个字节:11110 + 地址高2位 + R/W位
  2. 第二个字节:地址低8位

格式看起来像这样:

START + 11110 + A9:A8 + R/W → 从机ACK → A7:A0 → 从机ACK → 数据...

10位地址和7位地址怎么区分?看第一个字节的高5位:如果是11110,那就是10位地址;否则就是7位地址。这个设计挺巧妙的,不会冲突。

特性 7位地址 10位地址
地址范围 0x00 ~ 0x7F 0x000 ~ 0x3FF
最大设备数 112(实际) 1024(理论)
发送字节数 1字节(含R/W) 2字节
兼容性 所有设备都支持 需要从机支持
使用频率 极高 低

I2C通信流程总览

下面这张图把I2C一次完整的读写过程画出来了。我建议你把它存下来,调试的时候对照着看。

I2C通信流程 I2C完整通信流程 主机(Master) 从机(Slave) 步骤1:主机产生起始条件(START) 步骤2:主机发送7位地址 + R/W位 步骤3:从机发送ACK应答(拉低SDA) 步骤4:主机/从机传输数据(每字节后跟ACK) 步骤5:主机产生停止条件(STOP) 注意:每次数据传输后,接收方必须发送ACK,否则通信终止

应答机制(ACK/NACK)

I2C的应答机制很有意思。每传完一个字节(8位),接收方要在第9个时钟周期拉低SDA表示应答(ACK)。如果不拉低,就是非应答(NACK)。

什么时候会出现NACK?我总结了几种情况:

  • 地址不匹配:总线上没有设备响应这个地址
  • 从机忙:正在处理上次的数据,没空理你
  • 读操作结束:主机读完数据后发NACK告诉从机「够了,别发了」
  • 从机故障:芯片坏了或者没上电
避坑指南:我曾经在一个项目里遇到从机偶尔不响应的问题。查了半天发现是电源纹波太大,导致从机复位了。从那以后,我设计I2C电路时都会在从机电源脚加个10μF电容,再串个10Ω电阻做滤波。

时钟同步与仲裁

I2C支持多主机模式,也就是多个主机可以挂在同一条总线上。但这就涉及一个问题——如果两个主机同时开始通信怎么办?

I2C的解决方案是时钟同步和仲裁:

  • 时钟同步:多个主机各自产生时钟,但SCL线是线与逻辑——谁先拉低,SCL就变低;谁最后释放,SCL才变高。结果就是所有主机的时钟被同步到最慢的那个。
  • 仲裁:多个主机同时发送数据时,谁先拉低SDA谁就赢。输的那个主机自动退出,等总线空闲后再重试。

说实话,多主机模式我在实际项目中用得很少。大部分场景都是一个MCU当主机,挂一堆传感器。但了解这个机制有助于理解I2C为什么是开漏结构——没有开漏,就没法做线与仲裁。

速率模式

I2C有几种标准速率模式:

模式 最大速率 适用场景
标准模式(Standard) 100 kHz 大多数传感器、EEPROM
快速模式(Fast) 400 kHz 需要更高吞吐量的场景
快速+模式(Fast Plus) 1 MHz 高速传感器、显示驱动
高速模式(High Speed) 3.4 MHz 视频、大容量存储

我一般默认用400kHz,够用又不至于信号质量太差。如果总线长度超过10cm,或者挂的设备超过5个,我会降到100kHz。嗯,稳定第一。

小技巧:调试I2C时,先把速率降到最低(比如10kHz),确认通信正常后再慢慢提上去。这样可以排除速率导致的信号完整性问题。我每次调新板子都这么干,省了不少时间。

好了,I2C的基础知识就讲到这里。这些概念是后面理解帧缓冲管理和DMA加速的基石。你先把这些搞明白,后面我们聊怎么用DMA来加速I2C通信,那才是真正提升性能的地方。