I2C总线基础:协议概述与信号线
I2C总线,说白了就是一根数据线加一根时钟线,让多个设备能互相聊天。我第一次接触I2C时觉得它挺神奇的——就两根线,怎么就能挂那么多设备呢?后来做项目多了才发现,这玩意儿虽然简单,但坑也不少。
I2C是飞利浦(现在的NXP)在80年代搞出来的,全称叫Inter-Integrated Circuit。它的设计初衷就是让微控制器和外围芯片之间能用最少的线通信。嗯,这个目标确实实现了。
SDA与SCL线
I2C总线就两条线:
- SDA(Serial Data Line)—— 数据线,负责传数据
- SCL(Serial Clock Line)—— 时钟线,负责同步节奏
这两条线都是开漏输出结构,需要外接上拉电阻。我见过不少新手在这上面栽跟头——忘了加上拉电阻,结果总线死活拉不高电平。
开漏结构的好处是:多个设备可以共用总线,谁想说话就把线拉低,不想说话就释放(高阻态)。这就避免了多个设备同时输出高低电平打架的问题。
起始条件与停止条件
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
10位地址模式
7位地址不够用怎么办?I2C规范后来扩展了10位地址模式。说实话,我在实际项目中用到10位地址的情况不多,但大系统里确实有需求。
10位地址的发送分两步:
- 第一个字节:11110 + 地址高2位 + R/W位
- 第二个字节:地址低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一次完整的读写过程画出来了。我建议你把它存下来,调试的时候对照着看。
应答机制(ACK/NACK)
I2C的应答机制很有意思。每传完一个字节(8位),接收方要在第9个时钟周期拉低SDA表示应答(ACK)。如果不拉低,就是非应答(NACK)。
什么时候会出现NACK?我总结了几种情况:
- 地址不匹配:总线上没有设备响应这个地址
- 从机忙:正在处理上次的数据,没空理你
- 读操作结束:主机读完数据后发NACK告诉从机「够了,别发了」
- 从机故障:芯片坏了或者没上电
时钟同步与仲裁
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的基础知识就讲到这里。这些概念是后面理解帧缓冲管理和DMA加速的基石。你先把这些搞明白,后面我们聊怎么用DMA来加速I2C通信,那才是真正提升性能的地方。