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

三、 [三击]程序

1.三击介绍

  • 三击判定:在完成第一次【单击】之后,在一定的时间间隔内(本程序使用的是300ms),接着完成第二次【单击】,时间间隔重新计时,然后又在这个时间间隔内,完成第三次【单击】,及判定为【三击】。
    注:【单击】是包括按下和释放按键的过程,判定方式沿用上文所说的,如果忘了,可以参考上文。
  • 三击响应时序图
    这里写图片描述
    注:
    T1:是单击判定的时间,范围:30ms < T1 < 3000ms。
    T2:是判定双击的时间间隔,是个定值 300ms。这个时间间隔的计算,是从第一次【单击】释放后开始计时,直到第二次【单击】释放按键后结束。在完成第一次【单击】后(释放按键后开始计时),在这个时间间隔内,如果再一次完成【单击】
    T3:是判定三击的时间间隔,双击之后,在这个时间内,再一次有【单击】,击判定为【三击】。其实质与T2一样。

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 T_KEY               3       //  Triple 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_time1 = 0;                  //  清零 时间间隔
                key_state1 = KEY_STATE_2;       //  改变 按键状态值
            } 
            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; 

        case KEY_STATE_2:                       // 按键状态2:判定是否有[三击]
            if (key_temp == S_KEY)              // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[三击],由于这里只扩展到[三击],所以马上返回 有效按键值为[三击]
            {
                 key_return = T_KEY;            // 返回 有效按键:[三击]
                 key_state1 = KEY_STATE_0;      // 返回 按键状态0,等待新的有效按键
            } 
            else                                // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击],则判定为 [双击]
            {
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 { 
                      key_return = D_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
  • 双击:取反LED2
  • 三击:取反LED3
  • 长按:点亮LED1,LED2,LED3

2)硬件:

  • 按键IO:P1.0
  • LED1 :P2.0
  • LED2 :P2.1
  • LED3 :P2.2

以下是 main.c 源代码:

// =========================== key.c ======================

#include "reg51.h"
#include "key.c"

#define LED_ALL_ON()    P2 |= 0x07
#define LED_ALL_OFF()   P2 &= ~0x07

sbit LED1 = P2.0;                   // 定义LEDIO口
sbit LED2 = P2.1;                   // 定义LEDIO口
sbit LED3 = P2.2;                   // 定义LEDIO口

unsigned char g_u8_KeyValue;        // 按键值
unsigned char g_flag_10ms_key;      // 10ms 计时标志


void 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 = !LED1; break;    // 单击 取反LED1
                case D_KEY: LED2 = !LED2; break;    // 双击 取反LED2
                case T_KEY: LED3 = !LED3; break;    // 三击 取反LED3
                case L_KEY: LED_ALL_ON(); break;    // 长按 点亮所有的LED
            }
        }
    }
}

// timer0 中断服务程序
void IRQ_T0(void) interrupt 1
{
    g_flag_10ms_key = 1;                        // 置位 10ms 定时标志
}

Pillar Peng
2016.3.29 - 17:30


有之前单击,双击,三击的介绍,四击以及N击只是一小段程序的区别。有兴趣的可以扩展一下4击以及N击,试着自己动手编写程序,其实很简单的,只需要更改key_read 函数即可,大家可以对照双击,三击和四击的程序,就可以看得出来,改动的地方很少。

将扩展的四击程序附在这里:

// =========================== 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 T_KEY               3       //  Triple click        三击
#define Q_KEY               4       //  Quadruple 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 --------------------------------
void 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_time1 = 0;                  //  清零 时间间隔
                key_state1 = KEY_STATE_2;       //  改变 按键状态值
            } 
            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; 

        case KEY_STATE_2:                       // 按键状态2:判定是否有[三击]
            if (key_temp == S_key)              // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[三击],由于这里只扩展到[三击],所以马上返回 有效按键值为[三击]
            {
                key_time1 = 0;                  // 清零 时间间隔
                key_state1 = KEY_STATE_3;       // 改变 按键状态值 
            } 
            else                                // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击],则判定为 [双击]
            {
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 { 
                      key_return = D_KEY;       // 返回 有效按键:[双击]
                      key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                 }
             }
             break; 

        case KEY_STATE_3:                       // 按键状态3:等待按键释放
            if (key_temp == S_key)              // 有[三击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[四击],马上返回有效按键值为[四击],
            { 
                 key_return = Q_key;            // 返回 有效按键:[四击]
                 key_state1 = KEY_STATE_0;      // 返回 按键状态0,等待新的有效按键
            } 
            else                                
            {                                   // 有[三击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击],则判定为 [三击]                 
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 { 
                      key_return = T_KEY;       // 返回 有效按键:[三击]
                      key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                 }              
             }             
             break; 

        default:                                //  特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候
            key_state1 = KEY_STATE_0;
            break;
    }

    return = key_return;                        // 返回 按键值
}     

Pillar Peng
2016.3.30 - 14:04


友情链接:

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

第二部分:【按键】[独立按键] - 2:双击

  • 27
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
要实现独立按键显示对应键值,需要通过程序来实现按键扫描和键值的显示。一般情况下,可以采用轮询方式来实现按键扫描。具体地说,程序会定时扫描按键状态,并判断是否有按键被按下。如果有按键被按下,则程序会根据按键编码来显示对应的键值。 下面是一个简单的示例代码,可以实现独立按键显示对应键值的功能: ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <windows.h> #define KEY_NUM 16 const char key_map[KEY_NUM] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F'}; int main() { bool key_status[KEY_NUM] = {false}; // 记录按键状态 int key_code; // 记录按键编码 while (1) { // 扫描按键状态 for (int i = 0; i < KEY_NUM; i++) { if (GetAsyncKeyState(i) & 0x8000) { if (!key_status[i]) { key_status[i] = true; key_code = i; break; } } else { key_status[i] = false; } } // 显示键值 if (key_code >= 0 && key_code < KEY_NUM) { printf("%c\n", key_map[key_code]); key_code = -1; } Sleep(10); // 等待一段时间,避免频繁扫描按键 } return 0; } ``` 在上面的示例代码中,我们通过GetAsyncKeyState()函数来获取按键状态,如果某个按键被按下,则该函数返回值的最高位为1。然后我们根据按键状态来记录按键状态,并根据按键编码来显示对应的键值。注意,在实际应用中,需要根据具体的硬件和操作系统来实现相应的按键扫描和键值显示逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PillarPeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值