Hallo liebes Microcontroller Forum,
ich nutze einen ATMEGA88A-PU. UART, 8Data Bit; 1 Stop Bit; BAUD: 9600
Ich programmiere den Controller mit C im Atmel Studio.
Dem Controller schicke ich über LabView 2 Byte. Zuerst das MSB dann das
LSB. Die beiden Bytes füge ich auf dem Controller in eine 16 Bit
Variable zusammen. Das klappt soweit ganz gut.
Wenn ich nun aber in einer Dauerschleife permanent das MSB und das LSB
an den Microcontroller schicke, springt der Wert der 16 Bit Variable auf
dem Controller kurz auf einen anderen Wert und dann wieder zurück auf
den Wert den ich permanent schicke.
Konkrekt bedeuetet das:
Wenn ich dauerhaft 1000 (dezimal) sende, springt die Variable kurzzeitig
auf 59624. Das bedeutet, dass das MSB = 232 ist und das LSB auch = 232
obwohl ja MSB ungleich LSB sein sollte. Etwas ähnliches passiert wenn
ich 100 (dezimal) sende.
Dann empfänge ich mittendrin kurzzeitig 25700. Das entspricht MSB =
100(dezimal) und LSB = 100 (dezimal). Es ist also ein deutliches Muster
zu erkennen. Leider habe ich keine Idee wie ich das beseitigen kann. Bin
sehr unerfahren in Sachen Mikrocontroller Programmierung. Im Folgendem
beschreibe ich wie ich den Empfang programmiert habe. Sicherlich keine
elegante Lösung. Vielleicht liegt hier schon das Problem.
Der Empfang auf dem Mikrocontroller läuft folgendermaßen ab:
Ich empfange Daten per Interrupt. Jedes mal wenn ein Byte empfangen
wird, also der Empfangsinterrupt ausgelöst wird, erhöhe ich eine
Zählvariable um den Wert 1.
Empfängt der Mikrocontroller nun den Befehl, dass er in die 16 Bit
Variable schreiben (Befehl: 85) soll, wird der Zähler = 0 gesetzt.
Das nächste Byte, das dann empfangen wird setzt den Zähler = 1. Das
erkennt meine if-Abfrage und speichert diesen Wert dann als MSB ab. Beim
nächsten Byte, das empfangen wurde ist der Zähler = 2. Auch das erkennt
eine if-Abfrage und speichert das Byte in einer Variablen namens LSB.
Das MSB und das LSB verknüpfe ich dann ODER und erhalte so den 16 Bit
Wert. Anschließend setze ich den Zähler = 10, sodass die beiden if
Bedingungen erst wieder erfüllt sind, sobald ich den Befehl schicke um
in diese Variable zu schreiben.
Viele Grüße & besten Dank
Generell sagen ein paar Zeilen Code mehr als tausend Worte.
Es klingt aber ganz danach als würde mit deiner Zählung irgendetwas
nicht stimmen, so dass das LSB versehentlich als MSB interpretiert wird.
Jay K. schrieb:> Es klingt aber ganz danach als würde mit deiner Zählung irgendetwas> nicht stimmen, so dass das LSB versehentlich als MSB interpretiert wird.
Deswegen schicken clevere die Zahl in ASCII mit einem newline als
Trennzeichen.
MfG Klaus
Vermutlich benötigst du eine Synchronisation zwischen Sender und
Empfänder für den Fall das irgendwann mal ein Byte verschütt geht.
Das geht z.B. über eine Sendepause, wenn LabView in eine gröberen
Zeitraster sendet als der UART zum Senden beider Bytes benötigt:
<Befehl><MSB><LSB>...Pause...<Befehl><MSB><LSB>...Pause...
Hier könntest du auf dem µC einen Timer mitlaufen lassen, der den
Abstand zwischen zwei empfangenen Bytes misst.
Oder du verpackst das in ein Protokoll, z.B. (binär)
0b00BB BBBB
0b01BB MMMM
0b10MM MMLL
0b11LL LLLL
B = Bits des Befehls
M = Bits des MSB
L = Bits des LSB
Die '0' und '1' ober wären dann Byte-Zähler, aus denen sich die
Reihenfolge rekonstruieren lässt, bzw. verlorene Bytes erkannt werden
können. Den Luxus muss man sich dann mit ein wenig Bitschieberei
erkaufen.
Klaus schrieb:> Jay K. schrieb:>> Es klingt aber ganz danach als würde mit deiner Zählung irgendetwas>> nicht stimmen, so dass das LSB versehentlich als MSB interpretiert wird.>> Deswegen schicken clevere die Zahl in ASCII mit einem newline als> Trennzeichen.>> MfG Klaus
Was zur Folge hat, dass du für eine uint16 in worst-case (0xffff) 5
ASCII Zeichen und damit 5 Bytes übertragen musst...und wenn eins
verloren geht, wird es trotzdem nicht erkannt?
Sinnvoller ist da ein Protokoll, wie von Sebastian vorgeschlagen.
Ein ganz einfaches wäre ein Acknowledge vom Empfänger nach jedem Byte
<Befehl1></ACK><MSB1></ACK><LSB1></ACK><Befehl2></ACK><MSB2></ACK><LSB2>
</ACK>
Hans D. schrieb:> Wenn ich nun aber in einer Dauerschleife permanent das MSB und das LSB> an den Microcontroller schicke, springt der Wert
Ev liegt Dein Problem gar nicht im Microcontroller, sondern im Sender,
d.h. Du hast einen Buffer-Overflow.
Der Vorteil eines ASCII-Protokolls liegt für mich in der einfachen
Debug-Möglichkeit, jedes Terminalprogramm reicht zum Testen.
Gruß, Stefan
Hans D. schrieb:> Empfängt der Mikrocontroller nun den Befehl, dass er in die 16 Bit> Variable schreiben (Befehl: 85) soll, wird der Zähler = 0 gesetzt.> Das nächste Byte, das dann empfangen wird setzt den Zähler = 1. Das> erkennt meine if-Abfrage und speichert diesen Wert dann als MSB ab. Beim> nächsten Byte, das empfangen wurde ist der Zähler = 2. Auch das erkennt> eine if-Abfrage und speichert das Byte in einer Variablen namens LSB.> Das MSB und das LSB verknüpfe ich dann ODER und erhalte so den 16 Bit> Wert. Anschließend setze ich den Zähler = 10, sodass die beiden if> Bedingungen erst wieder erfüllt sind, sobald ich den Befehl schicke um> in diese Variable zu schreiben.
Darf dein Programm zwischen dem Empfangen vom MSB und LSB auf die Werte
zugreifen? Für mich klingt das nämlich danach, dass der uC ab und an
schneller als der UART ist und dann auf deine 16 bit Variable schon
zugreift obwohl die noch gar nicht fertig gebaut ist.
Du musst hier also einen atomaren Zugriff auf die 16 bit Variable
zusammenstellen damit man nicht während des Neubaus schon mal auf die
Variable zugreift.
Jay K. schrieb:> Was zur Folge hat, dass du für eine uint16 in worst-case (0xffff) 5> ASCII Zeichen und damit 5 Bytes übertragen musst
Was für eine Verschwendung!
Stefan K. schrieb:> Der Vorteil eines ASCII-Protokolls liegt für mich in der einfachen> Debug-Möglichkeit, jedes Terminalprogramm reicht zum Testen.
Warum sind wohl die meißten Protokolle im Internet ASCII? Clever, halt.
MfG Klaus
M. K. schrieb:> Für mich klingt das nämlich danach, dass der uC ab und an> schneller als der UART ist und dann auf deine 16 bit Variable schon> zugreift obwohl die noch gar nicht fertig gebaut ist.
Oder er ist langsamer und der nächste Wert des MSB liegt an bevor der
alte abgeholt wurde.
Quelltext als Anhang, bitte.
Jim M. schrieb:> Oder er ist langsamer und der nächste Wert des MSB liegt an bevor der> alte abgeholt wurde.
Die Übertragung eines Bytes dauert 1ms. So langsame Controller gibt es
gar nicht.
Hans D. schrieb:> Dem Controller schicke ich über LabView 2 Byte. Zuerst das MSB dann das> LSB. Die beiden Bytes füge ich auf dem Controller in eine 16 Bit> Variable zusammen. Das klappt soweit ganz gut.
Woher soll der Empfänger wissen, welche das erste und welches das zweite
Byte ist. Jeweils ein Start- und ein Stopbit reichen nicht, um die Bytes
beim Empfänger zu unterscheiden. Du könntest drei Byte übertragen. Das
würde für 21 Bit Daten zzgl. 1 Bit Synchronisationsinformation pro
gesendetem Byte reichen.
Hallo an alle,
ich bin sehr erfreut, dass mir so viele Leute helfen wollen.:)
Schonmal ein Dankeschön dafür.
Den Quellcode findet ihr im Anhang.
Viele Grüße
Du verwendest UARTInterruptZaehler sowohl im Interrupt als auch in der
Hauptroutine und deine if() Abfragen sind sicherlich nicht atomar -> Es
kann passieren dass du gerade einen deiner if() Blöcke abarbeitest, dann
kommt ein Interrupt mit neuen Daten und "Command" wird überschrieben.
Du könntest das ganze synchronisieren indem du nicht ständig die
Hauptroutine laufen lässt, sondern in der ISR eine Art Semaphore setzt,
das in der Hauptroutine gepollt und ausgewertet wird.
In der ISR selbst setzt du nur das Semaphore und pufferst evtl noch
"Command" weg, so dass die Hauptroutine mit einer Kopie von "Command"
arbeitet
Mir ist grade klar geworden, dass es sinnvoll wäre nochmal zu
beschreiben wie der Sender arbeitet:
Er sendet:
<Befehl 85><warten auf Quittierung vom Mikrocontroller uC sendet 1
(dezimal)><sendet MSB><warten auf Quittierung vom Mikrocontroller uC
sendet 1 (dezimal)><sendet LSB><Wartet auf 2 Byte(uC antwortet mit MSB
und LSB)>
Anschließend beginnt der Zyklus von Vorne, also wieder:
<Befehl 85><warten auf Quittierung vom Mikrocontroller uC sendet 1
(dezimal)><sendet MSB><warten auf Quittierung vom Mikrocontroller uC
sendet 1 (dezimal)><sendet LSB><Wartet auf 2 Byte(uC antwortet mit MSB
und LSB)>
....
Das Ganze in einer Dauerschleife. Dabei treten dann die oben
beschriebenen Probleme auf.
Hans D. schrieb:> Das Ganze in einer Dauerschleife. Dabei treten dann die oben> beschriebenen Probleme auf.
Ja aber wie verhinderst du, dass der Mikrocontroller lesend auf die 16
bit Variable während grade eine neue 16 bit Variable an den
Mikrocontroller übertragen wird? Bei einen atomaren Zugriff wird
verhindert, dass während eines Lesezugriffs schreibend auf die Variable
zugegriffen wird und umgekehrt.
Auf die 16 Bit Variable "Aufstromeit" greift der uC nur zu, wenn ich ihm
den Befehl 5 (dezimal) sende.
Verhindert das nicht, dass ich die Variable lese während ich Sie
schreibe?
Hans D. schrieb:> Auf die 16 Bit Variable "Aufstromeit" greift der uC nur zu, wenn ich ihm> den Befehl 5 (dezimal) sende.
Hm? Du greifst auch beim Zusammenbauen darauf zu. Und genau da kann was
schief gehen, wenn du einen Interrupt bekommst und dann in "Command" das
LSB geschrieben wird, obwohl du in deiner MSB if-Schleife bist
Hans D. schrieb:> Mir ist grade klar geworden, dass es sinnvoll wäre nochmal zu> beschreiben wie der Sender arbeitet:
Dir sollte klar werden, daß es totaler Mist ist, was du machst.
Wenn ein Datensatz, der aus mehreren Bytes besteht, übertragen werden
soll, brauchst du ein Protokoll. Das sieht minimal so aus:
Präambel
Daten
Postambel
Wenn die Länge der Daten immer gleich ist, kann man auf die Postambel
auch verzichten. Fehlererkennung bzw. -korrektur lassen wir mal aussen
vor.
Der Sinn der Präambel ist, dem Empfänger eindeutig zu signalisieren, daß
jetzt ein Satz von Daten folgt. Der der Postambel ist es, ihm
mitzuteilen, daß die Übertragung jetzt zuende ist.
Die Daten, die dazwischen übertragen werden, dürfen weder für eine
Präambel noch für eine Postambel gehalten werden. Alles, was dafür nötig
ist, wurde schon vor Jahrzehnten erfunden und nennt sich ASCII. Also
benutze das auch. Keiner hat hier Lust, deinen Murks zum Laufen zu
bringen.
Thomas E. schrieb:> Wenn ein Datensatz, der aus mehreren Bytes besteht, übertragen werden> soll, brauchst du ein Protokoll. Das sieht minimal so aus:>> Präambel> Daten> Postambel
Übernimmt in dem Fall nicht der Befehl die Funktion der Präambel?
Wie man das mit ASCII macht, verstehe ich nicht. Finde nirgendwo eine
Anleitung dafür..
Hi,
bei 2 Byte langt doch auch der 9Bit Modus der UART, MSB hat 9. bit
gesetzt, LSB nicht. (wo das zusätzliche bit ist würde ich halt erst
gucken wie die Darstellung im Atmega ist. Minimal, keine Absicherung,
aber schon mal kein Vertauschen von MSB mit LSB.
MfG
myasuro
Okay....wenn das alles Murks ist, würde ich versuchen ein richtiges
Übertragungsprotokoll, das mit ASCII Zeichen arbeitet, zu
programmieren.
Habe sowas leider noch nie gemacht. Könnt ihr mir mit Links weiterhelfen
wo beschrieben steht wie man sowas macht?
Vielen Dank
Thomas E. schrieb:> Präambel> Daten> Postambel>> Wenn die Länge der Daten immer gleich ist, kann man auf die Postambel> auch verzichten.
Oder wenn im Präambel die Länge der Daten mitkodiert ist, oder wenn in
den Daten selber das Ende mitkodiert ist (wie zB bei MIDI).
Hans D. schrieb:> Okay....wenn das alles Murks ist, würde ich versuchen ein> richtiges Übertragungsprotokoll,
Das hast du eigentlich schon, nur ein wenig zu umständlich. Poste
erstmal deine vollständige Code. Als C-Datei.
Hans D. schrieb:> Okay....wenn das alles Murks ist, würde ich versuchen ein richtiges> Übertragungsprotokoll, das mit ASCII Zeichen arbeitet, zu> programmieren.
Nein, lass es sein. Das sind die Tipps von Leuten, die keine
Ahnung haben, weil die so etwas noch nie implementiert haben.
Mach es mit: <SOF><LSB><MSB><EOF>
Das sind (und bleiben) immer 4 Byt.
Bei ASCII sind es zwischen 1 und 5 Bytes ( + CR/LF) und das Ganze
muss dann auch noch umgerechnet werden.
<SOF> ist bei mir 0xAA, <EOF> ist 0xA9 und da gab es noch nie
irgendwelche Probleme damit.
Auf <SOF> warten.
Weitere 3 Bytes einlesen.
Letztes Byte == <EOF> ?
Hans D. schrieb:> Übernimmt in dem Fall nicht der Befehl die Funktion der Präambel?> Wie man das mit ASCII macht, verstehe ich nicht. Finde nirgendwo eine> Anleitung dafür..
Jein.
Was sendest du? Die Zahl 85?
Die Zahl 85 gehört zum Textsatz des ASCII. Diese Codes werden auch nur
dafür benutzt. Guck in deine ASCII-Tabelle, dann siehst du, wie das
aufgebaut ist. Solltest du keine haben, nimm diese hier:
http://www.ascii-code.com/
Es gibt Steuercodes und Textcodes. Steuercodes sind die von 0 bis 31.
So, und jetzt ist Schluß mit dezimal. Da mußt du durch.
Steuercodes gehen von 0 - 0x1F.
Darstellbare Zeichen von 0x20 - 0x7F
Erweiterte darstellbare Zeichen von 0x80 - 0xFF
Für die Präambel benutzt man Zeichen aus dem Steuercode, für die Daten
aus dem Textcode. Warum macht man das so? Damit Steuercodes nicht mit
Daten verwechselt werden können. Das ist das A und O der Übertragung.
Die Übertragung beginnt also mit einem Steuerzeichen. Escape, 0x1B, wird
dafür gerne genommen. Mit ein paar Ausnahmen, kann man aber auch jedes
andere dafür nehmen. Bleiben wir mal bei Esc.
Darauf folgen die Daten. Wir übertragen den Wert 27. Nun hat die
Dezimalzahl 27 die unangenehme Eigenschaft aus den gleichen Bits zu
bestehen, wie unser Escape. Deswegen können wir das nicht so übertragen.
Die Zahl in Hex umzuwandeln, 0x1B, nützt uns natürlich auch nichts.
Deswegen wird die Zahl in Text umgewandelt. Und zwar Nibble für Nibble.
Spätestens jetzt ist definitiv Schluß mit Dezimalzahlen. Das Byte mit
dem Inhalt 27 = 0x1B wird in zwei Zeichen umgewandelt:
Aus dem Highnibble 1 wird '1' und aus dem Lownibble B wird 'B'.
Hä?
1 ist die Zahl mit dem Wert 1. Also 1 Meter, 1 Apfel oder was weiß ich.
'1' ist in C die Schreibweise für das Zeichen 1, welches, wenn es
gedruckt wird, vereinbarungsgemäß der Zahl 1 entspricht. B und 'B'
entsprechend.
Jetzt guckst du in die Tabelle und siehst '1' hat den Code 0x31 und 'B'
den Code 0x42. Diese beiden Codes werden jetzt statt der Zahl 27
übertragen. Darauf folgt in gleicher Weise die Hexzahl B.
Die Umwandlung ist ganz einfach: Man addiert einfach zu dem Nibble die
Zahl 0x30. Wenn diese Zahl dann größer als 0x39 ist, müssen nochmal 7
hinzuaddiert werden. Um die Hexumwandlung brauchst du dich nicht zu
kümmern. Die Zahl steht sowieso so im Register. Dezimal ist nur für uns
wenn wir es lesen wollen.
Der gesamte Datensatz sieht jetzt also so aus:
0x1B = Escape
0x31 = '1'
0x42 = 'B'
...
...
0x0A = '\n'(Line Feed)
Der Empfänger macht zunächst nichts anderes als auf das Escape zu
warten.
Hat er dieses empfangen, setzt er den Empfangszähler, -counter,
-pointer, nenne es, wie du willst, auf 0 und schreibt die Bytes , die
darauf folgen in einen Puffer. Der Puffer muß so groß, daß alles, was an
Daten kommt, da reinpasst. Wenn er das 0x0A empfangen hat, setzt er ein
Flag, das im Hauptprogramm abgefragt wird und dieses bearbeitet die
Daten. Das mit dem Flag setzten, ist eine einfache Möglichkeit von
vielen, das zu handhaben.
Soweit erstmal klar?
Marc V. schrieb:> Nein, lass es sein. Das sind die Tipps von Leuten, die keine> Ahnung haben, weil die so etwas noch nie implementiert haben.
Sprücheklopfer.
Thomas E. schrieb:> Sprücheklopfer.
Nein, nur jemand der so etwas tatsächlich geschrieben und zum laufen
gebracht hat.
Warum sollte er 2 Bytes umrechnen, 6 Zeichen senden und empfangen,
diese dann wieder zurückrechnen, wenn es mit nur 4 Bytes genausogut,
sogar besser geht, weil kürzer und ohne Umrechnen ?
P.S.
Und, um dich zu zitieren:
Thomas E. schrieb:> Denn die gebetsmühlenartig> vorgetragene So-kurz-wie-möglich-Regel ist zwar nicht kompletter Unsinn,> sollte aber einem, bezogen auf das gesamte Programm, "So kurz wie nötig"> weichen.
Mein Gott, entscheidend ist doch dass ein Protokoll verwendet
wird, ihr streitet jetzt darum welches es denn sein soll ....
Bin gespann wer Recht hat/bekommt ;-)
Thomas E. schrieb:> Der Empfänger macht zunächst nichts anderes als auf das Escape zu> warten.> Hat er dieses empfangen, setzt er den Empfangszähler, -counter,> -pointer, nenne es, wie du willst, auf 0 und schreibt die Bytes , die> darauf folgen in einen Puffer. Der Puffer muß so groß, daß alles, was an> Daten kommt, da reinpasst. Wenn er das 0x0A empfangen hat, setzt er ein> Flag, das im Hauptprogramm abgefragt wird und dieses bearbeitet die> Daten. Das mit dem Flag setzten, ist eine einfache Möglichkeit von> vielen, das zu handhaben.
Geht das in die richtige Richtung?
Hallo Hans,
das einfachste und mit nur zwei Byte geht so.
uC im MPCM mode betreiben.
LabView sendet das erste Byte mit MARK Parity.
uC empfängt das Byte und schaltet das MPCM ab.
LabView sendet das zweite Byte mit SPACE Parity.
uC empfängt das Byte und schaltet das MPCM wieder an.
Das Mark Parity ist der eindeutige Start.
ms
ms schrieb:> Das Mark Parity ist der eindeutige Start.
Wird Zeit dass wir hier endlich die uint9_t / int9_t und
uint18_t / int18_t Datentypen salonfähig machen, wa?
Marc V. schrieb:> Mach es mit: <SOF><LSB><MSB><EOF>> Das sind (und bleiben) immer 4 Byt.>> <SOF> ist bei mir 0xAA, <EOF> ist 0xA9 und da gab es noch nie> irgendwelche Probleme damit.
Und was passiert, wenn die binären Nutzdaten zufällig 0xAA oder 0xA9
sind??
Offensichtlich hast du selbst nicht die geringste Ahnung von einem
zuverlässig funktionierenden Protokoll. Aber angeben wie der Gröpaz.
Georg
Georg schrieb:> Und was passiert, wenn die binären Nutzdaten zufällig 0xAA oder 0xA9> sind??
Nichts, werden richtig gedeutet, da vor den Daten schon ein 0xAA
empfangen wurde und nach den Daten ein 0xA9 gekommen ist.
Dein Protokoll zur Kommunikation mit dem Gehirn funktioniert wohl
nicht, oder Gehirn nicht vorhanden ?
> Offensichtlich hast du selbst nicht die geringste Ahnung von einem> zuverlässig funktionierenden Protokoll. Aber angeben wie der Gröpaz.
Nein, aber das scheint bei dir der Fall zu sein. Auf jeden Fall hast
du mit Sicherheit keine Ahnung.
Arduinoquäler schrieb:> Wird Zeit dass wir hier endlich die uint9_t / int9_t und> uint18_t / int18_t Datentypen salonfähig machen, wa?
man muss sich nur zu helfen wissen.
ms
Hans D. schrieb:> ISR(USART_RX_vect)> { UARTInterruptZaehler=UARTInterruptZaehler + 1;> Command = USART_Empfangen();> if (Command == 0x1B)> {> UARTInterruptZaehler = 0;> }> if (Command == 0x0A)> {> dataready=1;> }> }
Im Prinzip.
Aber für den Zähler interessiert sich eigentlich niemand.
Marc V. schrieb:> Nichts, werden richtig gedeutet, da vor den Daten schon ein 0xAA> empfangen wurde und nach den Daten ein 0xA9 gekommen ist.
Dazu ist jeder sachliche Kommentar reine Zeitverschwendung. Dummheit
siegt, wenn sie nur laut genug vorgetragen wird.
Georg
Marc V. schrieb:> Nichts, werden richtig gedeutet, da vor den Daten schon ein 0xAA> empfangen wurde und nach den Daten ein 0xA9 gekommen ist.
Das setzt aber voraus, dass beim Empfang bisher kein Byte verschluckt
wurde. Damit führst Du dann aber auch den sicheren Empfang eines Frames
ad absurdum und Du könntest damit auf Start- und Ende-Zeichen komplett
verzichten, weils denselben Effekt hat - nämlich keinen.
Wenn die Start- und Ende-Zeichen auch in den Daten vorkommen, muss man
sie entweder "escapen" durch ein Vorschaltzeichen, oder man muss
sicherstellen, dass die beiden Zeichen in den eigentlichen Daten nicht
vorkommen. Sonst kann man im Falle einer Fehlübertragung (z.B.
verschlucktes Byte beim Empfangen) nicht mehr sicher am Anfang eines
neuen Frames aufsetzen, um sich wieder zu synchronisieren. Wenn Du
versuchst, auf das nächste 0xAA aufzusetzen, könnte es sein, dass Du
mitten in den Daten aufsetzt. Und das ist ein Fehler. Ganz schlimm ist
es, wenn dann im Datenstrom tatsächlich noch ein 0xA9 steckt. Wo ist
Dein Frame dann zuende?
Ich kann Dir auch gern ein Beispiel konstruieren, wo es sicher
fehschlägt. Aber ich nehme an, Du verstehst es auch so: Eine Sicherung
einer Datenübertragung darf nicht eine korrekte Übertragung
voraussetzen, denn sonst ist sie unnütz. Aber genau das machst Du.
Thomas E. schrieb:> ber für den Zähler interessiert sich eigentlich niemand.>> volatile unsigned char Rbuf[4];> volatile unsigned char Ready = 0;>> ISR(USART_RX_vect)> {> static unsigned char ind = 0;> unsigned char udr = UDR;>> switch(udr)> {> case 0x1B: ind = 0; break;> case 0x0A: Ready = 1; break;> default:> RBuf[ind++] = udr;> ind %= 4;> }> }
Gut...deinen Code verstehe ich so:
Jedesmal, wenn ein Byte empfangen wird, wird die Variable "ind" = 0
gesetzt und der Inhalte des Empfangsregisters in die Variable udr
geschrieben. Die Variable ind wird dann bei jedem Interrupt um 1 erhöht.
Irgendwann wird dann 0x1B empfangen und ind = 0 gesetzt. Das Byte das
danach empfangen wird, wird dann in RBuf[1] gespeichert, das danach in
RBuf[2], dann RBuf[3] und zum Schluss RBuf[4].
Wird 0x0A empfangen, wird Ready gleich 1 gesetzt.
Ready = 1 müsste dann in der while(1)-Schleife erkannt werden und der
Empfangsbuffer ausgelesen werden?
Hans D. schrieb:> Jedesmal, wenn ein Byte empfangen wird, wird die Variable "ind" = 0> gesetzt
Nein, nicht jedesmal. Das ist eine statische Variable. Die verhält sich
wie eine globale Variable, nur daß man sie außerhalb der ISR nicht
sieht.
Den Rest hast du richtig verstanden.
Du solltest die Lücken in deinen C-Kenntnissen schließen.
Frank M. schrieb:> Ich kann Dir auch gern ein Beispiel konstruieren, wo es sicher> fehschlägt. Aber ich nehme an, Du verstehst es auch so: Eine Sicherung> einer Datenübertragung darf nicht eine korrekte Übertragung> voraussetzen, denn sonst ist sie unnütz. Aber genau das machst Du.
Das mag ich. Alle auf einen, dass die anderen Beispiele gar keine
Fehlerprüfung haben, macht ja nichts. Erinnert mich an ein bestimmtes
Tier aus Afrika, fängt mit H und endet mit e. Kann mich nicht mehr
genau auf den Namen erinnern, aber egal, vielleicht kommt einer von
Euch auf den Namen...
Okay.
Pseudocode auf die schnelle, so aus dem Kopf:
1
constc_SOF=0xAA
2
constc_EOF=0xA9
3
constsofOK=0
4
consteofOK=1
5
6
RxPtr=0
7
RxFlag=0
8
9
main()
10
while(RxFlag.eofOK<>1)
11
12
13
ISR:
14
RxByt=UDDR
15
ifRxFlag.eofOK==1thenreturn
16
17
ifRxFlag.sofOK==0
18
ifRxByt==c_SOFthen
19
RxPtr=0
20
RxFlag=(1<<sofOK)
21
endif
22
23
else
24
RxPtr++
25
ifRxPtr>=3then
26
IfRxByt<>c_EOFthen
27
RxFlag=0// Habe auch irgendwo eine Routine welche die
28
RxPtr=0// schon empfangenen Bytes auf SOF prüft
29
else
30
RxFlag|=(1<<c_EOF)
31
endif
32
endif
33
endif
34
RxBuff[RxPtr]=RxByt
Hier kann zwar ein ganzes Frame verlorengehen, aber aus dem Takt
kann diese Rotine niemals kommen .
Und mit der oben erwähnten Routine nichteinmal das.
Und jetzt bitte ich um dein konstruiertes Beispiel.
Thomas E. schrieb:> Nein, nicht jedesmal. Das ist eine statische Variable. Die verhält sich> wie eine globale Variable, nur daß man sie außerhalb der ISR nicht> sieht.> Den Rest hast du richtig verstanden.>> Du solltest die Lücken in deinen C-Kenntnissen schließen.
Mit den Lücken stimme ich dir zu. Bei den Dingen, die ich bisher so
programmiert habe, bin ich mit meinen "Kenntnissen" zum Ziel gekommen.
Hier ist wohl eine Grenze erreicht.
Die empfangenen Daten würde ich dann so wieder decodieren?
Hans D. schrieb:
> Zeichen1 = Zeichen1 + 0x30;> if(Zeichen1 > 0x39)
Nein. So macht das im Prinzip der Sender.
Du empfängst für die Zahl 1 das Zeichen '1'. Also 0x31. Daraus mußt du
0x01 machen.
unsigned char AsciiToByte(char nH, char nL)
{
if(nH & 0x40) nH += 9;
if(nL & 0x40) nL += 9;
return (nH << 4) + (nL & 0x0F);
}
Hans D. schrieb:
> Befehl = (Zeichen1<<24)|(Zeichen2<<16)|(Zeichen3<<8)|(Zeichen4);
Damit hast du ein lückenhaftes Long gebastelt.
1
unsignedintValue=AsciiToByte(RBuf[3],RBuf[2])<<8;
2
Value+=AsciiToByte(RBuf[1],RBuf[0]);
Die Byte-Reihenfolge ist jetzt:
RBuf[0] = Lowbyte Lownibble
RBuf[1] = Lowbyte Highnibble
RBuf[2] = Highbyte Lownibble
RBuf[3] = Highbyte Highnibble
Diese Reihenfolge ist grundsätzlich beliebig. Du solltest aber in jedem
deiner Programme dieselbe Reihenfolge verwenden. Sonst steigst du
spätestens beim übernächsten Programm nicht mehr durch.
Denn diese sehr einfache Empfangsroutine...
1
volatileunsignedcharRbuf[4];
2
volatileunsignedcharReady=0;
3
4
ISR(USART_RX_vect)
5
{
6
staticunsignedcharind=0;
7
unsignedcharudr=UDR;
8
9
switch(udr)
10
{
11
case0x1B:ind=0;break;
12
case0x0A:Ready=1;break;
13
default:
14
RBuf[ind++]=udr;
15
ind%=4;
16
}
17
}
... lässt sich auch sehr einfach universell einsetzen:
1
#define MAX_DATA 13
2
volatileunsignedcharRbuf[MAX_DATA];
3
volatileunsignedcharReady=0;
4
5
ISR(USART_RX_vect)
6
{
7
staticunsignedcharind=0;
8
unsignedcharudr=UDR;
9
10
switch(udr)
11
{
12
case0x1B:ind=0;break;
13
case0x0A:Ready=1;break;
14
default:
15
RBuf[ind++]=udr;
16
ind%=MAX_DATA;
17
}
18
}
Damit kannst du Daten bis 13 Bytes Länge empfangen. Sind die Daten
kürzer, wird nach z.B. 4 Bytes durch Ready das Ende signalisiert.
vn nn schrieb:> Diese Behauptung halte ich gelinde gesagt für Blödsinn.
Dem kann man nicht widersprechen. ASCII ist ein Code und kein Protokoll.
Dieser Code macht aber Protokolle sehr einfach.
Thomas E. schrieb:> Dieser Code macht aber Protokolle sehr einfach.
Aha.
Wenn die Computer lesen könnten.
Ansonsten nimmt man CRC und hat ein Byte overhead anstatt doppelter
Lange.
Thomas E. schrieb:> vn nn schrieb:>> Diese Behauptung halte ich gelinde gesagt für Blödsinn.>> Dem kann man nicht widersprechen. ASCII ist ein Code und kein Protokoll.> Dieser Code macht aber Protokolle sehr einfach.
Mein Kommentar bezog sich eher auf die Behauptung, dass die meisten
Protokolle im Internet auf ASCII aufbauen würden, dass er es auch noch
falsch ausgedrückt hat, hab ich überlesen.
ACK: Frame als gültig erkannt
NACK: Frame wird verworfen
Lediglich 1 Byte wird verschluckt, nämlich das, welches ich oben mit xx
gekennzeichnet habe. Danach ist Deine Empfängerroutine aus dem Takt und
kann keinen einzigen der nachfolgenden Frames mehr erkennen.
vn nn schrieb:>> Warum sind wohl die meißten Protokolle im Internet ASCII?>> Diese Behauptung halte ich gelinde gesagt für Blödsinn.
Die wichtigsten Dienste im Internet sind meiner Meinung nach Web (HTTP),
Mail (IMAP, SMTP, POP3), Chat (XMPP = Jabber = Facebook-Chat =
Google-Chat, IRC) und Telefonie (SIP). Das sind, wie die meisten offenen
Standards, alles ASCII-Protokolle.
Verbreitete Binärprotokolle sind z.B. Skype, H.323 und RTP.
Frank M. schrieb:> Lediglich 1 Byte wird verschluckt, nämlich das, welches ich oben mit xx> gekennzeichnet habe. Danach ist Deine Empfängerroutine aus dem Takt und> kann keinen einzigen der nachfolgenden Frames mehr erkennen.
Ja.
Nur ist das weitere senden von Daten zwecklos, da immer wieder die
selben Daten kommen.
Natürlich kann das bei ASCII nicht vorkommen, oder ?
Auf deinem Prinzip basierend, kann ich dasselbe für jedes
Protokoll konstruieren.
Wie schon gesagt, man nimmt binär und CRC, wenn Zeichen dauernd
"verschluckt" werden (wo passiert das ?).
Oder man baut die Verbindung richtig auf, den mit richtiger Verbindung
gibt es auch kein "Verschlucken" von Zeichen.
S. R. schrieb:> Die wichtigsten Dienste im Internet sind meiner Meinung nach Web (HTTP),> Mail (IMAP, SMTP, POP3), Chat (XMPP = Jabber = Facebook-Chat => Google-Chat, IRC) und Telefonie (SIP). Das sind, wie die meisten offenen> Standards, alles ASCII-Protokolle.
Tja, nur werden diese ASCII nicht RAW übertragen, sondern eingebettet.
Marc V. schrieb:> Nur ist das weitere senden von Daten zwecklos, da immer wieder die> selben Daten kommen.
Ich kann auch Beispiele mit variierenden Daten aufführen, wo Du mehr als
einen Frame pro Übertragungsfehler wegwerfen musst. Das heisst, die
Frameerkennungsrate sinkt auch so beträchtlich im Vergleich zu einem
Protokoll, welches als Framebegrenzungen spezielle Sonderzeichen
verwendet, die in den Daten selbst nicht vorkommen.
> Natürlich kann das bei ASCII nicht vorkommen, oder ?
Nein, da gibt es ein oder zwei Ende-Zeichen, in der Regel. NL oder CRNL.
Diese kommen in den Daten nicht vor.
Beispiel:
1234 <NL>
A9AA <NL>
AAA9 <NL>
AAA9 <NL>
AAA9 <NL>
Bei einem Bytefehler geht max. ein Frame verloren.
Noch besser wäre das:
<STX> 1234 <ETX>
<STX> A9AA <ETX>
<STX> AAA9 <ETX>
<STX> AAA9 <ETX>
<STX> AAA9 <ETX>
Aber das ist etwas unleserlicher in einem Datenmitschnitt.
> Auf deinem Prinzip basierend, kann ich dasselbe für jedes> Protokoll konstruieren.
Konstruiere es bitte für ASCII <NL> oder <STX>...<ETX>
Frank M. schrieb:> Zeig es mir :-)
Nicht nötig, du weisst auch, dass so etwas ohne Probleme geht.
Aber ich kann mir sinnvollere Sachen vorstellen, als mit Gewalt
unrealistische Beispiele zu konstruieren.
Wie man das Ganze Bulletproof macht, ist schon seit Jahrzehnten
bekannt, hier ging es um ein Beispiel (von mir) wo Zeichen
verschluckt werden und ein Beispiel (von anderen) wo Zeichen
nicht verschluckt werden.
Die Frage für den Deutschen:
- Wie viele Leute sind in dem Bus ?
- 1,2,3, ... 49.
- Genau !!!
Und die Frage für den Juden:
- Und jetzt sagen Sie uns die Namen und Vornamen von diesen Leuten.
Hallo,
es gibt leute die hier was lernen wollen. Ob man jetzt ASCII oder Binär
verwendet kommt auf die Situation drauf an. Ich bin auch kein freund von
ASCII aber hier in diesem fall scheint es passend zu sein.
@Moderator
ein Moderator sollte das Ziel (Fragestellung) nicht aus den Augen
verlieren.
Er lenkt das gespräch gezielt auf eine Lösung hin und nicht zu einer
Diskussion. Punkt.
ms
Soweit habe ich es jetzt verstanden:
Es soll zum Beispiel 5000(dezimal) übertragen werden.
1. Schritt: 5000 --> 0x1388
2.Jede einzelne Ziffer als Zeichen betrachten und in der ASCII Tabelle
die entsprechende Hexadezimalzahl raussuchen.
3. Folgenden Frame an den uC senden um 5000 zu übertragen:
0x1B
0x38
0x38
0x33
0x31
0x0A
Dank der Hilfe von Thomas Eckmann (vielen Dank, das hätte ich allein auf
keinen Fall geschafft), bin ich jetzt soweit, dass ich mit HTerm Frames
mit dem gegebenen Aufbau an den uC senden kann und diese wieder
decodiert werden.
Wie gehts jetzt am besten weiter?
Wie übertrage ich das jetzt am besten auf mein altes Programm sodass ich
möglichst wenig ändern muss.
Jetzt müsste ich doch 85(dez.) zunächst so codiert an den uC senden und
im Anschluss daran meinen 16 Bit Wert, den ich in eine bestimmte
Variable schreiben möchte oder?
Vielen Dank an alle und ein schönes Wochenende:)
MIDI war gar kein so schlechtes Beispiel.
Man könnte das oberste Bit als Sync-Bit nehmen, und pro byte einfach nur
7 Datenbit übertragen.
Dann käme man mit 3 Byte pro Übertragung aus.
Sinngemäß:
Hans D. schrieb:> Wie gehts jetzt am besten weiter?
Du dekodierst deine Daten im Hauptprogramm. Das Befehlsbyte liegt vor
den Daten. Dieses kommt in eine Variable(unsigned char) und die
eigentlichen Daten in eine andere Variable(unsigned int). Und dann
wertest du das aus:
1
if(Befehlsbyte==85)
2
{
3
// tu was
4
}
oder
1
switch(Befehlsbyte)
2
{
3
case0:
4
//tu dies
5
break;
6
case1:
7
//tu das
8
break;
9
case85:
10
//tu jenes
11
break;
12
}
Alles im Hauptprogramm oder in Funktionen, die aus dem Hauptprogramm
aufgerufen werden.
Marc V. schrieb:>> Die wichtigsten Dienste im Internet sind meiner Meinung nach Web (HTTP),>> Mail (IMAP, SMTP, POP3), Chat (XMPP = Jabber = Facebook-Chat =>> Google-Chat, IRC) und Telefonie (SIP). Das sind, wie die meisten offenen>> Standards, alles ASCII-Protokolle.>> Tja, nur werden diese ASCII nicht RAW übertragen, sondern eingebettet.
Weil das Internet Protocol ein Binärprotokoll ist, ist ein
ASCII-Protokoll im Internet logisch unmöglich? Unfug schreibst du ja
öfter, aber ich wüsste manchmal gerne, warum.
Hans D. schrieb:> Es soll zum Beispiel 5000(dezimal) übertragen werden.
Ja. Das geht am einfachsten, indem du die Dezimalzahl ziffernweise als
Text überträgst und z.B. mit einem Endezeichen abschließt.
Dein Frame besteht also aus den Bytes { '5', '0', '0', '0', '\n' } oder,
in hexadezimaler Darstellung { 0x35, 0x30, 0x30, 0x30, 0x0A }. (Beide
Darstellungen sind exakt gleich.)
> 1. Schritt: 5000 --> 0x1388
Du willst eine Dezimalzahl übertragen.
Warum wandelst du sie dann in Hex um? Ist doch nur Arbeit!
> 2.Jede einzelne Ziffer als Zeichen betrachten und in der ASCII Tabelle> die entsprechende Hexadezimalzahl raussuchen.
Wenn du eine Zahl in eine Zeichenkette umgewandelt hast, dann ist jedes
Zeichen darin (abzüglich dem Nullbyte am Ende) eine Ziffer. Jede Ziffer
liegt bereits nach Tabelle passend codiert vor.
> 3. Folgenden Frame an den uC senden um 5000 zu übertragen:> 0x1B -- ESC> 0x38 -- '8'> 0x38 -- '8'> 0x33 -- '3'> 0x31 -- '1'> 0x0A -- '\n'
Erstens überträgst du damit die Hexadezimaldarstellung, ohne sie zu
markieren (üblicherweise markiert man sowas mit einem "0x"-Präfix).
Zweitens überträgst du die Zahl rückwärts und machst es damit dem
empfandenden System schwerer, sie zu dekodieren.
Thomas hat vollkommen Recht und Marc schreibt mal wieder halbgaren
Unfug.
Wenn deine Kommunikation immer aus "Befehl Parameter Parameter" besteht,
dann kannst du dein Protokoll auch so definieren:
- ASCII-Zahl ( Leerzeichen ASCII-Zahl )wiederholen Zeilenende
Also "85 17 32 535\n" bedeutet dann "Befehl 85 mit den Parametern 17, 32
und 535". Dein Empfangscode schreibt also so lange Bytes in einen
Puffer, bis ein "\n" kommt und übergibt die gesamte Zeile auf einmal an
eine Auswertefunktion. Die zerlegt die Zeile dann in die Stücke.
S. R. schrieb:> Ja. Das geht am einfachsten, indem du die Dezimalzahl ziffernweise als> Text überträgst und z.B. mit einem Endezeichen abschließt.
Nope, am einfachsten geht das indem man die zwei Bytes überträgt und das
ist dann auch noch schneller.
Sender macht:
LowByte = 5000 & 0xff;
HighByte = (5000 >> 8) & 0xff;
Empfänger macht:
LowByte = EmpfangsByte;
HighByte = EmpfangsByte << 8;
Value = HighByte | LowByte;
Und mit einem geeigneten Protokoll weiß man was wann gesendet wird bzw.
angekommen ist.
S. R. schrieb:> Weil das Internet Protocol ein Binärprotokoll ist, ist ein> ASCII-Protokoll im Internet logisch unmöglich? Unfug schreibst du ja> öfter, aber ich wüsste manchmal gerne, warum.Einen ASCII-Protokoll gibt es nicht.
Das, wovon du redest sind Nutzdaten, es ist absolut uninteressant ob
da ASCII-Zeichen oder binäre Daten übertragen werden.
Schreib nicht wieder so einen Unfug bitte, du machst dich nur immer
wieder lächerlich mit deinem Halbwissen.
S. R. schrieb:> Thomas hat vollkommen Recht und Marc schreibt mal wieder halbgaren> Unfug.
Erstens ist das nicht wahr und zweitens ist das, was ich vorgeschlagen
habe weder die einzig richtige Methode, noch habe ich geschrieben,
dass der Thomas Unrecht hat und seine Methode falsch ist.
Was willst du also beweisen (ausser deinem Unwissen und notorischem
rumhaken an mir weil ich klüger und wahrscheinlich auch schöner bin) ?
ms schrieb:> es gibt leute die hier was lernen wollen.> Ob man jetzt ASCII oder Binär> verwendet kommt auf die Situation drauf an.
Das sehe ich genauso.
> Ich bin auch kein freund von> ASCII aber hier in diesem fall scheint es passend zu sein.
Auch hier stimme ich Dir zu. Der mögliche Wertebereich liegt bei max. 5
Ziffern, ist zudem portabel und ist unabhängig von Endianess des Senders
oder Empfängers.
> @Moderator> ein Moderator sollte das Ziel (Fragestellung) nicht aus den Augen> verlieren.> Er lenkt das gespräch gezielt auf eine Lösung hin und nicht zu einer> Diskussion. Punkt.
Das allerdings sehe ich anders. Auch die Anregung zu einer Diskussion
kann zu einer Führung der Teilnehmer auf den richtigen Weg führen und
hat sogar noch einen gewissen Vorteil: die Teilnehmer können sich die
Lösung selbst erarbeiten.
Aber so oberlehrerhaft möchte ich das ganze gar nicht aufhängen: auch
ich bin in der Regel ein ganz normaler Forumsteilnehmer, der sich hier
gerne an solchen Diskussionen beteiligen möchte.
Aus meiner Erfahrung mit IR-Protokollen kann ich jedoch beurteilen, dass
eine Rahmung des Frames mit einer Präambel, die selbst nicht in den
Nutzdaten vorkommen kann, zu einer erheblichen Verbesserung der
Kommunikationssicherheit führt. Genau das wollte ich mit meinen
Beiträgen als Erwiderung auf Marcs 0xAA-0xA9-Lösung zum Ausdruck
bringen.
Ich sehe daher keinen Grund zur Aufregung.
Marc V. schrieb:> Nicht nötig, du weisst auch, dass so etwas ohne Probleme geht.
Sorry, Marc. An dieser Stelle kann ich Dir nicht folgen. Ich habe es
versucht, aber ich kann beim besten Willen bei dem einfachen
zeilenweisen Protokoll,
12345 <NL>
23456 <NL>
34567 <NL>
...
wo ein simples Newline-Zeichen, welches selbst in den Nutzdaten nicht
vorkommen kann, lediglich einen einzigen Frame durch Wegnehmen,
Hinzufügen oder Kippen eines Bits im Datenstrom kaputtmachen - mehr
nicht.
Ich bekomme es nicht hin, hier mehr als einen Frame zu zerstören.
> Aber ich kann mir sinnvollere Sachen vorstellen, als mit Gewalt> unrealistische Beispiele zu konstruieren.
Bei dem Beispiel ging es um das Vorkommen der Präambel/Postambel in den
Nutzdaten, was durchaus bei Deinem 0xAA-0xA9-Protokoll vorkommen kann.
Genau das habe ich durchexerziert. Dabei kann man leicht zeigen, dass
durch genau einen Übertragungsfehler, ein, zwei oder mehr Frames
zerstört werden. Zugegebenermaßen war mein Beispiel der Worst-Case, der
passieren kann. Aber es gibt noch jede Menge andere mögliche
Kombinationen, die mehr als einen Frame dabei kaputtmachen. Allein die
theoretische Möglichkeit sollte einen davon abbringen, solch eine Lösung
zu wählen. Murphy existiert!
Ich beschäftige mich schon seit vielen Jahren mit
Kommunikationsprotokollen, zudem habe ich mit der Entwicklung von
IRMP und der Dekodierung von mittlerweile 50 IR-Protokollen jede
Menge Erfahrung sammeln können. Das Fazit daraus: Ohne eine
ausgezeichnete Präambel wird es verdammt schwierig, die Daten sicher
sicher zu extrahieren. Da habe ich schon immer die Entwickler des
Denon-Protokolls verflucht. Alle anderen mir bekannten Protokolle
arbeiten da glücklicherweise anders.
Okay, eine ausgezeichnete Präambel ist eher wichtig bei
Übertragungswegen, bei denen es bei Übertragungspausen zu vermehrten
Rauschen kommen kann. Das ist eher bei Funk und bei IR der Fall. Bei
UART-Übertragung kann man darauf verzichten, daher ist dort nicht
unbedingt eine Form von
<STX>Data<ETX>
notwendig. Hier reicht daher ein Trenner per Postambel, zum Beispiel das
einfache Newline. Wichtig ist aber auch hier, dass dieses Zeichen nicht
in den Nutzdaten vorkommt.
Frank M. schrieb:> Kommunikationssicherheit führt. Genau das wollte ich mit meinen> Beiträgen als Erwiderung auf Marcs 0xAA-0xA9-Lösung zum Ausdruck> bringen.
Es ist keine Lösung, natürlich. Es war nur ein Vorschlag, nur wusste
ich damals noch nicht, dass die Verbindung bei binärer Übertragung
so schlecht ist, bei ASCII-Zeichen aber einwandfrei funktioniert,
sonst hätte ich gleich CRC oder Checksum vorgeschlagen...
Marc V. schrieb:> Es ist keine Lösung, natürlich. Es war nur ein Vorschlag, nur wusste> ich damals noch nicht, dass die Verbindung bei binärer Übertragung> so schlecht ist, bei ASCII-Zeichen aber einwandfrei funktioniert,> sonst hätte ich gleich CRC oder Checksum vorgeschlagen...
Eine ausgezeichnete Präambel/Postambel schützt natürlich noch lange
nicht vor gefälschten Daten. Sie hilft nur bei der Eingrenzung der
Frames. Wer hier auch noch eine gewisse Datensicherheit innerhalb der
Frames möchte, kommt natürlich nicht um die zusätzliche Übertragung von
redundanten Daten herum. Die damit verbundene Erhöhung der
Datensicherheit ist mathematisch berechenbar. Ein Paritätsbit ist dabei
die geringstmögliche Erhöhung (fifty-fifty, also 1/2), eine CRC8 erhöht
die Sicherheit immerhin schon auf 255/256. Und so weiter...
Frank M. schrieb:> Sorry, Marc. An dieser Stelle kann ich Dir nicht folgen. Ich habe es> versucht, aber ich kann beim besten Willen bei dem einfachen> zeilenweisen Protokoll,>> 12345 <NL>> 23456 <NL>> 34567 <NL>
Normalerweise ja.
Aber diese von mir konstruierte Verbindung ist ein bisschen komisch
- es wird genau der <NL> verschluckt und das auch nur wenn
ASCII-Zeichen übertragen werden - bei binären Werten funktioniert die
Übertragung ausgezeichnet :)
> Ich beschäftige mich schon seit vielen Jahren mit> Kommunikationsprotokollen
Ich auch, deswegen schrieb ich, dass ich da noch eine Routine habe,
welche bei auftretendem Fehler die schon empfangenen Bytes nach SOF
abklappert und dann von dort wieder anfängt.
Und es ist sehr, sehr selten, dass mit UART/RS485/CAN ein Byte
verschluckt wird - falsch empfangen ja, dann aber mit Frame error
und der CRC oder Checksum stimmt dann auch nicht mehr.
Bei IR vielleicht möglich, da habe ich wenig bis Null Erfahrung.
Marc V. schrieb:> bei binären Werten funktioniert die Übertragung ausgezeichnet :)
Bei binären Werten ist es natürlich auch um das Newline schlecht
bestellt. Bei reiner ASCII-Übertragung (d.h. der Übertragung von
druckbaren 7-Bit-Zeichen) klappt natürlich auch die Übertragung mit
<AA>...<A9>, da der Wertebereich der Daten hier nur maximal von 0x20 bis
0x7E geht.
Bei binären Daten, welche zudem auch noch eine variable Länge haben,
kann man sich natürlich auch einen Frame-Trenner aussuchen, der dann in
den Daten selbst "escaped" wird.
Beispiel:
Wir benutzen als Frame-Trenner das Newline <NL>, oder auch Linefeed <LF>
(0x0A) genannt. Kommt es in den Daten selbst vor (binäre Übertragung),
dann wird ein <ESC> (0x1B) vorangestellt. Kommt das <ESC> in den Daten
vor, wird es verdoppelt.
Aus dem Datenstrom <ESC> wird also <ESC><ESC>, aus <LF> wird <ESC><LF>.
Alle anderen Zeichen werden 1:1 ausgegeben und am Ende des Frames mit
<LF> terminiert. Man muss jedoch auf der Empfängerseite darauf achten,
dass die Datengröße eines Frames sich im Worst-Case verdoppeln kann.
Diese Methode benutzten in der Vergangenheit schon Modem-Protokolle wie
Kermit, X-Modem und wie sie alle hießen, um Binärdaten zu übertragen.
Die oben genannte Methode, Frametrenner selbst zu "escapen", kann man
natürlich auch in C übersetzen:
1
#define ESC 0x1B
2
#define LF 0x0A
3
4
voidput16(uint16_tw)
5
{
6
uint8_th=w>>8;
7
uint8_tl=w&0xFF;
8
9
if(h==ESC||h==LF)
10
{
11
putc(ESC);
12
}
13
14
putc(h);
15
16
if(l==ESC||l==LF)
17
{
18
putc(ESC);
19
}
20
21
putc(l);
22
23
putc(LF);
24
}
(Der Code ist ungetestet hier reingetippt, ohne Gewähr auf Tippfehler)
Hier wird das obere Byte zuerst übertragen, dann das untere Byte. Das
kann man natürlich auch umdrehen. Die Framelänge wird durch das
"escapen" variabel, d.h. sie kann zwischen 3 und 5 Bytes betragen.
Die Empfängerroutine überlasse ich dabei dem geneigten Leser :-)
Frank M. schrieb:> Aus dem Datenstrom <ESC> wird also <ESC><ESC>, aus <LF> wird <ESC><LF>.> Alle anderen Zeichen werden 1:1 ausgegeben und am Ende des Frames mit> <LF> terminiert.
Ohne jetzt endlos auf verschiedenen Vorteilen und Nachteilen
rumreiten zu wollen, aber ich mache es seit langem so:
<SOF><RcvAdr><SndAdr><Len><Cmnd><Data>...<CRC><EOF>
Habe die entsprechenden Bibliotheken geschrieben, brauche nicht mehr
darüber nachzudenken, wird einfach eingebunden.
Funktioniert ohne irgendwelche Probleme. Fehler werden erkannt und
(fast alle) selbständig in der Routine behandelt - nochmaliges senden,
automatische Bestätigung und ähnlich.
Aber ob es sich lohnt, so etwas für 2 Bytes zu schreiben, ist eine
ganz andere Frage.
Eine Pause zwischen 2 Blocks würde es auch ohne weiteres tun, da
könnte man in der Timerroutine ganz einfach den Flag für ein neues
Packet setzen...
Marc V. schrieb:> Ohne jetzt endlos auf verschiedenen Vorteilen und Nachteilen> rumreiten zu wollen, aber ich mache es seit langem so:
Ohne Frage, wenn man die obige put16()-Funktion um beliebige Datenmengen
plus zusätzlicher CRC erweitern will, wird man irgendwann auf dieselbe
oder zumindest ähnliche Form kommen, die Du mit
> <SOF><RcvAdr><SndAdr><Len><Cmnd><Data>...<CRC><EOF>
skizziert hast. Dabei kann <Data> auch eine "escaped" Form sein, was
dann im Ganzen gesehen die höchstmögliche Frame- und Datensicherheit
bringen kann.
Bei lediglich 16-Bit-Daten ist das jedoch wie Kanonen auf Spatzen.
> Eine Pause zwischen 2 Blocks würde es auch ohne weiteres tun, da> könnte man in der Timerroutine ganz einfach den Flag für ein neues> Packet setzen...
Ich bin kein Freund davon. Stecken zwischen Sender und Empfänger
irgendwelche Repeater, Medienwandler (z.B. UART-IP-UART-Brücke) oder
ähnliches, ist das Zeitverhalten schlecht vorhersehbar. Da müsste man
die Pausen schon arg lang machen, um sich sicher zu sein. Das drückt
aber die Datenrate.
Frank M. schrieb:> Ich bin kein Freund davon. Stecken zwischen Sender und Empfänger> irgendwelche Repeater, Medienwandler (z.B. UART-IP-UART-Brücke) oder
Ich auch, aber der TO hat dieses Problem ganz eindeutig nicht ;)
Bei ihm würde eine Pause von etwa 3-4 Bytes vollkommen reichen.
Marc V. schrieb:> Einen ASCII-Protokoll gibt es nicht.
Stimmt, denn es wäre "ein ASCII-Protokoll", weil Substantiv Neutrum.
> Das, wovon du redest sind Nutzdaten, es ist absolut uninteressant ob> da ASCII-Zeichen oder binäre Daten übertragen werden.
Nö. Bei HTTP sende ich einen Header, gefolgt von den eigentlichen
Nutzdaten. Der Header hingegen besteht laut Standard ausschließlich
aus ASCII-Zeichen (inklusive CR und LF).
Die Nutzdaten dürfen beliebige binäre Daten sein *und sind
selbstverständlich nicht Teil des Protokolls*, denn das Protokoll sagt
über deren Struktur nichts aus. (Sie sind aber Teil der Kommunikation.)
Da das Protokoll ausschließlich ASCII-Zeichen verwendet, spricht man von
einem ASCII-basiertem Protokoll. Und die haben Vor- und Nachteile.
Du darfst das in deiner Welt natürlich jederzeit so umdefinieren, wie du
möchtest, aber dann musst du damit leben, dass andere dir vorwerfen,
Unfug zu schreiben.
Ich wäre dann damit raus, da der Marc ja laut eigener Aussage der
klügste und schönste Unfugerzähler von allen ist. Merkt man ja auch
daran, dass seine Aussagen grundsätzlich und vor allem hier auf
vollständige Zustimmung stoßen.
Frank M. schrieb:> Bei binären Werten ist es natürlich auch um das Newline schlecht> bestellt.
Deine vollkommen erfolglosen Versuche, Marc klarzumachen, dass
eindeutige Trennzeichen in den Daten nicht vorkommen dürfen, weil sie
dann eben nicht mehr eindeutig sind, beweist nur eines:
Auch beim Programmieren hat das postfaktische Zeitalter begonnen.
Georg
S. R. schrieb:>> Einen ASCII-Protokoll gibt es nicht.>> Stimmt, denn es wäre "ein ASCII-Protokoll", weil Substantiv Neutrum.
Tja, ich bin kein Deutscher, das ist nicht meine Muttersprache, ich
benutze es in der letzten Zeit praktisch nur hier, im Forum.
Im übrigen verweise ich auf Mark Twain und seine Bemerkungen dazu.
>> Das, wovon du redest sind Nutzdaten, es ist absolut uninteressant ob>> da ASCII-Zeichen oder binäre Daten übertragen werden.>> Nö. Bei HTTP sende ich einen Header, gefolgt von den eigentlichen> Nutzdaten. Der Header hingegen besteht laut Standard ausschließlich> aus ASCII-Zeichen (inklusive CR und LF).
Nö - Nein - No - Nada - Njet.
Der Header, inclusive ev. Nutzdaten muss erst beim Empfänger ankommen.
Dazu wird das Ganze in einem (oder mehreren) Paketen verschickt.
Wenn dieser Paket beim Empfänger angekommen ist, sind sowohl der Header
als auch ev. Nutzdaten 100% fehlerfrei.
Das wird durch den benutzten Protokoll garantiert.
Insofern ist es absolut uninteressant, ob da ASCII oder binary
übertragen wird.
Ist hoffe, dir ist das Ganze jetzt ein bisschen klarer geworden,
obwohl, viel Hoffnung habe ich nicht...
Georg schrieb:> Deine vollkommen erfolglosen Versuche, Marc klarzumachen, dass> eindeutige Trennzeichen in den Daten nicht vorkommen dürfen, weil sie
Du hast doch schon gezeigt, dass du ein Moron bist, musst du das
immer wieder beweisen ?
Und warum erklärst du es hier, warum schreibst du nicht die Firmen,
die sich mit Protokollen beschäftigen und davon leben ?
Vielleicht sehen diese ihren Fehler ein, gehen zu ASCII rüber und
du wirst reich und berühmt?
Marc V. schrieb:> Das, wovon du redest sind Nutzdaten, es ist absolut uninteressant ob> da ASCII-Zeichen oder binäre Daten übertragen werden.
Das kommt wohl ganz schwer auf das Protokoll drauf an. Wenn das
spezielle Steuerzeichen für die Synchronisation verwendet, z.B. für
Start einen Datenblocks, kann es durchaus passieren, dass diese
Steuerzeichen nicht anderweitig im Datenstrom auftauchen dürfen, bzw.
mittels zusätzlicher Escape Sequenz markiert werden müssen. Es sei denn,
man baut zusätzlichen Protokolloverhead ein, um sich dagegen zu
schützen. Bei reinen ASCII-Daten kannst du Codes für Daten und Steuerung
trennen, bei Binärdatenübertragung nicht.
Früher (tm) hat man für eine saubere Synchronisation bei Übertragung von
16-Bit Binärdaten einfach 3 Byte übertragen, in jedem Byte ein Bit für
die Synchronisation (S/s) reserviert und dann sogar noch Platz für eine
Datensatzkennung/Adresse (a) mit 6 Bit, also z.B.
1
1 2 3
2
Saaaaaad sddddddd sddddddd
Das Synchronisationsbit wird beim ersten Byte auf 1 (S) gesetzt und beim
zweiten und dritten auf 0 (s).
Heutzutage ist das bei nahezu unbegenzter Datenrate wohl zu einfach und
Protokollökonomie nicht sonderlich gefragt.
Mit weniger Overhead kann man bei diesen Simpelprotokoll aus, wenn man
die Daten zu größeren Blöcken zusammenfasst und auf die Adressbits
verzichtet, also bei einem Nutzdatenblock mit 7 oder 14 Bytes.
Ist schon lustig zu lesen, bin mal gespannt wie lange es noch dauert bis
einer von euch erkennt, dass ihr beide recht habt und gezielt aneinander
vorbei redet.
Die Diskussion über Protokolle ist das Eine.
Aber die Ursache für die vermutlich gedroppten Bytes ist das andere.
Einmal: wenn die serielle Übertragung mit Parity konfiguriert ist, wie
ist das bei UART auf der Empfängerseite, das Byte wird einfach
verworfen?
Oder bekommt man das Byte trotzdem?
Ansonsten die Zuverlässigkeit der Verbindung:
Welche Baudrate, wie lange ist das Kabel und ist es geschirmt?
Ich finde es etwas seltsam dass so häufig etwas verschluckt wird.
Ich verwende mit uCs jede Menge serielle Übertragung und wenn man
Baudrate etc passen sehe ich quasi nie falsche Bytes.
Damit meine ich jetzt nicht, dass es das Protokoll nicht braucht.
Aber ich meine da wurde ein Schritt übersprungen, die Verbindung
zuverlässig genug zu machen.
Denn wenn am Ende nur 80% durchkommen ist das ja auch Mist.
M. K. schrieb:> Ist schon lustig zu lesen, bin mal gespannt wie lange es noch dauert bis> einer von euch erkennt, dass ihr beide recht habt
Kaum.
Wenn das Ganze so rausgesendet würde, hätte er recht und ich nicht.
Da dies aber nicht der Fall ist...
Conny G. schrieb:> Einmal: wenn die serielle Übertragung mit Parity konfiguriert ist, wie> ist das bei UART auf der Empfängerseite, das Byte wird einfach> verworfen?> Oder bekommt man das Byte trotzdem?
Man bekommt es, nur wird der Flag PE gesetzt (für Parity Error).
> Denn wenn am Ende nur 80% durchkommen ist das ja auch Mist.
Selbstverständlich.
Und die ganze Diskussion über Trennzeichen und ähnliches auch.
Conny G. schrieb:> Einmal: wenn die serielle Übertragung mit Parity konfiguriert ist, wie> ist das bei UART auf der Empfängerseite, das Byte wird einfach> verworfen?
Kommt drauf an. Bei den AVRs kann - wie von Marc beschrieben - das Byte
trotzdem ausgeliefert werden. Bei Paritätsfehlern wird dann ein Flag
gesetzt, welches man vor dem Lesen aus UDR checken muss. Bei anderen
Systemen wie zum Beispiel Unix/Linux werden die Daten mit Parity-Error
einfach verworfen.
> Oder bekommt man das Byte trotzdem?
Du bekommst es auf jeden Fall, wenn Du auf der Empfängerseite den UART
ohne Parity, aber mit Anzahl Datenbits + 1, einstellst.
Beispiel:
Sender 7E1
Empfänger: 8N1
Dann kannst Du selbst entscheiden, ob Du das Paritätsbit auswerten
willst oder nicht. Aber Du musst es auf jeden Fall aus den Daten
rausmaskieren.
Ich benutze das an ATmegas mit vorgeschaltetem Multiplexer. Dort können
dann Geräte wie z.B. Barcode-Leser sowohl mit 8N1 als auch mit 7E1
angeschlossen werden. Auf Empfängerseite (ATmega) ist immer 8N1
eingestellt und ich entscheide selbst über das Paritätsbit.
> Ich finde es etwas seltsam dass so häufig etwas verschluckt wird.
Das passiert meist dann, wenn man keinen oder einen zu kleinen
Ringbuffer auf der Empfängerseite hat. Läuft der Ringbuffer voll, weil
das Programm gerade mit etwas anderem beschäftigt ist, gehen Daten
verloren.
Dagegen gibt es zwei Mittel - eventuell auch in Kombination:
1. Ringbuffer vergrößern
2. Mit dem Sender in einen Dialog treten, d.h. nach einer bestimmten
Anzahl von Frames mit einer Quittung antworten, auf die der Sender
warten muss. Dadurch kann man den Sender dann ausbremsen.
> Ich verwende mit uCs jede Menge serielle Übertragung und wenn man> Baudrate etc passen sehe ich quasi nie falsche Bytes.
Das kommt immer auf die Anwendung an.
Moin,
ich wollte nur Bescheid sagen, dass ich mich Montag wieder zurückmelde
sobald ich den Controller weiter programmiere.
Bis dahin ein schönes Wochenende
Viele Grüße
Frank M. schrieb:>> Oder bekommt man das Byte trotzdem?>> Du bekommst es auf jeden Fall, wenn Du auf der Empfängerseite den UART> ohne Parity, aber mit Anzahl Datenbits + 1, einstellst.
Man bekommt es immer, egal wie der UART auf der Empfängerseite
eingestellt ist.
Es werden nicht die Flanken oder ähnliches gezählt, es werden ganz
einfach Samples zu bestimmten Zeiten genommen und am Ende wird nach
Majority Prinzip entschieden, was da genau empfangen worden ist.
D.h. konkret beim AVR werden 16 Samples pro bit genommen. Samples
8,9 und 10 werden dannn ausgewertet.
Beim Stoppbit sind es nur 10 Samples aber es sind wiederum die Samples
8,9 und 10 die ausgewertet werden.
> Sender 7E1> Empfänger: 7N1
Byte wird empfangen, Parity wird als Stoppbit empfangen.
> Empfänger: 8N1
Byte wird empfangen, Parity wird als bit7 empfangen, Stoppbit OK.
> Empfänger: 8E1
Byte wird empfangen, Parity wird als bit7 empfangen, Stoppbit wird
als Parity empfangen.
Das gesendete Byte wird manchmal falsch, manchmal richtig gedeutet,
aber es wird immer empfangen und bleibt in UDR.
Marc V. schrieb:>> Stimmt, denn es wäre "ein ASCII-Protokoll", weil Substantiv Neutrum.> Tja, ich bin kein Deutscher, das ist nicht meine Muttersprache,
Dann sei dir verziehen und fass es als Kompliment auf.
Mein Schwedisch ist bei Weitem nicht so gut.
>>> Das, wovon du redest sind Nutzdaten, es ist absolut uninteressant ob>>> da ASCII-Zeichen oder binäre Daten übertragen werden.>> Nö. Bei HTTP sende ich einen Header, gefolgt von den eigentlichen>> Nutzdaten. Der Header hingegen besteht laut Standard ausschließlich>> aus ASCII-Zeichen (inklusive CR und LF).> Nö - Nein - No - Nada - Njet.
Wenn im Protokoll festgeschrieben steht, dass die Daten ASCII zu sein
haben, dann darf ich da keine beliebigen Binärdaten reinkippen. Außerdem
ist die Interpretation der Daten ziemlich genau vorgegeben.
Beispiel:
"Content-Length: 1024\r\n" ist so spezifiziert, dass die Zeichenfolge
"1024" als (beliebig lange!) ASCII(!)-Repräsentation einer
Dezimalzahl(!) aufgefasst wird. Ich darf da kein Zeichen
reinschreiben, welches nicht zwischen 0x30 und 0x39 liegt!
Diese Eigenschaft (also menschenlesbare Inhalte zu übertragen) zeichnet
ASCII-Protokolle aus.
> Der Header, inclusive ev. Nutzdaten muss erst beim Empfänger ankommen.
Nein. Bevor der Empfänger den Header nicht analysiert hat, weiß er noch
nichtmal, wieviele Nutzdaten überhaupt ankommen werden.
Wenn ich eine 1 GB große Datei aus dem Internet herunterlade, dann ist
für den Browser nur der HTTP-Header relevant, denn was in der Datei drin
ist, hat ihn nicht zu interessieren. Er will aber wissen, wie sie heißt
und wie groß sie ist - ohne sie vollständig herunterzuladen.
> Insofern ist es absolut uninteressant, ob da ASCII oder binary> übertragen wird.
Nein. Nicht alle Transportkanäle erlauben die Übertragung von rohen
Binärdaten. Da ASCII aber nur eine Teilmenge aller Binärwerte ist, muss
der Kanal es aber auch nicht sein. Eine serielle Schnittstelle mit dem
Format 7N1 wird den Bytewert 0xFF nur als 0x7F übertragen.
Aus genau diesem Grund sind Internet-Protokolle wie SMTP eben keine
Binärprotokolle (sondern ASCII-Protokolle!), denn sie verlangen nicht,
dass der Transportkanal "8 bit clean" sein muss. Und aus genau diesem
Grund verlangt SMTP auch, dass die Nutzdaten keine Binärdaten sind.
Also werden E-Mails vor dem Versand so codiert (Base64,
quoted-printable, ...), damit sie nur noch ASCII-Zeichen enthalten. Sie
dürfen keine Binärdaten sein, denn sonst können sie nicht korrekt
mittels SMTP übertragen werden!
> Ist hoffe, dir ist das Ganze jetzt ein bisschen klarer geworden,> obwohl, viel Hoffnung habe ich nicht...
Für dich sind ASCII-Wert normale Binärwerte. Das ist auch richtig. Aber
nicht jeder Binärwert ist auch ein ASCII-Wert und daraus ergeben sich
Konsequenzen, die dazu führen, dass
- jedes ASCII-Protokoll ein Binärprotokoll ist, aber
- nicht jedes Binärprotokoll ein ASCII-Protokoll ist.
Und darum wird zwischen ASCII-Protokollen und Binärprotokollen
unterschieden.
S. R. schrieb:>> Der Header, inclusive ev. Nutzdaten muss erst beim Empfänger ankommen.>> Nein. Bevor der Empfänger den Header nicht analysiert hat, weiß er noch> nichtmal, wieviele Nutzdaten überhaupt ankommen werden.
Nachdem sich die Gemüter ein bisschen beruhigt haben, versuche ich zu
erklären, was (meinerseits) gemeint ist:
Du schickst ein Paket nach Schweden - was drin steht ist vorerst
uninteressant, aber die Adresse muss draufstehen (und zwar in ASCII).
Die Post hat die Aufgabe, dieses Paket unbeschädigt und ohne dessen
Inhalt zu verändern, nach Schweden zu befördern.
Das passiert auch, dieses Paket ist intakt nach Schweden angekommen,
der erste Teil des Headers (die Adresse auf dem Paket) hat ihren
Zweck erfüllt.
Jetzt wird dein Paket aufgemacht und der Inhaltsverzeichnis (der
zweite Teil des Headers) wird gelesen. Nachdem du das gemacht hast,
weisst du was sich in dem Paket befindet (das wären dann Nutzdaten).
Aber:
Ob sich nun in deinem Paket Briefe oder Bücher auf Deutsch (ASCII)
oder auf Chinesisch (Binär) befinden, spielt gar keine Rolle.
Warum:
Weil die Daten (oder Paket) dank der Post (Internet) fehlerfrei
angekommen sind. Die Post ihrerseits hat verschiedene Systeme der
Nachrichten- sowie Brief- und Paketübermittlung entwickelt. Du als
Nutzer weisst gar nicht wie das alles funktioniert, brauchst du
auch nicht, aber deswegen kann man nicht sagen:
1
"Ich habe ein Paket ohne Beschädigung (Fehler) nach Schweden getragen"
- nein, du hast nur dieses Paket bei der Post abgegeben.
Somit sind Fehler bei der Übertragung (Zustellung) ausgeschlossen
(bzw. nicht ausgeschlossen aber du hast keinen Einfluss auf diese)
deswegen hat es auch keinen Sinn, in den Briefen nur jede zweite
Zeile zu beschreiben oder nur auf Deutsch und nicht auf Chinesisch
zu schreiben, um die Übertragung (Zustellung) sicherer zu machen.
Ich hoffe, ich habe mich einigermassen verständlich ausgedrückt...
Marc V. schrieb:> Kaum.> Wenn das Ganze so rausgesendet würde, hätte er recht und ich nicht.> Da dies aber nicht der Fall ist...
Ich denke schon, dass ihr aneinander vorbei geredet habt und in den
letzten Posts wird das IMO deutlich. Der eine redet von Protokollen, die
die Datenübertragung betreffen, der andere redet von Protokollen, die
die Dateninterpretation betreffen. Du hast das jetzt mit dem
Postpaketbeispiel IMO schön verdeutlicht. Deshalb finde ich, dass ihr
beide recht habt, ihr spracht nur nicht vom Gleichen ;)
Hallo Hans,
vielleicht etwas altmodisch aber für deine Anwendung ideal.
Die serielle schnittstelle besteht nicht nur aus TX und RX du kannst
auch noch das RST Signal verwenden.
Mein Vorschlag:
LabView aktiviert das RST vor dem senden nach einer kuzen pause sendet
es deine [85][INT_LO/HI] [INT_LO/HI] und optinal [CS] danach eine kuze
Pause und deaktiviert das RST.
Dein uC erkennt den Pegelwechsel vom RST und beginnt alle Variabel zu
initalisieren und aktiviert den RX Interrupt. Nach einem erneuten Pegel
wechesel des RST wird der RX Interrupt deaktiviert.Jetzt kann man die
Prüfsumme abgleichen ist alles OK dan Flag setzen für das Hauptprogramm.
Jetzt dürfte es egal sein ob du 0-65535 als 1-5 byte ASCII oder nur mit
2 Byte überträgst.
ms
Ja, du hast dich verständlich ausgedrückt.
Das ändert aber nichts an der Tatsache, dass man sowohl das Format der
Nutzdaten als auch das Format sämtlicher sonstiger Kommunikation auf
ASCII einschränken kann.
Warum sollte man das tun? Weil du in deiner Analogie den Zollbeamten
vergessen hast, der das Paket aufmacht und alles wegwirft, was er nicht
lesen kann.
Protokolle, die auf ASCII aufbauen, sind oft einfacher zu
- verstehen und zu debuggen (Terminalprogramm reicht zum Zuschauen)
- erweitern (Länge des Headers nicht vorgegeben *)
- aktualisieren (weil z.B. Zahlenbereiche nicht implizit begrenzt sind)
- übertragen (geringere Anforderungen an den Transportweg).
* Beispiel: Weil HTTP die Datenlängen im Header als ASCII-Zahl
überträgt, kann es implizit mit beliebig großen Dateien umgehen.
Binärprotokolle können das nicht.
Das sind Gründe, ASCII-Protokolle von Binärprotokollen zu unterscheiden.
Man könnte sagen: ASCII-Übertragung ist ein Protokoll mit Feldern
variabler Länge, das mit Trennzeichen/Trenntags arbeitet. So wie XML und
Verwandte.
Und ist damit robuster und flexibler als feste Länge mit/ohne Binär.
S. R. schrieb:> Warum sollte man das tun? Weil du in deiner Analogie den Zollbeamten> vergessen hast, der das Paket aufmacht und alles wegwirft, was er nicht> lesen kann.
LOL.
Frank hat eine Verbindung konstruiert, bei der nur ASCII fehlerfrei
durchkommt, bei binär werden Zeichen an genau bestimmbaren Stellen
verschluckt, du hast diesen Zollbeamten...
Also:
Zollbeamter wird gefeuert oder er muss einen Sprachkurs erfolgreich
beenden.
Punkt.
Schande, sowas.
Dann nimm eine serielle Verbindung mit 7N1 als Beispiel.
Da geht ASCII fehlerfrei durch, aber nicht jeder Binärwert.
Nachtrag: PS/2 wäre auch ein binäres Protokoll, wo selektiv Bytes
verschluckt werden können. Viele Controller filtern nämlich sämtlichen
Traffic, der nicht wie "kommt von einer Tastatur oder eine Maus"
aussieht - das ist für (Multi-)Touchpads tatsächlich ein Problem.
Geht das nicht in deinen Kopf rein, dass sowas ein Problem sein kann
(nicht muss)?
S. R. schrieb:> .... Kommunikation auf> ASCII einschränken kann.>> Warum sollte man das tun?
Nun, man muß sowas nicht wirklich tun, wenn die ganze Konfiguration
überschaubar genug ist. Und in dem eingangs geschilderten Falle ist sie
überschaubar.
Was der TO nicht wirklich verstanden hat ist, daß er hier zwei
unabhängige Instanzen (PC und µC) vor sich hat, die eine Art
Synchronisation benötigen, um sinnvoll miteinander kommunizieren zu
können.
Ob man das über bestimmte ASCII-Zeichen macht oder mit anderen
Binärwerten, ist vom Prinzip her egal. Wichtig sind nur zwei Dinge:
- Synchronisation, also Erkennung beim Empfänger, an welcher Stelle
einer Datenübertragung man ist, wenn man ein Zeichen empfangen hat
- Fehlererkennung, also ob eine Übertragung OK ist oder nicht.
Oft ist ebenso wichtig, daß man mit leichtem Gepäck daherkommt, also
ohne übertrieben aufwendigen Umfang an zusätzlichem Zeugs.
Nochwas:
Wenn man bei beiden Teilnehmern sowas wie 8N1 einstellt und back to back
sendet, dann ist die Gefahr groß, daß man damit nach einiger Zeit einen
Übertragungsfehler verursacht, weil die Bit-Takte beider Teilnehmer
dezent differieren können. Ist ja häufig, daß man im µC eine gewünschte
Baudrate nicht ganz exakt hinbekommt und einen Restfehler von einigen
Prozent hat. Dagegen hilft eigentlich immer, mit 8N2 zu senden oder mal
eine Pause einzulegen.
W.S.
W.S. schrieb:> Wenn man bei beiden Teilnehmern sowas wie 8N1 einstellt und back to back> sendet, dann ist die Gefahr groß, daß man damit nach einiger Zeit einen> Übertragungsfehler verursacht, weil die Bit-Takte beider Teilnehmer> dezent differieren können
In Zahlen:
Ist der Sender langsamer als der Empfänger, so trifft das nächste
Startbit später ein als erwartet, das ist garkein Problem, ist ja
asynchron. Im umgekehrten Fall kommt das Starbit zu früh; es gibt zwar
unzählige Implementationen von UARTs, aber i.A. kann man davon ausgehen,
dass der Empfänger zufrieden ist, wenn er das Stopbit (in der Mitte der
Bit-Zeit) verifiziert hat, also darf das nächste Startbit um maximal
eine halbe Bitzeit zu früh kommen, das ergibt also 9,5 statt 10
Bitzeiten und damit 5% zulässige Abweichung. Das ist bei Quarz auf
beiden Seiten kein Problem, andrerseits erklärt das auch, warum eben
Quarztakt für UARTs empfohlen wird.
Allerdings wird das in diesem Forum immer wieder vehement bestritten.
Georg
W.S. schrieb:>> .... Kommunikation auf ASCII einschränken kann.>> Warum sollte man das tun?> Nun, man muß sowas nicht wirklich tun, wenn die ganze Konfiguration> überschaubar genug ist.
Marc behauptete, es gäbe keine ASCII-Protokolle, und dem widerspreche
ich vehement.
Wenn die Bandbreite kein Problem darstellt, bevorzuge ich
ASCII-Protokolle allein aus dem Grund, dass ich einen Teilnehmer
entwickeln und im Terminal testen kann, bevor die Gegenstelle bereit
ist. So habe ich zu jeder Zeit nur eine Baustelle.
> Wenn man bei beiden Teilnehmern sowas wie 8N1 einstellt und back to back> sendet, dann ist die Gefahr groß, daß man damit nach einiger Zeit einen> Übertragungsfehler verursacht, weil die Bit-Takte beider Teilnehmer> dezent differieren können.
Ja, ist eine allgemeine Grundregel für Kommunikation:
- ist der Empfänger zu schnell, nutzt du die Bandbreite nicht optimal,
- ist der Sender zu schnell, gibt es irgendwann Datenverlust.
Georg schrieb:> Allerdings wird das in diesem Forum immer wieder vehement bestritten.
Hängt vom Anwendungsfall ab. Eine serielle Debugschnittstelle, die nur
im Labor bei Raumtemperatur benutzt wird, braucht keinen Quarztakt. Aber
die wird auch nicht dauerhaft senden...
S. R. schrieb:> Ja, ist eine allgemeine Grundregel für Kommunikation:> ...> - ist der Sender zu schnell, gibt es irgendwann Datenverlust.
Prinzipiell gibt es doch das Stop-Bit als Puffer. Je nach dem, wo der
Empfänger das Stop-Bit abtastet, könnte er doch sofort danach wieder auf
die Flanke vom nächsten Start-Bit warten und damit deutlich schneller
sein, als der Sender, auch wenn die Symbolrate des Senders etwas nach
oben abweicht.
Ist der Sender dauerhaft schneller als der Empfänger, wird irgendwann
jeder dazwischenliegende Puffer voll sein. Egal welcher Größe.
Außerdem redest du davon, dass der Empfänger schneller ist, nicht der
Sender.
S. R. schrieb:> Außerdem redest du davon, dass der Empfänger schneller ist, nicht der> Sender.
Nein, ich rede davon, dass der Empfänger mit einer etwas geringeren
Symbolrate abtastet als der Sender die Zeichen sendet und damit
langsamer bezogen auf die Bitrate ist. Einen gewissen Taktratenfehler
kann er trotzdem ausgleichen, wenn er das Stopbit nicht am Ende
abtastet, sondern vorher schon wieder in Idle geht und auf die nächste
Start-Flanke wartet, während der Sender das Stop-Bit in voller Länge
senden muss.
Ich hielt das OSI Schichtenmodell eigentlich für zu theoretisch. Aber
wenn ich lese, wie hier die Übertragung von Bits, die Sicherung der
Übertragung und die Nutzdaten durcheinandergeworfen werden, sollte so
mancher sich doch damit beschäftigen.
http://www.elektronik-kompendium.de/sites/kom/0301201.htm
MfG Klaus
Klaus schrieb:> Ich hielt das OSI Schichtenmodell eigentlich für zu theoretisch. Aber> wenn ich lese, wie hier die Übertragung von Bits, die Sicherung der> Übertragung und die Nutzdaten durcheinandergeworfen werden, sollte so> mancher sich doch damit beschäftigen.
LOL.
Stimme ich voll zu, nur wollte ich nicht wieder derjenige sein, der
darauf rumreitet.
M. Keller schrieb:> Ihr redet tatsächlich an einander vorbei. Denn ihr habt beide recht, da> ihr verschiedene OSI Schichten meint...
Kann sein, nichtsdestotrotz hat der HTTP mit Transport, sicherer
Übertragung und Datenintegrität genausoviel zu tun, wie die
Würstchenbude an der Ecke, nämlich genau nichts.
Schritt 1.
Client bereitet eine HTTP-Anfrage vor.
Schritt 2.
Die Anfrage wird fur den Versand vorbereitet, also in einen Paket
verpackt. Die Headerdaten werden dafür benutzt.
Schritt 3.
Das Paket wird über Internet verschickt. Ab jetzt ist der ganze
HTTP-Kram nur noch Paketinhalt, also Nutzdaten.
Schritt 4.
Paket ist beim Empfänger angekommen, wird ausgepackt, die Headerdaten
werden wieder benutzt.
Schritt 5.
Ab jetzt wird HTTP wieder verwendet.
Es geht um Schritt 3 und 4, also um sicheren Transport der Daten.
Was vorher und nachher mit diesen Daten gemacht wird und wie das Ganze
dann interpretiert wird, ist eine ganz andere Baustelle und hat mit dem
Transport nichts zu tun.
Insofern ist es absolut uninteressant, was da als Nutzdaten
transportiert wird - ASCII, Binär, HTTP, Deutsch, Chinesisch...
P.S.
Auch ein Zollbeamter hat mit Transport herzlich wenig zu tun...
Marc V. schrieb:> Schritt 3. Das Paket wird über Internet verschickt. Ab jetzt ist der> ganze> HTTP-Kram nur noch Paketinhalt, also Nutzdaten
Ja klar. Ab hier ist die nächste Schicht verantwortlich, nämlich TCP.
Es ging w.a. darum, dass die niedrigen Schichten (transport,
sicherungsschicht...) ebenfalls ASCII basiert sein können, wodurch diese
Schichten einfacher zu debuggen sind.
Z.b. modbus, das gibt es als ASCII oder RTU (binär)
Marc V. schrieb:> Kann sein, nichtsdestotrotz hat der HTTP mit Transport, sicherer> Übertragung und Datenintegrität genausoviel zu tun, wie die> Würstchenbude an der Ecke, nämlich genau nichts.
Richtig. Und genausowenig hat es etwas mit Paketen zu tun.
Marc V. schrieb:> Schritt 2.> Die Anfrage wird fur den Versand vorbereitet, also in einen Paket> verpackt. Die Headerdaten werden dafür benutzt.
Bei HTTP hab ich noch nie Pakete gesehen. Da schreibt man einfach in
einen Socket, solange man was zu sagen hat. Und sowohl die HTML-Header
als auch der HTML-Body sind reines 7-Bit ASCII, selbst simple Umlaute
muß man escapen. Ist man fertig, schließt man den Socket wieder.
Will man Pakete sehen, muß eine Schicht tiefer schauen, da sieht man
aber HTTP nicht. In den Paketen könnte auch SMTP oder FTP oder auch
Telnet sein. Das Protokol auf dieser Ebene heißt aber auch TCP und nicht
HTTP.
MfG Klaus
Lassen wir das.
Marc geht davon aus, dass jeder Übertragungskanal fähig ist, binäre
(Nutz-)Daten vollständig zu übertragen und folgert daraus korrekt, dass
jede Datenübertragung im Endeffekt binär ist. Jede Beschränkung (z.B.
auf ASCII) ist daher Verschwendung und Unsinn.
Dieses Weltbild ist schlüssig.
Da aber die Annahme in dieser Allgemeinheit falsch ist, sind auch die
Schlussfolgerungen nicht gültig. Gegenbeispiele habe ich genannt.
Ob binäre Nutzdaten nun durch einen ungeeigneten Kommunikationskanal,
einen übereifrigen Zollbeamten (technisch: "Firewall mit DPI") oder
einen Programmfehler dahingerafft werden, ist unerheblich. Gewöhnliches
ASCII hat hier eine höhere Überlebenschance (nebst anderen Vor- und
Nachteilen).
S. R. schrieb:> Lassen wir das.>> Marc geht davon aus, dass jeder Übertragungskanal fähig ist, binäre> (Nutz-)Daten vollständig zu übertragen und folgert daraus korrekt, dass> jede Datenübertragung im Endeffekt binär ist. Jede Beschränkung (z.B.> auf ASCII) ist daher Verschwendung und Unsinn.>> Dieses Weltbild ist schlüssig.>> Da aber die Annahme in dieser Allgemeinheit falsch ist, sind auch die> Schlussfolgerungen nicht gültig. Gegenbeispiele habe ich genannt.>> Ob binäre Nutzdaten nun durch einen ungeeigneten Kommunikationskanal,> einen übereifrigen Zollbeamten (technisch: "Firewall mit DPI") oder> einen Programmfehler dahingerafft werden, ist unerheblich. Gewöhnliches> ASCII hat hier eine höhere Überlebenschance (nebst anderen Vor- und> Nachteilen).
Stimmt nicht ganz. Wenn Daten kaputt gehen, dann geht auch ASCII kaputt.
Aber das Prinzip ASCII + Control Characters vermischt einfach Nutzdaten
und Übertragungssteuerung in einem Byte, also zwei verschiedene
Schichten des ISO-Modells :-)
S. R. schrieb:> jede Datenübertragung im Endeffekt binär ist. Jede Beschränkung (z.B.> auf ASCII) ist daher Verschwendung und Unsinn.
Nein, selbstverständlich nicht.
Bei 100% fehlerfreier Übertragung ist ASCII eine (ganz akzeptable)
Möglichkeit, Datenpakete zuverlässig zu unterscheiden, genau wie
Präambel oder <SOF>.
Bei GPS (NMEA) startet eine Nachricht mit "$" und endet mit CR/LF.
Checksum ist Bestandteil der Nachricht. Funktioniert fehlerfrei, da
diese Zeichen sonst nirgendwo in der Nachricht vorkommen.
Aber es gibt auch ein binär Protokoll für GPS, welches viel schneller
ist, aber nicht HR ist.
Natürlich macht es für mich keinen Sinn, GPS mit ASCII zu benutzen,
da die Umwandlung nach binär doppelte Arbeit darstellt.
Aber zur Kontrolle (Debug) ist es natürlich besser.
> Da aber die Annahme in dieser Allgemeinheit falsch ist, sind auch die> Schlussfolgerungen nicht gültig. Gegenbeispiele habe ich genannt.
Nein, missverstanden, bin in dieser Beziehung niemals ausschlieslich
geworden, ASCII hat auch Vorteile, unbestritten.
S. R. schrieb:> Verstehe. Ob moderne UARTs das machen, weiß ich allerdings nicht - ich> vermute "eher nein".
Ich hab's beim Schreiben des STM32-Bootlader-Programms neulich mal
wieder erlebt: FTDI-Chip auf PC-Seite, Bootlader per uart1 auf STM
Seite. Kann jeder nachvollziehen. Mit 8N2 am PC war die Sache dann
erledigt.
W.S.
Moin Thomas,
ich bin jetzt soweit, dass ich mit LabView einen Wert an den uC senden
möchte. Ich habe im Control Panel, ein uint16 Eingabefeld. Wenn ich da
eine 77 (dezimal) eintrage, muss ja zum uC der String 4D gesendet
werden.
Kannst du mir erklären, wie ich einen uint16 so in LabView umrechne,
dass das richtige an den uC geschickt wird?
Danke & viele Grüße
Thomas E. schrieb:> Du dekodierst deine Daten im Hauptprogramm. Das Befehlsbyte liegt vor> den Daten. Dieses kommt in eine Variable(unsigned char) und die> eigentlichen Daten in eine andere Variable(unsigned int). Und dann> wertest du das aus:>> if(Befehlsbyte == 85)> {> // tu was> }>> oderswitch(Befehlsbyte)> {> case 0:> //tu dies> break;> case 1:> //tu das> break;> case 85:> //tu jenes> break;> }>> Alles im Hauptprogramm oder in Funktionen, die aus dem Hauptprogramm> aufgerufen werden.
Hans D. schrieb:> Kannst du mir erklären, wie ich einen uint16 so in LabView umrechne,> dass das richtige an den uC geschickt wird?
Das ist wohl das finale Ergebnis des gesamten Threads.
W.S.
Hans D. schrieb:> Ich habe im Control Panel, ein uint16 Eingabefeld. Wenn ich da> eine 77 (dezimal) eintrage, muss ja zum uC der String 4D gesendet> werden.
Nein.
Ich fürchte, du hast den Unterschied zwischen Dezimalzahl,
Hexadezimalzahl und String nicht verstanden.
Conny G. schrieb:> Stimmt nicht ganz. Wenn Daten kaputt gehen, dann geht auch ASCII kaputt.
Dann nimm nicht ASCII, sondern irgendwelche Codeworte mit einem
Hammingabstand größer zwei. Schon damit kannst du Einzelfehler im
Codewort korrigieren und die effektive Sicherheit der Übertragung
erheblich steigern.