In the previous post I showed how to build a single digit display using one 7 segment LED unit. That’s nice but not very useful as such, since single digits can only show limited amounts of data. To go further, you can either add digits and chain them together, as will be seen in Part 3 of this project, or, you can use a four segment LED unit and one shift register.

This time we will use a four segment LED to produce a countdown clock. You will see that this setup is very versatile and you can easily modify it to show the current time, for example, or temperature as in 23°C or 77°F, with the third digit providing the degree character and the last digit the temperature scale.

4 digit, 7 segment LED

This is the most common four digit, seven segment LED unit. It has 12 pins, not ten like the one digit thing. That’s because it has been wired so that every digit has its own anode, and the remaining 8 pins correspond to the segments.

The four segment LED works due to the persistence of vision. Not every digit is on all the time, but rather, the four digits are refreshed at 5 millisecond intervals, ie. 200 times per second, that the eye can’t discern them as individually lit. For this, you need to send Arduino four bytes of data via a shift register as before, to be displayed in quick succession.

Parts and connections

So, your parts list is the same as before with a breadboard, a 74HC595 shift register, a bunch of male-male jumper wires, and a display as shown above. My application for this little device is a countdown timer, and the code includes a method for taking a value in seconds, then finding out the minutes and seconds, further dividing them into tens of minutes, minutes, tens of seconds and seconds. These four, single digit, values are sent to the shift register as four consecutive bytes. This is the pins list:

PIN NUMBERPIN NAMEGOES TO
74HC5951Q1LED 5
74HC5952Q2LED 10
74HC5953Q3LED 1
74HC5954Q4LED 2
74HC5955Q5LED 4
74HC5956Q6LED 7
74HC5957Q7LED 11
74HC5958GNDGND
74HC5959DATA OUTNEXT SHIFT REGISTER
74HC59510MR5V
74HC59511SH_CPARD Clock Pin
74HC59512ST_CPARD Latch Pin
74HC59513OEGND
74HC59514DSARD Data Pin
74HC59515Q0LED 3
74HC59516VCC5V

The connections on the 74HC595 are as before, with the difference that you need to supply all four anodes with their own power. This is achieved by connecting the anodes on the LED unit as follows:

PIN NUMBERGOES TO 
LED PIN1SHIFT Q3
LED PIN2SHIFT Q4
LED PIN3SHIFT Q0
LED PIN4SHIFT Q5
LED PIN5SHIFT Q1
LED PIN6ARD 4Anode pin
LED PIN7SHIFT Q6
LED PIN8ARD 5Anode pin
LED PIN9ARD 6Anode pin
LED PIN10SHIFT Q2
LED PIN11SHIFT Q7
LED PIN12ARD 7Anode pin

Logic and coding

Your application may vary, but the essential thing is to be able to separate each digit into its own byte, so 27 isn’t 27, but a 2 and a 7. When I made the countdown timer, I did some Excel first to find out the digits in every moment (45 minutes is 2700 seconds):

secminssecssplitmins1splitmins2splitsecs1splitsecs2
27004504500

Mins is derived from int(seconds/60), which drops the remainder.

Secs is the modulo 60 division of Minutes, which gives just the remainder.

secminssecssplitmins1splitmins2splitsecs1splitsecs2
27004504500
269944594459
269844584458
269744574457
269644564456
269544554455
269444544454

etc.

Splitmins 1 and 2 are again an integer division of minutes, and modulo division of minutes. Splitsecs are the same for seconds. After I got this working in Excel, I did the same thing for Arduino:

int second = 2700;          // Define variable for starting time in seconds 
int mins= 0;                // variable for holding minutes
int secs = 0;               // variable for holding seconds
int splitmins1 = 0;         // variable for holding first digits of minutes
int splitmins2 = 0;         // variable for holding second digits of minutes
int splitsecs1 = 0;         // variable for holding first digits of seconds
int splitsecs2 = 0;         // variable for holding second digits of seconds
byte myArray[] = {0x99, 0x92, 0xc0, 0xc0}; // an array holding the four digits
initially with the numbers 4,5,0,0 in it

There is also a second array that holds the bytes that form the numbers and letters:

byte num[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};

These are the values of 0 through F in hexadecimal notation. So, if you want to send the values 1 2 3 4 to the display, you place 0xf9, 0xa4, 0xb0, and 0x99 in myArray. You remember the picking of values from the previous post, where it is handled in more detail.

This is the actual maths:

second--;                     // second minus 1
if (second==0) {              // handling the time out issue
  second = 2700;
}
mins = int(second/60);        // minutes as 45, 44, 43...
secs = int(second % 60);      // seconds of every minute
splitmins1 = int(mins / 10);  // first digit of minutes
splitmins2 = int(mins % 10);  // second digit of minutes
splitsecs1 = int(secs / 10);  // first digit of seconds
splitsecs2 = int(secs % 10);  // second digit of seconds
myArray[0] = num[splitmins1]; // get first digit hexcode from num array
myArray[1] = num[splitmins2]; // get second digit hexcode from num array
myArray[2] = num[splitsecs1]; // get third digit hexcode from num array
myArray[3] = num[splitsecs2]; // get fourth digit hexcode from num array

After this it’s just the issue of sending this out to the shift register, then having it deal out the four digits in quick succcession. The code below is again based on Freenove’s excellent tutorials, but has been edited to do more than just show 0123.

#include <FlexiTimer2.h>  // Contains FlexiTimer2 Library
int latchPin = 5; //12;          // Pin connected to ST_CP of 74HC595(Pin12)
int clockPin = 6;//13;          // Pin connected to SH_CP of 74HC595(Pin11)
int dataPin = 7; //11;           // Pin connected to DS of 74HC595(Pin14)
int comPin[] = {13, 12, 11, 10};  //{7, 6, 5, 4};// Common pin (anode) of 4 digit 7-segment display
int doorPin = 3;            // pin for monitoring room door being closed
int second = 2700;          // Define variable for starting time in seconds
int mins= 0;                // variable for holding minutes
int secs = 0;               // variable for holding seconds
int splitmins1 = 0;         // variable for holding first digits of minutes
int splitmins2 = 0;         // variable for holding second digits of minutes
int splitsecs1 = 0;         // variable for holding first digits of seconds
int splitsecs2 = 0;         // variable for holding second digits of seconds

// Define the encoding of characters 0-F for the common-anode 7-Segment Display
byte num[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
byte myArray[] = {0x99, 0x92, 0xc0, 0xc0}; //array for representing led characters, inititally 4500
void setup() {
  // set pins to output
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  //pinMode(doorPin, INPUT);
  for (int i = 0; i < 4; i++) {
    pinMode(comPin[i], OUTPUT);
  }
  digitalWrite(doorPin, HIGH);       // set door lock monitor pin to high
  FlexiTimer2::set(1000, timerInt);  // configure the timer and interrupt function
  FlexiTimer2::start();              // start timer
}

void loop() {
  showArray();
}

void showArray(){
  for (int i = 0; i < 4; i++) {
    // Select a single 7-segment display
    chooseCommon(i);
    // Send data to 74HC595
    writeData(myArray[i]);
    delay(5);
    // Clear the display content
    writeData(0xff);
  }
}

// the timer interrupt function of FlexiTimer2 is executed every 1s

void timerInt() {
  //if(digitalRead(doorPin)==LOW){  // while the switch at pin 3 is closed, this will run
    second--;                     // second minus 1
    if (second==0) {              // handling the time out issue
      second = 2700;
    }
    mins = int(second/60);        // minutes as 45, 44, 43…
    secs = int(second % 60);      // seconds of every minute
    splitmins1 = int(mins / 10);  // first digit of minutes
    splitmins2 = int(mins % 10);  // second digit of minutes
    splitsecs1 = int(secs / 10);  // first digit of seconds
    splitsecs2 = int(secs % 10);  // second digit of seconds
    myArray[0] = num[splitmins1]; // get first digit hexcode from num array
    myArray[1] = num[splitmins2]; // get second digit hexcode from num array
    myArray[2] = num[splitsecs1]; // get third digit hexcode from num array
    myArray[3] = num[splitsecs2]; // get fourth digit hexcode from num array
  //}
  //else {                          // switch opens, the clock shows the time left at opening
  //  delay(10);
  //}

}

void chooseCommon(byte com) {
  // Close all single 7-segment display
  for (int i = 0; i < 4; i++) {
    digitalWrite(comPin[i], LOW);
  }
  // Open the selected single 7-segment display
  digitalWrite(comPin[com], HIGH);
}

void writeData(int value) {
  // Make latchPin output low level
  digitalWrite(latchPin, LOW);
  // Send serial data to 74HC595
  shiftOut(dataPin, clockPin, LSBFIRST, value);
  // Make latchPin output high level, then 74HC595 will update the data to parallel output
  digitalWrite(latchPin, HIGH);
}

This works when you have just one 4 digit, 7 segment LED. If you want to use more such units, you can connect the shift registers to eah other via the data output of the first register wired to the data input of the next. This will hopefully be covered in the fourth episode of this three-part blog on LEDs, but first I will show you how to convert this small scale countdown timer to a big digit timer. The elements I got are 4″, 10cm tall, red ones, and it looks rather impressive. They also have to be fed 12V, which is not manageable via 74HC595, but instead you need a chip called TPIC6B595 which can handle the voltage.

Final product

But just to show you how this project turned out, here’s a little video of it in operation.

The final countdown.


0 Comments

Leave a Reply

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

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.