jijuxie321 发表于 2007-8-22 16:45:38

对于想用OS但又觉得单片机资源太过紧张,状态机是个不错的选择---分享一种状态机设计

如何利用有限状态机实现多任务



墨鱼



许多嵌入式应用领域,软件都是基于输入响应的组织方式,也叫反应式系统。把输入

信息进行归类有:离散的事件(如二值开关信号)、可以表示某个外部信号引发的中断或者例如发生了定时器溢出等。而数值信号则用于传递例如一次A/D采样的结果。有限状态机正是利用了这些输入的事件做为状态变更的依据,每一种状态对应执行一组操作。

(个人观点)所以这种方式最好是执行在一个由中断建立起来的硬件环境。例如键盘的输入是与中断相结和的,所以会有周立公的ZLG7290芯片(当然7290并不是因为这样才产生的)。这样就可充分利用这种机制而不必频繁的执行扫描。

不过由于需要根据输入的事件做相应的状态转换,使利用状态机进行软件的设计和编码带来的额外的负担。系统的复杂程度直接影响了设计人员对软件的理解和组织。借助有效的状态机设计工具可以降低设计人员的负担。

根据多任务操作系统的分类有协作型(也叫任务轮循),一个任务一直运行,直到该任主动放弃CPU,调度器安排另外一个任务运行。相应的在没有调度器的情况下,我们可以把对状态机的调用过程依次安排在一个超循环里。

       While( 1 ){

            //调用状态机1,处理事件1。

            StateMachine_NO_1( );

            //调用状态机2,处理事件2。

            StateMachine_NO_1( );

            。。。。。。。。。。。。。。。。。。。

               。。。。。。。。。。。。。。

             //状态机n

             StateMachine_NO_n( );

   }

各状态机在设计的时候必须保证不会出现死循环,长时间等待某一事件。例如以往的前后台方式等待定时器溢出使用的方法是不断查询标志位直到溢出为止,此时应该改为首先打开定时器,然后切换状态进入查询是否溢出,未溢出立即退出,转而执行其它状态机。待下一次执行到该状态机时由于状态仍处在查询定时溢出的状态之下,如果此时查询结果是定时器溢出将切换到另一种状态执行相应操作。

StateMachine_NO_1(void ){

       Static uint8 sm_no1_state=0;      //用于存放状态机当前状态的值

      。。。。。。。。。。。。。。。。。。。。。。。。。;         //其它变量。



       If( sm_no1_state= =0){//状态机等于‘0’吗?

             //执行操作,例如执行点亮一个LED 500mS

         。。。。。。。。。。。。。。。。。。。。。。。。。。

             //执行完打开LED操作后切换状态进入查询500ms时间到否

         sm_no1_state=1;

          //并打开定时器计数,500ms

         Star_Timer0_Delay_Ms(500);

      }



       If( sm_no1_state= = 1){ //状态机等于‘1’吗?

            //判断定时器是否溢出。

            If( Rt_Timer0_full( ) = = 1){//每次只查询一次

            //定时器溢出,关闭定时器,关闭LED

               。。。。。。。。。。。。。。。。。。

             //同时切换状态

            sm_no1_state=2;

            //再次打开定时器

            Star_Timer0_Delay_Ms(500);

            }

             else ;//定时器未溢出保持当前状态退出,转而执行其它状态机。

      }



      If( sm_no1_state = = 2){ //状态机等于‘2’吗?

            If( Rt_Timer0_full( ) = = 1){

                     //定时器溢出,关闭定时器

                。。。。。。。。。。。。。。。。。。

            //同时切换状态

               sm_no1_state=0;

      }

         else ;//定时器未溢出保持当前状态退出,转而执行其它状态机。



   }



      Return ; //状态机1执行返回主循环。

}



如上所示状态机’1’执行了一个每隔1S点亮和关闭LED的任务。

类似的只要合理设计每个状态机所等待和处理的事件,就可实现多任务的并发执行。解决的状态机的原理后还需要考虑各状态机之间的资源共享和同步问题。

除状态机轮循这种简单的利用等待事件发生的空闲时间处理并发执行外还有基于事件优先级等等组织方式。不过由于过于复杂,大家可以借助于UML实时系统建模生成的框架,设计一个并发执行的软件只需要设计好状态图,当然实际实行起来UML过于抽象。

补充:状态机在执行状态切换时要执行入口操作和退出操作。

相对于使用RTOS的优点是对硬件的资源消耗较少特别是SRAM,但各状态机所使用的变量生存期需要根据实际好好斟酌。相比于前后台方式会有大量的静态变量。动态分配的变量仍然只存在于可重入函数中作临时审请的变量。

另外UML也支持RTOS,我想RTOS+状态机的开发方式再复杂的项目也能轻松应对了。

由于表达能力的不足无法很好的诠释状态机的更多优点和特性。而其缺点和应该注意的地方我想大家在往后的应用中会有很好的体会。过于复杂的项目如果没有掌握好状态机的设计工具建议不要选择这种方式,很可能会导致项目失败。

durgy 发表于 2007-8-22 17:38:39

在某些程序我也是这样做的

ATmega32 发表于 2007-8-22 17:52:45

用usmartx,不用给任务单独分配堆栈。

占用sram少,1K的SRAM跑的很好。

512字节SRAM都可以。

04081612 发表于 2007-8-22 19:57:04

谢谢虽然还不太理解状态机的精髓 但感觉确实是很有用的东西

xyq4513 发表于 2009-4-18 11:17:36

mark

wajlh 发表于 2009-4-18 12:37:18

学习

wukaka 发表于 2010-8-12 19:24:18

mark

GNMXD 发表于 2011-9-6 10:51:25

mark

zzw223226 发表于 2011-9-6 11:57:35

现在还在于表面东西的学习。。。。看不太懂。。。。来一个具体的程序就好了~从main开始调用子函数什么的~

howmoney 发表于 2011-9-6 23:23:57

睡前记号 明天下班再看

stm8s 发表于 2011-9-7 00:37:16

有时间细看 mark

eleven_sue 发表于 2012-2-27 16:55:37

mark

tangaoo 发表于 2012-6-1 10:31:34

MAKE楼主哪能稍微再详细点不

jz701209李 发表于 2012-6-1 14:04:19

学习学习,謝謝

fengtianzhifeng 发表于 2012-6-27 09:08:08


学习学习,謝謝

nicksean 发表于 2012-6-27 14:14:52

最近在一个项目中使用了消息队列加状态机的方式,中断负责发出触发事件,状态机在主while循环中根据事件进行状态转换和动作输出。状态机是用状态转换表实现的,本来想用VisualState的(新版本没破解,就先放下了)。还没完成,过程略有坎坷。{:sad:}

yyd0076 发表于 2012-6-27 19:57:32

mark!!!!

wenzi4402996 发表于 2012-6-27 20:08:23

mark 虽然不是很懂 慢慢研究

lu976046395 发表于 2013-7-13 16:39:55

mark      

zhouyan 发表于 2013-7-13 19:10:35

还没有领悟到起中的精髓!
页: [1]
查看完整版本: 对于想用OS但又觉得单片机资源太过紧张,状态机是个不错的选择---分享一种状态机设计