Need 8 digit 7 Segment LED display using only 3 I/O lines?

I've been trying to avoid multiplexing for a ganged LED display and came across this LED package. You can find them listed on eBay under 5V MAX7219 8-Digit Red LED and they sell for less than $5. The library can be downloaded at:

http://www.pjrc.com/teensy/td_libs_LedControl.html

The sample code below shows how easy it is to interface with the display. I have no financial interest in the device, but found it perfect for my ATtiny85 project that requires more than 4 digits.

/*****
  Make sure your LedControl library is added to the Arduino IDE and that 
  the first exxecutable lines in the header file contain:
   
#ifndef LedControl_h
#define LedControl_h

#if (ARDUINO < 100)
#include <WProgram.h>
#else
#include <Arduino.h>
#endif

You will get error messages on Arduino IDE 1.05 if this change is not made. THe
LedControl library may be downloaded at:

http://www.pjrc.com/teensy/td_libs_LedControl.html

Documentation is also at the site.

Code provided by Dr. Jack Purdum March 12, 2014
*****/

#include <LedControl.h>
#include <stdlib.h>

#define ARDUINOUNO
//#define ARDINOMEGA    1

#ifdef ARDUINOUNO          // Uncomment ARDUINOUNO and comment out ARDUINOMEGA
#define DINPIN        10   //  to use these pins...
#define CLKPIN        13
#define LOADPIN       11
#else                      // ...otherwise MEGA pins are assumed
#define DINPIN        45
#define CLKPIN        44
#define LOADPIN       43
#endif

#define DISPLAYDIGITS 8

// inputs: DIN pin (45), CLK pin (44), LOAD pin (43). number of chips (1)
// The number of chips is one even though it controls 8 LED digit displays.
LedControl mydisplay = LedControl(DINPIN, CLKPIN, LOADPIN, 1);

void setup() {
  mydisplay.shutdown(0, false);  // turns on display
  mydisplay.setIntensity(0, 5); // 15 = brightest 
}

void loop() {
  int i, j;
  char buffer[10];

  mydisplay.clearDisplay(0);                 // Clear out previous data
  for (i = 0; i < DISPLAYDIGITS; i++) {
    for (j = 1; j < 10; j++) {
      mydisplay.setDigit(0, i, j, false);    // Count to 10
      delay(100);
    }
    mydisplay.setDigit(0, i, 0, false);      // Repeat for each digit
    delay(100);    
  }
  mydisplay.clearDisplay(0);
  ConvertNumberToString(1234, 1, buffer);    // Format with one decimal point
  delay(2000);
  mydisplay.clearDisplay(0);
  ConvertNumberToString(5678, 0, buffer);    // Format with no decimal point
  delay(2000);
}

/*****
  This function converts a number to a string representation and then displays
  it on the LED array. THe output is right-justified on the display.
  
  Parameter List:
    float val                  the number to be converted
    unsigned char precision    the number of digits after the decimal point
    char *buffer               a character array. It is assumed to be large
                               enough to hold the number represented as an
                               ASCII number plus the null.
  Return value:
    void
*****/
void ConvertNumberToString(float val, unsigned char precision, char *buffer)
{
  int i;
  int start;
   
  dtostrf(val, DISPLAYDIGITS, precision, buffer);
  start = DISPLAYDIGITS - precision - 1;
  
  for (i = 0; i < DISPLAYDIGITS; i++) {
    if (buffer[i] != ' ') {              // If we have a digit character
      if (buffer[i + 1] == '.') {        // Need a decimal point?
        mydisplay.setDigit(0, start--, buffer[i] - '0', true);   // Yes
        i++;
      } else {
        mydisplay.setDigit(0, start--, buffer[i] - '0', false);  // No
      }
    } else {
      start--;
    }  
  }   
}

Yes, MAX7219 is good for driving 8 7-segment display. The display MUST be common cathode and work from 5V (generally single LED per segment).
Another method is to use daisy chained TPIC6B595, one per digit, to sink current from common anode displays. Good for 12V displays - such as using LED strips powered from 12V for each segment. This board was designed for just that, supporting up to 12 digits, combining 12 shift registers and Arduino functionality with offboard USB/Serial adapter (adapter unplugged after programming).
http://www.crossroadsfencing.com/BobuinoRev17/

Nice! But, this looks simpler...

Depends what your display is.
Two HC595s will drive little digits, need big hardware to drive 12V LED strips.

I just got few of those modules and, I need the lib in the first message (the link no longer works)...
It' s just for test purpose, testing all leds ...

:slight_smile:

Can anybody point me to a sketch that will scroll text across one of these displays?

Thanks!

I have a post about those displays here: Gammon Forum : Electronics : Microprocessors : Interfacing LED displays with the MAX7219 driver

I wrote a library to help do the displaying. It doesn't scroll, per se, but you would just have a string of characters, and then pull out a substring of 8 at a time, every second or so. That would make it scroll.

Nick's comment for a scrolling display works and...somewhere...I wrote one for the display pictured above. I used memcpy(), something like:

memcpy(buffer, &buffer[1], 7);

and then updated the first digit with the new value. Can't put my hands on it, though.

Here is an example of a scrolling display with my library:

#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

Thanks, Nick! I'll try that out!

Hey, Nick, I tried your sketch but it didn't work on my display. It compiled and loaded just fine, but the display gives one brief flash then dies. My array has a pair of 595 chips and your sketch seems to be written for the MAX7219, I'm guessing that's where my problem lies. Thanks for posting it, though, it gave me a good starting point.

Absolutely that's where the problem lies. Using a 595 require different techniques.

Most of this thread is about the 7219 which is why I posted that link.

However I did a post about a LED strip with lots of individual LEDs, driven by 595s, and the results are here.

Nick: I'm playing with the following sketch borrowed from your excellent forum, but I'm getting an error code :

sketch_apr05a.ino: In function 'void letter(byte)':
sketch_apr05a:36: error: 'cp437_font' was not declared in this scope

I'm guessing the error is related to my use of your MAX7219 library, which I have stored in my "libraries" folder. Can you see where I'm going wrong?

#include <SPI.h>
#include <avr/pgmspace.h>
#include <MAX7219>

//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++)
    {
    byte b = pgm_read_byte (&cp437_font [c] [col]);
    sendByte (col + 1, b); 
    }   // end of for each column
 }  // 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

OK, I got that figured out--needed to put the font.h file in the same folder as the sketch. Also helps to wire the thing correctly... :-[

Nick, I appreciate the amount of effort it must take to generate the volume of information you post here and on your website--THANK YOU!

Glad you worked it out. Yes, the font file goes into the sketch in a separate tab.

Nick, I appreciate the amount of effort it must take to generate the volume of information you post here and on your website--THANK YOU!

You are welcome, and thanks for the comment. :slight_smile:

Nick: Another dumb newbie question for you:

How can I use your "font.h" characters in this sketch:

#include "LedControlMS.h"

/*
  pin 12 is connected to the DataIn (green)
 pin 11 is connected to the CLK (purple)
 pin 10 is connected to LOAD (grey)
 Set up for 5 MAX72XX.  Make sure you have the correct number of devices
 in all the lines of code or the message won't scroll properly,
 */
#define NBR_MTX 5 
LedControl lc=LedControl(12,11,10, NBR_MTX);


/* we always wait a bit between updates of the display */
String scrollString= "HANG UP AND DRIVE        ";
int stringLength=scrollString.length();
char ch0, ch1, ch2, ch3, ch4, ch5;
int nextCharIndex=0;


void setup() {
  /*
   The MAX72XX is in power-saving mode on startup,
   we have to do a wakeup call
   */
  Serial.begin (9600);
  Serial.println("Setup");
  Serial.println(scrollString);
  Serial.println(stringLength);

  for (int i=0; i< NBR_MTX; i++){
    lc.shutdown(i,false);
  /* Set the brightness to a low value */
    lc.setIntensity(i,1);
  /* and clear the display */
    lc.clearDisplay(i);
  }
  delay(100);
  lc.clearAll();
  ch0= scrollString[0];
  ch1= scrollString[1];
  ch2= scrollString[2];
  ch3= scrollString[3];
  ch4= scrollString[4];
  ch5= scrollString[5];

  nextCharIndex=5;
}

void loop(){
  lc.displayChar(0, lc.getCharArrayPosition(ch0));
  lc.displayChar(1, lc.getCharArrayPosition(ch1));
  lc.displayChar(2, lc.getCharArrayPosition(ch2));
  lc.displayChar(3, lc.getCharArrayPosition(ch3));
  lc.displayChar(4, lc.getCharArrayPosition(ch4));
  ch0=ch1;
  ch1=ch2;
  ch2=ch3;
  ch3=ch4;
  ch4=ch5;
  ch5=scrollString[nextCharIndex++];
  if (nextCharIndex>=stringLength) nextCharIndex=0;
  delay(300);
  lc.clearAll();
  delay(25);
}

Thanks!

  ch0= scrollString[0];
  ch1= scrollString[1];
  ch2= scrollString[2];
  ch3= scrollString[3];
  ch4= scrollString[4];
  ch5= scrollString[5];
...
  lc.displayChar(0, lc.getCharArrayPosition(ch0));
  lc.displayChar(1, lc.getCharArrayPosition(ch1));
  lc.displayChar(2, lc.getCharArrayPosition(ch2));
  lc.displayChar(3, lc.getCharArrayPosition(ch3));
  lc.displayChar(4, lc.getCharArrayPosition(ch4));

Time to start using arrays.


How can I use your "font.h" characters in this sketch:

I can't really see what you are doing, but the code on my page has examples of using the font.

Here is a pointer to a youtube video of two 8-digits sticks controlled by the included sketch running on an UNO. These are the MAX7219 8-digit 7-segment sticks available on eBay for a few bucks. They come in several colors. I have no financial anything with the vendor except as a satisfied user.

The sketch can control multiple sticks; you pass the appropriate chip select pin along with the long number to be converted to decimal and displayed. If the number is greater than 99,999,999 or more negative than -9,999,999, an error is indicated.

The setup code sets the internal register bit for the chip select pin high prior to making the pin an output, so as to prevent a negative glitch on chip select. Nevertheless, a pullup on active-low chip selects is always a good precaution. Also, I explicitly program the 7219 out of lamp test mode.

// Exercise to control 8-digit 7-seven display with MAX7219
// Displays decimal integer in the range -9999999 to 99999999
// with leading zeroes suppressed.
// Displays EEEEEEEE and returns true if input is out of range

#define mosi 11     // assigned by SPI protocol
#define sck 13     //  ""
#define csPin4 4    //  arbitrary
#define csPin3 3    // second display

long myNumber;

void setup() {
  pinMode(mosi, OUTPUT);          //serial data
  pinMode(sck, OUTPUT);           //and clock
  startupMAX(csPin3);             //initialize the MAX7219 and blank display
  startupMAX(csPin4);             //and do the other
}

void loop() {                   //demonstrate various illustrative cases
  displayDec(csPin3, 0);
  displayDec(csPin4, myNumber++);
  delay(1000);
  displayDec(csPin3, 100);
  displayDec(csPin4, myNumber++);
  delay(1000);
  displayDec(csPin3, 12345678);
  displayDec(csPin4, myNumber++);
  delay(1000);
  displayDec(csPin3, 99999999);
  displayDec(csPin4, myNumber++);
  delay(1000);
  displayDec(csPin3, 100000000);
  displayDec(csPin4, myNumber++);
  delay(1000);
  displayDec(csPin3, -10);
  displayDec(csPin4, myNumber++);
  delay(1000);
  displayDec(csPin3, -9876543);
  displayDec(csPin4, myNumber++);
  delay(1000);
  displayDec(csPin3, -9999999);
  displayDec(csPin4, myNumber++);
  delay(1000);
  displayDec(csPin3, -10000000);
  displayDec(csPin4, myNumber++);
  delay(1000);
  setIntensity(csPin4, 15);          //very bright
  delay(1000);
  setIntensity(csPin4, 0);          //very dim
  delay(1000);
  setIntensity(csPin4, 4);          //back to medium
}

void startupMAX(char CS) {
  digitalWrite(CS, HIGH);         //try to make this come up high
  pinMode(CS, OUTPUT);
  shift16(CS, 0x0000);            //nop to get it in sync
  shift16(CS, 0x0f00);            //get it out of lamp test mode
  shift16(CS, 0x09ff);            //decode all digits
  shift16(CS, 0x0a04);            //medium intensity
  shift16(CS, 0x0b07);            //display all digits
  shift16(CS, 0x0c01);            //normal operation
  for (int i = 1; i < 9; i++) {              //blank all digits
    shift16(CS, (i << 8 | 0x0f));            //one at aa time
  }
}

int displayDec(char CS, long theNumber) {     //display a decimal number
  long power10[8] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000};  
  int digit;                                   //the digit we are currently working on
  int value;                                   //working
  boolean leading = true;                      //will blank leading zeroes
  boolean negative = false;                    //assume not negative
  long myCopy = theNumber;                     //working copy of input
  if (myCopy < 0) {                            //test for negative
    myCopy =  abs(myCopy);                     //convert to positive
    negative = true;                           //note negative
  }
  if ((negative == true) && (myCopy > 9999999)) {  //test for less than -9,999,999
    for (int i = 1; i < 8; i++) {                    //display
      shift16(CS, i << 8 | 0xB);               //  EEEEEEE               
    }
    shift16(CS, 0x080a);                       //  leading minus sign      
    return true;
  }
  if ((negative == false) && (myCopy > 99999999)) {  //test for > 99,999,999
    for (int i = 1; i < 9; i++) {                    //display EEEEEEEE
      shift16(CS, i << 8 | 0xB);
    }
    return true;
  }
  for (digit = 7; digit > -1; digit--) {       //each digit, each power of 10
    value = 0;
    if ((negative == true) && (digit == 7)) {  //test for negative and right-most digit
      value = 10;                              //minus sign
    }
    else {
      while (myCopy >= power10[digit]) {         //if so, can subtract once more
        value++;                                  //result
        myCopy = myCopy - power10[digit];         //reduce by power of ten
      }
    }

    unsigned int pattern = digit + 1 ;      //form the command:  register number
    pattern = pattern << 8;                 //aligned
    if ((value == 0) && (leading == true) && (digit != 0)) {   //replace leading zeroes
      value = 0x0f;                          //with blank decode
    }
    else if ((value > 0) && (value != 10)) {   //still leading zeroes?
      leading = false;                         //no more
    }
    pattern = pattern | value;               //the number to display
    shift16(CS, pattern);                    // display it
  }                                          // goes with for (digit....
  return false;                              //success
}

void setIntensity(char CS, char intensity) {
  shift16(CS, (0x0a00 | intensity));            //form the command and send it
}
void shift16(char CS, unsigned int shiftee) {   //actually write a MAX7219 register
  digitalWrite(CS, LOW);                        //chip select
  shiftOut(mosi, sck, MSBFIRST, shiftee >> 8);  //first eight bits
  shiftOut(mosi, sck, MSBFIRST, shiftee);       //low order eight bits
  digitalWrite(CS, HIGH);
}

Software bit banging when Uno talks very nicely to Max7219 using SPI.transfer( ) at 4 MHz rate - why do folks do that?

Load your data to be displayed in an array
byte dataArray[] = {0,1,2,3,4,5,6,7,}; // test data
Then a simple for:loop to send it out;

digitalWrite (csPin, LOW);
for (x=0; x<8; x=x+1){
SPI.transfer(x+1); // data register address, from 1 to 8
SPI.transfer(dataArray[x]);
}
digitalWrite (csPin, HIGH);