




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
状态机思路在单片机程序设计中的应用1,状态机的概念:状态机是软件编程中的一个重要概念。比这个概念更重要的是对它的灵活应用。在一个思路清晰而且高效的程序中,必然有状态机的身影浮现。比如说一个按键命令解析程序,就可被看做状态机:本来在A状态下,触发一个按键后切换到了B状态;再触发另一个键后切换到C状态,或者返回到A状态。最简按键状态机例。实际按键解析程序比这更复杂,但不影响我们对状态机的认识。进一步,击键动作本身也可看做一个状态机。一个细小的击键动作包含了:释放、抖动、闭合、抖动和重新释放等状态。同样,一个串行通信的时序(不管它是遵循何种协议,标准串口也好、I2C也好;也不管它是有线的、还是红外的、无线的)也都可看做由一系列有限的状态构成。显示扫描程序也是状态机;通信命令解析程序也是状态机;甚至连继电器的吸合/释放控制、发光管(LED)的亮/灭控制又何尝不是个状态机。当我们打开思路,把状态机作为一种思想导入到程序中去时,就会找到解决问题的一条有效的捷径。有时候用状态机的思维去思考程序该干什么,比用控制流程的思维去思考,可能会更有效。这样一来状态机便有了更实际的功用。2,程序其实就是状态机:也许你还不理解上面这句话。请想想看,计算机的大厦不就是建立在“0”和“1”两个基本状态的地基之上么?3,状态机的要素:状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:①现态:是指当前所处的状态。②条件:又称为“事件”。当一条件被满足,将触发一动作,或执行一状态的迁移。③动作:条件满足后执行的动作。动作执行完毕后,可迁移到新状态,也可仍旧保持原状。动作非必需的,当条件满足后,也可不执行任何动作,直接迁移到新状态。④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。若我们进一步归纳,把“现态”和“次态”统一起来,而把“动作”忽略(降格处理),则只剩下两个最关键的要素,即:状态、迁移条件。状态机的表示方法有多种,可用文字、图形或表格的形式来表示一个状态机。纯粹用文字描述是很低效的,所以就不介绍了。接下来先介绍图形的方式。4,状态迁移图(STD):状态迁移图(STD),是一种描述系统的状态、以及相互转化关系的图形方式。状态迁移图的画法有许多种,不过一般都大同小异。我们结合一个例子来说明一下它的画法,如图1所示。图1状态迁移图①状态框:用方框表示状态,包括所谓的“现态”和“次态”。②条件及迁移箭头:用箭头表示状态迁移的方向,并在该箭头上标注触发条件。③节点圆圈:当多个箭头指向一个状态时,可用节点符号(小圆圈)连接汇总。④动作框:用椭圆框表示。⑤附加条件判断框:用六角菱形框表示。状态迁移图和我们常见的流程图相比有着本质的区别,具体体现为:在流程图中,箭头代表了程序PC指针的跳转;而在状态迁移图中,箭头代表的是状态的改变。我们会发现,这种状态迁移图比普通程序流程图更简练、直观、易懂。这正是我们需要达到的目的。状态迁移表除了状态迁移图,我们还可用表格的形式来表示状态之间的关系。这种表一般称为状态迁移表。表1就是前面介绍的那张状态迁移图的另一种描述形式。表1状态迁移表状态(现态)状态描述条件动作次态状态1条件1状态2条件2动作1状态3状态2条件3附加条件满足状态4附加条件不满足状态3条件5状态1条件6动作2状态2状态3条件4状态4条件5状态1状态4条件5状态1①采用表格方式来描述状态机,优点是可容纳更多的文字信息。例如,我们不但可在状态迁移表中描述状态的迁移关系,还可把每个状态的特征描述也包含在内。②若表格内容较多,过于臃肿不利于阅读,我们也可将状态迁移表进行拆分。经过拆分后的表格根据其具体内容,表格名称也有所变化。③比如,我们可把状态特征和迁移关系分开列表。被单独拆分出来的描述状态特征的表格,也可称为“状态真值表”。这其中比较常见的就是把每个状态的显示内容单独列表。这种描述每个状态显示内容的表称之为“显示真值表”。同样,我们把单独表述基于按键的状态迁移表称为“按键功能真值表”。另外,若每一个状态包含的信息量过多,我们也可把每个状态单独列表。④由此可见,状态迁移表作为状态迁移图的有益补充,它的表现形式是灵活的。⑤状态迁移表优点是信息涵盖面大,缺点是视觉上不够直观,因此它并不能取代状态迁移图。比较理想的是将图形和表格结合应用。用图形展现宏观,用表格说明细节。二者互为参照,相得益彰。用状态机思路实现一个时钟程序接下来,我将就状态机的应用,结合流程图、状态迁移图和状态迁移,举一个实际例子。下面这张图是一个时钟程序的状态迁移图,如图2所示。图2时钟程序状态迁移图把这张图稍做归纳,就可得到它的另一种表现形式——状态迁移表,如表2所示。表2时钟程序状态迁移表工作状态(现态)状态描述A键B键显示内容编号说明次态功能次态功能0显示时间1-3-时:分:秒(12:00:001状态2显示闹钟2-5-时:分(12:00)2显示秒表0--启动/停止时:分:秒(12:00:003设置小时-时+14-(12:▋▋:▋▋)4设置分钟-分+1;秒=00-(▋▋:00:00)5设置闹钟“时”-时+16-(TM:00:▋▋)6设置闹钟“分”-分+17-(TM:▋▋:00)7设置鸣叫时间-鸣叫分钟+11-(TM:SP:00状态机应用的注意事项基于状态机的程序调度机制,其应用的难点并不在于对状态机概念的理解,而在于对系统工作状态的合理划分。初学者往往会把某个“程序动作”当作是一种“状态”来处理。我称之为“伪态”。那么如何区分“动作”和“状态”。本匠人的心得是看二者的本质:“动作”是不稳定的,即使没有条件的触发,“动作”一旦执行完毕就结束了;而“状态”是相对稳定的,若没有外部条件的触发,一个状态会一直持续下去。初学者的另一种比较致命的错误,就是在状态划分时漏掉一些状态。我称之为“漏态”。“伪态”和“漏态”这两种错误的存在,将会导致程序结构的涣散。因此要特别小心避免。更复杂的状态机前面介绍的是一种简单的状态结构。它只有一级,并且只有一维,如图3所示。图3线性状态机结构若有必要,我们可建立更复杂的状态机模型。1多级状态结构状态机可是多级的。在分层的多级状态机系统中,一“父状态”下可划分多个“子状态”,这些子状态共同拥有上级父状态的某些共性,同时又各自拥有自己的一些个性。在某些状态下,还可进一步划分子状态。如我们可把前面的时钟例子修改如下:
把所有和时钟功能有关的状态,合并成1个一级状态。在这个状态下,又可划分出3个二级子状态,分别为显示时间、设置小时、设置分钟;同样,我们也可把所有和闹钟功能有关的状态,合并成1个一级状态。在此状态下,再划分出4个二级子状态,分别为显示闹钟、设置“时”、设置“分”、设置鸣叫时间。我们需要用另一个状态变量(寄存器)来表示这些子状态。子状态下面当然还可有更低一级的孙状态(子子孙孙无穷尽也),从而将整个状态体系变成了树状多级状态结构,如图4所示。图4树状多级状态结构2多维状态结构状态结构也可是多维的。从不同的角度对系统进行状态的划分,这些状态的某些特性是交叉的。比如,在按照按键和显示划分状态的同时,又按照系统的工作进程做出另一种状态划分。这两种状态划分同时存在,相互交叉,从而构成了二维的状态结构空间。举一个这方面的例子,如:空调遥控器,如图5所示。图5多维状态机结构同样,我们也可构建三维、四维甚至更多维的状态结构。每一维的状态都需要用一个状态变量(寄存器)来表示。无论多级状态结构和多维状态结构看上去多么迷人,匠人的忠告是:我们依然要尽可能地简化状态结构,能用单级、单维的结构,就不要给自己找事,去玩那噩梦般的复杂结构。简单的才是最有效的。结束语对状态机的理解需要一个由浅入深的过程。这个过程应该是与实践应用和具体案例思考相结合的。当一种良好的思路成为设计的习惯,它就能给设计者带来回报。愿这篇手记里介绍的基于状态机的编程思路能给新手们带来一些启迪,帮助大家找到“程序设计”的感觉。【转载1】有限状态机的实现<type="text/javascript">有限状态机(FiniteStateMachine或者FiniteStateAutomata)是软件领域中一种重要的工具,很多东西的模型实际上就是有限状态机。最近看了一些游戏编程AI的材料,感觉游戏中的AI,第一要说的就是有限状态机来实现精灵的AI,然后才是A*寻路,其他学术界讨论比较多的神经网络、模糊控制等问题还不是很热。FSM的实现方式:1)switch/case或者if/else这无意是最直观的方式,使用一堆条件判断,会编程的人都可做到,对简单小巧的状态机来说最合适,但是毫无疑问,这样的方式比较原始,对庞大的状态机难以维护。2)状态表维护一个二维状态表,横坐标表示当前状态,纵坐标表示输入,表中一个元素存储下一个状态和对应的操作。这一招易于维护,但是运行时间和存储空间的代价较大。3)使用StatePattern使用StatePattern使得代码的维护比switch/case方式稍好,性能上也不会有很多的影响,但是也不是100%完美。不过RobertC.Martin做了两个自动产生FSM代码的工具,forjava和forC++各一个,在/resources/index上有免费下载,这个工具的输入是纯文本的状态机描述,自动产生符合StatePattern的代码,这样developer的工作只需要维护状态机的文本描述,每必要冒引入bug的风险去维护code。4)使用宏定义描述状态机一般来说,C++编程中应该避免使用#define,但是这主要是因为若用宏来定义函数的话,很容易产生这样那样的问题,但是巧妙的使用,还是能够产生奇妙的效果。MFC就是使用宏定义来实现大的架构的。在实现FSM的时候,可把一些繁琐无比的if/else还有花括号的组合放在宏中,这样,在代码中可3)中状态机描述文本一样写,通过编译器的预编译处理产生1)一样的效果,我见过产生C代码的宏,若要产生C++代码,己软MFC可,那么理论上也是可行的。这儿以四位密码校验作为状态机的例子,连续输入2479就可通过密码测试。一个非常简单的例子,在实际的状态机实例中,状态转移表要更復雜一些,不過方式非常類似。在狀態查詢的地方可做優化,同時對于輸入量也可做有效性優化。具體代碼如下:viewplaincopytoclipboardprint?c.htypedefenum{STATE1=1,STATE2,STATE3,STATE4,STATE5,//passwordpass//...ADDhere}STATE;typedefenum{INPUT1='2',INPUT2='4',INPUT3='7',INPUT4='9',}INPUT;typedefstruct{STATEcur_state;INPUTinput;STATEnext_state;}STATE_TRANS;c.htypedefenum{STATE1=1,STATE2,STATE3,STATE4,STATE5,//passwordpass//...ADDhere}STATE;typedefenum{INPUT1='2',INPUT2='4',INPUT3='7',INPUT4='9',}INPUT;typedefstruct{STATEcur_state;INPUTinput;STATEnext_state;}STATE_TRANS;c.c#include<stdio.h>#include"c.h"STATE_TRANSstate_trans_arry[]={{STATE1,INPUT1,STATE2},{STATE2,INPUT2,STATE3},{STATE3,INPUT3,STATE4},{STATE4,INPUT4,STATE5},};#defineSTATE_TRANS_CNT(sizeof(state_trans_arry)/sizeof(state_trans_arry[0]))intmain(){inti;charch;STATEstate_machine=STATE1;while(ch!='e'){ch=getchar();if((ch>='0')&&(ch<='9'))//fordigitpasswordinputonly{for(i=0;i<STATE_TRANS_CNT;i++){if((ch==state_trans_arry[i].input)&&(state_machine==state_trans_arry[i].cur_state)){state_machine=state_trans_arry[i].next_state;continue;}elseif(i==(STATE_TRANS_CNT-1))//notransfermatch,resetstate{state_machine=STATE1;}}if(state_machine==STATE5)printf("Passwordcorrect,statetransfermachinepass!\n");}}return0;}c.c#include<stdio.h>#include"c.h"STATE_TRANSstate_trans_arry[]={{STATE1,INPUT1,STATE2},{STATE2,INPUT2,STATE3},{STATE3,INPUT3,STATE4},{STATE4,INPUT4,STATE5},};#defineSTATE_TRANS_CNT(sizeof(state_trans_arry)/sizeof(state_trans_arry[0]))intmain(){inti;charch;STATEstate_machine=STATE1;while(ch!='e'){ch=getchar();if((ch>='0')&&(ch<='9'))//fordigitpasswordinputonly{for(i=0;i<STATE_TRANS_CNT;i++){if((ch==state_trans_arry[i].input)&&(state_machine==state_trans_arry[i].cur_state)){state_machine=state_trans_arry[i].next_state;continue;}elseif(i==(STATE_TRANS_CNT-1))//notransfermatch,resetstate{state_machine=STATE1;}}if(state_machine==STATE5)printf("Passwordcorrect,statetransfermachinepass!\n");}}return0;}状态图--一个图的数据结构!1.while+switch;2.状态机:是指定系统的所有可能的状态及状态间跳转的条件,然后设一个初始状态输入给这台机器,机器就会自动运转,或最后处于终止状态,或在某一个状态不断循环。游戏中状态切换是很频繁的。可能以前要切换状态就是if~else,或者设标志,但这些都不太结构化,若把它严格的设为一种标准的状态机,会清楚的多。如控制一扇门的运动,初始时门是关的,当有力作用在门上时,门开始慢慢打开,力的作用完后,门渐渐停止不动,当有反向的力时,门又渐渐关上,知道回到初始关的状态。这个你会怎么来编程实现呢。似乎很麻烦,的确,没有状态机的思想时会很烦,设很多标志,一堆if条件。用状态机的话,不只是代码更清晰,关键是更符合逻辑和自然规律,不同状态不同处理,满足条件则跳转到相关状态。伪码如下:enum{CLOSED,//关上状态CLOSING,//正在关状态OPENED,//打开状态OPENING,//正在开的状态}doorState=CLOSED;//初始为关Update(){switch(doorState)caseCLOSED:if(有人推门)doorState=OPENING;//跳转到正在开状态break;caseOPENING:door.Rotation+=DeltaAngle;//门的旋转量递增if(门的速度为零)//力的作用已去doorState=OPENED;//跳转到开状态break;caseOPENED:if(有人关门)doorState=CLOSING;break;caseCLOSING:door.Rotation-=DeltaAngle;//门的旋转量递减if(门的旋转角度减为零)doorState=CLOSED;//门已关上break;}//而绘制代码几乎不用怎么变,门就是会严格按照状态机的指示去运动,该停就会停Render(){RotateView(door.Rotation);DrawDoor(door.Position);}enum{CLOSED,//关上状态CLOSING,//正在关状态OPENED,//打开状态OPENING,//正在开的状态}doorState=CLOSED;//初始为关Update(){switch(doorState)caseCLOSED:if(有人推门)doorState=OPENING;//跳转到正在开状态break;caseOPENING:door.Rotation+=DeltaAngle;//门的旋转量递增if(门的速度为零)//力的作用已去doorState=OPENED;//跳转到开状态break;caseOPENED:if(有人关门)doorState=CLOSING;break;caseCLOSING:door.Rotation-=DeltaAngle;//门的旋转量递减if(门的旋转角度减为零)doorState=CLOSED;//门已关上break;}//而绘制代码几乎不用怎么变,门就是会严格按照状态机的指示去运动,该停就会停Render(){RotateView(door.Rotation);DrawDoor(door.Position);}这是一个简单但很典型的例子,状态机的应用太多了。就说一个基本游戏的运转:(用到了一个状态然后还有子状态)UpdateGame()BEGIN;switch(gameState)case等待选择菜单://它有三个子状态。if(选择菜单项==开始){游戏初始;gameState=开始游戏}if(选择菜单项==选项)gameState
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 乐高考试题及答案
- 广东法学小自考考试题及答案
- 蓝月亮考试题及答案
- 口腔病历考试题及答案
- 课件时间轴模板
- 辽宁省沈文新高考研究联盟2025-2026学年高二上学期开学考试政治试题(含答案)
- 客房考试题及答案合集
- 浸润剂配置工突发故障应对考核试卷及答案
- 羽绒羽毛加工处理工技能比武考核试卷及答案
- 铁合金高炉冶炼工三级安全教育(车间级)考核试卷及答案
- 画法几何及土木工程制图课件
- 第2课 树立科学的世界观《哲学与人生》(高教版2023基础模块)
- 录入与排版教学计划
- 2023免拆底模钢筋桁架楼承板图集
- 云计算技术基础应用教程(HCIA-Cloud)PPT完整全套教学课件
- 呼吸衰竭小讲课课件
- 成人学士学位英语1000个高频必考词汇汇总
- GB/T 5271.29-2006信息技术词汇第29部分:人工智能语音识别与合成
- 全屋定制家居橱柜衣柜整装安装服务规范
- 沥青及沥青混合料试验作业指导书
- 义务教育阶段学生艺术素质测评指标体系小学音乐
评论
0/150
提交评论