Button Pad Hookup Guide

Pages
Contributors: Byron J.
Favorited Favorite 15

Exercise #3: RGB LEDs and Buttons

Wiring

Hang in there, only 8 more wires to go!

With the red LEDs and buttons working, let's finish up by adding the blue and green LEDs to the mix.

FunctionWire
Color
Button Pad
Connection
Mega
Connection
Green Row 1GreenBlue1*24
Blue Row 1BlueGreen1*26
Green Row 2GreenBlue2*31
Blue Row 2BlueGreen2*32
Green Row 3GreenBlue3*34
Blue Row 3BlueGreen3*35
Green Row 4GreenBlue4*37
Blue Row 4BlueGreen4*38

* Keep in mind that the LEDs we've used have their green and blue legs transposed when referencing the PCB markings. Yes, we are intentionally swapping green and blue in the wiring!

alt text

With all of these wires, it's starting to look like a bird's nest!

alt text

Code

The final sketch builds on the previous two.

language:c

/******************************************************************************
rgb-plus-buttons.ino
Byron Jacquot @ SparkFun Electronics
1/6/2015

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

Exercise 3 in a series of 3.
https://learn.sparkfun.com/tutorials/button-pad-hookup-guide/exercise-3-rgb-leds-and-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 (3)

#define MAX_DEBOUNCE (3)

// Global variables
static uint8_t LED_outputs[NUM_LED_COLUMNS][NUM_LED_ROWS];
static int32_t next_scan;

static const uint8_t btnselpins[4]   = {50,51,52,53};
static const uint8_t btnreadpins[4] = {46,47,48,49};
static const uint8_t ledselpins[4]   = {42,43,44,45};

// RGB pins for each of 4 rows 
static const uint8_t colorpins[4][3] = {{22,24,26}, {30,31,32},{33,34,35},{36,37,38}};


static int8_t debounce_count[NUM_BTN_COLUMNS][NUM_BTN_ROWS];

static void setuppins()
{
    uint8_t i;

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

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

    for(i = 0; i < NUM_BTN_COLUMNS; i++)
    {
        pinMode(btnselpins[i], OUTPUT);

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

    // key return lines
    for(i = 0; i < 4; i++)
    {
        pinMode(btnreadpins[i], INPUT_PULLUP);
    }

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

    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;

    //run
    digitalWrite(btnselpins[current], LOW);
    digitalWrite(ledselpins[current], LOW);

    for(i = 0; i < NUM_LED_ROWS; i++)
    {
        uint8_t val = (LED_outputs[current][i] & 0x03);

        if(val)
        {
            digitalWrite(colorpins[i][val-1], HIGH);
        }
  }


  delay(1);

  for( j = 0; j < NUM_BTN_ROWS; j++)
  {
    val = digitalRead(btnreadpins[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);

          LED_outputs[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);
        }
      }
    }
  }// for j = 0 to 3;

  delay(1);

  digitalWrite(btnselpins[current], HIGH);
  digitalWrite(ledselpins[current], HIGH);

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

  current++;
  if (current >= NUM_BTN_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
  next_scan = millis() + 1;

  for(uint8_t i = 0; i < NUM_LED_ROWS; i++)
  {
    for(uint8_t j = 0; j < NUM_LED_COLUMNS; j++)
    {
      LED_outputs[i][j] = 0;
    }
  }

  Serial.println("Setup Complete.");
}

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

  if(millis() >= next_scan)
  {
    next_scan = millis()+1;
    scan();
  }
}

Now, instead of toggling the red LEDs between on and off, pressing a button cycles between the different colors. The cycle is off, then red, then green, then blue, then off again.

If you've been following the code changes between each exercise, you can probably anticipate what has been added.

  • The array that defines the color output pins has been expanded to be two-dimensional. The second dimension adds pins for the green and blue LEDs.
  • When the button scan detects a button press, it increments the corresponding value in the LED_outputs array.
  • When the LED scan sets the LED outputs, it uses some bitwise operations to convert the button counter into a color bit, and apply it to the LED row outputs.
  • This version doesn't allow for color mixing -- we'll explore why below.

The Limits of I/O Pins

If you've worked with modern microcontrollers, you're probably used to hanging LEDs off digital output pins, as we've been doing in these examples.

We're actually being a little sloppy by doing this - we're taking advantage of the fact that the Atmel ATMega2560 microcontroller has very robust digital inputs and outputs. They are specified to an absolute maximum of 40 mA, and they behave reasonably well when this limit is exceeded, limiting the current.

Similarly, the overall maximum rated current for the entire processor is 200 mA.

The reason that this example constrains each LED to only red, green or blue is to stay safely within these limits. If we were to light an entire column of LEDs (that's twelve LEDs total, each at roughly 20 mA), they'd add up to 240 mA!

Older microcontrollers allowed much less current per port pin, often in the range of 1 mA. If that range was exceeded, the pin could be fatally damaged. Designing with such controllers meant considering the current consumption per pin more carefully - refer back to the TR-808 schematic, and you'll notice that the pins that drive the LEDs use discrete transistors to increase the current drive capabilities.

For added safety, we should put a resistor inline with each LED scan line. The appropriate values can be calculated using the formula in our resistor applications tutorial.