Minimizing power consumption

Here come the results of my “low power experiments”. The goal was to decrease power consumption as much as possible while retaining precise timing. Needless to say that I had to do this without an Arduino Board.

Here is what I did:

  1. I used an Atmega 48PV, basically an Arduino chip but with less memory and I used the 10 MHz version (this is because I intend to decrease the voltage as well). I used it without the standard 16 MHz crystal, instead I fused it to internal 8MHz RC Oscillator divided by 8, thus the clock runs at ~1MHz

  2. I connected a 32kHz watch crystal to XTAL1 and XTAL2.

  3. I shutdown all peripherals but timer2

      PRR = ~(1<<PRTIM2);
  1. Just in case somebody wonders: I did not use the bootloader, I used an ISP. To be more precise an AVRISPmkII. My cheap ISP from Ebay was useless because I still can not figure out how to decrease SCK for the cheap ISP.

  2. I put the prescaler to 1024 and interrupt after 1 tick → each 1/16s. Then I enable the pullup for some button pin, wait 5 cycles and poll if it is high or low. Then disable the pullup again. I use timer2 to wakeup the device in the meantime I go to power_save mode.

  3. After the button is pushed and released I switch to sleeping (PWR_SAVE) for 8s, wake up, count and go to sleep again. I also enable PCInterrupts for the button that was polled before.

Here are my measurements running at 5V, 8MHz RC, CKDIV8, external 32kHz 7pF crystal, no additional load (but the breadboard), 20°C:

Polling every 1/16s, button not pushed → 4-4.5 uA
Polling every 1/16s, button pushed → 4.5uA
Counting every 8s → 1.6uA

Conclusion: it is possible to achieve the advertised performance. Actually the device performed a little bit better (but of course at 5°C below the advertised temperature). However it achieved it while running a 32kHz oscillator. I am satisfied :slight_smile:

The biggest pitfall I found was that updating timer2 registers must be finished prior to going to sleep. The magic incanation that did the trick was

      while (ASSR & ((1<<TCN2UB)|(1<<OCR2AUB)|(1<<OCR2BUB)|(1<<TCR2AUB)|(1<<TCR2BUB))) { nop(); }

Additional insight: it is most probably completely impossible to implement such low power consumptions after you conceived a design. Either you go for <10uA from the start or you better forget it. The point is that any processing must happen as fast as possible in order to get the device back to sleep ASAP. Even pullups become an issue because they easily leak several 100uA if a button gets pushed. So you have to control everything to get power consumption really down.

The next experiment will focus on frequency stability at low power.


I would be interested in reading up on some more low power arduino info…

Is there a list of the things that can be done to get the power consumption low?
I heard that setting all pins as output decreased power consumption or something. I am sure there are a load of things and things to avoid such as cerain hardware pull-up configurations etc.


Two things…

Atmega 48PV
3) I shutdown all peripherals but timer2
PRR = ~(1<<PRTIM2);

The ATtiny84V datasheet recommends disabling peripherals before turning off the power.

Did you do anything to prevent unused pins from floating?

Yes, I pulled all pins to high by means of the pullups. That is all but the pushbutton. The pushbutton is pulled down while sleeping.

For low power consumption it is mandatory to have no pins float at all.
It is also mandatory to go for low frequencies while sleeping, to wake up fast and to go to sleep ASAP again. If you need no timer and shut down power instead of going to power save mode (leaving only the interrupts enables) you can get down even further with power consuption.

@Mowcius: yes there is a list. It is the datasheet. Everything I needed is in the datasheet. However it is sometimes hard to read. And sometimes the details are not in “the” datasheet but in “the other” datasheet. That is there may be several datasheets for the same device and they are not 100% equal. With regard to low power Arduino: forget it. You have to get rid of:

  1. 7805 regulator (consumes some mA of current)
  2. serial interface (at least most of the time)
  3. The bootloader (consumes to much power during startup and requires the serial interface anyway)
  4. The 16 Mhz Crystal clock (consumes to much power, enforces higher voltage while running, takes to long to startup (thus consuming power while starting up)
  5. most of the default settings (like ADC and timer0 active)

You have to go for maximum performance and put the device to sleep as long as possible → the digitalRead and digitalWrite performance sucks → you have to go for direct port manipulation

Once you strip off everything that sucks unnecessary power it is not really an Arduino anymore.

On the other hand: I started with a Boarduino. I put it into the breadboard without a chip. Thus I got a power supply, an FTDI and a reset button to the breadboard. Then I connected the Mega48pv (not the tiny84!!!) with power, serial, reset, and ISP connections. So the Boarduino is really useful even without a processor in it :wink:

The final result does not need the boarduino at all. In fact I had to disconnect the ISP and serial connections to avoid powering the device through these connections thus spoiling my measurements.


One more remark on the pushbutton. I switch it to input before I pull it up. Because the pullup is ~10-50k and because I run at 1 MHz this implies that I have to wait >1.5 cycles although the datasheet says 1.5 cycles are enough for the input buffers. The reason here is that althoug the processor can read the pin that fast it does not mean that it will go up that fast. Obviously the breadboard and the wires add enough capacitance to make the pin go up somewhat slower. So after pulling it up I wait 5 cycles before I actually read it.


  1. I connected a 32kHz watch crystal to XTAL1 and XTAL2.

Is this used to run Timer2?

Does the processor run off the internal osciallator?

Exactly. I run it with 8Mhz internal RC oscillator and CKDIV 8 and timer2 asynchronously with the 32kHz crystal. Thus I run timer2 at 32kHz while sleeping and the CPU at 1MHz while awake.


One more thought: I could prescale the RC clock while awake. However I will NOT do this. The reason is that the oscillator will consume power while running so I keep the clock as high as possible.

Then you might wonder why I use CKDIV = 8. Here the reason is that I want to lower VCC as much as reasonable possible. According to the datasheet the relevant frequency is NOT the oscillator frequency but the CPU clock. Thus it is OK to divide the 8 MHz to 1 MHz to lower the voltage. According to the datasheet the lowest reasonable voltage is 1.8V and it can run at 1MHz with this. So no need to divide it further.


I have an ATtiny84V application running nicely from two AA batteries.

In my testing, the combination of internal oscillator and lower voltage makes a big difference on power consumption. Going from 5V / 8MHz to 3V / 1MHz the current went from 7.33 mA to 0.94 mA. Turning off ADC, comparator, and USI the power dropped to 0.62 mA. Putting the processor into Idle drops the current to 0.19 mA. All these things can be done on a standard Arduino with just one caveat, millis won’t be as accurate.

It’s my understanding that an alkaline battery is considered depleted when it’s output reaches 0.9 volts. An AVR processor set to run at 1.8 volts should completely deplete a pair of batteries. It may be a few months (or years) before I find out!

190uA is OK if you run from 2AAs. However I want to run from coin cells which have typically 190mAh. So I need less power consumption. 4uA was much harder todo than 180uA. I think with an Arduino setup this is basically the limit you can reach. Then you will have to refuse.


Does your application need the accuracy of a crystal? Could you use the watchdog timer and power-down sleep mode?

Right now I could use the watchdog timer. But I am experimenting. That is the application is still to be determined :wink: According to the datasheet the watchdog timer does not result in very much decreased power consumption anyway. So I intend to stay with the crystal. Seems easier to implement for me. Especially because it also implies that I can keep timer 2 running. That is I can have have PWM while sleeping. This is something that I can not implement with the watchdog. At least I would not know how.

Another thing that I wonder is: could I have an external RC or LC oscillator in order to decrease the clock and thus the power consumption even further? If so, how would this be done?

Another idea: are there any things like ultra low frequency crystals? Maybe a piezo buzzer instead of a crystal? Did anyone ever try this?

The goal would not be frequency stability but lower frequency thus less power consumption.


Are you planning on doing this for the pushbutton…

  • Pin starts as input / pullup. No current flows because the button is open. The pin does not float because it is held at 5 V.

  • Human clicks the button which wakes the processor. Current flows from the pullup, through the button, to ground.

  • Your code detects that the button is pressed and turns off the pullup. The pin does not float because it is held to ground by the button. Current no longer flows because the pullup has been disabled.

  • The processor goes back to sleep.

  • Human releases the button. If the pin floats high, the processor wakes, detects the button is no longer held down, and enables the pullup. Or, if the pin remain at 0 volts, the processor wakes at the next timer tick, detects the button is no longer held down, and enables the pullup.

  • The processor goes back to sleep.

No. This just does not work because the button pin would pick up random noise. What I do is:

#define read_bit(value, bit) (((value) >> (bit)) & 1)
#define set_bit(value, bit) ((value) |= (1UL << (bit)))
#define clear_bit(value, bit) ((value) &= ~(1UL << (bit)))
#define toggle_bit(value, bit) ((value) ^= ~(1UL << (bit)))
#define write_bit(value, bit, bit_value) (bit_value ? set_bit(value, bit) : clear_bit(value, bit))

inline void nop() {
      asm volatile ("nop"::);

inline uint8_t button_state() {
    // this is trickier than it looks
      //   - the input must have a defined value to avoid undesired switching
      //   - we could pull it up by default but then power consumption for pushing the button may be as high as 300uA
      //   - so we have to pull it down until we want to evaluate it
      //   - before we pull it up we must switch to input
      //   - pulling it up with a ~10-40k resistor means as little as 75uA to pull up
      //     - the input buffers will require 1.5 cycles till they see what happened
      //     - we need to compensate for possible gate charge times
      //     --> after enabling the pullup we have to wait a little bit
      write_bit(BUTTON_DDR,  BUTTON_BIT, input);     // allow input
      write_bit(BUTTON_PORT, BUTTON_BIT, high);      // enable pullup
      nop();      nop();      nop();      nop();  nop();         // wait for input pin to get to high state (if possible)
      uint8_t button_pin_state = BUTTON_PIN;         // read input port
      write_bit(BUTTON_PORT, BUTTON_BIT, low);       // disable pullup
      write_bit(BUTTON_DDR,  BUTTON_BIT, output);    // ensure pin will not dangle
      return (button_pin_state >> BUTTON_BIT ) & 1;  // normalize output to 0/1, 0 = pushed

However it looks that the number of nop() statements is critical and might be to low.

I will poll the button as described above every 1/16s. The rest of the time the processor sleeps. Keeping only the 32kHz oscillator running on timer2.


One more issue I noticed: although it can run with as little as 5uA it does not mean that it can start up with 5uA. This is especially troublesome in my case where I have a very high impedance power supply and a cap to compensate. This setup gives me very slow rising power and thus the AVR will lock the whole circuit at ~1.6-1.8V. I am currently investigating how to deal with this.

So I have now two issues:

  1. reliably reading the pushbutton
  2. reliable start up on high impedance source

For (1) I think it might help to connect the button to more than one pin thus allowing to enable several pullups.

For (2) I am experimenting with the different possible fuse settings and the BOD.

Keeping power consumption below 10uA is much more tricky than staying below 100uA.


According to the datasheet brown out detector current is >20uA no what I will do. So this option is already out.

Now I will try delaying the reset as long as possible.


With regard to my pullup issue: shorter wires already did the trick. So if I pullup with some uA only, then the lenght of the wires to the button becomes already critical.

I went down from 60cm to 10cm and now everything is fine.


Running on a 32kHz crystal as the main clock was pretty disapointing. Even when sleeping Idle and setting CKDIV = 8 the CPU will draw 700-1000uA. So this is not any reasonable option to go.


Now there is an issue that will preliminarily stop all further experiments. My ISP denies to talk to the CPU after fusing it to external low power crystal and CKDIV 8 (4kHz main clock).

There was no issue to get the ISP frequency down to 6kHz. However it fails to go down to 100 Hz. Hence I can not flash this processor anymore.

Since my STK500 still fails I am left without HV programming and thus without recovery options. I already tried to connect an external frequency generator instead of the crystal without success :frowning: