ATmega328P Power-Down sleep current higher than expected

Hello, I've been trying to familiarize myself with the 328P's sleep modes. In the course of trying to reduce the current consumption I ended up constructing a socketed test jig with all the MCU's I/O pins connected to ground via 100K resistors and 3 10-ohm resistors that feed power to the Vcc, AVcc, and Vref pins, respectably. I'm using a differential amplifier that I can connect across any 10-ohm resistor in order to view on an oscilloscope the current draw of the pin that it feeds. The amplifier has a gain of 10, so 1 mV on the scope should indicate a current of 10 µA.

The MCU is running from its internal 8-MHz oscillator with the DIV8 fuse set, for an operation frequency of 1 MHz.

My code has been reduced down to nothing more than setting up the MCU for minimal power consumption and then a loop that puts the MCU into power-down sleep, from which it gets woken by the watchdog timer every ~35 mS.

On the scope I can clearly see the wake-up cycles occurring, with the current on the Vcc pin going to just about 1 mA, which corresponds nicely to what the datasheet shows for active current at 5V and 1MHz (The current draw on the AVcc and VRref pins are below what I can detect, as expected). However, during sleep the Vcc current only drops to about 190 µA, when I'd expect it to be somewhere close to 7µA, as per the datasheet. This a a big difference!

I would greatly appreciate it if anyone could enlighten me as to what I'm overlooking.

Edit: I realized I should also mention that I'm using MCUdude's Minicore package.

Edit 2: The fuse settings are as follows:
Extended: 0xFF; High: 0xD1; Low: 0x42
CKDIV8: Yes
CKOUT: No
SUT1: Yes
SUT0: Yes
CKSEL3: Yes
CKSEL2: Yes
CKSEL1: No
CKSEL0: Yes

RSTDISBL: No
DWEN: No
SPIEN: Yes
WDTON: No
EESAVE: Yes
BOOTSZ1: Yes
BOOTSZ0: Yes
BOOTRST: No

BODLEVEL2: No
BODLEVEL1: No
BODLEVEL0: No

My code:


#include <avr/wdt.h>

void setup()
{
  
  wdt_disable();  // Disable Watchdog Timer
  MCUSR = 0;      // Clear reset flags

  noInterrupts();
  
  DDRB = 0;  // All PORTB pins are inputs and pulled down externally
  PORTB = 0;

  DDRC = 0;  // All PORTC pins (except PC6/~RESET) are inputs and pulled down externally
  PORTC = 0;
  
  DDRD = 0;  // All PORTD pins are inputs and pulled down externally
  PORTD = 0;
  
  // Disable Digital Input Buffers on ADC lines
  DIDR0 = (1 << ADC0D) |
          (1 << ADC1D) |
          (1 << ADC2D) |
          (1 << ADC3D) |
          (1 << ADC4D) |
          (1 << ADC5D);
  
  // Disable unused interrupts
  EIMSK = 0;   // External Interrupts
  PCICR = 0;   // Pin Change interrupts
  UCSR0B = 0;  // Serial interrupts
  TIMSK0 = 0;  // Timer0 interrupts
  TIMSK1 = 0;  // Timer1 interrupts
  TIMSK2 = 0;  // Timer2 interrupts
  SPCR = 0;    // SPI interrupt
  EECR &= ~(1 << EERIE);  // EEPROM Ready interrupt
  TWCR = 0;    // TWI interrupt
  
  ACSR = 0;  // Disable Analog Comparator
  ADCSRA = 0;  // Disable ADC
  
  TCCR0B = 0;  // Stop Timer0
  TCCR1B = 0;  // Stop Timer1
  TCCR2B = 0 ; // Stop Timer2
  
  PRR |=  (1 << PRTWI)    |    // Shut down TWI
          (1 << PRTIM2)   |    // Shut down Timer2
          (1 << PRTIM0)   |    // Shut down Timer0
          (1 << PRTIM1)   |    // Shut down Timer1
          (1 << PRSPI)    |    // Shut down SPI
          (1 << PRUSART0) |    // Shut down USART0
          (1 << PRADC) ;       // Shut down ADC
  
  wdt_enable(WDTO_30MS);  // Configure Watchdog Timer for 30 milliseconds
  WDTCSR |= (1 << WDIE);  // Put the WDT into Interrupt mode
  
  interrupts();
  
  for (;;)
  {
    set_sleep_mode(SLEEP_POWER_DOWN);
    sleep();
    set_sleep_mode(SLEEP_IDLE);
  }
  
}


void loop()
{
}


ISR(WDT_vect)
{
  WDTCSR |= (1 << WDIE);  // Put the WDT back into Interrupt mode
}


My circuit:

I might be way way off with this guess, and this is just a first guess. I see that you are connecting 5V to the AREF pin through a 10 ohm resistor. This seems a little weird to me because there is already a 5V reference in the microcontroller, and to use the external reference you would actually have to call the analogReference() function.

With that in mind, I know there is also an internal 32K resistor connected to the AREF pin, and at 5V that would potentially see a current of around 155µA which seems close to your reading of around 190µA. So, maybe try removing R4 and see if that brings the sleep current down?

Hope this helps!

This is a common mistake; AREF should NOT be connected to VCC, but only to a filter capacitor.

Do the sleep mode function you're calling set BODS/BODSE in MCUCR?
The ability to disable the brownout detection from software is the only difference between the 328P and the 328 that we've been able to notice.

High sleep current is well documented as one sign of a counterfeit ATmega328p.

1 Like

You're right; I had forgotten that Aref is not supposed to be connected to 5V. I removed R4, however I saw no difference in the sleep current into Vcc. So there's still something amiss.

I don't believe that the set_sleep_mode function modifies MCUCR. However, I have BOD disabled via the config fuses, something I neglected to mention in my original post. I'll go back and add my fuse settings there.

That's good information, but I doubt that I'm dealing with counterfeits. My MCU's were purchased directly from Mouser. The one I've been working with was bought from Mouser about ten years ago. I just tried using a different one that I just got from Mouser last week and I can't tell a difference between their behaviors.

You should be OK with Mouser, but first fix problems like this and tell us how the results change. Who knows whether Microchip made changes in the manufacturing, after the takeover of Atmel?

Quite a while back Nick Gammon made some pretty definitive measurements of the ATmega328P sleep current under a variety of conditions (verifying the manufacturer's claims), and if you haven't seen his article, it offers excellent insight.

Somehow I missed Nick's article. I will take a very close look at it and see what I can learn.

Thank you to everyone who has replied!

1 Like

In case it might be useful for comparison, the code below is what I use to measure sleep current of a 328P. It differs from your setup in that all of the oscillators are disabled, including the watchdog. So the sketch goes into power-down sleep and never wakes up. This would be the situation if wakeup is effected by a transition on a GPIO pin rather than a timer. It produces sleep current of some fraction of 1uA on a genuine 328P.

One thing to note is that the BOD disable must be done immediately before the sleep command. Otherwise it doesn't work.

/*
low_fuses=0xFF
high_fuses=0xDA
extended_fuses=0xFD
*/


#include <avr/sleep.h>
#include <avr/wdt.h>

void setup(){

  ADCSRA = 0;                         // disable ADC for power saving
  wdt_disable();                      // disable WDT for power saving
  set_sleep_mode (SLEEP_MODE_PWR_DOWN); // Deep sleep
  sleep_enable();
  sleep_bod_disable();                // disable brownout detector during sleep
  sleep_cpu();                        // now go to sleep

}

void loop(){
}

Thank you, the code you provided does work on my test setup! I used it as a reference and began making modifications to my code one at a time to see what would be the key difference. What I found was quite surprising: The sleep mode being selected was actually "ADC Noise Reduction" instead of "Power-down".

In my code I was using:
set_sleep_mode(SLEEP_POWER_DOWN);
as opposed to:
set_sleep_mode (SLEEP_MODE_PWR_DOWN);

It turns out that SLEEP_MODE_PWR_DOWN is defined as (0x02<<1) in the file iom328p.h, but in the minicore package, SLEEP_POWER_DOWN is defined as 2, and not bitshifted into the proper position to apply to the SM[2:0] bits in SMCR.

I was following the instructions here:

I guess I found a bug in the minicore package?

Thanks again to everyone who answered!

2 Likes

Yes, that appears to be an error in Minicore. Do you have to use Minicore?

1.

In the ATmega328P MCU of OP, the EF (extended fuse bit) is 0xFF which says that the BOD circuit is disabled. If so, then do we still need to execute the above code?

2.
Why is there no ISR() routine in your sketch of post #10 when the MCU is being waked-up by external interrupt?

I don't know. The FF fuse setting disables the BOD from operating, but I don't know if that also means it won't consume any power during sleep.

The purpose of the test sketch is only to measure power-down sleep current, and the processor is never waked up. So I didn't bother to include any code to wake it up. But the sleep current would be the same if there was an ISR and loop() code. It would not be the same if a timer is used to wake up - because then an oscillator would be running.

1 Like

No, I don't actually need to use Minicore. I'd selected it earlier when I was exploring some of its other functionality and just never switched the IDE back over to Arduino Uno.

During my continued testing after I got my original issue resolved, I did take note that with the BOD disabled by the fuse setting, the current draw did not change with or without the sleep_bod_disable() line.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.