当按键按下时,由于金属弹片的振动可能会导致多个按键中断产生,消除按键抖动所产生多个中断的思想很简单,我们只取其中一个按键中断。
最为常规的办法就是用定时器来实现
按键按下可能会因弹片振动产生多个中断,但是这些中断间隔时间非常的短,所以当产生按键中断时,触发的中断处理例程并不是直接处理按键信号,而是在中断例程中设置一个10ms的定时器,如果10ms内依然会因为弹片振动导致按键中断产生,那么触发的中断处理程序将清除原先的定时器,修改定时器,然后把修改的定时器加到定时器链表中。为什么要10ms来定时检测呢?因为人是不可能做到10ms间隔连续按键。如果10ms内没有按键中断产生,将会触发定期器中断,定时器中断处理例程中我们把原先在按键中断处理例程中的程序移植到这里。
整理一下思路
按键触发中断,中断例程为清除前一个定时器,重置定时器,并将定时器加入到定时器队列中
定时器中断处理例程中处理按键中断
static int sixth_drv_init(void)
{
major = register_chrdev(0, "sixth_drv", &sencod_drv_fops);
sixthdrv_class = class_create(THIS_MODULE, "sixth_drv");
/* 为了让mdev根据这些信息来创建设备节点 */
sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon + 1;
gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
gpgdat = gpgcon + 1;
/*初始化定时器,并将定时器加入到定时器链表中*/
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
add_timer(&buttons_timer);
return 0;
}
首先在module_init函数中初始化定时器,为什么要在这里初始化定时器?因为按键中断触发定时器,如果我们不在这里初始化定时器,就只能在按键中断处理程序中初始化定时器,那么每次按键中断我们都要初始化定时器,如何还要清除前一个定时器,显然不合理,所以在module_init中初始化是唯一的选择。但是这样也会带来一些问题,那就是模块被装载到内核中的第一时间便会启动定时器,而这个时候很有可能没有按键中断产生,所以我们要在定时器中断例程入口处检查是否有按键中断产生
我查一下内核驱动代码,发现很少有把定时器初始化放在module_init中,但是没有办法,在这里只能是唯一的选择
按键中断处理例程
我们在open中初始化中断,尽量不要在module_init中初始化中断
static int sixth_drv_open(struct inode *inode, struct file *file)
{
/*以阻塞的方式打开文件*/
if(file->flags & O_NONBLOCK)
{
if (down_trylock(&button_lock))
return -EBUSY;
}
else
{
/* 获取信号量 */
down(&button_lock);
}
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
}
在初始中断时,由于我所使用的内核版本中尚没有设备树,所以中断号不能从设备树中获得
中断处理例程
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/* 10ms后启动定时器 */
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&buttons_timer, jiffies+HZ/100);
return IRQ_RETVAL(IRQ_HANDLED);
}
我们直接用mod_timer做到了之前所说的“清除前一个定时器,重置定时器,并将定时器加入到定时器队列中”
如下是内核中对mod_timer的注释
/**
* mod_timer - modify a timer's timeout
* @timer: the timer to be modified
* @expires: new timeout in jiffies
*
* mod_timer() is a more efficient way to update the expire field of an
* active timer (if the timer is inactive it will be activated)
*
* mod_timer(timer, expires) is equivalent to:
*
* del_timer(timer); timer->expires = expires; add_timer(timer);
*
* Note that if there are multiple unserialized concurrent users of the
* same timer, then mod_timer() is the only safe way to modify the timeout,
* since add_timer() cannot modify an already running timer.
*
* The function returns whether it has modified a pending timer or not.
* (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an
* active timer returns 1.)
*/
mod_timer(timer, expires) is equivalent to:
del_timer(timer); timer->expires = expires; add_timer(timer);
mod_timer相当于这三个步骤,无论当前时钟是否有注册,删除掉,然后重置当前时钟的超时时间,注册这个时钟。这和我们的要求完全一致。
定时器中断处理例程中将会处理按键中断
static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval;
if (!pindesc)
return;
pinval = s3c2410_gpio_getpin(pindesc->pin);
if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
}
ev_press = 1; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
kill_fasync (&button_async, SIGIO, POLL_IN);
}
当获得信息以后,唤醒睡眠进程,并对button_asyn发送异步通知信号
这个程序主要是专注于用定时器中断实现按键防抖,我没有列出完整的代码,如果有疑问,欢迎和我交流
2915

被折叠的 条评论
为什么被折叠?



