LedControl
An Arduino library for MAX7219 and MAX7221

The Arduino library code

The LedControl library was initially written for Arduino boards based on 8-bit AVR processors. But since code does not use any sophisticated internal functions of processor it is highly portable und should run on any Arduino(-like) board that supports pinMode() and digitalWrite() functions.

A single MAX72XX Led driver is able to control 64 Leds. The library supports up to 8 daisy chained MAX72XX drivers. Controlling 512 Leds should be more than enough for most purposes.

The library code comes with three demo sketches . Your probably want to have a look at this code while reading this section. The demo code is well documented and provides some insight how all different parts of library work together.


Library initialization

To include library into your Arduino code you have to write a few lines of initialization code.


Add library to your sketch

This works like with any other Arduino libray, you can either use Include Library menu function from IDE, or you simply add an

#include "LedControl.h"

statement to top of your sketch.


Create a LedControl variable

All library API-functions are called through a variable of type LedControl which should be defined right at top of your sketch to make it acessible to rest of projects code.

The typical code for library initialization looks like this :

/* Include LedControl library */
#include "LedControl.h"
/*H******************************************************* 
Create a new LedControl variable.
* We use pins 12,11 and 10 on Arduino for SPI interface
* Pin 12 is connected to DATA IN-pin of first MAX7221
* Pin 11 is connected to CLK-pin of first MAX7221
* Pin 10 is connected to LOAD(/CS)-pin of first MAX7221
* There will only be a single MAX7221 attached to arduino 
********************************************************/
LedControl lc1 = LedControl( 12, 11, 10, 1 ); 

The initialization code for variable through which we talk to MAX72XX devices takes 4 arguments. The first 3 arguments are pin-numbers on the Arduino that are connected to MAX72XX. These can be any of digital IO-pins on an arduino. In example pins 12 , 11 and 10 where choosen arbitrarily. The library code does no sanity checks on pin-numbers to be valid in any way. Passing in something stupid (pin 123 ??), or simply the wrong pin-number will break code without notice or error messages. You don't have to initialize IO-pins as outputs or set them to a certain state, library will do that for you.

The fourth argument to LedControl(dataPin,clockPin,csPin,numDevices) is the number of cascaded MAX72XX devices you're using with this LedControl . The library can address up to 8 devices from a single LedControl -variable. There is a little performance penalty implied with each device you add to chain, but amount of memory used by library-code will stay same, no matter how many devices you set. Since one LedControl cannot address more than 8 devices, only values between 1..8 are allowed here.

If you sketch needs to control more than 8 MAX72XX, another LedControl -variable needs to be created that uses 3 different pins on your arduino-board.

#include "LedControl.h"  
LedControl lc1=LedControl( 12, 11, 10, 8 ); // Create LedControl for first 8 devices...  
LedControl lc2=LedControl( 9, 8, 7, 8 ); // ... and for next 8 devices.   

Get number of attached devices

There is no way to read IO-pin numbers from code, but there is a function that gets maximum number of devices attached to an LedControl .

/*F******************************************************* 
* Gets maximum number of devices attached to this LedControl.
* Returns : * int number of devices attached to this LedControl 
********************************************************/ 
int LedControl::getDeviceCount(); 

The function is used to loop over full list of attached MAX72XX devices. Here is a piece of code that switches all of MAX72XX-devices from power saving mode into normal operation. The idea behind this code should be clear even though shutdown(addr) function is introduced later on.

#include "LedControl.h"
lc1 = LedControl( 12, 11, 10, 5 );
void 
setup() 
{ 
    for( int index =0; index < lc1.getDeviceCount() ; index++ )
        lc1.shutdown( index, false ); 
} 

We iterate over list of devices by an index that runs from 0 to getDeviceCount()-1 . The index is address of each device. This address is first argument of every function that sets a feature or a (Led-)value on a device. Note that getDeviceCount() returns number of devices attached, but the address of an device starts at 0 for first one, 1 for second one,.. getDeviceCount()-1 for last one.


Power saving mode

Leds consume quite a lot of energy when they are lit. Battery operated devices need a way save power by switching whole display off, when user doesn't need it. The MAX72XX supports a power shutdown mode.

In shutdown mode device switches off all Led's on display, but the data is retained. When device comes out of shutdown mode same Leds will be lit as before it went to sleep. It is even possible to send new data during shutdown mode. When device is reactivated, new data will appear the display. Here is an example for an invisible countdown on a 7-segment display:

/*F********************************************************************
*
**********************************************************************/
void 
countDown() 
{ 
    int i =9; 
    lc.setDigit( 0, (byte)i, false );            // DIGIT '9' APPEARS ON DISPLAY
    delay( 1000 ); 
    lc.shutdown( 0, true);                              // GO INTO SHUTDOWN MODE
    while( i > 1 )                                 // AND COUNT DOWN SILENTLY
    {                                          // DATA IS UPDATED, BUT NOT SHOWN
        lc.setDigit( 0, (byte)i, false ); 
        i--; 
        delay( 1000 ); 
    } 
    // COMING OUT OF SHUTDOWN MODE HAVE ALREADY REACHED '1' 
    lc.shutdown( 0, false );
    lc.setDigit( 0, (byte)i, false ); 
} 

Here is prototype for method LedControl.shutdown(addr,status)

/*F******************************************************* 
 * Set shutdown (power saving) mode for device
 * Params :
 *   addr   The address of display to control
 *   status If true device goes into power-down mode. Set to false
 *          for normal operation.
 ********************************************************/
 void
 shutdown( int addr, bool status );

Note : The MAX72XX is always in shutdown mode when Arduino is powered up.


Limiting number of digits (ScanLimit)

This is a kind of experts feature not really needed by most users of the library. Since library initializes the MAX72XX to safe default values, you don't have to read this section just to make your hardware work

When a new LedControl is created it will activate all 8 digits on all devices. Each lit digit will be switched on for 1/8 of a second by multiplexer circuit that drives digits. If you have any reason to limit number of scanned digits Led's get switched on more frequently, and therefore will be on for longer periods of time.

The effect of setting scan limit to 4 is that a lit Led is now switched on for 1/4 of a second instead of standard 1/8 of a second. The MAX72XX has to provide current on segment-driver for a longer period of time.

You should read relevant section of MAX72XX datasheet carefully! Its actually possible to destroy a MAX72XX by choosing a bad combination of resistor RSet that limits current going through Led's and number of digits scanned by device. The only reason to tweak scanlimit at all, is that display looks too dark. But this is most likely due to fact that intensity on was not raised on startup. Here's the prototype of setScanLimit() for those who need it:

/*F******************************************************* 
Set number of digits (or rows) to be displayed.
 * See datasheet for side effects of scanlimit on brightness
 * of display.
 * Params :
 *   addr     The address of display to control
 *   limit    The number of digits to be displayed
 ********************************************************/
void
setScanLimit( int addr, int limit) ;

Setting display brightness

There are three factors that determine brightness of a display.

With setIntensity(int addr, int intensity) method brightness of the Leds can be set in 16 discrete steps( 0..15 ). Higher values make display brighter. Values greater than 15 will be discarded without changing the brightness. Even lowest value 0 will not switch display off completely off.

/*F******************************************************* 
Set brightness of display.
 * Params:
 *   addr address of display to control
 *   intensity brightness of display.
*******************************************************/
void 
setIntensity( int addr, int intensity); 

Device initialization

When a new LedControl is created library will initialize hardware with

A blanked display is probably what everybody wants on startup. But with the intensity at a minimum and device in shutdown-mode no Leds will light up in startup configuration. Most users will do their own initialization inside setup() -function. Here is a piece of code that can be used as a template for creating an LedControl that is ready to light up Leds at a medium brightness as soon as display data arrives.

#include "LedControl.h"LedControl lc=LedControl(12,11,10,1);

/*F********************************************************************
*
**********************************************************************/
void 
setup() 
{ //wake up MAX72XX from power-saving mode
    lc.shutdown( 0, false);              // SET A MEDIUM BRIGHTNESS FOR LEDS
    lc.setIntensity( 0, 8); 
} 

Led-Matrix

With all initialization code in place its now time to control some Leds.

image


Clearing display

The name of function LedControl.clearDisplay(addr) already suggests what it does.

/*F*******************************************************
 * Switch all Leds on display off.
 * Params:
 *   addr The address of display to control 
********************************************************/
void
clearDisplay( int *addr );

All Leds on selected device are switched off. Its important to understand that this is different from shutdown mode , where data is retained.


Control a single Led

This is prototype of function that switches a single Led on or off.

/*F*******************************************************
 * Set status of a single Led.
 * Params :
 *   addr  address of display
 *   row   row of Led (0..7)
 *   col   column of Led (0..7)
 *   state If true led is switched on, if false it is switched off 
********************************************************/
void
setLed( int addr, int row, int col, boolean state);

The idea behind addr and state arguments should be clear, but what do row and column arguments refer to? This depends on wiring between MAX72XX and your matrix. The LedControl -library assumes setup used in this schematic:

image

There are 8 rows (indexed from 0..7) and 8 columns (also indexed from 0..7) in matrix. If Led located at very right of 3'rd row from top is to be lit, index of Led 2.7 must used as row and column arguments.

This code excerpt shows how few Leds on first MAX72XX device are set

lc.setLed( 0, 2, 7, true ); // TURN ON LED IN 3'RD ROW 8'TH COLUMN ADDR =0
lc.setLed( 0, 0, 1, true );           // LED AT ROW 0, SECOND FROM LEFT SIDE
delay( 500 ); 
lc.setLed( 0, 2, 7, false );   // SWITCH FIRST lED OFF (SECOND ONE STAYS ON)

The setLed() function is fine for lighting up a few Leds, but if more Leds need to be updated, this would require many lines of code. So there are two more functions in library, that control a complete row and column with a single command.


Control row of a matrix

The setRow(addr,row,value) -function takes 3 arguments. The first one is the already familiar address of device. The second one is row that needs to be updated and third one value to be set for this row.

The value argument takes an 8-bit wide byte where each bit set to 1 represents a lit led and a bit set to 0 a Led that is to be switched off.

For an example, Leds marked in red are to be switched on, all others switched off.

image

The index for row to be updated is 2 (counted from top). The value argument has to be set to byte-value for Leds to be lit. The easiest approach is to include standard header-file <binary.h> into to your sketch. The value is written in binary encoding is an exact mapping between bits set to 1 and Leds to be switched on.

//include this file at top of your Sketch
<binary.h>
// ...Initialization code omitted ...
// Setting leds from third row (index=2) of first device 
lc.setRow( 0, 2, B10110000 ); 

When specifying value in binary encoding is not possible, a simple table that maps decimal values of each bit to Led it affects can help. The two rows at bottom show decimal value for example is to be calculated.

Led2.0 Led2.1 Led2.2 Led2.3 Led2.4 Led2.5 Led2.6 Led2.7
Bit-Value 128 64 32 16 8 4 2 1
Led On? Yes No Yes Yes No No No No
Row-Value 128 0 32 16 0 0 2 0

value=176 (128+32+16)

The statement lc.setRow(0,2,176) updates third row on the first MAX72XX attached to Arduino.

The setRow() -call is obviuosly much faster than calling setLed() eight times for all Leds in a row. The Hardware of a MAX72XX causes setRow() function also to be 8 times faster than setColumn() function introduced in next section. If performance of sketch code is important factor use setRow() function whereever possible.

The Prototype of function

/*F*******************************************************
 * Set all 8 Led's in a row to a new state
 * Params:
 *   addr   ddress of display
 *   row    row which is to be set (0..7)
 *   value  each bit set to 1 will light up corresponding Led.  
********************************************************/
void
setRow( int addr, int row, byte value);

Control column of a matrix

The setColumn() -function works just like setRow() command but updates 8 Leds in a vertical column.

Again, Leds marked in red are to be switched on, all others switched off.

image

This time 4 leds at bottom of column 6 are to be lit. With binary encoding leftmost bit in value refers to Led at top of column.

//include this file at top of your Sketch
<binary.h>
// ...Initialization code omitted ...
// Setting leds from third row (index=2) of first device 
lc.setRow(0,2,B00001111); 

A table similar to one from setRow() section helps if binary encoding of value is not an option.

Led2.0 Led2.1 Led2.2 Led2.3 Led2.4 Led2.5 Led2.6 Led2.7
Bit-Value 128 64 32 16 8 4 2 1
Led On Yes No Yes Yes No No No No
Row-Value 128 0 32 16 0 0 2 0

=15 (8+4+2+1)

The Prototype of function:

/*F*******************************************************
Set all 8 Led's in a column to a new state
 * Params:
 *   addr  address of display
 *   col   column which is to be set (0..7)
 *   value each bit set to 1 will light up corresponding Led.  
********************************************************/
void
setColumn( int addr, int col, byte value);

Control 7-Segment displays

image


Print numbers on a 7-Segment display

The most common use of 7-segment displays is to print numbers. The LedControl library has a function that simply takes an argument of type byte and prints corresponding digit on specified column. Valid values for digit are from 0 to 15 to allow displaying hex values . Values greater than 15 (or negative values) are silently discarded. The function also provides an argumment to switch decimal point on column on or off.

Here is a code excerpt that prints an int value (-999..999) on a display with 4 digits.

void 
printNumber( int v) 
{  
    int ones, tens, hundreds; 
    bool  negative = false;

    if( v < -999 || v > 999)  
        return;  
    if( v < 0) 
    {  
        negative =true; 
        v =v *-1;  
    }
    ones = v % 10;  
    v = v / 10;  
    tens = v % 10;  
    v = v / 10; hundreds = v;  
    if( negative ) 
        lc.setChar( 0, 3, '-', false );        // PRINT CHAR '-' IN LEFT COL
    else 
        lc.setChar( 0, 3, ' ', false);       // PRINT A BLANK IN SIGN COLUMN
    lc.setDigit( 0, 2, (byte)hundreds, false ); // PRINT NMBR DIGIT BY DIGIT
    lc.setDigit( 0, 1, (byte)tens, false ); 
    lc.setDigit( 0, 0, (byte)ones, false ); 
} 

The prototype for function:

/*F*******************************************************
 * Display a (hexadecimal) digit on a 7-Segment Display
 * Params:
 *  addr   address of display
 *  digit  position of digit on display (0..7)
 *  value  value to be displayed. (0x00..0x0F)
 *  dp     sets decimal point.  
********************************************************/
void
setDigit( int addr, int digit, byte value, boolean dp);

The digit -argument must be in range 0..7 because MAX72XX can control up to eight digits on a 7-segment display.


Print characters on a 7-Segment display

There is a limited set of characters that make (visual) sense on a 7-segment display. A common use would be - character to prepend negative values and 6 characters from 'A'..'F' for integer hex-values.

The setChar(addr,digit,value,dp) -function accepts a value of type char for the in range of a 7-bit ASCII encoding. Since recognizable patterns are limited, most of defined characters will print <SPACE> -char. But there are quite a few characters that make sense on a 7-segment display.

Here is set of printable characters:

The hexadecimal characters ( 0..F ) have been redefined at the character values 0x00...0x0F. This makes it possible to mix digits and characters values. The byte value for setDigit() -function can be used with setChar() and will print hexadecimal representation of value.

The prototype of function looks very similar to one for displaying digits.

/*F*******************************************************
 * Display a character on a 7-Segment display.
 * Params:
 *   addr  address of display
 *   digit position of character on display (0..7)
 *   value character to be displayed.
 *   dp    sets decimal point.  
********************************************************/
void
setChar( int addr, int digit, char value, boolean dp);

Library Demos

The library comes with three well documented demo sketches . Basic methods to control a Led-Matrix, a 7-Segment display and access to daisy-chained MAX72XX devices are shown. The demos might be a good start for writing your own sketches.