文章目录
前言
再学完上一篇文章所讲内容的基础上,我们来看一些更有挑战性的
1.按键长按
蓝桥杯以前考过:
1.1.按键过程抽象:
1.1.1.按键状态分类
在上一节的基础上,我们可以将按键过程抽象为四个状态:
按键抬起状态(KEY_UP):按键没有按下。
按键消抖状态(KEY_DEBOUNCE):消除按键抖动。
按键按下状态(KEY_DOWN):确认按键已经按下。
按键长按状态(KEY_LONG):确认按键已经长按。
1.1.2.按键状态转换图
1.2.代码逐句分析:
代码如下(示例):
#include "STC15F2K60S2.h"
//矩阵键盘的行列定义
sbit Heng1 = P3^3;
sbit Heng2 = P3^2;
sbit Lie1 = P3^5;
sbit Lie2 = P3^4;
#define Key_Num 4
#define uint8_t unsigned char
#define uint16_t unsigned int
typedef enum
{
KEY_UP = 0,
KEY_DEBOUNCE,
KEY_DOWN,
KEY_LONG
}KEY_STATE; //按键的四种状态,通过枚举类型实现
//数组的特性,部分初始化对于没给初值的其他数组元素自动补0
KEY_STATE KeyState[Key_Num] = {KEY_UP};
uint8_t KeyFlag[Key_Num] = {0};
uint8_t KeyFlag_Long[Key_Num] = {0}; //按键长按标志,1按下,0没按
uint16_t KeyFlag_Count = 0;
void Key_State(uint8_t i,bit Lie)
{
switch(KeyState[i])
{
case KEY_UP:
{
if(Lie == 0) //读到低电平,按键进入抖动状态
{
KeyState[i] = KEY_DEBOUNCE;
}
}
break;
case KEY_DEBOUNCE:
{
if(Lie == 0) //读到低电平,按键进入按下状态,并将按键按下标志置1,同时开始按键计时
{
KeyState[i] = KEY_DOWN;
KeyFlag[i] = 1;
KeyFlag_Count = 0;
}
else //读到高电平,按键进入抬起状态
{
KeyState[i] = KEY_UP;
}
}
break;
case KEY_DOWN:
{
if((Lie == 0)&&(++KeyFlag_Count >= 100)) //低电平的情况下,每10ms加1,加到100正好1s
{
KeyState[i] = KEY_LONG;
KeyFlag_Long[i] = 1;
KeyFlag_Count = 0;
}
else if(Lie == 0) //低电平的情况下,时间不足1s,保持按下状态
{
KeyState[i] = KEY_DOWN;
}
else
{
KeyState[i] = KEY_UP;
KeyFlag[i] = 0;
}
break;
}
case KEY_LONG:
{
if(Lie == 1) //读到高电平,按键进入抬起状态,同时将长按键、短按键标志置0
{
KeyState[i] = KEY_UP;
KeyFlag_Long[i] = 0;
KeyFlag[i] = 0;
}
}
break;
}
}
void Scan_Key()
{
Lie1 = 1; Lie2 = 1;
Heng1 = 0; Heng2 = 1;
Key_State(0,Lie1); //扫描S12按键
Key_State(1,Lie2); //扫描S16按键
Heng1 = 1; Heng2 = 0;
Key_State(2,Lie1); //扫描S13按键
Key_State(3,Lie2); //扫描S17按键
}
2.按键双击
必备技巧,很有可能考
2.1.按键过程抽象:
2.1.1.按键状态分类
在上一节的基础上,更进一步,我们可以将按键过程抽象为五个状态:
按键抬起状态(KEY_UP):按键没有按下。
按键消抖状态(KEY_DEBOUNCE):消除按键抖动。
按键按下状态(KEY_DOWN):确认按键已经按下。
按键等待状态(KEY_WAIT):按键确认按下后又松开。
按键双击状态(KEY_DOUBLE):确认按键为双击。
2.1.2.按键状态转换图
2.2.代码逐句分析:
typedef enum
{
KEY_UP = 0,
KEY_DEBOUNCE,
KEY_DOWN,
KEY_DOUBLE,
KEY_WAIT
}KEY_STATE; //按键的五种状态,通过枚举类型实现
KEY_STATE KeyState[Key_Num] = {KEY_UP};
uint8_t KeyFlag[Key_Num] = {0};
uint8_t KeyFlag_Double[Key_Num] = {0}; //按键双击标志,1按下,0没按
uint16_t KeyFlag_Count = 0;
void Key_State(uint8_t i,bit Lie)
{
switch(KeyState[i])
{
case KEY_UP:
{
if(Lie == 0) //读到低电平,按键进入抖动状态
{
KeyState[i] = KEY_DEBOUNCE;
}
else //读到高电平,按键按下标志置0
{
KeyFlag[i] = 0;
}
}
break;
case KEY_DEBOUNCE:
{
if(Lie == 0) //读到低电平,按键进入按下状态
{
KeyState[i] = KEY_DOWN;
}
else //读到高电平,按键进入抬起状态
{
KeyState[i] = KEY_UP;
}
}
break;
case KEY_DOWN:
{
if(Lie == 1) //读到高电平,按键进入等待状态
{
KeyState[i] = KEY_WAIT;
KeyFlag_Count = 0;
}
break;
}
case KEY_WAIT:
{
if((Lie == 1)&&(++KeyFlag_Count >= 20)) //高电平的情况下,每10ms加1,加到20正好0.2s,如果大于0.2秒,进入抬起状态,下一次按下,不视为双击
{
KeyState[i] = KEY_UP;
KeyFlag[i] = 1;
KeyFlag_Count = 0;
}
else if(Lie == 1)
{
KeyState[i] = KEY_WAIT;
}
else //小于0.2s再次读到低电平视为双击,将双击标志位置1
{
KeyState[i] = KEY_DOUBLE;
KeyFlag_Double[i] = 1;
}
}
break;
case KEY_DOUBLE:
{
if(Lie == 1) //读到高电平,按键进入抬起状态
{
KeyState[i] = KEY_UP;
KeyFlag_Double[i] = 0;
}
}
break;
}
}
3.总结
不论长按还是双击,代码主体依然是上一篇文章按键扫描的内容,基本做到只在扫描代码的基础上做加法,而不是减法,这样更利于理解和记忆。