A Function Pointer Based State Machine

Overview

This type of state machine involves:

  • An enumeration of all possible states, and one of all possible events
  • A state, event, function-to-be-called structure
  • A state transition matrix formed from this structure
  • A ‘getNextEvent’ function which returns an event
  • A state and event variable holding the current state and event that’s happened (they hold one of the values defined in the enumerations)
  • A simple main loop that does not need to be edited (all the control is changed by modifying the state transition matrix)
The advantages/disadvantages:
Advantages Disadvantages
The ‘flow’ of the program is really easy to understand Single event to function-to-call correlation, i.e. you cannot call a function only when two or more events have occurred.
The ‘flow’ is really easy to change It is not necessarily clear what the function-to-call does (i.e. does it change the state or keep in the same?)
Function calling is fast, as there is no switch/if statement to determine function to call (instead uses function pointers) Event determination can be slow when ‘if’ statements are used.
   
This type of state machine is explained and demo’d below with a simple light flashing circuit that is turned on and off with a button press.

The State And Event Enumerations

These enumerations hold values for every possible state and every possible event that your program will require. I have prefixed all states with ST_ and events with EV_ for readability. Sometimes, you could just use #define commands for every state and manually associate them with a number. But since the number doesn’t actually matter, why not use an enumeration? Enumerations also allow you to insert new states/events anywhere on the list, allowing to keep a visual clue of the flow between states/events (one after the other), which would get messy if you used the #define command.

The State/Event/Function-to-Call Structure

This defines the structure of the state transition matrix (which is just an array of this structure). This structure contains a state, and event, and a function pointer.

The State Transition Matrix

The state transition matrix is the heart of this state machine methodology. It specifies what function should be called when a particular event occurs when the state machine is in a particular state. It is built as an array of the state/event/function-to-call structure defined above.

The Current State And Event Variables

These variables hold the current state and recently occurred event, which trigger the state machine to act if they match a set define in the transition matrix. These are initiated outside of any function (so they a public to the c file that they are in). Here you can also specify an initial state to start in. The event doesn’t matter as much as this will be overwritten by whatever the GetEvent() function returns.

The Get Event Function

The function looks at flags and other variables and returns an event for the state machine as appropriate. Note that as mentioned in the disadvantages section, one of the limitations of this state machine methodology is that only one event may be returned at once. Hence if you wish to cause a state transition on the occurrence of two events, you have to work around this by declaring a new event which occurs when the other two events would have occurred.

The Main Loop

The main loop for this state machine is pretty generic and simple, and you don’t usually have to modify it. All of the logic is controlled by the state transition matrix above. Essentially, the main loop gets an event (lets call it the current event), and then runs through the state transition matrix row by row to find a pre-defined state and event pair that match the current state and event. If so, it runs the function pointed to by the function pointer in the same row.

The State Functions

The state functions are the functions which are called when the current state and event matches a pair in the state transition matrix. While the rest of the state machine controls the high-level flow, the state functions are the guts of the state machine and are the functions which actually DO THINGS. They return a state (of the states_t kind), that is, the state to go to once the function has been called. This could be the current state (leave it unchanged), or go to a new state. This is another disadvantage of this method, you cannot be 100% sure what state is going to be next just by looking at the state transition matrix (although if you name these functions well, it should be pretty obvious…).

  • Geoffrey Hunter

    Will fix the formatting issues!

  • Rob

    Great howto. What kind of hardware have you implemented on?

    • I have used this on both Atmel AVR’s and PSoC 3/5’s. However, I do prefer the RTOS approach (using threads to reduce the number of state machines, and then using switch statements to implement small ones inside those threads) over this for most projects.