Today's blog article is about how to optimize the power consumption of an AVR microcontroller running on batteries.

We're going to explore one of the ATmega32's sleep modes and how it affects power consumption. We will eventually reach a state where it actually makes sense to power the device from batteries. Besides sleeping, the microcontroller should do some work for us, requested by a single button press.

So let's get started. The microcontroller board we'll use for this example is built around an ATmega32 clocked by an external 16 MHz quartz crystal. There is a button connected to PB2 and an LED connected to PB6. The button will later be utilized for waking up the device, so we can make the LED light up for two seconds to indicate the wake up.

In the initial configuration, the microcontroller draws about 22 mA from the batteries without doing anything (LED off). For a battery capacity of 1900 mAh this means:

1900 mAh / 22 mA = 86 h

This leaves us with less than four days until we need to change batteries -- not even taking into account that we probably might want to drive our LED at some intervals during that period. Let's see if the AVR has something built in that we can use to improve that.

Entering power-down mode

The ATmega32 offers six sleep modes. For maximum power saving, the 'power-down' mode might be a good fit. According to the datasheet, in this mode the external oscillator is stopped while external interrupts stay active. This mode "basically halts all clocks".

On page 32 in the datasheet, the sequence necessary for putting the device into power-down mode is described as follows:
1) Select sleep mode
2) Set sleep enable bit
3) Execute sleep instruction
4) Clear sleep enable bit after wake up

Represented in code, this would look like:

// 1)
MCUCR &= ~(1<<SM2);
MCUCR |= (1<<SM1);
MCUCR &= ~(1<<SM0);

// 2)
MCUCR |= (1<<SE);

// 3)
asm volatile("sleep");

// 4)
MCUCR &= ~(1<<SE);

After executing these lines, the current consumption goes down significantly to 0.6 mA. Using the same batteries as in the calculation above, this would give us about 130 days.
This is already pretty good compared to the initial situation. As a second optimization it might be interesting to see where the remaining 600 uA come from. Is there anything we can possibly do to eliminate that current flow as well?

A look at the fuses

Hoping to find an answer to this question, let's check the fuse settings of the device. In the example above, the AVR fuses were actually set to 0x3e98, which is essentially the default settings plus the configuration for the external quartz.

We can refer to the datasheet or use this AVR fuse calculator to determine the meaning of each bit in the fuse bytes.

You may notice by looking at the tables that (among others) the JTAG debugging interface is enabled. What happens if we turn it off for now? Flipping the JTAG bit results in 0x3ed8. Of course, we won't be able to debug the controller anymore, but: 0.0 mA current consumption! That is, as accurate as the internal current measurement of my power supply is.

Conclusion / Outlook

I'm sure you've noticed that we are not done yet. We still need to wire up the wake-up button so a key press is recognized by the sleeping microcontroller. After that, switching on the LED for two seconds will be the easiest part. Can't wait to get into these topics in the next episode!

By the way, you can find the code for this little example at github.com/ronalterde/avr-sleep. We will most likely extend that later.

If you have any remarks, please contact me on twitter: @ronalterde.

References

ATmega32 Datasheet
AVR fuse calculator
avr-libc manual: inline assembler