ATMEGA32U4 sleep mode

Hi Guys,

So, I have this problem that I need to turn on my microcontroller every 1 hour for 200ms to send data and go back to sleep again. My microcontroller is ATMEGA32U4 and people on the internet say it will draw 3.5uA in deep sleep mode. However, using Nick Gammon's code I get 80uA. I tried other codes as well but all give the same number.
In order to wake up from sleep using timers you have 2 ways:

  • Using watchdog timer (Which is maximum 8 seconds)
  • Using external RTC

my questions are :

  • What could be wrong that I get 80uA instead of 3.5uA? (All my GPIOs are left open)
  • Do interrupts work in deep sleep mode? If I'm at 3.5uA, will interrupts trigger the micro to turn on?
  • ATMEGA32U4 has 3 internal timers, can we use any of them to turn on the microcontroller form deep sleep mode every 1 hour? I read each timer consumes about 10uA so It won't matter much.)
  • Is there a way to extend watchdog timer to 1 hour?

Thanks guys...

I've no personal experience of the ATMEGA32U4 but generally watchdog timers have a maximum timeout period of a few seconds. They were not designed to provide extended "sleep" times, as that is not their purpose.

On the 328p there is a maximum timeout of 8 seconds. In order to use the watchdog as a poor mans alarm clock, you need to wake on the watchdog timeout and reset it back to another 8 seconds. Repeat this until you achieve your desired sleep time.

Normally, leaving parts of the chip active consumes more power, however small that may be. With regards to the timers, I don't think any timer is going to count up/down in 1 go to give a 1 hour timer. Depending on the division of the timer clock source, you may get several seconds or tens of seconds before it times out. Then you are back to resetting it and going back to sleep again. Not much different to the watchdog.

External interrupts usually work in deep sleep mode. I think there is a slightly slower response from code because the micro has to wake up first.

Finally, the excessive current draw could be down to several factors. It could be an issue with the way you are measuring the current, or it could be as a result of the components in your design, or you could have a non-genuine chip. There's a youtube video about suspect chips (I think they were MEGA328P devices) that drew "excessive" current in sleep modes. But that's a long shot.

Section 7 of the datasheet goes through all the sleep modes, and has suggestions for things to turn off to minimize power consumption. Some of these are turned off automatically in Power-Down sleep mode, but others have to be manually disabled. Table 7-1 lists the sleep modes along with what will wake up each of them.

Table 29-1 says Icc is typically less than 10uA in Power-Down sleep, and is 1uA if the internal voltage regulator is disabled (that's used for USB I think).

Do you have a link to Nick Gammon's code for sleep current testing of the 32U4? Or could you just post the code here?

Edit: Anyway, yes, you can use an RTC to wake it up, and with a few extra parts you can actually power down the 32U4 and let the RTC turn the power back on every hour. Then you don't have to worry about sleep current. The DS3231 running on its lithium coin cell would be a good choice for that.

ShermanP:
Section 7 of the datasheet goes through all the sleep modes, and has suggestions for things to turn off to minimize power consumption. Some of these are turned off automatically in Power-Down sleep mode, but others have to be manually disabled. Table 7-1 lists the sleep modes along with what will wake up each of them.

Table 29-1 says Icc is typically less than 10uA in Power-Down sleep, and is 1uA if the internal voltage regulator is disabled (that's used for USB I think).

Do you have a link to Nick Gammon's code for sleep current testing of the 32U4? Or could you just post the code here?

Edit: Anyway, yes, you can use an RTC to wake it up, and with a few extra parts you can actually power down the 32U4 and let the RTC turn the power back on every hour. Then you don't have to worry about sleep current. The DS3231 running on its lithium coin cell would be a good choice for that.

Hi, so here's Nick Gammon's amazing code.
https://www.gammon.com.au/power

I also have a noob question about watch dog timer, I read somewhere that we can extend the watchdog timer to 1 hour by putting it in a “FOR” loop by the following code:

  // sleep for a total of 3600 seconds
  int i;
  for (i = 0; i <450; i++)
  { 
    myWatchdogEnable (0b100001);  // 8 seconds
  }

Now my question is, does the watchdog timer wake up the Microcontroller every 8 seconds and then go back to sleep again? If yes, how long does it take for it to do so.

Second question, can we have unequal numbers for watchdog timer ON/OFF duty cycles? (Like 8 seconds OFF and 200ms ON)? If yes, how?

Thank you very much

Yes, the watchdog wakes the micro every 8 seconds. On the grand scheme of things it takes almost no time at all.

I'm not sure I understand the on/off question. The watchdog timer is either ON (in which case it is counting towards its timeout), or it is OFF and plays no active part in the micro's operation.

dizzycoder:
Hi, so here's Nick Gammon's amazing code.
https://www.gammon.com.au/power

That's all about the Atmega328P. What you have is the Atmega32U4. You may need to do different things for that chip to get to the lowest sleep current.

Also, are you using the barebones 32U4 with just a crystal, or are you using a Leonardo or Pro Micro board?

According to the ATmega32U4 data sheet, you can expect about 6 uA current draw in power down sleep mode at Vcc=3V and T = 25 C, with the WDT is enabled.

people on the internet

The data sheet is your very best source of information.

markd833:
Yes, the watchdog wakes the micro every 8 seconds. On the grand scheme of things it takes almost no time at all.

I'm not sure I understand the on/off question. The watchdog timer is either ON (in which case it is counting towards its timeout), or it is OFF and plays no active part in the micro's operation.

Sorry, my bad. What I meant by ON/OFF was the Microcontroller. Can we program the microcontroller using Watchdog timer so that it is ON for 200ms and OFF for 3600 seconds?

jremington:
According to the ATmega32U4 data sheet, you can expect about 6 uA current draw in power down sleep mode at Vcc=3V and T = 25 C, with the WDT is enabled.
The data sheet is your very best source of information.

I think I'm missing something, because I just use the code from Nick in which he said if all GPIOs are floating you should be drawing 3.5uA on Sleep mode. While he tested it for "Atmega328P" according to the datasheet, I should be at around 6uA, as you pointed out but instead, I consume 80uA. So, I think I should look deeper into it to find what's causing that. I'll post it here if I find it.

Since you have told us nothing useful about your setup, you can't expect us to guess the problem.

This comment is certainly not relevant to the ATmega32u4.

I just use the code from Nick in which he said if all GPIOs are floating you should be drawing 3.5uA on Sleep mode

ShermanP:
That’s all about the Atmega328P. What you have is the Atmega32U4. You may need to do different things for that chip to get to the lowest sleep current.

Also, are you using the barebones 32U4 with just a crystal, or are you using a Leonardo or Pro Micro board?

I’m using a custom made board with a 16MHz crystal, no regulator and no LEDs, just placing necessary capacitors and the !HWB pin is also grounded with a zero ohm resistor. I’m running it on 3 volts.

According to the ATmega32U4 data sheet, 16 MHz is well outside spec for operation at 3V. 8 MHz is allowed.

If any inputs are floating, expect current draw to be higher than spec in sleep mode.

Note that the ATmega32u4 has a second power reduction register, PRR1, which controls several modules not present on the ATmega328. Of course Nick Gammon's code won't turn those off.

Finally:

If the On-chip debug system is enabled by the OCDEN Fuse and the chip enters sleep mode,
the main clock source is enabled, and hence, always consumes power. In the deeper sleep
modes, this will contribute significantly to the total current consumption.
There are three alternative ways to disable the OCD system:
• Disable the OCDEN Fuse.
• Disable the JTAGEN Fuse.
• Write one to the JTD bit in MCUCR.

The watchdog won't control how long the micro is "awake" (i.e. your 200ms). That's down to you to manage in your code. On the grand scheme of 3600 seconds - 1 hour - would it matter if the micro was awake for 150ms or even 1s.

You can get the micro to sleep for 3600 seconds by carrying out 450 x 8 second watchdog timeouts.

The watchdog is not particularly accurate - it's not designed that way. I have several 328p micros sleeping for 1 hour and in real world timing they generally over sleep by around 4 to 5 minutes, waking up after around 1hr 4min or so. In my application it's not that critical that it's exactly 1 hour. If it is important to your application that it is 1hr give or take a second or so, then you should consider an external Real Time Clock module.

markd833:
The watchdog won't control how long the micro is "awake" (i.e. your 200ms). That's down to you to manage in your code. On the grand scheme of 3600 seconds - 1 hour - would it matter if the micro was awake for 150ms or even 1s.

You can get the micro to sleep for 3600 seconds by carrying out 450 x 8 second watchdog timeouts.

The watchdog is not particularly accurate - it's not designed that way. I have several 328p micros sleeping for 1 hour and in real world timing they generally over sleep by around 4 to 5 minutes, waking up after around 1hr 4min or so. In my application it's not that critical that it's exactly 1 hour. If it is important to your application that it is 1hr give or take a second or so, then you should consider an external Real Time Clock module.

Thanks for your reply Mark. So, having an accurate watchdog timer is not very important to me. My intention is to send a message every 1 hour to a receiver and if that message is not received within 1 hour 10 minutes, I will trigger an alarm. Every time the receiver gets a message, this 1 hour 10 minute timer will be set back to zero. Because the transmitter needs to be cheap, I can't put a RTC in it.

So…
I have a bigger problem since yesterday. When I run a sketch from Nick Gammon, I can get the microcontroller down to 80uA. When I put it in my code, this value increases to 450uA. Which is 5 times worse.
I played with my interrupts, They are active low and connected to the ground while being pulled up. They have to be **Normally Closed **for my project which means that they are triggered when they are Not connected.
When I saw 400uA, I opened the interrupt lines (5 interrupts) and after disconnecting them all, **the current went down to 40uA. **
I will write the code for you guys:

#include <arduino.h>
#include "HopeDuino_LoRa.h"
#include <avr/sleep.h>
#include "LowPower.h"
#include <avr/power.h>

#define   RX_MODE        1
#define   TX_MODE        (!RX_MODE)

#define LEN 32
const int buttonPin0 = 0;  // the number of the pushbutton pin
const int buttonPin1 = 1;
const int buttonPin2 = 2;
const int buttonPin3 = 3;
const int buttonPin4 = 7;
// variables will change:
volatile int stud0,stud1,stud2,stud3,stud4= 0;        // variable for reading the pushbutton status

#define LED_PIN    6
#define LED_PORT()         pinMode(LED_PIN,OUTPUT)
#define LED_HIGH()         digitalWrite(LED_PIN,HIGH)
#define LED_LOW()          digitalWrite(LED_PIN,LOW)

/*****************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
byte str[LEN] = {'H','o','p','e','R','F',' ','R','F','M',' ','C','O','B','R','F','M','9','5','W','A','+','+','+'};
byte getstr[LEN];
byte mode = RX_MODE;  
const byte app_syncword[] = { 0x2D, 0xD4, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 } ;  

void setup(void)
{
  byte i;    
  LED_PORT();
  
  Modulation     = LORA;            ///Was FSK
  COB            = RFM95;           // Was RFM95
  Frequency      = 915000;          // was 866000
  OutputPower    = 10;              //17dBm OutputPower
  PreambleLength = 8;               //8Byte preamble
  FixedPktLength = true;            //explicit header mode for LoRa
  PayloadLength  = 21;
  CrcDisable     = true ;
  
  //for LORA parameter
  SFSel      = SF9;
  BWSel      = BW125K;
  CRSel      = CR4_5;

 // initialize the pushbutton pin as an input:
  pinMode(buttonPin0, INPUT_PULLUP);
  pinMode(buttonPin1, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
  pinMode(buttonPin3, INPUT_PULLUP);
  pinMode(buttonPin4, INPUT_PULLUP);
  // Attach an interrupt to the ISR vector
  attachInterrupt(digitalPinToInterrupt(buttonPin0|buttonPin1|buttonPin2|buttonPin3|buttonPin4), pin_ISR, HIGH); 
  vInitialize();
  _delay_ms(10);
  /************************
   *    LOW POWER parameters
   ************************/
        // disable ADC
        ADCSRA = 0; 
        delay(50);   
        power_adc_disable();
        delay(50);

        power_usart0_disable();
        delay(50);

        power_spi_disable();
        delay(50);

        power_twi_disable();
        delay(50);

        power_timer1_disable();
        delay(50);

        power_timer2_disable();
        delay(50);

        power_timer3_disable();
        delay(50);

        power_usart1_disable();
        delay(50);

        power_usb_disable();
        delay(50);

        USBCON |= (1 << FRZCLK);             // Freeze the USB Clock              
        PLLCSR &= ~(1 << PLLE);              // Disable the USB Clock (PPL) 
        USBCON &=  ~(1 << USBE  );           // Disable the USB  
        delay(50);

        // Switch to RC Clock 
        UDINT  &= ~(1 << SUSPI); // UDINT.SUSPI = 0; Usb_ack_suspend
        USBCON |= ( 1 <<FRZCLK); // USBCON.FRZCLK = 1; Usb_freeze_clock
        PLLCSR &= ~(1 << PLLE); // PLLCSR.PLLE = 0; Disable_pll
 
        CLKSEL0 |= (1 << RCE); // CLKSEL0.RCE = 1; Enable_RC_clock()
        while ( (CLKSTA & (1 << RCON)) == 0){}    // while (CLKSTA.RCON != 1);  while (!RC_clock_ready())
        CLKSEL0 &= ~(1 << CLKS);  // CLKSEL0.CLKS = 0; Select_RC_clock()
        CLKSEL0 &= ~(1 << EXTE);  // CLKSEL0.EXTE = 0; Disable_external_clock
        delay(100);   
}
  
  void loop(void)
  {
 static byte last_mode=RX_MODE;
 byte tmp;
 
pin_ISR();
 stud0 = digitalRead(buttonPin0);
 stud1 = digitalRead(buttonPin1);
 stud2 = digitalRead(buttonPin2);
 stud3 = digitalRead(buttonPin3);
 stud4 = digitalRead(buttonPin4);
 digitalWrite(LED_PIN, LOW);
 
if (stud0||stud1||stud2||stud3||stud4==1){ 
     bSendMessage(str, 26);
     LED_HIGH();
     _delay_ms(250);
     LED_LOW(); 
     }
     else {
      LED_LOW();
     }
     ADCSRA = 0;  
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  noInterrupts ();           // timed sequence follows
  sleep_enable();
// turn off brown-out enable in software
//  MCUCR = bit (BODS) | bit (BODSE);
// MCUCR = bit (BODS); 
  interrupts ();             // guarantees next instruction executed
  sleep_cpu ();  
  }
 void pin_ISR() {
  stud0 = digitalRead(buttonPin0);
  stud1 = digitalRead(buttonPin1);
  stud2 = digitalRead(buttonPin2);
  stud3 = digitalRead(buttonPin3);
  stud4 = digitalRead(buttonPin4);
  digitalWrite(LED_PIN, stud0);
  digitalWrite(LED_PIN, stud1);
  digitalWrite(LED_PIN, stud2);
  digitalWrite(LED_PIN, stud3);
  digitalWrite(LED_PIN, stud4);
 }

How can I modify the interrupts so that it doesn’t draw current?

There is an ATmega32u4 application note regarding a hardware bug associated with interrupts leading to high sleep current, but I don't recall the fix.

We recommend to avoid using interrupts. They are rarely necessary and especially with beginners, interrupts almost always cause more problems than they solve.

I doubt this will work as you imagine:

// Attach an interrupt to the ISR vector
  attachInterrupt(digitalPinToInterrupt(buttonPin0|buttonPin1|buttonPin2|buttonPin3|buttonPin4), pin_ISR, HIGH);

The button flags stud0 etc. are declared integer. In general, multibyte variables shared with an interrupt must be protected from corruption when being accessed by the main program. Either make those flags bytes, or protect them by making a copy with the interrupts turned off, then use the copy. Example:

// in main code...
noInterrupts();
int stud0_copy = stud0;
interrupts();
if (stud0_copy == 1) do_something();

jremington:
There is an ATmega32u4 application note regarding a hardware bug associated with interrupts leading to high sleep current, but I don't recall the fix.

We recommend to avoid using interrupts. They are rarely necessary and especially with beginners, interrupts almost always cause more problems than they solve.

I doubt this will work as you imagine:

// Attach an interrupt to the ISR vector

attachInterrupt(digitalPinToInterrupt(buttonPin0|buttonPin1|buttonPin2|buttonPin3|buttonPin4), pin_ISR, HIGH);




The button flags stud0 etc. are declared **integer**. In general, multibyte variables shared with an interrupt must be protected from corruption when being accessed by the main program. Either make those flags bytes, or protect them by making a copy with the interrupts turned off, then use the copy. Example:


// in main code...
noInterrupts();
int stud0_copy = stud0;
interrupts();
if (stud0_copy == 1) do_something();

Thanks for the reply, actually the top line of code you highlighted work well. I've been testing it for a long time. Thanks for the protection code as well.
So I think for the power management there is no other way left than turning the interrupts off during sleep mode to save that 450uA. As you said these interrupts are not intended to be used as normally closed switches.

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