单片机按键学习总结

基本的按键程序结构分析:

 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

 文件中定义了需要的变量、数据结构,以及函数声明。

原文地址:https://www.cnblogs.com/xbook-ben/p/7073974.html