Forum: Compiler & IDEs Interrupt vor Sleepmode wieder aktivieren


von Chris (Gast)


Lesenswert?

Hallo,

ich bin gerade dabei, eine Fernbedienung für Canon-Spiegelreflexen zu
basteln. Da das Ganze von einer Knopfzelle laufen soll, muss Strom
gespart werden.
Leider habe ich noch ein Problem: die AVRs (in meinem Fall noch ein
Mega8, später ein Tiny) wachen an INTx nur bei low-Pegel auf und laufen
während Low-Pegel einfach in einer Schleife.
Bei (m)einer Fernbedienung etwas unbrauchbar, da der Befehl pro
Tastendruck nur einmal gesendet werden sollte.
Deshalb lasse ich in der Interrupt-Routine sofort nach dem Senden die
Interrupts deaktivieren, in main() werden in einer unendlichen Schleife
die Interrupts wieder aktiviert und die CPU wieder in den sleep
geschickt.
Das Problem ist nun: Obwohl ich die Interrupts aktiviert habe, reagiert
der AVR nicht darauf, obwohl es ja eigentlich funktionieren müsste...

Der Code sieht folgendermaßen aus:

SIGNAL(SIG_INTERRUPT0) { //INT0
  sendcommand(RELEASE_DIRECTLY);
  GICR &= ~(1<<INT0); //Interrupts deaktivieren
}

int main() {
  [...  ]
  MCUCR = (0<<ISC01) | (0<<ISC01) | (0<<ISC11) | (0<<ISC11); //INT0 und
INT1 bei jeder Flanke auslösen
  sei(); //Interrupts aktivieren
  TCCR0 = (0<<CS02) | (1<<CS01) | (0<<CS00); //Timer0 Prescaler = 8
  while(1) {
    set_sleep_mode(SLEEP_MODE_PWR_SAVE); //Sleepmodus setzen
    GICR |= ((1<<INT0) | (1<<INT1));
    sleep_mode(); //AVR in den Schlaf versetzen
  }
  return 0;
}

von Stefan K. (_sk_)


Lesenswert?

>  MCUCR = (0<<ISC01) | (0<<ISC01) | (0<<ISC11) | (0<<ISC11); //INT0 >
                            // und INT1 bei jeder Flanke auslösen

Aufwachen aus Sleep funktioniert nicht bei flankengetriggerten INTs, da
der mc für die Erkennung der Flanke einen Takt braucht (und der ist
während Sleep ja abgeschaltet).

Meine Empfehlung:
Aufwachen per pegelgetriggertem INT.
Tastenerkennung NICHT per INT (da hast Du nur massig Probleme,
Tastenprellen etc.), sondern per Timer-Interrupt (z.B. Code von Peter
Danegger).

Viele Grüße. Stefan

von Chris (Gast)


Lesenswert?

Hallo Stefan,

der Kommentar ist noch falsch.
Der Code macht pegelgetriggerten Int.
Um Tastenentprellung mache ich mir wenig sorgen, da der Code, der im
IRQ ausgeführt wird, lang genug ist.
Wichtiger wäre mir, dass ich die Interrupts auch ohne µC-Reset
nacheinander auslösen kann...

Viele Grüße

Chris

von Stefan K. (_sk_)


Lesenswert?

> Wichtiger wäre mir, dass ich die Interrupts auch ohne µC-Reset
> nacheinander auslösen kann...

Was meinst Du damit?

Kann es sein, dass Dein Code (meistens) einmal nach Reset funktioniert,
und danach nicht mehr?

>  while(1) {
>    set_sleep_mode(SLEEP_MODE_PWR_SAVE); //Sleepmodus setzen
>    GICR |= ((1<<INT0) | (1<<INT1));
>    sleep_mode(); //AVR in den Schlaf versetzen
>  }

Schau Dir mal genau an, was passiert, wenn zwischen
 GICR |=    und
 sleep_mode()
Dein IR dazwischenfunkt: die IR schaltet die IRs ab, dann kommt slepp,
und dann kommt - nichts mehr.

> Um Tastenentprellung mache ich mir wenig sorgen, da der Code, der im
> IRQ ausgeführt wird, lang genug ist.

Was bedeutet "lang genug"?
Wahrscheinlich wird die IR immer noch kürzer laufen, als Du den Finger
auf der Taste hälst. D.h. sie wird sofort wieder gestartet, sobald Dein
main die IR mit GICR |= wieder freischaltet.
Das oben beschriebene Szenario wird also praktisch immer eintreten.

Viele Grüße, Stefan

von Chris (Gast)


Lesenswert?

Hallo Stefan,

> [IRQ erst nach µC-Reset]
> Was meinst Du damit?

momentan ist es so: ich aktiviere einen Interrupt, der AVR sendet den
Befehl einmal (weil beim Auslösen des Interrupts IRQs deaktiviert
werden). Beim Loslassen der Taste läuft der AVR ja weiter und arbeitet
die Befehle in main() ab - und im Idealfall sollte dies die Schleife
sein...

> Schau Dir mal genau an, was passiert, wenn zwischen
> GICR |=    und
> sleep_mode()
> Dein IR dazwischenfunkt: die IR schaltet die IRs ab, dann kommt
> slepp, und dann kommt - nichts mehr.

habe ich noch nicht in Betracht gezogen...

> Was bedeutet "lang genug"?
> Wahrscheinlich wird die IR immer noch kürzer laufen, als Du den
> Finger auf der Taste hälst. D.h. sie wird sofort wieder gestartet,
> sobald Dein

die ausgeführte Routine ist in knapp 10ms lang.
Prellen kann momentan schon die Ursache sein, allerdings ist der
aktuelle Taster, der den IRQ auslöst kein Taster, sondern ein
Drahtstück, dass ich ins Breadboard stecke ;)
Also meiner Meinung relativ unwahrscheinlich, dass das Prellen beim
hineinstecken so gleichmäßig ist, dass der IRQ nach 30x betätigen nicht
wieder aktiviert wird...

Viele Grüße

Chris

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Aktuelle AVRs können übrigens auch per pin-change interrupt
aufwachen.

Für deinen Fall ist es sinnvoll, den SEI genau vor dem SLEEP
zu haben, aber ansonsten den entsprechenden Externinterrupt zu
verbieten.  Der Kommentar in
http://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html
(Unterstriche in der URL bitte mit der Hand korrigieren, die
Forum-Software verhohnepiepelt sie) zeigt, wie man das machen
kann.

von Stefan K. (_sk_)


Lesenswert?

Hallo Chris,

das ist das, was Du denkst, was er tun soll ;-)

Um Dir die Reihenfolge der Befehle während eines Tastendrucks deutlich
zu machen, habe ich mal folgende Tabelle gemacht. Ich gehe mal aus von
einem Tastendruck von 50ms Dauer.


Befehl                  von (Quelle)  GICR           Zeit ab T-druck
----------------------------------------------------------------------
set_sleep_mode(...);    main          GICR =  0      - x ms
GICR |= ((...);         main          GICR => INT0   - x ms
sleep_mode()            main          GICR =  INT0   - x ms

-> Taste gedrückt -> sleep wird verlassen -> Int wird ausgelöst

sendcommand(...);       INT0          GICR =  INT0   0ms
GICR &= ~(1<<INT0);     INT0          GICR => 0      10ms

set_sleep_mode(...);    main          GICR =  0      10ms
GICR |= ((...);         main          GICR => INT0   10ms

sendcommand(...);       INT0          GICR =  INT0   10ms
GICR &= ~(1<<INT0);     INT0          GICR => 0      20ms

sleep_mode()            main          GICR =  0  !!! 20ms

-> Taste losgelassen nach 50ms

... sleep well!


Um das zu verhindern, musst Du - wie Jörg schon sagte, die GICR-Flags
bei global gesperrten IRs ändern, dann per sei() Befehl die IRs wieder
freigeben und SOFORT danach den sleep einleiten.

Allerdings behebt dies nicht, dass Dein Programm sendcommand()
wiederholt ausführt, solange Du auf die Taste drückst. Kann sein, dass
das in Deiner Anwendung nicht stört (die Kamera wird wohl nicht im
10ms-Abstand Bilder knipsen), sauber ist es aber nicht.

Gruß, Stefan

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.