1. 程式人生 > >【按鍵】[獨立按鍵] - 1: 單擊,雙擊,三擊以及N擊

【按鍵】[獨立按鍵] - 1: 單擊,雙擊,三擊以及N擊

此按鍵程式的實現的功能是單個獨立按鍵的[單擊],[長按],[雙擊],[三擊]以及[多擊]。本文分為三個部分,

  • 第一個部分是說[單擊],[長按]的程式;
  • 第二部分是講[雙擊]
  • 第三部分是講[三擊],[N擊]

一、 [單擊]、[長按]程式

1. 簡單介紹本按鍵程式的單擊和長按

首先說一下單擊,長按的響應情況,就是按多久算單擊或者長按,按下按鍵馬上返回有效鍵值,還是釋放之後返回有效鍵值等等,下面說下它在什麼情況下返回有效的【單擊】和【長按】。

首先看一張時序圖:
這裡寫圖片描述

注:
T1:是單擊的按鍵消抖時長,這裡預設的是30ms,也可以根據需求自行定義;
T2:是單擊時,按鍵釋放的有效時間段,提前或者超過這個時間段釋放的按鍵都再是單擊了。提前釋放則是無效鍵值,超過後釋放則是長按。
T3:是長按時長,按鍵超過此時長,則為長按。這裡的預設值是3s,同樣可根據需求自行更改。

  • 【單擊】:按鍵按下超過消抖時長T1(30ms),並且在T2時間段內釋放按鍵,按鍵一釋放,馬上返回有效按鍵值—【單擊】。
    注意:單擊是釋放後,才返回有效按鍵值,不釋放時,是無效按鍵值。

  • 【長按】:按鍵按下的時間超過預設的長按時長T3(3s) ,馬上返回有效按鍵值—【長按】;
    注意:長按是隻要按下的時間超過預設的長按時長,馬上返回有效鍵值。但是,如果按鍵一直按著不釋放,則只返回一次有效按鍵值,不重複返回,直到釋放之後,才開始重新讀取鍵值。

2. 按鍵程式的架構

按鍵程式可以分為四個部分,

  1. 第一部分:判斷有無按鍵按下
  2. 第二部分:按鍵是否有效(按鍵消抖)
  3. 第三部分:確定有效按鍵的種類(單擊還是長按)
  4. 第四部分:等待按鍵釋放

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


其餘分享連結:

第二部分:【按鍵】[獨立按鍵] - 2: 雙擊

第三部分:【按鍵】[獨立按鍵] - 3: 三擊以及N擊