|
Implementing State Machines
with Examples in C |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
hardware.h
#ifndef HARDWARE_H
#define HARDWARE_H
// LED Settings.
typedef enum
{
LED_OFF,
LED_ON,
LED_NUMBER_OF_SETTINGS
} led_setting;
// Motor Settings.
typedef enum
{
MOTOR_CLOSING,
MOTOR_OFF,
MOTOR_OPENING,
MOTOR_NUMBER_OF_SETTINGS
} motor_setting;
// Events.
typedef enum
{
CLOSE_BUTTON_PRESSED,
CLOSED_DETECTED,
OPEN_BUTTON_PRESSED,
OPENED_DETECTED,
NUMBER_OF_EVENTS
} event;
// Control of the LED Indicators.
void
set_closed_indicator(led_setting value);
void
set_opened_indicator(led_setting value);
// Control of the Motor.
void
set_motor(motor_setting value);
#endif
Aside: Enumeration constants have the values \(0, 1, \ldots, n-1\)
which allows us to use the _NUMBER_OF_ idiom.
hardware.c
#include "hardware.h"
// The LED Indicators
void
set_closed_indicator(led_setting value)
{
// Details omitted
}
void
set_opened_indicator(led_setting value)
{
// Details omitted
}
// The Motor
void
set_motor(motor_setting value)
{
// Details omitted
}
Note: All of the functions and enumerations have file scope.
if statementsswitch statements (see below)switch Statementsstatemodel.h
#ifndef STATEMODEL_H
#define STATEMODEL_H
#include "hardware.h"
// The states
typedef enum
{
CLOSED,
CLOSING,
OPENED,
OPENING,
NUMBER_OF_STATES
} state;
// The event handler.
void
handle_event(event current_event);
void
setup_states();
#endif
switch Statements (cont.)statemodel.c
#include "statemodel.h"
// The current state (which is initially OPENED).
static state current_state = OPENED;
// The event handler.
void
handle_event(event current_event)
{
switch(current_state)
{
case CLOSED:
switch(current_event)
{
case OPEN_BUTTON_PRESSED:
set_closed_indicator(LED_OFF); // exit from CLOSED
current_state = OPENING; // transition to OPENING
set_motor(MOTOR_OPENING); // entry to OPENING
break;
}
break;
case CLOSING:
switch (current_event)
{
case CLOSED_DETECTED:
set_motor(MOTOR_OFF); // effect of CLOSED_DETECTED when CLOSING
current_state = CLOSED; // transition to CLOSED
set_closed_indicator(LED_ON); // entry to CLOSED
break;
case OPEN_BUTTON_PRESSED:
current_state = OPENING; // transition to OPENING
set_motor(MOTOR_OPENING); // entry to OPENING
break;
}
break;
case OPENED:
switch (current_event)
{
case CLOSE_BUTTON_PRESSED:
set_opened_indicator(LED_OFF); // exit from OPENED
current_state = CLOSING; // transition to CLOSING
set_motor(MOTOR_CLOSING); // entry to CLOSING
break;
}
break;
case OPENING:
switch (current_event)
{
case CLOSE_BUTTON_PRESSED:
current_state = CLOSING; // transition to CLOSING
set_motor(MOTOR_CLOSING); // entry to CLOSING
break;
case OPENED_DETECTED:
current_state = OPENED; // transition to OPENED
set_opened_indicator(LED_ON); // entry to OPENED
set_motor(MOTOR_OFF); // effect of OPENED_DETECTED when OPENING
break;
}
break;
}
}
Note: current_state has file scope and internal linkage
(because it is decalred to be static)
switch Statementsstruct
is usedstate.h
#ifndef STATE_H
#define STATE_H
// Add an alias: For a type (to the global name space)
// |-type -| |- alias -|
// |-tag-|
typedef struct state state;
// Add an alias: For the event handlers
// |-return type-| |- alias -||- params -|
typedef state* event_handler(void);
// Add an alias: For the actions
typedef void action(void);
// Define the identifier/tag state as a struct.
struct state {
event_handler* close_button_pressed;
event_handler* closed_detected;
event_handler* open_button_pressed;
event_handler* opened_detected;
action* entry_to;
action* exit_from;
};
// Declare variables to hold pointers to the default_event_handler and
// the default_action
extern event_handler* default_event_handler;
extern action* default_action;
// Declare a variable to hold the default_state
// |- type -| |- name -|
// |-tag-|
extern struct state default_state;
// Note: Because of the typedef for the alias state, this could also be:
// extern state default_state;
#endif
Note: default_event_handler, default_action
and default_state are extern because they
must be declared in the scope of each individual state but are defined
in states.c.
state.c
#include "state.h"
#include <stdlib.h> // For NULL
// Define all of the functions that are not exposed
// by the header file.
state*
return_null()
{
return NULL;
}
void
return_void()
{
}
// Define the default_event_handler and default_action
event_handler* default_event_handler = return_null;
action* default_action = return_void;
// Define the default_state
struct state default_state = {
return_null, // close_button_pressed
return_null, // closed_detected
return_null, // open_button_pressed
return_null, // opened_detected
return_void, // entry_to
return_void // exit_from
};
closed.h
#ifndef CLOSED_H #define CLOSED_H #include "state.h" // Declare all of the functions performed when in the closed state. static state* open_button_pressed(); static void entry_to(); static void exit_from(); // Define the closed state and initialize it to the default_state // Note: setup_closed() must be called to complete the definition. struct state closed; #endif
closed.c
#include "closed.h"
#include "hardware.h"
#include "statemodel.h" // For the other states
void
setup_closed()
{
closed = default_state;
closed.open_button_pressed = open_button_pressed;
closed.entry_to = entry_to;
closed.exit_from = exit_from;
}
static state*
open_button_pressed()
{
exit_from();
return &opening;
}
static void
entry_to()
{
set_closed_indicator(LED_ON);
}
static void
exit_from()
{
set_closed_indicator(LED_OFF);
}
statemodel.h
#ifndef STATEMODEL_H #define STATEMODEL_H #include "hardware.h" #include "state.h" // Declare all of the states used in the state model extern state closed; extern state closing; extern state opened; extern state opening; // Declare all of the state setup functions void setup_closed(); void setup_closing(); void setup_opened(); void setup_opening(); // Declare all of the functions used in the state model itself (i.e., // not in the individual states). void setup_states(); void handle_event(event current_event); #endif
Note: Each of the individual states are extern because,
though they are declared here, they are defined in the header files
for the individual states.
statemodel.c
#include "statemodel.h"
#include <stdlib.h> // For NULL
// Define the initial state.
static state* current_state = &opened;
void
setup_states()
{
setup_closed();
setup_closing();
setup_opened();
setup_opening();
}
// Define the functions.
void
handle_event(event current_event)
{
state* next_state;
next_state = NULL;
switch(current_event) // exit current_state and have the appropriate effect
{
case CLOSE_BUTTON_PRESSED:
next_state = current_state-%gt;close_button_pressed();
break;
case CLOSED_DETECTED:
next_state = current_state-%gt;closed_detected();
break;
case OPEN_BUTTON_PRESSED:
next_state = current_state-%gt;open_button_pressed();
break;
case OPENED_DETECTED:
next_state = current_state-%gt;opened_detected();
break;
}
if (next_state != NULL)
{
current_state = next_state; // Change states
current_state-%gt;entry_to(); // enter the new state
}
}
closed.h and
closed.c
switch
statements?