1. 程式人生 > >5---linux定時器消除按鍵抖動

5---linux定時器消除按鍵抖動

概要:上一篇中我們用到了中斷來讀取按鍵電平,似乎很成功,然而當我們快速按下鬆開時,會有資料異常,是什麼導致呢?

按鍵抖動,在我們按下的時候按鍵可能會發生抖動,如圖

在這裡插入圖片描述
我們的中斷是雙邊沿觸發的,假如我們按鍵發生了抖動,那麼就觸發了五次中斷,這對我們來說實際應用上來看。是非常不穩定的,我們有兩種解決方法,一種是硬體消抖,雖然我懂一些硬體,但我不想拿自己的弱處來改進它。那麼另一種就是是軟體消抖,這一聽就在我的水平範疇內,我們可以使用定時器消抖

1.定時器

1.1 定義一個定時器結構體

static struct timer_list buttons_timer;

timer_list結構體如下:

struct timer_list {
	struct list_head entry;
	unsigned long expires; //超時時間,當jiffies大於它的時候會執行一次超時函式

	void (*function)(unsigned long); //定時器超時函式
	unsigned long data;

	struct tvec_t_base_s *base;
#ifdef CONFIG_TIMER_STATS
	void *start_site;
	char start_comm[16];
	int start_pid;
#endif
};

1.2 初始化定時器

init_timer(&buttons_timer);

->1.2.1) 定義超時函式

buttons_timer. function= buttons_timer_ function;

這個超時函式等一下我們需要編寫

1.3 向核心加入一個定時器

add_timer(&buttons_timer);  
到這一步我們定時器的初始化基本完成了,就差一個超時函式沒寫,寫之前我們來看一下按鍵的中斷處理函式:
static irqreturn_t  buttons_irq (int irq, void *dev_id)       //中斷服務函式
{ irq_dev_id =(struct pin_desc *)dev_id; mod_timer(&buttons_timer, jiffies+HZ/100); return IRQ_RETVAL(IRQ_HANDLED); }

mod_timer(&buttons_timer, jiffies+HZ/100);
當我們按鍵按下發生中斷的時候,會進入這個中斷函式,我們先不著急把電平值傳出去,因為可能按鍵還有抖動。我們在按鍵中斷服務函式裡面啟動定時器。怎麼啟用?
答:使用 mod_timer(&buttons_timer, jiffies+HZ/100); 這個函式會更改我們定時器的超時時間。jiffies是啥?可以理解為電腦啟動到目前為止的時間,比如說我開機到現在是10秒,那麼這個jiffies就是10秒。HZ是啥?一個HZ就是電腦的一秒。那麼這個函式就是把超時時間就是(當前時間 + 10ms)。

  假設我們按下按鍵,這時進入中斷服務函式,把定時器超時時間變成(當前時間(假設是10s) + 10ms),
  過了5ms,然後按鍵發生抖動了,我們又進入中斷處理函式,又把定時器超時時間變成(當前時間(10s+5ms) + 10ms)
實現超時函式
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);   /* 喚醒休眠的程序 */
}

在超時函式裡,很簡單,把key值傳遞出來,並喚醒read()。

這樣我們就實現了軟體消抖