# Atmega Millis with Quartz crystal

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 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 = {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(!btn_1){
if(state == 0){
state = 1;
selP = 0;
}else{
if(selP >= 3) state = 0;
else selP++;
}
tm = 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){
tm = 0;
changed = 1;
}
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;
str = numbers[led01];
str = numbers[led02];
str = numbers[led11];
str = numbers[led12];
str[selP]=str[selP]|(1<<3);
}
digitalWrite(ST_CPH1, LOW);
shiftOut(DATAH1, SH_CPH1,MSBFIRST, str);
digitalWrite(ST_CPH1, HIGH);

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

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

digitalWrite(ST_CPM2, LOW);
shiftOut(DATAM2, SH_CPM2,MSBFIRST, str);
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 '20 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 '20 at 14:38

• Seems to me like the `switch`/`case` construct is about setting the time with push buttons. – Edgar Bonet May 11 '20 at 18:49