用定时器去除按键抖动

时间:2023-01-23 23:28:56

首先,我们要先搞明白两个问题:

1. 为什么要去抖动?

    按键所用开关为机械弹性开关,当机械触点断开,闭合时,由于机械触点的弹性作用,开关不会马上稳定地接通或者断开。因而在闭合及断开的瞬间总是伴随一连串的抖动。

2. linux中为什么要用定时器去抖?

    按键去抖动的方法主要有两种,一是硬件电路去抖动;另一种是软件延时去抖。而延时又一般分为两种,一种是for循环等待,另一种是定时器延时。在操作系统中,由于效率方面的原因,一般不允许使用for循环来等待,只能使用定时器。

 

定时器是用来流程:

1. 定义定时器变量

 1 使用struct timer_list结构定义一个定时器变量,
 2 struct timer_list {
 3     /*
 4      * All fields that change during normal runtime grouped to the
 5      * same cacheline
 6      */
 7     struct list_head entry;
 8     unsigned long expires;     //设置定时器超时时间
 9     struct tvec_base *base;
10 
11     void (*function)(unsigned long);   //定时器超时处理函数
12     unsigned long data;
13 
14     int slack;
15 
16 #ifdef CONFIG_TIMER_STATS
17     int start_pid;
18     void *start_site;
19     char start_comm[16];
20 #endif
21 #ifdef CONFIG_LOCKDEP
22     struct lockdep_map lockdep_map;
23 #endif
24 };
例:struct timer_list key_timer;

2. 初始化定时器

1 初始化定时器有两步:
2 1. init_timer;
3 2. 设置超时函数。
4 例:init_timer(&key_timer);
5       key_timer.function = key_timer_func;

 

3. add_timer注册定时器

 1 例:  add_timer(&key_timer); 

4. mod_timer启动定时器

1 例: 
2  /*jiffies为全局变量,表示当前的时间,在linux系统中1秒中有1000滴答,
3    即1秒jiffies增加1000,HZ代表一秒*/          
4     mod_timer(&key_timer, jiffies + 1*HZ);

 

以下为带定时器去抖的按键中断驱动程序:

 

  1 #include <linux/module.h>
  2 #include <linux/init.h>
  3 #include <linux/miscdevice.h>
  4 #include <linux/interrupt.h>
  5 #include <linux/fs.h>
  6 #include <linux/io.h>
  7 #include <mach/gpio.h>  
  8 #include <mach/regs-gpio.h>  
  9 #include <linux/irq.h>
 10 
 11 #define GPH2CON 0xe0200c40
 12 #define GPH2DAT 0xe0200c44
 13 
 14 struct timer_list key_timer;
 15 unsigned *gpio_data;
 16 
 17 irqreturn_t key_irq(int irq, void * dev_id)
 18 {
 19     //1. 检测是否发生了按键中断
 20     
 21     
 22     //2. 清除已经发生的按键中断
 23     
 24     
 25     //3. 打印按键值
 26     
 27     /*启动定时器*/
 28     mod_timer(&key_timer, jiffies + HZ/50); //超时为20ms
 29     
 30     return 0;
 31 }
 32 
 33 
 34 /*超时函数*/
 35 void key_timer_func(unsigned long data)
 36 {
 37     key_val = readl(gpio_data) & 0x01;
 38         if(key_val == 0)
 39             printk("key_down!\n");
 40 }
 41 
 42 
 43 int key_open (struct inode *inode, struct file *filp)
 44 {
 45     return 0;
 46 }
 47 
 48 
 49 void key_hw_init()
 50 {
 51     //unsigned int data; 
 52     unsigned int *gpio_config;
 53     unsigned int key_val;
 54     
 55     gpio_config = ioremap(GPH2CON, 4);
 56     data = readl(gpio_config);
 57     data &= ~0b1111;
 58     data |= 0b1111;
 59     writel(data,gpio_config);
 60     
 61     gpio_data = ioremap(GPH2DAT, 4);   
 62 }
 63 
 64 
 65 struct file_operations key_fops =
 66 {
 67         .open = key_open,
 68 };
 69 
 70 
 71 struct miscdevice key_miscdevice =
 72 {       
 73     /*MISC_DYNAMIC_MINOR代表动态分配次设备号,即由系统自动分配*/
 74     .minor = MISC_DYNAMIC_MINOR,
 75     .name = "key_miscdev",
 76     .fops = &key_fops,
 77 };
 78 
 79 
 80 static int key_init()
 81 {
 82     /*注册混杂设备*/
 83     misc_register(&key_miscdevice);
 84     
 85     /*申请中断,如果内核中已有按键中断驱动,则需要把arch/arm/mach-s5pv210/mach-mini210.c文件的gpio_buttons定义的相关按键去掉,
 86      不然板子上的按键中断就已经被占用,不能注册中断*/
 87     /*注意:中断号这个参数应该用gpio_to_irq(S5PV210_GPH2(0)),假如用了IRQ_EINT16_31则按键驱动不会工作,我个人认为是内核代码写错了*/
 88     request_irq(gpio_to_irq(S5PV210_GPH2(0)), key_irq, IRQF_TRIGGER_FALLING, "key_miscdev", 0);
 89     
 90     /*初始化定时器*/
 91     init_timer(&key_timer);
 92     key_timer.function = key_timer_func;
 93     
 94     /*注册定时器*/
 95     add_timer(&key_timer);
 96     
 97     
 98     return 0;
 99         
100 }
101 
102 
103 static void key_exit()
104 {
105     /*注销设备*/
106     misc_deregister(&key_miscdevice);
107 }
108 
109 
110 MODULE_LICENSE("GPL");
111 
112 module_init(key_init);
113 module_exit(key_exit);