PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : UART ohne CrLf?



Geistesblitz
08.08.2014, 13:01
Ich frage mcih, ob es möglich ist, UART (oder auch alle anderen seriellen Kommunikationsmöglichkeiten) möglichst gut auszunutzen, indem man CrLf weglässt und stattdessen wirklich jedes übertragene Byte einzeln auswertet. Vorteil sehe ich darin, dass man die Bytes wirklich als ganzes Byte auswerten kann, würde man CrLf nicht ignoroeren, so würde die Übertragung ja unerwartet abgebrochen werden, wenn der zu übertragende Wert ausgerechnet 10 oder 13 ist. Daher ließe sich das doch bestimmt so lösen, dass Sender und Empfänger wissen, was es so an Befehlen gibt und dass nach gewissen Befehlen x Bytes als Daten interpretiert werden sollen. Wäre sowas denkbar oder ist das ungünstig? Wonach müsste ich suchen, um dazu mehr zu erfahren? Bisher Musste ich Zahlen als Strings übertragen, was ich jedoch als furchtbar ineffizient empfinde. Außerdem schafft es der Pololu-Maestro ja auch, daher würde mich interessieren, wie man so eine Kommunikation am besten aufbauen kann.

malthy
08.08.2014, 13:09
Hey!

Ich kenne das Problem auch. Die simpel-Lösung die ich bisher verwende, ist die Daten in ASCII hexcodiert zu übertragen. Damit kann man dann immernoch die klassischen Steuerzeichen verwenden (CR/LF), hat aber den Vorteil das 1.) die Übertragung im Mittel etwas effizienter ist als ASCII dezimalcodiert und 2.) die Zahlen eine feste Breite von zwei ASCII Zeichen haben. Das ist für meinen Geschmack ein großer Vorteil bei der Nachrichtenauswertung, die wird dann zu einem einfachen hex2dec oder dec2hex (je nach Sprache).

Natürlich könnte man das Prinzip weitertreiben und ein eigenes Zahlenformat verwenden, das 254 Datenzeichen und 1 Steuerzeichen (zB Nachricht Ende) verwendet. Bisher war ich zu faul das mal zu implementieren.

Gruß
Malte

Mxt
08.08.2014, 13:31
Hallo,

habe da schon verschiedene Protokolle gesehen:

Zum Beispiel STX Datenbytes ETX, wenn in den Datenbytes die Werte der Steuerzeichen vorkommen wird z.B. ESC STX, ESC ETX und ESC ESC gesendet. STX (Start of Transmission) usw. findet man in einer ASCII Tabelle.

Dann eine Variante von ISO-TP angepasst an UART: STX Framennummer-Byte Anzahl-Datenbytes-Byte Datenbytes Checksum-Byte.
http://de.wikipedia.org/wiki/ISO_15765-2

markusj
08.08.2014, 14:18
Es steht dir vollkommen frei, ein beliebiges Protokoll zu entwickeln und zu implementieren, bei dem die ASCII-Steuerzeichen wie CR und LF kein oder eine komplett andere Bedeutung haben. Der primäre Einsatzzweck von CR/LF ist die Ausgabe auf einer Textkonsole, da verbessert der Zeilenumbruch die Lesbarkeit. Für ein Binärprotokoll ist das aber komplett irrelevant.

mfG
Markus

oberallgeier
08.08.2014, 14:45
... ob es möglich ist, UART ... möglichst gut auszunutzen ... CrLf weglässt und stattdessen wirklich jedes übertragene Byte einzeln auswertet ...Ich weiß nicht, ob ich das jetzt richtig verstehe. Meine UART-Telegramme sind - bis auf Textausgaben an Terminal - eigentlich immer ohne CR/LF. Mal Beispiele.

Befehle per UART sind bei mir meist Steuerbefehle zu Testzwecken von PC nach Controller aber eben auch einzelne Controller-Controller-Kommunikation (der 1284 hat praktischerweise zwei UARTs). So ein Telegramm hat dann allerlei Inhalt - aber praktisch nie ein CrLF (Ausnahme die Terminalmeldungen) - es wird allerdings der gesendete String mit einer 0x00 abgeschlossen. Der Befehlsempfänger weiß dann, wann welcher Wert beginnt und wann er aufhört. Die Telegramme sind dabei recht kurz, derzeit deutlich unter 14 Bytes (einschließlich er String-Null). Soweit die Übertragung von Zeichen und Zahlen per ASCII-String. Mal ein aktuelles Beispiel vom Kopfcontroller meines Archie.
#define KOMMANDO_APPS 'A' // Anwendungsprogramme fahren, diverse
#define KOMMANDO_APPS_LEN 7 // Annkkkk
// nn =: 01..99 - Programmnummer
// kkkk Parameter für Prog

#define KOMMANDO_DATS 'D' // (aktuelle) Daten der Servos
#define KOMMANDO_DATS_LEN 1 // Kommandolänge

#define KOMMANDO_NORM 'N' // Normposition der Servos anfahren
#define KOMMANDO_NORM_LEN 1 // Kommandolänge

#define KOMMANDO_OFFA 'O' // Offset anzeigen für akt Servo x
#define KOMMANDO_OFFA_LEN 2 // Kommandolänge

#define KOMMANDO_OFFC 'o' // Offset ändern (Change) für akt Servo x
#define KOMMANDO_OFFC_LEN 5 // Kommandolänge
// bei ox333 => akt. Wert ins EEPROM schreiben

#define KOMMANDO_POSER 'P' // Positionierbefehl Servo
#define KOMMANDO_POSER_LEN 6 // Pn3600 {1-9, j, z} 10 Servo
// n =: 1..9, j=10, z=alle
#define klz 122 // kleines "z"
#define GRZ 90 // Grosses "Z"

#define KOMMANDO_WICHTIG 'W' // Wichtiger Befehl
#define KOMMANDO_WICHTIG_LEN 1

#define KOMMANDO_TEST 'T' // Test x {0 .. 9} aufrufen
#define KOMMANDO_TEST_LEN 2 // .. in do_test/r4n

#define KOMMANDO_VOICE 'V' // Audioausgabe aufrufen
#define KOMMANDO_VOICE_LEN 4 // .. in ~aud~/plyWTV

#define KOMMANDO_PC_CON 'Z' // Zurück zur Console
#define KOMMANDO_PC_CON_LEN 2

// Muss nur das laengste Kommando aufnehmen koennen.
//#define KOMMANDO_BUFFER_LAENGE 14 // ##>> Deklaration => ~com~
// eigener BUFFER-Speicher um aus dem 'unsichtbaren' FIFO-Speicher umzukopieren
//u8 mein_rx_buff [KOMMANDO_BUFFER_LAENGE]; // ##>> Deklaration => ~com~

Der rot markierte String ist hier nur EIN Beispiel für einen tatsächlich gesendete/empfangene String OHNE String-Ende-Marke. Der bedeutet in diesem Beispiel
Fahre Position von Servo n auf den Wert 3600. Anm.: Ich stelle meine analogen Servos mit einem Puls der fast 14 Bit fein codierbar ist.
Zu sehen ist hier auch deutlich, dass die Länge des Befehlsstrings vom Kommandoanfang codiert wird. Das hat nicht zuletzt den Hintergrund mir selbst eine Eselsbrücke bein Programmieren und beim Testen zu bauen.

Solche Strings sendet auch in manchen Situationen per UART der Master an Slaves.

I²C: Werte mit ein oder zwei Bytes übertrage ich häufig, insbes. per I²C - und dann als "richtige" Integerwerte mit ein oder zwei Bytes. Da muss der Befehlsempfänger eben wissen wie er die verschiedenen Bytes auszuwerten hat. Das löse ich dadurch, dass z.B. eine Dekade Pufferbytes für Motorenwerte benutzt wird. Byte x9 ist das Statusbyte - ist das auf 85 so der Puffer bereit zur Aufnahme eines neuen Befehls. Dann schreibt der Master seine z.B. zwei Motorstellwerte, z.B. 240 / 250 in die Pufferzellen 52-53 und 54-55, in die 59 kommt ne 99 als Zeichen, dass neue Daten im Puffer stehen. Der Slave liest das und quittiert mit ner 85 auf Byte 59.

Ist das der Hintergrund der Frage?

Geistesblitz
08.08.2014, 14:53
Danke schonmal für die Anregungen. Hex-Strings hören sich schonmal ganz gut an, mich würde trotzdem mal interessieren, wie man da noch weiter gehen könnte. Ich hab auch schonmal ein Testprogramm in Bascom geschrieben, das nicht erst auf CrLf warten soll, sondern direkt das jeweils empfangene Byte aufnimmt, allerdings kam so keine Kommunikation zustande.

Peter(TOO)
08.08.2014, 15:15
Hallo,


Ich frage mcih, ob es möglich ist, UART (oder auch alle anderen seriellen Kommunikationsmöglichkeiten) möglichst gut auszunutzen, indem man CrLf weglässt und stattdessen wirklich jedes übertragene Byte einzeln auswertet.

Grundsätzlich ist das kein Problem, wenn man es kann.

Bei 8 Datenbits, kann man 256 unterschiedliche Symbole übertragen. Für den Computer sind das erst mal nur Binärwerte.

Nun begab es sich, dass man verschieden Geräte, z.B. einen Computer und ein Terminal mit einander verbinden wollte.
Das geht aber nur, wenn beide Geräte die selbe Sprache sprechen, sonst hat man nur Datensalat.
Irgendwer hat dann mal den ASCII-Code definiert, weil die Amis waren, dachte da keiner an Umlaute und 7 Bit genügten.
http://de.wikipedia.org/wiki/ASCII

IBM ging einen anderen Weg:
http://de.wikipedia.org/wiki/EBCDIC

Du kannst ja mal durchspielen, was sich so tut, wenn du zwei Geräte verbindest, welche diese beiden unterschiedliche Codierungen verwenden. :-)

Bevor man Bildschirmterminals hatte, waren TTYs das Standartgerät, kennt man auch als Telex oder Fernschreiber.
Da brauchte man in der Norm auch Steuerzeichen. bei ASCII sind die ersten 32 Zeichen als Steuerzeichen reserviert.
CR komm von Carriage Return (Wagenrücklauf) und LF ist Line Feed (Zeilenvorschub) dinge die man bei einem TTY halt noch hat.

Zu dieser Zeit hatte man noch Lochstreifen als Speichermedium.
Um den Lochstreifen in den Leser einlegen zu können, braucht man am Anfang schon Löcher, dazu wurde das Zeichen NUL benötigt. Allgemein hat man am Anfang des Lochstreifens eine ganze Reihe NUL gestanzt.
Da man damals ganze Dateien auf Lochstreifen gespeichert hat, gibt es noch Steuerzeichen für diese, wie z.B. EOF (End Of File).
Dann brauchte man noch ein paar Steuerzeichen für die Regelung des Datenverkehrs.

Das Ganze hat dann dazu geführt, dass Standard-Treiber entwickelt wurden, welche den Datenverkehr regeln, ohne dass jeder Programmierer dies in seinem Programm extra programmieren muss.

Allerdings kann man normalerweise bei jedem Betriebssystem diese Treiber auf Transparent, bzw. Binär, umschalten, dann werden die ganzen Steuerzeichen nicht ausgewertet.

Wenn man jetzt Binäre Daten übertragen will, hat man ein kleines Problem, man benötigt immer noch Steuerzeichen, aber es sind schon alle möglichen 256 Kombinationen belegt.

Wie schon erwähnt wurde kann man alles in ASCII-Hex wandeln, das verdoppelt aber die Datenmenge.
Eine andere Methode ist MIME
http://de.wikipedia.org/wiki/MIME

Bei MIME-64 wird der Datenstrom zuerst in 6-Bit Grüppchen aufgeteilt. Zu den 6-Bit Werten addiert man dann noch 32 (für die reservierten Steuerzeichen) und sendet das so über die Leitung. Das Datenvolumen wird dann nur um den Faktor 1.5 aufgebläht.

Um jetzt bei einem reinen binären Datenstrom noch zusätzlich Steuerzeichen zu gewinnen, arbeitet man mit einem Escape-Zeichen.
Gerne wird dafür der Wert 0x1B (ESC) verwendet.
Will man ein Steuerzeichen einfügen sendet man zuerst ein ESC und dann das Steuerzeichen.
Da der Wert 0x1B aber auch in den Daten vorkommen kann, sendet man in diesem Falle zwei mal ESC.

Fazit:
Du kannst die Daten senden wie dir Lustig ist. Musst dann aber die Software für Sender und Empfänger selber schreiben!
Was du aber auch nicht vergessen darfst, ist, dass immer damit zu rechnen ist, dass Zeichen verloren gehen oder gestört ankommen.
Da muss dein Protokoll dann wieder synchronisieren können, andernfalls hängt sich das einfach auf. :-(

Bei der crlf-Methode geschieht die Synchronisierung immer an dieser Stelle.
Stell dir vor du sendest lauter Zahlen, der Empfänger weiss, dass jede Zahl 5 Stellen hat.
Wenn du kein crlf verwendest und ein Zeichen verloren geht, gibt es keine Möglichkeit herauszufinden wo jetzt welche Zahl aufhört und wo die nächste beginnt.

MfG Peter(TOO)

oberallgeier
08.08.2014, 15:25
... ein Testprogramm in Bascom geschrieben, das nicht erst auf CrLf warten soll ... allerdings kam so keine Kommunikation zustande.Schon klar dass es so nicht geht. Die üblichen Bibliotheken warten eben auf einen Standard-Abschluss. Da muss man schon selbst aktiv werden. Bei mir stehen die empfangenen Daten noch dazu in einem FIFO und die Kommunikation läuft "im Hintergrund" interruptgesteuert. Geht recht fix.

Mal ein paar rausgeschnibbelte Code-Zeilen um das Vorgehen zu skizzieren:


// ==================================================
/* ... auf Inhalt im FIFO ... wartem */
if ( ! ukbhit0 () )
continue;


// ==================================================
/* EIN EINZIGES Zeichen aus FIFO lesen. */
zeichen_aus_fifo = ugetchar0();


// ==================================================
/* Wenn noch kein Kommando bekannt ist, muessen wir erst einen
Kommando-Buchstaben erkennen.

Randbemerkung
Hier ist auch ein switch/case-Dingsda erlaubt, da zeichen_aus_fifo und
auch der Define genau ein char-Zeichen bzw. ein numerisch auswertbarer
einfacher Datentyp sind. */
if (zeiger == 0)
{ //
if (zeichen_aus_fifo == KOMMANDO_APPS) // Fahre Anwendungsprogramme
telegrammlaenge = KOMMANDO_APPS_LEN; // mit und ohne Parameter

if (zeichen_aus_fifo == KOMMANDO_DATS) // Servo Daten von allen
telegrammlaenge = KOMMANDO_DATS_LEN; // Servos anzeigen

if (zeichen_aus_fifo == KOMMANDO_NORM) // Servo Normposition
telegrammlaenge = KOMMANDO_NORM_LEN; // anfahren

if (zeichen_aus_fifo == KOMMANDO_OFFA) // Offsetwert A nzeigen
telegrammlaenge = KOMMANDO_OFFA_LEN; // von Servo x
....
}


// ==================================================
/* Wenn keine Telegrammlaenge bekannt ist, dann ist auch kein Kommando
bekannt, dann muss auch nichts weiter gemacht werden, als auf das
naechste Zeichen zu warten bzw. es abzuholen und dann wieder auf
einen Kommando-Buchstaben zu testen */
if (telegrammlaenge == 0)
continue;

// ==================================================
/* Wenn ein erkanntes Kommando seine Telegrammlaenge angegeben hat, dann
muessen wir nun die EINZELN aus dem FIFO abgeholten Zeichen in den BUFFER
schreiben. Da ja schon der erkannte Kommando-Buchstabe in der Variablen
"zeichen_aus_fifo" steht, und "zeiger" noch nicht veraendert wurde, wird
auch zum Glueck der Kommando-Buchstabe direkt als erstes Zeichen in
unserem BUFFER landen.
Und alle weiteren Zeichen werden dank "zeiger++" dahinter geschrieben. */
mein_rx_buff [zeiger] = zeichen_aus_fifo;
zeiger++;

// ==================================================
/* Und jetzt das geniale Ergebnis:
Wenn nun "zeiger" und "telegrammlaenge" gleich sind,
dann haben wir ein fertig empfangenes Kommando mit
seiner erwarteten Laenge. */
if (zeiger == telegrammlaenge)
{
/* DIESE INITIALISIERUNGEN SIND LEBENSWICHTIG */
zeiger = 0;
telegrammlaenge = 0;

/* Die Funktionen koennen nun gemuetlich das
Telegramm auswerten. */

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Fahre verschiedene Anwendungsprogramme, je nach Nummer
if (mein_rx_buff [0] == KOMMANDO_APPS) // Tastatur-Eingabe "Annkkkk"
{ // Annkkkk
// nn =: 01..99 - Programm-Casenummer
// kkkk Parameter für Prog
// Dekodiere Programmkennziffer im Byte mein_rx_buff 1 und 2
for ( u8 i=0; i<=1; i++) { s[i] = mein_rx_buff [i + 1]; }
s[2] = 0; // Markiere Stringende "0"
nmbr = atoi ( s ); //

// - - - - - - - - - - - - - - -
// case
// 10 Augen rollen k mal (erstes "k")
// 11 Alle Lider auf, full speed
// 12 Alle Lider zu, full speed
....
// Fahrgeschwindigkeit "Servo-Standard+SloMo"
// 43 Augenprogramm AuP43 = ähnlich 42, eher langsamer

// - - - - - - - - - - - - - - -
switch (nmbr) // Fahre gewählte Aktionen
{ //
case 10: // Augen rollen k-mal
s[0] = mein_rx_buff [3]; s[1] = 0; // Dekodiere 1stes k
npar1 = atoi ( s ); //
AuRoll ( npar1 , 100 ); // Augen rollen mit Standard-slow
for ( u8 i=1; i<=6; i++) {mein_rx_buff [i] = '9'; }
mein_rx_buff [7] = 0; // Hilft das bei unvollständiger Eingabe ?
break;

case 11: // Alle Lider auf, full speed
// s[0] = mein_rx_buff [3]; s[1] = 0; // Dekodiere 1stes k
// npar1 = atoi ( s ); //
Srv_tm [ 5] = 2400;
Srv_tm [ 6] = 4800;
Srv_tm [ 8] = 5400;
wms ( 500); //
break; //
.....
case 51: // NUR ANSCHLUSS SERVO 1 mit TasteA/3 auf- und abdrehen
// AuP51 ( ); // Fahre AuP51 r4n
N1Srvo (); // Fahre Servo-Einzel-Einrichtung r2n
break; //

// - - - - - - - - - - - - - - -
default:
break;
} // Ende switch (nmbr)
} // Ende if (mein_rx_buff [0] == KOMMANDO_APPS)

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Wurde "KOMMANDO_DATS" = "D" erkannt ? DatenAusgabe Servos
if (mein_rx_buff [0] == KOMMANDO_DATS) // Tastatur-Eingabe "D"
{ //
// Daten für ALLE zehn Servos ausgeben
uputs0 ("\r\tServodaten"); //
dat_aus (); // Servodaten ausgeben
} // Ende if (mein_rx_buff [0] == KOMMANDO_DATS)

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Wurde "KOMMANDO_NORM" = "N" erkannt ? Normposition der Servos anfahren
if (mein_rx_buff [0] == KOMMANDO_NORM) // Tastatur-Eingabe "N"
{ //
uputs0 ("\r\tServonormposition,"); //
kal_0(); // Initialiswerte einstellen kal
uputs0 ("\tneue Servodaten :"); //
dat_aus (); // Servodaten ausgeben

} // Ende if (mein_rx_buff [0] == KOMMANDO_NORM)


und so weiter

Ist das so (andeutungsweise) verständlich ?

Che Guevara
08.08.2014, 17:34
Hi,

ich hab auch vor einiger Zeit mein eigenes Protokoll entwickelt (mittlerweile gibts ein paar Verbesserungen, aber das wäre zuviel).
Es ging damals darum, ein Byte-Array von einem µC auf einen PC und andersrum zu übertragen. Da ich aber nichtmehr soviel Platz auf dem µC hatte, dachte ich mir, jedes einzelne Byte, das gespart werden kann, wird gespart!
Also hab ich mir die Situation rausgesucht, bei der die meisten Byte übertragen werden müssen und diese Byte-Anzahl als Norm-Länge verwendet.
Nun wird einfach immer ein Array dieser Länge (bei mir sind 132 Byte) übertragen, incl. Erkennungsbytes (damit der Empfänger weiß, was er mit den Daten machen soll).
Um niemals die Synchronisation zu verlieren, frage ich regelmäßig die Anzahl der bereits empfangenen Daten ab und kucke mir die Zeit an, seit der sich die Anzahl nicht mehr verändert hat. Wird eine bestimmte Zeit überschritten, gehe ich davon aus, dass Daten verlorengegangen sind und setze die Zähler zurück.
Das ganze läuft bei mir auf dem µC per DMA, so wird sogut wie keine Zeit für das Empfangen verschwendet.
Neuerdings schwirren mir wieder ein paar Verbesserungen im Kopf rum, welche ich demnächst wohl mal einpflegen werde.
Zum Beispiel will ich von der festen Array-Länge weg, da es in manchen Fällen vorkommen kann, dass nur 10% von den 132 Bytes effektiv genutzt werden. Das ist zwar grundsätzlich egal, sieht aber nicht schön aus.
Auch wäre eine Verbindung verschiedener µCs untereinander nicht schlecht, sodass am Anfang eines Arrays zuerst der Empfänger, dann der Sender, dann die Anzahl der folgenden Bytes und anschließend evtl. noch eine Checksumme integriert sind. Außerdem wäre es eine schicke Idee, die Daten sozusagen Seitenweise zu übertragen, heißt:
Eine Seite ist z.b. 10Bytes lang, in ihr befinden sich alle relevanten Informationen für die Übertragung ansich, ebenso wie die zu übertragenden Daten selbst. Unter den für die Übertragung relevanten Daten finden sich noch zwei Werte, die akt. Seitenzahl und die Gesamtseitenzahl. So liesen sich mit nur 10Bytes Speicher große Datenmengen übertragen.
Das Alles noch mit einem ACK abgerundet, fertig ist ein High-Level-Protokoll.

Es kommt aber eben darauf an, WAS du willst. Willst du Daten schnell austauschen, störsicher, große Datenmengen, etc ... ?

Gruß
Chris

malthy
08.08.2014, 18:05
Ich denke man braucht dedizierte Steuerzeichen (das heißt Zeichen, die nur Steuerzeichen sind). Ob das nun historische wie LF oder CR sind, ist dabei natürlich erstmal wurscht.


Um jetzt bei einem reinen binären Datenstrom noch zusätzlich Steuerzeichen zu gewinnen, arbeitet man mit einem Escape-Zeichen.
Gerne wird dafür der Wert 0x1B (ESC) verwendet.
Will man ein Steuerzeichen einfügen sendet man zuerst ein ESC und dann das Steuerzeichen.
Da der Wert 0x1B aber auch in den Daten vorkommen kann, sendet man in diesem Falle zwei mal ESC.

Die Logik verstehe ich nicht ganz. Wenn der Wert des Steuerzeichens auch in den Daten vorkommen kann, wie kann man dann ausschließen, dass es sich bei einem "scheinbaren" Steuerbefehl nicht zufällig um zwei Datenwerte handelt, die aussehen wie ein Steuerbefehl. Also konkret: wenn 0x1B einen Befehl markieren würde und ein 0x50 würde zB die Ausgabe von der folgenden drei Bytes (binary) auf einem LCD bedeuten, wie wäre der Fall davon zu unterscheiden, dass einfach zufällig dezimal 27 und 80 in einem anderen Zusammenhang aufgetaucht sind? Sich nämlich darauf zu verlassen, dass man immer sicher im Leseraster ist (was natürlich eine Lösung wäre), finde ich etwas gewagt ... Oder habe ich da irgendwo einen Denkfehler?


Ich hab auch schonmal ein Testprogramm in Bascom (http://www.rn-wissen.de/index.php/Bascom) geschrieben, das nicht erst auf CrLf warten soll, sondern direkt das jeweils empfangene Byte aufnimmt, allerdings kam so keine Kommunikation zustande.

Hm, verstehe ich nicht ganz ... Ich verwende zwei Varianten. Die eine ist im Hauptloop - also quasi durch permanentes Polling - mit


If Ischarwaiting() = 1

jeweils zu gucken ob was im UART Puffer für mich zum abholen liegt, und falls ja, es mit


Uart_buf(uart_buf_i) = Inkey()

abzuholen. Meist schreibe ich die Zeichen in einen Ringpuffer, den ich dann nach jedem neuen Byte nach der Sequenz absuche, die das Ende der Nachricht signalisiert (bei mir idR CR/LF, das kann definitionsgemäß nicht als Datenwerte vorkommen). Wenn das Ende erreicht ist, wird eine Anzahl Bytes vorher im Ringpuffer, die der Nachrichtenlänge entspricht ausgewertet. Das heißt in meinem Falle von Hex (String) nach Dezimal (oder wie auch immer man die Variable dann interpretiert) umgewandelt. Meist schleppe ich noch einen 8 bit CRC Wert mit, der dann noch ausgewertet wird.

Die andere Varainte geht fast genauso, da verwende ich dann allerdings den urxc Interrupt, dh mit


On Urxc Rxc_isr

wird bei jedem neuen Zeichen die entsprechende ISR angesprungen, die dann die Auswertung wie eben gesagt macht.

Ist methodisch nicht sehr intellektuell und die Codierung ist auch "far from optimal", aber es funktioniert stumpf :-)

Gruß
Malte

Mxt
08.08.2014, 18:33
Die Logik verstehe ich nicht ganz. Wenn der Wert des Steuerzeichens auch in den Daten vorkommen kann, wie kann man dann ausschließen, dass es sich bei einem "scheinbaren" Steuerbefehl nicht zufällig um zwei Datenwerte handelt, die aussehen wie ein Steuerbefehl.

Das ist genauso wie mit den Escape-Sequenzen in C. Man schreibt da ja z.B. "\r\n" für das CR LF. Wenn man einen Backslash schreiben will, schreibt man "Dies ist ein \\". Der Backslash ist hier das Escape Zeichen.

- - - Aktualisiert - - -


Hi,
Zum Beispiel will ich von der festen Array-Länge weg, da es in manchen Fällen vorkommen kann, dass nur 10% von den 132 Bytes effektiv genutzt werden. Das ist zwar grundsätzlich egal, sieht aber nicht schön aus.
Auch wäre eine Verbindung verschiedener µCs untereinander nicht schlecht, sodass am Anfang eines Arrays zuerst der Empfänger, dann der Sender, dann die Anzahl der folgenden Bytes und anschließend evtl. noch eine Checksumme integriert sind. Außerdem wäre es eine schicke Idee, die Daten sozusagen Seitenweise zu übertragen, heißt:
Eine Seite ist z.b. 10Bytes lang,

Was du da beschreibst, machen z.B. Automobil Steuergeräte häufig mit dem oben schon erwähnten abgewandelten ISO-TP.

Beispiel:

Es sollen die Daten 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 übertragen werden. In der Praxis natürlich viel mehr Daten, aber ich will nicht so viel schreiben.

Entweder die beteiligten Geräte wissen, welche maximale Paketgröße die jeweilige Gegenstelle verträgt, oder sie handeln das auch noch mit dem Protokoll aus. Das lasse ich hier aber mal weg.


Fall 1 ein großer Controller akzeptiert alle Daten auf einmal:
Der Sender sendet 0x02 0x01 0x08 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 yy. Das sind ein STX, die Framenummer 1, Datenzahl 8, die Daten und irgendeine Prüfsumme yy.
Der Empfänger bestätigt z.B. 0x02 0x01 0x06 yy . Das sind STX, Framenummer, ACK und Prüfsumme.


Fall 2 ein kleine Controller akzeptiert maximal 4 Datenbytes auf einmal:
Der Sender sendet 0x02 0x01 0x04 0x00 0x01 0x02 0x03 yy
Der Empfänger bestätigt mit 0x02 0x01 0x06 yy
Der Sender sendet 0x02 0x02 0x04 0x04 0x05 0x06 0x07 yy ( STX; 2. Frame; 4 Bytes; Daten; Prüfsumme )
Der Empfänger bestätigt mit 0x02 0x02 0x06 yy

Hoffe jetzt wird es etwas klarer.

malthy
08.08.2014, 18:46
Das ist genauso wie mit den Escape-Sequenzen in C

Gutes Stichwort :-) Hast natürlich vollkommen Recht, das habe ich übersehen.

Geistesblitz
08.08.2014, 22:16
Danke für die guten Tipps, da hilft mir schon einiges von weiter.
Gedacht ist es dafür, wenn ich eine Reihe von (auch selbstgebauten) Servoreglern mit Sollwerten und/oder Parametern füttern will, vor allem die Sollwerte sollten da nicht zu lange zum Übertragen brauchen. Und wenn man sich einen kaskadierten Regelkreis aufbauen will, wo man Sollposition, Sollgeschwindigkeit und Sollstrom beispielsweise innerhalb 1ms oder kürzer übertragen will, muss man sich nunmal was einfallen lassen. Ich würde wohl in dem Fall Position mit 4 Bytes und die anderen beiden mit jeweils 2 Bytes angeben, dann noch bisschen drum herum und man kommt dann sicher auf 10-12 Bytes wenn das richtig knapp gehalten wird. Würde man die reinen Zeichen übertragen, wären da locker 20+ Bytes nötig. Um Übertragungsfehlern vorzubeugen, ist es dann wohl besser, die Baudrate nicht zu hoch zu wählen.

@Mxt: wofür steht eigentlich das STX-Byte? Ist das die Adresse des Zielcontrollers oder wie? Und wofür ist die Frame-Nummer? Würde die nicht irgendwann einfach überlaufen, wenn die immer nur hochzählt? Oder markiert die zusammenhängende Daten?

Peter(TOO)
09.08.2014, 03:33
Hallo,

Die Logik verstehe ich nicht ganz. Wenn der Wert des Steuerzeichens auch in den Daten vorkommen kann, wie kann man dann ausschließen, dass es sich bei einem "scheinbaren" Steuerbefehl nicht zufällig um zwei Datenwerte handelt, die aussehen wie ein Steuerbefehl. Also konkret: wenn 0x1B einen Befehl markieren würde und ein 0x50 würde zB die Ausgabe von der folgenden drei Bytes (binary) auf einem LCD bedeuten, wie wäre der Fall davon zu unterscheiden, dass einfach zufällig dezimal 27 und 80 in einem anderen Zusammenhang aufgetaucht sind? Sich nämlich darauf zu verlassen, dass man immer sicher im Leseraster ist (was natürlich eine Lösung wäre), finde ich etwas gewagt ... Oder habe ich da irgendwo einen Denkfehler?


Also: 27 80 als Daten wird als 27 27 80 gesendet.

Sobald 27 im Datenstrom auftaucht wird dieses beim Empfänger entfernt und das nächste Byte auch, weil dies der Befehl ist. Das folgende 80 ist dann der Befehl fürs LCD ...

Der Befehl 27 bedeutet dann, füge 27 in den Ausgabestrom ein.

In Pseudocode:


z1 = LeseZeichen()
If (z1 = 27) then
Z2 = Lese Zeichen()
if (z2 = 0) then .....
if (z2 = 1) then .....
if (z2 = ..) then .....
if (z2 = ..) then .....
if (z2 = 27) then SchreibeZeichen(27)
if (z2 = ..) then .....
if (z2 = 80) then SchreibeLCD()
if (z2 = ..) then .....
else
Schreibe Zeichen(z1)
endif


MfG Peter(TOO)

schorsch_76
09.08.2014, 08:11
Danke für die guten Tipps, da hilft mir schon einiges von weiter.
Gedacht ist es dafür, wenn ich eine Reihe von (auch selbstgebauten) Servoreglern mit Sollwerten und/oder Parametern füttern will, vor allem die Sollwerte sollten da nicht zu lange zum Übertragen brauchen. Und wenn man sich einen kaskadierten Regelkreis aufbauen will, wo man Sollposition, Sollgeschwindigkeit und Sollstrom beispielsweise innerhalb 1ms oder kürzer übertragen will, muss man sich nunmal was einfallen lassen. Ich würde wohl in dem Fall Position mit 4 Bytes und die anderen beiden mit jeweils 2 Bytes angeben, dann noch bisschen drum herum und man kommt dann sicher auf 10-12 Bytes wenn das richtig knapp gehalten wird. Würde man die reinen Zeichen übertragen, wären da locker 20+ Bytes nötig. Um Übertragungsfehlern vorzubeugen, ist es dann wohl besser, die Baudrate nicht zu hoch zu wählen.

@Mxt: wofür steht eigentlich das STX-Byte? Ist das die Adresse des Zielcontrollers oder wie? Und wofür ist die Frame-Nummer? Würde die nicht irgendwann einfach überlaufen, wenn die immer nur hochzählt? Oder markiert die zusammenhängende Daten?

Na da würde ich auf ein einfach umzusetzendes, aber standardisiertes Protokoll wie USS setzen.
http://cache.automation.siemens.com/dnl/jgxNzU1MQAA_24178253_HB/uss_24178253_spez_00.pdf

Der Kern ist
STX LGE ADR xx xx xx BCC



1. Einleitung
Das USS

-Protokoll (Universelles-s
erielles-S
chnittstellen-Protokoll) definiert ein Zugriffsverfahren nach dem
Master-Slave-Prinzip für die Kommunikation über einen seriellen Bus. Als Untermenge ist darin auch die Punkt-
zu-Punkt-Verbindung eingeschlossen
Wesentliche Merkmale des USS

-Protokolls sind:

Unterstützung einer mehrpunktfähigen Kopplung, z. B. EIA RS 485-Hardware

Master-Slave-Zugriffsverfahren

Single Master-System

Maximal 32 Teilnehmer (max. 31 Slaves)

Einfacher, sicherer Telegrammrahmen

Einfach implementierbar

Wahlweiser Betrieb mit variablen oder festen Telegrammlängen.
Am Bus können ein Master und max. 31 Slaves angeschlossen werden. Die einzelnen Slaves werden vom
Master über ein Adreßzeichen im Telegramm angewählt. Ein Slave kann niemals von sich aus die
Sendeinitiative ergreifen, ein direkter Nachrichtenaustausch zwischen den einzelnen Slaves ist nicht möglich.
Die Kommunikation erfolgt im Halbduplex-Betrieb.
Die Masterfunktion kann nicht weitergegeben werden (Single-Master-System).
....
....
4.1. Datencodierung
Die Informationen sind wie folgt codiert:

STX (Start of Text): ASCII-Zeichen: 02 Hex

LGE (Telegrammlänge): 1 Byte, enthält die Telegrammlänge als Binärzahl,
siehe Abschnitt 4.2

ADR (Adreßbyte): 1 Byte, enthält die Slave-Adresse und den Telegrammtyp.
Binär codiert, siehe Abschnitt 4.3

Nutzzeichen: Je ein Byte, Inhalt auftragsabhängig

BCC: 1 Byte, Datensicherungszeichen (Block Check Charakter),
Bildungsgesetz, siehe Abschnitt 4.4


Das ganze setzt auf RS232 für PtP auf oder auf RS485 für mehrere Slaves und einen Master.

Mxt
09.08.2014, 09:01
@Mxt: wofür steht eigentlich das STX-Byte?
Es zeigt einfach den Beginn einer Übertragung an.

Möglicherweise hat der Empfänger ja den Anfang nicht mitgekriegt, weil er z.B. erst später seine Schnittstelle aufgemacht hat. Er schmeißt also erstmal alle Bytes weg, bis er auf ein STX trifft.

Dann sind wir wieder bei meinem ersten Beispiel aus meiner ersten Antwort. Man schickt einfach

STX ... beliebig viele Datenbytes ... ETX

und wenn in den Datenbytes der Wert von STX, ETX oder ESC auftaucht, dann sendet man zusätzlich noch ein ESC davor. Das ist auch das, was die anderen, wie Peter, meinen.


Die Sache mit den Frames kommt ins Spiel, wenn man große Datenmengen schicken will, aber Sender oder Empfänger nicht genug freien Speicher dafür haben. Dann müssen die Daten in Pakete zerlegt werden. Die Framenummer ist die Paketnummer. Man kann das auch beliebig erweitern, z.B. in eine "Dies ist Paket 1 von 4" Numerierung mit zwei Framenummerbytes.

ISO-TP kommt eigentlich vom CAN Bus. Da hat man das Problem, dass eine Message nur maximal 8 Datenbytes haben kann. Mit Hilfe der Framenummern kann man aber, aufgeteilt auf viele Botschaften, bis zu 4095 Bytes auf definierte Weise übertragen. Die Framenummer darf normalerweise nicht überlaufen, deshalb legt die Anzahl der verfügbaren Nummern die maximale Größe einer Übertragung fest.

Verwendet wird das z.B. bei den Daten auf dem Diagnosestecker des Autos:
http://de.wikipedia.org/wiki/Unified_Diagnostic_Services
http://de.wikipedia.org/wiki/KWP2000

malthy
09.08.2014, 11:19
Hallo Peter(TOO),

ja, vielen Dank für die Erläuterung, nach dem Hinweis von Mxt auf die Escape Sequenzen ist mir dann ein Licht aufgegangen. Du hattest es vorher ja im Prinzip auch schon gesagt.

Gruß
Malte

Peter(TOO)
10.08.2014, 19:40
Hallo,

Die Sache mit den Frames kommt ins Spiel, wenn man große Datenmengen schicken will, aber Sender oder Empfänger nicht genug freien Speicher dafür haben. Dann müssen die Daten in Pakete zerlegt werden. Die Framenummer ist die Paketnummer. Man kann das auch beliebig erweitern, z.B. in eine "Dies ist Paket 1 von 4" Numerierung mit zwei Framenummerbytes.

Frames braucht man auch, wenn man Störungen hat und Datenblöcke deshalb erneut übertragen muss.

Besonders zu Zeiten der Modem-Übertragung über das Telefon, erzeugten die Gebührenimpulse gerne gestörte Daten :-(

Du willst eine Datei übertragen, welche dafür 60 Sekunden benötigt.
Nun kommt aber alle 30s ein Gebührenimpuls.
Am Stück bekommst du die Datei nie rüber!
Wenn man jetzt die Datei in Blöcke zerlegt, welche z.B. jeweils 10s dauern, musst du 2 Blöcke doppelt übertragen um die Datei fehlerfrei zu übertragen.

MfG Peter(TOO)