Two alternative state machine structures with Timer 1 millisecond counter

These are the two versions of the dsPIC30F state machine code we looked at in today’s class, both of which use a Timer 1 interrupt service routine to increment a millisecond counter that is used to trigger timeout state transitions. The two versions are functionally more or less equivalent, but one may produce clearer code than the other in certain situations.

The first version is what I call “if inside while”. A single while(1) loop drives the state machine. Each time around the loop, all sensors are read at the start of the loop and then an if-else-if statement is used to select the code for the current state.

//
// dsPIC30F4011 state machine example with timer
// Version 1 - "if inside while"
//
// Written by Ted Burke, 13-11-2017
//

#include <xc.h>
#include <libpic30.h>

#define M_PI 3.141592653589793

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

// Function prototype for Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void);

// Millisecond counter and state variable
int millis = 0;
int state = 1;  

void main()
{
    // Make every pin in Port D an output
    TRISD = 0;

    // Configure Timer 1 to generate interrupt every 1ms
    PR1 = 30000;          // Set the Timer 1 period (max 65535)
    TMR1 = 0;             // Reset Timer 1 counter
    IEC0bits.T1IE = 1;    // Enable Timer 1 interrupt
    T1CONbits.TCKPS = 0;  // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256)
    T1CONbits.TON = 1;    // Turn on Timer 1
    
    // State loop (if inside while)
    while(1)
    {
        // Read sensors
        //colour = analogRead(0);
        //distance = analogRead(1);
        
        if (state == 1)
        {
            // Set actuators
            LATD = 0b0001;
            
            // Change state?
            if (millis > 500) gotoState(2);
        }
        else if (state == 2)
        {
            // Set actuators
            LATD = 0b0010;
            
            // Change state?
            if (millis > 1000) gotoState(3);
        }
        else if (state == 3)
        {
            // Set actuators
            LATD = 0b0100;
            
            // Change state?
            if (millis > 1500) gotoState(1);
        }
    }
}

// Reset millisecond counter and change state
void gotoState(int newState)
{
    millis = 0;
    state = newState;
}

// Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void)
{
    // Clear Timer 1 interrupt flag
    IFS0bits.T1IF = 0;
  
    millis = millis + 1;
}

The second version is what I call “whiles inside while”. In this structure, each state has its own dedicated while loop, which must include reading any relevant sensors as well as setting actuators and checking for state transitions. Each state’s loop can be preceded by initialisation code if required (e.g. resetting the millisecond timer). If initialisation code should only be executed when actually entering the state, then it can be enclosed in an if statement.

//
// dsPIC30F4011 state machine example with timer
// Version 2 - "whiles inside while"
//
// Written by Ted Burke, 13-11-2017
//

#include <xc.h>
#include <libpic30.h>

#define M_PI 3.141592653589793

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

// Function prototype for Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void);

// Millisecond counter and state variable
int millis = 0;
int state = 1;  

void main()
{
    // Make every pin in Port D an output
    TRISD = 0;

    // Configure Timer 1
    PR1 = 30000;          // Set the Timer 1 period (max 65535)
    TMR1 = 0;             // Reset Timer 1 counter
    IEC0bits.T1IE = 1;    // Enable Timer 1 interrupt
    T1CONbits.TCKPS = 0;  // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256)
    T1CONbits.TON = 1;    // Turn on Timer 1
    
    // State loop (whiles inside while)
    while(1)
    {
        millis = 0;
        while (state == 1)
        {
            // Set actuators
            LATD = 0b0001;
            
            // Read relevant sensors
            // ?????
            
            // Change state?
            if (millis > 500) state = 2;
        }
        
        millis = 0;
        while (state == 2)
        {
            // Set actuators
            LATD = 0b0010;
            
            // Read relevant sensors
            // ?????
            
            // Change state?
            if (millis > 1000) state = 3;
        }
        
        millis = 0;
        while (state == 3)
        {
            // Set actuators
            LATD = 0b0100;
            
            // Read relevant sensors
            // ?????
            
            // Change state?
            if (millis > 1500) state = 1;
        }
    }
}

// Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void)
{
    // Clear Timer 1 interrupt flag
    IFS0bits.T1IF = 0;
  
    millis = millis + 1;
}
Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s