Four Digit Seven Segment Display Clock/Timer

I’m having a lot of trouble with this, all I want to do is build a simpletimer. I have pins 9, 10, 11, and 12, attached to the digits and the rest are the segment controls, starting with pin 1 being ‘a’, etc. I’m using an Arduino UNO. So in setup, I set all the pins to output, and then in loop I do this:

int d1=9,d2=10,d3=11,d4=12;
void setup()
{
  int i;
  for(i = 1; i < 13; i++)
    pinMode(i, OUTPUT);
}
 
void loop()
{
  // light up first digit only
  digitalWrite(d2,HIGH);
  digitalWrite(d3,HIGH);
  digitalWrite(d4,HIGH);
  digitalWrite(d1,LOW);
    // light up segments of first digit, display number #1, segments bc
  one();

delay(20);
  // light up second digit only
  digitalWrite(d1,HIGH);
  digitalWrite(d3,HIGH);
  digitalWrite(d4,HIGH);
  digitalWrite(d2,LOW);
  two();
    delay(20);
}

void one()
{
  int i;
  for(i = 0; i < 8; i++)
  {
    if(i == 2 || i == 3)
      digitalWrite(i, HIGH);
    else
      digitalWrite(i, LOW);
  }
}

void two()
{
  int i;
  for(i = 0; i < 8; i++)
  {
    if(i == 1 || i == 2 || i == 4 || i == 5 || i == 7)
      digitalWrite(i, HIGH);
    else
      digitalWrite(i, LOW);
  }
}

So I get a the two numbers 21 on the third and fourth digits of my segment, spelling 21. But how would I get it to count up? I get the cycling back and forth but perhaps I need help on the algorithm. I just want it to count up to 100. The problem I see is changing one number while keeping the other static. Any ideas? I’ve been wrestling with this for a couple of hours, scouring the web but nothing. Do I need shift registers or something? I just have it hooked up directly into the pins using a couple resistors on some of the wires. Thanks!

Well, first thing I would do is not use delays. I would recommend using interrupts for precise timing.

Basic interrupt timer example code

void setup() {
  int frequency = 50; // in hz
  //Interupt Service Routine and timer setup
  noInterrupts();// kill interrupts until everybody is set up
  //We use Timer 1 to refresh the cube, its the only 16 bit timer
  TCCR1A = B00000000;//Register A all 0's since we're not toggling any pins
    // TCCR1B clock prescalers
    // 0 0 1 clkI/O /1 (No prescaling)
    // 0 1 0 clkI/O /8 (From prescaler)
    // 0 1 1 clkI/O /64 (From prescaler)
    // 1 0 0 clkI/O /256 (From prescaler)
    // 1 0 1 clkI/O /1024 (From prescaler)
  TCCR1B = B00001011;//bit 3 set for CTC mode, will call interrupt on counter match, bits 0 and 1 set to divide clock by 64, so 16MHz/64=250kHz
  TIMSK1 = B00000010;//bit 1 set to call the interrupt on an OCR1A match
  OCR1A  = (unsigned long)((250000UL / frequency) - 1UL);//our clock runs at 250kHz, which is 1/250kHz = 4us
  interrupts();//let the show begin, this lets the multiplexing start
}

void loop() {
  
}

ISR(TIMER1_COMPA_vect){ //Interrupt Service Routine, Timer/Counter1 Compare Match A
  
}

Now, lets take an arbitrary refresh rate of 60hz per digit and multiply it by 4 digits, this will be our interrupt frequency (240hz).

Now, everytime you go into the interrupt, update the next digit (1,2,3,4,1,2,3,4,1,2,3,4,…) with the number you want.

Advice: Instead of calculating each digit’s segments everytime you want to display a number, just use a “decoder”, an array holding which segments should be set. Like this,

boolean segmentDecoder[10][7] = {{1,1,1,1,1,1,0},{...},{...},{...},{...},{...},{...},{...},{...},{...}}; //10 digits 0-9 each holding 7 segment details. If it is a 1, then turn the segment on

//Example of reading it
//Lets say you want to output a 3
byte segmentDisplay[7] = {the pins to each segment, example, 3,4,5,6,7,8,9};
byte output = 3; //the desired number to display
for(byte i = 0; i < 7; i++)
  digitalWrite(segmentPin[i], segmentDecoder[output][i]);

Multiplexing! That looks really complicated so please excuse me if that went over my head my friend Ps991, I am still new at this I’m only into my 2nd year as a ECE student. I got down and wrote an algorithm that basically just counts from 0 up. I finally got it to work! The trick is to incorporate the delay between switching digits with the timer! So after each delay I would sum up the delays into an integer and whenever that integer reached 1000ms, I advanced the numbers. And once that seconds hit the 10 digit I would reset it to 0 and advance the tenths digit. Its really rudimentary but itll work! So I was thinking of expanding this to make my own seven segment displays using 12V led strips. Is it possible for the arduino to power this? What else would I need to buy? Thanks for the help by the way! Heres the code for anyone interested!

const char d1 = 9, d2 = 10, d3 = 11, d4 = 12; //digit controller (NOT SEGMENT)
const int numDigits = 4; //four digit display
const int interrupt = 5; //in ms, the delay for multiplexing (switching between each digit rapidly to update screen)
int startNum[] = {0, 0, 0, 0};
int loopTime = 0; //controls timer loop, summed from interrupt, everytime it reaches 1000 ms, or 1 second, it increments the seconds digit
const int interval = 100;
void setup() {
  int i;
  for(i = 0; i < 13; i++)
  {
    pinMode(i, OUTPUT); //set all pins :)
  }
}

void loop() {
  // put your main code here, to run repeatedly:
    int digit = d1;
    int number = numDigits - 1;
    int i;
    for(i = 0; i < numDigits; i++)
    {
      selectDigit(digit); //choose from digits 1 through 4 to display
      pickNumber(startNum[number]); //choose a number 0 - 9
      delay(interrupt);
      ++digit; //next digit
      --number; //i started from end of array :) 
    }
    loopTime += interrupt * numDigits;
    if(loopTime == interval) //controls seconds digit
    {
      ++startNum[numDigits - 1];
      if(startNum[numDigits - 1] == 10) //controls tenth digit
      {
        startNum[numDigits - 1] = 0;
        ++startNum[numDigits - 2];
        if(startNum[numDigits - 2] == 10) //controls hundreds digit
        {
          startNum[numDigits - 2] = 0;
          ++startNum[numDigits - 3];
          if(startNum[numDigits - 3] == 10) //thousands! 
          {
            startNum[numDigits - 3] = 0;
            ++startNum[numDigits - 4];
          }
        }
      }
      loopTime = 0;
    }
  }


void selectDigit(int digit)
{
  int i;
  for(i = d1; i < (d4 + 1); i++)
  {
    if(i == digit)
    {
      i++;
    }
    digitalWrite(i, HIGH);
  }
  digitalWrite(digit, LOW);
}

void pickNumber(int x)   //define pickNumber(x)to display number x
{
  switch(x)
  {
  default: 
    zero(); 
    break;
  case 1: 
    one(); 
    break;
  case 2: 
    two(); 
    break;
  case 3: 
    three(); 
    break;
  case 4: 
    four(); 
    break;
  case 5: 
    five(); 
    break;
  case 6:
    six();
    break;
  case 7:
    seven();
    break;
  case 8:
    eight();
    break;
  case 9: 
    nine();
    break;
  }
}

void zero()
{
  digitalWrite(1, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  digitalWrite(7, LOW);
}

void one()  // define 1 as cathode pin switch
{
  digitalWrite(1, LOW);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, LOW);
  digitalWrite(5, LOW);
  digitalWrite(6, LOW);
  digitalWrite(7, LOW);
}

void two()
{
  digitalWrite(1, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(6, LOW);
  digitalWrite(7, HIGH);
}

void three()
{
  digitalWrite(1, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, LOW);
  digitalWrite(6, LOW);
  digitalWrite(7, HIGH);

}

void four()
{
  digitalWrite(1, LOW);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, LOW);
  digitalWrite(5, LOW);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);
}

void five()
{
  digitalWrite(1, HIGH);
  digitalWrite(2, LOW);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, LOW);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);
}

void six()
{
  digitalWrite(1, HIGH);
  digitalWrite(2, LOW);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);
}

void seven()
{
  digitalWrite(1, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, LOW);
  digitalWrite(5, LOW);
  digitalWrite(6, LOW);
  digitalWrite(7, LOW);
}

void eight()
{
  digitalWrite(1, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);
}

void nine()
{
  digitalWrite(1, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, LOW);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);
}

The thing is, your code is 212 lines long...it is completely unnecessary to be that long and inefficient, my code could probably do that in 1/3 or 1/4 that amount. I have provided you with the tools and some examples, I would recommend trying testing them out so you can learn new and better ways to do things. If you have any immediate questions, we are here to help.

The trick is to incorporate the delay between switching digits with the timer! So after each delay I would sum up the delays into an integer and whenever that integer reached 1000ms, I advanced the numbers.

I'm sorry, but this is just the wrong way to do it.

If you want to increase a number every 1 second, try this example:

void setup(){
  Serial.begin(9600);
}

unsigned long previousMillis = 0;
int seconds = 0;

void loop(){
  if(millis() - previousMillis >= 1000){
    previousMillis() = millis();
    Serial.println(++seconds);
  }
}