Forum: Mikrocontroller und Digitale Elektronik Digitaler Datenbus + Ein & Ausgänge + Timer


von Markus (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Hannes L. (hannes)


Lesenswert?

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.

...

von Bernhard S. (bernhard)


Lesenswert?

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

von Cri Gri (Gast)


Lesenswert?

WOW!!! ich bin platt :)
Aber die Hälfte hab ich nicht verstanden! :)
Was ist ein Ringbuffer und was macht der?

Mfg Cri

von Hannes L. (hannes)


Lesenswert?

> 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.

...

von Karl H. (kbuchegg)


Lesenswert?

> 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?

von Rahul (Gast)


Lesenswert?

@Hannes: "Triggerst" du die UART-Ausgabe auch üner einen Timer? Dafür
gibt es doch so schöne UART-eigene Interrupts.

von peter dannegger (Gast)


Lesenswert?

@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

von Hannes L. (hannes)


Lesenswert?

> "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.

...

von Rahul (Gast)


Lesenswert?

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.

von Hannes L. (hannes)


Lesenswert?

> 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...

von Rahul (Gast)


Lesenswert?

Werd ich wohl ausprobieren müssen...
Liegt vielleicht auch an meiner "C-Programmiererei"...

von Markus (Gast)


Lesenswert?

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?

von Bernhard S. (bernhard)


Lesenswert?

@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

von Markus (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.