Forum: Mikrocontroller und Digitale Elektronik Sleep Modes mit externen Interrupt


von Martin (Gast)


Lesenswert?

Hallo,

ich möchte einen Atmega8 über einen externen Interrupt (Int 0) mit einer 
steigenden Flanke ein- und ausschalten.

Das Verständnissproblem, wenn ich in der ISR ausschalte, wie kann ich 
dann mit der selben ISR ihn wieder einschalten? Da würde ja das ein- und 
ausschalten gleichzeitig im ISR stehen!?

Mein Ansatz:

// Externer Interrupt 0  zum Ein- Ausschalten

MCUCR |= (1<<ISC00) | (1<<ISC01);  //steigende Flanke, INT0

GICR |= (1<<INT0);    //INT0 Freigabe




ISR(INT0_vect)
{
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_mode();
}


Danke für euere Hilfe.

von Lutz (Gast)


Lesenswert?

Du brauchst einfach nur eine (volatile) globale Variable, in der Du den 
letzten Status speicherst. In der ISR schaust Du dann, ob gleich Null 
(dann z.B. einschalten), bei 1 dann ausschalten.

von Oliver (Gast)


Lesenswert?

Martin schrieb:
> Das Verständnissproblem, wenn ich in der ISR ausschalte, wie kann ich
> dann mit der selben ISR ihn wieder einschalten? Da würde ja das ein- und
> ausschalten gleichzeitig im ISR stehen!?

Der Prozessor schaltet nicht in der ISR ein, der schaltet bei Auftreten 
der passenden Flanke am Eingang ein, und setzt dann die entsprechenden 
ISR-Flags, und macht dann ganz normal weiter. Wenn der innerhalb einer 
ISR in den sleep-modus gegangen ist, führt der zunächst die laufenden 
ISR zu Ende aus, und springt dann erneut in die ISR, um den neu 
aufgetretenen "Aufweck"-Interrupt zu bearbeiten.

Aber wofür soll das alles gut sein?

Oliver

von Falk B. (falk)


Lesenswert?

@  Martin (Gast)

>ich möchte einen Atmega8 über einen externen Interrupt (Int 0) mit einer
>steigenden Flanke ein- und ausschalten.

Das geht nicht, der ATmega8, kann nur mit einem Low Level Interrupt 
aufgeweckt werden, siehe Sleep Mode. ALternativ kann man den 
ATmega88 verwenden, der hat einen Pin Change Interrupt, mit dem geht 
das.

>Das Verständnissproblem, wenn ich in der ISR ausschalte, wie kann ich
>dann mit der selben ISR ihn wieder einschalten? Da würde ja das ein- und
>ausschalten gleichzeitig im ISR stehen!?

Die ISR bleibt leer, die Verarbeitung erfolgt im Hauptprogramm, siehe 
den Artikel oben.

Mfg
Falk

von Martin (Gast)


Lesenswert?

Euere Hilfe ist schneller als ich Zeit habe - Vielen Dank

@ Falk

Danke für den hilfreichen Link, im AVR-GCC-Tutorial fand ich keinen Link 
dazu.

Ich habe nun folgenden Quelltext versucht:

ISR(INT0_vect)
{

  PORTB &= ~(1 << PIN2);      //LED an
  _delay_ms(1000);

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);

  sleep_mode();

  GICR &= ~(1 << INT0);           // externen Interrupt
  _delay_ms( 1000 );

  GICR |= (1<<INT0);

  PORTB |= (1 << PIN2);     //LED aus
}



// Externer Interrupt 0  zum Ein- Ausschalten

  MCUCR |=  (1<<ISC01);  //fallende Flanke, INT0

  GICR |= (1<<INT0);  //INT0 Freigabe

Wenn ich eine negative Flanke auf den Eingang (INT0 / PD2) gebe, geht er 
in die ISR und schaltet die LED an, soweit so gut.

Wenn die zweite negative Flanke kommt, sollte der Prozessor seine Arbeit 
nach dem sleep_mode() Befehl wieder aufnehmen und die LED ausschalten?

Leider bleibt die LED dauerhaft an.

Wo könnte das Problem liegen?

von Martin (Gast)


Lesenswert?

Und die Frage ist, warum geht die LED nicht beim sleep_mode() Befehl 
aus?

Es scheint als geht der Controller nicht in den Schlaf Modus.

von Falk B. (falk)


Lesenswert?

@  Martin (Gast)

>Ich habe nun folgenden Quelltext versucht:


Mit vielen Fehlern. In ISRs benutzt man KEINE Warteschleifen, schon gar 
nicht 1s. Auch wenn das hier nur ein Test ist, der Ansatz ist Murks.
Ausserdem schaltet man in der IST nicht wieder in den Sleep Mode, das 
gibt aus verschiedenen Gründen Kuddelmuddel.
Schau in den Artikel Sleep Mode, dort ist in einem Beispiel fast 
genau das drin, was du willst.

>// Externer Interrupt 0  zum Ein- Ausschalten

>  MCUCR |=  (1<<ISC01);  //fallende Flanke, INT0

>  GICR |= (1<<INT0);  //INT0 Freigabe

Und wenn du den Artikel so das Datenblatt deines ATmega8 mal lesen 
würdest, wäre dir aufgefallen, dass fallende FLANKE und Power Down nicht 
zusammenpasst. Hab ich nun schon zweimal geschrieben.

MfG
Falk

von Martin (Gast)


Lesenswert?

@ Frank
Danke für deine Antwort. Ich bin noch blutiger Anfänger in dem Bereich, 
also verzeih mir bitte die unqualifizierten Fragen, aber ich möchte mich 
einarbeiten.


ISR(INT0_vect)
{
    if(aus == 0)
    aus = 1;

    else
    aus = 0;
}


//Im Hauptprogramm

uint8_t aus = 0;

if(aus == 1)
{
     set_sleep_mode(SLEEP_MODE_PWR_DOWN);
     sleep_mode();        // in den Schlafmodus wechseln

     aus=0;
}

Das Aus- und Anschalten funktioniert soweit.

Bis das Hauptprogramm durchlaufen wird vergehen ca. 2 - 15 min 
(einstellbare Farbwechselgeschwindigkeit von RGB Led's). Das heißt, wenn 
ich im Hauptprogramm nur an einer Stelle if(aus == 1) abfrage und dann 
ausschalte, kann es im Extremfall bis zu 15 min dauern.

Ich könnte die if Anweisung mehrfach kopieren und damit öffter abfragen, 
was wohl ziehmlicher Pfusch wäre.

Gibt es eine andere Möglichkeit, damit der Controller sofort auschaltet?

Und muss ich vor sleep_mode(); die LED's (alle Ausgänge) manuell 
ausschalten? Denn der Controller scheint dieß nicht automatisch zu 
machen.

von Peter D. (peda)


Lesenswert?

Martin schrieb:
> Bis das Hauptprogramm durchlaufen wird vergehen ca. 2 - 15 min
> (einstellbare Farbwechselgeschwindigkeit von RGB Led's). Das heißt, wenn
> ich im Hauptprogramm nur an einer Stelle if(aus == 1) abfrage und dann
> ausschalte, kann es im Extremfall bis zu 15 min dauern.

Das solltest Du ändern.
Du hast warscheinlich 100kB Code per Copy&Paste hintereinander 
geklatscht, der fast immer was ähnliches macht.
Sowas verbraucht nicht nur Unmengen an Code, es ist auch schwer 
verstehbar und erweiterbar.

Du mußt Deinen Code in kleine Aufgaben aufteilen, die dann in einer 
Schleife aufgerufen werden. Dann kann man in der Schleife auch ne Taste 
abfragen und hat sofort ne Reaktion.

Warscheinlich ist es nur eine einzige Aufgabe: für eine bestimmte Zeit 
bestimmte Werte als PWM ausgeben.
Dann ist die Sache ganz einfach. Du legst alles in einer Tabelle (Array) 
ab und die Funktion holt sich alles aus der Tabelle. Dann wird der 
Tabellenindex hochgezählt und die Loop macht weiter.


Peter

von Falk B. (falk)


Lesenswert?

@  Martin (Gast)

>@ Frank

Who is this?

>ISR(INT0_vect)
>{
    if(aus == 0)
    aus = 1;

    else
    aus = 0;
}


>//Im Hauptprogramm

>uint8_t aus = 0;

>if(aus == 1)
>{
>     set_sleep_mode(SLEEP_MODE_PWR_DOWN);
>     sleep_mode();        // in den Schlafmodus wechseln

>     aus=0;
>}

Das passt so nicht. Denn der uC macht folgendes.

Er geht in den Sleep Mode.
Der Low Level Interrupt weckt ihn auf.
Er springt in den Low Level Interrupt, aus ist immer noch 1!
aus wird in der ISR auf 0 gesetzt
Er springt zurück zum Hauptprogramm und setzt aus=0, was aber dort 
sinnlos ist, auch wenn es keinen Schaden macht.

>Das Aus- und Anschalten funktioniert soweit.

Schön.

>Bis das Hauptprogramm durchlaufen wird vergehen ca. 2 - 15 min
>(einstellbare Farbwechselgeschwindigkeit von RGB Led's). Das heißt, wenn
>ich im Hauptprogramm nur an einer Stelle if(aus == 1) abfrage und dann
>ausschalte, kann es im Extremfall bis zu 15 min dauern.

Falscher Ansatz. Richtig macht man es mit Multitasking. Klingt 
kompliziert, ist es aber nicht. Lies den Artikel und schau dir das 
Beispiel an. Dann ändere dein Programm.

>Ich könnte die if Anweisung mehrfach kopieren und damit öffter abfragen,
>was wohl ziehmlicher Pfusch wäre.

Ja ;-)

>Gibt es eine andere Möglichkeit, damit der Controller sofort auschaltet?

Siehe Artikel Multitasking.

>Und muss ich vor sleep_mode(); die LED's (alle Ausgänge) manuell
>ausschalten?

Wenn du Strom sparen willst.

> Denn der Controller scheint dieß nicht automatisch zu
>machen.

Warum sollte er das?

MFG
Falk

von Martin (Gast)


Angehängte Dateien:

Lesenswert?

Erstmal danke an Peter und Falk für euere Hilfe.


Falk Brunner schrieb:
> Richtig macht man es mit Multitasking.

Wieder ein sehr guter Link. Ich habe das Kooperative Multitasking 
versucht umzusetzen, sprich alle Funktionen sind sehr kurz gehalten und 
damit klappt es sehr gut.


Da ich festgestellt habe, dass mein "Programmierstil" bei weitem nicht 
ideal ist, würde ich mich freuen wenn ihr mal drüberfliegen könntet und 
mir Tips zu Verbesserung geben könntet. (C- File im Anhang)

Eines Vorweg, das Programm funktioniert und erfüllt folgendes:

- Ein- und Ausschalten über einen "Klatschschalter" an Int0

- Bei Neustart startet eine kurze Farb Demo der RGB Leiste

- Danach werden zufällige Farbverläufe gestartet (Hauptaufgabe)

- Die Geschwindigkeit der Farbverläufe ist über einen Poti einstellbar

- steht der Poti auf "ganz langsamm" (Halt) wird der Farbverlauf 
gestoppt

- beim ausschalten im Halte-Modus, wird die "Haltefarbe" gespeichert und 
beim Wiedereinschalten wieder geladen (falls der Poti noch auf "halten" 
steht)


Interessant wäre noch eine automatische Abschaltung nach 2 Stunden.
Mein Ansatz war, den Timer1 zu nutzen. Allerdings einfach eine Variable 
bis X hochzählen, damit bekommt man keine 2h hin. Was meint ihr?


Vielen Dank für eure Geduld und Mühe.

von Falk B. (falk)


Lesenswert?

@  Martin (Gast)

>mir Tips zu Verbesserung geben könntet. (C- File im Anhang)

Zuviele Leerzeilen, das zerreißt den Code.

>Interessant wäre noch eine automatische Abschaltung nach 2 Stunden.
>Mein Ansatz war, den Timer1 zu nutzen.

Richtig. Du hast doch aber schon einen Timer für die PWM, denn kann man 
problemlos für mehrere Sachen gleichzeitig nutzen.

> Allerdings einfach eine Variable
> bis X hochzählen, damit bekommt man keine 2h hin. Was meint ihr?

Wo ist das Problem? Dein jetztiger Timerinterrupt läuft mit 25,6kHz, 
macht 25.600 Zählschritte/s, bzw. 92.160.000 Schritte/h. Mit einer 32 
Bit Variablen Null Problemo.

Ansonsten sieht den Programm schon recht gut aus.

MfG
Falk

von Martin (Gast)


Lesenswert?

Falk Brunner schrieb:
> Dein jetztiger Timerinterrupt läuft mit 25,6kHz,
> macht 25.600 Zählschritte/s, bzw. 92.160.000 Schritte/h. Mit einer 32
> Bit Variablen Null Problemo.

Du hast Recht, es funktioniert einwandfrei.

Nun wollte ich das ganze noch mit #define machen:

#define ausschaltzeit 1    //in Minuten

#define off ( F_PWM*PWM_STEPS*60*ausschaltzeit )

//Berechenet die Standby Zeit in Systemtakte um

Dummerweise scheint in dem Fall das "off" < 32 Bit zu sein, da der 
Compiler integer overflow anzeigt.

Habe leider im Forum nichts in der Richtung gefunden. Besteht die 
Möglichkeit "größere Textzuweisungen" (define) zu machen?

von Falk B. (falk)


Lesenswert?

@  Martin (Gast)

>Dummerweise scheint in dem Fall das "off" < 32 Bit zu sein, da der
>Compiler integer overflow anzeigt.

Versuchs mal mit

#define F_PWM 100L

MFG
Falk

von Martin (Gast)


Lesenswert?

Falk Brunner schrieb:
> Versuchs mal mit
>
> #define F_PWM 100L

Ich habe es soeben erfolgreich getestet - nur weiß ich leider nicht was 
das "L" bewirkt.

Könntest du mir das bitte noch erklären?

von Kluchscheißernder N. (kluchscheisser)


Lesenswert?

> nur weiß ich leider nicht was das "L" bewirkt.

Die Konstante wird damit vermutlich im Format "Long" festgenagelt.

MfG

von Falk B. (falk)


Lesenswert?


von Martin (Gast)


Lesenswert?

Falk Brunner schrieb:
> http://www.mikrocontroller.net/articles/Bitmanipul...

Alles klar, ich habs verstanden.

Damit wäre mein erstes kleines Projekt erfolgreich umgesetzt. Hat zwar 
viel Zeit in Anspruch genommen aber ich hab einiges gelernt - auch Dank 
eurer Hilfe.

Ich werde mich nun mal an ein LCD Display wagen. Ich hoffe ich kann auf 
euch zählen ;-)

Vielen Dank.

Martin

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.