在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
相关产品推荐
相关产品推荐

