akin 发表于 2011-7-22 13:31:04

发一个多功能的按键检测程序,非常精简。

这几天学习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

akin 发表于 2011-7-22 13:34:49

主要用法如下:

while(1)
{
    // 获取一次按键状态
    BSP_KeyUpdate();

    // 下面分别检测各种按键事件,并对事件作出响应
    if(BSP_KeyEvent(0x****, KEY***)){...}
    if(BSP_KeyEvent(0x****, KEY***)){...}
    if(BSP_KeyEvent(0x****, KEY***)){...}
    if(BSP_KeyEvent(0x****, KEY***)){...}

}

akin 发表于 2011-7-22 13:37:30

最大支持32个按键。
原本想增加双击检测功能,但是我的工作方向主要是电表/仪表之类的,这类设备上很少会用到双击,所以没有做。
有好思路解决双击检测的朋友不妨试试。

laolu 发表于 2011-7-22 13:59:43

程序看得不是很明白,想请教一下楼主:是否支持某个键短按的时候是一个功能,长按执行另一个功能,但长按的时候不会先执行一次短按功能?

akin 发表于 2011-7-22 18:09:11

回复【3楼】laolu
程序看得不是很明白,想请教一下楼主:是否支持某个键短按的时候是一个功能,长按执行另一个功能,但长按的时候不会先执行一次短按功能?
-----------------------------------------------------------------------
支持,做法是先检查按键长按状态,如果失败则检查按键上升沿(弹起瞬间),大致程序如下:

if(BSP_KeyEvent(0x1000, KEY_O)){
    printf("长按\n");
}
else if(BSP_KeyEvent(0x0001, KEY_O)){
    printf("短按\n");
}

dmxfeng 发表于 2011-7-22 19:44:20

记号

dmxfeng 发表于 2011-7-22 19:45:10

记号

Onsunsl 发表于 2011-7-22 20:03:21

借签了

lsy5110 发表于 2011-7-22 21:01:49

mark

ITOP 发表于 2011-7-22 21:53:15

MARK!

xinyou 发表于 2011-7-22 22:09:58

MARK!

jackiezeng 发表于 2011-7-22 22:33:15

多谢楼主,,

cuikai12345 发表于 2011-7-22 22:42:49

mark

cqfeiyu 发表于 2011-7-23 00:12:19

思路不错

ideadz 发表于 2011-7-23 08:39:19

mark

ljmdzyx 发表于 2011-10-31 12:20:39

mark

blavy 发表于 2011-11-1 09:39:19

借鉴了,我这里也有一个程序,给大家分享一下,以前在网上找的。
很简洁,很耐用。
Cont是长按。
Trg是短按。
程序如下:       
void XXXXXXX
{
         uint ReadData;
        press_time = 0xfe;
        ReadData =~P1;
        while(--press_time)
        {
             Trg = ReadData & (ReadData ^ Cont);
          }       
        Cont = ReadData;
}

farmerzhangdl 发表于 2011-11-1 17:48:37

javine 发表于 2011-11-1 17:52:18

mark

mvip 发表于 2011-11-2 13:39:22

学习了,借鉴一下

vjcmain 发表于 2011-11-2 13:40:20

学习下

luy3728000 发表于 2011-12-2 16:19:50

mark

dong889 发表于 2011-12-2 16:27:36

mark!

badboy.tao 发表于 2011-12-9 10:10:53

mark

mypc16888 发表于 2012-2-24 17:52:52

mark

yywin1986 发表于 2012-2-24 18:39:50

学习

woshixinshou 发表于 2012-2-25 18:30:32

MARK

popwolf 发表于 2012-2-25 21:29:51

mark!~~

XIE2099 发表于 2012-2-25 23:18:54

这个要试试,多谢分享

pycerl 发表于 2012-2-25 23:33:00

mark

728196 发表于 2012-2-26 00:54:52

学习了!

adswads 发表于 2012-3-2 19:52:17

mark

enovo2468 发表于 2012-3-2 21:53:32

mark

ubuntuman 发表于 2012-3-2 21:59:21

mark

linnjing 发表于 2012-3-2 22:44:59

记号

anvy178 发表于 2012-3-3 10:13:31

记号参考

spely 发表于 2012-3-3 15:39:43

mark~

yxeglxl 发表于 2012-3-3 17:00:40

wsm80828 发表于 2012-3-4 08:11:10

记得

lisen090 发表于 2012-3-30 09:20:27

mark学习一下

LOVEDOVE 发表于 2012-4-11 16:59:04

必须顶,mark

LOVEDOVE 发表于 2012-4-12 00:23:57

谢谢楼主分享

ihyni2004 发表于 2012-4-12 04:13:59

好东东,记号参考一下

leiyin 发表于 2012-4-12 05:03:18

学习了!

ooppoo11 发表于 2012-4-12 07:48:38

mark随时学习

jiaowoxiaolu 发表于 2012-4-12 08:42:31

学习。标记

guanxihui 发表于 2012-4-12 09:08:42

为什么不用状态机?

xyz2008 发表于 2012-4-12 09:22:07

本帖最后由 xyz2008 于 2012-4-12 09:32 编辑

这个按键程序强大,楼主要是能对程序详细解释下就好了,光看程序很难明白楼主的思路

xyz2008 发表于 2012-4-12 09:33:53

刚才仔细看了下楼主的程序有点晕,要是能给出一个完整的测试工程就好了

tranquilly86 发表于 2012-10-25 15:49:16

blavy 发表于 2011-11-1 09:39 static/image/common/back.gif
借鉴了,我这里也有一个程序,给大家分享一下,以前在网上找的。
很简洁,很耐用。
Cont是长按。


你的检测要等待消抖,可有不等待消抖的?

blavy 发表于 2012-10-27 19:44:13

tranquilly86 发表于 2012-10-25 15:49 static/image/common/back.gif
你的检测要等待消抖,可有不等待消抖的?

那倒没有,轻触开关消抖是肯定要的,不会耗时太多。如果是完全没有抖动噪音的开关,那去掉那个等待的时间就是不消抖的了嘛。

多哈达 发表于 2012-10-27 20:22:03

MARK!   

tranquilly86 发表于 2012-10-29 08:07:12

blavy 发表于 2012-10-27 19:44 static/image/common/back.gif
那倒没有,轻触开关消抖是肯定要的,不会耗时太多。如果是完全没有抖动噪音的开关,那去掉那个等待的时间 ...

你好!我意思不是说要不要消抖,是想问下,可不可以消抖不是在延时等待的,而是查询的方式

lin338 发表于 2012-10-29 23:24:33

标记{:victory:}

blavy 发表于 2012-11-2 16:48:27

tranquilly86 发表于 2012-10-29 08:07 static/image/common/back.gif
你好!我意思不是说要不要消抖,是想问下,可不可以消抖不是在延时等待的,而是查询的方式 ...

那没有。

lcmdw 发表于 2012-11-2 17:10:57

mark{:smile:}

zhoufeng332 发表于 2014-11-26 21:39:55

学习学习
页: [1]
查看完整版本: 发一个多功能的按键检测程序,非常精简。