Go Down

Topic: Arduino as a chronometer ? Know what you are doing ! (Read 7103 times) previous topic - next topic

Coding Badly

Here are the results:
Period_unloaded  = 1,003,595 µs
Period_loaded     = 1,00,1726µs            frequency_loaded = 15,942,950 Hz
From which we derive                       frequency_unloaded =  15,973,908 Hz
Compared to the nominal 16 Mhz figure that's a 1600 ppm error.

I think you made a mistake...

The period going from unloaded (1,003,595) to loaded (1,001,726) DECREASED.  For that to happen, the processor clock frequency has to INCREASE.

In your derivation, the frequency going from unloaded (15,973,908) to loaded (15,942,950) DECREASED.  The frequency changed in the wrong direction.


You are right and I thought I was very stupid not to realize that.
So I recomputed everything, getting to a new unloaded frequency that was totally incompatible with the results I quoted yesterday. Now I felt even more stupid. Then I looked at my notes on paper and concluded that, yes I was definitively stupid: I switched the loaded and unloaded numbers when I copied them to the screen.
The good news is that all results are consistant again, I guess you can call that consistant stupidity.


The update is pretty much what you can see above:
1- tested your code, no improvement.
2- focused on the oscillator: it's off by 1600 ppm!
No programming feat can cure that.

Next step: include a DS3231 RTC or similar in my design: it will give me a 2ppm, temperature compensated, 1-second signal against which I can calibrate every Arduino time measurement.


Mar 07, 2011, 01:19 pm Last Edit: Mar 07, 2011, 01:27 pm by hobbified Reason: 1
I'm actually working on a project with similar requirements to yours -- I'm building a GPS-disciplined NTP server on an Arduino (not for any serious industrial need, just to see how good of a job I can do), so of course the core of it is an adjustable timekeeping loop using one of the AVR's hardware timers.

I decided to leave alone the TIMER0 which is used by the arduino library's micros() etc. and instead programmed TIMER4 myself (I've got a Mega2560 board, which has the ICP4 pin routed but not ICP1; a 2009 should work as well if everything is changed over to use TIMER1, since that board has ICP1 routed but there is no ICP4). It pre-divides the 16MHz clock by 8 for 0.5uS counter resolution, and uses a timer overflow value of (nominally) 62500 to give 32 interrupts per second. By varying that value +/- 127, I can get +/- 2048 ppm speed adjustment in units of 16 ppm, but by dithering I'm able to make the adjustment accurate to +/- 1 ppm over a period of 0.5 sec and +/- 1/16 ppm over a period of 8 sec.

Coupled to that is a PLL that locks to a 1PPS input signal connected to the ICP4 pin (substitute ICP1 for the 2009), and the code is able to timestamp events on the input capture pin as well as read the current time from the counter.

Consistent with what you saw, I observed about a 24uS jitter associated with having another interrupt delayed by my timer ISR. Using the input capture pin can mitigate this, but since there's only one ICP per timer, you'd need to multiplex it cleverly if you wanted to use it for external events and for locking to the 1PPS.

Also somewhat consistent with what you saw, the crystal on my board runs about 240ppm fast at room temperature, and it's very temperature-sensitive, with a constant of about 15ppm/degC. I've got a 1-wire temperature probe wired up and taped to the crystal, and I'm working on putting temperature compensation ahead of the PLL, but it's a little bit fiddly and I haven't finished with that yet.

My code is admittedly fairly scary and I haven't reached the point where I was ready to show it to the world yet, and the PLL is really ham-fisted because this is NOT my specialty, but if you want to have a look at any of it, it can be found at http://cleverdomain.org/git/arduino/HEAD/tree .


To minimize (but not eliminate) the jitter caused by delayed interrupts there's one fairly crude technique that works well if your overall loop() is not time constrained: deliberately add "interruptible time" in the loop, like this:
Code: [Select]

void loop()
  task1(); // sucessives tasks executed by loop()

  // Interruptible segment
  long segment_duration = 10;                         // segment duration (in ms), pick the value that works best for your case
  long segment_start = millis();                      // time (in ms) when the interruptible segment starts
  while (millis() < segment_start + segment_duration) // loop here until duration has elapsed

The assumption is that tasks 1 to n contain interruptible as well as non-interruptible code; the "interruptible segment" is a way to increase the proportion of interruptible time in the loop.
This black box statistical approach to interrupt management has its limitations (beyond being frowned upon by "real programmers"): it does nothing for concurrent interrupts happening during the interruptible segment, it slows execution of loop().
In my case I have about 2 seconds between interrupts: an 8 ms delay divided the measurement dispersion by 5 compared with no delay at all.


Why do you think it is "zero carbon footprint"? It runs from some power source and it had to be manufactured. So why should it be zcf?
Check out my experiments http://blog.blinkenlight.net


It's not my opinion Udo, it's what written on the back of my Arduino Uno board!
I mentioned it, tongue-in-cheek, because I find this statement both untrue and silly. Untrue: I need not explain the amount of energy consumed in semiconductor manufacturing; silly: there are so many useful specs that the user would like to have before knowing whether or not this is a zcf product!

Go Up