看了uid:130230的状态机按键扫描,感觉应该可以实现我想要的功能:多按键检测长按,短按和连按(按下不放连加或连减),之前看到的例程多为短按和长按组合,或短按和连按组合,没有看到过长按和连按如何识别,一直很纠结。
之前发过比较简单有效的状态机按键检测的例程,但那个例程里没有加入长按检测,如果哪位高手有兴趣,也可以在我之前的贴子里修改,不胜感激!
http://www.51hei.com/bbs/dpj-225323-1.html
下面这个例程是uid:130230高手写的,原例程只有一个按键,现在我把我需要的按键放进去了,还加了一些注释,不知道理解的对不对?还请各位大神们多多指点,先谢谢了!
.h文件
#ifndef __key_H__
#define __key_H__
#include "xx.h"
#define PD 0x22|(P1&0x51)|(P2<<1&0x80)|(P3<<2&0x0c) //P1.0/P3.0 p3.1/P4.2 p4.3组合5个按键为1个字节数据
// |0101 0001 |0100 0000 |0000 0011
// 0010 0010 |0101 0001 |1000 0000 |0000 1100 1100 1101 移位后或:1111 1111 PD = 0xff;
sbit up = P2^6; //0111 1111 7f
sbit unit = P1^0; //1111 1110 fe
sbit down = P3^0; //1111 1011 fb
sbit md = P1^6; //1011 1111 bf
sbit k5 = P3^1; //1111 0111 f7
sbit k6 = P1^4; //1110 1111 ef
sbit md1 = P1^5;
//按键值
typedef enum
{
NONE_CLICK = 0, //无按键
SINGLE_CLICK , //单击
DOUBLE_CLICK, //双击
LONG_PRESS, //长按
LIANFA_CLICK, //连发
NUM_KEY,
}KEY_VALUE_EN;
//按键状态
typedef enum
{
KEY_IDLE = 0, //空闲
KEY_FILTER, //去抖
KEY_FIRST, //第一次按下
KEY_CHECK_FREE, //抬起检测(长按)
NUM_KEY_STAUS,
}KEY_STATUS_EN;
//按键扫描状态
typedef struct
{
unsigned char Statu;
unsigned char CntDly;
unsigned char KeyValue;
}TASK_KEY_STATU_ST;
void Task_Keyscan(void);
void Switch_KeyState(unsigned char state);
#endif
.c文件
#include "key.h"
TASK_KEY_STATU_ST gstKey1 = {0}; //任务键状态
/***********************************************
功能:按键扫描
作者:jzb2019年12月27日
备注:50ms运行一次这个函数,理论上可以扩展到n连击
最大响应延时500ms(单击时)
***********************************************/
void Task_Keyscan(void)
{
unsigned char key;
key=PD&0xFF;
switch (gstKey1.Statu)
{
case KEY_IDLE: //按键空闲
{
if(key!= 0xFF)
{
Switch_KeyState(KEY_FILTER);//按键按下了去抖
}
}
break;
case KEY_FILTER:
{
if(key!= 0xFF)
{
gstKey1.CntDly++;
if(gstKey1.CntDly >= 60)
{
gstKey1.KeyValue = LONG_PRESS;//长按
Switch_KeyState(KEY_CHECK_FREE);//抬起检测
}
}
else
{
if(gstKey1.CntDly == 0)
{
Switch_KeyState(KEY_IDLE);//如果没有计数则证明是毛刺滤掉
}
else
{
Switch_KeyState(KEY_FIRST);//转到第一次按下状态
}
}
}
break;
case KEY_FIRST:
{
if(key == 0xff)//释放了
{
gstKey1.CntDly++;
if(gstKey1.CntDly > 10)//500ms
{
gstKey1.KeyValue = SINGLE_CLICK; //单击
Switch_KeyState(KEY_IDLE);//回到空闲
}
}
else//按下了
{
if(gstKey1.CntDly == 0)//没有计数证明是没有释放按键的动作
{
}
else//这个分支里面已经是第二次按下了
{
gstKey1.KeyValue = DOUBLE_CLICK;
Switch_KeyState(KEY_CHECK_FREE);//抬起检测
}
}
}
break;
case KEY_CHECK_FREE:
{
if(key == 0xff)
{
Switch_KeyState(KEY_IDLE);
}
}
break;
default :
{
Switch_KeyState(KEY_IDLE);
}
break;
}
}
/****************************************************************************
功能:执行切换状态的动作,同时清掉当前状态内的计数
入口:要切换的状态
作者:jzb 2019年12月27日
备注:暂时先不考虑通用的问题,直接操作全局变量如果扩展按键再说
*****************************************************************************/
void Switch_KeyState(unsigned char state)
{
gstKey1.CntDly = 0;
gstKey1.Statu = state;
}
|