1. I2C总线基础与通信原理
I2C(Inter-Integrated Circuit)总线是飞利浦公司开发的一种简单、双向二线制同步串行总线。作为嵌入式系统中最常用的通信接口之一,I2C以其简洁的硬件设计和灵活的扩展性著称。让我们深入剖析其工作机制。
1.1 物理层特性解析
I2C总线仅需两根信号线即可实现全双工通信:
- SCL(Serial Clock):时钟线,由主设备控制
- SDA(Serial Data):数据线,双向传输
这两根线都需要通过上拉电阻连接到VCC(典型值3.3V或5V)。上拉电阻的取值很有讲究:
- 阻值过小会导致电流过大
- 阻值过大会影响上升沿速度
- 常用计算公式:Rp(min) = (Vcc - 0.4V)/3mA
- 典型值范围:1kΩ~10kΩ
总线空闲时,SCL和SDA都保持高电平状态。这种设计使得多个设备可以方便地挂载在同一总线上,只需通过地址识别即可实现通信。
实际布线时要注意:总线电容不得超过400pF(标准模式)或550pF(快速模式),否则可能导致信号畸变。长距离传输时需要降低通信速率。
1.2 协议层关键机制
1.2.1 通信时序详解
I2C通信的基本单元由以下几部分组成:
-
起始条件(START):
- SCL为高电平时,SDA从高电平跳变到低电平
- 这个下降沿触发所有从设备开始监听总线
-
地址帧:
- 7位从设备地址(可扩展为10位)
- 1位读写方向(0-写,1-读)
- 总共8位,MSB先传
-
应答位(ACK/NACK):
- 每传输8位数据后,接收方在第9个时钟周期拉低SDA表示应答
- 若保持高电平则为非应答(NACK)
-
数据帧:
- 每个字节同样遵循MSB优先原则
- 数据变化只能在SCL低电平期间进行
- SCL高电平期间数据必须保持稳定
-
停止条件(STOP):
- SCL为高电平时,SDA从低电平跳变到高电平
- 释放总线控制权
c复制// 典型I2C时序模拟代码(GPIO模拟实现)
void i2c_start(void) {
SDA_HIGH();
SCL_HIGH();
delay();
SDA_LOW();
delay();
SCL_LOW();
}
void i2c_stop(void) {
SDA_LOW();
SCL_HIGH();
delay();
SDA_HIGH();
delay();
}
1.2.2 多主机仲裁机制
当多个主设备同时尝试控制总线时,I2C通过巧妙的仲裁机制避免冲突:
- 各主设备在发送数据时同时监测SDA线状态
- 如果检测到的电平与自己发送的不一致,立即退出竞争
- 仲裁失败的设备转为从模式
- 整个过程不会造成数据丢失
这种基于"线与"逻辑的仲裁方式,使得I2C特别适合多主设备系统,如智能家居中的多个控制节点。
2. Linux I2C子系统架构设计
2.1 分层架构解析
Linux内核中的I2C子系统采用经典的四层架构设计,各层职责明确:
code复制应用层
↓
I2C设备驱动层(如lm75.c、at24c08.c)
↓
I2C核心层(drivers/i2c/i2c-core.c)
↓
I2C总线驱动层(如i2c-imx.c)
↓
硬件层(SoC内置I2C控制器)
2.1.1 硬件层实现细节
现代SoC通常集成多个I2C控制器,如i.MX6ULL就有4个I2C接口。控制器硬件主要完成:
- 时钟分频(根据SCL频率要求)
- 起始/停止条件生成
- 数据移位和采样
- 中断/DMA控制
寄存器配置示例(以i.MX6ULL为例):
c复制// 设置时钟分频寄存器
writel(0x10, base + I2C_IFDR);
// 使能I2C控制器
writel(I2C_CR_IEN, base + I2C_CR);
2.1.2 总线驱动层关键实现
I2C Adapter是连接硬件与核心层的桥梁,
