jz2440_按键驱动程序的防抖机制

问题

jz2440_字符设备按键驱动程序 这篇文章中,我们描述了使用按键驱动程序的几种方法。

不过在实际操作后,我们可能会出现下面的问题:
在这里插入图片描述
当我们连续快速按下按键,或者因为某些原因机器的按键发生了抖动,就会导致按键只被按下一次,却产生了多次中断,在某些特定情况中会出现一些程序问题。

在这里插入图片描述
为了解决上述的问题。我们可以使用内核中的定时器机制。

定时器

使用定时器有两个要素:

  1. 定时器的超时时间设置;
  2. 定时器超时后,要执行的处理函数;

如何使用定时器防止抖动?
在这里插入图片描述
看到上图:

  1. 在 A 点,产生了一次中断,我们就设置定时器在10ms后再执行中断处理函数;
  2. 在 B 点,又产生了一次中断,由于按键抖动的时间间隔是非常小的,不可能超过10ms。因此,我们就可以重置定时器,让其重新计时10ms;
  3. 以此类推,在 F 点又产生了一次中断,会再重置定时器的时间;
  4. 最终,即使因为按键抖动产生了多次中断,也只会执行一次中断处理函数;

驱动程序

使用内核的 timer 定时器,其 API 说明可以参考 Linux内核API 定时机制|极客笔记 (deepinout.com)

  1. 定义一个 timer 定时器

    static struct timer_list s3c2440_button_timer;
    
  2. 进行初始化,在 init 函数中进行初始化:

    static int __init s3c2440_buttons_init(void)
    {
    	major = register_chrdev(0, DEVICE_NAME, &s3c2440_buttons_fops);
        if (major < 0) {
    	      printk(DEVICE_NAME " can't register major number\n");
    	      return major;
        }
    	
    	s3c2440_buttons_class = class_create(THIS_MODULE, "s3c2440_buttons_poll");
    	if (IS_ERR(s3c2440_buttons_class)) 
    		return PTR_ERR(s3c2440_buttons_class);
    
    	s3c2440_buttons_dev = class_device_create(s3c2440_buttons_class, 
                                                  NULL, MKDEV(major, 0), NULL, "button_shake");
    	if (unlikely(IS_ERR(s3c2440_buttons_dev)))
    		return PTR_ERR(s3c2440_buttons_dev);
    
    	init_timer(s3c2440_button_timer);	/* 对定时器进行初始化 */
    	s3c2440_button_timer.data 	  = NULL;					/* 传给处理函数的参数 unsigned long类型 */
    	s3c2440_button_timer.function = s3c2440_buttons_timer;	/* 定时器超时的处理函数 */
        add_timer(&s3c2440_button_timer);						/* 将定时器注册进内核,
        														 * 当定时器超时处理函数就会被调用 */
    	printk(DEVICE_NAME " initialized!\n");
    	return 0;
    }
    

    上面我们设置了 处理函数 还没有设置超时时间。

  3. 在中断处理函数中设置超时时间:

    static irqreturn_t s3c2440_buttons_handler(int irq, void *dev_id)
    {
    	/* 10ms后启动定时器 */
    	mod_timer(&s3c2440_button_timer, jiffies + (HZ/100));
    	return IRQ_HANDLED;
    }
    

    我们将原来的中断处理的代码移动到了定时器超时的处理函数中,而中断处理函数只进行定时器超时的设置。

    jiffies:是一个内核中的全局变量。每种计算机底层体系结构都提供了一些执行周期性操作的手段,通常的形式是定时器中断。jiffies 递增的频率同体系结构有关,取决于内核中一个主要的常数 HZ。该常数的值通常介于100和1 000中间。换言之,jiffies 的值每秒递增的次数在100至1 000次之间。

    对于 jz2440 开发板来说,这值为 FCLK 的值,也就是100HZ。也就是说,一秒时间内会产生100次定时器中断,每次中断都会使 jiffies 的值加一。

    基于 jiffies 的计时相对粒度较粗,因为目前1 000 Hz已经算不上很高的频率了。在底层硬件能力允许的前提下,内核可使用高分辨率的定时器提供额外的计时手段,能够以纳秒级的精确度和分辨率来计量时间。

    计时的周期是可以动态改变的。在没有或无需频繁的周期性操作的情况下,周期性地产生定时器中断是没有意义的,这会阻止处理器降低耗电进入睡眠状态。动态改变计时周期对于供电受限的系统是很有用的,例如笔记本电脑和嵌入式系统。

    j i f f i e s + ( H Z / 100 ) jiffies + (HZ/100) jiffies+(HZ/100) 让定时器在 jiffies 的基础上,加上百分之一的 HZ 时间,就可以使得定时器在10ms后触发。

    在 A 点时,设置为了 jiffies + 10 后触发中断执行;而在 B 点时,又设置为 jiffies + 10 后触发中断执行。这样无论在触发了多少次中断,最终结果都是在最后一次中断的 jiffies + 10 后执行处理函数。

  4. 定时器超时处理函数:

    static void s3c2440_buttons_timer(unsigned long data)
    {
    	struct pin_desc *pindesc = (struct pin_desc *)s3c2440_timer_data.dev_id;
    	if (pindesc == NULL)
    		return;
    	
    	unsigned char pinval;
    	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(&s3c2440_buttons_async_queue, SIGIO, POLL_IN);
    }
    

    if (pindesc == NULL):是为了防止定时器,刚刚在 init 创建通过 add_timer(&s3c2440_button_timer); 添加到内核就立即被执行一次。

  5. 在 exit 函数中,从内核移除定时器:

    static void __exit s3c2440_buttons_exit(void)
    {
    	/* 卸载驱动程序 */
    	class_device_destroy(s3c2440_buttons_class, MKDEV(major, 0));
    	class_destroy(s3c2440_buttons_class);
    	unregister_chrdev(major, DEVICE_NAME);
    	del_timer(&s3c2440_button_timer);		/* 移除定时器 */
    	printk(DEVICE_NAME " unregister\n");
    }
    

完整的驱动程序

按键驱动程序防止抖动的源代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值