1. I2C总线基础:从历史到实战
各位工程师朋友,咱们今天聊聊I2C总线。说实话,这玩意儿我用了快二十年了,从最早的8051单片机到现在的ARM Cortex-M7,几乎每个项目都离不开它。你想想看,两根线就能挂一堆设备,多省事啊。
1.1 I2C的历史与标准演进
I2C是Philips(现在的NXP)在1982年搞出来的。初衷很简单——减少PCB上的走线。那时候的电视、音响里,芯片之间连来连去,线多得跟蜘蛛网似的。
我最早接触I2C是在2005年,当时用AT24C02存配置参数。那会儿的标准还是100kHz的标准模式(Standard Mode)。后来慢慢有了400kHz的快速模式(Fast Mode),再到1MHz的快速模式+(Fast Mode Plus)。
嗯,这里有个坑要提醒大家:不是所有I2C设备都支持高速。我曾经在一个项目里,把一颗只支持100kHz的传感器硬挂在400kHz总线上,结果数据读出来全是乱的。排查了整整两天才找到原因。
| 模式 | 最大速率 | 推出年份 |
|---|---|---|
| 标准模式(Sm) | 100 kHz | 1982 |
| 快速模式(Fm) | 400 kHz | 1992 |
| 快速模式+(Fm+) | 1 MHz | 2007 |
| 高速模式(Hs) | 3.4 MHz | 1998 |
| 超快速模式(UFm) | 5 MHz | 2007 |
1.2 物理层特性:开漏输出与上拉电阻
I2C的物理层,说白了就是两根线:SDA(数据线)和SCL(时钟线)。这两根线都是开漏输出结构。
什么叫开漏?就是芯片只能把线拉低到GND,不能主动拉高到VDD。要拉高怎么办?靠外部上拉电阻。这就是为什么I2C总线上必须要有上拉电阻。
上拉电阻的取值很关键。我见过不少新手,随便焊个10kΩ上去就完事了。结果总线频率一高,波形就变成正弦波了。
上拉电阻计算公式(经验版):
R_min = (VDD - VOL_max) / IOL_max
R_max = tr / (0.8473 × C_bus)
其中:tr是上升时间,C_bus是总线电容
实际项目中,我一般这样选:
- 100kHz:4.7kΩ ~ 10kΩ
- 400kHz:2.2kΩ ~ 4.7kΩ
- 1MHz以上:1kΩ ~ 2.2kΩ
1.3 协议层概述:时序与握手
I2C的协议层,其实就几个关键动作。搞懂了它们,波形分析就成功了一半。
起始条件(Start Condition)
SCL为高电平时,SDA从高电平跳变到低电平。就这么简单。我习惯叫它「S信号」。
// 起始条件的C语言实现(伪代码)
void i2c_start(void) {
SDA_HIGH();
SCL_HIGH();
delay_us(5); // 保持时间
SDA_LOW(); // SCL高时,SDA拉低
delay_us(5); // 建立时间
SCL_LOW(); // 准备发送数据
}
停止条件(Stop Condition)
SCL为高电平时,SDA从低电平跳变到高电平。这是「P信号」。
void i2c_stop(void) {
SDA_LOW();
SCL_HIGH();
delay_us(5);
SDA_HIGH(); // SCL高时,SDA拉高
delay_us(5);
}
数据有效性
这个规则很简单:SCL高电平时,SDA上的数据必须稳定。数据变化只能在SCL低电平时发生。
为什么?因为接收方是在SCL的上升沿采样SDA。如果SDA在SCL高时乱跳,采到的数据就是错的。
应答机制(ACK/NACK)
每发送完一个字节(8位数据),发送方会释放SDA,接收方在第9个时钟周期拉低SDA表示应答(ACK),或者保持高电平表示非应答(NACK)。
这里有个细节:主机发送完地址后,从机必须应答。如果从机没应答,说明地址不对、设备没上电、或者总线被锁死了。
我曾经遇到过最奇葩的情况:从机偶尔不应答,但重新上电就好了。查了三天,发现是电源纹波太大,从机内部复位了。加了个100μF的电解电容搞定。
| 场景 | 应答行为 | 含义 |
|---|---|---|
| 主机发送地址 | 从机拉低SDA | 设备在线,准备通信 |
| 主机发送数据 | 从机拉低SDA | 数据接收成功 |
| 主机读取数据 | 主机拉低SDA | 继续读取下一字节 |
| 主机读取数据 | 主机保持高电平 | 读取结束,发送停止条件 |
好了,I2C的基础就这些。说白了就是两根线、一个起始、一个停止、8位数据加1位应答。但就是这简单的协议,在实际项目中能玩出各种花样来。
下一章咱们聊聊波形分析,我会拿几个真实案例出来,看看那些「看起来正常但就是不通」的波形到底是怎么回事。