Difference between revisions of "ES101 - Lesson 11 : State Machine and Board IO"

From Embedded Systems Learning Academy
Jump to: navigation, search
 
(17 intermediate revisions by the same user not shown)
Line 3: Line 3:
  
 
== State Machine Design ==
 
== State Machine Design ==
State machine design can be easily constructed by considering <i>what</i> you want to do based on a certain system state. For example, you might want consider building a state machine for an obstacle-avoidance robot, which acts differently based on if it detects an obstacle or not. In other words, you might have a state to avoid an obstacle, and another state to follow a light-source if there is an obstacle in the way. This helps separate the functionality of what should happen when an obstacle is in the way, and what should happen if an obstacle is not in the way.
+
State machine design can be constructed by considering <i>what</i> you want to do based on a certain system state. For example, you might want consider building a state machine for an obstacle-avoidance robot, in which you have various states that dictates robot behavior such as :
 
+
*  No Obstacle --> Move forward
Each state should contain meaningful code that should be executed in that state. Furthermore, state transition code should be put in place that provides a way out of the current state to the next state/
+
*  Obstacle to the left --> Turn right
 +
*  Obstacle to the right --> Turn left
  
 
== ENUM ==
 
== ENUM ==
The <b>enum</b> provides a way to assign names to a sequence of integer values. For example, if a state machine contains two unique states, you should use an <font size = 3><code>enum</code></font> to declare the names, and the compiler guarantees that the names have a unique identifier. The alternative is to declare separate variables set to a different value, but this approach uses more unnecessary memory and is error-prone.
+
The <b>enum</b> provides a way to assign names to a sequence of integer values. For example, if a state machine contains two unique states, you should use an <font size = 3><code>enum</code></font> to declare the names. The alternative is to declare separate variables set to a different value, but this approach uses unnecessary memory and is error-prone.
  
<font size = 3>
 
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
// Auto-set the identifiers of the variables
+
// Each enumeration value is unique :
enum{stateSTART, stateEND, stateSENSOR};
+
typedef enum{start, end} myStateType;
 +
 
 +
// Manually set the enumeration values :
 +
typedef enum{start = 1, end = 2} myStateType;
 +
 
 +
// Another example of our own variable "myInt" :
 +
typedef enum { one=1, two=2 } myInt;
 +
 
 +
// We can now declare a variable whose type is "myInt"
 +
myInt my_int_var = one;
 +
my_int_var = two;
  
// Manually set the identifiers of the variables
 
enum{stateSTART = 1, stateEND = 2, stateSENSOR = 3};
 
 
</syntaxhighlight>
 
</syntaxhighlight>
</font size>
 
  
 
The enumerator variables can be automatically assigned a value, or the value can be manually chosen. In either case, unique values should be set for enumerator variables. An enumerator does not have to be used just for a state machine design, but it can be used anytime when you need to simply set unique identifiers to certain names.
 
The enumerator variables can be automatically assigned a value, or the value can be manually chosen. In either case, unique values should be set for enumerator variables. An enumerator does not have to be used just for a state machine design, but it can be used anytime when you need to simply set unique identifiers to certain names.
 +
 +
=== typedef ===
 +
The typedef stands for the type definition of variable.  The name that follows the ending '''}''' is the type of the variable that has the enumeration values.  Instead of <B><code>int state = start;</code></b>, we can instead use <B><code>myStateType state = start;</code></b>.  Essentially, you created your own variable type that can have the values defined inside the enumeration.  You can see in the example above that we created our own '''<code>myInt</code>''' that can have the values of '''<code>one</code>''' and '''<code>two</code>'''
  
 
==Switch Statement==
 
==Switch Statement==
 +
The C/C++ programming languages feature <b>switch/case</b> statements. This is similar to using a series of <b>if/else</b> statements. However, it is easier to read and write code using <b>switch/case</b> statements.
  
The C/C++ programming languages feature <b>switch/case</b> statements. This is similar to using a series of <b>if/else</b> statements. However, it is easier to read and write code using <b>switch/case</b> statements in some situations such as for a state machine design.
+
<BR/>
 
 
<BR>
 
<font size = 3>
 
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
// Test the "currentState" variable with one of the cases given below
 
// Test the "currentState" variable with one of the cases given below
 
switch(currentState)
 
switch(currentState)
 
{   
 
{   
   case stateSTART:
+
   case start:
       i = 0;
+
       // Your code goes here
      break;
 
  case stateEND:
 
      i = 0;
 
 
       break;
 
       break;
   case stateSENSOR:
+
   case end:
       i = 2;
+
       // Your code goes here
 
       break;
 
       break;
 
   default:
 
   default:
       i = 3;
+
       // Your code goes here
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
</font>
 
 
<BR>
 
<BR>
  
As seen by the previous figure, the <b>switch/case</b> statements look elegant, and are easier to read compared to <b>if/else</b> statements had they been used instead. The variable inside the round brackets <font size = 3><code>(currentState)</code></font> is what is being compared to the other cases. Each statement has a colon (:) at the end. Also, each case ends with a break. Forgetting to include the break will force the code to go to the next state without comparison. For example, if <font size = 3><code>currentState</code></font> was equal to <font size = 3><code>stateSTART</code></font> and there was no <font size = 3><code>break</code></font> statement at the end of the <font size = 3><code>stateSTART</code></font> case, the code would continue without <font size = 3><code>currentState</code></font> comparison with <font size = 3><code>stateEND</code></font> and execute the code inside the <font size = 3><code>stateEND</code></font> case as well. This may appear to be a programming flaw, however, the switch/case statements were designed this way and some complex designs omit the break statements on purpose.
+
Lets clarify the syntax of the switch/case code :
 +
*  Variable inside switch() is what is being compared
 +
*: This variable will be compared with each case.
 +
*  '''case''' is followed by a value you want to compare, followed by a colon.
 +
*  '''break''' is required at the end of every case
 +
*:    See the next section for more clarification
 +
*  Default case is like an '''else''' statement when neither cases equal the variable being compared.
 +
*:    See the '''Default case''' section below for details.
  
The <font size = 3><code>default</code></font> case should be the last case, it is for the case if the variable <font size = 3><code>currentState</code></font> does not match any of the case statements, and this case does not have the <font size = 3><code>break statement</code></font>. The default case should be used to indicate an error state or unexpected state fault.
+
=== Why break? ===
 +
When a case comparison succeeds, it requires the '''break''' statement to discontinue the code continuation.  Without the '''break''' statement, the code would otherwise continue to the next case '''without comparison'''.  This is an intentional design of the switch/case statement to handle more complex logic.
  
==Static Declaration of Variables==
+
=== Default case ===
 +
The <font size = 3><code>default</code></font> case should be the last case.  It is for the case if the variable <font size = 3><code>currentState</code></font> does not match any of the case statements.  This case doesn't need to have the '''break''' because there should be no more cases after the default case for the code to continue.  The default case should be used to indicate an error state or unexpected state fault.
  
So far, you have learned how to locally declare variables inside a function, but sometimes, you want to declare the local variables as static so their values are preserved across multiple calls to the same function.
+
==Assignment==  
 
+
Build the state machine given in the next figure. You will notice that the SENSOR state goes to IDLE state upon Button#1, and using the same button, the IDLE state transitions to SENSOR state. This may cause unintentional state change if you hold a button for too long. First, complete the assignment, then fix this runtime behavior using any solution you come up with.
<BR>
 
<font size = 3>
 
<syntaxhighlight lang="c">
 
void foo()
 
{
 
  int i = 0;
 
  i++;
 
  printf("Value of i = %i\n", i);
 
}
 
int main(void)
 
{
 
  foo(); // Prints: Value of i = 1
 
  foo(); // Prints: Value of i = 1
 
  foo();  // Prints: Value of i = 1
 
}
 
</syntaxhighlight>
 
</font>
 
<BR>
 
  
In the code above, whenever <font size = 3><code>foo()</code></font> is called, it declares a new variable called <font size = 3><code>i</code></font> and sets it to 0. Then it increments by 1 and prints it out. Whenever <font size = 3><code>main()</code></font> calls it, <font size = 3><code>foo()</code></font> does the same thing, and each time it sets <font size = 3><code>i</code></font> as 1 and when it exits, the variable <font size = 3><code>i</code></font> goes out of scope.
+
=== Program Behavior ===
 +
*  Whenever the state is changed, print on the screen what state the system is in.  The statement should only print once - not every time the state machine function is called.
 +
*  In the sensor state, print the your favorite sensor value.  Due to the 100ms delay, as long as you are in this state, you should see your sensor values 10 times a second.
  
<BR>
+
=== Hints ===
<font size = 3>
+
* The transition out of a state should be handled in the same state.  In other words, if button 1 takes you out of the '''start''' state, then write your <b><code>if(button1)</code></b> condition should be in this '''start''' state.
<syntaxhighlight lang="c">
+
*  Needless to mention, but whatever activity that occurs in a state should have its code underneath this state.
void foo()
 
{
 
  static int i = 0;
 
  i++;
 
  printf("Value of i = %i\n", i);
 
}
 
int main(void)
 
{
 
  foo(); // Prints: Value of i = 1
 
  foo();  // Prints: Value of i = 1
 
  foo();  // Prints: Value of i = 1
 
}
 
</syntaxhighlight>
 
</font>
 
<BR>
 
  
In the code above, the result is different because the variable <font size = 3><code>i</code></font> is declared as <font size = 3><code>static int</code></font>. It should be observed that the variable <font size = 3><code>i</code></font> is only declared once, and the function <font size = 3><code>foo()</code></font> preserves the value of <font size = 3><code>i</code></font> over repeated function calls. For this assignment, if your state machine is enclosed in a function called <font size = 3><code>stateMachine()</code></font>, the please decide which variables should preserve their value, and declare them as static. Declaring a static variable is a better programming practice than declaring a global variable which would be visible to the entire program.
+
[[File:stateMachine10.png|500px|center|State Machine Design]]
 
 
==Assignment==
 
Build the state machine given in the next figure. Enclose your state machine inside a function and call the function every 100ms from your <font size = 3><code>main()</code></font> function. You will notice that the SENSOR state goes to IDLE state upon Button#1, and using the same button, the IDLE state transitions to SENSOR state. First, answer the question in the "Questions" section of this document.
 
 
 
[[File:stateMachine10.png|600px|center|State Machine Design]]
 
 
 
Whenever the state is changed, print on the screen what state the system is in. The statement should only print once - not every time the state machine function is called.
 
  
 
==Sample Code==
 
==Sample Code==
 
<BR>
 
<BR>
<font size = 3>
 
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
void myStateMachine()
+
int main()
 
{
 
{
   // Use typedef to create a new variable type called States
+
   // Use typedef to create a new variable type called myStateType
   typedef enum {start, end} States; // Name your states inside { and }
+
   typedef enum {start, end} myStateType; // Name your states inside { and }
   static States currentState = start; // Initialize static variable of States
+
   myStateType currentState = start;     // Declare our state variable and initialize to start state
 +
 
 +
  while(1) {
 +
  delay_ms(100);
  
 
   switch(currentState)
 
   switch(currentState)
Line 125: Line 102:
 
         }
 
         }
 
         break;
 
         break;
 +
 
       case end:
 
       case end:
 
         // Check if button 0 is pressed in this state and transition out of this state
 
         // Check if button 0 is pressed in this state and transition out of this state
Line 133: Line 111:
 
         }
 
         }
 
         break;
 
         break;
 +
 
       default:
 
       default:
 
         printf("State machine ERROR!\n");
 
         printf("State machine ERROR!\n");
 
   }
 
   }
 +
 +
  } // end while()
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
</font>
 
 
<BR>
 
<BR>
  
 
==Questions==
 
==Questions==
 
# Describe the behavior you see if you press and hold Button#1 for longer than 100ms in the SENSOR state.
 
# Describe the behavior you see if you press and hold Button#1 for longer than 100ms in the SENSOR state.

Latest revision as of 22:57, 29 July 2013

Objective

The objective of this lab is to learn how to design a state machine.

State Machine Design

State machine design can be constructed by considering what you want to do based on a certain system state. For example, you might want consider building a state machine for an obstacle-avoidance robot, in which you have various states that dictates robot behavior such as :

  • No Obstacle --> Move forward
  • Obstacle to the left --> Turn right
  • Obstacle to the right --> Turn left

ENUM

The enum provides a way to assign names to a sequence of integer values. For example, if a state machine contains two unique states, you should use an enum to declare the names. The alternative is to declare separate variables set to a different value, but this approach uses unnecessary memory and is error-prone.

// Each enumeration value is unique :
typedef enum{start, end} myStateType;

// Manually set the enumeration values :
typedef enum{start = 1, end = 2} myStateType;

// Another example of our own variable "myInt" :
typedef enum { one=1, two=2 } myInt;

// We can now declare a variable whose type is "myInt"
myInt my_int_var = one;
my_int_var = two;

The enumerator variables can be automatically assigned a value, or the value can be manually chosen. In either case, unique values should be set for enumerator variables. An enumerator does not have to be used just for a state machine design, but it can be used anytime when you need to simply set unique identifiers to certain names.

typedef

The typedef stands for the type definition of variable. The name that follows the ending } is the type of the variable that has the enumeration values. Instead of int state = start;, we can instead use myStateType state = start;. Essentially, you created your own variable type that can have the values defined inside the enumeration. You can see in the example above that we created our own myInt that can have the values of one and two

Switch Statement

The C/C++ programming languages feature switch/case statements. This is similar to using a series of if/else statements. However, it is easier to read and write code using switch/case statements.


// Test the "currentState" variable with one of the cases given below
switch(currentState)
{  
   case start:
      // Your code goes here
      break;
   case end:
      // Your code goes here
      break;
   default:
      // Your code goes here
}


Lets clarify the syntax of the switch/case code :

  • Variable inside switch() is what is being compared
    This variable will be compared with each case.
  • case is followed by a value you want to compare, followed by a colon.
  • break is required at the end of every case
    See the next section for more clarification
  • Default case is like an else statement when neither cases equal the variable being compared.
    See the Default case section below for details.

Why break?

When a case comparison succeeds, it requires the break statement to discontinue the code continuation. Without the break statement, the code would otherwise continue to the next case without comparison. This is an intentional design of the switch/case statement to handle more complex logic.

Default case

The default case should be the last case. It is for the case if the variable currentState does not match any of the case statements. This case doesn't need to have the break because there should be no more cases after the default case for the code to continue. The default case should be used to indicate an error state or unexpected state fault.

Assignment

Build the state machine given in the next figure. You will notice that the SENSOR state goes to IDLE state upon Button#1, and using the same button, the IDLE state transitions to SENSOR state. This may cause unintentional state change if you hold a button for too long. First, complete the assignment, then fix this runtime behavior using any solution you come up with.

Program Behavior

  • Whenever the state is changed, print on the screen what state the system is in. The statement should only print once - not every time the state machine function is called.
  • In the sensor state, print the your favorite sensor value. Due to the 100ms delay, as long as you are in this state, you should see your sensor values 10 times a second.

Hints

  • The transition out of a state should be handled in the same state. In other words, if button 1 takes you out of the start state, then write your if(button1) condition should be in this start state.
  • Needless to mention, but whatever activity that occurs in a state should have its code underneath this state.
State Machine Design

Sample Code


int main()
{
   // Use typedef to create a new variable type called myStateType
   typedef enum {start, end} myStateType; // Name your states inside { and }
   myStateType currentState = start;      // Declare our state variable and initialize to start state

  while(1) {
   delay_ms(100);

   switch(currentState)
   {
      case start:
         // Check if button 0 is pressed in this state and transition out of this state
         if( /*button 0 is pressed */)
         {
            currentState = end;
            printf("Current state: END. Press Button 1 to go to START");
         }
         break;

      case end:
         // Check if button 0 is pressed in this state and transition out of this state
         if( /*button 1 is pressed */)
         {
            currentState = start;
            printf("Current state: START. Press Button 0 to go to END");
         }
         break;

       default:
         printf("State machine ERROR!\n");
   }

  } // end while()
}


Questions

  1. Describe the behavior you see if you press and hold Button#1 for longer than 100ms in the SENSOR state.