Clock with 2 shift registers

Hello!

I want to create a clock with 2 shift registers. I appended the wireing diagram and the code:

![image|690x389](upload://aDCnT79buPoJwLBq52xWPEw1Tph.png)
// **Arduino Nano 4-Digit Clock with 74HC595** 

// Pin assignments
const int LATCH_PIN = 4;    // ST_CP (latch) pin of 74HC595 chain
const int CLOCK_PIN = 7;    // SH_CP (clock) pin 
const int DATA_PIN  = 8;    // DS (data) pin
const int BUZZER_PIN = 3;    // Buzzer control pin (for tone output)
const int MODE_BTN = 4;      // Mode/Set button (cycles through setting modes)
const int HOUR_BTN = 5;      // Hour increment button
const int MIN_BTN  = 6;      // Minute increment button
// (PM indicator LED is on shift register IC1 QE output in this design)

// Segment bit patterns for 0-9 on a 7-seg common anode (active LOW segments).
// Format:  bit7 bit6 ... bit0 correspond to DP, G, F, E, D, C, B, A (we will define the mapping)
// Here we use bit0->A, bit1->B, ..., bit6->G, bit7->DP for convenience.
const byte SEGMENT_PATTERNS[10] = {
  // For common anode: '0' means segment ON, '1' means OFF.
  // 0 -> ABCDEF on, G off  (to display 0)
  B11000000,  // 0: segments A,B,C,D,E,F = 0 (ON), G=1 (OFF), DP=1 (off)
  B11111001,  // 1: segments B,C = 0, others 1 (only B,C on)
  B10100100,  // 2: A, B, G, E, D on
  B10110000,  // 3: A, B, C, D, G on
  B10011001,  // 4: F, G, B, C on
  B10010010,  // 5: A, F, G, C, D on
  B10000010,  // 6: A, F, E, D, C, G on
  B11111000,  // 7: A, B, C on
  B10000000,  // 8: all segments on
  B10010000   // 9: A, B, C, D, F, G on (segment E off)
};
// (If your segment wiring order is different, or if using DP differently, adjust patterns accordingly.)

// Variables for time and alarm
volatile unsigned long millisAtLastSecond = 0;  // track when we last updated the seconds
int hours = 12;
int minutes = 0;
int seconds = 0;
bool isPM = false;         // true = PM, false = AM
int alarmHour = 6;
int alarmMin = 30;
bool alarmEnabled = true;
bool alarmTriggered = false;  // becomes true when alarm is sounding

// Variables for button handling
byte mode = 0;  // 0 = normal run, 1 = set hours, 2 = set minutes, 3 = set alarm hours, 4 = set alarm minutes
bool lastModeBtnState = HIGH;
bool lastHourBtnState = HIGH;
bool lastMinBtnState = HIGH;
unsigned long modeBtnDebounceTime = 0;
unsigned long hourBtnDebounceTime = 0;
unsigned long minBtnDebounceTime = 0;
const unsigned long DEBOUNCE_DELAY = 50;  // 50 ms debounce

// Forward declarations
void updateDisplay();
void setShiftRegisterOutput(byte digitMask, byte segmentsPattern);

void setup() {
  // Pin modes
  pinMode(LATCH_PIN, OUTPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(DATA_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(MODE_BTN, INPUT_PULLUP);
  pinMode(HOUR_BTN, INPUT_PULLUP);
  pinMode(MIN_BTN, INPUT_PULLUP);

  // Initialize shift register outputs to 0 (all segments off, all digits off)
  setShiftRegisterOutput(0xFF, 0xFF); // digit mask 0xFF (no digit active, since PNP active low), segments 0xFF (all off)
}

void loop() {
  unsigned long currentMillis = millis();

  // --- Timekeeping using millis() ---
  if (currentMillis - millisAtLastSecond >= 1000) {
    // One second has passed
    millisAtLastSecond += 1000;
    seconds++;
    if (seconds >= 60) {
      seconds = 0;
      minutes++;
      if (minutes >= 60) {
        minutes = 0;
        hours++;
        if (hours >= 24) {
          hours = 0;
        }
      }
    }
    // Manage AM/PM flag
    if (hours == 12) {
      // when hours rolls to 12, toggle AM/PM (0->12 means it's noon, 11->12 means PM flips; 23->0 means midnight flips)
      isPM = !isPM;
    } else if (hours == 0) {
      // 24-hour 0 = 12 AM
      isPM = false;
    }
    // Check alarm
    if (alarmEnabled && hours == alarmHour && minutes == alarmMin && seconds == 0) {
      // Trigger alarm at the exact match (on the minute)
      alarmTriggered = true;
      tone(BUZZER_PIN, 1000);  // start alarm tone at 1 kHz
    }
  }

  // --- Handle buttons (debounced) ---
  // Read raw states
  bool modeBtnState = digitalRead(MODE_BTN);
  bool hourBtnState = digitalRead(HOUR_BTN);
  bool minBtnState  = digitalRead(MIN_BTN);
  // Mode button (cycle through modes)
  if (modeBtnState != lastModeBtnState) {
    modeBtnDebounceTime = currentMillis;
    lastModeBtnState = modeBtnState;
  }
  if ((currentMillis - modeBtnDebounceTime) > DEBOUNCE_DELAY) {
    if (lastModeBtnState == LOW) { // button pressed
      // cycle mode on press (falling edge)
      mode = (mode + 1) % 5;  // cycles 0->1->2->3->4->back to 0
      // If returning to normal mode, we could save or do something if needed
    }
  }
  // Hour button (increment hour in either time set or alarm set mode)
  if (hourBtnState != lastHourBtnState) {
    hourBtnDebounceTime = currentMillis;
    lastHourBtnState = hourBtnState;
  }
  if ((currentMillis - hourBtnDebounceTime) > DEBOUNCE_DELAY) {
    if (lastHourBtnState == LOW) { // pressed
      if (mode == 1) {        // setting hours (current time)
        hours = (hours + 1) % 24;
      } else if (mode == 3) { // setting alarm hours
        alarmHour = (alarmHour + 1) % 24;
      }
      // If in 12h display mode, we will convert when displaying, but internal is 24h
    }
  }
  // Minute button (increment minute for time or alarm)
  if (minBtnState != lastMinBtnState) {
    minBtnDebounceTime = currentMillis;
    lastMinBtnState = minBtnState;
  }
  if ((currentMillis - minBtnDebounceTime) > DEBOUNCE_DELAY) {
    if (lastMinBtnState == LOW) { // pressed
      if (mode == 2) {        // setting minutes (current time)
        minutes = (minutes + 1) % 60;
      } else if (mode == 4) { // setting alarm minutes
        alarmMin = (alarmMin + 1) % 60;
      }
    }
  }

  // If alarm is ringing and any button is pressed, stop the alarm
  if (alarmTriggered && (lastModeBtnState == LOW || lastHourBtnState == LOW || lastMinBtnState == LOW)) {
    noTone(BUZZER_PIN);
    alarmTriggered = false;
  }

  // --- Update display (multiplex) ---
  updateDisplay();  // handles refreshing the 4-digit display via multiplexing
}

// This function handles multiplexing the 4-digit display
void updateDisplay() {
  static byte currentDigit = 0;         // which digit we are currently displaying (0-3 for D1-D4)
  static unsigned long lastRefresh = 0;
  const unsigned long REFRESH_INTERVAL = 2;  // refresh each digit every 2ms (gives ~500 Hz multiplex rate / 125Hz per digit)
  unsigned long now = millis();
  if (now - lastRefresh < REFRESH_INTERVAL) {
    return; // not time to update yet
  }
  lastRefresh = now;
  
  // Determine the value to display on this digit
  int displayHour = hours;
  bool pmIndicator = isPM;
  // For 12-hour format display:
  int hour12 = displayHour % 12;
  if (hour12 == 0) hour12 = 12; // display 12 instead of 0
  // We will display hour12 on D1 and D2 (with leading 0 blanked out), and minutes on D3, D4.

  byte segmentsByte;
  byte digitMask = 0xFF;  // all high (no digit on by default, PNP off)

  // Select which digit to update this cycle (currentDigit cycles 0-3 corresponding to D1..D4)
  switch (currentDigit) {
    case 0: // D1 (tens of hour)
      // tens of hour (if hour is 7, tens digit is blank, we can display as blank or 0 depending on preference)
      if (hour12 >= 10) {
        int tens = hour12 / 10;
        segmentsByte = SEGMENT_PATTERNS[tens];
      } else {
        // If hour < 10, turn off all segments (blank) or optionally display 0 if you want leading zero
        segmentsByte = B11111111;  // all segments off (blank)
      }
      // If in a time-setting mode, we can blink this digit to indicate it's being set:
      if (mode == 1) {
        // Blink the hour digits when setting current time hours
        static bool blinkOn = true;
        if ((millis() % 500) < 250) { // 2Hz blink
          segmentsByte = B11111111;  // half the time blank it
        }
      } else if (mode == 3) {
        // Blink the alarm hour if in alarm hour set mode
        if ((millis() % 500) < 250) {
          segmentsByte = B11111111;
        }
      }
      digitMask = ~(1 << 0); // activate digit 0 (D1) -> ~(00000001) = 0xFE for PNP logic active low
      break;
    case 1: // D2 (ones of hour)
      segmentsByte = SEGMENT_PATTERNS[hour12 % 10];
      if (mode == 1) {
        // Blink hour ones too during setting
        if ((millis() % 500) < 250) segmentsByte = B11111111;
      } else if (mode == 3) {
        // Blink alarm hour ones
        if ((millis() % 500) < 250) segmentsByte = B11111111;
      }
      digitMask = ~(1 << 1); // activate D2 only
      break;
    case 2: // D3 (tens of minute)
      segmentsByte = SEGMENT_PATTERNS[minutes / 10];
      if (mode == 2) {
        // Blink minute tens if setting current minutes
        if ((millis() % 500) < 250) segmentsByte = B11111111;
      } else if (mode == 4) {
        // Blink alarm minute tens
        if ((millis() % 500) < 250) segmentsByte = B11111111;
      }
      digitMask = ~(1 << 2); // activate D3
      break;
    case 3: // D4 (ones of minute)
      segmentsByte = SEGMENT_PATTERNS[minutes % 10];
      if (mode == 2) {
        // Blink minute ones during setting
        if ((millis() % 500) < 250) segmentsByte = B11111111;
      } else if (mode == 4) {
        // Blink alarm minute ones
        if ((millis() % 500) < 250) segmentsByte = B11111111;
      }
      digitMask = ~(1 << 3); // activate D4
      break;
  }

  // If alarm is currently ringing, we can also flash the display or the AM/PM LED as a visual indicator (optional)
  // e.g., if (alarmTriggered) { segmentsByte = (millis() % 200 < 100) ? segmentsByte : B11111111; }

  // Set the shift register outputs for this digit:
  setShiftRegisterOutput(digitMask, segmentsByte);

  // Move to next digit for next refresh cycle
  currentDigit = (currentDigit + 1) % 4;
}

// Helper: Update the two shift registers (digit control and segments) and latch the output
void setShiftRegisterOutput(byte digitMask, byte segmentsPattern) {
  // digitMask: bit mask for digit drivers (active low for PNP transistors)
  // segmentsPattern: pattern for segments (active low for segments that should light)
  digitalWrite(LATCH_PIN, LOW);
  // Send the digit control byte (plus PM LED bit). Here we also want to include the PM indicator state on one of the upper bits.
  // Assume we wired PM LED to IC1 QE (which corresponds to bit4 of the digit register byte).
  byte digitByte = digitMask;
  // Set the PM indicator bit (bit4) in digitByte:
  if (isPM) {
    // PM LED on -> for common anode LED connected to shift reg output, assume it lights when output is LOW (like segments)
    // If we wired LED anode to 5V and cathode to shift reg, then LOW output lights it.
    digitByte &= ~(1 << 4);  // set bit4 to 0 (active low to turn LED on)
  } else {
    digitByte |= (1 << 4);   // set bit4 to 1 (LED off)
  }
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, digitByte);
  // Send the segments byte second
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, segmentsPattern);
  digitalWrite(LATCH_PIN, HIGH);
}

Welcome to the Forum!
Please insert your code using the code tags. You can go back and fix your original post.
Read the forum guidelines to see how to properly insert the code and some other good information on making a good post.

Is there a problem or do you have a question?

What is the purpose of the left hand shift register? Are you trying to make 16 extra outputs and you haven't yet drawn the outputs for the left shift register?
You only have outputs connected to the right hand shift register, but that one doesn't have any data going into its pin 14. Normally, if a 16 bit shift register is required, the two would be wired as explained here

Sorry I missed seeing those connections, however my comment about the right hand one not getting any data is still valid.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.