kann man Interrupts schachteln, d.h. wenn ich eine A/D Wandlung mache, beim Fertig-Interrupt das Ergebnis an die RS232 Schnittstelle zum PC senden. Die Programmierung in dieser Form ist übersichtlich, aber wahrscheinlich nicht zweckmäßig, sofern das überhaupt geht. Wenn A/D Interrupt, Timer-Interrupt von mehreren Timern zusammentreffen, sehe ich Schwierigkeiten. Timer-Interrupt müßte sofort bearbeitet werden und RS232 z.B. müßte die Baudrate eingehalten werden. Wenn 1 Timerint. bearbeitet wird, dann kann der andere Timerint. nicht bearbeitet werden und es entstehen Fehler. mfg Quehl
Nein, es entstehen keine Fehler. Die Interrupt routinen muessen nur kurz genug sein. Der ADC Interrupt, liest den Wert und startet eine neue Wandlung. Ein Timer macht einen Reload und scheibt eine Boolean.
Wolfram Quehl wrote: > kann man Interrupts schachteln, d.h. wenn ich eine A/D Wandlung mache, > beim Fertig-Interrupt das Ergebnis an die RS232 Schnittstelle zum PC > senden. Die Programmierung in dieser Form ist übersichtlich, aber > wahrscheinlich nicht zweckmäßig, sofern das überhaupt geht. > > Wenn A/D Interrupt, Timer-Interrupt von mehreren Timern zusammentreffen, > sehe ich Schwierigkeiten. Timer-Interrupt müßte sofort bearbeitet werden > und RS232 z.B. müßte die Baudrate eingehalten werden. Wenn 1 Timerint. > bearbeitet wird, dann kann der andere Timerint. nicht bearbeitet werden > und es entstehen Fehler. > > mfg > Quehl Deshalb macht man die ISRs auch so kurz (und schnell) wie irgendwie möglich. Anstatt langwierige Aufgaben zu erledigen, sichert man nur die zeitkritischen (also die flüchtigen) Werte, setzt ein Jobflag für die Mainloop und kehrt zur Tagesordnung zurück. In der Mainloop werden dann die Jobflags geprüft und die zugehörigen Jobs ausgeführt, wobei der Job sein Jobflag wieder löscht. Einen ADC-Complete-Interrupt braucht man eigentlich nur, wenn man den Sleepmode ADC-Noise-Reduction nutzt, in dem die Timer abgeschaltet werden. Ansonsten kann man den ADC "im Vorbeigehen" in einem zyklisch aufgerufenen Job auslesen, ADC-Werte "verfallen" nicht, wenn man sie verspätet ausliest. ...
Ich weiß nicht genau, wie das beim mega32 ist, beim mega8/88/16/168 sieht es so aus: Beim Einsprung in einen Interrupt wird das Globale Interruptflag zurückgenommen und erst beim Verlassen des Interupts wieder gesetzt. Das führt dazu, dass der gerade aktive Interrupt durch keinen anderen (wie hoch prior auch immer) unterbrochen werden kann. Willst Du den Interrupt seinerseits wieder unterbrechbar machen, kannst Du das Interruptflag setzen (SEI). Das funktioniert auch! Jedoch ist der Stackbedarf entsprechend höher. Ciao Willi Wacker
> Ich weiß nicht genau, wie das beim mega32 ist, beim mega8/88/16/168 > sieht es so aus: Das ist bei allen AVRs so... Nested Interrupts (also unterbrechbare und damit verschachtelte ISRs) sollte man nur in Ausnahmefällen und auch dann nur unter Beachtung der möglichen Folgen verwenden. Es soll ja Leute geben, die versuchen, einen externen Level-Interrupt unterbrechbar zu machen und sich dann wundern, wenn es sofort bei Auftreten des besagten Interrupts zum Komplettabsturz kommt... Generell: Interrupt Handler so kurz wie möglich und alles, was nicht innerhalb der nächsten Mikrosekunde nach eintreten des Interrupt-Ereignisses abgearbeitet werden muss, ins Hauptprogramm schmeißen und in der ISR nur ein Jobflag setzen, das dann im Hauptprogramm abgefragt wird.
>ADC-Werte "verfallen" nicht, wenn man sie verspätet ausliest.
Das stimmt nicht ganz, bei freilaufendem ADC mit schneller
Samplefrequenz werden die alten Werte im ADCH/ADCL-Register schnell mal
überschrieben (in minimal 13µs). Wenn man die dann nicht abholt, gibt es
unnachvollziehbare Sprünge im Datenstrom. Bei einzeln ausgelösten
Wandlungen bleiben die alten Werte bis zum Ende der neuen Wandlung
gültig, was dann nicht so kritisch ist.
> Das stimmt nicht ganz, ...
Richtig, aber keine Aussage stimmt immer und überall ganz. Es gibt immer
einen Spezialfall, in dem allgemeine Aussagen nicht mehr ganz stimmen.
Wenn man mit einer (womöglich grenzwertigen) Samplerate eine
Wechselspannung erfassen (digitalisieren) will, dann bestimmt der ADC
natürlich das Timing, denn dann will man ja keine Abtastung verlieren.
Aber hier ging es um die Antwort auf eine Anfänger-Verständnisfrage und
nicht um das letzte Quäntchen Optimierung hart am Ende des Machbaren.
Deshalb habe ich mich etwas einfach ausgedrückt.
Gruß nach HBS...
...
Johannes M. wrote: >> Ich weiß nicht genau, wie das beim mega32 ist, beim mega8/88/16/168 >> sieht es so aus: > Das ist bei allen AVRs so... > > Nested Interrupts (also unterbrechbare und damit verschachtelte ISRs) > sollte man nur in Ausnahmefällen und auch dann nur unter Beachtung der > möglichen Folgen verwenden. Es soll ja Leute geben, die versuchen, einen > externen Level-Interrupt unterbrechbar zu machen und sich dann wundern, > wenn es sofort bei Auftreten des besagten Interrupts zum Komplettabsturz > kommt... > > Generell: Interrupt Handler so kurz wie möglich und alles, was nicht > innerhalb der nächsten Mikrosekunde nach eintreten des > Interrupt-Ereignisses abgearbeitet werden muss, ins Hauptprogramm > schmeißen und in der ISR nur ein Jobflag setzen, das dann im > Hauptprogramm abgefragt wird. Stimmt natürlich, ich habe allerdings einen Anwendungsfall, wo es wichtig ist, einen festen Takt einzuhalten, und dann läuft der entsprechende Timer-Interrupt 5ms (bei einem Takt von 10ms). Das ist schon recht gruselig! Damit während dieser Zeit auch andere (externe, extrem kurz, von mir!) Interrupts zum Zuge kommen, war der Dreh nötig. Ich hab das nur geschrieben, weil ich dachte, es interessiert. Ich hatte es so nicht erwartet. Möge es als abschreckendes Beispiel dienen! Ciao Willi Wacker
Willi, im Einzelfall kann es sinnvoll sein, in einer ISR andere Interrupts zuzulassen. Man sollte sich nur nicht daran gewöhnen, immer so zu programmieren. Dass (D)eine ISR 5ms dauert, sollte allerdings zu Denken geben, auch wenn sie nur alle 10ms aufgerufen wird. Ich vermute, da wäre sicher was mit Jobflag und Mainloop machbar gewesen. Aber wenn es funktioniert, ist es ja gut, das nächste Programm wird besser. Ich verbessere meine alten Programme, die aus heutiger Sicht schlecht programmiert sind, aber zuverlässig arbeiten, auch nicht mehr. Die nächsten werden besser - oder auch nicht... ;-) ...
die ADC sind ja recht schnell, wenn ich da warte und erst im Hauptprogramm irgendwann die Werte zum PC schicke, dann sind die vorhergehenden AD Werte schon überschrieben. Ein einfaches Abspeichern im RAM geht da so nicht. Man könnte einen Puffer organisieren, aber das find ich recht aufwendig, zumal der doch überläuft, weil das Ausgeben an die RRS232 sehr viel länger dauert. Ich denke da jetzt doch, im Interruptzweig des ADC die Daten zum PC zu schicken, aber da dann nur mit der Flagabfrage. Da könnte dann der Timerinterrupt dazwischengehen, denke ich. Der AD Interrupt dauert dadurch dann recht lange, aber Ausgaben zum PC dauern nun mal. mfg Quehl
Ich frage mich langsam, wo jetzt wirklich das Problem liegt. Es macht sowieso keinen Sinn, mit dem A/D-Wandler schneller zu wandeln, als die Daten im Programm verarbeitet oder an den PC gesendet werden können. Die Baudrate des UART (siehe OP) hat überhaupt nichts mit irgendwelchen Interrupts zu tun. Das UART ist eine komplett autarke Hardware-Einheit, die, einmal konfiguriert und mit einem Byte im Sende-Datenregister ganz unbeeindruckt vom Rest der Welt vor sich hinrattert. Wenn das UART fertig ist, meldet es über sein TXC- oder UDRE-Flag, dass das nächste Byte gesendet werden kann. Bei Implementierung einer Flusssteuerung ist ein bisschen mehr Interaktion erforderlich, aber das sollte für den µC kein Problem sein. Der ADC ist übrigens überhaupt nicht besonders schnell. Er braucht bei optimal eingestellter Taktfrequenz und maximaler Samplingrate (15 kSPS bei 200 kHz) immerhin fast 70 µs für eine Wandlung. In dieser Zeit kann der µC ne Menge anderer Dinge erledigen (wenn er nicht grad mit einer sehr kleinen Taktfrequenz läuft).
Da ich nicht nachvollziehen kann, was Du vorhast, kann ich Dir keinen konkreten Rat geben. Aber: Die Übertragung eines Bytes zum PC dauert nicht irgendwie "lange", sondern dauert eine exakt berechenbare Zeit. Ebenso kann man den Zeitbedarf des ADC auch exakt berechnen (oder anderweitig ermitteln). Da Du nun weißt (wissen kannst), wie schnell Du werden darfst, kannst Du auch das Programm dazu schreiben. Gehen wir erstmal davon aus, dass der AVR nichts weiter zu tun hat, als einen ADC-Wert in Echtzeit an den PC zu schicken. Dann wäre es sinnvoll: - Den ADC-Vorteiler so einzustellen, dass der ADC schneller ist als ein Byte per UART übertragen werden kann. Single Conversion starten. - TX mit Innterrupt laufen lassen, der Interrupt tritt auf, wenn UART für das nächste zu sendende Byte bereit ist. Dann im UART-Interrupt: - ADC auslesen und in UDR legen (UART hat also Nachschub und klappert im Hintergrund weiter) - ADC für nächste Messung starten, wenn diese fertig ist, bleibt der ADC stehen. Der Messwert auch, er wird dann im nächsten UART-Interrupt ausgelesen. Für die Mainloop bleibt erstmal nur eine leere Endlos-Schleife oder besser der Aufruf von SLEEP übrig, es sei denn, der AVR soll nebenbei noch andere Aufgaben erledigen (Zeit dazu hat er massig). Willst Du mehrere ADC-Quellen einlesen, so geht das genauso, nur dass die Quelle jedesmal umgeschaltet wird. Es ist auch möglich, im SRAM eine Liste mit allen ADC-Quellen anzulegen, in der der jeweils letzte Wert oder der Mittelwert über die letzten Messungen (z.B. über 256 Messungen, ist ein sehr einfacher Algorithmus) gehalten werden. Dann wäre es auch sinnvoll, den ADC im ADC-Interrupt auszulesen (Mittelwert bilden) und ins SRAM zu legen. Wenn die ISRs effizient geschrieben sind, stören sie sich nicht gegenseitig. Es gibt viele verschiedene Möglichkeiten der Realisierung, aber nicht jede ist für jeden Einzelfall optimal. Ohne Einzelheiten der Aufgabenstellung kommen wir also nicht weiter. ...
das Problem ist, daß ich beim AVR noch Anfänger bin und erstmal ein möglichst gutes Programm erstellen will, ohne dauernd zu testen, wie ich das beim PC sonst mache, weil der Flash nicht ewig hält. 2. Problem ist, daß ich beim PC ein Programm geschrieben hatte, daß 2x im Jahr abgestürzt ist, aber keine Folgen hatte. Nach 8 Jahren trafen da 2 Interrupts unglücklich zusammen, so daß es einen Absturz gab und meine angeschlossene heizung für 5000 Euro defekt ging. darum ist mir die Interruptverarbeitung und die Zeitabläufe besonders wichtig. Beim AD hatte ich nur den 200khz Takt im Kopf und dachte 5ms wäre die Umsetzung. War kleiner Irrtum von mir, weil ich nicht alles gleich im Kopf habe. Danke, werd das mal neu überdenken. mfg Quehl
Danke an Hannes u.a. so genau kann ich das noch nicht sagen, was ich machen will. Das ergibt sich aus den Möglichkeiten und den Ergebnissen nach der Programmierung. Das kann sich nachher durchaus als unbrauchbar herausstellen. Daher hier mal meine Gedanken zu den Aufgaben: Anlaß war, daß ich mit dem PC nicht alles programmieren konnte, was ich brauche. Von der Geschwindigkeit her ist der PC weitaus besser. Das sind mehr Ein-Ausgänge, Analoge Daten lesen, Timeraufgaben. Das kann der PC nicht in ausreichendem Maß. Es sollen Temperaturen eingelesen werden, wobei ich da verschiedene Ausführungen und Schnittstellen testen will. Für Feuchtemessung kommt der Sensirion in Betracht. PWM Eingänge habe ich schon mal im anderen Thread geschrieben. Lichtsensor und Beschleunigungssensor wollte ich auch mal ausprobieren. Ob ichs praktisch gebrauchen kann, ist allerdings fraglich. Uhr, DCF soll auch rein. Verbrauchsmessung diverser elektr. Geräte. Ausgaben auf Relais sollten an sich auch rein, muß ich aber mangels Pins erst mal zurückstellen. Vielleicht finde ich später noch Pins für ein Schieberegister. Zunächst wollte ich die Aufgaben im AVR so gering wie möglich halten und Ergebnisse an den PC senden und dann dort weiterverarbeiten. Es ist aber nicht ausgeschlossen, daß ich vielleicht später alles im AVR mache. Das wird sich später herausstellen. Aber ich suche deswegen auch jetzt schon nach optimalen Möglichkeiten, Zeit und Speicher verballern passiert sowieso von alleine. Dazu muß man sich nicht anstrengen. Wenn man aber später keine Resourcen mehr zur Verfügung hat und deswegen alles umstellen muß, ist das nicht so schön. Gründe für die Auswahl des ATMEGA 32: Kaum externe Hardware erforderlich. PC Programmierung mit Ponyprog möglich. Viel Speicher und Resourcen. Übersichtliche Programmierung und Anlehnung an PC Syntax. Entwicklungsbord für 14,95 verfügbar. DIP Gehäuse für einfache Lötung auf Leiterplatte. Programm für DS1820 steht zur Verfügung. (mußte ich nur wegen anderer zusätzlicher Hardware abändern) Nachteilig war, daß die Atmel Entwicklungumgebung nicht mit Win98 läuft und der Assembler zunächst nur in einer alten Version zur Verfügung stand. Erst später stand die jetzige Assembler Version auf der Atmel Seite. Jetzt geht es mir darum, die Pausenzeiten im Programm sich überlappen zu lassen, damit diese minimiert werden. Das, was ich will und worauf es ankommt, erfährt man eben erst im Laufe der Zeit und mit Eurer Hilfe. mfg Quehl
Hallo, auch das aktuelle AVR-Studio läuft unter Win98SE... Ein Minimum an Ram ist nötig (bei mir 96MB, mehr kann das alte Thinkpad nicht), der Simulator ist nicht nutzbar (Ram...). Starten dauert mit ca. 10 Files natürlich etwas, wenn alles offen ist, kann man aber durchaus damit arbeiten. Assemblieren läuft erträglich schnell, PonyProg lauft auch unter Win98 ohne Probleme. Ist mein Bastelstuben-Rechner, 233MHz P-II mobile. Zur Lebensdauer des Flash: hatten wir letztens hier im Forum. War irgendwas mit ca. 1 1/2 Jahre bei täglich 20x neu flashen... Mir ist daran noch kein AVR gestorben, andererseits liegt mit Sicherheit einer Reserve in der Schachtel. ;) Gruß aus Berlin Michael
> Jetzt geht es mir darum, die Pausenzeiten im Programm sich überlappen zu > lassen, damit diese minimiert werden. Das erfordert nur eine andere Denkweise beim Programmieren. Pausen dürfen keine Rechenzeit verbraten. Kann ein Job im Moment (noch) nicht ausgeführt werden, weil eine Bedingung (Zeitverzögerung einhalten, Busy abfragen) noch nicht erfüllt ist, dann ist gefälligst zur Mainloop zurückzuspringen, wo vermutlich noch andere Jobs zu erledigen sind. Ich realisiere mir meist mit einem Timer eine Art "Taktgeber", der mir die Jobflags entsprechend setzt. Diese Jobflags werden von der Mainloop geprüft und die Jobs ggf. aufgerufen. So wird z.B. alle 20ms die Entprellroutine aufgerufen, alle 1..2ms ein Zeichen aus dem Bildschirmspeicher ans LCD gesendet, usw... Wenn ein Job die Voraussetzung für einen anderen geschaffen hat, dann setzt er eben dessen Jobflag. Er wird halt bei der nächsten Runde der Mainloop mit ausgeführt. Muss z.B. ein längerer Impuls (definierter Länge) generiert werden, so wartet man nicht, bis seine Zeit abgelaufen ist, sondern setzt eine Timeout-Variable. Ein Job der Mainloop zählt diese (zyklisch) runter (falls sie nicht schon 0 ist) und schaltet beim Erreichen von 0 den Ausgang wieder ab. Und in der Zwischenzeit klappert die Mainloop die Jobflags ab und sucht sich sinnvollere Arbeit. Oftmals rufe ich die Jobs auch nicht als Unterprogramm (rcall/call-ret) auf, sondern mit rjmp/jmp und mit rjmp mainloop zurück. Damit wird die Mainloop nach jedem Job von vorn abgearbeitet, was eine Prioritätsreihenfolge ergibt. Sind keine Jobs mehr offen, wird sleep aufgerufen. Trifft der nächste (Jobflag setzende) Interrupt ein, ehe alle Jobs abgearbeitet wurden, dann wird ggf. der Job mit der geringsten Priorität verpennt. Dieser ist dann meist die Ausgabe eines Zeichens zum LCD, wenn das mal ausfällt, dann stört das auch nicht weiter. Und wenn es sein muss, kann dieses System auch mal kurzzeitig abgeschaltet werden, um z.B. gelegentlich ein OWI-Dewice "zu Fuß" zu bedienen. Es gibt viele Möglichkeiten... Meine neueren Programme verbringen die meiste Zeit im Sleep. ...
danke, ganz interessant wenn ich das richtig verstehe, darf ein Job nicht einen anderen Job aufrufen, sondern nur ein Flag dafür setzen. Und nun übersetze ich das mal in eine höhere Geschwindigkeit. Der Timer ist der Takt. Bei jedem Takt wird ein Interruptflag abgefragt, ob vielleicht ein anderer Job auszuführen ist. (Das müßte ich mir noch genauer ansehen, wie das zu programmieren ist) Ist kein Flag gesetzt, geht es in der Mainloop weiter. Ist ein Interruptflag gesetzt, wird die Interruptroutine (Job) ausgeführt. Ist der Job zu Ende, geht es mit Reti zurück zur Mainloop. In der Interruptroutine sind dann keine weiteren Jobs auszuführen, Interrupt sperren und ggf. das Interruptflag des weiteren Jobs zu setzen. Dann wird in der Mainloop irgendwann der dazugehörige Job aufgerufen. Etwas stören tut mich noch die nicht unterbrechbare Jobausführung. Wenn diese in der Mainloop ausgeführt wird, habe ich auch die entsprechende Verzögerung. Solange etwas nicht zeitkritisch ist, ist das egal. Eine Uhr dürfte trotzdem noch funktionieren. Wie werden definierte Wartezeiten am besten programmiert? Ich habe für die Uhr den Timer1 genommen mit oc1 zur Timinganpassung. Für die Wartezeit wollte ich den gleichen Timer nehmen, Timer auslesen, Wartezeit addieren und in compare2 schreiben. Nun kann die Mainloop nicht unmittelbar weiterlaufen, weil die Wartezeit erst ablaufen muß. Wenn ich jetzt die Aufgabe überspringe, wird das recht unübersichtlich und fehleranfällig. Wenn die Wartezeit abgelaufen ist, woher soll der reti dann noch die richtige Rücksprungadresse finden. Übrigens: Deine Schaltung mit der IR Diode und Fernbedienung fand ich interessant, weil ich das Ding gekauft habe und dies bisher als unbrauchbar eingestuft habe. Die Relais können keine 230V schalten und der IR Teil gibt parallel aus, so daß das in den PC nicht eingelesen werden kann. Das IR Teil darf nicht in der Nähe meines Fernsehers stehen, weil dieser bei jedem Tastendruck reagiert und keine Jumperadresse hat. Kann man das Programm noch so ergänzen, daß die gedrückte Taste seriell an den PC ausgegeben werden kann? mfg Quehl
Quehl wrote: > danke, ganz interessant > > wenn ich das richtig verstehe, darf ein Job nicht einen anderen Job > aufrufen, sondern nur ein Flag dafür setzen. Von "Dürfen" möchte ich nicht reden, man kann auch anders programmieren. Ob's immer sinnvoll ist, entscheidet der Einzelfall. Ich mache es gern so, dass die Jobs der Mainloop in Punkto Ausführungszeit überschaubar bleiben. > Und nun übersetze ich das mal in eine höhere Geschwindigkeit. > Der Timer ist der Takt. Bei jedem Takt wird ein Interruptflag abgefragt, > ob vielleicht ein anderer Job auszuführen ist. Nicht so hastig. Interrupt-Flags und Jobflags sind zweierlei. Die Jobflags sind nur Bits in einem Register (also Boolsche Variablen), das selbst deklariert wurde. Sie haben absolut nichts mit den Flags des SREG oder diversen Flags der I/O-Units zu tun. Jobflags sind selbstdefinierte Bit-Variablen. > (Das müßte ich mir noch > genauer ansehen, wie das zu programmieren ist) Ist kein Flag gesetzt, > geht es in der Mainloop weiter. Ist ein Interruptflag gesetzt, wird die > Interruptroutine (Job) ausgeführt. Ist der Job zu Ende, geht es mit Reti > zurück zur Mainloop. Nein. Das, was ich "Jobs" nenne, wird von der Mainloop aufgerufen, wenn das entsprechende Jobflag gesetzt ist. Der "Job" (also dessen Routine) löscht das Jobflag und erledigt den Job. Dann gehts zurück zur Mainloop. Dabei gibt es zweierlei Varianten. Aufruf des Jobs als Unterprogramm (call, rcall) und Rücksprung mit ret oder Aufruf mit "(r)jmp jobname" und Rücksprung mit "(r)jmp mainloop". Letzteres hat den Vorteil, dass man die Jobs nach Priorität anordnen kann. Denn nach jedem Job beginnt die Mainbloop von vorn und behandelt zuerst die wichtigen, zeitkritischen Aufgaben. Es muss allerdings sichergestellt sein, dass zwischen den regelmäßig auftretenden Interrupts genügend Zeit für die Jobs bleibt. Da es gelegentlich auch unregelmäßig auftretende Interrupts (ICP, Ext Int) gibt, besteht dann die Möglichkeit, dass von einer solchen ISR ein Jobflag gesetzt wird, ehe alle Jobs erledigt sind. Dann ist Priorität hilfreich. Wenn dabei z.B. mal die Ausgabe eines Zeichens aus dem Buffer ins LCD verpennt wird, so ist das nicht weiter tragisch. > In der Interruptroutine sind dann keine weiteren Jobs auszuführen, > Interrupt sperren und ggf. das Interruptflag des weiteren Jobs zu > setzen. Dann wird in der Mainloop irgendwann der dazugehörige Job > aufgerufen. Das Sperren der Interrupts geschieht automatisch beim Aufruf der ISR. Die Freigabe der Interrupts erfolgt durch RETI (das ist der Unterschied zwischen RET und RETI, RETI setzt das I-Flag im SREG). Die ISR soll so kurz wie möglich sein, also nur die allerwichtigsten Dinge erledigen wie Daten sichern. Deshalb wird in der ISR das Jobflag für den Job gesetzt, der die darauf folgende Arbeit macht. Somit ist die ISR schnell beendet, was andere Interrupts ermöglicht. Das Programm macht also mit der Mainloop weiter, findet ein gesetztes Jobflag und arbeitet den Job ab. Sinnvollerweise schickt die Mainloop den Controller in den Sleep-Mode Idle, wenn alle Jobs abgearbeitet sind. Der nächste Interrupt weckt den AVR, führt die ISR aus, und landet wieder in der Mainloop, führt diese aus, bis alle Jobs abgearbeitet sind und geht wieder pennen. > > Etwas stören tut mich noch die nicht unterbrechbare Jobausführung. Wieso ist die nicht unterbrechbar? Jeder Interrupt kann die Jobs unterbrechen und Jobflags setzen. Wenn man darauf achtet, dass die Jobs klein genug bleiben, geht es auch schnell genug zur Mainloop zurück, wo auf andere Jobflags reagiert werden kann. > Wenn > diese in der Mainloop ausgeführt wird, habe ich auch die entsprechende > Verzögerung. Solange etwas nicht zeitkritisch ist, ist das egal. Eine > Uhr dürfte trotzdem noch funktionieren. Eine Uhr funktioniert bestens. Der Timer erzeugt alle 10ms einen Interrupt. In dieser ISR wird das Jobflag für die Tastenentprellung gesetzt (oder die Entprellung in der ISR erledigt, sind im günstigsten Fall 11 Takte), die Hundertstelsekunde hochgezählt und auf Erreichen von 100 geprüft, bei 100 das Jobflag "Neue_Sekunde" gesetzt und die Hundertstelsekunde auf 0 gesetzt. Das Addieren der Sekunden, Minuten, Stunden, Tage, Wochen/Monate, Jahre, Jahrhunderte übernimmt dann der Job für Neue Sekunde. Da dieser nur einmal pro Sekunde auftritt, aber nie verpasst werden darf, sollte er eine hohe Priorität haben, also am Anfang der Mainloop stehen. > Wie werden definierte Wartezeiten am besten programmiert? Ich habe für > die Uhr den Timer1 genommen mit oc1 zur Timinganpassung. Für die > Wartezeit wollte ich den gleichen Timer nehmen, Timer auslesen, > Wartezeit addieren und in compare2 schreiben. Es kommt darauf an, was alles getimt werden muss. Wenn ein LCD im Spiel ist, arbeite ich gern mit einem Buffer (inzwischen als "Bildschirmspeicher" organisiert, also für jede Ausgabeposition ein Byte im SRAM). Sämtliche Schreibzugriffe aus dem Programm schreiben nur in diesen Buffer. Ein Job der Mainloop mit niedrigster Priorität, aber häufigem Aufruf (1..2ms) schickt das jeweils nächste Zeichen aus dem Buffer an das LCD. Hier geht es also nur darum eine Mindestpause zwischen den LCD-Zugriffen einzuhalten. Also brauche ich einen Timer, der alle 1ms oder 2ms einen Interrupt auslöst. In dessen ISR setze ich das Jobflag für LCD_UPDATE und zähle eine Variable hoch (oder runter). Diese steuert die langsameren Jobsynchronisationen bzw. auch die Uhr. Z.B. Int alle 1ms, jedes mal das LCD_Update-Flag setzen, jedes zehnte mal die Uhr hochzählen und die Tasten entprellen, wenn erforderlich, dann ein Jobflag für Hundertstelsekunde setzen. Braucht man eine Wartezeit, so nimmt man statt eines Jobflags eine Variable, die in der Mainloop alle Hundertstelsekunde (oder bei Bedarf auch alle Sekunde) heruntergezählt wird, falls sie nicht schon auf 0 ist und dann, wenn sie zu 0 wird, die entsprechende Aktion auslöst. Sind mehrere Termine zu verwalten, dann lohnt sich ein Scheduler, siehe Codesammlung. War mir bisher aber immer zu komplex und nicht erforderlich. > Nun kann die Mainloop > nicht unmittelbar weiterlaufen, weil die Wartezeit erst ablaufen muß. Nein, die Mainloop kümmert sich um die anderen Jobs. Sie lässt halt diesen Job aus, der jetzt (in dieser Runde der Mainloop) noch nicht dran ist. > Wenn ich jetzt die Aufgabe überspringe, wird das recht unübersichtlich > und fehleranfällig. Überhaupt nicht. > Wenn die Wartezeit abgelaufen ist, woher soll der > reti dann noch die richtige Rücksprungadresse finden. Reti springt aus der ISR zurück. Die (Timer-) ISR synchronisiert zwar über die Jobflags die einzelnen Teile der Mainloop, springt aber keine Jobs direkt an. Sie wird immer sauber mit RETI verlassen, ehe ein von ihr angemeldeter Job begonnen wird. Ich betone nochmal: Das ist eine Möglichkeit, zu programmieren, keinesfalls die einzig mögliche und keinesfalls die einzig richtige. Wer es anders macht, der soll es tun. Es ging hier nur darum, zu zeigen, dass die befürchteten Probleme sich bei genauer Betrachtung relativieren. > > Übrigens: Deine Schaltung mit der IR Diode und Fernbedienung fand ich > interessant, Falls Du mich meinst, meinst Du vermutlich den Fernbedienungsempfänger von Pollin, oder? Wenn ja, da gibt es inzwischen auch andere Varianten von, nicht nur die für die Gartenbahntreffen. > weil ich das Ding gekauft habe und dies bisher als > unbrauchbar eingestuft habe. Die Relais können keine 230V schalten und > der IR Teil gibt parallel aus, so daß das in den PC nicht eingelesen > werden kann. Das lässt sich ändern. Dazu müsste ein baudratentauglicher Quarz und ein MAX232 nachgerüstet werden, dann kann ich da auch eine serielle Ausgabe reinbauen. Die benötigten Pins sind frei und zugänglich. Es ist aber vermutlich besser, dafür eine neue Platine zu entwickeln. Sie enthält dann nur Tiny2313, Quarz, MAX232, den IR-Empfänger und diverse Rs und Cs. > Das IR Teil darf nicht in der Nähe meines Fernsehers > stehen, weil dieser bei jedem Tastendruck reagiert und keine > Jumperadresse hat. Stimmt, das Original-Programm ist sehr störanfällig. Da der Quelltext nicht öffentliuch vorliegt, habe ich das Programm völlig neu geschrieben. Ich habe dabei darauf geachtet, dass der Empfang sehr zuverlässig wird, ein Telegramm wird nur dann akzeptiert, wenn es zweimal hintereinander > Kann man das Programm noch so ergänzen, daß die gedrückte Taste seriell > an den PC ausgegeben werden kann? Bei oben beschriebener Hardwareerweiterung ja. > > mfg > Quehl ...
@Hannes Bei Deiner Methode wird die Interruptverarbeitung aber je nach Länge des Mainprogramms verzögert und zwar um eine undefinierte Zeit. Gerade bei Timerinterrupts kann das aber zu unzulässigen Verzögerungen führen. ggf. auch beim A/D Interrupt. Oben habe ich mich wohl nicht so gut ausgedrückt mit dem Schnellerwerden. Darum noch mal klarer: Bei jedem Befehl des Prozessors fragt dieser die IntEnable bits ab, hardwaremäßig. Das würde bei Dir den gesetzten Softwarebits im Hauptprogramm entsprechen. Bei gesetzten intEnable bits verzweigt das Programm zur Interruptroutine, hardwaremäßig. Das würde bei Dir bedeuten, Du verzweigst zum Jobunterprogramm, softwaremäßig. Am Ende kehrt die Interruptroutine zum Mainprogramm zurück. Das bedeutet bei Dir, Du kehrst zum Mainprogramm zurück. Du löschst das Jobbit softwaremäßig. Die Hardware löscht das Enablebit im Interruptprogramm. Unterschiede: Bei Dir kann zu jeder Zeit ein Interrupt akzeptiert werden. In der Interruptroutine bei mir könnte das Bit freigegeben werden und damit wären auch weitere Interrupts akzeptierbar. Bei den Rückkehradressen dürfte es keine Probleme geben. Nur für die InterruptEnable Bits gibt es keinen Stack. Nachteil bzw. Vorteil: Durch die hardwaremäßige Jobabfrage bei jedem Befehl kann der Job schneller reagieren. Dadurch können aber mehr Verschachtelungen auftreten als bei der softwaremäßigen Abfrage. Ich hoffe, Du erkennst zumindest die Ähnlichkeit zwischen Deiner Lösung und der Hardwarelösung. (meine Lösung will ich da nicht schreiben). Entscheidend für die Wahl wird wohl im Einzelfall die Reaktionszeit sein. ggf. auch eine Mischung beider Verfahren, daß das aber eher unübersichtlich machen würde. Hat eigentlich jeder Interrupt ein eigenes InterruptEnabel bit? Mit der Fernbedienung ist das so, daß die keine Störungen abgibt, sondern daß das die gleiche ist, die zu meinem Fernseher paßt, nur in einem anderen Gehäuse. Darum schaltet die mit jeder Taste meinen Fernseher. Taste 1, 1. Programm u.s.w. Die RS232 ist kein Problem. Hab ich einen extra Umsetzer gekauft. Quarz sollte auch kein Problem sein, weil noch nicht zusammengelötet, muß ich sehen, wo ich einen passenden herbekomme. Welcher wird denn benötigt? Sicherlich ist der auch von den IR Impulsen und dem Programm abhängig.
Wolfram Quehl wrote: > @Hannes > > Bei Deiner Methode wird die Interruptverarbeitung aber je nach Länge des > Mainprogramms verzögert und zwar um eine undefinierte Zeit. Gerade bei > Timerinterrupts kann das aber zu unzulässigen Verzögerungen führen. Nööö. Der zeitkritische Teil der Interruptverarbeitung erfolgt ja in der ISR. Es wäre Unsinn, den nächsten Interrupt-Termin erst in der Mainloop zu vereinbaren, das mach ich ja auch in der ISR. Somit ist der nächste Interrupt auch dann pünktlich, wenn die restliche Arbeit, die zu diesem Termin dran ist, einige hundert Takte verzögert abgearbeitet wird. > ggf. > auch beim A/D Interrupt. Es kommt darauf an, was der messen soll. Das Digitalisieren von NF mit hoher Samplerate sollte wohl für den AVR die Ausnahme sein, bei den meisten Anwendungen würde es nichtmal stören, wenn eine Abtastung verpennt wird. Die nächste Abtastung ist halt aktueller... > > Oben habe ich mich wohl nicht so gut ausgedrückt mit dem > Schnellerwerden. > Darum noch mal klarer: > Bei jedem Befehl des Prozessors fragt dieser die IntEnable bits ab, > hardwaremäßig. ja, und falls ein (zugelassenes) Interrupt-Ereignis eingetroffen ist, dann wird der angefangene Befehl abgearbeitet, der PC auf Stack gelegt und zur dem interrupt entsprechenden Adresse verzweigt. Dort steht dann der Sprung zur ISR. > Das würde bei Dir den gesetzten Softwarebits im > Hauptprogramm entsprechen. Nööö. Das passiert bei mit trotzdem. Nur beschränkt sich meine ISR auf die zeitkritischen Dinge und verlagert den Rest in einen Job der Mainloop. Dadurch ist meine ISR schneller fertig und macht Platz für andere Interrupts, die damit schneller reagieren können. > Bei gesetzten intEnable bits verzweigt das > Programm zur Interruptroutine, hardwaremäßig. Ja. > Das würde bei Dir > bedeuten, Du verzweigst zum Jobunterprogramm, softwaremäßig. Nööö. > Am Ende > kehrt die Interruptroutine zum Mainprogramm zurück. Das bedeutet bei > Dir, Du kehrst zum Mainprogramm zurück. Du löschst das Jobbit > softwaremäßig. Die Hardware löscht das Enablebit im Interruptprogramm. Nööö. Ich glaube, wir reden völlig aneinander vorbei. > > Unterschiede: > Bei Dir kann zu jeder Zeit ein Interrupt akzeptiert werden. Nein, während eine ISR läuft, muss der nächste auftretende Interrupt warten bis die laufende ISR fertig ist. > In der Interruptroutine bei mir könnte das Bit freigegeben werden und > damit wären auch weitere Interrupts akzeptierbar. Dieses Vorgehen ist sehr gefährlich. Wenn man da die Übersicht verliert und derselbe Interrupt erneut auftritt, bevor seine ISR fertig ist, dann hat man ganz schnell Stacksalat. Ich habe diese Methode zwar auch schon verwendet, aber da gab es nur zwei Interrupts und es war (durch Timing) sichergestellt, dass derselbe Interrupt nicht dazwischenfunken kann. > Bei den > Rückkehradressen dürfte es keine Probleme geben. Aber sicher, der Stack ist endlich. > Nur für die > InterruptEnable Bits gibt es keinen Stack. Muss ich das jetzt verstehen? > Nachteil bzw. Vorteil: > Durch die hardwaremäßige Jobabfrage bei jedem Befehl kann der Job > schneller reagieren. Dadurch können aber mehr Verschachtelungen > auftreten als bei der softwaremäßigen Abfrage. Ich frage die "Ereignisse" (die Interrupt-Flags) nicht per Software ab. Jobflags sind selbstdeklarierte Bool'sche Variablen. Sie haben nix und garnix mir den System-Flags der Interruptlogik zu tun. > > Ich hoffe, Du erkennst zumindest die Ähnlichkeit zwischen Deiner Lösung > und der Hardwarelösung. (meine Lösung will ich da nicht schreiben). > Entscheidend für die Wahl wird wohl im Einzelfall die Reaktionszeit > sein. Nö, ich vermute stark, dass wir uns missverstehen. > ggf. auch eine Mischung beider Verfahren, daß das aber eher > unübersichtlich machen würde. Meine Art zu Programmieren ist lediglich eine Optimierung. Ich nutze die hardwaremäßige Interruptlogik so, wie es der Hersteller vorgesehen hat. Ich mache lediglich die ISRs schneller, indem ich die Teile, die nicht zeitkritisch sind, in "Jobs der Mainloop" auslagere und dann erledige, wenn Zeit dazu ist. > Hat eigentlich jeder Interrupt ein eigenes InterruptEnabel bit? Das sollte ein Blick ins Datenblatt klären können, oder?? > > Mit der Fernbedienung ist das so, daß die keine Störungen abgibt, > sondern daß das die gleiche ist, die zu meinem Fernseher paßt, nur in > einem anderen Gehäuse. Darum schaltet die mit jeder Taste meinen > Fernseher. Taste 1, 1. Programm u.s.w. Nunja, es ist ziemlich sinnfrei, zwei Fernbedienungen mit identischer Codierung im selben Raum für unterschiedliche Zwecke nutzen zu wollen. > Die RS232 ist kein Problem. Hab ich einen extra Umsetzer gekauft. Quarz > sollte auch kein Problem sein, weil noch nicht zusammengelötet, muß ich > sehen, wo ich einen passenden herbekomme. Welcher wird denn benötigt? > Sicherlich ist der auch von den IR Impulsen und dem Programm abhängig. Nein, ist er nicht. Das gesammte Programm muss zwar völlig neu geTIMEt werden, weil der Controllertakt ja verändert wird, aber die Frequenz wird von der UART-Einheit bestimmt. Der Quarz sollte also baudratentauglich sein. ...
Hallo, @Hannes Lux: ich benutze eigentlich die geliche Version beim Programmaufbau, Du hast es allerdings noch weiter perfektioniert und ich werde davon mit Sicherheit nächstes Mal was einbauen. :) Das Problem, welches offenbar gesehen wird, wo es garnicht ist, scheint mir oft die Fehleinschätzung zu sein, was eigentlich wann und wie oft passieren muß. Wenn man z.B. ein Thermometer mit Anzeige baut, macht es wenig Sinn, den ADC 1000x in der Sekunde auslesen zu wollen, weil man sinnvoll sowieso nur 2-3 Werte in der Sekunde sinnvoll auf dem Display ablesen kann. Wenn die Uhr die Anzeige um eine 10tel Sekunde zu spät umschaltet, nimmt man das nicht wahr. Wenn die main für einen Durchlauf mehr als 100ms braucht, hat man wohl ganz andere Probleme mit dem Konzept und der benutzten CPU. Bei ADC, Counter-IRQ usw. mache ich es in der ISR oft so, daß ich deren Inhalt garnicht durchlauf, wenn das vorige Ergebnis noch nicht bearbeitet wurde. Wenn also das Jobflag in der Main noch nicht gelöscht wurde, wird die ISR beim Aufruf gelich wieder beendet. Sonst werden die Werte in feste Register oder Ramplätze geschrieben und sind damit in jedem Fall außerhalb gültig. Eigentliche in simples Semaphoren-Handling, ISR holt die neuen Werte nur dann, wenn Jobflag gelöscht und setzt es dann. Beim kritischen Sachen kann man dann z.B. problemlos ein Error-Flag für die Main setzen, wenn der ISR-Aufruf erfolgte, bevor der Job bearbeitet wurde. Ich habe auch schon die ISR-Bearbeitung absichtlich warten lassen, also das Jobflag des ADC nicht in Main->ADC bearbeiten gelöscht, wenn der Ablauf sowieso sicherstellt, daß die ADC-Werte innerhal eines Main-Durchlaufs von z.B. maximal 10ms bearbeitet werden, ich den nächsten ADC-Wert aber eigentlich nur alle 10s wieder brauche, dann hat das Löschen die Sekundenroutine erledigt. Das ist eigentlich das zweite Problem, das übersehen wird: der AVR kann nur genau in einerm Programmteil zu einer Zeit sein und das bearbeiten. Die ISR muß sowieso mit dem vorgefundenen Zustand verlassen werden, nur Jobflag und zugehörige Wertänderung werden angefasst. Man weiß also eigentlich zu jeder Zeit beim Programmieren, was wo ist und was in jedem Fall als nächstes passiert. Der AVR würfelt nicht, es sei denn, der Programmierer baut sich einen Würfel ein. ;) Gruß aus Berlin Michael
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.