小程序无所谓,工程略微一大,状况一多,这种办法就比较有用
转载正文1有限状况机FSM思想广泛应用于硬件操控电路规划,也是软件上常用的一种处理办法(软件上称为FMM--有限音讯机)。它把杂乱的操控逻辑分解成有限个安稳状况,在每个状况上判别事情,变接连处理为离散数字处理,契合计算机的作业特色。一起,由于有限状况机具有有限个状况,所以能够在实践的工程上完成。但这并不意味着其只能进行有限次的处理,相反,有限状况机是闭环体系,有限无量,能够用有限的状况,处理无量的业务。有限状况机的作业原理如图1所示,产生事情(event)后,依据当时状况(cur_state),决议履行的动作(action),并设置下一个状况号(nxt_state)。 ------------- | |-------->履行动作action 产生事情event ----->| cur_state | | |-------->设置下一状况号nxt_state ------------- 当时状况 图1 有限状况机作业原理 e0/a0 --->-- | | -------->---------- e0/a0 | | S0 |----- | -<------------ | e1/a1 | | e2/a2 V ---------- ---------- | S2 |-----<-----| S1 | ---------- e2/a2 ---------- 图2 一个有限状况机实例 -------------------------------------------- 当时状况 s0 s1 s2 | 事情 -------------------------------------------- a0/s0 -- a0/s0 | e0 -------------------------------------------- a1/s1 -- -- | e1 -------------------------------------------- a2/s2 a2/s2 -- | e2 -------------------------------------------- 表1 图2状况机实例的二维表格表明(动作/下一状况) 图2为一个状况机实例的状况搬运图,它的意义是: 在s0状况,假如产生e0事情,那么就履行a0动作,并坚持状况不变; 假如产生e1事情,那么就履行a1动作,并将状况搬运到s1态; 假如产生e2事情,那么就履行a2动作,并将状况搬运到s2态; 在s1状况,假如产生e2事情,那么就履行a2动作,并将状况搬运到s2态; 在s2状况,假如产生e0事情,那么就履行a0动作,并将状况搬运到s0态; 有限状况机不只能够用状况搬运图表明,还能够用二维的表格代表。一般将当时状况号写在横行上,将事情写在纵列上,如表1所示。其间“--”表明空 (不履行动作,也不进行状况搬运),“an/sn”表明履行动作an,一起将下一状况设置为sn。表1和图2表明的意义是彻底相同的。 调查表1可知,状况机能够用两种办法完成:竖着写(在状况中判别事情)和横着写(在事情中判别状况)。这两种完成在本质上是彻底等效的,但在实践操作中,作用却天壤之别。==================================竖着写(在状况中判别事情)C代码片段================================== cur_state = nxt_state; switch(cur_state){ //在当时状况中判别事情 case s0: //在s0状况 if(e0_event){ //假如产生e0事情,那么就履行a0动作,并坚持状况不变; 履行a0动作; //nxt_state = s0; //由于状况号是本身,所以能够删除此句,以进步运转速度。 } else if(e1_event){ //假如产生e1事情,那么就履行a1动作,并将状况搬运到s1态; 履行a1动作; nxt_state = s1; } else if(e2_event){ //假如产生e2事情,那么就履行a2动作,并将状况搬运到s2态; 履行a2动作; nxt_state = s2; } break; case s1: //在s1状况 if(e2_event){ //假如产生e2事情,那么就履行a2动作,并将状况搬运到s2态; 履行a2动作; nxt_state = s2; } break; case s2: //在s2状况 if(e0_event){ //假如产生e0事情,那么就履行a0动作,并将状况搬运到s0态; 履行a0动作; nxt_state = s0; } }==================================横着写(在事情中判别状况)C代码片段==================================//e0事情产生时,履行的函数void e0_event_function(int * nxt_state){ int cur_state; cur_state = *nxt_state; switch(cur_state){ case s0: //调查表1,在e0事情产生时,s1处为空 case s2: 履行a0动作; *nxt_state = s0; }}//e1事情产生时,履行的函数void e1_event_function(int * nxt_state){ int cur_state; cur_state = *nxt_state; switch(cur_state){ case s0: //调查表1,在e1事情产生时,s1和s2处为空 履行a1动作; *nxt_state = s1; }}//e2事情产生时,履行的函数void e2_event_function(int * nxt_state){ int cur_state; cur_state = *nxt_state; switch(cur_state){ case s0: //调查表1,在e2事情产生时,s2处为空 case s1: 履行a2动作; *nxt_state = s2; }} 上面反正两种写法的代码片段,完成的功用彻底相同,可是,横着写的作用显着好于竖着写的作用。理由如下: 1、竖着写隐含了优先级排序(其实各个事情是同优先级的),排在前面的事情判别将毫无疑问地优先于排在后边的事情判别。这种if/else if写法上的约束将损坏事情间原有的联系。而横着写不存在此问题。 2、由于处在每个状况时的事情数目不一致,并且事情产生的时刻是随机的,无法预先确认,导致竖着写流浪为次序查询办法,结构上的缺点使得许多时刻被糟蹋。关于横着写,在某个时刻点,状况是仅有确认的,在事情里查找状况只需运用switch句子,就能一步定位到相应的状况,推迟时刻能够预先精确预算。并且在事情产生时,调用事情函数,在函数里查找仅有确认的状况,并依据其履行动作和状况搬运的思路清晰简练,功率高,赋有美感。 总归,我个人以为,在软件里写状况机,运用横着写的办法比较稳妥。 竖着写的办法也不是彻底不能运用,在一些小项目里,逻辑不太杂乱,功用精简,一起为了节省内存消耗,竖着写的办法也不失为一种适宜的挑选。 在FPGA类硬件规划中,以状况为中心完成操控电路状况机(竖着写)似乎是仅有的挑选,由于硬件不太可能靠事情驱动(横着写)。不过,在FPGA 里有一个大局时钟,在每次上升沿时进行状况切换,使得竖着写的功率并不低。虽然在硬件里竖着写也要运用IF/ELSIF这类查询句子(用VHDL开发),但他们映射到硬件上是组合逻辑,查询只会引起门级推迟(ns量级),并且硬件是真实并行作业的,这样竖着写在硬件里就没有负面影响。因而,在硬件规划里,运用竖着写的办法成为必定的挑选。这也是为什么许多搞硬件的工程师在规划软件状况机时下意识地只运用竖着写办法的原因,盖思想定势使然也。 TCP和PPP结构协议里都运用了有限状况机,这类软件状况机最好运用横着写的办法完成。以某TCP协议为例,见图3,有三种类型的事情:上层下达的指令事情;基层抵达的标志和数据的收包事情;超时定时器超时事情。 上层指令(open,close)事情 ----------------------------------- -------------------- | TCP | <----------超时事情timeout -------------------- ----------------------------------- RST/SYN/FIN/ACK/DATA等收包事情 图3 三大类TCP状况机事情 由图3可知,此TCP协议栈选用横着写办法完成,有3种事情处理函数,上层指令处理函数(如tcp_close);超时事情处理函数 (tmr_slow);基层收包事情处理函数(tcp_process)。值得一提的是,在收包事情函数里,在各个状况里判别RST/SYN/FIN/ACK/DATA等标志(这些标志类似于事情),看起来象竖着写办法,其实,假如把包头和数据当作一个全体,那么,RST/SYN/FIN/ACK/DATA等标志就不用被当作独立的事情,而是归于同一个收包事情里的细节,这样,就不会以为在状况里查找事情,而是总体上看,是在收包事情里查找状况(横着写)。 在PPP里更是处处都能见到横着写的现象,有时刻的话再细说。我个人感觉在完成PPP结构协议前有必要了解反正两种写法,并且只要运用横着写的办法才干比较完美地完成PPP。