User Tools

Site Tools


coding_state_machines

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

coding_state_machines [2019/04/08 20:11] (current)
Line 1: Line 1:
 +===== How to Write Good Code for State Machines ======
 +
 +The Zyante text does a reasonable job explaining how to implement control as software-based state machines. However, it provides little guidance regarding a good implementation strategy. I tried several different strategies over the summer and have found one that works well. We will adopt it as part of the coding standard for state machines for this course. ​
 +
 +==== In the Tick Function: State Action First, State Update Last ====
 +
 +In the text, the authors implement the ''​tick()''​ function with two switch statements. We will still follow this approach. However, whereas the text implements the ''​tick()''​ method so that the state update happens first, followed by the state action, we will reverse this. Our ''​tick()''​ functions will perform the state-action first, followed by the state-update. The only implementation difference is that we place the switch-statement that performs the state-action first followed by the switch-statement that performs the state-update.
 +
 +Why do this? Because it allows you to have an "​init"​ state that can perform initializations. Say, for example, that you only stay in the "​init"​ state for a single tick (this is often the case). With the organization proposed in the text, you will never execute a state-action that is associated with the "​init"​ state because the first thing that happens in the tick() function is to update the state and then perform the state action. By the time that you reach the switch for the state-action,​ you are no longer in the "​init"​ state. By switching the order that state-update and state-action are performed, the state-action associated with the "​init"​ state will be performed before the state is updated. This may seem confusing for a minute but if you think carefully about it, it should begin to make sense.
 +
 +==== File Organization ====
 +Your state-machine will be completely contained in a single file. The name of this file will be given in the description for each of the labs. This single file will contain everything that the state-machine needs to implement control. Other non-control functions such as updating the display and so forth will be located in a different file. You will also have a single "​.h"​ file to advertise the tick function. The name that you must use with this "​.h"​ will be given to you in the lab description.
 +
 +==== All Variables Used By the tick() Function Have File Scope ====
 +All of the variables, such as currentState,​ for example, are global to the file containing the tick() function.
 +
 +==== You Will Set the Initial State of currentState in the Enum Declaration ====
 +You will initialize currentState in the enum declaration that is used to declare the states as an enumeration.
 +
 +==== Example ====
 +I will provide a snippet from my implementation of the clock lab so that you can see what I mean.
 +
 +<​code>​
 +// States for the controller state machine.
 +enum clockControl_st_t {
 + init_st, ​                // Start here, stay in this state for just one tick.
 + never_touched_st, ​       // Wait here until the first touch - clock is disabled until set.
 + waiting_for_touch_st, ​   // waiting for touch, clock is enabled and running.
 + ad_timer_running_st, ​    // waiting for the touch-controller ADC to settle.
 + auto_timer_running_st, ​  // waiting for the auto-update delay to expire ​
 +                                 // (user is holding down button for auto-inc/​dec)
 + rate_timer_running_st, ​  // waiting for the rate-timer to expire to know when to perform the auto inc/dec.
 + rate_timer_expired_st, ​  // when the rate-timer expires, perform the inc/dec function.
 + add_second_to_clock_st ​  // add a second to the clock time and reset the ms counter.
 +} currentState = init_st;
 +
 +void clockControl_tick() {
 +  // Perform state action first.
 +  switch(currentState) {
 +    case init_st:
 +      break;
 +    case never_touched_st:​
 +      break;
 +    case waiting_for_touch_st:​
 +      break;
 +    case ad_timer_running_st:​
 +      break;
 +    case auto_timer_running_st:​
 +      break;
 +    case rate_timer_running_st:​
 +      break;
 +    case rate_timer_expired_st:​
 +      break;
 +     ​default:​
 +      printf("​clockControl_tick state action: hit default\n\r"​);​
 +      break;
 +  }
 +
 +  // Perform state update next.
 +  switch(currentState) {
 +    case init_st:
 +      break;
 +    case never_touched_st:​
 +      break;
 +    case waiting_for_touch_st:​
 +      break;
 +    case ad_timer_running_st:​
 +      break;
 +    case auto_timer_running_st:​
 +      break;
 +    case rate_timer_running_st:​
 +      break;
 +    case rate_timer_expired_st:​
 +      break;
 +    case add_second_to_clock_st:​
 +      break;
 +    default:
 +      printf("​clockControl_tick state update: hit default\n\r"​);​
 +      break;
 +  }
 +}
 +</​code>​
 +
 +==== Requirements ====
 +You are required to follow the template shown above.
 +  - State-action before state-update.
 +  - You must declare a ''​currentState''​ variable and initialize it as part of the enum used to declare state enumerations. Use ''​currentState''​ as shown above.
 +  - The state names used in the ''​enum''​ must be the same names as those used in your state diagrams.
 +  - Each state machine will be placed in its own file.
 +  - Follow the ''​enum''​ approach shown above. Avoid the "​-1"​ nonsense used in the Zyante text. It is not portable and won't even compile with some compilers.
  
coding_state_machines.txt ยท Last modified: 2019/04/08 20:11 (external edit)