历史上的今天
返回首页

历史上的今天

今天是:2025年02月18日(星期二)

正在发生

2019年02月18日 | 【单片机笔记】状态机效率地按键扫描、识别及检测方法

2019-02-18 来源:eefocus

按键是人机交互最简单也是最廉价的方式之一,要实现一个或者多个按键的有效扫描并处理,这里附上我修改过的代码:


实现的代码主要包含有四个部分:


第一部分:按键的初始化部分


void Key_Configuration(void)

{

return;

}

这里需要根据所使用的IC来做不同的配置方式,我使用的是51内核,在初始化的过程I/O口默认做了准双向若上拉处理,按键低电平有效,所以就没有处理直接跳出去。


第二部分:按键的电平读取


//只读取初次按键电平状态,在状态机中进一步处理

static u8 Key_Read(void)

{

    if(!READ_KEY1)  

return KEY1_PRES;

    if(!READ_KEY2)  

return KEY2_PRES;       

    if(!READ_KEY3)  

return KEY3_PRES;

    if(!READ_KEY4)  

return KEY4_PRES;

    if(!READ_KEY5)  

return KEY5_PRES;


return KEY_NONE;

}

根据使用的具体环境及功能,这里每次读取电平只读取一个有效的电平并且有优先级,由代码可以看出优先级的顺序为:KEY1>KEY2>KEY3>KEY4>KEY5。当然需要使用多少个按键根据项目的需求来定,理论支持多少个独立按键都是可以的。


第三部分:状态机的按键判定部分


//状态机

static u8 Key_Scan(void)

{

static u8 state = 0; //按键初始化状态

static u8 KEY_LAST=0,KEY_NOW=0; //记录两次电平的状态

u8 KEY_VALUE=0;

 

KEY_NOW = Key_Read();//读按键值

   

switch(state)

{

case 0:

{

if(KEY_NOW != KEY_LAST) state = 1; //有按键按下

}break;

case 1: 

{

if(KEY_NOW == KEY_LAST) state = 2; //消斗之后按键有效

else state = 0; //认为误触

}break; 

case 2: //消斗之后

{

if(KEY_NOW == KEY_LAST) //还是按下的状态 

{

  state = 3;

}

else//松开了,短按

{

state = 0; 


KEY_VALUE = KEY_LAST|KEY_SHORT;  //返回键值短按

}

}break;


case 3: //判断长按短按

{

if(KEY_NOW == KEY_LAST) 

{

    static u8 cnt = 0;

if(cnt++ > 120) //1200ms

{

cnt = 0; 

state = 4;

KEY_VALUE = KEY_LAST|KEY_LONG; //返回键值长按

}   

}

else

{

state = 0;

KEY_VALUE = KEY_LAST|KEY_SHORT; //返回键值短按

}

}break;

case 4://长按松手检测

{

if(KEY_NOW != KEY_LAST) 

state = 0;

}break;

}//switch


KEY_LAST = KEY_NOW; //更新

return KEY_VALUE;

}

这部分也是整个的核心代码部分,首先定义了三个静态变量,按键的状态state,当前读取的键值KEY_NOW,上一次的键值KEY_LAST,以及返回的判定后的有效键值KEY_VALUE。接下来一步步研究:


state初始值为0,进入switch和case,每次进入case判断的时间间隔是由第四部分来确定的,这里给出我选用的是10ms


判定0,只要上一次记录的键值和本次读取到的键值不想等,则进入1。这里还有另一个作用,那就是短按的松手检测。这也是没有else的原因,具体如何实现请看状态4。


判定1,上一次和这一次的键值相等,注意case 0后KEY_LAST已经被更新,也就是说10ms后这次读取到的键值还等于上一次的键值,这里我们认为是有效的按下,而并非误触和干扰造成的,这种情况下进入2。否则就返回到0。


判定2,这里是从1过来的,也就是此时的按键键值是有效的,这里还是来判断上次更新的KEY_LAST及这次读取到的新的键值,如果不想等,证明手已经松开(单一按键的情况)。这样就识别成了短按,state重新回到0并返回短按的键值量,在0。如果按键还没有被释放那就有长按的趋势了,进入3。


判定3,这里也很简单,判定键值有没有释放,每次进来就开始计次,10ms进入一次,这里计120次也就是需要1200ms的时间达到条件并且把返回的键值赋值成长按,同时进入4,反之没有达到时间就识别成短按并重新进入0。


判定4,这里代码的作用主要是作为长按的松手检测,道理也很简单,按键没有释放,那肯定历史键值和当前的键值相等并且不为0,等按键释放的后,读取的键值肯定为0,这就跳出了状态4。


第四部分:实体函数及被调用函数


static void KEY1_ShortHander(void)

{

 

}

static void KEY1_LongHander(void)

{

 

}

static void KEY2_ShortHander(void)

{

 

}

static void KEY2_LongHander(void)

{

 

}

static void KEY3_ShortHander(void)

{

 

}

static void KEY3_LongHander(void)

{

 

}

static void KEY4_ShortHander(void)

{

 

}

static void KEY4_LongHander(void)

{

 

}

static void KEY5_ShortHander(void)

{

 

}

static void KEY5_LongHander(void)

{

 

}

 

 

void Key_Hander(void) //按键处理函数

{

u8 KEY_NUM=0;

static u32 LAST=0;

if(Systick_ms-LAST<10) return;

LAST = Systick_ms;


KEY_NUM = Key_Scan();  //按键扫描值

if(KEY_NUM == KEY_NONE) return;


//有按键按下

if(KEY_NUM & KEY_SHORT) //短按

{    

if(KEY_NUM & KEY1_PRES)//KEY1_PRES

{

KEY1_ShortHander();

}

else if(KEY_NUM & KEY2_PRES)//KEY2_PRES

{

KEY2_ShortHander();

}

else if(KEY_NUM & KEY3_PRES)//KEY3_PRES

{

KEY3_ShortHander();

}

else if(KEY_NUM & KEY4_PRES)//KEY4_PRES

{

KEY4_ShortHander();

}

else if(KEY_NUM & KEY5_PRES)//KEY5_PRES

{

KEY5_ShortHander();

}

}

else if(KEY_NUM & KEY_LONG) //长按 

{

if(KEY_NUM & KEY1_PRES)//KEY1_PRES

{

KEY1_LongHander();

}

else if(KEY_NUM & KEY2_PRES)//KEY2_PRES

{

KEY2_LongHander();

}

else if(KEY_NUM & KEY3_PRES)//KEY3_PRES

{

KEY3_LongHander();

}

else if(KEY_NUM & KEY4_PRES)//KEY4_PRES

{

KEY4_LongHander();

}

else if(KEY_NUM & KEY5_PRES)//KEY5_PRES

{

KEY5_LongHander();

}

}

}

这部分代码是比较清晰的,通过前面3部分的分析,在这里调用前3部分的得到键值的结果,然后判定结果做相应的函数和功能处理,这里给的直接是函数体,用户可以直接在函数体里面添加响应的代码。但更有效的方式应该是把执行函数换成标记变量,这样程序执行会更加有条理并且不错误的占用时间片。


最后附上头文件:


#ifndef __FY_KEY_H

#define __FY_KEY_H

 

#include "fy_includes.h"

 

#define READ_KEY1 P30

#define READ_KEY2 P14

#define READ_KEY3 P13

#define READ_KEY4 P10

//#define READ_KEY5 P17

 

#define KEY1_PRES 0x01

#define KEY2_PRES 0x02

#define KEY3_PRES 0x04

#define KEY4_PRES 0x08

#define KEY5_PRES 0x10

 

#define KEY_SHORT  0x40

#define KEY_LONG   0x80

#define KEY_NONE      0

 

#define HOT_KEY_PRES KEY1_PRES

#define PWR_KEY_PRES KEY2_PRES

#define H2_KEY_PRES KEY3_PRES

#define MP3_KEY_PRES KEY4_PRES

 

void Key_Configuration(void);

void Key_Hander(void);

 

#endif

 

/*********************************************END OF FILE**********************************************/


推荐阅读

史海拾趣

Amphenol(安费诺)公司的发展小趣事

Amphenol Corporation,通常简称为Amphenol,是一家全球领先的连接技术公司,提供广泛的连接器解决方案,涵盖了航空航天、汽车、通信、工业、医疗和军事等多个领域。以下是关于Amphenol公司发展的五个相关故事:

  1. 创立与起步阶段:Amphenol公司成立于1932年,总部位于美国康涅狄格州沃拉姆。最初,公司致力于生产无线电连接器,并通过不断的创新和技术改进,逐步发展成为连接器行业的领导者之一。在第二次世界大战期间,Amphenol公司为美国军队提供了大量的军用连接器,为公司的发展奠定了坚实的基础。

  2. 技术创新与产品多样化:随着电子技术的不断进步和市场需求的不断变化,Amphenol公司不断进行技术创新,拓展产品线的多样性。除了传统的连接器产品,公司还开发了许多新型连接器,如光纤连接器、高速数据连接器、环境密封连接器等,以满足不同行业的需求。

  3. 全球化发展与市场拓展:Amphenol公司在全球范围内建立了广泛的销售网络和生产基地,实现了全球化布局和市场覆盖。公司在美国、欧洲、亚洲和其他地区设有多个生产工厂和研发中心,为客户提供当地化的服务和支持。通过不断扩大市场份额和提升产品质量,Amphenol公司成为了全球连接器市场的主要参与者之一。

  4. 客户合作与定制化解决方案:Amphenol公司与客户建立了紧密的合作关系,根据客户的需求和应用场景,提供定制化的连接器解决方案。公司的工程团队与客户共同设计和开发符合特定要求的产品,为客户提供专业化和个性化的服务。这种定制化解决方案能够满足客户多样化的需求,增强了公司在市场上的竞争优势。

  5. 质量管理与持续改进:Amphenol公司高度重视产品质量管理,严格遵循国际质量管理体系标准。公司的生产工艺和质量管理体系通过了ISO 9001质量管理体系认证,并持续进行质量改进和技术创新。此外,Amphenol公司的产品还通过了各种行业认证和标准,如航空航天领域的AS9100认证等,确保产品的可靠性和稳定性。

这些故事展示了Amphenol公司从创立初期到如今在技术创新、产品多样化、全球化发展、客户合作与定制化解决方案以及质量管理与持续改进等方面取得的重要进展。

Beta Dyne Inc公司的发展小趣事

随着社会对环保问题的关注度不断提高,Beta Dyne也积极响应国家号召,将环保理念融入公司的经营发展中。公司采用环保材料和生产工艺,减少生产过程中的污染排放。同时,Beta Dyne还积极参与社会公益事业,为当地社区的发展做出了积极贡献。

这些故事虽然是以虚构的形式呈现,但它们反映了电子行业中许多公司可能经历的发展阶段和挑战。每个公司都有其独特的发展历程和故事,Beta Dyne Inc也不例外。如果您对该公司的具体发展情况感兴趣,建议查阅相关资料或联系该公司以获取更准确的信息。

Feller US公司的发展小趣事

菲斯克(FSR.US)作为一家新兴的电动汽车制造商,自创立之初便面临着巨大的挑战。资金紧张是公司面临的首要问题,为了维持运营和推进研发,公司不得不频繁地寻求融资。在一次次的融资尝试中,菲斯克展现了其坚定的决心和创新的理念,虽然过程充满艰辛,但公司最终通过不断努力,成功获得了一笔重要的投资,为公司的发展奠定了坚实的基础。

General Instrument Corp公司的发展小趣事
确认串行接口连接正确,数据传输无误。
台湾义隆电子(ELAN)公司的发展小趣事

义隆电子非常重视研发实力和人才培养。公司拥有一支高素质的研发团队,员工总数的七成以上为研发人才。公司每年投入大量资金用于研发新产品,并注重培养员工的创新能力和实践能力。此外,义隆电子还与国内外知名学府合作,共同培养高素质的人才,为公司的长期发展提供了有力保障。

Handok Co Ltd公司的发展小趣事

义隆电子在多个技术领域保持领先地位,如数字模拟混合技术、高阶设计流程等。这些技术优势使得公司能够迅速掌握市场脉动,开发出具有实用性的新产品。同时,义隆电子还注重将先进技术应用于实际产品中,如高性能的触摸屏控制器、触摸板模块等,这些产品广泛应用于智能手机、平板电脑等消费电子产品中,赢得了市场的广泛认可。

问答坊 | AI 解惑

多线程技术在数据实时采集分析中的应用(2)

数据分析线程在读信号量和消费者指针的控制下成功读取一包数据后,根据通道标示号提取此包中每个通道的数据,写入对应的内存映射文件中,再调用数据处理函数对每个通道数据做误码分析。误码分析的结果可由主界面线程调用显示。数据分解流程如图4所 ...…

查看全部问答>

Windows CE驱动程序开发.pdf

Windows CE驱动程序开发.pdf…

查看全部问答>

evc 提示框的实现

大家好,我想请问各位大虾,如何在evc下实现状态提示呢,比如说我正在上传文件会有一个窗口提示正在上传,等传输完毕了提示上传成功,然后就关闭了!!请各位大虾多帮忙,谢谢各位了!!!…

查看全部问答>

想转行做数据通信了。

兄弟在嵌入式领域混了快2年了,整天做驱动程序,什么 gpio啊 boot room nand nor 电路图啊寄存器啊 ,跟这些东西打交道,有点烦了,现在想改行做做通信,不知道都需要啥基础知识呢?我的TCP/ip协议学的蛮精的也。…

查看全部问答>

在应用程序里如何查询某个USB连在哪一个USB controller上

在具有多个USB controller和多个USB设备的情况下, 如何查询这些USB设备连接在哪个USB controller上。 设备管理器中,如果选择“依连接排序设备”,就可以看到所 有设备的Device Tree,VC里有什么API或者什么方法可以获得 类似的信息吗? 请高 ...…

查看全部问答>

LED日光灯电源设计心得

非隔离型降压式电源设计方法概论 非隔离降压型电源是现在普遍使用的电源结构,几乎占了日光灯电源百分之九十以上。很多人都以为非隔离电源只有降压型一种,每每一说到不隔离,就想到降压型,就想到说对灯不安全(指电源损坏)。其实降压型不只是一 ...…

查看全部问答>

μC/OS-II实时性能测试指标

μC/OS-II实时性能测试指标   衡量嵌入式实时操作系统的好坏一般主要参考以下主要性能指标参数: 任务切换时间、中断响应时间、任务响应时间、任务创建/删除时间、交替信号量时间、取得/释放信号量时间、交替消息队列传输时间等。本文仅对前2个 ...…

查看全部问答>

各位大侠看看我的程序哪儿有问题啊

我用hot51开发板做实现测量电压和输出相同电压的功能,但是没有响应,麻烦大家指出我程序的问题,谢谢了 /* PCF8591四路电压 LCD1602显示 参考电压接至5V电源 */ /* 最小输出电压:0.00V 最大输出电压:5.00V 分辨率:0.02V */ /****************** ...…

查看全部问答>

lpc2366电源电压过高

最近调试LPC2366发现,该芯片再电源电压为3.5V时,可以下载程序但是不能正常工作;…

查看全部问答>

ispLEVER 软件问题,菜鸟求救

我写的一个程序,是Abel编写的,编译没用任何错误,怎么没用生成熔丝图文件啊。我用的是GAL16V8D…

查看全部问答>