Go Down

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

joeSeggiola

I don't think I loose ms on each loop. Let's see the first code I post:
Code: [Select]
 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;

If you think about it, you will notice that m is a "cumulative-ms" variable. On every loop, I increase seconds by m / 1000, but then I do m = m % 1000 in order to not loose left-over ms... Right?

mem

#16
Jul 07, 2008, 09:17 pm Last Edit: Jul 07, 2008, 09:19 pm by mem Reason: 1
Joe, you are quite right that its not your code that  is causing time errors but I remain surprised that your crystal is so inaccurate.  I have just completed a test using a third arduino board (as I mentioned, two other boards were tested to be accurate to within a few seconds per day measured over many days) to see if I could reproduce the kind of results you posted.

My test on the third board has been running for over 7 and a half hours and the arduino is within on second of internet checked atomic time. So I am not sure why your results are so different to mine, and why they seem to be outside the spec for a 30ppm crystal.

mem

Hi Joe, update on the arduino time test:  My test has been running for over 24 hours with the arduino clock remaining within two seconds of an external time reference over that period. Thats three different boards verified as keeping time within about 2 secs per day.


Is your board using a resonator instead of a crystal?

joeSeggiola

Quote
Is your board using a resonator instead of a crystal?

Umh, I don't know... Mine is an Arduino Diecimila...

joker

Quote
Quote
Do you know what the specs say for the accuracy of the crystal used on Arduinos?

No, I don't know. I've only measured time difference with a stopwatch, looking at Arduino and an atomic clock via internet. I can be a little imprecise, but over 6 hours of measuring, results are clear:
Code: [Select]
0h       0.00s
1h      -0.70s
2h      -1.10s
3h      -1.60s
4h      -2.00s
5h      -2.40s
6h      -3.00s
7h      -3.50s


This morning I read result of the overflow test from my Arduino EEPROM. It's exactly the value you calculate: 34359737 before overflow, 0 after overflow. So I will use:
Code: [Select]
#define MAX_MILLIS_VALUE 34359738
...
if (current_millis_value < previous_millis_value)
    m += MAX_MILLIS_VALUE - previous_millis_value + current_millis_value;
else m += current_millis_value - previous_millis_value;

On the simpler overflow (from 34359737 to 0):
MAX_MILLIS_VALUE - previous_millis_value + current_millis_value = 34359738 - 34359737 + 0 = 1
1 millisecond is elapsed.
Thank you Ben!  ;)


Nice post indeed. I though I could understand the question in snap... no way  ;)
Question is: why nobody uses the modulo function in order to have a smooth constant elapsed time calculation? Kind of:

elapsed_time=(max_counts+this_tick-last_tick)%max_counts ?

Am I misleading?


mikalhart

#20
Sep 08, 2008, 05:52 pm Last Edit: Sep 08, 2008, 05:58 pm by mikalhart Reason: 1
Joker,

The express you propose,

elapsed_time = (max_counts + this_tick - last_tick) % max_counts;

is indeed more succinct and readable than the equivalent

if (this_tick < last_tick)
 elapsed_time = max_counts + this_tick - last_tick;
else
 elapsed_time = this_tick - last_tick;


However, I think you'll find that many programmers opt for the latter for the simple reason that in most computer architectures the modulus operation is much more costly in terms of CPU consumption.

Cheers,

Mikal

joker

I see, thanks for the hint.
See, I am still from the memory greedy VB.NET, and I have to get used to smaller environments (that I love anyway); takes me back to the early 80's and HP1000 running progs in 32K!  :o

How many ticks for a mod?

joker

#22
Feb 08, 2010, 09:47 am Last Edit: Feb 08, 2010, 09:48 am by joker Reason: 1
Thanks for the example.
Finally, I jumped out the frying pan (millis function  :-/ ) into the fire (Timers  :P). It took me a little time  ::) before being confortable about them, but it's OK now and find them muuuuch more handy: now I can really control time accuracy and forget about roll-overs, changes in versions, etc. And they are accurate: heavily tested on functions generators http://forums.adafruit.com/viewtopic.php?f=25&t=14149  ;)

Waterppk

Alright, things aren't working so hot :-(

I'm running this code:
Code: [Select]

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

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 setup()
{
 lcd.begin(2, 16);
 lcd.clear();
}
void loop()
{
 cli();
 current_millis_value = millis();
 sei();
 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;
 lcd.clear();
 lcd.print("t:");
 lcd.print(hours);
 lcd.print(":");
 lcd.print(minutes);
 lcd.print(":");
 lcd.print(seconds);
 
 delay(150);
}


on a Duemilanove and am able to watch the Arduino run too slowly (loses 1/2 a second in the first minute).  Anyone have any thoughts?  Are there delays/interrupts in the LCD library?

I just need a timer that counts up accurately when some event is true, it only needs to run for about an hour max so the overflow isn't an issue at all for me.  Is there a better/more precise way to be doing this?  I've dug through the code and stripped it down to what you see, there isn't much left except for the LCD screen writes that could be losing/taking time but I shouldn't matter how long the program since that's all accounted for.

Any thoughts?   >:(

PaulS

#24
Feb 09, 2010, 12:05 pm Last Edit: Feb 09, 2010, 12:05 pm by PaulS Reason: 1
More questions than answers, really. Why are disabling interrupts before calling millis?

Why are you calling delay?

There are more efficient ways of computing new values for minutes and hours. minutes only needs to be updated if seconds has reached 60. An if test (if seconds == 60) and two assignments (seconds = 0; minutes++;) is faster than the call to the modulo function. The same hold true for hours. hours only needs to change when minutes equals 60. hours can be reset to 0 faster using an if test than a modulo function call, too.

mem

perhaps try something like this:

Code: [Select]
void loop()
{
 while( millis() - previous_millis_value >= 1000){      
   elapsedSeconds++;
   previous_millis_value += 1000;      
 }
 seconds = elapsedSeconds % 60;
 minutes = (elapsedSeconds / 60) % 60;
 hours =  elapsedSeconds / 3600;  
}

Waterppk

#26
Feb 09, 2010, 03:20 pm Last Edit: Feb 09, 2010, 03:21 pm by Waterppk Reason: 1
Quote
Why are disabling interrupts before calling millis?


That's covered further up this thread, someone suggested that an interrupt would corrupt data from the millis call, doesn't seem to have any effect in my testing.

Quote
Why are you calling delay?


Delay is so the screen flicker on the LCD display isn't terrible.  If you run it any faster than 150 milliseconds it starts to blend things together (when the milliseconds readout is running, which isn't in this code).

Quote
There are more efficient ways of computing new values for minutes and hours.


I agree that when possible the most efficient path to the objective should be used, but fundamentally does it matter in this loop losing time?  Since the loop is just counting up how many milliseconds have iterated between loops and adding it to a value I don't think it has any effect on the loss of time (which is what I'm worried about before beginning to optimize anything).

Quote
mem says: perhaps try something like this: . . .


I ran this, after about 3 minutes the Arduino was off by exactly a second, it again is running too slowly.


The only way this could be happening is if the millis() call isn't returning the correct time, which is a function directly of the processor crystal right? Can anyone else test this code (mem's) and see if they're having similar results?  I'll run this code on a mega later today and see if I have similar issues or if things clear up; until then any other ideas?

Thanks everyone for help!

PaulS

Quote
I ran this, after about 3 minutes the Arduino was off by exactly a second, it again is running too slowly.


Compared to what?

Waterppk

It's now been running for:
33 minutes 43 seconds
clock time,
arduino says:
33 minutes 30 seconds

I'm using a stopwatch for comparison, I previously tested the stopwatch against http://www.time.gov/timezone.cgi?Central/d/-6/java for ~8 hours without losing a second, so for now the stopwatch is much more accurate than the Arduino  ;)

Waterppk

Alright, I switched over to using Serial.print and watching on the console and it's running spot on at 4 minutes.

I'll figure out who wrote the LCD library and follow up with them, obviously there's something in that library messing up a timing routine.

Thanks for your help guys.

Go Up