1. I2C总线概述
1.1 I2C总线历史
I2C(Inter-Integrated Circuit)总线由飞利浦半导体(现恩智浦半导体)于1982年发明,最初用于电视接收机中微控制器与外设之间的低速通信。其设计目标是以最少的引脚数(仅需两根线)实现芯片间的可靠通信。
| 时间 | 里程碑 | 关键特性 |
|---|---|---|
| 1982年 | 飞利浦发布I2C总线规范v1.0 | 标准模式100 kbit/s,7位地址 |
| 1992年 | 规范升级至v2.0 | 增加快速模式400 kbit/s,10位地址 |
| 1998年 | 规范升级至v2.1 | 完善时序参数,统一命名 |
| 2007年 | NXP发布v3.0 | 增加高速模式3.4 Mbit/s |
| 2012年 | NXP发布v4.0 | 超快速模式5 Mbit/s,仅单向 |
| 2014年 | NXP发布v5.0 | 增加US模式,统一文档 |
内核开发者视角: Linux内核自2.6系列起内置了完善的I2C子系统(drivers/i2c/),支持标准、快速、高速三种模式。嵌入式显示驱动中,DDC/CI、EDID读取、触摸控制器、背光调节等场景均依赖I2C总线。
1.2 物理层特性
I2C总线采用开漏(Open-Drain)结构,仅需两根信号线:
- SDA(Serial Data Line):串行数据线,双向传输
- SCL(Serial Clock Line):串行时钟线,由主设备驱动
电气连接要点:
- 所有设备的SDA和SCL引脚均为开漏输出,通过上拉电阻连接至VDD(通常为3.3V或5V)
- 总线空闲时,SDA和SCL均为高电平(由上拉电阻拉高)
- 设备通过将对应引脚拉低来驱动总线
- 上拉电阻典型值:1kΩ~10kΩ(取决于总线电容和速率)
- 总线电容限制:标准模式≤400pF,快速模式≤200pF
显示驱动中的典型连接: 在嵌入式显示系统中,I2C总线通常连接:
- HDMI/DVI的DDC通道(EDID读取)
- 触摸屏控制器(如FT5x06、GT911)
- 背光驱动芯片(如TI LP8556)
- 显示面板的配置寄存器(如MIPI DSI桥接芯片)
1.3 SDA/SCL信号时序
I2C总线的数据传输遵循严格的时序协议,核心信号包括:
| 信号事件 | SDA状态 | SCL状态 | 说明 |
|---|---|---|---|
| 起始条件(S) | 高→低 | 高 | SCL为高时,SDA由高变低 |
| 停止条件(P) | 低→高 | 高 | SCL为高时,SDA由低变高 |
| 数据位传输 | 稳定数据 | 高→低→高 | SCL低时改变SDA,SCL高时采样 |
| 应答位(ACK) | 低(应答) | 第9个时钟脉冲 | 接收方拉低SDA表示应答 |
| 非应答位(NACK) | 高 | 第9个时钟脉冲 | 接收方释放SDA表示非应答 |
调试陷阱: 在Linux内核I2C驱动调试中,最常见的硬件问题是:
- SDA或SCL被拉死(一直为低)→ 检查上拉电阻和从设备是否异常
- 无应答(NACK)→ 地址错误、设备未上电或总线速率不匹配
- 时钟拉伸(Clock Stretching)→ 从设备拉低SCL,主设备需等待
1.4 I2C地址与仲裁机制
地址格式
I2C总线使用两种地址格式:
| 地址类型 | 位数 | 可用地址数 | 地址范围 | 典型应用 |
|---|---|---|---|---|
| 7位地址 | 7 | 112(保留16个) | 0x08 ~ 0x77 | 大多数I2C设备(EDID、触摸、背光) |
| 10位地址 | 10 | 1024 | 0x000 ~ 0x3FF | 需要更多地址的复杂系统 |
显示驱动中的常见I2C地址:
- EDID EEPROM(DDC通道):0x50(7位地址)
- HDMI DDC:0x50 ~ 0x5F(取决于显示器和显卡)
- 触摸控制器FT5x06:0x38
- 触摸控制器GT911:0x5D 或 0x14
- 背光驱动LP8556:0x2C
仲裁机制
I2C总线支持多主设备通信,仲裁机制确保多个主设备同时发起传输时不会造成数据冲突:
- 仲裁发生在SDA线上:多个主设备同时输出数据时,输出低电平的设备会“覆盖”输出高电平的设备
- 仲裁原则:谁先输出低电平,谁赢得总线控制权
- 丢失仲裁的设备:检测到SDA电平与自己输出不一致时,立即释放SDA和SCL,转为从模式
- 仲裁不破坏数据:赢得仲裁的设备继续传输,丢失仲裁的设备不产生数据损坏
- 时钟同步:多个主设备通过SCL线与逻辑实现时钟同步,以最慢设备的时钟为准
1.5 数据传输格式
I2C总线上的数据以帧为单位传输,每帧包含:
- 起始条件(S)
- 从设备地址(7位或10位) + 读写位(R/W)
- 从设备应答位(ACK)
- 数据字节(8位) + 应答位(可重复多次)
- 停止条件(P)
Linux内核中的I2C消息结构:
struct i2c_msg {
__u16 addr; /* 从设备地址(7位或10位) */
__u16 flags; /* 标志位:I2C_M_RD(读)、I2C_M_TEN(10位地址)等 */
__u16 len; /* 消息数据长度(字节数) */
__u8 *buf; /* 数据缓冲区指针 */
};
显示驱动中典型的I2C读写操作:先写寄存器地址,再读寄存器值。