Birds Like Wires

Feed the Birds

Saving Power with your ATmega

As many projects using an MCU end up running from batteries, it’s nice to have those batteries last as long as possible. There are a numerous different ways of doing this, depending upon your circuit, but there are two that I’ve been reading up on recently: reducing the clock speed of the ATmega, and sending it to sleep. These techniques might even be essential if you’re planning a solar-powered project.

On an Ardunio board, chip timing comes from a 16MHz oscillator; it probably looks like a little rounded rectangular can just north of the ATmega. This makes sure you get good execution and serial programming speeds, but if your project is simple or doesn’t require that speed, it makes sense to reduce the rate at which the chip is running. Fewer cycles per second should mean fewer milliamps required in use.

In my earlier article I wrote about running an ATmega168 at 8MHz using its internal oscillator to reduce parts. But what about dropping down to 1MHz?

Reducing Clock Speeds

Microcontrollers like the ATmega have a set of ‘fuses’ which control various operational parameters of the chip. These persistent values are usually set when you flash a bootloader and they determine the behaviour of your MCU, including whether to use the internal 8MHz oscillator and whether to divide that value by 8 for timing. Set a fuse bit appropriately and your ATmega will be running at 1MHz.

Of course, it’s not quite that straightforward. Your bootloader needs to be compiled to work at that given clock speed and the Arduino SDK needs to know what speed you are running at, otherwise you’ll discover that a delay(1000) in your code will be way off actually lasting 1 second. Being a bit of a completist, I decided to compile bootloaders for the ATmega168 and ATmega320P for 1MHz and 8MHz operation.

Having received this cheerful message from the official bootloader when compiling…

ATmegaBOOT_168.c:586:101: error: 'EEWE' undeclared (first use in this function)

…I immediately gave up and used the Arduino 1K Bootloader because it’s smaller, it compiled first time and I hadn’t spotted OptiBoot.

Bootloader and Hardware Configuration

arduino_hwconf_1kbl_1-8mhz.zip [6.88 kB] (578)

If you download the archive on the right, you’ll get a copy of the bootloaders, ready compiled, and the matching hardware configuration file. If you drop that into your ~/Documents/Arduino/hardware/ or equivalent, when you next start the Arduino SDK there will be options for ATmega168 and ATmega328 on a breadboard, with the 1K bootloader, running at either 1MHz or 8MHz. Flashing the bootloader as described previously using those options will set all of the appropriate fuses and get you up and running at low frequencies.

One of the other things this sets is the ‘brownout’ fuse bit, which determines the voltage at which the ATmega will enter a paused state to prevent erratic activity (like accidentally overwriting its own EEPROM). This is disabled from the factory, but should be set according to your operating frequency; at 1MHz, we can set it at 1.8V, which is the lowest value available. On the 8MHz bootloader, brownout is at 2.7V; the next setting up.

If you’re in the midst of developing code, I wouldn’t recommend that you start out at 1MHz from the beginning. Running at this rate slows everything down, so your reflash time is going to be eight times slower than at 8MHz, which might get frustrating. The lower clock speed can also reduce accuracy, so while a delay(1000) will be approximately 1 second at both 8MHz and 1MHz, it’ll be less approximate at 8MHz. If you catch my meaning. When you’re after more accuracy, go for the higher rate.

Sleeping

Another way of saving on power usage is to send the chip to sleep. There are big power savings to be made from sleep modes, and the most straightforward way that I found of doing this is a great little library called Narcoleptic. Just drop it into ~/Documents/Arduino/libraries/ and import it into your sketch:

#include <Narcoleptic.h>

You can now use the function Narcoleptic.delay() in your code. Instead of sitting in a delay loop, the chip will actually drop into sleep mode for the given number of milliseconds, which reduces it’s power consumption at 1MHz from a couple of milliamps to a couple of hundred microamps. Impressive.

There are, of course, some caveats; the primary one being that analogue outputs will simply switch off in sleep mode. But this needn’t be a disaster. Say you are fading up an LED using an analogue pin with PWM. If you’re taking the LED to full brightness, why not switch from analogWrite() to digitalWrite() once you’ve got there? Digital outputs stay live in sleep mode, so you can keep that LED lit while the MCU has a rest.

Another issue is that there seems to be a limit to how long sleep mode can be maintained with Narcoleptic. I don’t know if this is a limitation of the library or not, but beyond around 30 seconds it’s behaviour became a little unpredictable for me. This is very easy to fix, however; just create a sleep() function that takes minutes as a input.

// Allows us to specify a sleep time in minutes.
void sleep(int minutes){
  for (int count = 0; count < (minutes * 2); count++){
    Narcoleptic.delay(30000);
  }
}

Every 30 seconds the chip will wake, then drop back to sleep almost immediately. My project required sleeping times of 20 minutes at a stretch, and it handled this very well. In fact, using the methods I’ve mentioned the ATmega168 kept running perfectly, even after the LEDs in my project became almost impossible to see when lit.

Further Reading

Hopefully this has given you enough pointers to get cracking on your own low power project with the ATmega168 or ’328. If you’d like some more information on all this, there are some great articles by experiment4.com and Ladyada has some top ones on fuses and the basics of chip programming.

In making the bootloaders, this Fuse Calculator was invaluable. Plenty of options to mess around with there.

If you do come up with an interesting project, please feel free to post a link to it in the comments!

← Recent Articles