Making stopwatch with 595 and 4 digit display

I am trying to make a stop watch that displays minutes and seconds, and after reaching 59,59, I want it to display hours and minutes. Then it also has a start and reset button. The set button starts the time, and if you press it again you pause the time, and if you press the reset button it resets the time. I have also attached LED's that will light up when certain hours are crossed. The issue I am having is that the display isn't displaying fast enough, it is flickering even though the delay I set for the screen is small enough, I have a common cathode 4 digit 7 segment display.

Connections:

Arduino 8, 9, 10 to 595 14, 12, 11 respectively.

595 1, 2, 3, 4, 5, 6, 7, 15 to display B, C, D, E, F, G, DP, A

595 8 goes to 595 13, which is connected to ground

The Cathodes are connected to 13,12,11, and 6 in the arduino through a resistor.

And my LED's are connected to 2, 3, 4 on the arduino.

My buttons start and reset are connected to 7 and 5 respectively.

Here is an image of what it looks like without the buttons and LED's

Here is my code that I have so far:

//Buzzer
int buzzer = 12; //the pin of active buzzer

//Register
int latch=9;  //74HC595  pin 9 STCP
int clock=10; //74HC595  pin 10 SHCP
int data=8;   //74HC595  pin 8 DS

//Lights
int red = 2; //the pin of red light
int yellow = 3; //the pin of the yellow light
int green = 4; //the pin of the green light

//Buttons
int start = 7; //the pin of start button
int reset = 5; //the pin of start button

//digits
int first = 0;
int second = 0;
int third = 0;
int fourth = 0;

//Hexadecimal Values for 0-9 for 7 Segment Display

unsigned char segmentTable[] = {
  0x3F, // 0
  0x06, // 1
  0x5B, // 2
  0x4F, // 3
  0x66, // 4
  0x6D, // 5
  0x7D, // 6
  0x07, // 7
  0x7F, // 8
  0x6F  // 9
};

unsigned char tableWithDP[] = {
    0xBF, // 0 with DP
    0x86, // 1 with DP
    0xDB, // 2 with DP
    0xCF, // 3 with DP
    0xE6, // 4 with DP
    0xED, // 5 with DP
    0xFD, // 6 with DP
    0x87, // 7 with DP
    0xFF, // 8 with DP
    0xEF  // 9 with DP
};

const int digitPins[] = {6, 11, 12, 13};

// Variables to track time
unsigned long previousMillis = 0;  // Stores the last time update occurred
unsigned long currentMillis = 0;
int seconds = 0;  // Total seconds elapsed
int minutes = 0;
int hours = 0;

// Variables for refresh timing
unsigned long lastRefreshTime = 0;
const int refreshInterval = 1000; // Refresh interval in microseconds (~1ms)
int currentDigit = 0;

// Function prototypes
void displayDigit(int number, int digitIndex);
void updateTime();

void setup() {
  // put your setup code here, to run once:

pinMode(buzzer, OUTPUT);

pinMode(latch,OUTPUT);
pinMode(clock,OUTPUT);
pinMode(data,OUTPUT);

pinMode(red, OUTPUT);
pinMode(yellow, OUTPUT);
pinMode(green, OUTPUT);

pinMode(start, INPUT);
pinMode(reset, INPUT);

// Initialize digit select pins
    for (int i = 0; i < 4; i++) {
        pinMode(digitPins[i], OUTPUT);
        digitalWrite(digitPins[i], HIGH); // Turn off all digits initially
    }
}



void loop() {
  // put your main code here, to run repeatedly:
    static int startPressed = 0;
    int resetPressed = 0;

    while(startPressed == 0){

        int displayDigits[] = {0, 0, 0, 0};

        /*for (int i = 0; i < 4; i++) {
        // Display one digit at a time
            displayDigit(displayDigits[i], i);
            delay(5); // Small delay for persistence of vision
        }*/

        if((digitalRead(start)==HIGH)){

            startPressed = 1;

        }

    }
    long int t1 = millis();

    while((digitalRead(start)==LOW)&&(startPressed==1)&&(resetPressed==0)){

        int displayDigits[] = {first, second, third, fourth};

        updateLights(second);

        // Update time every second
        currentMillis = millis();
        if (currentMillis - previousMillis >= 1000) { // Check if 1 second has passed
            previousMillis = currentMillis;
            updateTime(); // Increment time
        }

        for (int i = 0; i < 4; i++) {
        // Display one digit at a time

            displayDigit(displayDigits[i], i);
            delay(50); // Small delay for persistence of vision
        }

        // Multiplexing: Update display at high speed
        if (micros() - lastRefreshTime >= refreshInterval) {
            lastRefreshTime = micros();

            // Determine what to display
            int displayDigits[4];
            if (hours == 0) {
            // Display MM:SS (minutes and seconds)
                displayDigits[0] = minutes / 10;
                displayDigits[1] = minutes % 10;
                displayDigits[2] = seconds / 10;
                displayDigits[3] = seconds % 10;
            } else {
                // Display HH:MM (hours and minutes)
                displayDigits[0] = hours / 10;
                displayDigits[1] = hours % 10;
                displayDigits[2] = minutes / 10;
                displayDigits[3] = minutes % 10;
            }

        // Display the current digit
        displayDigit(displayDigits[currentDigit], currentDigit);

        // Move to the next digit
        currentDigit = (currentDigit + 1) % 4;
        }

        if(digitalRead(reset)==HIGH){

            resetPressed = 1;

        }
        updateLights(second);


    }

  //start button already pressed and pressed again - stops timer

    while((startPressed==1)&&(resetPressed==0)){

        if(digitalRead(reset)==HIGH){

            resetPressed = 1;

        }

        if(digitalRead(start)==HIGH) {break;};

    }

  //reset button pressed - resets timer and startPressed
    if(resetPressed == 1){

        seconds = 0;
        minutes = 0;
        hours = 0;

    }

}

void displayDigit(int number, int digitIndex) {
  // Turn off all digits
    for (int i = 0; i < 4; i++) {
        digitalWrite(digitPins[i], HIGH);
    }

  // Send segment data for the number to the shift register
    if(digitIndex==1){
        shiftOut(data, clock, MSBFIRST, tableWithDP[number]);
    } else {
        shiftOut(data, clock, MSBFIRST, segmentTable[number]);
    }

  // Latch the data to display it
    digitalWrite(latch, LOW);
    digitalWrite(latch, HIGH);

  // Enable the current digit
    digitalWrite(digitPins[digitIndex], LOW);
}

// Function to update the time
void updateTime() {
    seconds++;
    if (seconds == 60) {
        seconds = 0;
        minutes++;
    }
    if (minutes == 60) {
        minutes = 0;
        hours++;
    }
    if (hours == 100) {
    // Reset to zero after 99 hours (to fit display)
        hours = 0;
    }
}

void updateLights(int hours){

    if(hours == 8){

        digitalWrite(red, HIGH);

    } else if(hours > 5){

        digitalWrite(yellow, HIGH);

    } else{

        digitalWrite(green, HIGH);

    }

}

Here is what chatgpt has given me instead, which is working slightly better but it is still flickering:

//Buzzer
int buzzer = 12; //the pin of active buzzer

//Register
int latch=9;  //74HC595  pin 9 STCP
int clock=10; //74HC595  pin 10 SHCP
int data=8;   //74HC595  pin 8 DS

//Lights
int red = 2; //the pin of red light
int yellow = 3; //the pin of the yellow light
int green = 4; //the pin of the green light

//Buttons
int start = 7; //the pin of start button
int reset = 5; //the pin of start button

//digits
int first = 0;
int second = 0;
int third = 0;
int fourth = 0;

//Hexadecimal Values for 0-9 for 7 Segment Display

unsigned char segmentTable[] = {
  0x3F, // 0
  0x06, // 1
  0x5B, // 2
  0x4F, // 3
  0x66, // 4
  0x6D, // 5
  0x7D, // 6
  0x07, // 7
  0x7F, // 8
  0x6F  // 9
};

unsigned char tableWithDP[] = {
    0xBF, // 0 with DP
    0x86, // 1 with DP
    0xDB, // 2 with DP
    0xCF, // 3 with DP
    0xE6, // 4 with DP
    0xED, // 5 with DP
    0xFD, // 6 with DP
    0x87, // 7 with DP
    0xFF, // 8 with DP
    0xEF  // 9 with DP
};

const int digitPins[] = {6, 11, 12, 13};

// Variables to track time
unsigned long previousMillis = 0;  // Stores the last time update occurred
unsigned long currentMillis = 0;
int seconds = 0;  // Total seconds elapsed
int minutes = 0;
int hours = 0;

// Variables for refresh timing
unsigned long lastRefreshTime = 0;
const int refreshInterval = 100; // Refresh interval in microseconds (~1ms)
int currentDigit = 0;

// Function prototypes
void displayDigit(int number, int digitIndex);
void updateTime();

void setup() {
  // put your setup code here, to run once:

pinMode(buzzer, OUTPUT);

pinMode(latch,OUTPUT);
pinMode(clock,OUTPUT);
pinMode(data,OUTPUT);

pinMode(red, OUTPUT);
pinMode(yellow, OUTPUT);
pinMode(green, OUTPUT);

pinMode(start, INPUT);
pinMode(reset, INPUT);

// Initialize digit select pins
    for (int i = 0; i < 4; i++) {
        pinMode(digitPins[i], OUTPUT);
        digitalWrite(digitPins[i], HIGH); // Turn off all digits initially
    }
}



unsigned long previousMillisDisplay = 0; // Tracks last refresh time
const int digitRefreshInterval = 2;      // Refresh each digit every 2 ms
                // Current digit being displayed

void loop() {
    static int startPressed = 0;
    int resetPressed = 0;

    // Wait for start button press
    while (startPressed == 0) {
        int displayDigits[] = {0, 0, 0, 0}; // Default display: 0000
        multiplexDisplay(displayDigits);   // Refresh display smoothly

        if (digitalRead(start) == HIGH) {
            startPressed = 1;
        }
    }

    // Stopwatch is running
    while ((digitalRead(start) == LOW) && (startPressed == 1) && (resetPressed == 0)) {
        // Update time every second
        currentMillis = millis();
        if (currentMillis - previousMillis >= 1000) {
            previousMillis = currentMillis;
            updateTime();        // Increment time
            updateLights(hours); // Update lights
        }

        // Determine digits to display
        int displayDigits[4];
        if (hours == 0) {
            // Display MM:SS
            displayDigits[0] = minutes / 10;
            displayDigits[1] = minutes % 10;
            displayDigits[2] = seconds / 10;
            displayDigits[3] = seconds % 10;
        } else {
            // Display HH:MM
            displayDigits[0] = hours / 10;
            displayDigits[1] = hours % 10;
            displayDigits[2] = minutes / 10;
            displayDigits[3] = minutes % 10;
        }

        // Refresh the display smoothly
        multiplexDisplay(displayDigits);

        // Check for reset button
        if (digitalRead(reset) == HIGH) {
            resetPressed = 1;
        }
    }

    while((startPressed==1)&&(resetPressed==0)){

        if(digitalRead(reset)==HIGH){

            resetPressed = 1;

        }

        if(digitalRead(start)==HIGH) {break;};

    } 

    // Reset or pause logic
    if (resetPressed == 1) {
        seconds = 0;
        minutes = 0;
        hours = 0;
        startPressed = 0; // Reset stopwatch
    }
}

// Function for non-blocking display multiplexing
void multiplexDisplay(int displayDigits[]) {
    unsigned long currentMillisDisplay = millis();

    // Check if it's time to update the next digit
    if (currentMillisDisplay - previousMillisDisplay >= digitRefreshInterval) {
        previousMillisDisplay = currentMillisDisplay;

        // Turn off all digits
        for (int i = 0; i < 4; i++) {
            digitalWrite(digitPins[i], HIGH);
        }

        // Send segment data for the current digit
        if (currentDigit == 1) {
            shiftOut(data, clock, MSBFIRST, tableWithDP[displayDigits[currentDigit]]);
        } else {
            shiftOut(data, clock, MSBFIRST, segmentTable[displayDigits[currentDigit]]);
        }

        // Latch the data to the shift register
        digitalWrite(latch, LOW);
        digitalWrite(latch, HIGH);

        // Enable the current digit
        digitalWrite(digitPins[currentDigit], LOW);

        // Move to the next digit
        currentDigit = (currentDigit + 1) % 4;
    }
}


void displayDigit(int number, int digitIndex) {
  // Turn off all digits
    for (int i = 0; i < 4; i++) {
        digitalWrite(digitPins[i], HIGH);
    }

  // Send segment data for the number to the shift register
    if(digitIndex==1){
        shiftOut(data, clock, MSBFIRST, tableWithDP[number]);
    } else {
        shiftOut(data, clock, MSBFIRST, segmentTable[number]);
    }

  // Latch the data to display it
    digitalWrite(latch, LOW);
    digitalWrite(latch, HIGH);

  // Enable the current digit
    digitalWrite(digitPins[digitIndex], LOW);
}

// Function to update the time
void updateTime() {
    seconds++;
    if (seconds == 60) {
        seconds = 0;
        minutes++;
    }
    if (minutes == 60) {
        minutes = 0;
        hours++;
    }
    if (hours == 100) {
    // Reset to zero after 99 hours (to fit display)
        hours = 0;
    }
}

void updateLights(int hours){

    if(hours == 8){

        digitalWrite(red, HIGH);
        digitalWrite(yellow, LOW);
        digitalWrite(green, LOW);

    } else if(hours > 5){

        digitalWrite(red, LOW);
        digitalWrite(yellow, HIGH);
        digitalWrite(green, LOW);

    } else{

        digitalWrite(red, LOW);
        digitalWrite(yellow, LOW);
        digitalWrite(green, HIGH);

    }

}

You commented this out, which looks like it is displaying each number.

This looks similar to the other digit display, but a munch longer delay()

I tried it but it is still flickering, even when the delay is small.

I just tried a barebones test with the following code:

And it is still flickering, I am not sure why. I checked my connections and they are all in the right place. Do you think that the 595 chip speed is what is slowing it down?

//Register
int latch=9;  //74HC595  pin 9 STCP
int clock=10; //74HC595  pin 10 SHCP
int data=8;   //74HC595  pin 8 DS

//Lights
int red = 2; //the pin of red light
int yellow = 3; //the pin of the yellow light
int green = 4; //the pin of the green light

//Buttons
int start = 7; //the pin of start button
int reset = 5; //the pin of start button

//digits
int first = 0;
int second = 0;
int third = 0;
int fourth = 0;

int led = 13;

//Hexadecimal Values for 0-9 for 7 Segment Display

unsigned char segmentTable[] = {
  0x3F, // 0
  0x06, // 1
  0x5B, // 2
  0x4F, // 3
  0x66, // 4
  0x6D, // 5
  0x7D, // 6
  0x07, // 7
  0x7F, // 8
  0x6F  // 9
};

unsigned char tableWithDP[] = {
    0xBF, // 0 with DP
    0x86, // 1 with DP
    0xDB, // 2 with DP
    0xCF, // 3 with DP
    0xE6, // 4 with DP
    0xED, // 5 with DP
    0xFD, // 6 with DP
    0x87, // 7 with DP
    0xFF, // 8 with DP
    0xEF  // 9 with DP
};

const int digitPins[] = {6, 11, 12, 13};

// Variables to track time
unsigned long previousMillis = 0;  // Stores the last time update occurred
unsigned long currentMillis = 0;
int seconds = 0;  // Total seconds elapsed
int minutes = 0;
int hours = 0;

// Variables for refresh timing
unsigned long lastRefreshTime = 0;
const int refreshInterval = 1000; // Refresh interval in microseconds (~1ms)
int currentDigit = 0;

// Function prototypes
void displayDigit(int number, int digitIndex);
void updateTime();

void setup() {
  // put your setup code here, to run once:

pinMode(latch,OUTPUT);
pinMode(clock,OUTPUT);
pinMode(data,OUTPUT);

pinMode(red, OUTPUT);
pinMode(yellow, OUTPUT);
pinMode(green, OUTPUT);

pinMode(start, INPUT);
pinMode(reset, INPUT);

pinMode(led, OUTPUT);

// Initialize digit select pins
    for (int i = 0; i < 4; i++) {
        pinMode(digitPins[i], OUTPUT);
        digitalWrite(digitPins[i], HIGH); // Turn off all digits initially
    }
}


int test = 0;
void loop() {
  // put your main code here, to run repeatedly:
    static int startPressed = 0;
    int resetPressed = 0;



  //while(startPressed == 0){

        int displayDigits[] = {0, 0, test,0};

        digitalWrite(digitPins[2], HIGH);
        shiftOut(data, clock, MSBFIRST, tableWithDP[test%10]);
        digitalWrite(latch, LOW);
    digitalWrite(latch, HIGH);
    digitalWrite(digitPins[2], LOW);
        
delay(10);


        if((digitalRead(start)==HIGH)){

            startPressed = 1;

            test++;


        }

  //}

}

void displayDigit(int number, int digitIndex) {
  // Turn off all digits

    digitalWrite(digitPins[0], HIGH);
    digitalWrite(digitPins[1], HIGH);
    digitalWrite(digitPins[2], HIGH);
    digitalWrite(digitPins[3], HIGH);

  // Send segment data for the number to the shift register
    if(digitIndex==1){
        shiftOut(data, clock, MSBFIRST, tableWithDP[number]);
    } else {
        shiftOut(data, clock, MSBFIRST, segmentTable[number]);
    }

  // Latch the data to display it
    digitalWrite(latch, LOW);
    digitalWrite(latch, HIGH);

  // Enable the current digit
    digitalWrite(digitPins[digitIndex], LOW);
}

// Function to update the time
void updateTime() {
    seconds++;
    if (seconds == 60) {
        seconds = 0;
        minutes++;
    }
    if (minutes == 60) {
        minutes = 0;
        hours++;
    }
    if (hours == 100) {
    // Reset to zero after 99 hours (to fit display)
        hours = 0;
    }
}

/*void UpdateLights(int hours){

    if(hours == 8){

        digitalWrite(red, HIGH);

    } else if(hours > 5){

        digitalWrite(yellow, HIGH);

    } else{

        digitalWrite(green, HIGH);

    }

}

*/

Maybe, update only the changing digit.

look this over

  • it separates the 3 main funcitons of the code: refreshing the display, recognizing a button press and updating the digits being displayed

  • it demonstrates refreshing the display using a timer and sequencing thru each digit on the display. i tested this with a [MultiFunction shield] (https://www.mpja.com/download/hackatronics-arduino-multi-function-shield.pdf) which has a 2nd 595 to drive the digit enable pins

  • it implements a debounced button detection w/o using delay() which would interfere with refreshign the display

  • it uses a state to determine how to update the display digits

  • it does not set the decimal pt appropriately

#undef MY_HW
#ifdef MY_HW
//Buzzer
int buzzer = 12; //the pin of active buzzer

int latch = 4;
int clock = 7;
int data  = 8;

//Lights
int red    = 13;
int yellow = 12;
int green  = 11;

//Buttons
int start = A1;
int reset = A2;

const byte SegmentTbl[] = {
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0X80, 0X90
};

int digitBits  [] = {1, 2, 4, 8};

// -------------------------------------
#else
//Buzzer
int buzzer = 12; //the pin of active buzzer

//Register
int latch=9;  //74HC595  pin 9 STCP
int clock=10; //74HC595  pin 10 SHCP
int data=8;   //74HC595  pin 8 DS

//Lights
int red = 2; //the pin of red light
int yellow = 3; //the pin of the yellow light
int green = 4; //the pin of the green light

//Buttons
int start = 7; //the pin of start button
int reset = 5; //the pin of start button

unsigned char SegmentTbl[] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};
#endif

// -----------------------------------------------------------------------------
const int digitPins[] = {6, 11, 12, 13};

unsigned char tableWithDP[] = {
    0xBF, // 0 with DP
    0x86, // 1 with DP
    0xDB, // 2 with DP
    0xCF, // 3 with DP
    0xE6, // 4 with DP
    0xED, // 5 with DP
    0xFD, // 6 with DP
    0x87, // 7 with DP
    0xFF, // 8 with DP
    0xEF  // 9 with DP
};

const int Ndigits = 4;
int displayDigits [] = { 9, 8, 7, 6 };
int digitIdx;

unsigned long  msec;

// -----------------------------------------------------------------------------
void displayDigit (
    int number,
    int digitIdx)
{
#ifdef MY_HW
    shiftOut (data, clock, MSBFIRST, SegmentTbl [number]);
    shiftOut (data, clock, MSBFIRST, digitBits  [digitIdx]);
#else
    for (int i = 0; i < Ndigits; i++)
        digitalWrite (digitPins[i], HIGH);      // Turn off all digits

    if (digitIdx == 1)
        shiftOut (data, clock, MSBFIRST, tableWithDP[number]);
    else
        shiftOut (data, clock, MSBFIRST, SegmentTbl[number]);

    digitalWrite (digitPins[digitIdx], LOW);   // turn on the digit
#endif

    // Latch the data to display it
    digitalWrite (latch, LOW);
    digitalWrite (latch, HIGH);
}

// -----------------------------------------------------------------------------
const unsigned long MsecDebounce = 30;

struct But {
    const byte    Pin;
    byte          state;
    unsigned long msec;
} buts [] {
    { A1 },
    { A2 },
};

enum { ButStart = 0, ButReset };

// -------------------------------------
// check for but press w/o using delay
bool
isPressed (
    int idx )
{
    if (buts [idx].msec)  {
        if (msec - buts [idx].msec < MsecDebounce)
            return false;

        buts [idx].msec = 0;                            // clear debounce
    }

    byte but = digitalRead (buts [idx].Pin);
    if (buts [idx].state != but)  {
        buts [idx].state  = but;
        buts [idx].msec = 0 == msec ? 1 : msec;         // avoid zero

        if (LOW == but)
            return true;
    }

    return false;
}

// -----------------------------------------------------------------------------
const unsigned long MsecPeriod = 5;
unsigned long msecDisp;
unsigned long msecClk;
unsigned long msec0;                // watch start time

enum { Reset, Run, Hold };
int  state = Reset;

// -------------------------------------
void loop ()
{
    msec = millis ();

    // -------------------------------------
    // refresh display
    if (msec - msecDisp >= MsecPeriod) {
        msecDisp += MsecPeriod;

        displayDigit (displayDigits [digitIdx], digitIdx);

        Serial.print (digitIdx);
        Serial.print (" ");
        Serial.println (displayDigits [digitIdx]);

        if (Ndigits <= ++digitIdx)
            digitIdx = 0;
    }

    // -------------------------------------
    // update time
    unsigned long dMsec = msec - msec0;

    switch (state)  {
    case Run:
        displayDigits [3] = (dMsec /= 100) % 10; 
        displayDigits [2] = (dMsec /= 10)  % 10; 
        displayDigits [1] = (dMsec /= 10)  % 10; 
        displayDigits [0] = (dMsec /= 10)  % 10; 
        break;

    case Hold:
        break;

    case Reset:
        displayDigits [3] = 0;
        displayDigits [2] = 0;
        displayDigits [1] = 0;
        displayDigits [0] = 0;
        break;
    }
    
    // -------------------------------------
    // check buttons
    if (isPressed (ButReset))
        state = Reset;

    if (isPressed (ButStart))  {
        switch (state) {
        case Reset:
            msec0 = msec;
            state = Run;
            break;

        case Hold:
            state = Run;
            break;

        case Run:
            state = Hold;
            break;
        }
    }
}


// -----------------------------------------------------------------------------
void setup () {
    Serial.begin (9600);

    pinMode (buzzer, OUTPUT);

    pinMode (latch,OUTPUT);
    pinMode (clock,OUTPUT);
    pinMode (data,OUTPUT);

    pinMode (red, OUTPUT);
    pinMode (yellow, OUTPUT);
    pinMode (green, OUTPUT);

    pinMode (start, INPUT);
    pinMode (reset, INPUT);

    // Initialize digit select pins
    for (int i = 0; i < 4; i++) {
        pinMode (digitPins[i], OUTPUT);
        digitalWrite (digitPins[i], HIGH); // Turn off all digits initially
    }
}

it's more efficient to map the number value to segment bits. This also makes is easier to handle the decimal-pt

#define MY_HW
#ifdef MY_HW
//Buzzer
int buzzer = 12; //the pin of active buzzer

int latch = 4;
int clock = 7;
int data  = 8;

//Lights
int red    = 13;
int yellow = 12;
int green  = 11;

//Buttons
int start = A1;
int reset = A2;

const byte SegmentTbl[] = {
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0X80, 0X90
};

int digitBits  [] = {1, 2, 4, 8};

const byte DecPt  = 0x80;
const byte SegOff = 0xFF;

// -------------------------------------
#else
//Buzzer
int buzzer = 12; //the pin of active buzzer

//Register
int latch=9;  //74HC595  pin 9 STCP
int clock=10; //74HC595  pin 10 SHCP
int data=8;   //74HC595  pin 8 DS

//Lights
int red = 2; //the pin of red light
int yellow = 3; //the pin of the yellow light
int green = 4; //the pin of the green light

//Buttons
int start = 7; //the pin of start button
int reset = 5; //the pin of start button

unsigned char SegmentTbl[] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

const byte DecPt  = 0x80;
const byte SegOff = 0x00;
#endif

// -----------------------------------------------------------------------------
const int digitPins[] = {6, 11, 12, 13};

unsigned char tableWithDP[] = {
    0xBF, // 0 with DP
    0x86, // 1 with DP
    0xDB, // 2 with DP
    0xCF, // 3 with DP
    0xE6, // 4 with DP
    0xED, // 5 with DP
    0xFD, // 6 with DP
    0x87, // 7 with DP
    0xFF, // 8 with DP
    0xEF  // 9 with DP
};

const int Ndigits = 4;
int displayDigits [] = { 9, 8, 7, 6 };
int displayBits   [] = { 9, 8, 7, 6 };
int digitIdx;

unsigned long  msec;

// -----------------------------------------------------------------------------
void displayDigit (
    int number,
    int digitIdx)
{
#ifdef MY_HW
    shiftOut (data, clock, MSBFIRST, displayBits [digitIdx]);
    shiftOut (data, clock, MSBFIRST, digitBits   [digitIdx]);
#else
    for (int i = 0; i < Ndigits; i++)
        digitalWrite (digitPins[i], HIGH);      // Turn off all digits

    shiftOut (data, clock, MSBFIRST, displayBits [digitIdx]);

    digitalWrite (digitPins[digitIdx], LOW);   // turn on the digit
#endif

    // Latch the data to display it
    digitalWrite (latch, LOW);
    digitalWrite (latch, HIGH);
}

// -----------------------------------------------------------------------------
const unsigned long MsecDebounce = 30;

struct But {
    const byte    Pin;
    byte          state;
    unsigned long msec;
} buts [] {
    { A1 },
    { A2 },
};

enum { ButStart = 0, ButReset };

// -------------------------------------
// check for but press w/o using delay
bool
isPressed (
    int idx )
{
    if (buts [idx].msec)  {
        if (msec - buts [idx].msec < MsecDebounce)
            return false;

        buts [idx].msec = 0;                            // clear debounce
    }

    byte but = digitalRead (buts [idx].Pin);
    if (buts [idx].state != but)  {
        buts [idx].state  = but;
        buts [idx].msec = 0 == msec ? 1 : msec;         // avoid zero

        if (LOW == but)
            return true;
    }

    return false;
}

// -----------------------------------------------------------------------------
const unsigned long MsecPeriod = 5;
unsigned long msecDisp;
unsigned long msecClk;
unsigned long msec0;                // watch start time

enum { Reset, Run, Hold };
int  state = Reset;

// -------------------------------------
void loop ()
{
    msec = millis ();

    // -------------------------------------
    // refresh display
    if (msec - msecDisp >= MsecPeriod) {
        msecDisp += MsecPeriod;

        displayDigit (displayDigits [digitIdx], digitIdx);

#if 0
        Serial.print (~DecPt, HEX);
        Serial.print (" ");
        Serial.print (digitIdx);
        Serial.print (" ");
        Serial.println (displayBits [digitIdx], HEX);
#endif

        if (Ndigits <= ++digitIdx)
            digitIdx = 0;
    }

    // -------------------------------------
    // update time
    unsigned long dMsec = msec - msec0;
    byte          d0 = 0;
    byte          d1 = 0;

    switch (state)  {
    case Run:
        displayBits [3] = SegmentTbl [(dMsec /= 100) % 10]; 
        displayBits [2] = SegmentTbl [(dMsec /= 10)  % 10]; 
        d1 = (dMsec /= 10)  % 10; 
        d0 = (dMsec /= 10)  % 10; 
        displayBits [1] = SegmentTbl [d1];
        displayBits [0] = SegmentTbl [d0];
        break;

    case Hold:
        break;

    case Reset:
        displayBits [3] = SegmentTbl [0];
        displayBits [2] = SegmentTbl [0];
        displayBits [1] = SegmentTbl [0];
        displayBits [0] = SegmentTbl [0];
        break;
    }

    if (SegOff)
        displayBits [2] &= ~ DecPt;
    else
        displayBits [2] |=   DecPt;

    // suppress leading zeros
    if (0 == d0) {
        displayBits [0] = SegOff;
        if (0 == d1)
            displayBits [1] = SegOff;
    }
    
    // -------------------------------------
    // check buttons
    if (isPressed (ButReset))
        state = Reset;

    if (isPressed (ButStart))  {
        switch (state) {
        case Reset:
            msec0 = msec;
            state = Run;
            break;

        case Hold:
            state = Run;
            break;

        case Run:
            state = Hold;
            break;
        }
    }
}


// -----------------------------------------------------------------------------
void setup () {
    Serial.begin (9600);

    pinMode (buzzer, OUTPUT);

    pinMode (latch,OUTPUT);
    pinMode (clock,OUTPUT);
    pinMode (data,OUTPUT);

    pinMode (red, OUTPUT);
    pinMode (yellow, OUTPUT);
    pinMode (green, OUTPUT);

    pinMode (start, INPUT);
    pinMode (reset, INPUT);

    // Initialize digit select pins
    for (int i = 0; i < 4; i++) {
        pinMode (digitPins[i], OUTPUT);
        digitalWrite (digitPins[i], HIGH); // Turn off all digits initially
    }
}