11.按键驱动之定时器防抖(详解)
本节目标:
通过定时器来防止按键抖动,测试程序是使用上节的:阻塞操作的测试程序
1.如下图所示,在没有定时器防抖情况下,按键没有稳定之前会多次进入中断,使得输出多个相同信息出来
2.按键波形图,如下所示:
3.如何消去按键抖动
通过定时器延时10ms,然后每当按键进入中断时就更新定时器延时10ms,若延时10ms到了说明已经过了抖动范围,然后再打印按键电平信息
4.定时器结构体和函数介绍
我们先来看看两个全局变量:
jiffies:是系统时钟,全局变量,默认每隔10ms加1
HZ:是每S的频率,通过系统时钟换算出来,比如每隔10ms加1,那么HZ就等于100
4.1定时器结构体timer_list
timer_list常用结构体成员如下所示:
)data //传递到*function超时处理函数的参数,主要在多个定时器同时使用时,区别是哪个timer超时。 )expires //定时器到期的时间,当expires小于等于jiffies时,这个定时器便到期并调用定时器超时处理函数,然后就不会再调用了, 比如要使用10ms后到期,赋值(jiffies+HZ/100)即可 )void (*function)(unsigned long) //定时器超时处理函数。
4.2 定时器常用函数
init_timer(struct timer_list*) //定时器初始化结构体函数, add_timer(struct timer_list*) //往系统添加定时器,告诉内核有个定时器结构体 mod_timer(struct timer_list *, unsigned long jiffier_timerout) //修改定时器的超时时间为jiffies_timerout, 当expires小于等于jiffies时,便调用定时器超时处理函数。 timer_pending(struct timer_list *) //定时器状态查询,如果在系统的定时器列表中则返回1,否则返回0; del_timer(struct timer_list*) //删除定时器。
5.修改驱动程序实现定时器消抖动
5.1首先定义一个定时器结构体:
static struct timer_list buttons_timer; //定义定时器结构体
5.2在init入口函数中初始化定时器结构体:
init_timer(&buttons_timer); //初始化结构体 /*本中断都是更新同一个定时器,所以成员.data无需初始,默认为0 不需要定时器到期时间,所以成员.expires无需初始化,默认为0,由于小于等于jiffies,会进入一次定时器超时函数*/ buttons_timer. function= buttons_timer_ function; add_timer(&buttons_timer); //告诉内核,有一个定时器
5.3定义全局变量*irq_dev_id,然后在中断服务函数中获取dev_id
struct pin_desc *irq_dev_id ; //定义全局变量获取dev_id
并修改中断服务函数:
static irqreturn_t buttons_irq (int irq, void *dev_id) //中断服务函数 { irq_dev_id =(struct pin_desc *)dev_id; //获取引脚描述结构体 /*每产生一次中断,则更新定时器10ms超时 */ mod_timer(&buttons_timer, jiffies+HZ/100); return IRQ_RETVAL(IRQ_HANDLED); }
5.4当10ms超时到了,进入定时器超时函数,处理*irq_dev_id来判断是哪个按键按下的
static void buttons_timer_function(unsigned long data) //定时器超时函数 { unsigned int pin_val=0; if(!irq_dev_id) //初始化时,由于定时器.expires成员=0,会进入一次,若irq_dev_id为0则退出 {printk("expires: timer out\n"); return ; } pin_val=s3c2410_gpio_getpin(irq_dev_id->pin); //获取按键值 if(pin_val) { /*按下 (下降沿),清除0x80*/ key_val=irq_dev_id->pin_status&0xef; } else { /*没有按下(上升沿),加上0x80*/ key_val=irq_dev_id->pin_status|0x80; } even_press=1; //退出等待队列 wake_up_interruptible(&button_wait); //唤醒 中断 kill_fasync(&button_async, SIGIO, POLL_IN); //发送SIGIO信号给应用层 }
6.测试效果
如下图所示,当定时器expire成员<=jiffies时会进入一次定时器超时函数,我们按键驱动就不需要这个,因为按键并没有按下,所以需要进入定时器超时函数,需要先判断一次,避免误操作:
如下图所示,我们运行测试程序,来快速按下按键试试:
版权声明:本文为博主原创文章,转载请标注出处: http://www.cnblogs.com/lifexy/p/7522122.html
7.本节测试程序代码使用的是上一节: 阻塞操作的测试程序
8.本节驱动程序sixth.c代码:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <asm/irq.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/poll.h> static struct timer_list buttons_timer; //定义定时器结构体 struct pin_desc *irq_dev_id ; //定义全局变量获取dev_id static struct class *sixthdrv_class; static struct class_device *sixthdrv_class_devs; /*定义互斥锁button_lock,被用来后面的down()和up()使用 */ static DECLARE_MUTEX(button_lock); /* 声明等待队列类型中断 button_wait */ static DECLARE_WAIT_QUEUE_HEAD(button_wait); /* 异步信号结构体变量 */ static struct fasync_struct * button_async; /* * 定义中断事件标志 * 0:进入等待队列 1:退出等待队列 */ static int even_press=0; /* * 定义全局变量key_val,保存key状态 */ static int key_val=0; /* *引脚描述结构体 */ struct pin_desc{ unsigned int pin; unsigned int pin_status; }; /* *key初始状态(没有按下): 0x81,0x82,0x83,0x84 *key状态(按下): 0x01,0x02,0x03,0x04 */ struct pin_desc pins_desc[4]={ {S3C2410_GPF0,0x01 }, {S3C2410_GPF2, 0x02 }, {S3C2410_GPG3, 0x03 }, {S3C2410_GPG11,0x04}, } ; int sixth_drv_class(struct inode *inode, struct file *file) //卸载中断 { free_irq(IRQ_EINT0,&pins_desc[0]); free_irq(IRQ_EINT2,&pins_desc[1]); free_irq(IRQ_EINT11,&pins_desc[2]); free_irq(IRQ_EINT19,&pins_desc[3]); /*释放信号量*/ up(&button_lock); return 0; } /* * 确定是上升沿还是下降沿 */ static irqreturn_t buttons_irq (int irq, void *dev_id) //中断服务函数 { irq_dev_id =(struct pin_desc *)dev_id; //获取引脚描述结构体 /*每产生一次中断,则更新定时器10ms超时 */ mod_timer(&buttons_timer, jiffies+HZ/100); return IRQ_RETVAL(IRQ_HANDLED); } static int sixth_drv_open(struct inode *inode, struct file *file) { if( file->f_flags & O_NONBLOCK ) //非阻塞操作,获取不到则退出 { if(down_trylock(&button_lock) ) return -1; } else //阻塞操作,获取不到则进入休眠 { down(&button_lock); } request_irq(IRQ_EINT0,buttons_irq,IRQT_BOTHEDGE,"S1",&pins_desc[0]); request_irq(IRQ_EINT2, buttons_irq,IRQT_BOTHEDGE, "S2", &pins_desc[1]); request_irq(IRQ_EINT11, buttons_irq,IRQT_BOTHEDGE, "S3", &pins_desc[2]); request_irq(IRQ_EINT19, buttons_irq,IRQT_BOTHEDGE, "S4", &pins_desc[3]); return 0; } static int sixth_drv_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { if( file->f_flags & O_NONBLOCK ) //非阻塞操作,获取不到则退出 { if(!even_press ) //没有按键按下 return -1; } /*阻塞操作,则直接进入休眠状态,直到有按键按下为止*/ /*进程 进入等待队列(休眠状态)*/ wait_event_interruptible(button_wait, even_press); /*有按键按下,退出等待队列,上传key_val 给用户层*/ if(copy_to_user(buf,&key_val,sizeof(key_val))) return EFAULT; even_press=0; return 0; } static unsigned sixth_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; poll_wait(file, &button_wait, wait); // 不会立即休眠 if (even_press) mask |= POLLIN | POLLRDNORM; return mask; } static int sixth_fasync (int fd, struct file *file, int on) { return fasync_helper(fd, file, on, & button_async); //初始化button_async结构体,就能使用kill_fasync()了 } static struct file_operations sixth_drv_fops={ .owner = THIS_MODULE, .open = sixth_drv_open, .read = sixth_drv_read, .release=sixth_drv_class, //里面添加free_irq函数,来释放中断服务函数 .poll = sixth_poll, .fasync= sixth_fasync, //初始化异步信号函数 }; static void buttons_timer_function(unsigned long data) //定时器超时函数 { unsigned int pin_val=0; if(!irq_dev_id) //定时器.expires成员=0,会进入一次,若irq_dev_id为0则退出 {printk("expires: timer out\n"); return ; } pin_val=s3c2410_gpio_getpin(irq_dev_id->pin); if(pin_val) { /* 按下 (下降沿),清除0x80*/ key_val=irq_dev_id->pin_status&0xef; } else { /*没有按下(上升沿),加上0x80*/ key_val=irq_dev_id->pin_status|0x80; } even_press=1; //退出等待队列 wake_up_interruptible(&button_wait); //唤醒 中断 kill_fasync(&button_async, SIGIO, POLL_IN); //发送SIGIO信号给应用层 } volatile int sixth_major; static int sixth_drv_init(void) { init_timer(&buttons_timer); //初始化定时器 buttons_timer. function= buttons_timer_function; //定时器超时函数 add_timer(&buttons_timer); //添加到内核中 sixth_major=register_chrdev(0,"sixth_drv",&sixth_drv_fops); //创建驱动 sixthdrv_class=class_create(THIS_MODULE,"sixth_dev"); //创建类名 sixthdrv_class_devs=class_device_create(sixthdrv_class, NULL, MKDEV(sixth_major,0), NULL,"buttons"); return 0; } static int sixth_drv_exit(void) { unregister_chrdev(sixth_major,"sixth_drv"); //卸载驱动 class_device_unregister(sixthdrv_class_devs); //卸载类设 class_destroy(sixthdrv_class); //卸载类 return 0; } module_init(sixth_drv_init); module_exit(sixth_drv_exit); MODULE_LICENSE("GPL v2");}