目录:
1、中断号
2、获取中断号
3、实现中断处理
4、中断编程—实现字符设备驱动框架
5、驱动实现将硬件数据传递给数据
6、示例
1、中断号
中断号是系统分配给每个中断源的代号,以便识别和处理。在采用向量中断方式的中断系统中,CPU必须通过它才可以找到中断服务程序的入口地址,实现程序的转移。
在ARM裸机中实现中断需要配置:
1 I/O口为中断模式,触发方式,I/O口中断使能
2 设置GIC中断使能,分发配置,分发总使能,CPU外部中断接口使能,中断优先级
在linux内核中实现中断,只需要知道:
1 中断号是什么,怎么得到中断号
2 中断处理方法
2、获取中断号的方法:
1)宏定义
在没有设备树的内核中,中断号定义为宏,IRQ_EINT
2)设备树文件中
arch/arm/boot/dts/exynos4412-fs4412.dts
1)看原理图,芯片手册找到中断源对应的中断号SPI Port No
2)进入设备树,在arch/arm/boot/dts/exynos4x12-pinctrl.dtsi中
1 gpx1: gpx1 {
2 gpio-controller;
3 #gpio-cells = [span style="color: rgba(128, 0, 128, 1)">2
4
5 interrupt-controller; //中断控制器
6 interrupt-parent = ; //继承于gic
7 interrupts = [span style="color: rgba(128, 0, 128, 1)">0 24 00 25 00 26 00 27 0
8 [span style="color: rgba(128, 0, 128, 1)">0 28 00 29 00 30 00 31 0
9 #interrupt-cells = [span style="color: rgba(128, 0, 128, 1)">2
10 };
括号中的24、 25等对应于SPI Port No,以上是系统中已经定义好的节点
在编程中,需要定义自己的节点,用来描述按键,打开可编辑的设备树文件:
arch/arm/boot/dts/exynos4412-fs4412.dts,进入文件。
3)定义节点,描述当前设备用的中断号
1 key_int_node{
2 compatible = "test_key";
3 interrupt-parent = ; //继承于gpx1
4 interrupts = [span style="color: rgba(128, 0, 128, 1)">2 4
5 }; //interrupts里长度由父母的-cell决定
再举个栗子,设置k4 --- GPX3_2(XEINT26) 的节点,中断号
1 key_int_node{
2 compatible = "test_key";
3 interrupt-parent = ; //继承于gpx3
4 interrupts = [span style="color: rgba(128, 0, 128, 1)">2 4
5 };
中断号的定位方法:
看I/O引脚,GPX1_2,中断号就是GPX1里面的第2个
4)编译设备树:make dtbs
更新设备树文件: cp -raf arch/arm/boot/dts/exynos4412-fs4412.dtb /tftpboot/
查看定义的节点:在根目录的 proc/device-tree/目录下
3、实现中断处理方法
在驱动中通过代码获取到中断号,并且申请中断
先看一下中断相关的函数:
1 a,获取到中断号码:
2 int get_irqno_from_node(void)
3 {
4 // 获取到设备树中的节点
5 struct device_node np = of_find_node_by_path("/key_int_node");
6 if(np){
7 printk("find node ok\n");
8 }else{
9 printk("find node failed\n");
10 }
11
12 // 通过节点去获取到中断号码
13 int irqno = irq_of_parse_and_map(np, 0);
14 printk("irqno = %d\n", irqno);
15
16 return irqno;
17 }
18 b,申请中断
19 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char name, void dev)
20 参数1: irq 设备对应的中断号
21 参数2: handler 中断的处理函数
22 typedef irqreturn_t (irq_handler_t)(int, void );
23 参数3:flags 触发方式
24 #define IRQF_TRIGGER_NONE 0x00000000 //内部控制器触发中断的时候的标志
25 #define IRQF_TRIGGER_RISING 0x00000001 //上升沿
26 #define IRQF_TRIGGER_FALLING 0x00000002 //下降沿
27 #define IRQF_TRIGGER_HIGH 0x00000004 // 高点平
28 #define IRQF_TRIGGER_LOW 0x00000008 //低电平触发
29 参数4:name 中断的描述,自定义,主要是给用户查看的
30 /proc/interrupts
31 参数5:dev 传递给参数2中函数指针的值
32 返回值: 正确为0,错误非0
33
34
35 参数2的赋值:即中断处理函数
36 irqreturn_t key_irq_handler(int irqno, void devid)
37 {
38 return IRQ_HANDLED;
39 }
43
44 c, 释放中断:
45 void free_irq(unsigned int irq, void dev_id)
46 参数1: 设备对应的中断号
47 参数2:与request_irq中第5个参数保持一致
代码实现获取中断号,并注册中断,按下按键引发中断,打印信息
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11
12 int irqno; //中断号
13
14
15 irqreturn_t key_irq_handler(int irqno, void devid)
16 {
17 printk("----------%s---------",FUNCTION);
18 return IRQ_HANDLED;
19 }
20
21
22 //获取中断号
23 int get_irqno_from_node(void)
24 {
25 //获取设备树中的节点
26 struct device_node *np = of_find_node_by_path("/key_int_node");
27 if(np){
28 printk("find node success\n");
29 }else{
30 printk("find node failed\n");
31 }
32
33 //通过节点去获取中断号
34 int irqno = irq_of_parse_and_map(np, 0);
35 printk("iqrno = %d",irqno);
36
37 return irqno;
38 }
39
40
41
42 static int init key_drv_init(void)
43 {
44 //演示如何获取到中断号
45 int ret;
46
47 irqno = get_irqno_from_node();
48
49 ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
50 "key3_eint10", NULL);
51 if(ret != 0)
52 {
53 printk("request_irq error\n");
54 return ret;
55 }
56
57 return 0;
58 }
59
60 static void exit key_drv_exit(void)
61 {
62 free_irq(irqno, NULL); //free_irq与request_irq的最后一个参数一致
63 }
64
65
66
67 module_init(key_drv_init);
68 module_exit(key_drv_exit);
69
70 MODULE_LICENSE("GPL");
key_drv.c
测试效果:
按键按下,打印信息,但出现了按键抖动
cat /proc/interrupt
4、 中断编程 --- 字符设备驱动框架
1 // 1,设定一个全局的设备对象
2 key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);
3
4 // 2,申请主设备号
5 key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);
6
7 // 3,创建设备节点文件
8 //代码效果参考:http://www.jhylw.com.cn/414939743.html
key_dev->cls = class_create(THIS_MODULE, "key_cls");9 key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major,0), NULL, "key0");
10
11 // 4,硬件初始化:
12 a.地址映射
13 b.中断申请
5、驱动实现将硬件所产生的数据传递给用户
1)硬件如何获取数据
key: 按下和抬起: 1/0
读取key对应的gpio的状态,可以判断按下还是抬起
读取key对应gpio的寄存器--数据寄存器
//读取数据寄存器
int value = readl(key_dev->reg_base + 4) & (1[2);
2)驱动传递数据给用户
在中断处理中填充数据:
key_dev->event.code = KEY_ENTER;
key_dev->event.value = 0;
在xxx_read中奖数据传递给用户
ret = copy_to_user(buf, &key_dev->event, count);
3)用户获取数据
while(1)
{
read(fd, &event, sizeof(struct key_event));
if(event.code == KEY_ENTER)
{
if(event.value)
{
printf("APP key enter pressed\n");
}else{
//代码效果参考:http://www.jhylw.com.cn/062323019.html
printf("APP key enter up\n");}
}
}
<a href="javascript:void(0);" onclick="co