【按鍵】[獨立按鍵] - 1: 單擊,雙擊,三擊以及N擊
此按鍵程式的實現的功能是單個獨立按鍵的[單擊],[長按],[雙擊],[三擊]以及[多擊]。本文分為三個部分,
- 第一個部分是說[單擊],[長按]的程式;
- 第二部分是講[雙擊];
- 第三部分是講[三擊],[N擊];
一、 [單擊]、[長按]程式
1. 簡單介紹本按鍵程式的單擊和長按
首先說一下單擊,長按的響應情況,就是按多久算單擊或者長按,按下按鍵馬上返回有效鍵值,還是釋放之後返回有效鍵值等等,下面說下它在什麼情況下返回有效的【單擊】和【長按】。
首先看一張時序圖:
注:
T1:是單擊的按鍵消抖時長,這裡預設的是30ms,也可以根據需求自行定義;
T2:是單擊時,按鍵釋放的有效時間段,提前或者超過這個時間段釋放的按鍵都再是單擊了。提前釋放則是無效鍵值,超過後釋放則是長按。
T3:是長按時長,按鍵超過此時長,則為長按。這裡的預設值是3s,同樣可根據需求自行更改。
【單擊】:按鍵按下超過消抖時長T1(30ms),並且在T2時間段內釋放按鍵,按鍵一釋放,馬上返回有效按鍵值—【單擊】。
注意:單擊是釋放後,才返回有效按鍵值,不釋放時,是無效按鍵值。【長按】:按鍵按下的時間超過預設的長按時長T3(3s) ,馬上返回有效按鍵值—【長按】;
注意:長按是隻要按下的時間超過預設的長按時長,馬上返回有效鍵值。但是,如果按鍵一直按著不釋放,則只返回一次有效按鍵值,不重複返回,直到釋放之後,才開始重新讀取鍵值。
2. 按鍵程式的架構
按鍵程式可以分為四個部分,
- 第一部分:判斷有無按鍵按下;
- 第二部分:按鍵是否有效(按鍵消抖)
- 第三部分:確定有效按鍵的種類(單擊還是長按);
- 第四部分:等待按鍵釋放。
3. 按鍵程式的原始碼以及註釋
程式的註釋寫的很詳細,應該是可以讀懂的,如果有疑問可以留言討論。
以下是key.c 的原始碼:
//============================ key.c ===================
#define KEY_INPUT P1.0 // 按鍵IO
#define KEY_STATE_0 0 // 按鍵狀態
#define KEY_STATE_1 1
#define KEY_STATE_2 2
#define KEY_STATE_3 3
#define LONG_KEY_TIME 300 // LONG_KEY_TIME*10MS = 3S
#define SINGLE_KEY_TIME 3 // SINGLE_KEY_TIME*10MS = 30MS
#define N_KEY 0 // no click
#define S_KEY 1 // single click
#define L_KEY 10 // long press
unsigned char key_driver(void)
{
static unsigned char key_state = 0; // 按鍵狀態變數
static unsigned int key_time = 0; // 按鍵計時變數
unsigned char key_press, key_return;
key_return = N_KEY; // 清除 返回按鍵值
key_press = KEY_INPUT; // 讀取當前鍵值
switch (key_state)
{
case KEY_STATE_0: // 按鍵狀態0:判斷有無按鍵按下
if (!key_press) // 有按鍵按下
{
key_time = 0; // 清零時間間隔計數
key_state = KEY_STATE_1; // 然後進入 按鍵狀態1
}
break;
case KEY_STATE_1: // 按鍵狀態1:軟體消抖(確定按鍵是否有效,而不是誤觸)。按鍵有效的定義:按鍵持續按下超過設定的消抖時間。
if (!key_press)
{
key_time++; // 一次10ms
if(key_time>=SINGLE_KEY_TIME) // 消抖時間為:SINGLE_KEY_TIME*10ms = 30ms;
{
key_state = KEY_STATE_2; // 如果按鍵時間超過 消抖時間,即判定為按下的按鍵有效。按鍵有效包括兩種:單擊或者長按,進入 按鍵狀態2, 繼續判定到底是那種有效按鍵
}
}
else key_state = KEY_STATE_0; // 如果按鍵時間沒有超過,判定為誤觸,按鍵無效,返回 按鍵狀態0,繼續等待按鍵
break;
case KEY_STATE_2: // 按鍵狀態2:判定按鍵有效的種類:是單擊,還是長按
if(key_press) // 如果按鍵在 設定的長按時間 內釋放,則判定為單擊
{
key_return = S_KEY; // 返回 有效按鍵值:單擊
key_state = KEY_STATE_0; // 返回 按鍵狀態0,繼續等待按鍵
}
else
{
key_time++;
if(key_time >= LONG_KEY_TIME) // 如果按鍵時間超過 設定的長按時間(LONG_KEY_TIME*10ms=200*10ms=2000ms), 則判定為 長按
{
key_return = L_KEY; // 返回 有效鍵值值:長按
key_state = KEY_STATE_3; // 去狀態3,等待按鍵釋放
}
}
break;
case KEY_STATE_3: // 等待按鍵釋放
if (key_press)
{
key_state = KEY_STATE_0; // 按鍵釋放後,進入 按鍵狀態0 ,進行下一次按鍵的判定
}
break;
default: // 特殊情況:key_state是其他值得情況,清零key_state。這種情況一般出現在 沒有初始化key_state,第一次執行這個函式的時候
key_state = KEY_STATE_0;
break;
}
return key_return; // 返回 按鍵值
}
使用注意:
1)硬體:按鍵的一端接地(GND),另一端接IO口。IO為輸入,一定要有上拉電阻。
2)定時器:這裡為了精確的定時,所以使用了定時器,定時器的時間是10ms。
3)掃描週期:呼叫此函式時,一定確保”掃描週期“要小於10ms。不然按鍵內所涉及的時間就會不準,會偏大。所涉及的時間包括消抖時長,按鍵長按時長等。
掃描週期定義:從 第一次進入按鍵掃描程式 開始,到第二次進入按鍵掃描程式時 結束,之間所用的時間。
測量掃描週期的方法:可以在按鍵掃描程式的第一句,新增IO口取反函式,然後用示波器檢視改IO口,其IO口週期的一般就是掃描週期了。
4. 按鍵程式的使用例項
這裡以C51位硬體平臺進行例項講解
1)例項程式的功能:
- 單擊:點亮LED1
- 長按:熄滅LED1
2)硬體:
- 按鍵IO:P1.0
- LED1 :P2.0
以下是 main.c 原始碼:
//============================ main.c ===================
#include "reg51.h"
#include "key.c"
sbit LED1 = P2.0; // 定義LEDIO口
unsigned char g_u8_KeyValue; // 按鍵值
unsigned char g_flag_10ms_key; // 10ms 計時標誌
//timer0,初始化函式 ,定時時間為 10ms
void T0_Init_10ms(void)
{
TMOD |= 0x01;
TH0 = (65535 - 10000)/256;
TL0 = (65535 - 10000)%256;
ET0 = 1;
TR0 = 1;
EA = 1;
}
//主函式
void main(void)
{
P1.0 = 1; // P1.0 拉高
T0_Init_10ms(); // 定時器0,初始化,定時10ms
while(1)
{
if(g_flag_10ms_key) // 等待10ms,定時完成
{
g_flag_10ms_key = 0; // 清零10ms定時標誌
g_u8_KeyValue = key_driver(); // 讀取按鍵值
switch(g_u8_KeyValue)
{
case S_KEY: LED1 = 1; break; // 單擊 點亮LED1
case L_KEY: LED1 = 0; break; // 長按 熄滅LED1
}
}
}
}
//timer0 中斷服務程式
void IRQ_T0(void) interrupt 1
{
g_flag_10ms_key = 1; // 置位 10ms 定時標誌
}
Pillar Peng
2016.3.23 18:00
其餘分享連結: