linux驱动之中断方式获取键值

linux驱动之中断方式获取键值

------------------------------------------------------------------------------------------------------------------------------------------------------

回顾在单片机下的中断处理

  1. 分辨是哪个中断
  2. 调用处理函数
  3. 清中断
-------------------------------------------------------------------------------------------------------------------------------------------------------

1、中断框架

trap_int  中构造

trap_int函数就是一些跳转指令

b...

b...

b  vector_irq + stubs_offset   ;vector_irq是链接地址    stubs_offset 是偏移地址


vector_irq:

  1. 保存被中断的现场
  2. asm_do_IRQ
  3. 恢复现场
  4. 。。。
asm_do_IRQ:

对中断的处理。

 request_irq  注册中断

request_irq(irq,handle,irqflags,devname,dev_id)

1、分配irqaction 结构

2、setup_irq(irq,action)

                 0、分配一个irqaction 结构

                 a、在irq_desc[irq]   -> action  

                 b、desc -> chip ->settype

                 c、desc -> chip -> startup /enable

irq要申请的硬件中断号+
handle是向系统登记的中断处理函数是一个回调函数,主哦功能短发生时,系统将调用这个函数,并将dev_id传递给它
irqflags是中断处理属性 

 free_irq   卸载中断

free_irq(irq, dev_id)

1、出链

2、禁止中断

2、代码编写

1、申请IRQ

这里我要特殊说明下:(向我这种小白在使用request_irq这个函数时遇到了一个问题就是  IRQ_EINT1、IRQ_EINT4、IRQ_EINT2、IRQ_EINT0这四个宏找不到,最终原因找到了 ,我配置eclipse的时候内核树的路径有些小问题。irqs.h这个文件是在 /linux-2.6.30.4/arch/arm/mach-s3c2410/include 这个路径下,我以为这个在mach-s3c2440也会有这个路径呢,其实不然。还有一些引脚的定义也在2410的路径里面,这里要注意一下 )。

	request_irq(IRQ_EINT1,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key1",&pins_desc[0]);
	request_irq(IRQ_EINT4,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key2",&pins_desc[1]);
	request_irq(IRQ_EINT2,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key3",&pins_desc[2]);
	request_irq(IRQ_EINT0,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key4",&pins_desc[3]);

这里的pins_desc 是一个结构体数组

struct pin_desc pins_desc[4] =
{
	{S3C2410_GPF1,0x01},
	{S3C2410_GPF4,0x02},
	{S3C2410_GPF2,0x03},
	{S3C2410_GPF0,0x04},
};

这个数组里面包含了这四个中断引脚和该引脚的键值。

结构体原型是这样的:

struct pin_desc{
	unsigned int pin;
	unsigned int key_value;
};

2、写中断处理函数

static irqreturn_t buttons_irq(int irq,void *dev_id)
{
	struct pin_desc * pindesc = (struct pin_desc *) dev_id;
	unsigned int pinval;
	pinval = s3c2410_gpio_getpin(pindesc -> pin);
	if(pinval)//松开
	{
		keyval = 0x80|pindesc->key_value;
	}
	else
	{
		keyval = pindesc->key_value;
	}
	ev_press =1;//中断发生
	wake_up_interruptible(&button_wait_q);
	printk("button is pressed : %d \n",irq);
	return IRQ_HANDLED;
}
说明:

pindesc :这个结构体会得知是哪个按键按下了
s3c2410_gpio_getpin:这个函数是内核提供的函数,可以获得当前引脚的高低电平
ev_press:中断事件标志,中断服务程序将他置1,read函数将他置0

wake_up_interruptible(&button_wait_q);  会唤醒休眠的进程,唤醒注册到等待队列上的进程,其中 button_wait_q是通过如下方式获得的:

static DECLARE_WAIT_QUEUE_HEAD(button_wait_q);   生成一个等待队列头wait_queue_head_t,名字为 button_wait_q

3、修改read函数

ssize_t button_dev_read(struct file *file,char __user *buf,size_t size,loff_t *ppos)
{
	if(size !=1)
	{
		return -EINVAL;
	}
	/*如果没有按键动作发生  就休眠*/
	wait_event_interruptible(button_wait_q,ev_press);
	/*如果有按键动作发生,直接返回*/
	copy_to_user(buf,&keyval,1);
	ev_press = 0;
	return 0;
}

其中:

wait_event_interruptible(button_wait_q,ev_press);   如果ev_press是假的话,就会休眠

ev_press =0; 每次读完,都将中断事件标志清零。

4、填充file_operations结构体

static struct file_operations button_sdv_fops =
{
	.owner 		= THIS_MODULE,
	.open  		= button_dev_open,
	.read 		= button_dev_read,
	.release 	= button_dev_close,
};

其中button_dev_close函数为:

int button_dev_close(struct inode* inode ,struct file *file)
{
	free_irq(IRQ_EINT1,&pins_desc[0]);
	free_irq(IRQ_EINT4,&pins_desc[1]);
	free_irq(IRQ_EINT2,&pins_desc[2]);
	free_irq(IRQ_EINT0,&pins_desc[3]);

	return 0;
}

free_irq为释放中断,其中需要两个参数。


本代码在上一篇按键的博文基础上修改。

完整的驱动代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/irqs.h>//这个在/opt/EmbedSky/linux-2.6.30.4/arch/arm/mach-s3c2410/include/mach 路径
#include <linux/interrupt.h>

MODULE_LICENSE("Dual BSD/GPL");

static struct class *buttondrv_class;
static struct class_devices *buttondrv_class_dev;

/*  */
static DECLARE_WAIT_QUEUE_HEAD(button_wait_q);
/*中断事件标志,中断服务程序将他置1,read函数将他置0*/
static volatile int ev_press =0;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;

static unsigned keyval;

struct pin_desc{
	unsigned int pin;
	unsigned int key_value;
};
/*按键按下时是:0x01 0x02 0x03 0x04*/
/*按键松开时是:0x81 0x82 0x83 0x84*/
struct pin_desc pins_desc[4] =
{
	{S3C2410_GPF1,0x01},
	{S3C2410_GPF4,0x02},
	{S3C2410_GPF2,0x03},
	{S3C2410_GPF0,0x04},
};
/*
 * 确定按键值
 */
static irqreturn_t buttons_irq(int irq,void *dev_id)
{
	struct pin_desc * pindesc = (struct pin_desc *) dev_id;
	unsigned int pinval;
	pinval = s3c2410_gpio_getpin(pindesc -> pin);
	if(pinval)//松开
	{
		keyval = 0x80|pindesc->key_value;
	}
	else
	{
		keyval = pindesc->key_value;
	}
	ev_press =1;//中断发生
	wake_up_interruptible(&button_wait_q);
	printk("button is pressed : %d \n",irq);
	return IRQ_HANDLED;
}
static int button_dev_open(struct inode *inode ,struct file* file)
{
	//配置按键的引脚 GPF0,1,2,4为输入引脚
	request_irq(IRQ_EINT1,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key1",&pins_desc[0]);
	request_irq(IRQ_EINT4,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key2",&pins_desc[1]);
	request_irq(IRQ_EINT2,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key3",&pins_desc[2]);
	request_irq(IRQ_EINT0,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key4",&pins_desc[3]);

	return 0;
}
ssize_t button_dev_read(struct file *file,char __user *buf,size_t size,loff_t *ppos)
{
	if(size !=1)
	{
		return -EINVAL;
	}
	/*如果没有按键动作发生  就休眠*/
	wait_event_interruptible(button_wait_q,ev_press);
	/*如果有按键动作发生,直接返回*/
	copy_to_user(buf,&keyval,1);
	ev_press = 0;
	return 0;
}
int button_dev_close(struct inode* inode ,struct file *file)
{
	free_irq(IRQ_EINT1,&pins_desc[0]);
	free_irq(IRQ_EINT4,&pins_desc[1]);
	free_irq(IRQ_EINT2,&pins_desc[2]);
	free_irq(IRQ_EINT0,&pins_desc[3]);

	return 0;
}
static struct file_operations button_sdv_fops =
{
	.owner 		= THIS_MODULE,
	.open  		= button_dev_open,
	.read 		= button_dev_read,
	.release 	= button_dev_close,
};
int major;
static int button_dev_init(void)//入口函数
{
	major = register_chrdev(0,"button_drv",&button_sdv_fops);

	buttondrv_class = class_create(THIS_MODULE,"button_drv");
	if(IS_ERR(buttondrv_class))
		return PTR_ERR(buttondrv_class);
	buttondrv_class_dev= device_create(buttondrv_class,NULL,MKDEV(major,0),NULL,"wq_button");
		if(unlikely(IS_ERR(buttondrv_class_dev)))
			return PTR_ERR(buttondrv_class_dev);

	/*映射物理地址*/
	gpfcon = (volatile unsigned long *) ioremap(0x56000050 ,16);
	gpfdat = gpfcon + 1;

	return 0;
}
static void button_dev_exit(void)
{
	unregister_chrdev(major,"button_drv");
	device_unregister(buttondrv_class_dev);
	class_destroy(buttondrv_class);

	iounmap(gpfcon);
}
module_init(button_dev_init);
module_exit(button_dev_exit);

测试代码:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/*
 * wq_device <dev> <on|off>
 */
int main(int argc, char **argv)
{
	int cnt=0;
	int fd;
	unsigned char key_val;
	fd = open("/dev/wq_button",	O_RDWR);
	if(fd<0)
	{
		printf("can't open \n");
	}
	while(1)
	{
		read(fd,&key_val,1);
		printf("key_val = 0x%x\n",key_val);
	}
	return 0;
}


























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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值