Go Down

Topic: Mega 2560 - Power Saving - Strom sparen (Read 16925 times) previous topic - next topic

combie

Quote
Wegen dem BOD (Brown Out Detection). Das ist eine Schutzfunktion in Hardware. Das hat nichts mit Strom sparen zu tun.
Eine aktivierte BOD braucht Strom.

Also:
Stromsparen -->> BOD aus.
Der Verstand, und die Fähigkeit ihn zu gebrauchen, sind zweierlei Fähigkeiten.

Doc_Arduino

Hallo,

okay, macht aber keinen Sinn ohne anders geartete Spannungsüberwachung. Meinste nicht auch. Das BOD wirkt zwar mit dem wenigen Leveln sehr grob, aber man hat wenigstens etwas.
Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

combie

Quote
Meinste nicht auch.
Ja, es macht nicht immer Sinn das abzuschalten ....
z.B. beim EEPROM gefummel, kann sich das sonst fatal auswirken.
Aber das tut man ja im Sleep nicht ;)



Der Verstand, und die Fähigkeit ihn zu gebrauchen, sind zweierlei Fähigkeiten.

jim_beam

Hallo,

vielen Dank für die Rückmeldung. Werde mir das mal am Wochenende ansehen, habe gerade wenig Zeit.

Was BOD angeht, habe ich bei Nick was zu gelesen, und wenn ich mich recht erinnere waren es 20uA. So kommt er am Ende auf einstellige uA für den 328P.

Er stellt den BOD in Software ab. Aber die Register heißen beim Mage anders oder es geht nich via Soft? Konnte ich noch nicht klären im Datenblatt. Weiß jemand zufällig genaueres?

Nick schreibt, dass der BOD den uC in Reset versetzt, da braucht er dann 1-2-stellige mA. Das hilft bei einer Batterieanwendung dann auch nicht weiter, wenn der BOD wegen zu geringer Spannung ein Reset auslöst. Die Batterie/Akku/ großes C (1F) sind dann ums o schneller ganz am Ende.

Soweit würde ich es gar nicht erst kommen lassen wollen und immer schön die Spannung des Akkus/Batt/C via Funkmodul zum Master übertragen. Der steht bei mit am Tisch, hat ein Netzteil gepufferten Akku und gibt dann eine Fehlermeldung (LED/LCD) (soll mal ein Funk-Sensor-Datennetz mit einem Master. z.B. Wetterstation, Home automation werden).

VG

Serenifly

Ja, die entsprechenden Bits im MCUCR Register gibt es wohl auf dem Mega nicht.

Du kannst BOD aber immer noch über die Fuse-Bits deaktivieren. Dann ist er aber immer aus. Nicht nur im Sleep Mode.

jim_beam

Ja, die entsprechenden Bits im MCUCR Register gibt es wohl auf dem Mega nicht.

Du kannst BOD aber immer noch über die Fuse-Bits deaktivieren. Dann ist er aber immer aus. Nicht nur im Sleep Mode.
Danke!  :)

jim_beam

Noch mal eine Frage zu den Sleep-Modes und Aufwachen durch WDT und oder Serial Rx und oder PCI.

Bei Nick Gammon habe ich gesehen, dass er wenn er mehr als 8sec im LowPowerMode schalfen möchte, die Sleep Funktion mehrfach ausführt. z.B in seinem Low-power temperature monitor Oder das zu schlafende WDT-Intervall mit in die Schlaf-Funktion übergibt.

Dabei wird jedes mal beim ausführen der Schlaf Funktion die Hardware aus- und nach dem Schlafen weider eingeschaltet.

Möchte man nun z.B ein vielfaches von 8Sec. oder eine "ungerade" Zeit z.B. 9796ms schlafen, wird jedes mal wenn ein WDT-Interverall beendet ist die gesamte Hardware ein- und dann weider aus geschaltet. ( Bsp: 9796ms: 1. WDT-Intervall 8sec, Rest 1796ms, 2. 1sec, Rest 796ms, 3. 512ms, Rest 284 ...usw.)

Meine Idee war es nun in der Sleep-Funktion nur ein mal am Anfang den gewünschten Sleep-Mode zu setzen (z.B. LowPowerSleep), dann die benötigte größte WDT-Periode zu setzen (z.B. 8sec) und in einer while-loop so lange sleep_enable () und sleep_cpu () bis die zu schlafende Zeit kleiner 8Sec ist. Das ganze dann mit dem nächste kleineren Intervall so lange wiederholen bis man bei 16ms angekommen ist.

Leider rast mein TestCode durch die Sleep Funktion durch ohne (wirklich) zu schlafen. Nur ganz kurz wird die WAKE_LED mal ausgeschaltet/flackert. Der uC scheint nicht wirklich in den Sleep Mode zu gehen, oder wird gleich weider aufgeweckt. Viellicht, weil der WDT nicht frisch gesetzt wurde??

Würde mich sehr freuen, wenn jemand einen Tipp für mich hat. Eventuell müssen ja gewisse Abfolgen im Code eingehalten werden oder ...


jim_beam

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

//### 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??

}


 

jim_beam

#68
Sep 05, 2015, 02:37 am Last Edit: Sep 05, 2015, 02:47 am by jim_beam
So wird es letztendlich im "Innern" von mySleep auch gemacht.

...ich kann inzwischen / sollte meine Frage auch verkürzen:

Als Vorlage nehme ich mal diesen Code von Nick:
pin change interrupts, and a watchdog timer interrupt, for the ATtiny 85

Original von Nick:
Code: [Select]

// ATtiny85 sleep mode, wake on pin change interrupt or watchdog timer
// Author: Nick Gammon
// Date: 12 October 2013

// ATMEL ATTINY 25/45/85 / ARDUINO
//
//                  +-\/-+
// Ain0 (D 5) PB5  1|    |8  Vcc
// Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1
// Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1
//            GND  4|    |5  PB0 (D 0) pwm0
//                  +----+

#include <avr/sleep.h>    // Sleep Modes
#include <avr/power.h>    // Power management
#include <avr/wdt.h>      // Watchdog timer

const byte LED = 3;  // pin 2
const byte SWITCH = 4; // pin 3 / PCINT4

ISR (PCINT0_vect)
 {
 // do something interesting here
 }  // end of PCINT0_vect
 
// watchdog interrupt
ISR (WDT_vect)
{
   wdt_disable();  // disable watchdog
}  // end of WDT_vect

void resetWatchdog ()
  {
  // clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset, clear existing interrupt
  WDTCR = bit (WDCE) | bit (WDE) | bit (WDIF);
  // set interrupt mode and an interval (WDE must be changed from 1 to 0 here)
  WDTCR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  // pat the dog
  wdt_reset(); 
  }  // end of resetWatchdog
 
void setup ()
  {
  resetWatchdog ();  // do this first in case WDT fires
 
  pinMode (LED, OUTPUT);
  pinMode (SWITCH, INPUT);
  digitalWrite (SWITCH, HIGH);  // internal pull-up
 
  // pin change interrupt (example for D4)
  PCMSK  = bit (PCINT4);  // want pin D4 / pin 3
  GIFR  |= bit (PCIF);    // clear any outstanding interrupts
  GIMSK |= bit (PCIE);    // enable pin change interrupts
  }  // end of setup

void loop ()
  {
  digitalWrite (LED, HIGH);
  delay (500);
  digitalWrite (LED, LOW);
  delay (500);
  goToSleep ();
  }  // end of loop
 
void goToSleep ()
  {
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  ADCSRA = 0;            // turn off ADC
  power_all_disable ();  // power off ADC, Timer 0 and 1, serial interface
  noInterrupts ();       // timed sequence coming up
  resetWatchdog ();      // get watchdog ready
  sleep_enable ();       // ready to sleep
  interrupts ();         // interrupts are required now
  sleep_cpu ();          // sleep               
  sleep_disable ();      // precaution
  power_all_enable ();   // power everything back on
  }  // end of goToSleep


Und würde folgende Änderungen vornehmen:
Code: [Select]


// watchdog interrupt
ISR (WDT_vect) {
 // wdt_disable(); // DON'T disable watchdog here, done after sleeping x_times
} // end of WDT_vect


void resetWatchdog (byte wdtPeriode) { // clear various "reset" flags
  MCUSR = 0; // allow changes, disable reset, clear existing interrupt
  WDTCR = bit (WDCE) | bit (WDE) | bit (WDIF); // set interrupt mode and an     interval (WDE must be changed from 1 to 0 here)

  WDTCR = bit (WDIE) |  wdtPeriode; // set WDIE, and WDT Periode !

  // pat the dog
  wdt_reset();
} // end of resetWatchdog


void goToSleep (long msToSleep) {  // sleep x times eg. 16sec x_times = 2
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
 
  // powerDownExternalHardware(extHwSettings);
  ADCSRA = 0; // turn off ADC
  power_all_disable (); // power off ADC, Timer 0 and 1, serial interface
 
  // WDT for 8sec
  noInterrupts (); // timed sequence coming up
  resetWatchdog (wdtPeriode8sec); // get watchdog ready just once??
  interrupts (); // interrupts are required here ???

  while(msToSleep >= 8000){
    msToSleep -= 8000;
    noInterrupts (); // timed sequence coming up 
    sleep_enable (); // ready to sleep
    interrupts (); // interrupts are required
    now sleep_cpu (); //

    sleep sleep_disable (); // precaution
  }//while


  // WDT for 1sec
  noInterrupts (); // timed sequence coming up
  resetWatchdog (wdtPeriode1sec); // get watchdog ready just once??
  interrupts (); // interrupts are required here ???

  while(msToSleep >= 1000){
    msToSleep -= 1000;
    noInterrupts (); // timed sequence coming up 
    sleep_enable (); // ready to sleep
    interrupts (); // interrupts are required
    now sleep_cpu (); //

    sleep sleep_disable (); // precaution
  }//while

  // WDT for x ms ...and so on ...so why not putting this while loops in one function
  // wich is called with the msToSleep periode and byte for WDT Periode as input?

  //  now disable wdt once ( not in wdt isr) is that ok so?
  wdt_disable();

  power_all_enable (); // power everything back on
  //powerUpExternalHardware(extHwSettings);
} // end of goToSleep



Ist das so sauber und wird es funktionieren? Kann es leider erst mal nicht testen.




jim_beam

Ok, dieser Sketch tut es (fast) so wie ich es mir gewünscht habe, außer das wake up by Serial Rx ( PCI Pin 0, beim Mega ist das PCINT8  auf PCI1) funktioniert nicht richtig. Sendet man etwas per Serial wird er scheinbar ein mal kurz wach, schläft aber dann weiter, stat wie beim PCI vom Switch das Schlafen komplett abzubrechen. Senden man schnell hintereinander (Enter gedrückt) wird scheinbar jede WDT Sleep periode abgebrochen und er kehrt sehr schnell zurück in die loop.  Versteh ich nicht warum, denn beim PCI vom switch klappt es ja??

Code: [Select]
/*
 * Original from Nick Gammon at http://gammon.com.au/forum/?id=11497&reply=6#reply6
 * Changed by Jim Beam on 05/09/15 to work with Meag2560, changed toSleep Function
 * Thanks to the guys of the Arduino Forum and Nick Gammon!
 * To-DO: add Wake by Serial Rx (D0) by PCI, simplify more PCI Pins, disable more Hardware,
 *        set sleepMode for toSleep Function, Auomatic detect WDT Periodes and bytes,
 *        add setable ext-Hardware-FCN and setable int. Hwardware FCN
 */

#include <avr/sleep.h>    // Sleep Modes
#include <avr/power.h>    // Power management
#include <avr/wdt.h>      // Watchdog timer

const byte LED = 12;  // pin 13
const byte LEDWAKE = 13;  // pin 13
const byte SWITCH = 10; // pin 10 / PCINT4
volatile bool sleep_enable = 1;     // 0= stop sleeping, go back to loop, set 0 by any ISR
volatile bool serial_isr = 0;       // Serial Rx isr was activ



ISR (PCINT0_vect)   // Wake Pin 10
 {
 // do something interesting here
  sleep_disable (); // precaution
  wdt_disable();
  sleep_enable = 0;
 }  // end of PCINT0_vect



 ISR (PCINT1_vect)  // Serial
 {
 // do something interesting here
  
  sleep_disable (); // precaution
  wdt_disable();
  sleep_enable = 0;
  serial_isr = 1;
 }  // end of PCINT0_vect


 
// watchdog interrupt
ISR (WDT_vect)
{
   //wdt_disable();  // disable watchdog
}  // end of WDT_vect



  
void setup ()
  {
  resetWatchdog (0b100001);  // do this first in case WDT fires
  pinMode (LED, OUTPUT);
  pinMode (LEDWAKE, OUTPUT);
  digitalWrite (LEDWAKE, HIGH);
  
  Serial.begin(115200);
  Serial.print(F("\n\nStarting...\n File: " __FILE__ "\n Date: " __DATE__ "\n Time: " __TIME__ "\n IDE : "));
  Serial.println(ARDUINO);
  Serial.println();
  
 
  pinMode (SWITCH, INPUT);
  digitalWrite (SWITCH, HIGH);  // internal pull-up


    //set PCI for Pin 0, Provide PCI ISR!!
    // pin change interrupt (example for D0) Mega 2560! to Waik by Serial if wanted!
    PCMSK1 |= bit (PCINT8); // want pin 0
    PCIFR  |= bit (PCIF1);   // clear any outstanding interrupts
    PCICR  |= bit (PCIE1);   // enable pin change interrupts for D0 to D7
    
    PCMSK0 |= bit (PCINT4); // want pin 10
    PCIFR  |= bit (PCIF0);   // clear any outstanding interrupts
    PCICR  |= bit (PCIE0);   // enable pin change interrupts for D0 to D7
  }  // end of setup



void loop ()
  {
  Serial.print(F("\n Loop"));
  checkSerial();
    
  for(byte i=0;i<15;i++){
    digitalWrite (LED, HIGH);
    delay (100);
    digitalWrite (LED, LOW);
    delay (100);
  }
  doSerial();
  Serial.flush();
  goToSleep (15999L);
  }  // end of loop


  
void goToSleep (long msToSleep)
  {
  noInterrupts ();       // timed sequence coming up
  sleep_enable = 1;
  byte old_ADCSRA = ADCSRA;
  // disable ADC
  ADCSRA = 0;  

  PCMSK1 |= bit (PCINT8); // want pin 0
  PCIFR  |= bit (PCIF1);   // clear any outstanding interrupts
  PCICR  |= bit (PCIE1);   // enable pin change interrupts for D0 to D7
  
  interrupts ();         // interrupts are required now
  
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  
  power_all_disable ();  // power off ADC, Timer 0 and 1, serial interface

  UCSR0B &= ~bit (RXEN0);  // disable receiver
  UCSR0B &= ~bit (TXEN0);  // disable transmitter


  wdtWhileLoop(msToSleep, 8000UL, 0b100001);  //8Sec  //Change: just give msToSleep, max WDT Periode and byte will be
                                                      //determined in FCN by switch case
  wdtWhileLoop(msToSleep, 1000UL, 0b000110);  //1Sec
  wdtWhileLoop(msToSleep, 128UL,  0b000011);  //128ms
  wdtWhileLoop(msToSleep, 16UL,   0b000000);  //16ms
  

  //  now disable wdt once ( not in wdt isr) is that ok so?
  wdt_disable();
  
  power_all_enable ();   // power everything back on
  ADCSRA = old_ADCSRA;
  PCICR  &= ~bit (PCIE1);   // disable pin change interrupts for Serial D0 Pin0 (Mega)
  UCSR0B |= bit (RXEN0);  // enable receiver
  UCSR0B |= bit (TXEN0);  // enable transmitter
  }  // end of goToSleep



void wdtWhileLoop(long &msToSleep, int wdtPeriodeMs, byte wdtPeriodeByte){

  //Change: just give msToSleep, max WDT Periode and byte will be
   //determined in FCN by switch case
  
  noInterrupts ();       // timed sequence coming up
  resetWatchdog (wdtPeriodeByte);      // get watchdog ready
  bool sleep_enable_c = sleep_enable;
  interrupts ();         // interrupts are required now
  
  while((msToSleep >= wdtPeriodeMs) && sleep_enable_c){
    msToSleep -= wdtPeriodeMs;
    noInterrupts (); // timed sequence coming up
    digitalWrite (LEDWAKE, LOW);
    sleep_enable (); // ready to sleep
    interrupts (); // interrupts are required now
    sleep_cpu (); //

    sleep_disable (); // precaution
    digitalWrite (LEDWAKE, HIGH);
    
    noInterrupts ();       // timed sequence coming up
    sleep_enable_c = sleep_enable;
    interrupts ();         // interrupts are required now
    
  }//while
}



void resetWatchdog (byte interval)
  {
  //byte interval = 0b100001; //8sec
   // 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??
  }  // end of resetWatchdog



void doSerial(){
  Serial.println();
  Serial.print(F("Echo: "));
  while(Serial.available()){
    Serial.print(Serial.read());
  }
  Serial.println();
}


void checkSerial(){
  noInterrupts ();
  bool serial_isr_c = serial_isr;
  interrupts ();

  if(serial_isr_c){
  noInterrupts ();
  serial_isr = 0;;
  interrupts ();

  Serial.println(F("\nWoke by Serial. pls. reenter Command now within 3 sec."));
  }
}







  

jim_beam

#70
Sep 06, 2015, 01:33 am Last Edit: Sep 06, 2015, 01:35 am by jim_beam
Ok konnte das Problem deutlich weiter eingrenzen!

Kurz: Die PCI ISR an Serial-Rx-Pin 0 wird nicht ausgeführt wenn ein (zu) kurzer low Puls (Daten) kommt. Lediglich der uC wird geweckt!!  :o 

Nur bei Baudraten von 9600 und kleiner wird die PCI ISR sauber auch schon von einem gesendeten Zeichen durchlaufen! Bei höheren BaudRaten müssen es mehr Zeichen sein. Bei 230400 die ich sonst so standardmäßig nutze kam die PCI ISR nie!. Aber der uC wacht auf.

Habe lange gesucht und hatte auch am Ende nicht wirklich dran gedacht, aber mal zu "Spass" Baudrate geändert. Kann dass Jemand (mir) erklären??

Es scheint ja, dass die PCI-ISR nicht angesprungen wird, das der low-Puls durch die schnellen Daten zu kurz ist!?

Ein Test mit einem Käbelchen in Pin0 und (internem) Pullup funktioniert sauber! Die (vermeintliche) Serial RX ISR wird ausgeführt (Test-LED an, und Flag gesetzt). 

Ich muss dazu sagen, dass ich meinen 16U2 nicht mehr drauf habe auf dem Mega, ist mit 10cm Kabel an ein leeres UNO Board zum proggen und für Serial verbunden. Könnte ev. auch eine Rolle spielen??

Hier mein TestCode. Freue mich über Rückmeldungen. Vielen Dank!  :)

jim_beam

#71
Sep 06, 2015, 01:51 pm Last Edit: Sep 06, 2015, 01:53 pm by jim_beam
Juhu jetzt läuft alles wie gewünscht und braucht im Schlaf nur 57uA (mit WDT an) und 51uA ohne WDT.   :)  (BOD bei beiden aus, brachte ca 20uA)

Das Problem ist tatsächlich, dass der sehr kurze Serial Rx zwar den uC weckt, aber der ja braucht weil CPU Clock aus war bis er so weit ist und bis dahin ist der PCI-Interrupt nicht mehr erkennbar/längst vorbei.

Code: [Select]
Datasheet 2560, Page 109
15. External Interrupts

Note that if a level triggered interrupt is used for wake-up from Power-down, the required level must be
 held long enough for the MCU to complete the wake-up to trigger the level interrupt. If the level
 disappears before the end of the Start-up Time, the MCU will still wake up, but no interrupt will be
generated. The start-up time is defined by the SUT and CKSEL Fuses as described in "System Clock and
 Clock Options" on page 39.


1. Lösung:
nur in den SLEEP_MODE_STANDBY Mode gehen, da bleibt die Clock Sourse an => 570uA (mir zu viel)

2. Lösung:
um zu erkennen dass es ein sehr schnelle Serial Rx war, das zwar den uC geweckt hat, aber kein Flag in der ISR gestzt hat,
setze ich vor jedem sleep selbst ein Flag ("volatile bool unrecodnised_isr = 1"), dass jede andere ISR (z.B WDT, andere PCI Pins, ...) wieder löscht.
An der Stelle wo der uC wieder aufwacht im Code frage ich das Flag ab, ist es noch gesetzt, wird es wohl ein wake up aufgrund des sehr schnell Serial Rx Signals gewesen sein, dass keine ISR geschafft hat zu triggern. (Und falls das Serial Rx sehr langsam/oder extrem lang ist, wird es auch richtig erkannt.)

Codeausschnitte, alle ISRs:
Code: [Select]

// alle ISRs clearen unrecodnised_isr

ISR (PCINT0_vect)               // Pin10
{
  sleep_disable();              // stop sleeping (needeed? or to just make sure?)
  sleep_enable = 0;             // stop further multiple sleeping in while loop (not yet implementet)
  unrecodnised_isr = 0;         // disable, since this ISR was carried out, too short SerialRx ISR wont clear this
 
  if(!digitalRead(10)){         //Pin is low = activ
    isr_pin10_flag = 1;         // indicate ISR has run
    PCMSK0 &= ~bit (PCINT4);    // clear pin 10 of PCI0, no further ISR
  }



ISR (PCINT1_vect)               // Pin 0 (Serial Rx), Pin 14 (wire 4 testing)
{
  sleep_disable();
  sleep_enable = 0;
  unrecodnised_isr = 0;         // disable, since this ISR was carried out, too short SerialRx ISR wont clear this
   
  if(!digitalRead(14)){         //Pin is low = activ
    isr_pin14_flag = 1;
    PCMSK1 &= ~bit (PCINT10);   // clear pin 14
  }
  //else if(!digitalRead(0)){   // with checking the stae (might be too slow and fast chaging while Data RXed)
  else{                         // must have been a LONG SerialRx! So manually set back unrecodnised_isr=1
                                // to ensure Serial is done in Main loop
    PCMSK1 &= ~bit (PCINT8);    // clear pin 0 of PCI1 alreaddy so cant be called again
    unrecodnised_isr = 1;       // manually set back unrecodnised_isr=1 to ensure Serial is done in Main loop
    //isr_serial_flag = 1;      // this is done after sleep in if(unrecodnised_isr){}
    digitalWrite (TEST_LED, HIGH);  // Some Extra Debug Serial Rx erkannt
  }
}   


ISR (PCINT2_vect)               // empty
{
  sleep_disable();              // stop sleeping (needeed? or to just make sure?)
  sleep_enable = 0;             // stop further multiple sleeping in while loop (not yet implementet)
  unrecodnised_isr = 0;         // disable, since this ISR was carried out, too short SerialRx ISR wont clear this



ISR (WDT_vect)
{     
  isr_wdt_flag = 1;
  unrecodnised_isr = 0;          // disable, since this ISR was carried out, too short SerialRx ISR wont clear this
  if(!sleep_enable){           // sleep was stopped by any other ISR
    wdt_disable();             // disable watchdog 
    wdt_time = millis();       // get time of wdt has fired (not implemented)
  }
}  // end of WDT_vect



Der Teil vor und nach dem schlafen legen des uC:
Code: [Select]
sleep_enable();
    unrecodnised_isr = 1;           // set, normal ISR will clear, too short SerialRx ISR wont clear
    digitalWrite (AWAKE_LED, LOW);  // show uC goes sleeping
   
    if(!sleep_enable){              // only sleep if not diesabled yet by any ISR (except WDT) (later while loop)
      // don't go sleeping, has already been disabled!
       interrupts ();               // but eable Interrtups anyway
      //break;  // in loop later
      //return;
    }
    else{                           // go sleeping
      interrupts ();
      sleep_cpu ();     
      //sleeps here...
     
      //...wakes here
      sleep_disable();
           
    }
   
    digitalWrite (AWAKE_LED, HIGH); // show uC is on
   
    byte SREG_old = SREG;
    noInterrupts();
       
    if(unrecodnised_isr){           // has not clear by ordanary ISR, so it must have been an short Serial woke
      sleep_enable = 0;             // stop further sleeping
      PCMSK1 &= ~bit (PCINT8);      // clear pin 0 of PCI1 if it was this Pin!?
      isr_serial_flag = 1;
      //digitalWrite (TEST_LED, HIGH);  // Debug Serial Rx erkannt
    }
   
    // nach while loop test for disabling wdt
    if(sleep_enable){               // sleep not stopped by any other ISR, so normal sleep end,         
      wdt_disable();                // then disable watchdog
    }                               // else: any other ISR has cleard sleep_enable, leave WDT 1x more on to measure time
                                    // WDT will disable it selfe if sleep_enable==0
    SREG = SREG_old;                // Interrupts might be enabled again



So ist alles unter einem Hut:
- großer Bruder vom Uno/328P (vergleichsweise viel Speicher)
- trotzdem extrem Strom Sparen (leider etwas um löten am Bord, und Programmieren "von außen" nötig)
- beliebig langes schlafen legen (durch mehrfach WDT Sleep in while loop)
- beim aufwachen aus WDT Sleeps trotzdem die Zeit etwa kennen (millis() + sleeped_ms)
- Aufwachen durch beliebige PinChangeInterrupts, auch
- aufwachen durch Serial Rx (1. Empfagnegen Daten werden verworfen, Serielles Kommando kann schon nach einigen ms gesendet werden)

Finde ich genial, so habe ich mir das vorgestellt. Mein Dank gilt allen hier im Forum und Nick für die vielen Tipps, Code-Beispiele und Ideen!!  :)

Go Up