Atmega328p Power Saving Techniques

Have you set ADCSRA = 0?

Our measurements may not likely be the same based on several factors. Firstly, you're using a different chip. Secondly, I don't know what your setup looks like to determine what's wrong. It could be a software issue or hardware issue, such as something wrong with the wiring.

Are you asking how to accurately measure current of the chip using a multi-meter or with the 1M Ohmz method Nick showed?

Here, follow these steps to get your power readings: http://www.gammon.com.au/forum/?id=11497

I was able to get the Attiny85V chip down to pretty low power in the uA as well using these steps.

dc42:

fungus:
So I'm as confused as ever. Going into sleep mode definitely uses less power (I can measure the difference!) but consumption doesn't seem to be anywhere near as low as it should be.

Did you read my reply #18 ?

Yep. I've done it with/without the programmer, it made no real difference.

fungus:

dc42:

fungus:
So I'm as confused as ever. Going into sleep mode definitely uses less power (I can measure the difference!) but consumption doesn't seem to be anywhere near as low as it should be.

Did you read my reply #18 ?

Yep. I've done it with/without the programmer, it made no real difference.

You've misunderstood my reply, I didn't say anything about the programmer. What have you done to ensure that none of the I/O pins is a floating input?

kentc:
I don't know what your setup looks like to determine what's wrong. It could be a software issue or hardware issue, such as something wrong with the wiring.

There are no wires, just a chip.

kentc:
Are you asking how to accurately measure current of the chip using a multi-meter or with the 1M Ohmz method Nick showed?

Any method that works.

kentc:
Here, follow these steps to get your power readings: http://www.gammon.com.au/forum/?id=11497

Yes, I've done ALL of that.

kentc:
I was able to get the Attiny85V chip down to pretty low power in the uA as well using these steps.

Question is: How do you know? How do you measure it?

dc42:
You've misunderstood my reply, I didn't say anything about the programmer. What have you done to ensure that none of the I/O pins is a floating input?

I set them to input with pullup resistors. I also set DIDR0 to all 1s for good measure.

Here's the complete sketch I'm using at the moment:

#include <Arduino.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/cpufunc.h>
#include <util/delay.h>

/*================================================================
  Go to sleep for 4 seconds
================================================================*/
// Watchdog interrupt vector
ISR (WDT_vect)
{
  WDTCR &= ~(1<<WDIE);    // Disable the watchdog interrupt
}

void doSleep()
{
  // Set watchdog time to 4 seconds
  cli();
  byte b = (1<<WDE)|(1<<WDP3)|(0<<WDP2)|(0<<WDP1)|(0<<WDP0);
  WDTCR = b;
  WDTCR = b|(1<<WDIE);    // Enable watchdog interrupt (this wakes me up!)
  wdt_reset();
  // sleep
  sei();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_mode();
}

/*================================================================
  Main program
================================================================*/
void setup()
{
  power_all_disable();
  // All pins input with pullups
  DDRB = 0;
  PORTB = 0x1f;
  // Do these achieve anything? They don't seem to...
  ADCSRA = 0;
  DIDR0 = 0x3c;
}

// Alternate between sleep/work (the reading on the multimeter will switch every 4s...)
void loop()
{
  // Sleep for 4s
  doSleep();
  // Work for 4s
  _delay_ms(4000);
}

With a 10k resistor in series with the 5V supply the voltage across the resistor changes between 2.2V (sleep mode) and 3.6V (in _delay_ms()). That corresponds to currents of 220uA and 360uA - two orders of magnitude more than what I should be getting.

kentc:
...with the 1M Ohmz method Nick showed?

Aside: I don't understand that method. With a 1M resistor on a 5V supply you can never measure than 5uA no matter what the chip is doing. It's Ohms law.

This is the complete circuit I'm using to get the 2.2/3.6V readings:

Try adding a 0.1uF decoupling capacitor between the Vcc and ground pins of the chip. Without it, the chip may misbehave.

btw I just found this in the attiny datasheet:

10.2.5 Digital Input Enable and Sleep Modes
As shown in Figure 10-2, the digital input signal can be clamped to ground at the input of the
schmitt-trigger. The signal denoted SLEEP in the figure, is set by the MCU Sleep Controller in
Power-down mode to avoid high power consumption if some input signals are left floating, or
have an analog signal level close to VCC/2.
SLEEP is overridden for port pins enabled as external interrupt pins. If the external interrupt
request is not enabled, SLEEP is active also for these pins. SLEEP is also overridden by various
other alternate functions as described in “Alternate Port Functions” on page 59.

So it looks like setting the pins to inputs with pullups enabled isn't necessary on the attiny. It might even be worth trying without the internal pullups enabled.

dc42:
Try adding a 0.1uF decoupling capacitor between the Vcc and ground pins of the chip. Without it, the chip may misbehave.

I was actually doing most of the tests in the little PCB shown below which has a capacitor. The breadboard was a paranoia test. :slight_smile:

(No, there's no difference in readings...)

dc42:
btw I just found this in the attiny datasheet:

...

So it looks like setting the pins to inputs with pullups enabled isn't necessary on the attiny. It might even be worth trying without the internal pullups enabled.

I just tried it...maybe a tiny bit lower without pullups, but background noise level.

There's a definite difference between sleep/awake (220uA vs. 360uA) so I'm fairly confident that the 'sleep' code is doing something.

This makes me think something is left switched on, something that uses 220uA. I don't know what it might be though, there's only four things you can switch off and I've done all of them (individually, all at once, it's the same).

2013-04-25 14.50.26.jpg

I built an attiny-based remote control a while ago, and I seem to be using different sleep code. This is what I use:

		// Wait for button to be pressed
		do 
		{	
			set_sleep_mode(SLEEP_MODE_PWR_DOWN);
			cli();
			if (PORTB & 0b00000100)
			{
				GIMSK = _BV(INT0);
				sleep_enable();
				sei();
				sleep_cpu();
				sleep_disable();
			}
			sei();
			uint32_t now = micros();
			while ((micros() - now) < 5000) {}
		} while (PORTB & 0b00000100);

Ignore the PORTB stuff, it checks that a button isn't pressed. I can't remember why I used sleep_enable(), sleep_cpu() and sleep_disable() rather than sleep_mode(), but I expect there was a reason (possibly to do with using an interrupt to wake up from sleep mode, but maybe not).

What does the current drop to if you don't use the watchdog?

dc42:
I built an attiny-based remote control a while ago, and I seem to be using different sleep code. This is what I use:

sleep_enable();

sei();
sleep_cpu();
sleep_disable();

The only difference between that and "sleep_mode()" is the "sei()" in the middle.

I tried it but it makes no difference (not really surprising because I powered down all the timers so there's no interrupts.

dc42:
What does the current drop to if you don't use the watchdog?

If I go to sleep and never wake up? No difference.

The watchdog is designed for this usage, I doubt it's responsible for the 220uA.

fungus:
The watchdog is designed for this usage, I doubt it's responsible for the 220uA.

It certainly shouldn't be, the datasheet shows the power-down current @ 3V to be about 0.4uA with watchdog enabled and 0.2uA with watchdog disabled.

I might just try this out tonight. What fuse settings are you using? Have you tried it with more than one attiny chip?

dc42:
I might just try this out tonight. What fuse settings are you using?

My boards.txt has:

attiny85.name=ATtiny85 (internal 1 MHz clock)
attiny85.bootloader.low_fuses=0x62
attiny85.bootloader.high_fuses=0xd7
attiny85.bootloader.extended_fuses=0xff
...

dc42:
Have you tried it with more than one attiny chip?

Yes.

Are you sure the watchdog is being set correctly? Normally you need to set WDCE before changing WDE or the prescaler bits.

BTW, after the watchdog interrupt WDIF is cleared automatically (so you don't need to do it in the ISR). Meanwhile the WDT is still running, and may reset the processor before the 4s delay finishes (not that it matters much in this case).

I also noticed that you're not switching the ADC off after disabling it (power reduction register, PRR).

tim7:
Are you sure the watchdog is being set correctly? Normally you need to set WDCE before changing WDE or the prescaler bits.

Not if the WDTON bit is set in the fuses (bit 4 of fuse high byte)...

tim7:
BTW, after the watchdog interrupt WDIF is cleared automatically (so you don't need to do it in the ISR). Meanwhile the WDT is still running, and may reset the processor before the 4s delay finishes (not that it matters much in this case).

This is still a bit mysterious to me. I checked the MCUSR:WRDF flag in setup() but I never had the watchdog cause a reset, only interrupts.

I need to investigate further but I'm more worried about the power consumption at the moment.

tim7:
I also noticed that you're not switching the ADC off after disabling it (power reduction register, PRR).

The "power_all_disable()" does that.

fungus:
The "power_all_disable()" does that.

Are you sure? The datasheet makes a point of saying that the ADC must be disabled (ADCSRA) before being shutdown (PRR). You commented that disabling the ADC doesn't seem to do anything, but it should make a noticeable difference.

tim7:
Are you sure? The datasheet makes a point of saying that the ADC must be disabled (ADCSRA) before being shutdown (PRR). You commented that disabling the ADC doesn't seem to do anything, but it should make a noticeable difference.

Bingo!

By putting the "power_all_disable()" at the end of setup() the voltage across my 10K resistor went from 2.2V to 68mV(!)

That means power consumption is 6.8uA ... and that's exactly where it should be for "5V power down with Watchdog".

Damn datasheets, you have to read every word.

++karma for you.

It all seems to be working now so I can get on with my project. :slight_smile:

As an aside...I just tried measuring the current directly with a cheapo multimeter on the 20uA scale instead of using the resistor method.

Result? It works just fine - gives exactly the same reading as with the resistor.

fungus:

dc42:
You've misunderstood my reply, I didn't say anything about the programmer. What have you done to ensure that none of the I/O pins is a floating input?

I set them to input with pullup resistors.

I found that enabling pullups while sleeping might use a LOT more power.

If there's a switch attached to a pin and the switch is held closed it uses over 0.1mA because of the pullup resistor. This could happen if a device with buttons on it is stuffed in a bag/pocket/whatever.

Maybe this is why they say to enable pullups on unused pins.

fungus:
If there's a switch attached to a pin and the switch is held closed it uses over 0.1mA because of the pullup resistor. This could happen if a device with buttons on it is stuffed in a bag/pocket/whatever.

Udo Klein demonstrated a way to reduce that to nearly zero. I believe he used a combination of pin-change interrupts, polling, and disabling/enabling the pull-up. He posted a fairly detailed explanation. If you have trouble finding the posts, let me know.