1. 简介
simulink的stateflow采用的是单事件触发的同步状态机模式。本文介绍了一种可行的状态机的实现方法,可根据stateflow由C code generator直接生成。
1.1 状态
状态(State)拥有Entry,During,Exit三个方法,分别在进入,循环,退出状态时发生。
1.2 转移
转移(Transition)的形式为:
event[guard]{action}
事件(event)触发状态转移。
守卫条件(guard condition)一般是一个返回bool类型的函数,如果不满足条件,事件会被“安静地丢弃”(discard silently)。
转移动作(action)是在转移过程中执行的函数。
1.3 伪状态
节点伪状态(Junction PseudoState)因为可以用相同方式处理,因此将条件、合并、分支伪状态统称。
历史伪状态 (History PseudoState)再一次进入子状态时,将上一次退出时的状态作为默认状态。
初始伪状态 顾名思义,状态机起点。
2. 事件
用宏定义的方式定义事件。-1表示没有事件发生。
#define untitled_CALL_EVENT (-1)
#define untitled_event_goGreen (0)
#define untitled_event_goOff (1)
#define untitled_event_goOn (2)
#define untitled_event_goQuickly (3)
#define untitled_event_goRed (4)
#define untitled_event_goSlowly (5)
#define untitled_event_goSteady (6)
#define untitled_event_goYellow (7)
3. 状态
3.1 一级状态
一级状态也使用用宏定义,从1开始。
#define untitled_IN_Off ((uint8_T)1U)
#define untitled_IN_On ((uint8_T)2U)
3.2 嵌套子状态
嵌套子状态多一个无激活的0状态。
Color状态的子状态:
#define untitled_IN_NO_ACTIVE_CHILD ((uint8_T)0U)
#define untitled_IN_Green ((uint8_T)1U)
#define untitled_IN_Red ((uint8_T)2U)
#define untitled_IN_Yellow ((uint8_T)3U)
Rate状态的子状态:
#define untitled_IN_NO_ACTIVE_CHILD ((uint8_T)0U)
#define untitled_IN_Flashing_Quickly ((uint8_T)1U)
#define untitled_IN_Flashing_Slowly ((uint8_T)2U)
#define untitled_IN_Steady ((uint8_T)3U)
3.3 状态结构体
状态结构体:
typedef struct {
real_T data; /* '<Root>/Chart' */
int32_T sfEvent; /* '<Root>/Chart' */
uint8_T is_active_c3_untitled; /* '<Root>/Chart' */
uint8_T is_c3_untitled; /* '<Root>/Chart' */
uint8_T is_COLOR; /* '<Root>/Chart' */
uint8_T was_COLOR; /* '<Root>/Chart' */
uint8_T is_active_COLOR; /* '<Root>/Chart' */
uint8_T is_RATE; /* '<Root>/Chart' */
uint8_T was_RATE; /* '<Root>/Chart' */
uint8_T is_active_RATE; /* '<Root>/Chart' */
} DW_untitled_T;
结构体包括状态机中的使用的变量:
real_T data; /* '<Root>/Chart' */
事件变量。由于是单事件同步模式,所以只需要一个事件。
int32_T sfEvent; /* '<Root>/Chart' */
一级状态。is_c3储存当前状态,is_active_c3储存当前是否激活(为了entry事件)
uint8_T is_active_c3_untitled; /* '<Root>/Chart' */
uint8_T is_c3_untitled; /* '<Root>/Chart' */
嵌套事件。is储存当前状态,is_active储存当前是否激活,was储存上一次退出时的状态,如果没有历史伪状态则不需要。
uint8_T is_COLOR; /* '<Root>/Chart' */
uint8_T was_COLOR; /* '<Root>/Chart' */
uint8_T is_active_COLOR; /* '<Root>/Chart' */
4. 流程
以下代码均为伪代码。
main函数调用如下。OverrunFlag标志进入临界区,保证线程安全。StateMachine_Step()为状态机更新调用函数。
OverrunFlag = true;
/* Save FPU context here (if necessary) */
/* Re-enable timer or interrupt here */
/* Set model inputs here */
/* Step the model */
StateMachine_Step();
/* Get model outputs here */
/* Indicate task complete */
OverrunFlag = false;
4.1 StateMachine_Step()
设置当前事件为无事件。事件可以由外界更改,也可由内部更改。
CurEvent = untitled_CALL_EVENT;
通过is_active判断是否执行entry任务。状态由初始伪状态变为第一个状态,同时初始化输出值。一级状态不可以有历史伪状态。
if (is_active_c3 == 0U) {
is_active_c3 = 1U;
is_c3 = InitState;
InitState_Entry();
}
else {
StateMachine_Select();
}
4.2 StateMachine_Select()
如果已经激活一级状态机。那么根据is_c3判断当前状态。每个状态都有一个处理函数。
switch (is_c3) {
case State1:
State1_Proc();
break;
case State2:
State2_Proc();
break;
...
}
4.3 State_Proc()
格式为:
//当前事件为转移事件,且满足守卫条件
if ((CurEvent == TranEvent) && Guard())
{
//转移action
Tran_Action();
//上一个状态子状态Exit
LastSubState_Exit();
//上一个状态Exit
LastState_Exit();
//状态变为下一个状态
CurState = NextState;
NextState_Entry();
//下一个状态子状态Entry
NextSubState_Entry();
}
如果存在条件伪状态(一般情况条件伪状态前是事件,后面是守卫条件):
else if((CurEvent == TranEvent2))
{
//有条件伪状态
if(Guard2())
{
...
}
else if(Guard3())
{
...
}
else
{
...
}
}
如果没有转移,那么就执行during。
else
{
LastState_During();
if (is_active_lastsubstate != 0U)
{
LastSubState_Select();
}
}
4.4 LastSubState_Exit()
switch (is_LastSubState) {
case LastSubState_Substate1:
Substate1_exit();
is_LastSubState = NO_ACTIVE_CHILD;
break;
case LastSubState_Substate2:
...
}
is_active_LastSubState = 0U;
4.5 NextSubState_Entry()
有历史伪状态,上一次退出状态保存在was_中。
is_active_NextSubState = 1U;
switch (was_NextSubState) {
case NextSubState_SubState1:
is_NextSubState = NextSubState_SubState1;
was_NextSubState = NextSubState_SubState1;
SubState1_entry();
break;
case NextSubState_SubState2:
...
}
无历史伪状态,进入默认初始状态。
is_active_NextSubState = 1U;
is_NextSubState = NextSubState_SubState1st;
SubState1_entry();
直接转移到子状态中某一状态的,要先处理父状态,然后处理子状态。
is_active_NextSubState = 1U;
is_NextSubState = NextSubState_SubStateInd;
SubState1_entry();
4.6 LastSubState_Select()
switch (is_LastSubState) {
case LastSubState_Substate1:
//与State_Proc()类似,判断转移
SubState1_Proc();
break;
case LastSubState_Substate2:
...
}
按照以上递推直到没有子状态,然后循环执行。
5. 总结
本状态的exit与下一个状态的entry都是在转移的过程中完成的,执行顺序为action->exit->entry。不发生状态转移才会执行本状态的during。
另外,stateflow中的OR状态有严格的先后执行顺序。