單片機按鍵學習總結


基本的按鍵程序結構分析:

 1 void key_scan(void)
 2 {
 3       if (!key)                           //檢測按鍵是否按下
 4       {
 5           delay_ms(20);                   //延時去抖,一般20ms
 6           if(!key)
 7           {
 8                ......
 9           }
10           while (!key);                   //等待按鍵釋放
11      }       
12 }            

注意:以上基本按鍵程序中,在按鍵執行之后必須要加上等待按鍵釋放,否則程序會出現一些奇怪的問題,比如說按鍵累加時按鍵一次,卻累加了多次。

可識別長擊和短擊按鍵程序(有限狀態機):

主函數文件:

main.c

 1 #include "key.h"
 2 
 3 sbit LED = P2^0;
 4 
 5 u8 timer0_flag;                                    //定時器10ms計時標記
 6 
 7 
 8 void timer0_init(void)
 9 {
10     TMOD |= 0x01;                                 //定時器0的方式1(16位定時器)
11     TH0 = 0xDC;                                    //定時10ms初值
12     TL0 = 0x00;
13     
14     ET0 = 1;
15     TR0 = 1;
16     EA  = 1;
17 }
18 
19 void main(void)
20 {
21     key_init();                                    //按鍵初始化,51單片機在讀取某個端口的值時,先拉高
22     timer0_init();                                 //定時器初始化
23     
24     for (;;)    
25     {
26         if (timer0_flag)
27         {
28             timer0_flag = 0;
29             
30             switch (key_driver())    
31             {
32                 case KEY_SHORT: LED = 0; break;    //短擊點亮LED燈
33                 case KEY_LONG : LED = 1; break;    //長擊熄滅LED燈
34             }
35         }
36     }
37 }
38 
39 void timer0_int(void) interrupt 1                //中斷處理函數
40 {
41     TH0 = 0xDC;
42     TL0 = 0x00;
43     timer0_flag = 1;
44 }

主文件里非常重要的有兩處:

1、時間粒度控制:本程序以10ms做時間單位,類似於時間片輪詢的方式,每隔10ms對按鍵狀態掃描一次,對應代碼為:

void timer0_int(void) interrupt 1                //中斷處理函數

{     

  TH0 = 0xDC;     

  TL0 = 0x00;     

  timer0_flag = 1;       //10ms時間

 }

2對按鍵驅動函數返回值的判斷,根據按鍵返回值,識別按鍵操作是短擊還是長擊后,執行相應的動作,對應代碼為:

switch (key_driver())    

{      case KEY_SHORT: LED = 0; break;    //短擊點亮LED
      case KEY_LONG : LED = 1; break;    //長擊熄滅LED

}

按鍵驅動文件:

key.c

 1 #include "key.h"
 2 
 3 sbit KEY_INPUT = P3^3;                                         //獨立按鍵
 4 
 5 key custom_key;                                                //按鍵的數據結構體
 6 
 7 void key_init(void)                                            //按鍵初始化
 8 {
 9     KEY_INPUT = 1;
10 }
11 
12 u8 key_driver(void)                                            //按鍵驅動函數
13 {
14     custom_key.press = KEY_INPUT;                              //讀取按鍵接口值
15     custom_key.value = KEY_NONE;                               //返回值初始化為無值
16     
17     switch(custom_key.state)                                   //按鍵狀態判斷
18     {
19         case KEY_STATE_JUDGE:                                  //判斷有無按鍵按下狀態
20             if (!custom_key.press)                             //有按鍵按下
21             {
22                 custom_key.state = KEY_STATE_DEBOUNCE;         //轉入消抖狀態
23                 custom_key.count = 0;                          //計數器清零
24             }
25         break;
26         
27         case KEY_STATE_DEBOUNCE:                               //消抖狀態
28             if (!custom_key.press)
29             {
30                 custom_key.count ++;
31                 if (custom_key.count >= SINGLE_KEY_TIME)
32                 {
33                     custom_key.state = KEY_STATE_SPAN;         //消抖確認是有效按鍵,轉入短擊和長擊判斷狀態
34                 }
35             }
36             else 
37                 custom_key.state = KEY_STATE_JUDGE;            //按鍵誤動作,返回判斷有無按鍵按下狀態
38         break;
39         
40         case KEY_STATE_SPAN:                                   //短擊和長擊判斷狀態
41             if (custom_key.press)                              //在長擊臨界值之前釋放按鍵,判斷為短擊
42             {
43                 custom_key.value = KEY_SHORT;
44                 custom_key.state = KEY_STATE_JUDGE;            //返回判斷有無按鍵按下狀態
45             }
46             else                                               //計數器值超過長擊臨界值,判斷為長擊
47             {
48                 custom_key.count ++;
49                 if (custom_key.count >= LONG_KEY_TIME)
50                 {
51                     custom_key.value = KEY_LONG;
52                     custom_key.state = KEY_STATE_RELEASE;      //進入按鍵釋放狀態
53                 }
54             }
55         break;
56         
57         case KEY_STATE_RELEASE:                                //按鍵釋放狀態
58             if (custom_key.press)
59             {
60                 custom_key.state = KEY_STATE_JUDGE;            //返回判斷有無按鍵按下狀態
61             }
62         break;
63         
64         default:                                               //默認返回判斷有無按鍵按下狀態
65             custom_key.state = KEY_STATE_JUDGE;
66             break;
67     }
68     return custom_key.value;                                   //返回按鍵值
69 }

 代碼中做了詳細的注釋,需要說明的是按鍵的四種狀態:

KEY_STATE_JUDGE:用來檢測是否有按鍵按下, 當有按鍵按下后,轉移到消抖狀態,否則每次時間片掃描時都處於此狀態  
KEY_STATE_DEBOUNCE:消抖狀態,用來檢測按鍵有效還是誤觸發,假如只是誤觸發,則返回到按鍵等待狀態
KEY_STATE_SPAN:判斷按鍵是長擊還是短擊,如果在延時消抖后,按鍵在長按的臨界值之前釋放,則判斷為短擊,否則判斷為長擊,此處的臨界值為2s
KEY_STATE_RELEASE:按鍵釋放狀態,擺脫用while循環等待按鍵釋放,當判斷為長擊以后,程序將進入此狀態,在此之后只需在每次時間片到了以后判斷是否釋放即可

此處需要理解的是“並行”的思想:主程序一直在for(;;)循環中運行,同時定時器也在不斷累加計數,當達到定時器中斷觸發條件后,定時器中斷當前的循環,進入定時器服務程序。因此,
這四種狀態在每次定時器中斷觸發后就會檢測判斷一次,相隔時間為10ms。

頭文件:

key.h

 1 #ifndef _KEY_H_
 2 #define _KEY_H_
 3 
 4 #include "reg52.h"
 5 
 6 typedef unsigned char u8;
 7 typedef unsigned int  u16;
 8 
 9 #define KEY_STATE_JUDGE         0        //判斷有無按鍵按下狀態
10 #define KEY_STATE_DEBOUNCE      1        //消抖狀態
11 #define KEY_STATE_SPAN          2        //判斷是短按還是長按
12 #define KEY_STATE_RELEASE       3        //按鍵釋放
13 
14 #define LONG_KEY_TIME            200     //按鍵持續超過2s,判斷為長擊
15 #define SINGLE_KEY_TIME         2        //消抖
16 
17 #define KEY_NONE                0        //沒有按下按鍵
18 #define KEY_SHORT                1       //短擊
19 #define KEY_LONG                2        //長擊
20 
21 typedef struct 
22 {
23     u8    press;                         //讀取按鍵接口
24     u8    state;                         //按鍵狀態
25     u8    value;                         //按鍵返回值
26     u16   count;                         //按鍵時間計數器
27 }key;
28 
29 //extern key custom_key;
30 
31 void key_init(void);                     //按鍵初始化函數            
32 u8 key_driver(void);                     //按鍵驅動函數
33 
34 #endif

 文件中定義了需要的變量、數據結構,以及函數聲明。


免責聲明!

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



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