Forum: Mikrocontroller und Digitale Elektronik ATmega328p timer2 sleep mode zu kurz


von Multi _. (multibaba)


Lesenswert?

Hallo Zusammen,

ich möchte meinen ATMega328p in den Schlafmodus versetzen.  Durch den 
Timer2 soll dieser wieder aufwachen.

dazu verwende ich folgenden Code:
1
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
2
  
3
  sei();
4
}
5
 
6
void loop(){
7
  
8
  
9
  // Timer 2 Konfigurieren
10
  TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20); // Prescaler 1024
11
    
12
  // Overflow Interrupt erlauben
13
  TIMSK2 |= (1<<TOIE0);
14
  
15
  sleep_enable();
16
  for (int i=0; i<1000; i++)
17
  {
18
19
    TCNT2 &= 0; //Timer resetten
20
    sleep_cpu();
21
  }
22
  sleep_disable();
23
  
24
  digitalWrite(ledPin, HIGH);
25
  delay(1000);
26
  digitalWrite(ledPin, LOW);
27
  
28
}
29
30
ISR(TIMER2_OVF_vect){  
31
}

meiner Meinung nach müsste der MCU dann für ca. 16 s im Schlafmodus 
bleiben:
256*1024*1000/16000000 (Der MCU läuft mit 16 MHz Takt)
Es zeigt sich jedoch, dass die LED genauso lang an, wie aus ist. Also 
etwa 1 Sekunde.
Wo ist mein Fehler?

von Peter D. (peda)


Lesenswert?

Zeig mal den vollständigen Code.

Power-Save geht nur mit T2 im asynchronen Mode, d.h. am 32kHz Quarz.
Und dann muß vor dem Sleep gewartet werden, bis der Zugriff beendet 
wurde (Löschen des Interruptflags ist ein Zugriff), d.h. die nächste 
32kHz Flanke erfolgte.
Ansonsten laufen die Timer nur bei Idle weiter.

von S. Landolt (Gast)


Lesenswert?

> Power-Save geht nur mit T2 im asynchronen Mode, d.h. am 32kHz Quarz.
Das lese ich im Datenblatt anders.

Das Programm, zumindest was von ihm zu sehen ist, sollte funktionieren. 
Unklar ist mir der Sinn von
> TCNT2 &= 0; //Timer resetten

Und
> TIMSK2 |= (1<<TOIE0)
klappt nur dank etwas Glück.

von Thomas E. (thomase)


Lesenswert?

S. Landolt schrieb:
>> Power-Save geht nur mit T2 im asynchronen Mode, d.h. am 32kHz Quarz.
> Das lese ich im Datenblatt anders.

2. If Timer/Counter2 is running in asynchronous mode.

Die Fußnote DB bezieht sich auf den Timer Oszillator. Der einzige Takt, 
der im Power Save aktiv ist und somit, neben dem Watchdog Timer, die 
einzige Möglichkeit des Controllers, sich selbst aufzuwecken. 
Nachzulesen im DB unter Sleep Modes.

S. Landolt schrieb:
> Unklar ist mir der Sinn von

Mir auch.

S. Landolt schrieb:
> klappt nur dank etwas Glück.

Warum braucht man dazu Glück?

von Multi _. (multibaba)


Lesenswert?

Vielen Dank für eure Antworten!

S. Landolt schrieb:
> Unklar ist mir der Sinn von
>> TCNT2 &= 0; //Timer resetten
>

hier setze ich den Timer zurück, damit er bei 0 zu zählen anfängt. Da 
war ich mich auch nicht sicher, ob das nötig ist. Aber der Timer läuft 
ja schon vorher oder? Das heißt, es könnte sein, dass er bei einem 
höheren Wert anfängt.

>
> Und
>> TIMSK2 |= (1<<TOIE0)
> klappt nur dank etwas Glück.
Könntest du das bitte etwas genauer erklären! Warum klappt das nicht 
immer?

Hier noch der volle Code:
1
#include <Arduino.h>
2
#include <avr/interrupt.h>
3
#include <avr/sleep.h>
4
5
int ledPin = 8;
6
7
 
8
void setup(){
9
  
10
  pinMode(ledPin,OUTPUT);
11
  digitalWrite(ledPin,LOW);
12
  
13
  
14
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
15
  
16
  sei();
17
}
18
 
19
void loop(){
20
  
21
  
22
  // Timer 2 Konfigurieren
23
  TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20); // Prescaler 1024
24
    
25
  // Overflow Interrupt erlauben
26
  TIMSK2 |= (1<<TOIE0);
27
  
28
  sleep_enable();
29
  for (int i=0; i<1000; i++)
30
  {
31
32
    TCNT2 &= 0;
33
    sleep_cpu();
34
  }
35
  sleep_disable();
36
  
37
  digitalWrite(ledPin, HIGH);
38
  delay(5000);
39
  digitalWrite(ledPin, LOW);
40
  
41
}
42
43
ISR(TIMER2_OVF_vect){  
44
}

von Peter D. (peda)


Lesenswert?

Thomas E. schrieb:
> Warum braucht man dazu Glück?

Z.B. beim ATmega8 sind TOIE0 und TOIE2 nicht die gleiche Bitnummer.

von Thomas E. (thomase)


Lesenswert?

Peter D. schrieb:
> Z.B. beim ATmega8 sind TOIE0 und TOIE2 nicht die gleiche Bitnummer.

Ja, gesehen. Klappt nur zufällig.

Ich dachte das bezog sich, wie in diesen Fällen üblich, auf die 
Zuweisung mit "|=".

von Multi _. (multibaba)


Lesenswert?

Peter D. schrieb:
> Z.B. beim ATmega8 sind TOIE0 und TOIE2 nicht die gleiche Bitnummer

Da hast du Vollkommen Recht. Danke!

Aber zurück zu meiner eigentlichen Frage. Warum ist der Sleep-Mode so 
kurz?

von Thomas E. (thomase)


Lesenswert?

Multi _. schrieb:
> Aber zurück zu meiner eigentlichen Frage. Warum ist der Sleep-Mode so
> kurz?

Weil das, was du da versuchst, nicht funktioniert. Nicht funktionieren 
kann:

Peter D. schrieb:
> Power-Save geht nur mit T2 im asynchronen Mode, d.h. am 32kHz Quarz.

Warum er trotzdem noch irgendwas macht? Keine Ahnung. Arduino?

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

S. Landolt schrieb:
> Das lese ich im Datenblatt anders.

Stimmt, beim ATmega8 ging Power-save nur im asynchron Mode.
Der synchrone Mode dürfte vom Stromverbrauch her ähnlich dem Idle sein.

von Thomas E. (thomase)


Lesenswert?

Peter D. schrieb:
> Stimmt

Stimmt?

Dazu müsste aber CPU-Takt laufen. Der wird aber im Power Save 
abgeschaltet.

von S. Landolt (Gast)


Angehängte Dateien:

Lesenswert?

an Thomas Eckmann
Warum sollte der Synchron-Modus nicht gehen?

von Peter D. (peda)


Lesenswert?

Ich würd mal das ganze Init-Zeugs aus der Loop nehmen:
1
void setup( void )
2
{
3
  pinMode(ledPin,OUTPUT);
4
  digitalWrite(ledPin,LOW);
5
  TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20); // Prescaler 1024
6
  TIMSK0 = 0; // no other Interrupts
7
  TIMSK1 = 0; // no other Interrupts
8
  TIMSK2 = (1<<TOIE2);
9
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
10
  sleep_enable();
11
  sei();
12
}
13
 
14
void loop( void )
15
{
16
  for (int i=0; i<1000; i++)
17
  {
18
    sleep_cpu();
19
  }
20
  digitalWrite(ledPin, HIGH);
21
  delay(5000);
22
  digitalWrite(ledPin, LOW);
23
}
24
25
ISR(TIMER2_OVF_vect)
26
{  
27
}

von Thomas E. (thomase)


Lesenswert?

S. Landolt schrieb:
> an Thomas Eckmann
> Warum sollte der Synchron-Modus nicht gehen?

Ja, ihr habt recht.

von S. Landolt (Gast)


Lesenswert?

Könnte es sein, dass die Arduino-Umgebung mit einem eigenen 
Timer-Interrupt das sleep_cpu ständig abbricht?

von S. Landolt (Gast)


Lesenswert?

> genauso lang an, wie aus ist. Also etwa 1 Sekunde.
Bei einer Schleife mit 1000 Durchläufen.

  Und irgendwo stand mal, "In Arduino, Timer0 is already set up to 
generate a millisecond interrupt to update the millisecond counter for 
millis()" oder so ähnlich.
  So würde also das sleep-cpu nach jeweils 1 ms unterbrochen werden 
zusätzlich zu den berechneten 16.384 ms = 256*1024/16000000 ms von 
Timer2.

von Peter D. (peda)


Lesenswert?

S. Landolt schrieb:
> zusätzlich zu den berechneten 16.384 ms = 256*1024/16000000 ms von
> Timer2.

Da er T2 nach jedem Sleep rücksetzt, würde der nie ablaufen. Es sind 
also ausschließlich die T0 Interrupts.

von S. Landolt (Gast)


Lesenswert?

Stimmt, wenn es sich denn überhaupt insgesamt so verhält. Können Sie 
dazu etwas sagen? Ich selbst kenne Arduino nur vom Hörensagen.

von Carl D. (jcw2)


Lesenswert?

Peter D. schrieb:
> S. Landolt schrieb:
>> zusätzlich zu den berechneten 16.384 ms = 256*1024/16000000 ms von
>> Timer2.
>
> Da er T2 nach jedem Sleep rücksetzt, würde der nie ablaufen. Es sind
> also ausschließlich die T0 Interrupts.

In "PowerSave" läuft der Timer0 aber gar nicht!
Atmel dazu: "2.If Timer/Counter2 is running in asynchronous mode"

Vermutlich schläft der auch nicht "PowerSave", weil die Arduino-RT den 
Wert von der Initialisierung nicht stehen läst. Die möchte nämlich 
Timer0 sehen.

Dann paßt auch das Verhalten:
LED an, 1000x1ms Schlafen,
LED aus, 1s delay

Ich kenne sleep eher als:
Set sleep mode
Enable sleep
Sleep
Disable sleep

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Carl D. schrieb:
> In "PowerSave" läuft der Timer0 aber gar nicht!

Wie ja schon festgestellt wurde, kann im Gegensatz zum ATmega8 beim 
ATmega328 der T2 im Power-Save auch synchron laufen.
Das impliziert, daß der CPU-Taktgeber und CPU-Prescaler aktiv bleiben 
müssen.
Grob gesagt mutiert das Power-Save bei synchronem T2 zum Idle.
Das Datenblatt schweigt sich allerdings zu Details aus. Und probiert hat 
das warscheinlich auch noch niemand.

von S. Landolt (Gast)


Lesenswert?

Also ich messe hier
3.6 mA bei idle
2.5 mA bei power-save
bei 5.0 V und 16 MHz.

von S. Landolt (Gast)


Lesenswert?

Vergessen zu schreiben:
Wie Carl Drexler betonte, läuft Timer0 in power-save tatsächlich nicht. 
Bleibt also die Frage, was Arduino aus dem Ganzen macht.

von Einer K. (Gast)


Lesenswert?

S. Landolt schrieb:
> Bleibt also die Frage, was Arduino aus dem Ganzen macht.
Gar nix!
Interessiert sich nicht dafür.
Und das ist auch gut so!

von S. Landolt (Gast)


Lesenswert?

> Gar nix!
> Interessiert sich nicht dafür.
Wie erklärt sich dann aber das eingangs beschriebene Verhalten?

von Einer K. (Gast)


Lesenswert?

S. Landolt schrieb:
> Wie erklärt sich dann aber das eingangs beschriebene Verhalten?

Wie sich das erklärt?
KA!
Interessiert mich auch nicht wirklich.

Natürlich löst Timer0, in der Arduino Welt, dauernd Interrupts aus.
Den Timer sollte man in den leichten Schlafmodi schon stilllegen.
Ansonsten, kann ich nur sagen, dass meine Arduinos nicht einfach so 
aufwachen.

Von der Arduino IDE werden typischer weise folgende ISR etabliert:
1. Timer0 (wenn setup() und loop() vorhanden)
2. UART (wenn setup() und loop() vorhanden)
3. I2C (wenn Wire genutzt wird)

Solange noch SendeDaten in der UART FIFO stecken ....(kann man sich ja 
denken)


Der Code im Eingangspost ist unvollständig. Und damit untestbar. 
Irgendwie macht sowas auf mich immer den Eindruck, als wolle man gar 
nicht, das man das Problem findet und löst.
Vermutlich wurde mal wieder vergessen das Wichtigste zu zeigen...

Wenn ich sowas lese, dann blättere ich einfach weiter:
> TIMSK2 |= (1<<TOIE0);
OK, mag sein, dass das funktioniert, aber falsch ist es trotzdem.


Der Code von  Multi __ macht sei(); . Das ist flüssiger, als Wasser, 
überflüssig. Den Rest habe ich mir dann gar nicht mehr angesehen.


Peter Dannegger vergisst den Clear des SE Bit nach dem sleep.
Habe ich noch nicht getestet, ob das dann funktioniert.
Meine Erinnerung sagt aber, dass man das unbedingt tun sollte.

Das Gcc Manual sagt, dass es so auszusehen hat:
1
#include <avr/interrupt.h>
2
#include <avr/sleep.h>
3
...
4
  set_sleep_mode(<mode>);
5
  cli();
6
  if (some_condition)
7
  {
8
    sleep_enable();
9
    sei();
10
    sleep_cpu();
11
    sleep_disable();
12
  }
13
  sei();
Ich sehe nicht ein, warum man davon abweichen sollte....
Freiwillig, ohne Not...
Es sei denn, es wurde die Doku nicht gelesen..


-----------

Arduino sagt dazu: 
http://playground.arduino.cc/Learning/ArduinoSleepCode

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.