Hallo :-) Ich habe eines noch nicht so ganz kappiert. Ein µC kann ja immer nur einen Befehl nach dem anderen ausführen. Ich habe aber jetzt z.B. einen Timer laufen für ein Bussystem laufen, damit ich also z.B. jede millisekunde ein Byte übertragen kann. Dazu möchte ich noch eine Echtzeit Uhr programmieren und zugleich noch auf eingehende Daten (von RS 232 und von meinem Bus) reagieren. Ich kann mir das im Kopf nicht so ganz vorstellen das so etwas klappen kann. Erst recht nicht wenn ich einen 2ten Timer für meinen Bus hernehme. Denn so löst doch der erste Timer einen Interrupt aus und damit springt das Programm in dessen routine. Dann löst Timer 2 einen Interrupt aus und somit springt danach das Programm in dessen Teil. Was passiert jetzt wenn ein RS232 Byte ankommt? Geht das alles normal so schnell das ich so etwas machen kann oder wie wird das in anderen Schaltungen gemacht? Mein Projekt ist ein kleiner Auto Boardcomputer der mit 2µCs funktionieren soll. Ein µC der das Display ansteuert und die Tasten auswertet und der 2te soll eben die Sensoren auswerten (Themperatur, Drehzahl, Geschwindigkeit) und das soll dann per BUS System an den anderen übermittelt werden. Ich hoffe ihr könnt mir etwas Tips geben wie so etwas normal funktioniert und ob ich da einfach nen Denkfehler drinn habe. Grüße Markus. PS: Ich arbeite mit AVRs.
Normalerweise werden in Interrupt-Routine die Interrupts gesperrt. Eine Interrupt Routine kann also nicht von einer anderen Interrupt Routine unterbrochen werden. Das macht aber nichts, den der Interrupt als solches ist dadurch nicht verloren. Es wird ganz einfach eine Markierung gesetzt, dass ein bestimmtes Ereignis aufgetreten ist. Sobeld dann Interrupts wieder freigegeben werden, wird dann der wartende Interrupt abgearbeitet. Und ja. Das ganze geht so schnell, dass es für uns Menschen den Anschein der Gleichzeitigkeit hat.
Das alles schafft sogar ein AVR ganz allein. Es kommt drauf an, in welchem Stil man ihn programmiert. Man kann z.B. so programmieren, dass an allen möglichen Ecken und Kanten auf irgendetwas gewartet werden muss (eintreffendes Byte, Busy-Flag des LCDs, Ergebnis des ADC,...) Man kann aber auch so programmieren, dass keine Wartezeiten entstehen, z.B.: - LCD-Ausgabe erfolgt auf einen Ringbuffer. - Im Timer-Interrupt wird 1 Zeichen des Ringbuffers an das LCD ausgegeben. - Im ADC-Complete-Interrupt wird der ADC-Wert ausgelesen und gesichert, dann die neue Messquelle ausgewählt (ADMUX), dann die neue Messung gestartet. - Ein Timer-Interrupt (10ms) zählt die Hundertstelsekunden hoch und bei Überlauf den Rest der Zeitzählung (Minute, bei Überl. Stunde...) Nebenbei entprellt er noch Eingänge für Tasten, zählt weitere Software-Timer (Status-Variable) für andere Zwecke, setzt Flags, wenn die Mainloop auf bestimmte Dinge reagieren soll. - Im RX-Interrupt wird das empfangene Byte in einem Empfangs- Ringbuffer gesichert, der von der Mainloop auf neue Zeichen gepollt wird. - TX kann mit über den Ringbuffer für das LCD erfolgen. Dabei ent- scheidet ein Flag, ob das Byte zum LCD oder zu UDR geschickt wird. Jeder Locate-Befehl legt eine Sequenz (0x00,Position) auf den Ring- Buffer, die den Cursor positioniert und auf LCD umschaltet, das Steuerzeichen 0x01 schaltet auf UART-Ausgabe um. Somit können alle LCD-Printroutinen (ASCII-Text erzeugung) auch auf UART verwendet werden. - Im ICP-Interrupt wird der Timestamp ausgelesen, die Differenz zur vorherigen Messung berechnet und gesichert, der aktuelle Timestamp für die nächste Messung gesichert, der Mainloop per Flag mitgeteilt, dass neue Daten da sind. Ggf. wird noch die ICP-Flanke geändert. - Jede ISR wird so kurz wie möglich gehalten, an keiner Stelle darf es Warteschleifen geben. Größere Aufgaben werden der Mainloop per Flags oder anderer geeigneter Variablen mitgeteilt. - Jedes Unterprogramm wird so organisiert, dass es keine Warteschleifen gibt. Kann es im Moment nix tun, dann wird unver- züglich zur Mainloop zurück gesprungen und ein anderer Job erledigt. - In der Mainloop werden die Jobflags geprüft und ggf zum erforder- lichen Unterprogramm verzweigt. Kann dieses den Job erledigen, dann löscht es das Jobflag und tut seine Arbeit. Sind alle Jobs abgear- beitet, dann wird in den Sleep "gefallen", aus dem der AVR durch den nächsten Interrupt wieder geweckt wird. - Dauert mal ein Job (z.B. eine aufwendige Berechnung) länger als die Pause zwischen 2 Interrupts, dann wird das laufende Programm eben von der ISR unterbrochen. Da man in der ISR das SREG sichert und auch die benutzten Register, gibt es damit keine Konflikte. Wenn die ISR fertig ist, wird das Programm an der unterbrochenen Stelle fortgeführt. Irgendwann ist die Mainloop dann wieder hinterher und kann bis zum nächsten Int in den Sleep gehen. ...
Hallo Markus, >Denn so löst doch der erste Timer einen Interrupt aus und >damit springt das Programm in dessen routine. Dann löst Timer 2 einen >Interrupt aus und somit springt danach das Programm in dessen Teil das sind typische Interrupt-Probleme ;) Im Normalfall werden bei einem momentan abgearbeiteten Interrupt alle anderen Interrupts gesperrt. Erst wenn dieser durch "reti" wieder verlassen wird, kann der nächste Interrupt ausgeführt werden. Deshalb versucht man bei zeitkritischen Problemchen die Interrupt-Routinen so schnell wie nur möglich abarbeiten zu lassen. Natürlich kann eine Verschachtelung der Interrupts vorgenommen werden, d.h. ein Interrrupt wird ausgelöst und wärend dieser abgearbeitet wird, kann auch ein anderer Interrupt auslösen. Aber mit diesen Verfahren ist mit Vorsicht zu genießen. Sollte Dein µC natürlich so stark durch Interrupts belästigt werden, dann hat er kaum Zeit, etwas anderes zu tun. Da Du eh mit einem Bus-System arbeiten möchtest, kannst Du die Zeitkritischen Routinen ohne weiteres auslagern. Ich vermute, dass es in Deinem konktreten Projekt so zeitkritisches nicht geben wird. Programmierst Du in Assembler? Bernhard
WOW!!! ich bin platt :) Aber die Hälfte hab ich nicht verstanden! :) Was ist ein Ringbuffer und was macht der? Mfg Cri
> Aber die Hälfte hab ich nicht verstanden! :) > Was ist ein Ringbuffer und was macht der? Oft steht man vor dem Problem, dass man eine ganze Zeichenkette an das LCD oder an UART senden will, LCD oder UART aber nach jedem Zeichen eine Pause brauchen, um das Zeichen zu verarbeiten. Das Programm würde nun all diese Pausen lang warten und wertvolle Rechenzeit verplempern, in der die andere Arbeit liegen bleibt. Deshalb richtet man sich einen Ringbuffer ein, das ist ein Stück SRAM mit zwei Pointern, in den man in einem Schub einen ganzen Text ablegen kann, der dann im Interrupt alle Millisekunde byteweise an das Ziel geschickt wird, bis beide Pointer (Lesepointer und schreibpointer) wieder Gleichstand haben. Vorteil: Die Routine, die diesen Text sendet, ist schnell fertig, da sie den Text ja nur ins SRAM legt. Die Routine, die die einzelnen Zeichen sendet, hat keinerlei Wartezeit, weil sie im Interrupt ausgeführt wird und der Interrupt-Abstand so gewählt wird, dass das LCD (oder auch UART) mit dem zuvor gesendeten Zeichen garantiert fertig ist. In der Zeit zwischen den Interrupts kann der AVR andere Jobs der Mainloop erledigen. Ein Beispiel, das so funktioniert, findest du hier: http://www.hanneslux.de/avr/stopuhr/index.html Es ist zwar kein Bordcomputer, vielleicht erkennst du aber trotzdem, was ich meine. ...
> Was ist ein Ringbuffer und was macht der?
Stell dir mal folgendes vor:
Du hast mit deiner Freundin eine Abmachung getroffen. Sie
schreibt dir auf einen Zettel Arbeiten auf und du erledigst
sie:
Geschirr abwaschen
Schuhe putzen
Noch während du dich über den ersten Punkt hermacht, ergänzt
sie die Liste
Geschirr abwaschen
Schuhe putzen
Staubsaugen
Das Prinzip kennst du also: das ist eine klassische Warteschlange.
Irgendwann bist du mit dem ersten Auftrag fertig und fängst
den nächsten an, usw. während sie immer weiter hinten an die
Liste ranschreibt. Was passiert aber wenn der Zettel voll ist?
Nun. Eine Möglichkeit ist es, dass man sich den Zettel Oberkante
mit Unterkante verklebt vorstellt und sie schreibt einfach oben
wieder weiter (und überschreibt dabei den ersten Eintrag 'Geschirr
abwaschen'). Das ist auch weiter kein Proböem, denn dieser Auftrag
ist ja bereits erledigt. Für uns wichtig: So ist aus dem ganzen
ein Ring geworden. Ihr benutzt also immer nur diesen einen Zettel.
Es darf halt nur nie so sein, dass deine Freundin einen Auftrag
überschreibt, denn du noch nicht angefangen hast.
Klarer?
@Hannes: "Triggerst" du die UART-Ausgabe auch üner einen Timer? Dafür gibt es doch so schöne UART-eigene Interrupts.
@Markus, das ist alles nicht so schlimm, wie es aussieht. Die Timer laufen nach einem Überlauf einfach weiter, d.h. auch wenn man etwas später reagiert, gibt es trotzdem keinen akkumulierten Fehler, solange nicht schon der nächste Überlauf erfolgte. Und die UART ist 3-fach gepuffert, bei 9600Baud sind das: 16MHz / (9600 10 3) = 50.000 Zyklen Zeit. Die CPU macht alles nacheinander und hat auch genügend Zeit dazu. Das mit dem 2.MC vergiß mal schnell wieder, das ist nur was für Experten. Ein zuverlässiges Kommunikationsprotokoll ist nämlich alles andere als einfach. Und wenn man sich dazu noch nen extra Bus audenken will, erst recht. Warum nicht die bereits vorhandenen (I2C, SPI) nehmen. Peter
> "Triggerst" du die UART-Ausgabe auch üner einen Timer? Ja... > Dafür > gibt es doch so schöne UART-eigene Interrupts. Ich weiß. Aber so habe ich nur einen "Printbuffer" zu verwalten. Dieser Ringbuffer enthält neben ASCII auch Steuerzeichen für Locate (Ausgabeposition auf LCD) und Zielauswahl (bisher LCD und UART, aber leicht erweiterbar). Die Zielauswahl wird mittels eines Flags gemanagt, das von Locate/UART-Steuerzeichen beeinflusst wird und nach dessen Zustand die Zeichenausgabe "verteilt" wird. Es ist für die Ausgaberoutine (eines Bytes) kein nennenswerter Aufwand, das Byte ins UDR zu werfen, anstatt es nibbleweise ans LCD zu schaufeln. Da die Steuerzeichen für die Umschaltung mit im Ringbuffer liegen, kann der Ringbuffer gleichzeitig offenen (noch auszugebenden) LCD-Text und UART-Text enthalten, ohne dass das Ausgabeziel verwechselt werden kann. Eine Erweiterung auf SPI oder gar eine LPT wäre auch mit vertretbarem Aufwand möglich. Der Nachteil, dass die UART-Ausgabe nicht mit maximal möglicher Geschwindigkeit erfolgt, wird durch den Vorteil, dass es quasi nebenbei erfolgt und dass alle Print-Routinen uneingeschränkt für alle Ausgabeziele nutzbar sind, überwogen. Müsste ich größere Datenmengen (was ist beim AVR "groß"?) als Bytestrom (also nicht zu ASCII-Text gewandelt) über UART senden, dann würde ich über eine eigene Senderoutine im UDRE-Interrupt nachdenken. Aber für meine jetzigen Ansprüche funktioniert die "Umleitungsmethode" ganz gut. Bei einem Projekt mit myAVR-Board2 (mit USB-Aufsatz) funktioniert das bei einem Bekannten auch über USB (allerdings nicht mit meinem Lappi, da dieser den USB-Treiber nicht mag): http://www.hanneslux.de/avr/stopuhr/index.html Das "Betriebssystem" (wenn man es so nennen darf!) des Commodore Plus/4 hat es auch nicht viel anders gemacht, man konnte die Standard-Ausgabe mal schnell auf eine andere Gerätenummer umleiten. Bei DOS ist es ja auch so, wenn auch etwas komplexer. ...
Um das UART und das LCD mit nur einem Printpuffer zu betreiben, reicht doch die Einführung eines weiteren Lese-Pointers. Der eine Pointer liefert die Daten für das LCD und der andere für das UART. Sobald der UART-Lese-Pointer den Schreibpointer erreicht hat, wird einfach das UDR-Interrupt ausgeschaltet. Sobald neue Daten in den Puffer geschrieben werden, wird es halt wieder eingeschaltet... So würde ich es zumindest machen.
> Der eine Pointer > liefert die Daten für das LCD und der andere für das UART. Da hätte ich Bedenken, dass sich das verheddert, also dass UART scneller ist als LCD und sich die falschen Bytes greift. Ich bleibe daher erstmal bei meiner Variante, auch wenn sie nicht optimal erscheint und sicherlich auch nicht ist. Aber ich verstehe sie und kann nachvollziehen, was (und wann) da passiert. Gruß an die Ostsee... ...HanneS...
Werd ich wohl ausprobieren müssen... Liegt vielleicht auch an meiner "C-Programmiererei"...
Hi Leute. Vielen Dank für die netten und sehr ausführlichen Antworten. Ich glaube jetzt bin ich ein großes Stück weiter gekommen. Also das mit dem Bus will ich auf jeden Fall realisieren. Ich möchte kein I²C oder SPI da ich dort immer ein clock signal benötige. Ich will ein 1 Wire Protokoll. Noch zu meinem Protokoll, es ist wohl das Sinnvollste ein Zeitbasierendes Protokoll mit einem Timer zu realisieren oder? Wenn ich's schaffe dann sollte es natürlich multi master fähig sein und eben 1Wire fall's ich's auf Glasfaser (erst mal natürlich nur Spielerei) ausbauen will. Ich weiß bei einer so einfachen Anwendung brauche ich keine 2µCs nur mich interessiert es einfach wie das geht und am besten lernt man doch wenn man es selbst ausprobiert. Ist erstmal einfach nur interessehalber aber wenn's klappt hab ich vielleicht ein Protokoll das ich für spätere Dinge nutzen kann. Das erste Protokoll hat mir 8 Zustände von Lichtschranken die ein µC auswertet zum anderen geschickt der das dann auf LEDs angezeigt hat. War ne Gleisbesetztmeldung für die Eisenbahn von meinem Cousin. Und da ich das schon geschafft hab möchte ich jetzt eben einen Schritt weiter gehen. Nur wollte ich noch eure Meinung hören ob man das mit dem Timer macht oder ob es da etwas anderese gibt?
@Karl Heinz Buchegger >Stell dir mal folgendes vor: >Du hast mit deiner Freundin eine Abmachung getroffen. Sie >schreibt dir auf einen Zettel Arbeiten auf und du erledigst >sie: Einfach sehr schön beschrieben!! Habe mir Deine Erklärung jetz schon mehrere Male durchgelesen. So gut erklärt, auch etwas humorvoll, dass selbst ich es verstanden habe :) Bernhard
Hi. Ich hätte ne grundsätzliche Frage zu meinem Protokoll das eben mit nur einem Draht + GND funktionieren soll. Ist es sinnvoll das über einen Timer zu realisieren? Gibt es ein Protokoll das ich brauche vielleicht schon fertig? Es soll folgendes können: -Multimasterfähig -1Wire -Geschwindigkeit ist nicht so wichtig Das ich praktisch mehrere AVRs an einem Bus hängen habe und ich darüber informationen wie z.B. welcher Taster gedrückt, Themperatur etc... austauschen kann. Jeder AVR kriegt dann eine Adresse. Also wenn's sowas schon fertig gibt dann wäre das natürlich super.
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.