You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Linux内核模块中通过irq_data判断中断触发边沿的方法咨询

树莓派3B+内核模块中断边沿判断问题解答

为什么当前代码输出总是3?

irqd_get_trigger_type()返回的是你调用request_irq()时设置的触发类型掩码,而非本次中断实际触发的边沿。你注册中断时用了IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,这两个宏的数值分别为1和2,按位或的结果就是3,所以无论实际触发的是上升沿还是下降沿,该函数都会返回3。

能不能通过struct irq_data获取实际触发边沿?

不能。树莓派搭载的BCM2835芯片的中断控制器及对应Linux内核驱动,不会将本次中断的具体触发边沿信息存入irq_data结构。irq_data主要保存中断的静态配置信息(比如你设置的触发掩码),而非中断触发时的动态事件细节。

可行的解决方案

目前最可靠的方式是在中断处理函数中读取当前引脚电平,结合上一次的电平状态判断边沿类型,具体实现如下:

修改后的代码示例

#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqnr.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("");
MODULE_DESCRIPTION("");

#define InputPinLine1 17
unsigned int IrqNumberInputLine1;

static irq_handler_t IrqCallbackInputLine1(unsigned int irq, void *dev_id, struct pt_regs *regs) 
{
    static int last_level = -1; // 初始化为无效值,避免第一次中断误判
    int current_level = gpio_get_value(InputPinLine1);

    printk("------Line 1 ISR\n");
    if (last_level != -1) {
        if (last_level == 0 && current_level == 1) {
            printk("触发类型: 上升沿\n");
        } else if (last_level == 1 && current_level == 0) {
            printk("触发类型: 下降沿\n");
        } else {
            printk("触发类型: 未知(电平无变化)\n");
        }
    }
    last_level = current_level;

    return (irq_handler_t) IRQ_HANDLED; 
}

static int __init ModuleInit(void) 
{
    printk("Loading module... \n");

    if (gpio_request(InputPinLine1, "rpi-gpio-17")) {
        printk("Error! Can not allocate GPIO InputPinLine1\n");
        return -1;
    }
    if (gpio_direction_input(InputPinLine1)) {
        printk("Error! Can not set GPIO 17 to input!\n");
        gpio_free(InputPinLine1);
        return -1;
    }
    IrqNumberInputLine1 = gpio_to_irq(InputPinLine1);
    if (request_irq(IrqNumberInputLine1, (irq_handler_t) IrqCallbackInputLine1, 
                    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "IrqCallbackInputLine1", NULL) != 0) {
        printk("Error! Can not request interrupt nr.: %d\n", IrqNumberInputLine1);
        gpio_free(InputPinLine1);
        return -1;
    }
    // 初始化last_level为当前引脚电平
    last_level = gpio_get_value(InputPinLine1);

    return 0;
}

static void __exit ModuleExit(void) 
{
    printk("Unloading module... \n");
    free_irq(IrqNumberInputLine1, NULL);
    gpio_free(InputPinLine1);
}

module_init(ModuleInit);
module_exit(ModuleExit);

注意事项

  • 模块初始化时需先读取一次引脚电平,初始化last_level,避免第一次中断时的无效判断
  • 若存在多CPU并发触发中断的场景,建议给last_level添加自旋锁保护,防止竞态问题(单核心场景可忽略)

内容的提问来源于stack exchange,提问作者danrudn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:02:04