通用按键消抖函数 -- 数据与过程分离
来源:互联网 发布:下载wps办公软件mac版 编辑:程序博客网 时间:2024/06/06 00:09
项目里经常处理按键消抖,
思路是:
//key.h 头文件-------------------------------------------------------------#ifndef _KEY_H#define _KEY_H#define _KEY_NONE 0#define _HAS_NO_KEY 0#define _HAS_KEY_DOWN 1#define _HAS_KEY_SURE 2#define _HAS_KEY_WAITUP 3#define _REENTER 1#define _NO_REENTER 2typedef struct { WORD PreDownKey; //上次检测到的键 BYTE KeyState; //状态 WORD SameKeyCntr; //同一键检测到按下的次数 WORD CurKey; //当前检测到的键, 用于处理长按的情况 BYTE (*KeyDownCallBack)(WORD, WORD); //键确认按下的回调函数指针 void (*KeyUpCallBack)(WORD); //键抬起的回调函数指针} struct_KeyInfo;void DitherlessKey(struct_KeyInfo* pInfo); //消抖的处理函数#endif//_KEY_H//消抖动的代码--------------------------------------------------------------#include "Key.h"//定时消抖的按键处理函数, 通常在定时中断中调用, void DitherlessKey(struct_KeyInfo* pInfo){ switch(pInfo->KeyState) { case _HAS_NO_KEY: pInfo->SameKeyCntr = 0; if(pInfo->CurKey != _KEY_NONE) { pInfo->KeyState = _HAS_KEY_DOWN; //进入有键按下状态 } break; case _HAS_KEY_DOWN: if(pInfo->PreDownKey == pInfo->CurKey) { pInfo->KeyState = _HAS_KEY_SURE; //确认键已按下状态 } else { pInfo->KeyState = _HAS_NO_KEY; //回到无键状态 } break; case _HAS_KEY_SURE: if(pInfo->CurKey == pInfo->PreDownKey) { pInfo->KeyState = _HAS_KEY_WAITUP; if(pInfo->KeyDownCallBack) { //这里回调函数的返回值决定了是否允许出现长按的情况 if(_REENTER == pInfo->KeyDownCallBack(pInfo->CurKey, pInfo->SameKeyCntr)) { pInfo->KeyState = _HAS_KEY_SURE; ++pInfo->SameKeyCntr; } } } else { pInfo->KeyState = _KEY_NONE; } break; case _HAS_KEY_WAITUP: if(pInfo->CurKey != pInfo->PreDownKey) { pInfo->KeyState = _HAS_NO_KEY; if(pInfo->KeyUpCallBack) { pInfo->KeyUpCallBack(pInfo->PreDownKey); } } break; default: break; } pInfo->PreDownKey = pInfo->CurKey; //保存上次按键值 return;}//应用代码片段---------------------------------------------------------------------------------------......//声明按键回调函数BYTE KeyDownCallBack(WORD Key, WORD Times);BYTE KeyDownCallBack2(WORD Key, WORD Times);//按键处理数据结构static struct_KeyInfo g_KeyInfo1 = {0, 0, 0, 0, KeyDownCallBack};static struct_KeyInfo g_KeyInfo2 = {0, 0, 0, 0, KeyDownCallBack2};////////////////////////////////////////////////////////////////////////////TIMER2 initialize - prescale:1024// WGM: Normal// desired value: 100Hz// actual value: 101.024Hz (1.0%)#pragma interrupt_handler timer2_ovf_isr:iv_TIM2_OVFvoid timer2_ovf_isr(void){ WORD temp; _TIMER2_LOAD; //reload counter value temp = Read165() ^ _KEY_MASK; //输入信息 g_KeyInfo1.CurKey = temp & 0x00FF; DitherlessKey(&g_KeyInfo1); g_KeyInfo2.CurKey = temp & 0xFF00; //同一个消抖函数处理不同的按键 DitherlessKey(&g_KeyInfo2);}//在回调函数中处理具体的键值BYTE KeyDownCallBack(WORD Key, WORD Times){ switch(Key) { case _KEY_F2: if(Times < 200) //长按 2s { return _REENTER; //2s内允许长按 } break; case _KEY_CLR_CNTR: if(Times < 1000) //四个键长按10s { return _REENTER; //允许长按 } default: break; } g_DownKey = Key; //输出按键信息, 给主循环处理. 这个回调函数是由定时中断中的代码调用的. return _NO_REENTER; //其余键, 不允许长按}BYTE KeyDownCallBack2(WORD Key, WORD Times){ switch(Key) { case _KEY_I: if(Times == 20) //数值 x 10 ms { g_DownKey |= _KEY_I; } else if(Times == 300) //长按3s时执行一个动作, 只会执行一次 { g_I++; } break; default: break; } return _REENTER; //始终允许长按, 直到键抬起}
本质就是个状态机.
_HAS_NO_KEY:未按下,
_HAS_KEY_DOWN:检测到一次按下,
_HAS_KEY_SURE:又检测到一次按下,
_HAS_KEY_WAITUP:等待键抬起.
状态转换图如下:
_HAS_NO_KEY