前面我们说到了按键的循环消抖,长按识别,连击识别,今天我们来对双击按键进行编写,其实双击就是连按连下,双击,三连按,四连按本质上都一样,今天我只进行双击的编写,其实三连按就是在双击的基础上多按一下,多记一次数,然后多一个键值
那么我们开始对代码分析:首先我们开始对事件的过程进行分析,首先我们按下一次按键,按下按键以后,达到消抖时间,再释放,再短时间内再次按下,那么,我们需要记录按下次数和释放的时间,那么就再加个计数和释放计时器,还是老规矩,定义一个结构体:
typedef struct {
uint8_t key_flag : 1;
uint8_t key_long_flag : 1;
uint8_t key_hit_flag : 1;
uint16_t key_out;
uint8_t key_click_num;
uint8_t key_num;
uint8_t key_long_num;
uint8_t key_double_num;
}KeyState;
这里首先我们需要消抖判断,然后是连击判断,长按判断,双击判断,按下计时器,释放计时器,按下此时,几个键值,那么这样一个结构体就定义好了
然后我们需要给定一个释放时间,如果释放按键次数超过这个时间那么就不是双击了,将它和前面其他的时间定义放在一块,然后是键值定义,可以是枚举可以是宏定义,我这里建议枚举,下面我们继续编写代码:
#ifndef _key_h_
#define _key_h_
#include "stm32f10x.h" // 这里如果是51的同学可以改成REGX51
#define KEY_Disp_Time 20
#define KEY_Long_Time 200
#define KEY_Hit_Time 300
#define KEY_Double_Time 50
#define KEY1_Disp_num 1
#define KEY1_Long_num 11
#define KEY1_double_num 12
#define KEY2_Disp_num 2
#define KEY2_Long_num 21
#define KEY2_double_num 12
#define KEY3_Disp_num 3
#define KEY3_Long_num 31
#define KEY3_double_num 32 // 还是可以枚举按键,也可以宏定义,我这里采用宏定义
#define KEY1 PAin(0)
#define KEY2 PAin(1)
#define KEY3 PAin(2) // 还是老规矩定义三个按键
typedef struct{
uint8_t key_flag : 1;
uint8_t key_long_flag : 1;
uint8_t key_hit_flag : 1;
uint8_t key_Double_flag : 1;
uint16_t key_out;
uint8_t key_cnt;
uint8_t key_click_num;
uint8_t key_num;
uint8_t key_long_num;
uint8_t key_double_num;
}KeyState;
void Key_Init(void);
uint8_t Key_Read_Output(void);
#endif
我们还是采用模块化思维,方便移植代码,然后我们开始主代码:
#include "key.h"
KeyState Key_State = {
{ 0, 0, 0, 0, 0, 0, KEY1_Disp_num, KEY1_Long_num, KEY1_double_num },
{ 0, 0, 0, 0, 0, 0, KEY2_Disp_num, KEY2_Long_num, KEY2_double_num },
{ 0, 0, 0, 0, 0, 0, KEY3_Disp_num, KEY3_Long_num, KEY3_double_num },
};
void KEY_init(void)
{
/*
* 依旧是GPIO初始化
*/
}
KeyState Key_ReadInput(void)
{
if (!KEY1) return &KeySatet[0];
else if (!KEY2) return &KeySatet[1];
else if (!KEY3) return &KeySatet[2];
return NULL;
}
uint8_t KEY_GETOutput(void)
{
KeyState *keytate = Key_ReadInput();
uint8_t key_num;
if (keystate == NULL)
{
for (uint8_t i = 0; i < 3; ++i)
{
if (Key_State[i].key_double_flag)
{
key_num = Key_State[i].key_double_num;
Key_State[i].key_double_flag = 0;
}
if (Key_State[i].key_click_num > 0)
{
Key_State[i].key_cnt++;
if (Key_State[i].key_cnt > KEY_Double_Time)
{
Key_State[i].key_cnt = 0;
Key_State[i].key_click_num = 0;
}
}
if (Key_State[i].key_flag)
{
if (Key_State[i].key_long_flag)
{
key_num = keystate->key_long_num;
} else {
key_num = keystate->key_num;
}
}
Key_State[i].key_flag = 0;
Key_State[i].key_long_flag = 0;
Key_State[i].key_out = 0;
}
} else if (!(keystate->key_double_flag || keystate->key_hit_flag)){
keystate->key_out++;
if (keystate->key_out == KEY_Disp_Time)
{
keystate->key_flag = 1;
keystate->key_click++;
if (keystate->key_click == 2)
{
keystate->key_double_flag = 1;
keystate->key_click = 0;
keystate->key_flag = 0;
}
keystate->key_cnt = 0;
}
if (keystate->key_out == KEY_Long_Time)
{
keystate->key_long_flag = 1;
keystate->key_click = 0;
}
if (keystate->key_out == KEY_Hit_Time)
{
keystate->key_hit_flag = 1;
keystate->key_long_flag = 0;
keystate->key_flag = 0;
keystate->key_out = 0
}
} else if (keystate->key_hit_flag) {
keystate->key_out++;
if (keystate->key_out == KEY_Disp_Time)
{
keystate->key_out = 0;
key_num = keystate->key_num;
}
}
return key_num;
}
在这里我解释一下代码,是首先,按下按键,我们对计数器开始计数,当计数器达到消抖时间那么就把短按标志位置1,证明有按键按下,然后把按键按下次数加1,如果按键次数达到两次就把双击标志位置1,我们再来看第二个elseif语句,(!(keystate->key_double_flag || keystate->key_hit_flag)),这个一个或,意思就是,这两个事件发生一个事件就不再进入这个判断,那么没有达到双击,我们就继续计数,如果发生长按了,那么我们就不是双击了,就把按下次数清零即可,如果达到连击时间,那么就是连击操作了,就进入下一个判断语句,并且同时将长按和短按标志位清零,当我们按键释放以后,我们的双击标志位如果为1,就证明发生了双击事件,那么就返回双击键值,并且同时将按键次数清零,反之如果计时按键次数如果大于零,这里按键标志没有为1,为1,会把次数清零,那么现在双击计时器就开始计数,如果超过了双击间隔时间,就把按下次数清零,这里如果进行长按操作,按下次数也会清零,所以如果长按了,这个if也不会满足,那么短按标志位为1然后进入判断,如果长按标志位为1,就是长按,不是就是短按
这样就完成了按键的操作了