资源预览内容
第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
第9页 / 共9页
亲,该文档总共9页全部预览完了,如果喜欢就下载吧!
资源描述
2016-8-6 排版 By:XiaoHe 单片机按键处理技巧及编程方式?1 / 9 单片机按键处理技巧及编程方式 2010-10-23 15:01 从这一章开始,我们步入按键程序设计的殿堂。在基于单片机为核心构成的应用系统中,用户输入是 必不可少的一部分。输入可以分很多种情况,譬如有的系统支持 PS2 键盘的接口,有的系统输入是基于编 码器,有的系统输入是基于串口或者 USB 或者其它输入通道等等。在各种输入途径中,更常见的是,基于 单个按键或者由单个键盘按照一定排列构成的矩阵键盘(行列键盘)。 我们这一篇章主要讨论的对象就是基于 单个按键的程序设计,以及矩阵键盘的程序编写。 按键检测的原理 常见的独立按键的外观如下,相信大家并不陌生,各种常见的开发板学习板上随处可以看到他们的身 影。 总共有四个引脚,一般情况下,处于同一边的两个引脚内部是连接在一起的,如何分辨两个引脚是否 处在同一边呢?可以将按键翻转过来,处于同一边的两个引脚,有一条突起的线将他们连接一起,以标示 它们俩是相连的。如果无法观察得到,用数字万用表的二极管挡位检测一下即可。搞清楚这点非常重要, 对于我们画 PCB 的时候的封装很有益。 它们和我们的单片机系统的 I/O 口连接一般如右图所示: 对于单片机 I/O 内部有上拉电阻的微控制器而言,还可以省掉外部的那个上拉电 阻。简单分析一下按键检测的原理。当按键没有按下的时候,单片机 I/O 通过上拉电阻 R1 接到 VCC,我们在程序中读取该 I/O 的电平的时候,其值为 1(高电平); 当按键 S1 按下的时候,该 I/O 被短接到 GND,在程序中读取该 I/O 的电平的时候,其值为 0(低 电平) 。这样,按键的按下与否,就和与该按键相连的 I/O 的电平的变化相对应起来。 结论: 我们在程序中通过检测到该 I/O 口电平的变化与否, 即可以知道按键是否被按下, 从而做出相应的响应。一切看起来很美好,是这样的吗? 现实并非理想 在我们通过上面的按键检测原理得出上述的结论的时候,其实忽略了一个重要的问题,那就是现实中 按键按下时候的电平变化状态。我们的结论是基于理想的情况得出来的,就如同下面这幅按键按下时候对 应电平变化的波形图一样: 2016-8-6 排版 By:XiaoHe 单片机按键处理技巧及编程方式?2 / 9 而实际中,由于按键的弹片接触的时候,并不是一接触就紧紧的闭合,它还存在一定的抖动,尽管这 个时间非常的短暂,但是对于我们执行时间以 us 为计算单位的微控制器来说, 它太漫长了。因而,实际的波形图应该如下面这幅示意图一样: 这样便存在这样一个问题。假设我们的系统有这样功能需求:在检测到按键按下的时候,将某个 I/O 的 状态取反。由于这种抖动的存在,使得我们的微控制器误以为是多次按键的按下,从而将某个 I/O 的状态不 断取反,这并不是我们想要的效果,假如该 I/O 控制着系统中某个重要的执行的部件,那结果更不是我们所 期待的。于是乎有人便提出了软件消除抖动的思想,道理很简单:抖动的时间长度是一定的,只要我们避 开这段抖动时期,检测稳定的时候的电平不久可以了吗?听起来确实不错,而且实际应用起来效果也还可 以。于是,各种各样的书籍中,在提到按键检测的时候,总也不忘说道软件消抖。就像下面的伪代码所描 述的一样。(假设按键按下时候,低电平有效) If(0 = io_KeyEnter) /如果有键按下了 Delayms(20) ; /先延时 20ms 避开抖动时期 If(0 = io_KeyEnter) /然后再检测,如果还是检测到有键按下 return KeyValue ; /是真的按下了,返回键值 else return KEY_NULL /是抖动,返回空的键值 while(0 = io_KeyEnter) ; /等待按键释放 乍看上去,确实挺不错,实际中呢?在实际的系统中,一般是不允许这么样做的。为什么呢?首先, 这里的 Delayms(20) , 让微控制器在这里白白等待了 20 ms 的时间,啥也没干,考虑我在学会释放 CPU 一章中所提及的几点,这是不可取的。其次 while(0 = io_KeyEnter) 所以合理的分配好微控制的处理时间, 是编写按键程序的基础,更是程序设计中的大忌(极少的特殊情况例外)。任何非极端情况下,都不要使用这 样语句来堵塞微控制器的执行进程。原本是等待按键释放,结果 CPU 就一直死死的盯住该按键,其它事情 都不管了,那其它事情不干了吗?你同意别人可不会同意 2016-8-6 排版 By:XiaoHe 单片机按键处理技巧及编程方式?3 / 9 消除抖动有必要吗? 的确,软件上的消抖确实可以保证按键的有效检测。但是,这种消抖确实有必要吗?有人提出了这样 的疑问。抖动是按键按下的过程中产生的,如果按键没有按下,抖动会产生吗?如果没有按键按下,抖动 也会在 I/O 上出现,我会立刻把这个微控制器锤了,永远不用这样一款微控制器。所以抖动的出现即意味着 按键已经按下,尽管这个电平还没有稳定。所以只要我们检测到按键按下,即可以返回键值,问题的关键 是,在你执行完其它任务的时候,再次执行我们的按键任务的时候,抖动过程还没有结束,这样便有可能 造成重复检测。所以,如何在返回键值后,避免重复检测,或者在按键一按下就执行功能函数,当功能函 数的执行时间小于抖动时间时候,如何避免再次执行功能函数,就成为我们要考虑的问题了。这是一个仁 者见仁,智者见智的问题,就留给大家去思考吧。所以消除抖动的目的是:防止按键一次按下,多次响应。 ? ? “从单片机初学者迈向单片机工程师”之 KEY 主题讨论 基于状态转移的独立按键程序设计 本章所描述的按键程序要达到的目的:检测按键按下,短按,长按,释放。即通过按键的返回值我们 可以获取到如下的信息:按键按下(短按),按键长按,按键连发,按键释放。不知道大家还记得小时候玩过 的电子钟没有,就是外形类似于 CALL 机(CALL )的那种,有一个小液晶屏,还有四个按键,功能是时钟, 闹钟以及秒表。在调整时间的时候,短按+键每次调整值加一,长按的时候调整值连续增加。小的时候很好 奇,这样的功能到底是如何实现的呢,今天就让我们来剖析它的原理吧。状态机,好像是很古老的东西了. 状态在生活中随处可见。譬如早上的时候,闹钟把你叫醒了,这个时候,你便处于清醒的状态,马上你就 穿衣起床洗漱吃早餐, 这一系列事情就是你在这个状态做的事情。 做完这些后你会去等车或者开车去上班, 这个时候你就处在上班途中的状态.中午下班时间到了,你就处于中午下班的状态,诸如此类等等,在每 一个状态我们都会做一些不同的事情,而总会有外界条件促使我们转换到另外一种状态,譬如闹钟叫醒我 们了,下班时间到了等等。对于状态的定义出发点不同,考虑的方向不同,或者会有些许细节上面的差异, 但是大的状态总是相同的。生活中的事物同样遵循同样的规律,譬如,用一个智能充电器给你的手机电池 充电,刚开始,它是处于快速充电状态,随着电量的增加,电压的升高,当达到规定的电压时候,它会转 换到恒压充电。总而言之,细心观察,你会发现生活中的总总都可以归结为一个个的状态,而状态的变换 或者转移总是由某些条件引起同时伴随着一些动作的发生。我们的按键亦遵循同样的规律,下面让我们来 简单的描绘一下它的状态流程转移图。 2016-8-6 排版 By:XiaoHe 单片机按键处理技巧及编程方式?4 / 9 2016-8-6 排版 By:XiaoHe 单片机按键处理技巧及编程方式?5 / 9 下面对上面的流程图进行简要的分析。 首先按键程序进入初始状态 S1,在这个状态下,检测按键是否按下,如果有按下,则进入按键消抖状 态 2,在下一次执行按键程序时候,直接由按键消抖状态进入按键按下状态 3,在此状态下检测按键是否按 下,如果没有按键按下,则返回初始状态 S1,如果有则可以返回键值,同时进入长按状态 S4,在长按状态 下每次进入按键程序时候对按键时间计数,当计数值超过设定阈值时候,则表明长按事件发生,同时进入 按键连发状态 S5。如果按键键值为空键,则返回按键释放状态 S6,否则继续停留在本状态。在按键连发状 态下, 如果按键键值为空键则返回按键释放状态 S6, 如果按键时间计数超过连发阈值, 则返回连发按键值, 清零时间计数后继续停留在本状态。 看了这么多,也许你已经有一个模糊的概念了,接下来让我们趁热打铁,一起来动手编写按键驱动程 序吧。 我使用的硬件的连接图如右图所示: 硬件连接很简单, 四个独立按键分别接在 P3033 四个 I/O 上面。 因为 51 单片机 I/O 口内部结构的限制, 在读取外部引脚状态的时候, 需要向端口写 1,在 51 单片机复位后,不需要进行此操作也可以进行读 取外部引脚的操作。因此,在按键的端口没有复用的情况下,可以省略此 步骤。而对于其它一些真正双向 I/O 口的单片机来说,将引脚设置成输入 状态,是必不可少的一个步骤。 /初始化引脚为输入 void KeyInit(void) io_key_1 = 1; io_key_2 = 1; io_key_3 = 1; io_key_4 = 1; /根据按键硬件连接定义按键键值 #define KEY_VALUE_1 0x0e #define KEY_VALUE_2 0x0d #define KEY_VALUE_3 0x0b #define KEY_VALUE_4 0x07 #define KEY_NULL 0x0f 下面我们来编写按键的硬件驱动程序。 根据第一章所描述的按键检测原理,我们可以很容易的得出如下的代码: static uint8 KeyScan(void) if(io_key_1 = 0)return KEY_VALUE_1 ; if(io_key_2 = 0)return KEY_VALUE_2 ; if(io_key_3 = 0)return KEY_VALUE_3 ; if(io_key_4 = 0)return KEY_VALUE_4 ; 2016-8-6 排版 By:XiaoHe 单片机按键处理技巧及编程方式?6 / 9 return KEY_NULL ; 其中 io_key_1 等是我们按键端口的定义,如下所示: sbit io_key_1 = P3 0 ; sbit io_key_2 = P3 1 ; sbit io_key_3 = P3 2 ; sbit io_key_4 = P3 3 ; KeyScan()作为底层按键的驱动程序,为上层按键扫描提供一个接口,这样我们编写的上层按键扫描函 数可以几乎不用修改就可以拿到我们的其它程序中去使用,使得程序复用性大大提高。同时,通过有意识 的将与底层硬件连接紧密的程序和与硬件无关的代码分开写,使得程序结构层次清晰,可移植性也更好。 对于单片机类的程序而言,能够做到函数级别的代码重用已经足够了。 在编写我们的上层按键扫描函数之前,需要先完成一些宏定义。 /定义长按键的 TICK 数,以及连发间隔的 TICK 数 #define KEY_LONG_PERIOD 100 #define KEY_CONTINUE_PERIOD 25 /定义按键返回值状态(按下,长按,连发,释放) #define KEY_DOWN 0x80 #define KEY_LONG 0x40 #define KEY_CONTINUE 0x20 #define KEY_UP 0x10 /定义按键状态 #define KEY_STATE_INIT 0 #define KEY_STATE_WOBBLE 1 #define
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号