Power Down consumption on custom board

From the datasheet...

9.11.3 PRR – Power Reduction Register

Bit 0 – PRADC: Power Reduction ADC
Writing a logic one to this bit shuts down the ADC. The ADC must be disabled before shut down.
The analog comparator cannot use the ADC input MUX when the ADC is shut down.

From the datasheet...

9.11.3 PRR – Power Reduction Register

Bit 0 – PRADC: Power Reduction ADC
Writing a logic one to this bit shuts down the ADC. The ADC must be disabled before shut down.
The analog comparator cannot use the ADC input MUX when the ADC is shut down.

But, if we don't enable the ADC at the 1st place, we don't have to do the above setting right?
Look at 9.10.1 of the datasheet.

The ADC is enabled by the core. I believe init in wiring.c does the deed.

Found it, it is in the wiring file in the core folder. The ADC is enabled by default.
For timers, I think for power down mode, the clock source is not available for it to work.

OK, trying to disable individual functions before stopping each function clock:

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

void setup()
{                
  byte i;
  
  // Ensure no floating pins
  for(i=0; i<20 ; i++)
  {
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);
  }
  
  // Power-down board
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  
  sleep_enable();
  // Disable functions 
  power_adc_disable();
  power_spi_disable();
  power_twi_disable();
  power_usart0_disable();
  power_timer0_disable();
  power_timer1_disable();
  power_timer2_disable();
  
  PRR = 0xFF;
  
  // Enter sleep mode
  sleep_mode();
}

void loop()
{
}

Consumption is still around 105 uA =(

Thanks guys!

I've investigated the possibility of "loosing" current through the decoupling capacitors but, even in the worst cases, they might be wasting a total of a few uAmps maximum. I think I'm going to order a couple of DIP-form atmegas in order to do the tests on a breadboard. Then I'll be able to mount only the essential components and measure the current consumption. That will let me discard any programming-related problem.

These...

// Disable functions
power_adc_disable();
power_spi_disable();
power_twi_disable();
power_usart0_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();

...and this...

PRR = 0xFF;

...are equivalent. You still have not disabled the ADC.

You're right!

Let's do then:

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

void setup()
{                
  byte i;
  
  // Ensure no floating pins
  for(i=0; i<20 ; i++)
  {
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);
  }
  
  // Power-down board
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  
  sleep_enable();
  
  // Disable ADC
  ADCSRA &= ~(1 << ADEN);

  // Power down functions
  PRR = 0xFF;
  
  // Enter sleep mode
  sleep_mode();
}

void loop()
{
}

And the result is... less than 0.2 uAmps !!!

Thanks a lot guys for your great help!!

Can you wake up on external interrupt with these power down settings??

I think Atmel calls them "asynchronous interrupts". Off the top of my head (read that as probably with mistakes): pin-change interrupts, USART activity, TWI address match, watchdog interrupt, INTx level. For the 328, the table in "9.1 Sleep Modes" provides a good overview.

I'm gonna add the other power down stuff then and see if the current draw goes down some then.

Yes, I'm able to periodically wake up the atmega from sleep using the watchdog. No problem except for the few uAmps that the watchdog sinks in power-down mode.

Baseline test with current sleep mode on my RF transmitter is 1.09mA, with highest current seen on my multimeter of 14.25mA during virtualwire transmission of 1 commanded byte (and however many bytes virtualwire adds to it for).
Now to add the other power down methods & see what happens:

// Disable ADC
ADCSRA &= ~(1 << ADEN);

// Power down functions
PRR = 0xFF;

A word of caution: some units may need to be reinitialized after power-off. I vaguely recall the USART being mentioned.

In other words, if something flaky happens after the processor awakens, check the datasheet first then start debugging!

Well, that sorta worked.
At initial power on, & first time entering sleep mode, current dropped to 0.99mA.
However, after the first key press/transmission, it jumped to 10.49mA after the send and stayed there.
Don't have the receiver with me so I can't tell if its still transmitting - the LED on D13 just stays on vs turning off after the transmission.

Did I get the code in the right place? And do I need to re-enable some part of it when I want to wake up for the next transmission?

// transmitter_Weapon_Feb2.pde
// Feb 2 - changed Lights Intensity to be used for Weapon select instead.
// Doesn't actually change any code, just a label change on the Remote keypad.

// -->> gave up on PCInts
// have to double Serial.Begin value with 8MHz ProMini
// have to double VirtualWire Bits/sec in Setup with 8MHz ProMini

// ALL SERIAL PRINTS COMMENTED OUT

// started with the code from this example
// http://www.arduino.cc/playground/Learning/arduinoSleepCode

// with sleep function called -> 1.09mA! should be >900 days (??) on 1000mAH batteries
// down from 8.95 mA in normal idle mode  -> just 7 days
// with bursts of ~14-15mA when a keypress is transmitted

// Processor: Arduino Mini-Pro running on PC USB power
// (ran on 3 AA batteries fed into VCC line prior to this Interrut attempt)
// Keypad: Velleman 4x4 Matrix
// RF Link Transmitter - Sparkfun MO-SAWR-A, 434MHz, WRL-08946

// Simple example of how to use VirtualWire to transmit messages
// Implements a simplex (one-way) transmitter with RF module. 

// See VirtualWire.h for detailed API docs
// Author: Mike McCauley (mikem@open.com.au)
// Copyright (C) 2008 Mike McCauley
// $Id: transmitter.pde,v 1.3 2009/03/30 00:07:24 mikem Exp $

// uses default pin 12 for transmit data
// uses default pin 11 for receive data

// added Keypad example from playground
// modified to send character in a buffer to the receiver from 4x4 matrix

// Velleman 4 x 4 matrix keypad 
// keypad 1 (Col1) to D10
// keypad 2 (Col2) to D9
// keypad 3 (Col3) to D8
// keypad 4 (Col4) to D7
// keypad 5 (Row1) to D6
// keypad 6 (Row2) to D5
// keypad 7 (Row3) to D4
// keypad 8 (Row4) to D3

// Rows have Internal Pullups

// Row 1, 2, 3, 4 Diode AND'D to D2 (INT0) to pull it low on a keypress
// Interrupt on D2 "wakePin" LOW - Input with Internal pullup

// D0, D1, D14, D15, D16, D17, D18, D19 set as inputs with internal pullups enabled

// 12/23/10 - started code to send out address read from switches to GND on D14, 15, 16, 17
// D14 - SW0
// D15 - SW1
// D16 - SW2
// D17 - SW3

// end of notes
// ***********************************************************************

                            // bring in the library(s)
#include <VirtualWire.h>    // Wireless transmitter/receiver library
#include <Keypad.h>         // Matrix Keypad library
#include <avr/sleep.h>      // powerdown library
#include <avr/interrupt.h>  // interrupts library

// ***********************************************************************
int SW0 = 14;               // bits to read in unique address - LSB
int SW1 = 15;               // bits to read in unique address
int SW2 = 16;               // bits to read in unique address
int SW3 = 17;               // bits to read in unique address - MSB
int address = 0;            // bits put together afteer reading switches
int add0;
int add1;
int add2;
int add3;

// define unused pins
int dpin0 = 0;              // apparently redefined by Serial as Serial Monitor works
int dpin1 = 1;              // apparently redefined by Serial as Serial Monitor works
int pin2 = 2;               // Int0 interrupt pin

int dpin18 = 18;
int dpin19 = 19;

// ***********************************************************************
// define the used pins

#define ledPin 13          // activity indicator, use for brief flash when transmit

// don't need to define pin12, handled by VirtualWire as default data_out pin to the transmitter

int sleep_count = 0;      // flag/counter to tell us to go sleep

// ***********************************************************************
// create an array to store data to be sent out
char msg[2]; // extra char added for address

// set up the Keypad
const byte ROWS = 4; // Four rows
const byte COLS = 4; // Four columns

// Define the Keymap
char keys[ROWS][COLS] = 
{
  {    '1','2','3','A'      }  ,  // row 1
  {    '4','5','6','B'      }  ,  // row 2
  {    '7','8','9','C'      }  ,  // row 3
  {    '*','0','#','D'      }  ,  // row 4
};

// Connect keypad ROW1, ROW2, ROW3 and ROW4 to these Arduino pins.
byte rowPins[ROWS] = { 6, 5, 4, 3  };  // Keypad uses internal pullups? No externals supplied
// these pins have separate external Diodes, anodes connected to Pin2 (Int0)
// Column pins are set low just before shut down,
// and Pin2 is pulled high internally.
// Pressing a Row button connects to a Low Columna and pulls the appropriate Row Diode Low for a Pin2 Low interrupt.
// Column pins are then set back high for the keypad library button determination.

// Connect keypad COL1, COL2, COL3, COL4 to these Arduino pins.
byte colPins[COLS] = { 10, 9, 8, 7 }; 

// Create the Keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

//***************************************************
// *  Name:        pin2Interrupt, "ISR" to run when interrupted in Sleep Mode
void pin2Interrupt()
{
  /* This brings us back from sleep. */
}

//***************************************************
// *  Name:        enterSleep
void enterSleep()
{
  /* Setup pin2 as an interrupt and attach handler. */
  attachInterrupt(0, pin2Interrupt, LOW);
  delay(50); // need this?
  /* the sleep modes
   SLEEP_MODE_IDLE - the least power savings
   SLEEP_MODE_ADC
   SLEEP_MODE_PWR_SAVE
   SLEEP_MODE_STANDBY
   SLEEP_MODE_PWR_DOWN - the most power savings
   */
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // setting up for sleep ...
  sleep_enable();                       // setting up for sleep ...
  
// <<<<<<<<<<<<<<<<  March 2, 2011 - added some more power down stuff  >>>>>>>>>>>>>>>>

    // Disable ADC
  ADCSRA &= ~(1 << ADEN);

  // Power down functions
  PRR = 0xFF;
// <<<<<<<<<<<<<<<<<  end of new March 2 stuff  >>>>>>>>>>>>>>>>>>>>>>>

  sleep_mode();                         // now goes to Sleep and waits for the interrupt

  /* The program will continue from here after the interrupt. */
  detachInterrupt(0);                 //disable interrupts while we get ready to read the keypad 

  /* First thing to do is disable sleep. */
  sleep_disable(); 

  // set all the keypad columns back high so can read keypad presses again
  digitalWrite(7, HIGH);
  digitalWrite(8, HIGH); 
  digitalWrite(9, HIGH);
  digitalWrite(10, HIGH); 
  // then go to the void Loop()
}

here's the setup & loop code

// ***********************************************************************
// set up the pins as Inputs, Outputs, etc.
void setup()
{
  /* Setup the pin directions, write inputs High to turn on internal pullups */
  pinMode(pin2, INPUT);                 // our sleep interrupt pin
  digitalWrite(pin2, HIGH);

  // define all the unused pins as inputs with internal pullups for lower power state
  pinMode(dpin0, INPUT);                  // apparently redefined by Serial as Serial Monitor works (receiving anyway)
  digitalWrite(dpin0, HIGH);              // apparently redefined by Serial as Serial Monitor works
  pinMode(dpin1, INPUT);                  // apparently redefined by Serial as Serial Monitor works
  digitalWrite(dpin1, HIGH);              // apparently redefined by Serial as Serial Monitor works

  pinMode(SW0, INPUT); // LSB of remote Address
  digitalWrite(SW0, HIGH);
  byte add0 = 0; // read the value of SW0
  pinMode(SW1, INPUT); // LSB+1
  digitalWrite(SW1, HIGH);
  byte add1= 0;
  pinMode(SW2, INPUT);  // LSB+2
  digitalWrite(SW2, HIGH);
  byte add2 = 0;
  pinMode(SW3, INPUT);  // MSB of address
  digitalWrite(SW3, HIGH);
  byte add3 = 0;
  
  // put our address together
  // read our expansion address
  add3 = digitalRead(SW3); // not using for now, needed to use the pin on the receiver
  // shift it left 3 places
  add3 = add3 << 3;
  add2 = digitalRead(SW2);
  // shift it left 2 places
  add2 = add2 << 2;
  add1 = digitalRead(SW1);
  // shift it left 1 place
  add1 = add1 << 1;
  add0 = digitalRead(SW0);
  // now OR it together
  address = address|add3;
  address = address|add2;
  address = address|add1;
  address = address|add0;
  //Serial.println("my address is: ");
  //Serial.println(address, BIN);
  msg[1]= address;
  
  pinMode(dpin18, INPUT);
  digitalWrite(dpin18, HIGH);
  pinMode(dpin19, INPUT);
  digitalWrite(dpin19, HIGH);

  // default pin for data to be transmitted out
  // pinMode(dpin12, OUTPUT);                 // taken care of by VirtualWire
  //                                          // VirtualWire takes care of 11 also.
  
  digitalWrite(ledPin, LOW);                   // pin 13, turn off the LED
//    Serial.begin(9600);	               // need for debug monitoring only with FTDI connected via USB to PC
//    Serial.println("TX setup");              // for debug only 

  // ***********************************************************************
  // Initialise the IO and ISR for VirtualWire
  vw_setup(4000);	                      // Bits per sec - had to double from 2000 with 8MHz 3.3V Pro-Mini

}                                            // end of void Setup()

// ***********************************************************************
// Main loop for reading the keypad and sending the button pushed out
// (with a unique address to be added eventually by reading 0-F from currently unused pins)

void loop()
{
  if (sleep_count>1000){                      // check if we should go to sleep because of "time" --> Try shorter versions of this
    sleep_count=0;                           // turn it off for when we wake up
//    Serial.println("Sleep");               // for debug only
                                             // set the columns low before sleeping, otherwise Keypad leaves them high and Rows have nothing to pull low.
    digitalWrite(7, LOW);
    digitalWrite(8, LOW); 
    digitalWrite(9, LOW);
    digitalWrite(10, LOW);   
    delay(100);                               // need this?
    enterSleep();                             // call Sleep function to put us out
    
                                              //  THE PROGRAM CONTINUEs FROM HERE after waking up in enterSleep()
  }                                           // end of checking to go to sleep

    // go read the keypad
  char key = keypad.getKey();                 // reading the keypad
  if(key)                                     // same as if(key != NO_KEY)- did something change?
  {
    msg[0]=key;                               // load the array with the key character
    // msg[1]=NULL;                           // Rx side seems to work without this

    digitalWrite(ledPin, true);               // Flash a light to show transmitting

    vw_send((uint8_t *)msg, strlen(msg));     // send the character out

    vw_wait_tx();                             // Wait until the whole message is gone

    delay (50);                               // need some delay or seem to miss key presses

    digitalWrite(ledPin, false);              // turn off the flash of LED

    sleep_count = 0;                          // reset count up to sleep if transmitted a key press

  }                                           // end of if(key) & transmitting a character

  sleep_count = sleep_count+1;                // start counting to go to sleep

}                                             // end of void loop

// Power down functions
PRR = 0xFF;

Two things...

  1. You may have to power-up the units to get them running.

  2. Be more selective in what you power-down. The timers, for example, will not consume power while the processor is in SLEEP_MODE_PWR_DOWN simply because the processor clock will be stopped. At a minimum, DON'T power-down the three timers.

Thanks Coding, do you know which three are those?
I am not measuring any time while sleeping, I just want to wake up and read the key press.

okay, read the Power Reduction Register part of the datasheet. Not really clear to me which part is keeping it from operating normally, so I just reset all of them on the interrupt and that seems to have it working normally.

Sleeping now comsumes 0.99mA. 1000mAH LiPo battery should go quite a while between charges now!

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // setting up for sleep ...
  sleep_enable();                       // setting up for sleep ...
  
// <<<<<<<<<<<<<<<<  March 2, 2011 - added some more power down stuff  >>>>>>>>>>>>>>>>

    // Disable ADC
  ADCSRA &= ~(1 << ADEN);

  // Power down functions
  PRR = 0xFF;
// <<<<<<<<<<<<<<<<<  end of new March 2 stuff  >>>>>>>>>>>>>>>>>>>>>>>

  sleep_mode();                         // now goes to Sleep and waits for the interrupt

  /* The program will continue from here after the interrupt. */
  detachInterrupt(0);                 //disable interrupts while we get ready to read the keypad 
  
  // <<<<<<<<<<<<<<<<  March 2, 2011 - added some more power down stuff  >>>>>>>>>>>>>>>>
    // Power up functions
  PRR = 0x00;
  // <<<<<<<<<<<<<<<<<  end of new March 2 stuff  >>>>>>>>>>>>>>>>>>>>>>>9

  /* First thing to do is disable sleep. */
  sleep_disable();

Thanks Coding, do you know which three are those?

I'm afraid I know very little about the PRR register. Probably the best choice for fine-tuning is to use the "power" functions from LIBC to manipulate the PRR register...

power_adc_disable();
power_spi_disable();
power_twi_disable();
power_usart0_disable();

power_adc_enable();
power_spi_enable();
power_twi_enable();
power_usart0_enable();

For my sleepy applications I modify the core so the ADC is never enabled and only Timer 0 is started. This seems to give about the same results as mucking with the PRR register with the benefit of not having to muck with the PRR register. :slight_smile: