Sleep und Interrupt

Gast #2259406
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
#2259409
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
Gast #2259435
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.
#2259873
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.
Gast #2259966
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
}
Gast #2259994
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?
#2260022
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.
Gast #2260055
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.
Gast #2260619
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!

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