曙海教育集团论坛单片机专区单片机高级 → 从单片机初学者迈向单片机工程师,之KEY主题讨论第二章,基于状


  共有6786人关注过本帖树形打印

主题:从单片机初学者迈向单片机工程师,之KEY主题讨论第二章,基于状

美女呀,离线,留言给我吧!
wangxinxin
  1楼 个性首页 | 博客 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:青蜂侠 帖子:1393 积分:14038 威望:0 精华:0 注册:2010-11-12 11:08:23
从单片机初学者迈向单片机工程师,之KEY主题讨论第二章,基于状  发帖心情 Post By:2010-12-8 10:47:17

关于这部分的按键处理,我基本上是没有按照原作者的思路了。因为前几章节作者都是把所有的代码都放出来,我只要稍作修改就能在自己的板子上看到结果,这一章节原作者仅仅贴出部分代码。而且原作者一次性把多个按键,单击,连发都一次性解决了,对于我这种菜鸟来说,一时还真反应不过来。于是自己重新查找了些资料,个人感觉还是能把这部分弄懂了。要看原作者的文章请进入:http://www.eehome.cn/read-htm-tid-30530.html。我这里主要是通过马潮的《基于AVR的单片嵌入式系统原理与实践应用》来讲解的。http://wenku.baidu.com/view/c98cc97931b765ce050814 98.html

  好了,言归正传。上一节我们已经讲到按键的一些基本情况。这章节我主要讲讲怎么用状态机的方式来处理按键。我们把单个按键作为一个简单的系统,根据状态机的原理对其动作和确认的过程进行分析,并用状态图表示出来,然后根据状态图编写出按键接口程序。把单个按键看成是一个状态机话,首先需要对一次按键操作和确认的实际过程进行分析,根据实际的情况和系统的需要确定按键在整个过程的状态,每个状态的输入信号和输出信号,以及状态之间的转换关系。最后还要考虑时间序列的间隔。采用状态机对一个系统进行分析是一项非常细致的工作,它实际上是建立在对真实系统有了全面深入的了解和认识的基础之上,进行综合和抽象化的模型建立的过程。这个模型必须与真实的系统相吻合,既能正确和全面的对系统进行描述,也能够适合使用软件或硬件方式来实现。在一个嵌入式系统中,按键的操作是随机的,因此系统软件对按键需要一直循环查询。由于按键的检测过程需要进行消抖处理,因此取状态机的时间序列的周期为10ms左右,这样不仅可以跳过按键抖动的影响,同时也远小于按键0.3-0.5秒的稳定闭合期,不会将按键操作过程丢失。很明显,系统的输入信号是与按键连接的I/O口电平,"1"表示按键处于开放状态,"0"表示按键处于闭合状态。而系统的输出信号则表示检测和确认到一次按键的闭合操作,用"1"表示。

  上图给出了一个简单按键状态机的状态转换图。在图中,将一次按键完整的操作过程分解为3个状态,采用时间序列周期为10ms。下面对该图做进一步的分析和说明,并根据状态图给出软件的实现方法。首先,读者要充分体会时间序列的作用。在这个系统中,采用的时间序列周期为10ms,它意味着,每隔10ms检测一次按键的输入信号,并输出一次按键的确认信号,同时按键的状态也发生一次转换。图中"状态0"为按键的初始状态,当按键输入为"1"时,表示按键处于开放,输出"0"(1/0),下一状态仍旧为"状态0"。当按键输入为"0",表示按键闭合,但输出还是"0"(0/0)(没有经过消抖,不能确认按键真正按下),下一状态进入"状态1"。"状态1"为按键闭合确认状态,它表示了在10ms前按键为闭合的,因此当再次检测到按键输入为"0"时,可以确认按键被按下了(经过10ms的消抖),输出"1"表示确认按键闭合(0/1),下一状态进入"状态2"。而当再次检测到按键的输入为"1"时,表示按键可能处在抖动干扰,输出为"0"(1/0),下一状态返回到"状态0"。这样,利用状态1,实现了按键的消抖处理。"状态2"为等待按键释放状态,因为只有等按键释放后,一次完整的按键操作过程才算完成。从对上图的分析中可以知道,在一次按键操作的整个过程,按键的状态是从"状态0"->"状态1"->"状态2",最后返回到"状态0"的。并且在整个过程中,按键的输出信号仅在"状态1"时给出了唯一的一次确认按键闭合的信号"1"(其它状态均输出"0")。所以上面状态机所表示的按键系统,不仅克服了按键抖动的问题,同时也确保在一次按键整个的过程中,系统只输出一次按键闭合信号("1")。换句话讲,不管按键被按下的时间保持多长,在这个按键的整个过程中都只给出了一次确认的输出,因此在这个设计中,按键没有"连发"功能,它是一个最简单和基本的按键。一旦有了正确的状态转换图,就可以根据状态转换图编写软件了。在软件中实现状态机的方法和程序结构通常使用多分支结构(IF-ELSEIF-ELSE、CASE等)实现。下面是根据上图、基于状态机方式编写的简单按键接口函数GetKey()。

  uchar GetKey()

  {

  uchar keyRetu=0; //返回的按键值

  static uchar s_keyState=0; //按键状态

  switch (s_keyState)

  {

  case 0:

  if(key1==0) //检测到有按键,转到状态1,相当于是消抖过程

  {

  s_keyState=1;

  }

  break;

  case 1:

  if(key1==0) //再次检测到有按键,确认按键按下,返回一个值,并转到状态2

  {

  keyRetu=1;

  s_keyState=2;

  }

  else

  {

  s_keyState=0; //没有检测到按键,说明状态0检测到是一个抖动,重新转到状态0

  }

  break;

  case 2:

  if(key1==1) //检测到按键松开,状态转到状态0,一次完整的按键过程结束

  {

  s_keyState=0;

  }

  break;

  }

  return keyRetu;

  }

  该简单按键接口函数GetKey()在整个系统程序中应每隔10ms调用执行一次,每次执行时进入用switch结构构成的状态机。switch结构中的case语句分别实现了3个不同状态的处理判别过程,在每个状态中将根据状态的不同,以及key1的值(状态机的输入)确定输出值(keyRetu),和确定下一次按键的状态值(s_keyState)。函数GetKey()的返回参数提供上层程序使用。返回值为0时,表示按键无动作;而返回1表示有一次按键闭合动作,需要进入按键处理程序做相应的键处理。在函数GetKey()中定义了2个局部变量,其中keyRetu为一般普通的局部变量,每次函数执行时,key_return为函数的返回值,总是先初始化为0,只有在状态1中重新置1,作为表示按键确认的标志返回。变量s_keyState非常重要,它保存着按键的状态值,该变量的值在函数调用结束后不能消失,必须保留原值,因此在程序中定义为"局部静态变量",用static声明。如果使用的语言环境不支持static类型的局部变量,则应将s_keyState定义为全局变量(关于局部静态变量的特点请参考我以前的文章:http://hi.baidu.com/dxstar/blog/item/90bdbe02d9e50 c8be950cdcd.html)。

  最后,我们来测试一下效果。这里要达到的效果就是:数码管循环显示00-99,每按一次键,数字加1。

  -----------------------const.h-------------------- -------

  #ifndef _CONST_H_

  #define _CONST_H_

  typedef unsigned char uchar;

  typedef unsigned int uint;

  #endif

  -----------------------main.c--------------------- --

  #include<reg52.h>

  #include"const.h"

  #include"Timer.h"

  #include"Display.h"

  #include"key.h"

  void main()

  {

  Timer0Init();

  EA=1;

  while(1)

  {

  if(g_systTime2Ms) //每2ms扫描显示

  {

  g_systTime2Ms=0;

  DsipNum();

  }

  if(g_time10Ms) //每10ms扫描一次按键

  {

  g_time10Ms=0;

  if(GetKey()==1) //接收到的值是否为1,即是否按键按下

  {

  if(++g_num>=100)

  {

  g_num=0;

支持(0中立(0反对(0单帖管理 | 引用 | 回复 回到顶部

返回版面帖子列表

从单片机初学者迈向单片机工程师,之KEY主题讨论第二章,基于状








签名