4 digit 7-segment counter with only 1 shift register

I'm a new Arduino user (convert?) and wanted to do something simple for my first project. As I hadn't seen anything similar to my approach I thought I would post my results to share with others. The result is a 4 digit counter I made from a pair of 2 digit common cathode 7-segment displays. Similar projects used a 75HC595 for each digit. I went for a multiplexed approach with a single shift register driving the segments (anodes) and 4 digital i/o pins each driving a NPN transistor to switch in each cathode one at a time. I connected all the 'a' segments of each display to each other and then connected that to the QA output of the shift register. See the great tutorial elsewhere on this site. Similarly, connect each of the 'b' thought 'h' segments of the display to each other and to the corresponding pin on the shift register. Next I wired in the cathode drivers by driving each one with a 2N3904 transistor so as to not overload the Arduino pins. Specific details are in the code comments. The sketch just counts from 0 to 9999 and then starts over. Not much really but I had a lot of fun making it and learned a lot about Arduino in the process. The next step is to attach the DS1307 and turn the display into yet-another-arduino-clock. Here is the code:

/*

2011-10-26

Paul Jenkins

This code is in the public domain.

//

Example code for driving a 4 digit common cathode 7-segment display with
a 74HC595 8-bit shift register and 4 NPN transistors.

The 74HC595 shift register attaches to pins 8, 11 and 12 of Arduino.
See Arduino ShiftOut tutorial for more info:

Arduino digital pins 4 to 7 drive the cathode for digits 1 through 4.
Digit 1 is the leftmost. Wire the emitter of the NPN transistor to ground.
Connect the Arduino digital pin to the base of the transistor via a
1 k-ohm resistor so as not to provide excessive base drive and damage
the transistor. Connect the collector of the transistor to the cathode of
the display.

Be sure to use current limiting resistors from the shift register outputs
Qa to Qf to the anode segments of the LED display. I used 220 ohm resistors.

  • Shift Register Arduino
  • 14 SER 11 Serial Input
  • 11 SRCLK 12 Shift Clock
  • 12 RCLK 8 Latch Enable

*/

// Define the bit-patterns for the 7-segment displays
const byte SevenSeg[16] =
{
// Hex digits
B11111100, B01100000, B11011010, B11110010, // 0123
B01100110, B10110110, B10111110, B11100000, // 4567
B11111110, B11110110, B11101110, B00111110, // 89AB
B00011010, B01111010, B10011110, B10001110, // CDEF

};

// Pin connected to latch pin (RCLK,12) of 74HC595
const int latchPin = 8;
// Pin connected to clock pin (SRCLK,11) of 74HC595
const int clockPin = 12;
// Pin connected to Data in (SER,14) of 74HC595
const int dataPin = 11;
// Pin connected to cathodes
const int disp1 = 7; // Displays are numbered from left to right
const int disp2 = 6; // as disp1 to disp4.
const int disp3 = 5; // Scan displays quickly and use POV to display
const int disp4 = 4; // 4 digits with only 1 shift register

//
void setup()
{
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(disp1, OUTPUT);
pinMode(disp2, OUTPUT);
pinMode(disp3, OUTPUT);
pinMode(disp4, OUTPUT);
}
//
void loop()
{
for (int i=0; i < 10000; ++i)
{
// Display i as 4 digits on the display.
// No leading zero blanking.
for (int j=0; j<20; j++) // Draw the display several times to slow it down
SevenSegDisplay(i);
}
}

void DisplayADigit(int dispno, byte digit2disp)
{

// Turn off the shift register pins
// while you're shifting bits:
digitalWrite(latchPin, LOW);

AllDispOff(); // Turn off all cathode drivers.

// shift the bits out:
shiftOut(dataPin, clockPin, LSBFIRST, digit2disp);

digitalWrite(latchPin, HIGH); // Set latch high to set segments.

digitalWrite(dispno, HIGH); // Drive one cathode low to turn on display.

delay(5); // Wait a bit for POV

}

void AllDispOff()
{
// Turn all cathode drivers off
digitalWrite(disp1, LOW);
digitalWrite(disp2, LOW);
digitalWrite(disp3, LOW);
digitalWrite(disp4, LOW);
}

void SevenSegDisplay(int number)
{
int d1,d2,d3,d4; // Temporary values for thousands, hundreds, tens and units

if (number > 9999)
number = 9999; // Do some bounds checks to avoid strangeness
if (number < 0)
number = 0;

d1 = (int) (number / 1000); // Get thousands
number = number - (d1 * 1000);
d2 = (int) (number / 100); // Get hundreds
number = number - (d2 * 100);
d3 = (int) (number / 10); // Get tens
d4 = number - (d3* 10); // Get units

DisplayADigit(disp1,byte(SevenSeg[d1])); // Show thousands
DisplayADigit(disp2,byte(SevenSeg[d2])); // Show hundreds
DisplayADigit(disp3,byte(SevenSeg[d3])); // Show tens
DisplayADigit(disp4,byte(SevenSeg[d4])); // Show units

}

Nice work!

If you attached a second shift register and use 4 of its outputs to drive the NPN transistors you are driving with digital pins, you may save those 4 pins and even go to 8 7-segment displays :wink: The shift registers are cheap.

Thanks for the suggestion liudr. I hadn't thought of that. I'll check the data sheet on the 595 to see if it can sink the current of all seven segments being on at once. I had bought a whole tube of the shift registers 'cuz they were so cheap and I figured I might burn out 1 or 2 of them in my tinkering. Some times I check the wiring after applying power. ]:slight_smile: I only have a small 30-row breadboard right now so it is packed. When I get to my bigger bb I'm going to take your suggestion and expand it to 6 digits (that's all the displays I have). Then there will be room for the time chip and battery too and I can include seconds with the time.

Somewhere I remember seeing a circuit similar to what you described to drive a 44780 type LCD in 8-bit mode with the RS and E lines wired to a second 595. There may even be a shield or breakout board (AdaFruit?) that does this. Personally I find it more fun and educational to build the circuits myself first on a breadboard and play with them rather than buy a ready-to-go solution.

I will post new code once I make the modifications. Cheers!

I also have tubes of 595s. IIRC, 20ma per channel and some max current per chip. I use 5 arduino pins and 2 595 to sense 16 keys and control 8 leds.
Picture:

Thank you Dr Liu for your words of encouragement. I never did go ahead with adding the digit drivers to a second shift register as I got busy with other aspects of the Arduino. I may add another pair of digits and a second shift register and make my breadboard circuit into a shield at some point in the future. I did add a DS1307 to this circuit and make a simple clock (who doesn't make a clock with their Arduino at some point :wink: ) and this also taught me some valuable lessons.

I was thinking of maybe adding some discrete LEDs to the shift register outputs and physically placing them on a design such as a Christmas tree and then getting the Arduino to blink them in various patterns. Perhaps in response to motion in the room (I do have an ultrasonic sensor board squirreled away somwhere) or sound. Maybe even play a tune (I've discovered RTTTL and got the Arduino to play music). I will be sure to share any results with my fellow forum members. This is such an awesome community and I want to give back as much as I can as I have benefited so much from reading other posts and being inspired by what all of you are doing with your Arduinos. Cheers.

Random thought: a x-mas tree only kids can light up. You take two sonic rangers one mount high 4 ft and one mount low, 2ft from floor. If the top one senses nothing and the bottom one senses something, turn on the blink sequence. Certainly you can sense ghosts too if only the top sensor returns values (assuming ghosts reflect ultrasonic waves of course).

Actually, you should not source or sink more than 6mA per pin on a 74HC595 and not more than 70mA for the whole chip. It's not intended to drive LEDs. If you have all 7 segments on at 20mA, that's 140mA.

I designed the Digit Shield to use a single 595 and a 74LS247 BCD to 7-segment driver to multiplex four 7-segment displays. Four of the 595 outputs control PNP transistors sourcing current to the common anode displays. The other 4 outputs drive the 4 inputs of the 74LS247 to set the value of the current digit. This approach only requires 3 Arduino pins, plus one more to control the decimal points. The shield uses pins 2,3,4,5.

More info: Digit Shield

megabump! not wanting to start a new thread when this one is perfectly acceptable!\

if exchanging the smaller digits for larger ones, specifically the KINGBRIGHT SA23-11EWA, rated at;
12v supply voltage, and 140mA max current

and using the TPIC6B595 in place of the 595, (150mA max supply current, 50v max voltage)

is the 2N3904 NPN transistor still a viable component? the data sheet does state;

collector base 60v
collector emitter 40v
emitter-base voltage 6v
collector current 200mA

All of my numbers are inside these figures, but a little yay or nay from the community will help!

Thanks

TPIC6B595 sinks current.
NPN transistor generally used to sink current also.
You want PNP to source current into the common anodes.

so, if i've understood you right, then this is what i've come up with...

it doesn't work however, hence my posting in here.

the 3rd segment isn't wired in here, but its run the same as the second.
at the moment, i'm running the arduino from the usb 5v, and the segments are from the 12v and the gnd's are connected (as shown)

also, the code i have is for 4 segments, and i'm only using 3, so i've just commented out the thousand section of the code, and reduced the number code to 999 instead of 9999

tiny edit, the segment is actually common cathode

 // Define the bit-patterns for the 7-segment displays 
const byte SevenSeg[16] = 
{
     // Hex digits
     B11111100, B01100000, B11011010, B11110010,   // 0123
     B01100110, B10110110, B10111110, B11100000,   // 4567
     B11111110, B11110110, B11101110, B00111110,   // 89AB
     B00011010, B01111010, B10011110, B10001110,   // CDEF
  
};

//Pin to clear the register
const int clearPin = 7;    // Pin connected to clear pin (SRCLR) of TPIC6B595
const int latchPin = 8;    // Pin connected to latch pin (RCK) of TPIC6B595
const int clockPin = 12;   // Pin connected to clock pin (SRCK) of TPIC6B595
const int dataPin = 11;    // Pin connected to Data in (SER IN) of TPIC6B595

// Pin connected to cathodes
const int disp1 = 7;  // Displays are numbered from left to right
const int disp2 = 6;  // as disp1 to disp4.
const int disp3 = 5;  // Scan displays quickly and use POV to display
const int disp4 = 4;  // 4 digits with only 1 shift register

void setup() {
  pinMode(clearPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin,  OUTPUT);
  pinMode(disp1, OUTPUT);
  pinMode(disp2, OUTPUT);
  pinMode(disp3, OUTPUT);
  pinMode(disp4, OUTPUT);
 
  // Always start by setting SRCLR high
  digitalWrite( clearPin, HIGH);
  
  // delay a little and then set 
  delay(100);
}


void loop() 
{
  for (int i=0; i < 1000; ++i)
  {
    // Display i as 4 digits on the display.
    // No leading zero blanking.
    for (int j=0; j<20; j++) // Draw the display several times to slow it down
      SevenSegDisplay(i);
  }
}

void DisplayADigit(int dispno, byte digit2disp){
  
  // Turn off the shift register pins
  // while you're shifting bits:
  digitalWrite(latchPin, LOW);
  
  AllDispOff();  // Turn off all cathode drivers.
  
  // shift the bits out:
  shiftOut(dataPin, clockPin, LSBFIRST, digit2disp);
  
  digitalWrite(latchPin, HIGH);  // Set latch high to set segments.
  digitalWrite(dispno, HIGH);  // Drive one cathode low to turn on display.
  
  delay(5);  // Wait a bit for POV
}

void AllDispOff(){
  // Turn all cathode drivers off
  digitalWrite(disp1, LOW);  // thousands off
  digitalWrite(disp2, LOW);  // hundreds off
  digitalWrite(disp3, LOW);  // tens off
  digitalWrite(disp4, LOW);  // units off
}

void SevenSegDisplay(int number)
{
  int d1,d2,d3,d4; // Temporary values for thousands, hundreds, tens and units
  
  if (number > 999)
     number = 999;  // Do some bounds checks to avoid strangeness
  if (number < 0)
     number = 0;
  
  d1 = (int) (number / 1000);    // Get thousands
  number = number - (d1 * 1000);
  d2 = (int) (number / 100);     // Get hundreds
  number = number - (d2 * 100);
  d3 = (int) (number / 10);      // Get tens
  d4 = number - (d3* 10);        // Get units
  
//  DisplayADigit(disp1,byte(SevenSeg[d1]));  // Show thousands (3 digits, don't show)
  DisplayADigit(disp2,byte(SevenSeg[d2]));  // Show hundreds
  DisplayADigit(disp3,byte(SevenSeg[d3]));  // Show tens
  DisplayADigit(disp4,byte(SevenSeg[d4]));  // Show units
 
}

PNP needs to connect to +5, LEDs are all upside down,
TPIC6B595 gets power from 5V, not 12.

the tpic needs 5v... damn, read the sheet wrong, read as max 50v input doh. how sensitive are these chips? have i probably destroyed it by pumping 12v in? i never got the pop / burn / smell...

i appreciate that the leds are the wrong way around in this diagram now

where the pnp needs to connect to +5, am i wrong in thinking that the output of the leds goes to the collector, the output (digital 5v) from the arduino goes to the base, and then the emitter goes to gnu?

so the transistor is acting like a switch, when the base goes HIGH the current flows to gnd, lighting the led?

ahh, i get it, ive got the transistor connected to gnd, when it should be supplying the current.

ill be back!

50V on the open drain outputs, yes, not on the vcc line. They are most likely goners.

thats what i managed, albeit without the resistors across the transistors.

and i have a crossed gnd somewhere as the mac just instantly shutdown when i added power... :-/

fresh eyes in the morning.

thanks mr crossroads.

mega apologies, the segments are common cathode, and the TPIC6B595 is a sinker not a supplier...

i have found the NPIC version which sources current, but i have on hand a max7219 so ill play with that provisionally and see where i get.

i would have thought that the code will be similar, if not the same.

Can use TPIC6B595 outputs to control PNP transistors and source current into the anodes. NPN on cathodes to sink current.

ok, so...

but i feel a bit dubious of the boosted current going back into the arduino's gnd.. how much can the arduino sink before issues?

i've filled this one out with the limiting resistors for the pnp's and the npn.

i've also left out the other 2 segments for this diagram

ah ha, by putting the resistor in between the 12v and the base of the pnp i have light!

not correctly lit up, but at least i've got progress!

Stil need current limiting resistors between PNP and LEDs so you don't have more than 20mA flowing thru the segments.
Connect G of TPIC6B595 to Gnd, or to a PWM pin for brightness control. Low = outputs on.
SRCLR high for normal ops. I would just tie it it high and shift in all 0's if you wanted no output.

Resistor between base of PNP and 12V should turn it off - TPIC6B595 pulling the base low should turn it on.