zeke

State Machine Example Code

26 posts in this topic

Hi Guys,

 

I've been thinking about the topic of state machines lately and I thought I'd try to whip up a posting on my approach to state machines.

 

I've written a state machine for my client but I can't post that without a lot of changes. So I'm going to convert it into a traffic light state machine example.

 

Hmmm... I just realized that I should talk about what a state machine is but I haven't got anything prepared yet. So, for now, I'll assume you know what it is.

 

Here's my definitions and declarations:

 

#include   // add header file for MSP430G2231 device.
#include 

#define VERSION(id, rev, tag) const char id##_version[] = "@(#)Id:" #id " Ver:" rev " Released by:" tag " Built:" __DATE__ " " __TIME__


#define INPUT         0
#define OUTPUT        1
#define TRUE          1
#define FALSE         0
#define HIGH          1
#define LOW           0
#define TOGGLE        2


#define nothing1     BIT0  // P1.0, 0x01, 0b0000 0001  
#define onGREENLED    BIT1  // P1.1, 0x02, 0b0000 1000
#define onREDLED      BIT2  // P1.2, 0x04, 0b0001 0000
#define nothing2      BIT3  // P1.3, 0x08, 0b0000 0010
#define nothing3     BIT4  // P1.4, 0x10, 0b0000 0100
#define onYELLOWLED   BIT5  // P1.5, 0x20, 0b0010 0000
#define nothing4  BIT6  // P1.6, 0x40, 0b0100 0000
#define nothing5     BIT7  // P1.7, 0x80, 0b1000 0000

#define TIMEBASE      16384 // This is selected ACLK/2, Production value = 16384
#define STARTTIMEOUT  5     // 5 seconds
#define OVERALLTIMEOUT 43200 // 12hr*60min*60sec = 43200 seconds


/*******************************************************************************
* Global Variables
* 
******************************************************************************/
unsigned char TimerTick;

 

 

 

So, here's my main function and interrupt routine. This will configure the msp430 to interrupt every TIMEBASE counts. In this case, the result is an interrupt every 0.25 seconds.

 

void main(void)
{  
 WDTCTL = WDTPW + WDTHOLD;   // Stop watchdog timer

 BCSCTL1 = CALBC1_1MHZ;
 DCOCTL  = CALDCO_1MHZ;

 BCSCTL3 |= XCAP_3; // Launchpad watch crystals need ~12.5 pF

 // Configure all IO's and their default states on the MSP430   
 InitMSP430();
 InitIOs();

 // Initialize the system tick count
 TimerTick = 0;

 // Initialize all state machines
 InitTimerStateMachine();
 InitTrafficLightStateMachine();


 VERSION(TrafficLightStateMachineExample, "Alpha1", "Zeke");

 _BIS_SR( GIE );   // Sooner or later, you're gonna have to Enter LPM3 w/ interrupt


 while(1)
 {
   // May have to add some servicing routines in here that aren't controlled by the 1 second timer
   // This is a place holder.
   // Decide on whether you're going to use an infinite loop or send the micro into LPM3 mode w/ Interrupts enabled.
 }

}

// Period of this interrupt is set in InitMSP430
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A(void)
{
 // Update the system tick count every interrupt
 TimerTick++;

 // Update these state machines every 1 second
 if( TimerTick % 2 )
 {
   UpdateTimerStateMachine();
   UpdateTrafficLightStateMachine();
 }

 // Update the output state of each control line every 0.5 seconds
 UpdateIOs();  
}

 

 

And here's my InitMSP430() function:

/*******************************************************************************
* InitMSP430()
* 
* Initializes the resources of the MSP430 for this program. 
* 
******************************************************************************/
void InitMSP430( void )
{
 // Configure these I/O pins to outputs
 pinModeP1( onGREENLED,   OUTPUT ); 
 pinModeP1( onREDLED,     OUTPUT );
 pinModeP1( onYELLOWLED,  OUTPUT );

 // Set these Outputs to default states
 digitalWriteP1( onGREENLED,   HIGH );
 digitalWriteP1( onREDLED,     HIGH );
 digitalWriteP1( onYELLOWLED , HIGH );

 // Configure the TimerA to run at a 1 second rate    
 CCTL0 = CCIE;             // CCR0 interrupt enabled
 CCR0  = TIMEBASE-1;       // 0.25 second timer operation
 TACTL = TASSEL_1 + MC_1;  // ACLK, upmode
}

 

So, these are the files that will lay the foundation for a well balanced state machine. In this example, I'm going to simulate a traffic light state machine. It seems to me that it is the example that every prof uses in control systems lab class.

 

I haven't posted up the routines that really matter yet. Namely, the UpdateStateMachine() functions. They are the gears in this transmission.

 

So, stay tuned for more. I hope you will like this.

gatesphere, jsolarski and RobG like this

Share this post


Link to post
Share on other sites

I have a state machine code for a pedestrian crossing traffic light simulator that I made. I'll post it here after cleaning it up - stay tuned :)

Share this post


Link to post
Share on other sites

I'm thinking that I should change my example code to something maybe a little simpler and possibly more useful. How about a countdown timer instead of a traffic controller?

 

After some time to think, I believe I should spend some time explaining my approach to state machines.

 

I should also point out that the method I propose is not the only way of creating a state machine. You may have a method that makes mine look quite silly. That being said, the method I present is one reliable way of driving an embedded state machine.

 

 

Generally speaking, this state machine example can be compared to a flywheel on a big old tractor. Maybe you have seen one? It stores kinetic energy to keep the crankshaft rotating in between 4-stroke cycles.

 

My example has a timer interrupt that fires regularly. That amount of time can be tuned for the needs of the overall system. If you wanted to check on other things like button presses or other inputs then maybe the interval would be shorter than one second. In this countdown timer example, the interval is one second.

 

Overall, the interrupt is the ticking of the clock. If a tick occurs then do something. In our case, we're going to update the tick counter, do some display updates then go back to waiting for the next tick.

Share this post


Link to post
Share on other sites

The first thing to do is design on paper. It's easier to fix your mistakes. Besides, you have to convince yourself that your approach is going to work.

 

First, characterize the state machine. Identify the:

  • requirements,
    input variables,
    start conditions,
    stop conditions,
    all states and,
    transition conditions

 

Requirements:

  • The device is a countdown timer. The time length is arbitrarily set at 10 seconds. When time is expired, turn on an LED to indicate timeout.

 

Input Variables:

  • The device will have a button. A button press will reset the countdown timer and begin the countdown.

 

Start Conditions:

  • A button press indicates a start signal.

 

Stop Conditions:

  • When the countdown reaches zero, the timer stops.

 

All States

  • OFF: Nothing is happening, no counting, micro isn't running yet
    RESET: No counting, all variables reset to default values
    IDLE: Not counting, variables not updating, waiting for a reset signal, LED OFF
    COUNTING: Counting variable decrementing, display updating
    FINISHED: Not counting, variables not updating, waiting for a reset signal, LED ON

 

Transition Conditions:

  • OFF -> RESET: Apply power. Micro should enter into RESET condition, reset all variables then signal a move to IDLE state
    RESET -> IDLE: Transition to this state once all variables are set to default conditions
    IDLE -> COUNTING: Transition to this state by pressing the Start Button
    COUNTING -> FINISHED: Transition to this state when the count variable has reached zero
    FINISHED -> RESET: Pressing the Start Button causes this transition

 

To the best of our abilities, this is what we know right now.

Share this post


Link to post
Share on other sites

Now, you should attempt to create a state diagram. Here is the state diagram for the above details.

 

This diagram will help sort out and refine the details of the state machine.

 

Use it as guidance on how to structure the C code switch statement.

post-955-135135503657_thumb.png

Share this post


Link to post
Share on other sites

Now is the time to analyze the paper design.

 

Ask yourself "Is everything here that I need?" or "Should I add more features or conditions?"

 

You could add in a pulsing LED to indicate counting activity. Will it be green or will the red LED pulse? It's up to you ultimately.

 

So what's next?

Share this post


Link to post
Share on other sites

The next thing is to figure out all the variables at play in the system.

 

What variables come from hardware?

Input:

  • S2 on P1.3

Output:

  • LED1 on P1.0
    LED2 on P1.6

 

What variables come from software?

Timer:

  • 0,
    60 seconds

Countdown Timer State Machine:

  • OFF,
    STARTING,
    RUNNING,
    COMPLETE

 

This process will reveal missing variables and possibly redundant variables.

Share this post


Link to post
Share on other sites

Now, list out what each variable is doing in each state.

 

OFF:

  • gTimerState = STOPPED
    LED1 = LEDOFF,
    LED2 = LEDOFF,
     
    if( ButtonS2 == FALSE)
    gCountdownTimerState = OFF
    else
    gCountdownTimerState = STARTING
    gTimerState = TICKING

 

STARTING:

  • gTimerState = TICKING
    LED1 = LEDON,
    LED2 = LEDOFF,
     
    if( getButtonState() == PRESSED )
    gCountdownTimerState = STARTING
    else
    if( getButtonState() == RELEASED )
    gCountdownTimerState = RUNNING

 

RUNNING:

  • gTimerState = TICKING
    LED1 = LEDOFF,
    LED2 = LEDON,
     
    if( getTimerCount() < TIMERTIMEOUT )
    gCountdownTimerState = RUNNING
    else
    gCountdownTimerState = COMPLETE

 

COMPLETE:

  • gTimerState = STOPPED
    LED1 = LEDON,
    LED2 = LEDOFF,
     
    if( getButtonPress() == RELEASED )
    gCountdownTimerState = COMPLETE
    else
    gCountdownTimerState = STARTING

 

And, as you can see, the C code naturally flows out of this analysis.

 

Now it's time to write up the code and see if it works.

 

By the way, is anyone reading this thread?

Should I continue?

Share this post


Link to post
Share on other sites

Zeke, a while back we had some exchanges in your MSP430 info thread. There you showed me a link to this thread on state machines and I think you invited me to continue discussion here. So here goes...

 

I have used the state machine approach to programming in several previous projects using giant switch statements in C or the equivalent in assembly language. (These projects were on AVR chips.)

 

I few weeks ago I finished going through Baugh's "MSP430 State Machine Programming." This was, in my opinion, a great book to work through. I learned a lot about C and many things like bit-banging a UART, etc. The exercises were done using an MSP430F2274 on an Olimex header board stuck in a breadboard. The one down-side to Baugh's approach for me (and maybe only me) is that the state machine structures were not easy to follow. It was easy to get lost in a maze of interconnected functions.

 

So I bought "Practical UML Statecharts in C/C++", 2nd. ed., by Miro Samek to learn about a more formal approach. This book is somewhat overwhelming, but written in a way to get you through many, many details. And the accompanying code includes a project for a small MSP430--at the very end of the book. In Chapter 3 Samek gives examples of generic approaches, the switch statement and the state transition table approaches. Before going further in the book I decided to try a programming exercise using the state transition table approach. This is it:

 

StateTableBlink2.zip

 

The program is written for an MSP430G2452 on my homebrew Launchpad/expansion board combo. It simply blinks three LEDs at different rates and duty cycles. (It could easily be modified for the G2211 on the Launchpad provided the Launchpad has the clock crystal installed and the third LED is eliminated from the code. One would also have to modify the #include statements for the processor header and the name of the TimerA Channel 0 ISR vector. I wrote it in C using the CCS complier, but it should be OK for the IAR compiler.)

 

Here is the key module:

//------------------------------------------------------------------------------
// Blink.c in project StateTableBlink2
//
// This program blinks three LEDs at different rates using a common state
// table framework.  The state transition table contains pointers to transition
// functions.  The table rows are states, the table columns are events.  
//
// The basic state table structure and functions are code copyrighted by Miro
// Samek in StateTable.c and StateTable.h.  The overall structure of the
// program follows the approach of Tom Baugh.
//
// When an event occurs, the transition function in the table for that event
// and the current state is executed via the StateTable_Dispatch function.
//
// The transition function performs the following:
//   - Check any transition guard conditions
//   - Execute any exit functions of the state being left
//   - Change the current state to the new state using the macro TRAN
//   - Execute any entry functions of the new state
//
// There are two states in this LED blinking state machine, LED_IsOn and
// LED_IsOff.  
//
// There is only one type of event in this state machine, a tick from a timer
// interrupt ISR.  Blink_ProcessEvents is called by main upon wake-up from
// this ISR.
//
// The transition functions in this state machine also implement a countdown
// timer for each state which determines how long that state is held. On
// initial entry the timer counter is set for the delay interval.  The counter
// is decremented if the state is active and a tick event occurs.  If the
// delay interval is not over no state transition occurs.  When the counter
// times out the guard condition is met and there is a transition to the new
// state.
//
// Alternately, the state delay timers could be decremented and the transition 
// guard conditions checked in Blink_ProcessEvents.  This simplifies the
// transition functions and gives somewhat faster execution since the
// StateTable_Dispatch function is only called on delay timeouts.  This
// approach requires Blink_ProcessEvents to directly access state machine
// variables, so could be considered less "clean."  
//
// Andrew Palm
// 2011.09.28
//------------------------------------------------------------------------------
#include 
#include 
#include "System.h"
#include "StateTable.h"
#include "Blink.h"

// Each tick approximately 3.9 ms with ACLK at 32768 Hz (256 ticks/sec)
#define LED1_IS_ON_TICKS   (TICKS_PER_SEC / 32)   // 1/3 Hz, short duty cycle
#define LED1_IS_OFF_TICKS  (95*(TICKS_PER_SEC / 32))

#define LED2_IS_ON_TICKS   (TICKS_PER_SEC / 2)   // 1 Hz, 50% duty cycle
#define LED2_IS_OFF_TICKS  (TICKS_PER_SEC / 2)

#define LED3_IS_ON_TICKS   (TICKS_PER_SEC / 32)  // 16 Hz, 25% duty cycle
#define LED3_IS_OFF_TICKS  (3*(TICKS_PER_SEC / 32))

//------------------------------------------------------------------------------
// Blink LED finite state machine
enum BlinkSignals
{                                      // Signals for the Blink FSM 
   TICK_SIG,

   MAX_SIG                            // Number of signals 
};

enum BlinkStates
{                                      // States for the Blink FSM 
   LED_IS_OFF_STATE,
   LED_IS_ON_STATE,

   MAX_STATE                          // Number of states 
};

typedef struct TickEvtTag
{
   Event super;                       // Derived from Event structure 
} TickEvt;

typedef struct BlinkTag
{                                      // The Blink FSM 
   StateTable super;                  // Derived from StateTable structure 
   uint16_t delay_ticks;              // Number of delay ticks remaining
   uint16_t led_is_on_ticks;          // Number of delay ticks for on state
   uint16_t led_is_off_ticks;         // Number of delay ticks for off state
   volatile uint8_t *led_port;        // Pointer to LED port
   uint8_t  led_bitmask;              // Bit mask for LED pin
   uint8_t  filler;
} Blink;

static Blink Blink_1;                  // Blink FSM for LED1
static Blink Blink_2;                  // Blink FSM for LED2
static Blink Blink_3;                  // Blink FSM for LED3

//------------------------------------------------------------------------------
// The "constructor"
void Blink_Ctor(Blink *me, 
               uint16_t led_is_on_ticks, uint16_t led_is_off_ticks,
               uint8_t  led_bitmask, volatile uint8_t *led_port); 

void Blink_Initial(Blink *me, Event const *e);         // Initial transition 
void Blink_LED_IsOff_TICK(Blink *me, Event const *e);  // Transition function 
void Blink_LED_IsOn_TICK(Blink *me, Event const *e);   // Transition function 

// State table for all Blink state machines (moved outside constructor)
static const Tran Blink_StateTable[MAX_STATE][MAX_SIG] = 
{
   { (Tran)&Blink_LED_IsOff_TICK },
   { (Tran)&Blink_LED_IsOn_TICK  }
};

//------------------------------------------------------------------------------
void Blink_Ctor(Blink *me, 
               uint16_t led_is_on_ticks, uint16_t led_is_off_ticks,
               uint8_t led_bitmask, volatile uint8_t *led_port)
{
   StateTable_Ctor(&me->super,
                   &Blink_StateTable[0][0], MAX_STATE, MAX_SIG,
                   (Tran)&Blink_Initial);   // Construct the superclass

   me->led_is_on_ticks = led_is_on_ticks;
   me->led_is_off_ticks = led_is_off_ticks;
   me->led_bitmask = led_bitmask;
   me->led_port = led_port;
}

//------------------------------------------------------------------------------
void Blink_Initial(Blink *me, Event const *e)
{
   (void)e;                               // Avoid unused parameter warning

   // Transistion to initial state
   TRAN(LED_IS_ON_STATE);
   // New state entry actions
   *(me->led_port) |= me->led_bitmask;    // Turn on LED
   me->delay_ticks = me->led_is_on_ticks; // Load delay timer
}

//------------------------------------------------------------------------------
void Blink_LED_IsOff_TICK(Blink *me, Event const *e)
{
   (void)e;                                   // Avoid unused parameter warning

// This state re-entry actions
   me->delay_ticks--;                         // Decrement timer counter

   if(0 == me->delay_ticks)                   // Transition guard condition
   {
       // Transistion to new state
       TRAN(LED_IS_ON_STATE);
       // New state entry actions
       *(me->led_port) |= me->led_bitmask;    // Turn on LED
       me->delay_ticks = me->led_is_on_ticks; // Reload delay timer
   }
}

//------------------------------------------------------------------------------
void Blink_LED_IsOn_TICK(Blink *me, Event const *e)
{
   (void)e;                                   // Avoid unused parameter warning

// This state re-entry actions
   me->delay_ticks--;                         // Decrement timer counter

   if(0 == me->delay_ticks)                   // Transition guard condition
   {
       // Transistion to new state
       TRAN(LED_IS_OFF_STATE);
       // New state entry actions
       *(me->led_port) &= ~(me->led_bitmask);   // Turn off LED
       me->delay_ticks = me->led_is_off_ticks;  // Reload delay timer
   }
}

//------------------------------------------------------------------------------
void Blink_InitializeHW(void)
{
   TACCR0 = TICK_DIVISOR;               // Prepare first interrupt
}

void Blink_InitializeApp(void)
{
   Blink_Ctor(&Blink_1, LED1_IS_ON_TICKS, LED1_IS_OFF_TICKS, LED1, &LEDOUTA); 
   StateTable_Init((StateTable *)&Blink_1);  // Make initial transition 

   Blink_Ctor(&Blink_2, LED2_IS_ON_TICKS, LED2_IS_OFF_TICKS, LED2, &LEDOUTA); 
   StateTable_Init((StateTable *)&Blink_2);  // Make initial transition 

   Blink_Ctor(&Blink_3, LED3_IS_ON_TICKS, LED3_IS_OFF_TICKS, LED3, &LEDOUTB); 
   StateTable_Init((StateTable *)&Blink_3);  // Make initial transition 
}

//------------------------------------------------------------------------------
void Blink_ProcessEvents(void)
// Called from main after wakeup
{
   static TickEvt tick_evt = { TICK_SIG };  // Single event type, a tick

   // Send tick event to each state machine
   StateTable_Dispatch((StateTable *)&Blink_1, (Event *)&tick_evt);        
   StateTable_Dispatch((StateTable *)&Blink_2, (Event *)&tick_evt);        
   StateTable_Dispatch((StateTable *)&Blink_3, (Event *)&tick_evt);        
}

//------------------------------------------------------------------------------
#pragma vector=TIMER0_A0_VECTOR
__interrupt void TimerA_CCR0_ISR(void)
// ISR to generate regular ticks
{
   TACCR0 += TICK_DIVISOR;            // Update for next interrupt
   __low_power_mode_off_on_exit();    // Wake the processor
}

 

The comments at the head of the Blink.c module briefly explain how the thing runs. There are only two states (on and off) and one event (tick from the ISR), but I jazzed things up a tad by making the state table structure generic for blinking an LED and setting the port and pin bit mask in the constructor. I also had the state transition functions implement the delay timers for the states. The generic state table structures and processing are in StateTable.c and StateTable.h (in the zip file with the rest of the modules). These are from Samek's book and are copyrighted.

 

Why this "simple" exercise? Well, in fact it took some head scratching to get a clear conceptual idea of what is going on here. In this approach the states themselves are represented as rows in Blink_StateTable. The functions pointed to in the table cells are the TRANSITION ACTIONS from one state to the other, not the states. This was a key insight for me. So any particular one of these functions contains code for transition guard conditions, current state exit functions, changing the current state to the new state, and the new state entry functions. This makes the primary model here a Mealy state machine (as opposed to a Moore).

 

In this particular case I have added code to implement a delay timer for each state within the transition functions, so when a tick event occurs but the delay is not completed, we can conceptually think of this as a loop-back transition to the current state. This requires the state table event dispatch function to be called for every ISR tick, but it is cleaner than having the event processor loop keep tabs of the timer counters internal to each LED state machine.

 

My next step is to develop state machines for push buttons, formalizing what Baugh did in his book. These will include normal presses and long presses for acceleration. I'll see how much the program size grows with these additions. By the way, I chose the G2452 because (a) I had some on hand, (B) they have a complete PORT2, and © they have plenty of code space for this messing around.

 

Cheers,

Andy

nuetron and bluehash like this

Share this post


Link to post
Share on other sites

*** Note: There is an upgraded version of this code in a following post. ***

 

This is a second state machine implementation based on material in Chapter 3 in Samek. Once again, it blinks three LEDs.

 

In this version there is no table of state transition functions. Instead there is one "state handler function" for each state. The state handler function contains a switch statement based on the Event/Signal received. There are special switch cases for state entry and exit functions, and these are automatically executed on a state transition by the state machine dispatch function. The state handler functions return status codes, but in this version one of these is actually used (the one for transitions). The others were intended, I believe, to be used in a tracing facility.

 

As for the previous implementation, the user adds state machine parameters or event parameters by derivation from base structures.

 

I have made a small change in the applicaton by replacing led_is_off_ticks with led_period_ticks. This was done in anticipation of a future implementation that uses pushbuttons to modify the led blink rates. I also increased the system tick period to about 15.9 ms, since this is small enough for led flashing.

 

Although this implementation uses a couple of funky macros and is somewhat more difficult to follow (especially the initialization steps), it is nice in that there is one state handler function for each state, and the entry and exit functions are clearly indicated. This makes for a better correspondence between code and the state diagram. Also, there is no storage needed for a state transition table which could get large and sparse in large systems.

 

On the down side, these handler functions might get long if there are many signal types to be checked in the switch statement, and this might be a problem when things must be accomplished within a single system tick. Fortunately, the switch statement in each handler only needs to include events/signals relevant to that state.

 

Cheers,

Andy N1KSN

 

StateMachineBlink.zip

 

The C code for the primary module:

 

//------------------------------------------------------------------------------
// Blink.c in project StateMachineBlink
//
// This program blinks three LEDs at different rates using state machine
// functions based on the QEP FSM processor found in Chapter 3 of "Practical
// UML Statecharts in C/C++" by Miro Samek.  (Copyright (C) 2002-2007 Miro
// Samek. All rights reserved.)
//   
// In this implementation there is no table of state transition functions.
// Instead there is one "state handler function" (SHF) for each state. Each SHF
// contains a switch statement with cases based on the event signals received.
// There are special cases for state entry and state exit functions, which are
// automatically invoked by the state machine dispatch function when a state
// transition occurs.  
//
// There are two states in this LED blinking state machine, LED_IsOn and
// LED_IsOff.  
//
// There is only one type of user event in this state machine, a tick from a
// timer interrupt ISR.  Blink_ProcessEvents is called by main upon wake-up
// from this ISR.
//
// The state handler functions in this state machine also implement a countdown
// timer which determines how long that state is held. On entry the timer 
// counter is set for the delay interval.  The counter is decremented if the
// state is active and a tick event occurs.  If the delay interval is not over
// no state transition occurs.  When the counter times out the guard condition
// is met and there is a transition to the new state.
//
// Andrew Palm
// 2011.09.30
//------------------------------------------------------------------------------
#include 
#include 
#include "System.h"
#include "StateMachine.h"
#include "Blink.h"

// Each tick approximately 15.6 ms with ACLK at 32768 Hz (256 ticks/sec)
#define LED1_PERIOD_TICKS  3*(TICKS_PER_SEC)
#define LED1_IS_ON_TICKS   (LED1_PERIOD_TICKS / 96) // 1/3 Hz, short duty cycle

#define LED2_PERIOD_TICKS  TICKS_PER_SEC
#define LED2_IS_ON_TICKS   (LED2_PERIOD_TICKS / 2)   // 1 Hz, 50% duty cycle

#define LED3_PERIOD_TICKS  (TICKS_PER_SEC / 8)
#define LED3_IS_ON_TICKS   (LED3_PERIOD_TICKS / 4)   // 8 Hz, 25% duty cycle

//------------------------------------------------------------------------------
// Blink LED finite state machine
enum BlinkSignals
{                                      // Signals for the Blink FSM 
   TICK_SIG = SM_USER_SIG
};

typedef struct TickEvtTag
{
   Event super;       // Derived from Event structure 
} TickEvt;             // For this application, no additional user parameters

typedef struct BlinkTag
{                                      // The Blink FSM 
   StateMachine super;                // Derived from StateTable structure 
   uint16_t delay_ticks;              // Number of delay ticks remaining
   uint16_t led_is_on_ticks;          // Number of delay ticks for on state
   uint16_t led_period_ticks;         // Number of ticks for flash period
   volatile uint8_t *led_port;        // Pointer to LED port
   uint8_t  led_bitmask;              // Bit mask for LED pin
   uint8_t  filler;
} Blink;

static Blink Blink_1;                  // Blink FSM for LED1
static Blink Blink_2;                  // Blink FSM for LED2
static Blink Blink_3;                  // Blink FSM for LED3

//------------------------------------------------------------------------------
// The "constructor" for Blink state machines
void Blink_Ctor(Blink *me, 
               uint16_t led_is_on_ticks, uint16_t led_period_ticks,
               uint8_t  led_bitmask, volatile uint8_t *led_port); 

static Status Blink_Initial(Blink *me, Event const *e);    // Initial trans 
static Status Blink_LED_IsOff(Blink *me, Event const *e);  // State handler fn 
static Status Blink_LED_IsOn(Blink *me, Event const *e);   // State handler fn 

//------------------------------------------------------------------------------
void Blink_Ctor(Blink *me, 
               uint16_t led_is_on_ticks, uint16_t led_period_ticks,
               uint8_t led_bitmask, volatile uint8_t *led_port)
{
   StateMachine_Ctor(&me->super, (SHF)&Blink_Initial); // Construct superclass
   // Set user parameters
   me->led_is_on_ticks = led_is_on_ticks;
   me->led_period_ticks = led_period_ticks;
   me->led_bitmask = led_bitmask;
   me->led_port = led_port;
}

//------------------------------------------------------------------------------
Status Blink_Initial(Blink *me, Event const *e)
{
   (void)e;                               // Avoid unused parameter warning

   // Insert any additional initialization actions here that are not state
   // entry actions or initializations done in the constructor.

return SM_TRAN(&Blink_LED_IsOn);       // Transition to initial state
}

//------------------------------------------------------------------------------
Status Blink_LED_IsOff(Blink *me, Event const *e)
{
   switch (e->sig)
{
       case TICK_SIG:
	{
           me->delay_ticks--;         // Decrement timer counter
           if(0 == me->delay_ticks)   // Transition guard condition
           {
               return SM_TRAN(Blink_LED_IsOn);  // Transistion to new state	
		}
		return SM_HANDLED();
	}
       // Insert any other user signal type cases here
	case SM_ENTRY_SIG:
	{
           *(me->led_port) &= ~(me->led_bitmask);   // Turn off LED
           // Reload delay timer
           me->delay_ticks = me->led_period_ticks - me->led_is_on_ticks;
		return SM_HANDLED(); 
	}
	case SM_EXIT_SIG:
	{
	    return SM_HANDLED();
	}
   }
   return SM_IGNORED();
}

//------------------------------------------------------------------------------
Status Blink_LED_IsOn(Blink *me, Event const *e)
{
   switch (e->sig)
{
       case TICK_SIG:
	{
           me->delay_ticks--;         // Decrement timer counter
           if(0 == me->delay_ticks)   // Transition guard condition
           {
               return SM_TRAN(Blink_LED_IsOff); // Transistion to new state	
		}
		return SM_HANDLED();
	}
       // Insert any other user signal type cases here
	case SM_ENTRY_SIG:
	{
           *(me->led_port) |= me->led_bitmask;    // Turn on LED
           me->delay_ticks = me->led_is_on_ticks; // Reload delay timer
		return SM_HANDLED();
	}
	case SM_EXIT_SIG:
	{
	    return SM_HANDLED();
	}
   }
   return SM_IGNORED();
}

//------------------------------------------------------------------------------
void Blink_InitializeHW(void)
{
   TACCR0 = TICK_DIVISOR;               // Prepare first interrupt
}

void Blink_InitializeApp(void)
{
   Blink_Ctor(&Blink_1, LED1_IS_ON_TICKS, LED1_PERIOD_TICKS, LED1, &LEDOUTA); 
   // Make initial transition
   StateMachine_Init((StateMachine *)&Blink_1, (Event *)0); 

   Blink_Ctor(&Blink_2, LED2_IS_ON_TICKS, LED2_PERIOD_TICKS, LED2, &LEDOUTA); 
   // Make initial transition
   StateMachine_Init((StateMachine *)&Blink_2, (Event *)0);   

   Blink_Ctor(&Blink_3, LED3_IS_ON_TICKS, LED3_PERIOD_TICKS, LED3, &LEDOUTB); 
   // Make initial transition
   StateMachine_Init((StateMachine *)&Blink_3, (Event *)0); 
}

//------------------------------------------------------------------------------
void Blink_ProcessEvents(void)
// Called from main after wakeup
{
   static TickEvt tick_evt = { TICK_SIG };  // Single event type, a tick

   // Send tick event to each state machine
   StateMachine_Dispatch((StateMachine *)&Blink_1, (Event *)&tick_evt);        
   StateMachine_Dispatch((StateMachine *)&Blink_2, (Event *)&tick_evt);        
   StateMachine_Dispatch((StateMachine *)&Blink_3, (Event *)&tick_evt);        
}

//------------------------------------------------------------------------------
#pragma vector=TIMER0_A0_VECTOR
__interrupt void TimerA_CCR0_ISR(void)
// ISR to generate regular ticks
{
   TACCR0 += TICK_DIVISOR;            // Update for next interrupt
   __low_power_mode_off_on_exit();    // Wake the processor
}

gordon likes this

Share this post


Link to post
Share on other sites

*** Note: There is an upgraded version of this code in a post below in this thread. ***

 

This is the final installment in this series of demo programs for using a state machine approach. This version continues to blink three LEDs, but I've added three pushbuttons to the setup. Each LED and each pushbutton is managed by a separate state machine. Both types of state machine use the same base functions and structures found in StateMachine.h and StateMachine.c that were used in the last version.

 

I've set things up so that each LED blinks with a period of one second, but the duty cycle can be modified either up or down using the pushbuttons as follows:

 

PB1 increases the LED duty cycle by a factor of 2 for each push, with a maximum of 50%.

PB2 decreases the LED duty cycle by a factor of 1/2 for each push, with a minimum of 3.125%.

PB3 selects which LED is active for modification by PB1 and PB2 by rotating through them, 1, 2, 3, 1, 2, 3, ....

 

PB1 and PB2 have an acceleration function. If pushed for 1 second they slew though their associated function 4 times a second. To keep things relatively simple in this demo, there is no user indication of which LED is active.

 

The pushbutton module is not hard-wired to the LED blink module. The LED blink module must "register" an "event sink" function with each pushbutton it wishes to use. It also specifies if the acceleration feature is wanted. This allows the pushbutton module to be used by more than one client module. Pushbutton functionality can be changed on the fly.

 

This approach to pushbutton management is found in Tom Baugh's book on state machine programming. The overall organization of the program also comes from his book, although I have made no efforts to minimize current use other than put the program into LPM3 between ticks. (Baugh is a master of low current design and I highly recommend his book.)

 

However, the state machine implementation is more formalized than Baugh's approach. The code in StateMachine.c and StateMachine.h is modified from Miro Samek's book "Practical UML Statecharts in C/C++" so I've included GNU Public License Version 2 notices in those files. Any software derived from this code must retain these notices, or at least that is my understanding of the legal mumbo-jumbo.

 

In this version the system ticks are now provided by the watchdog timer instead of Channel 0 on TimerA, and the system tick interval is set to about 16 ms (corresponding to a tick divisor of 512 when using a 32768 Hz clock crystal). The state machine timers have been moved out of the state handler functions and now reside in the timer event processor functions. Thus the state handler functions are no longer called at each system tick, but only upon timeout events. The timers for the LEDs run continuously, but the timers for the pushbuttons do not, so a timer_active flag was added for them. Also, the pushbutton state machine uses the timer for both debouncing and timing event sink execution in acceration mode.

 

Another difference between the LED and PB state machines is that a port pin interrupt for a pushbutton generates an event from the port pin ISR. Thus, events for the state machine are generated in more than one place.

 

The full code plus selected comments are shown below. With the changes made to this version, one should be able to run it on the MSP430G2211 or similar chips by changing only the device header include statements. I have not tested compilation under IAR Kickstart, but I see no reason for this to be a problem, as only standard intrinsic functions are used.

 

Wrapup: Using state machines is an alternative to using a real time operating system to manage separate execution threads in a program. These demo programs illustrate the basic use of state machines to independently blink three LEDs and manage pushbutton functions. (If code size or current draw are big issues the "naked" state machine approach of Baugh is preferable, perhaps at the cost of some code readability.) Learning some of this material has also increased my knowledge of the C language, especially when it comes to structures and function pointers. Note that these state machines are non-hierarchical. Greater flexibility would be obtained using hierarchical state machines, but these remain a future study topic for me.

 

I hope some of you will find these programs as interesting as I did.

 

Andy

 

StateMachineBlink2.zip

 

//------------------------------------------------------------------------------
// main.c in project StateMachineBlink2
//
// The overall structure of this program follows the approach of Tom Baugh
// found in "MSP430 State Machine Programming."
// 
// This program was written to explore the use of non-hierarchical state
// machines as a way of organizing a program.  The state machine structures
// and functions used here are based on the QEP FSM processor found in Chapter
// 3 of "Practical UML Statecharts in C/C++" by Miro Samek.  
// 
// The modules in this program:
//
//   Main:  General shell program for initialization and execution of the
//          system tick loop.  Program sleeps in LPM3 when not executing a
//          tick.  This version uses the watchdog timer to generate system
//          ticks, leaving TimerA free for other modules.
//
//   System:  Does general low-level hardware initialization and contains
//            the WDT ISR.
//
//   StateMachine:  Contains base state machine and event structures and
//                  performs base state machine functions.  This code is
//                  modified from code copyrighted by Miro Samek under the
//                  GNU Public License Version 2.  This code is free for any
//                  use or distribution provided that the copywrite notice
//                  at the beginning of the module files is retained.
//
//   Blink:  Sets up and runs state machines that blink three LEDs.
//
//   PushButton:  Sets up and runs state machines that control pushbutton
//                functions, including debouncing and an optional acceleration
//                feature.  The functions actually executed on a push are
//                determined by "event sink registration functions" called by
//                the client, which in this case is the Blink module.
//  
// See the individual module headers for further descriptions.
//
// This program is written for the MSP430G2452, but should be easily changed
// to the MSP430G2211 or similar with 2K or more of flash memory by modifying
// the device header file include statements.  Note that a clock crystal of
// 32768 Hz is used to allow LPM3 sleep between ticks.  Also, this program was
// compiled and tested using CCS Workbench, with the compiler set to level 4
// optimiazation and minimum size.  It should compile under IAR Kickstart with
// no modifications.
//
// Andrew Palm
// 2011.10.04
//------------------------------------------------------------------------------

 

//------------------------------------------------------------------------------
// Blink.c in project StateMachineBlink2
//
// This module blinks three LEDs at different duty cycles using state machines.
//
// This version adds three pushbuttons with the following functions:
//    PB1:  Increases duty cycle of the active LED
//    PB2:  Decreases duty cycle of the active LED
//    PB3:  Selects the active LED.  Rotates through the three LEDs in the
//          order 1, 2, 3, 1, 2, 3,.... 
//
// To simplify this demo, all three LEDs have the same period of one
// second--only the duty cycles can be changed by the pushbuttons.  And there
// is no indication of which LED is currently active based on PB3 pushes.
//
// PB1 and PB2 are set up to have an acceleration feature.  Holding the button
// down for one second causes the assocated action to occur four times a
// second.  This feature is selectable using the event sink registration
// function for each pushbutton.
//
// There is one "state handler function" (SHF) for each state. Each SHF
// contains a switch statement with cases based on the event signals received.
// There are special cases for state entry and state exit functions, which are
// automatically invoked by the state machine dispatch function when a state
// transition occurs.  
//
// There are two states in this LED blinking state machine, LED_IsOn and
// LED_IsOff.  
//
// There is only one type of user event in this state machine, a delay timer
// timeout event.  Blink_ProcessEvents is called by main upon wake-up
// from a timer interrupt, decrements the timer counters, and issues timeout
// events at the end of the delays.  
//
// The state handler functions in this state machine set up these timers
// which determine how long a state is held. On entry the time counter is
// set for the delay interval.  When the counter times out there is a 
// transition to the new state.
//
// Andrew Palm
// 2011.10.04
//------------------------------------------------------------------------------

 

//------------------------------------------------------------------------------
// PushButton.c in project StateMachineBlink2
//
// This module uses state machines to debounce three pushbuttons.  The state
// machines use the same machinery as the module Blink.c.  Events are sent to
// the state machine dispatch function by either the timer tick generator or
// the port ISR.  The port ISR is used to detect the initial push.  The timer
// is used for both debouncing and the accelerated mode interval.
//
// The function of each pushbutton is controlled by the client application
// Blink.c, which upon initialization "registers" with each pushbutton the
// address of a function to execute when a push is confirmed.  This function
// is called an "event sink."  This method is based on the approach found in
// "MSP430 State Machine Programming" by Tom Baugh.
//
// The pushbutton state machine has four states:
//   - IsIdle:  Wait for a push detected by port pin ISR
//   - PushDetected:  Wait for the debounce delay, check pin status, execute
//                    registered event sink if button still pushed
//   - PushConfirmed:  Wait for button release or extended push.  If released,
//                     return to idle.  If extended push, enter accelerated
//                     mode.
//   - Accelerated:  Execute event sink at specified interval until release.
//
// The client module sets whether acceleration is used by setting the
// accel_mode variable to FALSE in the registration call for that button.
// In this demonstration program, the pushbuttons are set up with PB1 and PB2
// accelerated and PB3 not accelerated.
//
// See Blink.c for the PB functions in this demo.
//
// Andrew Palm
// 2011.10.04
//------------------------------------------------------------------------------

Share this post


Link to post
Share on other sites

Well, I thought I was done with this thread for a while, but after posting the last demo program some improvements came to mind. First, I added an alternate function capability to the pushbuttons. This means that any pushbutton can be set up to be:

 

NORMAL: One function, activated on push and release.

ALT_FN: Two functions, one activated on a short push and release, the second activated on a long (1 sec) push and release.

ACCEL: One function, activated once on a short push and release, activated repeatedly during a long push.

OFF: Does nothing.

 

I also cleaned up the state handler functions, especially how the pushbutton interval timers are turned on and off.

 

So let's have a little wrap-up now. Why did I do all this? I've been looking for a general approach to micro software organization that could have multiple threads of execution but not require a real time operating system (RTOS). The only RTOS I've ever been able (or willing) to get working was 4AVROS for the Atmel AVRs. (Nice program, by the way.) But for the small chips I usually work with an RTOS is overkill, and there can be a stiff learning curve and overhead when using them. On the other hand, for moderately complicated projects a round-robin or nested switch statement approach gets messy and difficult to debug or modify.

 

At this time it appears to me that Baugh's approach is the way to go. One can have multiple threads without much, if any, overhead. I pulled in the flat state machine routines of Samek in these demos to make it easier to set up and follow the state machine operations, but by doing this one looses the minimal current draw found in Baugh's programs. So my strategy for future projects will be to use the structure found in these demos unless the micro's current draw is an issue (for example if the project is to operate for long periods on a coin cell).

 

A side benefit to this work has been the deepening of my C knowledge from reading Baugh and Samek. Although Baugh's book is for C, he looks at the assembly code generated by the C code to determine how best to write things. In other words, know your compiler and what it actually does. Baugh and Samek also make liberal use of structures, and before reading them I didn't appreciate just how powerful a tool they can be. Samek shows how to organize and use them as a substitute for C++ classes. For example, each pushbutton state machine is represented by an instance of a structure derived from a base state machine structure, like class inheritance. By doing this in C you get the benefits of some object orientation without the overhead of C++.

 

Cheers,

Andy

 

(Revised 10/13/2011)

StateMachineBlink3.zip

 

Here are two UML (unified modeling language) statechart diagrams of the state machines in the code. The rounded boxes are the states. They show the entry and exit actions, plus any additional "Do" actions. The diamonds represent pseudostates for decisions. The "guard" conditions controlling these these are shown in brackets. The arrows leaving states are labelled with the signal that causes the transition.

 

I drew these diagrams with this cool little free program called "UMLet" that I found on the web. It is easy to use and produces decent diagrams for statecharts and other UML tools. You might want to check it out.

 

post-4636-135135517143_thumb.jpg

 

post-4636-13513551715_thumb.jpg

Share this post


Link to post
Share on other sites

Hey Zeke, great post.

 

However, I am curious as to why you implemented your state machine in this manner. As opposed to say using a bit-field -> setting bit-flags via the interrupt routine -> then reacting / branching based on the bit-field from within the main loop. -> clearing said bit-field once an action has completed based on that bit-flag. Thus keeping the ISR callback time to a minimum.

 

 

Just curious, and hoping to learn.

Share this post


Link to post
Share on other sites

Awesome Stuff Zeke,

 

Did you ever get around to using the QM modeling software?

 

I just got the Miro Samek book and am starting to go through it. I have been working on larger and larger projects and my nested states are getting a little over whelming. 

 

When I start using the QM tool I will post here my results.

Share this post


Link to post
Share on other sites
A side benefit to this work has been the deepening of my C knowledge from reading Baugh and Samek. Although Baugh's book is for C, he looks at the assembly code generated by the C code to determine how best to write things. In other words, know your compiler and what it actually does. Baugh and Samek also make liberal use of structures, and before reading them I didn't appreciate just how powerful a tool they can be. Samek shows how to organize and use them as a substitute for C++ classes. For example, each pushbutton state machine is represented by an instance of a structure derived from a base state machine structure, like class inheritance. By doing this in C you get the benefits of some object orientation without the overhead of C++.

 

When I first started to learn Fortran, I was always reminded to leave the complexity in the structures. Now after using OOP lanuages (C#) I have a much better understanding of this. 

 

Its pretty cool the stuff you can do with data structures to simplify design.

Share this post


Link to post
Share on other sites

When I first started to learn Fortran, I was always reminded to leave the complexity in the structures. Now after using OOP lanuages (C#) I have a much better understanding of this. 

 

Its pretty cool the stuff you can do with data structures to simplify design.

 

Design patterns and data structures is an area where I personally have been needing.to work on. So now I am having fun learning, while applying OOP techniques to a language ( C ) I have not used much in quite a while. Finite state machines seem to help me achieve the goal of event driven programming. Where as a language such as C# would / may just hand you delegates and event handlers.

 

So in effect what I am saying is that while languages like C# may have made me aware of event driven development. It took a language like C to make me want to learn more about the how's, and why's.

 

In retrospect, I find it rather ironic that when i first started programming I started with quick basic and quickly / briefly moved onto ASM, and then C. After that I had trouble letting go of procedural programming ideas when moving on to VB.NET / C#. Now moving in reverse back into the land of procedural languages, I do not want to let go of OOP concepts..Which I think this time is a good thing.

 

Anyway, I look forward to talking about and contributing some ideas in this thread. But for now, I am still trying to wrap my brain around some of ( most of ? lol ) the hardware.

Share this post


Link to post
Share on other sites

Hey Zeke, great post.

 

However, I am curious as to why you implemented your state machine in this manner. As opposed to say using a bit-field -> setting bit-flags via the interrupt routine -> then reacting / branching based on the bit-field from within the main loop. -> clearing said bit-field once an action has completed based on that bit-flag. Thus keeping the ISR callback time to a minimum.

 

Just curious, and hoping to learn.

 

Simplicity. 

 

I did it so that the code was easy to read.

 

Sometimes, clever coding techniques are hard to understand one or two years down the road. I end up asking myself "What was I thinking when I wrote this code?!" 

 

But as I ponder your question, I can see the bit-fields toggling in my mind's eye. That's an interesting idea that you present. 

 

It reminds me of my assembly coding days and testing bit flags in registers. Super easy in assembly. How hard is it in C these days?

Share this post


Link to post
Share on other sites

Awesome Stuff Zeke,

 

Did you ever get around to using the QM modeling software?

 

I just got the Miro Samek book and am starting to go through it. I have been working on larger and larger projects and my nested states are getting a little over whelming. 

 

When I start using the QM tool I will post here my results.

 

Sadly, I did not get around to the QM modeling software.

 

The need to complete the project and get paid out weighed the curiosity to learn other stuff.

 

The state machine technique I started to describe above is a data (or event) centric design. I've since learned that there is a time centric method of doing a state machine. I haven't had a pressing need to learn the time centric method but I have it on my bucket list to learn and use it somewhere soon.

Share this post


Link to post
Share on other sites

Simplicity. 

 

I did it so that the code was easy to read.

 

Sometimes, clever coding techniques are hard to understand one or two years down the road. I end up asking myself "What was I thinking when I wrote this code?!" 

 

But as I ponder your question, I can see the bit-fields toggling in my mind's eye. That's an interesting idea that you present. 

 

It reminds me of my assembly coding days and testing bit flags in registers. Super easy in assembly. How hard is it in C these days?

 

Ah fair enough Zeke, perfectly reasonable.

 

Well to be perfectly honest, working with bitfields in my own code is not something I had considered until recently myself. But should be trivial as far as implementation goes in C. The implications I am not so sure about.

 

I was considering something like:

if ( flags & Bit(n)) { Branch();}

 

Where flags could be any type you choose, n being any define, const, or enum value( for clarity ).  Bit(n),  being defined as  Bit(n) (1<<n) if required. So in the interrupt routine.

 

 

 

__interrupt TIMER ()
{
    flags |= Bit(n);
}

 

 

You set as many bits as needed. From there . . . not quite sure, but many different ways I can think of to handle the situation. From within main() I mean. Hell no one even says you have to be looping in main() at any given point in time for that matter.

 

Still I had not fully thought this out, which is where my question came in. Branch() also may need to take an argument(or two) to be effective. Again, I have not given it tons of thought yet, and may even decide later that my idea was silly.

Share this post


Link to post
Share on other sites

Take it easy. I think that you may be too hard on yourself.  Your idea has merit. 

 

I can see it being very useful in a resource constrained system. In fact, it would be the proper method if writing code in assembly. I wouldn't do it any other way in that instance.

 

When coding, it is my belief that you have to be explicitly clear with your intentions and comment why and what you are trying to accomplish. Why? Because in one or two years when you revisit that code you are going to forget why you did things that way. Tell yourself what problem you were trying to solve and why you selected a particular coding solution. 

 

Also, there is nothing wrong with having a coding style and habit. If you solved a problem then reusing that code in the future is totally cool. It's your code and you're free to solve it your way.

 

Don't let anyone tell you that your solution doesn't work. It's a working solution!

Share this post


Link to post
Share on other sites

Zeke, well no, I m not beating myself up, or doubting my code. I was just thinking, the way development goes as you progress through a project. You often are thinking of better ways to do things. Especially when you have something working, and you're considering possible refactoring  into the code ( speed size related etc ).

 

Like not 30 minutes after I wrote those two small snippets, I was considering if:

 

if (flags) {Scheduler(&flags);}

 

And let the "state machine" work it all out. Thus keeping the program ( in my mind ) more modular. Then, in the Scheduler, priority is based on which statements come first. Like, in the case of a stop light, you may want to check / process the red light functionality first. e.g. isRed, or isChanging etc.

 

Either way, as you may, or may not know. I am usually very chatty on forums and IRC( when on IRC ), because I am always in search of trying to see things form a different perspective. Because I do know that despite being very opinionated, I do not know everything.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now