Follow our public account, mark it, reply 1024 to get learning materials, improve a little every day.
-
Current State: Refers to the state currently occupied.
-
Condition: Also known as “event”, when a condition is met, it will trigger an action or execute a state transition.
-
Action: The action executed after the condition is met. After the action is completed, it can transition to a new state or remain in the original state. Action is not mandatory; when the condition is met, no action can also be taken, directly transitioning to a new state.
-
Next State: The new state to which the transition occurs after the condition is met. The “next state” is relative to the “current state”; once activated, it becomes the new “current state”.
As shown in the figure, it is a timer counter that exists in two states, one for setting and one for timing.
Setting State
-
“+” “-” buttons are used to set the initial countdown -
When the countdown value is set, press the confirm button to start timing, transitioning to the timing state
Timing State
-
Pressing “+” “-” will input the password; ” + ” means 1, ” – ” means input 0; the password has 4 digits -
Confirm button: Only when the entered password equals the default password, can the timing be stopped; otherwise, the timing goes directly to zero and executes related operations
/***************************************
1. List all states
***************************************/
typedef enum{
SETTING,
TIMING
} STATE_TYPE;
/***************************************
2. List all events
***************************************/
typedef enum{
UP_EVT,
DOWN_EVT,
ARM_EVT,
TICK_EVT
} EVENT_TYPE;
/***************************************
3. Define structures related to state machine
***************************************/
struct bomb
{
uint8_t state;
uint8_t timeout;
uint8_t code;
uint8_t defuse_code;
} bomb1;
/***************************************
4. Initialize state machine
***************************************/
void bomb1_init(void)
{
bomb1.state = SETTING;
bomb1.defuse_code = 6; //0110
}
/***************************************
5. Dispatch state machine events
***************************************/
void bomb1_fsm_dispatch(EVENT_TYPE evt ,void* param)
{
switch(bomb1.state)
{
case SETTING:
{
switch(evt)
{
case UP_EVT: // " + " button pressed event
if(bomb1.timeout< 60)
++bomb1.timeout;
bsp_display(bomb1.timeout);
break;
case DOWN_EVT: // " - " button pressed event
if(bomb1.timeout > 0)
--bomb1.timeout;
bsp_display(bomb1.timeout);
break;
case ARM_EVT: // "Confirm" button pressed event
bomb1.state = TIMING;
bomb1.code = 0;
break;
}
}
break;
case TIMING:
{
switch(evt)
{
case UP_EVT: // " + " button pressed event
bomb1.code = (bomb1.code <<1) | 0x01;
break;
case DOWN_EVT: // " - " button pressed event
bomb1.code = (bomb1.code <<1);
break;
case ARM_EVT: // "Confirm" button pressed event
if(bomb1.code == bomb1.defuse_code)
{
bomb1.state = SETTING;
}
else
{
bsp_display("bomb!")
}
break;
case TICK_EVT:
if(bomb1.timeout)
{
--bomb1.timeout;
bsp_display(bomb1.timeout);
}
if(bomb1.timeout == 0)
{
bsp_display("bomb!")
}
break;
}
}
break;
}
}
Advantages
Simple, coherent code reading, easy to understand
Disadvantages
-
As the number of states or events increases, the code state functions need to be modified frequently, and the code volume for state event handling will continue to increase
-
The state machine is not encapsulated, leading to poor portability.
-
The operations for entering and exiting states are not implemented. Entering and exiting are particularly important in state machines:
-
Entry event: only triggered once when entering, mainly used to perform necessary initialization for the state
-
Exit event: only triggered once when switching states, mainly used to clear intermediate parameters generated by the state, providing a clean environment for the next entry
(*) The transition to setting occurs only when (code == defuse_code).
/*1. List all states*/
enum
{
SETTING,
TIMING,
MAX_STATE
};
/*2. List all events*/
enum
{
UP_EVT,
DOWN_EVT,
ARM_EVT,
TICK_EVT,
MAX_EVT
};
/*3. Define state table*/
typedef void (*fp_state)(EVT_TYPE evt , void* param);
static const fp_state bomb2_table[MAX_STATE][MAX_EVENT] =
{
{setting_UP, setting_DOWN, setting_ARM, null},
{setting_UP, setting_DOWN, setting_ARM, timing_TICK}
};
struct bomb_t
{
const fp_state const *state_table; /* the State-Table */
uint8_t state; /* the current active state */
uint8_t timeout;
uint8_t code;
uint8_t defuse_code;
};
struct bomb bomb2=
{
.state_table = bomb2_table;
}
void bomb2_init(void)
{
bomb2.defuse_code = 6; // 0110
bomb2.state = SETTING;
}
void bomb2_dispatch(EVT_TYPE evt , void* param)
{
fp_state s = NULL;
if(evt > MAX_EVT)
{
LOG("EVT type error!");
return;
}
s = bomb2.state_table[bomb2.state * MAX_EVT + evt];
if(s != NULL)
{
s(evt , param);
}
}
/* List all event handling functions corresponding to states */
void setting_UP(EVT_TYPE evt, void* param)
{
if(bomb1.timeout< 60)
++bomb1.timeout;
bsp_display(bomb1.timeout);
}
-
Advantages -
Each state is relatively independent for the user, and adding events and states does not require modifying previously existing state event functions. -
Encapsulating the state machine leads to better portability
typedef void (*Tran)(struct StateTableTag *me, Event const *e);
void Bomb2_setting_ARM (Bomb2 *me, Event const *e);
typedef struct Bomb
{
struct StateTableTag *me; // Must be the first member
uint8_t private;
}
-
Disadvantages
-
The most obvious disadvantage is that the function granularity is too small; one function is generated for one state and one event, and when there are many states and events, the handling functions will increase rapidly, making the logic scattered when reading the code.
-
Did not implement entry and exit actions.
One-Dimensional State Transition Table
Implementation Principle:

typedef void (*fp_action)(EVT_TYPE evt,void* param);
/* Basic structure of transition table */
struct tran_evt_t
{
EVT_TYPE evt;
uint8_t next_state;
};
/* Description of state */
struct fsm_state_t
{
fp_action enter_action; // Enter action
fp_action exit_action; // Exit action
fp_action action;
tran_evt_t* tran; // Transition table
uint8_t tran_nb; // Size of transition table
const char* name;
}
/* Main state table */
#define ARRAY(x) x,sizeof(x)/sizeof(x[0])
const struct fsm_state_t state_table[]=
{
{setting_enter, setting_exit, setting_action, ARRAY(set_tran_evt), "setting" },
{timing_enter, timing_exit, timing_action, ARRAY(time_tran_evt), "timing" }
};
/* Build a state machine */
struct fsm
{
const struct state_t * state_table; /* the State-Table */
uint8_t cur_state; /* the current active state */
uint8_t timeout;
uint8_t code;
uint8_t defuse_code;
} bomb3;
/* Initialize state machine */
void bomb3_init(void)
{
bomb3.state_table = state_table; // Point to state table
bomb3.cur_state = setting;
bomb3.defuse_code = 8; //1000
}
/* State machine event dispatch */
void fsm_dispatch(EVT_TYPE evt, void* param)
{
tran_evt_t* p_tran = NULL;
/* Get the transition table of the current state */
p_tran = bomb3.state_table[bomb3.cur_state]->tran;
/* Check if any possible transition matches the currently triggered event */
for(uint8_t i=0; i<x; i++)
{
if(p_tran[i]->evt == evt) // The event triggers the transition
{
if(NULL != bomb3.state_table[bomb3.cur_state].exit_action)
{
bomb3.state_table[bomb3.cur_state].exit_action(NULL); // Execute exit action
}
if(bomb3.state_table[_tran[i]->next_state].enter_action)
{
bomb3.state_table[_tran[i]->next_state].enter_action(NULL); // Execute entry action
}
/* Update current state */
bomb3.cur_state = p_tran[i]->next_state;
}
else
{
bomb3.state_table[bomb3.cur_state].action(evt, param);
}
}
}
/*************************************************************************
setting state related
************************************************************************/
void setting_enter(EVT_TYPE evt, void* param)
{
}
void setting_exit(EVT_TYPE evt, void* param)
{
}
void setting_action(EVT_TYPE evt, void* param)
{
}
tran_evt_t set_tran_evt[] =
{
{ARM , timing},
}
/*timing state related*/
-
Advantages
-
Each state is relatively independent for the user, and adding events and states does not require modifying previously existing state event functions.
-
Implemented entry and exit actions
-
Easy to design based on state transition diagrams (state transition diagrams list the possible transitions for each state, which is the transition table here)
-
Flexible implementation, can implement complex logic, such as last state, adding monitoring conditions to reduce the number of events. Can implement non-completely event-driven
-
Disadvantages -
The granularity of functions is relatively small (smaller than two-dimensional and grows slowly), and you can see that each state requires at least three functions, and all transition relationships need to be listed.
Characteristics
-
Event-driven programming -
The Hollywood Principle: Unlike traditional sequential programming methods such as “super loop”, or traditional RTOS tasks, the vast majority of modern event-driven systems are constructed based on the Hollywood principle (Don’t call me; I’ll call you.)
-
Object-oriented -
Classes and single inheritance
-
Tools -
QM: A software that describes state machines through UML class diagrams and can automatically generate C code

-
QS Software Tracking Tool


-
Implementation

/* qevent.h ----------------------------------------------------------------*/
typedef struct QEventTag
{
QSignal sig;
uint8_t dynamic_;
} QEvent;
/* qep.h -------------------------------------------------------------------*/
typedef uint8_t QState; /* status returned from a state-handler function */
typedef QState (*QStateHandler) (void *me, QEvent const *e); /* argument list */
typedef struct QFsmTag /* Finite State Machine */
{
QStateHandler state; /* current active state */
} QFsm;
#define QFsm_ctor(me_, initial_) ((me_)->state = (initial_))
void QFsm_init (QFsm *me, QEvent const *e);
void QFsm_dispatch(QFsm *me, QEvent const *e);
#define Q_RET_HANDLED ((QState)0)
#define Q_RET_IGNORED ((QState)1)
#define Q_RET_TRAN ((QState)2)
#define Q_HANDLED() (Q_RET_HANDLED)
#define Q_IGNORED() (Q_RET_IGNORED)
#define Q_TRAN(target_) (((QFsm *)me)->state = (QStateHandler) (target_),Q_RET_TRAN)
enum QReservedSignals
{
Q_ENTRY_SIG = 1,
Q_EXIT_SIG,
Q_INIT_SIG,
Q_USER_SIG
};
/* file qfsm_ini.c ---------------------------------------------------------*/
#include "qep_port.h" /* the port of the QEP event processor */
#include "qassert.h" /* embedded systems-friendly assertions */
void QFsm_init(QFsm *me, QEvent const *e)
{
(*me->state)(me, e); /* execute the top-most initial transition */
/* enter the target */
(void)(*me->state)(me , &QEP_reservedEvt_[Q_ENTRY_SIG]);
}
/* file qfsm_dis.c ---------------------------------------------------------*/
void QFsm_dispatch(QFsm *me, QEvent const *e)
{
QStateHandler s = me->state; /* save the current state */
QState r = (*s)(me, e); /* call the event handler */
if (r == Q_RET_TRAN) /* transition taken? */
{
(void)(*s)(me, &QEP_reservedEvt_[Q_EXIT_SIG]); /* exit the source */
(void)(*me->state)(me, &QEP_reservedEvt_[Q_ENTRY_SIG]);/*enter target*/
}
}
// Implementing the above timer example
#include "qep_port.h" /* the port of the QEP event processor */
#include "bsp.h" /* board support package */
enum BombSignals /* all signals for the Bomb FSM */
{
UP_SIG = Q_USER_SIG,
DOWN_SIG,
ARM_SIG,
TICK_SIG
};
typedef struct TickEvtTag
{
QEvent super; /* derive from the QEvent structure */
uint8_t fine_time; /* the fine 1/10 s counter */
} TickEvt;
typedef struct Bomb4Tag
{
QFsm super; /* derive from QFsm */
uint8_t timeout; /* number of seconds till explosion */
uint8_t code; /* currently entered code to disarm the bomb */
uint8_t defuse; /* secret defuse code to disarm the bomb */
} Bomb4;
void Bomb4_ctor (Bomb4 *me, uint8_t defuse);
QState Bomb4_initial(Bomb4 *me, QEvent const *e);
QState Bomb4_setting(Bomb4 *me, QEvent const *e);
QState Bomb4_timing (Bomb4 *me, QEvent const *e);
/*--------------------------------------------------------------------------*/
/* the initial value of the timeout */
#define INIT_TIMEOUT 10
/*..........................................................................*/
void Bomb4_ctor(Bomb4 *me, uint8_t defuse) {
QFsm_ctor_(&me->super, (QStateHandler)&Bomb4_initial);
me->defuse = defuse; /* the defuse code is assigned at instantiation */
}
/*..........................................................................*/
QState Bomb4_initial(Bomb4 *me, QEvent const *e)
{
(void)e;
me->timeout = INIT_TIMEOUT;
return Q_TRAN(&Bomb4_setting);
}
/*..........................................................................*/
QState Bomb4_setting(Bomb4 *me, QEvent const *e)
{
switch (e->sig)
{
case UP_SIG:
{
if (me->timeout < 60)
{
++me->timeout;
BSP_display(me->timeout);
}
return Q_HANDLED();
}
case DOWN_SIG:
{
if (me->timeout > 1)
{
--me->timeout;
BSP_display(me->timeout);
}
return Q_HANDLED();
}
case ARM_SIG:
{
return Q_TRAN(&Bomb4_timing); /* transition to "timing" */
}
}
return Q_IGNORED();
}
/*..........................................................................*/
void Bomb4_timing(Bomb4 *me, QEvent const *e)
{
switch (e->sig)
{
case Q_ENTRY_SIG:
{
me->code = 0; /* clear the defuse code */
return Q_HANDLED();
}
case UP_SIG:
{
me->code <<= 1;
me->code |= 1;
return Q_HANDLED();
}
case DOWN_SIG:
{
me->code <<= 1;
return Q_HANDLED();
}
case ARM_SIG:
{
if (me->code == me->defuse)
{
return Q_TRAN(&Bomb4_setting);
}
return Q_HANDLED();
}
case TICK_SIG:
{
if (((TickEvt const *)e)->fine_time == 0)
{
--me->timeout;
BSP_display(me->timeout);
if (me->timeout == 0)
{
BSP_boom(); /* destroy the bomb */
}
}
return Q_HANDLED();
}
}
return Q_IGNORED();
}
-
Advantages
-
Uses an object-oriented design method, leading to good portability
-
Implemented entry and exit actions
-
Appropriate granularity, and the granularity of events is controllable
-
High efficiency by changing pointers during state switching
-
Can be extended to hierarchical state machines
-
Disadvantages -
The greatest difficulty in design lies in defining events and controlling the granularity of events; for example, when a frame of data is received from the serial port, whether to update these variables as a separate event or treat the serial port data reception as one event. Additionally, when using this programming method for display screens, how to design events.
-
Fully supports hierarchical state nesting, including guaranteed entry/exit actions for any state transition topology under up to 4 layers of state nesting
-
Supports up to 8 concurrently executing, deterministic, thread-safe event queues for active objects
-
Supports a one-byte wide (255 signals) signal and a scalable parameter that can be configured to 0 (no parameters), 1, 2, or 4 bytes
-
Uses a FIFO queuing strategy for direct event dispatching
-
Each active object has a one-time time event (timer), with a configurable dynamic range of 0 (no time event), 1, 2, or 4 bytes
-
Built-in cooperative vanilla kernel
-
Built-in preemptive RTC kernel named QK-nano
-
Low-power architecture with idle callback functions for convenient implementation of power-saving modes.
-
Prepared for non-standard extensions for C compilers for popular low-end CPU architectures in the code (e.g., allocating constant objects in code space, reentrant functions, etc.)
-
Error handling strategy based on assertions
-
Code style
Original text:http://t.csdn.cn/VZC0X
The article is sourced from the internet, and the copyright belongs to the original author. If there is any infringement, please contact for deletion.
Follow our public account, mark it, reply 1024 to get learning materials, improve a little every day.
Statement:
The original articles, images, etc. from this account belong to the original authors. If there is any infringement, please contact for deletion.

