【按键】短按,长按,按键释放,三种模式的按键扫描程序(软件消抖动)

先来说一下这三种模式的意思:

1. 短按模式:单击按键时,返回一次有效按键值;长按时也只返回一次有效按键值。这样可以有效地排除因不小心长按带来的返回多次有效按键,进而执行多次按键处理程序。

2. 长按模式: 单击按键时,返回一次有效按键;长按时,返回多次有效按键值。这样可以很快的调节某个较大的参数,比如时间的时分秒参数。

3. 按键释放模式:这个模式与短按模式是相对的。短按模式只要按键按下去,立即返回有效键值,进而试行按键处理程序;二按键释放模式,却是要等到按下键,释放之后,才会返回有效键值,进而执行按键处理程序。

接下来说一下扫描程序:
1)采用的是轮询的方式(非中断)
2)消抖动的方式:多次扫描,来确定按键值。下面的程序的是设定了5次。主要是根据扫描周期来确定,次数的多少。
注:
扫描周期:从进入按键扫描程序开始,直到到下一次进入按键扫描程序时 结束,之间所用的时间。

下面是整个按键扫描程序的源码,可以读一读,语句都很简单,而且每一句都有注释,一步一步看下去,应该能明白。
如果不明白,可以留言谈论。
以下是 KeyScan.c 文件的内容,

//======================================================
//KeyScan.c
//======================================================
//注意:该宏定义,定义在keyscan.h文件中
//#define KEYDEBOUNCE 0x05 //消抖动,按键扫描次数。如果连续5次都是扫描的都是相同键值,则认为是有效键值,否则是误触发

unsigned int g_uiCurrKey;            //当前按键值
unsigned int g_uiLastKey;            //上次按键值
unsigned int g_uiKeyScanCount;       //按键扫描计数,作用:消抖动

unsigned int g_uiKeyDown;            //键被按下,返回的键值。 作用:单次按键,单次返回有效键值;按住不放,也只返回被按下的一个键值
unsigned int g_uiKeyRelease;         //键被释放后,返回的键值。 作用:只有按下的按键被释放后,才返回按下的键值
unsigned int g_uiKeyContinue;        //键连续按键,重复返回的键值。 作用:只要按住不放,就会重复地返回相同键值

//P0口的低八位作为按键
//没有按键时,返回的是0xff,
void Int_Key_Scan(void)
{
    static unsigned short LastReadKey;        //上次从IO口读取的键值 ,注意是静态变量
    unsigned short CurrReadKey;               //当前从IO口读取的键值

    CurrReadKey = P0 & 0x00ff;                //获取当前的键值

    if(CurrReadKey == LastReadKey)            //如果当前读取的键值与上次从IO口读取的键值相同
    {
        if(g_uiKeyScanCount >= KEYDEBOUNCE)   //首先判断是否大于等于debounce的设定值(即是,是否大于等于设定的采样次数)
        {
            //按住不放,多次响应
            g_uiCurrKey = CurrReadKey;                //如果是,将当前的读取值判定为有效按键值(如果是,在采样周期中,都是这个值,则判定为有效按键值)

            //按住不放只响应一次
            if(g_uiPreKeyValue == g_uiCurrKey)
            {
                g_uiKeyDown = 0xff;                    //没有键值
            }
            else
            {
                g_uiKeyDown = g_uiCurrKey;             //如果不同,按键有效,(就是第一次有效值时)
            }

            //按键释放时,按键值才有效 
            if(g_uiCurrKey == 0xff)                  //当有效按键值从非0到0的状态时(即是,从有按键到无按键,表示已经释放了),表示之前按键已经释放了
            {
                g_uiKeyRelease = g_uiPreKeyValue;
            }

            g_uiLastKey = g_uiCurrKey;               //记录上次有效按键值
        }
        else                                       //如果否,则debounce加一(如果否,则继续采样键值)
        {
            g_uiKeyScanCount++;
        }
    }
    else                                          //如果当前读取的键值与上次从IO口读取的键值不同,说明按键已经变化
    {
        g_uiKeyDown = 0xff;                       //放开按键后第一次进入扫描程序,清零g_uiKeyDown.作用:消除一个BUG(你猜BUG是什么?)
        g_uiKeyScanCount = 0;                     //清零之前的按键的debounce计数
        LastReadKey = CurrReadKey;                //将当前读取的键值记录为上次读取的按键值
    }   
}

以下是KeyScan.h文件内容

//======================================================
//KeyScan.h
//======================================================

//宏定义
#define KEYDEBOUNCE 0x05 //消抖动,按键扫描次数。如果连续5次都是扫描的都是相同键值,则认为是有效键值,否则是误触发

//声明变量
extern unsigned int g_uiCurrKey;            //当前按键值
extern unsigned int g_uiLastKey;            //上次按键值
extern unsigned int g_uiKeyScanCount;       //按键扫描计数,作用:消抖动

extern unsigned int g_uiKeyDown;            //键被按下,返回的键值。 作用:单次按键,单次返回有效键值;按住不放,也只返回被按下的一个键值
extern unsigned int g_uiKeyRelease;         //键被释放后,返回的键值。 作用:只有按下的按键被释放后,才返回按下的键值
extern unsigned int g_uiKeyContinue;        //键连续按键,重复返回的键值。 作用:只要按住不放,就会重复地返回相同键值

//函数声明
void Int_Key_Scan(void);

使用注意:
1.作为按键使用的相应IO口,必须设置为输入模式(如果是51单片机的话,无需关心)
2.按键的硬件连接必须是一端接GND,一端接IO口。

下面介绍一下程序的使用方法:
这里以51单片机的 按键点亮和熄灭LED灯作为例子。
硬件:
1)按键使用单片机的P0端口
2)LED灯使用P1.0的IO口,低电平点亮

返回的按键值:
没有键按下, 返回键值是0xFF
如果P0.0按下,返回键值是0xFE
如果P0.1按下,返回键值是0xFD
如果P0.2按下,返回键值是0xFB
如果P0.3按下,返回键值是0xF7

如果P0.7按下,返回键值是0x7F

下面是例子的参考源码:

//======================================================
//main.c
//======================================================
#include "reg51.h"
#include "KeyScan.h"

sbit LED = P1.0;       //定义LEDIO口

void main(void)
{
    P0 = 0xff;
    LED = 0;           //点亮LED

    while(1)
    {
        Int_Key_Scan();      //按键扫描程序

        //第一种:KeyDown的使
        //单按时和长按时,都只返回一次有效键值(无需等到按键释放,就可以返回有效键值)
        switch(g_uiKeyDown)
        {
            case 0xFE:
                        //P0.0按键程序
                        LED = !LED; 
                        break;

            case 0xFD:
                        //P0.1按键程序 
                        //...
                        break;

            case 0xFB:
                        //P0.2按键程序 
                        //...
                        break;

            case 0xF7:
                        break;

            case 0xEF:
                        break;

            case 0xDF:
                        break;

            case 0xBF:
                        break;

            case 0x7F:
                        break;      

            case 0xFF:
                        //没有按键程序
                        //...
                        break;
        }

        //第二种:KeyRelease的使用
        //只有当按键释放之后,才返回一次有效键值,即是按键释放后,才执行相应的函数
        switch(g_uiKeyRelease)
        {
            case 0xFE:
                         //P0.0按键程序
                         LED = !LED; 
                         break;

            case 0xFD:
                        //P0.1按键程序 
                        //...
                        break;

            case 0xFB:
                        //P0.2按键程序 
                        //...
                        break;

            case 0xF7:
                        break;

            case 0xEF:
                        break;

            case 0xDF:
                        break;

            case 0xBF:
                        break;

            case 0xEF:
                        break;      

            case 0xFF:
                        //没有按键程序
                        //...
                        break;  
        }

        //第三种:KeyContinue的使用
        //1)单次按键(非长按),返回一次有效值。
        //2)长按,返回多次相同有效值
        switch(g_uiKeyContinue)
        {
            case 0xFE:
                         //P0.0按键程序
                         LED = !LED; 
                         break;

            case 0xFD:
                        //P0.1按键程序 
                        //...
                        break;

            case 0xFB:
                        //P0.2按键程序 
                        //...
                        break;

            case 0xF7:
                        break;

            case 0xEF:
                        break;

            case 0xDF:
                        break;

            case 0xBF:
                        break;

            case 0xEF:
                        break;      

            case 0xFF:
                        //没有按键程序
                        //...
                        break;  
        }
    }

}

Pillar Peng
2015.5.25 - 18:23

扩展篇:
该方法的矩阵键盘程序

你可能感兴趣的:(按键)