Mega 2560 - Power Saving - Strom sparen

Hier mal die wichtigsten Ausschnitte (gesamt Code ist zu lang zu Posten, daher oben als Download):

//### mySleep set wake sources, switch off Hardware and sleep uC (for given Periode / till Interrupt) ##
unsigned long mySleep(byte sleep_mode, long toSleep, bool wakeBySerialRx, byte pci_pins[] ,\
byte pin_cnt, byte extHwSet, byte intHwSet){
// Sleep Function, set: SleepMode, WDT Sleep Periode, if Serial waiks up*, PCI Pins to wake up uC*,
// Settings for external* and internal Hardware shutdown*
// *) ... not yet (fully) implemented

long recent_sleeped = 0; // in recent mySleep call actual sleeped ms (in low Power + delay)
// is -1 if WDT was off => no information abaut sleeped ms! (check RTC)

byte oldSREG = SREG; // Save old interrupt state
noInterrupts(); // disable Interrupts
sleep_en = 1; // set save-way
SREG = oldSREG; // restore old interrupt state (enable)

PowerDownExternalHardware( extHwSet); // shut down associated ext. Harware

PowerDownInternalHardware( intHwSet); // shut down associated int. Harware

if((toSleep > 0) && (sleep_mode > 0)){ // enable wake by WDT only if WDT Periode is set
// setup wdt to periode and sleep x times till periode is smaler then the WDT Sleep Periode

// Set Sleep Mode
byte oldSREG = SREG; // Save old interrupt state
noInterrupts(); // disable Interrupts
switch (sleep_mode){
//case 0: break; //no sleep mode, stay on, only delay
case 1: set_sleep_mode (SLEEP_MODE_IDLE); break;
case 2: set_sleep_mode (SLEEP_MODE_ADC); break;
case 3: set_sleep_mode (SLEEP_MODE_PWR_SAVE); break;
case 4: set_sleep_mode (SLEEP_MODE_EXT_STANDBY); break;
case 5: set_sleep_mode (SLEEP_MODE_STANDBY); break;
//case 6: == default:
default: set_sleep_mode (SLEEP_MODE_PWR_DOWN); // Sleep Mode (lowest)
}//switch
SREG = oldSREG; // restore old interrupt state (enable)

WDT_while_loop(toSleep, recent_sleeped, WDT_8000MS); // Sleep in WDT_Periode as long as toSleep > WDT_Periode
WDT_while_loop(toSleep, recent_sleeped, WDT_1000MS); // Sleep in WDT_Periode as long as toSleep > WDT_Periode
WDT_while_loop(toSleep, recent_sleeped, WDT_128MS); // Sleep in WDT_Periode as long as toSleep > WDT_Periode
WDT_while_loop(toSleep, recent_sleeped, WDT_16MS); // Sleep in WDT_Periode as long as toSleep > WDT_Periode

// indicate that not sleeping anymore
oldSREG = SREG; // Save old interrupt state
noInterrupts(); // disable Interrupts
is_sleeping = 0; // indicate for ISR that NOT sleeping anymore
SREG = oldSREG; // restore old interrupt state (enable)

// 'Waste' rest time in Delay OR return early(er) to loop? (more Power saving possible with IDEL MODE or ...??)
//byte shorten_sleep = 3; // ms to shorten sleep (only for this delay part, not for WDT part!
while(toSleep > shorten_sleep){
toSleep -= 1;
delayMicroseconds(DELAY_1SEC); // correct measurement needed, then DELAY_1SEC [us]
total_sleeped += 1;
recent_sleeped += 1; // in this mySleep call actual sleeped ms
}

}// if((toSleep > 0) && (sleep_mode > 0))

else if(toSleep < 0){ // no WDT Periode was given =>don't wake periodicly
// wake only by Interrtupt from PCI, ADC, Serial RX ...
// start sleeping...
// disable sleeping after Interrupt (or better disable sleep in ISRs?)
byte oldSREG = SREG; // Save old interrupt state
noInterrupts(); // disable Interrupts
bool sleep_en_c = sleep_en; // get local copie save
SREG = oldSREG; // restore old interrupt state (enable)

if(sleep_en_c){

recent_sleeped = -1; // Sleeped ms are unknown!

noInterrupts(); // disable Interrupts

//setSleepMode(sleep_mode);

// for now just power down
set_sleep_mode (SLEEP_MODE_PWR_DOWN);

is_sleeping = 1; // indicate for ISR that sleep is activ

sleep_enable ();
interrupts ();
sleep_cpu ();
//sleeps here...

//...wakes in ISR and comes back here.

// check if(is_sleeping) ?? NOT NEEDED HERE!
sleep_disable ();
// indicate that not sleeping anymore
oldSREG = SREG;
noInterrupts();
is_sleeping = 0; // indicate for ISR that NOT sleeping anymore
SREG = oldSREG; // restore old interrupt state (enable)


}//if(sleep_en_c)
}//else

PowerUpInternalHardware(0b1111111); // internal Hardware back on
PowerUpExternalHardware(0b1111111); // external Hardware back ton

return recent_sleeped; // in this mySleep call actual sleeped ms, -1 unknown because WDT was not activ;
}// mySleep()





//### WDT while Loop (waste Time in sleep Mode with WDT on till toSleep ms is < WDT Intervall)##
void WDT_while_loop(long &toSleep, long &recent_sleeped, unsigned int wdt_intervall){
if(toSleep >= wdt_intervall){

byte oldSREG = SREG; // Save old interrupt state
noInterrupts(); // disable Interrupts
bool sleep_en_c = sleep_en; // get local copie save
SREG = oldSREG; // restore old interrupt state (enable)

byte wdt_periode = 0; // set WDT periode (see Datasheet)
switch (wdt_intervall){
case 10000 ... 6500: wdt_periode = 0b100001; break; // 8sec
// skip 4 and 2 sec
case 1500 ... 600: wdt_periode = 0b000110; break; // 1sec
// skip 512, 256ms
case 200 ... 80: wdt_periode = 0b000011; break; // 128ms
//skip 64, 32ms
//case 4 ... 30: wdt_periode = 0b000000; break; // 16ms
default: wdt_periode = 0b000000; break; // 16ms
}

myWatchdogEnable(wdt_periode); // set to wdt periode and enable

while(sleep_en_c && toSleep >= wdt_intervall){ // sleep for x * 8sec
toSleep -= wdt_intervall;

// final prepare sleeping...
noInterrupts();
is_sleeping = 1; // indicate for ISR that sleep is activ
digitalWrite (LED_AWAKE, LOW); // Idicate CPU is off
//myWatchdogEnable(wdt_periode); // set to wdt periode and enable
set_sleep_mode (SLEEP_MODE_STANDBY);
sleep_enable ();
interrupts ();
sleep_cpu ();
//sleeps here...

//...wakes here or in ISR and comes back here.

// check if(is_sleeping) ?? not needed!?
// sleep_disable (); // precautio, here or at end of this loop? ???
//wdt_disable();
digitalWrite (LED_AWAKE, HIGH); // Idicate CPU is on

//if(is_sleeping){ // if an other ISR, not WDT, woke this will be wrong! => check RTC for current time
total_sleeped += wdt_intervall; // store sleeped ms for millis_sleep() (might be wrong! Tasks are done to early.)
recent_sleeped += wdt_intervall; // in this mySleep call actual sleeped ms

oldSREG = SREG;
noInterrupts();
sleep_en_c = sleep_en;
SREG = oldSREG;
}//while

wdt_disable();

}//if(WDT Periode > 8sec)

}//void WDT_while_loop()



//### Set Watchdogtimer Intervall and enable WDT
void myWatchdogEnable (const byte interval){
// clear various "reset" flags
MCUSR = 0;
// allow changes, disable reset
WDTCSR = bit (WDCE) | bit (WDE);
// set interrupt mode and an interval
WDTCSR = bit (WDIE) | interval; // set WDIE, and requested delay
wdt_reset(); // pat the dog, only 1.st time needed? is it possible to let it running while in
//sleep-loops??

}