☰
嵌入式 Linux I2C 驱动开发核心知识总结:设备树与 platform 架构全解析
2026/10/14 19:40:28 网站建设 项目流程

嵌入式Linux I2C驱动开发核心知识点总结(设备树+platform架构)

本文基于IMX6ULL平台Linux 4.1内核,梳理I2C外设驱动开发的完整知识体系,覆盖驱动框架、设备树、字符设备、调试排障全流程。

一、Linux驱动分层架构总览

Linux驱动采用分层解耦设计思想,从应用层到硬件形成完整调用链:

  1. 应用层:用户空间程序,通过标准文件IO(open/read/write)访问设备
  2. VFS与系统调用层:统一文件抽象接口,屏蔽不同设备类型的差异
  3. 字符设备核心层:管理设备号、cdev结构体,建立文件操作与驱动的映射
  4. 内核子系统层:I2C子系统、platform平台总线,提供总线级通用能力
  5. 芯片驱动层:具体外设(如LM75温度传感器)的硬件操作逻辑
  6. 设备树层:独立描述板级硬件资源,实现驱动代码与硬件信息分离
  7. 硬件层:外设芯片通过I2C总线挂载到SOC的I2C控制器

二、platform平台驱动模型

1. 核心设计思想

平台总线模型实现驱动与设备分离:platform总线下分为platform_driver(驱动逻辑)和platform_device(硬件信息,现由设备树替代),通过匹配机制自动绑定。

2. 驱动核心结构体

struct platform_driver { int (*probe)(struct platform_device *); // 匹配成功后执行初始化 int (*remove)(struct platform_device *); // 设备移除时执行资源清理 struct device_driver driver; const struct platform_device_id *id_table; // 传统ID匹配表 const struct of_device_id *of_match_table; // 设备树匹配表 };

3. probe函数核心职责

  • 解析设备树节点,提取硬件资源(I2C地址、引脚、中断号等)
  • 注册字符设备、创建/dev下的设备节点
  • 芯片硬件初始化、申请中断资源
  • 初始化驱动私有数据结构体

4. remove函数核心职责

  • 释放中断、注销字符设备
  • 销毁设备节点、释放内核内存
  • 芯片硬件复位/下电,完成收尾清理

三、设备树(DTS)匹配机制

1. 匹配核心:compatible属性

驱动与设备树节点通过compatible字符串完全精确匹配,大小写、空格、标点必须完全一致,是驱动probe函数执行的前提。

2. I2C设备节点编写规范

&i2c1 { clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c1>; status = "okay"; lm75@48 { compatible = "alientek,lm75"; reg = <0x48>; // I2C外设器件地址 }; };
  • 节点必须挂载到硬件对应的I2C控制器节点下
  • reg属性为I2C芯片的器件地址
  • status = "okay"表示启用该节点

3. 设备树生效验证

开发板端通过/proc/device-tree目录实时验证节点状态:

# 查看I2C控制器下的子节点 ls /proc/device-tree/soc/i2c@021a0000/ # 查看节点compatible属性 cat /proc/device-tree/soc/i2c@021a0000/lm75@48/compatible

四、I2C子系统驱动架构

1. I2C核心三要素

  1. I2C适配器(adapter):SOC内部I2C控制器的驱动,一般由芯片厂商提供
  2. I2C设备(client):外设芯片的内核抽象,对应设备树中的I2C节点
  3. I2C驱动(driver):外设芯片的业务逻辑,与client匹配后执行probe

2. I2C驱动注册流程

  1. 定义i2c_driver结构体,配置of_match_table设备树匹配表
  2. 调用i2c_add_driver将驱动注册到内核I2C总线
  3. 总线遍历设备树I2C节点,通过compatible匹配驱动
  4. 匹配成功自动调用驱动的probe函数

3. 核心通信API

  • i2c_smbus_read_byte_data:读取芯片指定寄存器的数据
  • i2c_smbus_write_byte_data:向芯片指定寄存器写入数据
  • 所有通信通过I2C适配器统一调度,无需直接操作SOC寄存器

五、内核模块加载与卸载

1. 常用操作命令

insmod xxx.ko # 加载内核模块 rmmod xxx # 卸载模块(无需加.ko后缀) lsmod # 查看系统已加载模块 modinfo xxx.ko # 查看模块详细信息 dmesg # 查看内核打印日志

2. 常见加载问题

  • File exists报错:内核已存在同名模块,属于重复加载,先执行rmmod再加载即可
  • 模块加载成功但probe不执行:90%以上为设备树compatible不匹配、设备树未更新、节点路径错误
  • lsmod引用计数为0:驱动未匹配到任何硬件设备,未与实际硬件完成绑定

六、字符设备驱动层实现

1. 字符设备注册步骤

  1. 申请或指定设备号(主设备号+次设备号)
  2. 初始化cdev结构体,绑定file_operations操作集
  3. 调用cdev_add向内核注册字符设备
  4. 创建设备类(class)、创建设备节点(device),自动生成/dev下的设备文件

2. file_operations核心操作集

struct file_operations { .owner = THIS_MODULE, .open = lm75_open, // 打开设备 .read = lm75_read, // 读取温度数据 .write = lm75_write, // 写入芯片配置 .release = lm75_release // 关闭设备 };

七、中断与并发控制

1. 中断处理流程

  • 从设备树节点解析获取中断号
  • 申请中断资源,注册中断处理函数
  • 中断上下文采用底半部机制(tasklet/工作队列)处理耗时逻辑
  • 驱动卸载时释放中断资源

2. 并发保护机制

  • 原子操作:适用于简单计数、标志位保护
  • 自旋锁:中断上下文、短时间临界区保护
  • 互斥锁:进程上下文、耗时操作的临界区保护
  • 禁止在中断上下文调用可能导致睡眠的函数

八、驱动调试与问题排查

1. 问题排查优先级

  1. 确认模块加载:执行lsmod确认模块已在内核中
  2. 确认设备树生效:通过/proc/device-tree验证节点存在
  3. 确认匹配字符串:逐字符对比驱动与设备树的compatible属性
  4. 查看内核日志:dmesg查看probe打印与错误信息
  5. 硬件通路验证:i2cdetect -y 总线号扫描I2C设备地址

2. 经典问题定位

  • 模块已加载但无设备节点:probe函数未执行,优先排查设备树匹配
  • i2cdetect扫不到设备地址:硬件接线错误、引脚复用未配置、I2C控制器未启用
  • insmod直接加载失败:内核版本不匹配、依赖模块未加载、驱动代码语法错误

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询