FSM:finite state machine 【有限状态机】,用通俗的语言来表达就是逻辑流程图。
当前状态满足触发条件时,就会切换到下一个状态,并执行对应的任务操作。传统代码做法是用if-else 或者 switch-case来处理。若要做到可扩展性良好的话,就需用状态表来设计。
用一句代码表达状态机原理:
if( state==CurrState && Event==True )
{
state = NextState;
Action();
}
举个简单的例子:某个电子设备的模式运行切换逻辑如下:
当前状态(CurrState) | 触发条件(Event) | 下个状态(Next) | 执行动作(Action) |
M_IDLE | 按钮1按下 | MODE1 | 亮红灯,蜂鸣器响1下 |
M_IDLE | 按钮2按下 | MODE2 | (无动作) |
MODE1 | 运行10秒后 | MODE3 | 亮绿灯,电机转速100 |
MODE2 | 运行5秒后和温度大于40度 | M_IDLE | 亮蓝灯,电机转速300 |
MODE3 | 运行30秒 | M_END | 关灯,关电机 |
M_END | 按钮1按下和按钮2按下 | M_IDLE | 蜂鸣器响2下 |
C语言写的伪代码,传统方法:
#define M_IDLE 0
#define MODE1 1
#define MODE2 2
#define MODE3 3
#define M_END 4
int state_mode = M_IDLE;
int run_cnt = 0;
int cur_temp = 0;
int btn1 = 0;
int btn2 = 0;
void main()
{
while (1)
{
switch (state_mode)
{
case M_IDLE:
if (btn1)
{
state_mode = MODE1;
light_red();
beep_cnt(1);
}
if (btn2)
{
state_mode = MODE2;
}
break;
case MODE1:
if (run_cnt >= 10)
{
state_mode = MODE3;
light_green();
motor_speed(100);
}
break;
case MODE2:
if (run_cnt >= 5 && cur_temp > 40)
{
state_mode = M_IDLE;
light_blue();
motor_speed(300);
}
break;
case MODE3:
if (run_cnt > 30)
{
state_mode = M_END;
light_off();
motor_off();
}
break;
case M_END:
if (btn1 && btn2)
{
state_mode = M_IDLE;
beep_cnt(2);
}
break;
}
}
}
C语言写的伪代码,状态机表格方法:
#include<stdio.h>
int run_cnt = 0;
int cur_temp = 0;
int btn1 = 0;
int btn2 = 0;
typedef enum
{
M_IDLE,
MODE1,
MODE2,
MODE3,
M_END
}EnumState;
int state_mode = M_IDLE;
typedef struct
{
EnumState curr_state;//当前状态
int(*event_condition)();//触发事件条件
EnumState next_state;//下一状态
void(*action)();//执行动作
}FSM_Table;
//状态切换事件条件
int event_condition0()
{
if(btn1==1)
return 1;
return 0;
}
//状态切换触发后并执行的操作
void action0()
{
light_red();
beep_cnt(1);
}
int event_condition0b()
{
if(btn2==1)
return 1;
return 0;
}
void none_action()
{
//没有动作
}
int event_condition1()
{
return(run_cnt>=10);
}
void action1()
{
light_green();
motor_speed(100);
printf("Runing action1\n");
}
int event_condition2()
{
return(run_cnt>5 && cur_temp>40);
}
void action2()
{
light_blue();
motor_speed(300);
printf("Runing action2\n");
}
int event_condition3()
{
return(run_cnt>30);
}
void action3()
{
light_off();
motor_off();
}
int event_condition_end()
{
return(btn1&&btn2);
}
void action_end()
{
beep_cnt(2);
}
FSM_Table fsm_list[]=
{
{M_IDLE,event_condition0,MODE1,action0},
{M_IDLE,event_condition0b,MODE2,none_action},
{MODE1,event_condition1,MODE3,action1},
{MODE2,event_condition2,M_IDLE,action2},
{MODE3,event_condition3,M_END,action3},
{M_END,event_condition_end,M_IDLE,action_end},
};
void main()
{
int table_size = sizeof(fsm_list)/sizeof(FSM_Table);
printf("fsm_size=%d.\n",table_size);
while(1)
{
for (size_t i = 0; i < table_size; i++)
{
if(state_mode==fsm_list[i].curr_state&&fsm_list[i].event_condition())
{
state_mode = fsm_list[i].next_state;
fsm_list[i].action();
}
}
}
}
经过这两种代码方法和思路的对比,可以看出,采用状态机数据结构表方案会十分的清晰贴合excel表上列举的功能思维,而且若后面扩展功能也十分的方便,只需要添加函数和关系表。
尤其当逻辑流程比较复杂多变时,用状态机表格的优势会比较突出。