了解内容
Linux中查看系统中断的统计信息:
1
cat /proc/interrupts
接下来我们来实现BUTTON1、BUTTON2中断。在原理图中分别对应GPX1_1(home键)、GPX1_2(back键),相应中断号的宏分别是IRQ_EINT(9)
、IRQ_EINT(10)
。下图来自4412用户手册“2.5 Pin Description”章节。
开始
由于内核中已经申请过home、back按键的中断,首先需要从内核中取消“KEYBOARD_GPIO”,路径是“Device Drivers”–>“Input device support”–>“Keyboards”–>“GPIO Buttons”。取消后,编译内核镜像进行烧录会发现cat /proc/interrupts
中已经没有BUTTON1、BUTTON2中断了。
紧接着添加平台文件mach-itop4412.c中设备信息,模仿LED平台设备信息添加即可,为了简单实验这里就不用#if宏了,也就是始终编译而不用改Kconfig:
上述完成后,重新编译内核镜像再次烧录。接下来完成keyirq驱动即可。
一些函数
-
注册中断函数request_irq,在
include/linux/interrupt.h
中有定义1
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
传参 描述 irq 申请的硬件中断号,比如 IRQ_EINT(9)
宏、IRQ_EINT(10)
宏,可以在arch/arm/mach-s3c24xx/include/mach/irqs.h
中找到定义。handler 中断服务函数。 flags 中断触发条件,可同时使用多个,在 include/linux/interrupt.h
中有定义:
IRQF_TRIGGER_RISING :上升沿触发
IRQF_TRIGGER_FALLING :下降沿触发
IRQF_TRIGGER_HIGH:高电平触发
IRQF_TRIGGER_LOW:低电平触发(实测,如果始终是低电平会一直触发)name 中断名,会出现在 cat /proc/interrupts
中dev 填写设备结构体,或者NULL。也就是probe函数的传参,有没有发现probe函数是driver中的,但是probe传参是device中的结构体。 返回值 0表示成功;
-INVAL表示中断号无效或处理函数指针为NULL;
-EBUSY表示中断已经被占用且不能共享。 -
声明一个中断服务程序,其中dev_id就是request_irq传参中的dev,是一样的
1 2 3 4 5
static irqreturn_t my_interrupt(int irq,void *dev_id) { printk("receive a interrupt!\n"); return IRQ_HANDLED; }
-
释放中断函数free_irq
1
free_irq(unsigned int irq, void *dev_id);
例程
驱动例程
keyirq_demo.c
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
//中断头文件
#include <linux/irq.h>
#include <linux/interrupt.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("jerry");
#define DRV_NAME "keyirq"
static irqreturn_t eint9_handler(int irq,void *dev_id)
{
printk("receive a interrupt eint9!\n");
return IRQ_HANDLED;
}
static irqreturn_t eint10_handler(int irq,void *dev_id)
{
printk("receive a interrupt eint10!\n");
return IRQ_HANDLED;
}
static int demo_probe(struct platform_device *pdv){
int ret1,ret2;
/*申请中断资源*/
ret1 = request_irq(IRQ_EINT(9),eint9_handler,IRQF_TRIGGER_FALLING,"eint9",pdv);
ret2 = request_irq(IRQ_EINT(10),eint10_handler,IRQF_TRIGGER_FALLING,"eint10",pdv);
if((ret1 < 0)||(ret2 < 0)){
printk(KERN_EMERG "irq_request failed!\n");
return 1;
}
printk(KERN_EMERG "demo_probe ok!\n");
return 0;
}
static int demo_remove(struct platform_device *pdv){
free_irq(IRQ_EINT(9),pdv); /*释放中断资源*/
free_irq(IRQ_EINT(10),pdv); /*释放中断资源*/
printk(KERN_EMERG "demo_remove ok!\n");
return 0;
}
static void demo_shutdown(struct platform_device *pdv){
printk(KERN_EMERG "demo_shutdown ok!\n");
}
static int demo_suspend(struct platform_device *pdv, pm_message_t state){
printk(KERN_EMERG "demo_suspend ok!\n");
return 0;
}
static int demo_resume(struct platform_device *pdv){
printk(KERN_EMERG "demo_resume ok!\n");
return 0;
}
static struct platform_driver platform_drv_demo = {
.probe = demo_probe,
.remove = demo_remove,
.shutdown = demo_shutdown,
.suspend = demo_suspend,
.resume = demo_resume,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};
static int demo_init(void){
platform_driver_register(&platform_drv_demo);
return 0;
}
static void demo_exit(void){
platform_driver_unregister(&platform_drv_demo);
}
module_init(demo_init);
module_exit(demo_exit);
测试结果
EINT9和EINT10在原理图上连接的是按键home和按键back。
编写简单Makefile:参考这里
编译生成模块后,拷贝到开发板中:
-
首先加载模块后正常执行probe函数;
-
查看
cat /proc/interrupts
中断信息可以看到已经生成了中断”eint9”和”eint10”,中断均产生0次。
-
交替按下3次home键和2次back键,控制台会打印如下内核信息:
然后再次查看中断信息,eint9中断产生了3次,eint10中断产生了2次。