一、中断的 request_irq 注册接口
c
// include/linux/interrupt.h
static inline int
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);1
2
3
4
2
3
4
request_irq() 是 Linux 系统中较老的中断注册函数。从 Linux 2.6.30 版本开始,新增了 request_threaded_irq() 这个通过线程处理中断的新接口。这种线程化中断处理方式是实时 Linux 系统开发中的新功能,主要目的是减少中断处理对系统实时性能的延迟影响。
区别
旧版函数把中断处理全放在内核中断上下文中执行,而新版函数允许将部分处理转移到独立线程中完成。这样可以避免长时间的中断处理占用 CPU,从而提升系统对实时任务的响应能力。
二、request_threaded_irq
c
// include/linux/interrupt.h
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id);1
2
3
4
2
3
4
request_threaded_irq() 参数说明
- irq(中断号):表示软件分配的中断编号,与硬件物理中断号不同。
- handler(主处理函数):中断触发时最先执行的函数。若未指定主处理函数(设为 NULL),但提供了线程函数(
thread_fn),则系统会自动调用默认主处理函数irq_default_primary_handler()。 - thread_fn(线程处理函数):用于在内核线程中执行的处理函数。若设置此参数,系统会自动创建线程。
- irqflags(中断标志):设置中断行为的标志位。
- devname(设备名称):用于标识中断来源的名称。
- dev_id(设备参数):需要传递给中断处理函数的参数。
注意
主处理函数(handler)和线程函数(thread_fn)不能同时为空。
1、IRQF_ 的中断标志
c
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_TRIGGER_PROBE 0x00000010
#define IRQF_SHARED 0x00000080
#define IRQF_PROBE_SHARED 0x00000100
#define __IRQF_TIMER 0x00000200
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
#define IRQF_NO_SUSPEND 0x00004000
#define IRQF_FORCE_RESUME 0x00008000
#define IRQF_NO_THREAD 0x00010000
#define IRQF_EARLY_RESUME 0x00020000
#define IRQF_COND_SUSPEND 0x000400001
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
触发类型
IRQF_TRIGGER_NONE:不指定任何触发条件(默认状态)。IRQF_TRIGGER_RISING:当信号线从低到高变化时触发。IRQF_TRIGGER_FALLING:当信号线从高到低变化时触发。IRQF_TRIGGER_HIGH:信号线保持高电平时触发。IRQF_TRIGGER_LOW:信号线保持低电平时触发。IRQF_TRIGGER_MASK:允许组合多种触发条件。
共享与特殊功能
IRQF_SHARED:允许多个设备共享同一个中断线。IRQF_PROBE_SHARED:提示系统可能存在共享冲突需要检测。IRQF_PERCPU:让中断只分配给特定 CPU 核心。IRQF_NOBALANCING:禁止系统自动调整中断在 CPU 间的分配。IRQF_IRQPOLL:将中断转为轮询模式。IRQF_ONESHOT:处理完中断后不再自动重新启用。IRQF_NO_SUSPEND:即使系统休眠,该中断仍保持启用状态。IRQF_FORCE_RESUME:系统恢复时强制启用中断。IRQF_NO_THREAD:禁止将中断处理转为线程模式。IRQF_EARLY_RESUME:系统恢复时优先处理该中断。
2、IRQS_ 的中断标志
文件位置
kernel/irq/internals.h
c
enum {
IRQS_AUTODETECT = 0x00000001,
IRQS_SPURIOUS_DISABLED = 0x00000002,
IRQS_POLL_INPROGRESS = 0x00000008,
IRQS_ONESHOT = 0x00000020,
IRQS_REPLAY = 0x00000040,
IRQS_WAITING = 0x00000080,
IRQS_PENDING = 0x00000200,
IRQS_SUSPENDED = 0x00000800,
IRQS_TIMINGS = 0x00001000,
};1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
常用状态标志:
IRQS_ONESHOT:表示中断会"只触发一次处理"。通常在注册中断时通过设置IRQF_ONESHOT选项开启。IRQS_PENDING:当中断被标记为"待处理"时会启用这个标志。
三、实验代码
引脚编号计算
- 公式 1:GPIO 小组编号计算:
number = group * 8 + X,其中 group 为小组号(A 为 0,B 为 1,C 为 2,D 为 3) - 公式 2:GPIO pin 脚计算:
pin = bank * 32 + number,其中 bank 为组号
故:GPIO1_A4 → 1 * 32 + 0 * 8 + 4 = 36
实验代码:
c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#define GPIO_PIN 36
// 中断处理函数
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
printk(KERN_INFO "Interrupt occurred on GPIO %d\n", GPIO_PIN);
printk(KERN_INFO "This is irq_handler\n");
return IRQ_HANDLED;
}
static int __init interrupt_init(void)
{
int irq_num;
printk(KERN_INFO "Initializing GPIO Interrupt Driver\n");
// 将 GPIO 引脚映射到中断号
irq_num = gpio_to_irq(GPIO_PIN);
printk(KERN_INFO "GPIO %d mapped to IRQ %d\n", GPIO_PIN, irq_num);
// 请求中断
if (request_irq(irq_num, gpio_irq_handler, IRQF_TRIGGER_RISING, "irq_test", NULL) != 0) {
printk(KERN_ERR "Failed to request IRQ %d\n", irq_num);
gpio_free(GPIO_PIN);
return -ENODEV;
}
return 0;
}
static void __exit interrupt_exit(void)
{
int irq_num = gpio_to_irq(GPIO_PIN);
free_irq(irq_num, NULL);
printk(KERN_INFO "GPIO Interrupt Driver exited successfully\n");
}
module_init(interrupt_init);
module_exit(interrupt_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("linux");1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
四、实验截图
insmod interrupt.ko
驱动加载打印:

拉高 GPIO,触发中断服务程序,打印如下:
