在测试按键驱动时,可能会出现按键抖动的问题,也就是按下或松开一次,却进入了多次中断程序。如下图所示:
产生这个问题是由于现实中的高电平转成低电平脉冲过程是机械式开关,可能会有机械的抖动导致多次触发中断
针对这个问题,我们可以采用延时检测来防止抖动,这就是这节要谈论的内核定时器,内核定时器基于软中断,因此在处理函数中不允许睡眠
一、内核定时器的使用方法
定时器系列函数使用方法如下:
struct timer_list timer; // 定义定时器 init_timer(&timer); // 初始化定时器,初始化timer成员 timer.function = timer_func; // 设置定时器触发函数 或 setup_timer(&timer, timer_func, (unsigned long)arg); /* 函数参数中jiffies表示表示当前的系统时钟中断总次数 * 一秒内时钟中断的次数等于Hz * jiffies + HZ / 100表示此定时器的间隔时间为0.01秒,也就是10ms */ mod_timer(&timer, jiffies + HZ / 100); // 设置定时器间隔时间 add_timer(&timer); // 开始定时器 del_timer(&timer); // 删除定时器
代码中的timer_func()函数需要执行的就是之前的中断程序中的代码,现在的中断程序用于设置定时器间隔时间来进行延时操作
示例代码如下:
1 struct key_device { 2 ... 3 struct timer_list key_timer; // 定义定时器 4 }; 5 6 static void key_timer_func(unsigned long arg) 7 { 8 struct key_device* dev = (struct key_device *)arg; 9 ... /* 之前中断程序的代码 */ 10 } 11 12 static irqreturn_t key_interrupt(int irq, void *dev_id) 13 { 14 dev->pindesc = (struct pin_desc *)dev_id; 15 16 mod_timer(&dev->key_timer, jiffies + HZ / 100); // 设置延时 17 18 return IRQ_HANDLED; 19 } 20 21 static int key_open(struct inode *nodep, struct file *filp) 22 { 23 struct key_device *dev = container_of(nodep->i_cdev, struct key_device, cdev); 24 ... 25 26 init_timer(&dev->key_timer); // 初始化定时器,初始化timer成员 27 setup_timer(&dev->key_timer, key_timer_func, (unsigned long)dev); 28 add_timer(&dev->key_timer); // 启动定时器 29 30 return 0; 31 } 32 33 static int key_release(struct inode *nodep, struct file *filp) 34 { 35 struct key_device *dev = filp->private_data; 36 ... 37 del_timer(&dev->key_timer); // 删除定时器 38 }
示例代码中需要注意的是:在key_open()函数中并没有设置定时器的间隔时间,而是在中断中设置的
这是因为我们是在中断中设置延时防止抖动;在其他函数中并不需要延时,直接启动定时器即可
二、完整代码
key源代码:

1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/init.h> 4 #include <linux/cdev.h> 5 #include <linux/slab.h> 6 #include <linux/device.h> 7 #include <linux/irq.h> 8 #include <linux/interrupt.h> 9 #include <linux/wait.h> 10 #include <linux/timer.h> 11 #include <linux/gpio.h> 12 #include <linux/sched.h> 13 #include <linux/mutex.h> 14 15 #include <asm/uaccess.h> 16 #include <asm/irq.h> 17 #include <asm/io.h> 18 19 #include <mach/gpio.h> 20 21 #define KEY_MAJOR 255 22 23 struct pin_desc { 24 int gpio; 25 int val; 26 char *name; 27 }; 28 29 struct key_device { 30 struct cdev cdev; 31 wait_queue_head_t r_head; 32 wait_queue_head_t w_head; 33 struct mutex lock; 34 struct fasync_struct *fasync; 35 struct timer_list key_timer; 36 struct pin_desc *pindesc; 37 }; 38 39 static struct pin_desc desc[4] = { 40 { EXYNOS4_GPX3(2), 0x01, "KEY0" }, 41 { EXYNOS4_GPX3(3), 0x02, "KEY1" }, 42 { EXYNOS4_GPX3(4), 0x03, "KEY2" }, 43 { EXYNOS4_GPX3(5), 0x04, "KEY3" }, 44 }; 45 46 static int g_major = KEY_MAJOR; 47 module_param(g_major, int, S_IRUGO); 48 49 static struct key_device* dev; 50 static struct class* scls; 51 static struct device* sdev; 52 static unsigned char key_val; 53 54 static void key_timer_func(unsigned long arg) 55 { 56 struct key_device* dev = (struct key_device *)arg; 57 58 unsigned int tmp; 59 60 tmp = gpio_get_value(dev->pindesc->gpio); 61 62 /* active low */ 63 printk(KERN_DEBUG "KEY %d: %08x\n", dev->pindesc->val, tmp); 64 65 if (tmp) 66 key_val = dev->pindesc->val; 67 else 68 key_val = dev->pindesc->val | 0x80; 69 70 set_current_state(TASK_RUNNING); 71 } 72 73 static irqreturn_t key_interrupt(int irq, void *dev_id) 74 { 75 dev->pindesc = (struct pin_desc *)dev_id; 76 77 mod_timer(&dev->key_timer, jiffies + HZ / 100); 78 79 return IRQ_HANDLED; 80 } 81 82 static ssize_t key_read(struct file *filp, char __user *buf, size_t len, loff_t * loff) 83 { 84 struct key_device *dev = filp->private_data; 85 86 // 声明等待队列 87 DECLARE_WAITQUEUE(rwait, current); 88 mutex_lock(&dev->lock); // 上锁 89 add_wait_queue(&dev->r_head, &rwait); 90 91 // 休眠 92 __set_current_state(TASK_INTERRUPTIBLE); 93 mutex_unlock(&dev->lock); // 解锁 94 schedule(); 95 96 // 有数据 97 kill_fasync(&dev->fasync, SIGIO, POLL_IN); 98 99 mutex_lock(&dev->lock); // 上锁 100 copy_to_user(buf, &key_val, 1); 101 mutex_unlock(&dev->lock); // 解锁 102 103 remove_wait_queue(&dev->r_head, &rwait); 104 set_current_state(TASK_RUNNING); 105 106 return 1; 107 } 108 109 static int key_open(struct inode *nodep, struct file *filp) 110 { 111 struct key_device *dev = container_of(nodep->i_cdev, struct key_device, cdev); 112 // 放入私有数据中 113 filp->private_data = dev; 114 115 int irq; 116 int i, err = 0; 117 118 for (i = 0; i < ARRAY_SIZE(desc); i++) { 119 if (!desc[i].gpio) 120 continue; 121 122 irq = gpio_to_irq(desc[i].gpio); 123 err = request_irq(irq, key_interrupt, IRQ_TYPE_EDGE_BOTH, 124 desc[i].name, (void *)&desc[i]); 125 if (err) 126 break; 127 } 128 129 if (err) { 130 i--; 131 for (; i >= 0; i--) { 132 if (!desc[i].gpio) 133 continue; 134 135 irq = gpio_to_irq(desc[i].gpio); 136 free_irq(irq, (void *)&desc[i]); 137 } 138 return -EBUSY; 139 } 140 141 init_waitqueue_head(&dev->r_head); 142 mutex_init(&dev->lock); // 初始化 143 144 init_timer(&dev->key_timer); 145 setup_timer(&dev->key_timer, key_timer_func, (unsigned long)dev); 146 add_timer(&dev->key_timer); 147 148 return 0; 149 } 150 151 static int key_fasync(int fd, struct file *filp, int mode) 152 { 153 struct key_device *dev = filp->private_data; 154 155 return fasync_helper(fd, filp, mode, &dev->fasync); 156 } 157 158 static int key_release(struct inode *nodep, struct file *filp) 159 { 160 struct key_device *dev = filp->private_data; 161 162 key_fasync(-1, filp, 0); 163 164 // 释放中断 165 int irq, i; 166 167 for (i = 0; i < ARRAY_SIZE(desc); i++) { 168 if (!desc[i].gpio) 169 continue; 170 171 irq = gpio_to_irq(desc[i].gpio); 172 free_irq(irq, (void *)&desc[i]); 173 } 174 175 del_timer(&dev->key_timer); 176 177 return 0; 178 } 179 180 static struct file_operations key_fops = { 181 .owner = THIS_MODULE, 182 .read = key_read, 183 .open = key_open, 184 .fasync = key_fasync, 185 .release = key_release, 186 }; 187 188 static int keys_init(void) 189 { 190 int ret; 191 int devt; 192 if (g_major) { 193 devt = MKDEV(g_major, 0); 194 ret = register_chrdev_region(devt, 1, "key"); 195 } 196 else { 197 ret = alloc_chrdev_region(&devt, 0, 1, "key"); 198 g_major = MAJOR(devt); 199 } 200 201 if (ret) 202 return ret; 203 204 dev = kzalloc(sizeof(struct key_device), GFP_KERNEL); 205 if (!dev) { 206 ret = -ENOMEM; 207 goto fail_alloc; 208 } 209 210 cdev_init(&dev->cdev, &key_fops); 211 ret = cdev_add(&dev->cdev, devt, 1); 212 if (ret) 213 return ret; 214 215 scls = class_create(THIS_MODULE, "key"); 216 sdev = device_create(scls, NULL, devt, NULL, "key"); 217 218 return 0; 219 220 fail_alloc: 221 unregister_chrdev_region(devt, 1); 222 223 return ret; 224 } 225 226 static void keys_exit(void) 227 { 228 dev_t devt = MKDEV(g_major, 0); 229 230 device_destroy(scls, devt); 231 class_destroy(scls); 232 233 cdev_del(&(dev->cdev)); 234 kfree(dev); 235 236 unregister_chrdev_region(devt, 1); 237 } 238 239 module_init(keys_init); 240 module_exit(keys_exit); 241 242 MODULE_LICENSE("GPL");
Makefile:

1 KERN_DIR = /work/tiny4412/tools/linux-3.5 2 3 all: 4 make -C $(KERN_DIR) M=`pwd` modules 5 6 clean: 7 make -C $(KERN_DIR) M=`pwd` modules clean 8 rm -rf modules.order 9 10 obj-m += key.o
测试文件:

1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 #include <string.h> 7 #include <signal.h> 8 9 int fd; 10 11 static void sig_handler(int signo) 12 { 13 unsigned char key_val; 14 read(fd, &key_val, 1); 15 printf("key_val = 0x%x\n", key_val); 16 } 17 18 int main(int argc, char** argv) 19 { 20 int oflags = 0; 21 22 signal(SIGIO, sig_handler); 23 24 fd = open("/dev/key", O_RDWR); 25 if (fd < 0) { 26 printf("can't open /dev/key\n"); 27 return -1; 28 } 29 30 fcntl(fd, F_SETOWN, getpid()); 31 oflags = fcntl(fd, F_GETFL); 32 fcntl(fd, F_SETFL, oflags | FASYNC); 33 34 while (1) { 35 sleep(1000); 36 } 37 38 close(fd); 39 40 return 0; 41 }
源代码中59、60行代码(如下),用于防止open()函数中add_timer()时进入timer函数
1 if (dev->pindesc == NULL) 2 return;
下一章 8、输入子系统