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
@ 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
> 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.
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
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.
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.
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
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 | }
|
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.
@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?
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.