I2C协议概述与物理层基础

咱们做嵌入式开发的,几乎没人能绕过I2C。从传感器读取数据,配置芯片寄存器,甚至控制电源管理芯片,到处都有它的影子。我记得刚入行那会儿,总觉得I2C就是两根线拉个电阻的事儿,简单得很。直到有一次,板子上的温湿度传感器隔三差五丢数据,折腾了两天才发现是上拉电阻选小了——嗯,从那以后,我对这个"简单协议"再也不敢马虎了。

今天这一章,我们先把这个协议的老底儿翻一翻。不聊虚的,就讲三点:总线长什么样,信号线有什么脾气,上拉电阻到底怎么选。

I2C总线拓扑:主从对决

I2C的总线结构其实非常简洁。两条线——SCL(时钟线)和SDA(数据线)——把总线上所有设备串起来。每个设备都有一个独一无二的地址,主设备发起通信,从设备响应请求。

你想想看,这就是一个典型的"主从模式"。主设备负责产生时钟信号,控制通信节奏。从设备只能被动响应,不能主动发起数据传输。我刚开始做项目时犯过一个低级错误:把两个主设备接在同一条I2C总线上,还指望它们能好好协作。结果呢?总线冲突,数据乱成一锅粥。

小提示: 同一个I2C总线上可以挂多个主设备,但需要总线仲裁机制。初学阶段建议一个主设备带多个从设备,省心省力。

典型的I2C总线连接是这样的:

  • 主设备(通常是MCU或SoC)——掌控时钟SCL,启动和停止通信
  • 从设备(传感器、EEPROM、ADC等)——等待主设备召唤
  • 上拉电阻Rp——将SCL和SDA拉高到VDD
  • 外部VDD——常用3.3V或5V

下面这张图可以帮你快速建立总线拓扑的直观印象:

I2C 总线拓扑结构 VDD Rp Rp SCL SDA 主设备 从设备1 从设备2 特征说明 1. SCL和SDA是开漏输出,必须通过上拉电阻接VDD 2. 所有从设备共享同一条总线,地址不能冲突 3. 主设备控制时钟,决定通信速率

SCL/SDA信号特性:开漏的秘密

I2C最与众不同的地方,就是SCL和SDA都是开漏输出。什么叫开漏?说白了,芯片内部的驱动管只能把引脚拉到低电平(GND),拉高全靠外部上拉电阻。想一想为什么这样设计?好处很明显:多个设备可以同时拉低总线,不会打架。

但这个特性也带来了一些麻烦事。我调试时经常遇到的情况是:从设备响应ACK时,主设备收不到低电平。查来查去,发现是从设备的开漏引脚驱动能力不够,上拉电阻又太小——从设备根本拉不动。

项目 I2C信号要求 与普通GPIO的区别
输出模式 开漏输出,仅能拉低 推挽输出,既能拉高也能拉低
高电平实现 依赖外部上拉电阻 由芯片内部驱动
线与能力 天然支持,多个设备可同时拉低 不支持,直接短接会导致冲突
上升沿时间 由上拉电阻和总线电容决定(RC延迟) 由芯片驱动能力决定,通常更快

标准的I2C信号时序包含以下几个关键动作:

  1. 起始条件(START): SCL为高时,SDA从高切换到低
  2. 从设备地址+读写位: 主设备发出7位或10位地址,第8位为R/W
  3. ACK/NACK: 每发送一个字节,从设备在第9个SCL周期拉低SDA表示应答
  4. 数据字节: 8位数据,高位在前(MSB first)
  5. 停止条件(STOP): SCL为高时,SDA从低切换到高

说到起始条件,我踩过一个坑:主设备想发送START信号,但SDA线在上次通信结束后被拉高了。如果SDA的上升沿太慢,主设备可能误判总线忙,一直发不了START。后来查手册才发现,是总线上挂的电容太大,上拉电阻却没调整。

注意: 标准模式和快速模式对上升/下降时间有严格约束。标准模式 SCL=100kHz 时,上升时间应 ≤1000ns;快速模式 SCL=400kHz 时,上升时间应 ≤300ns。超了,数据就会出错。

上拉电阻选择:实战中的艺术

上拉电阻选多大?这个问题看似简单,但真到了项目里,它就是个两难选择。电阻太小,总线电流大,功耗高,而且从设备可能拉不动;电阻太大,上升沿太慢,高频通信时波形畸变。

我个人习惯按以下思路来计算:

  • 从设备的最小灌电流能力决定了电阻最小值。标准I2C器件要求3mA灌电流时,VOL ≤ 0.4V。
  • 总线电容(包括PCB走线、每个器件的引脚电容)决定了电阻最大值。上升时间需求限制了RC时间常数。

举个实际的例子:

假设VDD = 3.3V,标准模式速率100kHz,总线电容Cb = 100pF。我们来计算一下:

  • 电阻最小值:Rp(min) = (VDD - VOL) / IOL = (3.3V - 0.4V) / 3mA ≈ 967Ω → 取整为1kΩ
  • 电阻最大值:Rp(max) = tr / (0.8473 × Cb) = 1μs / (0.8473 × 100pF) ≈ 11.8kΩ

还记得我之前提到的那个传感器丢数据的问题吗?当时我莫名其妙用了330Ω的上拉电阻,总线电流差不多10mA。传感器拉低SDA时,根本到不了0.4V以下,主设备收不到ACK,重试几次就超时了。换成4.7kΩ,好了。

总线速率 Cb 常见范围 推荐Rp范围
标准模式 100kHz 20pF ~ 200pF 2.2kΩ ~ 10kΩ
快速模式 400kHz 20pF ~ 200pF 1.5kΩ ~ 4.7kΩ
快速+模式 1MHz 10pF ~ 100pF 1kΩ ~ 2.2kΩ
实战避坑: 我曾经在一款产品中用过5V供电的I2C总线,但选了4.7kΩ上拉电阻,没仔细算。结果发现传感器有时不响应。测量波形发现,从设备拉低SDA时只拉到1.2V,离0.4V差太远。换成1.8kΩ后解决了。所以,电压越高,上拉电阻就要选小一些。

总线电容与PCB布局

总线电容对高速I2C影响很大。每条信号线上的电容,来自每个器件的引脚电容、焊盘电容和PCB走线电容。标准规定,总线上所有电容之和不能超过400pF。超过这个值,上升沿就会被严重拖慢。

在布局上,我有几个习惯:

  • SCL和SDA走线尽量短,尤其在高频项目中
  • 避免总线分叉(star topology),推荐菊花链(daisy chain)
  • 上拉电阻靠近主设备放置,减少噪声耦合
  • 如果使用接插件,留意接触电容和走线电感

你可能会问:如果板上I2C从设备太多,总线电容超标怎么办?嗯,这时候两个办法:一是用I2C总线缓冲器/中继器(如PCA9517),隔离电容;二是降低速率,延长上升时间要求。

好了,I2C总线的基础硬件要点就这些。记住:物理层搞不定,时序分析做得再漂亮也没用。下一部分,我们要开始啃时序参数这块硬骨头了——那个才是调试I2C从设备时最磨人的地方。