Thursday, June 18

Embed With Elliot: Shifting Gears With AVR Microcontrollers

Most modern computers are able to dynamically adjust their operating frequency in order to save power when they’re not heavily used and provide instantaneous-seeming response when load increases. You might be surprised to hear that the lowly 8-bit AVR microcontrollers can also switch CPU-speed gears on the fly. In this edition of Embed with Elliot, we’ll dig into the AVR’s underappreciated CPU clock prescaler.

The Fuses

Most (all?) AVR microcontrollers come straight from the factory with their system clock set to run at one eighth of the internal oscillator’s speed. For instance, a stock ATmega168 has an internal RC oscillator ticking away at 8 MHz, but it’s divided down to 1MHz by default. In the bad old days, when dinosaurs roamed the earth and the ATmega8 was the new chip on the block, you had to “burn a fuse” to change this and get the chip up to full speed.

The fuses are configuration bits that stay constant across reboots and aren’t changeable from your code or from within a bootloader — they have to be changed using an external hardware programmer. The idea is that they contain configuration bits that are so sensitive you don’t want something as commonplace as buggy code to mess them up. Things like low-power brownout detection, chip start-up times, lock bits to stop you from reading out the program memory, and so on are stored in the fuse bits.

Basically the fuses contain all the settings that can “brick” your AVR and force you to reach for the high-voltage programmer. Figuring the fuse settings you need is a bit tedious: I’ve always used an online fuse calculator to make sure I get them right.

embed-with-elliot-shifting-gears-with-AVR_current_freqAtmel apparently initially thought that CPU clock speed was one of those don’t-touch parameters. Fair enough; we’ll look into what breaks when you change gears below.

But keeping you from changing the clock speed dynamically also removed an important means of reducing the chip’s power consumption. So the newer chips let you control the system clock prescaler from software.

Note that if you’re using an external crystal oscillator, you’ve still got to program the appropriate fuse bits to enable its use. You can’t switch back and forth from an external clock to the internal one from within your code. However once you’ve got the AVR running on an external crystal, you can still use the clock prescaler to change gears (relative to the crystal’s frequency) on the fly. Which is to say, you can change the CPU prescaler on an AVR-based Arduino.

Using The Prescaler

Which chip families have a software-adjustable clock prescaler? All of the ATmegax8 chips (48, 88, 168, and 328) and most of the modern Tinys, from the Tiny2313 to the Tinyx4, x5 and x61 families, support changing the clock multiplier on the fly. There are others as well, and you can sift through the io.h file for your favorite chip to look for CLKPS definitions.

The short version is that your chip will probably have a prescaler unless you’ve got a stash of neolithic ATmega8’s kicking around that you’re trying to finally use up.

The actual sequence for changing the prescaler is deliberately made a little bit complicated: like turning the keys to launch a nuclear missile, it’s not something you want to do by mistake. First, there’s a prescaler-change enable bit that must be set in order to change the clock division bits. But the enable bit gets automatically reset four CPU clock cycles after it’s turned on, so the timing is tight to get the new division bits in. If an interrupt fires in the process, you won’t manage to set the clock speed in time, so you’d better turn off interrupts when changing speeds as well.

Conveniently enough, the GNU AVR libc provides pre-written assembly code that does this dance for you, takes care of differences across chips, and make your code a ton easier to read in the process. Once you include “avr/power.h“, changing the CPU clock is an easy as clock_prescale_set(clock_div_64). Now your chip is running at 125kHz (= 8MHz / 64).

Instead of the old-school fuse-flashing to get your chip running at full speed, just include clock_prescale_set(clock_div_1) somewhere in your initialization routine. Piece of cake.

The Catch(es)

So now you can change all of the CPU clock speed at will from your code. What could possibly go wrong? In short, everything that relies on the system clock for its timing.

embed-with-elliot-shifting-gears-with-AVR_clock_treeAs you can see, most of the AVR’s hardware peripherals use clocks that are derived from the CPU system clock run through the system clock prescaler. The timer/counters, all of the serial I/O (USART, SPI, and I2C), and the analog-to-digital converter (ADC) all have their own sub-clocks that are divided down versions of the system clock.

The good news is that changing any of these to work at your new CPU speed is as easy as re-setting their clock divisors. The simplest solution is to write something like a “switch_to_8MHz()” function that switches the CPU speed and reinitializes the various hardware peripherals at the same time.

The commonly used standard delay functions, _delay_ms() and _delay_us(), are going to be messed up because they rely on a compile-time macro definition, F_CPU, and that can only take one value. If the macro F_CPU is set to 1MHz but then you change the clock speed down to 125kHz, all of your delays are going to be eight times too long when you’re running slow.

The delay problem is a bit tricky because the built-in delay functions only take constants as arguments, so you’ll need to write your own delay routines that take the current CPU speed into account. This could be as easy as simply looping around a fixed delay eight times when you’re running the CPU eight times faster, for instance. Setting a “delay_multiplier” global variable to specify the number of loops and updating it when you change CPU speed is probably the best way to go.

In Conclusion

So you can see that while it’s trivial to change clock speed from your code, a little care is needed to make sure all of the AVR’s peripherals play along. But if you’re looking to save a little power and the sleep modes won’t work for you, changing the gears that divide the system clock is a great way to go. And even if you’re not going to change the CPU speed on the fly, at least you don’t have to burn the fuse bits just to get an AVR chip running at full speed. Give the CPU prescaler a look!


Filed under: ATtiny Hacks, Hackaday Columns, Microcontrollers

No comments:

Post a Comment