Friday, August 15, 2014

A square peg in a round hole

The initial design of the crazy clock board uses a 4.096 MHz crystal because the prescaler combinations that are available are all powers of 2, and 1024 is the best one for the timer prescaler. But we want a 10 Hz basic interrupt source, and I just sort of gave up on trying to do that math.

But in figuring out the Martian clock's need to insert what amounts to a very whacky number of extra cycles, I remembered some work I did a long time ago in designing a custom programmable PLL circuit.

A lot of the programmable PLLs out there are designed with a special intermediate output to control a  prescaler that can do both divide-by-n and divide-by-n+1. So you can, for example, get a divide-by-5/divide-by-6 prescaler and your PLL will have a very low lock frequency, but still have a very minute tuning resolution because the divide ratio can effectively include fractions.

I used that to insert 99 extra counts for every 3600 by inserting an extra every 37 counts for 36 cycles, then every 36 counts for 63 cycles. 37*36+36*63 = 3600, and 36+63=99. And 24 hours becomes 24:39:36 and you're suddenly on Mars.

Well, could the same thing be done to let the basic mechanism work with a 2 MHz crystal? I can't find 2.048 MHz crystals, but a 2 MHz crystal might further reduce power consumption (going from 16 MHz to 4 MHz cut power consumption roughly in half).

Well, 2 MHz with a divide-by-4 prescaler is 500 kHz. That is, more or less, a match for the prototype's system clock. If we then set up timer 0 with a divide by 1024 prescaler, then what do we use for the CTC value?

500 kHz divided by 1024 is 488 + 9/32. Or put another way, to get 10 Hz, it's 48 and 53/64s. So if we set the CTC top to 49 for 53 cycles, and then 48 for 11, that solves the problem. The fact that some 10 Hz intervals will be 2.048 µS longer than others will be utterly insignificant.

All we have to do is replace sleep_mode() with calls to...

#define CLOCK_CYCLES (64)
// Don't forget to decrement the OCR0A value - it's 0 based and inclusive
#define CLOCK_BASIC_CYCLE (48 - 1)
#define CLOCK_NUM_LONG_CYCLES (53)

void do_sleep() {
  static unsigned char cycle_pos = 0xfe; // force a reset

  if (++cycle_pos == CLOCK_NUM_LONG_CYCLES)
    OCR0A = CLOCK_BASIC_CYCLE;
  if (cycle_pos >= CLOCK_CYCLES) {
    OCR0A = CLOCK_BASIC_CYCLE + 1;
    cycle_pos = 0;
  }

  sleep_mode();
}

Turns out Mouser has a 2 MHz crystal that conveniently fits in the footprint of the prototype board and even has a 10 ppm tolerance... but it's backordered until October. :(

No comments:

Post a Comment