打印

状态机调度HWT_Scheduler(util_virtual_state)

[复制链接]
2044|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
CC2530|  楼主 | 2011-3-10 09:35 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一个简单例子,跑马灯,说明:

1.如何定义一个状态机
2.如何声明一个状态机
3.如何调度一个状态机
4.如何声明状态机的一个状态,可以跨文件使用
5.如何声明状态机的一个状态,只能本文件使用
6.如何实现状态机状态入口处理
7.如果实现状态机状态延时
8.如何实现状态机状态跳转


#include "proj_incs.h"

VS_CreateNull(X);   //定义一个状态机X,初始化为NULL
//如果需要定义一个状态机初始一个状态,用VS_Create,如VS_Create(X,A),表示定义一个状态机X,初始状态为(A).
//如果初始为双名状态,则用VS2_Create,如如VS_Create(X,A,B);表示定义一个状态机X,初始状态为(A,B).

VSH_Extern(X,A);    //声明状态机X的状态(A),此状态(A)可以跨文件使用.
//声明外部单名状态用VSH_Extern,声明外部双名状态用VSH2_Extern
//声明本地单名状态用VSH_Local,如VSH_Local(X,A);表示声明状态机X的状态(A),只能在本文件使用,不能跨文件使用
//声明本地双名状态用VSH2_Local,如VSH2_Local(X,A,B);表示声明状态机X的状态(A,B),只能在本文件使用,不能跨文件使用


VSH2_Extern(X,B,1);  //声明状态机X的外部双名状态用(B,1),状态(B,1)在另外的文件中实现,这里用VSH2_Extern


int main()
{
    PORTB = 0x00;
    DDRB = _BV(0)|_BV(1)|_BV(2);
     
    HS_TicksInit();          //节拍初始化,
     
    __enable_interrupt();    //开中断
     
    VS_Goto(X,A);            //状态机X跳转到状态A,状态A作为状态机X的第一次运行状态
    //跳转到单名状态用VS_Goto,跳转到双名状态用VS2_Goto
      
    while(1)
    {
       while(VS_Sched(X));            //状态机X调度      
       //VS_Sched(X)返回值可以作为休眠参考值,为0表示没有状态运行,可以休眠,非0表示有状态在运行,不休眠
            
       SleepIdle();                   //休眠处理
        
/*
       这里也可以写成:
       if(!VS_Sched(X))
       {
           SleepIdle();
       }
        
*/

        
    }
}








#include "proj_incs.h"

#define LED_A     IO_BIT_ALIAS(&PORTB,0)               //LED_A,IO口位域别名
#define LED_B_1   IO_BIT_ALIAS(&PORTB,1)               //LED_B_1,IO口位域别名
#define LED_B_2   IO_BIT_ALIAS(&PORTB,2)               //LED_B_2,IO口位域别名

VS_Extern(X);          //声明一个装态机X
VSH2_Extern(X,B,1);    //声明装态机X的一个状态(B,1)

VSH_Implement(X,A)     //状态机X状态(A)实现
{
   if(VS_GetAndClearEnterStateFlag(X))               //如果状态机由Goto进入,表示状态机入口
   {     
        LED_B_2=0;     //LED_B_2熄灭
        LED_A=1;       //LED_A点亮   
   }
/*  
   状态机由Goto进入,使用
   if(VS_GetAndClearEnterStateFlag(X))
   {

   }
   这里仅仅是由Goto进入,不一定是由其他状态跳转而来,也可能是自己跳转到自己.
   如果需要在由其他状态跳转而来的入口处理,用
   if(VS_GetAndClearNewStateFlag(X))
   {

   }   
*/

        
    VS2_StartDelay(X,VTMR_NUM_VS_X_DELAY,1000);     //状态机延时1000ms
   //状态机可以延时,需要设置 UTIL_VSTA_USE_VTMR为1,在util_config.h里.
   //状态机延时是在状态退出后,状态机不运行任何状态,直到延时时间到,再运行状态机
   //这里的VS2_StartDelay与下面的VS2_Goto执行顺序并不重要.
   //这里VTMR_NUM_VS_X_DELAY表示一个util_virtual_timer的一个序号
   //util_virtual_timer的序号最大值必须小于UTIL_VTMR_MAX_COUNT,在util_config.h里.
      
    VS2_Goto(X,B,1);                                //状态机X跳转到(B,1)
    //跳转到双名状态用VS2_Goto,跳转到用单名状态VS_Goto     
    return ;
    //通常,调用VS_Goto或VS2_Goto后,紧接着就是函数返回.函数返回后,下一次进行状态机调度的时候,就会运行Goto的目的状态.
}


#include "proj_incs.h"

#define LED_A     IO_BIT_ALIAS(&PORTB,0)               //LED_A,IO口位域别名
#define LED_B_1   IO_BIT_ALIAS(&PORTB,1)               //LED_B_1,IO口位域别名
#define LED_B_2   IO_BIT_ALIAS(&PORTB,2)               //LED_B_2,IO口位域别名

VS_Extern(X);           //声明一个装态机X
VSH_Extern(X,A);        //声明装态机X的一个状态(A)
VSH2_Extern(X,B,1);     //声明装态机X的一个状态(B,1)
VSH2_Extern(X,B,2);     //声明装态机X的一个状态(B,2)

VSH2_Implement(X,B,1)     //状态机X状态(B,1)实现
{
   if(VS_GetAndClearEnterStateFlag(X))   //如果状态机由Goto进入,表示状态机入口
   {        
        LED_A=0;       //LED_A熄灭
        LED_B_1=1;     //LED_B_1点亮
   }
   
   VS2_StartDelay(X,VTMR_NUM_VS_X_DELAY,1000);     //状态机延时1000ms
   VS2_Goto(X,B,2);                                //状态机X跳转到(B,2)
   return ;   
}

VSH2_Implement(X,B,2)     //状态机X状态(B,2)实现
{
   if(VS_GetAndClearEnterStateFlag(X))        //如果状态机由Goto进入,表示状态机入口
   {         
        LED_B_1=0;     //LED_B_1熄灭
        LED_B_2=1;     //LED_B_2点亮
   }
   
   VS_StartDelay(X,VTMR_NUM_VS_X_DELAY,1000);    //状态机延时1000ms
   VS_Goto(X,A);                                 //状态机X跳转到(A)
   return ;
}

HWT_Scheduler.rar

423.83 KB

评分
参与人数 1威望 +1 收起 理由
心静自然凉 + 1

相关帖子

沙发
CC2530|  楼主 | 2011-3-10 09:35 | 只看该作者
一般状态实现:

VSH_Implement(.....)      
{
   if(VS_GetAndClearEnterStateFlag(...))     
   {     
        //入口处理
        ..........
   }
   
   if(......) //满足条件,跳转到新的状态
   {
        //出口处理
        ..........
         
        VS_Goto(.....);
        return;          //Goto之后,紧接着是return   
   }
   
   //状态内部处理
   .........
   .........
   .........
}

使用特权

评论回复
板凳
CC2530|  楼主 | 2011-3-10 09:35 | 只看该作者
本帖最后由 CC2530 于 2011-3-10 09:40 编辑

双名用两个标识符表示一个状态,单名状态用一个标识符表示一个状态.
本质上差别并不大.但是如果状态跳转比较复杂,跳转的像蜘蛛网似的,全部使用单名状态不利于编程分析.
使用双名状态,可以把多个功能非常紧密的状态合成一个大的状态组,这个大的状态组也可以认为是
一个状态,可以更好地对状态进行分组处理.

举个例子,比如有一组双名状态:
(A,START),(A,END),(A,0),(A,1),(A,2),(A,3),(A,4),(A,5),.......
对这组状态可以这样处理:
所有组内状态(A,...)都在同一个文件中实现;
只有(A,START)对外是可见的,其他组内状态(A,...)都是本地状态,对外不可见.其他组外状态要跳转到
组内状态,只能跳转到(A,START),(A,START)相当于A组状态入口状态;
A组状(A,...)要跳转到组外的其他状态,必须先跳转到(A,END),再由(A,END)跳转到组外状态,
其他组内状态不能直接跳转到组外状态.(A,START)相当于A组状态出口状态;
这样整个A组状态,可以看组是一个大的状态.(A,START)是这个大状态A的入口处理,(A,END)是这个大状态
A的出口处理.其他组内状态都是大状态A的内部处理.大状态又分成许多小状态,而每个小状态也有自己的
入口处理,出口处理,内部处理.

这样处理方便把小状态合并成大状态,大状态分解成小状态.层次更加清晰.

使用特权

评论回复
地板
心静自然凉| | 2011-3-10 10:01 | 只看该作者
愿意分享的同志都是好同志

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

0

主题

262

帖子

1

粉丝