Revision abea5000-66db-43ba-b176-6b8b156920c0 - Arduino Stack Exchange

Yours is a classical problem of _clock syncrhonization_. You have a
software clock (your program's idea of the current time) which you want
to keep in sync with a reference hardware clock (the RTC). This is
typically achieved by using a [phase-locked
loop](https://en.wikipedia.org/wiki/Phase-locked_loop), or PLL. It works
as follows:

* you measure the difference between your software clock's time and the
 reference time
* you adjust your software clock based on this measurement.

This is essentially what you are already doing, except that you are
fixing your clock by stepping it abruptly, whereas a PLL would usually
slew the clock to get it in sync progressively.

Stepping the clock has some undesirable effects: it makes the time
discontinuous and, more annoyingly, it can make it non-monotonous. I
would thus recommend you try to slew your clock instead.

Here is the approach I would try if I were in your shoes. I am assuming
you are using an Arduino Uno, or a similar AVR-based board with a 16-bit
Timer 1:

* configure Timer 1 in mode 4: CTC mode with TOP = OCR1A
* set the prescaler to 64 and OCR1A = 12499 in order to get a period of
 50 ms; TIMER1\_COMPA\_vect will be your data gathering interrupt
* configure your RTC to generate a 1 Hz output, and route this
 signal to the input capture pin ICP1 (pin 8 on the Uno)
* run the PLL logic inside TIMER1\_CAPT\_vect.

Since the RTC's 1 Hz period is a multiple of the timer period, you
would expect the timer's input capture unit to always capture the same
value. The difference between two consecutive captured values is thus a
direct measure of your clock's drift in units of 4 ppm
(4 µs/s). The ISR would be basically along these lines:

<!-- language: lang-c++ -->

 ISR(TIMER1_CAPT_vect)
 {
 static uint16_t last_capture;
 uint16_t this_capture = ICR1;
 int16_t drift = this_capture - last_capture;
 last_capture = this_capture;

 // Reduce the drift modulo 12500 into [-6250, +6250).
 if (drift >= 6250) drift -= 12500;
 if (dirft < -6250) drift += 12500;

 tune_clock(drift);
 }

where `tune_clock()` is your PLL algorithm responsible for slewing the
software clock.

Pay attention to the signedness of the variables. The subtraction of the
captured values should be done with unsigned numbers, _then_ the result
should be made signed.

AltStyle によって変換されたページ (->オリジナル) /