news 2026/3/17 16:59:08

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/3/16 4:11:07

【安全测试】1_安全测试体系 _安全测试介绍

文章目录一、安全测试介绍1.1 安全测试与传统测试的区别1.2 安全测试与渗透测试的区别二、安全测试常用方法三、安全测试维度四、安全测试点一、安全测试介绍 安全测试就是发现软件安全漏洞的过程&#xff0c;旨在保护软件系统的数据与功能。 安全测试以破坏系统的安全策略为…

作者头像 李华
网站建设 2026/3/4 22:58:24

贸发局主办全球最大一站式珠宝商贸平台

荟萃环球珍品 新设硬足金展馆展现黄金崭新技术由香港贸易发展局&#xff08;香港贸发局&#xff09;主办的全球最大一站式珠宝商贸平台&#xff0c;将于3月初以“两展两地”的成功模式揭幕。第12届香港国际钻石、宝石及珍珠展于3月2至6日在亚洲国际博览馆举行&#xff0c;展出…

作者头像 李华
网站建设 2026/3/13 5:39:28

去年的国自然本子修改之后可以今年再提交吗?

国自然评审意见公布后&#xff0c;不少科研同行都会陷入困境&#xff1a;拿到修改意见无从下手&#xff0c;去年未中的本子改来改去仍抓不住重点&#xff0c;专家点评言简意赅却藏着“潜台词”&#xff0c;解读不到位就会南辕北辙。作为过来人&#xff0c;我深知国自然修改比初…

作者头像 李华
网站建设 2026/3/16 3:36:51

DeerFlow镜像免配置:预置Chrome Headless环境保障稳定网页渲染

DeerFlow镜像免配置&#xff1a;预置Chrome Headless环境保障稳定网页渲染 1. 引言&#xff1a;告别网页渲染的烦恼 你有没有遇到过这样的场景&#xff1f;好不容易部署好一个AI研究工具&#xff0c;想让它帮你搜索资料、分析网页内容&#xff0c;结果第一步就卡住了——网页…

作者头像 李华