State table implementation

In this implementation the state transition table is stored directly in the program memory and this is used to update the current state of the system in response to events. The columns of the table correspond to the states and the rows of the table correspond to the possible events. A disadvantage of this approach is that not all states will respond to all events so the table is often sparse and this wastes memory. However, the implementation is straight forward and it is easy to extend of modify the FSM. To implement the caps lock example we first define the


/* States. */
#define DEFAULT 0
#define SHIFTED 1

/* Events */
#define SWITCHPRESS 0

We then define the state transition table where the entry for each row (indexed by the event) and column (indexed by the current state) contains the next state.


char transtab[1][2] = /* transtab[Input][CurrentState] => NextState */
{
/* DEFAULT SHIFTED */
SHIFTED, DEFAULT /* SWITCHPRESS */
};

To specify the actions associated with the transitions it is also necessary to define an action table. Similarly to the state transition table the rows index the event and the columns index the current state, the entry at each location identifies the action function.


/* Actions */
#define DEFAULTACTION  0
#define SHIFTACTION         1

char actiontab[1][2] = /* transtab[Input][CurrentState] => NextState */
{
/* DEFAULT SHIFTED */
SHIFTACTION, DEFAULTACTION /* SWITCHPRESS */
};

The individual action functions are defined as before. The implementation of the state machine within the endless while loop has to carry out two operations the first ‘if’ conditional statement checks for an event and the second ‘switch’ statement implements the state dependent activity as defined by the ‘currentstate’ variable. Within the first ‘if’ conditional statement the ‘switch’ statement is used to select and implement the action based on the current state and the event. Then the state variable ‘currentstate’ is updated with the next state from the state transition table.


while(1)
 {
 if (ShiftPress())
 {
 switch (actiontab[inputevent][currentstate])
 {
 case DEFAULTACTION: DefaultAction();
 break;
 case SHIFTACTION: ShiftAction();
 break;
 }
 currentstate=transtab[inputevent][currentstate]; //Update state
 }

switch (currentstate)
 {
 case DEFAULT: DefaultActivity();
 break;
 case SHIFTED: ShiftActivity();
 break;
 }
 }

The complete code listing for the caps lock example is available below. This code makes use of the S1 switch on the Sparkfun Color LCD Shield as the caps lock key and will display any lower case strings that are sent over the USB link from a terminal program to the Arduino board on the LCD screen in either lower or upper case depending on the state. The string must be terminated with a carriage return (+CR). This code also requires the USART and Color_LCD_driver modules contained within a Utilities folder to be in the same folder as your AVR studio 5 project. These modules are available as a zip file here. You will also need to link to these files in the solution explorer.

/*
 * StickyShift.c
 *
 * Created: 16/12/2011 07:48:59
 * Author: Benn
 */
#define F_CPU 16000000UL /* 16 MHz Crystal Oscillator */
#define BAUDRATE 9600
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "....UtilitiesColor_LCD_driver.h"
#include "....UtilitiesUSART.h"

/* States. */
#define DEFAULT 0
#define SHIFTED 1

/* Events */
#define SWITCHPRESS 0

/* Actions */
#define DEFAULTACTION 0
#define SHIFTACTION 1

char transtab[1][2] = /* transtab[Input][CurrentState] => NextState */
{
/* DEFAULT SHIFTED */
SHIFTED, DEFAULT /* SWITCHPRESS */
};

char actiontab[1][2] = /* transtab[Input][CurrentState] => NextState */
{
/* DEFAULT SHIFTED */
SHIFTACTION, DEFAULTACTION /* SWITCHPRESS */
};

#define RELEASED 0
#define PRESSED 1

// Function Prototypes
char ShiftPress(void);
void DefaultActivity(void);
void ShiftActivity(void);
void DefaultAction(void);
void ShiftAction(void);
char debounce(char pressed);

// Note the use of the volatile keyword to ensure that the memory for these variables is reserved
volatile int i=0;
volatile char buffer[20];
volatile char StrRxFlag=0;

int main(void)
{
 LCDInit(); //Initialize the LCD
 LCDClear(BLUE);
 USART_interrupt_init();

 char currentstate = DEFAULT;
 char inputevent = SWITCHPRESS;
 while(1)
 {
 if (ShiftPress())
 {
 switch (actiontab[inputevent][currentstate])
 {
 case DEFAULTACTION: DefaultAction();
 break;
 case SHIFTACTION: ShiftAction();
 break;
 }
 currentstate=transtab[inputevent][currentstate]; //Update state
 }

switch (currentstate)
 {
 case DEFAULT: DefaultActivity();
 break;
 case SHIFTED: ShiftActivity();
 break;
 }
 }
}

void ShiftAction(void)
{
 LCDPutStr("Shifted state", 110, 15, LARGE, WHITE, BLUE);
}

void DefaultAction(void)
{
 LCDPutStr("Default state", 110, 15, LARGE, WHITE, BLUE);
}

void ShiftActivity(void)
{
 char ind=0;
 if (StrRxFlag)
 {
 while ( buffer[ind] != 0x00)
 {
 buffer[ind]=buffer[ind]-0x20;
 ind++;
 }
 LCDPutStr(buffer,80,5,SMALL,WHITE,BLUE); //Write to LCD Display
 StrRxFlag=0; // Reset String received flag
 }
}

void DefaultActivity(void)
{
 if (StrRxFlag)
 {
 LCDPutStr(buffer,80,5,SMALL,WHITE,BLUE); //Write to LCD Display
 StrRxFlag=0; // Reset String received flag
 }
}

char ShiftPress(void)
{ //Returns true when a button is pressed and then released. Includes software debounce
 static char press=RELEASED; // State variable (PRESSED | RELEASED)

switch (press)
 {
 case RELEASED:
 {
 if (debounce((PINS1 & (1<<S1)))) press=PRESSED; //Update state
 return 0;
 }
 break;
 case PRESSED:
 {
 if (debounce(!(PINS1 & (1<<S1))))
 {
 press=RELEASED; //Update state
 return 1; // Return true (Button pressed and released)
 }
 else return 0;
 }
 break;
 }
}

char debounce(char pressed)
{
 static char count=0; // Incremented each time the button is in the desired state

 if (pressed) count=0; //Reset count if switch is released
 else count++; //Increment count if switch is pressed
 _delay_us(100);
 if (count > 10)
 {
 count=0;
 return 1;
 }
 else return 0;
}

ISR(USART0_RX_vect)
{
 buffer[i]=UDR0; //Read USART data register
 if(buffer[i++]=='r') //check for carriage return terminator and increment buffer index
 {
 // if terminator detected
 StrRxFlag=1; //Set String received flag
 buffer[i-1]=0x00; //Set string terminator to 0x00
 i=0; //Reset buffer index
 }
}

The nice thing about the state table approach is that is readily extends to larger and more complex state machines. An example of using this approach to control the menu system of a stop watch is given here.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s