转自:http://blog.csdn.net/lwj103862095/article/details/17589311
上一节里,实现同一时刻只能有一个进程使用同一个设备,例如:只能有一个进程,在同一时刻里使用/dev/buttons这个设备。
上一节文章链接:http://blog.csdn.net/xishuang_gongzi/article/details/50722750
相信大家在写单片机的按键程序时,也必将会涉及一点,就去按键去抖动。按键去抖动的方法无非有二种,一种是硬件电路去抖动,这种在要求不是特别高的情况下是不会被采用的;另一种就是延时去抖动了。而延时又一般分为二种,一种是for循环死等待,一种是定时延时。对,这一节里我们来使用内核的定时器去抖动。
问:linux内核定时器有哪些要素?
答:有两个要素:
一、超时时间
二、处理函数
问:linux定时器结构是怎样的?
答:
- struct timer_list {
- struct list_head entry;
- unsigned long expires;
- void (*function)(unsigned long);
- unsigned long data;
- struct tvec_base *base;
- .....
- };
问: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
详细请参考驱动源码:
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/delay.h>
- #include <linux/irq.h>
- #include <asm/uaccess.h>
- #include <asm/irq.h>
- #include <asm/io.h>
- #include <linux/module.h>
- #include <linux/device.h> //class_create
- #include <mach/regs-gpio.h> //S3C2410_GPF1
- //#include <asm/arch/regs-gpio.h>
- #include <mach/hardware.h>
- //#include <asm/hardware.h>
- #include <linux/interrupt.h> //wait_event_interruptible
- #include <linux/poll.h> //poll
- #include <linux/fcntl.h>
- /* 定义并初始化等待队列头 */
- 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;
- };
- struct pin_desc *irq_pindes
- static struct pin_desc pins_desc[4] = {
- {S3C2410_GPF1,0x01},
- {S3C2410_GPF4,0x02},
- {S3C2410_GPF2,0x03},
- {S3C2410_GPF0,0x04},
- };
- 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 0
- static atomic_t canopen = ATOMIC_INIT(1); //定义原子变量canopen并初始化为1
- #endif
- static DEFINE_SEMAPHORE(button_lock); /*定义一个信号量并初始化为1*/
- /* 用户中断处理函数 */
- static irqreturn_t buttons_irq(int irq, void *dev_id)
- {
- int ret;
- irq_pindes = (struct pin_desc *)dev_id;
- /* 修改定时器定时时间,定时10ms,即10ms后定时器超时
- * HZ是每秒系统节拍数,这里是100个jiffies,所以每个jiffies是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 <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h> //sleep
- #include <poll.h>
- #include <signal.h>
- #include <fcntl.h>
- /* 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]# ls
- Qt fourth_drv.ko sixth_drv.ko
- TQLedtest fourth_test sixth_test
- app_test home sixthdrvtest
- bin lib sys
- buttons_all_drv.ko linuxrc third_drv.ko
- buttons_all_test mnt third_test
- dev opt tmp
- driver_test proc udisk
- etc root usr
- fifth_drv.ko sbin var
- fifth_test sddisk web
- first_drv.ko second_drv.ko
- first_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 = 1
- key_val: 0x81, ret = 1
- key_val: 0x1, ret = 1
- key_val: 0x81, ret = 1
- key_val: 0x4, ret = 1
- key_val: 0x84, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x3, ret = 1
- key_val: 0x83, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- key_val: 0x2, ret = 1
- key_val: 0x82, ret = 1
- [WJ2440]# ./buttons_all_test &
- [WJ2440]# top
- Mem: 9996K used, 50168K free, 0K shrd, 0K buff, 7180K cached
- CPU: 0.3% usr 0.5% sys 0.0% nic 99.0% idle 0.0% io 0.0% irq 0.0% sirq
- Load 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]
由测试结果可知,无论按多少次,按键都是成对出现的,即按下、松开;按下、松开;按下、松开,而不会出现按下、按下、按下、松开这种抖动情况,这就完成了定时器消抖动的目的。
这里贴一张韦老师的定时器消抖动的按键分析图: