初级会员
- 积分
- 63
- 金钱
- 63
- 注册时间
- 2019-11-13
- 在线时间
- 21 小时
|
## 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 |
|