Lecture 08 - Finite State Machines ---------------------------------- 10:10 Cyclic Executive Recap The cyclic executive is a template to process events in a way so it appears the processing is done concurrently for all events at the same time. while (1) { if (input_event_1_detected()) do_something_1(); if (input_event_2_detected()) do_something_2(); if (input_event_3_detected()) do_something_3(); ... } It works as long as the while (1) loop is fast enough so that no event will ever occur in real life that will not be picked up by a corresponding input_event_x_detected. The golden rule to achieve this, was that the cyclic executive should never stop scanning for events. If a microcontroller has 3 inputs (as in the example above), and all three of them can receive an event at any time, then the microcontroller must continuously call all 3 input_event_x_detected(). A corollary is that the do_something_x() calls should not take too long. The actions done in response to detected events have to be compact, since the microcontroller has no time to waste to check for the next event. As we go forward, we will refine this programmming principle and make it more sophisticated. However, the starting point is the simple while loop as shown above, with a sequence of if-then statements in it. We also discussed how a hardware timer can be introduced in the cyclic executive, such that it allows us to measure (wall clock) time precisely. int TimerExpired() { static unsigned int previousSnap; unsigned int currentSnap, ret; currentSnap = Timer32_getValue(TIMER32_0_BASE); ret = (currentSnap > previousSnap); previousSnap = currentSnap; return ret; } As with the cyclic executive, we will later discuss more sophisticated mechanisms for measuring time and dealing with real-time. 10:15 Event Sequences The current model of cyclic executive is very primitive. It can respond 'instantaneously' to an event when it is present. However, the model is inconvenient to expressq an event sequence. A sequence of events is a series of physical events for which you care about the ORDER of events. For example, assume you have a system with two buttons. An event sequence could be for example the following: "ButtonLeft is pressed and held, followed by ButtonRight" ButtonLeft ----------------+ | +---------------------- ButtonRight -----------------------+ | +--------------- <*> | EVENT SEQUENCE DETECTED Note that there are sequences that are not a valid implementation of the event sequence. For example ButtonLeft -------------------------+ | +------------ ButtonRight -----------------+ | +-------------------- In this case, even though both buttons are eventually pressed, the sequence is wrong. We first detect buttonRight, followed by buttonLeft. The challenge is to find a good method to capture this in C. Using the standard cyclic executive, this is not so easy, because a cyclic executive looks only at one event at a time, with no memory of the past. Everything is instantaneous. while (1) { if (buttonLeft_down()) do_something_1(); if (buttonRight_down()) do_something_2(); } Furthermore, we cannot just detect both buttons instantaneously, as follows: while (1) { if (buttonLeft_down() && buttonRight_down()) do_something() } The reason is that in this code, we don't know if button1 was pressed first or else button2. So clearly we need a more powerful programming mechanism. That programming mechanism is the FINITE STATE MACHINE. It is a very important element of the cycle-executive programming concept. 10:25 Finite State Machine What is a finite state machine? - A finite set of states {S0, S1, S2, ..., SN}, expressing the N states that a system can be in. - A set of inputs (I1, I2, I3, ..) - A set of outputs (O1, O2, O3, ..) - A set of state transitions, expression how a system can move from one state Si to another state Sj Si -> Sj In most interesting, useful cases, the transition from one state to another does not only depend on the current system state, but also on the input. (Si, I) -> Sj State transitions are also associated with specific outputs or actions (Si, I) -> (Sj, O) Let's do an example. Let's design a finite state machine that detects if two buttons are pressed in sequence. The two events (inputs) are: buttonleft_down and buttonright_down We create a system with three states: idle (initial state) and firstleft and thenright We have the following state transitions (captured in pseudocode, we'll do C in a moment) idle: if (buttonleft_down & !buttonright_down) goto firstleft else goto idle firstleft: if (buttonleft_down & buttonright_down) goto thenright else if (!buttonleft_down) goto idle else goto firstleft thenright: if (!buttonleft_down & !buttonright_down) goto idle else goto thenright In the initial state (idle), we specifically wait for a case where buttonleft goes down while buttonright is not down yet. This makes sure that buttonleft_down is the first step of the event sequence. In the firstleft state, we wait until we see both buttons being held down, which would mark the event sequence. However, when buttonleft is released while buttonright is not pressed yet, we have to return to idle. In the thenright state, we have detected a sequence of buttonleft pressed followed by buttonright pressed. Before we can 'handle' the next event sequence, we need to make sure that both buttons are released. We can capture this in a graphical notation. (Graph) 10:35 Finite State Machine in Software Let's write this FSM (Finite State Machine) in Software. It has minor differences in the final state, compared to the previous example (this FSM is taken from the lecture example). The FSM has two inputs. These two inputs are the boolean equivalent of buttons that are pressed. So, BL (below) is the same as buttonleft_down (above) and BR (below) is the same as buttonright_down (above) Furthermore, the FSM has one output: when it first enters the thenright state, is returns true, otherwise, the FSM returns false. bool TwoButtonFSMOn(bool BL, bool BR) { typedef enum {IDLE, FIRSTLEFT, THENRIGHT} tState; static tState state = IDLE; bool retval = false; switch (state) { case IDLE: if (BL & !BR) state = FIRSTLEFT; break; case FIRSTLEFT: if (BL & BR) state = THENRIGHT; else if (BL & !BR) state = FIRSTLEFT; else state = IDLE; break; case THENRIGHT: retval = true; state = IDLE; break; } return retval; } Important notes: - Note the definition of the State typedef enum {IDLE, FIRSTLEFT, THENRIGHT} tState; static tState state = IDLE; This FSM has three states. The states are defined in a typedef enum, which allows you to create the state as an enum type. The state variable itself is STATIC. This ensures that the finite state machine remembers the state between calls. - An FSM is a FUNCTION. Each call to the FSM makes one state transition. An FSM cannot block or wait for an event. It can modify its own state variable to remember the occurrence of a certain input, but it cannot block or wait. (ie. no while loops). This will ensure that we can use the FSM in a cyclic executive without breaking the cyclic-executive principle (= don't block so you don't loose events) - Note that the inputs and outputs of the FSM are all of type 'bool'. An FSM helps you with logic, and hence it processes logic. - Each FSM function can only be used for one purpose, since it contains a state variable. Thus, you cannot share calls to an FSM between different sets of buttons, for example. 10:40 Example The full program example is the following. - The colorLED will be turned on when a user presses the left button followed by the right button. - The colorLED will be turned off when a user presses the right button followed by the left button. Both FSM are nearly identical, and differ only in the sequence in which they process events. So there is a TwoButtonFSMOn and a TwoButtonFSMOff. Each of them processes two button variables, and each of them returns true when the event sequence is detected. Here is the main function: int main(void) { bool bpleft, bpright; WDT_A_hold(WDT_A_BASE); InitButtons(); InitLEDs(); while (1) { bpleft = ButtonLeftPressed(); bpright = ButtonRightPressed(); if (TwoButtonFSMOn(bpleft, bpright)) ColorLEDOn(); if (TwoButtonFSMOff(bpleft, bpright)) ColorLEDOff(); } } 10:45 Assignment "Design a ButtonFSM that returns true when ButtonLeft is pressed twice. The FSM output is used to toggle the ColorLED". States: ONE, (wait for first press down) ONEwait, (wait for first button release) TWO, (wait for second press down) TWOwait (wait for second button release) ONE: if (buttonLeft) goto ONEwait else goto ONE ONEwait: if (! buttonLeft) goto TWO else goto ONEwait TWO: if (buttonLeft) goto TWOwait else goto TWO TWOwait: if (! buttonLeft) goto ONE else goto TWOwait The FSM can return true, for example, the first time it enters TWOwait Draw a graph Full Program: bool ButtonFSM(bool B1) { typedef enum {ONE, ONEwait, TWO, TWOwait} tState; static tState state = ONE; bool retval = false; switch (state) { case ONE: if (B1) state = ONEwait; break; case ONEwait: if (!B1) state = TWO; break; case TWO: if (B1) { state = TWOwait; retval = true; } break; case TWOwait: if (!B1) { state = ONE; } break; } return retval; } int main(void) { bool bp; WDT_A_hold(WDT_A_BASE); InitButtons(); InitLEDs(); while (1) { bp = ButtonLeftPressed(); if (ButtonFSM(bp)) ColorLEDToggle(); } } 10:59 Conclusions - Principle of cyclic executive with a state machine