字符设备驱动程序之——定时器防抖动

按键操作过程中,会发生机械抖动,导致一次按键,产生多个中断。这样,一次按键就会返回多个按键值。为了消除这种机械抖动,我们选择使用定时器延时触发来忽略按键后瞬间的抖动。
       一般情况下,按键机械抖动会在很短时间内结束。选择10ms延时处理即可消除大部分抖动。当第一个中断来到时,触发定时器工作,定时器计时10ms,然后再来处理这个中断。由于抖动,第二个中断产生,又会再次触发,这个会将之前的中断触发计时取消,重新计时10ms,以此类推。在10ms内,只有最后一个中断才能被执行。

代码如下:

  1 #include <linux/module.h>
  2 #include <linux/kernel.h>
  3 #include <linux/fs.h>
  4 #include <linux/init.h>
  5 #include <linux/delay.h>
  6 #include <asm/uaccess.h>
  7 #include <asm/irq.h>
  8 #include <linux/irq.h>
  9 #include <asm/irq_regs.h>
 10 #include <asm/io.h>
 11 #include <linux/device.h>
 12 #include <mach/hardware.h>
 13 #include <mach/regs-gpio.h>
 14 #include <mach/gpio.h>
 15 #include <linux/wait.h>
 16 #include <linux/sched.h>
 17 #include <linux/interrupt.h>
 18 #include <linux/poll.h>
 19 #include <linux/semaphore.h>
 20 #include <linux/jiffies.h>
 21 
 22 int major;
 23 static struct class *sixthdrv_class;
 24 static struct class    *sixthdrv_class_dev;
 25 volatile unsigned long *gpfcon ;
 26 volatile unsigned long *gpfdat ;
 27 volatile unsigned long *gpgcon ;
 28 volatile unsigned long *gpgdat ;
 29 static struct timer_list buttons_timer;
 30 //static atomic_t canopen = ATOMIC_INIT(1);   //定义原子变量,初始化为1
 31 static DEFINE_SEMAPHORE(button_lock);  //定义互斥锁(定义向量)
 32 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
 33 /* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */
 34 static volatile int ev_press = 0;
 35 static struct fasync_struct *button_async;
 36 struct pin_desc{
 37     unsigned int pin;
 38     unsigned int key_val;   
 39 };
 40 /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
 41 /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
 42 static unsigned char key_val;
 43 struct pin_desc pins_desc[4] = {
 44     { S3C2410_GPF(0),  0x01 },
 45     { S3C2410_GPF(2),  0x02 },
 46     { S3C2410_GPG(3),  0x03 },
 47     { S3C2410_GPG(11), 0x04 }
 48 };
 49 static struct pin_desc *irq_pd    ;
 50 /*
 51   * 确定按键值
 52   */
 53 static irqreturn_t bottons_irq(int irq, void *dev_id)
 54 {
 55     irq_pd = (struct pin_desc * )dev_id;
 56     /*修改定时器超时值*/
 57     mod_timer(&buttons_timer,jiffies + HZ/100 ); //jiffies当前值,HZ/100=10ms 
 58     return IRQ_RETVAL(IRQ_HANDLED);
 59 }
 60 static void buttons_timer_function(unsigned long data)
 61 {
 62         struct pin_desc * pindesc = irq_pd;   
 63         unsigned int pinval;
 64         if(!pindesc)
 65             return;      
 66         pinval = s3c2410_gpio_getpin( pindesc->pin );   
 67         if (!pinval)
 68         {   
 69             /* 按下 */
 70             key_val = pindesc->key_val;
 71         }
 72         else
 73         {
 74             /* 松开 */
 75             key_val = 0x80 | pindesc->key_val;
 76         }
 77         ev_press = 1;                   /* 设置中断标志,不再休眠*/ 
 78         kill_fasync(&button_async, SIGIO, POLL_IN);  
 79         wake_up_interruptible(&button_waitq);     /* 唤醒休眠的进程 */
 80 }
 81 static int sixth_drv_open(struct inode *inode,struct file *file)
 82 {
 83 #if 0  /*原子操作法*/
 84     if (!atomic_dec_and_test(&canopen))
 85         {
 86             atomic_inc(&canopen);
 87             return -EBUSY;
 88         }
 89 #endif
 90     if (file->f_flags & O_NONBLOCK)   //判断是否是"非阻塞"
 91     {
 92         if(down_trylock(&button_lock))  //无法获得信号量,立马返回,不休眠等待
 93         return -EBUSY;     
 94     }
 95     else                             //"阻塞操作"
 96     {
 97         down(&button_lock);        //获取信号量
 98     }
 99     /* 设置中断*/   
100     request_irq(IRQ_EINT0,  bottons_irq, IRQ_TYPE_EDGE_BOTH, "s2", &pins_desc[0] );
101     request_irq(IRQ_EINT2,  bottons_irq, IRQ_TYPE_EDGE_BOTH, "s3", &pins_desc[1] );
102     request_irq(IRQ_EINT11, bottons_irq, IRQ_TYPE_EDGE_BOTH, "s4", &pins_desc[2] );
103     request_irq(IRQ_EINT19, bottons_irq, IRQ_TYPE_EDGE_BOTH, "s5", &pins_desc[3] );
104     return 0;   
105 }
106 static ssize_t sixth_drv_read(struct file *file, char __user *buf,size_t size, loff_t *ppos)
107 {
108     if(size != 1)    //判断
109     return -EINVAL;
110     if (file->f_flags & O_NONBLOCK)    //非阻塞
111     {
112         if (!ev_press)    //没有按键动作
113             return -EAGAIN;  
114     } 
115     else        /* 如果没有按键动作, 休眠 */
116     {
117         wait_event_interruptible(button_waitq, ev_press);
118     }  
119     /* 如果有按键动作, 返回键值 */
120     copy_to_user(buf,&key_val,1);
121     ev_press = 0;
122     return 1;      
123 }
124 sixth_drv_close(struct inode * inode,struct file * file)
125 {
126     //atomic_inc(&canopen);        //关闭前,将变量自加还原为1 
127     free_irq(IRQ_EINT0  ,&pins_desc[0]);
128     free_irq(IRQ_EINT2  ,&pins_desc[1]);
129     free_irq(IRQ_EINT11 ,&pins_desc[2]);
130     free_irq(IRQ_EINT19 ,&pins_desc[3]);
131     up(&button_lock);
132     return 0;
133 }
134 static unsigned int sixth_drv_poll(struct file *file, poll_table *wait)
135 {
136     unsigned int mask = 0;  
137     poll_wait(file, &button_waitq, wait);
138     if (ev_press)
139         mask |= POLLIN | POLLRDNORM;
140     return mask;
141 }
142 static int sixth_drv_fasync(int fd, struct file *filp, int on)
143 {
144     return fasync_helper(fd, filp, on, &button_async);
145 }
146 /* 这个结构是字符设备驱动程序的核心
147  * 当应用程序操作设备文件时所调用的open、read、write等函数,
148  * 最终会调用这个结构中指定的对应函数
149  */
150 static struct file_operations sixth_drv_fops = {
151     .owner    =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
152     .open     =   sixth_drv_open,
153     .read      =      sixth_drv_read,
154     .release  =   sixth_drv_close,
155     .poll      =      sixth_drv_poll,   
156     .fasync   =   sixth_drv_fasync,
157 };
158 int sixth_drv_init(void)
159 {
160     major = register_chrdev(0,"sixth_drv",&sixth_drv_fops);  //注册,告诉内核    
161     init_timer(&buttons_timer);            /*初始化定时器*/  
162     buttons_timer.function = buttons_timer_function;
163     //buttons_timer.expires =0 ;
164     add_timer(&buttons_timer);
165     sixthdrv_class = class_create(THIS_MODULE, "sixthdrv");
166     sixthdrv_class_dev = device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/button */
167     gpfcon=(volatile unsigned long)ioremap(0x56000050,16);//gpio 映射
168     gpfdat=gpfcon+1;
169     gpgcon=(volatile unsigned long)ioremap(0x56000060,16);//gpio 映射
170     gpgdat=gpgcon+1;  
171     return 0;
172 }
173 void sixth_drv_exit(void)
174 {
175     unregister_chrdev(major,"sixth_drv");  //卸载,告诉内核
176     device_destroy(sixthdrv_class, MKDEV(major, 0));
177     class_destroy(sixthdrv_class);
178     iounmap(gpfcon);
179     iounmap(gpgcon);
180 }
181 module_init(sixth_drv_init);
182 module_exit(sixth_drv_exit);
183 MODULE_LICENSE("GPL");
View Code

 

测试程序:

 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <fcntl.h>
 4 #include <stdio.h>
 5 #include <poll.h>
 6 #include <signal.h>
 7 #include <unistd.h>
 8 #include <fcntl.h>
 9 /* buttonsdrvtest
10   *
11   */
12 int fd;
13 #if 0
14 void my_signal_fun(int signum)
15 {
16     unsigned char key_val;
17     read(fd, &key_val,1);
18     printf("key_val = 0x%x\n", key_val);
19 }
20 #endif
21 int main(int argc, char **argv)
22 {
23     int ret;
24     int Oflags;
25     unsigned char key_val;
26     struct pollfd fds[1];
27    
28     fd = open("/dev/buttons", O_RDWR);      //阻塞格式
29 //    fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);    //非阻塞格式
30     if (fd < 0)
31     {
32         printf("can't open!\n");
33         return -1    ;
34     }
35 //    signal(SIGIO,my_signal_fun);
36 //    fcntl(fd, F_SETOWN, getpid());
37 //    Oflags = fcntl(fd, F_GETFL);
38 //    fcntl(fd, F_SETFL, Oflags | FASYNC);
39     while(1)
40     {
41         ret = read(fd, &key_val,1);
42         printf("key_val = 0x%x,ret = %d\n", key_val,ret);
43         key_val = 0;
44         //sleep(5);       
45     }
46     return 0;
47 }
View Code

 

总结:按键时,发生中断,调用 irqreturn_t bottons_irq ,mod_timer设置定时器超时时间10ms,10ms后再调用buttons_timer_function,进行按键操作处理。

 

posted on 2013-11-20 16:44  民天  阅读(295)  评论(0)    收藏  举报

导航