news 2026/4/18 12:51:28

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
中断下半部:延迟工作实验

一、该驱动程序的功能

这个驱动程序是基于 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查看)。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 7:57:11

手把手教你排查Raspberry Pi上spidev0.0 read255

当SPI读出全是0xFF&#xff1f;别慌&#xff0c;带你一步步揪出Raspberry Pi上 spidev0.0 read255 的真凶 你有没有遇到过这种情况&#xff1a;在树莓派上用C通过 /dev/spidev0.0 读取一个SPI传感器&#xff0c;结果每次收到的数据都是 0xFF &#xff08;也就是十进制255…

作者头像 李华
网站建设 2026/4/18 7:41:07

LED阵列汉字显示实验在公交站牌中的实战案例

从实验室到街头&#xff1a;一场关于LED汉字显示的硬核实战你有没有在等公交时&#xff0c;盯着站牌上那条缓慢滚动的红色文字发过呆&#xff1f;“15路 还有2分钟到达”——简单几个字&#xff0c;背后却是一整套嵌入式系统在默默运行。这看似普通的显示效果&#xff0c;其实正…

作者头像 李华
网站建设 2026/4/18 7:29:02

PaddlePaddle批量处理折扣:大批量任务费用优化

PaddlePaddle批量处理折扣&#xff1a;大批量任务费用优化 在AI工业化落地的今天&#xff0c;一个现实问题正摆在企业面前&#xff1a;如何用有限的算力预算&#xff0c;支撑动辄百万级图像识别、文本解析或目标检测任务&#xff1f;尤其是在中文OCR、电商内容审核、金融票据处…

作者头像 李华
网站建设 2026/4/18 7:22:05

通过Arduino实现L298N驱动直流电机启停控制核心要点

从零开始掌握直流电机控制&#xff1a;用Arduino和L298N实现精准启停与调速你有没有遇到过这样的情况&#xff1f;花了一下午时间接好线&#xff0c;烧录代码&#xff0c;结果电机不转、抖动、甚至模块发烫冒烟……最后发现只是电源没共地&#xff0c;或者PWM脚接错了&#xff…

作者头像 李华
网站建设 2026/4/18 8:16:53

【2025最新】基于SpringBoot+Vue的旅游管理系统管理系统源码+MyBatis+MySQL

&#x1f4a1;实话实说&#xff1a;C有自己的项目库存&#xff0c;不需要找别人拿货再加价。摘要 随着全球旅游业的快速发展和数字化转型&#xff0c;旅游管理系统的需求日益增长。传统旅游管理模式在信息处理、资源调度和用户体验方面存在诸多不足&#xff0c;亟需通过技术手段…

作者头像 李华
网站建设 2026/4/18 1:44:00

Windows平台x64dbg下载配置实战案例

从零开始配置x64dbg&#xff1a;新手也能上手的Windows逆向调试实战指南 你是不是也曾在搜索“x64dbg下载”时&#xff0c;面对五花八门的镜像站和版本选择一头雾水&#xff1f; 明明只想安个调试器&#xff0c;结果刚打开就被杀毒软件报警、界面全是英文、插件不会装……更别…

作者头像 李华