打印

戏法人人会变,各有巧妙不同--用有限状态机对键盘模块的另

[复制链接]
3105|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
dongshan|  楼主 | 2008-1-11 11:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
   最近阿姨家里的几个表哥都在热烈的讨论着状态机,表弟我心痒,也想来凑凑热闹。要想加入他们的讨论,首先得明白什么是状态机,这是今天的主题之一,不过我会首先给出定义,然后谈谈我的浅薄的理解。另外,前些日子正在学习GOF的《设计模式》,其中的state模式,就是面向对象的状态机,此处我会针对嵌入式的键盘模块,用c++语言写出一个例程,及在写此例程过程中的思考过程,这是主题之二。好了,先哆嗦一下,唉,没办法这是多年的习惯了!请不要不看,请耐心看完,请不要说难,let's go!

   状态机是一个有向图形,由一组节点和一组相应的转移函数组成。状态机通过响应一系列事件而“运行”。每个事件都在属于“当前”节点的转移函数的控制范围内,其中函数的范围是节点的一个子集。函数返回“下一个”(也许是同一个)节点。这些节点中至少有一个必须是终态。当到达终态,状态机停止。
    这一段抽象的数学定义,或许会晕倒一片人,但是我们能不能换一个说法来更好的理解状态机呢,学习方法中,我最推崇“类比", 因为天地一大宇宙,人身一小宇宙,天地间一切可类比。就像电流与水流,电学与力学一样。在此表弟我不才,也想对状态机类比一下。
   其实在我看来,有限状态机就是一个人的悲喜剧,生命不息,状态不止。在父母面前,他是一个好的儿子,他所做的事就是孝敬父母,在妻子面前他是一个好丈夫,呵护着自己的老婆,支撑着这个家,在儿女面前,他是一个好爸爸淳淳教导着自己的子女,在老板面前,他是一个好职员,他所做的是为公司的明天而打拼. 如此环环不息,直至生命的终结,没有人是不死的,状态机也一样(在此不考虑无限状态机). 此类比中的"好儿子“,"好丈夫","好爸爸" 就是一个人的不同的自我,在扮演着不同的角色,演绎着不同的故事,可以称之为“状态”,而“孝敬父母”“呵护老婆”“教导子女”则是在不同状态下的行为。 以下是对以上类比的C伪码抽象.

           switch(人.状态)
           {
              case "好儿子":
                孝敬父母;
                break;    
              case "好丈夫":
                 呵护老婆;
                 break; 
              case "好爸爸":
                 教导子女;
                 break;
              default:
            }
以上可能并不准确,因为没考虑其间的状态改变,但是其状态改变是显而易见的,比如到公司上班,就是好员工状态,回到家中,在妻子的面前,你就处于“好丈夫”状态在接儿女放学的路上,你就处在一个“好爸爸”的状态。
  
   接下来,就谈谈FSM(用英文并不是表弟我祟洋媚外,而是少打几个字)在嵌入式键盘模块的应用。并且用C++,及state模块实现之。键盘模块有哪些状态呢,表哥们应都清楚,无非就是,空闲(idle),按下(press),按住(hold),释放(release)状态。每个状态下有不同的行为,也就是要DoSomething,于是用c++抽象一个类如下:
为了简便,在此省略掉hold状态,假设系统仅是有idle,press,release三态
class CKeyState   
   {
   public:
     char m_StFlag;  //用于键盘模块中的状态转换,state模式中不是这样
     CKeyState()
     {
       m_StFlag=0;
     }
     void virtual DoSth(void)=0;  //不同状态下所做的事情,纯虚函数。
     void virtual delay(void)     //延时函数,虚函数,不同状态可以有不同的延时函数,故声明为虚函数
     {
      for(int i=0;i<100;i++)
        for(int j=0;j<255;j++);
     }
   };

class CIdleSt : public CKeyState  //空闲状态
{
  void virtual DoSth(void)
  {
    if(!KEY)                 //有键按下
    {
      delay();               //去抖
      if(!KEY)m_StFlag=1;    //仍然有键按下,则去press态
    }
  }
};

class CPressSt : public CKeyState  //按下状态
{
  void virtual DoSth(void)         
  {
    LED=1;                  //具体的动作,此处为点亮一个LED
    if(KEY)m_StFlag=1;      //按键释放则去Relese态
  }
};

class CReleaseSt : public CKeyState
{
  void virtual DoSth(void)
  {
    delay();     //释放时去抖动
    if(KEY)      
    {
      m_StFlag=0;  //释放则回到IDLE态
      
    }
    else           
    {
      m_StFlag=0;  //是抖动,则仍在press态
    } 
  }
};

以上就是对状态的简单抽象,以下是键盘模块的类的定义,
class CKeyModule
{
public:
  CKeyState *st;       //键盘模块当然包含一个状态
  CKeyModule()
  {
    st=new CIdleSt();  //初始化为idle状态
  }

  void UpdateState(void);  //状态的更新和转换
};

void CKeyModule:: UpdateState()
{
  char tmp=st->m_StFlag;    //根据m_StFlag的值来转换状态
  switch(tmp)
  {
  case 0:
    st = new CIdleSt();
    break;
  case 1:
    st=new CPressSt();
    break;
  case 2:
    st=new CReleaseSt();
    break;      
  default:
    st=new CIdleSt();
  }
}

于是乎在我们的程序中则可应用如下:
#include<ioAT89S52.h>

#define KEY P0_bit.P0_0
#define LED P0_bit.P0_1
。。。

CKeyModule pSysKey;  //定义一个键盘模块对象

int main(void){
  KEY=1;
  LED=0;

  for(;;){
  pSysKey.st->DoSth();    //根据不同的状态,做不同的事,此处正是虚函数的精要
  pSysKey.UpdateState();  //更新状态
  }
}

相关代码在IAR中通过编译,也软件仿真过,本打算在cw08中编译,因为我有一个68hc908jl3的小板,但没想到的是,jl3竟提示RAM不够,看来cw08的编译质量或许比iar差一些。这都只是个人的一点浅见,无意中得罪哪位表哥,请不要放在心上。
   本来就是信手而写,再加上水平太差,如有不当之外,请随意拍砖,就狠狠心,不要把我当表弟看!
   好了,汇报完毕,enjoy it!

相关帖子

沙发
hotpower| | 2008-1-11 11:46 | 只看该作者

沙发~~~

使用特权

评论回复
板凳
dongshan|  楼主 | 2008-1-11 11:56 | 只看该作者

大叔动作好快! 谢谢光顾.

使用特权

评论回复
地板
LastNew| | 2008-1-11 12:07 | 只看该作者

c++忘了差不多了

敬意

使用特权

评论回复
5
gyt| | 2008-1-11 12:18 | 只看该作者

看看

使用特权

评论回复
6
古道热肠| | 2008-1-11 12:23 | 只看该作者

楼主的“类比”理论很不错

只是你的比喻不太贴切。其实人一天干的活也可以类比成状态机。由时间和外部事件触发。
从“早上起床”状态开始,由于定时事件和外部事件很多,导致状态出现很多分枝,但最终返回到“晚上上床睡觉”。

使用特权

评论回复
7
hq_y| | 2008-1-11 12:25 | 只看该作者

呵呵强贴,难得的是语言幽默

使用特权

评论回复
8
xf.zhu| | 2008-1-11 16:09 | 只看该作者

我这个更好!!!!!!

https://bbs.21ic.com/club/bbs/bbsview.asp?essenceID=9054 

使用特权

评论回复
9
dongshan|  楼主 | 2008-1-12 08:46 | 只看该作者

回六楼的

当然是事件驱动的,你为什么去上班,因为早上8点钟到了,你为什么去接小孩放学,因为你老婆打电话让你去接。这些都是事件。

使用特权

评论回复
10
dld2| | 2008-1-12 09:04 | 只看该作者

关于比喻

感觉楼主的比喻有些问题。
状态是排他的,一个时间只能处于一个稳定状态。
而“好丈夫”和“好爸爸”貌似可以同时达到,因此这个类似“或”关系的属性标志。

题外:老婆和妈发生口角,楼主在“好丈夫”和“好儿子”之间频繁切换,恐怕是现实的考虑。因为这两个属性很难同时满足,嘿嘿。

使用特权

评论回复
11
sz_kd| | 2008-1-12 19:10 | 只看该作者

比喻好,顶

使用特权

评论回复
12
lenglx| | 2008-1-14 08:59 | 只看该作者

呵呵。

好。
只不过,程序中有内存泄露。

使用特权

评论回复
13
zhh124| | 2008-1-28 19:33 | 只看该作者

这只是个架子哦~

这只是个架子哦~
真正实用的就是那个hold态

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

79

主题

1143

帖子

7

粉丝