1. I2C协议基础:从一根线到一套生态
各位同学,咱们今天聊聊I2C。这玩意儿在嵌入式开发里太常见了——传感器、触摸屏、EEPROM、音频Codec,十有八九都挂在这两根线上。我刚开始做驱动时,总觉得I2C协议简单,不就是两根线嘛。结果有一次调一个触摸屏,死活读不到设备ID,折腾了两天才发现是应答时序差了半个时钟周期……嗯,从那以后我再也不敢小看这个“简单”协议了。
1.1 I2C总线的起源
I2C(Inter-Integrated Circuit)是飞利浦公司在1982年搞出来的。说白了,就是为了解决板级芯片之间的通信问题——那时候芯片引脚多、走线乱,飞利浦想了个办法:只用两根线,把所有外设串起来。
我个人习惯把I2C理解成“芯片间的电话线”。主设备拨号(起始条件),从设备接听(应答),然后开始通话(数据传输)。挂断(停止条件)后,其他人才能用这条线。
| 特性 | 说明 |
|---|---|
| 发明时间 | 1982年,飞利浦(现NXP) |
| 物理层 | 2线制:SDA(数据线)+ SCL(时钟线) |
| 拓扑结构 | 多主多从,总线式连接 |
| 最大速率 | 标准模式100kHz,快速模式400kHz,高速模式3.4MHz |
| 最大设备数 | 受限于总线电容(通常不超过400pF) |
1.2 物理层特性:SDA与SCL
I2C的物理层就两根线:SDA(串行数据线)和SCL(串行时钟线)。这两根线都是开漏输出,需要外部上拉电阻拉到VCC。
为什么用开漏?你想想看,如果多个设备同时拉低一根线,用推挽输出就直接短路了。开漏就不一样——谁想拉低就拉低,谁想释放就释放,大家共用一根线,互不打架。
关键参数:上拉电阻选择
上拉电阻太小,功耗大;太大,上升沿太慢。我一般按这个经验公式估算:
- 标准模式(100kHz):4.7kΩ ~ 10kΩ
- 快速模式(400kHz):1.5kΩ ~ 4.7kΩ
- 高速模式(3.4MHz):几百Ω级别
具体选多大,还得看总线电容。我在T113上挂过6个I2C设备,总线电容大概150pF,用的4.7kΩ上拉,跑400kHz没问题。
避坑指南
我曾经遇到过一个问题:板子上I2C设备不多,但SCL波形上升沿特别缓。查了半天,发现是PCB走线太长,寄生电容太大。后来把上拉电阻从10kΩ换成3.3kΩ,波形立马干净了。所以——示波器是调I2C的必备工具,别省。
1.3 协议层:时序与握手
I2C的协议层其实就几个基本动作:起始、停止、数据收发、应答。把这些动作组合起来,就能完成一次完整的通信。
1.3.1 起始条件与停止条件
这两个条件定义了通信的边界:
- 起始条件(S):SCL为高电平时,SDA从高电平切换到低电平
- 停止条件(P):SCL为高电平时,SDA从低电平切换到高电平
说白了,起始条件就是“我要开始说话了”,停止条件就是“我说完了,你们聊”。
注意:起始和停止条件只能由主设备产生。从设备没有这个权限。如果从设备想主动发数据?不行,它只能等主设备来读。
1.3.2 数据有效性
I2C的数据传输有个铁律:数据在SCL高电平时有效,在SCL低电平时变化。
为什么这样设计?你想想看,如果数据在SCL高电平时变化,那接收方就搞不清楚当前到底是数据还是起始/停止条件了。所以协议规定:
- SCL高电平期间,SDA必须保持稳定
- SCL低电平期间,SDA可以变化(准备下一位数据)
每次传输8位数据(一个字节),高位在前(MSB first)。
1.3.3 应答机制
每发送完一个字节,接收方必须回复一个应答位(ACK)。这是I2C协议里我最喜欢的设计——每步都有反馈,出了问题立刻知道。
应答机制是这样的:
- 发送方在第9个时钟周期释放SDA(拉高)
- 接收方如果正常接收,在第9个时钟周期拉低SDA(ACK)
- 接收方如果不想接收或出错了,保持SDA高电平(NACK)
我在T113上调试I2C时,经常用逻辑分析仪看应答位。如果看到NACK,基本就是设备地址错了或者设备没上电。
1.4 7位地址与10位地址
I2C总线上每个设备都有一个唯一地址。地址分两种:7位和10位。
| 地址类型 | 地址范围 | 最大设备数 | 兼容性 |
|---|---|---|---|
| 7位地址 | 0x00 ~ 0x7F | 112(保留16个) | 所有设备都支持 |
| 10位地址 | 0x000 ~ 0x3FF | 1024 | 需要主从设备都支持 |
7位地址是最常用的。地址的高4位通常由芯片厂商固定,低3位由硬件引脚决定。比如AT24C02的地址是0x50(固定)+ A2/A1/A0(引脚电平)。
10位地址用得少,我做了这么多年驱动,也就用过一两次。它的格式比较特殊:
- 第一个字节:1111 0XX + R/W(XX是地址的高2位)
- 第二个字节:地址的低8位
说白了,10位地址就是用一个特殊标记(1111 0)告诉从设备:“后面还有8位地址,别急着应答”。
经验之谈
在T113上,I2C控制器默认只支持7位地址。如果你要用10位地址,得在设备树里配一下,或者驱动里手动设置。我个人建议:能用7位就别用10位,省心。
1.5 知识结构总览
下面这张图把I2C协议的核心知识点串起来了。我画这张图时,特意把物理层、协议层、地址体系分成了三个层次——你从上往下看,就是I2C从硬件到软件的完整脉络。
从这张图你能看到,I2C协议其实不复杂。物理层搞定两根线的电气特性,协议层定义通信的时序规则,地址体系解决“谁跟谁说话”的问题。三层加起来,就是I2C的全部。
1.6 小结
这一章我们聊了I2C协议的来龙去脉。从飞利浦的初衷,到SDA/SCL的物理特性,再到起始、停止、数据、应答这些基本动作,最后是7位和10位地址的区别。