7、内核定时器


 

在测试按键驱动时,可能会出现按键抖动的问题,也就是按下或松开一次,却进入了多次中断程序。如下图所示:

产生这个问题是由于现实中的高电平转成低电平脉冲过程是机械式开关,可能会有机械的抖动导致多次触发中断

针对这个问题,我们可以采用延时检测来防止抖动,这就是这节要谈论的内核定时器,内核定时器基于软中断,因此在处理函数中不允许睡眠

 

 

一、内核定时器的使用方法

定时器系列函数使用方法如下:

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");
View Code

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
View Code

测试文件:

 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 }
View Code

 

源代码中59、60行代码(如下),用于防止open()函数中add_timer()时进入timer函数

1 if (dev->pindesc == NULL)
2     return;

 

 

下一章  8、输入子系统

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM