/************************************************************************************************************************** 建立头文件"key.h" **************************************************************************************************************************/ #ifndef __KEY_H #define __KEY_H #include "stm8_macro.h" /* 说 明: 1.按事件 :按键按下时即产生一次按事件; 2.长按事件:按下时间超过(最短长按时间)即产生长按事件,任何一次出现的长按操作都属于 长按事件;长按时,会一直产生长按事件直至长按结束,且每秒更新一次长按事件, 每秒产生的长按事件不一样。 3.短按事件:在(最短长按时间)内按下松开之后,在接下来的(最大双击间隔时间)内若没有第二 次短按操作即产生一次短按事件; 4.双击事件:2次短按操作间隔时间<(最大双击间隔时间),则2次短按操作为1次双击事件, 且2次短按都取消。 特别操作情况定义: 1.短按操作和长按操作间隔<(最大双击间隔时间),以及,长按操作和短按操作间隔<(最大双击间隔时间),均不产生双击事件; 2.(最大双击间隔时间)内,连续n次短按操作,最多只产生1次双击事件和n次按事件 */ //////////////////////// 参数配置 //////////////////////// //长按识别的最大时间,单位:秒,若长按超过这个时间,则输出此时间 #define LONGKEY_RECOGNITION_MAXTIME (30ul) ////////////////////////////////////////////////////////// #if LONGKEY_RECOGNITION_MAXTIME >= (256 - 10) #error "LONGKEY_RECOGNITION_MAXTIME is too large!" #endif //按键事件 #define KEY_NOKEY ((u8)0) //无 #define KEY_DOWN ((u8)1) //按事件, 按键按下时即产生一次按事件 #define KEY_SHORT ((u8)2) //短按事件, #define KEY_DOUBLECLICK ((u8)3) //双击事件 #define KEY_LONG_S(n) ((u8)10+n) //长按事件,按下超过(n)秒 //Input Attribute typedef struct { vu8 (*IsPress)(void); //是否按下按键,输出当前按键状态 1:按下 0:未按下 vu16 LongPressTime; //最短长按时间,按下时间>LongPressTime,表示长按,单位MS vu16 DCIntervalTime; //最大双击间隔时间,双击时,两次短按之间的最长间隔时间,单位MS } KeyAttr_TypeDef; //Ouput key status typedef struct { vu8 KeyEvent; //按键事件,KEY_NOKEY,KEY_DOWN,KEY_SHORT,KEY_DOUBLECLICK,KEY_LONG_S(n) vu8 Busy; //忙状态,0:空闲,1:忙,当按键按下及短按后的DCIntervalTime时间内为忙状态 } KeyStat_TypeDef; //按键控制块 typedef struct { KeyAttr_TypeDef Attr; //Input Attribute, 配置按键属性 KeyStat_TypeDef Status;//Ouput key status,输出按键状态:事件/闲忙 } Key_TypeDef; void Key_Init(Key_TypeDef* key, u8 (*input)(void), u16 longpresstime, u16 dcitvtime); KeyStat_TypeDef GetKeyStatus(Key_TypeDef *key_structure); #endif /*__KEY_H*/ /************************************************************************************************************************** 建立源文件"key.c" **************************************************************************************************************************/ #include "key.h" //---------------------------------------------------------------- /******** 驱动层 ********/ /* @Function : 按键的初始化 @Input : key 按键控制块 input 按键状态 1:按下按键 0:未按下按键 longpresstime 最短长按时间,按下时间>longpresstime,表示长按,单位MS dcitvtime 最大双击间隔时间(双击时,两次短按之间的最长间隔时间),单位MS @Output : None **/ void Key_Init( Key_TypeDef* key, //按键控制块 u8 (*input)(void), //按键状态 u16 longpresstime, //最短长按时间 u16 dcitvtime) //最大双击间隔时间 { key->Attr.IsPress = input; key->Attr.LongPressTime = longpresstime; //最短长按时间 ms key->Attr.DCIntervalTime = dcitvtime; //双击最长间隔时间 ms key->Status.KeyEvent = KEY_NOKEY; key->Status.Busy = 0; } /* @Function : 按键扫描(10MS扫描一次) @Input : key_input 当前按键状态,是否按下按键 0:未按下,1:按下 longpresstime 按键超过该时间为长按,单位ms @Output : 0 KEY_NOKEY 无 1 KEY_DOWN 按下按键瞬间 2 KEY_SHORT 短按,按下放开,1次短按操作后,间隔0.5s内没有短按操作 n>10 长按,按下超过(n-10)秒 **/ static u8 Key_driver(u8 key_input, u16 longpresstime) { u8 res = KEY_NOKEY; static u8 status; static u16 keypress_count; switch(status) { case 0: //确定按键是否按下 if(key_input) { status = 1; keypress_count = 0; } break; case 1: //消斗 if(key_input) { status = 2; res = KEY_DOWN; } else { status = 0; } break; case 2: if(!key_input) { res = KEY_SHORT; status = 0; } else if(++keypress_count >= longpresstime/10) { res = KEY_LONG_S(1);//按下超过1s status = 3; } break; case 3: ++keypress_count; if(keypress_count/100 >= LONGKEY_RECOGNITION_MAXTIME)//按下超过30s { res = 10 + LONGKEY_RECOGNITION_MAXTIME; } else { res = (u8)(10 + (u8)(keypress_count/100));//按下超过(keypress_count/100)秒 } if(!key_input) { status = 0; } break; } return res; } /* @Function : 按键扫描(10MS扫描一次),并将按键的状态记录下来 @Input : keyattr 按键属性 @Output : 按键状态 **/ KeyStat_TypeDef GetKeyStatus(Key_TypeDef *key_structure) { static u8 status; static u16 key_count; u8 key_temp; u8 input; u16 longpresstime, intervaltime; u8 keyevent = KEY_NOKEY; u8 busy = 0; KeyStat_TypeDef res; input = key_structure->Attr.IsPress(); longpresstime = key_structure->Attr.LongPressTime; intervaltime = key_structure->Attr.DCIntervalTime; key_temp = Key_driver(input, longpresstime); //双击判别 switch(status) { case 0: if(key_temp == KEY_SHORT)//检测到第一次短按 { key_count = 0; status = 1; } else { keyevent = key_temp;//返回原事件 } break; case 1: if(++key_count < intervaltime/10)//双击最大间隔时间内 { if(key_temp == KEY_SHORT)//检测到第二次短按,返回双击事件 { status = 2; keyevent = KEY_DOUBLECLICK; } else { keyevent = key_temp;//返回原事件 } } else { status = 0; keyevent = KEY_SHORT; } break; case 2: //双击最大间隔时间内,连续N次短按,只算一次双击时间 if(++key_count < intervaltime/10) { if(key_temp != KEY_SHORT) { keyevent = key_temp; } } else { status = 0; } break; } //更新忙状态 if(keyevent != KEY_NOKEY || status != 0) { busy = 1; } //output key_structure->Status.KeyEvent = keyevent; key_structure->Status.Busy = busy; res.Busy = busy; res.KeyEvent = keyevent; return res; }