按键扫描与多功能按键

一开始用的正点原子的按键扫描,代码简单,但用于多功能按键的实现就有点不够用了,后来我自己写了一下,新手刚写完,望多多指教。谢谢。

  1. 按键对应IO口.h(写在对应H文件里)

#define KEY_mid   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)
#define KEY_up    GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_11)
#define KEY_down  GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)
#define KEY_left  GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)
#define KEY_right GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)
  1. 按键扫描结构体.h

typedef struct
{
    uchar PressTime;
    uchar LooseTime;
    uchar press;//按下
    uchar loose;//一直松开
    uchar PressLoose;//按下松开有效
    uchar ContiPress;
} key_stat;

typedef struct
{
    uchar MPressTime;
    uchar MLooseTime;
    uchar MShortPress;//短按下
    uchar MLongPress;//长按下
    uchar Loose;//一直松开
    uchar MPressLoose;//按下松开有效
    uchar MPressNumber;
    uchar MContiPress;//连按与否
} key_multi;

extern key_stat Key_Up;
extern key_stat Key_Down;
extern key_multi MKey_Right;
extern key_multi MKey_Left;

extern key_multi MKey_Right;
extern key_multi MKey_Left;

void Key_Scan(uint8_t mode);
void Key_Multi(void);
  1. 运用结构体.c

key_stat Key_Up;
key_stat Key_Down;
key_stat Key_Left;
key_stat Key_Right;
key_stat Key_Mid;

key_multi MKey_Right;
key_multi MKey_Left;

//配置按键GPIO
  1. 按键扫描函数.c

void Key_Scan(uint8_t mode)//单个按键扫描
{
    uint8_t Key_dely = 5;//我这个延时是在状态机里,每过1ms扫描.
                         //延时消抖根据自己的情况设定,延时函数或者定时器
    //上按键
    if (mode)//连按与按下一次有效
    {
        Key_Up.ContiPress = 0;//mode=0,按下为1,松开为0,按键按下只执行一次。
    }                         //也就是松开才执行再次进入,当mode=1,则按下一直有效。
                              //比如通讯这块,需要当按键按下只发一次通讯,不连续发送
    if (KEY_up == 0)//按键按下
    {
        if (!Key_Up.ContiPress)//连按与按下一次有效
        {                    
            Key_Up.LooseTime = 0;
            Key_Up.PressTime++;
            if (Key_Up.PressTime > Key_dely)//消抖
            {
                    Key_Up.press = 1;
                    Key_Up.loose=2;//松开
                    Key_Up.ContiPress=1;
                    Key_Up.PressLoose=0;//按下松开
                    Key_Up.PressTime = 0;
            }
        }
    }
    else
    {
        Key_Up.LooseTime++;
        if (Key_Up.LooseTime > Key_dely)
        {
                Key_Up.press = 0;
                Key_Up.ContiPress = 0;
                Key_Up.LooseTime=0;    
                Key_Up.PressTime = 0;                            
                if(Key_Up.loose==2)//需要按下松开才进入,没有要求的可以删除
                {                  //有些时候需要按松开一次有效,比如松开发一次通讯
                        Key_Up.PressLoose=1;
                        Key_Up.loose=0;                                         
                }
                else//松开一直有效
                {
                        Key_Up.loose=1; 
                }
        }                
    }

    //下按键
    if (mode)
    {
        Key_Down.ContiPress = 0;
    }
        if (KEY_down == 0)
        {
            if (!Key_Down.ContiPress)
            {                    
                Key_Down.PressTime++;
                Key_Down.LooseTime = 0;
                if (Key_Down.PressTime > Key_dely)
                {
                        Key_Down.press = 1;
                        Key_Down.loose=2;
                      Key_Down.ContiPress=1;
                        Key_Down.PressTime = 0;
                }
            }
        }
        else
        {
                Key_Down.LooseTime++;
                if (Key_Down.LooseTime > Key_dely)
                {
                        Key_Down.press = 0;
                        Key_Down.loose=1;
                        Key_Down.ContiPress = 0;
                        Key_Down.LooseTime = 0;    
                        Key_Down.PressTime = 0;                            
                }
        }
}
  1. 长短按键,组合按键,连击按键.c

void Key_Multi(void)
{
    //长短按
        if(KEY_right==0)
        {
            MKey_Right.MLooseTime=0;
            MKey_Right.MPressTime++;
            if(MKey_Right.MPressTime>200)
            {
                MKey_Right.MLongPress=1;
                MKey_Right.Loose=0;
                MKey_Right.MPressTime=0;
            }
        }
        else
        {
            MKey_Right.MLooseTime++;
            if(MKey_Right.MLooseTime>5)
            {
                    if((MKey_Right.MPressTime>5)&&(!MKey_Right.MLongPress))
                    {
                        MKey_Right.MShortPress=1;
                    }
          else
                    {                        
                      MKey_Right.MShortPress=0;
                        MKey_Right.Loose=0;
                    }
                    MKey_Right.MLongPress=0;
                    MKey_Right.MLooseTime=0;
                    MKey_Right.MPressTime=0;
            }
        }
        
        //组合按键
       /*直接用按键扫描组合*/
        
        //按键次数,连按两次,三次(MPressNumber)等
        if(KEY_left==0)
        {
            if(!MKey_Left.MContiPress)
            {                
                MKey_Left.MLooseTime=0;
                MKey_Left.MPressTime++;
                if(MKey_Left.MPressTime>5)
                {
                    MKey_Left.MPressNumber++;
                    MKey_Left.MContiPress=1;
                    MKey_Left.MPressTime=0;
                }
            }
        }
        else
        {
            MKey_Left.MLooseTime++;
            if(MKey_Left.MLooseTime>5)
            {
                MKey_Left.MPressTime=0;
                MKey_Left.MContiPress=0;
                if(MKey_Left.MLooseTime>100)
                {
                    MKey_Left.MLooseTime=0;
                    MKey_Left.MPressNumber=0;
                }
            }
        }
}
  1. 应用

//主函数里调用    
    Key_Scan(0);
    if (Key_Up.press)//上按键按下
    {
        //执行事件1
    }
    else//上按键松开
    {
       //执行事件2
    } 
   
//多功能按键
//长按
Key_Multi();
if(MKey_Right.MLongPress)//长按
{
    //执行事件3
}
else if(MKey_Right.MShortPress)//短按
{
   //执行事件4
}
else //松开
{
    //执行事件5
}
//组合
Key_Scan(0);
if (Key_Up.press&&Key_Down.press)
{
   //执行事件6
}    
//连击
if (MKey_Left.MPressNumber==3)//连击三下有效
{
   //执行事件7
}        

注意

消抖我依赖于状态机里执行的,每隔1ms扫描一次,如果没有用状态机,一般用延时函数,定时器等来实现消抖,长短,连击按键的时间。