在測試按鍵驅動時,可能會出現按鍵抖動的問題,也就是按下或松開一次,卻進入了多次中斷程序。如下圖所示:

產生這個問題是由於現實中的高電平轉成低電平脈沖過程是機械式開關,可能會有機械的抖動導致多次觸發中斷
針對這個問題,我們可以采用延時檢測來防止抖動,這就是這節要談論的內核定時器,內核定時器基於軟中斷,因此在處理函數中不允許睡眠
一、內核定時器的使用方法
定時器系列函數使用方法如下:
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、輸入子系統
