单片机的万能语言——状态机
warmonkey2011/03/06电子技术 IP:广东
版权属于原作者
原文XXXXXXXXXXXXXXXXXXXX/bbs/bbs_content.jsp?bbs_sn=4585393&bbs_page_no=1&bbs_id=9999

不用怀疑,单片机的万能语言就是状态机。还希望大家不要条件反射式的看到状态机就
以为我要讲什么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 true;                                                                                
    }
总结:    从范例可知,这种状态机就是一根筋……并不是说他走不出什么分支来,而
          是说通常他没有办法让多个状态同时处于激活状态。

状态机入门第二式:if 判断变化无穷
要点:     用if else…else 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_XXXXXXXXXXts
    #define FSM_STOP_ALL_ACTIONS()      do {s_XXXXXXXXXXlue = 0;}while(0)
    #define FSM_START                   (0 == s_XXXXXXXXXXlue)
    #define FSM_STATE_A                 FSM_ACTION_XXXXXXXT0
    #define FSM_STATE_B                 FSM_ACTION_XXXXXXXT1
    …
    #define FSM_STATE_H                 FSM_ACTION_XXXXXXXT7

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

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

Have a good time.
+44  科创币    玄明    2011/03/08 转帖实用资料
来自:电子信息 / 电子技术
5
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
warmonkey 作者
13年10个月前 IP:未同步
284536
此状态机包含了时序逻辑,而且可以做出更为复杂的功能。这是一种广义的编程思想。
状态机很可靠,不会因为一个没预料到的异常就死掉了。因为在状态流图上很容易看出程序可能遇到的各种情况,并且加以保护。
引用
评论
1
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
warmonkey
学者 机友
文章
363
回复
7990
学术分
12
2008/10/11注册,13时48分前活动

Cubesat

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}