找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1554|回复: 2
收起左侧

单片机状态机按键程序怎么能响应两个不同时长的长按?

[复制链接]
ID:91521 发表于 2019-10-25 18:25 | 显示全部楼层 |阅读模式
我在学习状态机按键程序时遇到一个问题:我用状态机按键方法,只能实现一个长按事件的响应。如果要能够响应两个不同时长的长按,程序该怎么修改呢?我试着修改了一下,可是不成功,百思不得其解,只好请各位高人指点迷津!

单片机程序如下(红色部分是我自己修改的):

#include<reg52.h>

sbit LED0 = P0^7;
sbit LED1 = P0^6;
sbit LED2 = P0^5;
sbit LED3 = P0^4;

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

sbit KEY_INPUT = P2^4;    //  按键IO

#define KEY_STATE_0         0       //  按键状态
#define KEY_STATE_1         1
#define KEY_STATE_2         2
#define KEY_STATE_3         3
#define KEY_STATE_4         4

#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

unsigned char g_u8_KeyValue;        // 按键值

// ----------------------------------- 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_return = L_KEY;            
                 key_state = KEY_STATE_0;      
            }
            else
            {
                key_time++;                     

                if(key_time >= 600)   
                {
                    key_return = 5;         
                    key_state = KEY_STATE_4;   
                }
            }
            break;    //我自己添加的代码结束


      case KEY_STATE_4:                         //  等待按键释放
          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;                          // 返回 按键值
}

void main()
{
    ENLED = 0;
    ADDR3 = 1;
    ADDR2 = 1;
    ADDR1 = 1;
    ADDR0 = 0;

    P2 = 0xF7;

    TMOD = 0x01;
    TH0 = 0xDC;
    TL0 = 0x00;
    TR0 = 1;
     
    while(1)
    {
        if(TF0 == 1)                         // 等待10ms,定时完成
        {
            TF0 = 0;                    // 清零10ms定时标志
            TH0 = 0xDC;
            TL0 = 0x00;

            g_u8_KeyValue = key_read();             // 读取按键值

            switch(g_u8_KeyValue)
            {
                case 5: LED1 = !LED1; break;    // 单击 取反LED1
                case D_KEY: LED2 = !LED2; break;    // 双击 取反LED2
                case L_KEY: LED3 = !LED3; break;    // 三击 取反LED3
//                case L_KEY: LED_ALL_ON(); break;    // 长按 点亮所有的LED
            }
        }
    }
}
回复

使用道具 举报

ID:94031 发表于 2019-10-26 11:27 | 显示全部楼层
用了定时0的中断标志TF0,却没有初始化定时器0以及相应的中断程序,说明没有理解程序原理。
回复

使用道具 举报

ID:405193 发表于 2019-10-26 22:09 | 显示全部楼层
你case KEY_STATE_3不能依照 case KEY_STATE_2这样复制下来,都有key_time++;    你应该在   case KEY_STATE_2里根据设置key_time  不同的时间来设置功能达到  case KEY_STATE_3的目的。  
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表