1 问题的引入—》软件消抖
在实际的应用中,如按键按下的的现象应该是:
松开(高电平)-》按下(低电平)-》松开(高电平)
但是由于实际按键的硬件原因导致的机械抖动,会导致产生多次中断,上报多次按键值。可是实际上,我们只要一次按键值。
为了避免这种情况的发生
1)、我们可以使用中断来处理,死等:
在按键中断程序中,可以循环判断几十亳秒,发现电平稳定之后再上报。但是这样违背了中断的原则之一:将尽可能的快。
2)、使用定时器
每次产生中断,在中断执行函数中,我们做的不是读按键值,而是修改定时器的超时时间:
eg: mod_timer(&timer, 10ms);
如果10ms内,再次发生了中断,就认为是抖动,再次修改定时器的超时时间。如果10ms内,无按键中断发生,就认为是已经稳定了。此时,才读取按键值。
2、定时器相关函数
a、void init_timer(struct timer_list *timer)
init_timer 函数负责初始化 timer_list 类型结构体(定时器的抽象)
b、void add_timer(struct timer_list *timer)
向 Linux 内核注册定时器,使用 add_timer 函数向内核注册定时器以后,
定时器就会开始运行
c、int del_timer(struct timer_list * timer)
删除一个定时器
d、int mod_timer(struct timer_list *timer, unsigned long expires)
注:expires基于jiffies来设置的。
mod_timer 函数用于修改定时值。
注:
它等同于:
del_timer(timer);
timer->expires = expires;
add_timer(timer);
更加高效。
示例:
struct timer_list timer; /* 定义定时器 */
/* 定时器回调函数 */
void function(unsigned long arg)
{
/* 定时器处理代码*/
/* 如果需要定时器周期性运行的话就使用 mod_timer
* 函数重新设置超时值并且启动定时器。 */
mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000));
}
/* 初始化函数 */
void init(void)
{
init_timer(&timer); /* 初始化定时器 */
timer.function = function; /* 设置定时处理函数 */
timer.expires=jffies + msecs_to_jiffies(2000);/* 超时时间 2 秒 */
timer.data = (unsigned long)&dev; /* 将设备结构体作为参数 */
add_timer(&timer); /* 启动定时器 */
}
/* 退出函数 */
void exit(void)
{
del_timer(&timer); /* 删除定时器 */
}
3、定时器的时间说明
重要的知识点:
1)、节拍率/系统滴答
就像人类的心跳一样,eg:节拍率=100;表示1s发生100次。可理解成,内核1s会产生100次滴答中断,系统心跳是1s100次。
2)、jiffies
这是一个全局变量,每产生一次中断,这个数值就会+1.
定时器的时间就是基于jiffies的。
注:
int mod_timer(struct timer_list *timer, unsigned long expires)
其中,expires基于jiffies来设置的。
我们修改超时时间时,一般使用这 2 种方法:
① 在 add_timer 之前,直接修改:
timer.expires = jiffies + xxx; // xxx 表示多少个滴答后超时,也就是 xxx*10ms
timer.expires = jiffies + 2*HZ; // HZ 等于 CONFIG_HZ, 2*HZ 就相当于 2 秒
② 在 add_timer 之后,使用 mod_timer 修改:
mod_timer(&timer, jiffies + xxx); // xxx 表示多少个滴答后超时,也就是 xxx*10ms
mod_timer(&timer, jiffies + 2*HZ); // HZ 等于 CONFIG_HZ, 2*HZ 就相当于 2 秒