Finite State Machine in Embedded C

此篇文章是有关有限状态机的笔记,有限状态机,顾名思义是由有限个状态、有限个触发的事件组成,经过触发的条件来切换不一样的状态。因为其特性是具备有限性,所以咱们在设计的时候须要考虑好全部的状态以及触发状态的事件,这是设计有限状态机的前提。api

如下是建立一个有限状态机的通常流程:数组

  • 确认用户的需求信息(冻结需求)框架

  • 分析需求并绘制状态转换流程图ide

  • 建立状态机的代码框架函数

  • 确保转换(状态改变)能正确运行测试

  • 测试已实现的状态机url

有限状态机在咱们平常生活中使用的一些电器里面有不少应用,好比说洗衣机、咖啡机、银行自助存款机、电饭煲等等,本篇笔记就以银行自助存款机为例,由于这个和money有关,money这个东西固然是越多越好咯,也但愿你们今年存越多越好咯~下面来和你们介绍下,有限状态机用C语言怎么实现。idea

首先咱们梳理下银行自助存款机的一些状态以及触发这些状态的事件:spa

一、ATM Machine State.net

  • Idle State

  • Card inserted State

  • Pin entered State

  • Option selected State

  • Amount entered State

二、ATM Machine Event

  • Insert card Event

  • Enter pin Event

  • Select option Event

  • Enter amount Event

  • Money Dispatch Event

下图就是根据上图状态和事件,画的一个状态机跳转图:

最初时,ATM机将处于空闲状态。当用户插入卡时,它会更改其状态并处理卡。卡处理后,ATM再次更改其状态,并要求用户输入密码。当用户输入密码时,它会要求您进行选择(余额查询,提款,存款),而后更改状态并要求输入金额或者其余查询,最后交易完成后点击退出,就又从新回到了空闲状态。

 

通常在C语言中,实现状态机经常使用的方法有两种:

  • 使用条件语句(switch-case或if-else)

  • 使用查询表(二维数组或者结构体数组)

基于条件语句的状态机

咱们使用if-else或switch case来检查状态和触发了的事件。若是状态组合和触发的事件匹配,请执行事件处理程序以服务该服务并更新下一个状态,这取决于检查第一状态或事件的要求。在下面的示例代码中,我首先验证状态,而后检查触发的事件。若是有须要,能够撤消该过程,能够先检查事件,而后再检查状态。









#include <stdio.h>typedef enum{ Idle_State, CardInsert_State, PinEntered_State, OptionSelected_State, AmountEntered_State,}eSystemState;typedef enum{ CardInsert_Event, PinEnter_Event, OptionSelection_Event, AmountEnter_Event, AmountDispatch_Event, }eSystemEvent;//deal with Amount Dispatch Event,return Idle_StateeSystemState AmountDispatch_Handler(void){ return Idle_State;}//deal with Card Insert Event,return CardInsert_StateeSystemState CardInsert_Handler(void){ return CardInsert_State;}//deal with Pin Enter Event,return PinEntered_StateeSystemState PinEnter_Handler(void){ return PinEntered_State;}//deal with Option Selection Event,return OptionSelected_StateeSystemState OptionSelection_Handler(void){ return OptionSelected_State;}//deal with Amount Enter Event,return AmountEntered_StateeSystemState AmountEnter_Handler(void){ return AmountEntered_State;}int main(void){ eSystemState eNextState = Idle_State; eSystemEvent eNewEvent;  while(1) { eSystemEvent eNewEvent;  //Read system Events,assume read from API code eNewEvent = ReadEvent();  switch(eNextState) { case Idle_State: { if(CardInsert_Event == eNewEvent) { eNextState = CardInsert_Handler(); } break; }  case CardInsert_State: { if(PinEnter_Event == eNewEvent) { eNextState = PinEnter_Handler(); } break; }  case PinEntered_State: { if(OptionSelection_Event == eNewEvent) { eNextState = OptionSelection_Handler(); } break; }  case OptionSelected_State: { if(AmountEnter_Event == eNewEvent) { eNextState = AmountEnter_Handler(); } break; }  case AmountEntered_State: { if(AmountDispatch_Event == eNewEvent) { eNextState = AmountDispatch_Handler(); } break; }  default: { break; } } } return 0;}

基于查询表的状态机

这里会建立一个二维数组,其中行下标和列下标分别表明状态和事件,这个二维数组的初始化是由指定的初始化程序来初始化,这个初始化也是个骚操做,反正小编以前看数组初始化的代码是没有见过的,这里先来个小例程,来给你们介绍下这个骚操做吧~通常初始化数组,常见的几种以下:





#define Max_Size 3//method one,common init the all elem of array with zeroint Array[Max_Size] = {0};//method two,common init the all elem of array with zeroint Array[Max_Size];void Init_Array(void){ for(int i = 0; i < Max_Size; i++) {    Array[i] = 0; }}//method three,common init arrayint Array[Max_Size] = {1, 2,};int Array[Max_Size] = {1, 2, 0};int Array[] = {1, 2, 0};

这些初始化的结果,只要学了C语言的,应该都会容易的看出来,下面来点骚操做吧~怎么直接初始化数组某个特定元素的值?




#define Max_Size 3#define Value 3//method oneint a[3] = {[0] = Value};//method twoint test_func(void){ return Value;}int a[3] = {[0] = test_func()};int main(void){  for(int i = 0; i < Max_Size; i++) { printf("a[%d] = %d\n", i, a[i]); }  return 0;}

猜猜打印到输出的结果是什么?

a[0] = 3

a[1] = 0

a[2] = 0

--------------------------------

Process exited after 0.152 seconds with return value 0

请按任意键继续. . .

以上就是怎么初始化数组指定元素的值,下面用查询表设计的状态机中的二维数组也会用到该种方法来初始化,来直接上代码。

 

 

#include <stdio.h>//Different state of ATM machinetypedef enum{ Idle_State, Card_Inserted_State, Pin_Eentered_State, Option_Selected_State, Amount_Entered_State, last_State} eSystemState; //Different type eventstypedef enum{ Card_Insert_Event, Pin_Enter_Event, Option_Selection_Event, Amount_Enter_Event, Amount_Dispatch_Event, last_Event} eSystemEvent; //typedef of 2d arraytypedef eSystemState (*const afEventHandler[last_State][last_Event])(void); //typedef of function pointertypedef eSystemState (*pfEventHandler)(void); //function call to dispatch the amount and return the ideal stateeSystemState AmountDispatchHandler(void){ return Idle_State;} //function call to Enter amount and return amount enetered stateeSystemState EnterAmountHandler(void){ return Amount_Entered_State;} //function call to option select and return the option selected stateeSystemState OptionSelectionHandler(void){ return Option_Selected_State;} //function call to enter the pin and return pin entered stateeSystemState EnterPinHandler(void){ return Pin_Eentered_State;} //function call to processing track data and return card inserted stateeSystemState InsertCardHandler(void){ return Card_Inserted_State;} int main(void){ eSystemState eNextState = Idle_State; eSystemEvent eNewEvent;        // Table to define valid states and event of finite state machine   static afEventHandler StateMachine = {        [Idle_State] ={[Card_Insert_Event]= InsertCardHandler()}, [Card_Inserted_State] ={[Pin_Enter_Event] = EnterPinHandler() }, [Pin_Eentered_State] ={[Option_Selection_Event] = OptionSelectionHandler()}, [Option_Selected_State] ={[Amount_Enter_Event] = EnterAmountHandler()}, [Amount_Entered_State] ={[Amount_Dispatch_Event] = AmountDispatchHandler()}, };  while(1) { // assume api to read the next event eSystemEvent eNewEvent = ReadEvent();  //Check NULL pointer and array boundary if( ( eNextState < last_State) && (eNewEvent < last_Event) && StateMachine[eNextState][eNewEvent]!= NULL) { // function call as per the state and event and return the next state of the finite state machine eNextState = (*StateMachine[eNextState][eNewEvent])(); } else { //Invalid } } return 0;}

这里的查询表是稀疏的,从初始化的代码里面也能够看出,若是状态和事件在增长,这种建立方法会增长内存的浪费。所以,在建立状态机以前,咱们须要在设计开始时就很是准确地说明全部事情。

状态机之结构体数组设计

这种方法的设计,是能够将状态和事件封装在结构体里面(这个部分的处理能够看看我以前的推文数据处理技巧及结构体数组的巧用),并在合适的状态和事件条件下调用函数指针。

 

#include <stdio.h>//Different state of ATM machinetypedef enum{ Idle_State, Card_Inserted_State, Pin_Eentered_State, Option_Selected_State, Amount_Entered_State, last_State} eSystemState; //Different type eventstypedef enum{ Card_Insert_Event, Pin_Enter_Event, Option_Selection_Event, Amount_Enter_Event, Amount_Dispatch_Event, last_Event} eSystemEvent; //typedef of function pointertypedef eSystemState (*pfEventHandler)(void); //structure of state and event with event handlertypedef struct{ eSystemState eStateMachine; eSystemEvent eStateMachineEvent; pfEventHandler pfStateMachineEvnentHandler;} sStateMachine; //function call to dispatch the amount and return the ideal stateeSystemState AmountDispatchHandler(void){ return Idle_State;} //function call to Enter amount and return amount entered stateeSystemState EnterAmountHandler(void){ return Amount_Entered_State;} //function call to option select and return the option selected stateeSystemState OptionSelectionHandler(void){ return Option_Selected_State;} //function call to enter the pin and return pin entered stateeSystemState EnterPinHandler(void){ return Pin_Eentered_State;} //function call to processing track data and return card inserted stateeSystemState InsertCardHandler(void){ return Card_Inserted_State;} //Initialize array of structure with states and event with proper handlersStateMachine asStateMachine [] ={ {Idle_State,Card_Insert_Event,InsertCardHandler()}, {Card_Inserted_State,Pin_Enter_Event,EnterPinHandler()}, {Pin_Eentered_State,Option_Selection_Event,OptionSelectionHandler()}, {Option_Selected_State,Amount_Enter_Event,EnterAmountHandler()}, {Amount_Entered_State,Amount_Dispatch_Event,AmountDispatchHandler()}}; //main functionint main(void){ eSystemState eNextState = Idle_State; while(1) { //Api read the event eSystemEvent eNewEvent = read_event(); if((eNextState < last_State) && (eNewEvent < last_Event)&& (asStateMachine[eNextState].eStateMachineEvent == eNewEvent) && (asStateMachine[eNextState].pfStateMachineEvnentHandler != NULL)) { // function call as per the state and event and return the next state of the finite state machine eNextState = (*asStateMachine[eNextState].pfStateMachineEvnentHandler)(); } else { //Invalid } } return 0;}
相关文章
相关标签/搜索