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 ©, 1993-2012, Liming Comm. Co., Ltd.
  • @file :RM_KEY.c
  • @brief :虚拟按键扫描
  • @author :czm
  • @version :V1.0
  • @date :2019-11-15
  • @time :20:22:35

  • 函数列表
    1. Enter the name of the first function
    1. 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 最大值7s_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 ©, 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
    1. 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 ((u8)10) // 用来存储按键码的缓存FIFO大小

/**

  • @enum RM_KEY_ID_E
  • @brief 按键的ID号
    */
    typedef enum _RM_KEY_ID_E
    {
    RM_KEY_ID_A = 0x00, ///< 按键 A
    RM_KEY_ID_B, ///< 按键 B
    RM_KEY_ID_C, ///< 按键 C
    RM_KEY_ID_D, ///< 按键 D
    RM_KEY_ID_O, ///< 按键 OK
    RM_KEY_ID_P ///< 按键 Power

} RM_KEY_ID_E;

/**

  • @enum RM_KEY_STATE_E
  • @brief 按键状态
    */
    typedef enum _RM_KEY_STATE_E
    {
    RM_KEY_STATE_IDLE = 0x00, ///< 常态 空闲
    RM_KEY_STATE_WAIT_COMB, ///< 过度状态 开始等待组合或者多击,弹开时也会用到这个状态,用来判断两次按键按下的间隔
    RM_KEY_STATE_WAIT_LONG, ///< 等待进入长按状态 或者不支持长按
    RM_KEY_STATE_WAIT_IDLE ///< 等待空闲状态,按键不松开,长期挺溜此状态

} RM_KEY_STATE_E;

/**

  • @brief 按键GPIO定义的结构体 需要设置按键有效的电平是高还是低
    /
    typedef struct _RM_KEY_GPIO_DEF_T
    {
    GPIO_TypeDef
    ptGpio; // GPIO 端口
    u16 u2Pin; // GPIO 管脚
    BOOL bActiveLevel; // 按键有效激活电平

} RM_KEY_GPIO_DEF_T;

/**

  • @brief RM 逻辑按键参数配置
    */
    typedef struct _RM_KEY_LOGIC_CONFIG_T
    {
    u8 uRmKeyBusData; // 按键总线数据 10ms更新一次 带滤波
    u8 uKeyBusDataCnt; // 当前有效按键的个数

    // 滤波是指按键按下,去掉抖动之后,平稳的时间达到阈值才生效
    u8 uFilterCtr; // 按键底层总线滤波,按键接近时,可滤波成同时按下,既多键

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

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

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

} RM_KEY_CONFIG_T;

/**

  • @brief 逻辑按键计数器
    */
    typedef struct _RM_KEY_RUN_DATA_T
    {
    RM_KEY_STATE_E nGobleState; // 按键处理状态机状态

    u8 uRmKeyData; // 有效的按键数据
    u8 uRmKeyDataCnt; // 有效按键个数

    u8 uFIlterCount; // 逻辑滤波计数器 单位毫秒 等于物理按键的滤波时间

    u8 uCombCnt; // 组合按键或者多击计数器
    u8 uClickCnt; // 按键单击次数

    u8 uLongCnt; // 长按计数器
    u8 uRepeatCnt; // 长按连续发送计数器

    u8 uIdleCnt; // 长按连续发送计数器

    // 消息缓存区设置
    // b15:长按/短按;b14:连发;b13:是否发生过组合;b10~b8:连击次数;
    // b7:按下/弹开;b6:连击/单击; b5~b0:按键
    u16 u2MsgBuf[RM_KEY_MSG_FIFO_SIZE]; // 按键消息缓存区
    u8 uWriteAddr; // 缓冲区写指针
    u8 uReadAddrA; // 缓冲区读指针1
    u8 uReadAddrB; // 缓冲区读指针2,不同的函数同时读取缓存区

} RM_KEY_RUN_DATA_T;

// 按键按下或者弹开 后面两个选项可以用来修饰按键的具体情况
#define RM_KEY_MASK_SHORT_D ((u16)0x0080) // 按键按下
#define RM_KEY_MASK_SHORT_U ((u16)0x0000) // 按键弹开

// 按键类型
#define RM_KEY_MASK_DOBLE ((u16)0x0040) // 连击

#define RM_KEY_MASK_ADD ((u16)0x2000) // 发生了组合

// 两个同时只能用一个 支持连发时 不会生产长按弹起 而是长按连发弹起!!!
#define RM_KEY_MASK_LONG ((u16)0x8000) // 长按
#define RM_KEY_MASK_LONG_P ((u16)0xC000) // 连发 一定长按

#define RM_KEY_BIT_DEF_A ((u16)0x0001)
#define RM_KEY_BIT_DEF_B ((u16)0x0002)
#define RM_KEY_BIT_DEF_C ((u16)0x0004)
#define RM_KEY_BIT_DEF_D ((u16)0x0008)
#define RM_KEY_BIT_DEF_O ((u16)0x0010)
#define RM_KEY_BIT_DEF_P ((u16)0x0020)
#define RM_KEY_BIT_DEF_ALL ((u16)0x003F)

#define RM_KEY_MASK_DU (RM_KEY_MASK_SHORT_D |RM_KEY_BIT_DEF_ALL) // 不区分 单击/连击 不区分 长按/连发
#define RM_KEY_MASK_DU_C (RM_KEY_MASK_DU |RM_KEY_MASK_DOBLE) // 区分 单击/连击 不区分长按/连发

#define RM_KEY_MASK_DU_L (RM_KEY_MASK_DU |RM_KEY_MASK_LONG) // 不区分 单击/连击 区分 长按
#define RM_KEY_MASK_DU_LP (RM_KEY_MASK_DU |RM_KEY_MASK_LONG_P) // 不区分 单击/连击 区分 长按/连发

// 支持连发的时候,连发结束后,有连发弹起,没有长按弹起
#define RM_KEY_MASK_DU_CL (RM_KEY_MASK_DU_C |RM_KEY_MASK_LONG) // 区分 单击/连击 区分 长按
#define RM_KEY_MASK_DU_CLP (RM_KEY_MASK_DU_C |RM_KEY_MASK_LONG_P) // 区分 单击/连击 区分 长按/连发

// 按键 A
#define RM_MSG_KEY_A_SD (RM_KEY_BIT_DEF_A | RM_KEY_MASK_SHORT_D) // 单键 短按按下
#define RM_MSG_KEY_A_SU (RM_KEY_BIT_DEF_A | RM_KEY_MASK_SHORT_U) // 单键 短按弹起
#define RM_MSG_KEY_A_C_SD (RM_MSG_KEY_A_SD | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按按下 最后一次按键是短按
#define RM_MSG_KEY_A_C_SU (RM_MSG_KEY_A_SU | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按弹起 最后一次按键是短按

// 按键 B
#define RM_MSG_KEY_B_SD (RM_KEY_BIT_DEF_B | RM_KEY_MASK_SHORT_D) // 单键 短按按下
#define RM_MSG_KEY_B_SU (RM_KEY_BIT_DEF_B | RM_KEY_MASK_SHORT_U) // 单键 短按弹起
#define RM_MSG_KEY_B_C_SD (RM_MSG_KEY_B_SD | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按按下 最后一次按键是短按
#define RM_MSG_KEY_B_C_SU (RM_MSG_KEY_B_SU | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按弹起 最后一次按键是短按

// 按键 C
#define RM_MSG_KEY_C_SD (RM_KEY_BIT_DEF_C | RM_KEY_MASK_SHORT_D) // 单键 短按按下
#define RM_MSG_KEY_C_SU (RM_KEY_BIT_DEF_C | RM_KEY_MASK_SHORT_U) // 单键 短按弹起
#define RM_MSG_KEY_C_C_SD (RM_MSG_KEY_C_SD | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按按下 最后一次按键是短按
#define RM_MSG_KEY_C_C_SU (RM_MSG_KEY_C_SU | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按弹起 最后一次按键是短按

// 按键 D
#define RM_MSG_KEY_D_SD (RM_KEY_BIT_DEF_D | RM_KEY_MASK_SHORT_D) // 单键 短按按下
#define RM_MSG_KEY_D_SU (RM_KEY_BIT_DEF_D | RM_KEY_MASK_SHORT_U) // 单键 短按弹起
#define RM_MSG_KEY_D_C_SD (RM_MSG_KEY_D_SD | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按按下 最后一次按键是短按
#define RM_MSG_KEY_D_C_SU (RM_MSG_KEY_D_SU | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按弹起 最后一次按键是短按

// 按键 O
#define RM_MSG_KEY_O_SD (RM_KEY_BIT_DEF_O | RM_KEY_MASK_SHORT_D) // 单键 短按按下
#define RM_MSG_KEY_O_SU (RM_KEY_BIT_DEF_O | RM_KEY_MASK_SHORT_U) // 单键 短按弹起
#define RM_MSG_KEY_O_C_SD (RM_MSG_KEY_O_SD | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按按下 最后一次按键是短按
#define RM_MSG_KEY_O_C_SU (RM_MSG_KEY_O_SU | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按弹起 最后一次按键是短按

// 按键 P
#define RM_MSG_KEY_P_SD (RM_KEY_BIT_DEF_P | RM_KEY_MASK_SHORT_D) // 单键 短按按下
#define RM_MSG_KEY_P_SU (RM_KEY_BIT_DEF_P | RM_KEY_MASK_SHORT_U) // 单键 短按弹起
#define RM_MSG_KEY_P_C_SD (RM_MSG_KEY_P_SD | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按按下 最后一次按键是短按
#define RM_MSG_KEY_P_C_SU (RM_MSG_KEY_P_SU | RM_KEY_MASK_DOBLE) // 组合或者双击等 短按弹起 最后一次按键是短按

// 按键 AB
#define RM_MSG_KEY_AB_SD (RM_KEY_BIT_DEF_A | RM_MSG_KEY_B_SD)
#define RM_MSG_KEY_AB_SU (RM_KEY_BIT_DEF_A | RM_MSG_KEY_B_SU)
#define RM_MSG_KEY_AB_C_SD (RM_KEY_BIT_DEF_A | RM_MSG_KEY_B_C_SD)
#define RM_MSG_KEY_AB_C_SU (RM_KEY_BIT_DEF_A | RM_MSG_KEY_B_C_SU)

// 按键 AC
#define RM_MSG_KEY_AC_SD (RM_KEY_BIT_DEF_A | RM_MSG_KEY_C_SD)
#define RM_MSG_KEY_AC_SU (RM_KEY_BIT_DEF_A | RM_MSG_KEY_C_SU)
#define RM_MSG_KEY_AC_C_SD (RM_KEY_BIT_DEF_A | RM_MSG_KEY_C_C_SD)
#define RM_MSG_KEY_AC_C_SU (RM_KEY_BIT_DEF_A | RM_MSG_KEY_C_C_SU)

// 按键 AD
#define RM_MSG_KEY_AD_SD (RM_KEY_BIT_DEF_A | RM_MSG_KEY_D_SD)
#define RM_MSG_KEY_AD_SU (RM_KEY_BIT_DEF_A | RM_MSG_KEY_D_SU)
#define RM_MSG_KEY_AD_C_SD (RM_KEY_BIT_DEF_A | RM_MSG_KEY_D_C_SD)
#define RM_MSG_KEY_AD_C_SU (RM_KEY_BIT_DEF_A | RM_MSG_KEY_D_C_SU)

// 按键 AO
#define RM_MSG_KEY_AO_SD (RM_KEY_BIT_DEF_A | RM_MSG_KEY_O_SD)
#define RM_MSG_KEY_AO_SU (RM_KEY_BIT_DEF_A | RM_MSG_KEY_O_SU)
#define RM_MSG_KEY_AO_C_SD (RM_KEY_BIT_DEF_A | RM_MSG_KEY_O_C_SD)
#define RM_MSG_KEY_AO_C_SU (RM_KEY_BIT_DEF_A | RM_MSG_KEY_O_C_SU)

// 按键 AP
#define RM_MSG_KEY_AP_SD (RM_KEY_BIT_DEF_A | RM_MSG_KEY_P_SD)
#define RM_MSG_KEY_AP_SU (RM_KEY_BIT_DEF_A | RM_MSG_KEY_P_SU)
#define RM_MSG_KEY_AP_C_SD (RM_KEY_BIT_DEF_A | RM_MSG_KEY_P_C_SD)
#define RM_MSG_KEY_AP_C_SU (RM_KEY_BIT_DEF_A | RM_MSG_KEY_P_C_SU)

// 按键 BC
#define RM_MSG_KEY_BC_SD (RM_KEY_BIT_DEF_B | RM_MSG_KEY_C_SD)
#define RM_MSG_KEY_BC_SU (RM_KEY_BIT_DEF_B | RM_MSG_KEY_C_SU)
#define RM_MSG_KEY_BC_C_SD (RM_KEY_BIT_DEF_B | RM_MSG_KEY_C_C_SD)
#define RM_MSG_KEY_BC_C_SU (RM_KEY_BIT_DEF_B | RM_MSG_KEY_C_C_SU)

// 按键 BD
#define RM_MSG_KEY_BD_SD (RM_KEY_BIT_DEF_B | RM_MSG_KEY_D_SD)
#define RM_MSG_KEY_BD_SU (RM_KEY_BIT_DEF_B | RM_MSG_KEY_D_SU)
#define RM_MSG_KEY_BD_C_SD (RM_KEY_BIT_DEF_B | RM_MSG_KEY_D_C_SD)
#define RM_MSG_KEY_BD_C_SU (RM_KEY_BIT_DEF_B | RM_MSG_KEY_D_C_SU)

// 按键 BO
#define RM_MSG_KEY_BO_SD (RM_KEY_BIT_DEF_B | RM_MSG_KEY_O_SD)
#define RM_MSG_KEY_BO_SU (RM_KEY_BIT_DEF_B | RM_MSG_KEY_O_SU)
#define RM_MSG_KEY_BO_C_SD (RM_KEY_BIT_DEF_B | RM_MSG_KEY_O_C_SD)
#define RM_MSG_KEY_BO_C_SU (RM_KEY_BIT_DEF_B | RM_MSG_KEY_O_C_SU)

// 按键 BP
#define RM_MSG_KEY_BP_SD (RM_KEY_BIT_DEF_B | RM_MSG_KEY_P_SD)
#define RM_MSG_KEY_BP_SU (RM_KEY_BIT_DEF_B | RM_MSG_KEY_P_SU)
#define RM_MSG_KEY_BP_C_SD (RM_KEY_BIT_DEF_B | RM_MSG_KEY_P_C_SD)
#define RM_MSG_KEY_BP_C_SU (RM_KEY_BIT_DEF_B | RM_MSG_KEY_P_C_SU)

// 按键 CD
#define RM_MSG_KEY_CD_SD (RM_KEY_BIT_DEF_C | RM_MSG_KEY_D_SD)
#define RM_MSG_KEY_CD_SU (RM_KEY_BIT_DEF_C | RM_MSG_KEY_D_SU)
#define RM_MSG_KEY_CD_C_SD (RM_KEY_BIT_DEF_C | RM_MSG_KEY_D_C_SD)
#define RM_MSG_KEY_CD_C_SU (RM_KEY_BIT_DEF_C | RM_MSG_KEY_D_C_SU)

// 按键 CO
#define RM_MSG_KEY_CO_SD (RM_KEY_BIT_DEF_C | RM_MSG_KEY_O_SD)
#define RM_MSG_KEY_CO_SU (RM_KEY_BIT_DEF_C | RM_MSG_KEY_O_SU)
#define RM_MSG_KEY_CO_C_SD (RM_KEY_BIT_DEF_C | RM_MSG_KEY_O_C_SD)
#define RM_MSG_KEY_CO_C_SU (RM_KEY_BIT_DEF_C | RM_MSG_KEY_O_C_SU)

// 按键 CP
#define RM_MSG_KEY_CP_SD (RM_KEY_BIT_DEF_C | RM_MSG_KEY_P_SD)
#define RM_MSG_KEY_CP_SU (RM_KEY_BIT_DEF_C | RM_MSG_KEY_P_SU)
#define RM_MSG_KEY_CP_C_SD (RM_KEY_BIT_DEF_C | RM_MSG_KEY_P_C_SD)
#define RM_MSG_KEY_CP_C_SU (RM_KEY_BIT_DEF_C | RM_MSG_KEY_P_C_SU)

// 按键 DO
#define RM_MSG_KEY_DO_SD (RM_KEY_BIT_DEF_D | RM_MSG_KEY_O_SD)
#define RM_MSG_KEY_DO_SU (RM_KEY_BIT_DEF_D | RM_MSG_KEY_O_SU)
#define RM_MSG_KEY_DO_C_SD (RM_KEY_BIT_DEF_D | RM_MSG_KEY_O_C_SD)
#define RM_MSG_KEY_DO_C_SU (RM_KEY_BIT_DEF_D | RM_MSG_KEY_O_C_SU)

// 按键 DP
#define RM_MSG_KEY_DP_SD (RM_KEY_BIT_DEF_D | RM_MSG_KEY_P_SD)
#define RM_MSG_KEY_DP_SU (RM_KEY_BIT_DEF_D | RM_MSG_KEY_P_SU)
#define RM_MSG_KEY_DP_C_SD (RM_KEY_BIT_DEF_D | RM_MSG_KEY_P_C_SD)
#define RM_MSG_KEY_DP_C_SU (RM_KEY_BIT_DEF_D | RM_MSG_KEY_P_C_SU)

// 按键 OP
#define RM_MSG_KEY_OP_SD (RM_KEY_BIT_DEF_O | RM_MSG_KEY_P_SD)
#define RM_MSG_KEY_OP_SU (RM_KEY_BIT_DEF_O | RM_MSG_KEY_P_SU)
#define RM_MSG_KEY_OP_C_SD (RM_KEY_BIT_DEF_O | RM_MSG_KEY_P_C_SD)
#define RM_MSG_KEY_OP_C_SU (RM_KEY_BIT_DEF_O | RM_MSG_KEY_P_C_SU)

extern u8 RM_KEY_GetKeyState(void);
extern void RM_KEY_Init(void);
extern void RM_KEY_Scan10ms(void);
extern void RM_KEY_PutKeyMsg(u16 u2KeyMsgCode);
extern u16 RM_KEY_GetKeyMsgA(void);
extern void RM_KEY_SetKeyParam(u8 uFilterCtr, u8 uCombCtr, u8 uClickCtr, u8 uLongCtr, u8 uRepeatCtr, u8 uIdleCtr);

#endif /* RM_KEY_H */

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

  1. STM32独立按键实现单击双击长按功能

    目录 前言 一.按键功能定义 二.使用步骤 1.按键初始化 2.按键扫描函数(重点) 总结 前言 在使用STM32或其他单片机开发项目时,经常需要用到独立按键进行控制. 通常一个独立按键需要使用一个I ...

  2. LED数显低功耗驱动芯片VK16K33,低功耗数码管LED驱动IC-SOP28/24/20,多封装选择,带按键扫描功能,兼容市面16K33

    品牌:永嘉微电/VINKA 型号:VK16K33 封装:SOP28/24/20 年份:新年份 概述: VK16K33是一个内存映射和多功能LED控制器驱动程序.VK16K33有28SOP.24SOP和 ...

  3. 独立按键扫描程序的思考(整合两种算法)

    摘自:http://blog.csdn.net/xuechaojie/article/details/6761772 网上看到Etual 2008年总结的<新型按键扫描>的博文,很有感触. ...

  4. STM32矩阵按键扫描冲突问题

    最近做了一个项目,有个部分用到了矩阵扫描的原理,要检测按键是否按下去执行一个动作.一开始硬件工程师的原理图实现是设计成传统矩阵按键方式,大致原理如下所示: 这种硬件实现方式当去检测单个按下.一整行按下 ...

  5. 独立按键检测短按、长按,松手后响应操作

    背景 有项目使用独立按键检测,短按.长按.根据使用效果,发现松手后,也就是按键弹起后响应操作比较好操作. 记得之前,博主写过一篇关于按键的检测的文章,但是过于复杂了.可能很难懂,这里就简单一点,只处理 ...

  6. php 按键连击,写了一个独立按键 支持组合键、单键长按,连发功能的例子

    你好,我在调试你贴上的程序,发现单个按键都正常,为什么组合键就不正常呢?代码如下: #include #define K_STOP                (1< #define K_ST ...

  7. 单片机独立按键模块(含短按,长按,连发功能)

    最近看了很多按键扫描的文章,发现各有长处,后来自己花了一点时间做了一个单片机独立按键扫描的模块,此模块优点颇多,支持短按,长按,连发功能,只要配置相关结构体就可以实现这些功能,唯一的缺点是不支持多按键 ...

  8. 【51单片机】独立按键,每个按键不同功能,数码管数值的加减,控制流水灯模式,包含按键消抖,数码显示,流水灯

    实验内容: 利用所学的单片机知识及电路知识编程实现单片机 I/O 口检测按键输入电平. 1.选中 8 位数码管的其中 1 位数码管,当独立按键 1 每按下一次,数码管加一(按下期间加 1),范围为 0 ...

  9. STM32F103 矩阵键盘4*4 16*16等任意矩阵 按键扫描程序

    基于STM32的按键扫描测试程序(学习记录): 目录: 源码: 4x4按键原理: 按键扫描逻辑: Tips: 粘贴代码时,粘贴在源文件存放的位置中(如:HARDWARE中的.c和.h文件),用C++编 ...

  10. 三行代码按键消抖 独立按键 矩阵按键 长按 短按 双击

    九层妖塔 起于垒土 直接跳转到三行代码 三行代码按键消抖 独立按键 矩阵按键 长按 短按 双击 一.基本理论 0.按键的常见名词:  ①按键抖动  ②按键稳定闭合时间 1.按键的扫描: 2.按键的消抖 ...

最新文章

  1. 第15章 进程间通行 15.6 XSI IPC 15.7 消息队列
  2. 玲珑杯 ACM Round #10
  3. 二叉树N叉数的前中后序遍历总结,python实现递归法和迭代法
  4. React之类式组件中的构造器与props
  5. LeetCode 20. Valid Parentheses(c++)
  6. Java多线程学习二十八:原子类和 volatile 有什么异同?
  7. 会写helloworld,不等于入门
  8. android查看数据库
  9. 第三届蓝桥杯省赛---马虎的算式
  10. 聊聊集成电路工程技术人员都有哪些?
  11. Mac新手必看教程,苹果电脑基本使用操作,苹果电脑基本操作
  12. 计算机视觉-1.2手写字体识别
  13. 8255芯片+8254芯片实现硬延时跑马灯
  14. 京东自营售前客服考试
  15. 微博php面试,新浪微博php实习生
  16. 芯准TTE“宏时隙“调度机制 ——基本原理
  17. let 连续复制_要在Word中使用“格式刷”对同一个格式进行多次复制时,应先用鼠标()。...
  18. python爬虫实战(四):selenium爬虫抓取阿里巴巴采购批发商品
  19. 北斗/GPS如何实现定位
  20. Project build error: Non-parseable POM D:\code\pom.xml: expected start tag name and not

热门文章

  1. 【渝粤题库】陕西师范大学201841 比较教育学 作业(专升本)
  2. python爬取京东商品_Python爬取京东的商品分类与链接
  3. 《动手学深度学习》入门环境安装
  4. 下载最新版Termux
  5. 华为手机助手(PC)无法连接的通用解决方案
  6. 草根程序员转型做项目管理走过的点点滴滴_康奈尔笔记法介绍
  7. 小福利,介绍excel高阶函数2
  8. ECCV 2022 | AirDet: 无需微调的小样本目标检测方法
  9. 网口压线顺序_RJ45水晶头排线顺序
  10. html 设置日期选择器样式,解决Element UI 日期选择器自定义修改多个样式