首先,我们要先搞明白两个问题:
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);