超牛的按键输入检测,中断方式,支持长按,短按,效率高

2019-07-21 02:25发布

按键输入检测是单片机系统最常用的功能之一,检测方法常用的有:
1:主函数main的while(1)不断检测。这种方法因为加入了延时delay_ms(10); 使得同在while(1)中的其他程序得不到及时的处理,效率极低
2:中断方式。将按键的检测放到中断中,将检测后的结果放到while(1)中执行,这种方法效率极高,不耽误定时器中断的其他操作,我用这种方法好几年了,适合各种复杂或者简单的系统。
3:外部中断方式。在功能较少,外部中断利用率低的情况下,也是不错的选择,但在复杂系统,就不推荐了,因为占用了极其重要的外部中断资源,如果需要按键检测的优先级非常高,也可以使用。
分析这几种检测方式,我推荐方式2,
方式1和方式2对比
方式1:常规方法-原子例程
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY3按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY_UP!!
u8 KEY_Scan(u8 mode)
{         
        static u8 key_up=1;//按键按松开标志
        if(mode)key_up=1;  //支持连按                  
        if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
        {
                delay_ms(10);//去抖动
                key_up=0;
                if(KEY0==0)return KEY0_PRES;
                else if(KEY1==0)return KEY1_PRES;
                else if(WK_UP==1)return WKUP_PRES;
        }else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1;             
        return 0;// 无按键按下
}

int main(void)
{
        vu8 key=0;       
        delay_init();                     //延时函数初始化          
        LED_Init();                                  //初始化与LED连接的硬件接口
        KEY_Init();                 //初始化与按键连接的硬件接口
        LED0=0;                                        //先点亮红灯
        while(1)
        {
                key=KEY_Scan(0);        //得到键值
                   if(key)
                {                                                  
                        switch(key)
                        {                                 
                                case WKUP_PRES:       
                                          LED0=!LED0;
                                        break;
                                case KEY1_PRES:                 
                                        LED1=!LED1;
                                        break;
                                case KEY0_PRES:       
                                        LED0=!LED0;
                                        LED1=!LED1;
                                        break;
                        }
                }else delay_ms(10);
        }         
}

因为延时的存在,这种方法局限性太强,除非用RTOS系统,否则在复杂的裸机环境中不太适用
方式2:中断方式检测--(具体细节请下载看例程
                                                                                                                     key.c
void KEY_Interrupt_WK_UP(void)
{
    static u8 fd=0;
    static u8 cg=0;
    static u8 degrees=1;       
    static u8 klj=0;
    static u8 long_press=0;
    static u8  er=0;
    if(WK_UP==1)
         {
               if(!cg)
                        {
                            fd++;
                                if(fd==4) klj=1;
                        }
                   if(fd>60)
                        {       
                                cg=1;
                                fd=0;
                                long_press=1;
                                er=1;
                        }
                        if(er)
                        {
                          //可按下时同步操作,不用释放时才有效果        ,只能执行一次,除非是cg还是0                  
                        }       
         }
    else
          {
               if(klj)
                    {
                                if(long_press)//长按
                                 {
                                         long_press=0;
                                         degrees=3;
                                         key_wkup=degrees;
                                         er=0;
                                 }
                                else//短按
                                 {                       
                                         ++degrees;
                                         if(degrees>=3) degrees=1;
                                         key_wkup=degrees;
                                 }
                             fd=0;
                            cg=0;
                            klj=0;
                    }
               else   fd=0;
       
          }
}

void KEY_Interrupt_MID(void)
{
    static u8 fd0=0;
    static u8 cg0=0;       
    static u8 klj0=0;       
    if(MID==0)
         {
               if(!cg0)
                        {
                            fd0++;
                                if(fd0==4) klj0=1;
                        }
         }
    else
         {
               if(klj0)
                    {
                    key_mid=1;
                        fd0=0;
                        cg0=0;
                                klj0=0;                          
                    }
               else   fd0=0;
       
         }
}


                                                                                                               time.c
void TIM6_IRQHandler(void)   //TIM6中断
{
        static u16 time_add=0;
        static u16 time_design=2;
        static u8 beep_change_state=1;
        u16 sfdb=0;
        if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
         {
                        TIM_ClearITPendingBit(TIM6, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源
                        time_start=1;
                        KEY_Interrupt_WK_UP();//
                        KEY_Interrupt_MID();               
         }
}

                                           main.c
#include "sys.h"
#include "delay.h"
#include "key.h"
#include "timer.h"

void KEY_Result_deal(void)//只能放到每次检测完按键的后面执行,太前面的话
{
        u8 i=0;
        if(key_wkup==1)//正常态
         {       
                 key_wkup=0;
                 //可加任务或命令 LED0=!LED0;
         }
        else if(key_wkup==2)//按下
         {
                  key_wkup=0;
              //可加任务或命令
             
         }               
        else if(key_wkup==3)//长按
         {               
                 key_wkup=0;
                 //可加任务或命令
         }
       
        if(key_mid==1)
    {
                key_mid=0;
                 //可加任务或命令
        }


}       

int main(void)
{  
        u8 hjk=0;       
        delay_init();                     //延时函数初始化          
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级       
        KEY_Init();                  //初始化与按键连接的硬件接口
        TIM6_Int_Init(999,719);//10Khz的计数频率,计数到10ms
        while(1)
         {       
              if(time_start==1)
                  {
                      switch(time_js)
                          {
                                  case 0:   
                                             ;
                                       break;                                 
                                  case 1:   
                                                ;
                                       break;
                                  case 2:   
                                        ;                                                  
                                       break;
                                  case 3:   
                                             
                                                  ;
                                       break;
                                  case 4:   
                                                   hjk++;       
                                                        if(hjk>=2)        //100ms
                                                        {
                                                           hjk=0;
                                                           KEY_Result_deal();
                                                          
                                                        }
                                       break;
                                  deault:break;
                                  
                      }
                     time_start=0;
                  }
     }
}

具体细节请下载附件,仅供参考,不当之处多指教!
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。