本文,我们还是在前面的按键驱动程序的基础上,引入定时器,来消除抖动。
一、内核定时器详解
1.timer_list结构体
在/include/linux/timer.h中。
struct timer_list { struct list_head entry; //定时器链表,用于存放软定时器,该链表根据定时器expirex字段的值将它们分组存放。 unsigned long expires; //定时器的到期时间,到达expires时间后,定时器将调用其成员函数function,其中将data字段作为function的参数。该字段表示的时间是以时间节拍为单位。例如如果你想定时一秒,则expires=jiffies+HZ*1。 void (*function)(unsigned long); //定时器到时处理函数 unsigned long data; //function 的参数 struct tvec_t_base_s *base; #ifdef CONFIG_TIMER_STATS void *start_site; char start_comm[16]; int start_pid; #endif }; |
时钟中断的发生频率设定为HZ,HZ是一个与体系结构无关的常数,在文件
2.初始化定时器
①init_timer
void init_timer(struct timer_list *timer) { debug_timer_init(timer); __init_timer(timer); }
static void __init_timer(struct timer_list *timer) { timer->entry.next = NULL; timer->base = __raw_get_cpu_var(tvec_bases); #ifdef CONFIG_TIMER_STATS timer->start_site = NULL; timer->start_pid = -1; memset(timer->start_comm, 0, TASK_COMM_LEN); #endif } |
init_timer()函数被定义在kernel/timer.c中,实际上是将timer的entry的next指针置为NULL,为base字段赋值。
②. timer=TIMER_INITIALIZER(function,expires,data);
采用这种初始化方式,必须首先先写好定时器处理函数function. TIMER_INITIALIZER宏的定义如下:
#define TIMER_INITIALIZER(_function, _expires, _data) { .entry = { .prev = TIMER_ENTRY_STATIC }, .function = (_function), .expires = (_expires), .data = (_data), .base = &boot_tvec_bases, } |
其中boot_tcec_bases是在kernel/timer中定义的一个全局的tvec_t_base_s类型的变量。
③.DEFINE_TIMER(timer,function,expires,data);
定义并初始化定时器timer,功能和前面差不多
#define DEFINE_TIMER(_name, _function, _expires, _data) struct timer_list _name = TIMER_INITIALIZER(_function, _expires, _data) |
④. setup_timer(&timer);
等同于前面,不过对base字段的赋值是调用了init_timer()函数。setup_timer()原型为:
static inline void setup_timer(struct timer_list * timer,void (*function)(unsigned long),unsigned long data) { timer->function = function; timer->data = data; init_timer(timer); } |
3.注册定时器
在定义并初始化了定时器之后,就要调用add_timer()函数来将该定时器注册到内核中,这样定时器才会工作。在注册之后,定时器就开始计时,在到达时间expires时,执行回调函数function(->data)。add_timer()函数的原型为:
static inline void add_timer(struct timer_list *timer) { BUG_ON(timer_pending(timer)); __mod_timer(timer, timer->expires); } |
4.删除定时器
int del_timer(struct timer_list *timer);
从内核中删除已经注册的定时器timer。如果该定时器是活动的,则返回1,否则返回0。
int del_timer(struct timer_list *timer) { tvec_base_t *base; unsigned long flags; int ret = 0; timer_stats_timer_clear_start_info(timer); if (timer_pending(timer)) { base = lock_timer_base(timer, &flags); if (timer_pending(timer)) { detach_timer(timer, 1); ret = 1; } spin_unlock_irqrestore(&base->lock, flags); } return ret; } |
5.修改定时器时间
如果所给的要修改的时间等于定时器原来的时间并且定时器现在正处于活动状态,则不修改,返回1,否则修改定时器时间,返回0。mod_timer()是一个非有效的更新处于活动状态的定时器的时间的方法,如果定时器处于非活动状态,则会激活定时器。在功能上,mod_timer()等价于:
del_timer(timer);
timer->expires=expires;
add_timer(timer);
int mod_timer(struct timer_list *timer, unsigned long expires) { BUG_ON(!timer->function); timer_stats_timer_set_start_info(timer); if (timer->expires == expires && timer_pending(timer)) return 1; return __mod_timer(timer, expires); } |
6.使用方法
1.创建定时器:struct timer_list my_timer;
2.初始化定时器:init_timer(&my_timer);
3.根据需要,设置定时器了:
my_timer.expires = jiffies + delay;
my_timer.data = 0;
my_timer.function = my_function;
4.激活定时器:add_timer(&my_timer);
7.实例解析
程序是在前面程序的基础上修改的,(博文地址:http://www.cnblogs.com/lihaiyan/p/4295961.html)
为了结构更简单,前面的程序使用了六个GPIO引脚,可能会相对复杂,为了更加坚定明了,此处简化为一个GPIO引脚了,思路和前面差不多,这里就是使用GPIO2_10这个引脚
① 定义定时器
②在打开函数中初始化定时器,并且申请GPIO2_10中断
③在中断中修改定时器,并且激活定时器,则没发生一次中断则激活一次定时器达到消除抖动的目的
此处的中断函数只有一个任务,就是激活定时器,然后将其他的功能均转移至定时器调用的函数中。
④可以发现,在定时器中,将前面中断中的功能全部实现了,一旦按键按下,接下来20ms内的抖动都无效,因为修改定时器无法修改已经激活的定时器,所以那20ms内高低电平的抖动都将无效,达到了消除抖动的功能
⑤编译测试。结果如下图所示:
附上驱动程序代码:
1 /******************************
2 linux key_query
3 *****************************/
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18 #include
19
20 #include "mx257_gpio.h"
21 #include "mx25_pins.h"
22 #include "iomux.h"
23
24 #define Driver_NAME "key_interrupt"
25 #define DEVICE_NAME "key_interrupt"
26
27 #define GPIO2_21 MX25_PIN_CLKO
28 #define GPIO3_15 MX25_PIN_EXT_ARMCLK
29 #define GPIO2_10 MX25_PIN_A24
30 #define GPIO2_11 MX25_PIN_A25
31 #define GPIO2_8 MX25_PIN_A22
32 #define GPIO2_9 MX25_PIN_A23
33 #define GPIO2_6 MX25_PIN_A20
34 #define GPIO2_7 MX25_PIN_A21
35 //command
36 #define key_input 0
37 #define version 1
38 //定义各个按键按下的键值
39 struct pin_desc{
40 unsigned int pin;
41 unsigned int key_val;
42 };
43 //当按键按下时,键值分别为 以下值
44 struct pin_desc pins_desc[8] = {
45 {GPIO2_6, 0x01},
46 {GPIO2_7, 0x02},
47 {GPIO2_8, 0x03},
48 {GPIO2_9, 0x04},
49 {GPIO2_10, 0x05},
50 {GPIO2_11, 0x06},
51 {GPIO2_21, 0x07},
52 {GPIO3_15, 0x08},
53 };
54 struct pin_desc * pindesc;
55 static int flag=0;
56 //定义一个全局变量,用于保存按下的键值
57 static unsigned int key_val;
58
59 struct timer_list my_timer;
60 unsigned int cnt_timer = 0;
61 #define TIMER_DELAY_20MS HZ/50
62
63 //interrupt head
64 static DECLARE_WAIT_QUEUE_HEAD(key_interrupt_wait);
65 static volatile unsigned char ev_press;
66
67 static int major=0;
68
69 //auto to create device node
70 static struct class *drv_class = NULL;
71 static struct class_device *drv_class_dev = NULL;
72
73 void timer_function(unsigned long data){
74 int index = data;
75 cnt_timer ++;
76 printk("<0>enter timer_%d__%d",index,cnt_timer);
77 if(flag){
78 //获取按键键值
79 gpio_direction_input(IOMUX_TO_GPIO(GPIO2_10));
80 key_val = gpio_get_value(IOMUX_TO_GPIO(pindesc->pin));
81 if(!key_val){
82 key_val = pindesc->key_val;
83 printk("<0>get key 0x%x",key_val);
84 printk("<0>KEY_DOWNn");
85 flag = 0;
86 ev_press = 1;
87 wake_up_interruptible(&key_interrupt_wait);
88 }
89 }
90 }
91 /* 中断程序key_irq */
92 static irqreturn_t key_irq(int irq, void *dev_id)
93 {
94 pindesc = (struct pin_desc *)dev_id;
95 //发生了中断
96 printk("<0>2_10function interrupt key_irq!nn");
97 mod_timer(&my_timer, jiffies+HZ/50);
98 flag = 1;
99
100 return IRQ_RETVAL(IRQ_HANDLED);
101 }
102
103
104 /* 应用程序对设备文件/dev/key_query执行open(...)时,
105 * 就会调用key_open函数*/
106 static int key_open(struct inode *inode, struct file *file)
107 {
108 printk("<0>function open 2_10! nn");
109
110 init_timer(&my_timer);
111 my_timer.function = timer_function;
112 my_timer.expires = jiffies+HZ/50;
113 add_timer(&my_timer); //定时器应该在此时被启动
114
115 request_irq(IOMUX_TO_IRQ(GPIO2_10), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_10", &pins_desc[4]);
116 return 0;
117 }
118
119
120
121 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
122 {
123 int ret;
124 if(filp->f_flags & O_NONBLOCK){
125 if(!ev_press)
126 return -EAGAIN;
127 }else{
128 //如果按键没有按下,没有中断,休眠
129 wait_event_interruptible(key_interrupt_wait,ev_press);
130 }
131 ret = copy_to_user(buff,&key_val,sizeof(key_val));
132 if(ret){
133 ;
134 }
135 ev_press = 0;
136 return sizeof(key_val);
137 }
138
139 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
140 {
141 printk("<0>function write!nn");
142