• 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 
    ",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 
    ",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 
    ");
    	}
    	while(1)
    	{
    		read(fd,&key_val,1);
    		printf("key_val = 0x%x
    ",key_val);
    	}
    	return 0;
    }
    


























  • 相关阅读:
    ECSHOP文章详情页的标题上加个链接
    点击复制代码到粘贴板代码
    ecshop商城用户名和邮箱都能登陆方法
    ECSHOP商品页发表评论时 取消EMAIL必填
    ECSHOP 模板结构说明
    ecshop文章分类页 显视当前文章分类名称及商品分类页显视当前分类名称
    ecshop商城用户名和邮箱都能登陆方法
    Ecshop品牌页如何自定义Title
    常见的颜色搭配、衣裤搭配指南
    ECSHOP首页显示积分商城里的商品
  • 原文地址:https://www.cnblogs.com/yfceshi/p/7147209.html
Copyright © 2020-2023  润新知