Button Interface

Platform Test Tools SAM3U2 Firmware nRF51422 Firmware Software
ASCII
DOT MATRIX
USB RS-232 Converter Master AP2 Emulator IAR
TeraTerm

Prerequisite Modules

Learning Objectives

This module is the first opportunity to provide input to the EiE development board using the buttons. By the end of this module you will:

  • Grow further knowledge about fundamental microcontroller GPIO hardware
  • Describe button debouncing and its implications to embedded systems
  • Build familiarity with consistent EiE API descriptions
  • Recognize the applicability of the button API
  • Know the different use cases for the Button API functions
  • Integrate the LED and Button functions to solve a challenge

API Description

If you think about a button, you might not expect a lot of complications. While a button is simple mechanically, there are many considerations to use them in an embedded system. This API completely abstracts the hardware so the polarity, port and pin of the buttons does not have to be known. The driver also provides debouncing and simple functions to provide typical button operations. Note that the reset button on the development boards is not included in this driver as it is purely a hardware switch on the processor’s reset line.

Signal bouncing is the fast, multiple make-and-break connections that occur in a mechanical switch before the signal is stable, as shown in the oscilloscope capture below of one of the development board buttons being released.

BouncingScopeShot

The processor would see three or four rising edge inputs as this button changed states even though they occur in just 300us. To mitigate this, a “debouncing” algorithm starts when the processor sees the first signal edge. It then simply waits a period of time to look at the signal state again when it should be stable to decide if there was a legitimate state change.


SKILL CHECK
Use an oscilloscope to probe one of the button inputs to see the bouncing occurring.

Draw a flow chart to show how a debouncing algorithm might work.


Type Definitions
There is only one button-specific type called “ButtonNameType”. This provides the button names applicable to the target development board as required wherever a button parameter is requested. The names are defined as enums in the board-specific header files (e.g. eief1-pcb-01.h). The quantity of buttons “U8_TOTAL_BUTTONS” is also there. This ensures the API will function with any dev board.

ASCII development board: BUTTON0, BUTTON1, BUTTON2, BUTTON3
Dot matrix development board: BUTTON0, BUTTON1


Physically, BUTTON0 is on the left side of the development board PCBs. You may argue that this is intuitively backwards if you relate it to bit positions where bit 0 is on the right of a number.

Public Functions
The following functions may be used by any application in the system at any time.

  • bool IsButtonPressed(ButtonNameType eButton_) – Returns TRUE if a particular button is currently pressed (and debounced).
  • bool WasButtonPressed(ButtonNameType eButton_) – Returns TRUE if a particular button was pressed since last time it was checked even if it is no longer pressed. This works together with ButtonAcknowledge().
  • void ButtonAcknowledge(ButtonNameType eButton_) – Clears the New Press state of a button — generally always called after WasButtonPressed() returns TRUE.
  • bool IsButtonHeld(ButtonNameType eButton_, u32 u32ButtonHeldTime_) – Returns TRUE if a button has been held for >= u32ButtonHeldTime_ time in milliseconds.

Only WasButtonPressed() needs to be used somewhat carefully. When a button is pressed, the driver sets a flag. Even if the button is released, the flag will remain set until it is cleared in firmware. This is called “latching” the signal. Latching ensures the signal is not missed. WasButtonPressed() checks this flag and ButtonAcknowledge() clears the flag. If it is not cleared, then the flag is still available for other tasks. This is very different than simply checking if a button is pressed or held as it ensures that a single button press can result in a single event – a very important capability that requires quite a few lines of code to implement manually, so it was included in the API.


SKILL CHECK
Write a short program in UserApp1SM_Idle() to increment counter variables whenever IsButtonPressed(BUTTON0) and WasButtonPressed(BUTTON0) (the same button) returns true. The pseudo code looks like this:

static u32 u32IsCounter = 0;
static u32 u32WasCounter = 0;

IsButton0Pressed?
  u32IsCounter++;

WasButton0Pressed?
  u32WasCounter++;
  AckButton0;

Run the program and press BUTTON0 a few times. Try a long press, and a short press. Halt the code and check the counters. Try pressing the button for just 100ms. What is the fastest button press you can do? Why is there always at least 25 counts when the button is pressed?


Examples

Start with a clean version of the Master firmware and make a new branch called “button_interface”. Add the following examples in user_app1.c:

ASCII development board:
First, make sure all LEDs are properly initialized in UserApp1Initialize():

  LedOff(WHITE);
  LedOff(PURPLE);
  LedOff(BLUE);
  LedOff(CYAN);
  LedOff(GREEN);
  LedOff(YELLOW);
  LedOff(ORANGE);
  LedOff(RED);

In UserApp1SM_Idle(), turn on the WHITE LED if BUTTON0 is currently pressed:

if( IsButtonPressed(BUTTON0) )
{
  /* The button is currently pressed, so make sure the LED is on */
  LedOn(WHITE);
}
else
{
  /* The button is not pressed, so make sure the LED is off */
  LedOff(WHITE);
}

Toggle blinking on/off of the YELLOW LED when BUTTON1 has been pressed. You’ll need a new static Boolean variable “bYellowBlink” to track the LED state. ButtonAcknowledge() must be called after WasButtonPressed():

  if( WasButtonPressed(BUTTON1) )
  {
    /* Be sure to acknowledge the button press */
    ButtonAcknowledge(BUTTON1);

    /* If the LED is already blinking, toggle it off */
    if(bYellowBlink)
    {
      bYellowBlink = FALSE;
      LedOff(YELLOW);
    }
    else
    {
     /* start blinking the LED at the current rate */
      bYellowBlink = TRUE;
      LedBlink(YELLOW, LED_1HZ);
    }
  }

Turn on the CYAN LED after BUTTON3 has been held for 2 seconds:

  if( IsButtonHeld(BUTTON3, 2000) )
  {
    LedOn(CYAN);
  }
  else
  {
    LedOff(CYAN);
  }

Dot matrix development board:
First, make sure all LEDs are properly initialized in UserApp1Initialize():

  LedOff(RED0);
  LedOff(BLUE0);
  LedOff(GREEN0);

  LedOff(RED1);
  LedOff(BLUE1);
  LedOff(GREEN1);

  LedOff(RED2);
  LedOff(BLUE2);
  LedOff(GREEN2);

  LedOff(RED3);
  LedOff(BLUE3);
  LedOff(GREEN3);

Turn on the BLUE0 LED if BUTTON0 is currently pressed:

if( IsButtonPressed(BUTTON0) )
{
  /* The button is currently pressed, so make sure the LED is on */
  LedOn(BLUE0);
}
else
{
  /* The button is not pressed, so make sure the LED is off */
  LedOff(BLUE0);
}

Toggle blinking on/off of the GREEN3 LED when BUTTON1 has been pressed. ButtonAcknowledge() must be called after WasButtonPressed():

  if( WasButtonPressed(BUTTON1) )
  {
    /* Be sure to acknowledge the button press */
    ButtonAcknowledge(BUTTON1);

    /* If the LED is already blinking, toggle it off */
    if(bYellowBlink)
    {
      bYellowBlink = FALSE;
      LedOff(GREEN3);
    }
    else
    {
     /* start blinking the LED at the current rate */
      bYellowBlink = TRUE;
      LedBlink(GREEN3, LED_1HZ);
    }
  }

Turn on the RED2 and GREEN2 LEDs after BUTTON1 has been held for 2 seconds:

  if( IsButtonHeld(BUTTON1, 2000) )
  {
    LedOn(RED2);
    LedOn(GREEN2);
  }
  else
  {
    LedOff(RED2);
    LedOff(GREEN2);
  }

Build the code and test out the functionality.

Exercise

Now add code to do the following on the ASCII board:

  • Instantly turn on/off the PURPLE and BLUE LEDs corresponding to BUTTON1 and BUTTON2.
  • Change the YELLOW blink rate to a variable value
  • If the YELLOW LED is currently on, then BUTTON2 will select the next fastest blink rate for the YELLOW LED. Use LED_1HZ, LED_2HZ, LED_4HZ, and LED_8HZ and notice that you can NOT increment these ENUM values.

For the dot matrix board:

  • Instantly turn on/off the RED1 LED corresponding to BUTTON1.
  • Change the GREEN3 blink rate to a variable value
  • If the GREEN3 LED is currently on, then BUTTON0 will select the next fastest blink rate for the GREEN3 LED. Use LED_1HZ, LED_2HZ, LED_4HZ, and LED_8HZ and notice that you can NOT increment these ENUM values.

SKILL CHECK
Think about the solution for the YELLOW LED. What data structure will help you solve this problem? What will you have to change in the code? One solution is provided below, but try to figure this out on your own.


For the variable speed blinking we will use an array to hold the blink rates and then a variable index value that will be adjusted to select different rates. A boolean variable bLedBlink will track if the LED is currently blinking and therefore if the rate can change. The solution presented is only for the ASCII board, but the solution on GitHub is complete for both boards.

  static LedRateType aeBlinkRate[] = {LED_1HZ, LED_2HZ, LED_4HZ, LED_8HZ};
  static u8 u8BlinkRateIndex = 0;
  static bool bLedBlink = FALSE;

Adjust the code to access aeBlinkRate[] for the blinking rate:

  if( WasButtonPressed(BUTTON1) )
  {
    /* Be sure to acknowledge the button press */
    ButtonAcknowledge(BUTTON1);

    /* If the LED is already blinking, toggle it off */
    if(bLedBlink)
    {
      bLedBlink = FALSE;
      LedOff(YELLOW);
    }
    /* else start blinking the LED at the current rate */
    else
    {
      bLedBlink = TRUE;
      LedBlink(YELLOW, aeBlinkRate[u8BlinkRateIndex]);
    }
  }

Check if an update should take place and select the next array value:

  if( WasButtonPressed(BUTTON2) )
  {
    /* Be sure to acknowledge the button press */
    ButtonAcknowledge(BUTTON2);

    /* Update blink rate and handle overflow if LED is blinking */
    if(bLedBlink)
    {
      u8BlinkRateIndex++;
      if(u8BlinkRateIndex == 4)
      {
        u8BlinkRateIndex = 0;
      }
      
      /* Request the rate udpate */
      LedBlink(YELLOW, aeBlinkRate[u8BlinkRateIndex]);
    }
  }

SKILL CHECK
Write a program to check if BUTTON2 and BUTTON3 have been pressed at exactly the same time. Turn on the green LED if you do it. How likely is it that you can? Why or why not?

Adjust the program to allow a reasonable amount of difference. Experiment with values like 5ms, 25ms, and 100ms. Choose the best value.


The Button API is obviously very useful and having the input available immediately adds a new dynamic to what you can do with the development board. You should have all that you need to work successfully with the buttons now.


REVISION HISTORY
2019-10-18: Update for new EiE book code
2017-MAR-08: Add skill checks + minor formatting and spelling updates
2016-MAR-06: First release