I2C 协议基础

做嵌入式这些年,跟 I2C 打交道的次数多到数不清。从传感器读温度、写 EEPROM 配置、跟 ADC 通信,几乎每个板子都离不开它。想把 DMA 和 I2C 搭起来做高速采集,第一步就得把 I2C 的那些基础细节摸透。说白了,I2C 就两条线——一条时钟线 SCL,一条数据线 SDA,所有通信全靠这两根线折腾。

总线结构——两条线搞定一切

I2C 总线只有两根线:串行时钟线 SCL 和串行数据线 SDA。每个设备都挂在上面,主设备产生时钟,从设备响应数据。为什么能做到只用两根线?因为 I2C 是 开漏输出 + 上拉电阻 的结构。

  • SDA:数据线,双向传输地址和数据。
  • SCL:时钟线,由主设备驱动,决定传输速率。
  • 上拉电阻:一般 4.7kΩ 或 10kΩ,保证总线空闲时被拉高到 VCC。
  • 总线电容:线长、设备多会导致电容增大,影响上升沿时间。

我记得有个项目,板子上的 I2C 总线挂了 8 个从设备,线长接近 30cm。一开始上拉电阻用了 10kΩ,结果波形惨不忍睹,SCL 上升沿都快成三角波了。后来换成 2.2kΩ,才把通信稳定下来。嗯,这里要注意:上拉电阻不是随便选的,总线电容越大,电阻就得越小,否则信号爬不上去。

常用上拉电阻选择参考
总线电容(pF)推荐电阻(kΩ)典型场景
50 – 1004.7 – 10短距离、少设备
100 – 2002.2 – 4.7中等长度、4-8 个设备
200 – 4001.0 – 2.2长线缆、多设备或高电容负载

主从模式——谁说了算

I2C 通信里,主设备(Master)负责产生时钟、发起通信、控制传输方向。从设备(Slave)只能被动响应,不能主动发起。一个总线上可以有多个主设备,但同一时刻只能有一个主设备控制总线。多主冲突时,靠仲裁机制决定谁赢。

我个人习惯把主设备想象成“项目经理”,从设备是“干活的人”。项目经理安排任务(发起起始条件)、说清楚找谁(发送从机地址)、告诉对方要读还是写(R/W 位),然后等着对方回答(应答位)。你觉得麻烦?其实习惯就好了,尤其是用 DMA 接管后,你连这些时序都不用操心。

我曾经踩过一个坑:某次用两个主设备共享一条 I2C 总线,一个主设备写 EEPROM,另一个主设备读温度传感器。逻辑上没问题,但时序没处理好多主仲裁,导致数据错乱。最后加了一个软件互斥锁,才解决冲突。所以,除非你有足够的经验,否则新手阶段建议让一个主设备独占总线。

起始与停止条件——通信的开端和结束

I2C 的数据传输以 起始条件 开始,以 停止条件 结束。这两个条件由主设备产生,所有从设备都能感知到。

  • 起始条件 (S):SCL 高电平时,SDA 从高切换到低。
  • 停止条件 (P):SCL 高电平时,SDA 从低切换到高。
  • 总线空闲时,SCL 和 SDA 都是高电平。

你想想看,为什么起始条件要这样设计?因为 SCL 高电平期间 SDA 的变化被定义为“控制信号”,而不是数据。这样从设备就能区分是“开始通信”还是“传输数据”。

数据帧格式——地址、读写、应答、数据一气呵成

一个完整的 I2C 数据帧结构大致长这样:

  1. 起始条件
  2. 7 位从机地址(或 10 位地址模式)
  3. 读/写位(1 = 读,0 = 写)
  4. 从设备应答位(ACK / NACK)
  5. 8 位数据字节 + 应答位(重复 1~N 次)
  6. 停止条件

做数据采集时最常用的是 7 位地址模式,因为大多数传感器和 ADC 芯片都支持。记住:地址左移 1 位,最后 1 位是 R/W。比如地址 0x48,写操作时发送 0x90,读操作时发送 0x91。

核心要点:数据帧中,每个字节后的应答位是从设备给的。如果从设备忙或者没收到,就会拉低 SDA(ACK)或不拉低(NACK)。主机必须等待这个应答位,否则会丢数据。——我在搞 DMA 时,这部分最容易出问题,因为 DMA 不会自动重试,NACK 需要软件处理。

为什么 DMA 要和 I2C 配合?先看一个典型时序

下面这张图展示了 I2C 一次完整的数据传输流程。记住这个流程,后面讲 DMA 接管时你就知道哪些步骤可以被硬件直接完成。

I2C 完整数据帧(写操作示例) 起始条件 7位地址 + R/W ACK 数据字节 ACK 停止条件 每个字节(地址或数据)后从设备必须回应 ACK,否则主机应停止或重试 SCL 波形示意: (省略具体时钟脉冲,参考标准 I2C 时序图)

小技巧:调试 I2C 时,用逻辑分析仪抓 SCL 和 SDA 波形,比看代码容易定位问题。我之前有个间歇性通信失败,看了半天代码没找到原因,一抓波形发现从设备有时会漏掉地址,原来是上拉电阻偏弱导致上升沿太慢。

关键参数:I2C 速率模式

I2C 有几种标准速率模式,做数据采集时最常用的是标准模式(100kHz)和快速模式(400kHz)。高速模式(3.4MHz)对硬件要求高,一般板级走线需要特别注意。DMA 配合 I2C 做高速采集,通常跑快速模式或者快速模式+(1MHz)。

I2C 速率模式一览
模式最大速率典型应用注意事项
标准模式100 kHzEEPROM、温度传感器兼容性好,几乎所有 MCU 都支持
快速模式400 kHz加速度计、ADC、部分显示设备需注意上升时间 ≤ 300ns
快速模式+1 MHz高速 ADC、图像传感器上拉电阻需更小(1kΩ~2kΩ)

注意:I2C 的速率不是想跑多快就多快。总线电容、上拉电阻、从设备自身限制都会影响实际工作速率。我曾经在快速模式+下遇到从设备偶尔不响应,查完数据手册发现该芯片只支持到 400kHz,白忙活一场。所以开始前先确认所有设备的规格上限。

避坑指南:一个容易被忽略的细节——时钟拉伸

I2C 协议允许从设备在应答时将 SCL 拉低,迫使主设备等待,这叫时钟拉伸。有些从设备处理数据需要时间,比如 EEPROM 内部写操作,就会用时钟拉伸来告诉主机“等一下”。如果你的 DMA 控制器没有处理时钟拉伸的逻辑,就可能提前发出下一个字节,导致数据错位。

我之前做一个高速数据采集系统,用的 DMA + I2C 读 24 位 ADC。ADC 每次转换完成后需要读取结果,但它会拉低 SCL 约 10μs 来准备数据。我的 MCU 的 I2C 外设支持时钟拉伸,但 DMA 配置时忘了使能“NACK 自动重试”功能,结果丢了好几个采样点。后来在 DMA 中断里加了超时重试的代码才解决。所以,选 MCU 时建议优先选 I2C 外设硬件支持时钟拉伸的型号,省掉很多麻烦。

小结

I2C 看似简单,但真正做高速采集时,细节决定成败。总结一下本章的核心:

  • 总线结构:两条开漏线 + 上拉电阻,多设备共享。
  • 主从模式:主设备主导,从设备响应,多主需要仲裁。
  • 起始/停止条件:SCL 高电平时 SDA 的下降沿/上升沿。
  • 数据帧格式:起始 + 地址 + R/W + ACK + 数据 + ACK + 停止。
  • 时钟拉伸:从设备主动拉低 SCL 要求等待,DMA 设计时必须考虑。

把这些基础吃透了,后面讲 DMA 配 I2C 的高速采集实战时,你才能一眼看出哪些步骤可以用 DMA 自动完成,哪些还得靠软件兜底。