由于中断信号的突发性,CPU要捕获中断信号,有两种方式。一是不断轮询是否有中断发生,这样有点傻;二是通过中断机制,过程如下:

中断源 ---> 中断信号  --->  中断控制器 --->  CPU

 中断源有很多,CPU拿到中断信号,如何区分是哪一个中断源产生?那么一定有一个序列,标识不同的中断源发出的信号,这就是中断号了。

 

ARM裸机开发中,使用中断前需要进行不少配置,比较繁琐。而从内核的角度,我们只要明确两个目标

  • 中断号
  • 中断的处理方法

 

实验:在驱动中添加中断机制,按键触发外部中断,中断产生后,驱动打印中断信息。

 

步骤

定义中断号

在通过原理图,芯片手册查询,从硬件连接最终定位到中断号

 

通过源码,系统设备树描述中

每一个设备的节点都要有一个compatible属性 ,用来查找节点(也可以通过节点名或节点路径查找指定节点);interrupt-parent表示结点继承至gic。

root@linux:~/linux-3.14-fs4412# vim arch/arm/boot/dts/exynos4x12-pinctrl.dtsi

		 gpx1: gpx1 {
                        gpio-controller;
                        #gpio-cells = <2>;

                        interrupt-controller;
                        interrupt-parent = <&gic>;
                        interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
                                     <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;
                        #interrupt-cells = <2>;
                };

 

手动定义设备树节点,参考上面的系统描述和硬件设备号。定义如下

root@linux:~/linux-3.14-fs4412# vim arch/arm/boot/dts/exynos4412-fs4412.dts

key_int_node{
     compatible = "test_key";
     interrupt-parent = <&gpx1>;
     interrupts = <2 4>;
};

 

重新编译设备树,并更新tftp根目录下的设备树文件

make dtbs
cp -raf arch/arm/boot/dts/exynos4412-fs4412.dtb  /tftpboot/

 

编写驱动代码

get_irqno_from_node函数通过设备树的路径到设备结点的中断号;然后request_irq申请中断,并设置触发方式是双边沿触发,key_irq_handler指定为中断处理函数;在key_irq_handler中只有一条打印信息,当中断产生,触发这条函数,打印信息;模块卸载时,通过free_irq释放掉之前申请的中断资源。

//key_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>

#include <asm/io.h>
#include <asm/uaccess.h>

irqreturn_t key_irq_handler(int irqno, void *devid);



static int irqno;


int get_irqno_from_node(void)
{	
	//从设备树路径,查找节点
	struct device_node *np = of_find_node_by_path("/key_int_node");
	if(np){
		printk("find node ok\n");
	}else{
		printk("find node failed\n");
	}

	int irqno = irq_of_parse_and_map(np, 0);
	printk("irqno = %d\n", irqno);
	
	return irqno;
}


static int __init key_drv_init(void)
{
	int ret;
	
	//拿到中断号
	irqno = get_irqno_from_node();

	//申请中断资源
	ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, 
					"key3_eint10", NULL);
	if(ret != 0)
	{
		printk("request_irq error\n");
		return ret;
	}

	return 0;
}

static void __exit key_drv_exit(void)
{
	//释放中断资源
	free_irq(irqno, NULL);

}


irqreturn_t key_irq_handler(int irqno, void *devid)
{
	printk("-------%s-------------\n", __FUNCTION__);
	
	return IRQ_HANDLED;
}



module_init(key_drv_init);
module_exit(key_drv_exit);

MODULE_LICENSE("GPL");

 

Makefile文件

ROOTFS_DIR = /nfs/rootfs

ifeq ($(KERNELRELEASE), )

KERNEL_DIR = /mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412
CUR_DIR = $(shell pwd)

all :
	make -C  $(KERNEL_DIR) M=$(CUR_DIR) modules

clean :
	make -C  $(KERNEL_DIR) M=$(CUR_DIR) clean
	
install:
	cp -raf *.ko   $(ROOTFS_DIR)/drv_module


else

obj-m += key_drv.o



endif

 

编译

Ubuntu环境编译,目标文件输出到nfs目录,nfs共享给开发板执行。

root@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make
root@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make install

 

实验结果

按下按键(1->0),触发一次中断。松下按键(0->1),又触发一次中断。