7 segment 4 digit display time counter

Hi,
so I would like to make a time counter (minutes and seconds) with a 7 segment 4 digit siplay but I am having some problems. My idea was to declare 4 values, one for each segment like so:

a - 1. segment
b - 2. segment
c - 3. segment
d - 4. segment

Every value would firstly be declared as 0 and every second D would get bigger by one. When D gets equal to 10, C would rise by one and D would reset to zero. Proces This way I would have to write code for 36 different scenarios instead of 6000.

I’ve tried something I thought would work but I have a problem. 1st segment is lit up all the time but others only turn on for a split second to display their value. I know this type of display has to switch between segents very fast to display numbers on all of them but I don’t know how to make it work without a 1 second delay (D gets bigger by 1 every time the code runs, this means every second due to delay(1000)).

Idea I have is to set a delay to 1 instead of 1000 and make D bigger by 0.001 every time the loop runs. This way all segments should work properly and D would get bigger by 1 every second, making it count correctly.

I don’t know if my idea would work and don’t know how to code it so any help would be much appreciated. I am new to Arduino and this is my first project that I am not literraly copying someone elses code.

I’ve attached both Arduino IDE and Pletformio VSCode files.


#include <Arduino.h> // I am using Platformio plugin in VSCode

int pinDOT = 3; // Declares pins to their segments or common pins on the display.
int pinA = 11;
int pinB = 7;
int pinC = 4;
int pinD = 2;
int pinE = 1;
int pinF = 10;
int pinG = 5;
int pinC1 = 12;
int pinC2 = 9;
int pinC3 = 8;
int pinC4 = 6;

int addon = 1; // Addon - gets added to a variable every time a second passes.
int a; // Variables that are used to write the time on display. A & B for minutes, C & D for seconds like so: AB:CD.
int b; // Variables are only introduced and not declared because they are declared lower so there is no need to scroll up to change them.
int c;
int d;

void C1() {
digitalWrite(pinC1, HIGH);
digitalWrite(pinC2, LOW);
digitalWrite(pinC3, LOW);
digitalWrite(pinC4, LOW); // C1() - C4() tells Arduino on which part of the display the number should be written.
}
void C2() {
digitalWrite(pinC1, LOW);
digitalWrite(pinC2, HIGH);
digitalWrite(pinC3, LOW);
digitalWrite(pinC4, LOW);
}
void C3() {
digitalWrite(pinC1, LOW);
digitalWrite(pinC2, LOW);
digitalWrite(pinC3, HIGH);
digitalWrite(pinC4, LOW);
}
void C4() {
digitalWrite(pinC1, LOW);
digitalWrite(pinC2, LOW);
digitalWrite(pinC3, LOW);
digitalWrite(pinC4, HIGH);
}
void N0() {
digitalWrite(pinA, LOW);
digitalWrite(pinB, LOW);
digitalWrite(pinC, LOW);
digitalWrite(pinD, LOW);
digitalWrite(pinE, LOW);
digitalWrite(pinF, LOW);
digitalWrite(pinG, HIGH);
digitalWrite(pinDOT, HIGH); //N0() - N9() tells Arduino which number to write on the display.
delay(1);
}
void N1() {
digitalWrite(pinA, HIGH);
digitalWrite(pinB, LOW);
digitalWrite(pinC, LOW);
digitalWrite(pinD, HIGH);
digitalWrite(pinE, HIGH);
digitalWrite(pinF, HIGH);
digitalWrite(pinG, HIGH);
digitalWrite(pinDOT, HIGH);
delay(1);
}
void N2() {
digitalWrite(pinA, LOW);
digitalWrite(pinB, LOW);
digitalWrite(pinC, HIGH);
digitalWrite(pinD, LOW);
digitalWrite(pinE, LOW);
digitalWrite(pinF, HIGH);
digitalWrite(pinG, LOW);
digitalWrite(pinDOT, HIGH);
delay(1);
}
void N3() {
digitalWrite(pinA, LOW);
digitalWrite(pinB, LOW);
digitalWrite(pinC, LOW);
digitalWrite(pinD, LOW);
digitalWrite(pinE, HIGH);
digitalWrite(pinF, HIGH);
digitalWrite(pinG, LOW);
digitalWrite(pinDOT, HIGH);
delay(1);
}
void N4() {
digitalWrite(pinA, HIGH);
digitalWrite(pinB, LOW);
digitalWrite(pinC, LOW);
digitalWrite(pinD, HIGH);
digitalWrite(pinE, HIGH);
digitalWrite(pinF, LOW);
digitalWrite(pinG, LOW);
digitalWrite(pinDOT, HIGH);
delay(1);
}
void N5(){
digitalWrite(pinA, LOW);
digitalWrite(pinB, HIGH);
digitalWrite(pinC, LOW);
digitalWrite(pinD, LOW);
digitalWrite(pinE, HIGH);
digitalWrite(pinF, LOW);
digitalWrite(pinG, LOW);
delay(1);
}
void N6(){
digitalWrite(pinA, LOW);
digitalWrite(pinB, HIGH);
digitalWrite(pinC, LOW);
digitalWrite(pinD, LOW);
digitalWrite(pinE, LOW);
digitalWrite(pinF, LOW);
digitalWrite(pinG, LOW);
delay(1);
}
void N7() {
digitalWrite(pinA, LOW);
digitalWrite(pinB, LOW);
digitalWrite(pinC, LOW);
digitalWrite(pinD, HIGH);
digitalWrite(pinE, HIGH);
digitalWrite(pinF, HIGH);
digitalWrite(pinG, HIGH);
delay(1);
}
void N8() {
digitalWrite(pinA, LOW);
digitalWrite(pinB, LOW);
digitalWrite(pinC, LOW);
digitalWrite(pinD, LOW);
digitalWrite(pinE, LOW);
digitalWrite(pinF, LOW);
digitalWrite(pinG, LOW);
digitalWrite(pinDOT, HIGH);
delay(1);
}
void N9() {
digitalWrite(pinA, LOW);
digitalWrite(pinB, LOW);
digitalWrite(pinC, LOW);
digitalWrite(pinD, HIGH);
digitalWrite(pinE, HIGH);
digitalWrite(pinF, LOW);
digitalWrite(pinG, LOW);
digitalWrite(pinDOT, HIGH);
delay(1);
}
void DOT() {
digitalWrite(pinDOT, LOW); // Tells Arduino to display a dot on the display. Usually combined with a number like so: N0(); DOT();
}
void setup() {
pinMode (pinA, OUTPUT);
pinMode (pinB, OUTPUT);
pinMode (pinC, OUTPUT);
pinMode (pinD, OUTPUT);
pinMode (pinE, OUTPUT);
pinMode (pinF, OUTPUT);
pinMode (pinG, OUTPUT);
pinMode (pinDOT, OUTPUT);
pinMode (pinC1, OUTPUT);
pinMode (pinC2, OUTPUT);
pinMode (pinC3, OUTPUT);
pinMode (pinC4, OUTPUT); // Sets all the pins to output mode.

a = 0; // Declares values. Explenation at the start of the code.
b = 0;
c = 0;
d = -1; // D is set to -1 so it starts with 0 when loop runs for the first time.
}
void loop()
{
d = d + addon; // Adds 1 to the current value of D. First time the loop runs it is equal to 0 after addition is completed.

if (d == 10) // If D reaches 10, reset it to 0 and add 1 to the current value of C. Meaning when D is equal to 10,
{ // write 1 and 0 on C3 and C4 (you can’t display 10 with one digit).
d = 0;
c = c + addon;
}

if (c == 6) // If C reaches 6, reset it to 0 and add 1 to B. 6 because 1 minute has 60 seconds.
{
c = 0;
b = b + addon;
}

if (b == 10) // If B reaches 10, reset it to 0 and add 1 A.
{
b = 0;
a = a + addon;
}

if (a == 10) // If A reaches 10, reset all values to 0 - restart counter.
{
a = 0;
b = 0;
c = 0;
d = 0;
}

C4(); // Tells Arduino to write a number on 4th segment. Number is the same value D is.
if (d == 0)
{
N0();
}
else if (d == 1)
{
N1();
}
else if (d == 2)
{
N2();
}
else if (d == 3)
{
N3();
}
else if (d == 4)
{
N4();
}
else if (d == 5)
{
N5();
}
else if (d == 6)
{
N6();
}
else if (d == 7)
{
N7();
}
else if (d ==
{
N8();
}
else if (d == 9)
{
N9();
}

C3(); // Tells Arduino to write a number on 3rd segment. Number is the same value C is.
if (c == 0)
{
N0();
}
else if (c == 1)
{
N1();
}
else if (c == 2)
{
N2();
}
else if (c == 3)
{
N3();
}
else if (c == 4)
{
N4();
}
else if (c == 5)
{
N5();
}
else if (c == 6)
{
N6();
}
C2(); // Tells Arduino to write a number on 2nd segment. Number is the same value B is.
if (b == 0)
{
N0();
}
else if (b == 1)
{
N1();
}
else if (b == 2)
{
N2();
}
else if (b == 3)
{
N3();
}
else if (b == 4)
{
N4();
}
else if (b == 5)
{
N5();
}
else if (b == 6)
{
N6();
}
else if (b == 7)
{
N7();
}
else if (b ==
{
N8();
}
else if (b == 9)
{
N9();
}

C1(); // Tells Arduino to write a number on 1st segment. Number is the same value A is.
if (a == 0)
{
N0();
}
else if (a == 1)
{
N1();
}
else if (a == 2)
{
N2();
}
else if (a == 3)
{
N3();
}
else if (a == 4)
{
N4();
}
else if (a == 5)
{
N5();
}
else if (a == 6)
{
N6();
}
else if (a == 7)
{
N7();
}
else if (a ==
{
N8();
}
else if (a == 9)
{
N9();
}
delay(1000); // Waits for 1 second - this means D gets bigger by 1 every second.
}

main.cpp (7.74 KB)

Counter.ino (7.73 KB)

1st segment is lit up all the time but others only turn on for a split second to display their value.

if you delay for 1 sec, only the last digit will be displayed. you need to display each digit roughly an equal amount of time for equal brightness.

i think a refresh rate of 1/30th of a second is a decent starting point. that mean display each digit for ~8ms.

your code would be much smaller if you could put each digit to be displayed in an array and in a for loop call the segment display routine for the value and the corresponding digit enable routine (e.g. C1()). and you could make this much small still

gcjr:
if you delay for 1 sec, only the last digit will be displayed. you need to display each digit roughly an equal amount of time for equal brightness.

i think a refresh rate of 1/30th of a second is a decent starting point. that mean display each digit for ~8ms.

your code would be much smaller if you could put each digit to be displayed in an array and in a for loop call the segment display routine for the value and the corresponding digit enable routine (e.g. C1()). and you could make this much small still

Do you know any good guides on how to use decimal values? I've looked at some but didn't quite understand how to use the float function so i dont know how to make a delay shorter without messing up time.

I know my code is still a bit long but firstly I would like to make everything work and then I'll play around with minimizing it. Thanks for the tip though :slight_smile:

to find a specific digit, can you do

int ((val / 10^n) % 10)   n=0,1,...

I know my code is still a bit long but firstly I would like to make everything work

i think a good start, would be to correctly display 1234

See if this works and what you can get from it. It multiplexes the four digits as described above.

I didn’t add the “dot” and may have gotten the order of the digits wrong or something. Compiles, not tested…

#include <Arduino.h> // I am using Platformio plugin in VSCode


//protos
void UpdateDisplayDigits( void );
void UpdateCount( void );
void UpdateDisplay( void );

#define NUM_DIGITS          4           //#     number of digits to display
#define NUM_SEGS            7           //#     number of discrete segments
#define TIME_PER_DIGIT      8333ul      //uS    time per digit in muxed display
#define COUNT_INTERVAL      1000ul      //mS    time per countdown tick

const byte pinDOT = 3; // Declares pins to their segments or common pins on the display.
//
const byte pinA = 11;
const byte pinB = 7;
const byte pinC = 4;
const byte pinD = 2;
const byte pinE = 1;
const byte pinF = 10;
const byte pinG = 5;
//
const byte pinC1 = 12;
const byte pinC2 = 9;
const byte pinC3 = 8;
const byte pinC4 = 6;

const byte Anodes[] = 
{
    pinA,
    pinB,
    pinC,
    pinD,
    pinE,
    pinF,
    pinG    
};

const byte Cathodes[] = 
{
    pinC1,
    pinC2,
    pinC3,
    pinC4
};

unsigned int
    Countdown;
    
byte Digits[NUM_DIGITS];

// digitCodes; which of the 7-segs is on to display a given digit
//  in this table: 1 = segment on
const byte digitCodes[] = 
{
    //GFEDCBA  Segments
    B00111111,  // 0
    B00000110,  // 1
    B01011011,  // 2
    B01001111,  // 3
    B01100110,  // 4
    B01101101,  // 5
    B01111101,  // 6
    B00000111,  // 7
    B01111111,  // 8
    B01101111   // 9
};

void setup() 
{
    byte
        idx;

    Serial.begin(115200);

    //init the segs
    for( idx=0; idx<NUM_SEGS; idx++ )
    {
        pinMode( Anodes[idx], OUTPUT );
        digitalWrite( Anodes[idx], HIGH );
        
    }//for

    //init the cathodes
    for( idx=0; idx<NUM_DIGITS; idx++ )
    {
        pinMode( Cathodes[idx], OUTPUT );
        digitalWrite( Cathodes[idx], LOW );
        
    }//for

    Countdown = 120;        //2 minute start (2 * 60-seconds == 120-sec)
    UpdateDisplayDigits();
    
}//setup;

void loop( void )
{
    UpdateDisplay();    //must run very frequently
    UpdateCount();

}//loop

void UpdateCount( void )
{
    static unsigned long
        timeCount=1000ul;
    unsigned long
        timeNow;

    //once per second decrement the Countdown value until it reaches zero
    timeNow = millis();
    if( (timeNow - timeCount) >= COUNT_INTERVAL )
    {
        timeCount = timeNow;
        
        if( Countdown > 0 )
            Countdown--;

        UpdateDisplayDigits();
        
    }//if
    
}//UpdateCount

void UpdateDisplayDigits( void )
{
    byte
        temp;

    //break countdown into its constituent digits
    temp = (byte)(Countdown / 60);
    Digits[3] = temp / 10;
    Digits[2] = temp - (Digits[3] * 10);
    //
    temp = (byte)(Countdown - (temp * 60));
    Digits[1] = temp / 10;
    Digits[0] = temp - (Digits[1] * 10);

    //for( temp = 0; temp<NUM_DIGITS; temp++ )
    //    Serial.print( Digits[3-temp] );    
    //Serial.println();
    
}//UpdateDisplayDigits

void UpdateDisplay( void )
{
    static unsigned long
        timeDigit = 0;
    unsigned long
        timeNow;
    byte
        dVal,
        digitMask;
    static byte 
        lastDigitIndex = 3,
        digitIndex = 0;

    //to achieve 30Hz on 4 digits means 
    //  t0   t1   t2   t3
    //  0----1----2----3----0...
    //  t0+t1+t2+t3 <= 33.33mS or 33333uS
    //  and
    //  t0=t1=t2=t3=8333.25; use 8333uS per digit (TIME_PER_DIGIT)

    timeNow = micros();
    if( (timeNow - timeDigit) >= TIME_PER_DIGIT )
    {
        timeDigit = timeNow;

        //turn off the current cathode
        digitalWrite( Cathodes[lastDigitIndex], LOW );

        //get the segment mask value of the digit to display in this time slice
        dVal = digitCodes[Digits[digitIndex]];
        digitMask = 0x01;
        for( byte i=0; i<7; i++ )
        {
            digitalWrite( Anodes[i], (dVal & digitMask) ? LOW:HIGH );
            digitMask << 1;
            
        }//for

        //Serial.println( dVal, BIN );

        //turn on the current cathode
        digitalWrite( Cathodes[digitIndex], HIGH );

        //remember current as last
        lastDigitIndex = digitIndex;
        
        //bump current to next; at last digit, return to first
        digitIndex++;
        if( digitIndex == 4 )
            digitIndex = 0;
        
    }//if
    
}//UpdateDisplay