【速成】蓝桥杯嵌入式省一教程:(四)按键短按、长按与双击
此外,短按、长按和双击涉及到按键的时间长短问题,因此我们需要利用定时器的基本计时功能来计算按键按下的长短。因此,大体可以敲定代码的框架:利用0.01s的定时器中断扫描按键,并在按下按键时开始计时,以区分长短按;然而,由于按键有限,在现实生活中,仅靠几个按键当然无法完成很多指令,于是人们想到了通过一个按键的短按、长按与双击来区分不同的指令,这也是蓝桥杯的考点之一。在这里,我们设置了0.01s的中断周
上一篇文章介绍了用定时扫描的办法判断按键是否按下。然而,由于按键有限,在现实生活中,仅靠几个按键当然无法完成很多指令,于是人们想到了通过一个按键的短按、长按与双击来区分不同的指令,这也是蓝桥杯的考点之一。接下来我们就来讲讲如何判断短按、长按与双击。
需要判断长短按和双击,首先需要判断按键是否按下,这通过上一讲内容就可以实现。此外,短按、长按和双击涉及到按键的时间长短问题,因此我们需要利用定时器的基本计时功能来计算按键按下的长短。因此,大体可以敲定代码的框架:利用0.01s的定时器中断扫描按键,并在按下按键时开始计时,以区分长短按;在松开按键时开始计时,判断是两次间隔较长的短按还是一次双击。
我们需要定义几个变量,来存储按键的状态以及判断过程。由于一共有四个按键,因此我们可以定义一个结构体,不同的结构体变量代表不同的按键。
在这个结构体中,首先我们需要三个标志变量:single_flag、long_flag和double_flag来存储短按、长按和双击的信息;接着,我们需要一个状态变量:key_status来存储按键端口的电平状态。由于这4个变量的值只有0和1,我们使用bool的变量类型(需要调用stdbool.h)。考虑到在按键按下或松开的瞬间,可能会出现机械抖动的情况(即按键状态发生改变的瞬间,其端口电平状态在高低之间反复跳动),我们需要等待电平状态稳定以后才开始对按键情况进行判断。在这里,我们设置了0.01s的中断周期,因此当按下按键时,在第二次进入定时器中断时,其电平状态就能稳定下来,故需要定义一个变量click_status来标志按键按下的状态是否稳定,以达到消抖的目的。因为要判断长短按,我们还需要一个变量click_time来存储按键时长;同时,要判断是两次短按还是一次双击。我们还需要一个状态变量double_status来判断是否已进入需要判断双击与否的状态(否则一次长按与一次短按或两次长按也有可能被识别为双击),一个变量double_time来存储两次按键的时间间隔。经过分析,我们定义如下结构体,key[0]~key[3]分别表示B1~B4。:
struct keys
{
bool single_flag;
bool long_flag;
bool double_flag;
bool key_status;
uint8_t click_status;
int click_time;
uint8_t double_status;
int double_time;
} key[4];
下面是以B1为例的一段代码:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM4)
{
key[0].key_status = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); //读取PB0的电平状态
switch (key[0].click_status) //根据不同的状态进行不同的判断
{
case 0: //状态0:第一次按下B1
if (key[0].key_status == GPIO_PIN_RESET) key[0].click_status = 1;
//跳转到状态1
break;
case 1: //状态1:电平已经稳定
if (key[0].key_status == GPIO_PIN_RESET) //若此时为低电平,说明B1的确被按下
{
key[0].click_status = 2; //跳转到状态2
key[0].click_time = 0; //开始计时
}
else key[0].click_status = 0; //B1没有被按下,可能是松开时的抖动
break;
case 2:
if (key[0].key_status == GPIO_PIN_RESET) key[0].click_time++;
//若B1持续被按下,则计时一直增加
else if (key[0].key_status==GPIO_PIN_SET && key[0].click_time>=70)
//若B1已经松开,且按下的持续时间大于一定值
{
key[0].long_flag = 1; //判定为长按
key[0].click_status = 0; //重置判断按键的状态
}
else if (key[0].key_status==GPIO_PIN_SET && key[0].click_time<70)
//若B1已经松开,且按下的持续时间小于一定值,则有可能是短按,有可能是双击
{
switch (key[0].double_status) //根据不同的状态进行不同的判断
{
case 0: //状态0:第一次松开按键
key[0].double_status = 1; //跳转到状态1
key[0].double_time = 0; //开始计时
break;
case 1: //状态1:第二次松开按键
key[0].double_flag = 1; //判定为双击
key[0].double_status = 0; //重置判断双击的状态
break;
}
/* 如果这一段看不明白,请继续往下看,再回头理解 */
key[0].click_status = 0; //重置判断按键的状态
}
break;
}
if (key[0].double_status == 1) //状态1:第一次松开后未按下按键
{
key[0].double_time++; //若一直未按下第二次按键,则计时增加
if (key[0].double_time >= 35) //若间隔时间大于一定值
{
key[0].single_flag = 1; //判定为短按
key[0].double_status = 0; //重置判断双击的状态
}
}
}
}
若要判断所有按键的情况,只需要将以上程序放置在循环中即可。为方便日后使用,将其封装在库中。
/* key.c */
#include "key.h"
struct keys key[4];
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM4)
{
key[0].key_status = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
key[1].key_status = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
key[2].key_status = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
key[3].key_status = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
for (int i=0; i<4; i++)
{
switch (key[i].click_status)
{
case 0:
if (key[i].key_status == GPIO_PIN_RESET) key[i].click_status = 1;
break;
case 1:
if (key[i].key_status == GPIO_PIN_RESET)
{
key[i].click_status = 2;
key[i].click_time = 0;
}
else key[i].click_status = 0;
break;
case 2:
if (key[i].key_status == GPIO_PIN_RESET) key[i].click_time++;
else if (key[i].key_status==GPIO_PIN_SET && key[i].click_time>=70)
{
key[i].long_flag = 1;
key[i].click_status = 0;
}
else if (key[i].key_status==GPIO_PIN_SET && key[i].click_time<70)
{
switch (key[i].double_status)
{
case 0:
key[i].double_status = 1;
key[i].double_time = 0;
break;
case 1:
key[i].double_flag = 1;
key[i].double_status = 0;
break;
}
key[i].click_status = 0;
}
break;
}
if (key[i].double_status == 1)
{
key[i].double_time++;
if (key[i].double_time >= 35)
{
key[i].single_flag = 1;
key[i].double_status = 0;
}
}
}
}
}
/* key.h */
#ifndef __KEY_H
#define __KEY_H
#include "main.h"
#include <stdbool.h>
struct keys
{
bool single_flag;
bool long_flag;
bool double_flag;
bool key_status;
uint8_t click_status;
int click_time;
uint8_t double_status;
int double_time;
};
extern struct keys key[];
#endif /* __KEY_H */
在使用按键时,只需要判断key[i].single_flag、key[i].long_flag和key[i].double_flag,并在执行完任务后将其重新置零即可。
本文参考自B站up主01Studio,并附加了笔者自己的一些感悟。按键识别比较难,需要大家反复敲代码进行实践!!!
更多推荐
所有评论(0)