Digital Stopwatch Using 4-Digit 7-Segment Display

Digital Stopwatch Using 4-Digit 7-Segment Display

HARDWARE REQUIRED:

  • PICUNO Microcontroller board
  • 1 × 5461AS-1 4-Digit 7-Segment Display (Common Cathode)
  • 2 × Push Buttons
  • 2 × 10kΩ resistor (pull-down)
  • 8 × 220Ω resistors (Current limiting for LEDs)
  • Breadboard
  • Jumper wires
  • USB cable

DESCRIPTION:

This project implements a digital stopwatch using a 4-digit 7-segment common cathode display (5461AS-1) and two push buttons. The stopwatch displays time in MM:SS format by rapidly switching between 4 digits using time multiplexing. A decimal point between the second and third digits acts as a colon separator. Button 1 starts/stops the stopwatch. Button 2 resets the stopwatch to 00:00.

CIRCUIT DIAGRAM:

Circuit Diagram
  • Connect the PICUNO board to the computer using a USB cable.
  • Connect segment pins A to G and DP of display to GPIO 6 to 13 using 220Ω resistors.
  • Connect Digit pins 1,2,3,4 of the display to GPIO 18,19,20,21 respectively.
  • Connect one side of both Start/Stop and Reset buttons to 3.3 V.
  • Connect the other sides of the buttons to GPIO 15 and 16 respectively and connect a 10kΩ resistor from each GPIO to GND.

SCHEMATIC:

A → 220Ω → GPIO 6

B → 220Ω → GPIO 7

C → 220Ω → GPIO 8

D → 220Ω → GPIO 9

E → 220Ω → GPIO 10

F → 220Ω → GPIO 11

G → 220Ω → GPIO 12

DP → 220Ω → GPIO 13

Digit 1 → GPIO 18

Digit 2 → GPIO 19

Digit 3 → GPIO 20

Digit 4 → GPIO 21

Start/Stop Button one side → 3.3 V

Other side → GPIO 15 → 10kΩ → GND

Reset Button one side → 3.3 V

Other side → GPIO 16 → 10kΩ → GND

CODE -- C:

const int segmentPins[] = {6, 7, 8, 9, 10, 11, 12, 13}; // A-G, DP
const int digitPins[] = {18, 19, 20, 21}; // Digit 1-4
const int startStopBtn = 15;
const int resetBtn = 16;

int digits[10][7] = {
  {1,1,1,1,1,1,0}, {0,1,1,0,0,0,0}, {1,1,0,1,1,0,1}, {1,1,1,1,0,0,1},
  {0,1,1,0,0,1,1}, {1,0,1,1,0,1,1}, {1,0,1,1,1,1,1}, {1,1,1,0,0,0,0},
  {1,1,1,1,1,1,1}, {1,1,1,1,0,1,1}
};

unsigned long previousMillis = 0;
int seconds = 0, minutes = 0;
bool running = false;
bool lastStartStopState = HIGH;
bool lastResetState = HIGH;

void setup() {
  for (int i = 0; i < 8; i++) pinMode(segmentPins[i], OUTPUT);
  for (int i = 0; i < 4; i++) pinMode(digitPins[i], OUTPUT);
  pinMode(startStopBtn, INPUT);
  pinMode(resetBtn, INPUT);
}

void loop() {
  handleButtons();
  if (running && millis() - previousMillis >= 1000) {
    previousMillis = millis();
    seconds++;
    if (seconds >= 60) {
      seconds = 0;
      minutes = (minutes + 1) % 60;
    }
  }
  displayTime();
}

void handleButtons() {
  bool currentStartStop = digitalRead(startStopBtn);
  if (currentStartStop == HIGH && lastStartStopState == LOW) {
    running = !running;
    delay(200);
  }
  lastStartStopState = currentStartStop;

  bool currentReset = digitalRead(resetBtn);
  if (currentReset == HIGH && lastResetState == LOW) {
    running = false;
    minutes = 0; seconds = 0;
    delay(200);
  }
  lastResetState = currentReset;
}

void displayTime() {
  int values[4] = {
    minutes / 10,
    minutes % 10,
    seconds / 10,
    seconds % 10
  };

  for (int i = 0; i < 4; i++) {
    digitalWrite(digitPins[i], LOW);
    showDigit(values[i]);
    
    if (i == 1) {
      digitalWrite(segmentPins[7], HIGH); // DP ON between D2 and D3
    } else {
      digitalWrite(segmentPins[7], LOW); // DP OFF
    }

    delay(2);
    clearSegments();
    digitalWrite(digitPins[i], HIGH);
  }
}

void showDigit(int num) {
  for (int i = 0; i < 7; i++) {
    digitalWrite(segmentPins[i], digits[num][i]);
  }
}

void clearSegments() {
  for (int i = 0; i < 8; i++) {
    digitalWrite(segmentPins[i], LOW);
  }
}
const int segmentPins[] = {6, 7, 8, 9, 10, 11, 12, 13}; - Maps GPIO pins for segments A to G and DP of the 7-segment display.

const int digitPins[] = {18, 19, 20, 21}; - Controls which digit (D1 to D4) is active.

int digits[10][7] - Defines which segments (A-G) to light up for digits 0-9.

handleButtons() - Implements edge detection to toggle the running state on button press. Prevents multiple toggles due to button bounce using a software debounce (delay(200)).

if (millis() - previousMillis >= 1000) - Uses Arduino's millis() function to increment time every 1 second. Avoids using delay() so display stays responsive.

CODE -- PYTHON:

from machine import Pin
import time

# Segment pins A-G + DP on GPIO 6 to 13
segmentPins = [Pin(i, Pin.OUT) for i in range(6, 14)]

# Digit control pins D1-D4 on GPIO 18 to 21
digitPins = [Pin(i, Pin.OUT) for i in range(18, 22)]

# Buttons
startStopBtn = Pin(15, Pin.IN)
resetBtn = Pin(16, Pin.IN)

# Digit segment bitmaps (A-G only)
digits = [
    [1,1,1,1,1,1,0], [0,1,1,0,0,0,0], [1,1,0,1,1,0,1], [1,1,1,1,0,0,1],
    [0,1,1,0,0,1,1], [1,0,1,1,0,1,1], [1,0,1,1,1,1,1], [1,1,1,0,0,0,0],
    [1,1,1,1,1,1,1], [1,1,1,1,0,1,1]
]

# Timer vars
minutes = 0
seconds = 0
running = False
lastStartStop = 0
lastReset = 0
lastMillis = time.ticks_ms()

def clearSegments():
    for pin in segmentPins:
        pin.value(0)

def showDigit(num):
    for i in range(7):
        segmentPins[i].value(digits[num][i])

def displayTime():
    values = [
        minutes // 10,
        minutes % 10,
        seconds // 10,
        seconds % 10
    ]

    for i in range(4):
        digitPins[i].value(0) # Enable digit (LOW)

        showDigit(values[i])
        # DP ON only after digit 2
        if i == 1:
            segmentPins[7].value(1)
        else:
            segmentPins[7].value(0)

        time.sleep_ms(2)
        clearSegments()
        digitPins[i].value(1) # Disable digit (HIGH)

while True:
    # Button logic
    if startStopBtn.value() == 1 and lastStartStop == 0:
        running = not running
        time.sleep_ms(200)
    lastStartStop = startStopBtn.value()

    if resetBtn.value() == 1 and lastReset == 0:
        running = False
        minutes = 0
        seconds = 0
        time.sleep_ms(200)
    lastReset = resetBtn.value()

    # Time counting logic
    if running and time.ticks_diff(time.ticks_ms(), lastMillis) >= 1000:
        lastMillis = time.ticks_ms()
        seconds += 1
        if seconds >= 60:
            seconds = 0
            minutes = (minutes + 1) % 60

    # Display update
    displayTime()
segmentPins = [Pin(i, Pin.OUT) for i in range(6, 14)] - Initializes GPIO pins 6 to 13 as output pins for segments A-G and DP.

digitPins = [Pin(i, Pin.OUT) for i in range(18, 22)] - Initializes GPIOs 18 to 21 for digit select (D1-D4).

digits = [...] - 2D list representing segments (A-G) needed to form numbers 0-9.

def showDigit(num): - Lights up appropriate segments (A-G) to show the number num. DP is controlled separately from inside displayTime().