Embedded Software Architecture Design – Event-Driven State Machine

Click the blue text above to learn more practical skills in embedded programming. If you find this article helpful, feel free to like and follow.

Introduction

Previously, we introduced finite state machines (FSM) and hierarchical state machines (HSM). This article mainly introduces the Event-Driven State Machine (ESM).

A widely used design method in embedded systems for managing and controlling state transitions. It links the switching of states with the occurrence of external events, allowing the system to respond more flexibly to different conditions and inputs.

Overview

The event-driven state machine divides the system into a set of states, each representing the behavior of the system under specific conditions. The transitions between states are triggered by the occurrence of external events rather than being directly controlled by internal conditions or method calls.

Main Components

  • States: The states that the system may be in under different conditions. Each state represents a specific behavior or state.

  • Events: External events that trigger state transitions, which can be changes in sensor data, user input, timer interrupts, etc.

  • Transitions: Define the rules for transitioning between states, indicating which event triggers the transition from one state to another.

  • Event Handlers: Each state can be associated with one or more event handlers to process events triggered during state transitions. Event handlers define the behavior between states.

Working Principle

The working principle of the event-driven state machine is as follows:

  • Initialization: The system is in the initial state during initialization.

  • Event Monitoring: The system continuously monitors the occurrence of external events.

  • Event Matching: When an external event occurs, the state machine checks the transition table for the current state to find the matching transition.

  • State Transition: If a matching transition is found, the system executes the state transition and calls the associated event handler. This may cause the system to switch from the current state to another state.

  • Waiting for Events: Once the event handler has completed, the system waits for the next external event to occur.

Differences

Event-driven state machines and ordinary state machines are both methods for managing and controlling state transitions, but they differ in design philosophy and implementation.

Ordinary State Machine

  1. State Switching Method: In an ordinary state machine, transitions between states are usually based on internal conditions or direct method calls. State transitions are typically directly controlled by the relationships and switching conditions between states.

  2. Switching Conditions: The state switching in an ordinary state machine is usually determined by hard-coded logical conditions, which may include input signals, timers, internal variables, etc.

  3. State Management: An ordinary state machine needs to maintain a transition table or logical judgments to control state switching and behavior. The relationships and switching conditions between states can complicate the code, especially when there are many states or complex relationships between states.

  4. Application Scenarios: Ordinary state machines are suitable for scenarios where state transitions are relatively simple and fixed, suitable for describing basic system behaviors and processes.

Event-Driven State Machine

  1. State Switching Method: The event-driven state machine relies on the occurrence of external events to trigger state transitions. Transitions between states are triggered by events, and the state machine determines the next state based on the occurrence of events.

  2. Switching Conditions: In an event-driven state machine, state transitions are not directly determined by internal logical conditions but are triggered by the occurrence of external events. The relationships and switching conditions between states are more loosely defined.

  3. State Management: An event-driven state machine does not need to maintain a complex transition table but triggers state switching based on specific events. This approach makes the design of the state machine more flexible and extensible.

  4. Application Scenarios: Event-driven state machines are suitable for complex state transitions and processes, especially when the relationships between states are complex or when flexible control of state transitions based on external events is required.

Overall, event-driven state machines are more flexible and extensible compared to ordinary state machines, suitable for complex embedded MCU application scenarios. They can better handle relationships between states and trigger transitions based on external events, providing more efficient, flexible, and scalable state management and control.

Code Implementation

Below is a simple example code of an event-driven state machine framework to illustrate its working principle.

#include <stdio.h>
#include <stdbool.h>

// Define state enum
typedef enum {
    STATE_IDLE,
    STATE_RUNNING,
    STATE_PAUSED,
    STATE_STOPPED
} State;

// Define event enum
typedef enum {
    EVENT_START,
    EVENT_PAUSE,
    EVENT_RESUME,
    EVENT_STOP
} Event;

// Define transition table
typedef struct {
    State current;  // Current state
    Event event;    // Event
    State next;     // New state after event trigger
    void (*action)(void);
} Transition;

// State handling functions
void handleIdle(void) 
{
    printf("System is idle.\n");
}

void handleRunning(void) 
{
    printf("System is running.\n");
}

void handlePaused(void) 
{
    printf("System is paused.\n");
}

void handleStopped(void) 
{
    printf("System is stopped.\n");
}

// State transition function
void stateTransition(State* currentState, Event event) 
{
    // Define transition table
    static Transition transitions[] = {
        {STATE_IDLE, EVENT_START, STATE_RUNNING, handleRunning},
        {STATE_RUNNING, EVENT_PAUSE, STATE_PAUSED, handlePaused},
        {STATE_PAUSED, EVENT_RESUME, STATE_RUNNING, handleRunning},
        {STATE_RUNNING, EVENT_STOP, STATE_STOPPED, handleStopped},
        {STATE_PAUSED, EVENT_STOP, STATE_STOPPED, handleStopped}
    };

    int numTransitions = sizeof(transitions) / sizeof(transitions[0]);

    // Find and execute state transition
    for (int i = 0; i < numTransitions; i++) 
    {
        if (transitions[i].current == *currentState 
            && transitions[i].event == event) 
        {
            printf("Transition: %d -> %d\n", 
            *currentState, transitions[i].next);
            transitions[i].action();
            *currentState = transitions[i].next;
            break;
        }
    }
}

int main() 
{
    State currentState = STATE_IDLE;

    // Simulate state transition events
    stateTransition(&currentState, EVENT_START);
    stateTransition(&currentState, EVENT_PAUSE);
    stateTransition(&currentState, EVENT_RESUME);
    stateTransition(&currentState, EVENT_STOP);

    return 0;
}

Run Result:

Transition: 0 -> 1
System is running.
Transition: 1 -> 2
System is paused.
Transition: 2 -> 1
System is running.
Transition: 1 -> 3
System is stopped.

Leave a Comment

×