按键处理模块,实现短按、长按、连击判断,附源码与图...
本帖最后由 dxgdsx 于 2018-9-24 19:41 编辑论坛已有几位大神发布了关于按键处理的C代码,写的都非常好,本人很受启发。
比如https://www.amobbs.com/forum.php?mod=viewthread&tid=5542774&highlight=%E6%8C%89%E9%94%AE
中秋闲来无事,我也自己动手写一写。
事先声明:本代码参考了论坛里面几位大神的开源代码,以及STM32库函数代码风格,此外也加入了个人的一点想法,欢迎批评指正!
首先简单单片机系统中,常见的按键操作有短按(点击)、长按、连击。
个人对“连击”操作的定义并不是很清晰,个人理解是在长按基础上继续按着,然后超过一段时间视为连击1次,...,直到弹起。
下图是个人对按键操作的理解:
那么源代码中定义一些操作判定用的时间常数:
//建议10ms扫描一次
//这些常数大小,可以根据项目实际需求进行修改
//按键按下消抖计数
#define KEY_PRESS_DB_CNT 3
//判断按键长按计数
#define KEY_LONG_PRESS_CNT 150
//判断按键连击计数
#define KEY_REPEAT_PRESS_CNT 50
//按键释放消抖计数
#define KEY_RELEASE_DB_CNT 3
设计了一个按键扫描处理状态机,共有5个状态编码:
//按键扫描状态机状态编码
typedef enum
{
//按键状态机:“按键等待被按下”处理
KEY_FSM_WAIT_PRESS = 0,
//按键状态机:“按键短按”处理
KEY_FSM_SHORT_PRESS,
//按键状态机:“按键长按”处理
KEY_FSM_LONG_PRESS,
//按键状态机:“按键连击”处理
KEY_FSM_REPEAT_PRESS,
//按键状态机:“按键释放”处理
KEY_FSM_RELEASE
}KEY_FsmTypeDef;
定义按键的不同,共有6个状态编码:
//按键状态编码
typedef enum
{
//按键状态:释放状态
KEY_RELEASED = 0,
//按键状态:短按状态
KEY_SHORT_PRESSED,
//按键状态:等待长按状态
KEY_LONG_PRESS_WAIT,
//按键状态:长按状态
KEY_LONG_PRESSED,
//按键状态:等待连击状态
KEY_REPEAT_PRESS_WAIT,
//按键状态:一次连击状态
KEY_REPEAT_PRESSED
}KEY_StateTypeDef;
定义一个独立按键的按键句柄结构体:
//按键句柄结构体
typedef struct
{
uint8_t KeyId; //按键编号
KEY_StateTypeDef KeyState; //按键当前状态(高4bit为上一个状态,低4bit为当前状态)
uint16_t KeyCnt; //内部计数器(高2bit给按下/弹出计数用,低14bit给长按/连击计数用)
KEY_FsmTypeDef KeyFsmState; //状态机状态
}KEY_HandleTypeDef;
声明一些函数:
//按键端口读取函数声明,需要用户根据硬件情况自行实现
uint8_t KEY_Read_Port(uint8_t KeyId);
//按键初始化函数声明
void KEY_Init(void);
//按键扫描函数声明
void KEY_Scan(KEY_HandleTypeDef* keyHandle);
//按键状态获取函数声明
KEY_StateTypeDef KEY_Get_State(KEY_HandleTypeDef* keyHandle);
其中,关键函数是KEY_Scan函数,事实上其它几个函数无关紧要,存在更多只是为了形式上的完整
KEY_Scan函数就是一个状态机处理函数,源代码如下:
/**
* @brief单个按键处理函数,需要被周期性调用,建议10ms扫描一次
* @param指定单个按键的句柄
* @retval None
*/
void KEY_Scan(KEY_HandleTypeDef* keyHandle)
{
//读取按键端口电平
uint8_t IsKeyDown = KEY_Read_Port(keyHandle->KeyId);
//获取状态机的当前状态
switch(keyHandle->KeyFsmState)
{
case KEY_FSM_WAIT_PRESS:
if(IsKeyDown) //检测到按键按下
{
keyHandle->KeyCnt = KEY_FULL_CNT(KEY_HIGH_CNT(keyHandle->KeyCnt)+1, 0);
//按键按下消抖:连续几次检测到按键按下,可以确认按键按下
if(KEY_HIGH_CNT(keyHandle->KeyCnt) >= KEY_PRESS_DB_CNT)
{
keyHandle->KeyCnt = 0; //复位计数器,状态跳转
keyHandle->KeyFsmState = KEY_FSM_SHORT_PRESS;
keyHandle->KeyState = KEY_FULL_STATE(KEY_RELEASED, KEY_SHORT_PRESSED);
}
}
else
keyHandle->KeyCnt = 0; //检测到按键释放,复位计数器
break;
case KEY_FSM_SHORT_PRESS:
keyHandle->KeyState = KEY_FULL_STATE(KEY_SHORT_PRESSED, KEY_LONG_PRESS_WAIT);
if(!IsKeyDown)//检测到按键释放
{
keyHandle->KeyCnt = KEY_FULL_CNT(KEY_HIGH_CNT(keyHandle->KeyCnt)+1, 0);
//按键释放消抖:连续几次检测到按键释放,可以确认按键释放
if(KEY_HIGH_CNT(keyHandle->KeyCnt) >= KEY_RELEASE_DB_CNT)
{
keyHandle->KeyCnt = 0; //复位计数器,状态跳转
keyHandle->KeyFsmState = KEY_FSM_RELEASE;
keyHandle->KeyState = KEY_FULL_STATE(KEY_SHORT_PRESSED, KEY_RELEASED);
}
}
else
{
keyHandle->KeyCnt = KEY_FULL_CNT(0, KEY_LOW_CNT(keyHandle->KeyCnt)+1);
if(KEY_LOW_CNT(keyHandle->KeyCnt) >= KEY_LONG_PRESS_CNT) //可以确认按键长按
{
keyHandle->KeyCnt = 0; //复位计数器,状态跳转
keyHandle->KeyFsmState = KEY_FSM_LONG_PRESS;
keyHandle->KeyState = KEY_FULL_STATE(KEY_LONG_PRESS_WAIT, KEY_LONG_PRESSED);
}
}
break;
case KEY_FSM_LONG_PRESS:
keyHandle->KeyState = KEY_FULL_STATE(KEY_LONG_PRESSED, KEY_REPEAT_PRESS_WAIT);
if(!IsKeyDown)//检测到按键释放
{
keyHandle->KeyCnt = KEY_FULL_CNT(KEY_HIGH_CNT(keyHandle->KeyCnt)+1, 0);
//按键释放消抖:连续几次检测到按键释放,可以确认按键释放
if(KEY_HIGH_CNT(keyHandle->KeyCnt) >= KEY_RELEASE_DB_CNT)
{
keyHandle->KeyCnt = 0; //复位计数器,状态跳转
keyHandle->KeyFsmState = KEY_FSM_RELEASE;
keyHandle->KeyState = KEY_FULL_STATE(KEY_LONG_PRESSED, KEY_RELEASED);
}
}
else
{
keyHandle->KeyCnt = KEY_FULL_CNT(0, KEY_LOW_CNT(keyHandle->KeyCnt)+1);
if(KEY_LOW_CNT(keyHandle->KeyCnt) >= KEY_REPEAT_PRESS_CNT) //可以确认按键连击
{
keyHandle->KeyCnt = 0; //复位计数器,状态跳转
keyHandle->KeyFsmState = KEY_FSM_REPEAT_PRESS;
keyHandle->KeyState = KEY_FULL_STATE(KEY_REPEAT_PRESS_WAIT, KEY_REPEAT_PRESSED);
}
}
break;
case KEY_FSM_REPEAT_PRESS:
keyHandle->KeyState = KEY_FULL_STATE(KEY_REPEAT_PRESSED, KEY_REPEAT_PRESS_WAIT);
if(!IsKeyDown)//检测到按键释放
{
keyHandle->KeyCnt = KEY_FULL_CNT(KEY_HIGH_CNT(keyHandle->KeyCnt)+1, 0);
//按键释放消抖:连续几次检测到按键释放,可以确认按键释放
if(KEY_HIGH_CNT(keyHandle->KeyCnt) >= KEY_RELEASE_DB_CNT)
{
keyHandle->KeyCnt = 0; //复位计数器,状态跳转
keyHandle->KeyFsmState = KEY_FSM_RELEASE;
keyHandle->KeyState = KEY_FULL_STATE(KEY_REPEAT_PRESS_WAIT, KEY_RELEASED);
}
}
else
{
keyHandle->KeyCnt = KEY_FULL_CNT(0, KEY_LOW_CNT(keyHandle->KeyCnt)+1);
if(KEY_LOW_CNT(keyHandle->KeyCnt) >= KEY_REPEAT_PRESS_CNT) //可以确认按键连击
{
keyHandle->KeyCnt = 0; //复位计数器,状态跳转
keyHandle->KeyFsmState = KEY_FSM_REPEAT_PRESS;
keyHandle->KeyState = KEY_FULL_STATE(KEY_REPEAT_PRESS_WAIT, KEY_REPEAT_PRESSED);
}
}
break;
case KEY_FSM_RELEASE:
keyHandle->KeyCnt = 0; //复位计数器,状态跳转
keyHandle->KeyFsmState = KEY_FSM_WAIT_PRESS;
keyHandle->KeyState = KEY_FULL_STATE(KEY_RELEASED, KEY_RELEASED);
break;
default:
break;
}
}
状态迁移图如下(部分默认分支没有画出):
KEY_Init函数是用于初始化按键对应的IO,若已有类似功能函数,则该函数就不需要再实现。
KEY_Read_Port函数是读取指定按键对应的IO口电平值,不需要消抖,一次读取即可,该函数需要用户根据硬件自行实现。
/**
* @brief按键初始化操作,如端口配置、按键句柄结构体初始化等
* @paramNone
* @retval None
*/
__weak void KEY_Init(void)
{
/*
hkey.KeyId = KEY_ID_1;
hkey.KeyReadFuncPtr = KeyReadFunc;
hkey.KeyState = KEY_RELEASED;
hkey.KeyCnt = 0;
hkey.KeyFsmState = KEY_FSM_FSTATE(KEY_FSM_WAIT_PRESS, KEY_FSM_WAIT_PRESS);
*/
}
/**
* @brief读取指定按键对应的IO口电平值,不需要消抖,一次读取即可,该函数需要用户根据硬件自行实现
* @param按键的ID值,用户自行编码
* @retval IO口电平值,本实现中1:按键按下,0:按键按下,
*/
__weak uint8_t KEY_Read_Port(uint8_t KeyId)
{
/*
//根据KeyId读取指定IO口电平值
switch(KeyId)
{
case KEY_ID_1:
return (HAL_GPIO_ReadPin(USEKEY_GPIO_Port, USEKEY_Pin));
break;
default:
return 0;
break;
}
*/
return 0;
}
另外,本代码中最重要的,或者说与其他实现方式不同的地方在于,按键动作识别。
关键在于按键句柄结构体中的KeyState成员,其高4bit为上一个状态,低4bit为当前状态,通过对这个组合字可以识别出按键的具体动作。
定义了短按、长按、连击的组合识别码:
//按键状态组合字(8位),ls为上一个状态(高4位),cs为当前状态(低4位)
#define KEY_FULL_STATE(ls, cs) (KEY_StateTypeDef)(((ls)<<4)|(cs))
//按键的当前状态值,取按键状态组合字的低4位
#define KEY_CURT_STATE(s) (KEY_StateTypeDef)((s)&0x0F)
//按键上一个状态值,取按键状态组合字的高4位
#define KEY_LAST_STATE(s) (KEY_StateTypeDef)(((s)&0xF0)>>4)
//按键内部计数组合字(16位),h2用于按下/释放消抖计数(高2位),l14用于长按/连击计数(低14位)
#define KEY_FULL_CNT(h2, l14) ((((h2)&0x0003)<<14)|((l14)&0x3FFF))
//按键按下/释放消抖计数值
#define KEY_LOW_CNT(c) ((c)&0x3FFF)
//按键长按/连击计数值
#define KEY_HIGH_CNT(c) (((c)&0xC000)>>14)
//按键短按动作识别字
#define KEY_SHORT_ACTION() KEY_FULL_STATE(KEY_SHORT_PRESSED, KEY_RELEASED)
//按键长按动作识别字
#define KEY_LONG_ACTION() KEY_FULL_STATE(KEY_LONG_PRESSED, KEY_RELEASED)
//按键连击动作识别字
#define KEY_REPEAT_ACTION() KEY_FULL_STATE(KEY_REPEAT_PRESS_WAIT, KEY_REPEAT_PRESSED)
上述代码最后形成两个文件:key_driver.c和key_driver.h
如何使用该模块:
/**
* @briefmain.c--以单个按键以例,仅把与本模块相关的代码写出,其余代码未写
* @param
* @retval
*/
//包含其他头文件
//包含按键处理模块头文件
#include "key_driver.h"
//定义一个按键句柄结构体,并进行初始化
#define KEY_ID_1 1
KEY_HandleTypeDef hkey = {KEY_ID_1, KEY_FULL_STATE(KEY_RELEASED, KEY_RELEASED), 0, KEY_FSM_WAIT_PRESS};
/*若有多个按键,可以定义按键句柄结构体数组
#define KEY_ID_1 1
#define KEY_ID_2 2
#define KEY_ID_3 3
KEY_HandleTypeDef hkey = {{KEY_ID_1, KEY_FULL_STATE(KEY_RELEASED, KEY_RELEASED), 0, KEY_FSM_WAIT_PRESS},
{KEY_ID_2, KEY_FULL_STATE(KEY_RELEASED, KEY_RELEASED), 0, KEY_FSM_WAIT_PRESS},
{KEY_ID_3, KEY_FULL_STATE(KEY_RELEASED, KEY_RELEASED), 0, KEY_FSM_WAIT_PRESS}};
*/
//用户根据硬件情况自行实现指定按键的IO电平读取,该函数将覆盖模块中的同名函数
uint8_t KEY_Read_Port(uint8_t KeyId)
{
switch(KeyId)
{
case KEY_ID_1:
return (HAL_GPIO_ReadPin(USEKEY_GPIO_Port, USEKEY_Pin));
break;
default:
return 0;
break;
}
}
//main函数
int main(void)
{
//按键端口配置,若其他代码已对其进行了配置,则可忽略
KEY_Init();
//其余代码
while(1)
{
//其余代码
//周期性调用按键扫描
KEY_Scan(&hkey);
//按键动作识别
if(KEY_SHORT_ACTION() == KEY_Get_State(&hkey)) //短按,点亮led
HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_SET);
if(KEY_LONG_ACTION() == KEY_Get_State(&hkey)) //长按,熄灭led
HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_RESET);
if(KEY_REPEAT_ACTION() == KEY_Get_State(&hkey)) //连击1次,led翻转1下
HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin);
//扫描间隔10ms
HAL_Delay(10);
}
return 0;
}
本按键代码模块在STM32L152 Discovery硬件上进行了验证,附源码供参考,欢迎批评指正!{:handshake:}
非常不错,有机会试一下 谢谢分享,收藏备用。 楼主的图用什么画的,很不错,说明很清楚! 写的很清楚,记得论坛之前也有一个类似的,程序写的也很不错 haohai 发表于 2018-9-24 18:51
楼主的图用什么画的,很不错,说明很清楚!
是用Visio画的。 收藏收藏 没有组合键吗 希望你这个模块站在前人的肩膀上,博采众长,成为集大成者。 机器人天空 发表于 2018-9-24 19:55
没有组合键吗
可以的,定义多个按键,然后对多个键进行组合判断即可(因为手头板子只有一个按键,多键组合未加验证)。 zcllom 发表于 2018-9-24 20:06
希望你这个模块站在前人的肩膀上,博采众长,成为集大成者。
惭愧惭愧,这个是自己练习用到。 很多场合不能等到按键释放再响应,那样人机交互感受不好。 非常不错,学习下,了解下 看起来不错,多谢分享 makesoft 发表于 2018-9-24 20:27
很多场合不能等到按键释放再响应,那样人机交互感受不好。
关于这一点,我也很疑问,个人觉得要看实际项目的需求来定。
事实上,这里的代码是可以获取每一个10ms检测时的按键上一次状态和当前状态,通过设置不同的组合状态字来进行判断,不一定要非等到按键释放以后再操作。
另外,代码只能检测过去,不可能预测未来。
就拿短按来看:
所以,下面几个动作识别字都是用来识别一个独立的操作,短按就是短按,长按就是长按。不能先认为短按,然后继续按着,就认为是长按
//按键短按动作识别字
#define KEY_SHORT_ACTION() KEY_FULL_STATE(KEY_SHORT_PRESSED, KEY_RELEASED)
//按键长按动作识别字
#define KEY_LONG_ACTION() KEY_FULL_STATE(KEY_LONG_PRESSED, KEY_RELEASED)
//按键连击动作识别字
#define KEY_REPEAT_ACTION() KEY_FULL_STATE(KEY_REPEAT_PRESS_WAIT, KEY_REPEAT_PRESSED)
当然,说到底,还是根据实际项目需求来定。
楼主不错,共享精神,值得肯定! 这个帖子得好好学习一下。 正想好好研究一下按键。 多谢分享 有机会试一下 收藏一下 学习,谢谢!!! 感谢楼主无私分享{:handshake:} 感谢楼主分享 好久没见干货了,帮顶一下 mark,按键程序
感谢无私分享 {:handshake:}{:handshake:}{:handshake:}{:handshake:} 收藏备用
谢谢分享,收藏备用+1 感谢楼主分享
收藏备用 很详细,不错。 感谢楼主分享 感谢分享,收藏备用 按键处理模块,实现短按、长按、连击判断,附源码与图 不错,值得好好的学习一下!{:handshake:} 状态机流程图画的很用心,学习了 之前也写过按键的模块,不过有一定的局限性,学习下
谢谢分享,收藏备用。 谢谢分享 谢谢楼主无私分享。
最近用矩阵键盘,有时间试试。
4*5矩阵键盘,而且GPIO不是连续的,不知道楼主代码支持吗? MRARK:按键状态机! 有些应用场合很变态,比如长按,还要区分长按不同的时间,需要根据按键释放来测算按下的时间,然后进入相应的动作 学习,谢谢!!! 楼主给力,按键处理 收藏学习!! 很详细,很好的资料,谢谢楼主 Excellence 发表于 2018-9-25 10:59
4*5矩阵键盘,而且GPIO不是连续的,不知道楼主代码支持吗?
暂时没有看过矩阵键盘,但是个人觉得实现思路基本差不多的。 fsmcu 发表于 2018-9-25 11:21
有些应用场合很变态,比如长按,还要区分长按不同的时间,需要根据按键释放来测算按下的时间,然后进入相应 ...
对的,所以要根据实际情况来实现。
可以在这个代码基础上增加时间常数与按键状态来拓展。 Excellence 发表于 2018-9-25 10:59
4*5矩阵键盘,而且GPIO不是连续的,不知道楼主代码支持吗?
个人觉得不管什么类型的键盘,只要根据具体硬件把KEY_Read_Port函数实现了,就可以直接使用。 谢谢分享,收藏备用
谢谢分享,收藏备用 本帖最后由 qq335702318 于 2018-9-25 14:37 编辑
不错!楼主是花了心思做流程分析的!
我认为的"连击"是短时间内按键 ”按下-弹起-再次按下-再次弹起”,也就是Double-Click
另外,每个按键都占用一个结构体的思路,虽然用起来灵活方便,但不适合RAM空间极小的8位机
qq335702318 发表于 2018-9-25 14:23
不错!楼主是花了心思做流程分析的!
我认为的"连击"是短时间内按键 ”按下-弹起-再次按下-再次弹起”,也 ...
“双击”确实如你所说,但是“连击”我确实没怎么搞明白定义。
比如一些设备上,先长按按键,屏幕上对应的数字闪烁(意味着进入修改状态),继续按着按键,该数字每隔一定时间加1。
结构体的使用,确实对于资源极小的单片机系统不够友好。 很好,先收藏了,谢谢! 谢谢分享 很好,先收藏 谢谢分享!!!!!!!!! 感谢,收藏学习 多谢楼主的分享和详细的图文解说 收藏,备用。。。虽然目前还用不到。 思路挺好,支持 {:smile:}谢谢分享 谢谢分享,收藏备用。 看起来很麻烦 不错思路清晰,谢谢分享。 a312835782 发表于 2018-9-26 08:37
看起来很麻烦
之前我看傻孩子老师工作室发布的通用按键模块,源码里面多个状态机loop,然后又是双队列,觉得麻烦,所以精简了一下,只剩下一个状态机loop。队列直接舍去。
当然如果只是读取按键的话,确实没必要这样写。但是毕竟还是想把按键这块做成一个相对独立的模块,所以稍微写的多一点。
你觉得上面代码还有哪部分可以精简一下?{:handshake:} dxgdsx 发表于 2018-9-26 08:47
之前我看傻孩子老师工作室发布的通用按键模块,源码里面多个状态机loop,然后又是双队列,觉得麻烦,所以 ...
不能一味的精简。
即然都在前人的基础上重写成了“模块”,就把队列加上吧,一步到位。 MARK Jmhh247 发表于 2018-9-26 10:26
不能一味的精简。
即然都在前人的基础上重写成了“模块”,就把队列加上吧,一步到位。 ...
队列并不是刚需。 谢谢楼主分享。学习了
谢谢楼主分享。学习了 这种资料真正有用,感谢 本帖最后由 Gorgon_Meducer 于 2018-9-27 23:37 编辑
dxgdsx 发表于 2018-9-26 10:31
队列并不是刚需。
首先,非常,非常,非常开心楼主认真的阅读了代码,并根据自己的理解做出了自己的实现,甚至花费宝贵的时间发到我的板块来分享。非常感谢。
其次,关于队列的问题,我想说:“等我书出来,你就知道打脸的感觉了。” 卖个关子。
为了避免被人带节奏催书的问题,这里先放一点原理图,聪明的人应该可以看出使用两个队列的其中一个原因(是的还有别的原因):
结论:通过替换数据处理的前端(Frontend)可以在不改写后续处理和APP的情况下,实现按键模块对不同类型键盘的支持(这是好处之一)。 Gorgon_Meducer 发表于 2018-9-27 23:06
首先,非常,非常,非常开心楼主认真的阅读了代码,并根据自己的理解做出了自己的实现,甚至花费宝贵的时 ...
终于看到大神前来指导了!激动,感谢感谢!
既然逮到大神了,我就抓住机会请教下:
我觉得双FIFO的存在,最好的作用是:用户从FIFO中获取的结果是已经处理好的最终结果,不需要用户进行再次判断。
而我写的里面,用户得到的结果是:上一次扫描到的按键状态 与 本次扫描到的按键状态 的组合状态,这个组合状态字自带了很多有用信息,用它来判断“短按”、“长按”、“连击”的操作是可以的。
但是如果要判断更多动作,可能需要用户二次处理。
所以,我这个严格来讲不能算是独立的模块,只能算是一段可供参考的代码。{:lol:}
makesoft 发表于 2018-9-24 20:27
很多场合不能等到按键释放再响应,那样人机交互感受不好。
有短按键和长按键同时使用的时候,只能取消短按键的这个功能了,也就是短按键必须等待按键释放才能响应,但是长按键可以在时间到达后立即响应,而无需等待按键释放。
在按键数量极其有限的情况下,楼主的方法是很有效律的,我的设计里用2个按键,也是实现了类似的长按,短按,连击等等功能,不过由于按键功能是逐步添加的,整个按键的处理代码都是分散加载的。 Gorgon_Meducer 发表于 2018-9-27 23:06
首先,非常,非常,非常开心楼主认真的阅读了代码,并根据自己的理解做出了自己的实现,甚至花费宝贵的时 ...
一直按键处理使用类似方法,最大的好处是做好按键识别和按键使用分离,增加了系统的可靠性和灵活性。 嗯,收藏了 dxgdsx 发表于 2018-9-28 08:58
终于看到大神前来指导了!激动,感谢感谢!
既然逮到大神了,我就抓住机会请教下:
我注意到了你在提供给客户的内容中加入了状态信息,但是这里有个问题,就是你没有办法
携带时间信息。而时间信息是很多用户行为识别的关键。另外,如果你仔细看我提供的图,
你会明白,这里队列存在的目的主要是两点:隔离和接口去耦。
我的理解是,队列先进先出的特性已经实际上保留了事件之间的前后状态,所以不必在一个
按键report中包含前后的相邻状态。同样,如果真的考虑应用可能需要这些信息,只保存一级
恐怕是不够的……最后用户还是要根据实际情况去队列里挖掘,而这个挖掘的过程还是离不开
使用状态机。
为了解决这个问题,我的处理方法实际上是,在保留各原始的按键事件的前提下,通过逐级
追加数据处理,根据已有的案件事件来进行模式识别,提取出更有用的按键事件,并追加到
事件流里面——比如,双击的处理就是根据单击的事件配合时间信息来实现的。
这里我想强调的是,逐级处理信息,由简单到抽象,这正是人类大脑进行信息处理的方式。
比如,视网膜第一级,简单的对光线和颜色敏感,第二级神经网络将第一集的点信息处理成
线条信息,第三极则对将点和线的信息处理成运动的矢量信息。你可以看出这里明显的逐级
处理,逐级追加有用信息,逐级抽象的过程。
我觉得你现在最大的问题是,太专注于按键扫描本身了。从技术的角度说需要适当扩大一点
思考的范围。 Gorgon_Meducer 发表于 2018-9-28 21:20
我注意到了你在提供给客户的内容中加入了状态信息,但是这里有个问题,就是你没有办法
携带时间信息。而 ...
真的非常受教,感谢!
逐级增加信息,逐级进行处理,确实是人类大脑认知、处理事务最有效的方式。大脑虽然厉害,但是一下子面对复杂事务却很难有效处理,经过分层逐级处理,可以将复杂问题分解成若干简单问题,然后逐步处理,最后解决复杂问题。
太受教了!
学习,谢谢!!! 学习了,谢谢 不错,赞赞赞! 这个程序长按的时候会有响应短按的时间窗口吗? liaihua1997 发表于 2018-9-29 16:11
这个程序长按的时候会有响应短按的时间窗口吗?
不会。
但是可以很容易实现这个功能。 值得学习!!!!!!!好贴 好贴!!!!!!!!
学习,谢谢!!! 好帖子,非常实用!谢谢! 嗯!借助实践触发和状态机,二合一就既有FSM思想好理解,又有时间控制,实现不同的击键要求 mark、以下!!!!!!!!! 收藏一下 也写过一个类似的key queue驱动,学习学习 不错,值得学习一下! 不错,值得学习一下! 收藏下!!! 按键处理程序 Mark mark~~有机会研究一下~ 按键处理程序 Mark
按键处理程序 Mark
页:
[1]
2