天天看點

字元裝置驅動程式之定時器防抖動

當按鍵按得比較快的時候,這裡出現了兩次中斷值,也即産生了抖動。

這裡産生了“抖動”,按鍵是機械開關,按下松開時裡面的金屬彈片可能抖動了好幾次。這種抖動産生了多次“脈沖”導緻多次中斷。

字元裝置驅動程式之定時器防抖動
字元裝置驅動程式之定時器防抖動

方法:

使用定時器來防抖動。

定時器有兩個概念:

① 逾時時間:

② 時間到了之後的“處理函數”。

可以在中斷進行中,如定時 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");

    sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "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)

{

    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,并且修改定時器的值。定時器定時一段時間後,會進入定時器處理函數,完成本來應該中斷處理程式完成的工作。​