FSM:finite state machine 【有限状态机】,用通俗的语言来表达就是逻辑流程图。
当前状态满足触发条件时,就会切换到下一个状态,并执行对应的任务操作。传统代码做法是用if-else 或者 switch-case来处理。若要做到可扩展性良好的话,就需用状态表来设计。
用一句代码表达状态机原理:
if( state==CurrState && Event==True )
{state = NextState;Action();
}
举个简单的例子:某个电子设备的模式运行切换逻辑如下:
当前状态(CurrState) | 触发条件(Event) | 下个状态(NextState) | 执行动作(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表上列举的功能思维,而且若后面扩展功能也十分的方便,只需要添加函数和关系表。
尤其当逻辑流程比较复杂多变时,用状态机表格的优势会比较突出。