标题:
51单片机学习:基于状态机的按键对时程序(短按,长按,连发)
[打印本页]
作者:
51黑电子迷
时间:
2017-1-14 23:43
标题:
51单片机学习:基于状态机的按键对时程序(短按,长按,连发)
之前的电子钟程序中,用的按键消抖处理方法是10ms的延时,这种方法效率比较低
所以现在利用状态机原理重写一下,效率很高啊
4个独立按键中用到3个,
keys5用于切换对时分秒等状态,keys2是减小数值,keys3是增加数值
同时可以判断按键的"短按,长按,连发"等功能
小于2秒视为短按,
大于2秒视为长按,
在长按状态下每0.2秒自动连发一次, 这样对时的时候就不用按N次了
程序分很多个文件 ,Keil uVision4 打包
30880aac-82da-33b2-a05a-45059eb2bcf3.png
(14.89 KB, 下载次数: 71)
下载附件
2017-1-14 23:40 上传
#include "keyScan.h"
#include <reg52.h>
/*按键初始化,若io没有复用的话可以省略此步骤
void KeyInit(void)
{
keyS2 = 1 ;
keyS3 = 1 ;
keyS4 = 1 ;
keyS5 = 1 ;
//即P3|=0xf0;
}*/
static u8 getKey(void) //获取P3口值
{
if(key_down == keyS2)
{
return KEYS2_VALUE ;
}
if(key_down == keyS3 )
{
return KEYS3_VALUE ;
}
if(key_down == keyS4 )
{
return KEYS4_VALUE ;
}
if(key_down == keyS5 )
{
return KEYS5_VALUE ;
}
return key_up ; //0xf0 没有任何按键
}
//函数每10ms被调用一次,而我们弹性按键过程时一般都20ms以上
//所以每次按键至少调用本函数2次
u8 read_key(u8* pKeyValue)
{
static u8 s_u8keyState=0; //未按,普通短按,长按,连发等状态
static u16 s_u16keyTimeCounts=0; //在计时状态的计数器
static u8 s_u8LastKey = key_up ; //保存按键释放时的P3口数据
u8 keyTemp=0; //键对应io口的电平
s8 key_return=0; //函数返回值
keyTemp=key_up & getKey(); //提取所有的key对应的io口
switch(s_u8keyState) //这里检测到的是先前的状态
{
case state_keyUp: //如果先前是初始态,即无动作
{
if(key_up!=keyTemp) //如果键被按下
{
s_u8keyState=state_keyDown; //更新键的状态,普通被按下
}
}
break;
case state_keyDown: //如果先前是被按着的
{
if(key_up!=keyTemp) //如果现在还被按着
{
s_u8keyState=state_keyTime; //转换到计时态
s_u16keyTimeCounts=0;
s_u8LastKey = keyTemp; //保存键值
}
else
{
s_u8keyState=state_keyUp; //键没被按着,回初始态,说明是干扰
}
}
break;
case state_keyTime: //如果先前已经转换到计时态(值为3)
{ //如果真的是手动按键,必然进入本代码块,并且会多次进入
if(key_up==keyTemp) //如果未按键
{
s_u8keyState=state_keyUp;
key_return=return_keyPressed; //返回1,一次完整的普通按键
//程序进入这个语句块,说明已经有2次以上10ms的中断,等于已经消抖
//那么此时检测到按键被释放,说明是一次普通短按
}
else //在计时态,检测到键还被按着
{
if(++s_u16keyTimeCounts>key_longTimes) //时间达到2秒
{
s_u8keyState=state_keyLong; //进入长按状态
s_u16keyTimeCounts=0; //计数器清空,便于进入连发重新计数
key_return=return_keyLong; //返回state_keyLong
}
//代码中,在2秒内如果我们一直按着key的话,返回值只会是0,不会识别为短按或长按的
}
}
break;
case state_keyLong: //在长按状态检测连发 ,每0.2秒发一次
{
if(key_up==keyTemp)
{
s_u8keyState=state_keyUp;
}
else //按键时间超过2秒时
{
if(++s_u16keyTimeCounts>key_autoTimes)//10*20=200ms
{
s_u16keyTimeCounts=0;
key_return=return_keyAuto; //每0.2秒返回值的第2位置位(1<<2)
}//连发的时候,肯定也伴随着长按
}
key_return |= return_keyLong; //0x02是肯定的,0x04|0x02是可能的
}
break;
default:
break;
}
*pKeyValue = s_u8LastKey ; //返回键值
return key_return;
}
复制代码
作者:
sdfhlzs
时间:
2017-1-16 08:15
没看到打包文件啊。
作者:
好心人
时间:
2021-11-1 18:02
没有看到打包文件
作者:
29417765
时间:
2021-11-2 08:04
方法不错,状态机,没有死等延时
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1