|
论坛上有不少关于按键的帖子,本人也献丑一下。
为什么是:抛砖引"砖"。因为我这里准备发布两个代码:
先说一个以前遇到的一个bug,刚开始工作不久,用了一个按键代码,批量后发现几个板子:只要上电,就会误动作。结果发现是按键短路,上电启动后,误认为是一次按键操作。后期就把按键代码改了加了一个上电状态判定:上电判断按键是不是短路了。
1:一楼一个:多年年前自己写的:支持正常的单击,双击,长按,软件中可以直接通过定义屏蔽双击和长按。使用方法简单。
a:用结构体定义了一个按键的属性。
b:可以定义无数多个按键。
c:每个按键的属性不一样,比如按键A,长按时间是1S,按键B可以定义长按时间为5S。
e:支持组合键:把一个按键长按的时间设计短点,然后在应用的:当检测到另一个按键按下的时候,判断这个按键是否处于长按中。
2:二楼一个,也大概是两年前写的,一直在用,后来花了几天时间仔细的研究了按键的应用:使用方法简单,按键状态通过属性直接获得。
a:有的按键是按下起作用,有的按键是弹起起作用。
b:在多击事件中,有的是立刻起作用,有的是:确认后起作用。
i:确认后起作用:比如一个按键:单击是一个功能,双击是一个功能。那么在判断上当发现按键按下一次后,需要超时等待确定没有第二次的按键按下,才可以判断是单击。同理三连击...
ii:立刻起作用的:常用的windos中按下字母按键或者鼠标,就不需要判断。仔细观察发现:双击鼠标,其实是执行一次单击的。
iii:如果按键用于:温度的增加,单击是增加1,双击是增加10,长按是一直增加,也是立刻起作用。
c:多连击事件,有时候,一个按键功能比较多,可以需要支持:单击,双击,三击,五击。。
d:组合键。类似于win的CTRL+XXX键。二楼,可以通过在应用层,用判断的方式实现。(比如设置按键A的长按时间比较短,然后在应用中,假设有B按下,再判断A的状态是不是长按。)
e:双击,长按的时间可以设置。同时也可以屏蔽。
f:按键取消功能(这个代码还没有实现),大家有没用用过,一种可以取消的按键的用法,比如:单击一个功能,长按一个功能(假设长按判定是5S)。单击后如果不松手,过个几秒(不超过5S)再松手,系统会不会执行单击的判定!
下面为第一种的代码(第二种的代码在二楼):
- 首先是定义:利用状态机的思想,定义了几种按键的状态。
- typedef enum
- {
- keyOriginalStatus = 0, // 解决按键一直被按下的问题
- keyUp = 1,
- keyPress = 2,
- keyPressButNotReachLongPress = 3,
- keyLongPress = 4,
- keyDoublePress = 5,
- doublePressKeyNotUp = 6,
- }keyStatus;
-
- typedef struct{
- keyStatus Status;
- u16 longPressCount; // 从按键按下到认为长按的计数!
- u16 doublePressCount; // 用于确定双击的时间间隔!
- u16 longPressThresholdValue; // 用于定义长按的判断时间。为0时禁止长按功能。
- u16 doublePressThresholdValue; // 用于定义双击的判断时间。为0时禁止双击功能。
- }keyProperty;
复制代码
然后是主代码:
/**
* @brief 按键属性初始化,建议20ms的扫描时间。双击时间建议设置为:500ms,长按为1000ms
*/
void keyInit( keyProperty* key,u16 doubluePressThresholdValue, u16 longPressThresholdValue )
{
memset(key,0,sizeof(keyProperty));
key->doublePressThresholdValue = doubluePressThresholdValue;
key->longPressThresholdValue = longPressThresholdValue;
}
/**
* @brief 下面程序应用于循环扫描10ms-20ms和任务或者定时器中
* 输入按键引脚的值,然后处理按键的状态。尽量少关连实际的硬件。
*/
void keyDetect( keyProperty* key,_Bool keyCurrentValue )
{
if( keyCurrentValue == 1 ) // 表示按键当前松开,实际中需要根据情况来定。
{
key->Status = keyUp;
if( key->doublePressCount != 0) // 双击用的是递减!
{
key->doublePressCount--;
}
}
else if( keyCurrentValue == 0 ) // 表示按键当前按下
{
switch( key->Status )
{
case keyOriginalStatus: // 这种状态也可以作为上电初始化的一种选择!
key->Status = keyOriginalStatus; // 仍然不变
break;
case keyUp: // 只有两种可能:要么进入keyDown状态,要么进入doublePress,显然是根据时间来判断的。
if( key->doublePressCount == 0 ) //时间已超时
{
key->Status = keyPress;
key->doublePressCount = key->doublePressThresholdValue; // 这个值会在日后递减,用于计时。参考Winddows的时间间隔默认为500ms
}
else
{
key->Status = keyDoublePress;
key->doublePressCount = 0; // 下次Press事件,不会从doublePress再次回到Double
}
break;
case keyPress:
key->Status = keyPressButNotReachLongPress;
key->longPressCount = 0;
if( key->doublePressCount != 0 )
{
key->doublePressCount--;
}
break;
case keyPressButNotReachLongPress:
if( key->doublePressCount != 0 )
{
key->doublePressCount--;
}
if( key->longPressThresholdValue == 0) // 此时在这个状态循环,不进入长按状态。
{
break;
}
else
{
key->longPressCount++;
if( key->longPressCount >= key->longPressThresholdValue )
{
key->Status = keyLongPress;
key->longPressCount = 0;
}
break;
}
case keyLongPress:
key->doublePressCount = 0; // 进入了长按,就不允许进入双击!
key->Status = keyLongPress; // 不变
break;
case keyDoublePress:
key->Status = doublePressKeyNotUp;
key->doublePressCount = 0; // 防止进入两次双击
break;
case doublePressKeyNotUp:
key->Status = doublePressKeyNotUp; // 不变
default:
break;
}
}
else // 不应该出现这样的值
{
printf("Error:Key value unexpected! Line:%d,Fun:%s\n",__LINE__,__func__);
}
}
最后是应用:只需要绑定一个按键,然后20ms循环,进入程序读就行了。
- keyPropertyV2 KeyUp; //
- keyInitV2( &KeyUp,25,50);
- keyDetectV2( &KeyUp, GPIOC->IDR & 0x01 ) // PC1引脚的电平值输入到检测函数中。
- switch( KeyUp.Status ) // 可以这里进行处理。
- {
- ...
- }
- ;
复制代码
|
阿莫论坛20周年了!感谢大家的支持与爱护!!
阿莫论坛才是最爱国的,关心国家的经济、社会的发展、担心国家被别国牵连卷入战争、知道珍惜来之不易的和平发展,知道师夷之长,关注世界的先进文化与技术,也探讨中国文化的博大精深,也懂得警惕民粹主义的祸国殃民等等等等,无不是爱国忧民的表现。(坛友:tianxian)
|