Arduino Atmega328 Schlafmodus mit internem Timer-Interrupt

Ich versuche, den Stromverbrauch meines Atmega328 -Arduino- zu reduzieren, indem ich die Schlafmodi verwende. In meinem Code möchte ich jeden aufwecken 10ms, den Wert von ADC lesen und wieder in den Schlafmodus wechseln.

Es gibt einige nützliche Anweisungen ( hier und hier ), aber die meisten basieren entweder auf Watch Dog Timre oder externem Interrupt von Pins.

Hier ist der vereinfachte Code von mir, funktioniert aber nicht (er geht nicht zur Interrupt-Handler-Funktion):

Im Setup kümmere ich mich um den Timer und seinen Handler:

void setup() {
   Serial.begin(19200);

   Timer1.initialize(10000);  //10ms sampling rate
   Timer1.attachInterrupt(ReadMyADC,10000);

   power_spi_disable(); // SPI
   power_timer0_disable();// Timer 0
}

Der Rest des Codes einschließlich loop()lautet wie folgt:

void ReadMyADC(){
    sleep_disable();
    PRR = PRR & 0b00000000;
    Serial.print("sth");
    power_adc_enable(); // ebable ADC converter
    //Read ADC
    power_adc_disable();
}
void loop() {
    // ----------------low power ---------------
    PRR = PRR | 0b00100000;
    sleep_enable();
    set_sleep_mode(SLEEP_MODE_STANDBY);
    EIMSK |= _BV(INT0);            //enable INT0
    ADCSRA = 0;                    //disable ADC
    cli();
    mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE);  //turn off the brown-out detector
    mcucr2 = mcucr1 & ~_BV(BODSE);
    MCUCR = mcucr1;
    MCUCR = mcucr2;
    sei();                         //ensure interrupts enabled so we can wake up again
    sleep_cpu();                   //go to sleep
    sleep_disable();               //wake up here
    Serial.print("Wake\n");
}

Anscheinend ist Timer1 nicht in der Lage, die CPU aus dem Schlafmodus zu verlassen. Hast du eine Idee, wie man das handhabt oder wo das Problem liegt?

Schauen Sie sich das Datenblatt des Atmega an. Sie werden feststellen, dass für alle tieferen Schlafmodi als Leerlauf und ADC-Rauschunterdrückungsmodus nur der wdt, ein externer Interrupt oder ein asynchroner Timer (externer 32-kHz-Quarz) die CPU aufwecken kann.

Antworten (2)

Deeper-Sleep-Modi schalten einen größeren Teil des Chips aus, um den Stromverbrauch zu reduzieren.

Die tieferen Modi schalten die Timer-Zähler aus, da sie ziemlich viel Strom verbrauchen.

Verwenden Sie stattdessen den Watchdog-Timer im Interrupt-Modus oder verwenden Sie für eine maximale Abschaltung ein mikrogesteuertes externes RC-Decay, um das Mikro nach kurzer Zeit zu unterbrechen.

Der Watchdog-Timer unterstützt keine Interrupts mit einer höheren Frequenz als 64 Hz. Es scheint also, dass ich für eine Frequenz von 100 Hz Timer1 bei SLEEP_MODE_IDLE verwenden muss, wobei unnötige Peripheriegeräte heruntergefahren werden.

Sleep_Mode_Standby lässt Timer1 nicht laufen, da die Taktdomäne ausgeschaltet ist. Sie können Timer1 nur in Sleep_Mode_Idle verwenden, Ihr Code sollte funktionieren, wenn Sie diesen Modus verwenden.

Verwenden Sie den WDT, da er einen eigenen Takt bei 128 kHz hat, der immer aktiviert ist, wenn er eingeschaltet ist.

Code wie dieser könnte für Sie funktionieren:

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

void setup () { }

const byte LED = 13;   //Nano LED

void flashLED ()
{
 pinMode (LED, OUTPUT);
  // Flash the LED 
 for (byte i = 0; i < 10; i++)
    {
    digitalWrite (LED, HIGH);
    delay (50);
    digitalWrite (LED, LOW);
    delay (50);
    }
 } 

// Watchdog ISR routine
ISR (WDT_vect) 
{
   wdt_disable();  // disable watchdog
   // Return from ISR
}  


void loop () 
{

  flashLED ();

  // disable ADC
  ADCSRA = 0;  

  // clear all MCUSR flags
  MCUSR = 0;     
  // allow changes, disable reset pin
  WDTCSR = bit (WDCE) | bit (WDE);
  // set interrupt mode and an interval 
  WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1) | bit (WDP0);    // set WDIE, and 2 second WDT delay
  wdt_reset();  // Start the timer

  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  noInterrupts ();           // timed sequence follows
  sleep_enable();

  // turn off brown-out detector
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 
  interrupts ();
  sleep_cpu (); 

   // Sleep here in powerdown state
  // Return here after WDT return from ISR 

  // disable sleep
  sleep_disable();

  // Do your thing here

  }