发一个多功能的按键检测程序,非常精简。
这几天学习STM32,突然来了个思路,将按键常用的功能都给检测出来了。主要功能有:
1、状态检测:按下、弹起状态
2、边沿检测:按下、弹起瞬间
3、按键之间逻辑与/或
4、长按检测
5、支持按键事件钩子函数
主要特性:
1、基本无抖动
2、程序很精简
3、很容易移植
4、运算量少
5、非阻塞运作方式
程序如下:
头文件:
#ifndef BSP_KEY_H
#define BSP_KEY_H
/********************************************************************************************
*** ***
*** 按键配置 ***
*** ***
********************************************************************************************/
#define key_t uint32_t
#define KEY_U (1U << 4)
#define KEY_D (1U << 3)
#define KEY_L (1U << 1)
#define KEY_R (1U << 2)
#define KEY_O (1U << 5)
#define KEY_C (1U << 0)
#define KEY_NONE 0
#define KEY_ALL (KEY_U | KEY_D | KEY_L | KEY_R | KEY_O | KEY_C)
/********************************************************************************************
*** ***
*** API ***
*** ***
********************************************************************************************/
void BSP_KeyInit (void);
void BSP_KeyUpdate (void);
int BSP_KeyEvent (int e, key_t k);
void BSP_KeyHook (void(*hook)(void));
#endif
.C文件:
#include "bsp.h"
#ifdef BSP_KEY_H
/********************************************************************************************
*** ***
*** 变量区 ***
*** ***
********************************************************************************************/
static void(*iKeyHook) (void);
static key_t iKSt; // 当前值,状态变化值,长按标志
static uint32_t iKeyTicker;
/********************************************************************************************
*** 函数名称 : KeyInputX
*** 函数功能 : 获取键值( 一个按键对应一个位,有按键按下时,对应的位置位)
*** 入口参数 : 无
*** 出口参数 : 键值
********************************************************************************************/
static key_t KeyInputX (void)
{
return ((~(GPIOG->IDR >> 10)) & 0x3f);
}
/********************************************************************************************
*** 函数名称 : BSP_KeyUpdate
*** 函数功能 : 对按键进行状态更新
*** 入口参数 : 无
*** 出口参数 : 无
********************************************************************************************/
void BSP_KeyUpdate (void)
{
key_t k = KeyInputX();
iKSt = iKSt ^ k;
iKSt = k;
if(iKSt){
iKeyTicker = BSP_TickGet();
iKSt = 0;
}
if(BSP_TickOut(&iKeyTicker, BSP_TICKS_PER_SEC * 2)){
iKSt = iKSt;
}
if(iKeyHook != NULL){
iKeyHook();
}
}
/********************************************************************************************
*** 函数名称 : BSP_KeyEvent
*** 函数功能 : 获取按键事件
*** 入口参数 : e e = 0xWXYZ(16进制数, W,X,Y,Z均为0或1)
*** --------------------------------------
*** | 赋 | W | X | Y | Z |
*** | |-------+-------+---------------|
*** | 值 | 长/短 | 与/或 | 检测目标 |
*** |----+-------+-------+---------------|
*** |0 |短 |与 | ------- |
*** |----+-------+-------+---------------|
*** |1 |长 |或 | ------- |
*** |----+-------+-------+---------------|
*** | 00 |-- |-- | 低电平 |
*** |----+-------+-------+---------------|
*** | 01 |-- |-- |上升沿(弹起) |
*** |----+-------+-------+---------------|
*** | 10 |-- |-- |下降沿(按下) |
*** |----+-------+-------+---------------|
*** | 11 |-- |-- | 高电平 |
*** --------------------------------------
*** k 需要检测的按键标志(允许多按键检测)
*** 出口参数 : 0/1 0:被检测事件未发生 1:被检测事件发生
***
*** 例 如 :
*** 检测左键按下状态 : if(BSP_KeyEvent(0x0000, KEY_L)) {...}
*** 检测左键按下瞬间 : if(BSP_KeyEvent(0x0010, KEY_L)) {...}
*** 检测左键弹起瞬间 : if(BSP_KeyEvent(0x0001, KEY_L)) {...}
*** 检测左键长按状态 : if(BSP_KeyEvent(0x1001, KEY_L)) {...}
*** 检测左右组合键按下状态: if(BSP_KeyEvent(0x0001, KEY_L|KEY_R)){...}
*** 检测某个按键按下状态: if(BSP_KeyEvent(0x0100, KEY_ALL)) {...}
*** 备 注 : 目前长按情况下不支持边沿检测
********************************************************************************************/
int BSP_KeyEvent (int e, key_t k)
{
int ret = 0;
switch(e){
/*----------------------------------------------------------------*/
case 0x0000: if( (iKSt&k)==k) { ret=1; } break;
case 0x0001: if( (iKSt&(~iKSt)&k) == k) { ret=1; } break;
case 0x0010: if( (iKSt&iKSt &k) == k) { ret=1; } break;
case 0x0011: if( (iKSt&k)==0) { ret=1; } break;
/*----------------------------------------------------------------*/
case 0x0100: if( (iKSt&k)!=0) { ret=1; } break;
case 0x0101: if( (iKSt&(~iKSt)&k) != 0) { ret=1; } break;
case 0x0110: if( (iKSt&iKSt &k) != 0) { ret=1; } break;
case 0x0111: if( (iKSt&k)!=k) { ret=1; } break;
/*----------------------------------------------------------------*/
case 0x1000: if(((iKSt&iKSt &k) == k)) { ret=1; } break;
case 0x1100: if(((iKSt&k)!=0)&&((iKSt&k)!=0)){ ret=1; } break;
/*----------------------------------------------------------------*/
default: break;
}
return ret;
}
/********************************************************************************************
*** 函数名称 : BSP_KeyInit
*** 函数功能 : 初始化按键(PG)
*** 入口参数 : 无
*** 出口参数 : 无
********************************************************************************************/
void BSP_KeyInit (void)
{
GPIOG->CRH &= 0x000000ff;
GPIOG->CRH |= 0x44444400;
iKSt = 0;
iKSt = 0;
iKSt = 0;
KeyInputX();
iKeyHook = NULL;
}
/********************************************************************************************
*** 函数名称 : BSP_KeyHook
*** 函数功能 : 设置按键动作事件钩子函数(请注意,钩子函数可能由中断调用)
*** 入口参数 : hook 钩子函数指针
*** 出口参数 : 无
********************************************************************************************/
void BSP_KeyHook (void(*hook)(void))
{
iKeyHook = hook;
}
#endif 主要用法如下:
while(1)
{
// 获取一次按键状态
BSP_KeyUpdate();
// 下面分别检测各种按键事件,并对事件作出响应
if(BSP_KeyEvent(0x****, KEY***)){...}
if(BSP_KeyEvent(0x****, KEY***)){...}
if(BSP_KeyEvent(0x****, KEY***)){...}
if(BSP_KeyEvent(0x****, KEY***)){...}
} 最大支持32个按键。
原本想增加双击检测功能,但是我的工作方向主要是电表/仪表之类的,这类设备上很少会用到双击,所以没有做。
有好思路解决双击检测的朋友不妨试试。 程序看得不是很明白,想请教一下楼主:是否支持某个键短按的时候是一个功能,长按执行另一个功能,但长按的时候不会先执行一次短按功能? 回复【3楼】laolu
程序看得不是很明白,想请教一下楼主:是否支持某个键短按的时候是一个功能,长按执行另一个功能,但长按的时候不会先执行一次短按功能?
-----------------------------------------------------------------------
支持,做法是先检查按键长按状态,如果失败则检查按键上升沿(弹起瞬间),大致程序如下:
if(BSP_KeyEvent(0x1000, KEY_O)){
printf("长按\n");
}
else if(BSP_KeyEvent(0x0001, KEY_O)){
printf("短按\n");
} 记号 记号 借签了 mark MARK! MARK! 多谢楼主,, mark 思路不错 mark mark 借鉴了,我这里也有一个程序,给大家分享一下,以前在网上找的。
很简洁,很耐用。
Cont是长按。
Trg是短按。
程序如下:
void XXXXXXX
{
uint ReadData;
press_time = 0xfe;
ReadData =~P1;
while(--press_time)
{
Trg = ReadData & (ReadData ^ Cont);
}
Cont = ReadData;
} 好 mark 学习了,借鉴一下 学习下 mark mark! mark mark 学习 MARK mark!~~ 这个要试试,多谢分享 mark 学习了! mark mark mark 记号 记号参考 mark~ 记得 mark学习一下 必须顶,mark 谢谢楼主分享 好东东,记号参考一下 学习了! mark随时学习 学习。标记 为什么不用状态机? 本帖最后由 xyz2008 于 2012-4-12 09:32 编辑
这个按键程序强大,楼主要是能对程序详细解释下就好了,光看程序很难明白楼主的思路 刚才仔细看了下楼主的程序有点晕,要是能给出一个完整的测试工程就好了 blavy 发表于 2011-11-1 09:39 static/image/common/back.gif
借鉴了,我这里也有一个程序,给大家分享一下,以前在网上找的。
很简洁,很耐用。
Cont是长按。
你的检测要等待消抖,可有不等待消抖的? tranquilly86 发表于 2012-10-25 15:49 static/image/common/back.gif
你的检测要等待消抖,可有不等待消抖的?
那倒没有,轻触开关消抖是肯定要的,不会耗时太多。如果是完全没有抖动噪音的开关,那去掉那个等待的时间就是不消抖的了嘛。 MARK! blavy 发表于 2012-10-27 19:44 static/image/common/back.gif
那倒没有,轻触开关消抖是肯定要的,不会耗时太多。如果是完全没有抖动噪音的开关,那去掉那个等待的时间 ...
你好!我意思不是说要不要消抖,是想问下,可不可以消抖不是在延时等待的,而是查询的方式 标记{:victory:} tranquilly86 发表于 2012-10-29 08:07 static/image/common/back.gif
你好!我意思不是说要不要消抖,是想问下,可不可以消抖不是在延时等待的,而是查询的方式 ...
那没有。 mark{:smile:} 学习学习
页:
[1]