码迷,mamicode.com
首页 > 其他好文 > 详细

11.按键驱动之定时器防抖(详解)

时间:2017-09-14 20:17:39      阅读:299      评论:0      收藏:0      [点我收藏+]

标签:module   lin   size_t   png   互斥锁   print   赋值   interrupt   lag   

本节目标:

  通过定时器来防止按键抖动,测试程序是使用上节的:阻塞操作的测试程序

 


1.如下图所示,在没有定时器防抖情况下,按键没有稳定之前会多次进入中断,使得输出多个相同信息出来

技术分享

2.按键波形图,如下所示:

 技术分享

3.如何消去按键抖动

通过定时器延时10ms,然后每当按键进入中断时就更新定时器延时10ms,若延时10ms到了说明已经过了抖动范围,然后再打印按键电平信息

4.定时器结构体和函数介绍

 


我们先来看看两个全局变量:

jiffies: 是系统时钟,全局变量,默认每隔10ms加1

HZ:是每S的频率,通过系统时钟换算出来,比如每隔10ms加1,那么HZ就等于100


 

 

4.1定时器结构体timer_list

timer_list常用结构体成员如下所示:

1)data     //传递到*function超时处理函数的参数,主要在多个定时器同时使用时,区别是哪个timer超时。

2)expires  //定时器到期的时间,当expires小于等于jiffies时,这个定时器便到期并调用定时器超时处理函数,然后就不会再调用了,
比如要使用10ms后到期,赋值(jiffies+HZ/100)即可
3)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");
}

 

11.按键驱动之定时器防抖(详解)

标签:module   lin   size_t   png   互斥锁   print   赋值   interrupt   lag   

原文地址:http://www.cnblogs.com/lifexy/p/7522122.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!