1. Linux Input 子系统与 I²C 驱动架构解析
在嵌入式系统开发中,I²C总线因其简单的两线制(SDA和SCL)和高效的多主从通信机制,成为连接各类传感器、触摸屏等外设的首选方案。作为在STM32MP1平台上开发过多个I²C设备驱动的工程师,我将从实际项目经验出发,深入剖析Linux内核中的I²C子系统实现。
1.1 I²C子系统三层架构设计
Linux内核将I²C子系统抽象为三个关键层次,这种分层设计与硬件架构完美对应:
-
I²C-Driver层:开发者主要工作的层面,负责特定设备的驱动实现。例如在触摸屏驱动中,我们需要处理坐标上报、压力检测等设备特有功能。
-
I²C-Core层:内核提供的核心框架,位于drivers/i2c/i2c-core-base.c。它就像交通指挥中心,管理着设备注册、匹配和电源管理等核心机制。
-
I²C-Adapter层:对接具体SoC的I²C控制器硬件,比如STM32的I²C外设驱动就在drivers/i2c/busses/i2c-stm32f7.c中实现。
实际开发中发现,不同SoC的Adapter层实现差异较大。比如NXP的I.MX6系列与STM32的I²C控制器在时钟配置上就有明显区别,需要仔细查阅对应芯片的参考手册。
1.2 关键数据结构解析
理解内核中的数据结构是开发驱动的基础,以下是几个核心结构体:
c复制struct i2c_client {
unsigned short flags; // 设备标志位
unsigned short addr; // 7位设备地址
char name[I2C_NAME_SIZE]; // 设备名称
struct i2c_adapter *adapter; // 所属适配器
// ...
};
struct i2c_driver {
int (*probe)(struct i2c_client *);
int (*remove)(struct i2c_client *);
struct device_driver driver;
const struct i2c_device_id *id_table;
// ...
};
struct i2c_adapter {
const struct i2c_algorithm *algo; // 通信算法
int nr; // 适配器编号
char name[48]; // 适配器名称
// ...
};
在开发si7006温湿度传感器驱动时,我们需要在probe函数中完成:
- 校验设备ID
- 初始化寄存器
- 创建sysfs接口或字符设备
- 设置轮询或中断机制
1.3 设备树配置实例
现代Linux驱动开发强烈建议使用设备树来描述硬件连接。以下是一个典型的I²C设备节点配置:
dts复制&i2c1 {
status = "okay";
clock-frequency = <100000>; // 标准模式100kHz
si7006: si7006@40 {
compatible = "silabs,si7006";
reg = <0x40>; // 设备I²C地址
