我们前面说到单片机的按键的循环消抖和长按识别那么今天来继续分享按键的连击,在前面的两节里面我们可以看出,按键的状态只有短按和长按,并且短按只能识别一次,有的时候,我们需要连续执行短按操作,这该怎么办?那么我们就给定一个时间然后,达到这个时间连续一个时间重新给按键赋键值但是这个过程又不能取消长按,那么该怎么办?这里我们再用一个标志位,这个标志位置1,那么就算达到了长按时间也不执行长按操作。
我们首先要给一个时间,连击状态的时间,要比长按长一点:
#define KEY_HIt_Time 300
这里和前面提到的长按短按时间一样,根据自己的需求修改,就不多说了,如果想不明白的,可以去看看我前面的两个分享
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_num;
uint8_t key_long_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 KEY1_Disp_num 1
#define KEY1_Long_num 11
#define KEY2_Disp_num 2
#define KEY2_Long_num 21
#define KEY3_Disp_num 3
#define KEY3_Long_num 31 // 还是可以枚举按键,也可以宏定义,我这里采用宏定义
#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;
uint16_t key_out;
uint8_t key_num;
uint8_t key_long_num;
}KeyState;
void Key_Init(void);
uint8_t Key_Read_Output(void);
#endif
继续主代码:
#include "key.h"
/*
* 还是定义按键的结构体
*/
KeyState Key_State[3] = {
{ 0, 0, 0, 0, KEY1_Disp_num, KEY1_Long_num },
{ 0, 0, 0, 0, KEY2_Disp_num, KEY2_Long_num },
{ 0, 0, 0, 0, KEY3_Disp_num, KEY3_Long_num },
};
void Key_Init(void)
{
/*
* 依旧是初始化GPIO
*/
}
/*
* 根据按键状态返回结构体指针
* 这里再详细解释一下为什么是
* 指针而不是结构体本身,前面
* 说到结构体没有空值这是一方
* 面的原因,还有就是,我们要
* 判断结构体本身的值,如果传
* 递的是结构体,我们是无法对
* 原来的结构体进行改变的,但
* 是指针可以,可能有的同学对
* 指针的概念有些不清楚,后面
* 我会抽出一节单独讲解指针
*/
KeyState* Key_ReadInput(void)
{
if (!KEY1) return &Key_State[0];
else if (!KEY2) return &Key_State[1];
else if (!KEY3) return &Key_State[1];
return NULL;
}
uint8_t Key_ReadOutput(void)
{
KeyState keystate;
uint8_t key_num = 0;
keystate = Key_ReadInput();
if (keystate == NULL)
{
for (uint8_t i = 0; i < 3; ++i)
{
if (Key_State[i].key_hit_flag)
{
Key_State[i].key_flag = 0;
Key_State[i].key_long_flag = 0;
Key_State[i].key_hit_flag = 0;
}
if (Key_State[i].key_flag)
{
if (Key_State[i].key_long_flag)
{
key_num = Key_State[i].key_long_num;
} else {
key_num = Key_State[i].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_hit_flag) {
keystate->key_out++;
if (keystate->key_out == KEY_Disp_Time)
{
keystate->key_flag = 1;
}
if (keystate->key_out == KEY_Long_Time)
{
keystate->key_long_flag = 1;
}
if (keystate->key_out == KEY_Hit_Time)
{
keystate->key_hit_flag = 1;
keystate->key_out = 0;
}
} else if (keystate->key_hit_flag) {
key_out++;
if (key_out == KEY_Disp_Time)
{
key_num = keystate->key_num;
key_out = 0;
}
}
return key_num;
}
这里我来解释一下代码,首先我们按键按下开始计数,如果达到了消抖数,就给标志位置1,等待按键释放,如果按键继续按下,那么一直计数,达到长按,将长按标志位置1,如果还不释放,达到连击时间,那么就将连击标志位置1,这时候,我们将计数器清零,这时候按键没有释放,不会进入第一个if语句,连击标志位为1,取反就是0,也就是假,不会进入第二个if语句,那么第三个if语句标志位为1,满足,进入开始计数,达到20次,就返回一次短按键值,然后给计数器清零,然后我们看按键释放以后,这里我们先判断连击标志位是否为1,如果是就把长按标志位和短按标志位清零,我们也可以去掉这个,直接在第二个if语句里面添加代码,如果达到连击时间,那么就把长按标志位和短按标志位清零
那么现在,我们的连击就完成了。