單片機---多功能按鍵處理


最近自己在琢磨按鍵程序,之前弄了,按鍵單擊程序,程序可以實現讀取按鍵的幾個狀態,然后根據需求使用。單擊按鍵的幾個狀態如下:

  • 單擊后,馬上返回按鍵值
  • 單擊后,釋放之后,返回按鍵值
  • 單擊后,按住不放,多次返回按鍵值

但是在其上面擴展雙擊和三擊以及多擊程序時,遇到了麻煩(其實就是不會啦),於是在網上看了某個老師的經典按鍵程序,里面講了單擊和雙擊,拿着源程序理解了半天,終於一知半解了(一知半解的狀態就是能讀懂思想,但是閉卷完全自己寫,寫不出)。但是個人覺得其中的消抖部分還有按鍵釋放的部分還可以改善,還有根據其思路擴展了三擊以及N擊,於是寫了這篇博客供自己以后遺忘時參考以及跟大家分享。


正文:

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

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

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

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

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

首先看一張時序圖:

注:

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

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

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

2. 按鍵程序的架構

按鍵程序可以分為四個部分,第一部分:判斷有無按鍵按下;第二部分:按鍵是否有效(按鍵消抖);第三部分:確定有效按鍵的種類(單擊還是長按);第四部分:等待按鍵釋放。

3. 按鍵程序的源代碼以及注釋

程序的注釋寫的很詳細,應該是可以讀懂的,如果有疑問可以留言討論。
以下是key.c 的源代碼:

 
 
 
         
  1. #define KEY_INPUT P1.0 //按鍵IO
  2. #define KEY_STATE_0 0 //按鍵狀態
  3. #define KEY_STATE_1 1
  4. #define KEY_STATE_2 2
  5. #define KEY_STATE_3 3
  6. #define LONG_KEY_TIME 300 //LONG_KEY_TIME*10MS = 3S
  7. #define SINGLE_KEY_TIME 3 //SINGLE_KEY_TIME*10MS = 30MS
  8. #define KEY_NONE 0 //no click
  9. #define KEY_SHORT 1 //single click
  10. #define KEY_LONG 10 //long press
  11. //10MS
  12. void key_driver(void)
  13. {
  14. static char key_state; //按鍵狀態變量
  15. static int key_time; //按鍵計時變量
  16. char key_press, key_return;
  17. key_return = KEY_NONE; // 清除 返回按鍵值
  18. key_press = KEY_INPUT; // 讀取當前鍵值
  19. switch (key_state)
  20. {
  21. case KEY_STATE_0: // 按鍵狀態0:判斷有無按鍵按下
  22. if (!key_press) // 有按鍵按下
  23. {
  24. key_time = 0; // 清零時間間隔計數
  25. key_state = KEY_STATE_1; // 然后進入 按鍵狀態1
  26. }
  27. break;
  28. case KEY_STATE_1: // 按鍵狀態1:軟件消抖(確定按鍵是否有效,而不是誤觸)。按鍵有效的定義:按鍵持續按下超過設定的消抖時間。
  29. if (!key_press)
  30. {
  31. key_time++; // 一次10ms
  32. if (key_time >= SINGLE_KEY_TIME) // 消抖時間為:SINGLE_KEY_TIME*10ms = 30ms;
  33. {
  34. key_state = KEY_STATE_2; // 如果按鍵時間超過 消抖時間,即判定為按下的按鍵有效。按鍵有效包括兩種:單擊或者長按,進入 按鍵狀態2, 繼續判定到底是那種有效按鍵
  35. }
  36. }
  37. else
  38. key_state = KEY_STATE_0; // 如果按鍵時間沒有超過,判定為誤觸,按鍵無效,返回 按鍵狀態0,繼續等待按鍵
  39. break;
  40. case KEY_STATE_2: // 按鍵狀態2:判定按鍵有效的種類:是單擊,還是長按
  41. if (key_press) // 如果按鍵在 設定的長按時間 內釋放,則判定為單擊
  42. {
  43. key_return = KEY_SHORT; // 返回 有效按鍵值:單擊
  44. key_state = KEY_STATE_0; // 返回 按鍵狀態0,繼續等待按鍵
  45. }
  46. else
  47. {
  48. key_time++;
  49. if (key_time >= LONG_KEY_TIME) // 如果按鍵時間超過 設定的長按時間(LONG_KEY_TIME*10ms=200*10ms=2000ms), 則判定為 長按
  50. {
  51. key_return = KEY_LONG; // 返回 有效鍵值值:長按
  52. key_state = KEY_STATE_3; // 去狀態3,等待按鍵釋放
  53. }
  54. }
  55. break;
  56. case KEY_STATE_3: // 等待按鍵釋放
  57. if (key_press)
  58. {
  59. key_state = KEY_STATE_0; // 按鍵釋放后,進入 按鍵狀態0 ,進行下一次按鍵的判定
  60. }
  61. break;
  62. default: // 特殊情況:key_state是其他值得情況,清零key_state。這種情況一般出現在 沒有初始化key_state,第一次執行這個函數的時候
  63. key_state = KEY_STATE_0;
  64. break;
  65. }
  66. return key_return; // 返回 按鍵值
  67. }

使用注意:
1)硬件:按鍵的一端接地(GND),另一端接IO口。IO為輸入,一定要有上拉電阻。
2)定時器:這里為了精確的定時,所以使用了定時器,定時器的時間是10ms。
3)掃描周期:調用此函數時,一定確保”掃描周期“要小於10ms。不然按鍵內所涉及的時間就會不准,會偏大。所涉及的時間包括消抖時長,按鍵長按時長等。

4. 按鍵程序的使用實例

這里以C51位硬件平台進行實例講解
1)實例程序的功能:
單擊:點亮LED1
長按:熄滅LED1
2)硬件:
按鍵IO:P1.0
LED1 :P2.0
以下是 main.c 源代碼:

 
 
 
         
  1. #include "reg51.h"
  2. #include "key.c"
  3. sbit LED1 = P2.0; //定義LEDIO口
  4. unsigned char g_u8_KeyValue; //按鍵值
  5. unsigned char g_flag_10ms_key; //10ms 計時標志
  6. //timer0,初始化函數 ,定時時間為 10ms
  7. void T0_Init_10ms(void)
  8. {
  9. TMOD |= 0x01;
  10. TH0 = (65535 - 10000)/256;
  11. TL0 = (65535 - 10000)%256;
  12. ET0 = 1;
  13. TR0 = 1;
  14. EA = 1;
  15. }
  16. //主函數
  17. void main(void)
  18. {
  19. P1.0 = 1; //P1.0 拉高
  20. T0_Init_10ms(); //定時器0,初始化,定時10ms
  21. while(1)
  22. {
  23. if(g_flag_10ms_key) //等待10ms,定時完成
  24. {
  25. g_flag_10ms_key = 0; //清零10ms定時標志
  26. g_u8_KeyValue = key_driver(); //讀取按鍵值
  27. switch(g_u8_KeyValue)
  28. {
  29. case S_Key: LED1 = 1; break; //單擊 點亮LED1
  30. case L_Key: LED1 = 1; break; //長按 熄滅LED1
  31. }
  32. }
  33. }
  34. }
  35. //timer0 中斷服務程序
  36. void IRQ_T0(void) interrupt 1
  37. {
  38. g_flag_10ms_key = 1; //置位 10ms 定時標志
  39. }

二、 [雙擊]程序

1.雙擊介紹

雙擊的判定
在完成第一次【單擊】之后,在一定的時間間隔內(本程序使用的是300ms),接着完成第二次【單擊】,及判定為【雙擊】。
雙擊的響應
1)在預設的時間間隔內完成第二次【單擊】,按鍵釋放后,響應返回有效鍵值【雙擊】。
2)如果第二次按下鍵並一直按住,當按住的時間超過設定的時間間隔(300ms)后,會響應第一個【單擊】,並返回有效鍵值【單擊】。
注:【單擊】是包括按下和釋放按鍵的過程,判定方式沿用上文所說的,如果忘了,可以參考上文。
雙擊響應時序圖

注:
T1:是單擊判定的時間,范圍:30ms < T1 < 3000ms
T2:是判定雙擊的時間間隔,是個定值 300ms。在完成第一次【單擊】后(釋放按鍵后開始計時),在這個時間間隔內,如果再一次完成【單擊】(釋放按鍵后結束計時),則判定為【雙擊】。
T3:與T1相同。

2.按鍵程結構

按鍵程序可以分為四個部分,第一部分:判斷有無單擊按鍵;第二部分:判斷雙擊即是在預設的時間間隔內,有無第二次【單擊】;第三部分:等待按鍵釋放。

3.雙擊程序的源代碼以及注釋

程序的注釋寫的很詳細,應該是可以讀懂的,如果有疑問可以留言討論。
上文的【單擊】函數作為這次【雙擊】程序的子函數
以下是key.c 的源代碼:

 
 
 
         
  1. #define KEY_INPUT P1.0 // 按鍵IO
  2. #define KEY_STATE_0 0 // 按鍵狀態
  3. #define KEY_STATE_1 1
  4. #define KEY_STATE_2 2
  5. #define KEY_STATE_3 3
  6. #define SINGLE_KEY_TIME 3 // SINGLE_KEY_TIME*10MS = 30MS 判定單擊的時間長度,軟件消抖
  7. #define KEY_INTERVAL 30 // KEY_INTERVAL*10MS = 300MS 判定雙擊的時間間隔
  8. #define LONG_KEY_TIME 300 // LONG_KEY_TIME*10MS = 3S 判定長按的時間長度
  9. #define KEY_NONE 0 // no click
  10. #define KEY_SHORT 1 // single click
  11. #define KEY_DOUBLE 2 // double click
  12. #define KEY_LONG 10 // long press
  13. // ----------------------------------- key_driver --------------------------
  14. unsigned char key_driver(void)
  15. {
  16. static unsigned char key_state = 0;
  17. static unsigned int key_time = 0;
  18. unsigned char key_press, key_return;
  19. key_return = KEY_NONE; // 清除 返回按鍵值
  20. key_press = KEY_INPUT; // 讀取當前鍵值
  21. switch (key_state)
  22. {
  23. case KEY_STATE_0: // 按鍵狀態0:判斷有無按鍵按下
  24. if (!key_press) // 有按鍵按下
  25. {
  26. key_time = 0; // 清零時間間隔計數
  27. key_state = KEY_STATE_1; // 然后進入 按鍵狀態1
  28. }
  29. break;
  30. case KEY_STATE_1: // 按鍵狀態1:軟件消抖(確定按鍵是否有效,而不是誤觸)。按鍵有效的定義:按鍵持續按下超過設定的消抖時間。
  31. if (!key_press)
  32. {
  33. key_time++; // 一次10ms
  34. if (key_time >= SINGLE_KEY_TIME) // 消抖時間為:SINGLE_KEY_TIME*10ms = 30ms;
  35. {
  36. key_state = KEY_STATE_2; // 如果按鍵時間超過 消抖時間,即判定為按下的按鍵有效。按鍵有效包括兩種:單擊或者長按,進入 按鍵狀態2, 繼續判定到底是那種有效按鍵
  37. }
  38. }
  39. else
  40. key_state = KEY_STATE_0; // 如果按鍵時間沒有超過,判定為誤觸,按鍵無效,返回 按鍵狀態0,繼續等待按鍵
  41. break;
  42. case KEY_STATE_2: // 按鍵狀態2:判定按鍵有效的種類:是單擊,還是長按
  43. if (key_press) // 如果按鍵在 設定的長按時間 內釋放,則判定為單擊
  44. {
  45. key_return = KEY_SHORT; // 返回 有效按鍵值:單擊
  46. key_state = KEY_STATE_0; // 返回 按鍵狀態0,繼續等待按鍵
  47. }
  48. else
  49. {
  50. key_time++;
  51. if (key_time >= LONG_KEY_TIME) // 如果按鍵時間超過 設定的長按時間(LONG_KEY_TIME*10ms=300*10ms=3000ms), 則判定為 長按
  52. {
  53. key_return = KEY_LONG; // 返回 有效鍵值值:長按
  54. key_state = KEY_STATE_3; // 去狀態3,等待按鍵釋放
  55. }
  56. }
  57. break;
  58. case KEY_STATE_3: // 等待按鍵釋放
  59. if (key_press)
  60. {
  61. key_state = KEY_STATE_0; // 按鍵釋放后,進入 按鍵狀態0 ,進行下一次按鍵的判定
  62. }
  63. break;
  64. default: // 特殊情況:key_state是其他值得情況,清零key_state。這種情況一般出現在 沒有初始化key_state,第一次執行這個函數的時候
  65. key_state = KEY_STATE_0;
  66. break;
  67. }
  68. return key_return; // 返回 按鍵值
  69. }
  70. // ----------------------------------- key_read --------------------------------
  71. void key_read(void)
  72. {
  73. static unsigned char key_state1 = 0, key_time1 = 0;
  74. unsigned char key_return, key_temp;
  75. key_return = KEY_NONE; // 清零 返回按鍵值
  76. key_temp = key_driver(); // 讀取鍵值
  77. switch (key_state1)
  78. {
  79. case KEY_STATE_0: // 按鍵狀態0:等待有效按鍵(通過 key_driver 返回的有效按鍵值)
  80. if (key_temp == KEY_SHORT) // 如果是[單擊],不馬上返回單擊按鍵值,先進入 按鍵狀態1,判斷是否有[雙擊]的可能
  81. {
  82. key_time1 = 0; // 清零計時
  83. key_state1 = KEY_STATE_1;
  84. }
  85. else // 如果不是[單擊],直接返回按鍵值。這里的按鍵值可能是:[長按],[無效按鍵]
  86. {
  87. key_return = key_temp; // 返回 按鍵值
  88. }
  89. break;
  90. case KEY_STATE_1: // 按鍵狀態1:判定是否有[雙擊]
  91. if (key_temp == KEY_SHORT) // 有[單擊]后,如果在 設定的時間間隔(KEY_INTERVAL*10ms=30*10ms=300ms) 內,再次有[單擊],則為[雙擊],但是不馬上返回 有效按鍵值為[雙擊],先進入 按鍵狀態2,判斷是否有[三擊]
  92. {
  93. key_return = KEY_DOUBLE; // 返回 有效按鍵:[雙擊]
  94. key_state1 = KEY_STATE_0; // 返回 按鍵狀態0,等待新的有效按鍵
  95. }
  96. else // 有[單擊]后,如果在 設定的時間間隔(KEY_INTERVAL*10ms=30*10ms=300ms)內,沒有[單擊]出現,則判定為 [單擊]
  97. {
  98. key_time1++; // 計數 時間間隔
  99. if (key_time1 >= KEY_INTERVAL) // 超過 時間間隔
  100. {
  101. key_return = KEY_SHORT; // 返回 有效按鍵:[單擊]
  102. key_state1 = KEY_STATE_0; // 返回 按鍵狀態0,等待新的有效按鍵
  103. }
  104. }
  105. break;
  106. default: // 特殊情況:key_state是其他值得情況,清零key_state。這種情況一般出現在 沒有初始化key_state,第一次執行這個函數的時候
  107. key_state1 = KEY_STATE_0;
  108. break;
  109. }
  110. return key_return; // 返回 按鍵值
  111. }

使用注意:

1)硬件:按鍵的一端接地(GND),另一端接IO口。IO為輸入,一定要有上拉電阻。
2)定時器:這里為了精確的定時,所以使用了定時器,定時器的時間是10ms。
3)掃描周期:調用此函數時,一定確保”掃描周期“要小於10ms。不然按鍵內所涉及的時間就會不准,會偏大。所涉及的時間包括消抖時長,按鍵長按時長等。
掃描周期定義:從 第一次進入按鍵掃描程序 開始,到第二次進入按鍵掃描程序時 結束,之間所用的時間。
測量掃描周期的方法:可以在按鍵掃描程序的第一句,添加IO口取反函數,然后用示波器查看改IO口,其IO口周期的一般就是掃描周期了。
4)特別注意以上程序的3個關於時間的宏,相當的重要。如果想更改按鍵的單擊的靈敏度,雙擊的速度,或者長按的時間,只需要修改這些宏的值即可。
比如:針對於老人的使用的按鍵,就需要將雙擊的速度調節的慢一點,就可以將KEY_INTERVAL的值增大。
SINGLE_KEY_TIME:單擊的靈敏度,值越小,越靈敏。
KEY_INTERVAL :雙擊的點擊速度,修改這個值,值越小,速度越快。同時這個值也決定了單擊的響應時間,因為單擊之后,還需要判斷在這個時間間隔內沒有第二次單擊,如果沒有才是真正的單擊,所以單擊響應的時間為:SINGLE_KEY_TIME+KEY_INTERVAL;
LONG_KEY_TIME :長按的時間,修改這個即可,值越大,時間越長。

4.按鍵程序的使用實例

這里以C51位硬件平台進行實例講解
1)實例程序的功能
單擊:點亮LED1
雙擊:熄滅LED1
長按:熄滅LED1
2)硬件:
按鍵IO:P1.0
LED1 :P2.0
以下是 main.c 源代碼:

 
 
 
         
  1. // =========================== key.c ======================
  2. #include "reg51.h"
  3. #include "key.c"
  4. sbit LED1 = P2.0; // 定義LEDIO口
  5. unsigned char g_u8_KeyValue; // 按鍵值
  6. unsigned char g_flag_10ms_key; // 10ms 計時標志
  7. void key_read(); // 聲明讀取按鍵函數
  8. void T0_Init_10ms(void) // timer0,初始化函數 ,定時時間為 10ms
  9. {
  10. TMOD |= 0x01;
  11. TH0 = (65535 - 10000)/256;
  12. TL0 = (65535 - 10000)%256;
  13. ET0 = 1;
  14. TR0 = 1;
  15. EA = 1;
  16. }
  17. // 主函數
  18. void main(void)
  19. {
  20. P1.0 = 1; // P1.0 拉高
  21. T0_Init_10ms(); // 定時器0,初始化,定時10ms
  22. while(1)
  23. {
  24. if(g_flag_10ms_key) // 等待10ms,定時完成
  25. {
  26. g_flag_10ms_key = 0; // 清零10ms定時標志
  27. g_u8_KeyValue = key_read(); // 讀取按鍵值
  28. switch(g_u8_KeyValue)
  29. {
  30. case S_Key: LED1 = 1; break; // 單擊 點亮LED1
  31. case D_Key: LED1 = 0; break; // 單擊 熄滅LED1
  32. case L_Key: LED1 = 0; break; // 長按 熄滅LED1
  33. }
  34. }
  35. }
  36. }
  37. // timer0 中斷服務程序
  38. void IRQ_T0(void) interrupt 1
  39. {
  40. g_flag_10ms_key = 1; // 置位 10ms 定時標志
  41. }

三、 [三擊]程序

1.三擊介紹

三擊判定:在完成第一次【單擊】之后,在一定的時間間隔內(本程序使用的是300ms),接着完成第二次【單擊】,時間間隔重新計時,然后又在這個時間間隔內,完成第三次【單擊】,及判定為【三擊】。
注:【單擊】是包括按下和釋放按鍵的過程,判定方式沿用上文所說的,如果忘了,可以參考上文。
三擊響應時序圖

注:
T1:是單擊判定的時間,范圍:30ms < T1 < 3000ms。
T2:是判定雙擊的時間間隔,是個定值 300ms。這個時間間隔的計算,是從第一次【單擊】釋放后開始計時,直到第二次【單擊】釋放按鍵后結束。在完成第一次【單擊】后(釋放按鍵后開始計時),在這個時間間隔內,如果再一次完成【單擊】
T3:是判定三擊的時間間隔,雙擊之后,在這個時間內,再一次有【單擊】,擊判定為【三擊】。其實質與T2一樣。

2.按鍵程序結果

按鍵程序結構可以分為三個部分:
第一部分:判斷有無單擊按鍵;
第二部分:判斷雙擊,即是在預設的時間間隔內,有無第二次【單擊】;
第三部分:判斷三擊,即是在【雙擊】后,在預設的時間間隔內,有無再一次的【單擊】。

3.三擊程序的源代碼以及注釋

程序的注釋寫的很詳細,應該是可以讀懂的,如果有疑問可以留言討論。
以下是key.c 的源代碼:

 
 
 
         
  1. #define KEY_INPUT P1.0 // 按鍵IO
  2. #define KEY_STATE_0 0 // 按鍵狀態
  3. #define KEY_STATE_1 1
  4. #define KEY_STATE_2 2
  5. #define KEY_STATE_3 3
  6. #define SINGLE_KEY_TIME 3 // SINGLE_KEY_TIME*10MS = 30MS 判定單擊的時間長度,軟件消抖
  7. #define KEY_INTERVAL 30 // KEY_INTERVAL*10MS = 300MS 判定雙擊的時間間隔
  8. #define LONG_KEY_TIME 300 // LONG_KEY_TIME*10MS = 3S 判定長按的時間長度
  9. #define N_KEY 0 // no click
  10. #define S_KEY 1 // single click
  11. #define D_KEY 2 // double click
  12. #define T_KEY 3 // Triple click
  13. #define L_KEY 10 // long press
  14. // ----------------------------------- key_driver --------------------------
  15. unsigned char key_driver(void)
  16. {
  17. static unsigned char key_state = 0;
  18. static unsigned int key_time = 0;
  19. unsigned char key_press, key_return;
  20. key_return = N_KEY; // 清除 返回按鍵值
  21. key_press = KEY_INPUT; // 讀取當前鍵值
  22. switch (key_state)
  23. {
  24. case KEY_STATE_0: // 按鍵狀態0:判斷有無按鍵按下
  25. if (!key_press) // 有按鍵按下
  26. {
  27. key_time = 0; // 清零時間間隔計數
  28. key_state = KEY_STATE_1; // 然后進入 按鍵狀態1
  29. }
  30. break;
  31. case KEY_STATE_1: // 按鍵狀態1:軟件消抖(確定按鍵是否有效,而不是誤觸)。按鍵有效的定義:按鍵持續按下超過設定的消抖時間。
  32. if (!key_press)
  33. {
  34. key_time++; // 一次10ms
  35. if (key_time >= SINGLE_KEY_TIME) // 消抖時間為:SINGLE_KEY_TIME*10ms = 30ms;
  36. {
  37. key_state = KEY_STATE_2; // 如果按鍵時間超過 消抖時間,即判定為按下的按鍵有效。按鍵有效包括兩種:單擊或者長按,進入 按鍵狀態2, 繼續判定到底是那種有效按鍵
  38. }
  39. }
  40. else
  41. key_state = KEY_STATE_0; // 如果按鍵時間沒有超過,判定為誤觸,按鍵無效,返回 按鍵狀態0,繼續等待按鍵
  42. break;
  43. case KEY_STATE_2: // 按鍵狀態2:判定按鍵有效的種類:是單擊,還是長按
  44. if (key_press) // 如果按鍵在 設定的長按時間 內釋放,則判定為單擊
  45. {
  46. key_return = S_KEY; // 返回 有效按鍵值:單擊
  47. key_state = KEY_STATE_0; // 返回 按鍵狀態0,繼續等待按鍵
  48. }
  49. else
  50. {
  51. key_time++;
  52. if (key_time >= LONG_KEY_TIME) // 如果按鍵時間超過 設定的長按時間(LONG_KEY_TIME*10ms=300*10ms=3000ms), 則判定為 長按
  53. {
  54. key_return = L_KEY; // 返回 有效鍵值值:長按
  55. key_state = KEY_STATE_3; // 去狀態3,等待按鍵釋放
  56. }
  57. }
  58. break;
  59. case KEY_STATE_3: // 等待按鍵釋放
  60. if (key_press)
  61. {
  62. key_state = KEY_STATE_0; // 按鍵釋放后,進入 按鍵狀態0 ,進行下一次按鍵的判定
  63. }
  64. break;
  65. default: // 特殊情況:key_state是其他值得情況,清零key_state。這種情況一般出現在 沒有初始化key_state,第一次執行這個函數的時候
  66. key_state = KEY_STATE_0;
  67. break;
  68. }
  69. return key_return; // 返回 按鍵值
  70. }
  71. void key_read(void)
  72. {
  73. static unsigned char key_state1 = 0, key_time1 = 0;
  74. unsigned char key_return, key_temp;
  75. key_return = N_KEY; // 清零 返回按鍵值
  76. key_temp = key_driver(); // 讀取鍵值
  77. switch (key_state1)
  78. {
  79. case KEY_STATE_0: // 按鍵狀態0:等待有效按鍵(通過 key_driver 返回的有效按鍵值)
  80. if (key_temp == S_KEY) // 如果是[單擊],不馬上返回單擊按鍵值,先進入 按鍵狀態1,判斷是否有[雙擊]的可能
  81. {
  82. key_time1 = 0; // 清零計時
  83. key_state1 = KEY_STATE_1;
  84. }
  85. else // 如果不是[單擊],直接返回按鍵值。這里的按鍵值可能是:[長按],[無效按鍵]
  86. {
  87. key_return = key_temp; // 返回 按鍵值
  88. }
  89. break;
  90. case KEY_STATE_1: // 按鍵狀態1:判定是否有[雙擊]
  91. if (key_temp == S_KEY) // 有[單擊]后,如果在 設定的時間間隔(KEY_INTERVAL*10ms=30*10ms=300ms) 內,再次有[單擊],則為[雙擊],但是不馬上返回 有效按鍵值為[雙擊],先進入 按鍵狀態2,判斷是否有[三擊]
  92. {
  93. key_time1 = 0; // 清零 時間間隔
  94. key_state1 = KEY_STATE_2; // 改變 按鍵狀態值
  95. }
  96. else // 有[單擊]后,如果在 設定的時間間隔(KEY_INTERVAL*10ms=30*10ms=300ms)內,沒有[單擊]出現,則判定為 [單擊]
  97. {
  98. key_time1++; // 計數 時間間隔
  99. if (key_time1 >= KEY_INTERVAL) // 超過 時間間隔
  100. {
  101. key_return = S_KEY; // 返回 有效按鍵:[單擊]
  102. key_state1 = KEY_STATE_0; // 返回 按鍵狀態0,等待新的有效按鍵
  103. }
  104. }
  105. break;
  106. case KEY_STATE_2: // 按鍵狀態2:判定是否有[三擊]
  107. if (key_temp == S_KEY) // 有[雙擊]后,如果在 設定的時間間隔(KEY_INTERVAL*10ms=30*10ms=300ms) 內,再次有[單擊],則為[三擊],由於這里只擴展到[三擊],所以馬上返回 有效按鍵值為[三擊]
  108. {
  109. key_return = T_KEY; // 返回 有效按鍵:[三擊]
  110. key_state1 = KEY_STATE_0; // 返回 按鍵狀態0,等待新的有效按鍵
  111. }
  112. else // 有[雙擊]后,如果在 設定的時間間隔(KEY_INTERVAL*10ms=30*10ms=300ms)內,沒有[單擊],則判定為 [雙擊]
  113. {
  114. key_time1++; // 計數 時間間隔
  115. if (key_time1 >= KEY_INTERVAL) // 超過 時間間隔
  116. {
  117. key_return = D_KEY; // 返回 有效按鍵:[雙擊]
  118. key_state1 = KEY_STATE_0; // 返回 按鍵狀態0,等待新的有效按鍵
  119. }
  120. }
  121. break;
  122. default: // 特殊情況:key_state是其他值得情況,清零key_state。這種情況一般出現在 沒有初始化key_state,第一次執行這個函數的時候
  123. key_state1 = KEY_STATE_0;
  124. break;
  125. }
  126. return key_return; // 返回 按鍵值
  127. }

使用注意:

1)硬件:按鍵的一端接地(GND),另一端接IO口。IO為輸入,一定要有上拉電阻。
2)定時器:這里為了精確的定時,所以使用了定時器,定時器的時間是10ms。
3)掃描周期:調用此函數時,一定確保”掃描周期“要小於10ms。不然按鍵內所涉及的時間就會不准,會偏大。所涉及的時間包括消抖時長,按鍵長按時長等。
掃描周期定義:從 第一次進入按鍵掃描程序 開始,到第二次進入按鍵掃描程序時 結束,之間所用的時間。
測量掃描周期的方法:可以在按鍵掃描程序的第一句,添加IO口取反函數,然后用示波器查看改IO口,其IO口周期的一般就是掃描周期了。
4)特別注意以上程序的3個關於時間的宏,相當的重要。如果想更改按鍵的單擊的靈敏度,雙擊(三擊)的速度,或者長按的時間,只需要修改這些宏的值即可。
比如:針對於老人的使用的按鍵,就需要將雙擊的速度調節的慢一點,就可以將KEY_INTERVAL的值增大。

SINGLE_KEY_TIME:單擊的靈敏度,值越小,越靈敏。
KEY_INTERVAL :雙擊的點擊速度,修改這個值,值越小,速度越快。同時這個值也決定了單擊的響應時間,因為單擊之后,還需要判斷在這個時間間隔內沒有第二次單擊,如果沒有才是真正的單擊,所以單擊響應的時間為:SINGLE_KEY_TIME+KEY_INTERVAL;
LONG_KEY_TIME :長按的時間,修改這個即可,值越大,時間越長。






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM