Unstable timing

Hello. I’m making an arduino-based binary clock. After testing it throughly, I’ve noticed that clock timing doesn’t seem to be stable. Sometimes it could run pretty stable for day or so, but sometimes I’ve notice that it’s not perfectly aligned and is slow by some seconds (which is not acceptable for digital clock). There’s only one relevant timing function that I call on each loop iteration, and it’s processTimer, which is supposed to go off with 1 second resolution and perform some periodic things (including ticking the clock by the one second). Here is the function:

inline void processTimer() //Check, if timer interval has passed and call handler if necessary
{
  if ((now - prevTimer) >= (TMR_SECOND-TimerCorr))
  {
    if ((now-prevTimer) > TMR_SECOND) TimerCorr = (now - prevTimer)-TMR_SECOND; //Timer correction allows to adjust interval if timer fired a little off
    else TimerCorr = 0;
    if (TimerCorr >= TMR_SECOND) TimerCorr = 0; //Timer correction interval shouldn't ever get anywhere near the second. However, if this happens for whatever reason, it would break the algorithm, so in that case we'd better drop the correction.
    onTimer(); //Call handler
    prevTimer = now;
  }
}

Here, now is a global variable and it’s assigned in loop to the millis(). If my calculations are right, then algorithm should be more or less correct and timing shouldn’t misalign even if timer was late by some milliseconds. In practice, I’ve tried monitoring the variables via Serial and never once saw TimerCorr to be non-zero, i.e. timer worked perfectly with 1000ms step, but from my paper calculations, algorithm should work correctly even in case of non-zero correction. It could lose a second only if correction is more than a second, which is almost impossible anyway, since the code is pretty lightweight overall and there’s no heavy loops, where it could get stuck, so it could be late for 1-2ms tops. So, that makes me wonder, why the clock could begin to run a little slow without clear reason? It could run correctly for more than a day, and might go slow after a few hours (sometimes it even seems to catch up again after some time). Is my timer algorithm wrong?

since the code is pretty lightweight

We'll just have to take your word for that, because we can't see it.

did you use the keyword volatile? I assume your code runs in an interrupt?

AWOL:

since the code is pretty lightweight

We’ll just have to take your word for that, because we can’t see it.

No problem, I’ve attached the zip with complete sketch. The code is not exactly short, but it mostly contains tons of nested ifs and a few short loops. There’s not really a lot of processing is going on.

robtillaart:
did you use the keyword volatile? I assume your code runs in an interrupt?

Actually, no. Entire thing runs in a loop function and I don’t use interrupts, so, I didn’t use volatile keyword. Basically, I just check current millis value in a loop and use processTimer function to check if a second has passed since last timer activation and state needs to be updated.

binclock.zip (13.4 KB)

Right now, after 10-11 hours, the clock got slow by about 10-12 seconds (at start, it was even one or two seconds fast), which makes at least 1 second per hour error. But error is not visually noticeable, since clock appear to run perfectly, and it accumulates noticeable error after a few hours. It would seem that 1000ms timer is actually triggering with a fraction of millisecond longer interval, something like 1000.20-1000.30ms which would accumulate a second error after about a hour. But then again, millis timing should work more or less correctly if I don't use interrupts, right? I believe it uses microsecond-resolution timer to make sure it won't lose fractions of milliseconds, so there must be my mistake. At least, from monitoring the timer with Serial, it shows that it triggers exactly at multiple-of-1000 milliseconds. 1 second error could be tolerable once per month, but when it accumulates dozen of seconds error in matter of hours, this doesn't look good at all.

If you're using an Uno, the fact that it's "not perfectly aligned" may be due to inaccuracy of the ceramic resonator. The part number shown on the schematic, reachable from the product page, is CSTCE16M0V53-R0. The manufacturer describes it as a 0.5% device. That relative error over a day would be more than 7 minutes.

My fiddling with frequency-sensitive sketches suggests that the resonator's frequency drifts as much as 50 or 60 ppm over a few minutes. If it ran at that relative error for a whole day, it would be 4 or 5 seconds off. Using crystal-based clones - Diavolino and Freeduino - gave more consistent results. Those two devices agreed within about 15 ppm, and measured about 40 ppm off against the audio clock in my laptop.

ultrabloxx: Right now, after 10-11 hours, the clock got slow by about 10-12 seconds

Roughly one second per hour, for a calculated accuracy of 1/3600 =~ 0.03%. That's way better than the worst-case accuracy of the ceramic resonator, and, at about 30 ppm, very respectable for a quartz crystal. That may be as good a result as you can expect using the Arduino's system clock as the frequency reference.

This is why RTC chips exist. The resonator used to create the system clock is there to produce a high frequency signal for very short events. Expecting it to be accurate over an entire day is like using a teaspoon to measure the capacity of a swimming pool and wanting a result to the nearest pint.

First I think I would make a simple test sketch. Make one that just counts up an hour or two and then turns an LED on. (Maybe turn a second one on at the exact moment you start counting, to allow for the bootloader processing). Get it going against a reference source (eg. a stopwatch). You could leave it for 59 minutes and then stare at the LED until it changes.

If this proves the resonator is a bit out (which I wouldn't be at all surprised at) then you have a few options:

[quote author=Nick Gammon link=topic=273015.msg1924382#msg1924382 date=1413524358] I bought 30 x DS1307 a while back on eBay for $10[/li][/list] [/quote]You were mugged! I paid 90 pence (less than $1.50) and that includes postage. :)

I think Nick said he bought 30.

LarryD:
I think Nick said he bought 30.

Ooops that makes sense :blush:

Oh, I see. I thought it may be an oscillator-related problem. It makes sense. I'm just new to electronics and I've seriously overestimated precision of Uno's quartz clock crystal over the long time intervals. Unfortunately, modifying the board and adding new components to it would be a bit difficult now that I've already ordered the PCBs, so I'll have either to resort to software-based correction, or use some different way. I've found a post with a very similar problem on instructables (there also was about one second per hour lag).

http://www.instructables.com/id/Make-an-accurate-Arduino-clock-using-only-one-wire/

I wonder, how that would even work? It doesn't make sense to me, since ATMega's PWM outputs are still driven by the same quartz oscillator, aren't they? But according to the author (and the comments under the post), he was able to get adequate precision for building an alarm clock.

Also, how to measure the clock crystal's frequency deviation from 16 MHz for use in software correction? It's probably slightly more than 1.00 second per hour, so just subtracting the second each hour will probably still give quite a large error.

ultrabloxx: Oh, I see. I thought it may be an oscillator-related problem. It makes sense. I'm just new to electronics and I've seriously overestimated precision of Uno's quartz clock crystal over the long time intervals.

It's not a crystal. That large crystal you see, if you look closely, is connected to the Atmega16U2 (USB chip). A much smaller resonator (less accurate) is adjacent to the clock inputs on the Atmega328P. USB timing needs to be precise, hence the expense. :)

ultrabloxx: It doesn't make sense to me, since ATMega's PWM outputs are still driven by the same quartz oscillator, aren't they?

Yes, of course. You can't make a silk purse out of a sow's ear, as they say. The PWM outputs would be directly related to the clock accuracy. They have to be.

There’s a real nice… Highly accurate Small I2C DS3231 RTC on Tindie for $10.00 from one of the forum members too…
[EDIT]…Cheaper and easier to use an RTC and the time library from the playground… Less time anyway…
And some few extra features too…

[quote author=Nick Gammon link=topic=273015.msg1924413#msg1924413 date=1413527282] It's not a crystal. That large crystal you see, if you look closely, is connected to the Atmega16U2 (USB chip). A much smaller resonator (less accurate) is adjacent to the clock inputs on the Atmega328P. USB timing needs to be precise, hence the expense. :) [/quote]

Oh, I see. Another large misunderstanding on my part. I was sure that it used a quartz crystal. Now it makes sense, why external quartz @ 8MHz could be used in the circuit and why the native one would be so inaccurate.

[quote author=Nick Gammon link=topic=273015.msg1924418#msg1924418 date=1413527427] Yes, of course. You can't make a silk purse out of a sow's ear, as they say. The PWM outputs would be directly related to the clock accuracy. They have to be. [/quote]

Looks like the only way now is to build a custom arduino clone with external quartz crystal instead of ceramic resonator for timing, or go with software correcttion. :(

ultrabloxx: Looks like the only way now is ...

You have more options than you might think. Others have listed some; here are some more:

  • Buy a clone with a crystal. Here are two: a Diavolino - http://shop.evilmadscientist.com/productsmenu/tinykitlist/180 - which claims to have a "better grade" 16MHz crystal, and requires an FTDI gizmo to interface to USB; and a Freeduino - http://www.nkcelectronics.com/freeduino-arduino-diecimila-compatible-board-complete-kit.html - which makes no claims about its crystal, and doesn't need an FTDI thing. Both are kits, and have to be assembled and soldered, so this may amount to "building a custom clone." Both are available from multiple sources. I bought mine locally, at a Microcenter store. Both are more stable, and appear to be more accurate, than my Uno. Note that the "better grade" crystal can still give you an accumulated error of a couple of seconds per day - if that won't suit you, you'll have to go to some external reference. One second per day is about 11.6 ppm.
  • Build or buy an external oscillator, hook it to T1 - digital pin 5 - and set up Timer 1 to count pulses on that pin. From what I can quickly see, the frequency has to be low enough to ensure that the signal doesn't change state for three or four system clock cycles, to accommodate the synchronization circuitry. That may wind up being more expensive that an RTC chip, though.
  • If you're connecting to AC power, connect a transformer-coupled power-frequency input to some pin on the Arduino, and count power cycles. The frequency isn't particularly accurate in the short term, but it's very accurate in the long term. Electric utilities continuously compare the cycle count against the actual time, and adjust the frequency slightly to keep those two numbers within some reasonable limit. At typical power frequencies, you can just poll the signal and look for transitions. Debouncing is fairly easy, by prohibiting the counter from advancing until the current power cycle is nearly over. Counting cycles is the typical way that consumer clocks keep time. If your clock isn't spot-on, you'll still be in agreement with nearly everyone else. If you back up with a battery, you can use the system clock to keep time while the AC is off.

Well, I've decided to go with a ready-to-use RTC PCB, which could be used in addition to my ordered clock I/O PCB. So, the problem is solved, I think. Thanks everyone for replies!

tmd3: You forgot a few.

Get a network interface board and use NTP to sync with a time server.

Get a GPS and sync to the pps output or read the time directly.

Get a WWV receiver and sync to that.

Buy your own atomic frequency standard and sync the arduino to that, but that is just silly. 8^) http://www.thinksrs.com/products/FS725.htm