User Tools

Site Tools


coding_and_debugging_state_machines

Differences

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

Link to this comparison view

coding_and_debugging_state_machines [2014/11/10 21:21]
hutch
coding_and_debugging_state_machines [2014/11/10 21:24] (current)
hutch
Line 5: Line 5:
  
  
-===== 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. 
- 
-===== Debugging Software State Machines ===== 
-Debugging software-based state machines, especially those that are time based (referred to as synchronous in the text) can be difficult using a conventional debugger because stopping the program interferes with execution and can make it difficult to find the bug. Often it is helpful to have your state machines print out messages as they change state so you can watch state transitions occur in real-time. 
- 
-The snippet of code shown below would be placed in the same file as the clock state-machine shown above. Call this function at the top of the tick function. The function shown below will print out state changes as they occur. What makes this code particularly helpful is that it only prints out a message when currentState changes value. This eliminates a lot of extraneous printing that often hides what you are trying to see. It keeps track of the previous value of currentState by using a static variable. Try using this function, I suspect that you will find it to be quite useful. 
- 
-Place this function before the tick() function in the file to keep the compiler happy. Also make sure to ''​include <​stdio.h>''​. 
-<​code>​ 
-// This is a debug state print routine. It will print the names of the states each 
-// time tick() is called. It only prints states if they are different than the 
-// previous state. 
-void debugStatePrint() { 
-  static clockControl_st_t previousState;​ 
-  static bool firstPass = true; 
-  // Only print the message if: 
-  // 1. This the first pass and the value for previousState is unknown. 
-  // 2. previousState != currentState - this prevents reprinting the same state name over and over. 
-  if (previousState != currentState || firstPass) { 
-    firstPass = false; ​               // previousState will be defined, firstPass is false. 
-    previousState = currentState; ​    // keep track of the last state that you were in. 
-    printf("​msCounter:​%d\n\r",​ msCounter); 
-    switch(currentState) {            // This prints messages based upon the state that you were in. 
-      case init_st: 
-        printf("​init_st\n\r"​);​ 
-        break; 
-      case never_touched_st:​ 
-        printf("​never_touched_st\n\r"​);​ 
-        break; 
-      case waiting_for_touch_st:​ 
-        printf("​waiting_for_touch_st\n\r"​);​ 
-        break; 
-      case ad_timer_running_st:​ 
-        printf("​ad_timer_running_st\n\r"​);​ 
-        break; 
-      case auto_timer_running_st:​ 
-        printf("​auto_timer_running_st\n\r"​);​ 
-        break; 
-      case rate_timer_running_st:​ 
-        printf("​rate_timer_running_st\n\r"​);​ 
-        break; 
-      case rate_timer_expired_st:​ 
-        printf("​rate_timer_expired_st\n\r"​);​ 
-        break; 
-     } 
-  } 
-} 
-</​code>​ 
coding_and_debugging_state_machines.txt ยท Last modified: 2014/11/10 21:24 by hutch