Page Links
MSP430F5529LP example code: Toggle LEDs with Buttons

Overview

This example uses the BUTTON hardware library to toggle the LEDs when the button next to the LED is pressed. This example demonstrates how to register a button, detect a button event, and take action based on the event. This is a very simple example that does not distinguish between the different kind of events, only that an event occurred.


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 (Toggle_LED_with_Buttons), 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 toggles the LEDs on the Texas Instruments MSP430F5529 Launchpad
 * development board using the Button hardware library. This is a very basic
 * demonstration for registering a button with the button service, and toggling
 * the LED when the callback indicates that a button event has occurred.
 *
 * Version 1.0
 *
 * Rev. 1.0, Initial Release
 *
 *                                                                            */
/* ===========================================================================*/

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


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


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


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

    // These are logical descriptions of the button service index values.
    #define BUTTON1     0
    #define BUTTON2     1


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

    static void initialize(void);

    static void Button_Callback(void);


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


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

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

    for (;;)    // Loop forever...
    {
        // Do nothing
    }

    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();
    MSP430F5529LP_BUTTONS_Initialize(8, 3, 100, 500);


    // ###################################################################
    // 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 = 1;         // Set P1.0 initial value
    P4OUT_bits.P4OUT7 = 1;         // Set P4.7 initial value

    // Register the buttons
    Set_Button_Callback(BUTTON1, MSP430F5529LP_BUTTON1, 0, Button_Callback);
    Set_Button_Callback(BUTTON2, MSP430F5529LP_BUTTON2, 0, Button_Callback);


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

    __enable_interrupt();
}


/******************************************************************************
    Subroutine:     Button_Callback
    Description:    This is the CallBack function that will be called when
                    either of the buttons generates an event.
    Inputs:         None
    Outputs:        None

******************************************************************************/
static void Button_Callback(void)
{
    /* By using Get_Button_Status(index).reg to perform the check, we are
     * looking for "any event" to have occurred. In this case, toggling the
     * correct LED depending on the button pressed.
     *
     * IMPORTANT: After taking the appropriate actions, the events flags
     * need to be cleared by using Clear_Button_Status(index).
     */

    if (0u != Get_Button_Status(BUTTON1).reg)
    {
        P1OUT_bits.P1OUT0 ^= 1;     // toggle P1.0 (LED1)
        Clear_Button_Status(BUTTON1);
    }

    if (0u != Get_Button_Status(BUTTON2).reg)
    {
        P4OUT_bits.P4OUT7 ^= 1;     // toggle P4.7 (LED2)
        Clear_Button_Status(BUTTON2);
    }
}


/******************************************************************************
   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();
    MSP430F5529LP_BUTTONS_Initialize(8, 3, 100, 500);


    // ###################################################################
    // 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 = 1;         // Set P1.0 initial value
    P4OUT_bits.P4OUT7 = 1;         // Set P4.7 initial value

    // Register the buttons
    Set_Button_Callback(BUTTON1, MSP430F5529LP_BUTTON1, 0, Button_Callback);
    Set_Button_Callback(BUTTON2, MSP430F5529LP_BUTTON2, 0, Button_Callback);


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

    __enable_interrupt();
}

Section 1 - initialization of the operating environment

Since the CLOCK, TIMERA2 and BUTTONS 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:

The initialization of the Button library includes parameters that customize the debouncing and button event detection behavior. The values passed to the initialization set a button service interval of 8ms, a debouncing threshold of 24 ms, a double-click threshold of 100 ms, and a long-press event threshold of 500 ms.

   MSP430F5529LP_BUTTONS_Initialize(8, 3, 100, 500); 
Section 2 - application specific initialization

The applications specific initialization for this example has two parts. The first sets the pin direction 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.

LEDs Circled

The second part registers two callback functions using the Button library function Set_Button_Callback. The format for this function is:

   void Set_Button_Callback(uint16_t index, uint16_t pin, uint16_t polarity, ButtonCallback_t callback); 
The callback functions are registered at indexes 0 and 1 using the logical names BUTTON1 and BUTTON2 that were defined at the top of the main.c file. It is important that the indexes registered are sequential, and start at 0. In order to limit the amount of unnecessary processing during interrupts, the processing of buttons will terminate when the first unregistered index is found. Processing starts at index 0.

The second parameter passed to the function is the pin number for the two buttons provided on all MSP430F5529LP development boards, which is passed using the definitions provided in the Button library header file.

    #define MSP430F5529LP_BUTTON1     30      // P2IN_bits.P2IN1
    #define MSP430F5529LP_BUTTON2     22      // P1IN_bits.P1IN1 

The third parameter indicates that the "active state" of the buttons is the low "0" state. This is a result of the button design, which is "normally-open" and has a pull-up resistor on the input signal. This creates a high "1" condition while the button is not pressed. When the button is pressed, the input signal is shorted to ground creating a low "0' condition.

The fourth parameter is the name of the callback function. In this example, when either button is pressed, the same callback function, Button_Callback, will be called.

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();.


The Main Loop

The main function consists of a simple "forever loop" that does nothing. This was done intentionally to make it clear that all of the actions are being performed by the Button library and the callback function.


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

    for (;;)    // Loop forever...
    {
        // Do nothing
    }

    return 0;
}



The Callback Function

The callback function is the link between the application code and the Button library. Based on the parameters passed during initialization, the TIMERA2 library will call the Button service every 8 ms, and the button service state machine will execute for each of the registered buttons. When a button event is detected for either of the registered buttons, the button status will be set, and the registered callback function will be called.

In this example both buttons are registered with the same callback function, Button_Callback. Since either button results in the execution of the Button_Callback function, it is not known which button experienced the event upon entry to the function. Inside the callback function, both buttons are tested, and depending on which button experienced the event, the corresponding LED is toggled. Notice that because the return type of the Get_Button_Status function is of type ButtonEvents_t, it is possible to use the following form to determine if the button status is 0, meaning no event occurred, or not 0, meaning that an event has occurred.

   Get_Button_Status(BUTTON1).reg 

If the button status is found to be non-zero, it must be cleared by using the Clear_Button_Status function before leaving the callback function. Failure to do so, could result in multiple status bits being set the next time the callback function is called (one or more of which are old values). This could result in inappropriate behavior.


static void Button_Callback(void)
{
    /* By using Get_Button_Status(index).reg to perform the check, we are
     * looking for "any event" to have occurred. In this case, toggling the
     * correct LED depending on the button pressed.
     *
     * IMPORTANT: After taking the appropriate actions, the events flags
     * need to be cleared by using Clear_Button_Status(index).
     */

    if (0u != Get_Button_Status(BUTTON1).reg)
    {
        P1OUT_bits.P1OUT0 ^= 1;     // toggle P1.0 (LED1)
        Clear_Button_Status(BUTTON1);
    }

    if (0u != Get_Button_Status(BUTTON2).reg)
    {
        P4OUT_bits.P4OUT7 ^= 1;     // toggle P4.7 (LED2)
        Clear_Button_Status(BUTTON2);
    }
}


NOTE: By using callback functions, the libraries can remain independent from the application. This allows the same library files to be linked between many projects, without having to create numerous copies. That is a nice feature. However, and this is important, because it looks like a regular function, it can be easy to forget that the callback functions execute in the context of an ISR. This means that interrupts are disabled inside the callback function. This has two important consequences. First, things like blocking delay functions that rely on interrupts to operate, will not work. Using something like this inside the callback function will cause the code to hang forever. Second, while executing inside the callback function, other interrupts will be prevented from executing. If those interrupts are time sensitive, staying in the callback function too long could have undesired side-effects.


The End Result

Once the example code is built and executed, the end result should be that the Red and Green LEDs turn on and stay on. Pressing either of the user buttons on the MSP430F5529LP development board, should toggle the state of the LED next to it.

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