Forum: Mikrocontroller und Digitale Elektronik Software-Datenflusssteurung über RS232


von Denglmann (Gast)


Lesenswert?

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

von Frank (Gast)


Lesenswert?

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.

von spontan (Gast)


Lesenswert?

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

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

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

von Zac Hobson (Gast)


Lesenswert?

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.

von Daniel V. (danvet)


Lesenswert?

Ich habe mal dieses Protokoll umgesetzt:

http://www.ibrtses.com/embedded/shortmsgprotocol.html

Ist sehr robust und kommt auch nicht aus dem Tritt, wenn ein Teilnehmer 
mal Käse sendet.

von Christof E. (Gast)


Lesenswert?

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;
  };
-----

von 900ss (900ss)


Lesenswert?

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.

von Dietrich L. (dietrichl)


Lesenswert?

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 ;-(

von Daniel V. (danvet)


Lesenswert?

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

von Dietrich L. (dietrichl)


Lesenswert?

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

von Blackbird (Gast)


Lesenswert?

HDLC? Geht auch für Binär-Daten.

Blackbird

von Zac Hobson (Gast)


Lesenswert?

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

von Quote & Escape (Gast)


Lesenswert?

Quoten bzw. escapen ist auf eine Möglichkeit, so macht es z.b. ppp.

von Konrad S. (maybee)


Lesenswert?

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?

von Zac Hobson (Gast)


Lesenswert?

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.

von Konrad S. (maybee)


Lesenswert?

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

von Klatec (Gast)


Lesenswert?

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.

von Klatec (Gast)


Lesenswert?

hallo

Mir ist ein Kopiefehlere unterlaufen. Beim Telegramm mit variabler Länge 
ist das Startbyte nicht 0x10h sondern 0x68.

lg.

johann K.

von Zac Hobson (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Konrad S. (maybee)


Lesenswert?

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?

von Daniel V. (danvet)


Lesenswert?

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.

von Daniel V. (danvet)


Lesenswert?

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.

von Konrad S. (maybee)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.