It is unclear for me the best way to put the AVR in deep sleep mode.
Looking at the LowPower library I read that:
LowPower.powerDown(period_t period, adc_t adc, bod_t bod) is the lowest current consumption state, however this function doesn’t put timers, spi, uart and twi off, such as the LowPower.idle(..) function. However this last function doesn't turn the BOD off
Should I to ensure the lowest power consumption mode to specify:
LowPower.idle(SLEEP_FOREVER, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF );
and
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
or something like:
sleep_bod_disable();
LowPower.idle(SLEEP_FOREVER, ADC_OFF, TIMER2_OFF,TIMER1_OFF, TIMER0_OFF,SPI_OFF, USART0_OFF,TWI_OFF );
Have a look here for the definitive guide Gammon Forum : Electronics : Microprocessors : Power saving techniques for microprocessors but also thnink about how do you want the processor to be woken up.
Are you going to set a watch dog timer ( max of 8 seconds with ATMEGA328P ) but you can have a series of these to extend the "quiescent" period or are you going to use an external interrupt ?
Currently I am using the 2nd solution, wake-up occurs via interrupt (Int 1) see below:
(Processor CPU 4MHz, BOD 1.8V, all unused Digital IO set as INPUT_PULLUP and unused Analog pins as INPUT connected to ground), and LED disconnected.
I have read all possible topic about low power configuration, but I don't have an ampermeter allowing me to measure micro ampers, so I have to rely on there theory.
However I am pretty sure that the power consumption is higher than expected, so I wonder if my sleep set-up is correct.
The consuming part is a RFM69W module which consumes max 45ms during 50 ms 10 times a day .
/***********************************************
Function: Sleep after RF transmission
/***********************************************/
void sleepMode ()
{
attachInterrupt (digitalPinToInterrupt(int1), buttonPressed, FALLING); // attach interrupt handler
radio.sleep(); // Set the radio transceiver in sleep mode
sleep_bod_disable();
LowPower.idle(SLEEP_FOREVER, ADC_OFF, TIMER2_OFF,TIMER1_OFF, TIMER0_OFF,SPI_OFF, USART0_OFF,TWI_OFF );
}
and
/**********************************************
Function: check button depressed after interrupt
***********************************************/
void buttonPressed ()
{
clic = ((word)PIND & 0xF0)>>4 | ((word)PINB & 0x01)<<4 |(word)(PINC &0x3F)<<5; // Access Port register directly rather than using a digital read
}
buttonFlag = true;
}
I have a somewhat similar application to yours in that I have an ATmega328p running at 4MHz with 1.8 volt brown out with a transmitter (nRF24L01) monitoring a physical mailbox.
The processor is woken up by the watchdog time every 8 seconds. Every 4th wakeup causes the processor to switch on an 3.3volt step-up converter ( to which all the peripherals are connected), tests the environment and sends a message with the status of the mailbox and other information such as its battery voltage. It then goes back to sleep.
The current 2 AA cells are 9 months old and are no where near exhausted.
I did try the Rocket Scream low power library, but had to resort to native AVR library commands because of a compatibility problem (I can't now remember exactly what problem I had to solve).
This is the command I used with Rocket Scream :
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF );
I now use the following code:
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/power.h>
. . .
. . .
// http://www.gammon.com.au/power
ADCSRA = 0; // disable ADC
// together within a few clock cycles, so some repetition
MCUSR = 0; // allow changes, disable reset
WDTCSR = bit (WDCE) | bit (WDE); // set interrupt mode and an interval
WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0); // set WDIE, and 8 seconds delay
wdt_reset(); // pat the dog
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 ();
// cancel sleep as a precaution
sleep_disable();
You'll see from the document I linked to the following power consumption listed for the various sleep modes:
SLEEP_MODE_IDLE: 15 mA
SLEEP_MODE_ADC: 6.5 mA
SLEEP_MODE_PWR_SAVE: 1.62 mA
SLEEP_MODE_EXT_STANDBY: 1.62 mA
SLEEP_MODE_STANDBY : 0.84 mA
SLEEP_MODE_PWR_DOWN : 0.36 mA
It appears that the Idle mode you are using is quite high up the list (in its raw form).
Edit.
I have a very cheap digital multimeter which has a scale to 200 microamps giving a resolution of 0.1 micro amps. I'm not sure how accurate it is but the results appear plausible.
Thanks for sharing your configuration, in fact the configuration that I am using above is already some kind of improvement I used, before that one my sleep configuration was:
void initSleep ()
{
attachInterrupt (digitalPinToInterrupt(int1), buttonPressed, FALLING); // attach interrupt handler
radio.sleep(); // Set the radio transceiver in sleep mode
power_spi_disable(); // Disable the Serial Peripheral Interface module.
power_timer0_disable(); // Disable the Timer 0 module..
power_timer1_disable(); // Disable the Timer 1 module.
power_timer2_disable(); // Disable the Timer 2 module
power_twi_disable(); // Disable the Two Wire Interface module.
power_usart0_disable(); // Disable the USART 0 module.
ADCSRA &= ~(1 << ADEN); // Ensure AD control register is disable before power disable
power_adc_disable(); // Disable the Analog to Digital Converter module
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
}
Looking at the lowPower library I saw that most of the sleep commands where covered by the LowPower.powerDown() and LowPower.idle() functions, so I did simplified the code, but I still doesn't know if LowPower.powerDown is sufficient (my initial question).
Practically, I am using a Moteino R4 (not Arduino) and my 3.6V battery is directly connected to the 3v3, bypassing the 5V to 3v3 LDO.
The only think I can think about is that the idle LDO output is uselessly consuming power of my battery (which last only several days when I was expecting several months).
Alternatively there is something wrong with my code (however I did crosscheck it several times), but debugging it is quite complex without a decent multimeter.
So I happy to read that you could achieve 0.1 micro amps, I will step back and simplify the code (using the Nick Gammon configuration as you suggest) to see if there is an hardware or software issue.
I will update this topic if I found something interesting.
Thanks
Robert
I had a quick look at the schematic for the Moteino R4 . It is quite bare but there is a LED on D9.
Since there is no clear ChipEnable pins on the transmitter/receiver elements, or the flash memory, just the standard SPI select pin which have to be held HIGH to deselect it, and the VCC is not switched (say by a transistor etc.), it requires an act of faith to believe that the radio elements are really fully shut off when your processor is in a shutdown state. But I guess you have to send a control sequence to these radio elements to shut them down and they wake up when they notice some activity on one of their pins.
It'll be the peripheral devices (radios, leds, regulators etc.) which cause the power consumption problems, not the processor which can be forced into a low micro amp consumption mode.
RFM69W transceiver is consuming 45mA max during transmission (I can decrease the power but I also decrease the coverage range).
Most of the time the RFM is in sleep mode and is activated only when an interrupt occurs.
So with a weak battery (offering 3v6, but no amps), program without interrupt is running well (battery is sufficient to power the AVR) which goes after set-up in sleep mode. Now once the interrupt occurs, the RFM pumps too much amps and the battery level goes below the BOD, (typically 1v4) so the processor is blocked, no matter if they are a sleep commands or not that follows the RFM transmission. At that time it looks that the RFM still active (not in sleep mode) and continues to discharge my battery.
So I believe my low power setting is is correctly configured, I could prove this by reducing the RFM transmission power. The solution (which is not trivial) is to limit the transmission time (and power).