当前位置: 首页 > news >正文

中断下半部:延迟工作实验

一、该驱动程序的功能

这个驱动程序是基于 Linux 中断下半部的 “延迟工作” 机制,实现按键中断的消抖处理:当按键触发中断时,不会立即处理按键事件,而是调度一个 “延迟工作”(延迟指定时间,避开按键抖动的窗口期),待延迟时间结束后,再执行工作函数,以此过滤按键抖动导致的多次误触发,保证按键事件的有效识别。

二、实验完整步骤(含代码)

步骤 1:准备实验环境
  1. 确保已安装 Linux 内核源码(需与开发板内核版本一致)。
  2. 准备交叉编译工具链(适配开发板架构,如 ARM 架构的arm-linux-gnueabihf-)。
  3. 开发板端确保已开启 GPIO 中断支持(内核配置中启用对应 GPIO 子系统、工作队列子系统)。
步骤 2:编写驱动代码(文件名:interrupt.c
#include <linux/init.h> #include <linux/module.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/delay.h> // 定义工作队列、延迟工作 struct workqueue_struct *test_workqueue; struct delayed_work test_workqueue_work; // 1. 延迟工作对应的工作函数(中断下半部,延迟后执行) void test_work(struct work_struct *work) { msleep(100); // 模拟实际按键处理(如读取GPIO状态) printk("This is test_work (按键抖动已过滤,执行有效处理)\n"); } // 2. 中断处理函数(中断上半部,快速响应) irqreturn_t test_interrupt(int irq, void *args) { printk("This is test_interrupt (按键触发中断,调度延迟工作)\n"); // 调度延迟工作:在自定义工作队列上,延迟3秒(3*HZ,HZ是内核时钟节拍,默认1000) queue_delayed_work(test_workqueue, &test_workqueue_work, 3 * HZ); return IRQ_HANDLED; } static int irq; // 中断号 // 3. 驱动初始化函数 static int __init interrupt_irq_init(void) { int ret; // (1)将GPIO4转换为对应的中断号(假设按键接在GPIO4) irq = gpio_to_irq(4); printk("irq is %d\n", irq); // (2)申请中断 ret = request_irq(irq, test_interrupt, IRQF_TRIGGER_RISING, "test", NULL); if (ret < 0) { printk("request_irq is error\n"); return -1; } // (3)创建自定义工作队列 test_workqueue = create_workqueue("test_workqueue"); // (4)初始化延迟工作(绑定工作函数test_work) INIT_DELAYED_WORK(&test_workqueue_work, test_work); printk("interrupt_irq_init success\n"); return 0; } // 4. 驱动退出函数 static void __exit interrupt_irq_exit(void) { // (1)释放中断 free_irq(irq, NULL); // (2)同步取消延迟工作(确保工作函数执行完毕) cancel_delayed_work_sync(&test_workqueue_work); // (3)销毁工作队列 destroy_workqueue(test_workqueue); printk("bye bye\n"); } module_init(interrupt_irq_init); module_exit(interrupt_irq_exit); MODULE_LICENSE("GPL");
步骤 3:编写 Makefile(文件名:Makefile

makefile

obj-m += interrupt.o KERNELDIR ?= /path/to/your/kernel/source # 替换为你的内核源码路径 PWD := $(shell pwd) all: make -C $(KERNELDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- # 适配开发板架构 clean: make -C $(KERNELDIR) M=$(PWD) clean
步骤 4:编译驱动模块

在主机端执行命令:

make

编译完成后,会生成interrupt.ko驱动模块文件。

步骤 5:加载驱动到开发板
  1. interrupt.ko通过 U 盘、SSH 等方式传到开发板。
  2. 开发板端执行加载命令:
insmod interrupt.ko
  1. 查看驱动加载状态(可选):
lsmod | grep interrupt
步骤 6:测试驱动功能
  1. 按下与 GPIO4 连接的按键,触发中断。
  2. 查看内核打印信息(开发板端执行):
dmesg -w
  • 会先打印This is test_interrupt (按键触发中断,调度延迟工作)(中断上半部)。
  • 延迟 3 秒后,打印This is test_work (按键抖动已过滤,执行有效处理)(延迟工作的下半部)。
步骤 7:卸载驱动模块

测试完成后,开发板端执行卸载命令:

rmmod interrupt

同时通过dmesg可查看退出打印的bye bye

我以RK3568 开发板为例,带你完成 “按键 + 延迟工作” 的完整实验,从硬件接线到软件验证一步到位:

一、先确认:开发板 GPIO 引脚对应(以 RK3568 为例)

RK3568 的 GPIO 编号规则是:编号 = 组索引*32 + 子组索引*8 + 引脚号,常用按键实验引脚(以 “GPIO0_A4” 为例):

开发板引脚丝印内核 GPIO 编号说明
GPIO0_A44对应内核中gpio=4

二、硬件接线(按键部分)

只需要 1 个按键 + 杜邦线:

  1. 按键的一端接开发板的GPIO0_A4引脚;
  2. 按键的另一端接开发板的GND引脚(接地);
  3. (可选)若开发板 GPIO 无内部上拉,需在GPIO0_A43.3V之间接一个 10KΩ 上拉电阻(大部分 RK 开发板 GPIO 默认内部上拉,可省略)。

三、实验步骤(分硬件验证→驱动修改→编译测试)

步骤 1:先验证按键硬件是否正常(用户空间临时测试)

先通过sysfs确认 GPIO 能识别按键状态,避免硬件问题:

  1. 开发板终端执行(导出 GPIO4):
    echo 4 > /sys/class/gpio/export
  2. 配置 GPIO4 为输入模式:
    echo in > /sys/class/gpio/gpio4/direction
  3. 读取 GPIO4 电平(未按按键时,内部上拉应为1;按下按键时变为0):
    cat /sys/class/gpio/gpio4/value
    → 按下按键再执行cat,若 value 从10,说明硬件接线正常。
步骤 2:完善驱动代码(补充 GPIO 申请逻辑)

之前的驱动缺少 GPIO 申请步骤,需修改interrupt.c(确保 GPIO 资源不冲突):

#include <linux/init.h> #include <linux/module.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/delay.h> struct workqueue_struct *test_workqueue; struct delayed_work test_workqueue_work; int gpio_num = 4; // 对应GPIO0_A4 int irq; // 延迟工作函数 void test_work(struct work_struct *work) { int key_val = gpio_get_value(gpio_num); // 读取按键电平 printk("按键当前电平:%d(已过滤抖动)\n", key_val); } // 中断处理函数 irqreturn_t test_interrupt(int irq, void *args) { printk("按键触发中断,调度延迟工作(3秒后执行)\n"); // 调度延迟工作(延迟3秒,避开按键抖动) queue_delayed_work(test_workqueue, &test_workqueue_work, 3 * HZ); return IRQ_HANDLED; } static int __init interrupt_irq_init(void) { int ret; // 1. 申请GPIO if (gpio_request(gpio_num, "key_gpio") < 0) { printk("GPIO%d申请失败\n", gpio_num); return -EBUSY; } // 2. 配置GPIO为输入 if (gpio_direction_input(gpio_num) < 0) { printk("GPIO%d配置输入失败\n", gpio_num); gpio_free(gpio_num); return -EINVAL; } // 3. 获取GPIO对应的中断号 irq = gpio_to_irq(gpio_num); printk("按键对应的中断号:%d\n", irq); // 4. 申请中断(触发方式:下降沿,对应按键按下时电平从1→0) ret = request_irq(irq, test_interrupt, IRQF_TRIGGER_FALLING, "key_irq", NULL); if (ret < 0) { printk("中断申请失败\n"); gpio_free(gpio_num); return -1; } // 5. 创建工作队列+初始化延迟工作 test_workqueue = create_workqueue("test_workqueue"); INIT_DELAYED_WORK(&test_workqueue_work, test_work); printk("驱动加载成功\n"); return 0; } static void __exit interrupt_irq_exit(void) { free_irq(irq, NULL); cancel_delayed_work_sync(&test_workqueue_work); destroy_workqueue(test_workqueue); gpio_free(gpio_num); // 释放GPIO printk("驱动卸载成功\n"); } module_init(interrupt_irq_init); module_exit(interrupt_irq_exit); MODULE_LICENSE("GPL");
步骤 3:编译驱动(同之前的 Makefile)

确保 Makefile 中KERNELDIR是你的 RK3568 内核源码路径,执行编译:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

生成interrupt.ko文件。

步骤 4:加载驱动并测试
  1. interrupt.ko传到开发板,执行加载:
    insmod interrupt.ko
  2. 查看内核日志(实时监控):
    dmesg -w
  3. 按下开发板上的按键,日志会输出:
    • 先打印:按键触发中断,调度延迟工作(3秒后执行)
    • 3 秒后打印:按键当前电平:0(已过滤抖动)(说明延迟工作执行,成功消抖)
步骤 5:卸载驱动(测试完成后)
rmmod interrupt

常见问题排查

  • 若中断不触发:检查 GPIO 引脚是否接错、中断触发方式是否匹配(比如改为IRQF_TRIGGER_RISING);
  • 若延迟工作不执行:确认工作队列是否成功创建(通过cat /proc/workqueue查看)。
http://www.jsqmd.com/news/146070/

相关文章:

  • PaddlePaddle镜像支持强化学习吗?RL模块使用初探
  • PaddlePaddle镜像中的分布式训练参数服务器模式配置
  • 跨平台开发效率提升:交叉编译最佳实践总结
  • PD多口适配器:多设备时代的充电效率革命
  • PaddlePaddle镜像常见问题解答(FAQ):新手避坑指南
  • 手把手教你排查Raspberry Pi上spidev0.0 read255
  • 一款开源网络流量监控与威胁检测工具,高颜值、跨平台
  • PaddlePaddle镜像能否直接读取HDFS数据?大数据对接方案
  • PaddlePaddle镜像是否支持Windows系统?Docker方案详解
  • 【无标题】人工智能通识
  • PaddleNLP全栈实践:基于PaddlePaddle镜像的文本分类与情感分析
  • PaddlePaddle镜像中的AutoDL模块介绍:自动网络结构搜索
  • ESP32-CAM图像数据传输流程图解说明
  • 【无标题】人工智能
  • PaddlePaddle批量处理折扣:大批量任务费用优化
  • PaddlePaddle镜像性能优化技巧:提升训练速度30%的秘密
  • 旅游管理系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
  • PaddlePaddle验证码验证:人机识别保障公平使用
  • 零基础搭建Arduino循迹小车:适配Uno的开发环境配置
  • PaddlePaddle镜像配合NAS进行神经架构搜索实战
  • 通过Arduino实现L298N驱动直流电机启停控制核心要点
  • 操作指南:ST7789V驱动在树莓派Pico上的移植步骤
  • 高速背板连接器区域的PCB布局布局实战指南
  • PaddlePaddle镜像如何支持ONNX模型导出?详细操作步骤
  • 从零实现工业网关通信:USB转485驱动实战
  • 隐私安全模块 Cordova 与 OpenHarmony 混合开发实战
  • PaddleRec推荐系统实战:基于PaddlePaddle镜像构建个性化推荐引擎
  • 精准投放:软文发稿的“靶心思维”
  • Windows平台x64dbg下载配置实战案例
  • 基于红外阵列的Arduino循迹小车:实战案例解析