-
KEY 硬件原理图
如下图所示,按键共有4个,K1——K4,对应的中断号为 XEINT26——29(并非与硬件实际的中断号对应),其对应的GPIO 引脚为GPX3_2——5。
-
KEY 相关寄存器
1)GPX3CON:GPIO PIN 模式设置寄存器
GPX3_2,对应的中断模式需设置为0xF,GPX3_2——5 ==》EXT_INT43[5——2]
......
2)EXT_INT43_CON:中断触发方式设置寄存器
EXT_INT43[5——2],中断触发方式,设置为0x4,上下边沿都触发;
......
......
从如下图,可知EXT_INT43,对应的中断ID 为64.
-
中断简介
1)中断源分类1:
中断包含内部中断和外部中断:内部中断:如若中断由控制器或者SOC 上的完整的设备产生的;比如 触摸屏、串口等中断方式;
外部中断:即只连接到GPIO 引脚上,中断信号通过SOC引脚从SOC外部得到的中断,没有其他外部的设备类控制其中断的发生,只能通过设置SFR 来配置中断模式、触发方式等;4412外部中断共计32个:
外部中断 | 中断ID | 中断号 |
中断 0—15 | 48——63 | EINT[0:15] |
中断16—31 | 64 | EINT[16:31] |
2)中断源分类2:
共享外部中断:公共的外接中断,中断源ID号: SPI[127:0]
私有中断:私有外设中断 中断源ID号: PPI[15:0]
软中断:由软件设置的中断源 中断源ID号: SGI[15:0]
3)中断处理流程:
首先保存断点 ==》查找中断入口函数 ==》中断处理 ==》返回断点;
- 中断源触发中断;
- 首先确认GIC是否接收到中断;然后确认是否将中断转给CPU;
- 首先确认CPU是否接收到中断;然后确认接受的中断号的权限及优先级;
3)exynos4412中断简介:
中断控制器 gic 作用是进行中断时间的接收和转发;其资源共160个中断源(软中断16个、私有16个、外部128个),而CPU只识别中断号,所以中断处理的第一步就是获取硬件触发的中断源,并转化为CPU可识别的中断号,该过程由gic完成。
4)中断体系结构
略;待补充后补全;
【遗留】exynos 4412芯片如何将中断和中断号对应
-
中断资源注册函数
1)编写驱动中断模块是,利用内核的如下函数接口注册中断:
int request_irq(
unsigned int irq, //中断号
irq_handler_t handler, //中断处理函数
unsigned long flags, //中断注册的触发方式
const char *name, //中断的名字
void *dev //给中断处理函数传入的参数
);
2)中断参数解释:
irq: //中断号
1> 对于内部中断号
arch/arm/mach-exynos/include/mach/irqs.h
2> 对于外部中断对应的中断号的获取
a. IRQ_EINT(eint);
b. 在知道gpio编号的情况下,可以通过gpio的编号,得到对应注册的中断号
gpio_to_irq(gpionum); //通过gpio编号返回中断号
handler: //中断处理函数
typedef irqreturn_t (*irq_handler_t)(int, void *);
中断处理函数的形参:(发生中断的中断号, 注册中断时为其传入的参数)
中断处理函数返回值:
IRQ_NONE //中断处理失败
IRQ_HANDLED //中断成功处理
flags: //中断注册的触发方式
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_SHARED 0x00000080
name: //中断的名字
dev: //给中断处理函数传入的参数
中断发生后,由内核调用处理函数时传给处理函数的第二实参
-
驱动程序代码
1)按键中断初始化:
5 // KEY 中断初始化: 中断号、按键名、按键计数
6 struct millkey{
7 int irqnum;
8 char *name;
9 int keycnt;
10 }keys[] = {
11 { IRQ_EINT(26), "KEY1", 0 },
12 { IRQ_EINT(27), "KEY2", 0 },
13 { IRQ_EINT(28), "KEY3", 0 },
14 { IRQ_EINT(29), "KEY4", 0 },
15 };
2)注册中断函数:
71 /*
72 ** driver init call func
73 */
74 static int __init demo_init(void)
75 {
76 return register_keys();
77 }
30 /*
31 ** register key irq devices
32 */
33 static int register_keys(void)
34 {
35 int i;
36 int ret;
37
38 for (i = 0; i < ARRAY_SIZE(keys); ++i) {
39 ret = request_irq(
40 keys[i].irqnum,
41 do_key_handler,
42 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
43 keys[i].name,
44 &keys[i] // irq handler's arg
45 );
46
47 if (ret < 0) {
48 goto error0;
49 }
50 }
51
52 return 0;
53
54 error0:
55 while (i--) {
56 free_irq(keys[i].irqnum, &keys[i]);
57 }
58
59 return ret;
60 }
3)中断处理函数:
17 static irqreturn_t do_key_handler(int irqnum, void *data)
18 {
19 struct millkey *pdev = data;
20
21 printk();
22 pdev->keycnt++;
23
24 printk("%s is %s, keycnt = %d\n", pdev->name,
25 pdev->keycnt%2?"press down":"release up", pdev->keycnt);
26
27 return IRQ_HANDLED;
28 }
4)中断注销函数:
62 static void unregister_keys(void)
63 {
64 int i;
65
66 for (i = 0; i < ARRAY_SIZE(keys); ++i) {
67 free_irq(keys[i].irqnum, &keys[i]);
68 }
69 }
70
79 module_init(demo_init);
80
81 static void __exit demo_exit(void)
82 {
83 unregister_keys();
84 }
-
测试程序代码
中断是由硬件触发,故无应用层的测试程序;
-
测试结果
1)插入驱动模块
[root@millet0923_interrupt]#
[root@millet0923_interrupt]# insmod demo.ko
[root@millet0923_interrupt]#
[root@millet0923_interrupt]#
[root@millet0923_interrupt]#
[root@millet0923_interrupt]# find / -name KEY*
/proc/irq/445/KEY4
/proc/irq/444/KEY3
/proc/irq/443/KEY2
/proc/irq/442/KEY1
[root@millet0923_interrupt]#
[root@millet0923_interrupt]#
2)查看当前已注册的中断,及其发生的情况
[root@millet0926_03_interrupt]# cat /proc/interrupts
CPU0 CPU1
28: 63591 472 GIC MCT
67: 0 0 GIC dma-pl330.0
68: 0 0 GIC dma-pl330.1
75: 0 0 GIC abc4412-wdt
......
......
442: 0 0 exynos-eint KEY1
443: 0 0 exynos-eint KEY2
444: 0 0 exynos-eint KEY3
445: 0 0 exynos-eint KEY4
3)按下按键,触发中断,可看到内核打印的日志;
中断设置双边沿触发,因按键有抖动,故实际按下1次,但弹道多次触发打印
[root@millet0923_interrupt]#
[root@millet0923_interrupt]#
[root@millet0923_interrupt]# [ 7945.490000] KEY1 is release up
[ 7945.625000] KEY1 is press down
[ 7945.625000] KEY1 is release up
[ 7946.225000] KEY2 is release up
[ 7946.350000] KEY2 is press down
[ 7946.940000] KEY3 is release up
[ 7947.110000] KEY3 is press down
[ 7947.665000] KEY4 is press down
[ 7947.665000] KEY4 is release up
[ 7947.665000] KEY4 is press down
[ 7947.665000] KEY4 is release up
[ 7947.790000] KEY4 is press down
[root@millet0923_interrupt]#
-
总结:
-
遗留问题
ret = request_irq(
40 keys[i].irqnum,
41 do_key_handler,
42 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
43 keys[i].name,
44 &keys[i] // irq handler's arg
45 );
1)如上中断注册函数,中断号 keys[i].irqnum 是如何和内核内核注册的硬件中断ID联系起来的?可参考如下文档,待分析;
https://blog.csdn.net/zqixiao_09/article/details/50926677
https://blog.csdn.net/zqixiao_09/article/details/50908125?locationNum=5&fps=1
https://blog.csdn.net/zqixiao_09/article/details/50916212
https://www.cnblogs.com/wang_yb/archive/2013/04/19/3030345.html《linux 内核设计与实现阅读笔记》
2)代码没有消抖,故设置双边沿触发时,会检测到多次中断,增加消抖处理机制;
定时器定时检测消抖、delay(ms)死等延时消抖、中断电平检测消抖? 分析优缺点;
- 若应用程序需根据按键功能实现需求,则它如何知晓哪个按键被按下了呢?