• 状态机的两种写法


    版权声明:本文为博主原创文章,未经博主同意不得转载。 https://blog.csdn.net/mao0514/article/details/32307857
    有限状态机FSM思想广泛应用于硬件控制电路设计,也是软件上经常使用的一种处理方法(软
    件上称为FMM--有限消息机)。它把复杂的控制逻辑分解成有限个稳定状态,在每一个状态
    上推断事件,变连续处理为离散数字处理,符合计算机的工作特点。同一时候,由于有限状
    态机具有有限个状态,所以能够在实际的project上实现。但这并不意味着其仅仅能进行有限
    次的处理,相反,有限状态机是闭环系统,有限无穷,能够用有限的状态。处理无穷的
    事务。
        有限状态机的工作原理如图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/EL
    SIF这类查询语句(用VHDL开发),但他们映射到硬件上是组合逻辑,查询仅仅会引起门级延
    迟(ns量级)。并且硬件是真正并行工作的,这样竖着写在硬件里就没有负面影响。

    因此
    。在硬件设计里。使用竖着写的方式成为必定的选择。这也是为什么非常多搞硬件的project
    师在设计软件状态机时下意识地仅仅使用竖着写方式的原因,盖思维定势使然也。

        TCP和PPP框架协议里都使用了有限状态机。这类软件状态机最好使用横着写的方式
    实现。以某TCP协议为例。见图3,有三种类型的事件:上层下达的命令事件;下层到达
    的标志和数据的收包事件;超时定时器超时事件。

                        上层命令(open,close)事件
                -----------------------------------
                        --------------------
                        |       TCP        |  <----------超时事件timeout
                        --------------------
                     RST/SYN/FIN/ACK/DATA等收包事件

                        图3 三大类TCP状态机事件

        由图3可知,此TCP协议栈採用横着写方式实现,有3种事件处理函数,上层命令处理
    函数(如tcp_close)。超时事件处理函数(tmr_slow);下层收包事件处理函数(tcp_proce
    ss)。值得一提的是,在收包事件函数里,在各个状态里推断RST/SYN/FIN/ACK/DATA等标
    志(这些标志相似于事件)。看起来象竖着写方式,事实上,假设把包头和数据看成一个整
    体。那么,RST/SYN/FIN/ACK/DATA等标志就不必被看成独立的事件。而是属于同一个收
    包事件里的细节,这样。就不会觉得在状态里查找事件。而是整体上看,是在收包事件
    里查找状态(横着写)。

        在PPP里更是到处都能见到横着写的现象,有时间的话再细说。我个人感觉在实现PP
    P框架协议前必须了解横竖两种写法,并且仅仅有使用横着写的方式才干比較完美地实现PP
    P。


  • 相关阅读:
    linux下XAMP集成开发环境搭建流程总结
    一行代码解决各种IE兼容问题,IE6,IE7,IE8,IE9,IE10 http://www.jb51.net/css/383986.html
    安装软件时出现错误提示:无效类
    新建.xlxs文件打不开,旧的可以打开的解决办法
    PostgreSQL 自定义自动类型转换(CAST) 删除用 drop function integer_to_text(integer) CASCADE;
    php扩展不能加载的原因
    remote_addr(::1)不返回IPv4地址127.0.0.1的解决办法
    php转换字符串编码 iconv与mb_convert_encoding的区别
    PHPExcel获取CSV文件数据不准确,用以下方法获取
    日文SJIS编码字符串字符数获取方法
  • 原文地址:https://www.cnblogs.com/ldxsuanfa/p/10044207.html
Copyright © 2020-2023  润新知