Fragen zum Sleep Mode

#7874781
Lesenswert?

Hallo liebe Foren Community,

ich beschäftige mich gerade etwas mit mit einem AT90S2313 auf einem aTeVaL Board und mir ist bewusst, dass der uC outdated ist. Ich habe mir bereits das AVR-GCC Tutorial hier angeschaut, als auch aus anderen Quellen Informationen gezogen. Dennoch bin ich etwas durcheinander.

Der AT90S2313 hat zwei Sleep Modi, den IDLE und Power-down Mode, soweit sogut. Um den, sagen wir mal Power-down Mode zu aktivieren, muss ich laut Datenblatt (Datei angehängt) die SE (Sleep enable) und SM (Sleep mode) im MCUCR (MCU Control Register) auf 1 setzen, wobei hierauf zu achten ist, dass der SE erst kurz vor dem Sleep mode ausgeführt werden soll.

Laut meinem laienhaften Verständnis, müsste der Code also folgendermaßen aussehen:

1
int main (void) {
2

3
  DDRD = 0b01100000;      // Output = 1; Input = 0
4
  MCUCR = 0b00010000;      // Sleep mode: power down
5

6
  while (1) {
7
    LED_RD_ON;        // Debugging
8
    _delay_ms(1000);
9
    LED_RD_OFF;
10
    _delay_ms(1000);
11
    
12
    MCUCR |= (1 << SE);    // Enable sleep mode
13
    sleep_mode();      // Activate sleep mode
14
  }
15
  return 0;
16
}

Jetzt lese ich aber im AVR-GCC Tutorial man müsse...

1
// set sleep mode
2
set_sleep_mode(SLEEP_MODE_PWR_DOWN);

...setzen. Hier nun die eigentliche Frage: Habe ich das denn nicht bereits durch das setzen der Bits "eingestellt"?

[edit] Ich glaube, ich habe es selbst herausgefunden. Laut Library Reference setzt die Funktion sleep_mode(): This macro automatically sets the sleep enable bit, goes to sleep, and clears the sleep enable bit. Und dann wird wohl durch die Funktion set_sleep_mode(<mode>) in dem Fall das SM Bit gesetzt, richtig? [/edit]

Angehängte Dateien:
Moderator Persönliche Seite #7874860
Lesenswert?

Beides sind verschiedene Dinge.

set_sleep_mode() legt fest, in welchen der mögliche Sleep-Modi (bei dir sind es nur zwei, bei anderen MCUs mehr) das Teil schlafen gehen soll, wenn man das nächste Mal sleep_mode() aufruft.

sleep_mode() kümmert sich wiederum um die Sequenz, die es benötigt, um tatsächlich die CPU in den Schlaf zu versetzen.

Wenn du kein set_sleep_mode() aufrufst, dann ist der default sleep mode "IDLE", d.h. die CPU legt sich zwar schlafen, aber alle Takte laufen weiter.

Der Vorteil von set_sleep_mode() gegenüber dem manuellen "Herumhacken" auf MCUCR (welches bei anderen MCUs anders heißen kann) ist halt, dass

1
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);

deutlich besser ausdrückt, was beabsichtigt ist, als

1
  MCUCR = 0b00010000;
#7874914
Lesenswert?

Eine Frage noch bzgl. Sleep Mode, besser gesagt dem dazugehörigen External Interrupt.

Wie im Eingangspost erwähnt, nutze ich einen AT90S2313. Weil mir das jetzt nach Stunden zu bunt wurde, habe ich einen ATmega328P eingesetzt und mit diesem Programm instant die gewünschte Funktion zum Laufen gebracht.

Mit Taste an PD2 aktiviere ich den Power Down. Angezeigt wird mir das duch eine LED an PD6. Mit dem aktivierten External Interrupt an PD3 wird der uC wieder aufgeweckt.

1
ISR(INT1_vect){}
2

3

4
int main(void)
5
{
6
  DDRD = 0b01100000;    // Output = 1; Input = 0
7
  PORTD = 0b00011100;    // Enable internal pull-up
8

9
  while(1)
10
  { 
11
    if(BUTTON_1_PRESSED)
12
    {
13
      // Debugging
14
      LED_RD_ON;
15
      _delay_ms(1000);
16
      LED_RD_OFF;
17

18
      // Pin change interrupt setup
19
      cli();                    // Disable interrupt for programming
20
//      PCICR |= (1<<PCIE2);            // 
21
//      PCMSK2 |= (1 << PD3);            // Turn on pin PB4, which is PCINT4
22
EICRA |= (1 << ISC11);
23
EICRA &= ~(1 << ISC10);
24
EIMSK |= (1 << INT1);
25
      sei();                    // Enable interrupt
26

27
      // Activate sleep mode
28
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // Sleep mode: Power down
29
      sleep_mode();              // Activate sleep mode  
30
    }
31
  }
32
  return 0;
33
}

Anschließend habe ich die Register EICRA -> MCUCR und EIMSK -> GIMSK an den AT90S2313 angepasst, aber es funktioniert trotzdem nicht.

Was ist daran bitte falsch?

1
ISR(INT1_vect)
2
{
3
}
4

5

6
int main(void)
7
{
8
  DDRD = 0b01100000;    // Output = 1; Input = 0
9
  PORTD = 0b00011100;    // Enable internal pull-up
10

11
  while(1)
12
  {
13
    if(BUTTON_1_PRESSED)
14
    {
15
      // Debugging
16
      LED_RD_ON;
17
      _delay_ms(1000);
18
      LED_RD_OFF;
19

20
      // Interrupt setup
21
      cli();                    // Disable interrupt for programming
22
      MCUCR |= (1 << ISC11);
23
      MCUCR &= ~(1 << ISC10);
24
      GIMSK |= (1 << INT1);
25
      sei();                    // Enable interrupt
26

27
      // Activate sleep mode
28
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // Sleep mode: Power down
29
      sleep_mode();              // Activate sleep mode
30
    }
31
  }
32
  return 0;
33
}
Moderator Persönliche Seite #7874985
Lesenswert?

Mirko schrieb:

Was ist daran bitte falsch?

Wenn ich deinen Code richtig deute, willst du auf eine fallende Flanke des Externinterrupts reagieren.

Damit kannst du die CPU nicht aufwecken.

"Only an External Reset, a Watchdog Reset (if enabled), an external level interrupt on INT0 or INT1 can wake up the MCU."

Die Betonung liegt auf external level interrupt.

Aufwachen per Pin-Change-Interrupt ging erst zwei AVR-Generationen später.

#7874992
Lesenswert?

Jörg W. schrieb:

Mirko schrieb:

Was ist daran bitte falsch?

Wenn ich deinen Code richtig deute, willst du auf eine fallende Flanke des Externinterrupts reagieren.

Korrekt.

Damit kannst du die CPU nicht aufwecken. "Only an External Reset, a Watchdog Reset (if enabled), an external level interrupt on INT0 or INT1 can wake up the MCU." Die Betonung liegt auf external level interrupt. Aufwachen per Pin-Change-Interrupt ging erst zwei AVR-Generationen später.

Das im ATmega328 Programm noch Pinchange steht ist falsch. Ich habe das nur als Vorlage genommen und die Register dem External Interrupt angepasst. Die Pinchange Register sind auch auskommentiert.

External Level Interrupt... das ist doch ein External Interrupt mit einer sich ändernden (Falling/Rising) Flanke/Level?

Moderator Persönliche Seite #7874997
Lesenswert?

Mirko schrieb:

External Level Interrupt... das ist doch ein External Interrupt mit einer sich ändernden (Falling/Rising) Flanke/Level?

Nein, eben nicht. Ein pegelgesteuerter Interrupt, kein flankengetriggerter. Ist übrigens die Voreinstellung im MCUCR beim AT90S2313.

Außerdem steht noch da, dass der (Low-)Pegel lange genug anliegen muss, bis der Oszillator und die CPU gestartet sind. Aber das sollte bei einem mit der Hand betätigten Taster kein Problem sein.

Hintergrund ist einfach, dass die Flankenerkennung einen Takt benötigt, den du im power-down mode nicht hast.

Moderator Persönliche Seite #7875002
Lesenswert?

Peter D. schrieb:

Ja, das ist der Unterschied zum ATmega328P.

So halb: ein „normaler“ Externinterrupt muss auch dort low-level sein zum Aufwachen. Allerdings gehört er eben besagter übernächster Generation AVRs an, die einen pin-change interrupt haben (der separat vom normalen Externinterrupt ist), und dieser kann ebenfalls die MCU aus dem power-down aufwecken.

Moderator Persönliche Seite #7875010
Lesenswert?

Peter D. schrieb:

Jörg W. schrieb:

So halb: ein „normaler“ Externinterrupt muss auch dort low-level sein zum Aufwachen.

Nö.

"Only an External Reset, a Watchdog System Reset, a Watchdog Interrupt, a Brown-out Reset, a 2-wire Serial Interface address match, an external level interrupt on INT0 or INT1, or a pin change interrupt can wake up the MCU."

INT0/INT1 also nach wie vor level-only.

(Datenblatt 10.5 Power-down Mode)

Moderator Persönliche Seite #7875013
Lesenswert?

PS: Ab AVR0 hat sich da wirklich was getan, aber wie ich gerade feststellen musste, war ich auch hier für ein aktuelles Projekt geringfügig zu blauäugig und hätte besser vorher ins Datenblatt schauen müssen. Dort wird zwischen "fully asynchronous pins" und "partially asynchronous pins" unterschieden. Letztere können allerdings zusätzlich zum level-triggered interrupt jetzt auch mit (dem neu hinzu gekommenen) "both edges" Interrupt aufwachen.

Moderator Persönliche Seite #7875022
Lesenswert?

Mirko schrieb:

Und wie kann ich diesen pegelgesteuerten Externen Level Interrupt "nutzen"?

Indem du ihn konfigurierst (d.h. die entsprechenden Bits in MCUCR auf 0 lässt) und dann auslöst. Am einfachsten also sicher, indem du den internen Pullup benutzt und den Pin mit dem Taster auf Low ziehst.

Du musst dann aber auch in der ISR gucken, dass du ihn abschaltest und erst nach einer Wartezeit wieder aktiviert, und nachdem der Pin wieder "high" ist.

#7875024
Lesenswert?

Jörg W. schrieb:

Indem du ihn konfigurierst (d.h. die entsprechenden Bits in MCUCR auf 0 lässt) und dann auslöst. Am einfachsten also sicher, indem du den internen Pullup benutzt und den Pin mit dem Taster auf Low ziehst.

Wo steht das, mit den Voreinstellungen? Ich finde es nicht :X

[edit] Hab es in der Tabelle 5 selbst gefunden... [/edit]

Du musst dann aber auch in der ISR gucken, dass du ihn abschaltest und erst nach einer Wartezeit wieder aktiviert, und nachdem der Pin wieder "high" ist.

Hast du vielleicht ein Beispiel?

Moderator Persönliche Seite #7875031
Lesenswert?

Mirko schrieb:

Du musst dann aber auch in der ISR gucken, dass du ihn abschaltest und erst nach einer Wartezeit wieder aktiviert, und nachdem der Pin wieder "high" ist.

Hast du vielleicht ein Beispiel?

Auf Anhieb nicht.

In der ISR musst du das Bit in GIMSK zurück nehmen. Bevor du es wieder setzt, musst du warten, bis der Pin (ausreichend lange) "high" war.

Ansonsten rennt deine ISR einfach nur Kreise, solange der low-Pegel anliegt.

#7875085
Lesenswert?

Jörg W. schrieb:

In der ISR musst du das Bit in GIMSK zurück nehmen. Bevor du es wieder setzt, musst du warten, bis der Pin (ausreichend lange) "high" war.

Ansonsten rennt deine ISR einfach nur Kreise, solange der low-Pegel anliegt.

Jep. Das ist eine Falle, in die viele Anfänger tappen. Ein levelgesteuerter Interrupt feuert, solange der aktive Level beibehalten wird. Man sieht das aber nicht gleich, weil Atmel (gerade deswegen) beim Einsprung in die ISR (jede ISR) erstmal jegliche Interrupts sperrt. Sonst würde sich die ISR nämlich gleich wieder selber unterbrechen und bei 1MHz+ CPU-Takt wäre der Stack ratzfatz übergelaufen.

#7875088
Lesenswert?

Jörg W. schrieb:

"Only an External Reset, a Watchdog System Reset, a Watchdog Interrupt, a Brown-out Reset, a 2-wire Serial Interface address match, an external level interrupt on INT0 or INT1, or a pin change interrupt can wake up the MCU."

Aber nur beim AT90S2313, ATTiny2313A.

Beim ATmega328P fehlt diese Einschränkung und deshalb funktioniert der Code dort. Siehe obige Table 10-1 aus dem Datenblatt.

Moderator Persönliche Seite #7875095
Lesenswert?

Peter D. schrieb:

Siehe obige Table 10-1 aus dem Datenblatt.

Siehe mein Zitat aus dem Datenblatt des ATmega328P. Dem trau ich mehr Detailtreue zu als einer zusammenfassenden Tabelle.

Die Tabelle besagt ja lediglich, dass INT0, INT1 und Pin-Change-Interrupts überhaupt die CPU aufwecken – aber nicht, unter welchen Bedingungen.

Dass ein INT0 oder INT1 die CPU aufwecken kann, ist wiederum auch beim AT90S2313 der Fall.

#7875109
Lesenswert?

Jörg W. schrieb:

Die Tabelle besagt ja lediglich, dass INT0, INT1 und Pin-Change-Interrupts überhaupt die CPU aufwecken – aber nicht, unter welchen Bedingungen.

Die Tabelle ist da eindeutig, X bei Power-Down. Außerdem wir das nochmal explizit erklärt: "13. External Interrupts Low level interrupts and the edge interrupt on INT2:0 are detected asynchronously. This implies that these interrupts can be used for waking the part also from sleep modes other than Idle mode."

Und sein ATmega328P Code funktioniert ja mit falling edge.

Moderator Persönliche Seite #7875126
Lesenswert?

Peter D. schrieb:

Die Tabelle ist da eindeutig, X bei Power-Down.

Nö, das ist nicht eindeutig, denn dass ein Externinterrupt aufwecken kann, war schon immer so.

Außerdem wir das nochmal explizit erklärt: "13. External Interrupts Low level interrupts and the edge interrupt on INT2:0 are detected asynchronously. This implies that these interrupts can be used for waking the part also from sleep modes other than Idle mode."

Das wiederum akzeptiere ich – allerdings steht es im Widerspruch zu Abschnitt 10.5 des gleichen Datenblatts, aus dem ich oben zitiert habe. Interessant, dass solche Fehler noch nach so vielen Jahren da drin sind …

Moderator Persönliche Seite #7875168
Lesenswert?

Mi N. schrieb:

Unbedingt

Naja. Nein, nicht unbedingt.

Wenn man den EEPROM benutzt, könnte es wohl maximal passieren, dass statt der gewünschten Zelle die von Adresse 0 zerschossen wird, wenn während des Schreibens die Spannung wegbricht.

Oder worauf wolltest du hinaus?

Da das Ganze aber (so wie ich es verstehe) eh nur ein Spiel- und Bastel-Board ist, dürfte das alles gar keine Rolle spielen.

#7875214
Lesenswert?

Die ersten AVRs (AT90S) hatten keine Spannungsüberwachung, sondern nur ein einfaches Zeitglied als Reset. Als ich das bei einer Produktvorstellung gegenüber Haakon Skar bemängelte, hat er nur abgewinkt. Ich habe daraufhin mein AVR-Projekt beendet und erst weiter mit den AVRs gebastelt, als dann doch das Brownout-Reset implementiert wurde. Die ersten AVRs hatten massive Probleme, die Fusebits richtig einzulesen, wenn die VCC nicht schnell und monoton von 0V anstieg. Da half dann auch kein externer Reset-IC, wenn statt des internen Taktes auf eine externe Taktquelle gewartet wurde.

Moderator Persönliche Seite #7875231
Lesenswert?

Peter D. schrieb:

Die ersten AVRs hatten massive Probleme, die Fusebits richtig einzulesen, wenn die VCC nicht schnell und monoton von 0V anstieg.

Klar, aber entgegen obiger Behauptung löschen sie sich nicht EEPROM oder Flash. Außerdem hat man gerade bei einem Bastel-Board das ja in der Hand, dass der Spannungsanstieg schnell genug ist.

In irgendeinem Seriengerät wiederum wird wohl heute keiner mehr einen AT90S2313 einsetzen.

Moderator Persönliche Seite #7875266
Lesenswert?

Mi N. schrieb:

Jörg W. schrieb:

Klar, aber entgegen obiger Behauptung löschen sie sich nicht EEPROM oder Flash.

Gut, dann formuliere ich anders: Sie funktionieren nicht mehr ;-) Woran das wohl liegen mag?

Bleibt trotzdem eine zumindest missverständliche Behauptung: sie funktionieren ja nur bei zu langsamem Spannungsanstieg unter den gegebenen Umständen nicht. Wenn du sie danach "normal" betreibst, funktionieren sie.

Für irgendeine Bastelplatine halte ich ein separates Reset-IC für komplett überflüssig, trotz der genannten potenziellen Probleme. (Ich habe auch selbst bei keinem AVR je eins benutzt, nichtmal bei den ersten AT90S1200 damals.)

#7875267
Lesenswert?

@Jörg W. Vielen Dank für die hilfreichen Hinweise. Damit habe ich glaube ich nun das Problem lösen können.

Das Programm verhält sich wie gewünscht... Wie vorhin bereits beschrieben, mit Taste 1 PD2 geht die LED PD6 für 1s an, danach verfällt der uC in den Power-Down Sleep Mode. Eine weitere Betätigung von Taste 1 bringt nichts mehr. Erst wenn ich Taste 2 PD3 drücke, kann ich mit PD2 wieder die LED/Sleep Mode aktivieren.

Wäre es noch möglich wenn vielleicht jemand den Code so überblicken könnte, ob etwas falsch ist oder ergänzt werden sollte? Ich war mir nicht sicher z.B., ob es sinnvoll ist, den Interrupt 1 Sense Control Low level aktiv auf 0 zu setzen, oder lieber einfach wegzulassen. Wobei dann vieleicht nicht klar ist was gewünscht ist... wenn ihr mich versteht, so zum Codeverständnis wenn man sich das mal später wieder durchlesen sollte.

Diesmal auch den kompletten Code zum compilieren @peda. Btw, ich wußte gar nicht, dass du DER Peter D. bist... jetzt weiß ich es ^^

1
#ifndef F_CPU
2
#define F_CPU 4000000UL
3
#endif
4

5

6
#include <avr/io.h>
7
#include <util/delay.h>
8
#include <avr/sleep.h>
9
#include <avr/interrupt.h>
10

11

12
#define BUTTON_1_PRESSED  (!(PIND & (1 << PD2)))    // PD2 - Button pressed
13
#define BUTTON_2_PRESSED  (!(PIND & (1 << PD3)))    // PD3 - Button pressed
14
#define BUTTON_3_PRESSED  (!(PIND & (1 << PD4)))    // PD4 - Button pressed
15
#define LED_GN_ON      (PORTD |= (1 << PD5))    // PD5 - LED green on
16
#define LED_GN_OFF      (PORTD &= ~(1 << PD5))    // PD5 - LED green off
17
#define LED_RD_ON      (PORTD |= (1 << PD6))    // PD6 - LED red on
18
#define LED_RD_OFF      (PORTD &= ~(1 << PD6))    // PD6 - LED red off
19

20

21
ISR(INT1_vect)
22
{
23
}
24

25

26
int main(void)
27
{
28
  DDRD = 0b01100000;    // Output = 1; Input = 0
29
  PORTD = 0b00011100;    // Enable internal pull-up
30

31
  while(1)
32
  {
33
    if(BUTTON_1_PRESSED)
34
    {
35
      // Debugging
36
      LED_RD_ON;
37
      _delay_ms(1000);
38
      LED_RD_OFF;
39

40
      // External interrupt setup
41
      cli();                    // Disable status register global interrupt - for programming
42
      MCUCR &= ~(1 << ISC11) | (1 << ISC10);    // Interrupt 1 sense control Low level 
43
      GIMSK |= (1 << INT1);            // Enable external interrupt request 1
44
      sei();                    // Enable status register global interrupt
45

46
      // Activate sleep mode
47
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // Sleep mode: Power down
48
      sleep_mode();              // Activate sleep mode
49
    }
50
  }
51
  return 0;
52
}
#7875277
Lesenswert?

Jörg W. schrieb:

Gut, dann formuliere ich anders: Sie funktionieren nicht mehr ;-) Woran das wohl liegen mag?

Bleibt trotzdem eine zumindest missverständliche Behauptung: sie funktionieren ja nur bei zu langsamem Spannungsanstieg unter den gegebenen Umständen nicht. Wenn du sie danach "normal" betreibst, funktionieren sie.

Nein, wenn sie einmal nicht mehr liefen, liefen sie anschließend erst wieder nach Neuprogrammierung. Wenn ich dem TO einen Reset-Baustein empfehle, dann in der Hoffnung, daß er diese Erfahrung nicht selber machen muß und an völlig anderen Stellen nach einem Fehler sucht. Ein ähnlicher 'Pferdefuß' war UART mit internem RC-Takt.

Moderator Persönliche Seite #7875286
Lesenswert?

Mi N. schrieb:

Nein, wenn sie einmal nicht mehr liefen, liefen sie anschließend erst wieder nach Neuprogrammierung.

Habe ich nie erlebt. Verpfuschte Fuses bei missglückten Programmierversuchen waren was anderes, aber da hat man ja vorsätzlich die Programmierung eingeleitet.

Ein ähnlicher 'Pferdefuß' war UART mit internem RC-Takt.

Der AT90S2313 hat weder UART noch internen RC-Oszillator.

Moderator Persönliche Seite #7875606
Lesenswert?

Peter D. schrieb:

Jörg W. schrieb:

Der AT90S2313 hat weder UART noch internen RC-Oszillator.

  • Full Duplex UART

OK, sorry, hätte ich nochmal nachlesen sollen. ;-) Aber RC-Oszillator hat er nicht, hatte ich nachgelesen … Damals musste man noch mindestens einen Resonator dranbammeln, weshalb auch die ersten Lowcost-Programmer sowas an Bord hatten.

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