天天看点

NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程

        首先是硬件初始化,主要是配置相应的寄存器:

#define PL_CFG0_REG 0x01F02C00
#define PL_EINT_CFG0 0x01F02E00
#define PL_EINT_CTL 0x01F02E10
#define PL_EINT_STATUS 0x01F02E14
#define PL_EINT_DEB 0x01F02E18

//设置PL3为外部中断模式:
	gpio_config = ioremap(PL_CFG0_REG,4);
	data = readl(gpio_config);
    data &= 0xffff0fff;
	data |= 0x00006000;
	writel(data,gpio_config);
	
	
	//下降沿触发中断
	gpio_config = ioremap(PL_EINT_CFG0,4);
	data = readl(gpio_config);
	data &= 0xffff0fff;
	data |= 0x00001000;
	writel(data,gpio_config);
	
	//使能中断
	gpio_config = ioremap(PL_EINT_CTL,4);
	data = readl(gpio_config);
	//data &= 0xFfffffff;
	data |= 0x00000008;
	writel(data,gpio_config);
           

        然后是注册中断号:

//注册中断处理程序
	/*IRQF_TRIGGER_FALLING:下降沿产生中断*/
	/*IRQ_EINT16_31(中断号)查找文件路径: /arch/arm/mach-s5pv210/include/mach/irqs.h 中*/
    //request_irq(gpio_to_irq(S5PV210_GPH2(0)),key_int,IRQF_TRIGGER_FALLING,"testkey",0);
    
	//request_irq(gpio_to_irq(43),key_int,IRQF_TRIGGER_FALLING,"testkey",0);
	//SUNXI_IRQ_EINTL          IRQF_TRIGGER_LOW|IRQF_SHARED
	val = request_irq(77,key_int,0,"testkey",NULL);
           

        在这个注册中断号这里走了很多的弯路:

        1、芯片引脚PL3的中断号不知道是多少,首先从芯片手册上查找,但是最后得出的结果是芯片PL这一组共用一个中断号,当时很不解,后来查找资料发现,PL这一组确实共用一个中断号,具体区分是哪个引脚的中断,有一个中断发生的寄存器,通过查看这个寄存器来判断是哪一个引脚发生的中断。从芯片手册上查到PL这一组的中断号是77,但是这个中断号和linux内核中的中断号是一样的吗?210开发板的时候,内核中的中断号的查找路径:

/*IRQ_EINT16_31(中断号)查找文件路径: /arch/arm/mach-s5pv210/include/mach/irqs.h 中*/
           

按照这个方式查找:

Z:\home\nanopi_h3\lichee\linux-3.4\arch\arm\mach-sunxi\include\mach\sun8i

下面有一个irqs-sun8iw7p1.h文件,这个文件里面有中断号。

NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程

        但是还不能太确定中断号是多少,根据推断,PL这组引脚的中断号应该为77,但是在向系统注册中断的时候一直返回错误码-16,表示注册中断失败,很可能是系统中已经把这个中断号注册了。

        然后用命令cat /proc/interrupts查看系统中已经申请的中断号,发现77已经被注册了。

        接下来就想,那么是不是可以使用共享中断呢?但是在经过漫长的尝试和查找资料后发现,如果使用共享中断的话,需要两边都将共享标志置位,加入系统中注册这个中断号的时候没有使用共享中断,那么其它的地方使用共享中断是不可以的,必须系统中申请这个中断号的时候也是用共享中断才可以。

        之后,就是想找到系统中是在什么地方申请的这个中断号?查找方法:根据irqs-sun8iw7p1.h文件中,PL中断的宏SUNXI_IRQ_EINTL,通过Source Insight在整个linux内核中查找,最好找到:

NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程

文件名:pinctrl-sun8iw7.c  路径:Z:\home\nanopi_h3\lichee\linux-3.4\drivers\pinctrl

推断,PL这组的中断号77很可能是在这里申请的,于是想从内核配置中将其去掉,结果发现从配置上面去不了:

NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程

然后就想,既然在这里注册的中断号,那么我把这里这的中断号给它改成一个其它的不存在的中断号,这样系统内核肯定就申请不到77这个中断号了,如图:

NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程

这个157是不存在中断号,这个应该不会影响系统的运行,最多也就是内核申请不到这个中断号。

编译、下载到M1的板子上,查看系统已经注册的中断号,发现77的中断号系统没有注册。这说明上面的推断是正确的。

NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程

        然后是触发类型这个参数:

NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程
NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程
NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程

最后,就是在按键处理函数中一定要清中断,否则会报错:

NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程
/*实现中断处理函数*/
irqreturn_t key_int(int irq, void *dev_id)
{	
	
	unsigned int data; 
    unsigned int *gpio_config;
	
    //1. 检测是否发生了按键中断
    
    
    //2. 清除已经发生的按键中断
    
    
    //3. 打印按键值
    printk("key down!\n");
    
	/*提交下半部*/
	/*将这些与硬件无关的代码程序提交给工作,有内核自动创建线程来执行,
	  这样就极大的减少了中断中执行程序的时间,中断中代码越少越好*/
	//3.挂载(提交)工作:
	/*这个挂载和提交只是将工作提交到工作队列中,工作并没有运行*/
	/*这个被挂载进队列的工作的运行是由系统内核自动创建的线程来给你运行的*/
	/*至于什么时候运行也是系统自己决定的,你无需管*/
	/*入口参数:工作队列指针;工作指针。指将这个工作挂载到这个工作队列中*/
	//queue_work(my_wq,work1);
	//schedule_work(work1);
	
	//清PL3中断:
	gpio_config = ioremap(PL_EINT_STATUS,4);
	data = readl(gpio_config);
	//data &= 0xFfffffff;
	data |= 0x08;
	writel(data,gpio_config);
	
	//button_exit();
	
    return 0;
    	
}
           

【另外需要注意的是】在注册中断的时候,返回-16一般是中断号被注册,-22是参数错误。在路径Z:\home\nanopi_h3\lichee\linux-3.4\drivers\input\keyboard下有按键的中断,在M1的板子上是专门的adc按键中断,正规的应该用这个按键,内核配置上对应:

NanoPi—M1(H3)———基于该平台的一个内核中的按键中断程序开发历程

完整代码:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/irq.h>

//#define GPHCON 0xE0200C40	/*tiny210v2开发板:按键K1->引脚GPH2_0->寄存器0xE0200C40*/

#define PA_CFG0_REG 0x01c20800
#define PA_CFG1_REG 0x01c20804
#define PA_DATA_REG 0x01c20810

//PA External Interrupt Configure Register 0
#define PA_INT_CFG0 0x01c20A00

//PA External Interrupt Control Register
#define PA_EINT_CTL_REG 0x01c20A10


#define PL_CFG0_REG 0x01F02C00
#define PL_EINT_CFG0 0x01F02E00
#define PL_EINT_CTL 0x01F02E10
#define PL_EINT_STATUS 0x01F02E14
#define PL_EINT_DEB 0x01F02E18

/*定义一个工作指针*/
struct work_struct *work1;

int key_open(struct inode *node,struct file *filp)
{
    return 0;	
}

struct file_operations key_fops = 
{
    .open = key_open,	
};

struct miscdevice key_miscdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "testkey",
    .fops = &key_fops,	
};


static void button_exit(void)
{
    misc_deregister(&key_miscdev);	
}


/*定义这项工作要执行的函数*/
void work1_fuc(struct work_struct *work)
{
	printk("this is work1->\n");
}


/*实现中断处理函数*/
irqreturn_t key_int(int irq, void *dev_id)
{	
	
	unsigned int data; 
    unsigned int *gpio_config;
	
    //1. 检测是否发生了按键中断
    
    
    //2. 清除已经发生的按键中断
    
    
    //3. 打印按键值
    printk("key down!\n");
    
	/*提交下半部*/
	/*将这些与硬件无关的代码程序提交给工作,有内核自动创建线程来执行,
	  这样就极大的减少了中断中执行程序的时间,中断中代码越少越好*/
	//3.挂载(提交)工作:
	/*这个挂载和提交只是将工作提交到工作队列中,工作并没有运行*/
	/*这个被挂载进队列的工作的运行是由系统内核自动创建的线程来给你运行的*/
	/*至于什么时候运行也是系统自己决定的,你无需管*/
	/*入口参数:工作队列指针;工作指针。指将这个工作挂载到这个工作队列中*/
	//queue_work(my_wq,work1);
	//schedule_work(work1);
	
	//清PL3中断:
	gpio_config = ioremap(PL_EINT_STATUS,4);
	data = readl(gpio_config);
	//data &= 0xFfffffff;
	data |= 0x08;
	writel(data,gpio_config);
	
	//button_exit();
	
    return 0;
    	
}

/*按键硬件初始化函数*/
void key_hw_init(void)
{
    unsigned int data; 
    unsigned int *gpio_config;
    
	/*将物理地址转化为虚拟地址,长度为4个字节*/
	/*将一个IO地址空间映射到内核的虚拟地址空间*/
	/*从起始地址GPHCON开始的4个字节的长度映射为虚拟地址,存放在指针变量gpio_cofig中*/
    //gpio_config = ioremap(PL_CFG0_REG,4);
	
	/*将地址(虚拟地址)gpio_config中的值读出来,放在变量data中。*/
    //data = readl(gpio_config);
    //data &= 0x0fffffff;
	//data |= 0x60000000;
	//writel(data,gpio_config);
	//writel(0x60000000,gpio_config);
	
	//设置PL3为外部中断模式:
	gpio_config = ioremap(PL_CFG0_REG,4);
	data = readl(gpio_config);
    data &= 0xffff0fff;
	data |= 0x00006000;
	writel(data,gpio_config);
	
	
	//下降沿触发中断
	gpio_config = ioremap(PL_EINT_CFG0,4);
	data = readl(gpio_config);
	data &= 0xffff0fff;
	data |= 0x00001000;
	writel(data,gpio_config);
	
	//使能中断
	gpio_config = ioremap(PL_EINT_CTL,4);
	data = readl(gpio_config);
	//data &= 0xFfffffff;
	data |= 0x00000008;
	writel(data,gpio_config);
	//
	//gpio_config = ioremap(PL_EINT_DEB,4);
	//data = readl(gpio_config);
	//data &= 0xFfffffff;
	//data |= 0x00000041;
	//writel(data,gpio_config);
	
	

	
	
	
	//创建工作:
	/*给指针分配空间*/
	//work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
	/*对相应的工作进行初始化:*/
	/*INIT_WORK函数的入口参数:要初始化的工作的指针;工作要执行的函数。*/
	//INIT_WORK(work1,work1_fuc);

}






	int id = 49;

/*模块初始化函数*/
static int button_init(void)
{
    int val = 0;
	
	
	misc_register(&key_miscdev);
    
    //按键硬件初始化
    key_hw_init();
    
    //注册中断处理程序
	/*IRQF_TRIGGER_FALLING:下降沿产生中断*/
	/*IRQ_EINT16_31(中断号)查找文件路径: /arch/arm/mach-s5pv210/include/mach/irqs.h 中*/
    //request_irq(gpio_to_irq(S5PV210_GPH2(0)),key_int,IRQF_TRIGGER_FALLING,"testkey",0);
    
	//request_irq(gpio_to_irq(43),key_int,IRQF_TRIGGER_FALLING,"testkey",0);
	//SUNXI_IRQ_EINTL          IRQF_TRIGGER_LOW|IRQF_SHARED
	val = request_irq(77,key_int,0,"testkey",NULL);
	
	printk("init key device ok!\n");
	
	printk("val = %d\n",val);
	
    return 0;
}




MODULE_LICENSE("GPL");

module_init(button_init);
module_exit(button_exit);