linux字符驱动之定时器去抖动按键驱动

转自:http://blog.csdn.net/lwj103862095/article/details/17589311

上一节里,实现同一时刻只能有一个进程使用同一个设备,例如:只能有一个进程,在同一时刻里使用/dev/buttons这个设备。

上一节文章链接:http://blog.csdn.net/xishuang_gongzi/article/details/50722750

相信大家在写单片机的按键程序时,也必将会涉及一点,就去按键去抖动。按键去抖动的方法无非有二种,一种是硬件电路去抖动,这种在要求不是特别高的情况下是不会被采用的;另一种就是延时去抖动了。而延时又一般分为二种,一种是for循环死等待,一种是定时延时。对,这一节里我们来使用内核的定时器去抖动。

问:linux内核定时器有哪些要素?

答:有两个要素:

一、超时时间

二、处理函数

问:linux定时器结构是怎样的?

答:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. struct timer_list {  
  2.     struct list_head entry;  
  3.     unsigned long expires;  
  4.     void (*function)(unsigned long);  
  5.     unsigned long data;  
  6.     struct tvec_base *base;  
  7.     .....  
  8. };  

问:void (*function)(unsigned long data)里面的参数是谁传给它的?

答:是timer_list.data传给它的,如果需要向function传递参数时,则应该设置timer_list.data,否则可以不设置。

问:与定时器相关的操作函数有哪些?

答:

一、使用init_timer函数初始化定时器

二、设置timer_list.function,并实现这个函数指针

三、使用add_timer函数向内核注册一个定时器

四、使用mod_timer修改定时器时间,并启动定时器

参考http://blog.chinaunix.net/uid-29783732-id-5615375.html

问:int mod_timer(struct timer_list *timer, unsigned long expires)的第二个参数为超时时间,怎么设置超时时间,如果定时为10ms?

答:一般的形式为:   jiffies + (HZ /100),HZ是每秒系统节拍数,这里是100个jiffies,所以每个jiffies是10ms 


详细请参考驱动源码:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <linux/kernel.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/init.h>  
  4. #include <linux/delay.h>  
  5. #include <linux/irq.h>  
  6. #include <asm/uaccess.h>  
  7. #include <asm/irq.h>  
  8. #include <asm/io.h>  
  9. #include <linux/module.h>  
  10. #include <linux/device.h>         //class_create  
  11. #include <mach/regs-gpio.h>       //S3C2410_GPF1  
  12. //#include <asm/arch/regs-gpio.h>    
  13. #include <mach/hardware.h>  
  14. //#include <asm/hardware.h>  
  15. #include <linux/interrupt.h>  //wait_event_interruptible  
  16. #include <linux/poll.h>   //poll  
  17. #include <linux/fcntl.h>  
  18.   
  19.   
  20. /* 定义并初始化等待队列头 */  
  21. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);  
  22.   
  23.   
  24. static struct class *sixthdrv_class;  
  25. static struct device *sixthdrv_device;  
  26.   
  27. static struct pin_desc{  
  28.     unsigned int pin;  
  29.     unsigned int key_val;  
  30. };  
  31. struct pin_desc *irq_pindes
  32.   
  33. static struct pin_desc pins_desc[4] = {  
  34.         {S3C2410_GPF1,0x01},  
  35.         {S3C2410_GPF4,0x02},  
  36.         {S3C2410_GPF2,0x03},  
  37.         {S3C2410_GPF0,0x04},  
  38. };   
  39.   
  40. static int ev_press = 0;  
  41.   
  42. /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */  
  43. /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */  
  44. static unsigned char key_val;  
  45. int major;  
  46.   
  47. static struct fasync_struct *button_fasync;  
  48. static struct timer_list buttons_timer;  /* 定义一个定时器结构体 */  
  49.   
  50. #if 0  
  51. static atomic_t canopen = ATOMIC_INIT(1);     //定义原子变量canopen并初始化为1  
  52. #endif  
  53.   
  54. static DEFINE_SEMAPHORE(button_lock);     /*定义一个信号量并初始化为1*/
  55.   
  56. /* 用户中断处理函数 */  
  57. static irqreturn_t buttons_irq(int irq, void *dev_id)  
  58. {  
  59.     int ret;  
  60.     irq_pindes = (struct pin_desc *)dev_id;  
  61.       
  62.     /* 修改定时器定时时间,定时10ms,即10ms后定时器超时 
  63.      *  HZ是每秒系统节拍数,这里是100个jiffies,所以每个jiffies是10ms 
  64.      */  
  65.     ret = mod_timer(&buttons_timer, jiffies + (HZ /100));  
  66.     if(ret == 1)  
  67.     {  
  68.         printk("mod timer success\n");  
  69.     }  
  70.     return IRQ_HANDLED;  
  71. }  
  72. static int sixth_drv_open(struct inode * inode, struct file * filp)  
  73. {  
  74. #if 0  
  75.     /* 自减操作后测试其是否为0,为0则返回true,否则返回false */  
  76.     if(!atomic_dec_and_test(&canopen))  
  77.     {  
  78.         atomic_inc(&canopen); //原子变量增加1  
  79.         return -EBUSY;  
  80.     }  
  81. #endif  
  82.   
  83.     /* 当打开的文件有O_NONBLOCK标记时,表示不阻塞 */  
  84.     if(filp->f_flags & O_NONBLOCK)  
  85.     {  
  86.         /* 尝试获取button_lock信号量,当获取不到时立即返回 */  
  87.         if (down_trylock(&button_lock))  
  88.                 return -EBUSY;  
  89.     }  
  90.     else  
  91.     {  
  92.         /* 获取button_lock信号量,当获取不到时,将会休眠 
  93.         * 但是这种休眠是不可以被中断打断的 
  94.         */  
  95.         down(&button_lock);  
  96.     }  
  97.           
  98.     /*  K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0 
  99.      *  配置GPF1、GPF4、GPF2、GPF0为相应的外部中断引脚 
  100.      *  IRQT_BOTHEDGE应该改为IRQ_TYPE_EDGE_BOTH 
  101.      */  
  102.     request_irq(IRQ_EINT1, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1",&pins_desc[0]);  
  103.     request_irq(IRQ_EINT4, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2",&pins_desc[1]);  
  104.     request_irq(IRQ_EINT2, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3",&pins_desc[2]);  
  105.     request_irq(IRQ_EINT0, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4",&pins_desc[3]);  
  106.     return 0;  
  107. }  
  108.   
  109. static ssize_t sixth_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos)  
  110. {  
  111.     if (size != 1)  
  112.             return -EINVAL;  
  113.   
  114.     /* 当打开的文件有O_NONBLOCK标记时,表示不阻塞 */  
  115.     if(file->f_flags & O_NONBLOCK)  
  116.     {  
  117.         /* 当ev_press = 0时,表示没有按键被按下,即表示没有数据 */  
  118.         if(!ev_press)  
  119.             return -EAGAIN;  
  120.     }  
  121.     else  
  122.     {  
  123.         /* 当没有按键按下时,休眠。 
  124.          * 即ev_press = 0; 
  125.          * 当有按键按下时,发生中断,在中断处理函数会唤醒 
  126.          * 即ev_press = 1;  
  127.          * 唤醒后,接着继续将数据通过copy_to_user函数传递给应用程序 
  128.          */  
  129.         wait_event_interruptible(button_waitq, ev_press);  
  130.     }  
  131.       
  132.   
  133.     copy_to_user(user, &key_val, 1);  
  134.       
  135.     /* 将ev_press清零 */  
  136.     ev_press = 0;  
  137.     return 1;     
  138. }  
  139.   
  140. static int sixth_drv_close(struct inode *inode, struct file *file)  
  141. {  
  142. #if 0  
  143.     atomic_inc(&canopen); //原子变量增加1  
  144. #endif  
  145.     free_irq(IRQ_EINT1,&pins_desc[0]);  
  146.     free_irq(IRQ_EINT4,&pins_desc[1]);  
  147.     free_irq(IRQ_EINT2,&pins_desc[2]);  
  148.     free_irq(IRQ_EINT0,&pins_desc[3]);  
  149.   
  150.     /* 释放信号量 */  
  151.     up(&button_lock);  
  152.     return 0;  
  153. }  
  154.   
  155. static unsigned int sixth_drv_poll(struct file *file, poll_table *wait)  
  156. {  
  157.     unsigned int mask = 0;  
  158.   
  159.     /* 该函数,只是将进程挂在button_waitq队列上,而不是立即休眠 */  
  160.     poll_wait(file, &button_waitq, wait);  
  161.   
  162.     /* 当没有按键按下时,即不会进入按键中断处理函数,此时ev_press = 0  
  163.      * 当按键按下时,就会进入按键中断处理函数,此时ev_press被设置为1 
  164.      */  
  165.     if(ev_press)  
  166.     {  
  167.         mask |= POLLIN | POLLRDNORM;  /* 表示有数据可读 */  
  168.     }  
  169.   
  170.     /* 如果有按键按下时,mask |= POLLIN | POLLRDNORM,否则mask = 0 */  
  171.     return mask;    
  172. }  
  173.   
  174. /* 当应用程序调用了fcntl(fd, F_SETFL, Oflags | FASYNC);  
  175.  * 则最终会调用驱动的fasync函数,在这里则是sixth_drv_fasync 
  176.  * sixth_drv_fasync最终又会调用到驱动的fasync_helper函数 
  177.  * fasync_helper函数的作用是初始化/释放fasync_struct 
  178.  */  
  179. static int sixth_drv_fasync(int fd, struct file *filp, int on)  
  180. {  
  181.     return fasync_helper(fd, filp, on, &button_fasync);  
  182. }  
  183.   
  184. /* File operations struct for character device */  
  185. static const struct file_operations sixth_drv_fops = {  
  186.     .owner      = THIS_MODULE,  
  187.     .open       = sixth_drv_open,  
  188.     .read       = sixth_drv_read,  
  189.     .release    = sixth_drv_close,  
  190.     .poll       = sixth_drv_poll,  
  191.     .fasync     = sixth_drv_fasync,  
  192. };  
  193.   
  194. /* 定时器处理函数 */  
  195. static void buttons_timer_function(unsigned long data)  
  196. {  
  197.     struct pin_desc *pindesc = irq_pindes;  
  198.     unsigned int pinval;  
  199.     pinval = s3c2410_gpio_getpin(pindesc->pin);  
  200.   
  201.     if(pinval)  
  202.     {  
  203.         /* 松开 */  
  204.         key_val = 0x80 | (pindesc->key_val);  
  205.     }  
  206.     else  
  207.     {  
  208.         /* 按下 */  
  209.         key_val = pindesc->key_val;  
  210.     }  
  211.   
  212.     ev_press = 1;                            /* 表示中断已经发生 */  
  213.     wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */  
  214.   
  215.     /* 用kill_fasync函数告诉应用程序,有数据可读了  
  216.      * button_fasync结构体里包含了发给谁(PID指定) 
  217.      * SIGIO表示要发送的信号类型 
  218.      * POLL_IN表示发送的原因(有数据可读了) 
  219.      */  
  220.     kill_fasync(&button_fasync, SIGIO, POLL_IN);  
  221. }  
  222.   
  223. /* 驱动入口函数 */  
  224. static int sixth_drv_init(void)  
  225. {  
  226.     /* 初始化定时器 */  
  227.     init_timer(&buttons_timer);  
  228.     /* 当定时时间到达时uttons_timer_function就会被调用 */  
  229.     buttons_timer.function  = buttons_timer_function;  
  230.     /* 向内核注册一个定时器 */  
  231.     add_timer(&buttons_timer);  
  232.       
  233.     /* 主设备号设置为0表示由系统自动分配主设备号 */  
  234.     major = register_chrdev(0, "sixth_drv", &sixth_drv_fops);  
  235.   
  236.     /* 创建sixthdrv类 */  
  237.     sixthdrv_class = class_create(THIS_MODULE, "sixthdrv");  
  238.   
  239.     /* 在sixthdrv类下创建buttons设备,供应用程序打开设备*/  
  240.     sixthdrv_device = device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons");  
  241.   
  242.     return 0;  
  243. }  
  244.   
  245. /* 驱动出口函数 */  
  246. static void sixth_drv_exit(void)  
  247. {  
  248.     unregister_chrdev(major, "sixth_drv");  
  249.     device_unregister(sixthdrv_device);  //卸载类下的设备  
  250.     class_destroy(sixthdrv_class);      //卸载类  
  251. }  
  252.   
  253. module_init(sixth_drv_init);  //用于修饰入口函数  
  254. module_exit(sixth_drv_exit);  //用于修饰出口函数      
  255.   
  256. MODULE_AUTHOR("LWJ");  
  257. MODULE_DESCRIPTION("Just for Demon");  
  258. MODULE_LICENSE("GPL");  //遵循GPL协议  

应用测试程序源码:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <sys/types.h>  
  3. #include <sys/stat.h>  
  4. #include <fcntl.h>  
  5. #include <unistd.h>   //sleep  
  6. #include <poll.h>  
  7. #include <signal.h>  
  8. #include <fcntl.h>  
  9.   
  10.   
  11. /* buttons_all_test 
  12.  */   
  13. int main(int argc ,char *argv[])  
  14. {  
  15.     int fd;  
  16.     unsigned char key_val;  
  17.     fd = open("/dev/buttons",O_RDWR);   /* 以阻塞方式读 */  
  18.     if (fd < 0)  
  19.     {  
  20.         printf("open error\n");  
  21.         return -1;  
  22.     }  
  23.   
  24.     while(1)  
  25.     {  
  26.         int ret =read(fd,&key_val,1);  
  27.         printf("key_val: 0x%x, ret = %d\n", key_val, ret);  
  28.         //sleep(3);  
  29.     }  
  30.     return 0;  
  31. }  

测试步骤:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. [WJ2440]# ls  
  2. Qt                  fourth_drv.ko       sixth_drv.ko  
  3. TQLedtest           fourth_test         sixth_test  
  4. app_test            home                sixthdrvtest  
  5. bin                 lib                 sys  
  6. buttons_all_drv.ko  linuxrc             third_drv.ko  
  7. buttons_all_test    mnt                 third_test  
  8. dev                 opt                 tmp  
  9. driver_test         proc                udisk  
  10. etc                 root                usr  
  11. fifth_drv.ko        sbin                var  
  12. fifth_test          sddisk              web  
  13. first_drv.ko        second_drv.ko  
  14. first_test          second_test  
  15. [WJ2440]# insmod  buttons_all_drv.ko   
  16. [WJ2440]# lsmod   
  17. buttons_all_drv 3936 0 - Live 0xbf000000  
  18. [WJ2440]# ls /dev/buttons -l   
  19. crw-rw----    1 root     root      252,   0 Jan  2 05:43 /dev/buttons  
  20. [WJ2440]# ./buttons_all_test   
  21. key_val: 0x1, ret = 1  
  22. key_val: 0x81, ret = 1  
  23. key_val: 0x1, ret = 1  
  24. key_val: 0x81, ret = 1  
  25. key_val: 0x4, ret = 1  
  26. key_val: 0x84, ret = 1  
  27. key_val: 0x2, ret = 1  
  28. key_val: 0x82, ret = 1  
  29. key_val: 0x3, ret = 1  
  30. key_val: 0x83, ret = 1  
  31. key_val: 0x2, ret = 1  
  32. key_val: 0x82, ret = 1  
  33. key_val: 0x2, ret = 1  
  34. key_val: 0x82, ret = 1  
  35. key_val: 0x2, ret = 1  
  36. key_val: 0x82, ret = 1  
  37. key_val: 0x2, ret = 1  
  38. key_val: 0x82, ret = 1  
  39. key_val: 0x2, ret = 1  
  40. key_val: 0x82, ret = 1  
  41. key_val: 0x2, ret = 1  
  42. key_val: 0x82, ret = 1  
  43. key_val: 0x2, ret = 1  
  44. key_val: 0x82, ret = 1  
  45. key_val: 0x2, ret = 1  
  46. key_val: 0x82, ret = 1  
  47. key_val: 0x2, ret = 1  
  48. key_val: 0x82, ret = 1  
  49. key_val: 0x2, ret = 1  
  50. key_val: 0x82, ret = 1  
  51. key_val: 0x2, ret = 1  
  52. key_val: 0x82, ret = 1  
  53. key_val: 0x2, ret = 1  
  54. key_val: 0x82, ret = 1  
  55. key_val: 0x2, ret = 1  
  56. key_val: 0x82, ret = 1  
  57. key_val: 0x2, ret = 1  
  58. key_val: 0x82, ret = 1  
  59. key_val: 0x2, ret = 1  
  60. key_val: 0x82, ret = 1  
  61. key_val: 0x2, ret = 1  
  62. key_val: 0x82, ret = 1  
  63. key_val: 0x2, ret = 1  
  64. key_val: 0x82, ret = 1  
  65. key_val: 0x2, ret = 1  
  66. key_val: 0x82, ret = 1  
  67. key_val: 0x2, ret = 1  
  68. key_val: 0x82, ret = 1  
  69. key_val: 0x2, ret = 1  
  70. key_val: 0x82, ret = 1  
  71. key_val: 0x2, ret = 1  
  72. key_val: 0x82, ret = 1  
  73. key_val: 0x2, ret = 1  
  74. key_val: 0x82, ret = 1  
  75. [WJ2440]# ./buttons_all_test  &  
  76. [WJ2440]# top  
  77. Mem: 9996K used, 50168K free, 0K shrd, 0K buff, 7180K cached  
  78. CPU:  0.3% usr  0.5% sys  0.0% nic 99.0% idle  0.0% io  0.0% irq  0.0% sirq  
  79. Load average: 0.02 0.05 0.01 1/23 604  
  80.   PID  PPID USER     STAT   VSZ %MEM CPU %CPU COMMAND  
  81.   604   589 root     R     2092  3.4   0  0.9 top  
  82.   589     1 root     S     2092  3.4   0  0.0 -/bin/sh  
  83.     1     0 root     S     2088  3.4   0  0.0 init  
  84.   590     1 root     S     2088  3.4   0  0.0 /usr/sbin/telnetd -l /bin/login  
  85.   587     1 root     S     1508  2.5   0  0.0 EmbedSky_wdg  
  86.   603   589 root     S     1428  2.3   0  0.0 ./buttons_all_test  
  87.   573     2 root     SW<      0  0.0   0  0.0 [rpciod/0]  
  88.     5     2 root     SW<      0  0.0   0  0.0 [khelper]  
  89.   329     2 root     SW<      0  0.0   0  0.0 [nfsiod]  
  90.     2     0 root     SW<      0  0.0   0  0.0 [kthreadd]  
  91.     4     2 root     SW<      0  0.0   0  0.0 [events/0]  
  92.     3     2 root     SW<      0  0.0   0  0.0 [ksoftirqd/0]  
  93.    11     2 root     SW<      0  0.0   0  0.0 [async/mgr]  
  94.   237     2 root     SW<      0  0.0   0  0.0 [kblockd/0]  
  95.   247     2 root     SW<      0  0.0   0  0.0 [khubd]  
  96.   254     2 root     SW<      0  0.0   0  0.0 [kmmcd]  
  97.   278     2 root     SW       0  0.0   0  0.0 [pdflush]  
  98.   279     2 root     SW       0  0.0   0  0.0 [pdflush]  
  99.   280     2 root     SW<      0  0.0   0  0.0 [kswapd0]  
  100.   325     2 root     SW<      0  0.0   0  0.0 [aio/0]  

由测试结果可知,无论按多少次,按键都是成对出现的,即按下、松开;按下、松开;按下、松开,而不会出现按下、按下、按下、松开这种抖动情况,这就完成了定时器消抖动的目的。

这里贴一张韦老师的定时器消抖动的按键分析图:




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值