FSM mealy型状态机实现
- FSM 状态机的概念
- FSM 状态
- FSM 状态机
- FSM 输入信号处理&状态切换
- FSM 状态的行为
- FSM(finite-state machine) 有限状态机。
- mealy型状态机:输入不仅与当前状态有关,还与输入有关;
FSM 状态机的概念
有限状态机(finite-state machine,FSM)可以解决有限个状态转换的问题。
- 状态:表示对象的某种形态,在当前形态下可能会拥有不同的行为和属性。
- 状态机:每一个时刻只能处于一个状态;拥有对象所有状态的集合;会接受输入,并根据对象当前的状态转换到下一个状态
- 输出:确切的说,输出指转换到某个状态后,在这个状态内对象所有的行为、表现等都是输出。
- 动作:指状态机的行为,包括进入状态动作,离开状态动作
以FSM为例,常见的FSM灯的控制大致可以分为几种状态
- 熄灭状态
- 常亮状态
- 闪烁状态
FSM状态切换如图所示:
FSM 状态
我们将FSM的状态以枚举的形式定义出来。为了方便,我们将默认状态值设置为0
,用C表示如下:
typedef enum
{
FSM_STATE_OFF = 0, // 熄灭状态
FSM_STATE_ON, // 常亮状态
FSM_STATE_BLINK, // 闪烁状态
FSM_STATE_BUTTON, // 状态阈值
} e_FSM_STATE;
对于 FSM 有限状态机
中的每一个状态而言,我们可以将状态的行为大致分为 进入状态
运行状态
和 退出状态
三种形式。用C语言来描述如下:
typedef void (*handle_t)(void);
typedef struct
{
char name[20]; // 状态名称
e_FSM_STATE state; // 状态
handle_t action_entry; // 进入状态
handle_t action_during; // 运行状态
handle_t action_exit; // 退出状态
}fsm_state_t;
至此,我们就能将led所有的状态用一张表来描述出来,用C语言来描述如下:
/****************************************************/
// FSM_STATE_OFF = 0, // 熄灭状态下三种行为
void led_off_entry(void);
void led_off_during(void);
void led_off_exit(void);
/****************************************************/
// FSM_STATE_ON, // 常亮状态下三种行为
void led_on_entry(void);
void led_on_during(void);
void led_on_exit(void);
/****************************************************/
// FSM_STATE_BLINK, // 闪烁状态下三种行为
void led_blink_entry(void);
void led_blink_during(void);
void led_blink_exit(void);
/****************************************************/
static fsm_state_t g_fsm_state_table[FSM_STATE_BUTTON] = {
{"led off", FSM_STATE_OFF, led_off_entry, led_off_during, led_off_exit},
{"led on", FSM_STATE_ON, led_on_entry, led_on_during, led_on_exit},
{"led blink", FSM_STATE_BLINK, led_blink_entry, led_blink_during, led_blink_exit},
};
/****************************************************/
FSM 状态机
接下来,我们需要知道状态怎样去运行每个状态的行为函数,由于我们已经将所有的状态抽象成一张表,那么我们如何对状态进行运行呢?
对于mealy状态机
,状态的切换是和输入的信号以及当前状态紧密相关的。我们需要知道它输入的信号,当前状态,下一个运行状态(是否需要进行跳转)等。用C语言来描述如下:
typedef struct
{
int led_on;
int led_off;
int led_blink;
}fsm_sig_t;
typedef struct
{
fsm_sig_t sig; // 状态机输入信号
e_FSM_STATE cur_state; // 当前运行状态
e_FSM_STATE nxt_state; // 跳转状态 PWR_STATE_BUTTON:下一次运行的还是当前状态
fsm_state_t *table; // 状态表指针 g_fsm_state_table
}fsm_info_t;
static fsm_info_t g_fsm_info;
我们对信号的处理
(对状态进行状态在下一节进行详细描述)。
我们先研究状态如何进行运行。
- 状态机运行
我们不难想象出,一个状态切换到另一个状态的方式应该是cur_state.exit() -> nxt_state.entry() -> nxt_state.during()
。保持一个状态运行行为应该处于cur_state.during()
中。
这个时候我们定义状态类型时,FSM_STATE_BUTTON 状态阈值
就能方便对当前状态是否需要进行切换做出判断。状态机的运行实体函数用C语言来描述如下:
void fsm_machine(void)
{
g_fsm_info.nxt_state = fsm_machine_get_nxt_state(); // 输入信号切换状态
if(g_fsm_info.nxt_state < FSM_STATE_BUTTON) // 状态跳转
{
printf("[FSM] exit:%s,%d\r\n",g_fsm_info.table[g_fsm_info.cur_state].name, g_fsm_info.table[g_fsm_info.cur_state].state);
g_fsm_info.table[g_fsm_info.cur_state].action_exit();
g_fsm_info.cur_state = g_fsm_info.nxt_state;
printf("[FSM] entry:%s,%d\r\n",g_fsm_info.table[g_fsm_info.cur_state].name, g_fsm_info.table[g_fsm_info.cur_state].state);
g_fsm_info.table[g_fsm_info.cur_state].action_entry();
g_fsm_info.nxt_state = FSM_STATE_BUTTON;
printf("[FSM] during:%s,%d\r\n",g_fsm_info.table[g_fsm_info.cur_state].name, g_fsm_info.table[g_fsm_info.cur_state].state);
g_fsm_info.table[g_fsm_info.cur_state].action_during();
}
else // 状态继续运行
{
g_fsm_info.table[g_fsm_info.cur_state].action_during();
}
}
- 状态机初始化
- 清零所有的输入信号。
- 将初始状态设置成默认状态(0)。
- 给状态表指针赋值。
- 第一次进入默认状态时,由于
nxt_state = FSM_STATE_BUTTON
,在fsm_machine
中直接进入cur_state.action_during()
,不会进入cur_state.action_entry()
,需要提前调用。
void fsm_init(void)
{
memset(&g_fsm_info.sig,0,sizeof(fsm_sig_t)); // 输入信号清零
g_fsm_info.cur_state = 0; // 默认状态 我们认为初始状态为默认状态 方便移植
g_fsm_info.nxt_state = FSM_STATE_BUTTON; // 默认状态运行 设置成状态阈值。
g_fsm_info.table = &g_fsm_state_table[0]; // 状态表指针
printf("[FSM] entry:%s,%d\r\n",g_fsm_info.table[g_fsm_info.cur_state].name, g_fsm_info.table[g_fsm_info.cur_state].state);
g_fsm_info.table[g_fsm_info.cur_state].action_entry(); // 默认状态进入行为
}
FSM 输入信号处理&状态切换
最最最重要的来了~在我们的状态机中,如何实现状态的切换。在上述fsm_machine
函数中不难看出,fsm_machine_get_nxt_state
是状态机切换的根源。我们将所有的输入信号以及当前状态的组成条件放入此函数中,用来切换状态。
首先,我们认为行为action_entry
和action_exit
在状态中都是瞬间的,在此状态行为下不应该触发状态切换。
e_FSM_STATE fsm_machine_get_nxt_state(void)
{
e_FSM_STATE state = FSM_STATE_BUTTON;
fsm_machin_get_sig(&g_fsm_info.sig); // 获取信号
if(g_fsm_info.nxt_state == FSM_STATE_BUTTON) // 在during行为下
{
switch(g_fsm_info.cur_state)
{
case FSM_STATE_OFF: // 熄灭状态
{
g_fsm_info.sig.led_off = 0;
if(g_fsm_info.sig.led_on == 1)
{
state = FSM_STATE_ON;
}
else if(g_fsm_info.sig.led_blink == 1)
{
state = FSM_STATE_BLINK;
}
break;
}
case FSM_STATE_ON: // 常亮状态
{
g_fsm_info.sig.led_on = 0;
if(g_fsm_info.sig.led_off == 1)
{
state = FSM_STATE_OFF;
}
else if(g_fsm_info.sig.led_blink == 1)
{
state = FSM_STATE_BLINK;
}
break;
}
case FSM_STATE_BLINK: // 闪烁状态
{
g_fsm_info.sig.led_blink = 0;
if(g_fsm_info.sig.led_on == 1)
{
state = FSM_STATE_ON;
}
else if(g_fsm_info.sig.led_off == 1)
{
state = FSM_STATE_OFF;
}
break;
}
default:
break;
}
}
return state;
}
FSM 状态的行为
/****************************************************/
// FSM_STATE_OFF = 0, // 熄灭状态下三种行为
void led_off_entry(void);
void led_off_during(void);
void led_off_exit(void);
/****************************************************/
// FSM_STATE_ON, // 常亮状态下三种行为
void led_on_entry(void);
void led_on_during(void);
void led_on_exit(void);
/****************************************************/
// FSM_STATE_BLINK, // 闪烁状态下三种行为
void led_blink_entry(void);
void led_blink_during(void);
void led_blink_exit(void);
/****************************************************/