OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 356|回复: 2

分享一个按键驱动代码支持单击、长按、组合键

[复制链接]

13

主题

102

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
350
金钱
350
注册时间
2018-11-2
在线时间
78 小时
发表于 2024-8-5 18:15:32 | 显示全部楼层 |阅读模式
看了好多按键驱动感觉写的都差点意思,没有完全脱离底层也不好移植。所以我觉得有必要分享一下我这个按键驱动,我这个按键驱动基于面向对象的理念十分方便扩展,无阻塞效率高不会影响其他代码执行。我在电路城和21ic都分享过,今天再分享一遍。
C文件:

#include "Key.h"

Key_Para Key_1,Key_2,Key_3,Key_4,Key_5,Key_6;
volatile uint16_t Key_Phy_Num=0;  //Key_Phy_Num每一个bit代表一个按键的状态
volatile uint8_t Key_Pulse_Num=0;//当某一个按键从按下到弹起的过程中(非长按)始终只有该按键被操作,则Key_Pulse_Num被修改为该键的序号
volatile uint16_t Key_LP_Num=0;   //Key_LP_Num每一个bit代表一个按键的长按状态
uint8_t KeyCom=0;//组合键是否出现

static void Key_Num_Read(Key_Para* Key,uint16_t KPN,uint8_t Pulse,uint8_t Key_Pin)
{
  if(Key_Pin == 0)
    {
        Key->KOC_EN=0;//按键按下立即清除(松开)计数
        if(Key->KeyCloseCount > KEY_PRESS_TIME)
        {
            /*消抖方法为检测到按键被(持续)按下超过20ms*/
            Key_Phy_Num|=KPN;//消抖完毕后记录被按下的按键的键值
            if(Key->KeyCloseCount > KEY_LONG_PRESS_TIME)
            {
                /*检测到按键被(持续)按下超过3秒*/
                Key->KEY_LONG_PRESS=KPL_ENABLE;
        Key_LP_Num|=KPN;
        Key->KCC_EN=0;
            }
      else
      {
        /*时间不够启动计数*/
        Key->KCC_EN=1;
      }
        }
        else
        {
            /*时间不够启动计数*/
            Key->KCC_EN=1;
        }
    }
    else
    {
        Key->KCC_EN=0;//按键松开立即清除(按下)计数
        if(Key->KeyOpenCount > KEY_PRESS_TIME)
        {
      if((Key_Phy_Num==KPN)&&(KeyCom==0)&&(Key->KEY_LONG_PRESS!=KPL_ENABLE))
      {
        //按键被按下过&&非长按&&不是在组合键周期,该按键释放时发出生命周期为直到被读取或者直到有新按键被按下的脉冲
        Key_Pulse_Num=Pulse;
      }
      //清除该位
      Key_Phy_Num&=(~KPN);
      Key_LP_Num&=(~KPN);
            /*检测到(持续)松开20ms*/
            Key->KEY_LONG_PRESS=KPL_DISABLE;
      Key->KOC_EN=0;
        }
        else
        {
            Key->KOC_EN=1;
        }
    }
}
/********************************************************/
static void Key_Count(Key_Para *Key)
{
    if(Key->KOC_EN==0)
    {
        Key->KeyOpenCount=0;
    }
    else if(Key->KeyOpenCount>=5000)
    {
        Key->KeyOpenCount=5000;
    }
    else
    {
        Key->KeyOpenCount++;
    }

    if(Key->KCC_EN==0)
    {
        Key->KeyCloseCount=0;
    }
    else if(Key->KeyCloseCount>=5000)
    {
        Key->KeyCloseCount=5000;
    }
    else
    {
        Key->KeyCloseCount++;
    }
}
/********************************************************/
void Clear_Key_Pulse_Num(void)
{
    Key_Pulse_Num=0;
}
/********************************************************/
void KeyCount_Run(void)
{
    Key_Count(&Key_1);
    Key_Count(&Key_2);
    Key_Count(&Key_3);
    Key_Count(&Key_4);
//    Key_Count(&Key_5);
//    Key_Count(&Key_6);
}
/********************************************************/
static void Recognition_KeyCombination(void)
{
    uint8_t i=0,j=0;
    uint16_t Data=0;
   
    Data=Key_Phy_Num;
    for(i=0;i<16;i++)
    {
        if(Data&0x8000)
        {
            j++;
        }
        Data<<=1;
    }
    /*发现多个bit为1,那指定多个按键按下了*/
    if(j>1)
    {
        KeyCom=1;
    }
    /*一切归于平静,又是一个因果循环*/
    if(Key_Phy_Num==0x0)
    {
        KeyCom=0;
    }
}
/********************************************************/
void Key_Scan(void)
{      
  Recognition_KeyCombination();
   
    Key_Num_Read(&Key_1,(uint16_t)0x0001   ,1,KEY_PIN1);
    Key_Num_Read(&Key_2,(uint16_t)0x0001<<1,2,KEY_PIN2);
    Key_Num_Read(&Key_3,(uint16_t)0x0001<<2,3,KEY_PIN3);
    Key_Num_Read(&Key_4,(uint16_t)0x0001<<3,4,KEY_PIN4);
//    Key_Num_Read(&Key_5,(uint16_t)0x0001<<4,5,KEY_PIN5);
//    Key_Num_Read(&Key_6,(uint16_t)0x0001<<5,6,KEY_PIN6);
}

H文件:
#ifndef __Key_H
#define __Key_H

#include "gpio.h"
#include "Header.h"

#define KEY_PIN1 PCin(13)
#define KEY_PIN2 PAin(0)
#define KEY_PIN3 PFin(1)
#define KEY_PIN4 PFin(0)
//#define KEY_PIN5 PAin(5)
//#define KEY_PIN6 PAin(6)

#define KEY_PRESS_TIME 20//消抖常数
#define KEY_LONG_PRESS_TIME 1000//单个按键长按阈值1s

enum KEYPULSENUM
{
    KPN_OK=1,
    KPN_LEFT,
    KPN_UP,
    KPN_DOWN,
    KPN_RIGHT,
    KPN_ESC
};
/*通过读取这三个变量即可获得按键的单按、长按和组合键信息*/
extern volatile uint16_t Key_Phy_Num;
extern volatile uint8_t Key_Pulse_Num;
extern volatile uint16_t Key_LP_Num;

typedef enum
{
    KPL_DISABLE=0,
    KPL_ENABLE
}K_L_P;//按键的长按状态
typedef struct
{
    K_L_P KEY_LONG_PRESS;
    uint16_t KeyOpenCount;
    uint8_t KOC_EN;
    uint16_t KeyCloseCount;
    uint8_t KCC_EN;
}Key_Para;

extern Key_Para Key_1,Key_2,Key_3,Key_4,Key_5,Key_6;

void Clear_Key_Pulse_Num(void);//当读取完Key_Pulse_Num后调用
void KeyCount_Run(void);//在1ms滴答里调用
void Key_Scan(void);//大循环或者滴答里边都行

#endif



正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

13

主题

102

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
350
金钱
350
注册时间
2018-11-2
在线时间
78 小时
 楼主| 发表于 2024-8-5 18:18:02 | 显示全部楼层
这是矩阵键盘驱动
H文件:
#ifndef __Key_matrix_H
#define __Key_matrix_H

#include "Header.h"

/*务必把这4个输出IO设置为上拉输入*/
#define KEY_HOR1 PAin(7)
#define KEY_HOR2 PAin(6)
#define KEY_HOR3 PAin(5)
#define KEY_HOR4 PAin(4)
/*务必把这4个输出IO设置为开漏*/
#define KEY_LON1 PBout(0)
#define KEY_LON2 PCout(5)
#define KEY_LON3 PCout(4)
//#define KEY_LON4 PCout(4)

#define KEY_PRESS_TIME 20//消抖常数
#define KEY_LONG_PRESS_TIME 1000//单个按键长按阈值1s
/*通过读取(只读)这三个变量即可获得按键的单按、长按和组合键信息*/
extern volatile uint16_t Key_Phy_Num;
extern volatile uint8_t Key_Pulse_Num;
extern volatile uint16_t Key_LP_Num;

typedef enum
{
    KPL_DISABLE=0,
    KPL_ENABLE
}K_L_P;//按键的长按状态
typedef struct
{
    K_L_P KEY_LONG_PRESS;
    uint16_t KeyOpenCount;
    uint8_t KOC_EN;
    uint16_t KeyCloseCount;
    uint8_t KCC_EN;
}Key_Para;

extern Key_Para Key_1,Key_2,Key_3,Key_4,Key_5,Key_6,Key_7,Key_8,
                Key_9,Key_10,Key_11,Key_12,Key_13,Key_14,Key_15,Key_16;

void Clear_Key_Pulse_Num(void);//当读取完Key_Pulse_Num后调用
void KeyCount_Run(void);//在1ms滴答里调用
void Key_Scan(void);//大循环或者滴答里边都行

#endif

C文件:
#include "Key_matrix.h"

Key_Para Key_1,Key_2,Key_3,Key_4,Key_5,Key_6,Key_7,Key_8,
         Key_9,Key_10,Key_11,Key_12,Key_13,Key_14,Key_15,Key_16;
volatile uint16_t Key_Phy_Num=0;  //Key_Phy_Num每一个bit代表一个按键的状态
volatile uint8_t Key_Pulse_Num=0;//当某一个按键从按下到弹起的过程中(非长按)始终只有该按键被操作,则Key_Pulse_Num被修改为该键的序号
volatile uint16_t Key_LP_Num=0;   //Key_LP_Num每一个bit代表一个按键的长按状态
uint8_t KeyCom=0;//组合键是否出现

static void Key_Num_Read(Key_Para* Key,uint16_t KPN,uint8_t Pulse,uint8_t Key_Hor)
{
  if(Key_Hor == 0)
    {
        Key->KOC_EN=0;//按键按下立即清除(松开)计数
        if(Key->KeyCloseCount > KEY_PRESS_TIME)
        {
            /*消抖方法为检测到按键被(持续)按下超过20ms*/
            Key_Phy_Num|=KPN;//消抖完毕后记录被按下的按键的键值
            if(Key->KeyCloseCount > KEY_LONG_PRESS_TIME)
            {
                /*检测到按键被(持续)按下超过3秒*/
                Key->KEY_LONG_PRESS=KPL_ENABLE;
        Key_LP_Num|=KPN;
        Key->KCC_EN=0;
            }
      else
      {
        /*时间不够启动计数*/
        Key->KCC_EN=1;
      }
        }
        else
        {
            /*时间不够启动计数*/
            Key->KCC_EN=1;
        }
    }
    else
    {
        Key->KCC_EN=0;//按键松开立即清除(按下)计数
        if(Key->KeyOpenCount > KEY_PRESS_TIME)
        {
      if((Key_Phy_Num==KPN)&&(KeyCom==0)&&(Key->KEY_LONG_PRESS!=KPL_ENABLE))
      {
        //按键被按下过&&非长按&&不是在组合键周期,该按键释放时发出生命周期为直到被读取或者直到有新按键被按下的脉冲
        Key_Pulse_Num=Pulse;
      }
      //清除该位
      Key_Phy_Num&=(~KPN);
      Key_LP_Num&=(~KPN);
            /*检测到(持续)松开20ms*/
            Key->KEY_LONG_PRESS=KPL_DISABLE;
      Key->KOC_EN=0;
        }
        else
        {
            Key->KOC_EN=1;
        }
    }
}
/********************************************************/
static void Key_Count(Key_Para *Key)
{
    if(Key->KOC_EN==0)
    {
        Key->KeyOpenCount=0;
    }
    else if(Key->KeyOpenCount>=50000)
    {
        Key->KeyOpenCount=50000;
    }
    else
    {
        Key->KeyOpenCount++;
    }

    if(Key->KCC_EN==0)
    {
        Key->KeyCloseCount=0;
    }
    else if(Key->KeyCloseCount>=50000)
    {
        Key->KeyCloseCount=50000;
    }
    else
    {
        Key->KeyCloseCount++;
    }
}
/********************************************************/
void Clear_Key_Pulse_Num(void)
{
    Key_Pulse_Num=0;
}
/********************************************************/
void KeyCount_Run(void)
{
    Key_Count(&Key_1);
    Key_Count(&Key_2);
    Key_Count(&Key_3);
    Key_Count(&Key_4);
    Key_Count(&Key_5);
    Key_Count(&Key_6);
    Key_Count(&Key_7);
    Key_Count(&Key_8);
    Key_Count(&Key_9);
    Key_Count(&Key_10);
    Key_Count(&Key_11);
    Key_Count(&Key_12);
//    Key_Count(&Key_13);
//    Key_Count(&Key_14);
//    Key_Count(&Key_15);
//    Key_Count(&Key_16);
}
/********************************************************/
static void Recognition_KeyCombination(void)
{
    uint8_t i=0,j=0;
    uint16_t Data=0;
   
    Data=Key_Phy_Num;
    for(i=0;i<16;i++)
    {
        if(Data&0x8000)
        {
            j++;
        }
        Data<<=1;
    }
    /*发现多个bit为1,那指定多个按键按下了*/
    if(j>1)
    {
        KeyCom=1;
    }
    /*一切归于平静,又是一个因果循环*/
    if(Key_Phy_Num==0x0)
    {
        KeyCom=0;
    }
}
/********************************************************/
void Key_Scan(void)
{   
  static uint8_t ScanCount=0;
  
  Recognition_KeyCombination();
    switch(ScanCount)
  {
    case 0:
    {
      KEY_LON1=0;KEY_LON2=1;KEY_LON3=1;//KEY_LON4=1;
      Key_Num_Read(&Key_1,(uint16_t)0x0001   ,1,KEY_HOR1);
      Key_Num_Read(&Key_2,(uint16_t)0x0001<<1,2,KEY_HOR2);
      Key_Num_Read(&Key_3,(uint16_t)0x0001<<2,3,KEY_HOR3);
      Key_Num_Read(&Key_4,(uint16_t)0x0001<<3,4,KEY_HOR4);
            KEY_LON1=1;KEY_LON2=0;KEY_LON3=1;//KEY_LON4=1;
      ScanCount++;
    }break;
    case 1:
    {
      KEY_LON1=1;KEY_LON2=0;KEY_LON3=1;//KEY_LON4=1;
      Key_Num_Read(&Key_5,(uint16_t)0x0001<<4,5,KEY_HOR1);
      Key_Num_Read(&Key_6,(uint16_t)0x0001<<5,6,KEY_HOR2);
      Key_Num_Read(&Key_7,(uint16_t)0x0001<<6,7,KEY_HOR3);
      Key_Num_Read(&Key_8,(uint16_t)0x0001<<7,8,KEY_HOR4);
            KEY_LON1=1;KEY_LON2=1;KEY_LON3=0;//KEY_LON4=1;
      ScanCount++;
    }break;
    case 2:
    {
      KEY_LON1=1;KEY_LON2=1;KEY_LON3=0;//KEY_LON4=1;
      Key_Num_Read(&Key_9 ,(uint16_t)0x0001<<8 , 9,KEY_HOR1);
      Key_Num_Read(&Key_10,(uint16_t)0x0001<<9 ,10,KEY_HOR2);
      Key_Num_Read(&Key_11,(uint16_t)0x0001<<10,11,KEY_HOR3);
      Key_Num_Read(&Key_12,(uint16_t)0x0001<<11,12,KEY_HOR4);
//            KEY_LON1=1;KEY_LON2=1;KEY_LON3=1;KEY_LON4=0;
            KEY_LON1=0;KEY_LON2=1;KEY_LON3=1;
//      ScanCount++;
            ScanCount=0;
    }break;
//    case 3:
//    {
//      KEY_LON1=1;KEY_LON2=1;KEY_LON3=1;KEY_LON4=0;
//      Key_Num_Read(&Key_13,(uint16_t)0x0001<<12,13,KEY_HOR1);
//      Key_Num_Read(&Key_14,(uint16_t)0x0001<<13,14,KEY_HOR2);
//      Key_Num_Read(&Key_15,(uint16_t)0x0001<<14,15,KEY_HOR3);
//      Key_Num_Read(&Key_16,(uint16_t)0x0001<<15,16,KEY_HOR4);
//            KEY_LON1=0;KEY_LON2=1;KEY_LON3=1;KEY_LON4=1;
//      ScanCount=0;
//    }break;
    default:
    {
      ScanCount=0;
    }break;
  }
}



回复 支持 反对

使用道具 举报

1

主题

13

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1895
金钱
1895
注册时间
2017-11-28
在线时间
140 小时
发表于 2024-8-6 10:21:43 | 显示全部楼层
学习一下,谢谢分享
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 07:15

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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