Button Pad Hookup Guide

Pages
Contributors: Byron J.
Favorited Favorite 15

Exercise #2: Monochrome plus Buttons

Our Second exercise is to add the buttons to the scan matrix. We'll use them as inputs, to toggle the corresponding LEDs on and off.

We're going to assume that you're following this guide in order, and have just completed exercise #1. This exercise will describe the additions needed to add the button matrix, building incrementally on top of exercise #1.

Wiring

We're going to add another 8 wires to interface the button matrix. This breaks down as four button columns and four button rows, and described in the following table.

FunctionWire
Color
Button Pad
Connection
Mega
Connection
Button Row 1BlackSwitch146
Button Row 2YellowSwitch247
Button Row 3GreenSwitch348
Button Row 4BlueSwitch449
Button Column AGreenSWT-GND-450
Button Column BYellowSWT-GND-351
Button Column CBlueSWT-GND-252
Button Column DWhiteSWT-GND-153

Again, the wires are prepared and soldered as described in the assembly section, and the other end is stuck into the header on the Mega. With these wires, we're adding column and row connections for the buttons. When they're in place, the connections to the PCB look like this.

alt text

Red LED and Button Connections

Code

The following sketch adds button support to the previous sketch.

language:c
/******************************************************************************
red-plus-buttons.ino
Byron Jacquot @ SparkFun Electronics
1/6/2015

Example to drive the red LEDs and scan the buttons of the RGB button pad.

Exercise 2 in a series of 3.
https://learn.sparkfun.com/tutorials/button-pad-hookup-guide/exercise-2-monochrome-plus-buttons

Development environment specifics:
Developed in Arduino 1.6.5
For an Arduino Mega 2560

This code is released under the [MIT License](http://opensource.org/licenses/MIT).

Distributed as-is; no warranty is given.
******************************************************************************/    
//config variables
#define NUM_LED_COLUMNS (4)
#define NUM_LED_ROWS (4)
#define NUM_BTN_COLUMNS (4)
#define NUM_BTN_ROWS (4)
#define NUM_COLORS (1)

#define MAX_DEBOUNCE (3)

// Global variables
static bool LED_buffer[NUM_LED_COLUMNS][NUM_LED_ROWS];

static const uint8_t btncolumnpins[NUM_BTN_COLUMNS] = {50, 51, 52, 53};
static const uint8_t btnrowpins[NUM_BTN_ROWS]       = {46, 47, 48, 49};
static const uint8_t ledcolumnpins[NUM_LED_COLUMNS] = {42, 43, 44, 45};
static const uint8_t colorpins[NUM_LED_ROWS]        = {22, 30, 33, 36};

static int8_t debounce_count[NUM_BTN_COLUMNS][NUM_BTN_ROWS];

static void setuppins()
{
  uint8_t i;

  // initialize
  // select lines
  // LED columns
  for (i = 0; i < NUM_LED_COLUMNS; i++)
  {
    pinMode(ledcolumnpins[i], OUTPUT);

    // with nothing selected by default
    digitalWrite(ledcolumnpins[i], HIGH);
  }

  // button columns
  for (i = 0; i < NUM_BTN_COLUMNS; i++)
  {
    pinMode(btncolumnpins[i], OUTPUT);

    // with nothing selected by default
    digitalWrite(btncolumnpins[i], HIGH);
  }

  // button row input lines
  for (i = 0; i < NUM_BTN_ROWS; i++)
  {
    pinMode(btnrowpins[i], INPUT_PULLUP);
  }

  // LED drive lines
  for (i = 0; i < NUM_LED_ROWS; i++)
  {
    pinMode(colorpins[i], OUTPUT);
    digitalWrite(colorpins[i], LOW);
  }

  // Initialize the debounce counter array
  for (uint8_t i = 0; i < NUM_BTN_COLUMNS; i++)
  {
    for (uint8_t j = 0; j < NUM_BTN_ROWS; j++)
    {
      debounce_count[i][j] = 0;
    }
  }
}

static void scan()
{
  static uint8_t current = 0;
  uint8_t val;
  uint8_t i, j;

  // Select current columns
  digitalWrite(btncolumnpins[current], LOW);
  digitalWrite(ledcolumnpins[current], LOW);

  // output LED row values
  for (i = 0; i < NUM_LED_ROWS; i++)
  {
    if (LED_buffer[current][i])
    {
      digitalWrite(colorpins[i], HIGH);
    }
  }

  // pause a moment
  delay(1);

  // Read the button inputs
  for ( j = 0; j < NUM_BTN_ROWS; j++)
  {
    val = digitalRead(btnrowpins[j]);

    if (val == LOW)
    {
      // active low: val is low when btn is pressed
      if ( debounce_count[current][j] < MAX_DEBOUNCE)
      {
        debounce_count[current][j]++;
        if ( debounce_count[current][j] == MAX_DEBOUNCE )
        {
          Serial.print("Key Down ");
          Serial.println((current * NUM_BTN_ROWS) + j);

          // Do whatever you want to with the button press here:
          // toggle the current LED state
          LED_buffer[current][j] = !LED_buffer[current][j];
        }
      }
    }
    else
    {
      // otherwise, button is released
      if ( debounce_count[current][j] > 0)
      {
        debounce_count[current][j]--;
        if ( debounce_count[current][j] == 0 )
        {
          Serial.print("Key Up ");
          Serial.println((current * NUM_BTN_ROWS) + j);

          // If you want to do something when a key is released, do it here:

        }
      }
    }
  }// for j = 0 to 3;

  delay(1);

  digitalWrite(btncolumnpins[current], HIGH);
  digitalWrite(ledcolumnpins[current], HIGH);

  for (i = 0; i < NUM_LED_ROWS; i++)
  {
    digitalWrite(colorpins[i], LOW);
  }

  current++;
  if (current >= NUM_LED_COLUMNS)
  {
    current = 0;
  }

}

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(115200);

  Serial.print("Starting Setup...");

  // setup hardware
  setuppins();

  // init global variables
  for (uint8_t i = 0; i < NUM_LED_COLUMNS; i++)
  {
    for (uint8_t j = 0; j < NUM_LED_ROWS; j++)
    {
      LED_buffer[i][j] = 0;
    }
  }

  Serial.println("Setup Complete.");

}

void loop() {
  // put your main code here, to run repeatedly:

  scan();

}

Once the code is loadeed, the keypad doesn't do anything on its own, but when you press a button, the corresponding LED toggles on and off.

alt text

Button matrix in command of LED matrix

The button scan is added in parallel to the LED scan. A column is selected, and a row is read. Some new definitions have been added to dimension the button matrix, plus some new data tables to define the pins for those functions.

The most complex part of this code is the button press detection, which involves a little extra work. When the buttons open or close, sometimes the change isn't especially clean -- the contacts might chatter or bounce when they close. The waveforms below show a couple of different switch actuations, captures from this button pad.

alt text

Captured switch closures

The nice closure on the left was the result of pressing the button quickly and decisively, straight down. The closure on the right is the result of pushing the button slowly, at an oblique angle. The ring inside twisted down onto the fingers of the PCB contacts. In this instance it takes roughly 20 milliseconds for the button to close solidly.

The glitchiness can cause the scanning system to misinterpret the chatter as multiple keypresses, when in fact the button was only actuated once.

To prevent this, the software uses some logic to perform debouncing. The scan system has to find a switch closed for several scans in a row before it decides that the switch is actually closed. It uses the two-dimensional array debounce_count to keep track of the number of successive scans. When a switch is found closed, the counter is incremented. When the counter reaches the constant MAX_DEBOUNCE, it decides that the switch has been solidly and cleanly closed, and acts on the keypress.

When a key is released, the counter counts back down to zero. This example prints a message when it detects a release. Some applications don't take any action on key releases (like the keypad on an ATM), but it's necessary in others (such as MIDI keyboards).

For the rubber button pad, a MAX_DEBOUNCE of 2 or 3 seems to work fairly well. Other types of buttons might require different debounce values.


With the red LEDs and buttons working, the final step is to get the green and blue LEDs working.