Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Electronics ➜ Microprocessors ➜ Interfacing LED displays with the MAX7219 driver

Interfacing LED displays with the MAX7219 driver

Postings by administrators only.

Refresh page


Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Mon 30 Jan 2012 10:22 PM (UTC)

Amended on Tue 31 Jan 2012 08:02 AM (UTC) by Nick Gammon

Message
This post describes how I interfaced a 8x8 LED matrix with as MAX7219 multiplexing LED driver.

The 8x8 LED matrix chip


The 8x8 LED is this one (BL-M12A881UR-11):



I got it from Adafruit for around $US 3.95:

http://www.adafruit.com/products/455




The first problem you have with this is trying to work out which pin is pin 1. It isn't particularly obviously marked.

The datasheet for the product shows this:



That seems to imply that pin 1 is the one on the left if you orient the device so that the part number is towards you, as shown on the photo above where the arrow is. This appears to be the case. I marked mine with a dot from a marker pen so I wouldn't lose track of it.

Flipping it over, now pin 1 is on the right, and the other pins follow, as shown:



Wiring it up can be a bit tedious, especially as it is a bit large to fit onto a standard breadboard. I eventually soldered a 16-core ribbon cable to it like this:



The cable is "flipped" at the half-way mark, so that the pin numbers at the other end go from 1 (the red line) to 16 (the other end).

I then soldered the other end of the cable to a 16-pin header block, like this:



That could then be plugged into a breadboard for patching up to the MAX7219.

The MAX7219 multiplexing chip


Now onto the multiplexing chip, the MAX7219.



I got this also from Adafruit for around $US 10:

http://www.adafruit.com/products/453

Pinouts:



This chip has the advantage of being a constant current driver, which means you don't need to have lots of resistors between it and the LEDs to stop them drawing too much current. A single resistor connected between the ISET pin (pin 18) and Vcc (not Gnd) lets you set the desired current draw for all LEDs.

This table shows the approximate resistor sizes (in K ohms):



Since the datasheet for the BL-M12A881UR-11 specifies that it has a forward voltage drop of around 2.2V at 20 mA, I chose a 27K resistor as the RSET resistor.

The MAX7219 lets you drive up to 8 x 8-segments LEDs (7 segments plus decimal place), or simply an 8 x 8 matrix like the one I am using.

Once you have downloaded the bit pattern for what you want it to display, it automatically multiplexes that out to the LEDs by sourcing current on the appropriate segments (A to G, plus DP) and then sinking current through digits 0 through to 7, in sequence. The overall effect, since the chip sequences through the digits at about 800 Hz, is that they all appear to be on at once.

Schematic


I wired it up like this:



The 27K resistor sets the output current. The 0.1uF and 10 uF capacitors are recommended to help decouple the power line, and absorb any transient spikes in power required to turn the LEDs on and off.

The 10K "pull-down" resistor on SS is used to stop data being clocked into the chip while the Arduino (or other processor) is being powered up, and the lines at that stage might be "floating".

Note that there are two ground pins: 4 and 9.

I have wired up the DP position before A through to G, because of this table:



To get the bit positions right you need to have DP next to A (and not next to G).

The finished wiring (a bit of a rats nest) looks like this:



Note that we only need 5 wires between the breadboard and the Arduino: +5V, Gnd, MOSI, SCK and SS.

Program code


To send data to the MAX7219 is easy, we just use SPI. To send data we need to send 16 bits, which is a "register" followed by "data", like this:


void sendByte (const byte reg, const byte data)
  {    
  digitalWrite (SS, LOW);
  SPI.transfer (reg);
  SPI.transfer (data);
  digitalWrite (SS, HIGH); 
  }  // end of sendByte


The registers are as follows:


const byte MAX7219_REG_NOOP        = 0x0;
const byte MAX7219_REG_DIGIT0      = 0x1;
const byte MAX7219_REG_DIGIT1      = 0x2;
const byte MAX7219_REG_DIGIT2      = 0x3;
const byte MAX7219_REG_DIGIT3      = 0x4;
const byte MAX7219_REG_DIGIT4      = 0x5;
const byte MAX7219_REG_DIGIT5      = 0x6;
const byte MAX7219_REG_DIGIT6      = 0x7;
const byte MAX7219_REG_DIGIT7      = 0x8;
const byte MAX7219_REG_DECODEMODE  = 0x9;
const byte MAX7219_REG_INTENSITY   = 0xA;
const byte MAX7219_REG_SCANLIMIT   = 0xB;
const byte MAX7219_REG_SHUTDOWN    = 0xC;
const byte MAX7219_REG_DISPLAYTEST = 0xF;


The NOOP (no operation) register is used when you need to cascade MAX7219 chips together.

Registers 1 through 8 are the digits (where the 8 bits of data are which segments to display for that digit).

The "decode mode" register is used, if required, with ordinary 7-segment displays to have the chip automatically translate numbers into the correct segments. I'm not using that mode here.

The "intensity" mode register lets you set an intensity level (0 to 15) which controls the duty cycle of the PWM sent to the LEDs, and thus how bright they are.

The "scan limit" register can be used if you are using 7-segment displays. Say you only had 4 digits, then by lowering the scan limit they could be brighter, because the chip doesn't waste time trying to send data to digits that don't exist.

The "shutdown" register can be used to blank all digits at once, either because you want them blank for some reason, or to save power.

The "display test" register turns all LEDs on, presumably to test the display.

Font


To display text (like letters, numbers, symbols) I have borrowed the CP437 font from another project. Put this into a "font.h" file:


// bit patterns for the CP437 font

byte cp437_font [256] [8] PROGMEM = {
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0x00
  { 0x7E, 0x81, 0x95, 0xB1, 0xB1, 0x95, 0x81, 0x7E }, // 0x01
  { 0x7E, 0xFF, 0xEB, 0xCF, 0xCF, 0xEB, 0xFF, 0x7E }, // 0x02
  { 0x0E, 0x1F, 0x3F, 0x7E, 0x3F, 0x1F, 0x0E, 0x00 }, // 0x03
  { 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00 }, // 0x04
  { 0x18, 0xBA, 0xFF, 0xFF, 0xFF, 0xBA, 0x18, 0x00 }, // 0x05
  { 0x10, 0xB8, 0xFC, 0xFF, 0xFC, 0xB8, 0x10, 0x00 }, // 0x06
  { 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00 }, // 0x07
  { 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF }, // 0x08
  { 0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00 }, // 0x09
  { 0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF }, // 0x0A
  { 0x70, 0xF8, 0x88, 0x88, 0xFD, 0x7F, 0x07, 0x0F }, // 0x0B
  { 0x00, 0x4E, 0x5F, 0xF1, 0xF1, 0x5F, 0x4E, 0x00 }, // 0x0C
  { 0xC0, 0xE0, 0xFF, 0x7F, 0x05, 0x05, 0x07, 0x07 }, // 0x0D
  { 0xC0, 0xFF, 0x7F, 0x05, 0x05, 0x65, 0x7F, 0x3F }, // 0x0E
  { 0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99 }, // 0x0F
  { 0x7F, 0x3E, 0x3E, 0x1C, 0x1C, 0x08, 0x08, 0x00 }, // 0x10
  { 0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x3E, 0x7F, 0x00 }, // 0x11
  { 0x00, 0x24, 0x66, 0xFF, 0xFF, 0x66, 0x24, 0x00 }, // 0x12
  { 0x00, 0x5F, 0x5F, 0x00, 0x00, 0x5F, 0x5F, 0x00 }, // 0x13
  { 0x06, 0x0F, 0x09, 0x7F, 0x7F, 0x01, 0x7F, 0x7F }, // 0x14
  { 0x40, 0xDA, 0xBF, 0xA5, 0xFD, 0x59, 0x03, 0x02 }, // 0x15
  { 0x00, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x00 }, // 0x16
  { 0x80, 0x94, 0xB6, 0xFF, 0xFF, 0xB6, 0x94, 0x80 }, // 0x17
  { 0x00, 0x04, 0x06, 0x7F, 0x7F, 0x06, 0x04, 0x00 }, // 0x18
  { 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00 }, // 0x19
  { 0x08, 0x08, 0x08, 0x2A, 0x3E, 0x1C, 0x08, 0x00 }, // 0x1A
  { 0x08, 0x1C, 0x3E, 0x2A, 0x08, 0x08, 0x08, 0x00 }, // 0x1B
  { 0x3C, 0x3C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00 }, // 0x1C
  { 0x08, 0x1C, 0x3E, 0x08, 0x08, 0x3E, 0x1C, 0x08 }, // 0x1D
  { 0x30, 0x38, 0x3C, 0x3E, 0x3E, 0x3C, 0x38, 0x30 }, // 0x1E
  { 0x06, 0x0E, 0x1E, 0x3E, 0x3E, 0x1E, 0x0E, 0x06 }, // 0x1F
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // ' '
  { 0x00, 0x06, 0x5F, 0x5F, 0x06, 0x00, 0x00, 0x00 }, // '!'
  { 0x00, 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x00 }, // '"'
  { 0x14, 0x7F, 0x7F, 0x14, 0x7F, 0x7F, 0x14, 0x00 }, // '#'
  { 0x24, 0x2E, 0x6B, 0x6B, 0x3A, 0x12, 0x00, 0x00 }, // '$'
  { 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, 0x00 }, // '%'
  { 0x30, 0x7A, 0x4F, 0x5D, 0x37, 0x7A, 0x48, 0x00 }, // '&'
  { 0x04, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, // '''
  { 0x00, 0x1C, 0x3E, 0x63, 0x41, 0x00, 0x00, 0x00 }, // '('
  { 0x00, 0x41, 0x63, 0x3E, 0x1C, 0x00, 0x00, 0x00 }, // ')'
  { 0x08, 0x2A, 0x3E, 0x1C, 0x1C, 0x3E, 0x2A, 0x08 }, // '*'
  { 0x08, 0x08, 0x3E, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // '+'
  { 0x00, 0x80, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00 }, // ','
  { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 }, // '-'
  { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }, // '.'
  { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 }, // '/'
  { 0x3E, 0x7F, 0x71, 0x59, 0x4D, 0x7F, 0x3E, 0x00 }, // '0'
  { 0x40, 0x42, 0x7F, 0x7F, 0x40, 0x40, 0x00, 0x00 }, // '1'
  { 0x62, 0x73, 0x59, 0x49, 0x6F, 0x66, 0x00, 0x00 }, // '2'
  { 0x22, 0x63, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // '3'
  { 0x18, 0x1C, 0x16, 0x53, 0x7F, 0x7F, 0x50, 0x00 }, // '4'
  { 0x27, 0x67, 0x45, 0x45, 0x7D, 0x39, 0x00, 0x00 }, // '5'
  { 0x3C, 0x7E, 0x4B, 0x49, 0x79, 0x30, 0x00, 0x00 }, // '6'
  { 0x03, 0x03, 0x71, 0x79, 0x0F, 0x07, 0x00, 0x00 }, // '7'
  { 0x36, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // '8'
  { 0x06, 0x4F, 0x49, 0x69, 0x3F, 0x1E, 0x00, 0x00 }, // '9'
  { 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, // ':'
  { 0x00, 0x80, 0xE6, 0x66, 0x00, 0x00, 0x00, 0x00 }, // ';'
  { 0x08, 0x1C, 0x36, 0x63, 0x41, 0x00, 0x00, 0x00 }, // '<'
  { 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00 }, // '='
  { 0x00, 0x41, 0x63, 0x36, 0x1C, 0x08, 0x00, 0x00 }, // '>'
  { 0x02, 0x03, 0x51, 0x59, 0x0F, 0x06, 0x00, 0x00 }, // '?'
  { 0x3E, 0x7F, 0x41, 0x5D, 0x5D, 0x1F, 0x1E, 0x00 }, // '@'
  { 0x7C, 0x7E, 0x13, 0x13, 0x7E, 0x7C, 0x00, 0x00 }, // 'A'
  { 0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00 }, // 'B'
  { 0x1C, 0x3E, 0x63, 0x41, 0x41, 0x63, 0x22, 0x00 }, // 'C'
  { 0x41, 0x7F, 0x7F, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // 'D'
  { 0x41, 0x7F, 0x7F, 0x49, 0x5D, 0x41, 0x63, 0x00 }, // 'E'
  { 0x41, 0x7F, 0x7F, 0x49, 0x1D, 0x01, 0x03, 0x00 }, // 'F'
  { 0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00 }, // 'G'
  { 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00 }, // 'H'
  { 0x00, 0x41, 0x7F, 0x7F, 0x41, 0x00, 0x00, 0x00 }, // 'I'
  { 0x30, 0x70, 0x40, 0x41, 0x7F, 0x3F, 0x01, 0x00 }, // 'J'
  { 0x41, 0x7F, 0x7F, 0x08, 0x1C, 0x77, 0x63, 0x00 }, // 'K'
  { 0x41, 0x7F, 0x7F, 0x41, 0x40, 0x60, 0x70, 0x00 }, // 'L'
  { 0x7F, 0x7F, 0x0E, 0x1C, 0x0E, 0x7F, 0x7F, 0x00 }, // 'M'
  { 0x7F, 0x7F, 0x06, 0x0C, 0x18, 0x7F, 0x7F, 0x00 }, // 'N'
  { 0x1C, 0x3E, 0x63, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // 'O'
  { 0x41, 0x7F, 0x7F, 0x49, 0x09, 0x0F, 0x06, 0x00 }, // 'P'
  { 0x1E, 0x3F, 0x21, 0x71, 0x7F, 0x5E, 0x00, 0x00 }, // 'Q'
  { 0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00 }, // 'R'
  { 0x26, 0x6F, 0x4D, 0x59, 0x73, 0x32, 0x00, 0x00 }, // 'S'
  { 0x03, 0x41, 0x7F, 0x7F, 0x41, 0x03, 0x00, 0x00 }, // 'T'
  { 0x7F, 0x7F, 0x40, 0x40, 0x7F, 0x7F, 0x00, 0x00 }, // 'U'
  { 0x1F, 0x3F, 0x60, 0x60, 0x3F, 0x1F, 0x00, 0x00 }, // 'V'
  { 0x7F, 0x7F, 0x30, 0x18, 0x30, 0x7F, 0x7F, 0x00 }, // 'W'
  { 0x43, 0x67, 0x3C, 0x18, 0x3C, 0x67, 0x43, 0x00 }, // 'X'
  { 0x07, 0x4F, 0x78, 0x78, 0x4F, 0x07, 0x00, 0x00 }, // 'Y'
  { 0x47, 0x63, 0x71, 0x59, 0x4D, 0x67, 0x73, 0x00 }, // 'Z'
  { 0x00, 0x7F, 0x7F, 0x41, 0x41, 0x00, 0x00, 0x00 }, // '['
  { 0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00 }, // backslash
  { 0x00, 0x41, 0x41, 0x7F, 0x7F, 0x00, 0x00, 0x00 }, // ']'
  { 0x08, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x08, 0x00 }, // '^'
  { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, // '_'
  { 0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x00 }, // '`'
  { 0x20, 0x74, 0x54, 0x54, 0x3C, 0x78, 0x40, 0x00 }, // 'a'
  { 0x41, 0x7F, 0x3F, 0x48, 0x48, 0x78, 0x30, 0x00 }, // 'b'
  { 0x38, 0x7C, 0x44, 0x44, 0x6C, 0x28, 0x00, 0x00 }, // 'c'
  { 0x30, 0x78, 0x48, 0x49, 0x3F, 0x7F, 0x40, 0x00 }, // 'd'
  { 0x38, 0x7C, 0x54, 0x54, 0x5C, 0x18, 0x00, 0x00 }, // 'e'
  { 0x48, 0x7E, 0x7F, 0x49, 0x03, 0x02, 0x00, 0x00 }, // 'f'
  { 0x98, 0xBC, 0xA4, 0xA4, 0xF8, 0x7C, 0x04, 0x00 }, // 'g'
  { 0x41, 0x7F, 0x7F, 0x08, 0x04, 0x7C, 0x78, 0x00 }, // 'h'
  { 0x00, 0x44, 0x7D, 0x7D, 0x40, 0x00, 0x00, 0x00 }, // 'i'
  { 0x60, 0xE0, 0x80, 0x80, 0xFD, 0x7D, 0x00, 0x00 }, // 'j'
  { 0x41, 0x7F, 0x7F, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // 'k'
  { 0x00, 0x41, 0x7F, 0x7F, 0x40, 0x00, 0x00, 0x00 }, // 'l'
  { 0x7C, 0x7C, 0x18, 0x38, 0x1C, 0x7C, 0x78, 0x00 }, // 'm'
  { 0x7C, 0x7C, 0x04, 0x04, 0x7C, 0x78, 0x00, 0x00 }, // 'n'
  { 0x38, 0x7C, 0x44, 0x44, 0x7C, 0x38, 0x00, 0x00 }, // 'o'
  { 0x84, 0xFC, 0xF8, 0xA4, 0x24, 0x3C, 0x18, 0x00 }, // 'p'
  { 0x18, 0x3C, 0x24, 0xA4, 0xF8, 0xFC, 0x84, 0x00 }, // 'q'
  { 0x44, 0x7C, 0x78, 0x4C, 0x04, 0x1C, 0x18, 0x00 }, // 'r'
  { 0x48, 0x5C, 0x54, 0x54, 0x74, 0x24, 0x00, 0x00 }, // 's'
  { 0x00, 0x04, 0x3E, 0x7F, 0x44, 0x24, 0x00, 0x00 }, // 't'
  { 0x3C, 0x7C, 0x40, 0x40, 0x3C, 0x7C, 0x40, 0x00 }, // 'u'
  { 0x1C, 0x3C, 0x60, 0x60, 0x3C, 0x1C, 0x00, 0x00 }, // 'v'
  { 0x3C, 0x7C, 0x70, 0x38, 0x70, 0x7C, 0x3C, 0x00 }, // 'w'
  { 0x44, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // 'x'
  { 0x9C, 0xBC, 0xA0, 0xA0, 0xFC, 0x7C, 0x00, 0x00 }, // 'y'
  { 0x4C, 0x64, 0x74, 0x5C, 0x4C, 0x64, 0x00, 0x00 }, // 'z'
  { 0x08, 0x08, 0x3E, 0x77, 0x41, 0x41, 0x00, 0x00 }, // '{'
  { 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x00 }, // '|'
  { 0x41, 0x41, 0x77, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // '}'
  { 0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x01, 0x00 }, // '~'
  { 0x70, 0x78, 0x4C, 0x46, 0x4C, 0x78, 0x70, 0x00 }, // 0x7F
  { 0x0E, 0x9F, 0x91, 0xB1, 0xFB, 0x4A, 0x00, 0x00 }, // 0x80
  { 0x3A, 0x7A, 0x40, 0x40, 0x7A, 0x7A, 0x40, 0x00 }, // 0x81
  { 0x38, 0x7C, 0x54, 0x55, 0x5D, 0x19, 0x00, 0x00 }, // 0x82
  { 0x02, 0x23, 0x75, 0x55, 0x55, 0x7D, 0x7B, 0x42 }, // 0x83
  { 0x21, 0x75, 0x54, 0x54, 0x7D, 0x79, 0x40, 0x00 }, // 0x84
  { 0x21, 0x75, 0x55, 0x54, 0x7C, 0x78, 0x40, 0x00 }, // 0x85
  { 0x20, 0x74, 0x57, 0x57, 0x7C, 0x78, 0x40, 0x00 }, // 0x86
  { 0x18, 0x3C, 0xA4, 0xA4, 0xE4, 0x40, 0x00, 0x00 }, // 0x87
  { 0x02, 0x3B, 0x7D, 0x55, 0x55, 0x5D, 0x1B, 0x02 }, // 0x88
  { 0x39, 0x7D, 0x54, 0x54, 0x5D, 0x19, 0x00, 0x00 }, // 0x89
  { 0x39, 0x7D, 0x55, 0x54, 0x5C, 0x18, 0x00, 0x00 }, // 0x8A
  { 0x01, 0x45, 0x7C, 0x7C, 0x41, 0x01, 0x00, 0x00 }, // 0x8B
  { 0x02, 0x03, 0x45, 0x7D, 0x7D, 0x43, 0x02, 0x00 }, // 0x8C
  { 0x01, 0x45, 0x7D, 0x7C, 0x40, 0x00, 0x00, 0x00 }, // 0x8D
  { 0x79, 0x7D, 0x16, 0x12, 0x16, 0x7D, 0x79, 0x00 }, // 0x8E
  { 0x70, 0x78, 0x2B, 0x2B, 0x78, 0x70, 0x00, 0x00 }, // 0x8F
  { 0x44, 0x7C, 0x7C, 0x55, 0x55, 0x45, 0x00, 0x00 }, // 0x90
  { 0x20, 0x74, 0x54, 0x54, 0x7C, 0x7C, 0x54, 0x54 }, // 0x91
  { 0x7C, 0x7E, 0x0B, 0x09, 0x7F, 0x7F, 0x49, 0x00 }, // 0x92
  { 0x32, 0x7B, 0x49, 0x49, 0x7B, 0x32, 0x00, 0x00 }, // 0x93
  { 0x32, 0x7A, 0x48, 0x48, 0x7A, 0x32, 0x00, 0x00 }, // 0x94
  { 0x32, 0x7A, 0x4A, 0x48, 0x78, 0x30, 0x00, 0x00 }, // 0x95
  { 0x3A, 0x7B, 0x41, 0x41, 0x7B, 0x7A, 0x40, 0x00 }, // 0x96
  { 0x3A, 0x7A, 0x42, 0x40, 0x78, 0x78, 0x40, 0x00 }, // 0x97
  { 0x9A, 0xBA, 0xA0, 0xA0, 0xFA, 0x7A, 0x00, 0x00 }, // 0x98
  { 0x01, 0x19, 0x3C, 0x66, 0x66, 0x3C, 0x19, 0x01 }, // 0x99
  { 0x3D, 0x7D, 0x40, 0x40, 0x7D, 0x3D, 0x00, 0x00 }, // 0x9A
  { 0x18, 0x3C, 0x24, 0xE7, 0xE7, 0x24, 0x24, 0x00 }, // 0x9B
  { 0x68, 0x7E, 0x7F, 0x49, 0x43, 0x66, 0x20, 0x00 }, // 0x9C
  { 0x2B, 0x2F, 0xFC, 0xFC, 0x2F, 0x2B, 0x00, 0x00 }, // 0x9D
  { 0xFF, 0xFF, 0x09, 0x09, 0x2F, 0xF6, 0xF8, 0xA0 }, // 0x9E
  { 0x40, 0xC0, 0x88, 0xFE, 0x7F, 0x09, 0x03, 0x02 }, // 0x9F
  { 0x20, 0x74, 0x54, 0x55, 0x7D, 0x79, 0x40, 0x00 }, // 0xA0
  { 0x00, 0x44, 0x7D, 0x7D, 0x41, 0x00, 0x00, 0x00 }, // 0xA1
  { 0x30, 0x78, 0x48, 0x4A, 0x7A, 0x32, 0x00, 0x00 }, // 0xA2
  { 0x38, 0x78, 0x40, 0x42, 0x7A, 0x7A, 0x40, 0x00 }, // 0xA3
  { 0x7A, 0x7A, 0x0A, 0x0A, 0x7A, 0x70, 0x00, 0x00 }, // 0xA4
  { 0x7D, 0x7D, 0x19, 0x31, 0x7D, 0x7D, 0x00, 0x00 }, // 0xA5
  { 0x00, 0x26, 0x2F, 0x29, 0x2F, 0x2F, 0x28, 0x00 }, // 0xA6
  { 0x00, 0x26, 0x2F, 0x29, 0x2F, 0x26, 0x00, 0x00 }, // 0xA7
  { 0x30, 0x78, 0x4D, 0x45, 0x60, 0x20, 0x00, 0x00 }, // 0xA8
  { 0x38, 0x38, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 }, // 0xA9
  { 0x08, 0x08, 0x08, 0x08, 0x38, 0x38, 0x00, 0x00 }, // 0xAA
  { 0x4F, 0x6F, 0x30, 0x18, 0xCC, 0xEE, 0xBB, 0x91 }, // 0xAB
  { 0x4F, 0x6F, 0x30, 0x18, 0x6C, 0x76, 0xFB, 0xF9 }, // 0xAC
  { 0x00, 0x00, 0x00, 0x7B, 0x7B, 0x00, 0x00, 0x00 }, // 0xAD
  { 0x08, 0x1C, 0x36, 0x22, 0x08, 0x1C, 0x36, 0x22 }, // 0xAE
  { 0x22, 0x36, 0x1C, 0x08, 0x22, 0x36, 0x1C, 0x08 }, // 0xAF
  { 0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00, 0x55, 0x00 }, // 0xB0
  { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 }, // 0xB1
  { 0xDD, 0xFF, 0xAA, 0x77, 0xDD, 0xAA, 0xFF, 0x77 }, // 0xB2
  { 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00 }, // 0xB3
  { 0x10, 0x10, 0x10, 0xFF, 0xFF, 0x00, 0x00, 0x00 }, // 0xB4
  { 0x14, 0x14, 0x14, 0xFF, 0xFF, 0x00, 0x00, 0x00 }, // 0xB5
  { 0x10, 0x10, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00 }, // 0xB6
  { 0x10, 0x10, 0xF0, 0xF0, 0x10, 0xF0, 0xF0, 0x00 }, // 0xB7
  { 0x14, 0x14, 0x14, 0xFC, 0xFC, 0x00, 0x00, 0x00 }, // 0xB8
  { 0x14, 0x14, 0xF7, 0xF7, 0x00, 0xFF, 0xFF, 0x00 }, // 0xB9
  { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00 }, // 0xBA
  { 0x14, 0x14, 0xF4, 0xF4, 0x04, 0xFC, 0xFC, 0x00 }, // 0xBB
  { 0x14, 0x14, 0x17, 0x17, 0x10, 0x1F, 0x1F, 0x00 }, // 0xBC
  { 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x1F, 0x1F, 0x00 }, // 0xBD
  { 0x14, 0x14, 0x14, 0x1F, 0x1F, 0x00, 0x00, 0x00 }, // 0xBE
  { 0x10, 0x10, 0x10, 0xF0, 0xF0, 0x00, 0x00, 0x00 }, // 0xBF
  { 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x10, 0x10, 0x10 }, // 0xC0
  { 0x10, 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10 }, // 0xC1
  { 0x10, 0x10, 0x10, 0xF0, 0xF0, 0x10, 0x10, 0x10 }, // 0xC2
  { 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x10, 0x10 }, // 0xC3
  { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, // 0xC4
  { 0x10, 0x10, 0x10, 0xFF, 0xFF, 0x10, 0x10, 0x10 }, // 0xC5
  { 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x14, 0x14, 0x14 }, // 0xC6
  { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x10 }, // 0xC7
  { 0x00, 0x00, 0x1F, 0x1F, 0x10, 0x17, 0x17, 0x14 }, // 0xC8
  { 0x00, 0x00, 0xFC, 0xFC, 0x04, 0xF4, 0xF4, 0x14 }, // 0xC9
  { 0x14, 0x14, 0x17, 0x17, 0x10, 0x17, 0x17, 0x14 }, // 0xCA
  { 0x14, 0x14, 0xF4, 0xF4, 0x04, 0xF4, 0xF4, 0x14 }, // 0xCB
  { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xF7, 0xF7, 0x14 }, // 0xCC
  { 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14 }, // 0xCD
  { 0x14, 0x14, 0xF7, 0xF7, 0x00, 0xF7, 0xF7, 0x14 }, // 0xCE
  { 0x14, 0x14, 0x14, 0x17, 0x17, 0x14, 0x14, 0x14 }, // 0xCF
  { 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x1F, 0x1F, 0x10 }, // 0xD0
  { 0x14, 0x14, 0x14, 0xF4, 0xF4, 0x14, 0x14, 0x14 }, // 0xD1
  { 0x10, 0x10, 0xF0, 0xF0, 0x10, 0xF0, 0xF0, 0x10 }, // 0xD2
  { 0x00, 0x00, 0x1F, 0x1F, 0x10, 0x1F, 0x1F, 0x10 }, // 0xD3
  { 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x14, 0x14, 0x14 }, // 0xD4
  { 0x00, 0x00, 0x00, 0xFC, 0xFC, 0x14, 0x14, 0x14 }, // 0xD5
  { 0x00, 0x00, 0xF0, 0xF0, 0x10, 0xF0, 0xF0, 0x10 }, // 0xD6
  { 0x10, 0x10, 0xFF, 0xFF, 0x10, 0xFF, 0xFF, 0x10 }, // 0xD7
  { 0x14, 0x14, 0x14, 0xFF, 0xFF, 0x14, 0x14, 0x14 }, // 0xD8
  { 0x10, 0x10, 0x10, 0x1F, 0x1F, 0x00, 0x00, 0x00 }, // 0xD9
  { 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x10, 0x10, 0x10 }, // 0xDA
  { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // 0xDB
  { 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0 }, // 0xDC
  { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 }, // 0xDD
  { 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF }, // 0xDE
  { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F }, // 0xDF
  { 0x38, 0x7C, 0x44, 0x6C, 0x38, 0x6C, 0x44, 0x00 }, // 0xE0
  { 0xFC, 0xFE, 0x2A, 0x2A, 0x3E, 0x14, 0x00, 0x00 }, // 0xE1
  { 0x7E, 0x7E, 0x02, 0x02, 0x06, 0x06, 0x00, 0x00 }, // 0xE2
  { 0x02, 0x7E, 0x7E, 0x02, 0x7E, 0x7E, 0x02, 0x00 }, // 0xE3
  { 0x63, 0x77, 0x5D, 0x49, 0x63, 0x63, 0x00, 0x00 }, // 0xE4
  { 0x38, 0x7C, 0x44, 0x7C, 0x3C, 0x04, 0x04, 0x00 }, // 0xE5
  { 0x80, 0xFE, 0x7E, 0x20, 0x20, 0x3E, 0x1E, 0x00 }, // 0xE6
  { 0x04, 0x06, 0x02, 0x7E, 0x7C, 0x06, 0x02, 0x00 }, // 0xE7
  { 0x99, 0xBD, 0xE7, 0xE7, 0xBD, 0x99, 0x00, 0x00 }, // 0xE8
  { 0x1C, 0x3E, 0x6B, 0x49, 0x6B, 0x3E, 0x1C, 0x00 }, // 0xE9
  { 0x4C, 0x7E, 0x73, 0x01, 0x73, 0x7E, 0x4C, 0x00 }, // 0xEA
  { 0x30, 0x78, 0x4A, 0x4F, 0x7D, 0x39, 0x00, 0x00 }, // 0xEB
  { 0x18, 0x3C, 0x24, 0x3C, 0x3C, 0x24, 0x3C, 0x18 }, // 0xEC
  { 0x98, 0xFC, 0x64, 0x3C, 0x3E, 0x27, 0x3D, 0x18 }, // 0xED
  { 0x1C, 0x3E, 0x6B, 0x49, 0x49, 0x00, 0x00, 0x00 }, // 0xEE
  { 0x7E, 0x7F, 0x01, 0x01, 0x7F, 0x7E, 0x00, 0x00 }, // 0xEF
  { 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00 }, // 0xF0
  { 0x44, 0x44, 0x5F, 0x5F, 0x44, 0x44, 0x00, 0x00 }, // 0xF1
  { 0x40, 0x51, 0x5B, 0x4E, 0x44, 0x40, 0x00, 0x00 }, // 0xF2
  { 0x40, 0x44, 0x4E, 0x5B, 0x51, 0x40, 0x00, 0x00 }, // 0xF3
  { 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x01, 0x07, 0x06 }, // 0xF4
  { 0x60, 0xE0, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0x00 }, // 0xF5
  { 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x08, 0x00, 0x00 }, // 0xF6
  { 0x24, 0x36, 0x12, 0x36, 0x24, 0x36, 0x12, 0x00 }, // 0xF7
  { 0x00, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00 }, // 0xF8
  { 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00 }, // 0xF9
  { 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00 }, // 0xFA
  { 0x10, 0x30, 0x70, 0xC0, 0xFF, 0xFF, 0x01, 0x01 }, // 0xFB
  { 0x00, 0x1F, 0x1F, 0x01, 0x1F, 0x1E, 0x00, 0x00 }, // 0xFC
  { 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x00, 0x00 }, // 0xFD
  { 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00 }, // 0xFE
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0xFF
};  //  end of cp437_font


They look like this:



This is stored in PROGMEM to save RAM in your sketch. To send a letter we just grab the 8 bytes (per character) and send them to the display like this:


 void letter (const byte c)
 {
  for (byte col = 0; col < 8; col++)
    sendByte (col + 1, pgm_read_byte (&cp437_font [c] [col]));
 }  // end of letter


We add one to the column because the digits are 1 to 8, not 0 to 7.

We can send a string of characters like this:


void showString (const char * s, const unsigned long time)
{
  char c;
  while (c = *s++)
    {
    letter (c); 
    delay (time);
    letter (' ');  // brief gap between letters
    delay (10);      
    }
}  // end of showString


Since I only had one matrix, I displayed a string a letter at a time, with a specified pause for you to view it. There is also a brief blank interval between letters (for things like "ee" in "greet") so the flicker shows you have changed letters.

The whole sketch is as follows:


#include <SPI.h>
#include <avr/pgmspace.h>
#include "font.h"

const byte SS = 10;  // omit this line for Arduino 1.0 onwards

 // define max7219 registers
const byte MAX7219_REG_NOOP        = 0x0;
const byte MAX7219_REG_DIGIT0      = 0x1;
const byte MAX7219_REG_DIGIT1      = 0x2;
const byte MAX7219_REG_DIGIT2      = 0x3;
const byte MAX7219_REG_DIGIT3      = 0x4;
const byte MAX7219_REG_DIGIT4      = 0x5;
const byte MAX7219_REG_DIGIT5      = 0x6;
const byte MAX7219_REG_DIGIT6      = 0x7;
const byte MAX7219_REG_DIGIT7      = 0x8;
const byte MAX7219_REG_DECODEMODE  = 0x9;
const byte MAX7219_REG_INTENSITY   = 0xA;
const byte MAX7219_REG_SCANLIMIT   = 0xB;
const byte MAX7219_REG_SHUTDOWN    = 0xC;
const byte MAX7219_REG_DISPLAYTEST = 0xF;
 

void sendByte (const byte reg, const byte data)
  {    
  digitalWrite (SS, LOW);
  SPI.transfer (reg);
  SPI.transfer (data);
  digitalWrite (SS, HIGH); 
  }  // end of sendByte
 
 
void letter (const byte c)
 {
  for (byte col = 0; col < 8; col++)
    sendByte (col + 1, pgm_read_byte (&cp437_font [c] [col]));
 }  // end of letter
 
void showString (const char * s, const unsigned long time)
{
  char c;
  while (c = *s++)
    {
    letter (c); 
    delay (time);
    letter (' ');  // brief gap between letters
    delay (10);      
    }
}  // end of showString

void setup () {
 
  SPI.begin ();
    
  sendByte (MAX7219_REG_SCANLIMIT, 7);   // show all 8 digits
  sendByte (MAX7219_REG_DECODEMODE, 0);  // using an led matrix (not digits)
  sendByte (MAX7219_REG_DISPLAYTEST, 0); // no display test
  
  // clear display
  for (byte col = 0; col < 8; col++)
    sendByte (col + 1, 0);

  sendByte (MAX7219_REG_INTENSITY, 7);  // character intensity: range: 0 to 15
  sendByte (MAX7219_REG_SHUTDOWN, 1);   // not in shutdown mode (ie. start it up)
                                        
}   // end of setup
 
void loop () 
 {
 showString ("Nick Gammon greets you! ", 500);
 }  // end of loop


Finished!


Photo of it in operation, displaying the lower-case letter "c":


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #1 on Tue 31 Jan 2012 03:50 AM (UTC)

Amended on Wed 18 Jun 2014 09:35 PM (UTC) by Nick Gammon

Message
Connecting a 7-segment LED


And now, just using the MAX7219 with a 7-segment display, like this:



I got the LED display from IteadStudio for around $US 2 each:

http://iteadstudio.com/store/index.php?main_page=product_info&cPath=35_39&products_id=205

The part number on that page seems to have changed, but the pin-outs look the same as I am using here. Or maybe this one:

http://iteadstudio.com/store/index.php?main_page=product_info&cPath=35_39&products_id=343

This is a 4-segment common-cathode display. It's important to get a common-cathode display because of the way the MAX7219 works.

The datasheet that I found shows:



Once again it is hard to tell where pin 1 is, but guessing it is in the same place as the other one (on the left with the part number towards you) that appeared to be confirmed by testing. To show on a photo:



Schematic


This time the wiring is pretty simple. Just follow the wiring diagram for the LED display and connect up the wires as marked:




Program code


This simple Arduino sketch shows how you can send numbers to the 7-segment display. This time we can let the MAX7219 work out which segments to light up by switching to "digits" mode. We also set the scan limit to 3 (in other words, 4 segments) which makes each one brighter.


#include <SPI.h>
const byte SS = 10;  // omit this line for Arduino 1.0 onwards

const byte MAX7219_REG_NOOP        = 0x0;
// codes 1 to 8 are digit positions 1 to 8
const byte MAX7219_REG_DECODEMODE  = 0x9;
const byte MAX7219_REG_INTENSITY   = 0xA;
const byte MAX7219_REG_SCANLIMIT   = 0xB;
const byte MAX7219_REG_SHUTDOWN    = 0xC;
const byte MAX7219_REG_DISPLAYTEST = 0xF;

void sendByte (const byte reg, const byte data)
  {    
  digitalWrite (SS, LOW);
  SPI.transfer (reg);
  SPI.transfer (data);
  digitalWrite (SS, HIGH); 
  }  // end of sendByte
 
void setup () 
  {
  SPI.begin ();
  sendByte (MAX7219_REG_SCANLIMIT, 3);      // show 4 digits
  sendByte (MAX7219_REG_DECODEMODE, 0xFF);  // use digits (not bit patterns)
  sendByte (MAX7219_REG_DISPLAYTEST, 0);    // no display test
  sendByte (MAX7219_REG_INTENSITY, 7);      // character intensity: range: 0 to 15
  sendByte (MAX7219_REG_SHUTDOWN, 1);       // not in shutdown mode (ie. start it up)
}   // end of setup
 
void number (const int num)
  {
 
 char buf [5];
 sprintf (buf, "%4i", min (max (num, 0), 9999));
 
 // send all 4 digits
 for (byte digit = 0; digit < 4; digit++)
   {
   byte c = buf [digit];
   if (c == ' ' )
     c = 0xF;  // code for a blank
   else
     c -= '0';
   sendByte (digit + 1, c);  
   }   
  }  // end of number
  
unsigned int i;

void loop () 
 {
 number (i++);
 delay (100);
 }  // end of loop



This sketch just outputs continuously "counting up" numbers.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #2 on Wed 13 Mar 2013 05:38 AM (UTC)

Amended on Mon 25 Mar 2013 03:02 AM (UTC) by Nick Gammon

Message
Putting it all together


I found a kit featuring the MAX7219, plus the 8x8 LED unit, plus a printed circuit board on eBay for $8 with free shipping.

This is incredibly cheap for what you get. Try searching for:

"MAX7219 Dot matrix module MCU control Display module DIY kits for Arduino"

This is what you get:



Soldered together:



Displaying the letter "W":



If you use this kit you need to modify the "letter" function to omit the change I made for my wiring method. It should read (for the kit):


 void letter (const byte c)
 {
  for (byte col = 0; col < 8; col++)
    {
    byte b = pgm_read_byte (&cp437_font [c] [col]);
    sendByte (col + 1, b); 
    }   // end of for each column
 }  // end of letter 


The kit is designed to "daisy-chain" the boards, so the DOUT of each board goes into the DIN of the next one, thus you could display multiple letters.

Displaying text upside down


These devices might be more easily mounted upside down (with the chip at the top and the flat surface at the bottom). In this case you can rotate the letters 180 degrees by replacing the "letter" function by this:


void letter (const byte c)
 {
  for (byte col = 0; col < 8; col++)
    {
    byte b = pgm_read_byte (&cp437_font [c] [col]);
    // flip letter over because I have the thing upside down
    byte d = 0;
    for (byte i = 0; i < 8; i++)
      {
      d <<= 1;  
      d |= b & 1;
      b >>= 1;
      }
    sendByte (8 - col, d);
    }   // end of for each column
 }  // end of letter

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #3 on Mon 21 Oct 2013 06:02 AM (UTC)

Amended on Mon 21 Oct 2013 06:14 AM (UTC) by Nick Gammon

Message
Daisy-chaining chips


The MAX7219 is designed to be daisy-chained so you can have multiple 64-pixel displays connected together (like those scrolling signs you see in shop windows).

To make use of this you basically have to treat them like a serial of shift register. When SS goes high the last 16 bits that were shifted out are latched into each display. So if you have two displays and you want to affect the further one then you have to send 32 bits, the first 16 are the "real" data, followed by a NOP (0x00) and another 0x00. The chips are designed to ignore the NOP, so only the further chip is affected. Of course, if you had three chips you would have to send:


REGISTER DATA NOP NOP NOP NOP


That way you are sending 48 bits, which get propagated along the chain to affect the third chip.

However if you have three chips and you want to update the middle one you need NOPs at each side, eg.


NOP NOP REGISTER DATA NOP NOP


Otherwise subsequent chips down the chain update when you don't want them to.

If you want to send the same thing to each chip (Eg. configuration) then you don't need the NOPs, you just have to send REGISTER/DATA x number of times, where x is the count of chips.

Here is an example of addressing two chips (in number mode) which between them display a 16-digit number.



#include <SPI.h>

const int CHIP_COUNT = 2;  // how many MAX7219s

const byte MAX7219_REG_NOOP        = 0x0;
// codes 1 to 8 are digit positions 1 to 8
const byte MAX7219_REG_DECODEMODE  = 0x9;
const byte MAX7219_REG_INTENSITY   = 0xA;
const byte MAX7219_REG_SCANLIMIT   = 0xB;
const byte MAX7219_REG_SHUTDOWN    = 0xC;
const byte MAX7219_REG_DISPLAYTEST = 0xF;

void sendToAll (const byte reg, const byte data)
  {    
  digitalWrite (SS, LOW);
  for (int chip = 0; chip < CHIP_COUNT; chip++)
    {
    SPI.transfer (reg);
    SPI.transfer (data);
    }
  digitalWrite (SS, HIGH); 
  }  // end of sendToAll
 
void setup () 
  {
  SPI.begin ();
  sendToAll (MAX7219_REG_SCANLIMIT, 7);      // show 8 digits
  sendToAll (MAX7219_REG_DECODEMODE, 0xFF);  // use digits (not bit patterns)
  sendToAll (MAX7219_REG_DISPLAYTEST, 0);    // no display test
  sendToAll (MAX7219_REG_INTENSITY, 15);      // character intensity: range: 0 to 15
  sendToAll (MAX7219_REG_SHUTDOWN, 1);       // not in shutdown mode (ie. start it up)
}   // end of setup
 
void sendChar (const int which, const char c)
  {
  byte i;
  
  // segment is in range 1 to 8
  const byte segment = 8 - (which % 8);
  // for each daisy-chained display we need an extra NOP
  const byte nopCount = which / 8;
  // start sending
  digitalWrite (SS, LOW);
  // start with enough NOPs so later chips don't update
  for (int i = 0; i < CHIP_COUNT - nopCount - 1; i++)
    {
    SPI.transfer (MAX7219_REG_NOOP);
    SPI.transfer (MAX7219_REG_NOOP);  // need 16 bits of NOP
    }    
  // send the segment number and data
  SPI.transfer (segment);
  SPI.transfer (c);
  // send extra NOPs to push the data out to extra displays
  for (byte i = 0; i < nopCount; i++)
    {
    SPI.transfer (MAX7219_REG_NOOP);
    SPI.transfer (MAX7219_REG_NOOP);  // need 16 bits of NOP
    }    
  // all done!
  digitalWrite (SS, HIGH); 
  }  // end of sendChar
  
// write an entire null-terminated string to the LEDs
void sendString (const char * s)
{
  for (int pos = 0; *s; pos++)
    sendChar (pos, *s++);
}  // end of sendString
  
void loop () 
 {
 sendString ("012345678901234567");
 delay (100);
 }  // end of loop

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #4 on Tue 17 Mar 2015 03:21 AM (UTC)
Message
Library to simplify using this chip


I have written a library to make using the chip easier for 7-segment displays, like this one from eBay:



The library can be downloaded from GitHub:

https://github.com/nickgammon/MAX7219

Install in your libraries folder (under your sketchbook folder) and restart the IDE.

A simple example of using it:


#include <SPI.h>
#include <bitBangedSPI.h>
#include <MAX7219.h>

const byte chips = 1;

// 1 chip, bit banged SPI on pins 6, 7, 8
MAX7219 display (chips, 6, 7, 8);  // Chips / LOAD / DIN / CLK

void setup ()
  {
  display.begin ();
  display.sendString ("HELLO");
  }  // end of setup

void loop () { }


You can have multiple daisy-chained chips. You can use either hardware or bit-banged SPI.

The bit-banged SPI library is available here:

https://github.com/nickgammon/bitBangedSPI

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #5 on Fri 20 Mar 2015 04:15 AM (UTC)

Amended on Wed 01 Apr 2015 03:58 AM (UTC) by Nick Gammon

Message
Using a MAX7219 8-digit LED display module


You can buy 8-digit display modules from eBay for around $US 2 to $US 5 each.

I got a batch of these ones a while ago, they are designed to butt up against each other vertically or horizontally, like this:



The reverse side:



The decimal point on the LED modules is on the bottom - that is adjacent to pin 1.


The modules come with the MAX7219 chip, a chip socket, some 10 µF capacitors (2 per board), a resistor (1 per board),
and some connecting pins.



The LED modules - available in various colours:






Assembly

Start by soldering on the two capacitors and the resistor - orientation does not matter.



Detail for the surface-mounted devices:





The SMD parts might look a bit daunting, but if you stay calm they can be soldered OK. The main problem is they are so small they tend to skitter away like frightened mice. You could try to hold them down with an alligator clip:



Or tape one side down and solder the other:



Once you have one side soldered you can remove the restraining device and solder the other side.




Solder together the links and the earth pads:



Add a single connector for data from the Arduino:



Note the data flow direction. The DIN (Data in) pin starts at one side and then flows out of the DOUT (Data out) pin into the DIN pin on the next board. Data direction shown by the arrows.




Solder on the chip sockets before the LEDs or you won't be able to do it afterwards.

Then turn it over and install the LEDs. I suggest putting all the LED modules on before you solder, in case you need to push them sideways slightly to make them fit. If you solder one in first that might be impossible later.

Finished product:



Plugged into the Arduino and running a test program:




#include <SPI.h>
#include <bitBangedSPI.h>
#include <MAX7219.h>

const byte chips = 3;

// 3 chips, bit banged SPI on pins 6, 7, 8
MAX7219 display (chips, 6, 7, 8);  // Chips / LOAD / DIN / CLK

void setup ()
  {
  display.begin ();
  display.sendString ("Testing 12345678ABCDEFGH");
  }  // end of setup

void loop () { }


Note that the furthest module from the data plug gets the first part of the text ("Testing" in this case). This is because the letters are "pushed" from one module to the next.




You could also solder them sideways if you just wanted to display a long number with more than 8 digits.

Scrolling display


For a scrolling display you can have a longer message, as in the example below, and pull out 8 (or 16 or 24) characters at a time from inside that message, and display that. Next time, move forwards one byte. Eventually wrap around back to the start.


#include <SPI.h>
#include <bitBangedSPI.h>
#include <MAX7219.h>

const byte chips = 1;

// 1 chip, bit banged SPI on pins 6, 7, 8
MAX7219 display (chips, 6, 7, 8);  // Chips / LOAD / DIN / CLK

const char message [] = "Hello there - testing 123456789 ";

void setup ()
  {
  display.begin ();
  }  // end of setup

unsigned long lastMoved = 0;
unsigned long MOVE_INTERVAL = 300;  // mS
unsigned int  messageOffset;

void updateDisplay ()
  {
  const unsigned int messageLength = strlen (message);
  
  // get each byte required
  for (byte i = 0; i < (8 * chips); i++)
    {
    // find the offset in the message array
    unsigned int charOffset = messageOffset + i;
    // if we have passed the end, go back to the start
    if (charOffset >= messageLength)
       charOffset -=  messageLength;
    // send that character
    display.sendChar (i, message [charOffset]);
    }
  // next time show one character on
  if (messageOffset++ >= messageLength)
    messageOffset = 0;
  }  // end of updateDisplay

void loop () 
  { 
    
  // update display if time is up
  if (millis () - lastMoved >= MOVE_INTERVAL)
    {
    updateDisplay ();
    lastMoved = millis ();
    }

  // do other stuff here    
     
  }  // end of loop

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #6 on Wed 13 May 2015 03:43 AM (UTC)

Amended on Fri 02 Oct 2015 02:00 AM (UTC) by Nick Gammon

Message
Library for the 8x8 LED matrix


I got some 8x8 LED modules from eBay, which look like this:



There are 4 of the 8x8 LED modules on one board. It can pre-assembled with a cable for connecting to the Arduino or daisy-chaining to another module.

These were $US 8.50 each, which isn't bad considering you get 4 x MAX7219 chips, the LEDs and the board for that price.

Back of board:



Note the data direction. The Arduino plugs into the IN end and you connect further modules to the OUT end.

Example of 3 such modules connected together (12 characters):



To simplify using them I modified the library above to make a version which supports the dot-matrix module. This can be downloaded from:

https://github.com/nickgammon/MAX7219_Dot_Matrix

Install in your libraries folder (under your sketchbook folder) and restart the IDE.




A simple example of using it:


// Demo of MAX7219_Dot_Matrix library
// Author: Nick Gammon
// Date: 13 May 2015

#include <SPI.h>
#include <bitBangedSPI.h>
#include <MAX7219_Dot_Matrix.h>

MAX7219_Dot_Matrix myDisplay (8, 10);  // 8 chips, and then specify the LOAD pin only

void setup ()
  {  
  myDisplay.begin ();
  myDisplay.sendString ("Hello there");
  }  // end of setup

void loop ()
  {
  // do whatever here
  }  // end of loop



You can have multiple daisy-chained chips. You can use either hardware or bit-banged SPI.

The bit-banged SPI library is available here:

https://github.com/nickgammon/bitBangedSPI

There is also a demo of scrolling text, however that scrolls a chip at a time, not a pixel at a time.

Smooth scrolling


Library updated on 2nd October 2015 to have smooth scrolling. A new example has been added which demonstrates this. To achieve this each for each display module (8 x 8 pixels) the code calculates the pixels for the byte before, the current byte, and the byte after. Then it applies the pixel offset to choose a starting point somewhere in those 24 (horizontally) pixels.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #7 on Sat 03 Oct 2015 12:02 AM (UTC)
Message
Warning regarding current consumption


During testing of the MAX7219 display modules, I noticed some unreliable behaviour. This was caused by the high current requirements of these modules, particularly if a lot of pixels are on, and it is in high-intensity mode.

For example, checking a single 64-pixel module:

All LEDs on (sending the character 0xDB):


Intensity  Milliamps
--------------------
    1          35
    7         131
   15         236


Some LEDs on (sending the letter 'A'):


Intensity  Milliamps
--------------------
    1          19
    7          68
   15         126


You can see from that, that if you had 16 modules (ie. displaying 16 characters at once) in full intensity, and if they happened to power on with all bits set, you would need:


16 * 236 = 3.776 amps


Yes, almost 4 amps! Now, there is no way that can be powered from the Arduino board (at least not without getting a big voltage drop, which could make the processor reset repeatedly, or making the voltage regulator get very hot).

For running multiple chips like this (for example, for a scrolling LED sign) you would need to provide a separate 5V power supply, suitably rated (eg. 5 amps) for the LED strip. Of course, the Ground wires need to be connected to the Arduino ground as well.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #8 on Tue 10 Apr 2018 06:05 AM (UTC)

Amended on Thu 12 Apr 2018 05:46 AM (UTC) by Nick Gammon

Message
MAX7219 and the KWL-R1230CDUGB 12-LED bar graph


There is a 12-bar LED module that you can get from Adafruit and other places that looks like this:



Bi-Color (Red/Green) 12-LED Bargraph - Pack of 2

There are 12 LED segments, each of which can be lit in red or green, and if you light both together you get yellow, so they could be used as a "safe/warning/danger" signal.

They would be tedious to wire with something like a shift register because you would need lots of current limiting resistors, and some driver transistors. The MAX7219 however can drive two of them fairly simply.

Internally they look like this:



Schematic:



Since these are common-cathode displays the cathodes go to the "digit" output of the MAX7219 (using 6 out of the possible 8 of them, if we are using two bargraphs) plus the anodes go to the anode pins, and are paralled up if we want more than one LED module.

Code



#include <SPI.h>

 // define max7219 registers
const byte MAX7219_REG_NOOP        = 0x0;
const byte MAX7219_REG_DIGIT0      = 0x1;
const byte MAX7219_REG_DIGIT1      = 0x2;
const byte MAX7219_REG_DIGIT2      = 0x3;
const byte MAX7219_REG_DIGIT3      = 0x4;
const byte MAX7219_REG_DIGIT4      = 0x5;
const byte MAX7219_REG_DIGIT5      = 0x6;
const byte MAX7219_REG_DIGIT6      = 0x7;
const byte MAX7219_REG_DIGIT7      = 0x8;
const byte MAX7219_REG_DECODEMODE  = 0x9;
const byte MAX7219_REG_INTENSITY   = 0xA;
const byte MAX7219_REG_SCANLIMIT   = 0xB;
const byte MAX7219_REG_SHUTDOWN    = 0xC;
const byte MAX7219_REG_DISPLAYTEST = 0xF;

// which groups of bars we are addressing
const byte BARS_1_4   = MAX7219_REG_DIGIT0;
const byte BARS_5_8   = MAX7219_REG_DIGIT1;
const byte BARS_9_12  = MAX7219_REG_DIGIT2;
const byte BARS_13_16 = MAX7219_REG_DIGIT3;
const byte BARS_17_20 = MAX7219_REG_DIGIT4;
const byte BARS_21_24 = MAX7219_REG_DIGIT5;

// within a group, which one (from the left)
const byte BAR1_RED = 0x80;
const byte BAR2_RED = 0x20;
const byte BAR3_RED = 0x08;
const byte BAR4_RED = 0x02;

const byte BAR1_GREEN = 0x40;
const byte BAR2_GREEN = 0x10;
const byte BAR3_GREEN = 0x04;
const byte BAR4_GREEN = 0x01;

// yellow is done by adding red and green
const byte BAR1_YELLOW = BAR1_RED + BAR1_GREEN;
const byte BAR2_YELLOW = BAR2_RED + BAR2_GREEN;
const byte BAR3_YELLOW = BAR3_RED + BAR3_GREEN;
const byte BAR4_YELLOW = BAR4_RED + BAR4_GREEN;

void sendByte (const byte reg, const byte data)
  {    
  digitalWrite (SS, LOW);
  SPI.transfer (reg);
  SPI.transfer (data);
  digitalWrite (SS, HIGH); 
  }  // end of sendByte
 
void setup () {
 
  SPI.begin ();
    
  sendByte (MAX7219_REG_SCANLIMIT, 5);   // show 6 digits
  sendByte (MAX7219_REG_DECODEMODE, 0);  // using an led matrix (not digits)
  sendByte (MAX7219_REG_DISPLAYTEST, 0); // no display test
  
  // clear display
  for (byte col = 0; col < 8; col++)
    sendByte (col + 1, 0);

  sendByte (MAX7219_REG_INTENSITY, 7);  // character intensity: range: 0 to 15
  sendByte (MAX7219_REG_SHUTDOWN, 1);   // not in shutdown mode (ie. start it up)
}   // end of setup
 
void loop () 
 {
  // send various test combinations
  sendByte(BARS_1_4, BAR1_RED | BAR2_GREEN | BAR3_RED | BAR4_GREEN);
  sendByte(BARS_5_8, BAR2_RED);
  sendByte(BARS_9_12, BAR2_YELLOW);
  sendByte(BARS_13_16, BAR1_RED | BAR2_GREEN | BAR3_RED | BAR4_GREEN);
  sendByte(BARS_17_20, BAR2_YELLOW);
  sendByte(BARS_21_24, BAR1_RED | BAR2_RED);
 }  // end of loop


The bars are in groups, so you light up a group of 4, as illustrated in "loop" above. For each group you then specify if you want red, green, or yellow, in the first/second/third/fourth position, relative to that group. Just send zero if you want all the bars to be off.

Example of them in action




As you can see from the photo, the bars are lit up in the order specified in the last few lines of the code.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


169,774 views.

Postings by administrators only.

Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.