In my last post, I completed the "hello world" of digital electronics-- making a single LED blink.  Today we'll be turning it up a notch and controlling 64 separate LEDs.  Ok, there ARE 64 LEDs, but the Pi's GPIO doesn't actually have enough output pins to control that many pins individually.  What's a hardware hacker to do??  

Follow The White Rabbit

I'm using an 8x8 LED Matrix that came with my Sunfounder Arduino starter kit.  This is a single-color matrix, part number 1588BS.  You can see from the picture on the right that all 64 LEDs are controlled by only 16 pins.  How can this be?  Well, the trick is, you can't control any combination of pins at once.  You can only control all the pins in a single column at once.  What you have to do is draw each column of LEDs, one after another, at a very fast rate.  Once you get above 60Hz or so, the flicker is invisible to the naked eye and it just looks like all the LEDs are on at the same time.  This is called persistence of vision.

LED matrices use a simple grid of wires. There's one pin for each column and one pin for each row.  The electrical characteristic of a diode is that current will only flow in one direction.  This is used to the advantage in this device to automatically help limit where the current flows so you can individually control each column of lights.  Controlling a larger number of devices than you have pins is called multiplexing. Here's a great tutorial that goes into detail about how LED matrices work:

http://www.appelsiini.net/2011/how-does-led-matrix-work

Figuring It Out

Unfortunately, my LED matrix came with no instructions and I couldn't find a pinout diagram on the Internet to save my life.  Consequently, I decided to just document it via trial and error using a simple 5v circuit and connecting each possible pair of two pins inline with a 330 Ohm resistor.  It was fairly easy to determine which pins applied to a row and which pins applied to a column.  I drew a diagram of the pins and decided to use my Raspberry Pi's GPIO pins 1-16 to keep things simple.  Remember, these are the Wiring Pi numbers, NOT the physical pin header position nor the Broadcom pin numbers!  Here's that handy reference again.

The circled numbers are the GPIO pin I assigned to that pin on the LED Matrix.  Note, this is drawn with LEDs up and the notch facing the front.  You'll also notice there's absolutely no rhyme or reason to the pin order.  Good work, guys :-/

Best Thing Since Sliced Breadboard

I joined two 65-row breadboards to get enough space to plug it all in.  It seems a little cluttered, but remember, it's just the same thing 8 times over.  I'm simply using the 3.3 volts on the digital pins to drive the LEDs now.  I'm aware that the Pi's GPIO can only put out a limited amount of current at a time, but I'm only illuminating one row at a time and this seems to work ok for now.  LEDs can often operate on as little as 1-5 mA.  Each anode (positive) end is connected to the 3.3V GPIO out in series with a 330 Ohm resistor.  This provides around 10 mA of current per LED which is enough forward bias it.

The cathode (negative) end of each column of LEDs must be switched to ground.  For this I used 8 NPN transistors (2N3904).  These are light duty and easily forward biased with very little current at the base.  The base is wired to the GPIO outs in series with a 10 K Ohm resistor providing just 330 Nano Ohms to switch it on.  The collector ties to the cathode ends of the LED matrix, and the emitter flows into ground.  So basically, a "high" on the corresponding GPIO pin turns on the switch and connects that row to ground, thus completing the circuit.  Whatever LED rows in that column that have voltage at their anode will illuminate.

Here's what the final product looked like.  Note, the "notch" in the LED matrix is facing to the left.  I ended up turning the matrix 90 degrees before using it so my rows really became my columns and vice versa, but I suppose it doesn't matter as long as you're consistent.

(Click to enlarge)

Programming It

As I mentioned above, the code for this illuminates a single column at once, but does so fast enough your eyes can't tell the difference.  I borrowed a page from my CommandBox Snake game and spun up a thread that just sits there and redraws the LED matrix while the main thread is free to modify the shape, or just sleep if it wishes.  The main thread signals to the "drawing" thread it's time to quit by switching a "keeprefreshing" flag and then waits to join back to the thread before shutting down.  

An issue I ran into early on is the  nice convenience methods in Pi4J waste too many CPU cycles to be real time.  I was getting around 27 Hz refresh rate (redrawing the entire matrix 27 times a second) which was flickertastic (not a good thing).  To streamline the code as much as possible, dipped into the back end of Pi4J to utilize its JNI calls to the native C library directly.  This increased my refresh rate to around 170 Hz which was much faster.  In fact, it was so fast the LEDs were kind of faint.  I added a sleep(1) to slow it down just enough to let the LEDs get a bit brighter.  This brought me down to 70 Hz which still looked fine.

<cfscript>
gpio = createObject( 'java', 'com.pi4j.wiringpi.Gpio')
gpioUtil = createObject( 'java', 'com.pi4j.wiringpi.GpioUtil')

// Ugly memory faults if you forget this line!
Gpio.wiringPiSetup()

// The GPIO's wiring pi pin numbers
pinRows = [ 16,15,14,13,12,11,10,9 ]
pinCols = [ 1,2,3,4,5,6,7,8 ]

// Set up each pin 
loop from="1" to="16" index="i" {
  GpioUtil.export( i, GpioUtil.DIRECTION_OUT );
  Gpio.pinMode( i, Gpio.OUTPUT );
}

// Initialize to default (all off)
matrix = [
  [0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0]
]

// Spawn a thread to keep refreshing the LED Matrix as fast as it can
keepRefreshing = true
threadName=createUUID()

thread action="run" name=threadName priority="HIGH" {
start=getTickCount()
count = 0
  while( keepRefreshing ) {
    flash( matrix  )
    count++
  }
  // Debugging code to output refresh frequency
  systemOutput(count/((getTickCount()-start)/1000))
  reset()
}

try {

  // Spell "CFML"
  matrix = [
    [1,1,1,0,1,1,1,0],
    [1,0,0,0,1,0,0,0],
    [1,0,0,0,1,1,0,0],
    [1,1,1,0,1,0,0,0],
    [1,0,0,1,0,1,0,0],
    [1,1,1,1,0,1,0,0],
    [1,0,0,1,0,1,0,0],
    [1,0,0,1,0,1,1,1]

  ]

  // Leave the message up for 10 seconds
  sleep(10000)

} catch ( any e ) {
  rethrow;
} finally {

  // If anything bad happens, make sure the thread exits
  keepRefreshing = false;
  thread action="join" name=threadName;

  loop from="1" to="16" index="i" {
  // Commented out for now to avoid "floating grounds" after script is done
  //  GpioUtil.unexport( i );
  }

}

// Flash row LEDs for a given column
function flash( matrix ) {
  var coli = 1
  for( var col in matrix ) {
    // turn off previous col
    ( coli == 1 ? gpio.digitalWrite( pinCols[pinCols.len()], false) : gpio.digitalWrite( pinCols[coli-1], false ) )
    // Turn on the correct rows
    var rowi = 1
    for( var row in col ) {
      ( row==1 ? gpio.digitalWrite( pinRows[rowi++], true) : gpio.digitalWrite( pinRows[rowi++], false) )
    }
    // Turn on the column
    gpio.digitalWrite( coli, true)
    sleep(1)
    coli++
  }
}

// Turn everything off again
function reset() {
  for( var row in pinRows ) gpio.digitalWrite( row, false)
  for( var col in pinCols ) gpio.digitalWrite( col, false)
}


</cfscript>

I ran that code from the CommandBox interactive shell like so:

CommandBox> reload
CommandBox> execute matrix.cfm

The "reload" is necessary to pick up any changes to the script between runs since Lucee caches the compiled template.  I put in a ticket to address this in a future version of CommandBox.  Here is what the "output" looks like:

Animations

I've just begun to play with animating the LEDs.  Since the "drawing" thread just sits in the background and refreshing the LEDs until you tell it to quit, you can modify the "matrix" array however you want in the main thread.  Here's an animated gif that shows the "matrix" array being updated every 200 ms in a loop.

Let your imagination be your guide!  Next, I'd like to clean up the code a bit and make it more re-usable.  I'll probably replace the array of arrays with a simple block of text to make creating designs easier.  I also want to try scrolling text next as well.  If you want more info on the wiring schematic or the code, just hit me up on Twitter.