欢迎来到淘文阁 - 分享文档赚钱的网站! | 帮助中心 好文档才是您的得力助手!
淘文阁 - 分享文档赚钱的网站
全部分类
  • 研究报告>
  • 管理文献>
  • 标准材料>
  • 技术资料>
  • 教育专区>
  • 应用文书>
  • 生活休闲>
  • 考试试题>
  • pptx模板>
  • 工商注册>
  • 期刊短文>
  • 图片设计>
  • ImageVerifierCode 换一换

    单片机按键处理技巧及C语言编程方式(共7页).doc

    • 资源ID:14960604       资源大小:94KB        全文页数:7页
    • 资源格式: DOC        下载积分:20金币
    快捷下载 游客一键下载
    会员登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录   QQ登录  
    二维码
    微信扫一扫登录
    下载资源需要20金币
    邮箱/手机:
    温馨提示:
    快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
    如填写123,账号就是123,密码也是123。
    支付方式: 支付宝    微信支付   
    验证码:   换一换

     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    单片机按键处理技巧及C语言编程方式(共7页).doc

    精选优质文档-倾情为你奉上单片机按键处理技巧及编程方式    在基于单片机为核心构成的应用系统中,用户输入是必不可少的一部分。输入可以分很多种情况,譬如有的系统支持PS2键盘的接口,有的系统输入是基于编码器,有的系统输入是基于串口或者USB或者其它输入通道等等。在各种输入途径中,更常见的是,基于单个按键或者由单个键盘按照一定排列构成的矩阵键盘(行列键盘)。我们这一篇章主要讨论的对象就是基于单个按键的程序设计,以及矩阵键盘的程序编写。 按键检测的原理: 它们和我们的单片机系统的I/O口连接一般如下:     对于单片机I/O内部有上拉电阻的微控制器而言,还可以省掉外部的那个上拉电阻。简单分析一下按键检测的原理。当按键没有按下的时候,单片机I/O通过上拉电阻R接到VCC,我们在程序中读取该I/O的电平的时候,其值为1(高电平); 当按键S按下的时候,该I/O被短接到GND,在程序中读取该I/O的电平的时候,其值为0(低电平) 。这样,按键的按下与否,就和与该按键相连的I/O的电平的变化相对应起来。结论:我们在程序中通过检测到该I/O口电平的变化与否,即可以知道按键是否被按下,从而做出相应的响应。一切看起来很美好,是这样的吗? 在我们通过上面的按键检测原理得出上述的结论的时候,那就是现实中按键按下时候的电平变化状态。我们的结论是基于理想的情况得出来的,而实际中,由于按键的弹片接触的时候,并不是一接触就紧紧的闭合,它还存在一定的抖动,尽管这个时间非常的短暂,但是对于我们执行时间以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就一直死死的盯住该按键,其它事情都不管了,那其它事情不干了吗?消除抖动有必要吗? 的确,软件上的消抖确实可以保证按键的有效检测。但是,这种消抖确实有必要吗?抖动的出现即意味着按键已经按下,尽管这个电平还没有稳定。所以只要我们检测到按键按下,即可以返回键值,问题的关键是,在你执行完其它任务的时候,再次执行我们的按键任务的时候,抖动过程还没有结束,这样便有可能造成重复检测。所以,如何在返回键值后,避免重复检测,或者在按键一按下就执行功能函数,当功能函数的执行时间小于抖动时间时候,如何避免再次执行功能函数,就成为我们要考虑的问题了。所以消除抖动的目的是:防止按键一次按下,多次响应。   基于状态转移的独立按键程序设计      本章所描述的按键程序要达到的目的:检测按键按下,短按,长按,释放。即通过按键的返回值我们可以获取到如下的信息:按键按下(短按),按键长按,按键连_发,按键释放。这样的功能到底是如何实现的呢,今天就让我们来剖析它的原理吧。下面让我们来简单的描绘一下它的状态流程转移图。   下面对上面的流程图进行简要的分析。 首先按键程序进入初始状态S1,在这个状态下,检测按键是否按下,如果有按下,则进入按键消抖状态2,在下一次执行按键程序时候,直接由按键消抖状态进入按键按下状态3,在此状态下检测按键是否按下,如果没有按键按下,则返回初始状态S1,如果有则可以返回键值,同时进入长按状态S4,在长按状态下每次进入按键程序时候对按键时间计数,当计数值超过设定阈值时候,则表明长按事件发生,同时进入按键连_发状态S5。如果按键键值为空键,则返回按键释放状态S6,否则继续停留在本状态。在按键连_发状态下,如果按键键值为空键则返回按键释放状态S6,如果按键时间计数超过连_发阈值,则返回连_发按键值,清零时间计数后继续停留在本状态。 下面让我们一起来编写按键驱动程序吧。   下面是我使用的硬件的连接图。    硬件连接很简单,四个独立按键分别接在P30-P33四个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 ;      return KEY_NULL ; 其中io_key_1等是我们按键端口的定义,如下所示: sbit io_key_1 = P30 ; sbit io_key_2 = P31 ; sbit io_key_3 = P32 ; sbit io_key_4 = P33 ; 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 KEY_STATE_PRESS             2 #define KEY_STATE_LONG             3 #define KEY_STATE_CONTINUE       4 #define KEY_STATE_RELEASE         5 接着我们开始编写完整的上层按键扫描函数,按键的短按,长按,连按,释放等等状态的判断均是在此函数中完成。对照状态流程转移图,然后再看下面的函数代码,可以更容易的去理解函数的执行流程。完整的函数代码如下: void GetKey(uint8 *pKeyValue)      static uint8 s_u8KeyState = KEY_STATE_INIT ;      static uint8 s_u8KeyTimeCount = 0 ;      static uint8 s_u8LastKey = KEY_NULL ;   /保存按键释放时候的键值      uint8 KeyTemp = KEY_NULL ;      KeyTemp = KeyScan() ;         /获取键值      switch(s_u8KeyState)               case KEY_STATE_INIT :                                       if(KEY_NULL != (KeyTemp)                                               s_u8KeyState = KEY_STATE_WOBBLE ;                                                break ;          case KEY_STATE_WOBBLE :       /消抖                                       s_u8KeyState = KEY_STATE_PRESS ;                               break ;          case KEY_STATE_PRESS :                                       if(KEY_NULL != (KeyTemp)                                               s_u8LastKey = KeyTemp ; /保存键值,以便在释放按键状态返回键值                          KeyTemp |= KEY_DOWN ;   /按键按下                          s_u8KeyState = KEY_STATE_LONG ;                                           else                                               s_u8KeyState = KEY_STATE_INIT ;                                                break ;          case KEY_STATE_LONG :                                       if(KEY_NULL != (KeyTemp)                                               if(+s_u8KeyTimeCount > KEY_LONG_PERIOD)                                                       s_u8KeyTimeCount = 0 ;                              KeyTemp |= KEY_LONG ;   /长按键事件发生                              s_u8KeyState = KEY_STATE_CONTINUE ;                                                                    else                                               s_u8KeyState = KEY_STATE_RELEASE ;                                                break ;          case KEY_STATE_CONTINUE :                                       if(KEY_NULL != (KeyTemp)                                               if(+s_u8KeyTimeCount > KEY_CONTINUE_PERIOD)                                                       s_u8KeyTimeCount = 0 ;                              KeyTemp |= KEY_CONTINUE ;                                                                    else                                               s_u8KeyState = KEY_STATE_RELEASE ;                                                break ;          case KEY_STATE_RELEASE :                                       s_u8LastKey |= KEY_UP ;                      KeyTemp = s_u8LastKey ;                      s_u8KeyState = KEY_STATE_INIT ;                           break ;          default : break ;           *pKeyValue = KeyTemp ; /返回键值     关于这个函数内部的细节我并不打算花过多笔墨去讲解。对照着按键状态流程转移图,然后去看程序代码,你会发现其实思路非常清晰。最能让人理解透彻的,莫非就是将整个程序自己看懂,然后想象为什么这个地方要这样写,抱着思考的态度去阅读程序,你会发现自己的程序水平会慢慢的提高。不管怎么样,这样的一个程序已经完成了本章开始时候要求的功能:按下,长按,连按,释放。事实上,如果掌握了这种基于状态转移的思想,你会发现要求实现其它按键功能,譬如,多键按下,功能键等等,亦相当简单,在下一章,我们就去实现它。 在主程序中我编写了这样的一段代码,来演示我实现的按键功能。 void main(void)           uint8 KeyValue = KEY_NULL;      uint8 temp = 0 ;        LED_CS11 = 1 ; /流水灯输出允许      LED_SEG = 0 ;      LED_DIG = 0 ;      Timer0Init() ;      KeyInit() ;      EA = 1 ;      while(1)               Timer0MainLoop() ;          KeyMainLoop(&KeyValue) ;                   if(KeyValue = (KEY_VALUE_1 | KEY_DOWN) P0 = 1 ;          if(KeyValue = (KEY_VALUE_1 | KEY_LONG) P0 = 2 ;          if(KeyValue = (KEY_VALUE_1 | KEY_CONTINUE) P0 = 0xf0;          if(KeyValue = (KEY_VALUE_1 | KEY_UP) P0 = 0xa5 ;           按住第一个键,可以清晰的看到P0口所接的LED的状态的变化。当按键按下时候,第一个LED灯亮,等待2 S后第二个LED亮,第一个熄灭,表示长按事件发生。再过500 ms 第58个LED闪烁,表示连按事件发生。当释放按键时候,P0口所接的LED的状态为: 灭亮灭亮亮灭亮灭,这也正是P0 = 0xa5这条语句的功能。专心-专注-专业

    注意事项

    本文(单片机按键处理技巧及C语言编程方式(共7页).doc)为本站会员(飞****2)主动上传,淘文阁 - 分享文档赚钱的网站仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知淘文阁 - 分享文档赚钱的网站(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    关于淘文阁 - 版权申诉 - 用户使用规则 - 积分规则 - 联系我们

    本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

    工信部备案号:黑ICP备15003705号 © 2020-2023 www.taowenge.com 淘文阁 

    收起
    展开