How to fix or limit crystal deviation?

I am trying to implement precision timing with arduino (actually freeduino / sanduino). However I noticed up 203 ppm (203 microseconds per second) frequency deviation between the different boards (all IRQs turned off). So I figure that this is due to the crystals.

Now here come my two questions:

  1. I do not own an osciloscope / logic analyzer. Anyway I have no clue where I would get one at a reasonable price with a reliable enough timebase. What would be the cheapest way to get a reliable timebase (better than 10ppm) ?

  2. Once I have a reliable timebase, how could I tune the crystal oscillator to match the timebase? What would be the best way to alter the capacitator setup to tweak the frequency for about +/- 200 ppm?

Any hints even for only one of the questions would be highly appreciated.

After 1 hour Googling I found out that there are lots of high precision 10 MHz modules on Ebay (like this: http://cgi.ebay.co.uk/DATUM-LPRO-101-Rubidium-10MHZ-Fre-Standard-Easy-Kit_W0QQitemZ170404340399QQcmdZViewItemQQptZLH_DefaultDomain_0?hash=item27ace3e2af).

Has anyone ever tried one of those and can report if it would be feasible to connect such a thing to the arduino? I have the impression that the easiest way would be to change the fuses and use this instead of the normal crystal. Thus I would get a somewhat slower Arduino with better than 1ppm drift.

The issue though is: are these used devices reliable?

Or could I find something cheaper, maybe some DCF77 module? But how precise are those?

Now I found this one: SparkFun Real Time Clock Module - BOB-12708 - SparkFun Electronics. Even cheaper and probably easier to interface but no hint on the expected deviation though. Has anyone ever tested such a module?

There are some other posts somewhere on this forum about poor timekeeping and crystal frequency deviation with these clock chips.
If you search long enough you may find them.

Your correct that all timing is 100% dependent on the accuracy of the crystal. You didn't say how you are measuring the deviation? There are a couple of methods an average hobbyist can try.

  1. Substitute a trimmer cap for one of the padding caps used on the crystal. This should allow a little pulling/pushing of the frequency. However it doesn't improve frequency variation due to temperature change and might even make it worst.

  2. Buy a bulk of cheap 16mhz crystals and try them all till you get one close enough to your requirement. Again this doesn't help with temp. drift.

  3. Buy a TCXO (temperature compensated crystal oscillator) module, remove your crystal and wire the TCXO output to the oscillator input pin. Don't know if you would have to change the processors fuse settings for oscillator type or not. There are good bargains on E-bay for these things if you are patient, or have the flexibility of using whatever frequency TCXO is available at any given time. I bought a nice 10mhz time base oscillator as a calibration reference for my various test equipment, and it has some pretty impressive specs for it's price, same as this one (http://cgi.ebay.com/10MHZ-ISOTEMP-Reserch-OCXO-134-10-CRYSTAL-OSCILLATOR_W0QQitemZ270495188296QQcmdZViewItemQQptZLH_DefaultDomain_0?hash=item3efac50548). It's an oven compensated oscillator so the next step up from a TCXO.

Other items talked about above:

http://cgi.ebay.com/10-Variable-Ceramic-6MM-TRIMMER-CAPACITOR-30-PF-REAR-RC_W0QQitemZ370260483529QQcmdZViewItemQQptZLH_DefaultDomain_0?hash=item56353e9dc9

http://cgi.ebay.com/PACK-20pcs-16MHz-16-000-MHZ-Crystal-Oscillator-HC-49S_W0QQitemZ220405578831QQcmdZViewItemQQptZLH_DefaultDomain_0?hash=item3351323c4f

http://cgi.ebay.com/Ultra-precision-1ppm-15-000MHz-TCXO-crystal-oscillator_W0QQitemZ180434658415QQcmdZViewItemQQptZLH_DefaultDomain_0?hash=item2a02be646f

Lefty

  1. The 203 ppm seems quite high, it would be worth trying another crystal or resonator.
  2. Could you compensate for the error in software?
  3. I have seen writeups on adding a precision watch crystal to an arduino e.g. http://www.adafruit.com/blog/2008/04/12/add-a-watch-crystal-to-a-boarduino/

However I noticed up 203 ppm (203 microseconds per second) frequency deviation between the different boards (all IRQs turned off). So I figure that this is due to the crystals.

Are you sure you got your math right here? If you refer to the MCU crystals at 16MHz, pulses wil be 62.5 nano seconds apart. A deviation of 203 pulses per minute would then equal 12.7 microeconds per minute.

203 pulses per minute

For crystal frequency tolerance they define PPM to mean Parts Per Million. A crystal running at 10mhz with a +/- 10 ppm stability rating would have a range of possible frequency outputs of 10mhz +/- 100hz.

Lefty

For crystal frequency tolerance they define PPM to mean Parts Per Million.

Thanks - that explains the discrepancy.

Udo, were some of the boards you measured using a resonator instead of a crystal? The crystals on all the Freeduino and Arduino boards I have looked at are accurate to around 30ppm (drift 2 or 3 seconds per day)

An Arduino crystal with an error of 200ppm is probably faulty or using incorrect value capacitors.

I'm a bit curious how you know you're off 203ppm without a scope or other instrumentation, and what application has such critical timing requirements.

-j

I think TXCO was the term I needed to feed Google. This helped :slight_smile: But by now I think I will opt for one of the used time normals. Just for the sake of a real overkill :slight_smile:

With regard of how to figure out 203 ppm deviation without a scope:

Step 1: figure out that it actually drifts at all. I used this with the help of a quick an dirty program like this.

#include <WProgram.h>

volatile uint32_t latency = 0;



void delay_micros(uint32_t us) {      

      // overhead of the function call delays > 1us
      if (--us == 0)
            return;

      // the following loop takes a 1/2 microsecond (8 cycles)
      // execute it 2 times for each microsecond requested.
      us <<= 1;

      // account for the time taken in the preceeding commands.
      us -= 1;

      __asm__ __volatile__ (                   
        "1: nop"                           "\n\t"   // 1 cylce 
        "   nop"                           "\n\t"   // 1 cylce                 
            "   subi %A[micros],1"             "\n\t"   // subtract immediate,  1 cycle
            "      sbci %B[micros],0"             "\n\t"   // subtract with carry, 1 cycle
            "      sbci %C[micros],0"             "\n\t"   // subtract with carry, 1 cycle
            "      sbci %D[micros],0"             "\n\t"   // subtract with carry, 1 cycle
            "   brne 1b"                       "\n\t"   //                      2 cycles (1 in the last pass)                   
            :
            : [micros] "a" (us) // constraint: simple upper register      
      );     
}


int main() {

      // pin 7 = portd 7 --> this will be our input
      // pin 6 = portd 6 --> this will be our output

      // we will set PIN 6 to low and wait till we see a high a PIN 7
      // the time in between is measured in lateancy

      const uint8_t in_mask = 0xff;

   
      pinMode(6,OUTPUT);
      digitalWrite(6, HIGH);

      pinMode(7,INPUT);
      digitalWrite(7, HIGH);            

      Serial.begin(115200);  

    Serial.println("countdown");
      for (int i = 2; i > 0; --i) {
            Serial.println(i,DEC);
            delay_micros(1000000);
      }
      Serial.println("go");


      // capture 1024 at 1 byte each 3 seconds 
      // trigger if any of the bits in mask changes

      uint8_t tmp;  // dummy for temporary register
      __asm__ __volatile__ (
            "push r24"                                     "\n\t"
            "push r25"                                     "\n\t"
            "push r26"                                     "\n\t"
            "push r27"                                     "\n\t"

            "eor r24,r24"                               "\n\t"
            "eor r25,r25"                               "\n\t"
            "eor r26,r26"                               "\n\t"
            "eor r27,r27"                               "\n\t"

            "cli"                                           "\n\t"
            "cbi %[out_port], %[out_pin]"      "\n\t"

       "1: adiw r24, 1"                               "\n\t"  // 2 cycles
            "adc  r26, __zero_reg__"             "\n\t"  // 1 cycle
            "adc  r27, __zero_reg__"             "\n\t"  // 1 cycle
            "nop"                                          "\n\t"  // 1 cycle
            "sbis %[in_port], %[in_pin]"    "\n\t"  // 1 cycle for not branching, 2 cycles for branching 
            "rjmp 1b"                  "\n\t"                      // 2 cycles

            "sei"                                           "\n\t"
            "sts latency, r24"                         "\n\t"
            "sts latency+1, r25"                   "\n\t"
            "sts latency+2, r26"                   "\n\t"
            "sts latency+3, r27"                   "\n\t"

            "pop r27"                                     "\n\t"
            "pop r26"                                     "\n\t"
            "pop r25"                                     "\n\t"
            "pop r24"                                     "\n\t"

            :
            : [out_port]      "M" (_SFR_IO_ADDR (PORTD)),
              [out_pin]     "M" (6),
              [in_port]            "M" (_SFR_IO_ADDR (PIND)),
              [in_pin]        "M" (7)
      ); 

      
      Serial.print("Microseconds: ");
      Serial.print(latency/2,DEC);
      Serial.print('.');
      Serial.println(5*(latency%2),DEC);
      Serial.println();
    for (;;) {}
}

It's actuall two of them each running on different boards. If I switch them I see that they deviate a lot. And since all interrupts are off this is most probably crystal drift. And by then I know that this is a relative error of 203 ppm.

Step 2: figure out which one in the culprit (or both):

Create a loop that runs for 100 000 seconds and then blinks
(actually I have already a clock application, but this describes the idea). So I run this for 100 000 seconds beside a DCF77 clock. Those are cheap. 203 ppm means that I can see 20 seconds deviation after a little more than a day.

Now how could I get it precise?

Step 3: either increase the time or increase the time resolution of reading. --> use a digital camera to make a video of the start and the end of the period and use the computer as a time lense.

Result: I can measure ~1ppm in a little more than one day :slight_smile:

What project has such requirements: my "beginner project" for learning the arduino is kind of a killer "stopwatch". Once it is finished I will exhibit and describe what I did. Until then I consider it bad practice to go into details of the unlaid egg. I confess that what I am doing is most probably the definite timing overkill. But who cares, it's my time (and my timer :wink: )

Current estimate: >7000 lines of code (in 5 months) so far, maybe 5000-10000 and 6 months more till completion of the software part. Hopefully everything finished till end of 2010. I think for a spare time project my speed is quite OK so far.

The bigger obstacles will be the hardware part and a proper enclosure. But I am determined to get a "professional" result.

The anyoing part right now is: I have 4 boards. The freeduino and arduino do not drift that much. But my hacked and heavily modified board that has to carry the application is the one that drifts. So this is really annoying :frowning:

I will opt for one of the used time normals.

What the heck does that mean. :wink:

Good luck on hatching your egg.

Lefty

Now I ordered this: http://cgi.ebay.com/Ultra-precision-1ppm-15-000MHz-TCXO-crystal-oscillator_W0QQitemZ180434658415QQcmdZViewItemQQptZLH_DefaultDomain_0?hash=item2a02be646f

because this is closest to what I need.

And I ordered this: http://cgi.ebay.de/ws/eBayISAPI.dll?ViewItem&item=130326712863&ssPageName=ADME:B:EOIBSA:DE:1123

because I could not resist :slight_smile:

"time normal" is a flawed translation of the German "Zeitnormal". I think a better translation is "frequency standard". Anyway this part is cool. 25 years ago DCF77 clocks where a really big thing. Now I can own a real frequency standard for close to nothing.

--> maybe I will end up with one of the most precise running Arduino boards ever :slight_smile: Just for the sake of a really big overkill...

I can see 20 seconds deviation after a little more than a day.

what boards are you using that deviate 20 seconds per day? And what sketch are you using to measure the elapsed seconds?

I am interested in understanding why your results are ten times worse than I have seen on the three different boards I have measured.

Anyway, those devices you have order are cool.

have fun!

I already copied one of the "sketches" I use into the thread. The other one is part of my current project and not easily copied standalone. But it is not the culprit. I used the code copied below to measure the other. Then switched boards till I was convinced that everything is not caused by the software, then I was running the boards against the DCF 77 clock.

Typically software glitches look completely different. They give consistent (and huge) timing deviations. And using the sketch below they show up as "big" deviations. However the measured sketches just lose here some cycles then there some cycles quite evenly distributed. Since I know the time consumption of my ISRs and I know the prescaler settings I know that any software glitch will consume at least several microseconds.

With regard to the bords, I use
one Arduino --> drift reasonable
one Freeduino --> drift reasonable
one Boarduino --> drifts a lot (but this is OK)
a "hacked" board --> this is the one that drifts unreasonable

"hacked" means this is a board I got from ebay. I came with integrated goodies like a GLCD display, 2 2-color LED and two pushbuttons plus 1 reset button, ISP and JTAG headers. It was originally equipped with a AMega32 and a 14.... MHz crystall. I switched this to a 644P ("sanguino") and a 16 Mhz crystall. Runs flawless except for the unreasoanble big drift. I already compared the board layout and capacitator values with the Atmel datasheets and design recommendations --> this looks good.

So I expect the crystal to be causing the issue. Maybe I overheated it while soldering or I just got a bad one. I will switch it soon and then I will know for sure. But since the other boards to not drift and the controller works flawless I have no idea what else could be causing this issue.

Anyway, once I get my precision time bases this issue will be history :wink:

I've tried several arduino boards and found them all to be accurate to within a second a day (11 ppm). The same can't be said for my 'homebrews' with cheap ceramic resonators. :slight_smile:

I think Udo was leading us on a wild goose chase. :wink:
It now seems that only one of his crystal based boards has significant drift, and that board is a clone of some sort and is not using the crystal it was made with.

Anyway, once I get my precision time bases this issue will be history

Maybe so, maybe not.

I can't help but recall a famous dead Chinese philosopher that said "Man with one watch always knows the time, man with two watches never quite certain." :sunglasses:

Lefty

This guy could have two watches AND be certain of the time: First Atomic Clock Wristwatch