状态机实践入门——程咬金只要三斧头厉害

不用怀疑,单片机的万能语言就是状态机。还希望大家不要条件反射式的看到状态机就以为我要讲什么VHDL的东西——状态机是一种思维模式,是计算机理论的立足之本(不相信请参考清华大学出版社的《自动机理论与应用》)——因此状态机的实现与语言本身关系并不是绝对的。本文要讨论的状态机,从实现方式上更类似于Java中常用的那种思维模式,而与VHDL相去甚远。

路要一步一步走,饭要一口一口吃,为了不把后来人吓跑,状态机理论中更多复杂的部分,我会在以后专门写文章讨论,这里我先找一个切入点,从我常用的2种状态机编写方式为大家慢慢展开。

首先,关于几个问题,比如:什么地方用状态机?状态机究竟有几种写法?状态机效率到底高不高?是不是把简单问题弄复杂了?这类问题统统不在本文讨论之列,简而言之——谁用谁知道。其实,还不能简单的就这么下了结论,套八股文而不求甚解的也大有人在——因此我要说:关于状态机的各种问题“谁思考谁实践谁坚持谁知道”。

状态机入门第一式:switch case一线到底

要点:switch结构配合一个状态变量,通过修改状态变量的值来切换状态。

范例:

    //! 定义状态名称与状态值之间的关系,增加可读性

    #define FSM_START                                   0x00

    #define FSM_STATE_A                                 0x01

    #define FSM_STATE_B                                 0x02

    …

    #define FSM_RESET                                   0xFF

    bool fsm_example_A( 形参列表 )

         {

        static uint8_t s_chFSMState = FSM_START;     //!< 定义状态变量

                     …

        switch ( s_chFSMState )

            {

            case FSM_START:

                //! 这里添加状态机初始化代码

                …

                s_chFSMState = FSM_STATE_A;   //!< 进入下一状态

                break;

            case FSM_STATE_A:

                //! 这里添加状态机A进入下一状态的检测代码

                if (某某条件)

                   {

                     //! 这里做一些进入下一状态时要做的准备工作

                     s_chFSMState = FSM_STATE_B;    //!< 进入下一状态

                }

                break;

            case FSM_STATE_B:

                //! 这里添加状态机A进入下一状态的检测代码

                if (某某条件>)

                {

                    //! 这里做一些进入下一状态时要做的准备工作

                    s_chFSMState = FSM_STATE_A; //!< 进入下一状态

                } else if (某某条件>)

                       {}

                  else if (某某条件>)

                       {

                    …

                        }

                else {

                }

                break;

                …

             case FSM_STOP:

             case FSM_RESET:

             default:

                 //! 这里添加状态机复位相关的代码

                 …

                chFSMState=FSM_START;         //!< 状态机复位

           //! 返回false表示状态机已经不需要继续运行了

             return false;                                            

          }

              //! 返回true表示状态机正在运行

             return ture;}

总结:从范例可知,这种状态机就是一根筋……并不是说他走不出什么分支来,而是说通常他没有办法让多个状态同时处于激活状态。

状态机入门第二式:if 判断变化无穷

要点:用if elseelse if结构的组合来描述状态流程图。

什么是状态流程图?我不想多解释,因为就那么个简单的东西,说多了反而神秘兮兮的。

状态流程图你可以简单粗暴的认为,他就是流程图,等你用得多了,你就渐渐明白为啥状态”二字;如果你后来或者先前学过状态图,那么很快你就会明白状态流程图比状态图“高级”了多少。

1、 不管怎么说,你可以先为你要处理的事物画一个流程图。如果流程图都不会画,就不用凑热闹了。

2、 接下来,把流程图上每一个方框或者判断框都“简单粗暴”地看成一个状态。

3、 将每一个状态用if结构表示出来

         if (状态标志>)

         {

         //! 状态代码

         

      }

4、 自己看着办,合并多余的状态,优化优化代码。

范例:   

   //! 首先将布尔量的状态标志压缩在一个字节里面以节省内存开支

    typedef union {

        uint8_t     Value;

        uint8_t     Byte;   

        struct {

            unsigned BIT0:1;

            unsigned BIT1:1;

            unsigned BIT2:1;

            unsigned BIT3:1;

            unsigned BIT4:1;

            unsigned BIT5:1;

            unsigned BIT6:1;

            unsigned BIT7:1;

        }Bits;

    }byte_t;

    #define FSM_ACTION_FLAG        s_tbState.Bits

    #define FSM_STOP_ALL_ACTIONS() do{s_tbState.Value =0;}while(0)

    #define FSM_START                   (0 == s_tbState.Value)

    #define FSM_STATE_A                 FSM_ACTION_FLAG.BIT0

    #define FSM_STATE_B                 FSM_ACTION_FLAG.BIT1

    …

    #define FSM_STATE_H                 FSM_ACTION_FLAG.BIT7

    bool fsm_example_B( 形参列表> ) {

        static byte_t s_tbState = {0};    //!< 定义状态变量

        if (FSM_START)

{   //!< 起始状态

            //! 这里放置状态机初始化的代码

            …

            FSM_STATE_A =true;//!<进入状态B,start装台自动结束

        }

        if (FSM_STATE_A) {                                           //!<一个典型的简单状态

            //! 这里放置状态A的代码或者

            …

            //! 这里放置某些条件以开启别的状态

            if (某些条件>) {

                //! 这里做一些“进入”下一个状态之前的准备工作

                FSM_STATE_B = true;     //!< 开启下一个状态

                FSM_STATE_A = false;    //!< 结束当前状态

            }

        }

        if (FSM_STATE_B) {              //!< 一个典型的监视状态

            …

            //! 这里检测某些条件

            if (某些条件>)

{

                //! 这里做一些“开启”某个状态的准备工作

                FSM_STATE_C = true;     //!< 开启某一个状态而不结束当前状态

                FSM_STATE_D = true;     //!< 你当然可以一次触发多个状态

                …

            } else if (某些条件>) {

                //! 满足某些条件以后关闭当前状态

                FSM_STATE_B = false;

            }

        }

        …

        if (FSM_STATE_F) {        //!< 一个典型的子状态机调用

            if (!fsm_example_a(实参列表>))

               {     //!< 等待子状态机返回false

                     //!子状态机运行完成,进入下一状态

                …

                FSM_STATE_F = false;     //!< 结束当前状态

                FSM_STATE_x = true;      //!< 进入下一状态x代表某个字母

            }

        }

 

        if (FSM_STATE_H) {               //!< 一个典型的中止状态

            //!< 某些状态机的操作,比如释放某些资源

            …

            FSM_STOP_ALL_ACTIONS();      //!< 复位状态机

            return false;                //!< 返回false表示状态机结束

        }

 

        return true;                     //!< 返回true表示状态机保持运行

    }

总结:从范例可知,这种状态机非常灵活,通过布尔变量的开启和关闭,你可以自由的控制某些状态的开启。同一时刻可能有多个状态是激活的。这种结构几乎可以翻译任何流程图。具体还有很多好处,可以在使用中体会。

状态机入门第三式:状态在心中,无态也变态

要点:所有的函数都可以看作是状态机,只不过普通的函数是一个只有单一状态的状态机。如果函数有返回值,且这个返回值能表征至少两种以上不同的状态(比如返回是一个指针,那么NULL和非NULL就是两种状态;比如返回是一个布尔变量,那么truefalse就是两种状态;比如返回的是一个整数,并且整数的某些特征可以被分类,那么这些不同分类就是几种不同的状态),那么这些返回值就可以被用作指示当前状态机的运行情况。状态机可以调用子状态机。所有的状态都应该是none-block的,简单说就是不会把系统定死在某一个状态里面很久都出不来,比如while(1)或者循环次数较大的for结构;否则状态机的存在意义就大打折扣——直接按照流程图写代码不就好了,干吗非要翻译成状态机?状态机中,状态的功能应该是等待某一个事件的发生(或者说条件的满足);某些情况下,一些一次性执行完的流程也可以独立成一个状态——它当然没有等待任何条件的满足,你可以认为他是无条件进行状态转移的。

总体说来,状态机是一个万能的计算机语言表述方式,与具体的载体语言关系不大。心中有状态,代码怎会无状态?状态机是裸机条件下多任务的廉价实现方案。在状态机多任务条件下,操作系统牵涉的几乎所有概念都会有所涉及,比如任务的同步,临界区的保护,任务间的通讯,任务的优先级,资源的动态分配等等。你可以这么理解,每一个状态机都是一个进程,每一个状态都是一个线程,因为进程有自己的资源,而同一个进程里面的多个线程是公用同一片资源的。你甚至可以在有抢占式操作系统的情况下用状态机,这个时候,操作系统的每一个任务都是个内核,那么整个系统开发就可以登小于多核系统开发了。 是不是很有意思?没意思就别看了!状态机的种种,以后再表。

Have a good time.

初学单片机的人就应该立即学习文中列出的那种八股文开发模式: 
   1、画(状态)流程图
   2、翻译状态流程图为状态机
   3、优化、调试
   形成习惯以后,能大大加快学习嵌入式系统的速度。不会被诸如按键扫描程序和数码管扫描程序无法很好协调工作的问题困惑,甚至在缺乏引导的情况下,养成非常不好的习惯,对由于这些习惯本身容易导致的栈溢出问题,存储器使用不当(指针类问题),产生恐惧心理。
   状态机是一种计算机软件开发的万能语言模式,他让我们能随心所欲的表达自己的思想。在这种基础下,自己的想法能够得到充 分的表达,从而自己的思想也能得到实践的充分验证。于是思维和实践能够得到充分的交互,尽早的形成良性循环。这个时候,书本上那些理论,只要能够理解,你就有足够的语言能力去检验于是也就能很快的体会其中的好坏。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值