MCU实战经验:多种的按键处理

按键通常有:IO口按键(BUTTON),AD按键(通过AD采样电压),IR(遥控器)按按键功能分:有短按键,长按键,连续按键。打个比方,遥控电视机,按一下音量键,音量增加1,这个就是短按键。按住音量键不放,音量连续加,,这个就是连续按键。按住一个按键5s,系统会复位,这个是长按键。

1、IO口按键,就是我们比较常见的一个IO接一个按键,或者是一个矩阵键盘。很多新人的处理方法可能是采样延时的方法,当年我也是这样的,如下

if(GETIO==low){delay_10ms()if(GETIO==low){//得到按键值}}

第三,如果这个按键是开关机按键系统在低功耗状态下,需要中断唤醒,这种方法比较容易出问题,如STM8S系列的 halt 模式。

/* 按键滤波时间50ms, 单位10ms *只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件 */#define BUTTON_FILTER_TIME5#define BUTTON_LONG_TIME300/* 持续1秒,认为长按事件 */ /*每个按键对应1个全局的结构体变量。其成员变量是实现滤波和多种按键状态所必须的*/typedef struct{/* 下面是一个函数指针,指向判断按键手否按下的函数 */unsigned char (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */unsigned char Count;/* 滤波器计数器 */unsigned char FilterTime;/* 滤波时间(最大255,表示2550ms) */unsigned short LongCount;/* 长按计数器 */unsigned short LongTime;/* 按键按下持续时间, 0表示不检测长按 */unsigned char State;/* 按键当前状态(按下还是弹起) */unsigned char KeyCodeUp;/* 按键弹起的键值代码, 0表示不检测按键弹起 */unsigned char KeyCodeDown;/* 按键按下的键值代码, 0表示不检测按键按下 */unsigned char KeyCodeLong;/* 按键长按的键值代码, 0表示不检测长按 */unsigned char RepeatSpeed;/* 连续按键周期 */unsigned char RepeatCount;/* 连续按键计数器 */}BUTTON_T; typedef enum{KEY_NONE = 0,/* 0 表示按键事件 */KEY_DOWN_Power,/* 按键键按下 */KEY_UP_Power,/* 按键键弹起 */KEY_LONG_Power,/* 按键键长按 */KEY_DOWN_Power_TAMPER/* 组合键,Power键和WAKEUP键同时按下 */}KEY_ENUM; BUTTON_T s_Powerkey;//是否有按键按下接口函数unsigned char IsKeyDownUser(void){if (0==GPIO_ReadInputPin(POWER_KEY_PORT, POWER_KEY_PIN) ) return 1;return 0;}void PanakeyHard_Init(void){ GPIO_Init (POWER_KEY_PORT, POWER_KEY_PIN, GPIO_MODE_IN_FL_NO_IT);//power key} void PanakeyVar_Init(void){/* 初始化USER按键变量,支持按下、弹起、长按 */s_Powerkey.IsKeyDownFunc = IsKeyDownUser;/* 判断按键按下的函数 */s_Powerkey.FilterTime = BUTTON_FILTER_TIME;/* 按键滤波时间 */s_Powerkey.LongTime = BUTTON_LONG_TIME;/* 长按时间 */s_Powerkey.Count = s_Powerkey.FilterTime / 2;/* 计数器设置为滤波时间的一半 */s_Powerkey.State = 0;/* 按键缺省状态,0为未按下 */s_Powerkey.KeyCodeDown = KEY_DOWN_Power;/* 按键按下的键值代码 */s_Powerkey.KeyCodeUp =KEY_UP_Power;/* 按键弹起的键值代码 */s_Powerkey.KeyCodeLong = KEY_LONG_Power;/* 按键被持续按下的键值代码 */s_Powerkey.RepeatSpeed = 0;/* 按键连发的速度,0表示不支持连发 */s_Powerkey.RepeatCount = 0;/* 连发计数器 */}void Panakey_Init(void){PanakeyHard_Init();/* 初始化按键变量 */PanakeyVar_Init();/* 初始化按键硬件 */}/***********************************************************************************************************函 数 名: bsp_DetectButton*功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。*形 参:按键结构变量指针*返 回 值: 无**********************************************************************************************************/ void Button_Detect(BUTTON_T *_pBtn){if (_pBtn->IsKeyDownFunc()){if (_pBtn->Count < _pBtn->FilterTime){_pBtn->Count = _pBtn->FilterTime;}else if(_pBtn->Count < 2 * _pBtn->FilterTime){_pBtn->Count++;}else{if (_pBtn->State == 0){_pBtn->State = 1;/* 发送按钮按下的消息 */if (_pBtn->KeyCodeDown > 0){/* 键值放入按键FIFO */Pannelkey_Put(_pBtn->KeyCodeDown);// 记录按键按下标志,等待释放}}if (_pBtn->LongTime > 0){if (_pBtn->LongCount < _pBtn->LongTime){/* 发送按钮持续按下的消息 */if (++_pBtn->LongCount == _pBtn->LongTime){/* 键值放入按键FIFO */Pannelkey_Put(_pBtn->KeyCodeLong);}}else{if (_pBtn->RepeatSpeed > 0){if (++_pBtn->RepeatCount >= _pBtn->RepeatSpeed){_pBtn->RepeatCount = 0;/* 常按键后,每隔10ms发送1个按键 */Pannelkey_Put(_pBtn->KeyCodeDown);}}}}}}else{if(_pBtn->Count > _pBtn->FilterTime){_pBtn->Count = _pBtn->FilterTime;}else if(_pBtn->Count != 0){_pBtn->Count–;}else{if (_pBtn->State == 1){_pBtn->State = 0;/* 发送按钮弹起的消息 */if (_pBtn->KeyCodeUp > 0) /*按键释放*/{/* 键值放入按键FIFO */Pannelkey_Put(_pBtn->KeyCodeUp);}}}_pBtn->LongCount = 0;_pBtn->RepeatCount = 0;}}//功能说明: 检测所有按键。10MS 调用一次void Pannelkey_Polling(void){Button_Detect(&s_Powerkey);/* USER 键 */}void Pannelkey_Put(void){// 定义一个队列 放入按键值} typedef struct{ unsigned char count;// unsigned char LongkeyFlag;/*是否长按键,1代表是*/ unsigned char PreKeyValue;/*按键值的一个备份,用于释放按键值*/ }ScanKeyDef; #define SHORT_PRESS_TIME_IR16 // 10ms #define SERIES_PRESS_TIME_IR10 #define LONG_PRESS_TIME_IR22#define KEY_RELEASE_TIME_OUT_IR12 // 10ms //提供5个接口函数,如下。 unsigned char get_irkey(void);unsigned char ISSeriesKey(unsigned char temp);//按键是否需要做连续按键unsigned char changeSeriesKey(unsigned char temp);//转换连续按键值unsigned char ISLongKey(unsigned char temp);//按键是否需要做连续按键unsigned char changeToLongKey(unsigned char temp);//转换连续按键值unsigned char KeyScan(void) {unsigned char KeyValue = KEY_NONE,KeyValueTemp = KEY_NONE;static unsigned char KeyReleaseTimeCount =0;KeyValueTemp = get_irkey();if(KeyValueTemp != KEY_NONE){ScanKeyDef.count++;KeyReleaseTimeCount =0;if(ScanKeyDef.count < LONG_PRESS_TIME_IR ){ScanKeyDef.LongkeyFlag = 0;if((ScanKeyDef.count == SERIES_PRESS_TIME_IR) && ISSeriesKey(KeyValueTemp)) //处理连续按键{KeyValue = changeSeriesKey ( KeyValueTemp );ScanKeyDef.PreKeyValue = KEY_NONE;}else if ( ScanKeyDef.count < SHORT_PRESS_TIME_IR ){ScanKeyDef.PreKeyValue = KeyValueTemp;}else{ScanKeyDef.PreKeyValue = KEY_NONE; // 无效按键}}else if ( ScanKeyDef.count == LONG_PRESS_TIME_IR ){if (ISLongKey(KeyValueTemp)){{ScanKeyDef.LongkeyFlag = 1;KeyValue = changeToLongKey ( KeyValueTemp );}}ScanKeyDef.PreKeyValue = KEY_NONE;}else if (ScanKeyDef.count > LONG_PRESS_TIME_IR ){ScanKeyDef.PreKeyValue = KEY_NONE; //无效按键}}else//release & no press{KeyReleaseTimeCount ++;if(KeyReleaseTimeCount >= KEY_RELEASE_TIME_OUT_IR){if ( ScanKeyDef.PreKeyValue != KEY_NONE ) //释放按键值{if ( ScanKeyDef.LongkeyFlag == 0 ){KeyValue =ScanKeyDef.PreKeyValue ;}}ScanKeyDef.count = 0;ScanKeyDef.LongkeyFlag = 0;ScanKeyDef.PreKeyValue = KEY_NONE;}}return(KeyValue);}

最好的感觉就是你什么都跟我说。

MCU实战经验:多种的按键处理

相关文章:

你感兴趣的文章:

标签云: