Hallo Leute!
Ich sitze wieder mal über der Aufgabe eine Datenverbindung zwischen
einem PC und einem uC über die RS232-Schnittstelle aufzubauen.
Zum Thema Datenflussstuereung (vllt. besser: Übertragungsprotokoll
aufgesetzt auf RS232)hab ich mir schon früher Gedanken gemacht und
einiges mehr oder weniger erfolgreich umgesetzt.
Mich würde jetzt aber interessieren wie ihr das macht und welche anderen
Methoden es gibt.
Um das ganze verständlicher zu machen, ein kleines Beispiel:
Variablen im uC sollen durch den PC gelesen oder beschrieben werden.
Folgendes Schema könnte man hier verwenden:
Sende 5 Daten-Bytes über RS232:
- 1 Byte: |Variablennamen+R/W|
- 4 Bytes: |Daten|
Der uC wartet also bis 5 Bytes eingetroffen sind und schreibt die Daten
von den letzten 4 Bytes in die Variable, die durch das 1. Byte definiert
wurde.(sofern "W" gesendet wurde, falls R: Sendet er den Inhalt der
Variablen an PC).
Das ganze ist schön und gut, nur kann es passieren, dass der uC
irgendwie aus dem Tritt kommt und die falschen 5Bytes sammelt (Bytes aus
zwei verschiedenen Datenströmen). Spätestens dann ist Schicht im Schacht
und der uC weiß nicht mehr wo ein Paket anfängt und wo aufhört.Lösung
wären natürlich Start und Stop-Zeichen. Da weiß ich nur nicht ganz, was
ich da genau senden muss, damit der Datenstrom nie die selben Bits wie
das Start/Stop-Zeichen enthält. Hier fehlt mir einfach die Praxis.
Also nochmal zur Frage: Wie macht ihr das, welche Lösungen sind üblich?
Vielen Dank!
Gruß Denglmann
Ich mache gerade etwas Ahnliches mit Pick-by-Light-Empfängern:
- Kommando-Byte muss ein ganz bestimmtes Bitmuster haben
- dann folgen zwei Adress-Bytes
- dann folgen zwei Daten-Bytes
- dann folgt ein Kontroll-Byte (XOR über die vorherigen 5 Byte)
Zusätzlich erfolgt die Sendung MIT Parität, nicht, wie meist üblich
ohne. Jedes Kommando vom PC zum yC muss vom adressierten yC bestätigt
werden. PC ist Master mit Adresse 1*, yC antwortet nur, wenn vorher
gefragt ... Der yC führt Kommandos nur aus, wenn oben genannte Bedingung
erfüllt. Der PC wiederholt Kommandos so lange, bis eine Bestätigung
erfolgt oder nach einer eingestellten Wiederholrate der Empfänger als
defekt markiert wird.
Der yC prüft nach jedem eingegangenen Byte, ob obige Struktur im Puffer
liegt und ob die Adresse seine eigene ist. Bei Pausen von mehr als einer
Sekunde zwischen den eintreffenden Bytes wird der gesamte Puffer
gelöscht.
Als Bus setze ich RS485 (MAX485) im 2-Draht-Modus bei 9600 Baud ein.
*Adresse Null ist reserviert für (neue) Empfänger, die sich im
"Lernmodus" nach 10s langem Tastendruck befinden, um per Kommando vom PC
die Adresse des Empfängers zu setzen.
@Frank
Probier doch mal "Alt Gr" + "m"
Dein Protokoll ist nett. Mehr auch nicht.
>- Kommando-Byte muss ein ganz bestimmtes Bitmuster haben>- dann folgen zwei Adress-Bytes>- dann folgen zwei Daten-Bytes>- dann folgt ein Kontroll-Byte (XOR über die vorherigen 5 Byte)
Auch ob mit oder ohne Parität ist überhaupt nicht entscheidend.
Da der Beginn der Message nicht durch ein exclusives Byte repräsentiert
wird, ist das "bestimmte Bitmuster" auch nur für den Decoder der Message
wichtig, für die Synchronisation des Empfängers taugt es nicht.
Das einzige Entscheidende ist wohl das Erkennen der Übertragungslücke
zwischen den Messages und das richtige Reagieren drauf.
Obs eine Sekunde sein muß, hängt doch schwer von der Aufgabenstellung
ab.
Ich kenn Protokolle, da gilt ein Timeout von 8 Millisekunden bei 38,4
KBaud.
Da kann sich jeder Entwickler was eigenes ausdenken. Auch mit
Packetwiederholung.
Ich packe das in einen Frame, ein Startbye gefolgt von den Daten,
dann ein Stopbyte. Das Startbyte löscht den Empfangspuffer, wurde
das Stopbyte empfangen wird der Inhalt des Empfangspuffers verarbeitet.
Zu empfehlen ist noch eine Prüfsumme über den Nutzinhalt (XOR oder
CRC).
Man sollte sich ein richtiges Master-Slave Protokol ausdenken. Der
Controller sendet von sich aus nichts, immer nur als Antwort auf eine
Anfrage. Die Anforderungen an eine Punkt zu Punkt Verbindung
1+ byte Vorspann
1 byte Datenlaenge falls variabel
1 byte Command
0+ byte Daten
2 byte CRC
Dieses Paket muss innerhalb .. ms quitiert werden. Entweder mit der
Antwort, oder einem Verstanden, oder einer Kopie des Commands.
Datenflusskontrolle wuerd ich nicht verwenden, da die zusaetzliche
Leitungen benoetigt. Eher die Baudrate genuegend langsam ansetzen, dass
der Controller genuegend Zeit hat und nicht ueberflutet wird.
Das Protokoll sollte zustandsfrei sein. Dh die Packete sollten
unabhaengig voneinander sein, Es kann auch mal eins ausfallen.
Es ist ganz einfach
Mache ein quasi - MasterSlave-Prinzip und zwar folgendermaßen.
Sende am PC Befehlswörtet beliebiger Länge und haenge am Schluss der
Zeichenkette eine 13 an. (=ist Carriage Return)= <CR>
Bsp: Ping<13>
der µComtroller sammelt alle eintreffenden Zeichen P-i-n-g--<13>
und merkt dann an der "13" AHA, hier ist das Befehlswort zu ende
Schon kann man in eine Case-Liste den Befehl interpretieren. Wenn was
passendes zu finden ist reagiert der µController und sendet Pong<13>.
SO kann mit einem Terminal oder einer höheren Software gesteuert werden.
Wenn das Befehlsformat erweitert wird, kann mit strtok eine
Delimiterliste interpretiert werden.
Befehl,wert1,Wert2<13>
Das heist, der µController ist Passiv
kann aber auch permanent selbst senden... so wie es der Entwickler will.
------------------------------- Befehlstructur
typedef struct
{
char ucaCmd[COMAMNDSIZEMAX +1];
float fCmdVal_1;
float fCmdVal_2;
}COMMAND_TYPE;
dann
COMMAND_TYPE gsCmd;
----------------------------- Trennen der Tokens
void CheckOrder(char *pcStrOrder) //von gcaRxStr
{
char * pStrTok; // für strtok_r
char * pucCh = &pcStrOrder[ strlen(pcStrOrder) ];
strcpy(gcaStr, pcStrOrder);
strcpy_P(gcaNumStr, PSTR(",")); //HILFSWEISE gcaNumStr für ","
if( (pStrTok = strtok_r( pcStrOrder, gcaNumStr , &pucCh)) !=
NULL)//Trenne CMD, von ID, -"$$$1,CMD
{
strcpy(gsCmd.ucaCmd, pStrTok);
}
else
{
*gsCmd.ucaCmd = 0;
};
gsCmd.ucaCmd[ COMAMNDSIZEMAX ] = 0; //Sicherheitsterminierung
COMAMNDSIZEMAX=10
gsCmd.fCmdVal_1 = gsCmd.fCmdVal_2 = 0;
if( (pStrTok = strtok_r( NULL, gcaNumStr, &pucCh)) != NULL)//Hole nun
einen Value nach "$$$1,COMMAND,VALUE
{
gsCmd.fCmdVal_1 = atof(pStrTok);// Und dann TO Float
};
if( (pStrTok = strtok_r( NULL, gcaNumStr, &pucCh)) != NULL)//Hole nun
einen Value nach "$$$1,COMMAND,VALUE
{
gsCmd.fCmdVal_2 = atof(pStrTok);// Und dann TO Float
};
};
--------------- Befehlsliste
if( !strcasecmp_P( gsCmd.ucaCmd, PSTR("PING")) )
{
Print_PStrCR( PSTR("PONG") );
return;
};
-----
Denglmann schrieb:> Da weiß ich nur nicht ganz, was> ich da genau senden muss, damit der Datenstrom nie die selben Bits wie> das Start/Stop-Zeichen enthält.
Das ist recht einfach. Du sendest als Steuerzeichen die "üblichen" STX
(0x02) und ETX (0x03).
Die Nutzdaten überträgst du aber nur mit darstellbaren Zeichen, also
z.B. A-Z, a-z. Satzzeichen gehen natürlich auch.
Damit kannst du ganz klar zwischen Steuerzeichen und Nutzdaten
unterscheiden.
Ein Nachteil der Methode ist, dass der Datendurchsatz kleiner wird da du
Hexwerte erst in ASCII umwandeln mußt. Z.B. deine Variable enthält 0x26.
Darus wird bei der Übertragung 0x32, 0x36. Also 2 ASCIIbytes statt einem
Byte direkt aus der Variablen.
Vollständig sieht das Beispiel dann so aus:
<STX> <0x32><0x36><ETX>
<0x02><0x32><0x36><0x03>
Zurückwandeln in den Hexwert mußt du im Emfänger natürlich auch noch.
Erweitern mit Prüfsummen u.s.w. geht natürlich, da sind der Fantasie
keine Grenzen gesetzt. Aber auch die Prüfsumme wieder in darstellbaren
ASCII-Zeichen übertragen.
Daniel V. schrieb:> Ich habe mal dieses Protokoll umgesetzt:>> http://www.ibrtses.com/embedded/shortmsgprotocol.html
Ich habe nur mal kurz reingeschaut, aber wie es aussieht, ist alles
außer Sync+ und STX nicht ASCII-kodiert, d.h. die Synchronisierung ist
nicht eindeutig und kann schief gehen.
Wenn schon, dann sollten alle Bytes ASCII-kodiert sein - natürlich
geht das auf Kosten der Geschwindigkeit.
Gruß Dietrich
Edit: ich sehe gerade, 900ss D. ich mit zuvorgekommen ;-(
Dietrich L. schrieb:> Daniel V. schrieb:>> Ich habe mal dieses Protokoll umgesetzt:>>>> http://www.ibrtses.com/embedded/shortmsgprotocol.html>> Ich habe nur mal kurz reingeschaut, aber wie es aussieht, ist alles> außer Sync+ und STX nicht ASCII-kodiert, d.h. die Synchronisierung ist> nicht eindeutig und kann schief gehen.> Wenn schon, dann sollten alle Bytes ASCII-kodiert sein - natürlich> geht das auf Kosten der Geschwindigkeit.>> Gruß Dietrich>> Edit: ich sehe gerade, 900ss D. ich mit zuvorgekommen ;-(
Im Prinzip hast du recht. Allerdings:
1.) im Header ist eindeutig definiert, wie Lange die Nachricht sein soll
2.) das Ganze schließt mit einer CRC ab
3.) Man kann auch definieren, dass das Startzeichen STX mehrmals kommen
muss, bevor man einen Start der Sendung akzeptiert
Falls die oben genannten Bedingungen nicht erfüllt sind, dann wird die
Nachricht nicht akzeptiert. Das finde ich schon sehr sicher.
Nachtrag: ASCII hat natürlich den Vorteil, dass es lesbar ist, z.B. in
Hyperterminal
Daniel V. schrieb:> Das finde ich schon sehr sicher.
... was auch immer "sehr" bedeutet. Für den Heimgebrauch und bei
unkritischen Anwendungen ist das vielleicht sicher genug.
(Ich bin halt "verdorben": habe in der Fernwirktechnik gearbeitet, da
werden mit den Telegrammen z.B. Schaltanlagen geschaltet. Im Fehlerfall
ist vielleicht eine ganze Stadt dunkel...)
Gruß Dietrich
>>Daniel V. schrieb:>> Das finde ich schon sehr sicher.>>... was auch immer "sehr" bedeutet. Für den Heimgebrauch und bei
unkritischen Anwendungen ist das vielleicht sicher genug.
Wenn keine Antwort auf das Packet kommt, ging's verloren, und wird
wieder ausgesandt, resp die Antwort nochmals angefordert. Daher ist es
wichtig, ohne Zustaende zu arbeiten.
Daniel V. schrieb:> 3.) Man kann auch definieren, dass das Startzeichen STX mehrmals kommen> muss, bevor man einen Start der Sendung akzeptiert
Kann mir einer erklären, was es bringt, wenn das STX wiederholt wird?
Die Zustandsmaschine kann sicherer re-synchronisieren wenn der Header
laenger ist. Mit einem laengeren Header hat man schon nach 5 Zeichen
Gewissheit dass etwas nicht stimmt und kann verwerfen.
An Sicherheit bezueglich den Daten gewinnt man nichts, denn der CRC
faengt ein verheddertes Packet eh ab.
@Zac Hobson
Bezieht sich dein Post von 19:43 auf meine Frage?
Konrad S. schrieb:> Kann mir einer erklären, was es bringt, wenn das STX wiederholt wird?
Dann ist mir entgangen, dass hier von synchronen Datenübertragung die
Rede ist!
Bei asynchroner Datenübertragung, wovon ich ausgegengen bin, bringt das
Wiederholen des STX - meiner Meinung nach - nichts. Ich bin in dieser
Sache einer anderen Meinung gegenüber durchaus aufgeschlossen, sofern
sie gut begründet ist.
(Man möge die Ausdrucksweise verzeihen. Ich hoffe, meine Skepsis ist
herauszulesen.)
Hallo
Die Fernwirktechnik bedient sich zweier Telegrammtypen.
Telegramm mit fixer Länge (z.B, drei Datenbyte)
Startbyte 0x10h
1.Datenbyte
2.Datenbyte
3.Datenbyte
Checksum
Stoppbyte 0x16h
Telegramm variabler Länge
Startbyte 0x10h
Telegrammlänge
Telegrammlänge Cpoy
Startbyte
1.Datenbyte
n.Datenbyte
Checksum
Stoppbyte 0x16h
Nur so eine Anregung
Lg.
Johann K.
>Dann ist mir entgangen, dass hier von synchronen Datenübertragung die
Rede ist!
Wir reden von einer asynchronen Uebertragung. Es geht im Wesentlichen um
die festen Zeichen eines Headers. Je mehr feste Zeichen enthalten sind,
desto eher merkt der Empfaenger wenn etwas nicht stimmt.
Der Empfaenger hat eine Zustandsmaschine mitlaufen, die vergleicht, was
wann kommen muss. Wenn der Header schon nicht stimmt, kann er schneller
neu synchronisierenm als wenn er's erst beim CRC merkt.
Der Ansatz mit mehreren SYN zu beginn weg ist daher gut.
Zac Hobson schrieb:> Der Ansatz mit mehreren SYN zu beginn weg ist daher gut.
Du gestattest, dass man anderer Meinung ist.
IMHO ist alles, was nicht dafür sorgt, dass das Synchronisierbyte nicht
in den Daten vorkommen kann, Müll.
Diverse Spielchen mit: Wir wissen wie lang der Header maximal ist,
Timeout und sonstiges sind alles Krücken, um das eigentlich Problem zu
verschleiern oder so zu verstecken, dass unerfahrene Programmierer nur
nicht bemerken, dass es hier eine potentielle Fehlerquelle gibt.
Das Synchroniserbyte muss EINDEUTIG aus dem Datenstrom identifizierbar
sein. Und das ist gar nicht mal so schwer zu erreichen. Das Stichwort
Escapen ist ja schon gefallen. Und dann braucht man auch nur genau 1
Synchronisierbyte und nicht mehr.
Ah, ja, wenn ich genügend "Synchronisationsbytes" übertrage, tritt
irgendwann ein Übertragungsfehler auf, den ich Empfänger dann erkennen
kann, ist klar. Soll ich jetzt mit Übertragungsfehlern morsen, oder was?
Je weniger Bytes übertragen werden, desto weniger "Platz" ist für
Fehler!
Gegen welche Art von Übertragungsfehlern soll das Verfahren denn helfen?
Konrad S. schrieb:> Ah, ja, wenn ich genügend "Synchronisationsbytes" übertrage, tritt> irgendwann ein Übertragungsfehler auf, den ich Empfänger dann erkennen> kann, ist klar. Soll ich jetzt mit Übertragungsfehlern morsen, oder was?> Je weniger Bytes übertragen werden, desto weniger "Platz" ist für> Fehler!> Gegen welche Art von Übertragungsfehlern soll das Verfahren denn helfen?
Übertragungsfehler werden nicht verbessert. Lediglich das Aufsetzen auf
den Anfang.
Das Ursprungsgegenargument war, dass in meinem verlinkten Protokoll
nicht eindeutig klar ist, ob SYN einen Anfang des Telegrams markiert
oder nicht. Das kann man dadurch verbessern, dass man definiert, dass
das Telegram generell mit z.B. 4x SYN beginnt, also:
<SYN><SYN><SYN><SYN><..>.
Die Wahrscheinlichkeit, dass im Datenstrom 4 mal SYN hintereinander
kommt ist geringer, als dass es 1 mal vorkommt. Übertraungssicherheit
gewinnt man dadurch nicht.
Nachtrag:
An meinem verlinkten Prokoll finde ich z.B. gut, dass man nicht
unbedingt in ASCCII senden muss, sondern auch Binärdaten übertragen
kann. Der Dateninhalt wird über das Befehlsbyte definiert.
Karl-Heinz hat davon gesprochen, das TimeOut eine Krücke ist. Richtig,
wenn ich sage, dass nach einer bestimmten Zeit das Telegramm zu Ende
ist. Im beschriebenen Protokoll ist das aber nicht der Fall. Im Header
steht drin, wie Lange die Nachricht ist. Insofern zähle ich einfach die
einkommenden Bytes durch. Wenn dann das Telegramm stimmig ist (CRC),
kann ich es verarbeiten. Theoretisch kann sogar ohne Pause das nächste
Telegramm eintrudeln.
Daniel V. schrieb:> Die Wahrscheinlichkeit, dass im Datenstrom 4 mal SYN hintereinander> kommt ist geringer, als dass es 1 mal vorkommt.
Wenn die Daten geeignet escapet sind, dann kommt SYN bzw. STX (oder was
auch immer) im Datenstrom garantiert nicht vor und das STX
signalisiert den Beginn der Nachricht. Den Quatsch mit SYN kann man sich
dann auch sparen. Eine Absicherung gegen Übertragungsfehler, z.B. durch
CRC, ist natürlich immer noch sinnvoll.
Wer mit AVRs arbeitet, dem sei der Abschnitt "Multi-processor
communication mode" bei der UART/USART-Beschreibung ans Herz gelegt. Man
spart sich das escapen, kann alle Byte-Werte übertragen und die
Adressierung ist gleichzeitig der Beginn einer Nachricht. OK, es kostet
ein Bit pro Byte mehr an Übertragungsbandbreite.