Hi, habe nichts zu dem Thema gefunden, da mir kein passendes Stichwort dazu eingefallen ist. Und zwar hab ich folgendes Problem. Überall wird ja immer gesagt das man seine Interrupts möglichst klein halten soll, deshalb hab ich mir überlegt ob man nicht aus der Interruptroutine springen und diese gleichzeitig beendet aber nicht zu der Stelle an der sie ausgelöst wurde zurückspringt. Das soll so was werden wie ein Notausschalter. Der so aussieht: In einem Timer Interrupt wird abgefragt ob ein Schalter gedrück ist, wenn er gedrückt ist spring das Programm in eine Schleife in der es etwas anderes arbeitet, bis ein anderer Schalter gedrückt wird. Das ganze sollte in C sein (CodeVision). Wahrscheinlich gibt es eine ganz einfache Lösung auf ich einfach nicht komm... Hoffe ihr habt verstanden was ich mein. mfg milka
In C geht das nicht so simpel, ausser du benützt den inline-Assembler. Man könnte das z.B. so lösen, dass in der Interrupt-Routine als erste Anweisung der Stack etwas manipuliert wird, sprich die Rücksprungadresse (der uP rettet ja den PC auf den Stack, bevor er in die Int Routine springt) ändert. Also etwa so: (Hauptprogramm) InterruptRoutine: pop Temporäres_Register mov Temporäres_Register,neue_Adresse push Temporäres_Register reti In 8051 Assembler. Das Programm macht folgendes: Sobald der Interrupt aufgerufen wird, wird der letzte Wert, der auf den Stack 'gepusht' wurde, zurückgeholt. Ggf. musst du noch nachschauen, was für Werte dein uP evtl. sonst noch auf den Stack rettet. Danach wird das Register, wo die alte Rücksprungadresse drin war, überschrieben (wir brauchen die Adresse ja nicht mehr, trotzdem muss sie aus dem Stack raus, da der ja sonst überläuft). Das Register kann jetzt wieder auf den Stack 'gepusht' werden, und mit reti springt der uP aus der Interruptroutine und das Interrupt pending-Flag wird gelöscht. Als Rücksprungadresse verwendet er den wert, den du zuvor auf den Stack gelegt hast. Möglicher Lösungsansatz, sicher nicht elegant, funktioniert aber. Und nun eine Gegenfrage: WOZU soll das dienen? Du kannst ja in deiner Interruptroutine auch nur ein Flag setzen, das dann im Mainloop geprüft wird, sodass dann das Programm ggf. an einer anderen Stelle fortfährt.
Hallo, naja, ohne jetzt über Grundsätzliches zu diskutieren... In ASM waren solche Tricks frücher durchaus üblich und eigentlich auch kein Problem, wenn man wusste, was man tat. Beim AVR: am Ende der IRQ mit 2x POP die dann nutzlose Rücksprungadresse vom Stack holen, das neue Ziel mit PUSH auf den Stack und RET. 2 Randbedingungen: das Register, welches zum pushen der Rücksprungadresse genutzt wird, hat in der angesprungenen Routine ungültigen Inhalt. Durch RET statt RETI wird der IRQ nicht wieder freigegeben, das muß die Routine erledigen, nachdem sie für definierte Zustände zur Fortsetzung gesorgt hat. Ist für bestimmte Anwendungen eine durchaus sparsame Lösung. Hauptnachteil: man muß solche Tricks auf das Verhalten des konkreten Prozessortyps zuschneiden, kein Schwein kann sowas hinterher ohne gute Kommentare lesen (auch man selbst nach 3 Monaten nicht...). Hauptvorteil: man kann zu jeder Zeit geziehlt eine solche Aktion auslösen, ohne x Abfragen einbauen zu müssen, solange der IRQ freigegeben ist. Warum hat der AVR keinen NMI??? ;-(( In C? Ich behaupte mal, keine Chance ohne ASM-Hilfe. Gruß aus Berlin Michael
@Michael: In C könnte es höchstens gehen, wenn man irgendwie auf den SP zugreifen kann. Evtl geht das irgendwie, auch ohne ASM-Hilfe. Aber ich stimme dir zu, ich würde sowas auch eher in Assembler schreiben. Jedenfalls den Teil, wo die Adressen vertauscht werden. Dazu gibts in C ja den Inline Assembler. @Timo: Ich weiss nicht, ob du den Inline Assembler kennst. Wenn nicht: Im C-Quellcode kannst du schreiben asm { ; normaler Assemblercode }; Falls es dein Compiler unterstützt. Wäre aber schade wenn nicht ;)
>n einem Timer Interrupt wird abgefragt ob ein Schalter gedrück ist, >wenn er gedrückt ist spring das Programm in eine Schleife in der es >etwas anderes arbeitet, bis ein anderer Schalter gedrückt wird. Offensichtlich geht es um sowas wie eine (Maschinen)steuerung. Das bedeutet, um es sauber zu programmieren, hast du ja eh eine (technologischen) Ablauf im Form einer Schrittkette, bzw. eines Zustandsgraaphen. Wenn du jetzt einen zusätzlichen Schritt einführst, desssen erreichen nur durch die Interruptroutine realisiert wird, hast du das sauber gelöst. In diesem Fault-Step kannst du solange verharren bis der andere Taster betätigt wird. Dann musst du (aus technologischen) Gründen eh (meist) wieder von vorn anfangen... Etwa so:
1 | ISR ( Timer ) |
2 | {
|
3 | if (Notaus betätigt) anlagenzustand=c_fehler; |
4 | }
|
5 | ...
|
6 | main
|
7 | {
|
8 | switch (anlagenzustand) |
9 | {
|
10 | case c_allesgut1: |
11 | ...
|
12 | case c_allesgut2: |
13 | ...
|
14 | case c_allesgut3: |
15 | ...
|
16 | case c_fehler: |
17 | ... // fehlerauswertung |
18 | if (anderer TAster gedrückt und Notaus losgelassen) anlagenzustand=... |
19 | }
|
20 | }
|
Hallo Timo, Prinzipiell geht das schon, so wie Du es beschreibst. In den meisten Prozessoren wird die Ruecksprung Adresse auf dem Stack abgelegt. Du kannst dann vor Verlassen des Interrupts die gewuenschte Zieladresse auf den Stack legen und ab gehts dahin. Die dortige Funktion sollte allerdings alle Register und Prozessor-Stati retten, damit sie bei Verlassen (was Dich an den orginalen Punkt zurueckbringt) keine Datenverluste verursacht. Je nach Compiler kann man das manchmal mit dem Zusatzwort "monitor" oder "interrupt" erreichen. Besonders empfehlenswert ist dieses Vorgehen aber nicht, denn Deine Hauptfunktion kann ja JEDERZEIT unterbrochen werden - selbst im unguenstigsten Moment. Dies kann zu sehr unvorhergesehenen Effekten fuehren. So bleibt vielleicht ein LCD nur zur Haelfte aktualisiert, oder ein PWM-Dimmer dreht ploetzlich 100% auf, oder ein kurzer Pieps aus dem Lautsprecher wird zur nervenden Sirene. Der normale Weg waere, im Interrupt ein Flag zu setzen "sonder-funktion gewuenscht". Im normalen Programm wird das Flag dann an den Punkten abgefragt, an denen ein problemloser Uebergang in die Sonderfunktion moeglich ist (zB zu Beginn der Hauptschleife). Bei komplexeren Systemen ist ein Multitasking Betriebssystem die beste Loesung. Ein Taskswitch ist im Endeffekt genau das was Du beschreiben hast. Darueber hinaus stellt Dir ein Betriebsystem aber eine Reihe von Werkzeugen zur Verfuegung, mit denen Du den Kontextwechsel kontrolliert ausfuehren lassen kannst. Gruss, Marc
Hallo Timo, als Grund für Deinen Wunsch führst Du an, dass man die Interrupt-Service-Routine möglichst kurz halten sollte. Bei einem echten Not-Aus-Schalter wäre mir das egal. Ansonsten weiß ich nicht, wie zeitaufwendig Dein main() Programm ist. Kann Dein main() Programm nicht einfach den Schalter-Zustand abfragen? Oder würde dann auf den Notfall nicht schnell genug reagiert? Etwa so: #include <stdbool.h> #include <avr\interrupt.h> // oder wie es bei Dir heißt volatile uin8_t Schalter_Zustand=0; ISR (Schalter_interrupt_vektor) { ++Schalter_Zustand; } int main(void){ switch (Schalter_Zustand) { case (0): { // tu was, wenn Schalter inaktiv } break; default: { // warte auf 2. Schalter } }; return 1; } Ginge natürlich auch mit while(), aber so hast Du die Möglichkeit, noch weiter Zustände abzuarbeiten. Wie gesagt, das hängt alles davon ab, wie häufig main() die switch() Bedingung überprüft. Gruß Fred Oooops, da habe ich zu lange getippt, Matthias hat das ähnlich geschrieben.
Hallo, vielen Dank für alle Antworten und vor allem so schnell! Am Anfang habe ich immer ein Flag gesetzt und diese abgefragt aber das schien mir nicht sehr gut für einen Notaus Schalter, weil in der Main Schleife etliche Delays und so was sind. Außerdem sollte man das Lcd noch ansteuern können während die Maschine angehalten ist, also kann ich das Programm nicht in dem Interrupt Schleifen lassen, was aber für einen Notaus Schalter auch eine Lösung wäre. Die Idee mit der Änderung der Rücksprungaddresse finde ich sehr gut, ich werde mich mal ausgiebig damit befassen. Mein Ursprüngliches Problem mit dem ich dann über viele Umwege auf dieses andere Problem gestossen bin war, dass ich eine Abfüllmaschine durch Tastendruck zum anhalten bringen wollte. Am Anfang machte ich diese durch setzen eines Flags. Nun funktionierte alles sehr gut blos ab und zu "resetete" der uC neu oder das LCD zeigte mist oder der uC sprach auf nichts mehr an so, dass ich nur noch durch eine Stromunterbrechung neustarten konnte. Also das Programm funktioniert zeitlich gesehen einwandfrei und es wird auch keine Spannung induziert (hatte zuvor ne Freilaufdiode vergessen^^). Manchmal kann ich die Maschine sehr oft hintereinander anhalten ohne das so etwas passiert, machmal aber auch passiert das ein paar mal hintereinander. Eine Wackelkontakt vermute ich nicht. Was könnte es aber dann sein? Was vermutet ihr?
@ Timo Benesch (milka) >Die Idee mit der Änderung der Rücksprungaddresse finde ich sehr gut, ich >werde mich mal ausgiebig damit befassen. Das denke ich nicht. Solche Taschenspielertricks sind selten notwendig und sinnvoll. Mach lieber eine solide State Machine etc. Und Delays haben in einem normen Programm mit (Timer) Interrupts sowieso nix verloren! >passiert das ein paar mal hintereinander. Eine Wackelkontakt vermute ich >nicht. Was könnte es aber dann sein? Was vermutet ihr? Prellen des Tasters. Entprellung MFG Falk
> Was könnte es aber dann sein? Was vermutet ihr? Spaghetti-Code. Offensichtlich hast du Wollknäul-Programmierung betrieben: >Main Schleife etliche Delays => Hälts du dort etwas das prg an, a la delay(xx) ?? >Lcd noch ansteuern können während die Maschine angehalten => Da tut sich delay schlecht.. >Änderung der Rücksprungaddresse =>Ist ganz großer Murks! >Abfüllmaschine => Zustandsautomat >Nun funktionierte alles sehr gut blos ab >und zu "resetete" der uC neu ode => Also funktioniert es eben NICHT sehr gut... Du hast nur eine Chance: Du musst aufhöre, nach Trial&Error zu programmieren und dir VORHER ein Konzept erstellen. -> was soll der reihe nach gemacht werden, um die Maschine arbeiten zu lassen -> wie soll das visualisiert werden? ich mache bei sowas IMMER zwei UNABHÄNGIGE programme (diese können ja der reihe nach in main aufgerufen werden) Das eine Programm steuert die Maschine. Dieses in NUR mit Zustandsgrafen, also mit Schrittketten realisiert. ALs Transitionen könne ja zB Taster dienen. Das zweite Programmteil (Visu) tut jetzt nur den Schrittkettenvariablen (also das, was in switch steht) entsprechende Bildchen zuweisen und das darstellen. Falls gebraucht, kann die Visu auch Parameter anzeigen und ändern... Sonst wird das nichts.. Beitrag "Re: Programm bzw. Ablaeufe steuern"
@ Falk Brunner (falk) Warum benutzt man keine Delays wenn man gleichzeitig Timer benutzt? Weil sonst die Zeit verlängert wird durch den Interrupt? Und was ist eine Solid State Machine? Ich glaube nicht, dass es am Prellen liegt da bei dem einem Taster ja ein Flag gesetzt wird also beim prellen dann halt öfters und mit dem anderen Taster wird es gelöscht. Wieso sollte es daran liegen? mfg milka
>Wieso sollte es daran liegen?
Weil das Setzen der Flags durch Interrupts geschieht, bei Dir.
Und das permanente Unterbrechen des Programmes zum Flagsetzen (beim
Prellen) ist der Programmabarbeitung nicht wirklcih förderlich
Hallo Timo, > ...Und was ist eine Solid State Machine? Die gibt's nicht. Gemeint war wohl eine solide State Machine, zu Deutsch "Zustandmaschine", oder "endlicher Automat". Hier eine schöne englische Definition: http://www.nist.gov/dads/HTML/finiteStateMachine.html Hier im Forum gibt es auch eine gute Zusammenfassung - finde ich nur gerade nicht. Gruß Fred
Ok da muss ich doch noch einiges klarstellen (mein schönes Programm verteidigen^^). Das Programm mag zwar nicht perfekt sein aber es funktioniert! Der Fehler tritt nur manchmal auf liegt vieleicht doch an einem Wackler.. Mit dem Prellen hat es wirklich nichts zu tun da die Erkennung in einem Timer Interupt steck, vieleicht hab ich das nicht erwähnt, sorry. Und zum Delay wenn ich was abfülle dann wird bei mir das Ventil geöffnet dann folgt ein Delay und dann geht es weiter, also d.h. ja nicht direkt unterbrechung, Interrupts können ja noch ausgelöst werden. Naja jedenfalls Danke an alle, ich werde mir so einiges nochmal genauer anschauen müssen. Mfg Milka
>ann folgt ein Delay und dann geht es weiter,
Das ist schon murks. Dafür fügt man einen extra Zustand ein, der nach x
(Milli)sekunden verlassen wird!
Siehe meinen Link
@ Timo Benesch (milka) >verteidigen^^). Das Programm mag zwar nicht perfekt sein aber es >funktioniert! Mag sein, aber das tut Windwos auch seit über 20 Jahren . . . ;-) >Der Fehler tritt nur manchmal auf liegt vieleicht doch an einem >Wackler.. Möglich, nach deinen beschreibungen aber unwahrscheinlich. Deine Beschreibung ist nicht viel wert, man müsste den Quelltext sehen. MFG Falk
Der AVR hat zwar keinen NMI, einen Reset hat er aber schon. Warum nicht den für Notaus benutzen? Deine Firmware im AVR setzt vielleicht eh schon nach einem Reset alles in den Start- bzw. Auszustand. Oder: Warum nicht den Watchdog aktivieren? Notaus löst Interrupt aus, dieser erledigt alles Notwenige und geht in eine Endlosschleife, womit der Watchdog auslöst -> Reset. Nebenbei bringt der Watchdog auch das mehr an Funktionssicherheit. Alternativ kann der Prozessor auch beim Hochfahren erkennen ob der WDT ausgelöst hat und entsprechend darauf reagieren. Für einen Notaus halte ich so eine Vorgehensweise durchaus angebracht, weil sehr sicher. Die verbogene Rücksprungadresse im Interrupt hätte die Folge, dass der Fordergrundprozess keine Unterprogramme mehr aufrufen darf, da sonst der Stackinhalt "korrupt" wird. Wenn, dann ein longjmp aus dem Interrupt und man gelangt sauber zum Aufruf von setjump (mit einem Parameter!). Etwas unkonventionell, aber der OP wollte es ja so. longjmp darf aber nicht direkt verwendet werden, weil dieser am Ende die Interrupts nicht wieder einschaltet. Evtl. kann man das auch vorher machen.
> Deine Beschreibung ist nicht viel wert, man müsste den Quelltext sehen.
Naja das will ich euch nun nicht antun. Ich muss schon zugeben es ist
sehr viel "Wollknäul-Programmierung" dabei. Weil das ist mein erstes
größeres Projekt und da fällt mir immer noch was ein was man dazu machen
kann und so wirds immer komplexer und Fehleranfälliger. Ich brauch das
Teil eigentlich nur noch morgen und der Fehler ist schon lang nicht mehr
aufgetreten. Und wenns doch passiert einfach mal schütteln oder sowas^^
Mfg
Milka
@ Timo Benesch (milka) >Naja das will ich euch nun nicht antun. Ich muss schon zugeben es ist >sehr viel "Wollknäul-Programmierung" dabei. Aha, wir nähern uns langsam der Wahrheit . . . ;-) MfG Falk
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.