Hallo, ich habe aktuell eine Ablaufsteuerung, bei der viele verschiedene Aktionen durchgeführt werden. Während der Ablaufsteuerung muss immer eine bestimmte Taste gedrückt sein und ab einem bestimmten Punkt darf eine Zeit X nicht überschritten werden. Sobald die Taste losgelassen wird oder die Zeit überschritten wird muss der Ablauf sofort unterbrochen werden und es soll eine Fehlermeldung kommen. Danach wird das Programm von vorne gestartet. Die Überwachung der Abschaltkritierien erolfgt regelmäßig über Timer Interrupts. Selbstverständlich kann ich mir dort einen Merker setzen und diesen im Hauptprgramm abfragen. Allerdings muss ich die diese Abfrage dann im Hauptprogramm an sehr viellen Stellen einfügen, damit es zu keiner großen Verzögerung der Abschaltung kommt. Aus diesem Grunde würde ich gerne direkt im Interrupt abschalten die Fehlermeldung ausgeben und danach das Programm wieder von vorne starten. Ich lese zwar öfters, dass es nicht sauber oder nicht der Sinn einer Interruptroutine ist, aber spricht theoretisch wirklich was dagegen? Wenn ich in der Interruptroutine mein Abschaltkriterium erkenne, dann mein SREG wieder zurückschreibe und den Interrupt durch cli zurücksetze bin ich doch wieder sauber und kann die Fehlermeldung ausgeben und mein Programm neu starten. Das aktuelle reti müßte dann doch gelöscht sein oder springt der mir beim nächsten reti an die alte Stelle? Vielen Dank, Ernie
Du kannst es doch einfach so machen, dass sobald er das Kriterium erkennt, der Stack so verändert wird, dass reti nicht die Rücksprungadresse holt sondern eine von der gewünschte Adresse (z.b. die Programmstartadresse).
Sascha T. wrote: > Wenn ich in der Interruptroutine mein Abschaltkriterium erkenne, dann > mein SREG wieder zurückschreibe und den Interrupt durch cli zurücksetze > bin ich doch wieder sauber Nö, bist Du nicht. Was ist mit dem Stack (Programmzähler)? Ich weiß nicht, ob ich zu 100% verstehe, was Du machen willst, aber wenn Du bei Auftreten eines speziellen Interrupt möchtest, dass nach der Rückkehr aus dem Handler die Interrupts nicht automatisch wieder freigegeben werden, dann mach den Rücksprung mit einem ret anstelle des reti. Der einzige Untschied des reti gegenüber einem ret ist schließlich, dass das reti das I-Bit im SREG wieder setzt. Wenn Du allerdings einfach aus dem Handler das Programm quasi als "Warmstart" von Anfang an neu starten willst (also ein Reset), dann kannst Du natürlich zur Adresse 0 jumpen und anschließend bei der Stack-Neuinitialisierung wird eh alles, was vorher da war, platt gemacht. Das setzt natürlich voraus, dass Dein Controller keinen Hardware-Stack hat und dass der Stack am Programmanfang tatsächlich explizit initialisiert wird (was bei neueren AVRs eigentlich nicht mehr unbedingt eforderlich ist, da der Reset-Wert des Stack Pointers schon entsprechend gesetzt ist). Von einer direkten Stackmanipulation ist abzuraten.
Um es kurz zu machen (und ohne das Programm gesehen zu haben): Dein Programmierstil ist falsch :-o Du solltest in deinem Programm zyklisch die Schritte
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
durchlaufen. Die Durchlaufzeit muß so kurz sein, dass deine Definition von "Echtzeit" erfüllt ist. In einer etwaigen Interruptroutine werden nur Flags gesetzt, die im Eingabeteil auf dein Prozessabbild eingelesen werden. Auf diese Art funktionieren zigtausende SPSen (und alle meine Programme) problemlos.
Gast
#1182361
Für Sonder-/Ausnahmebehandlung kann man so ein Design durchaus machen. Ich sehe erstmal nichts, was dagegen spricht, insofern du das Programm dann vollständig neu startest und alle Hardware nicht in gefährliche Zustände reinfahren kann.
Vielen Dank für die ersten Hinweise. Im Grunde will ich halt sicherstellen, dass z.B. alle 100µs überprüft wird, ob die Taste noch gedrückt ist und nicht losgelassen wurde. Diese regelmäßige Überprüfung ist ein Sicherheitskriterium und ist halt nur mit einem Interrupt sauber zu lösen. Während des Programmablaufs werden halt viele Routinen durchlaufen und ich müßte in jeder Routine eine Abfrage reinbringen und ob in dem Timer Interrupt ein Merker für das Loslassen der Taste gesetzt wurde. So wäre es vielleicht "sauber" programmiert. Wenn ich aber den Programmablauf direkt im Interrupt abbreche, kann ich mir diese Abfragen ja sparen und ich finde diesen Ablauf vom Sinn her "sauberer". Der Interrupt soll ja den normalen Programmablauf unterbrechen :o) Die Frage ist halt, was ich alles berücksichtigen müßte, damit der Interrupt sauber beendet wird.
Du kannst Dir nen Longjump in Assembler proggen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
Peter
Gast
#1182498
> Im Grunde will ich halt sicherstellen, dass z.B. alle 100µs überprüft > wird, ob die Taste noch gedrückt ist und nicht losgelassen wurde. Diese > regelmäßige Überprüfung ist ein Sicherheitskriterium und ist halt nur > mit einem Interrupt sauber zu lösen. Dann zieh' halt mit der Taste Reset gegen einen PullDown-R nach Vcc... ;-) ...
Sascha T. wrote: > Im Grunde will ich halt sicherstellen, dass z.B. alle 100µs überprüft > wird, ob die Taste noch gedrückt ist und nicht losgelassen wurde. Kein Mensch kann nur 100µs lang ne Taste betätigen. Bei menschlicher Betätigung sind 100ms vollkommen ausreichend. > Diese > regelmäßige Überprüfung ist ein Sicherheitskriterium und ist halt nur > mit einem Interrupt sauber zu lösen. 100µs sind gut geeignet, alle möglichen Störimpulse fälschlich als Loslassen zu erkennen. > Während des Programmablaufs werden halt viele Routinen durchlaufen und > ich müßte in jeder Routine eine Abfrage reinbringen und ob in dem Timer > Interrupt ein Merker für das Loslassen der Taste gesetzt wurde. So wäre > es vielleicht "sauber" programmiert. Sauber (d.h. Fehlersicher) wäre es, wenn Du die Taste entprellst (4 Abfragen etwa alle 10ms sind gleich). Peter
Gast
#1182576
Hatte mich sowieso verschriebn. Aktuell prüfe ich die Taste alle 100ms. Entprellung steht sowieso schon hardwareseitig. Aber es geht hier doch um was anderes. Normalerweise habe ich auch immer strikt zyklisch programmiert und alle Interruptroutinen sauber beendet. In diesem Fall habe ich aber mal umgedacht und zwar aus folgenden Gründen: Der Tastendruck und das Halten der Taste ist für einen bestimmten Programmteil die Voraussetzung und höchste Priorität. Einen Reset direkt kann ich nicht durchführen, da ich ja eine Fehlermeldung erzeugen muss. Während diese Programmablauf werden z.B. UART und SPI Kommunikationsroutinen aufgerufen. Diese Routinen rufen ja wieder Abfrageroutinen auf z.B. ob etwas empfangen wurde. Diese Abfrageroutinen laufen natürlich Schleifen durch und werden z.B. abgebrochen wenn mehrere Sekunden lang nichts empfangen wurde. Wenn ich jetzt "normal" programmiere, dann frage ich in der Schleife einen Merker ab, ob bei dem Timer Interrupt ein Loslassen der Taste festgestellt wurde. Falls ja breche ich ab und springe zu der Routine darüber. Dort muss ich natürlich auch wieder abfragen, ob der Merker gesetzt wurde und dann diese Routine verlassen. Wenn ich dann im Hauptprogramm bin, dann muss ich natürlich auch wieder gucken ob der Merker gesetzt wurde und dann springe ich endlich in meine Fehlerroutine. Ist ja so weit auch kein Problem. Allerdings muss ich dann diese Abfragen überall einfügen und die Gefahr besteht, dass ich irgendwo mal eine vergesse und somit doch mal eine Verzögerung bis zum Erkennen des Loslassen habe. Somit ist die Beendigung des Programmablaufs direkt im Interrupt natürlich eine sichere Sache. Ich habe das Programm ja aktuell so laufen und es funktioniert auch einwandfrei. Aber ich wollte halt mal theoretisch nachvollziehen, ob diese Art der Interruptbeendigung zu Problemen führen könnte.
Gast
#1182686
Hi! Ja das geht, habe ich selbst schon gemacht. Aber du musst auch den Resetstatus der Register und E/A's bedenken wenn du dir keine Fehler einbauen willst. Eine andere Möglichkeit wäre noch den µC vom Wachhund schnappen zu lassen. Das ist dann, glaube ich jedenfalls, ein sauberes Reset.
Gast
#1182695
Die Register und E/A werden ja im Reset wieder definiert. Aber was ist dem Stack. Habe ich da was zu befürchten?
Gast
#1182704
Hi!
>Aber was ist dem Stack. Habe ich da was zu befürchten?
Hä? Initialisierst du den am Anfang nicht? Das ist doch Standard.
Da muss man eher aufpassen dass alle I-Flags gelöscht werden.
(meine nicht das I-Flag im SREG)
Viel Erfolg, Uwe
Gast
#1182713
@ Sascha Ich meine Deine Gründe zu verstehen und die Lösungsansätze die hier genannt wurden, sind sicherlich sinnvoll. Trotzdem kräuselt sich da was bei mir und ich habe mir überlegt ob es nicht doch eine "saubere" Möglichkeit gibt. Wenn Du den Ablauf in mehrere Teilschritte unterteilen kannst (vermutlich rufst Du ohnehin verschiedene Funktionen auf und hast nicht eine Riesenfunktion) dann wäre eine Lösung, das Du eine State-Machine daraus machst und vor dem Wechsel in den jeweiligen Folgezustand ein Flag abfragst, das in dem fraglichen Interrupt gesetzt werden kann. Auf diese Weise wärst Du sicher, regelmäßig die Abfrage zu machen, hast aber trotzdem nur eine Stelle im Code wo diese geschieht und vermeidest dieses return-from-int ohne RETI. Genaugenommen hat diese Methode allerdings doch einen Nachteil. Wie fein die möglichen Abbruchstellen im Ablauf verteilt sind (das ist ein bischen ungeschickt ausgedrückt) hängt davon ab, wieviele Zustände Du der State-Machine gibtst. Je mehr desto feiner. Naja. Vielleicht hilft Dir das was.
Uwe wrote: > Hä? Initialisierst du den am Anfang nicht? Das ist doch Standard. Hab ich oben schon mal erwähnt: Bei den neueren AVRs muss man den Stack Pointer nicht mehr zwangsläufig von Hand initialisieren, da der Reset-Wert schon auf RAMEND voreingestellt ist...
Gast
#1182842
Vielen Dank für die weiteren Hinweise. @Klugscheisser: Ich verstehe jetzt nicht ganz was Du meinst. Ist diese State Machine denn nicht dasselbe wie ein Merker, den ich mir in der Interruptroutine setzte und dann bei jeder Unterroutine abfrage? @Uwe: Du hast ja recht, natürlich initialisiere ich den Stack zu Beginn. Also dürfte ja kein Risiko bestehen, wenn ich im Interrupt die Fehlerausgabe mache und einen Reset durchführe. Welche Interrupt Flags meinst Du denn noch?
Gast
#1182858
@ Sascha >Ich verstehe jetzt nicht ganz was Du meinst. Ist diese >State Machine denn nicht dasselbe wie ein Merker, den ich mir in der >Interruptroutine setzte und dann bei jeder Unterroutine abfrage? Möglicherweise verstehe ich Dich jetzt nicht. Ein Merker ist grundsätzlich etwas anderes als eine State-Machine. Ich konstruiere mal ein Beispiel: Gegeben sei etwa folgernder Pseudcode, den es gilt unterbrechbar zu machen. In diesen würdest Du dann (was Du ja vermeiden möchtest den Test auf das Flag einfügen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
Daraus würde ich nun die State-Machine machen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
Das kann man noch varieren, aber so ungefähr. Klar?
Gast
#1182875
Sorry, aber da ich in Assembler programmiere kann ich dir auf Anhieb nicht folgen. Deshalb kenne ich wohl auch keine State Machine :(
Gast
#1182888
State-Machines sind ein Konzept. Hat nichts mit der Programmiersprache zu tun. Soll heissen: Wenn Du Asm kannst dann begreifst Du auch das. Ein wenig gurgeln sollte die grundlegende Idee klarmachen. (More-Mealy vielleicht noch vom Studium bekannt?)
Sascha T. wrote: > Hallo, > > Sobald die Taste losgelassen wird oder die Zeit überschritten wird muss > der Ablauf sofort unterbrochen werden und es soll eine Fehlermeldung > kommen. Danach wird das Programm von vorne gestartet. > > Die Überwachung der Abschaltkritierien erolfgt regelmäßig über Timer > Interrupts. Selbstverständlich kann ich mir dort einen Merker setzen und > diesen im Hauptprgramm abfragen. Allerdings muss ich die diese Abfrage > dann im Hauptprogramm an sehr viellen Stellen einfügen, damit es zu > keiner großen Verzögerung der Abschaltung kommt. > > Aus diesem Grunde würde ich gerne direkt im Interrupt abschalten die > Fehlermeldung ausgeben und danach das Programm wieder von vorne starten. > Ich lese zwar öfters, dass es nicht sauber oder nicht der Sinn einer > Interruptroutine ist, aber spricht theoretisch wirklich was dagegen? Was spricht dagegen, das in der ISR zu machen? Wenn die Bedingung ni der ISR erfüllt ist, wird die Fehlermeldung ausgegeben (wie immer das auszusehen hat) und der µC macht per WatchDog nen RESET. Wenn die Fehler absolute Priorität hat, dann ist's nun mal so. Ich würd mir da keinen Kopp um irgendwelche Dogmen machen (eine ISR darf nicht "lange" dauern, etc.) Nach der Meldung erfolgt der RESET per WatchDog, weil er im Gegensatz zu einem direkten Sprung zum Reset-Vektor die Hardware zurücksetzt (Timer, UART, SPI, ...) Und dann begint das Spiel von neuem. Wozu also die Applikation verfrickeln mit Abfragen? Weitere Möglichkeit könnte ein Software-Interrupt sein, aber dafür weiß man zu wenig über deine Anwendung. Johann
Gast
#1182917
Stimmt, dass ist eigentlich eine sehr gute Idee. Sobald in der Interruptroutine erkenne, dass die Taste losgelassen wurde gebe ich kurz die Fehlermeldung aus (3 Bytes über SPI) und dann lasse ich den Wachhund los. Mit einem Softwarinterrupt habe ich mich bisher nocht nicht beschäftigt. Hört sich aber auch gut interessant an.
> Deshalb kenne ich wohl auch keine State Machine :( Ich hatte den Verdacht schon recht früh: >> Dein Programmierstil ist falsch :-o Sieh dir mal das an:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
Und hier exemplarisch die Funktion zum Zeicheneinlesen
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
Es wird nicht gewartet, bis ein Zeichen da ist. Sondern wenn kein Zeichen da ist, bin ich sofort fertig und es geht weiter in der Haupt-Schleife. In keiner der Unterprogramme wird auf irgendwas gewartet. Es wird erst geprüft, ob sich etwas geändert hat (z.B. ein Eingang, Timer oder ein anderer Zustand) und dann darauf reagiert. @ Johann L. > Nach der Meldung erfolgt der RESET per WatchDog, weil er im Gegensatz > zu einem direkten Sprung zum Reset-Vektor die Hardware zurücksetzt > (Timer, UART, SPI, ...) > Und dann begint das Spiel von neuem. Na toll, hoffentlich passiert das nicht beim Landeanflug.... :-/ Game Over *Insert Coin*
Gast
#1182988
Naja von Automaten habe ich schonmal was gehört, auch wenn es auch schon lange her ist. State-Machine war mir bisher allerdings kein Begriff. Aber vielen Dank Lothar. Mit der Assembler Routine ist das Prinzip direkt bei mir angekommen. Das trifft halt meine Synapsen schneller als die C Routinen, ist ja auch "Hardwarenäher" :o) Ich werde mir mein Programm morgen nochmal dahingehend anschauen und denke, dass ich es dann auch sehr gut ohne Interrupt Abbruch hinbekomme.
Lothar Miller wrote: > @ Johann L. >> Nach der Meldung erfolgt der RESET per WatchDog, weil er im Gegensatz >> zu einem direkten Sprung zum Reset-Vektor die Hardware zurücksetzt >> (Timer, UART, SPI, ...) >> Und dann begint das Spiel von neuem. > Na toll, hoffentlich passiert das nicht beim Landeanflug.... :-/ > Game Over *Insert Coin* Klar. Ein AKW schaltet man auch nicht einfach aus. Ich vermute mal, daß die Anwendung kein AKW oder Airbus steuert. Und wenn für die Anwendung ein RESET ok ist, kommt das als "Design Pattern" auch in Betracht. (Ich selbst würd es zugegebenermassen ohne Reset machen). Zumal 3 Bytes per SPI auszugeben ist doch kein Drama, also warum nicht einfach ausgeben? Ausser, wenn die Übtrtragungsrate 1 Bit/Minute ist. Und selbst dann geht das über des SPI-IRQ (falls HW-SPI). Johann
Johann L. wrote: > Und wenn für die Anwendung > ein RESET ok ist, kommt das als "Design Pattern" auch in Betracht. (Ich > selbst würd es zugegebenermassen ohne Reset machen). Ich würds mal so formulieren: Da das Kind nun mal in den Brunnen gefallen ist, ist es vernünftig das Beste draus zu machen und einen Schwimmreifen hinterher zu werfen. Aber für die Zukunft merken dass ein Gitter am Brunnen das Ganze hätte verhindern können.
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.