机械按键在按下的过程中会出现抖动的情况,如下图,这样就会导致本来按下一次按键的过程会出现多次中断,导致判断出错。在按键驱动程序中我们可以这么做:

在按键驱动程序中我们可以这么做来取消按键抖动的影响:当出现一个按键中断后不会马上去处理它,而是延时一个抖动时间(一般10ms),如果在这个时间内再次出现中断那么再次延时10ms。这样循环,一直到在这个10ms内只有一个按键中断,那么就认为这次是真的按键值,然后在定时器处理函数里处理它。上述过程可以利用内核的定时器来实现。

定时器二要素:定时时间、定时时间到后做什么事情。根据这两个要素来编写程序,直接在sixth_drv.c的驱动程序上更改直接看到代码:

1、定时器的创建,先建立一个定时器结构

static struct timer_list buttons_timer;//定义一个定时器

2、在模块装载时初始化定时器

static int sixth_drv_init(void)
{
/*增加一个定时器用于处理按键抖动*/
init_timer(&buttons_timer);
buttons_timer.expires = ;//定时器的定时时间
// buttons_timer->data = (unsigned long) cs;
buttons_timer.function = buttons_timeout;//定时时间到后的处理函数
add_timer(&buttons_timer);//将定义的定时器放入定时器链表 sixthmajor = register_chrdev(, "buttons", &sixth_drv_ops);//注册驱动程序 if(sixthmajor < )
printk("failes 1 buttons_drv register\n"); sixth_drv_class = class_create(THIS_MODULE, "buttons");//创建类
if(sixth_drv_class < )
printk("failes 2 buttons_drv register\n");
sixth_drv_class_dev = class_device_create(sixth_drv_class, NULL, MKDEV(sixthmajor,), NULL,"buttons");//创建设备节点
if(sixth_drv_class_dev < )
printk("failes 3 buttons_drv register\n"); gpfcon = ioremap(0x56000050, );//重映射
gpfdat = gpfcon + ;
gpgcon = ioremap(0x56000060, );//重映射
gpgdat = gpgcon + ; printk("register buttons_drv\n");
return ;
}

3、编写定时器处理函数

static void buttons_timeout(unsigned long data)
{
unsigned int pin_val;
static long cnt=; //printk("timeout cnt : %d\n",++cnt);
if(pin_des==NULL)
return;
else
{
// printk("pin_des != NULL\n"); pin_val = s3c2410_gpio_getpin(pin_des->pin); if(pin_val) //按键松开
key_val = 0x80 | pin_des->key_val;
else
key_val = pin_des->key_val; wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
ev_press = ; kill_fasync(&sixth_fasync, SIGIO, POLL_IN);//发生信号给进程
}
}

4、当在卸载驱动时将定时器删除;在中断处理程序中直接改变定时器的超时时间,并记录下是哪个按键按下的即可,其他处理都在定时器超时函数中。直接看到完整代码:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h> //含有iomap函数iounmap函数
#include <asm/uaccess.h>//含有copy_from_user函数
#include <linux/device.h>//含有类相关的处理函数
#include <asm/arch/regs-gpio.h>//含有S3C2410_GPF0等相关的
#include <linux/irq.h> //含有IRQ_HANDLED\IRQ_TYPE_EDGE_RISING
#include <asm-arm/irq.h> //含有IRQT_BOTHEDGE触发类型
#include <linux/interrupt.h> //含有request_irq、free_irq函数
#include <linux/poll.h>
#include <asm-generic/errno-base.h> //含有各种错误返回值
//#include <asm-arm\arch-s3c2410\irqs.h> static struct class *sixth_drv_class;//类
static struct class_device *sixth_drv_class_dev;//类下面的设备
static int sixthmajor; static unsigned long *gpfcon = NULL;
static unsigned long *gpfdat = NULL;
static unsigned long *gpgcon = NULL;
static unsigned long *gpgdat = NULL; struct fasync_struct *sixth_fasync; static unsigned int key_val; struct pin_desc
{
unsigned int pin;
unsigned int key_val;
}; static struct pin_desc pins_desc[] =
{
{S3C2410_GPF0,0x01},
{S3C2410_GPF2,0x02},
{S3C2410_GPG3,0x03},
{S3C2410_GPG11,0x04}
}; static struct pin_desc *pin_des=NULL; static unsigned int ev_press;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//注册一个等待队列button_waitq static atomic_t open_flag = ATOMIC_INIT(); //定义原子变量open_flag 并初始化为1 static DECLARE_MUTEX(button_lock); //定义互斥锁 static struct timer_list buttons_timer;//定义一个定时器
/*
*0x01、0x02、0x03、0x04表示按键被按下
*/ /*
*0x81、0x82、0x83、0x84表示按键被松开
*/ /*
*利用dev_id的值为pins_desc来判断是哪一个按键被按下或松开
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
pin_des = (struct pin_desc *)dev_id;//取得哪个按键被按下的状态
mod_timer(&buttons_timer, jiffies+HZ/);//10ms之后调用定时器处理函数 return IRQ_HANDLED;
} static int sixth_drv_open (struct inode * inode, struct file * file)
{
int ret; // if(atomic_dec_and_test(&open_flag)==0)//自检后是否为0,不为0说明已经被人调用
// {
// atomic_inc(&open_flag);//原子变量+1
// return -EBUSY;
// }
if(file->f_flags & O_NONBLOCK)//非阻塞方式
{
if(down_trylock(&button_lock))//获取信号量失败则返回
return -EBUSY;
}
else
down(&button_lock);//获得信号量 ret = request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "s1", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 1\n");
return -;
}
ret = request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "s2", (void * )& pins_desc[]);
if(ret)
{
printk("open failed 2\n");
return -;
}
ret = request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "s3", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 3\n");
return -;
}
ret = request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "s4", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 4\n");
return -;
} return ;
} static int sixth_drv_close(struct inode * inode, struct file * file)
{
// atomic_inc(&open_flag);//原子变量+1
up(&button_lock);//释放信号量 free_irq(IRQ_EINT0 ,(void * )&pins_desc[]); free_irq(IRQ_EINT2 ,(void * )& pins_desc[]); free_irq(IRQ_EINT11 ,(void * )&pins_desc[]); free_irq(IRQ_EINT19 ,(void * )&pins_desc[]); return ;
} static ssize_t sixth_drv_read(struct file * file, char __user * userbuf, size_t count, loff_t * off)
{
int ret; if(count != )
{
printk("read error\n");
return -;
} if(file->f_flags & O_NONBLOCK)//非阻塞方式
{
if(!ev_press)//判断是否有按键按下,如果没有直接返回
{
key_val = ;
ret = copy_to_user(userbuf, &key_val, );
return -EBUSY;
}
}
else//如果没有按键动作,直接进入休眠
wait_event_interruptible(button_waitq, ev_press);//将当前进程放入等待队列button_waitq中 ret = copy_to_user(userbuf, &key_val, );
ev_press = ;//按键已经处理可以继续睡眠 if(ret)
{
printk("copy error\n");
return -;
} return ;
} static unsigned int sixth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int ret = ;
poll_wait(file, &button_waitq, wait);//将当前进程放到button_waitq列表 if(ev_press)
ret |=POLLIN;//说明有数据被取到了 return ret;
} static int sixth_drv_fasync(int fd, struct file * file, int on)
{
int err;
printk("fansync_helper\n");
err = fasync_helper(fd, file, on, &sixth_fasync);//初始化sixth_fasync
if (err < )
return err;
return ;
} static struct file_operations sixth_drv_ops =
{
.owner = THIS_MODULE,
.open = sixth_drv_open,
.read = sixth_drv_read,
.release = sixth_drv_close,
.poll = sixth_drv_poll,
.fasync = sixth_drv_fasync, }; static void buttons_timeout(unsigned long data)
{
unsigned int pin_val;
static long cnt=; //printk("timeout cnt : %d\n",++cnt);
if(pin_des==NULL)
return;
else
{
// printk("pin_des != NULL\n"); pin_val = s3c2410_gpio_getpin(pin_des->pin); if(pin_val) //按键松开
key_val = 0x80 | pin_des->key_val;
else
key_val = pin_des->key_val; wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
ev_press = ; kill_fasync(&sixth_fasync, SIGIO, POLL_IN);//发生信号给进程
}
} static int sixth_drv_init(void)
{
/*增加一个定时器用于处理按键抖动*/
init_timer(&buttons_timer);
buttons_timer.expires = ;//定时器的定时时间
// buttons_timer->data = (unsigned long) cs;
buttons_timer.function = buttons_timeout;//定时时间到后的处理函数
add_timer(&buttons_timer);//将定义的定时器放入定时器链表 sixthmajor = register_chrdev(, "buttons", &sixth_drv_ops);//注册驱动程序 if(sixthmajor < )
printk("failes 1 buttons_drv register\n"); sixth_drv_class = class_create(THIS_MODULE, "buttons");//创建类
if(sixth_drv_class < )
printk("failes 2 buttons_drv register\n");
sixth_drv_class_dev = class_device_create(sixth_drv_class, NULL, MKDEV(sixthmajor,), NULL,"buttons");//创建设备节点
if(sixth_drv_class_dev < )
printk("failes 3 buttons_drv register\n"); gpfcon = ioremap(0x56000050, );//重映射
gpfdat = gpfcon + ;
gpgcon = ioremap(0x56000060, );//重映射
gpgdat = gpgcon + ; printk("register buttons_drv\n");
return ;
} static void sixth_drv_exit(void)
{
del_timer(&buttons_timer);
unregister_chrdev(sixthmajor,"buttons"); class_device_unregister(sixth_drv_class_dev);
class_destroy(sixth_drv_class); iounmap(gpfcon);
iounmap(gpgcon); printk("unregister buttons_drv\n");
} module_init(sixth_drv_init);
module_exit(sixth_drv_exit); MODULE_LICENSE("GPL");

5、测试代码还是沿用sisth_test.c。将驱动程序和测试程序编译后的文件放入网络文件系统,测试发现不再出现抖动情况。具体过程参考Linux驱动之按键驱动编写(中断方式)

Linux驱动之定时器在按键去抖中的应用的更多相关文章

  1. FPGA按键去抖verilog代码

    按键去抖的原因及其分类就不罗嗦了. 在这里解释一段代码,代码是网上找的,看了半天没懂,无奈查了半天想了半天,终于明白了... module sw_debounce(clk,rst_n,sw1,sw2, ...

  2. linux驱动之定时器的使用

    被文章摘自一下几位网友.非常感谢他们. http://blog.sina.com.cn/s/blog_57330c3401011cq3.html Linux的内核中定义了一个定时器的结构: #incl ...

  3. ARM Linux 驱动Input子系统之按键驱动测试

    上一篇已经谈过,在现内核的中引入设备树之后对于内核驱动的编写,主要集中在硬件接口的配置上了即xxxx.dts文件的编写. 在自己的开发板上移植按键驱动: 1.根据开发板的原理图 确定按键的硬件接口为: ...

  4. linux驱动之定时器的介绍和内核时间的学习

    本文章摘自下面的网友: http://blog.sina.com.cn/s/blog_6e5b342e0100m87d.html 一.内核中如何记录时间 任何程序都需要时间控制,其主要目的是: 测量时 ...

  5. javascript中的函数节流和函数去抖

    带着问题去尝试 首先我们要知道为什么要用到函数节流和函数去抖?我们带着以下的疑问来进行分析! 1.比如搜索框,你会用到什么事件(change.blur.keyup等)?去做什么效果?2.再比如scro ...

  6. hover时显示可跟随鼠标移动的浮动框,运用函数节流与去抖进行优化

    在很多笔试面试题中总能看到js函数去抖和函数节流,看过很多关于这两者的讨论,最近终于在一个需求中使用了函数去抖(debounce)和函数节流(throttle). 需要完成的效果是,鼠标在表格的单元格 ...

  7. Linux代码的重用与强行卸载Linux驱动

    (一)Linux代码的重用 重用=静态重用(将要重用的代码放到其他的文件的头文件中声明)+动态重用(使用另外一个Linux驱动中的资源,例如函数.变量.宏等) 1.编译是由多个文件组成的Linux驱动 ...

  8. Linux驱动之按键驱动编写(中断方式)

    在Linux驱动之按键驱动编写(查询方式)已经写了一个查询方式的按键驱动,但是查询方式太占用CPU,接下来利用中断方式编写一个驱动程序,使得CPU占有率降低,在按键空闲时调用read系统调用的进程可以 ...

  9. Linux驱动之按键驱动编写(查询方式)

    在Linux驱动之LED驱动编写已经详细介绍了一个驱动的编写过程,接着来写一个按键驱动程序,主要是在file_operations结构中添加了一个read函数.还是分以下几步说明 1.查看原理图,确定 ...

随机推荐

  1. py-day3-4 python 匿名函数

    # 匿名函数 lamdba name = 'xiaoma' f = lambda x:x+'jun' res = f(name) print('匿名函数的运行结果:',res) 匿名函数的运行结果: ...

  2. MVC Ajax.BeginForm 提交上传图片

    吃水不忘挖井人,如果对你有帮助,请说声谢谢.如果你要转载,请注明出处.谢谢! 异步提交时,出现图片不能上传. 起初我定格在  System.Web.Mvc  中.查询源码时,也是没有问题的.那问题出现 ...

  3. 如何修改MSSQL的用户名

    Alter LOGIN sa DISABLE Alter LOGIN sa WITH NAME = [systemAccount] "systemAccount" 为SA的新名称, ...

  4. CentOS7和CentOS6怎样开启MySQL远程访问

    CentOS6开启MySQL远程访问 1.开放MySQL访问端口3306 修改防火墙配置文件 vi /etc/sysconfig/iptables  加入端口配置      -A INPUT -m s ...

  5. SAS PROC PRINT 常用选项和语句说明

    常用选项1.使用选项OBS=修改观测序号标签2.使用NOOBS选项不显示观测序号列3.使用ID语句在输出中取代观测序号列4.使用VAR选择输出的变量5.使用WHERE语句选择输出的观测6.使用数据集选 ...

  6. 用GDB调试程序(二)

    GDB的命令概貌——————— 启动gdb后,就你被带入gdb的调试环境中,就可以使用gdb的命令开始调试程序了,gdb的命令可以使用help命令来查看,如下所示: /home/hchen> g ...

  7. 用java打印图形

    代码如下 public static void main(String[] args) { for (int i = 0; i <7; i++) { for (int j = 0; j < ...

  8. SpringBoot 之jsp

    Boot 内嵌的tomcat 是不支持jsp 的, jetty 也是. 虽然boot也是有默认配置一个InternalResourceViewResolver ,但是它并不像我们在springmvc ...

  9. java编程思想(2)--一切都是对象

    1创建对象 String s ;创建引用,并未初始化,即引用未关联任何东西 String s2="asda"; 初始化 System.out.println(s2); System ...

  10. python——元组和字典类型简明理解

    元组类型: 元祖创建: 不需要括号可以但是一个元素就当成了字符串类型了 >>> tup1="a"; >>> type(tup1) <cla ...