Thursday, May 28, 2009

Max 7221 LED matrix driver & Arduino

I’ve parked my current project for a while because I get fascinated by new shiny things.

As summer is coming I thought some light type stuff for the yard would be fun.  I’ve barely progressed beyond the blink example and moved straight to the 8x8 as a stepping stone to 8x8x8 and ambient influences (wind, temperature, humidity, proximity and how pissed the guests are)

But the first step is getting the $30 arduino to address more outputs.  One of the ways is via multiplexing with a Max7221 LED driver.  This chip is designed to operate an 8x8 matrix, or a 7 segment character display.

The datasheet for the 7221 is certainly comprehensive and there’s a well documented control library (LedControl.h) available but even so it took me a few days to figure out, what in hindsight is obvious.  Hopefully this will help someone else in the future.

Things to consider -

  • The pins you use for DIN, CLK and LOAD/CS must be set to output (they default to input)
  • The 7219/7221 initialises in power saving mode.  Makes sense but is also kind of dumb.  So you need to explicitly turn the MAX output pins on if you want to see the fruits of your labour.  To be fair the datasheet says this, but it’s obscured in the LedControl docs.  Again, to be fair the datasheet is referenced and this behaviour is documented but as a beginner lots of the data may as well be in Martian and so I probably skipped past it with glazed eyes.

On initial power-up, all control registers are reset, the
display is blanked, and the MAX7219/MAX7221 enter
shutdown mode. Program the display driver prior to
display use. Otherwise, it will initially be set to scan one
digit, it will not decode data in the data registers, and
the intensity register will be set to its minimum value.

  • Brightness.  Following on from the last sentence the range from low to high is 0..15 and you don’t get anywhere near 15 by default.  Blurb from the datasheet is below but the details are abstracted by LedControl and the setBrightness() function.  That’s just as well.

The MAX7219/MAX7221 allow display brightness to be
controlled with an external resistor (RSET) connected
between V+ and ISET. The peak current sourced from
the segment drivers is nominally 100 times the current
entering ISET. This resistor can either be fixed or vari-
able to allow brightness adjustment from the front
panel. Its minimum value should be 9.53kΩ, which typi-
cally sets the segment current at 40mA. Display bright-
ness can also be controlled digitally by using the
intensity register.

Digital control of display brightness is provided by an
internal pulse-width modulator, which is controlled by
the lower nibble of the intensity register. The modulator
scales the average segment current in 16 steps from a
maximum of 31/32 down to 1/32 of the peak current set
by RSET (15/16 to 1/16 on MAX7221). Table 7 lists the
intensity register format. The minimum interdigit blank-
ing time is set to 1/32 of a cycle.

  • Capacitors… no idea if they’re necessary.  In principal I understand the need to smooth out a noisy +5v but for my first simple test it all works fine with out them.  I have a spare 7221 in case I killed the first one so I’ll try cascading it and see if that makes a difference.  The next milestone however is to move from a breadboard to a 8 foot square grid and bust apart some coat hangers.

What really got me was that as a beginner I didn’t really know if I’d messed up the wiring or what.  An end to end example would have been great and so, code follows.  I’ll add breadboard photos in the next day or two so that the whole world can marvel at it…

This example powers a single row (row 0) of 8 LEDs (0..7).  Now this works (video to follow) it should be easy to wire common cathode to the remaining DIGn pins (DIG0, or pin 2 is all that’s used in this example)

#include "LedControl.h"

int DIN = 12;
int CLK = 11;
int LOADCS = 10;
int flashDelay = 10;  // delay in MS (1000=1 second)
int ledBrightness = 15;  // range is 0-15.  0=lowest, 15 = full power

// define the LedControl instance - add more if >8 required
LedControl lc1=LedControl(DIN,CLK,LOADCS,1);    // DIN, CLK, Load/CS, 1 = only one chip MAX chip attached.

void setup()                    // run once, when the sketch starts
{
  // mark the three pins for the MAX signals as outputs
  pinMode(DIN, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(LOADCS, OUTPUT);
  // take pins out of power save mode.  No updates otherwise.
  for(int index=0;index<lc1.getDeviceCount();index++) {
      lc1.shutdown(index,false);
  }
  lc1.setIntensity(0,ledBrightness  );
}

void loop()                     // run over and over again
{

    int row = 0;
    int chipId = 0;
    // for this test I only wired up one row attached to PIN 2 (row 0)
    // this is why row is defined as loop scoped and initialised to 0
    // you could easily wrap row in another for (int row...) construct
    // LED's share +5v/cathode with Anodes on each LED connected
    //to pins SegA - SegG
    for (int col=0; col<=7; col++) {
      lc1.setLed(chipId, row, col,true);
      delay(flashDelay);
      lc1.setLed(chipId, row, col, false);
      delay(flashDelay);  
  }
}