Driving the Max7219 with the Raspberry Pi


The Max7219 lets us control lots and lots of LEDs using just a few Raspberry Pi pin-outs. No hassles with multiplexing, latching, refreshing or using up all your outputs – it handles everything for us. We just send commands to the Max7219 and we can control up to 64 LEDs or eight seven (8 including the decimal point?) segment displays, you can even chain multiple Max7219s together to drive loads more. All this via just a few pins.

Bit banging the max7219

Both the Raspberry Pi and the Max7219 support SPI (Serial Peripheral Interface?), a good idea then to the get the RPi to talk to a Max7219 via its very own SPI interface, right? It didn’t work. I enabled SPI easily enough, by default it’s turned off, and I even managed to send and receive data, I think. I just couldn’t seem to get the Max7219 to do anything apart from tease me with an ‘E’ (error?) on the 7 seg display.

Easier, I thought, to just ignore the hardware SPI and connect 3 normal digital GPIO pins to the Max7219 and bit bang directly.

Connecting pins 1 (data), 12 (load) and 13 (clock) to the RPi GPIO pins 17, 23 and 22.
Pins 4 and 9 to RPi Ground.
Pin 19 to RPi’s 5V along with pin 18 via a resistor of your choice depending on your LED.

max7219_diagram

It’s then a matter of sending commands to the Max7219 via the data in, clock and load line.

For instance, to display the number 4 on digit 1 we would send the command 0x104 (0x01 : use digit 1, 0x04 : display the number 4). Which might look something like this :

Set LOAD to 1.
Send 16 bits command and data (0x104 = 0000000100000100), and for each binary digit we : set CLOCK to 0; send the binary digit, set CLOCK to 1.
To finish, set LOAD to 0 then set LOAD to 1.

At this point it might be worth mentioning that the Max7219 can work in two modes, BCD decode on or BCD decode off. With BCD on we can just send the number ( 0 to 9 ) we want to appear to the IC and it will display it. As well as sending 0 to 9, 10 to 15 correspond to -, E, H, L, P and a blank.

With BCD set to off we can control each segment directly. Each segment corresponds to a bit (see below). We might use this mode if we were controlling a bunch of LEDs rather than a 7 segment display.

bit  –  segment
0  –  g
1  –  f
2  –  e
3  –  d
4  –  c
5  –  b
6  –  a
7  –  dp

So binary 1110111, 0x77, would display a letter A on a 7seg display.

The quick and dirty demo code, using Gordon’s excellent wiringPi library to control the GPIO pins, is here :


/*

 max7219.c
 
 Raspberry Pi driving the Max7219

 to compile : gcc max7219.c -o max7219 -lwiringPi


*/
 
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>


// define our pins :

#define DATA        0 // GPIO 17 (WiringPi pin num 0)  header pin 11
#define CLOCK       3 // GPIO 22 (WiringPi pin num 3)   header pin 15
#define LOAD        4 // GPIO 23 (WiringPi pin num 4)   header pin 16


// The Max7219 Registers :

#define DECODE_MODE   0x09                       
#define INTENSITY     0x0a                        
#define SCAN_LIMIT    0x0b                        
#define SHUTDOWN      0x0c                        
#define DISPLAY_TEST  0x0f                         



static void Send16bits (unsigned short output)
{
	
  unsigned char i;

  for (i=16; i>0; i--) 
  {
    unsigned short mask = 1 << (i - 1); // calculate bitmask
  
    digitalWrite(CLOCK, 0);  // set clock to 0
    
    // Send one bit on the data pin
    
    if (output & mask)   
      digitalWrite(DATA, 1);          
		else                              
      digitalWrite(DATA, 0);  
        
    digitalWrite(CLOCK, 1);  // set clock to 1
  	 
  }

}


// Take a reg numer and data and send to the max7219

static void MAX7219Send (unsigned char reg_number, unsigned char dataout)
{
  digitalWrite(LOAD, 1);  // set LOAD 1 to start
  Send16bits((reg_number << 8) + dataout);   // send 16 bits ( reg number + dataout )
  digitalWrite(LOAD, 0);  // LOAD 0 to latch
  digitalWrite(LOAD, 1);  // set LOAD 1 to finish
}




int main (void)
{
	
  printf ("\n\nRaspberry Pi Max7219 Test using WiringPi\n\n");

  if (wiringPiSetup () == -1) exit (1) ;

  //We need 3 output pins to control the Max7219: Data, Clock and Load

  pinMode(DATA, OUTPUT);  
  pinMode(CLOCK, OUTPUT);
  pinMode(LOAD, OUTPUT);  
	
  MAX7219Send(SCAN_LIMIT, 7);     // set up to scan all eight digits


/* 

 BCD decode mode off : data bits correspond to the segments (A-G and DP) of the seven segment display.

 BCD mode on :  0 to 15 =  0 to 9, -, E, H, L, P, and ' '
	
*/

  MAX7219Send(DECODE_MODE, 1);   // Set BCD decode mode on
 
  MAX7219Send(DISPLAY_TEST, 0);  // Disable test mode

  MAX7219Send(INTENSITY, 1);     // set brightness 0 to 15
  	
  MAX7219Send(SHUTDOWN, 1);      // come out of shutdown mode	/ turn on the digits
  	
  MAX7219Send(1,6); 		 // displays the number 6 on digit 1

  return 0;
}



25 thoughts on “Driving the Max7219 with the Raspberry Pi

  1. Hello

    Thanks for this article.
    I have a question, why are you using GPIO 17,21,22 ?
    I read that pins GPIO 9,10 (MOSI,MISO) are for SPI.

    Thank you.

    1. I’m not using the Raspberry Pi’s hardware SPI. I tried, but decided to just bitbang the Max7219 directly on normal digital out pins.

  2. It works fine without a level converter at 3.3. I should really have used a couple of capacitors as in the data sheet.

    Hadn’t seen that rpiblog site, I wouldn’t be surprised if all their posts are stolen!

  3. Hello, David. You say that Rasberry’s hardware SPI doesn’t seem to work as intended. Can you describe the way you tried to use hardware SPI? Did you try to work through BCM2835 library in your project?

    1. Hi Yuriy, I’m sure it would work for someone that understood it. I didn’t really know what I was doing. Didn’t use the BCM2835 library directly as far as I know!

      I unblacklisted the spi-bcm2708 module and then found some code (here it is : http://www.cmdrkeen.net/2012/11/06/playing-with-raspberry-pi-issue-2-using-spi-on-the-pi/ ) to test it out. The loopback test worked but I didn’t really understand how to then implement that with the max7219.

      It all seemed like an awful lot of hassle when I could just do it directly with 3 normal gpio pins and some straightforward code.

      Steve, thanks… timestamps added!

  4. I’m trying to get this working with BCD decode turned off. It works fine until I send 0xXf or 0xfX on MAXSend dataout. I’m trying to make it a logic problem, but nothing seems to be working out. Any thoughts?

  5. Hi John,

    Just ran some tests and it works fine here. What are you expecting?

    Not sure what to suggest, try looking at it in binary might make it clearer which LEDs are going to be on or off.

    MAX7219Send(1,0xff); // bank 1, all on, actual 16 bits sent : 0000000111111111 (command:00000001,data:11111111)

    MAX7219Send(1,0xf0); // bank 1, half on, 0000000111110000 (command:00000001,data:11110000)

  6. Thanks David. You answered my question, that is exactly what I am looking for it to do. It just doesn’t work on mine. Bad chip perhaps? I’d better check my wiring and try some other banks as well. I’m using the 5V from the Pi. Maybe my resistor choice is bad and I’m not getting enough potential across all eight LED’s? Ugh.

  7. Hi,

    I want to drive an 8×8 DotMatrix with a MAX7219 with my Raspberry Pi.Not a 7-Segment-Display! I need some help for the code. Im an noob in C but I think I can manage this if somebody gimme a good start. Excuse my english please.

  8. PiBwoy, the above example should be good enough as a starter. One 7seg display is just a bunch of LEDs (usually 8 despite the 7 segment name, they don’t tend to count the decimal point)… anyway, just turn BCD off and you can drive each LED/segment individually using the above code. Your English is perfect.

  9. Would I need to run your code through a loop if I wanted to display 4 different numbers on 4 different 7 segment LEDs. So I would be changing the MAX7219Send(1,6) portion of the code every loop through to the corresponding number and location I wanted it displayed? Or could I just do the following all at once in the code:
    MAX7219Send(0,6)
    MAX7219Send(1,2)
    MAX7219Send(2,4)
    MAX7219Send(3,5)

  10. Mike, either would work. Just use whatever you’re comfortable with really. I’d probably do it without a loop for just 4 function calls like your example but if you’re doing other things per call and you find yourself duplicating lots of code then I’d probably throw it in a loop and produce cleaner code.

  11. I just have a doubt: how i can wire a “7 segments 4 digits display” to the max7219?! Which pins i must use in display?

  12. Well, i tried everything. Made the wiring correcly in my 7 segments 4 digits display, but nothing happens when i run the compiled code. I’m using a 10k resistor in pin #18 of max7219. Also tried 100k, but i guess that the problem is another one. Suggestions?

    1. I don’t know it could be absolutely anything.

      Start at the very beginning, Ignore the max7219 for now, can you turn a single LED on and off with wiringpi. Just basic gpio pin switching?

  13. Remark for those using MAX7221. I tried with MAX7221, and this code works except for one thing: LOAD must be LOW (not HIGH) before sending the bits to the MAX7221:

    static void MAX7219Send (unsigned char reg_number, unsigned char dataout)
    {
    digitalWrite(LOAD, 0); // –> MAX7221: set LOAD 0 to start / MAX7219 set LOAD to 1
    Send16bits((reg_number < obsolete for MAX7221
    digitalWrite(LOAD, 1); // set LOAD 1 to finish
    }
    Hope this is helpful!

  14. Thanks – just got that working with a cheap 8 digit display using DIN (Data), CS (Clock) and Clock (CLK).

    The only hitch I had is the example code only turns on BCD decoding for one digit: After a brief look at the datasheet.

    MAX7219Send(DECODE_MODE, 0xFF); // Turn on BCD decoding for all digits and then MAX7219Send(2, 3) – works as expected..

  15. Pingback: nikkor.lino.press

Leave a Reply

Your email address will not be published. Required fields are marked *