1

I followed https://www.arduino.cc/en/Tutorial/ArduinoToBreadboard this tutorial to program my Atmega 328p to run on a breadboard with a 16 MHZ Quarz crystal and two 22pF capacitor. I want to program a digital clock without a RTC (code below). So I wrote my code by using millis(), and set the time exactly to the time of my mobile. After two minutes the Atmega was about a quarter minute early, but it seems to stay there. Why is that? Doesen't it use the crystal? Do I have to program it differently? Or will it stay at this time difference and run more or less accurate now?

My code:

#define ZERO 0b01110111
#define ONE 0b00000110
#define TWO 0b10110011
#define THREE 0b10010111
#define FOUR 0b11000110
#define FIVE 0b11010101
#define SIX  0b11110101
#define SEVEN 0b00000111
#define EIGHT 0b11110111
#define NINE 0b11010111
#define SECOND 1000
static int changed = 0;

static int led01 = 1;
static int led02 = 3;

static int led11 = 4;
static int led12 = 5;

static int state = 0;
static int selP = 0;

static int blink = 0;
static int btn_1 = 0, btn_2 = 0;

static long last = millis();
static long tm = 0;

static int seconds = 0, minutes = 45, hours = 13;

static const int DATAH1 = 2, ST_CPH1 = 3, SH_CPH1 =4;
static const int DATAH2 = 5, ST_CPH2 = 6, SH_CPH2 =7;

static const int DATAM1 = 11, ST_CPM1 = 12, SH_CPM1 =13;
static const int DATAM2 = A0, ST_CPM2 = A1, SH_CPM2 =A2;

static const char numbers[10] = {ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE};
void setup()
{
  pinMode(DATAH1, OUTPUT);
  pinMode(ST_CPH1, OUTPUT);
  pinMode(SH_CPH1, OUTPUT);

  pinMode(DATAH2, OUTPUT);
  pinMode(ST_CPH2, OUTPUT);
  pinMode(SH_CPH2, OUTPUT);

  pinMode(DATAM1, OUTPUT);
  pinMode(ST_CPM1, OUTPUT);
  pinMode(SH_CPM1, OUTPUT);

  pinMode(DATAM2, OUTPUT);
  pinMode(ST_CPM2, OUTPUT);
  pinMode(SH_CPM2, OUTPUT);

  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
}


// main loop
void loop()
{
  long curr = millis();
  tm += curr-last;
  if(tm < 0 || tm > 900000){
    tm = 0;
  }
  last = curr; 
  if(digitalRead(9) == LOW){
    if(!btn_1){
      if(state == 0){
        state = 1;
        selP = 0;
      }else{
        if(selP >= 3) state = 0;
        else selP++;
      }
      tm = 0;
      blink = 0;
    }
    btn_1 = 1;
  }else btn_1 = 0;

  if(state == 0){ //ZEIT LAEUFT
    if(tm >= SECOND){
      seconds+=tm/SECOND;
      tm = tm%SECOND;
      changed = 1;
    }
    if(seconds >= 60){
      minutes += seconds/60;
      seconds = seconds%60;
    }
    if(minutes >= 60){
      hours += minutes/60;
      minutes = minutes%60;
    }
    if(hours >= 24){
      hours = 0;
    }
  }else{ //UHR STELLEN
    if(tm>=500){
        blink = blink?0:1;
        tm = 0;
        changed = 1;
      }
    if(digitalRead(10) == LOW){
      if(!btn_2){
        changed = 1;
        switch(selP){
          case 0: 
            hours+=10;
            if(hours >= 24) hours -= 20;
            break;
          case 1:
            if(hours/10 < 2){ //in else case zaehlt man die 2. Ziffer nur noch bis 3, da 24 kein Bestandteil mehr ist
              if(hours%10 == 9){
                hours-= 9;
              }else hours++;
            }else{
              if(hours%10 == 3){
                hours-= 3;
              }else hours++;
            }
          break;
          case 2:
            minutes+=10;
            if(minutes >=60) minutes = 0;
          break;
          case 3:
             if(minutes%10 == 9){
              minutes-= 9;
            }else minutes++;
        }
      }
      btn_2 = 1;
    }else btn_2 = 0;
  }
  if(changed){
    led01 = hours/10;
    led02 = hours%10;
    led11 = minutes/10;
    led12 = minutes%10;
    byte str[4];
    str[0] = numbers[led01];
    str[1] = numbers[led02];
    str[2] = numbers[led11];
    str[3] = numbers[led12];
    if(blink){
      str[selP]=str[selP]|(1<<3);
    }
    digitalWrite(ST_CPH1, LOW);
    shiftOut(DATAH1, SH_CPH1,MSBFIRST, str[0]);
    digitalWrite(ST_CPH1, HIGH);

    digitalWrite(ST_CPH2, LOW);
    shiftOut(DATAH2, SH_CPH2,MSBFIRST, str[1]);
    digitalWrite(ST_CPH2, HIGH);

    digitalWrite(ST_CPM1, LOW);
    shiftOut(DATAM1, SH_CPM1,MSBFIRST, str[2]);
    digitalWrite(ST_CPM1, HIGH);

    digitalWrite(ST_CPM2, LOW);
    shiftOut(DATAM2, SH_CPM2,MSBFIRST, str[3]);
    digitalWrite(ST_CPM2, HIGH);
  }
}
  • I measured the time with a timer and the time between two minutes seems to be exactly a minute. So why the initial difference. – Sprinklerkopf May 11 at 13:47
  • Maybe you could simplify your code by using RTC_Millis(), which already implements the logic of a millis()-based clock. – Edgar Bonet May 11 at 14:38
1

I don’t see anything in this code that allows you to set the time. You set it by hard coding the numbers. That’s fine but it takes some time to compile and load to the board and the board is going to start at that time you hard coded. If the board doesn’t get to start running the program for a few seconds while you upload then it’s obviously going to be a few seconds off.

It’s going to be nearly impossible to guess exactly what time the program starts running. Maybe you could set the time for several minutes into the future, upload, and then unplug and wait until exactly that time to plug the board in and start it. Remember there’s a couple seconds for the boot loader before the program starts to run. It may take some trial and error.

Or you can write code there that allows you to set the time through the serial monitor or something.

| improve this answer | |
  • Seems to me like the switch/case construct is about setting the time with push buttons. – Edgar Bonet May 11 at 18:49
  • Oh ok. I see that part now. I got confused there. Delete the answer I guess. – Delta_G May 11 at 19:08
  • Sorry, I am a IT student and used to write code, not build things - so yeah, thats why my code is that messy :) . So you are saying the global static long last = millis() makes the difference because of the startup time? could I prevent this by putting it at the end of setup()? I could also set it in the first iteration of loop – Sprinklerkopf May 12 at 9:06
  • No that's not what I was saying. First, if it is global then it doesn't need to be static unless there's a whole lot to this project that you're not showing us. I was talking about the line where you give seconds, minutes, and hours their initial value. I thought that was to set the time. Imagine you gave hours an initial value of 13 and then you don't start the board until 15:00, it would be off by two hours. – Delta_G May 12 at 13:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.