Driving Multiplexed 7 Segment Display with Shift Register

I am trying to drive a 4 character, common cathode (4), 7 segment display (datasheet here: http://www.jameco.com/Jameco/Products/ProdDS/335194.pdf) with an SN74HC595N shift register (datasheet here: http://www.ti.com/lit/ds/symlink/sn74hc595.pdf). I want to make a countdown timer, and I have successfully created all the numbers using bytes so that only one segment is lit at a time per number with no delay() use. I am controlling each cathode with a PN2222A NPN transistor (datasheet here: Intelligent Power and Sensing Technologies | onsemi) to turn them on or off.

Pictures:
http://www.flickr.com/photos/brandondub/8526389172/in/photostream
Imgur

At this point, I would like to expand its functionality beyond the single digit I have working now to being able to use at least 3 digits so that I can have a countdown timer capable of times upwards of a minute.

What I am wondering is if there is a way to make the time easily changeable and automate when digits turn on and off as a function of the "time" remaining on the clock. So for example, make it to where I can change a variable to 100 and the clock would start at 1.40 (100 seconds) and then count down. I am not sure how to automate the cathode enabling in respect to time remaining or how to program the clock to countdown from minutes to seconds (2.00 - 1.59) or even from 0.10 - 0.09.

Does anyone have experience with this? Thank you in advance for any advice you can give me.

Here is my code. The original post was too long with it.

boolean data = 2;
boolean latch = 3;
boolean clock = 4;
boolean cathodeOne = 9;
boolean cathodeTwo = 10;
boolean cathodeThree = 11;
boolean cathodeFour = 12;
unsigned long timer;
unsigned long previousMillis = 0;
byte Zero[6][8] = {
  {
    0,0,1,0,0,0,0,0  }
  ,
  {
    0,0,0,1,0,0,0,0  }
  ,
  {
    0,0,0,0,1,0,0,0  }
  ,
  {
    0,0,0,0,0,1,0,0  }
  ,
  {
    0,0,0,0,0,0,1,0  }
  ,
  {
    0,0,0,0,0,0,0,1  }
  ,
};
byte One[2][8] = {
  {
    0,0,0,0,0,1,0,0  }
  ,
  {
    0,0,0,0,0,0,1,0  }
};

byte Two[5][8] = {
  {
    0,1,0,0,0,0,0,0  }
  ,
  {
    0,0,0,1,0,0,0,0  }
  ,
  {
    0,0,0,0,1,0,0,0  }
  ,
  {
    0,0,0,0,0,0,1,0  }
  ,
  {
    0,0,0,0,0,0,0,1  }
};

byte Three[5][8] = {
  {
    0,1,0,0,0,0,0,0  }
  ,
  {
    0,0,0,0,0,1,0,0  }
  ,
  {
    0,0,0,0,1,0,0,0  }
  ,
  {
    0,0,0,0,0,0,1,0  }
  ,
  {
    0,0,0,0,0,0,0,1  }
  ,
};

byte Four[4][8] = {
  {
    0,1,0,0,0,0,0,0  }
  ,
  {
    0,0,1,0,0,0,0,0  }
  ,
  {
    0,0,0,0,0,1,0,0  }
  ,
  {
    0,0,0,0,0,0,1,0  }
  ,
};

byte Five[5][8] = {
  {
    0,1,0,0,0,0,0,0  }
  ,
  {
    0,0,0,0,0,1,0,0  }
  ,
  {
    0,0,0,0,1,0,0,0  }
  ,
  {
    0,0,1,0,0,0,0,0  }
  ,
  {
    0,0,0,0,0,0,0,1  }
};

byte Six[6][8] = {
  {
    0,0,1,0,0,0,0,0  }
  ,
  {
    0,0,0,1,0,0,0,0  }
  ,
  {
    0,0,0,0,1,0,0,0  }
  ,
  {
    0,0,0,0,0,1,0,0  }
  ,
  {
    0,1,0,0,0,0,0,0  }
  ,
  {
    0,0,0,0,0,0,0,1  }
  ,
};

byte Seven[3][8] = {
  {
    0,0,0,0,0,1,0,0  }
  ,
  {
    0,0,0,0,0,0,1,0  }
  ,
  {
    0,0,0,0,0,0,0,1  }
  ,
};

byte Eight[7][8] = {
  {
    0,1,0,0,0,0,0,0  }
  ,
  {
    0,0,1,0,0,0,0,0  }
  ,
  {
    0,0,0,1,0,0,0,0  }
  ,
  {
    0,0,0,0,1,0,0,0  }
  ,
  {
    0,0,0,0,0,1,0,0  }
  ,
  {
    0,0,0,0,0,0,1,0  }
  ,
  {
    0,0,0,0,0,0,0,1  }
  ,
};

byte Nine[6][8] = {
  {
    0,1,0,0,0,0,0,0  }
  ,
  {
    0,0,1,0,0,0,0,0  }
  ,
  {
    0,0,0,0,1,0,0,0  }
  ,
  {
    0,0,0,0,0,1,0,0  }
  ,
  {
    0,0,0,0,0,0,1,0  }
  ,
  {
    0,0,0,0,0,0,0,1  }
  ,
};

void clockTick()
{
  digitalWrite(clock, HIGH);
  digitalWrite(clock, LOW);
}

void latchTick()
{
  digitalWrite(latch, HIGH);
  digitalWrite(latch, LOW);
}

void zero()
{
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Zero[0][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Zero[1][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Zero[2][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Zero[3][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Zero[4][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Zero[5][x]);
    clockTick();
  }
  latchTick();
}

void one()
{
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, One[0][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, One[1][x]);
    clockTick();
  }
  latchTick();
}

void two()
{
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Two[0][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Two[1][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Two[2][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Two[3][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Two[4][x]);
    clockTick();
  }
  latchTick();
}

void three()
{
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Three[0][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Three[1][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Three[2][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Three[3][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Three[4][x]);
    clockTick();
  }
  latchTick();
}

void four()
{
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Four[0][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Four[1][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Four[2][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Four[3][x]);
    clockTick();
  }
  latchTick();
}

void five()
{
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Five[0][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Five[1][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Five[2][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Five[3][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Five[4][x]);
    clockTick();
  }
  latchTick();
}

void six()
{
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Six[0][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Six[1][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Six[2][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Six[3][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Six[4][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Six[5][x]);
    clockTick();
  }
  latchTick(); 
}

void seven()
{
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Seven[0][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Seven[1][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Seven[2][x]);
    clockTick();
  }
  latchTick();
}

void eight()
{
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Eight[0][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Eight[1][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Eight[2][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Eight[3][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Eight[4][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Eight[5][x]);
    clockTick();
  }
  latchTick();
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Eight[6][x]);
    clockTick();
  }
  latchTick();
}

void nine()
{
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Nine[0][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Nine[1][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Nine[2][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Nine[3][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Nine[4][x]);
    clockTick();
  }
  latchTick();
  for (int x = 0; x < 8; x++)
  {
    digitalWrite(data, Nine[5][x]);
    clockTick();
  }
  latchTick();
}

void setup()
{
  pinMode(data, OUTPUT);
  pinMode(latch, OUTPUT);
  pinMode(clock, OUTPUT);
  pinMode(cathodeOne, OUTPUT);
  pinMode(cathodeTwo, OUTPUT);
  pinMode(cathodeThree, OUTPUT);
  pinMode(cathodeFour, OUTPUT);

}

void loop()
{
  timer = millis();
  digitalWrite(cathodeOne, HIGH);

  if(timer - previousMillis < 1000)
    nine();
  else if(timer - previousMillis >= 1000 && timer - previousMillis < 2000)
    eight();
  else if(timer - previousMillis >= 2000 && timer - previousMillis <3000)
    seven();
  else if(timer - previousMillis >= 3000 && timer - previousMillis <4000)
    six();
  else if(timer - previousMillis >= 4000 && timer - previousMillis <5000)
    five();
  else if(timer - previousMillis >= 5000 && timer - previousMillis <6000)
    four();
  else if(timer - previousMillis >= 6000 && timer - previousMillis <7000)
    three();
  else if(timer - previousMillis >= 7000 && timer - previousMillis <8000)
    two();
  else if(timer - previousMillis >= 8000 && timer - previousMillis <9000)
    one();
  else zero();
}

Seems way more complicated, both code and electronics, than needed.

Why the SN74HC595?

I used the SN74HC595 because I wanted to try my hand at using a shift register instead of just using 8 I/O pins.

I wanted to try and make each segment of the number light up one at a time so that the change in brightness say from a "1" to an "8" would be as nominal as possible.

At this point I'm assuming it would be more reasonable (assuming I keep using the shift register) to set each number up as an array with every segment lit at the same time and just flash entire digits at once?

I am pretty new to electronics in general. I have looked at several tutorials and whatnot but never picked up enough from them to really know what I was doing, so I set out to learn by doing.

What would you suggest as far as simplification?

So you want one of these configurations with the shift register in place of the Arduino outputs.
Drive a digit, in 2mS drive the next digit, etc.

I redid my code so I've done away with the huge number bytes and rearranged them into one byte, but no matter what number I try to run it just lights up all of the LEDs. Any ideas what could be going wrong? I have run my original countdown from nine code and it works, so nothing is wrong with the wiring or anything physical... its here somewhere.

boolean data = 2;
boolean clock = 3;
boolean latch = 4;
boolean cathodeOne = 9;
boolean cathodeTwo = 10;
boolean cathodeThree = 11;
boolean cathodeFour = 12;

void clockTick()
{
  digitalWrite(clock, HIGH);
  digitalWrite(clock, LOW);
}

void latchTick()
{
 digitalWrite(latch, HIGH);
digitalWrite(latch, LOW); 
}

byte numbers[11][8]={
  {0,0,1,1,1,1,1,1}, //zero
  {0,0,0,0,0,1,1,0}, //one
  {0,1,0,1,1,0,1,1}, //two
  {0,1,0,0,1,1,1,1}, //three
  {0,1,1,0,0,1,1,0}, //four
  {0,1,1,0,1,1,0,1}, //five
  {0,1,1,1,1,0,0,1}, //six
  {0,0,0,0,0,1,1,1}, //seven
  {0,1,1,1,1,1,1,1}, //eight
  {0,1,1,0,1,1,1,1}, //nine
  {0,0,0,0,0,0,0,0}, //off
};

/*byte Zero[1][8]={0,0,1,1,1,1,1,1};
byte One[1][8]={0,0,0,0,0,1,1,0};
byte Two[1][8]={0,1,0,1,1,0,1,1};
byte Three[1][8]={0,1,0,0,1,1,1,1};
byte Four[1][8]={0,1,1,0,0,1,1,0};
byte Five[1][8]={0,1,1,0,1,1,0,1};
byte Six[1][8]={0,1,1,1,1,0,0,1};
byte Seven[1][8]={0,0,0,0,0,1,1,1};
byte Eight[1][8]={0,1,1,1,1,1,1,1};
byte Nine[1][8]={0,1,1,0,1,1,1,1};*/

void zero()
{
  for(int x=0; x<8; x++)
  {
  digitalWrite(data, numbers[0][x]);
  clockTick();
  }
  latchTick();
  delay(2);
}

void one()
{
  for(int x=0; x<8; x++)
  {
  digitalWrite(data, numbers[1][x]);
  clockTick();
  }
  latchTick();
}

void two()
{
  for(int x=0; x<8; x++)
  {
  digitalWrite(data, numbers[2][x]);
  clockTick();
  delay(5);
  }
  latchTick();
}

void three()
{
  for(int x=0; x<8; x++)
  {
  digitalWrite(data, numbers[3][x]);
  clockTick();
  }
  latchTick();
}

void four()
{
  for(int x=0; x<8; x++)
  {
  digitalWrite(data, numbers[4][x]);
  clockTick();
  }
  latchTick();
}

void five()
{
  for(int x=0; x<8; x++)
  {
  digitalWrite(data, numbers[5][x]);
  clockTick();
  }
  latchTick();
}

void six()
{
  for(int x=0; x<8; x++)
  {
  digitalWrite(data, numbers[6][x]);
  clockTick();
  }
  latchTick();
}

void seven()
{
  for(int x=0; x<8; x++)
  {
  digitalWrite(data, numbers[7][x]);
  clockTick();
  }
  latchTick();
}

void eight()
{
  for(int x=0; x<8; x++)
  {
  digitalWrite(data, numbers[8][x]);
  clockTick();
  }
  latchTick();
}

void nine()
{
  for(int x=0; x<8; x++)
  {
  digitalWrite(data, numbers[9][x]);
  clockTick();
  }
  latchTick();
}

void off()
{
   for(int x = 0; x < 8; x++)
  {
  digitalWrite(data, numbers[10][x]);
  clockTick();
  }
  latchTick();
}

void setup()
{
  pinMode(clock, OUTPUT);
  pinMode(data, OUTPUT);
  pinMode(latch, OUTPUT);
  pinMode(cathodeOne, OUTPUT);
  pinMode(cathodeTwo, OUTPUT);
  pinMode(cathodeThree, OUTPUT);
  pinMode(cathodeFour, OUTPUT);
  digitalWrite(cathodeOne, HIGH);
}

void loop()
{
 
zero();
delay(5);

}

Disregard the last post. I accidentally switched the Shift Clock and Storage Clock in the program.

Here's some code I wrote to multiplex 2 digits, the common cathode is from a 2nd shift register. Easy to expand to 4 digits.

It shows one way to multiplex using arrays.

simpletest.ino (1.89 KB)

#define ENTRIES(ARRAY)          (sizeof(ARRAY) / sizeof(ARRAY[0]))

const uint8_t   data            = 2;
const uint8_t   clock           = 3;
const uint8_t   latch           = 4;
const uint8_t   cathodeOne      = 9;
const uint8_t   cathodeTwo      = 10;
const uint8_t   cathodeThree    = 11;
const uint8_t   cathodeFour     = 12;

const uint8_t   pins[]          = { data, clock, latch, cathodeOne, cathodeTwo, cathodeThree, cathodeFour };

void clockTick()
{
    digitalWrite(clock, HIGH);
    digitalWrite(clock, LOW);
}

void latchTick()
{
    digitalWrite(latch, HIGH);
    digitalWrite(latch, LOW);
}

void digit(uint8_t value)
{
    static byte segments[][8] =
    {
        //    PGFEDCBA
          { 0b00111111 }    // 0
        , { 0b00000110 }    // 1
        , { 0b01011011 }    // 2
        , { 0b01001111 }    // 3
        , { 0b01100110 }    // 4
        , { 0b01101101 }    // 5
        , { 0b01111001 }    // 6
        , { 0b00000111 }    // 7
        , { 0b01111111 }    // 8
        , { 0b01101111 }    // 9
        , { 0b00000000 }    // off
    };

    if ( (value < 0) || (value > 9) )
    {
        value = ENTRIES(segments);
    }
    
    for ( uint8_t mask = 0b10000000; mask; mask >>= 1, clockTick() )
    {
        digitalWrite(data, ((segments[value] & mask) ? HIGH : LOW));
    }

    latchTick();
    delay(2);
}

void loop()
{
    for ( int i = 10; i--; )
    {
        digit(i);
        delay(1000UL);
    }
}

void setup()
{
    for ( int i = ENTRIES(pins); i--; )
    {
        pinMode(pins[i], OUTPUT);
    }

    digitalWrite(cathodeOne, HIGH);
}

Thank you very much. I will take a look at the code and see if I can have an AHA moment. :slight_smile:

Might be easier to use SPI. All this clocktick and latchtick stuff looks tedious.