www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ATmega8 und Sleep


Autor: Christian Rötzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich versuche gerade für den ATmega8 eine Routine zu schreiben, die den
AVR in den Sleep-Modus schickt. Über den INT0 soll per Levelinterrupt
geweckt werden. Das funktioniert soweit auch alles. Es gibt da nur ein
Problem:
Der Interrupt wird ausgelöst, wenn INT0 auf 0-Pegel geht. Bevor man nun
die Sleep-Anweisung losschickt, muß man den globalen Interrupt
aktivieren. Dieser Interrupt deaktiviert nach aufwachen unmittelbar
wieder den INT0-Interrupt. Wenn nun der Pegel aber schon vor Aufruf der
Sleep-Anweisung auf 0 geht, wird demnach vor Aufruf der
Sleep-Anweisung der Interrupt ausgelöst, dann die Sleep-Anweisung
ausgeführt und somit kann man den AVR gar nicht mehr wecken. Wie ist
denn hier die anerkannte Vorgehensweise?

Grüße

Christian

Autor: mr.chip (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Ich würde es so machen:

INT0:
 WENN flag nicht gesetzt
   PIN = 1
 SONST
   tue etwas...


MAIN:
  flag = 0
  interrupt aktivieren
  sleep
  flag = 1


Alternativ könnte man auch die Flankenwechsel auswerten anstatt ein
bestimmter Pegel.

Autor: Christian Rötzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was meinst Du mit PIN = 1? Evtl.:

WENN flag nicht gesetzt
  tue nix
SONST
  tue etwas...

Quasi ein Sub-Interrupt-Enable. Hm, das könnte gehen... Ich habe jetzt
gerade mal ausprobiert, im Interrupt nichts zu tun, und unmittelbar
nach der Sleep-Anweisung den Interrupt wieder auszuschalten:

// Global interrupt enable
sei();
// Now, goto sleep
sleep_mode();
// Immediately turn off interrupts
cli();

Ich programmiere in (GC)C und daher vergeht zwischen dem Aufwachen und
dem Cli soviel Zeit, dass der Interrupt (wegen Level-Interrupt, was
anderes geht nicht) viermal auslöst.
Wahrscheinlich werde ich einfach damit leben...

Autor: TravelRec. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alternative: in ASM programmieren... ;-) Das deaktivieren des Int0 würde
reichen, dazu muß vor dem Sleep aber auch das zugehörige Flag durch
Schreiben einer "1" gelöscht werden, damit der Prozi überhaupt erst
einschläft und nicht gleich wieder in den Interrupt läuft, weil die
Int0-Leitung zwischendurch mal "gewackelt" hat.

Autor: Christian Rötzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tja, da kann ich Dir nur recht geben. Eine wirklich saubere Lösung
scheint's aber nicht zu geben. Eine schweinische hätte ich noch: Im
Zuge der Abarbeitung des INT0-Interrupts nachsehen, ob die
Rücksprungaddresse schon "hinter" den sleep-Befehl zeigt. Nur in
diesem Fall disablet sich der INT0 selber. Wie gesagt, schweinisch,
sowas würde ich nieeeeee programmieren :-)

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alle meine Programme nutzen einen Timer-Interrupt.
Fast alle meiner Programme gehen in der Mainloop in den Sleep-Zustand
und werden vom Timer-Int wieder geweckt. Dazu ist Sleep-Mode 'Idle'
erforderlich, damit die Timer weiter laufen können.

Soll der Controller nun in der 'Tiefschlaf' (Power-Down-Mode)
versetzt werden, dann ist der Low-Level-Interrupt zum Wecken scharf zu
machen (vorher das Interrupt-Flag in GIFR löschen, indem man das Bit
setzt) und der Sleepmode auf Power-Down umzuschalten:
tiefschlaf:         ;AVR in Standby-Betrieb schalten um Strom zu
sparen
 ldi wl,down            ;Sleep auf Power-Down
 out mcucr,wl           ;umschalten (Strom sparen)
 ldi wl,1<<int0         ;
 out gifr,wl            ;Flag vom vorhergehenden Ext-Int löschen
 out gimsk,wl           ;externen Interrupt einschalten
 out tccr1,null         ;Timer1 deaktivieren
 ldi wl,taste           ;PullUp nur für Interrupt-Eingang
 out tap+2,wl           ;aktivieren und Lautsprecher auf L-Pegel
 rjmp mainloop1         ;schlafen legen...
Die Mainloop schickt den AVR dann statt in den Halbschlaf (Idle) in den
Tiefschlaf zum Strom sparen:
mainloop:           ;Hauptschleife
 sbrc tfl,klintas       ;Klingel-Taster betätigt gewesen? - nein...
 rjmp start             ;ja, abarbeiten...
 sbrc tfl,stoptas       ;Stop-Taster betätigt gewesen? - nein...
 rjmp stop              ;ja, abarbeiten...
 sbrc tfl,busy          ;Soundausgabe aktiv? nein...
 rjmp mainloop2         ;ja...
 dec aus                ;Timeout runterzählen
 breq tiefschlaf        ;AVR ausschalten...
mainloop1:
 sleep                  ;bis zum nächsten Interrupt schlafen
 rjmp mainloop          ;nochmal
Ein Low-Level am ext-Int löst den Interrupt aus. In dieser ISR wird der
Sleepmode sofort auf 'Idle' zurückgestellt und der externe Interrupt
deaktiviert, er soll schließlich nur einmal pro Wecken auslösen:
EXT_INT0:           ;ISR externer Interrupt
 ldi wl,idle            ;Sleep auf IDLE
 out mcucr,wl           ;umschalten
 out gimsk,null         ;ext.Int ausschalten
 ldi wl,tasten          ;PullUps für alle Tasten
 out tap+2,wl           ;aktivieren
 clr num                ;Soundnummer voreinstellen
 reti                   ;fertig...
Die gezeigten Fragmente stammen aus einem Programm für Tiny15, das
einen Türgong mit mehreren Melodien realisiert.

...

Autor: Christian Rötzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, nicht schlecht, aaaaber: Was ist denn, wenn INT0 schon auf 0 liegt,
bevor sleep aufgerufen wird? Praktisch in Listing "tiefschlaf" ab
Zeile 8...

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Wenn nun der Pegel aber schon vor Aufruf der Sleep-Anweisung auf 0
geht, wird demnach vor Aufruf der Sleep-Anweisung der Interrupt
ausgelöst."

Ganz so dusselig war Atmel nicht. SEI aktiviert Interrupts erst nachdem
der nächste Befehl gestartet wurde. Somit ist die Folge
  SEI
  SLEEP
narrensicher, weil zwischen SEI und SLEEP kein Interrupt reinpasst.

Geht auch in C, dafür empfiehlt sich allerdings die neueste WinAVR
Version.

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ja, nicht schlecht, aaaaber: Was ist denn, wenn INT0 schon auf 0
> liegt,
> bevor sleep aufgerufen wird?

Das kommt im Normalfall nicht vor. Und wenn es vorkommt, dann wird der
AVR wieder geweckt und geht wieder in den Normalbetrieb, fragt also in
der Timer-ISR die Taster ab und tut seine Arbeit. Erst wenn der
Timeout-Zähler abgelaufen ist (nach 256 Timer-Interrupts) wird dann
wieder in den Tiefschlaf geschaltet. Wurde in dieser Zeit aber der
Starttaster erneut betätigt, dann startet das Programm einen neuen
Ausgabezyklus, in dem bei jedem Timer-Interrupt der Timeout-Zähler
gelöscht wird, so dass dieser während der Ausgabe nicht ablaufen kann.
Daher kann der Int0 beim Ablaufen des Timeouts garnicht low sein.

...

Autor: Christian Rötzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.
Wenn das stimmt, was Du sagst ist's in der Tat kein Problem. Ich kann
die Stelle im AVR Instruction Set aber nicht finden. Das müßte ich
glatt ausprobieren!

@HanneS
<Prinzipienreiter on>
Wenn es doch vorkommt, wird Dein INT0-Interrupt weitere INT0-Interrupts
ausschalten. Dann wird sleep ausgeführt. Der AVR wird dann nicht mehr
aufwachen.
<Prinzipienreiter off>

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So schwer zu finden? Aus der AVR Instruction Set Reference (ja, die gibt
es! ~150 Seiten), über SEI:

"Sets the Global Interrupt Flag (I) in SREG (Status Register). The
instruction following SEI will be executed before any pending
interrupts."

Autor: Christian Rötzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie gesagt: "Ich kann die Stelle im AVR Instruction Set nicht finden".
Damit wollte ich sagen, dass ich ins AVR Instruction Set geschaut habe.
Es mag daran liegen, dass meines nur 133 Seiten aufweist.
<Bin mal kurz bei Atmel>
Aha. 150 Seiten. Na also, da steht's ja! Mit Beispiel! Bezieht sich
exakt auf meine Situation :-) Damit erkläre ich die Sache für
hinreichend geklärt. Danke an alle!

Grüße

Christian

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wenn es doch vorkommt, wird Dein INT0-Interrupt weitere INT0-
> Interrupts
> ausschalten. Dann wird sleep ausgeführt. Der AVR wird dann nicht
> mehr
> aufwachen.

Das sehe ich anders.

- Vor Einschalten des ext-Int wird das Interrupt-Flag gelöscht.
  Somit kann ein alter Int nicht greifen.
- Sleep wird am Ende der Mainloop aufgerufen, und zwar immer, auch
  wenn der AVR arbeitet, nur ist dann der Mode 'Idle' ausgewählt.
- Wird direkt vor Sleep der ext-Int ausgelöst, dann wird noch inner-
  halb der ISR der Sleepmode auf 'idle' umgeschaltet, worauf der
  Timer seinen Takt bekommt und den AVR wecken kann. Es herrscht
  also Normalbetrieb.
- Wie um alles in der Welt soll der von dir befürchtete Zustand
  eintreten???

...

Autor: Christian Rötzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann seh ich's genauso! Der Timer tritt dem AVR quasi in den Hintern,
wenn er im Idle mode ist. Dann ist es ja in Ordnung :-)

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe mir nochmal die ersten Beiträge durchgelesen und bin auf Dieses
gestoßen:

> Bevor man nun
> die Sleep-Anweisung losschickt, muß man den globalen Interrupt
> aktivieren.

Dieser Fall tritt praktisch nie auf.

Der globale Interrupt wird bei mir grundsätzlich bereits am Ende der
Reset-Routine aktiviert. Denn jedes meiner Programme braucht mindestens
einen Timer-Interrupt zur Steuerung des gesamten Programmablaufs. Und
daher ist (bei den neueren Programmen) die Mainloop so gestaltet, dass
sie nach jedem Interrupt solange durchlaufen wird, bis alle (per
Jobflags verwalteten) Jobs und noch anstehenden (bereits entprellten)
Tastendrücke abgearbeitet wurden. Danach geht die Mainloop in den
Sleep-Zustand, aus dem ein Interrupt (meist der Timer) den AVR wieder
aufweckt und nach der ISR die nächste Runde der Mainloop auslöst.
Dies ist bei mir Normalzustand für ein arbeitendes Programm. Meist ist
das Abarbeiten der Jobs fertig, bevor der nächste Interrupt auftritt.
Wenn nicht, dann finden eben mehrere Interrupts statt, bis das Programm
wieder in der Mainloop und im Sleep landet.

Der globale Interrupt ist also immer (außerhalb von ISRs) aktiv,
abgesehen von einigen Ausnahmen, bei denen der globale Interrupt mal
kurzzeitig gesperrt wird, wie z.B. EEPROM-Schreibzugriff.

...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.