嵌入式软件架构设计-分层状态机
扫描二维码
随时随地手机看文章
前言
之前介绍的状态机是有限状态机(FSM),这篇介绍一下分层状态机(HSM)的理解。
分层状态机是一种用于描述系统行为和控制流的模型,它将系统划分为多个层级,并将每个层级表示为一个状态机。每个状态机描述了一个特定的状态集合以及状态之间的转移规则。每个状态机都有自己的输入和输出,可以与其他状态机进行交互,从而形成整个系统的行为。
分层状态机的设计有助于降低系统复杂度,并且能够更好地组织系统的功能和行为。在分层状态机中,每个状态机负责处理一个特定的子任务或功能,这使得系统更易于理解和维护。
在分层状态机中,高层状态机负责协调和控制下层状态机的行为。高层状态机可以通过向下层状态机发送命令或事件来触发状态转换,下层状态机可以将其处理结果返回给上层状态机,以便上层状态机作出相应的决策。
区别
分层状态机(HSM)和有限状态机(FSM)是两种不同的模型,虽然它们都用于描述系统的行为和控制流,但它们之间还是存在一些区别的。
-
层次结构不同:分层状态机是将系统划分为多个层级,并将每个层级表示为一个状态机,每个状态机有自己的输入和输出,可以与其他状态机进行交互,形成整个系统的行为。而有限状态机则是单层的结构,描述系统在特定条件下的状态和状态之间的转移。
-
状态数量不同:分层状态机中的状态数量通常比有限状态机更多。由于分层状态机的每个层级都有自己的状态集合,因此整个系统的状态集合是各层状态集合的并集。而有限状态机只有一个状态集合,状态数量相对较少。
-
状态之间的转移规则不同:在分层状态机中,状态之间的转移规则可以根据不同的层级进行定义,每个状态机有自己的转移规则。而有限状态机的状态之间的转移规则通常是全局统一的。
-
功能不同:分层状态机通常用于描述复杂系统的行为和控制流,其设计目的是为了降低系统的复杂度并更好地组织系统的功能和行为。而有限状态机通常用于描述简单的控制流程或者算法。
总之,分层状态机和有限状态机虽然都是描述系统行为和控制流的模型,但是它们的层次结构、状态数量、状态之间的转移规则和应用场景等方面存在较大的不同
举例说明
假设有一个自动售货机,它需要根据用户选择的按钮来售出对应的商品。我们可以使用有限状态机和分层状态机分别来实现这个自动售货机。
首先,我们来看一下如何使用有限状态机来实现自动售货机。假设我们有三个商品可以售卖,分别是饮料、糖果和薯片,对应的按钮分别为A、B和C。那么,我们可以定义以下状态:
初始状态:待命状态
状态1:等待用户选择按钮
状态2:出货状态
根据这些状态,我们可以设计状态转移规则如下:
待命状态:如果用户按下按钮A、B或C,则转移到等待用户选择按钮状态。
等待用户选择按钮状态:如果用户按下按钮A,则转移到出货状态1;如果用户按下按钮B,则转移到出货状态2;如果用户按下按钮C,则转移到出货状态3。
出货状态1:出售饮料,并转移到待命状态。
出货状态2:出售糖果,并转移到待命状态。
出货状态3:出售薯片,并转移到待命状态。
流程图:
接下来,我们来看一下如何使用分层状态机来实现自动售货机。首先,我们可以将自动售货机分解成三个层次:选择商品层、支付层和出货层。每个层次都有自己的状态和状态转移规则,如下所示:
选择商品层:等待用户选择按钮状态
支付层:等待用户支付状态
出货层:出货状态
然后,我们可以进一步细化每个层次的状态和状态转移规则,如下所示:
选择商品层:
初始状态:待命状态
状态1:等待用户选择按钮
转移规则:如果用户按下按钮A、B或C,则转移到等待用户支付状态,并保存用户选择的商品。
支付层:
初始状态:待命状态
状态1:等待用户支付
转移规则:如果用户完成支付,则转移到出货状态,并发送出货指令。
出货层:
初始状态:待命状态
状态1:等待出货指令
状态2:正在出货
转移规则:如果接收到出货指令,则转移到正在出货状态,并执行出货操作;如果出货操作完成,则转移到待命状态。
流程图:
需要注意的是,有限状态机和分层状态机都可以用于自动售货机的设计,选择哪种方法取决于具体的需求和复杂度。如果系统相对简单,可以使用有限状态机。如果需要管理多个子系统、处理多个事件和状态,可以考虑使用分层状态机。
最重要的是,在实际应用中,理解分层状态机的关键是正确地设计状态转移逻辑和事件处理函数。如果状态转移逻辑不正确,可能会导致状态机无法正确响应事件;如果事件处理函数不正确,可能会导致状态机无法正确处理事件。因此,在设计分层状态机时,需要认真考虑状态转移和事件处理的逻辑,并进行充分的测试和验证。
代码参考
有限状态机
有限状态机可以使用switch/case或if/else语句实现状态转移,并在每个状态转移时执行相应的操作。
#include#include enum State { ST_IDLE, ST_WAIT_SELECT, ST_WAIT_PAY, ST_DISPENSE }; enum Button { BTN_NONE, BTN_A, BTN_B, BTN_C }; enum State current_state = ST_IDLE; enum Button current_button = BTN_NONE; void update_fsm(enum Button button) { switch (current_state) { case ST_IDLE: if (button != BTN_NONE) { current_button = button; current_state = ST_WAIT_SELECT; printf("商品选择: %c\n", button); } break; case ST_WAIT_SELECT: if (button == BTN_NONE) { current_state = ST_WAIT_PAY; printf("等待支付...\n"); } break; case ST_WAIT_PAY: if (button == BTN_NONE) { current_state = ST_DISPENSE; printf("出货中...\n"); } break; case ST_DISPENSE: if (button == BTN_NONE) { current_state = ST_IDLE; printf("出售完成\n"); } break; } } int main() { update_fsm(BTN_NONE); update_fsm(BTN_A); update_fsm(BTN_NONE); update_fsm(BTN_NONE); update_fsm(BTN_NONE); update_fsm(BTN_NONE); return 0; }
分层状态机
而分层状态机中由于更加复杂,单纯地采用switch/case或if/else语句已经不太适合不同状态机及其子状态的转移,最好的方式是采用表驱动的方式去实现,然后在数组表中定义每个状态机及其子状态,并且有回调函数用来处理每个状态机及其子状态的处理等,提高系统的可维护性和可扩展性。
由于实现较为复杂,以下代码仅供参考,不具体实现
#include#include #include // 状态机状态枚举 typedef enum { STATE_IDLE, STATE_WAITING_FOR_COIN, STATE_WAITING_FOR_SELECTION, STATE_DISPENSING, STATE_COUNT } state_t; // 事件枚举 typedef enum { EVENT_INSERT_COIN, EVENT_MAKE_SELECTION, EVENT_DISPENSE, EVENT_COUNT } event_t; // 状态转移条件函数类型定义 typedef bool (*condition_func_t)(void); // 状态机状态结构体定义 typedef struct { void (*enter_func)(void); // 进入状态函数 void (*exit_func)(void); // 退出状态函数 void (*poll_func)(void); // 事件轮询处理函数 condition_func_t (*cond_func)(void); // 转移条件函数 } state_info_t; // 状态转移表 static const int transition_table[STATE_COUNT][EVENT_COUNT] = { // EVENT_INSERT_COIN EVENT_MAKE_SELECTION EVENT_DISPENSE { STATE_WAITING_FOR_COIN, STATE_IDLE, STATE_IDLE }, // STATE_IDLE { STATE_WAITING_FOR_COIN, STATE_WAITING_FOR_SELECTION, STATE_IDLE }, // STATE_WAITING_FOR_COIN { STATE_WAITING_FOR_COIN, STATE_WAITING_FOR_SELECTION, STATE_DISPENSING }, // STATE_WAITING_FOR_SELECTION { STATE_WAITING_FOR_COIN, STATE_WAITING_FOR_SELECTION, STATE_IDLE } // STATE_DISPENSING }; // 状态机状态数组 static const state_info_t state_table[STATE_COUNT] = { // 进入状态函数 退出状态函数 事件轮询处理函数 转移条件函数 { NULL, NULL, NULL, NULL }, // STATE_IDLE { wait_for_coin_enter, wait_for_coin_exit, wait_for_coin_poll, wait_for_coin }, // STATE_WAITING_FOR_COIN { wait_for_select_enter, wait_for_select_exit, wait_for_select_poll, waiting_for_select }, // STATE_WAITING_FOR_SELECTION { dispensing_enter, dispensing_exit, dispensing_poll, dispensing } // STATE_DISPENSING }; // 当前状态 static state_t current_state = STATE_IDLE;





