news 2026/4/18 3:45:19

Linux内核驱动——中断子系统与 I2C 子系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux内核驱动——中断子系统与 I2C 子系统

目录

一、中断子系统

1.1 中断子系统架构

1.2 代码实例分析

1.2.1 tasklet

1.2.2 workqueue

1.3 对比总结

二、I2C 子系统

2.1 I2C 子系统架构

2.2 代码实例分析:LM75 温度传感器驱动

2.2.1 设备树配置

2.2.2 驱动注册与设备绑定

2.2.3 数据传输实现

2.2.4 应用层数据解析

三、总结与应用场景

3.1 核心设计思想

3.2 典型应用组合

3.3 开发关键要点


一、中断子系统

1.1 中断子系统架构

中断是 CPU 响应外部设备请求的一种机制。当某个硬件(如按键)发生事件时,会向 CPU 发送一个中断信号,触发中断服务程序执行。

Linux内核采用 “顶半部 + 底半部” 的设计思想来优化中断处理效率:

  • 顶半部(上半部,Top Half):快进快出, 仅完成 “紧急操作”(如置标志位、保存数据),不能阻塞、不能休眠(运行在中断上下文)。
  • 底半部(下半部,Bottom Half):处理非紧急、耗时操作(如数据解析、唤醒应用),运行在中断上下文或进程上下文,核心实现有两种:
    • tasklet:基于软中断实现,不能阻塞、不能休眠(中断上下文),适合短耗时处理;
    • workqueue:封装为普通内核线程(进程上下文),可以休眠、阻塞,适合长耗时操作。
中断子系统

1.2 代码实例分析

1.2.1 tasklet

// 底半部:tasklet(不能休眠) static struct tasklet_struct tsk; static void key_tasklet_handler(unsigned long arg) { condition = 1; wake_up_interruptible(&wq); // 唤醒应用层阻塞 printk("key_tasklet_handler arg = %ld\n", arg); } // 顶半部:中断触发后快速调度底半部 static irqreturn_t key_irq_handler(int irq, void * dev) { int arg = *(int *)dev; if(100 != arg) return IRQ_NONE; // 确认是目标中断 tasklet_schedule(&tsk); // 调度tasklet printk("irq = %d dev = %d\n", irq, arg); return IRQ_HANDLED; } // 初始化 tasklet_init(&tsk, key_tasklet_handler, 100);

关键点分析

  • 在中断处理函数中,通过 tasklet_schedule 调度 tasklet
  • tasklet 在软中断上下文中执行,不能睡眠
  • 适合快速处理按键事件,但不适合执行耗时操作

1.2.2 workqueue

// 底半部:workqueue(可以休眠) static struct work_struct work; static void key_work_func(struct work_struct *work) { ssleep(1); // 休眠1秒 condition = 1; wake_up_interruptible(&wq); printk("key_work_func ...\n"); } // 顶半部:中断触发后快速调度底半部 static irqreturn_t key_irq_handler(int irq, void * dev) { int arg = *(int *)dev; if(100 != arg) return IRQ_NONE; // 确认是目标中断 schedule_work(&work); // 调度workqueue printk("irq = %d dev = %d\n", irq, arg); return IRQ_HANDLED; } // 初始化 INIT_WORK(&work, key_work_func);

关键点分析:

  • 在中断处理函数中,通过 schedule_work 调度 work
  • work 在普通线程上下文中执行,可以调用 ssleep 等可能睡眠的函数
  • 适合执行可能耗时的操作,如 I/O 操作、复杂计算等操作

1.3 对比总结

特性taskletworkqueue
是否可阻塞
是否可休眠
是否可被调度
运行上下文中断上下文进程上下文
并发控制同类型 tasklet 串行执行可配置并发策略
适用场景紧急任务非紧急任务

二、I2C 子系统

2.1 I2C 子系统架构

Linux I2C 子系统采用分层设计,使驱动开发更加模块化和可重用。I2C子系统分为多个层次:

  • 硬件层:物理 I2C 控制器和设备
  • I2C总线驱动层(I2C Adapter):实现 I2C 总线通信协议,与硬件交互
  • I2C核心层(I2C Core):提供 I2C 总线通信的公共接口和数据结构
  • I2C设备驱动层(I2C Client):针对特定 I2C 设备的驱动程序
  • 应用层:用户空间应用程序
I2C子系统

2.2 代码实例分析:LM75 温度传感器驱动

2.2.1 设备树配置

在设备树中正确配置 I2C 设备节点:

lm75@48 { compatible = "ti,lm75"; reg = <0x48>; };

2.2.2 驱动注册与设备绑定

I2C 设备驱动通过 i2c_add_driver() 注册,核心是 of_device_id 匹配设备树中的 compatible 属性:

// 探测函数 static int probe(struct i2c_client * client, const struct i2c_device_id * device) { int ret = misc_register(&misc_dev); if(ret < 0) goto err_misc_register; lm75_client = client; printk("lm75 probe\n"); return 0; err_misc_register: printk("lm75 probe misc_register failed\n"); return ret; } // 设备树匹配表(与dts中lm75节点的compatible一致) static const struct of_device_id of_lm75_table[] = { {.compatible = "ti,lm75"}, {} }; // I2C驱动结构体 static struct i2c_driver lm75_driver = { .probe = probe, // 设备匹配成功后执行 .remove = remove, .driver = {.name = DEV_NAME, .of_match_table = of_lm75_table}, .id_table = lm75_table };

probe() 函数的核心工作:注册 misc 设备(提供 /dev/lm75 节点),保存 i2c_client 指针(用于后续通信)。

2.2.3 数据传输实现

I2C 设备驱动通过 i2c_msg 结构体描述通信数据,调用 master_xfer() 完成底层传输:

static ssize_t read(struct file * file, char __user * buf, size_t size, loff_t * loff) { int ret = 0; unsigned char temp[2] = {0}; // 构造I2C读取消息:从lm75地址读取2字节数据 struct i2c_msg msg = { .addr = lm75_client->addr, // 设备从机地址 .flags = I2C_M_RD, // 读操作 .len = 2, // 数据长度 .buf = temp // 数据缓冲区 }; // 调用Adapter的master_xfer执行传输 lm75_client->adapter->algo->master_xfer(lm75_client->adapter, &msg, 1); ret = copy_to_user(buf, temp, sizeof(temp)); // 数据传递给应用层 return sizeof(temp); }

2.2.4 应用层数据解析

应用层通过访问 /dev/lm75 读取原始数据,按 LM75 的寄存器格式解析为温度值:

#include <linux/i2c-dev.h> int main(int argc, const char *argv[]) { int fd = open("/dev/lm75", O_RDWR); if (fd < 0) { perror("open iic failed"); return 1; } unsigned char temp[2] = {0}; while(1) { int ret = read(fd, temp, sizeof(temp)); float t = 0.5 * ((temp[0] << 8 | temp[1]) >> 7); printf("t = %.1f\n", t); sleep(1); } close(fd); return 0; }

工作流程:

  1. 打开 /dev/lm75 设备节点。
  2. 每秒读取一次温度寄存器(共2字节)。
  3. 解析原始数据:0.5 × (value >> 7) 得到摄氏度。
  4. 输出当前温度。

注意:temp[0] << 8 | temp[1] 组合成 16 位值,右移 7 位去除小数部分,乘以 0.5 得到真实温度。

三、总结与应用场景

3.1 核心设计思想

  • 中断子系统:快慢分离,顶半部保证响应速度,底半部处理耗时逻辑,平衡实时性与效率;
  • I2C 子系统:分层解耦,核心层屏蔽底层差异,设备驱动专注外设逻辑,降低开发复杂度。

3.2 典型应用组合

实际开发中,两个子系统常结合使用:例如 I2C 传感器(如 LM75)配置中断引脚,当温度超过阈值时触发中断,中断底半部调度 I2C 数据读取,应用层通过阻塞等待获取最新数据,实现 “事件触发 + 高效通信” 的闭环。

3.3 开发关键要点

  • 中断驱动:注意资源释放(free_irq())、底半部选择(根据是否需要休眠)、阻塞等待的正确使用;
  • I2C 驱动:确保设备树 compatible 属性匹配、i2c_msg 参数正确(地址、长度、读写标志)、数据格式按外设手册解析。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/4 15:19:56

小白程序员必看:AI大模型时代,产品经理的职业新机会点与学习路径

AI技术正深度融入各行各业&#xff0c;重塑硬件设备&#xff0c;催生情感服务闭环产品。AI发展将取代部分传统岗位的重复性工作&#xff0c;但对能定义方向、结合技术与商业需求的AI产品经理需求激增。AI产品经理需具备跨学科整合能力&#xff0c;理解模型能力、数据逻辑、用户…

作者头像 李华
网站建设 2026/4/12 23:07:00

游戏循环与Pinia:解决循环更新问题

在开发游戏时,游戏循环是核心组件之一,它负责持续更新游戏状态。最近,我在尝试使用Pinia来管理一个游戏的全局状态时,遇到了一个令人头疼的错误。这个问题主要发生在游戏循环初始化时,尤其是在尝试访问Pinia的store之前,Pinia实例尚未被安装到Vue应用中。下面我将详细解释…

作者头像 李华
网站建设 2026/4/9 3:34:19

TRENDS整合50多个系统,打造适配AI的制造业数据核心架构

澳大拉西亚地区最大的品牌周边产品制造商携手Boomi整合50多个分散系统&#xff0c;搭建合规可控、近实时的企业数据核心架构。 AI驱动自动化领域的领导者Boomi今日宣布&#xff0c;TRENDS Promotional Products采用Boomi企业平台&#xff0c;将50多个定制应用程序和传统系统整合…

作者头像 李华
网站建设 2026/4/12 13:00:39

Beast Industries宣布收购Step,正式将金融服务纳入其平台版图

此次战略性收购将创新金融科技能力引入Beast Industries持续壮大的全球平台&#xff0c;通过整合技术、内容与使命&#xff0c;面向新一代用户重塑金融服务体验作为全球规模最大、最具创新力的创作者平台&#xff0c;Beast Industries今日宣布收购金融科技公司Step。Step专注于…

作者头像 李华
网站建设 2026/4/15 21:13:19

2026冲刺季:考研失利如何快速转型求职

对于那些拼尽全力却未能如愿“上岸”的同学&#xff0c;沮丧、迷茫、焦虑可能如影随形。然而&#xff0c;时间不等人&#xff0c;春招的号角已经吹响&#xff0c;这不应是终点&#xff0c;而是职业生涯新篇章的起点。考研失利&#xff0c;并非否定你的优秀&#xff0c;而是引导…

作者头像 李华