Page Links
MSP430F5529LP example code: Using the TimerA2 Non-Blocking Delay

Overview

This example uses the TIMERA2 hardware library to blink the LEDs using a state machine and non-blocking delays. This example performs the same task as in the blocking delay example, however this non-blocking example maintains the capacity for the processor to perform other tasks. For anything other than the most simple tasks, non-blocking delays are highly recommended.


Required Hardware

This example only requires the MSP430F5529LP development board.

MSP430F5529 Launchapd Photo

Required Libraries

In order to build the example project, follow the instructions here for how to create and configure a new project. This example requires the following operating environment files:


Example Code

The example code is provided as a main.c file that is available on GitHub at (Blink_LED_Blocking_Timer), or if preferred, it can be cut-pasted from the window below.


/* ########################################################################## */
/*
 * This file was created by www.DavesMotleyProjects.com
 *
 * This software is provided under the following conditions:
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *                                                                            */
/* ########################################################################## */


/* ===========================================================================*/
/*
 * This program blinks the LEDs on the Texas Instruments MSP430F5529 Launchpad
 * development board using a non-blocking delay function using the GetTick and
 * Elapse functions in the TIMER_A2 peripheral files. This demonstrates how to
 * use a more versatile delay function than the basic "delay".
 *
 * Version 1.0
 *
 * Rev. 1.0, Initial Release
 *
 *                                                                            */
/* ===========================================================================*/

#include "MSP430F5529LP.h"
#include "MSP430F5529LP_CLOCK.h"
#include "MSP430F5529LP_TIMERA2.h"


/******************************************************************************
   PUBLIC DEFINITIONS
******************************************************************************/


/******************************************************************************
   PUBLIC VARIABLES
******************************************************************************/


/******************************************************************************
   PRIVATE DEFINITIONS (static const)
******************************************************************************/

    typedef enum {
        LED_STATE_1,
        LED_STATE_2
    } LED_States_t;


/******************************************************************************
   PRIVATE FUNCTION PROTOTYPES (static)
******************************************************************************/

    static void initialize(void);


/******************************************************************************
   PRIVATE VARIABLES (static)
******************************************************************************/

    static LED_States_t LED_State;
    static uint16_t start;


/******************************************************************************
    Subroutine:     main
    Description:    program entry point at startup.
    Inputs:         None
    Outputs:        0

******************************************************************************/
int main( void )
{
    // perform initialization
    initialize();

    for (;;)    // Loop forever...
    {
        switch (LED_State)
        {
            case (LED_STATE_1):
            {
                if (Expired(500, start, GetTick()))
                {
                    P1OUT_bits.P1OUT0 ^= 1;     // toggle P1.0 (LED1)
                    start = GetTick();          // collect new start time
                    LED_State = LED_STATE_2;    // advance to state 2
                }
            }
            break;

            case (LED_STATE_2):
            {
                if (Expired(500, start, GetTick()))
                {
                    P1OUT_bits.P1OUT0 ^= 1;     // toggle P1.0 (LED1)
                    P4OUT_bits.P4OUT7 ^= 1;     // toggle P4.7 (LED2)
                    start = GetTick();          // collect new start time
                    LED_State = LED_STATE_1;    // advance to state 1
                }
            }
            break;

            default:
            {
                start = GetTick();          // collect new start time
                LED_State = LED_STATE_1;    // advance to state 1
            }

        }   // end switch (LED_State)
    }   // end for (;;)

    return 0;
}


/******************************************************************************
    Subroutine:     initialize
    Description:    Performs the initalization of the main program. This also
                    initializes the MSP430F5529LP operating environment.
    Inputs:         None
    Outputs:        None

******************************************************************************/
void initialize(void)
{
    // ###################################################################
    // Add operating environment initialization here

    MSP430F5529LP_CLOCK_Initialize();
    MSP430F5529LP_TIMERA2_Initialize();


    // ###################################################################
    // Add program specific initialization here

    // Enable the Debug LEDs
    P1DIR_bits.P1DIR0 = 1;         // Set P1.0 (LED1) to an Output
    P4DIR_bits.P4DIR7 = 1;         // Set P4.7 (LED2) to an Output
    P1OUT_bits.P1OUT0 = 0;         // Set P1.0 initial value
    P4OUT_bits.P4OUT7 = 1;         // Set P4.7 initial value


    LED_State = LED_STATE_1;    // Initialize the main loop state machine
    start = GetTick();          // Collect the inital start time


    // ###################################################################
    // Last step before exiting, enable global interrupts

    __enable_interrupt();
}


/******************************************************************************
   End of File: main.c
******************************************************************************/



Initialization

In the example code there are 3 main sections to the initialization. The first section is the initialization of the operating environment files, the second section includes application specific initialization, and finally the third section is the enable of the global interrupts. It is recommended that all projects using the operating environment follow this convention.


void initialize(void)
{
    // ###################################################################
    // Add operating environment initialization here

    MSP430F5529LP_CLOCK_Initialize();
    MSP430F5529LP_TIMERA2_Initialize();


    // ###################################################################
    // Add program specific initialization here

    // Enable the Debug LEDs
    P1DIR_bits.P1DIR0 = 1;         // Set P1.0 (LED1) to an Output
    P4DIR_bits.P4DIR7 = 1;         // Set P4.7 (LED2) to an Output
    P1OUT_bits.P1OUT0 = 0;         // Set P1.0 initial value
    P4OUT_bits.P4OUT7 = 1;         // Set P4.7 initial value


    LED_State = LED_STATE_1;    // Initialize the main loop state machine
    start = GetTick();          // Collect the inital start time


    // ###################################################################
    // Last step before exiting, enable global interrupts

    __enable_interrupt();
}

Section 1 - initialization of the operating environment

Since the CLOCK and TIMERA2 library modules are included in this example, the initialization for these modules is performed in section one. The standard convention for the operating environment is that every library module includes an initialization function, and that function must be called before the module or its functions are used. The order of initialization is not important, except for the following rules:

Section 2 - application specific initialization

The applications specific initialization for this example sets the pin direction and initial state for the two user-controllable LEDs. LED1 (Red) is connected to pin P1.0, and LED2 (Green) is connected to pin P4.7. The example code sets both of these pins as outputs (Direction = 1) and then sets the initial (Output) state. Notice that one LED is set to 0 (off) and the other to 1 (on). This will create an alternating LED pattern.

LEDs Circled

The only difference between this initialization, and that of the blocking delay example, is the assignment of two application specific variables. The first variable LED_State is an enumerated type (see below), and is assigned the value LED_State_1. This variable maintains the current state of the main loop state machine that will be discussed later. The benefit of using an enumerated type here is that the compiler will warn against accidental assignments of illegal values. The second variable start is assigned the current tick value using the TIMERA2 library function GetTick(). this initial value will be used to determine when the initial delay of the first state in the state machine has expired.


/******************************************************************************
   PRIVATE DEFINITIONS (static const)
******************************************************************************/

    typedef enum {
        LED_STATE_1,
        LED_STATE_2
    } LED_States_t;


/******************************************************************************
   PRIVATE VARIABLES (static)
******************************************************************************/

    static LED_States_t LED_State;
    static uint16_t start;
    
    
Section 3 - enable global interrupts

The last sub-section simply enables the global interrupts. This should always be done as the last step during initialization, unless there is a very specific need to do it otherwise. The initialization of any interrupt driven library files will have enabled the interrupts for those modules, but they will not run until the global interrupts are enabled using __enable_interrupt();. It is important that the global interrupts are always enabled, as parts of the operating environment depend on it.


The Main Loop

Like the blocking delay example, the main function consists of a simple "forever loop". Inside this loop however is a state machine implemented with a switch statement. The state machine consists of two states: LED_STATE_1 and LED_STATE_2. Each time execution reaches the switch statement, execution is passed to the case statement corresponding to the current state. Inside each state, an if statement checks to see if the 500 ms timeout has expired. If it has not, then execution leaves the switch statement, and no action is taken. This also means that the value of the current state was not changed, so each time the switch statement is entered, execution will continue to return to the current state, until the timeout finally expires. When this occurs, the action will be taken (toggling the LED), a new start time will be collected, the state variable will be changed to be the new state, and then execution leaves the switch statement.

The next time that execution enters the switch statement, it will pass execution to the case statement corresponding to the new state, and it will remain there until the new timeout has expired. This repeats over and over, with actions only being taken during the transition between states.


int main( void )
{
    // perform initialization
    initialize();

    for (;;)    // Loop forever...
    {
        switch (LED_State)
        {
            case (LED_STATE_1):
            {
                if (Expired(500, start, GetTick()))
                {
                    P1OUT_bits.P1OUT0 ^= 1;     // toggle P1.0 (LED1)
                    start = GetTick();          // collect new start time
                    LED_State = LED_STATE_2;    // advance to state 2
                }
            }
            break;

            case (LED_STATE_2):
            {
                if (Expired(500, start, GetTick()))
                {
                    P1OUT_bits.P1OUT0 ^= 1;     // toggle P1.0 (LED1)
                    P4OUT_bits.P4OUT7 ^= 1;     // toggle P4.7 (LED2)
                    start = GetTick();          // collect new start time
                    LED_State = LED_STATE_1;    // advance to state 1
                }
            }
            break;

            default:
            {
                start = GetTick();          // collect new start time
                LED_State = LED_STATE_1;    // advance to state 1
            }

        }   // end switch (LED_State)
    }   // end for (;;)

    return 0;
}

Note: The default: case statement protects against the possibility that the LED_State variable gets corrupted or isn't initialized properly. If the LED_State variable contained a value not equal to either state, no actions would ever be taken, and the code would hang. Every switch statement should include a default case! In the event of a corruption, the default collects a new start time, then re-assigns the state variable to the starting state (LED_STATE_1). This allows the code execution to resume gracefully after an error. In more complex applications with error logging, the default case would also generate an error.


The visual representation of the state machine created by the code above looks like this. The major difference between the blocking example, and this non-blocking example, is that the processor is free most of the time to perform other tasks. Every time the execution enters the state machine (the switch statement) it takes one and only one path defined by the arrows below, and then leaves the state machine. None of the paths are blocking, meaning that execution never waits for something to occur. If the event that execution is waiting for hasn't occurred yet, then execution moves on to do other things, and comes back later to check again. In this example there isn't anything else to do so it just re-enters the state machine to check again. In most applications execution would move on to perform other tasks, and then return to service the state machine again later. Preferably, all of the "other tasks" would also be implemented as non-blocking state machines.

state_machine.png


The End Result

Once the example code is built and executed, the end result is exactly the same as in the blocking example. The Red LED is blinking once per second, and Green LED is blinking once every 2 seconds, as shown below.

final_led_blinking_wdt

Now that the example code is up an running, consider changing a few things an see what happens. For example: