Hallo, Wie handhabt ihr die Programmierung der IR Routinen? Versucht Ihr nur Flags zu setzen oder Variablen zu ändern, die dann im Hauptprogramm aufgerufen und endverarbeitet werden, oder packt ihr schon größere Längen Code in den Interrupt? Oder unterscheidet sich das gar für die Art des IR (Timer, Capture)? Vorteil von wenig Code im IR ist die schnelle Rückkehr ins Programm, und mögliche "Aufarbeitung" ansonsten verpasster IR's. Das Problem ist dann natürlich, dass man im Hauptprogramm die Flags und Variablen pollen muss, was sinnlos Zyklen verheizt. Viel Code im ISR dagegen spart zwar Zyklen, aber wenn im Hauptprogramm was zeitkritisches läuft (UART kommt rein...), könnte man da Probleme bekommen. Irgendwelche Tipps oder Ansichten, oder frei nach soviel wie nötig und so wenig wie möglich?
Tine Schwerzel schrieb: > Hallo, > > Wie handhabt ihr die Programmierung der IR Routinen? Versucht Ihr nur > Flags zu setzen oder Variablen zu ändern, die dann im Hauptprogramm > aufgerufen und endverarbeitet werden, oder packt ihr schon größere > Längen Code in den Interrupt? ... Hallo, es gibt eine 3. Möglichkeit: nur minimale Bearbeitung (Daten holen, Flag rücksetzen usw.), dann aber nicht Rückkehr ins Hauptprogramm (oder was immer sonst gerade läuft), sondern in eine Fortsetzungsroutine der Interrupt-Bearbeitung. Das setzt natürlich perfekte Kenntnis des Befehlsablaufs, des Stackaufbaus usw. voraus. Und da diese Routine mit niedrigerer Priorität läuft und selbst interrupted werden kann - das ist ja der Zweck der Sache - sollte man auch in threaded und concurrent programming fit sein. Im Prinzip nutzt man so verschachtelte Interrupts obwohl sie der Prozessor nicht kann. Die Fehlersuche kann recht schwierig werden. Wenn der Prozessor nested interrupts kann, braucht man das nicht. Gruss Reinhard
Ich packe immer soviel Code wie möglich in die ISR. Die Obergrenze gibt die noch verfügbare Rechenzeit an. Wenn der Controller sonst nicht viel zu tun hat und dies auch nicht sonderlich Zeitkritisch ist kann die ISR ruhig etwas Voluminöser ausfallen.
Ich habe es bisher immer so gehandhabt, möglichst nur einen Timer-Interrupt zu benutzen. Das Hauptprogramm selbst agiert nur über Variablen im Speicher und "sieht" bestenfalls von der Hardware gar nichts. Wenn etwas für einen Timerzyklus zu lange dauert (z.B. Umwandlung von PS2-Tastatur nach ASCII), dann teile ich das über eine FSM auf mehrere Slots auf. Um beim Beispiel PS2-Tastatur zu bleiben, das Hauptprogramm nutzt letztendlich nur den aktuellen Tastaturcode (oder 0 wenn keine Taste gedrückt ist), den letzten Tastaturcode (wird nicht auf 0 zurückgesetzt) und ein Byte mit dem Status der Shift-, Control- und Alternate-Tasten. Für eine mobile Anwendung lese ich dann z.B. Tasten an einem Port ein und schreibe die entsprechenden Codes (Pfeiltasten, ENTER, ESC). Dem Hauptprogramm ist es dabei letztendlich egal, woher die Daten kommen. Ich kenne aber auch genug Leute in verschiedenen Foren , die so eine Herangehensweise für absolut grauselig halten, weil sie der "reinen Lehre von den minimalen Interruptroutinen" wiederspricht. Jörg
Joerg Wolfram schrieb: > weil sie der "reinen > Lehre von den minimalen Interruptroutinen" wiederspricht. Du solltest nicht so tun, als wäre das ein grundloses Dogma. Ich halte diesen Ansatz "Fasse dich kurz" - zumindest für meine Projekte - auf jeden Fall für richtig. Ein Interrupt hat meist den Zweck, eine Aktion auszulösen, die einigermaßen eilig erledigt werden muss. Hätte ich alle Zeit der Welt, bräuchte ich keinen Interrupt. Dann würde es reichen, im Hauptprogramm das Flag bei Gelegenheit abzufragen. Verschiedene Interrupts blockieren sich aber. ("NOBLOCK" widerspricht da meiner Vorstellung von "eilig") Deshalb sollte jede ISR den Controller möglichst schnell wieder frei geben. Dein Beispiel mit der PS/2-Tastatur (vermutlich über USART angeschlossen) würde ich zum Beispiel so gestalten, dass die ISR die Scancodes entgegen nimmt und wie bei entsprechenden UART-Routinen erst einmal in einen Puffer schreibt. Nur das Vorkommen einer Unterbrecher-Taste würde ich noch detektieren und ein entsprechendes Flag setzen. Die Auswertung der Tasten (Shift usw.) würde erst die Routine übernehmen, die eine Taste zurück liefern soll. Im Gegensatz zu dir nutze ich auch eher mehr Interrupts. Das ist sicher auch ein Grund, warum ich hier eine anderen Standpunkt habe. Gruß, DetlevT
Möglichst wenig in der Interrupt-Routine ist bei einfache Behandlung ohne sei() Pflicht. Daraus kann man aber keine Regel machen. Z.B. habe ich eine Firmware so aufgebaut, dass im Hauptprogramm nur der Bildspeicher vom Controller zum Display übertragen wird, und das ohne Pause in einer while(1)-Schleife. Das eigentliche Programm, bzw. seine verschiedenen Teile, werden von einem Timer-Interrupt, je nach Wichtigkeit, verschieden oft aufgerufen. Mit dieser Methode ist garantiert, dass alles Wichtige im Timer abgearbeitet wird und das Display nur die wirkliche Freizeit verbraucht. Mann muss sich aber in nested Interruptus rein denken, denn der Timer-Interrupt unterbricht sich dabei selbst.
Hallo Zwölfliter, wir sollten vielleicht einmal klar stellen, was unter "ISR" zu verstehen ist, sonst reden wir aneinander vorbei. Ich verstehe unter einem ISR eine Routine, die Hardware (intern oder extern) möglichst zeitnah zu einem Ereignis ansteuern soll. Was du da beschreibst, nutzt den Interrupt als technisches Mittel, um ein Multi-Threading-System mit unterschiedlichen Prioritäten zu erzeugen. Das ist etwas ganz anderes als was ich mit da vorstelle. In solchen Fällen würde ich wohl eher auf ein entsprechendes (Pseudo-) RTOS zurückgreifen. Gruß, DetlevT
Eine Interruptroutine muß soviel erledigen, wie bis zum Eintreffen des nächsten Interrupts unbedingt erforderlich ist. Bei einem Zyklus von 1ms reicht es aus, die Dekodierung von Tastatureingaben im weiterverarbeitenden Programmteil auszuführen, da höchstens alle 0,1s ein neues Ereignis auftritt UND ein verlorener Tastendruck nicht so schlimm ist; man drückt noch einmal. Liefert eine Mechanik ein Ereignis, was gezählt werden und immer stimmen muß, gehört die Verarbeitung in den Interrupt. Soll ferner ein Timer mit 1ms hochgezählt werden, muß dies in der Routine erfolgen. Das leuchtet ein :-) Was nie in eine Interruptroutine gehört, sind Warteschleifen, die über wenige µs hinausgehen.
Ferkel schrieb: > ...ein verlorener > Tastendruck nicht so schlimm ist; man drückt noch einmal. Solche Geräte kenne ich auch, und ärgere mich jedesmal. Das muss einfach nicht sein. Wenn für sowas keine Zeit übrig ist, hat der Entwickler was falsch gemacht.
Hallo, pauschal kann man das nicht sagen: Wenn ich nur einen einzigen Timer-Interrupt habe (z.B. DCF77-Uhr) dann ist fast der ganze Code im Interrupt (Multiplexen, Signalerfassung, Tastaturabfrage). Im Hauptprogramm ist dann nur noch der 1-Sekunden Tick. (Decodieren, Zeit hochzählen usw). Wenn ich mehrehre zeitkritische Interrupts mit unvorhersehbaren Zeitabständen habe dann wird im Interrupt nur der erfaßte Wert in einen Ringpuffer gepackt oder ein Flag (Tastaturabfragetick, Sekundentick) fürs Hauptprogramm gesetzt. bei einem 16MHz AVR komme ich dann auf max. ca 60 Takte = 5us Interruptdauer. Gruß Anja
>Solche Geräte kenne ich auch, und ärgere mich jedesmal.
Natürlich, aber ich meinte mit diesem Beispiel die unerwartete, absolute
Ausnahme, die nie auftreten wird. Falls doch, wäre es dennoch nicht
schlimm.
Wenn der Endschalter eines Antriebes nicht erkannt wird, wäre dies
fatal.
Siehe Interrupt und Multitasking. Das richtige Pollen von Flags verbrät keine nennenswerte CPU-Leistung. MFG Falk
Ferkel schrieb: > Wenn der Endschalter eines Antriebes nicht erkannt wird, wäre dies > fatal. Klar, dafür würde ich noch gleichzeitig den Motorstrom überwachen, zusätzlich ein Zeitlimit per Timer setzen und wenn alles nichts hilft per Watchdog abschalten. Das alles darf mich aber nicht daran hindern, das "Human-Interface" am Leben zu halten. Der Frustrierte am Not-Aus-Schalter kann mehr Mist bauen, als Du Dir vorstellen kannst.
Das hängt davon ab, ob die CPU Interruptprioritäten hat oder nicht. Ohne Prioritätslevel behindern sich alle Interrupts gegenseitig. Dann müssen alle Interrutps zusammen weniger Zeit benötigen, als der schnellste Interrupt. Hast Du z.B. einen Timerinterrupt alle 100µs auf einem AVR mit 16MHz, dann darf die Worst-Case Summe aller Interrupts max 1600 Zyklen betragen. Bei MCs mit Prioritäten (z.B. 8051) kann man das deutlich entspannter angehen. Nur die Interrupts mit hoher Priorität müssen schnell genug sein. Daneben sollte aber auch die Mainloop nicht zu stark verzögert werden. Z.B. ein Userinterface sollte schon alle <300ms zum Zug kommen. Peter
Tine Schwerzel schrieb: > Ansichten, oder frei nach soviel wie nötig und so wenig wie möglich? Die Programmierin ist der Gott des Computers. Letzterer macht es so wie DU willst und es vorschreibst. Wichtig ist nur, dass DU am Ende sagen kannst: "Und sie sah, dass es gut war." Wenn es zuverlässig wie gewünscht funktioniert, ist es ok. Im Glauben an das gute Programmieren brauchen wir keine Religionskriege. Ich bin ein Fan davon, zu messen, wie lange so eine ISR dauert. Das kann über einen Pin geschehen, der am Anfang der ISR gesetzt und am Ende zurückgesetzt wird (dann mit Oskar o.ä. anzeigen), das kann über ein Flag gemacht werden, mit dem die ISR prüft, ob sie rekursiv aufgerufen wird, das kann man manchen, indem die ISR die Zeit für ihren eigenen Ablauf misst und den Maximalwert irgendwo abrufbar abspeichert. So kann man dann gut einschätzen, ob man eine flinke Gazelle oder einen unter dem eigenen Gewicht sich erdrückenden Saurier programmiert hat.
Grolle schrieb: > Die Programmierin ist der Gott des Computers Die Programmzeile "Es werde Licht" hat bisher noch kein Compiler angenommen - oder ist das nur bei mir so?
Klaus 2m5 schrieb: > ist das nur bei mir so? Du verstehst halt nicht, worum es hier geht, lässt nur mal wieder ein wenig Verspottung am Überlauf raus, und schreibst deswegen komplett OT.
Peter Dannegger schrieb: > Bei MCs mit Prioritäten (z.B. 8051) kann man das deutlich entspannter > angehen. Nur die Interrupts mit hoher Priorität müssen schnell genug > sein. IMO verschiebt man das Problem damit aber nur. Denn wenn etwas nicht schnell sein muss, warum dann überhaupt ein Interrupt? Dann kann ich auch in der Hauptschleife nur das Flag pollen. Das sieht vielleicht nicht so elegant aus, erfüllt aber seinen Zweck. Wichtiger als irgendwelche Hardware-Spezialitäten ist es wohl, am Anfang eine ordentliche Aufstellung der zeitlichen Anforderungen aufzustellen. Diesen Punkt überspringen viele aus Faulheit und versuchen dann erst am Ende, den Schaden zu beheben. Gute Planung sind nach meiner Erfahrung 4/5 der Programmiererei.
>Denn wenn etwas nicht >schnell sein muss, warum dann überhaupt ein Interrupt? Damit man nicht unnütz Zeit vergeudet, immer wieder ein Ereignis abzufragen und dennoch optimal schnell reagieren kann. Auch bei geringer Baudrate ist es besser, per RX-Interrupt einen Ringpuffer zu füllen, als dauernd das RXC-Bit zu pollen.
Klaus 2m5 schrieb: > Grolle schrieb: >> Die Programmierin ist der Gott des Computers > > Die Programmzeile "Es werde Licht" hat bisher noch kein Compiler > angenommen - oder ist das nur bei mir so? http://www.bible-reading.com/godpc.html
Tine Schwerzel schrieb: > Wie handhabt ihr die Programmierung der IR Routinen? Versucht Ihr nur > Flags zu setzen oder Variablen zu ändern, die dann im Hauptprogramm > aufgerufen und endverarbeitet werden, oder packt ihr schon größere > Längen Code in den Interrupt? Es kommt nicht so sehr auf die Länge des Codes in der ISR an, sondern auf die Zeit, die der Code im Interrupt verbrät. Man kann durchaus größere Zustandsautomaten in der ISR programmieren. Wenn pro Interrupt-Durchlauf davon nur ein Bruchteil durchlaufen wird, ist alles im grünen Bereich. Ich selbst habe für IRMP eine ellenlage ISR geschrieben - da würden sich bei einigen hier die Zehennägel hochkringeln. Tatsächlich wird aber pro Zustand... - Es bleibt "hell" - Es bleibt "dunkel" - Wechsel von "dunkel" auf "hell" - Wechsel von "hell" auf "dunkel" nur immer ein Teil des Codes durchlaufen. Während der meisten Zeit (keine Taste an der IR-Fernbedienung gedrückt) ist die Durchlaufzeit sogar verschwindend gering. So kann ein 2 bis 3 kb großer Code durchaus auch in einer ISR vertretbar sein. Daher halte ich die generelle Regel "Der Code in der ISR soll möglichst kurz sein" für absurd und falsch. Eher könnte es heissen: "Eine ISR sollte möglichst schnell beendet werden" Dies lässt sich durch intelligente Verarbeitung/Beschreibung von Zuständen (Flags) erreichen. Gruß, Frank
> Die Programmzeile "Es werde Licht" hat bisher noch kein Compiler > angenommen - oder ist das nur bei mir so? Du mußt das natürlich korrekt formulieren. Oder hast du noch nie eine LED angesteuert?
Es hängt sehr von der Aufgabe ab. Gerde die Programme auf kleinen µCs sind sehr verschieden. Wenn es nur darum geht ein Falg zu setzen, braucht man oft gar keine ISR mehr, das Flag kann man oft direkt aus der Hardware Auslesen. Die Länge der ISR reicht von nur einen Pin toogeln bis zum Extremfall wo praktisch das ganze Programm in einer ISR steht, inclusive Wartezeiten im Sekundenbereich: das Hauptprogramm hat nur noch die Initialisierung und eine Endlosschleife, die den µC in den Stromsparmodus versetzt. Die Regel die ISR kurz zu halten, ist vor allem ein erster Anhaltspunkt, vor allem für Anfänger. Wenn man weiss was man macht, ist fast alles erlaubt, auch eine ISR nicht normal zu beenden, sonderen nach Korrektur des Stacks an eine bestimmte Stelle des Hauptprogramms zu springen. Wenn jemand anders das Programm verstehen soll, braucht es dann aber guter Kommentare und ggf. etwas länger.
Wenn ich keine IRQ-Prioritäten verteilen kann, darf jede ISR nur genau so lange dauern, wie sie keine weitere ISRs behindert. Der Rest ist doch nur Faustregel "Wie gehe ich als Anfänger ran". Nutze ich einen µC für hardwarenahe Signalverarbeitung, kommt manchmal raus, dass sich der Prozessor zu 99% seiner sinnvollen Prozessortätigkeit in IRS-en betätigt...
Juergen schrieb: >> Die Programmzeile "Es werde Licht" hat bisher noch kein Compiler >> angenommen - oder ist das nur bei mir so? > > Du mußt das natürlich korrekt formulieren. > Oder hast du noch nie eine LED angesteuert?
1 | LED0_DDR = 1; |
2 | LED0 = LED_ON; // "Es werde Licht" |
funktioniert bei mir einwandfrei. Peter
Peter Dannegger schrieb: > Juergen schrieb: >>> Die Programmzeile "Es werde Licht" hat bisher noch kein Compiler >>> angenommen - oder ist das nur bei mir so? >> >> Du mußt das natürlich korrekt formulieren. >> Oder hast du noch nie eine LED angesteuert?
1 | LED0_DDR = 1; |
2 | LED0 = LED_ON; // "Es werde Licht" |
> > funktioniert bei mir einwandfrei. Funktioniert das auch mit einer LED von der Größe einer Sonne? Ich muss in 7 Tagen mit allem fertig sein!
ich unterscheide das immer noch art des iinteruppts. Bei timerinterupts pack ich einfach so viel rein wie geht, ohne das der nächste overflow interupt oder so stört. Bei ADC und AC interupts ist das etwas kritischer. Da pack ich nur sehr wenig rein. und verarbeite die daten anschließend im hauptprogramm.
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.