传统方式和设备树方式获取中断

框架:

字符设备驱动,参照上文

主要分析作为驱动开发者,中断资源的获取的来龙去脉

以key为例。

硬件信息:

首先打开开发板PCB,芯片数据手册收集一波硬件信息

例如我这次要动手的是开发板上的Home键

在底板PCB搜索关键词“Home”

可见,它是连在UART_RING这个网络标签上,继续追查看它连着核心板的哪里

一查就看到了,得知了这个按键的gpio是GPX1_1,还有中断号是XEINT9

好好好,最后,只要得到GPX1的寄存器基地址就好了

在芯片手册查关键词“GPX1CON”,可以得到关键信息,地址是0x11000000+0x0c20=0x11000C20

这就是GPX1寄存器的物理地址了,

再瞄一眼,按顺序是CON,DAT,PUD,DRV寄存器,也就是说~基地址+0是CON,基地址+1是DAT,基地址+2是PUD,基地址+3是DRV。(以unsigned int*为基准)

把gpio对应位配置为0x0,输入模式。默认就是输入模式,不想写这部分的代码也行。

现在硬件信息都知道了:

//key GM_INT9--GPX1_1--XEINT(9)--0x11000C20 

下面分传统方式和设备树方式写硬件部分的代码:

传统方式:

		//	4.1直接从原理图得到硬件信息
		//key GM_INT9--GPX1_1--0x11000C20 
		key_dev->regmap_size = 4;
		key_dev->phyreg_base = 0x11000C20 ;	//寄存器基地址

		//+0是CON	+1是DAT		+2是PUD		+3是DRV
		key_dev->virreg_base = ioremap(key_dev->phyreg_base,4*key_dev->regmap_size);//映射4个寄存器
		if(key_dev->virreg_base==NULL){
			printk(KERN_EMERG "ioremap error\n");
			ret = -ENOMEM;
			goto err_4;

		}
        
		//配置GPX1-1为输入 注:这些不写也行,写了更好
		*(key_dev->virreg_base) &= (~(0xF<<4));	//清除4-7位
		*(key_dev->virreg_base) |=((0x0)<<4);	//将4-7位置为0x0 输入模式
		//初始化GPX1-1为高电平,即和原理图一样
		*(key_dev->virreg_base+1) &= (~(0x1<<1));	//清除第1位
		*(key_dev->virreg_base+1) |=((0x1)<<1);	//将第1位置为0x1,即高电平
		//初始化GPX1-1为上拉,即和原理图一样
		*(key_dev->virreg_base+2) &= (~(0x3<<2));	//清除【3:2】位
		*(key_dev->virreg_base+2) |=((0x3)<<2);	//将其置为0x3,即上拉

        //申请中断
        request_irq(IRQ_EINT(9),keyirq_handler,IRQ_TYPE_EDGE_FALLING,"keyirq",NULL);
        


设备树方式:

其实设备树和传统方式是差不太多的,反正都是为了获得中断号,然后向linux系统注册一个中断

设备树节点编写:

在/kernel/arch/arm/boot/dts/exynos4412-itop-elite.dts中

	mykey {
            //节点名字
			compatible = "gpio-key";            //没有匹配,随便写
			gpios = <&gpx1 1 GPIO_PULL_UP>;     //指明gpio是gpx1的第1个
			interrupt-parent = <&gpx1>;         //继承gpx1中断控制器
			interrupts = <1 IRQ_TYPE_EDGE_BOTH>;//继承过来第1个中断,后面那个参数无所谓
                                                //代码里可以再配置
	};

字符设备驱动中“硬”的部分代码:

	//	4.2设备树方式获得硬件信息
	
		//	/mykey compatible = "gpio-key"
        /* 定位到mykey节点 */
		key_dev->mynode = of_find_node_by_name(NULL,"mykey");
		if(key_dev->mynode==NULL){
			printk(KERN_EMERG "of_find_node_by_name error\n");
			ret = -ENOMEM;
			goto err_4;
		}
        /* 
        在这个节点里拿到gpio信息,返回gpio号
        有了这个gpiO号就可以对此gpio配置了,上拉下拉,输入输出,高电平低电平等 
        */
		key_dev->mygpio = of_get_named_gpio_flags(key_dev->mynode,"gpios",0,NULL);
		if (!gpio_is_valid(key_dev->mygpio))
			printk("gpio isn't valid\n");
		else printk("gpio num=%d",key_dev->mygpio);
        /* 向系统申请这个gpio的使用权 */
		ret= gpio_request(key_dev->mygpio, "gpios");
		if(ret!=0){
			printk(KERN_EMERG "gpio_request error\n");
			ret = -ENOMEM;
			goto err_5;
		}
        /* 这个是配置gpio模式的,通过gpio号就能配置 这里配置为输入 */
		gpio_direction_input(key_dev->mygpio);//初始化为输入模式
		/* 从这个节点拿到中断号 */
		//获取中断号--申请中断
		key_dev->irqno = of_irq_get(key_dev->mynode,0);
    	ret = request_irq(key_dev->irqno,keyirq_handler,IRQ_TYPE_EDGE_BOTH,"keyirq",NULL);
		if(ret==0){ //返回0说明正确
			printk(KERN_EMERG "key_dev->irqno  is %d\n",key_dev->irqno);
		}

完整代码:

驱动代码:

hw_charDev_describe.h

//#include <linux/device.h>
#include <linux/cdev.h>

struct key_event{
	int code;		//表示按键的内涵:home/esc/Q/W/E/R/T/ENTER
	int value;		///按下1,抬起0
};

struct hw_describe{
	char *dev_name;			//外设名字
	unsigned int dev_major;	//外设设备号
	struct class *hw_class;	//外设设备类型
	struct device *dev; 	//外设设备节点
	struct cdev mycdev;		//注册cdev到系统
	struct device_node *mynode;					//设备节点
	unsigned int phyreg_base;					//外设寄存器物理基地址
	volatile unsigned int *virreg_base;			//外设寄存器虚拟基地址
	
	unsigned int regmap_size;	//ioremap的长度,一个寄存器是4,即4Byte,32位
	int mygpio;					//gpio号
	int irqno;					//中断号

	//外设类型
	struct key_event mykey_event;
	
};




5th_charDev_addirq.c

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("LCH");

#include "hw_charDev_describe.h"
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <uapi/linux/input-event-codes.h>



struct hw_describe *key_dev;//用于描述硬件外设
unsigned char key_state = 0;//0表示没有按下,1表示有按下



/*打开操作*/
static int chardevnode_open(struct inode *inode, struct file *file){
	printk(KERN_EMERG "chardevnode_open is success!\n");
	
	return 0;
}
/*关闭操作*/
static int chardevnode_release(struct inode *inode, struct file *file){
	printk(KERN_EMERG "chardevnode_release is success!\n");
	
	return 0;
}
/*IO操作*/
static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
//	printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %ld,arg is %ld \n",cmd,arg);
	
	return 0;
}

ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){
	//用户要读,ret = copy_to_user(buf用户的,给用户数据的指针,count);ret!=0是不正常的
	int ret;
	if(key_state==1){
		ret = copy_to_user(buf,&key_dev->mykey_event,count);
		if(ret>0){
			printk(KERN_EMERG "copy_to_user error!\n");
			return -EFAULT;
		}
		memset(&key_dev->mykey_event,0,sizeof(key_dev->mykey_event));
		key_state=0;
	}
	return 0;
}

ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){
	//用户写进来,ret = copy_from_user(存数据的指针,buf用户写进来的,count);ret!=0是不正常的

	return 0;
}


struct file_operations fops ={
	.open = chardevnode_open,
	.release = chardevnode_release,
	.unlocked_ioctl = chardevnode_ioctl,
	.read = chardevnode_read,
	.write = chardevnode_write,
};

static irqreturn_t keyirq_handler(int irqno,void *devid){
	int value;
	key_state = 1;
    printk("receive a interrupt 9!\n");
//	4.1非设备树方式
//	value = *(key_dev->virreg_base+1)&(0x1<<1);

//	4.2设备树方式
	value = gpio_get_value(key_dev->mygpio);
	if(value==1){	//没按下		==  key_event.value = 0
		key_dev->mykey_event.code = KEY_ENTER;
		key_dev->mykey_event.value = 0;
    	printk("key up!\n");
	}
	else if(value==0){			//按键按下
		key_dev->mykey_event.code = KEY_ENTER;
		key_dev->mykey_event.value = 1;
		printk("key down!\n");
	}

	return IRQ_HANDLED;
}

static int __init charDev_init(void)
{
	int ret =0 ;

	
	printk(KERN_EMERG "charDev_init\n");

	
	//0.给外设对象分配空间
	key_dev = kmalloc(sizeof(struct hw_describe),GFP_KERNEL);
	if(key_dev ==NULL){
		printk(KERN_EMERG "kmallock error\n");
		return(-ENOMEM);
	}
	key_dev->dev_name = "mykey";

	
	//1.申请主设备号 设备号是MKDEV(key_dev->dev_major,自选)
	key_dev->dev_major=register_chrdev(0,key_dev->dev_name,&fops);
	
	if(key_dev->dev_major<0)
	{
		printk(KERN_EMERG "register_chrdev error\n");
		ret = -ENODEV;
		goto err_0;
	}
	else
	{
		printk(KERN_EMERG "dev_major is %d\n",key_dev->dev_major);
	}

	//2.注册设备到系统 /proc/devices
	
    cdev_init(&(key_dev->mycdev),&fops);
    (key_dev->mycdev).owner = THIS_MODULE;
    (key_dev->mycdev).ops = &fops;

    
    ret = cdev_add(
&(key_dev->mycdev),MKDEV(key_dev->dev_major,0),1);
    if(ret){
        		printk(KERN_EMERG "cdev_add is fail! %d\n",ret);
				goto err_1;
    }else{
        		printk(KERN_EMERG "cdev_add is success!\n");
    }

	//3.生成设备节点
	key_dev->hw_class = class_create(THIS_MODULE,"charDev_class");
	if(IS_ERR(key_dev->hw_class))
	{
		printk(KERN_EMERG "class_create error\n");
		ret = PTR_ERR(key_dev->hw_class);
		goto err_2;
	}
	key_dev->dev=device_create(key_dev->hw_class,NULL,MKDEV(key_dev->dev_major,0),NULL,key_dev->dev_name);//设备节点名字
	if(IS_ERR(key_dev->dev))
	{
		printk(KERN_EMERG "device_create error\n");
		ret = PTR_ERR(key_dev->dev);
		goto err_3;
	}
	else
	{
		printk(KERN_EMERG "dev_name is %s\n",key_dev->dev_name);
	}

	//4.获取硬件资源
	/*
		//	4.1直接从原理图得到硬件信息
		//key GM_INT9--GPX1_1--0x11000C20 
		key_dev->regmap_size = 4;
		key_dev->phyreg_base = 0x11000C20 ;	//寄存器基地址

		//+0是CON	+1是DAT		+2是PUD		+3是DRV
		key_dev->virreg_base = ioremap(key_dev->phyreg_base,4*key_dev->regmap_size);//映射4个寄存器
		if(key_dev->virreg_base==NULL){
			printk(KERN_EMERG "ioremap error\n");
			ret = -ENOMEM;
			goto err_4;

		}

		//配置GPX1-1为输入
		*(key_dev->virreg_base) &= (~(0xF<<4));	//清除4-7位
		*(key_dev->virreg_base) |=((0x0)<<4);	//将4-7位置为0x1
		//初始化GPX1-1为高电平,即和原理图一样
		*(key_dev->virreg_base+1) &= (~(0x1<<1));	//清除第1位
		*(key_dev->virreg_base+1) |=((0x1)<<1);	//将第1位置为0x1,即高电平
		//初始化GPX1-1为上拉,即和原理图一样
		*(key_dev->virreg_base+2) &= (~(0x3<<2));	//清除【3:2】位
		*(key_dev->virreg_base+2) |=((0x3)<<2);	//将其置为0x3,即上拉
	*/
	//	4.2设备树方式获得硬件信息
	
		//	/mykey compatible = "gpio-key"
		key_dev->mynode = of_find_node_by_name(NULL,"mykey");
		if(key_dev->mynode==NULL){
			printk(KERN_EMERG "of_find_node_by_name error\n");
			ret = -ENOMEM;
			goto err_4;
		}
		key_dev->mygpio = of_get_named_gpio_flags(key_dev->mynode,"gpios",0,NULL);
		if (!gpio_is_valid(key_dev->mygpio))
			printk("gpio isn't valid\n");
		else printk("gpio num=%d",key_dev->mygpio);

		ret= gpio_request(key_dev->mygpio, "gpios");
		if(ret!=0){
			printk(KERN_EMERG "gpio_request error\n");
			ret = -ENOMEM;
			goto err_5;
		}
		gpio_direction_input(key_dev->mygpio);//初始化为输入模式
		
		//获取中断号--申请中断
		key_dev->irqno = of_irq_get(key_dev->mynode,0);
    	ret = request_irq(key_dev->irqno,keyirq_handler,IRQ_TYPE_EDGE_BOTH,"keyirq",NULL);
		if(ret==0){ //返回0说明正确
			printk(KERN_EMERG "key_dev->irqno  is %d\n",key_dev->irqno);
		}

	
	return 0;

err_5:
	gpio_free(key_dev->mygpio);

err_4:
	device_destroy(key_dev->hw_class,MKDEV(key_dev->dev_major,0));
err_3:
	class_destroy(key_dev->hw_class);
err_2:
	unregister_chrdev(key_dev->dev_major,key_dev->dev_name);
err_1:
	cdev_del(&(key_dev->mycdev));
err_0:
	kfree(key_dev);
	return(ret);


}
static void __exit  charDev_exit(void)
{
	free_irq(key_dev->irqno,NULL);
	gpio_free(key_dev->mygpio);
	//iounmap(key_dev->virreg_base);
	device_destroy(key_dev->hw_class,MKDEV(key_dev->dev_major,0));
	class_destroy(key_dev->hw_class);
	cdev_del(&(key_dev->mycdev));
	unregister_chrdev(key_dev->dev_major,key_dev->dev_name);
	kfree(key_dev);

}

module_init(charDev_init);
module_exit(charDev_exit);

应用层代码:

app_readkey.c

(阻塞io)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>


struct key_event{
	int code;		//表示按键的内涵:home/esc/Q/W/E/R/T/ENTER
	int value;		///按下1,抬起0
};

//打开设备节点  传入cmd ,
int main(int argc ,char **argv){
    int fd,res,digital,readNum;
	struct key_event mykey;
    char *readSWDigitalNode="/dev/mykey";
	unsigned char readBuffer;
    //打开节点
    fd = open(readSWDigitalNode,O_RDWR);
    if(fd>0)printf("app open readSWDigitalNode success\n");
    else {
        printf("app open readSWDigitalNode err\n");
		return -1;
    }
	while(1){
		readNum = read(fd, (void *)(&mykey),sizeof(struct key_event));//读取1B的数据
		if(mykey.value==1){ //按下1,抬起0
        	printf("app key down\n");
		}
		else if(mykey.value==0){
        	printf("app key up\n");
		}
	}
    close(fd);
    return ;
}



运行效果:

加载驱动:

运行app:

查看注册的irq:

 cat /proc/interrupts

错误分析:

如果遇到这种错误:

说明申请gpio失败/申请中断失败

原因是有别的驱动已经占用了这个gpio

有两种方法解决

1.在设备树中将有关这个gpio的注释掉

在/kernel/arch/arm/boot/dts/exynos4412-itop-elite.dts中搜索关键词”gpx1“或者“Home”"key"

可见果然有别的地方占用了,把home里面的内容都注释掉即可,我们自己的驱动就不会报错了

2.在内核配置make menuconfig中找出用到了这个gpio资源的驱动,去掉它。这个比较麻烦,不熟悉板级驱动的还真不太好搞,这个可以去参考讯为的手册,我记得他有讲过。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值