ATmega328p timer2 sleep mode zu kurz

OP #4869156
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?
#4869245
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.
Gast #4869525
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.
#4869569
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?
OP #4869575
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
}
#4869604
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
}
Gast #4870466
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.
#4871622
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
#4871645
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.
Gast #4871843
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren