OpenEdv-开源电子网

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

STM32 独立按键扫描功能大全-支持连击、组合连击、任意连击

[复制链接]

2

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
63
金钱
63
注册时间
2019-11-13
在线时间
21 小时
发表于 2020-6-29 16:07:04 | 显示全部楼层 |阅读模式

## STM32 独立按键扫描功能大全-支持连击、组合连击、任意连击


本人刚学习STM32开发,最近看了硬汉的按键检测程序,进行了架构的深度优化,所以跟大家分享一下:本人使用的STM32F103,有6个独立的按键;A、B、C、D、OK、Power,目前实现的功能如下:

1:单键短按,长按,连发,双击,3连击。。。。。最多不限制;

2:各种组合按键,组合按键的短按,长按,连发,组合按键的双击,连击;

3:这些功能都可以随意配置;

性能测试,STM32使用内部时钟,64M,有按键的时候,按键扫描函数执行时间是12us;

程序先不跟大家分享了,分享一下我对按键扫描的理解和实现的大概流程:

*********************************************************************************************************
*        虚拟按键扫描功能说明:V1.0-2019.12.30

按键定义:单键/多键;单击/组合(连击);短按/长按;连发;

注:对于应用层来说,组合也是单击,不需要标识出来。A和B组合生效,可以单键A和单键B组合,也可以是多键AB单击;

定义说明:
1: 单键/多键:只有1个按键变化;多键:2个及以上的按键同变化(变化的时间接近即可,底层滤波自动处理,总线滤波参数控制);
    后文中提到的“按键”,包括单键或者多键,多键也可以称之为一个按键!!!,大家认为的组合按键的双击,我们称之为多键连击;
    “不同的按键”不能有相同的部分,如单键A,和多键AB不是不相同的按键;

2: 单击:设定的时间内,有按键(单键或多键)按下1次;如不支持连击/组合,单击:有按键(单键或多键)按下1次;

3: 组合:设定的时间内,有多个不同的按键(单键或者多键)按下1次;单键和单键,单键和多键,多键和多键都可以组合;

4: 连击:设定的时间内,相同的按键(单键或多键)按下2次及以上;

5: 长按:单击/组合/连击发生后,并且持续稳定的时间超过设定的时间阈值;

6: 连发:单击/组合/连击长按之后,按照设定的周期,不断的产生单击/组合/连击事件;

注:设定的时间内,要么发生连击,要么发生不同的按键进行组合,两者为互斥事件;
    按照时间顺序排列,有几个按键(单键还是多键),是单击还是组合还是连击,稳定之后是长按,还是短按,长按之后是否连发;


按键扫描流程:
1: 将物理按键映射到逻辑按键上,多个按键映射成并口的数据通道,按键检测,转换成采集通道上的数据。

2:  某一个数据线上不用独立滤波,而是进行总线数据滤波。也就是多个按键一起滤波;

3: 组合/连击:总线上有数据时(有按键按下后),开始定时,发生数据变化时(又有按键按下),判断是组合,还是连击,
    连击是只指相同的按键,组合是指不同的按键;    连击时,按键次数增加,组合时,有效按键个数增加,组合和连击,只能发生一个;

4: 连击时,可以复位定时器,组合时,可以不复位,也可以复位,正常不需要复位;

**********************************************************************`在这里插入代码片`***********************************
/*******************************************************************************
* Copyright (C), 1993-2012, Liming Comm. Co., Ltd.
* @file    :RM_KEY.c
* @brief   :虚拟按键扫描
* @Author  :czm_hyt@163.com
* @version :V1.0
* @date    :2019-11-15
* @time    :20:22:35
*

******************************************************************************
* 函数列表   
*            
* 1. Enter the name of the  first function
* 2. Enter the name of the second function
******************************************************************************
* 修改历史   
*
* 2019-11-15                 czm                  建立文件     
*******************************************************************************/

/*
*********************************************************************************************************
*   虚拟按键扫描功能说明:V1.0-2019.12.30

按键定义:单键/多键;单击/组合(连击);短按/长按;连发;

注:对于应用层来说,组合也是单击,不需要标识出来。A和B组合生效,可以单键A和单键B组合,也可以是多键AB单击;

定义说明:
1: 单键/多键:只有1个按键变化;多键:2个及以上的按键同变化(变化的时间接近即可,底层滤波自动处理,总线滤波参数控制);
    后文中提到的“按键”,包括单键或者多键,多键也可以称之为一个按键!!!,大家认为的组合按键的双击,我们称之为多键连击;
    “不同的按键”不能有相同的部分,如单键A,和多键AB不是不相同的按键;

2: 单击:设定的时间内,有按键(单键或多键)按下1次;如不支持连击/组合,单击:有按键(单键或多键)按下1次;

3: 组合:设定的时间内,有多个不同的按键(单键或者多键)按下1次;单键和单键,单键和多键,多键和多键都可以组合;

4: 连击:设定的时间内,相同的按键(单键或多键)按下2次及以上;

5: 长按:单击/组合/连击发生后,并且持续稳定的时间超过设定的时间阈值;

6: 连发:单击/组合/连击长按之后,按照设定的周期,不断的产生单击/组合/连击事件;

注:设定的时间内,要么发生连击,要么发生不同的按键进行组合,两者为互斥事件;
    按照时间顺序排列,有几个按键(单键还是多键),是单击还是组合还是连击,稳定之后是长按,还是短按,长按之后是否连发;


按键扫描流程:
1: 将物理按键映射到逻辑按键上,多个按键映射成并口的数据通道,按键检测,转换成采集通道上的数据。

2:  某一个数据线上不用独立滤波,而是进行总线数据滤波。

3: 组合/连击:总线上有数据时(有按键按下后),开始定时,发生数据变化时(又有按键按下),判断是组合,还是连击,
    连击是只指相同的按键,组合是指不同的按键;    连击时,按键次数增加,组合时,有效按键个数增加,组合和连击,只能发生一个;

4: 连击时,可以复位定时器,组合时,可以不复位,也可以复位,正常不需要复位;

*********************************************************************************************************
*/

#include "RM_KEY.h"

static RM_KEY_CONFIG_T s_RM_KEY_CFG = {0};

// RM 硬件键实时运行状态
static RM_KEY_RUN_DATA_T s_tRmKeyRunData = {0};

//  硬件按键GPIO和PIN定义
static const RM_KEY_GPIO_DEF_T s_atRmKeyGpioDef[RM_HARD_KEY_NUM] =
{
    // 按键都是高电平有效 TRUE
    {GPIOA, GPIO_Pin_4,  TRUE},     // KEY A    按键 PA4
    {GPIOC, GPIO_Pin_0,  TRUE},     // KEY B    按键 PC0
    {GPIOC, GPIO_Pin_15, TRUE},     // KEY C    按键 PC15
    {GPIOC, GPIO_Pin_5,  TRUE},     // KEY D    按键 PC5
    {GPIOC, GPIO_Pin_14, TRUE},     // KEY OK   按键 PC14
    {GPIOA, GPIO_Pin_0,  TRUE},     // Power    按键 PA0
};

/**
* @brief 虚拟按键转化表,将所有实体按键都转换成一个bit
*/
static const u8 s_auKeyBitDef[RM_HARD_KEY_NUM] =
{
    // 单按键定义 A、B、C、D、OK、Power
    0x01, 0x02, 0x04, 0x08, 0x010, 0x20,
    // 组合按键定义
};

// 可以用于LCD 没有操作时息屏 有操作时亮屏
///* 用于按键超时进入屏保 */
//static int32_t s_KeyTimeOutCount = 0;
//static uint8_t s_LcdOn = 1;



/**
* @brief   计算8位数据中1的个数
*
* @param [in] uData       需要计算的数据,8位
*
* @return  数据中1的个数
* @author  
* @since   trunk.00001
* @bug
*/
u8 Ones8(u8 uData)
{
    uData -= ((uData >> 1) & 0x55);
    uData  = (((uData >> 2) & 0x33) + (uData & 0x33));
    uData += (uData >> 4);

    return (uData & 0x0F);
}



/**
* @brief   判断单独按键管脚是否有效 对上屏蔽底层 对上TRUE就是有效,FALSE就是弹开
*
* @param [in] nKeyID       按键ID 0 ~ RM_HARD_KEY_NUM-1
*
* @return  TRUE:按键有效;FALSE:按键无效
* @author  
* @since   trunk.00001
* @bug
*/
static BOOL RM_KEY_IsActive(RM_KEY_ID_E nKeyID)
{
    BOOL bPinLevel;

    // 判断按键管脚IO电平 TRUE:高电平
    if (s_atRmKeyGpioDef[nKeyID].ptGpio->IDR & s_atRmKeyGpioDef[nKeyID].u2Pin)
    {
        bPinLevel = TRUE;
    }
    else
    {
        bPinLevel = FALSE;
    }

    if (bPinLevel == s_atRmKeyGpioDef[nKeyID].bActiveLevel)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }

}

// A和B如果几乎同时按下 滤波时间会增加2倍数,此时认为AB是同时按下的

/**
* @brief   更新按键管脚状态 带逻辑整体滤波 非物理
* @brief   将每一个实体按键转化成1 bit,便于虚拟按键映射
*
* @param [in] nKeyID       按键ID 0 ~ RM_HARD_KEY_NUM-1
*
* @return  TRUE:按键有效;FALSE:按键无效
* @author  
* @since   trunk.00001
* @bug
*/
// 需要优化
static void RM_KEY_Update(void)
{
    static u8 s_uRmKeyBusData   = 0;    // 上一次Key总线数据
    u8 uRmKeyBusData_New        = 0;    // 最新Key总线数据
    u8 uTemp                    = 0;


    // 按键按下,对应比特置1,物理按键转逻辑按键
    for (uTemp = 0; uTemp < RM_HARD_KEY_NUM; uTemp++)
    {   
        if (RM_KEY_IsActive(uTemp))
        {
            uRmKeyBusData_New |= s_auKeyBitDef[uTemp];
        }   
    }

    // 滤波的定义:相同状态持续设置的阈值 状态就生效
    if (s_uRmKeyBusData == uRmKeyBusData_New)     // 最新状态与上次的状态相同,计数判断稳定时间
    {
        if (s_RM_KEY_CFG.uFilterCtr > s_tRmKeyRunData.uFIlterCount)
        {
            s_tRmKeyRunData.uFIlterCount++;
        }        
        else if (s_RM_KEY_CFG.uFilterCtr == s_tRmKeyRunData.uFIlterCount)    // 稳定时间达到滤波阈值
        {
            s_RM_KEY_CFG.uRmKeyBusData  = uRmKeyBusData_New;    // 更新最新的按键状态
            s_RM_KEY_CFG.uKeyBusDataCnt = Ones8(s_RM_KEY_CFG.uRmKeyBusData);
            s_tRmKeyRunData.uFIlterCount++;     // 只更新一次
        }
        // 按键总线数据稳定后,也要判断是否与记录的状态同步
        else if (uRmKeyBusData_New != s_RM_KEY_CFG.uRmKeyBusData)
        {
            s_tRmKeyRunData.uFIlterCount = 0;   // 状态不同步 启动滤波更新
        }
    }
    else    // 状态只要发生变化,就启动一次状态更新
    {
        s_tRmKeyRunData.uFIlterCount = 0;           // 启动一次状态更新 带滤波      
        s_uRmKeyBusData = uRmKeyBusData_New;        // 状态发生变化 更新保存
    }
}


/**
* @brief   将1个键值压入按键FIFO缓冲区。可用于模拟一个按键。
*
* @param [in] nKeyMsgCode       按键代码
*
* @return 无
* @author  
* @since   trunk.00001
* @bug
*/
void RM_KEY_PutKeyMsg(u16 u2KeyMsgCode)
{
    // 有按键按下 可以开屏幕背光 进行省点相关操作

    s_tRmKeyRunData.u2MsgBuf[s_tRmKeyRunData.uWriteAddr] = u2KeyMsgCode;

    if (RM_KEY_MSG_FIFO_SIZE <= (++s_tRmKeyRunData.uWriteAddr))
    {
        s_tRmKeyRunData.uWriteAddr = 0;
    }

//    s_KeyTimeOutCount = GetSleepTimeMinute() * 60 * 100u;  /* 10ms单位 */
}

//{
////    /* 屏幕熄灭阶段,丢弃唤醒键 */
////    if (s_LcdOn == 0)
////    {        
////        u8 key;
////        
////        key = ((_KeyCode - 1) % KEY_MSG_STEP) + 1;
////        if (key == KEY_1_UP || key == KEY_1_LONG_UP)
////        {
////            s_LcdOn = 1;
////        }        
//////        LCD_DispOn();     
//////        LCD_SetBackLight(BRIGHT_DEFAULT);   /* 打开背光 */            
////        return;
////    }
//   
//    s_RM_KEY_CFG.uMsgBuf[s_RM_KEY_CFG.uWriteAddr] = nKeyMsgCode;

//    if (RM_KEY_MSG_FIFO_SIZE <= (++s_RM_KEY_CFG.uWriteAddr))
//    {
//        s_RM_KEY_CFG.uWriteAddr = 0;
//    }
//            
////    s_KeyTimeOutCount = GetSleepTimeMinute() * 60 * 100u;  /* 10ms单位 */
//}


/**
* @brief   从按键FIFO缓冲区读取一个键值。
*
* @param [in] 无
*
* @return 按键代码
* @author  
* @since   trunk.00001
* @bug
*/
u16 RM_KEY_GetKeyMsgA(void)
{
    u16 u2MsgCode = 0x0000;

    // 判断是否有按键消息
    if (s_tRmKeyRunData.uWriteAddr != s_tRmKeyRunData.uReadAddrA)
    {
        u2MsgCode = s_tRmKeyRunData.u2MsgBuf[s_tRmKeyRunData.uReadAddrA];

        // 读消息地址指针保护
        if (RM_KEY_MSG_FIFO_SIZE <= (++s_tRmKeyRunData.uReadAddrA))
        {
            s_tRmKeyRunData.uReadAddrA = 0;
        }
    }

    return u2MsgCode;
}

/**
* @brief   从按键FIFO缓冲区读取一个键值。
* @brief   两个函数调用不冲突?
*
* @param [in] 无
*
* @return 按键代码
* @author  
* @since   trunk.00001
* @bug
*/
u16 RM_KEY_GetKeyMsgB(void)
{
    u16 u2MsgCode = 0x0000;

    // 判断是否有按键消息
    if (s_tRmKeyRunData.uWriteAddr != s_tRmKeyRunData.uReadAddrB)
    {
        // 有消息,则读取消息
        u2MsgCode = s_tRmKeyRunData.u2MsgBuf[s_tRmKeyRunData.uReadAddrB];

        // 读消息地址指针保护
        if (RM_KEY_MSG_FIFO_SIZE <= (++s_tRmKeyRunData.uReadAddrB))
        {
            s_tRmKeyRunData.uReadAddrB = 0;
        }
    }
    return u2MsgCode;
}


/**
* @brief   读取最新按键状态
*
* @param [in] 无
*
* @return 当前最新按键数据
* @author  
* @since   trunk.00001
* @bug
*/
u8 RM_KEY_GetKeyState(void)
{
    return s_RM_KEY_CFG.uRmKeyBusData;
}


/**
* @brief   读取按键的状态
*
* @param [in] nKeyID       // 按键逻辑ID
* @param [in] uCombCtr     // 组合、连击
* @param [in] uLongCtr     // 长按
* @param [in] uRepeatCtr   // 连发
*
* @return 无
* @author  
* @since   trunk.00001
* @bug
*/
void RM_KEY_SetKeyParam(u8 uFilterCtr, u8 uCombCtr, u8 uClickCtr,  u8 uLongCtr, u8 uRepeatCtr, u8 uIdleCtr)
{
    s_RM_KEY_CFG.uFilterCtr = uFilterCtr;           // 按键底层总线滤波,按键接近时,可滤波成同时按下,既多键

    s_RM_KEY_CFG.uCombCtr   = uCombCtr;             // 组合、连击等待时间阈值 0 表示不支持,最大2.5s
    s_RM_KEY_CFG.uClickCtr  = uClickCtr & 0x07;     // 连击最大次数,必须支持组合/连击时才生效 最小值2 最大值7

    s_RM_KEY_CFG.uLongCtr   = uLongCtr;             // 长按计数器 等待阈值 0 表示不支持,最大2.5s
    s_RM_KEY_CFG.uRepeatCtr = uRepeatCtr;           // 长按连续发送阈值, 0 表示不支持,最大2.5s,必须支持长按时才生效

    s_RM_KEY_CFG.uIdleCtr   = uIdleCtr;             // 按键弹开后,如何进入IDLE模式,0:必须没有按键才能进入,其它:延时对应时间,强制进IDLE
}

/**
* @brief   设置按键默认初始化参数
*
* @param [in] 无
*
* @return 当前最新按键数据
* @author  
* @since   trunk.00001
* @bug
*/

void RM_KEY_SetKeyParamDefault(void)
{

    s_RM_KEY_CFG.uRmKeyBusData  = 0;
    s_RM_KEY_CFG.uKeyBusDataCnt = 0;

    s_RM_KEY_CFG.uFilterCtr = RM_KEY_FILTER_TIME;   // 按键底层总线滤波,按键接近时,可滤波成同时按下,既多键

    s_RM_KEY_CFG.uCombCtr   = RM_KEY_COMB_TIME;     // 组合、连击等待时间阈值 0 表示不支持,最大2.5s
    s_RM_KEY_CFG.uClickCtr  = RM_KEY_CLICK_CTR;     // 连击最大次数,必须支持组合/连击时才生效 最小值2 最大值7

    s_RM_KEY_CFG.uLongCtr   = RM_KEY_LONG_TIME;     // 长按计数器 等待阈值 0 表示不支持,最大2.5s
    s_RM_KEY_CFG.uRepeatCtr = RM_KEY_REPEAT_TIME;   // 长按连续发送阈值, 0 表示不支持,最大2.5s,必须支持长按时才生效

    s_RM_KEY_CFG.uClickCtr  = RM_KEY_IDLE_TIME;     // 单键或组合按键,最多连击的次数


    s_tRmKeyRunData.nGobleState   = 0;
    s_tRmKeyRunData.uRmKeyData    = 0;
    s_tRmKeyRunData.uRmKeyDataCnt = 0;

    s_tRmKeyRunData.uFIlterCount  = 0;

    s_tRmKeyRunData.uCombCnt   = 0;
    s_tRmKeyRunData.uClickCnt  = 0;

    s_tRmKeyRunData.uLongCnt   = 0;
    s_tRmKeyRunData.uRepeatCnt = 0;

    s_tRmKeyRunData.uIdleCnt   = 0;

    // 按键消息缓存区清零
    s_tRmKeyRunData.uWriteAddr = 0;
    s_tRmKeyRunData.uReadAddrA = 0;
    s_tRmKeyRunData.uReadAddrB = 0;


}



/**
* @brief   清空按键FIFO缓冲区
*
* @param [in] 无
*
* @return 无
* @author  
* @since   trunk.00001
* @bug
*/
void RM_KEY_ClearKeyMsg(void)
{
    s_tRmKeyRunData.uReadAddrA = s_tRmKeyRunData.uWriteAddr;
}


// 按键定义:单键/多键;单击/组合(连击);短按/长按;连发;
// 注:对于应用层来说,组合也是单击,不需要标识出来。A和B组合生效,可以单键A和单键B组合,也可以是多键AB单击;

/**
* @brief   读取最新按键状态
*
* @param [in] 无
*
* @return 当前最新按键数据
* @author  
* @since   trunk.00001
* @bug
*/
static void RM_KEY_Detect(void)
{
    static u8  s_uRmKeyBusData_Last = 0;    // 记录上一次按键总线上的数据
    static u16 s_u2CombLongFlag     = 0;    // 组合/连击 长按/连发 标志
    u8 uRmKeyBusData_Temp           = 0;

    switch (s_tRmKeyRunData.nGobleState)
    {
    case RM_KEY_STATE_IDLE:
        {        
            // 按键总线上有数据
            if (s_RM_KEY_CFG.uRmKeyBusData)
            {
                s_u2CombLongFlag            = 0;    // 下面马上要使用到 所以要先初始化
                s_tRmKeyRunData.uCombCnt    = 0;    // 组合/连击计数器
                s_tRmKeyRunData.uLongCnt    = 0;    // 长按计数器
                s_tRmKeyRunData.uRepeatCnt  = 0;    // 连发计数器

                // 记录第一个有效按键的总线数据 单键或多键
                s_tRmKeyRunData.uRmKeyData    = s_RM_KEY_CFG.uRmKeyBusData;
                s_tRmKeyRunData.uRmKeyDataCnt = s_RM_KEY_CFG.uKeyBusDataCnt;
                s_tRmKeyRunData.uClickCnt     = 1;  // 第一次按下

                // 支持组合/连击
                if (s_RM_KEY_CFG.uCombCtr)
                {
                    s_tRmKeyRunData.nGobleState = RM_KEY_STATE_WAIT_COMB;   // 进入等待组合/连击模式
                }
                else
                {
                    // 输出单击按键数据
                    RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag | RM_KEY_MASK_SHORT_D);
                    s_tRmKeyRunData.nGobleState = RM_KEY_STATE_WAIT_LONG;   // 进入等待长按
                }
            }
        }
    break;

    // 要么是组合要么是连击 组合:按键个数增加;连击:按键次数增加;
    case RM_KEY_STATE_WAIT_COMB:
        {
            // 定时器超时 判断单击/连击 状态是否维持中,组合也是单击
            if (s_RM_KEY_CFG.uCombCtr <= (++s_tRmKeyRunData.uCombCnt))
            {
                // 所有的按键必须是按下状态 s_tRmKeyRunData.uRmKeyData记录所有按下过的按键
                if (s_RM_KEY_CFG.uRmKeyBusData == s_tRmKeyRunData.uRmKeyData)
                {
                    // 单击/连击 按下,组合也是单击
                    RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag | RM_KEY_MASK_SHORT_D);
                    s_tRmKeyRunData.nGobleState = RM_KEY_STATE_WAIT_LONG;                    
                }
                else
                {
                    // 单击/连击 按下,组合也是单击
                    RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag | RM_KEY_MASK_SHORT_D);

                    s_tRmKeyRunData.nGobleState = RM_KEY_STATE_WAIT_IDLE;
                    s_tRmKeyRunData.uIdleCnt   = s_RM_KEY_CFG.uIdleCtr;
                }
            }
            else    // 除了连击 否则不允许有重复的按键按下
            {               
                // 判断按键发生变化的位置 有可能没有按键按下
                uRmKeyBusData_Temp = s_RM_KEY_CFG.uRmKeyBusData ^ s_uRmKeyBusData_Last;

                // 判断按键是按下 还是弹开
                uRmKeyBusData_Temp &= s_RM_KEY_CFG.uRmKeyBusData;

                // 判断是否有新的按键按下 新的按键跟之前按键的关系 包含 交集 独立?


                // 有交集发生 这个交集是中间状态 先判断 进行大的区分
                if (uRmKeyBusData_Temp & s_tRmKeyRunData.uRmKeyData)
                {
                    // 判断是否是连击
                    if (uRmKeyBusData_Temp == s_tRmKeyRunData.uRmKeyData)
                    {                        
                        // 按键相同 也没有发生过组合 就是连击
                        if (s_tRmKeyRunData.uRmKeyDataCnt == Ones8(s_tRmKeyRunData.uRmKeyData))
                        {
                            s_tRmKeyRunData.uClickCnt++;
                            s_u2CombLongFlag |= RM_KEY_MASK_DOBLE;    // 连击
                            // 复位定时器
                            s_tRmKeyRunData.uCombCnt = 0;

                            // 更新记录 连击次数
                            s_u2CombLongFlag &= 0xF8FF;
                            s_u2CombLongFlag |= (s_tRmKeyRunData.uClickCnt << 8) & 0x0700;                           

                            if (s_RM_KEY_CFG.uClickCtr <= s_tRmKeyRunData.uClickCnt)   // 判断连击门限值
                            {
                                RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag | RM_KEY_MASK_SHORT_D);
                                s_tRmKeyRunData.nGobleState = RM_KEY_STATE_WAIT_LONG;
                            }
                        }
                    }
                    else    // 非法的重复按键发生
                    {
                        // 单击/连击 按下,组合也是单击
                        RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag | RM_KEY_MASK_SHORT_D);

                        s_tRmKeyRunData.nGobleState = RM_KEY_STATE_WAIT_IDLE;
                        s_tRmKeyRunData.uIdleCnt   = s_RM_KEY_CFG.uIdleCtr;
                    }                    
                }
                else    // 没有新的按键按下 或者有新的 但是不是重复的按键按下
                {
                    if (uRmKeyBusData_Temp)     // 有按键按下
                    {
                        // 有新的按键按下 但是没有发生过连击 那就是组合
                        if (1 == s_tRmKeyRunData.uClickCnt)
                        {
                            // 更新新加入的按键
                            s_u2CombLongFlag |= RM_KEY_MASK_ADD;     // 标志是否发生过组合
                            s_tRmKeyRunData.uRmKeyData |= s_RM_KEY_CFG.uRmKeyBusData;
                        }
                        else    // 发生过连击
                        {
                            // 单击/连击 按下,组合也是单击
                            RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag | RM_KEY_MASK_SHORT_D);

                            s_tRmKeyRunData.nGobleState = RM_KEY_STATE_WAIT_IDLE;
                            s_tRmKeyRunData.uIdleCnt   = s_RM_KEY_CFG.uIdleCtr;
                        }                        
                    }
                }
            }           
        }
    break;

    case RM_KEY_STATE_WAIT_LONG:
        {
            // 按键数据保持不变
            if ((s_tRmKeyRunData.uRmKeyData == s_RM_KEY_CFG.uRmKeyBusData) && (0 < s_RM_KEY_CFG.uLongCtr))
            {
                // 可以加上COMB等待的时间 否则时间过长!!!!!!!
                // 支持长按 长按消息只发一次
                if (s_RM_KEY_CFG.uLongCtr > s_tRmKeyRunData.uLongCnt)
                {
                    if (s_RM_KEY_CFG.uLongCtr <= (++s_tRmKeyRunData.uLongCnt))
                    {
                        // 长发消息
                        s_u2CombLongFlag |= RM_KEY_MASK_LONG;
                        RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag | RM_KEY_MASK_SHORT_D);
                    }

                }
                // 支持连发
                else if (0 < s_RM_KEY_CFG.uRepeatCtr)
                {
                    if (s_RM_KEY_CFG.uRepeatCtr <= (++s_tRmKeyRunData.uRepeatCnt))
                    {
                        s_tRmKeyRunData.uRepeatCnt = 0;
                        // 连发消息
                        s_u2CombLongFlag |= RM_KEY_MASK_LONG_P;
                        // 长发连发
                        RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag | RM_KEY_MASK_SHORT_D);
                    }                    
                }
            }
            else
            {
                s_tRmKeyRunData.nGobleState = RM_KEY_STATE_WAIT_IDLE;
                s_tRmKeyRunData.uIdleCnt    = s_RM_KEY_CFG.uIdleCtr;
            }        
        }
    break;

    // 延时一段时间 然后进入IDLE
    case RM_KEY_STATE_WAIT_IDLE:
        {
            // 延时后直接进入IDLE模式
            if (s_RM_KEY_CFG.uIdleCtr)
            {
                if (0 < s_tRmKeyRunData.uIdleCnt)
                {
                    s_tRmKeyRunData.uIdleCnt--;

                    // 没有按键也进入空闲模式
                    if (0 == s_RM_KEY_CFG.uRmKeyBusData)
                    {                    
                        s_tRmKeyRunData.nGobleState = RM_KEY_STATE_IDLE;
                        // 单击/连击 弹起,组合也是单击
                        RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag);
                    }
                }
                else
                {
                    s_tRmKeyRunData.nGobleState = RM_KEY_STATE_IDLE;
                    // 单击/连击 弹起,组合也是单击
                    RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag);
                }
            }
            else    // 必须所有按键都弹开之后才能进入IDLE模式
            {
                if (0 == s_RM_KEY_CFG.uRmKeyBusData)
                {                    
                    s_tRmKeyRunData.nGobleState = RM_KEY_STATE_IDLE;
                    // 单击/连击 弹起,组合也是单击
                    RM_KEY_PutKeyMsg((u16)s_tRmKeyRunData.uRmKeyData | s_u2CombLongFlag);
                }
            }
        }
    break;

    default:
    break;

    }

    // 记录当前按键状态值
    s_uRmKeyBusData_Last = s_RM_KEY_CFG.uRmKeyBusData;

}


/**
* @brief   按键扫描函数 10ms调用一次
*
* @param [in] 无
*
* @return 无
* @author  
* @since   trunk.00001
* @bug
*/
void RM_KEY_Scan10ms(void)
{  
    // 更新一次各个按键状态
    RM_KEY_Update();
    RM_KEY_Detect();        

//    if (s_KeyTimeOutCount > 0)
//    {
//        if (--s_KeyTimeOutCount == 0)
//        {
////            LCD_SetBackLight(0);   /* 关闭背光 */               
////            LCD_DispOff();
////            s_LcdOn = 0;    /* 屏幕关闭 */
//        }
//    }
}


/**
* @brief   按键初始化函数
*
* @param [in] 无
*
* @return 无
* @author  
* @since   trunk.00001
* @bug
*/
void RM_KEY_Init(void)
{
    GPIO_InitTypeDef GPIO_InitTypeDefStr;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);   // 使能A、C口时钟

    // Power:PA0 按键初始化,下拉输入
    GPIO_InitTypeDefStr.GPIO_Pin       = RM_GPIO_KEY_P_PIN;
    GPIO_InitTypeDefStr.GPIO_Mode      = GPIO_Mode_IPD;
    GPIO_Init(RM_GPIO_KEY_P, &GPIO_InitTypeDefStr);

    // A:PA4 按键初始化,下拉输入
    GPIO_InitTypeDefStr.GPIO_Pin       = RM_GPIO_KEY_A_PIN;
    GPIO_InitTypeDefStr.GPIO_Mode      = GPIO_Mode_IPD;
    GPIO_Init(RM_GPIO_KEY_A, &GPIO_InitTypeDefStr);

    // B:PC0 按键初始化,下拉输入
    GPIO_InitTypeDefStr.GPIO_Pin       = RM_GPIO_KEY_B_PIN;
    GPIO_InitTypeDefStr.GPIO_Mode      = GPIO_Mode_IPD;
    GPIO_Init(RM_GPIO_KEY_B, &GPIO_InitTypeDefStr);

    // C:PC15 按键初始化,下拉输入
    GPIO_InitTypeDefStr.GPIO_Pin       = RM_GPIO_KEY_C_PIN;
    GPIO_InitTypeDefStr.GPIO_Mode      = GPIO_Mode_IPD;
    GPIO_Init(RM_GPIO_KEY_C, &GPIO_InitTypeDefStr);

    // D:PC5 按键初始化,下拉输入
    GPIO_InitTypeDefStr.GPIO_Pin       = RM_GPIO_KEY_D_PIN;
    GPIO_InitTypeDefStr.GPIO_Mode      = GPIO_Mode_IPD;
    GPIO_Init(RM_GPIO_KEY_D, &GPIO_InitTypeDefStr);

    // OK:PC14 按键初始化,下拉输入
    GPIO_InitTypeDefStr.GPIO_Pin       = RM_GPIO_KEY_O_PIN;
    GPIO_InitTypeDefStr.GPIO_Mode      = GPIO_Mode_IPD;
    GPIO_Init(RM_GPIO_KEY_O, &GPIO_InitTypeDefStr);

    // 初始化按键参数
    RM_KEY_SetKeyParamDefault();

}

#ifndef _RM_KEY_H_
#define _RM_KEY_H_

/*******************************************************************************
* Copyright (C), 1993-2012, Liming Comm. Co., Ltd.
* @file    :RM_KEY.h
* @brief   :Enter the brief description of this file
* @author  :czm
* @version :V1.0
* @date    :2019-11-15
* @time    :20:21:49
*
******************************************************************************
* 函数列表   
*            
* 1. Enter the name of the  first function
* 2. Enter the name of the second function
******************************************************************************
* 修改历史   
*
* 2019-11-15                 czm                  建立文件     
*******************************************************************************/
#include "RM_GPIO_DEFS.h"


// 按键滤波检测时间 按下或者弹起都滤波 单位ms
#define RM_KEY_FILTER_TIME          ((u8)3)             // 按键滤波时间,总线稳定后,持续的时间 可以短一些 影响多键的判断
#define RM_KEY_COMB_TIME            ((u8)25)            // 组合按键或者连击等待时间 影响到组合
#define RM_KEY_LONG_TIME            ((u8)80)            // 按键长按判断 1秒钟算长按 可以包含COMB也可以不包含
#define RM_KEY_REPEAT_TIME          ((u8)10)            // 长按后连续触发按键的周期
#define RM_KEY_IDLE_TIME            ((u8)25)            // 按键弹开后,如何进入IDLE模式,0:必须没有按键才能进入,其它:延时对应时间,强制进IDLE

#define RM_KEY_CLICK_CTR            ((u8)7)             // 最多支持多少连击 最小值是2 最大值7,3个bit计数,支持组合时生效,否则不生效

#define RM_HARD_KEY_NUM             ((u8)6)             // RM 硬件按键个数

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

使用道具 举报

0

主题

2

帖子

0

精华

初级会员

Rank: 2

积分
77
金钱
77
注册时间
2019-10-24
在线时间
16 小时
发表于 2023-10-21 09:43:04 | 显示全部楼层
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2024-11-23 19:44

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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