Controlling clock frequency with the AVR system prescaler

With the retirement of the original Orangutan (sad!) it looks like all of the Orangutan boards will be shipping with 20MHz crystals installed, and the AVR fuses set to use them. The “divide clock by 8 internally” fuse will be disabled, but that fuse is still the bane of many AVR first-timers making use of the internal oscillator, and hesitant to mess with the fuse settings.

While you can’t change AVR fuse settings from within a program, it turns out you can control this prescaler in software using the CLKPR register. Unfortunately there is a special procedure for changing the CLKPR bits, and a limit on how many clock cycles you can do it in (described in section 7.11 of the ATMega168 datasheet). Instead of messing around with this myself, I copied this excellent function from the MyUSB library written by Dean Camera:

static inline void SetSystemClockPrescaler(uint8_t PrescalerMask){
	uint8_t tmp = (1 << CLKPCE);
	__asm__ __volatile__ (
			"in __tmp_reg__,__SREG__" "\n\t"
			"cli" "\n\t"
			"sts %1, %0" "\n\t"
			"sts %1, %2" "\n\t"
			"out __SREG__, __tmp_reg__"
			: /* no outputs */
			: "d" (tmp),
			  "M" (_SFR_MEM_ADDR(CLKPR)),
			  "d" (PrescalerMask)
			: "r0");

His website is called 4WalledCubicle, the MyUSB project is here, and the function documentation (which essentially says “it works”) is here.

The library is written for the AT90USB line of AVRs, but so far it has worked on the couple of ATMega models I have handy to try it on as well. Just insert it above your main function, then call SetSystemClockPrescaler(PrescalerMask) to set the prescaler at any time. The effect of values of PrescalerMask may depend on which AVR you’re using, but for example, the available settings on the ATMega168 are:

This gives you several more frequency options than just X or X/8. Setting the CLKPR register overrides the prescaler set by the “CKDIV8” fuse, but doesn’t actually change the fuse setting, so it will still be set or cleared when you download a new program. Neat stuff!


Very cool! Thanks for sharing this info!

- Ben