Go Down

Topic: Clock with Arduino (Read 7 times) previous topic - next topic

joeSeggiola

Hi everyone, I'm new on Arduino forum.

I would like to realize a clock (hh:mm:ss) with Arduino Diecimila. I've found this code on the web, that should work also with the ~9h millis() overflow:

Code: [Select]
unsigned long current_millis_value = 0;
unsigned long previous_millis_value = 0;
unsigned long m = 0;
unsigned int seconds = 0;
unsigned int minutes = 0;
unsigned int hours = 0;

void loop() {
 current_millis_value = millis();
 m += current_millis_value - previous_millis_value;
 seconds += m / 1000;
 m = m % 1000;
 minutes += seconds / 60;
 seconds = seconds % 60;
 hours += minutes / 60;
 minutes = minutes % 60;
 hours = hours % 24;
 previous_millis_value = current_millis_value;
 delay(150);
}

With some tests I notice that Arduino timer is not so precise: every hour it run 1 seconds faster than real time. So, if Arduino starts at 00:00:00, at 02:00:00 (real time), Arduino says 02:00:01. Is it normal???
This problem can be solved by reducing error: every 2 hours, I decrease the seconds variable...

But there is another problem, that really I can't understand! At the precise moment of overflow, the time (started at 00:00:00) goes from 09:32:39 to 09:45:27! Why?!?! Please, help me!

Thank you, Lorenzo.

bens

Your code doesn't correctly handle the millis() overflow because the overflow doesn't occur on an even data boundary.  You should check to see when current_millis_value is less than previous_millis_value, at which point you should be able to increment m by 1 (since your loop should be fast enough to catch the overflow before more than a millisecond has elapsed).  Or, if you want to be completely accurate, you can increment m by:

MAX_MILLIS_VALUE - previous_millis_value + current_millis_value

Also, you should disable interrupts while calling millis(), otherwise you run the risk of getting corrupted data should the timer0 overflow occur while millis() is performing its computation:

cli();  // disable interrupts
current_millis_value = millis();
sei();  // enable interrupts

And yes, if the uncertainty of your crystal is 0.05%, you can expect a worst case error of around 2 seconds per hour, but this error will be a constant that you can calibrate away.

- Ben

joeSeggiola

Quote
Your code doesn't correctly handle the millis() overflow because the overflow doesn't occur on an even data boundary.  You should check to see when current_millis_value is less than previous_millis_value, at which point you should be able to increment m by 1 (since your loop should be fast enough to catch the overflow before more than a millisecond has elapsed).  Or, if you want to be completely accurate, you can increment m by:
MAX_MILLIS_VALUE - previous_millis_value + current_millis_value

Ok! I've completely understand your explanation! In fact, ~9h does not correnspond to 2^32-1 milliseconds...
And what is the MAX_MILLIS_VALUE? How can I get it? I search the forum: someone says that:
"it would appear that the millis() overflow occurs at 34,359,739ms or 9h, 32m, 39s, and 739ms"
There is a way to calculate it? This seems a result of a test, not of a precise calculation...

Quote
Also, you should disable interrupts while calling millis(), otherwise you run the risk of getting corrupted data should the timer0 overflow occur while millis() is performing its computation:
cli();  // disable interrupts
current_millis_value = millis();
sei();  // enable interrupts

I will.

Quote
And yes, if the uncertainty of your crystal is 0.05%, you can expect a worst case error of around 2 seconds per hour, but this error will be a constant that you can calibrate away.

Ok, perfect.

Thank you Ben!

bens

#3
Jul 06, 2008, 01:14 pm Last Edit: Jul 06, 2008, 01:16 pm by bens Reason: 1
You can find the millis() and timer0 overflow code in wiring.c in your arduino-0011/hardware/cores/arduino directory, so you should be able to compute from this what the maximum overflow value is.

Actually, since I just went to look for the code myself it was easy enough for me to just calculate the value for you.  millis() will reach a maximum value of:

33554431

before overflowing back to zero.  This comes out to 9.320675 hours.  But as I said, your loop is tight enough that you can probably just detect the overflow and assume 1 ms has elapsed.  This will almost always be true, but there is a slim chance that this could cause you to be 1 ms off (and losing one millisecond every 9.3 hours is not going to be that big of a deal).

- Ben

joeSeggiola

I've found wiring.c, but I can't understand how you have calculated your maximum value... Sorry, can you explain me?

About the overflow detection... My loop has got a delay of 150ms, so the maximum error is 150ms every 9h, not only 1ms...

Go Up