韦东山嵌入式Linux学习笔记之——第12课第8节 字符设备驱动程序之定时器防抖动

注:本文部分内容摘自《鱼树学员笔记》。

当按键按得比较快的时候,这里出现了两次中断值,也即产生了抖动。

这里产生了“抖动”,按键是机械开关,按下松开时里面的金属弹片可能抖动了好几次。这种抖动产生了多次“脉冲”导致多次中断。

方法:

使用定时器来防抖动。

定时器有两个概念:

① 超时时间:

② 时间到了之后的“处理函数”。

可以在中断处理中,如定时 10ms 后处理确定按键值上报。

之后再产生中断:

在中断中加定时器,当遇到 A 中断时加一个 10ms 的定时器,过了 10ms 后就去执行“处理函数”(确定按键值上报)。因为机械的抖动会非常快,没等到 10ms 后的处理,这时因为抖动又来了一个中断 B,这时中断 B 把之前的那个定时器修改了。所以 A 中断的定时器就取消了。最后又来了一个中断 C,同样会修改掉 B 中断的定时器。上图中是假设抖动时产生了 3 个中断,所以对于同一个“定时器”,最终中断 C 的定时器没有被修改,所以 10ms 后由中断 C 的处理函数上报了按键值。最后这个 10ms 是从抖动 C 处开始。这样 3 个抖动的中断只会导致最后处理一个“上报按键值”(定时器过后的处理函数只会执行一次)。以上便是用定时器消除抖动的原理。因为是修改同一个定时器,所以前面的定时又取消,相当于把“闹钟”时间往后调整时,最终只要响一次闹铃。

1. 在内核代码中查找add_timer 的用法。

① 首先定义一个timer_list 类型的静态结构体变量buttons_timer:

② 然后初始化该结构体,并定义按键定时器函数buttons_timer_function

注意:这里的buttons_timer_function定时器中断处理函数需要我们自己去定义。函数的原型为:

这里的buttons_timer_function函数返回值为void,参数是初始化结构体时传入的data参数。

这样定时器就算定义完毕了,其中定时器的两个必要条件超时时间和处理函数,其中处理函数已经定义好了,超时时间在后面的中断服务程序中加入

要启用定时器,还必须通过add_timer()函数将定义好的定时器加入到内核中,主要任务是告诉内核,当定时器中设置好的超时时间到了之后,内核就会自动调用buttons_timer_function函数。

2. 修改中断处理函数

将之前中断处理函数中的按键处理内容全部转移到按键定时器函数 buttons_timer_function 中来完成。

② 在之前的中断服务程序中只需要修改定时器的超时时间即可。

注意:这里的超时时间就是我们在前面所提到的最终闹钟到来的时间。

mod_timer的函数原型为:

mod_timer函数中的jiffies参数:

定时器的定义和触发时间: 定时器的超时时间就是基于jiffies来设置的。 

jiffies 是一个全局变量,系统每隔10ms,该值就会改变并产生一个系统时钟中断,在中断中该值就会累加(jiffies+1)。

这里定义的HZ的值为100,含义就是1秒的时间计数100次,那么计数一次的时间就是10ms,因此jiffies这个参数这里就加了HZ/100(相当于jiffies+1),刚好是10ms。如果没有按键按下,系统不会触发中断也就不会往后推迟时间,那么一旦在这期间过了10ms,系统就会去调用那个按键定时器函数buttons_timer_function

接下来再定义一个发生中断时候的引脚描述结构体

(该结构体指针是全局变量)

用来记录发生中断时候的设备号

另外还有一个问题需要去考虑,就是在入口函数中没有设置定时器的超时时间,也就是说开始的超时时间就是0,那么通过add_timer函数将定时器放进内核以后,一旦进入系统时钟的中断,jiffies这个参数的值就会大于0,就会随即调用按键定时器函数buttons_timer_function,但是这时候并没有按键中断产生。

因此要在按键定时器处理函数中加入判断:如果是空,则直接返回不做处理。

至此,按键定时器防抖动的程序就算是写完了。

跟以前的函数做一个简单的对比:

before:只要有按键按下就会进入中断处理函数buttons_irq,在该函数中获取引脚值和按键值,之后唤醒休眠的进程,最后发送信号。

now:为了防抖动,现在只要有按键按下进入中断处理函数buttons_irq中,首先获取设备号,然后修改定时器的超时时间为10ms以后,之后便直接返回而不采取任何操作。

根据这个原理图我们可以很容易理解,如果按键有抖动,不断进入中断处理函数,那么在中断中就会不断将系统时钟的超时时间向后推迟10ms,一旦抖动不再发生(注意:一般情况下抖动都小于10ms),过了系统定时器超时时间(10ms),那么就会进入按键定时器处理函数中,这时候才会跟以前一样获取引脚值和按键值,之后唤醒休眠的进程,最后发送信号。

猜你喜欢

转载自blog.csdn.net/u011663005/article/details/81061499
今日推荐