Microcontroller Programming Techniques – State Machine Programming

Follow and star the official account to reach exciting content directly.

Microcontroller Programming Techniques - State Machine Programming

Source: Play with Embedded Systems

Author: Alicedodo

Abstract: I wonder if everyone has this feeling, that you can play with microcontrollers and drive various functional modules, but when asked to write a complete set of code, there is no logic or framework, you just start writing! Copying and pasting here and there. This indicates that your programming is still at a relatively low level. So how can you improve your programming skills? Learning a good programming framework or a programming philosophy can be beneficial for a lifetime! For example, modular programming, framework programming, state machine programming, etc., are all good frameworks.

Today we will talk about state machine programming. Due to the length of the article, please enjoy it slowly. So what is a state machine? A state machine has five elements: State, Transition, Event, Action, and Condition.

Microcontroller Programming Techniques - State Machine Programming

What is a State Machine?

A state machine is something like this: a state machine has five elements: state, transition, event, action, and condition.

State: A stable working condition that a system exists at a certain moment; the system may have multiple states during its entire working cycle. For example, an electric motor has three states: forward, reverse, and stop.

A state machine needs to select one state from the set of states as the initial state.

Transition: The process of the system moving from one state to another is called a transition. Transitions do not happen automatically; they require external influence on the system. A stopped motor will not start by itself; it needs to be powered on.

Event: A significant occurrence at a certain moment for the system; the state machine transitions occur because events arise. For the motor, applying positive voltage, negative voltage, or cutting off power are events.

Action: During the transition of the state machine, the state machine will perform some other behaviors, which are actions; actions are the responses of the state machine to events. Applying positive voltage to a stopped motor will transition it from the stopped state to the forward state, and it will also start the motor, which can be seen as an action, a response to the power-on event.

Condition: The state machine does not respond to events unconditionally; even if an event occurs, the state machine must meet certain conditions to transition states. Taking the stopped motor as an example, even if the power is turned on, if there is a problem with the power supply line, the motor still cannot start.

Discussing concepts alone is too abstract; here is a small example: one microcontroller, one button, two LED lights (denoted as L1 and L2), and one person is enough!

Rules description:

1.L1L2 state transition sequenceOFF/OFF--->ON/OFF--->ON/ON--->OFF/ON--->OFF/OFF

2. Control the state ofL1L2 through the button; each state transition requires pressing the button continuously5 times

3.L1L2 initial stateOFF/OFF

Microcontroller Programming Techniques - State Machine Programming
Figure 1

The following code is written according to the functional requirements.

Program Listing List1:

void main(void)
{
 sys_init();
 led_off(LED1);
 led_off(LED2);
 g_stFSM.u8LedStat = LS_OFFOFF;
 g_stFSM.u8KeyCnt = 0;
 while(1)
 {
  if(test_key()==TRUE)
  {
   fsm_active();
  }
  else
  {
   ; /*idle code*/
  }
 }
}
void fsm_active(void)
{
 if(g_stFSM.u8KeyCnt > 3) /*Check if key press is 5 times*/
 {
  switch(g_stFSM.u8LedStat)
  {
   case LS_OFFOFF:
    led_on(LED1); /*Output action*/
    g_stFSM.u8KeyCnt = 0;
    g_stFSM.u8LedStat = LS_ONOFF; /*State transition*/
    break;
   case LS_ONOFF:
    led_on(LED2); /*Output action*/
    g_stFSM.u8KeyCnt = 0;
    g_stFSM.u8LedStat = LS_ONON; /*State transition*/
    break;
   case LS_ONON:
    led_off(LED1); /*Output action*/
    g_stFSM.u8KeyCnt = 0;
    g_stFSM.u8LedStat = LS_OFFON; /*State transition*/
    break;
   case LS_OFFON:
    led_off(LED2); /*Output action*/
    g_stFSM.u8KeyCnt = 0;
    g_stFSM.u8LedStat = LS_OFFOFF; /*State transition*/
    break;
   default: /*Illegal state*/
    led_off(LED1);
    led_off(LED2);
    g_stFSM.u8KeyCnt = 0;
    g_stFSM.u8LedStat = LS_OFFOFF; /*Restore initial state*/
    break;
  }
 }
 else
 {
  g_stFSM.u8KeyCnt++; /*State does not transition, only record key press count*/
 }
}
C

In fact, in state machine programming, the correct order should be to have the state transition diagram first, followed by the program; the program should be written based on the designed state diagram. However, considering that some students may find the code more approachable than the transition diagram, I placed the program first.

This state transition diagram is drawn using UML (Unified Modeling Language) syntax elements; the syntax is not very standard, but it is sufficient to explain the issue.

Microcontroller Programming Techniques - State Machine Programming
Figure 2 Button Control Flow Light State Transition Diagram

Rounded rectangles represent the various states of the state machine, with the state names labeled inside.

Arrows represent state transitions, starting from the initial state and ending at the next state.

The text in the figure explains the transitions, formatted as: event[condition]/action list (the last two items are optional).

The meaning of “event[condition]/action list” is: if an “event” occurs in a certain state, and the state machine meets the “[condition]”, then the state transition should occur, along with a series of “actions” to respond to the event. In this example, I used “KEY” to represent the key press event.

There is a solid black circle in the figure, indicating an unknown state that the state machine is in before it starts working; before running, the state machine must forcibly transition from this state to the initial state, which can have an action list (as shown in Figure 1), but does not require an event trigger.

There is also a circle containing a solid black dot, indicating the end of the state machine’s lifecycle; in this example, the state machine is everlasting, so there is no state pointing to that circle.

I won’t elaborate more on this state transition diagram; I believe everyone can easily understand it in conjunction with the code above. Now let’s talk about Program Listing List1.

First, let’s look at the functionfsm_active(); the statementg_stFSM.u8KeyCnt = 0; appears 5 times in theswitch-case; the first four times it appears as an action for each state transition. From the perspective of code simplification and efficiency improvement, we could combine these 5 occurrences into one before the switch-case statement, and the effect would be exactly the same. The reason the code is verbose is to clearly indicate all action details in each state transition, which is entirely consistent with the intent expressed in the state transition diagram in Figure 2.

Next, let’s look atg_stFSM, the state machine structure variable, which has two members:u8LedStat and u8KeyCnt. Using this structure to create a state machine seems a bit verbose; can we use just one integer variable like u8LedStat to create the state machine?

Of course! We can split these four states in Figure 2 into five smaller states, so using 20 states can also implement this state machine, and only one unsigned char variable is sufficient. Every key press will trigger a state transition, and after five transitions, the LED light state will change; from the outside, both methods will have the same effect.

Suppose I change the functional requirement, requiring 100 consecutive key presses to change the state ofL1L2. In this case, the second method would require4X100=400 states! And the functionfsm_active() would need to have400 cases; could such a program even be written?!

For the same functional change, if we implement the state machine usingg_stFSM, the functionfsm_active() would only need to changeif(g_stFSM.u8KeyCnt>3) toif(g_stFSM.u8KeyCnt > 98)!

g_stFSM‘s two members, u8LedStat can be seen as a qualitative change factor, equivalent to the main variable; u8KeyCnt can be seen as a quantitative change factor, equivalent to the auxiliary variable. The gradual accumulation of the quantitative change factor will trigger the change of the qualitative change factor.

A state machine like g_stFSM is called an Extended State Machine; I don’t know the official Chinese term in the industry, so I can only borrow the English phrase.

2. Advantages of State Machine Programming

Having discussed so much, you probably understand what a state machine is and how to write state machine programs. So what are the benefits of writing microcontroller programs using state machines?

(1) Improved CPU Utilization

Microcontroller Programming Techniques - State Machine Programming

Whenever I see programs filled withdelay_ms(), I feel a headache; having dozens ofms or even hundreds ofms of software delays is a huge waste of CPU resources, wasting preciousCPU cycles onNOP instructions. Programs that are idly waiting for a pin level change or serial data to arrive also make me very troubled; if the event never occurs, do you want to wait until the end of the world?

By making the program state machine-based, this situation can be significantly improved. The program only needs to use a global variable to record the working state and can turn to do other work. Of course, after finishing those tasks, it should check whether the working state has changed. As long as the target event (timer not reached, level not changed, serial data not fully received) has not occurred, the working state will not change, and the program will continue to repeat the “check – do other work – check – do other work” cycle, thusCPU will not be idle. In Program Listing List3, the content under theif{}else{} statement in theelse block (not added in the code, just indicated by a/*idle code*/ comment) is what I referred to as “other work”.

This approach essentially inserts some meaningful work into the program while waiting for events, so that theCPU is not idly waiting all the time.

(2) Logical Completeness

I believe logical completeness is the biggest advantage of state machine programming.

Have you ever written a simple calculator program in C? I wrote one a long time ago, and when I tested it, it was a disaster! When I entered a correct expression, the program could return the correct result, but if I deliberately input a random combination of numbers and operators, the program always produced inexplicable results.

Later, I tried to simulate the working process of the program in my mind; the thought process for a correct expression was clear and smooth, but when faced with an irregular expression, I got confused. So many flags, so many variables, changing back and forth, and I couldn’t analyze it anymore.

It wasn’t until much later that I learned about state machines and suddenly realized that the previous program had logical loopholes. If we treat this calculator program as a reactive system, then a number or operator can be seen as an event, and an expression is a set of event combinations. For a logically complete reactive system, regardless of the combination of events, the system can correctly handle the events, and its working state remains knowable and controllable. Conversely, if a system’s logical functions are incomplete, under certain specific event combinations, the system may enter an unknowable and uncontrollable state, contrary to the designer’s intent.

State machines can solve the problem of logical completeness.

A state machine is a design method centered around system states and using events as variables. It focuses on the characteristics of each state and the relationships of transitions between states. The transitions between states are precisely caused by events, so when studying a specific state, we will naturally consider how each event affects that state. Thus, every event that occurs in each state will be taken into account, leaving no logical loopholes.

This might sound too abstract, but practice makes perfect. One day, if you really need to design a logically complex program,

I guarantee you will say: Wow! State machines are really useful!

(3) Clear Program Structure

Programs written using state machines have a very clear structure.

Microcontroller Programming Techniques - State Machine Programming

One of the most painful things for programmers is reading code written by others. If the code is not very standardized and you don’t have a flowchart, reading the code can be dizzying, and you have to read through the program over and over again to vaguely understand the overall working process. Having a flowchart helps a bit, but if the program is large, the flowchart won’t be very detailed, and many details still need to be understood from the code.

In contrast, programs written using state machines are much better. With a standardUML state transition diagram and some concise text descriptions, all elements in the program are clear at a glance. What states are in the program, what events will occur, how the state machine responds, and which state it transitions to are all very clear, and many action details can even be found in the state transition diagram. It is not an exaggeration to say that with aUML state transition diagram, there is no need to write a program flowchart.

To borrow a slogan: You won’t know unless you use it!

Microcontroller Programming Techniques - State Machine Programming
Produced by Guozi Ge

Note: This article is a reprint from a PDF document by author Alicedodo, aimed at sharing technical knowledge. If there is any infringement, please contact for deletion!

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
Recommended Reading:
Embedded Programming Collection
Linux Learning Collection
C/C++ Programming Collection

Qt Advanced Learning Collection


Follow my official WeChat account and reply “Add Group” to join the technical exchange group according to the rules.

Click “Read Original” to see more shares.

Leave a Comment