Forum: Mikrocontroller und Digitale Elektronik Sleep und Interrupt


von DJ T. (Gast)


Lesenswert?

Hallo,

ich hatte die Woche ein interessantes Problemchen.
Meine Hauptschleife soll einmal pro Mikrosekunde durchlaufen werden. 
Dazu hab ich einen Timer-Interrupt, der einmal pro Mikrosekunde kommt. 
Ich arbeite meine Hauptschleife ab, gehe dann in einen Low-Power-Mode 
und der Timer-Interrupt weckt dann die CPU wieder auf.
Da ich neben dem Timer noch andere Interrupts habe, setz ich im 
Timer-Interrupt ein Flag und prüfe in der Hauptschleife, ob es beim 
Aufwachen gesetzt ist. Falls nicht, geh ich wieder in den 
Low-Power-Mode. Sieht in etwa so aus:


  while(1)
  {
    while ( !TimerFlag)
    {
      sleep();
    }
    ...
  }


Jetzt hatte ich das Phänomen, daß es regelmäßig vorkommt, daß die CPU 
einen Mikrosekundenzyklus verpennt.
Der Grund ist folgender:
- Ein anderer Interrupt (in meinem Fall der ADC) weckt die CPU.
- Nach Abarbeiten der ADC-ISR wird das Timer-Flag geprüft, dieses ist 
nicht gesetzt, also weiter zum Low-Power-Befehl.
- JETZT kommt der Timer-Interrupt und setzt das Flag.
- Die CPU geht wieder schlafen, obwohl das Flag gesetzt ist und verpennt 
bis zum nächsten Interrupt

Um dies zu vermeiden, mußte ich einen Weg finden, den Timer-Interrupt 
zwischen der Abfrage des Flags und dem Low-Power-Befehl zu verbieten, 
aber gleichzeitig mit dem Low-Power-Befehl wieder zu erlauben.
In meinem Fall hatte ich das Glück, daß der verwendete uC (PIC24) einen 
speziellen Befehl hat ("DISI"), mit dem man die Interrupts für eine 
bestimmte Anzahl von Instruction-Cycles verbieten kann. Ich hab dann die 
Abfrage in Assembler programmiert, "DISI" dazu und das Problem war 
gelöst.

Allerdings frage ich mich, wie man dieses Problem auf einem AVR lösen 
kann. Meines Wissens hat der keinen vergleichbaren Mechanismus.

Grüße,

Dosmo

von Falk B. (falk)


Lesenswert?

@  Sebastian F. (dosmo)

>Meine Hauptschleife soll einmal pro Mikrosekunde durchlaufen werden.

Meinst du nicht, dass das ETWAS zu schnell ist?

>Dazu hab ich einen Timer-Interrupt, der einmal pro Mikrosekunde kommt.
>Ich arbeite meine Hauptschleife ab, gehe dann in einen Low-Power-Mode
>und der Timer-Interrupt weckt dann die CPU wieder auf.

Das nenne ich mal Kurzschlaf ;-)

>Jetzt hatte ich das Phänomen, daß es regelmäßig vorkommt, daß die CPU
>einen Mikrosekundenzyklus verpennt.

Was nicht verwundert.

>Allerdings frage ich mich, wie man dieses Problem auf einem AVR lösen
>kann. Meines Wissens hat der keinen vergleichbaren Mechanismus.

Dein Problem ist viel grundlegender. Ein Timerinterrupt mit 1 
MIKROsekunde hat gerade mal 20 Takte Zykluzszeit bei 20 MHz. Das ist 
auch ohne Sleep Mode unsinnig.

MfG
Falk

von g457 (Gast)


Lesenswert?

> Allerdings frage ich mich, wie man dieses Problem auf einem AVR lösen
> kann. Meines Wissens hat der keinen vergleichbaren Mechanismus.

Brauchts auch nicht. Man nimmt dafür einfach sei() und cli(). Im 
Handbuch stehts dokumentiert warum das funktioniert wenn man die an die 
richtigen(tm) Stellen setzt.

von Peter D. (peda)


Lesenswert?

Sebastian F. schrieb:
> Dazu hab ich einen Timer-Interrupt, der einmal pro Mikrosekunde kommt.

Wußte garnicht, daß es schon GHz-AVRs gibt.

Rechne mal mit 50 .. 100 Zyklen pro Interrupthandler.
Und laß auch was für die Main-Loop übrig.


Peter

von Purzel H. (hacky)


Lesenswert?

Es geht um den PIC-24, das ist dasselbe wie ein PIC-33, aber ohne MAC 
oder so. Der laeuft also mit 66MHz oder so. Nichtsdestotrotz waere ein 
Stueck Hardware passender, zB ein FPGA, oder so.

von DJ T. (Gast)


Lesenswert?

Hallo,

sorry, ich meinte doch 1 Millisekunde!

Grüße,

Dosmo

von (prx) A. K. (prx)


Lesenswert?

Sebastian F. schrieb:

> sorry, ich meinte doch 1 Millisekunde!

Klingt besser.

> Jetzt hatte ich das Phänomen, daß es regelmäßig vorkommt, daß die CPU
> einen Mikrosekundenzyklus verpennt.

Deshalb muss die Sequenz Test-and-Sleep ununterbrechbar implementiert 
werden. Das kann bei jeder Architektur etwas anders ablaufen. Bei AVRs 
beispielsweise hilft hierbei sehr, dass der Befehl zum Einschalten der 
Interrupts (SEI) nicht vor Ausführung des Folgebefehls (SLEEP) wirksam 
wird. Wie das bei den PIC24 zu regeln ist habe ich grad nicht parat.

von Peter D. (peda)


Lesenswert?

Sebastian F. schrieb:
> Allerdings frage ich mich, wie man dieses Problem auf einem AVR lösen
> kann. Meines Wissens hat der keinen vergleichbaren Mechanismus.


Das geht ganz einfach mit CLI/SEI.
Das SEI hat wie beim 8051 den Trick, daß der folgende Befehl noch unter 
Interruptsperre erfolgt.
Ein SEI direkt vor dem SLEEP ergibt eine nicht unterbrechbare Sequenz.


Peter

von Ralf (Gast)


Lesenswert?

Sollte das nicht auch ohne zeitkritische Akrobatik gehen?
1
while (1)
2
{
3
 sleep();
4
 if (TimerFlag)
5
 {
6
  cli();
7
//
8
// bereitgestellte Daten aus den verschiedenen ISRs abholen
9
//
10
  sei();
11
//
12
// Daten verarbeiten
13
//
14
 }
15
}

von (prx) A. K. (prx)


Lesenswert?

Ralf schrieb:

> Sollte das nicht auch ohne zeitkritische Akrobatik gehen?

Nein. Zwischen dem Test und dem Sleep kann der Interrupt erfolgen auf 
den TimerFlag testet. Worauf dann Sleep folgt und erst der nächste 
Timer-Interrupt ihn aufweckt. Dann fehlt einer.

von Ralf (Gast)


Lesenswert?

@A. K.
Ich bin mir nicht 100% sicher, dass mein Ansatz richtig ist. Deshalb 
wollte ich noch mal nachhaken und beziehe mich auf mein Beispiel oben.
1. Der Controller wird durch einen Interrupt (außer Timer) aufgeweckt:
   -> legt sich gleich wieder schlafen
2. Der Controller wird durch einen Timer-Interrupt aufgeweckt:
   -> jede Menge andere Interrupts drängeln sich bis zum cli() noch mit 
rein
      = gern! da sind die Daten wenigstens schön aktuell.
Wo ist jetzt mein Denkfehler?

von (prx) A. K. (prx)


Lesenswert?

Ralf schrieb:

> Wo ist jetzt mein Denkfehler?

Darin, dass die bei gesetztem TimerFlag stattzufindende Aktion einen 
Timer-Event verpennt, wenn dieser Interrupt genau zwischen dem Test und 
dem Sleep reinrutscht und erst der nächste Timer-Interrupt ihn wieder 
aus dem Schlaf reisst.

Ablauf:

if(TimerFlag) => falsch, if-Zweig wird nicht ausgeführt.
Timer-Interrupt => setzt TimerFlag
Sleep => schnarch
Timer-Interrupt => setzt gesetztes TimerFlag nochmal, weckt auf
if(TimerFlag) => passt, if-Zweig wird ausgeführt.

Eine der von TimerFlag abhängigen Aktionen fehlt hier.

von Ralf (Gast)


Lesenswert?

Ah, alles klar. Ich habe immer nur berücksichtigt, was zwischen sleep() 
und dem Test passiert (Da schadet ein Timer-Interrupt ja nichts)...
Da muss es doch akrobatischer zugehen, wenn die Hauptschleife nicht 
komplett gesperrt werden soll.

von DJ T. (Gast)


Lesenswert?

Peter Dannegger schrieb:
>Das geht ganz einfach mit CLI/SEI.
>Das SEI hat wie beim 8051 den Trick, daß der folgende Befehl noch unter
>Interruptsperre erfolgt.
>Ein SEI direkt vor dem SLEEP ergibt eine nicht unterbrechbare Sequenz.

Das wollte ich wissen, danke!

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.