找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 663|回复: 2
打印 上一主题 下一主题
收起左侧

51单片机长按短按连击2-10次函数 带仿真演示程序

[复制链接]
跳转到指定楼层
楼主
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)


单片机源程序如下:
#ifndef _KEY_H
#define _KEY_H

//#include"reg51.h"
sbit KEY_INPUT  =        P1^2;    //  按键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 KEY_STATE_5         5
#define KEY_STATE_6         6
#define KEY_STATE_7         7
#define KEY_STATE_8         8
#define KEY_STATE_9         9

#define SINGLE_KEY_TIME     3       //  SINGLE_KEY_TIME*10MS = 30MS  判定单击的时间长度,软件消抖
#define KEY_INTERVAL        70      //  KEY_INTERVAL*10MS    = 300MS 判定双击的时间间隔(仿真调慢了700MS)实际30-40
#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 F_KEY                 4        //4击
#define FIVE_KEY              5        //5击
#define SIX_KEY               6        //6击
#define SEVEN_KEY             7        //7击
#define EIGHT_KEY              8        //8击
#define NINE_KEY               9        //9击
#define TEN_KEY                10        //10击
#define L_KEY                  15       //  long press
//unsigned char key_driver(void);
//unsigned char key_read(void);  
#endif

#include "key.h"
        // =========================== key.c ======================
// ----------------------------------- 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;       //  改变 按键状态值
        // 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;

        case KEY_STATE_2:                       // 按键状态2:判定是否有[三击]
            if (key_temp == S_KEY)              // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[三击],由于这里只扩展到[三击],所以马上返回 有效按键值为[三击]
            {
           //  key_return = T_KEY;
               key_time1 = 0;                                                           // 返回 有效按键:[三击]
                 key_state1 = KEY_STATE_3;      // 返回 按键状态0,等待新的有效按键
            }
            else                                // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击],则判定为 [双击]
            {
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 {
                      key_return =  D_KEY;       // 返回 有效按键 返回 有效按键:[2击】
                      key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                 }              
             }              
             break;
                          case KEY_STATE_3:                       // 按键状态3:判定是否有[4击]
            if (key_temp == S_KEY)              // 有[3击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[三击]
            {
               //  key_return = F_KEY;            // 返回 有效按键:[4击]
                                                           key_time1 = 0;  
                 key_state1 = KEY_STATE_4;      // 返回 按键状态0,等待新的有效按键
            }
            else                                // 有[3击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[4击],则判定为 [3击]
            {
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 {
                      key_return = T_KEY;         // 返回 有效按键:[3击]
                      key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                 }              
             }              
             break;
                  case KEY_STATE_4:                       // 按键状态4:判定是否有[5击]
            if (key_temp == S_KEY)              // 有[4击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[5击],由于这里只扩展到[5击],
            {
                 //key_return = FIVE_KEY;            // 返回 有效按键:[5击]
                                                           key_time1 = 0;
                 key_state1 = KEY_STATE_5;      // 返回 按键状态0,等待新的有效按键
            }
            else                                // 有[4击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[5击],则判定为 [4击]
            {
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 {
                      key_return = F_KEY;         // 返回 有效按键:[4击]
                      key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                 }              
             }              
             break;
                 case KEY_STATE_5:                       // 按键状态5:判定是否有[5击]
            if (key_temp == S_KEY)              // 有[5击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[6击]
            {
                // key_return = SIX_KEY;            // 返回 有效按键:[6击]
                                                          // key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                                                           key_time1 = 0;
                 key_state1 = KEY_STATE_6;      // 返回 按键状态0,等待新的有效按键
            }
            else                                // 有[5击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[6击],则判定为 [5击]
            {
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 {
                      key_return = FIVE_KEY;         // 返回 有效按键:[5击]
                      key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                 }              
             }              
             break;
                        case KEY_STATE_6:                       // 按键状态6:判定是否有[6击]
            if (key_temp == S_KEY)              // 有[6击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[7击]
            {
                // key_return = SEVEN_KEY;            // 返回 有效按键:[7击]
                // key_state1 = KEY_STATE_0;      // 返回 按键状态0,等待新的有效按键
                                                           key_time1 = 0;
                 key_state1 = KEY_STATE_7;      // 返回 按键状态0,等待新的有效按键
            }
            else                                // 有[6击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[7击],则判定为 [6击]
            {
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 {
                      key_return = SIX_KEY;         // 返回 有效按键:[6击]
                      key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                 }              
             }              
             break;         
                        case KEY_STATE_7:                       // 按键状态7:判定是否有[7击]
            if (key_temp == S_KEY)              // 有[7击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[8击]
            {
                // key_return = EIGHT_KEY;            // 返回 有效按键:[8击]
                // key_state1 = KEY_STATE_0;      // 返回 按键状态0,等待新的有效按键
                                                                key_time1 = 0;
                key_state1 = KEY_STATE_8;      // 返回 按键状态0,等待新的有效按键
            }
            else                                // 有[7击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[8击],则判定为 [7击]
            {
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 {
                      key_return = SEVEN_KEY;         // 返回 有效按键:[7击]
                      key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                 }              
             }              
             break;                 
                        case KEY_STATE_8:                       // 按键状态7:判定是否有[8击]
            if (key_temp == S_KEY)              // 有[8击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[9击]
            {
                // key_return = NINE_KEY;            // 返回 有效按键:[9击]
                // key_state1 = KEY_STATE_0;      // 返回 按键状态0,等待新的有效按键
                                                          key_time1 = 0;
                key_state1 = KEY_STATE_9;      // 返回 按键状态0,等待新的有效按键

            }
            else                                // 有[8击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[9击],则判定为 [8击]
            {
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 {
                      key_return = EIGHT_KEY;         // 返回 有效按键:[8击]
                      key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                 }              
             }              
             break;                 
                          case KEY_STATE_9:                       // 按键状态7:判定是否有[9击]
            if (key_temp == S_KEY)              // 有[9击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[9击]
            {
                 key_return = TEN_KEY;            // 返回 有效按键:[10击]
                 key_state1 = KEY_STATE_0;      // 返回 按键状态0,等待新的有效按键
            }
            else                                // 有[9击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[10击],则判定为 [9击]
            {
                key_time1++;                    // 计数 时间间隔
                if(key_time1 >= KEY_INTERVAL)   // 超过 时间间隔
                 {
                      key_return = NINE_KEY;         // 返回 有效按键:[9击]
                      key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
                 }              
             }              
             break;                                                         
   //=============================================================================================
        default:                                //  特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候
            key_state1 = KEY_STATE_0;
            break;
    }

        return key_return;                      // 返回 按键值
}     

// =========================== main.c ======================

#include "reg51.h"
#include "key.c"
#define FOSC 11059200L

#define T1MS (65536-FOSC/12/100)   //1ms timer calculation method in 12T mode
sbit LED1 = P2^0;                   // 定义LEDIO口
sbit LED2 = P2^1;   
sbit LED3 = P2^2;                   // 定义LEDIO口
sbit LED4 = P2^3;
sbit LED5 = P2^4;                   // 定义LEDIO口
sbit LED6 = P2^5;
sbit LED7 = P2^6;
sbit LED8 = P2^7;

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;                    //set timer0 as mode1 (16-bit)
    TL0 = T1MS;                     //initial timer0 low byte
    TH0 = T1MS >> 8;                //initial timer0 high byte
    TR0 = 1;                        //timer0 start running
    ET0 = 1;                        //enable timer0 interrupt
    EA = 1;      
}

// 主函数
void main(void)
{
   // P1^2=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=0;LED2=1;LED3=1;LED4=1;LED5=1;LED6=1;LED7=1;LED8=1; break;       // 单击 点亮LED1
                case D_KEY:        LED1=1;LED2=0;LED3=1;LED4=1;LED5=1;LED6=1;LED7=1;LED8=1; break;        // 双击
                                                          case T_KEY:        LED1=1;LED2=1;LED3=0;LED4=1;LED5=1;LED6=1;LED7=1;LED8=1; break;       // 三击
                                                          case F_KEY:        LED1=1;LED2=1;LED3=1;LED4=0;LED5=1;LED6=1;LED7=1;LED8=1; break;      // 四击                              
                                                          case FIVE_KEY:     LED1=1;LED2=1;LED3=1; LED4=1; LED5=0;LED6=1;LED7=1;LED8=1;break;    //五击
                                                          case SIX_KEY:      LED1=1;LED2=1;LED3=1; LED4=1; LED5=1;LED6=0;LED7=1;LED8=1;break;     //六击
                                                          case SEVEN_KEY:    LED1=1;LED2=1;LED3=1; LED4=1; LED5=1;LED6=1;LED7=0;LED8=1;break;     //7击
                                                          case EIGHT_KEY:    LED1=1;LED2=1;LED3=1; LED4=1; LED5=1;LED6=1;LED7=1;LED8=0;break;     //8击
                                                                case NINE_KEY:     LED1=0;LED2=0;LED3=0; LED4=0; LED5=1;LED6=1;LED7=1;LED8=1;break;      //9击
                                                          case TEN_KEY:      LED1=1;LED2=1;LED3=1; LED4=1; LED5=0;LED6=0;LED7=0;LED8=0;break;      //10击
                                                        
                                                                case L_KEY: LED1=1;LED2=1;LED3=1; LED4=1; LED5=1;LED6=1;LED7=1;break;      // 长按 熄灭所有LED
            }
        }
    }
}

// timer0 中断服务程序
void IRQ_T0(void) interrupt 1
{
          TL0 = T1MS;                     //reload timer0 low byte
    TH0 = T1MS >> 8;                //reload timer0 high byte
    g_flag_10ms_key = 1;                        // 置位 10ms 定时标志
}

        
仿真程序: 按键单击多击长按扫描演示 -状态机.7z (135.29 KB, 下载次数: 50)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏4 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:849894 发表于 2024-1-4 15:28 | 只看该作者
这个程序的主要作用是什么?
回复

使用道具 举报

板凳
ID:430492 发表于 2024-1-4 16:04 | 只看该作者
可以用少量的按键,实现多个按钮才能实现的功能。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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