打印
[程序源码]

东拼西凑按键扫描,一键三值,队列循环

[复制链接]
1324|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
ailingg|  楼主 | 2017-12-11 10:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 ailingg 于 2017-12-11 10:34 编辑


       安富莱的按键队列和单按键扫描,本坛的“datouyuan”的“我根据5个IO口扫描25个按键写的的代码”里的按键键值表。这种搞法很费ram,但组合键相当方便。
每个按键都有自己的去抖、长按、重复键值计数器变量,每个按键有自己的参数,包括长按、重复键值、去抖的计数值,每个按键有按下、弹起、长按三个键值。
       组合键,可以任意组合。
/************************************************************************************************************
* 文 件 名   : bsp_Key.c
* 负 责 人   : lll
* 创建日期   : 2017年11月28日
* 文件描述   : 按键扫描与按键事件处理
* 版权说明   : Copyright (c) 2008-2017   xx xx xx xx 技术有限公司
* 其    他   :
* 修改日志   :
**************************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>

#include "bsp_Vacuum260.h"
#include "bsp_Key.h"
#include "bsp_LED.h"
#include "bsp_74hc595.h"



typedef struct
{
    void  (*KeyEventFunc)(void);
    UINT16 LongTime;                        /* 长按键生效时间,0表示非长按键  */
    UINT8  ReentryTime;                     /* 长按键重复生效时间,0表示键值不重复生效  */
    UINT8  FiltTime;                        /* 去抖滤波时间      */
    UINT8  KeyVal;                          /* 按键起始键值,每个按键有三个键值,enum定义依次为“按下”“弹起”“长按”  */
   
}KeyElem_TypeDef;

// 按键元素表
KeyElem_TypeDef const KeyElem[KEY_QTY] = {
    { Key_StopKey,           600,    0,    4,   KEY_0_DOWN },
    { Key_VacuumTimeSet,     0,      0,    4,   KEY_1_DOWN },
    { Key_SealTimeSet,       0,      0,    4,   KEY_2_DOWN },
    { Key_DigitInc,          150,    20,   4,   KEY_3_DOWN },
    { Key_DigitDec,          150,    20,   4,   KEY_4_DOWN },
    { Key_HeatLevelSet,      0,      0,    4,   KEY_5_DOWN },
    { Key_SealTimeMaxInc,    150,    20,   4,   KEY_6_DOWN }, // KEY_0和KEY_3组合
    { Key_SealTimeMaxDec,    150,    20,   4,   KEY_7_DOWN }  // KEY_0和KEY_4组合
};


KeyMode_TypeDef KeyMode;

KeyMsg_TypeDef  KeyMsg;

KeyQueue_TypeDef KeyQueue;                   /* 键值循环队列结构变量  */

QelemType KeyCode[KEY_QTY];                  /* 循环队列键值数组  */

persistent KeyVar_TypeDef  KeyVar[KEY_QTY];  /* 按键扫描结构变量 */

persistent volatile UINT8 KeysState;
persistent volatile UINT8 Key_SetPoint;
persistent volatile UINT8 *gParamPtr;        /* 读写参数缓存区指针  */


extern UINT8 ByteMod( UINT8 dividend, UINT8 divisor );
extern UINT8 ByteDiv( UINT8 dividend, UINT8 divisor );
/***********************************************************************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url]  按键参数初始化
************************************************************************************************************/
void Key_Init(void)
{
    UINT8 i;
   
    KeyQueue.Buf = KeyCode;
    KeyQueue.rear = 0;
    KeyQueue.front = 0;   
   
    for( i = 0; i < KEY_QTY; i++ )
    {
        KeyVar[i].KeyGet = FALSE;
        KeyVar[i].KeyState = KEY_UP;
        KeyVar[i].FiltCnt = 0;
        KeyVar[i].LongCnt = 0;
        KeyVar[i].ReentryCnt = 0;
    }
    Key_SetPoint = NO;
    KeyMode.bMode = 0; //0=直接加减方式,1=位选加减方式
}

/**************************************************************************************************************
* @brief  按键队列压入键值
* @param  _KeyCode
**************************************************************************************************************/
void Key_QueueIn( UINT8 _KeyCode )
{
    KeyQueue.Buf[KeyQueue.rear] = _KeyCode;

    if( ++KeyQueue.rear >= KEY_QTY )
    {
        KeyQueue.rear = 0;
    }
}
/**************************************************************************************************************
* @brief   按键队列弹出键值
* [url=home.php?mod=space&uid=266161]@return[/url]  键值
**************************************************************************************************************/
UINT8 Key_QueueOut( void )
{
    UINT8 ret;

    if( KeyQueue.front == KeyQueue.rear )
    {
        return KEY_NONE;
    }
    else
    {
        ret = KeyQueue.Buf[KeyQueue.front];

        if( ++KeyQueue.front >= KEY_QTY )
        {
            KeyQueue.front = 0;
        }
        return ret;
    }
}
/**************************************************************************************************************
* @brief   按键扫描,获取按键ID
*          矩阵式扫描,RB7,RB6 为扫描读取口;RB2,RB1,RB0通过NPN三极管反向后作为键盘的扫描口。
* @Param   pPORTx:键盘扫描所在端口指针   
*          pKeyID: 按键ID顺序存放数组指针                                    
* [url=home.php?mod=space&uid=536309]@NOTE[/url]    按键引脚硬件分布
*          矩阵式扫描,RB7,RB6 为扫描读取口;RB2,RB1,RB0通过NPN三极管反向后作为键盘的扫描口。
*
*                          RB0_____。_____|_____________|____________
*                                         |0            |3
*                          RB1_____。_____|_____________|____________
*                                         |1            |4
*                          RB2_____。_____|_____________|____________
*                                         |2            |5
*                          RB6____________|             |
*                                                       |
*                          RB7__________________________|                                          
**************************************************************************************************************/

/* 根据硬件情况,扫描从低位左移,检测从高位右移,因此按键ID表的排列顺序是3,0,4,1, 5,2   */
const UINT8 KeyID_Table[3][2] = { { 3, 0 },{ 4, 1 },{ 5, 2 } };

/* 按键扫描后,按键ID按0-N的排列顺序存入数组 */
UINT8 KeyID[KEY_QTY] = { 0 ,1, 2, 3, 4, 5, 6, 7 };

static void GetKeyID( PORT_TypeDef *pPORTx, UINT8 *pKeyID )
{
    UINT8 idx, i, j;

    for( i = 0; i < KEY_QTY; i++ )
    {
        pKeyID[i] = 255;                         /* 255表示按键未按下 */
    }
    for( i = 0; i < 3; i++ )                     /* 扫描所有按键  */
    {
        *pPORTx &= ~KEY_SCAN_PINS_SET;
        NOP();
        NOP();
        *pPORTx |= 1 << i;
        NOP();
        NOP();
        for ( j = 0; j < 2; j++ )
        {
            if( ( *pPORTx & ( 0x80 >> j ) ) == 0 )
            {
                    idx = KeyID_Table[i][j];
                    pKeyID[idx] = KeyID_Table[i][j]; /* 按键ID按0-N的顺序存入 数组     */
            }
        }
    }      
}
/**************************************************************************************************************
* @brief  单个按键扫描
*         完成按键去抖,获取按键“按下”“弹起”“长按”三个键值,并将键值压入队列。
* @Param  idx:按键索引,表示扫描哪一个按键
*         KeyID:
**************************************************************************************************************/
static void GetKeyValue( UINT8 idx, UINT8 keyID )
{
    KeyVar_TypeDef  *pKeyVar;
    KeyElem_TypeDef const *pKeyElem;
   
    pKeyVar  = &KeyVar[idx];
    pKeyElem = &KeyElem[idx];
   
    if( keyID != 255 )
    {   
        if( pKeyVar->FiltCnt < pKeyElem->FiltTime )
        {
            pKeyVar->FiltCnt = KeyElem->FiltTime;
        }
        if( pKeyVar->FiltCnt < 2 * KeyElem->FiltTime )
        {
            pKeyVar->FiltCnt++;
        }
        else
        {
            /* 如果长按时间设置大于0,说明是长按键  */
            if( pKeyElem->LongTime > 0 )
            {
                if( pKeyVar->LongCnt < pKeyElem->LongTime )
                {
                    if( ++pKeyVar->LongCnt == pKeyElem->LongTime )
                    {
                        pKeyVar->KeyState = LONG_DOWN;
                        Key_QueueIn( pKeyElem->KeyVal + 2 );    /* 获取当前按键“长按”键值,一次按下只获取一次  */
                    }
                }
                else
                {   
                    /* 如果键值重复生效时间设置大于0,说明需要重复获取键值  */
                    if( pKeyElem->ReentryTime > 0 )
                    {
                        if( ++pKeyVar->ReentryCnt > pKeyElem->ReentryTime )
                        {
                            pKeyVar->ReentryCnt = 0;
                            pKeyVar->KeyState = KEY_DOWN;
                            Key_QueueIn( pKeyElem->KeyVal );   /* 长按键重复获取“按下”键值  */
                        }
                    }
                    
                }
            }
            if( pKeyVar->KeyGet == FALSE )                    /* 非长按键一次按下只获取一次“按下”键值  */
            {
                pKeyVar->KeyGet = TRUE;
                pKeyVar->KeyState = KEY_DOWN;
                Key_QueueIn( pKeyElem->KeyVal );               /* 当前按键“按下”键值压入按键队列  */
            }
        }
    }
    else/* 判断按键释放  */
    {
        if( pKeyVar->FiltCnt > KeyElem->FiltTime )
        {
            pKeyVar->FiltCnt = KeyElem->FiltTime;
        }
        if( pKeyVar->FiltCnt > 0 )
        {
            pKeyVar->FiltCnt--;
        }
        else
        {
            if( pKeyVar->KeyGet == TRUE )                    /* 当前按键“弹起”键值只获取一次     */
            {
                pKeyVar->KeyGet = FALSE;
                Key_QueueIn( pKeyElem->KeyVal + 1 );          /* 当前按键“弹起”键值压入按键队列  */
            }
            pKeyVar->LongCnt = 0;
            pKeyVar->ReentryCnt = 0;
            pKeyVar->KeyState = KEY_UP;            
        }
    }
}
/**************************************************************************************************************
* @brief  按键扫描函数
*         矩阵式扫描,RB7,RB6 为扫描读取口;RB2,RB1,RB0反向后作为键盘的行扫描。
* @note   RB2,RB1,RB0反向后也是数码管和 LED的位驱动口,因此键盘扫描前要先保存
*         扫描端口的位值,扫描结束后还原。
* @Param  *PORTx    键盘扫描所在端口   
**************************************************************************************************************/
void KeyScan( PORT_TypeDef *pPORTx )
{
    UINT8 i;
    UINT8 PortKeep;

    C595_Pin_OE = 1;                    /* 关闭 74ls595 三态缓冲输出  */
    PortKeep = *pPORTx;                 /* 保存扫描端口  */   
   
    GetKeyID( pPORTx, KeyID );          /* 获取按键ID */
   
    *pPORTx = PortKeep;                 /* 还原 PORT 口  */
    C595_Pin_OE = 0;                    /* 允许 74595 三态缓冲输出      */  
   
    // 组合键ID
    if( ( KeyID[0] != 255 ) && ( KeyID[3] != 255 ) )
    {
        KeyID[6] = 6;
        KeyID[3] = 255;
    }
    if( ( KeyID[0] != 255 ) && ( KeyID[4] != 255 ) )
    {
        KeyID[7] = 7;
        KeyID[4] = 255;
    }   
    KeysState = 0;
    for( i = 0; i < KEY_QTY; i++ )
    {
        GetKeyValue( i, KeyID[i] );
        KeysState |= KeyVar[i].KeyState;
    }
}
/*************************************************************************************************************
* @brief  按键事件处理函数
*         采用状态机判断键值,根据键值调用相应的按键事件函数,键值由按键队列弹出。
*************************************************************************************************************/
void KeyEventFunc( void )
{
    switch( Key_QueueOut( ) ){

        case KEY_0_DOWN:
            KeyElem[0].KeyEventFunc();
            break;
        case KEY_0_LONG:
            Key_SetPoint = SET_SEAL_MAX;
            DispBuffer[DPYCC_C1_TENS] = Segment[ ByteDiv( ParamBuf[P_SEAL_MAX_IDX], 10 ) ] | 0x80;
            DispBuffer[DPYCC_C2_UNIT] = Segment[ ByteMod( ParamBuf[P_SEAL_MAX_IDX], 10 ) ];
            BeepCtrl = SIGNAL;
            break;
        case KEY_0_UP:
            if( Key_SetPoint == SET_SEAL_MAX )
            {
                KeyMsg.EnterKey = YES;
                Key_SetPoint = NONE;
            }
            break;              
        case KEY_1_DOWN:
            KeyElem[1].KeyEventFunc();
            break;
        case KEY_2_DOWN:
            KeyElem[2].KeyEventFunc();
            break;
        case KEY_3_DOWN:
            KeyElem[3].KeyEventFunc();
            break;
        case KEY_4_DOWN:
            KeyElem[4].KeyEventFunc();
            break;
        case KEY_5_DOWN:
            KeyElem[5].KeyEventFunc();
            break;
        case KEY_6_DOWN:
            KeyElem[6].KeyEventFunc();
            break;
        case KEY_7_DOWN:
            KeyElem[7].KeyEventFunc();         
            break;
        default:
            break;
    }
}


相关帖子

沙发
ailingg|  楼主 | 2017-12-11 10:09 | 只看该作者
本帖最后由 ailingg 于 2017-12-11 11:23 编辑
#ifndef __bsp_Key_H__
#define __bsp_Key_H__

#include<pic.h>
#include"genericTypeDef.h"



typedef volatile unsigned char PORT_TypeDef;




#define LID_PRESS_ERR            1
// Logic Switch non
#define NEVER                    0
#define ALREADY                  1

// Logic Switch
#define YES                      1
#define NO                       0
#define ENABLE                   1
#define DISABLE                  0
// Key state
#define KEY_UP                   0
#define KEY_DOWN                 1
#define LONG_DOWN                2
// Beep action
#define ALARM                    3  
#define SIGNAL                   4

// Parameter Set Key
#define SET_VACUUM_TIME          5
#define SET_SEAL_TIME            6
#define SET_SEAL_MAX             7
#define NONE                     0


#define KEY_QTY                  8

// Key pins position,                           // PORTB : I I x x  x O O O
#define KEY_SCAN_PINS_SET        0x07           // 因为 RB2 ~ RB0 经过UNL2003反向后作为键盘的行扫描,所以判断是否有按键按下是将所有扫描引脚置高
#define KEY_PORT_DIR             TRISB
#define KEY_PORT                 PORTB

#define GPIO_PIN_KeyScan0        PORTBbits.RB0  // 数码管与按键共用扫描口,在 bsp_LED.h
#define GPIO_PIN_KeyScan1        PORTBbits.RB1
#define GPIO_PIN_KeyScan2        PORTBbits.RB2

/* 按键循环队列结构 */
typedef volatile unsigned char QelemType;
typedef volatile unsigned char QelemInxType;   
   
typedef struct{
   
    QelemType *Buf;
    QelemType rear;
    QelemType front;
    QelemType Lock;
   
}KeyQueue_TypeDef;

extern KeyQueue_TypeDef KeyQueue;

/* 按键模式 */
typedef struct
{
    unsigned bMode    :1;
    unsigned bToggle  :1;
    UINT16   ToggleCnt;
   
}KeyMode_TypeDef;

extern KeyMode_TypeDef KeyMode;

/** 按键消息变量 */
typedef struct{
   
    unsigned Emergency     :1;
    unsigned DataRenew     :1;
    unsigned EnterKey      :1;
    unsigned bSync         :1;
   
    UINT8    Command;
   
}KeyMsg_TypeDef;

extern KeyMsg_TypeDef KeyMsg;


/** 按键扫描变量 */
typedef struct
{
    unsigned KeyGet   :1;  
    unsigned KeyState :2;
    unsigned FuncID   :4;
   
    UINT8    FiltCnt;
    UINT8    ReentryCnt;
    UINT16   LongCnt;
   
}KeyVar_TypeDef;

extern persistent KeyVar_TypeDef KeyVar[KEY_QTY];



extern persistent UINT8 KeysState;
extern persistent UINT8 Key_SetPoint;

/*
        定义键值代码, 必须按如下次序定时每个键的按下、弹起和长按事件
        推荐使用enum, 不用#define,原因:
        (1) 便于新增键值,方便调整顺序,使代码看起来舒服点
        (2) 编译器可帮我们避免键值重复。
*/
typedef enum
{
        KEY_NONE = 0,                        /* 0 表示按键事件 */

        KEY_0_DOWN,                                /* 1键按下 */
        KEY_0_UP,                                /* 1键弹起 */
        KEY_0_LONG,                                /* 1键长按 */

        KEY_1_DOWN,                                /* 2键按下 */
        KEY_1_UP,                                /* 2键弹起 */
        KEY_1_LONG,                                /* 2键长按 */

        KEY_2_DOWN,                                /* 3键按下 */
        KEY_2_UP,                                /* 3键弹起 */
        KEY_2_LONG,                                /* 3键长按 */

        KEY_3_DOWN,                                /* 4键按下 */
        KEY_3_UP,                                /* 4键弹起 */
        KEY_3_LONG,                                /* 4键长按 */

        KEY_4_DOWN,                                /* 5键按下 */
        KEY_4_UP,                                /* 5键弹起 */
        KEY_4_LONG,                                /* 5键长按 */

        KEY_5_DOWN,                                /* 6键按下 */
        KEY_5_UP,                                /* 6键弹起 */
        KEY_5_LONG,                                /* 6键长按 */

        /* 组合键 */
        KEY_6_DOWN,                                /* 7键按下 */
        KEY_6_UP,                                /* 7键弹起 */
        KEY_6_LONG,                                /* 7键长按 */

        KEY_7_DOWN,                            /* 8键按下 */
        KEY_7_UP,                                /* 8键弹起 */
        KEY_7_LONG,                            /* 8键长按 */
}KEY_ENUM;

// 函数声明,其他文件条用
extern void Key_Init( void );
extern void KeyScan( PORT_TypeDef *PORTx );
extern void KeyEventFunc(void);
extern void BoardOutputKill( PORT_TypeDef *PORTx );

// 函数声明,本文件内调用
//extern BOOL IsKey_1( void );
//extern BOOL IsKey_2( void );
//extern BOOL IsKey_3( void );
//extern BOOL IsKey_4( void );
//extern BOOL IsKey_5( void );
//extern BOOL IsKey_6( void );
//extern BOOL IsKey_7( void );
//extern BOOL IsKey_8( void );

extern void Key_VacuumTimeSet(void);
extern void Key_SealTimeSet(void);
extern void Key_DigitDec(void);
extern void Key_DigitInc(void);
extern void Key_HeatLevelSet(void);
extern void Key_StopKey(void);
extern void Key_SealTimeMaxInc( void );
extern void Key_SealTimeMaxDec( void );

#endif

使用特权

评论回复
板凳
zqx1000| | 2018-4-10 23:37 | 只看该作者
kankan

使用特权

评论回复
地板
tlled| | 2018-4-11 08:40 | 只看该作者
有对应的硬件部分电路吗?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

18

主题

167

帖子

2

粉丝