The personal website of Scott W Harden

Divide 10 MHz to 1PPS with a Microcontroller

How to use a microcontroller to inexpensively scale down a 10 MHz reference clock into a one pulse per second (1pps) signal

I often find it useful to gate frequency counters using a 1 pulse per second (1PPS) signal derived from a 10 MHz precision frequency reference. However, a divide-by-10-million circuit isn’t trivial to implement. Counter ICs exist which enable divide-by-100 by combining multiple divide-by-2 and divide-by-5 stages (e.g., MC74HC390A around $0.85 each), but dividing 10 MHz all the way down to 1Hz would require at least 4 of these chips and a lot of wiring.

You can clock a microcontroller at 10 MHz and use its timer and interrupt systems to generate 1PPS. For example, an ATTiny202 in an 8-pin SOIC package is available from Mouser (>50k stocked) for $0.51 each. Note that modern series AVRs require a special UDPI programmer.

ATTiny202 ($0.51) ATTint826 ($0.95)

This page documents a divide-by-10-million circuit achieved with a single microcontroller to scale a 10MHz frequency reference down to 1PPS. I’m using an ATTiny826 because that is what I have on hand, but these concepts apply to any microcontroller with a 16-bit timer.

ATTiny Breakout Board

Some AVRs come in DIP packages but their pin numbers may be different than the same chip in a SMT package. To facilitate prototyping using designs and code that will work identically across a breadboard prototype and a PCB containing SMT chips, I prefer to build DIP breakout boards using whatever SMT package I intend to include in my final builds. In this case it’s ATTint826 in a SOIC-20 package, and I can easily use this in a breadboard by soldering them onto SOIC-to-DIP breakout boards.

I assembled the breakout board by hand using a regular soldering iron. When working with small packages it helps so much to coat the pins with a little tack flux to facilitate wetting and prevent solder bridges. I’m presently using Chip Quik NC191. Even if flux is advertized as “no-clean”, it’s good practice and makes the boards look much nicer to remove remaining flux with acetone and Q-tips or brushes.


Configuration Change Protection (CCP)

Traditional AVR microcontrollers used fuse bits to set the clock source, but modern series chips can change the clock source from within code. However, modifying the clock source requires temporarily disabling the configuration change protection (CCP) system.

Disabling the CCP only lasts four clock cycles, so the immediate next statement must be assignment of the new value. I use the following function to facilitate this action.

/* Write a value to a CCP-protected register */
void ccp_write(volatile register8_t* address, uint8_t value){
	*address = value;
// Use internal 20 MHz clock with CKOUT pin enabled

Do not use compound statements when writing to the CCP register. The code below fails to change clock as one may expect by looking at the code, presumably because the combined OR operation with the assignment exceeds four clock cycles. Instead of direct assignment, use the ccp_write function described above.

// WARNING: This code does not actually change the clock source

Configuring the Clock Source

Internal 10 MHz clock

This is the configuration I use to achieve a 10 CPU clock using the built-in 20 MHz oscillator.

void configure_clock_internal_10mhz(){
	ccp_write(&CLKCTRL.MCLKCTRLA, CLKCTRL.MCLKCTRLA | CLKCTRL_CLKOUT_bm); // 20 MHz internal clock, enable CKOUT
	ccp_write(&CLKCTRL.MCLKCTRLB, CLKCTRL_PEN_bm); // enable divide-by-2 clock prescaler

External 10 MHz clock

This is the configuration I use to clock the CPU from an external 10 MHz clock source applied to the EXTCLK pin.

void configure_clock_external(){
	ccp_write(&CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_EXTCLK_gc | CLKCTRL_CLKOUT_bm); // external clock, enable CKOUT
	ccp_write(&CLKCTRL.MCLKCTRLB, 0); // disable prescaler

Configuring the 16-Bit Timer

This is how I configured my ATTiny826’s TCA0 16-bit timer to fire an interrupt every 200 ms.

void configure_1pps(){
	// 10 MHz system clock with div64 prescaler is 156,250 Hz.
	// Setting a 16-bit timer's top to 31,250 means 5 overflows per second.
	TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm; // overflow interrupt
	TCA0.SINGLE.PER = 31249UL; // control timer period by setting timer top
	TCA0.SINGLE.CTRLA |= TCA_SINGLE_CLKSEL_DIV64_gc; // set clock source
	TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm; // start timer

Alternatively, multiple timers could be cascaded to achieve a similar effect. Modern AVR series microcontrollers have sections in their datasheet describing considerations for cascading two 16-bit timers to create a single 32-bit timer. Using this strategy one could set the top of the counter to 5 million and arrange an interrupt to toggle an LED, resulting in a 1Hz signal with 50% duty.

Configuring the Interrupt System

This method is called whenever the timer’s overflow interrupt is triggered. Since it’s called 5 times per second, I just need a global counter to count the number of times it was called, and set an output pin to high on every 5th invocation.

uint8_t overflow_count;

	if (overflow_count == 5){
		overflow_count = 0;
		PORTB.OUT = PIN1_bm;
    } else {
		PORTB.OUT = 0;
	TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm; // indicate interrupt was handled

Do not forget to enable global interrupts in your start-up sequence! This is an easy mistake to make, and without calling this function the overflow function will never be invoked.

sei(); // enable global interrupts


We have achieved a light that blinks exactly once per second with roughly the same precision as the 10 MHz frequency reference used to clock the microcontroller. This output signal is ready to use for precision measurement purposes, such as toggling the gate of a discrete frequency counter.