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

在按键驱动程序中我们可以这么做来取消按键抖动的影响:当出现一个按键中断后不会马上去处理它,而是延时一个抖动时间(一般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驱动之定时器在按键去抖中的应用的更多相关文章
- FPGA按键去抖verilog代码
按键去抖的原因及其分类就不罗嗦了. 在这里解释一段代码,代码是网上找的,看了半天没懂,无奈查了半天想了半天,终于明白了... module sw_debounce(clk,rst_n,sw1,sw2, ...
- linux驱动之定时器的使用
被文章摘自一下几位网友.非常感谢他们. http://blog.sina.com.cn/s/blog_57330c3401011cq3.html Linux的内核中定义了一个定时器的结构: #incl ...
- ARM Linux 驱动Input子系统之按键驱动测试
上一篇已经谈过,在现内核的中引入设备树之后对于内核驱动的编写,主要集中在硬件接口的配置上了即xxxx.dts文件的编写. 在自己的开发板上移植按键驱动: 1.根据开发板的原理图 确定按键的硬件接口为: ...
- linux驱动之定时器的介绍和内核时间的学习
本文章摘自下面的网友: http://blog.sina.com.cn/s/blog_6e5b342e0100m87d.html 一.内核中如何记录时间 任何程序都需要时间控制,其主要目的是: 测量时 ...
- javascript中的函数节流和函数去抖
带着问题去尝试 首先我们要知道为什么要用到函数节流和函数去抖?我们带着以下的疑问来进行分析! 1.比如搜索框,你会用到什么事件(change.blur.keyup等)?去做什么效果?2.再比如scro ...
- hover时显示可跟随鼠标移动的浮动框,运用函数节流与去抖进行优化
在很多笔试面试题中总能看到js函数去抖和函数节流,看过很多关于这两者的讨论,最近终于在一个需求中使用了函数去抖(debounce)和函数节流(throttle). 需要完成的效果是,鼠标在表格的单元格 ...
- Linux代码的重用与强行卸载Linux驱动
(一)Linux代码的重用 重用=静态重用(将要重用的代码放到其他的文件的头文件中声明)+动态重用(使用另外一个Linux驱动中的资源,例如函数.变量.宏等) 1.编译是由多个文件组成的Linux驱动 ...
- Linux驱动之按键驱动编写(中断方式)
在Linux驱动之按键驱动编写(查询方式)已经写了一个查询方式的按键驱动,但是查询方式太占用CPU,接下来利用中断方式编写一个驱动程序,使得CPU占有率降低,在按键空闲时调用read系统调用的进程可以 ...
- Linux驱动之按键驱动编写(查询方式)
在Linux驱动之LED驱动编写已经详细介绍了一个驱动的编写过程,接着来写一个按键驱动程序,主要是在file_operations结构中添加了一个read函数.还是分以下几步说明 1.查看原理图,确定 ...
随机推荐
- maven 项目使用本地jar
<dependency> <groupId>com.yeepay.g3</groupId> <artifactId>yop</artifactId ...
- 2.4 Visio2007显示动态对齐网格
- bootstrap之navbar
container:固定960px宽度,(如果又引入了响应式样式,则会适当调整,例如1600*900,它会显示1200px) container-fluid:自适应屏幕宽度,即满屏显示. row和co ...
- java 代码实现使用Druid 链接池获取数据库链接
因为原先使用的c3p0链接池,时常出现:APPARENT DEADLOCK!!! Creating emergency threads for unassigned pending tasks,以及出 ...
- MVC Ajax.BeginForm 提交上传图片
吃水不忘挖井人,如果对你有帮助,请说声谢谢.如果你要转载,请注明出处.谢谢! 异步提交时,出现图片不能上传. 起初我定格在 System.Web.Mvc 中.查询源码时,也是没有问题的.那问题出现 ...
- head命令用法总结
head命令用法总结 head命令用于显示文件的开头的内容.在默认情况下,head命令显示文件的头10行内容. 1.语法 head(选项)(参数) 2.选项 -c, --bytes=[-]K 显示每个 ...
- GitExtentions添加SSH证书,pull和push时不必输入密码
1. 工具-->设置,选择SSH,选择PuTTY 2. 选择 3. 在新打开的页面,可以生成key或者加载已有的key.putty的key是.ppk结尾的特殊格式.关于生成key和在githu ...
- Linux中加速访问github
1. 登录http://tool.chinaz.com/dns/ 2. 查询以下域名映射,并分别取访问速度较快的一个ip github.global.ssl.fastly.net -> ...
- 用到的linux命令
1.修改文件权限 chmod 777 文件路径 修改文件下所有文件权限 chmond -R 777 文件路径 2.修改文件 (保存文件的方法,在命令行窗口 shift+:换出底部命令行, q表示退出, ...
- (Android第一行代码实验一)活动的最佳实践
活动的最佳实践 1.知晓当前是在哪一个活动 这个技巧将教会你,如何根据程序当前的界面就能判断出这是哪一个活动. 首先需要新建一个 BaseActivity 继承自 Activi ...