Advertisements

Arduino: Dividing an External Frequency

A friend asked for a Totally Featureless Clock (it’s a long story) and in order to build that, I must concoct a WWVB simulator. Not needing really precise atomic time, I can use ordinary bench-grade crystals and suchlike; microsecond-level jitter isn’t a big problem.

Anyhow, I must divide down an external 60 kHz signal to produce a 10-Hz interrupt to drive the modulator. The 60 kHz comes from a 12 MHz crystal, through a divide-by-200 counter, and feeds the ATmega T1 input (aka Arduino pin 5, normally the PWM5 output).

The key is setting up Timer1 to divide T1 inputs by 6000 (count 0 through 5999), then have it produce an interrupt when the maximum count occurs. You’ll want to read Chapter 15 of The Fine Manual to learn how the hardware works.

I used CTC Mode 12, so that the counts occur on the falling edge of the signal on T1 with the maximum value stored in ICR1. That causes the Input Capture Interrupt to occur when ICR1 == TCNT1.

Review the avr-lib interrupt doc to get the proper interrupt vector names. You want the ICF interrupt, enabled with ICIE1.

Note that Table 15-4 is misleading. The TOV1 Flag may be set when TCNT == MAX, but unless ICR1 == MAX it’ll never get there. You (well, I) can spend a distressing amount of time figuring out why TOV1 doesn’t happen.

With that in mind, Timer1 setup is straightforward:

TCCR1B = 0;                // stop Timer 1 by shutting off the clock
TCNT1 = 0;                 // force count to start from scratch
TCCR1A = 0;                // no compare outputs to OC1A OC1B, WGM1 1:0 = 00
TCCR1C = 0;                // no forced compares
ICR1 = 5999;               // count 0 through 5999 = divide by 6000
TIMSK1 = 1 << ICIE1; // allow interrupt on capture event (TCNT == ICF)
TCCR1B = B00011110;        // start Timer 1: CTC mode = 12, TOP=ICR1, ext clock on T1, falling edge

The interrupt handler can do whatever you want. This one just flips an output bit (ultimately connected to the modulator) to show it’s arrived:

ISR(TIMER1_CAPT_vect) {
   PINB |= _BV(1);
}

That weird-looking line takes advantage of an Arduino hardware feature: if you write a 1 to a bit in the PIN register, the corresponding PORT value toggles. It’s documented in The Fine Manual on page 74, section 13.2.2 and mentioned there.

Then the rest of the simulator is just a simple matter of software…

Advertisements

,