【按键】[独立按键] - 2:双击

来源:互联网 发布:不怎么花钱的网络手游 编辑:程序博客网 时间:2024/06/12 00:24

二、 [双击]程序

1.双击介绍

  • 双击的判定
    在完成第一次【单击】之后,在一定的时间间隔内(本程序使用的是300ms),接着完成第二次【单击】,及判定为【双击】。
  • 双击的响应
    1)在预设的时间间隔内完成第二次【单击】,按键释放后,响应返回有效键值【双击】。
    2)如果第二次按下键并一直按住,当按住的时间超过设定的时间间隔(300ms)后,会响应第一个【单击】,并返回有效键值【单击】。
    注:【单击】是包括按下和释放按键的过程,判定方式沿用上文所说的,如果忘了,可以参考上文。
  • 双击响应时序图
    这里写图片描述

注:
T1:是单击判定的时间,范围:30ms < T1 < 3000ms
T2:是判定双击的时间间隔,是个定值 300ms。在完成第一次【单击】后(释放按键后开始计时),在这个时间间隔内,如果再一次完成【单击】(释放按键后结束计时),则判定为【双击】。
T3:与T1相同。

2.按键程结构

按键程序可以分为四个部分,第一部分:判断有无单击按键;第二部分:判断双击即是在预设的时间间隔内,有无第二次【单击】;第三部分:等待按键释放

3.双击程序的源代码以及注释

程序的注释写的很详细,应该是可以读懂的,如果有疑问可以留言讨论。
上文的【单击】函数作为这次【双击】程序的子函数
以下是key.c 的源代码:

// =========================== key.c ======================#include"reg51.h"#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 SINGLE_KEY_TIME     3       //  SINGLE_KEY_TIME*10MS = 30MS  判定单击的时间长度,软件消抖#define KEY_INTERVAL        30      //  KEY_INTERVAL*10MS    = 300MS 判定双击的时间间隔#define LONG_KEY_TIME       300     //  LONG_KEY_TIME*10MS   = 3S    判定长按的时间长度#define N_KEY               0       //  no click#define S_KEY               1       //  single click#define D_KEY               2       //  double click#define L_KEY               10      //  long press// ----------------------------------- key_driver --------------------------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=300*10ms=3000ms), 则判定为 长按                {                    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;                         //  返回 按键值} // ----------------------------------- key_read --------------------------------unsigned char key_read(void)                            {     static unsigned char key_state1=0, key_time1=0;    unsigned char key_return,key_temp;    key_return = N_KEY;                         //  清零 返回按键值    key_temp = key_driver();                    //  读取键值    switch(key_state1)     {                 case KEY_STATE_0:                       //  按键状态0:等待有效按键(通过 key_driver 返回的有效按键值)            if (key_temp == S_KEY )             //  如果是[单击],不马上返回单击按键值,先进入 按键状态1,判断是否有[双击]的可能            {                  key_time1 = 0;                 //  清零计时                 key_state1 = KEY_STATE_1;             }                         else                                //  如果不是[单击],直接返回按键值。这里的按键值可能是:[长按],[无效按键]            {                 key_return = key_temp;         //  返回 按键值            }            break;        case KEY_STATE_1:                       //  按键状态1:判定是否有[双击]            if (key_temp == S_KEY)              //  有[单击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[双击],但是不马上返回 有效按键值为[双击],先进入 按键状态2,判断是否有[三击]            {                               key_return = D_KEY;            //  返回 有效按键:[双击]                 key_state1 = KEY_STATE_0;      //  返回 按键状态0,等待新的有效按键            }             else                                //  有[单击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击]出现,则判定为 [单击]            {                key_time1++;                    //  计数 时间间隔                if(key_time1 >= KEY_INTERVAL)   //  超过 时间间隔                 {                       key_return = S_KEY;       //  返回 有效按键:[单击]                      key_state1 = KEY_STATE_0; //  返回 按键状态0,等待新的有效按键                 }                           }                           break;         default:                                //  特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候            key_state1 = KEY_STATE_0;            break;    }        return key_return;                      // 返回 按键值}     

使用注意:
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 源代码:

// =========================== 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 计时标志unsigned char key_read();           // 声明读取按键函数void T0_Init_10ms(void)             // timer0,初始化函数 ,定时时间为 10ms{    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_read();         // 读取按键值            switch(g_u8_KeyValue)            {                case S_KEY: LED1 = 1; break;    // 单击 点亮LED1                case D_KEY: LED1 = 0; 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.29 10:25


其余分享链接:

第一部分:【按键】[独立按键] - 1: 单击

第三部分:【按键】[独立按键] - 3: 三击以及N击

0 0