博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
字符设备驱动程序——定时器去抖动按键驱动
阅读量:6428 次
发布时间:2019-06-23

本文共 10250 字,大约阅读时间需要 34 分钟。

hot3.png

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

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

有两个要素:

一、超时时间

二、处理函数

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

struct timer_list {	struct list_head entry;	unsigned long expires;	void (*function)(unsigned long);	unsigned long data;	struct tvec_base *base;	.....};

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

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

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

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

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

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

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

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

一般的形式为:   jiffies + (HZ /100),HZ 表示100个jiffies,jiffies的单位为10ms,即HZ = 100*10ms = 1s

详细请参考驱动源码:

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
//class_create#include
//S3C2410_GPF1//#include
#include
//#include
#include
//wait_event_interruptible#include
//poll#include
/* 定义并初始化等待队列头 */static DECLARE_WAIT_QUEUE_HEAD(button_waitq);static struct class *sixthdrv_class;static struct device *sixthdrv_device;static struct pin_desc{ unsigned int pin; unsigned int key_val;};static struct pin_desc pins_desc[4] = { {S3C2410_GPF1,0x01}, {S3C2410_GPF4,0x02}, {S3C2410_GPF2,0x03}, {S3C2410_GPF0,0x04},}; struct pin_desc *irq_pindes;static int ev_press = 0;/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 *//* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */static unsigned char key_val;int major;static struct fasync_struct *button_fasync;static struct timer_list buttons_timer; /* 定义一个定时器结构体 */#if 0static atomic_t canopen = ATOMIC_INIT(1); //定义原子变量canopen并初始化为1#endifstatic DECLARE_MUTEX(button_lock); //定义互斥锁/* 用户中断处理函数 */static irqreturn_t buttons_irq(int irq, void *dev_id){ int ret; irq_pindes = (struct pin_desc *)dev_id; /* 修改定时器定时时间,定时10ms,即10秒后启动定时器 * HZ 表示100个jiffies,jiffies的单位为10ms,即HZ = 100*10ms = 1s * 这里HZ/100即定时10ms */ ret = mod_timer(&buttons_timer, jiffies + (HZ /100)); if(ret == 1) { printk("mod timer success\n"); } return IRQ_HANDLED;}static int sixth_drv_open(struct inode * inode, struct file * filp){#if 0 /* 自减操作后测试其是否为0,为0则返回true,否则返回false */ if(!atomic_dec_and_test(&canopen)) { atomic_inc(&canopen); //原子变量增加1 return -EBUSY; }#endif /* 当打开的文件有O_NONBLOCK标记时,表示不阻塞 */ if(filp->f_flags & O_NONBLOCK) { /* 尝试获取button_lock信号量,当获取不到时立即返回 */ if (down_trylock(&button_lock)) return -EBUSY; } else { /* 获取button_lock信号量,当获取不到时,将会休眠 * 但是这种休眠是不可以被中断打断的 */ down(&button_lock); } /* K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0 * 配置GPF1、GPF4、GPF2、GPF0为相应的外部中断引脚 * IRQT_BOTHEDGE应该改为IRQ_TYPE_EDGE_BOTH */ request_irq(IRQ_EINT1, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1",&pins_desc[0]); request_irq(IRQ_EINT4, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2",&pins_desc[1]); request_irq(IRQ_EINT2, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3",&pins_desc[2]); request_irq(IRQ_EINT0, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4",&pins_desc[3]); return 0;}static ssize_t sixth_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos){ if (size != 1) return -EINVAL; /* 当打开的文件有O_NONBLOCK标记时,表示不阻塞 */ if(file->f_flags & O_NONBLOCK) { /* 当ev_press = 0时,表示没有按键被按下,即表示没有数据 */ if(!ev_press) return -EAGAIN; } else { /* 当没有按键按下时,休眠。 * 即ev_press = 0; * 当有按键按下时,发生中断,在中断处理函数会唤醒 * 即ev_press = 1; * 唤醒后,接着继续将数据通过copy_to_user函数传递给应用程序 */ wait_event_interruptible(button_waitq, ev_press); } copy_to_user(user, &key_val, 1); /* 将ev_press清零 */ ev_press = 0; return 1; }static int sixth_drv_close(struct inode *inode, struct file *file){#if 0 atomic_inc(&canopen); //原子变量增加1#endif free_irq(IRQ_EINT1,&pins_desc[0]); free_irq(IRQ_EINT4,&pins_desc[1]); free_irq(IRQ_EINT2,&pins_desc[2]); free_irq(IRQ_EINT0,&pins_desc[3]); /* 释放信号量 */ up(&button_lock); return 0;}static unsigned int sixth_drv_poll(struct file *file, poll_table *wait){ unsigned int mask = 0; /* 该函数,只是将进程挂在button_waitq队列上,而不是立即休眠 */ poll_wait(file, &button_waitq, wait); /* 当没有按键按下时,即不会进入按键中断处理函数,此时ev_press = 0 * 当按键按下时,就会进入按键中断处理函数,此时ev_press被设置为1 */ if(ev_press) { mask |= POLLIN | POLLRDNORM; /* 表示有数据可读 */ } /* 如果有按键按下时,mask |= POLLIN | POLLRDNORM,否则mask = 0 */ return mask; }/* 当应用程序调用了fcntl(fd, F_SETFL, Oflags | FASYNC); * 则最终会调用驱动的fasync函数,在这里则是sixth_drv_fasync * sixth_drv_fasync最终又会调用到驱动的fasync_helper函数 * fasync_helper函数的作用是初始化/释放fasync_struct */static int sixth_drv_fasync(int fd, struct file *filp, int on){ return fasync_helper(fd, filp, on, &button_fasync);}/* File operations struct for character device */static const struct file_operations sixth_drv_fops = { .owner = THIS_MODULE, .open = sixth_drv_open, .read = sixth_drv_read, .release = sixth_drv_close, .poll = sixth_drv_poll, .fasync = sixth_drv_fasync,};/* 定时器处理函数 */static void buttons_timer_function(unsigned long data){ struct pin_desc *pindesc = irq_pindes; unsigned int 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函数告诉应用程序,有数据可读了 * button_fasync结构体里包含了发给谁(PID指定) * SIGIO表示要发送的信号类型 * POLL_IN表示发送的原因(有数据可读了) */ kill_fasync(&button_fasync, SIGIO, POLL_IN);}/* 驱动入口函数 */static int sixth_drv_init(void){ /* 初始化定时器 */ init_timer(&buttons_timer); /* 当定时时间到达时uttons_timer_function就会被调用 */ buttons_timer.function = buttons_timer_function; /* 向内核注册一个定时器 */ add_timer(&buttons_timer); /* 主设备号设置为0表示由系统自动分配主设备号 */ major = register_chrdev(0, "sixth_drv", &sixth_drv_fops); /* 创建sixthdrv类 */ sixthdrv_class = class_create(THIS_MODULE, "sixthdrv"); /* 在sixthdrv类下创建buttons设备,供应用程序打开设备*/ sixthdrv_device = device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); return 0;}/* 驱动出口函数 */static void sixth_drv_exit(void){ unregister_chrdev(major, "sixth_drv"); device_unregister(sixthdrv_device); //卸载类下的设备 class_destroy(sixthdrv_class); //卸载类}module_init(sixth_drv_init); //用于修饰入口函数module_exit(sixth_drv_exit); //用于修饰出口函数 MODULE_AUTHOR("LWJ");MODULE_DESCRIPTION("Just for Demon");MODULE_LICENSE("GPL"); //遵循GPL协议

应用测试程序源码:

#include 
#include
#include
#include
#include
//sleep#include
#include
#include
/* buttons_all_test */ int main(int argc ,char *argv[]){ int fd; unsigned char key_val; fd = open("/dev/buttons",O_RDWR); /* 以阻塞方式读 */ if (fd < 0) { printf("open error\n"); return -1; } while(1) { int ret =read(fd,&key_val,1); printf("key_val: 0x%x, ret = %d\n", key_val, ret); //sleep(3); } return 0;}

测试步骤:

[WJ2440]# lsQt                  fourth_drv.ko       sixth_drv.koTQLedtest           fourth_test         sixth_testapp_test            home                sixthdrvtestbin                 lib                 sysbuttons_all_drv.ko  linuxrc             third_drv.kobuttons_all_test    mnt                 third_testdev                 opt                 tmpdriver_test         proc                udisketc                 root                usrfifth_drv.ko        sbin                varfifth_test          sddisk              webfirst_drv.ko        second_drv.kofirst_test          second_test[WJ2440]# insmod  buttons_all_drv.ko [WJ2440]# lsmod buttons_all_drv 3936 0 - Live 0xbf000000[WJ2440]# ls /dev/buttons -l crw-rw----    1 root     root      252,   0 Jan  2 05:43 /dev/buttons[WJ2440]# ./buttons_all_test key_val: 0x1, ret = 1key_val: 0x81, ret = 1key_val: 0x1, ret = 1key_val: 0x81, ret = 1key_val: 0x4, ret = 1key_val: 0x84, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x3, ret = 1key_val: 0x83, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1key_val: 0x2, ret = 1key_val: 0x82, ret = 1[WJ2440]# ./buttons_all_test  &[WJ2440]# topMem: 9996K used, 50168K free, 0K shrd, 0K buff, 7180K cachedCPU:  0.3% usr  0.5% sys  0.0% nic 99.0% idle  0.0% io  0.0% irq  0.0% sirqLoad average: 0.02 0.05 0.01 1/23 604  PID  PPID USER     STAT   VSZ %MEM CPU %CPU COMMAND  604   589 root     R     2092  3.4   0  0.9 top  589     1 root     S     2092  3.4   0  0.0 -/bin/sh    1     0 root     S     2088  3.4   0  0.0 init  590     1 root     S     2088  3.4   0  0.0 /usr/sbin/telnetd -l /bin/login  587     1 root     S     1508  2.5   0  0.0 EmbedSky_wdg  603   589 root     S     1428  2.3   0  0.0 ./buttons_all_test  573     2 root     SW<      0  0.0   0  0.0 [rpciod/0]    5     2 root     SW<      0  0.0   0  0.0 [khelper]  329     2 root     SW<      0  0.0   0  0.0 [nfsiod]    2     0 root     SW<      0  0.0   0  0.0 [kthreadd]    4     2 root     SW<      0  0.0   0  0.0 [events/0]    3     2 root     SW<      0  0.0   0  0.0 [ksoftirqd/0]   11     2 root     SW<      0  0.0   0  0.0 [async/mgr]  237     2 root     SW<      0  0.0   0  0.0 [kblockd/0]  247     2 root     SW<      0  0.0   0  0.0 [khubd]  254     2 root     SW<      0  0.0   0  0.0 [kmmcd]  278     2 root     SW       0  0.0   0  0.0 [pdflush]  279     2 root     SW       0  0.0   0  0.0 [pdflush]  280     2 root     SW<      0  0.0   0  0.0 [kswapd0]  325     2 root     SW<      0  0.0   0  0.0 [aio/0]

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

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

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

转载于:https://my.oschina.net/cht2000/blog/908439

你可能感兴趣的文章
【转】Install SmartGit via PPA in Ubuntu 13.10/13.04/12.04/Linux Mint
查看>>
PNG怎么转换成32位的BMP保持透明
查看>>
经验分享:CSS浮动(float,clear)通俗讲解
查看>>
WTL中最简单的实现窗口拖动的方法(转)
查看>>
数据结构—队列
查看>>
C. Adidas vs Adivon
查看>>
BZOJ4241 : 历史研究
查看>>
(LeetCode)两个队列来实现一个栈
查看>>
[WebGL入门]十九,遮挡剔除和深度測试
查看>>
Codeforces 220B - Little Elephant and Array 离线树状数组
查看>>
jquery封装常用方法
查看>>
Rest API 开发 学习笔记
查看>>
小练习:元素的拖放
查看>>
什么是ICE (Internet Communications Engine)
查看>>
移动web开发之屏幕三要素
查看>>
求按小时统计的语句,该怎么处理
查看>>
python中的@
查看>>
TRUNCATE,DORP,DELETE
查看>>
Chrome的开发必备小技巧
查看>>
can-i-win(好)
查看>>