1. I2C/SPI总线驱动模型概述
在嵌入式系统开发中,I2C和SPI总线就像城市中的公交系统,负责连接各种外设与主控芯片。这两种总线各有特点:I2C采用两线制(SCL时钟线和SDA数据线),支持多主多从架构;SPI则需要四线(SCK时钟、MOSI主出从入、MISO主入从出、CS片选),但传输速率更高。Linux内核为它们设计了统一的驱动框架,让开发者能专注于设备功能实现,而不必重复造轮子。
我曾在一个工业控制器项目中使用TMP102温度传感器,最初直接操作GPIO模拟I2C时序,不仅代码复杂还容易出错。后来改用内核的标准I2C框架后,稳定性大幅提升。这个经历让我深刻体会到理解内核驱动模型的重要性——它不仅是API调用,更是一套经过验证的最佳实践。
2. I2C总线驱动架构解析
2.1 三层模型设计
Linux的I2C驱动采用典型的分层架构:
-
硬件抽象层(Adapter):对应具体的I2C控制器硬件,如i.MX6ULL的I2C1控制器。开发板级支持包(BSP)通常会提供这一层驱动。
-
核心层(Core):实现总线注册、设备匹配等通用逻辑,相当于交通调度中心。开发者一般不需要修改这部分代码。
-
设备驱动层(Client Driver):如TMP102温度传感器驱动,实现具体的业务功能。
这种设计带来的最大好处是隔离变化——更换不同型号的I2C传感器时,只需替换对应的设备驱动,无需改动其他部分。
2.2 关键数据结构详解
2.2.1 i2c_adapter结构体
c复制struct i2c_adapter {
const struct i2c_algorithm *algo; // 硬件操作函数集
void *algo_data; // 寄存器基地址等私有数据
int nr; // 总线编号i2c-0、i2c-1等
struct device dev; // 关联的设备模型
// ... 其他字段省略
};
在RK3399平台上注册一个I2C控制器的典型过程:
c复制static int rk3x_i2c_probe(struct platform_device *pdev)
{
struct rk3x_i2c *i2c;
struct i2c_adapter *adap;
// 1. 分配内存
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
// 2. 映射寄存器
i2c->regs = devm_platform_ioremap_resource(pdev, 0);
// 3. 设置算法操作集
i2c->adap.algo = &rk3x_i2c_algorithm;
i2c->adap.algo_data = i2c;
// 4. 注册适配器
i2c_add_adapter(&i2c->adap);
}
2.2.2 i2c_algorithm操作集
c复制stat
