當按鍵按得比較快的時候,這裏出現了兩次中斷值,也即產生了抖動。
這裏產生了“抖動”,按鍵是機械開關,按下鬆開時裏面的金屬彈片可能抖動了好幾次。這種抖動產生了多次“脈衝”導致多次中斷。
方法:
使用定時器來防抖動。
定時器有兩個概念:
① 超時時間:
② 時間到了之後的“處理函數”。
可以在中斷處理中,如定時 10ms 後處理確定按鍵值上報。
之後再產生中斷:
在中斷中加定時器,當遇到 A 中斷時加一個 10ms 的定時器,過了 10ms 後就去執行“處理函數”(確定按鍵值上報)。因爲機械的抖動會非常快,沒等到 10ms 後的處理,這時因爲抖動又來了一箇中斷 B,這時中斷 B 把之前的那個定時器修改了。所以 A 中斷的定時器就取消了。最後又來了一箇中斷 C,同樣會修改掉 B 中斷的定時器。上圖中是假設抖動時產生了 3 箇中斷,所以對於同一個“定時器”,最終中斷 C 的定時器沒有被修改,所以 10ms 後由中斷 C 的處理函數上報了按鍵值。最後這個 10ms 是從抖動 C 處開始。這樣 3 個抖動的中斷只會導致最後處理一個“上報按鍵值”(定時器過後的處理函數只會執行一次)。以上便是用定時器消除抖動的原理。因爲是修改同一個定時器,所以前面的定時又取消,相當於把“鬧鐘”時間往後調整時,最終只要響一次鬧鈴。
部分代碼解析:
static struct timer_list buttons_timer;//定義一個定時器
mod_timer(&buttons_timer, jiffies+HZ/100);//修改定時器的值
init_timer(&buttons_timer);//初始化定時器
buttons_timer.function = buttons_timer_function;//給定時器添加處理函數
add_timer(&buttons_timer); //將定時器註冊進內核,此時定時器啓動了
操作步驟:
1.
static struct timer_list buttons_timer;//定義一個定時器
static int sixth_drv_init(void)
{
init_timer(&buttons_timer);//初始化定時器
buttons_timer.function = buttons_timer_function;//給定時器添加處理函數
//buttons_timer.expires = 0;
add_timer(&buttons_timer);//將定時器註冊進內核,此時定時器啓動了major = register_chrdev(0, "sixth_drv", &sencod_drv_fops);
sixthdrv_class = class_create(THIS_MODULE, "sixth_drv");
/* 爲了讓mdev根據這些信息來創建設備節點 */
sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon + 1;gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
gpgdat = gpgcon + 1;return 0;
}
2. 按鍵按下觸發中斷,
static struct pin_desc *irq_pd;
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/* 10ms後啓動定時器 */
irq_pd = (struct pin_desc *)dev_id; //給定時器處理函數使用
mod_timer(&buttons_timer, jiffies+HZ/100); //修改定時器的值,10ms後定時器產生中斷
return IRQ_RETVAL(IRQ_HANDLED);
}
3.
static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval;if (!pindesc)
return;
pinval = s3c2410_gpio_getpin(pindesc->pin);if (pinval)
{
/* 鬆開 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
}ev_press = 1; /* 表示中斷髮生了 */
wake_up_interruptible(&button_waitq); /* 喚醒休眠的進程 */
kill_fasync (&button_async, SIGIO, POLL_IN);
}
程序流程
當註冊了驅動程序後,其實定時器已經啓動了。這時我們讀取的話,因爲沒有按鍵按下返回值爲負,所以應用程序裏休眠5秒鐘,什麼也沒有做。這時定時器也啓動了,那它在幹嘛呢?因爲此時沒有修改定時器的值,它還是初始值0,所以它一直會進入定時器處理函數,但是由於按鍵沒有按下,無法進行後續的處理,也會返回,不會做什麼事情!如果按下按鍵的話,會進入中斷處理函數。在中斷處理函數中,會將ev_press置爲1,並且修改定時器的值。定時器定時一段時間後,會進入定時器處理函數,完成本來應該中斷處理程序完成的工作。