Forum: Mikrocontroller und Digitale Elektronik Allgemeine Frage zum Datenempfang via UART


von DruckPause (Gast)


Lesenswert?

Angenommen mir werden von außen Telegramme geschickt. Ungefährer Aufbau 
wäre sowas:

ID (1Byte)| Länge in Byte (1Byte)| Daten (max. 255 Bytes)| CRC (2 Bytes)


Wie realisiere ich das im Programmablauf am besten, dass ich weiß ob ein 
Telegramm vollständig und korrekt empfangen wurde.

Beispiel:
Ich empfange zwei Bytes, also die ID und die Länge in Byte. Jetzt könnte 
ich daraus ja folgern "aha, also kommen noch X Datenbytes + die CRC".
Allerdings weiß ich ja erst mit der CRC, ob meine empfangenen Daten 
überhaupt korrekt sind.

Versteht ihr was ich meine?

Wie kann ich prüfen, ob ich Daten vollständig und korrekt empfangen 
habe?!

von Karl H. (kbuchegg)


Lesenswert?

DruckPause schrieb:

> Beispiel:
> Ich empfange zwei Bytes, also die ID und die Länge in Byte. Jetzt könnte
> ich daraus ja folgern

Nicht 'könnte'.
Das ist alles was du hast!

Der Konjunktiv ist da nicht angebracht, denn du hast sowieso keine 
andere Wahl bei diesem Protokoll.

> Wie kann ich prüfen, ob ich Daten vollständig und korrekt empfangen
> habe?!

Das Telegramm fängt mit der bekannten ID an. Ab dort gelten die Bytes. 
Wenn dann hinten nach die CRC nicht stimmt, dann läuft dein Empfänger 
asynchron und hat ein paar Datenbytes mit der ID verwechselt. Deine 
einzige Hoffnung lautet: überlesen von Bytes, bis die ID wieder 
auftaucht und dann hoffen, dass das auch wirklich die ID war und du 
nicht schon wieder auf ein paar Datenbytes mit zufällig genau den 
richtigen ID-Werten hereingefallen bist.

von Holger P. (Gast)


Lesenswert?

In dem du wartest bis nach Deiner ID+Byte die Anzahl der Daten kommen. 
(TimeOut). Sind Deine Daten Byte(Anzahl)+2Byte(CRC) da, rechnest du den 
CRC zurück und schaust ob er mit den letzten zwei Byte gleich ist ( bzw. 
0 )

von DruckPause (Gast)


Lesenswert?

Es gibt ja verschiedene Telegramm IDs wovon dann die Gesamtlänge 
abhängt.

Im Prinzip löst man das also tatsächlich mit einem Timeout? So wie von 
Holger P. beschrieben?

Holger P. schrieb:
> In dem du wartest bis nach Deiner ID+Byte die Anzahl der Daten kommen.
> (TimeOut). Sind Deine Daten Byte(Anzahl)+2Byte(CRC) da, rechnest du den
> CRC zurück und schaust ob er mit den letzten zwei Byte gleich ist ( bzw.
> 0 )

von Cyblord -. (cyblord)


Lesenswert?

Wenn zwischen den Telegrammen in jedem Fall Pausen bestimmter Länge 
sind, kann man erstmal auf diese Pausen Syncronisieren. Ein CRC Check 
des gesamten Pakets ist trotzdem immer angebracht. Dann kann man noch 
feste Header und Trailer vorsehen auf die man testet. z.B. ein STX an 
den Anfang und ein ETX ans Ende.
Prinzipiell kannst du auch mit Steuerzeichen arbeiten welche durch ein 
"Escape-Zeichen" von den Daten getrennt werden. Damit kannst du die 
Steuerzeichen eindeutig von den Daten unterscheiden.
http://de.wikipedia.org/wiki/Escape-Sequenz

Ist aber IMO meist nicht nötig wenn man die obigen Verfahren einsetzt.

gruß cyblord

von Karl H. (kbuchegg)


Lesenswert?

DruckPause schrieb:
> Es gibt ja verschiedene Telegramm IDs wovon dann die Gesamtlänge
> abhängt.
>
> Im Prinzip löst man das also tatsächlich mit einem Timeout?

Ein Timeout ist immer die schlechteste Methode für die Synchronisierung. 
Ein Timeout löst die Fragestellung: Ist die Gegenstelle überhaupt noch 
da?

Stell dir einfach den simplen Fall vor:
Aus irgendeinem Grund wird das UART Kabel aus der Buchse gezogen und neu 
eingesteckt.

Ein gutes Protokoll kommt damit klar und synchronisiert sich wieder in 
den laufenden Datenstrom.

Denn: solange nichts unvorhergesehenes passiert ist alles sowieso kein 
Problem. Interessant sind immer die Fälle, bei denen dann eben doch 
etwas unvorhergesehenes passiert, wie zb ein Zusammenbruch der 
Verbindung. Aus welchem Grund auch immer und sei es nur, weil die 
Putzfrau das Kabel runtergerissen hat.

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ein Timeout ist immer die schlechteste Methode.
>
> Stell dir einfach den simplen Fall vor:
> Aus irgendeinem Grund wird das UART Kabel aus der Buchse gezogen und neu
> eingesteckt.
>
> Ein gutes Protokoll kommt damit klar und synchronisiert sich wieder.
> Auch ohne irgendwelche Timeouts.

wie soll das den gehen ohne timeouts? Wenn ständig irgendwelche Daten 
ankommen (wirklich alles 0-255) dann schafft es die client sich nur 
durch zuffall zu synchen.

Meist wartet aber der Host auf eine Antwort, und diese lücke kann der 
client ausnutzen um sich neu zu synchen (also Timeout). Wie soll das 
denn ohne timeout und ohne extra startbyte/endbyte denn gehen?

von Bastler (Gast)


Lesenswert?

Also ich verwende immer eine Sate-macine in der UART-ISR.
Hat sich so bewährt.

Startzeichen?
(sonst IDLE)

Länge => zum füttern der Schleife im Datenempfang

Daten ....

CRC

Stoppzeichen


Wenn das Stoppzeichen und die Länge zusammenpasst wirde der Status auf 
FINISHED gesetzt.

In der while(1) wird abgefragt ob der Satus FINISHED ist.
Wenn ja wird die CRC berechnet und verglichen.


Ein Timeout kommt zum Schutz oben drauf.
Beim Empfang des Startzeichens wird ein Timer gestartet und beim 
Stoppzeichen beendet.
Ansonsten springt er in seine ISR und setzt die Statemachine zurück.

von Route_66 H. (route_66)


Lesenswert?

Peter II schrieb:
> wie soll das den gehen ohne timeouts?

Hallo!
Du vermischt hier die Bedeutung etwas. Du meinst Lücke im Datenstrom, 
die der Sender bewußt einfügt. Timeout im Empfänger heißt, es kommen in 
vorgegebener Zeit keine, oder zu wenig Zeichen an.

von Peter II (Gast)


Lesenswert?

Route 66 schrieb:
> Hallo!
> Du vermischt hier die Bedeutung etwas. Du meinst Lücke im Datenstrom,
> die der Sender bewußt einfügt. Timeout im Empfänger heißt, es kommen in
> vorgegebener Zeit keine, oder zu wenig Zeichen an.

es kann ein timeout auch beim Empfänger geben.

In der Art: zwischen 2 zeichen innerhalb des Packets darf maximal eine 
lücke von 10ms sein.

Dann hat der client die Möglichkeit, sich zurückzusetzen und auf das 
nächste packet zu warten.

von Route_66 H. (route_66)


Lesenswert?

Hatte ich nicht geschrieben "Timeout im Empfänger..."?
Grübel, Grübel.

von Peter II (Gast)


Lesenswert?

Route 66 schrieb:
> Hatte ich nicht geschrieben "Timeout im Empfänger..."?
> Grübel, Grübel.

ja, sorry ab mit was soll ich es dann da vermischen ?

von Route_66 H. (route_66)


Lesenswert?

Peter II schrieb:
> ja, sorry ab mit was soll ich es dann da vermischen ?
Man darf nicht einfach die verschiedenen Lückenarten vermischen: es gibt 
"gute" Lücken, die immer vorkommen (müssen) und "schlechte" Lücken durch 
Übertragungfehler, Unterbrechungen etc. Beide muß man beherrschen und 
unterschiedlich darauf reagieren.
Die erste Lücke führt zur normalen Abarbeitung und die zweite zur 
Fehlerbehandlung.
EDIT: Die erste merkt man normalerweise nicht!

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:

> Meist wartet aber der Host auf eine Antwort, und diese lücke kann der
> client ausnutzen um sich neu zu synchen (also Timeout). Wie soll das
> denn ohne timeout und ohne extra startbyte/endbyte denn gehen?

Genau das ist der springende Punkt:
Es geht nur mit dezidierten Start/Stopp Bytes, die in den Daten nicht 
vorkommen. Das ist die sicherste Art und Weise. Da kann dir nichts 
passieren, auch wenn es durch möglicherweise notwendiges Escapen ein 
wenig aufwendig ist.

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Es geht NUR mit dezidierten Start/Stopp Bytes, die in den Daten nicht
> vorkommen.

aber das ist doch dann auch nicht Lösung aller Probleme. Damit habe ich 
dann Probleme wenn ich viele daten effektiv übertragen will.

Und die häufigste Anwendung ist doch das ein Host bei client anfragt und 
auf die Antwort wartet. Wenn der client das Packet nicht verstanden 
(weil er den anfang nicht mitbekommen hat) dann kann man das Problem 
geschickt mit einem timeout lösen.

von Reinhard Kern (Gast)


Lesenswert?

Peter II schrieb:
> Wenn der client das Packet nicht verstanden
> (weil er den anfang nicht mitbekommen hat) dann kann man das Problem
> geschickt mit einem timeout lösen.

Du willst also immer einen Fehler provozieren, damit das folgende Paket 
synchronisiert werden kann? Wie verträgt sich das mit der von dir 
(unnötigerweise) geforderten Effektivität?

Gruss Reinhard

von Peter II (Gast)


Lesenswert?

Reinhard Kern schrieb:
> Du willst also immer einen Fehler provozieren, damit das folgende Paket
> synchronisiert werden kann? Wie verträgt sich das mit der von dir
> (unnötigerweise) geforderten Effektivität?

was für ein Fehler provozieren?

Wenn nur das halbe Packet, wegen einen kabel fehler, nicht bei client 
ankommt hat nicht mit Fehler provozieren zu tun?

von W.S. (Gast)


Lesenswert?

DruckPause schrieb:
> Versteht ihr was ich meine?

Ja. Du hast eine grundfalschen Denkansatz.

Also erstens: Wenn du alle möglichen Bytes 0..255 für alles zuläßt, dann 
hast du garkeine Möglichkeit zu irgendwelcher Kontrolle, außer solchen 
Methoden, wie sie z.B. beim RDS Dekodieren angesagt sind, wo also über 
eine Vielzahl von empfangenen Bits ein bestimmtes Muster gesucht wird 
zur Synchronisation.

Zweitens: Was hier an Vorschlägen kam über Timeouts usw. ist Mumpitz. 
Schließlich reden wir hier über den Datenverkehr über einen UART, 
gelle? Und wenn man Daten mit einem PC austauscht, dann geht sowas wie 
Timeouts sowieso nicht, denn das Programm im PC läuft ja sowieso nur in 
den Zeitabschnitten, die es vom BS zugeteilt bekommt.

Also: Was du brauchsr, ist ein Protokoll. Beispielsweise kannst du als 
allersimpelste Methode folgende Festlegung treffen:
a) von den 256 möglichen Codes guckst du dir 2 Codes aus, die was 
Besonderes sein sollen. Der eine besondere Code ist das 
Synchronisierzeichen. Der andere besondere Code ist ein Anzeiger, daß 
nach ihm 2 Zeichen kommen, die jeweils ein Hex-Nibble enthalten, die im 
Empfänger zusammengesetzt werden müssen und damit ein übertragenes Byte 
ergeben. Damit sind dann auch die Codes der zwei besonderen Zeichen 
sendbar (eben in Form von 3 Bytes: Markierung, hex, hex).

W.S.

von Peter II (Gast)


Lesenswert?

W.S. schrieb:
> Zweitens: Was hier an Vorschlägen kam über Timeouts usw. ist Mumpitz.
> Schließlich reden wir hier über den Datenverkehr über einen UART,
> gelle? Und wenn man Daten mit einem PC austauscht, dann geht sowas wie
> Timeouts sowieso nicht, denn das Programm im PC läuft ja sowieso nur in
> den Zeitabschnitten, die es vom BS zugeteilt bekommt.

geht bei mir sehr gut. Und man muss IMMER mit Timeouts arbeiten.

Wenn der PC zum client eine Anfrage schickt und darauf eine Antwort 
erwarten - wie lange willst du ihn denn warten lassen ohne Timeout?

man muss also auf dem PC eine Timeout für eine Antwort festlegen, damit 
es irgendwann weiter geht. Und in dieser Zeit erfolgt keine 
Datenübertragung auf dem Bus (PC warten ja). Und diese Lücke kann der 
client nutzen um seine Packet-State-Machine zurückzusetzen. Denn nach 
der Lücke kommt auf jeden Fall der Anfang von einem Packet.

von Cyblord -. (cyblord)


Lesenswert?

W.S. schrieb:
> DruckPause schrieb:
>> Versteht ihr was ich meine?
>
> Ja. Du hast eine grundfalschen Denkansatz.
>
> Also erstens: Wenn du alle möglichen Bytes 0..255 für alles zuläßt, dann
> hast du garkeine Möglichkeit zu irgendwelcher Kontrolle, außer solchen
> Methoden, wie sie z.B. beim RDS Dekodieren angesagt sind, wo also über
> eine Vielzahl von empfangenen Bits ein bestimmtes Muster gesucht wird
> zur Synchronisation.

Oder Escapen, wie ich weiter oben vorgeschlagen hatte. GAR KEINE ist 
hier komplett falsch.

gruß cyblord

von Joachim (Gast)


Lesenswert?

W.S. schrieb:
> Der andere besondere Code ist ein Anzeiger, daß
> nach ihm 2 Zeichen kommen, die jeweils ein Hex-Nibble enthalten, die im
> Empfänger zusammengesetzt werden müssen und damit ein übertragenes Byte
> ergeben. Damit sind dann auch die Codes der zwei besonderen Zeichen
> sendbar (eben in Form von 3 Bytes: Markierung, hex, hex).
>
> W.S.

Hm, das mußt du nochmal erklären. Ich glaub das geht so nicht, zumindest 
nicht ohne Einschränkung. Ich stell mir vor: mein Sync-Zeichen ist 0x05, 
mein "2-Nibble-Anzeiger" ist 0x06. Jetzt möchte ich das Zeichen 0x56 
senden. Dann sieht mein Datenstrom nach deiner Idee doch so aus:

0x05 - 0x06 - 0x05 - 0x06
  |      |      |      '----- Nibble 2
  |      |      '------------ Nibble 1
  |      '------------------- 2-Nibble-Anzeiger
  '-------------------------- Sync
oder?

Dann hab ich aber zweimal hinereinander Sync und den 2-Nibble-Anzeiger. 
Und dann ist Feierabend. Geht das nicht nur dann, wenn meine beiden 
Sonderzeichen größer 0x0F sind?

Gruß,
Joachim

von Karl H. (kbuchegg)


Lesenswert?

Joachim schrieb:

> Hm, das mußt du nochmal erklären. Ich glaub das geht so nicht, zumindest
> nicht ohne Einschränkung.

Sagen wir so:
Du hast einen Fall konstruiert, bei dem diese Form des Escapens unsinnig 
ist.

> Ich stell mir vor: mein Sync-Zeichen ist 0x05,

Wähle ein anderes Sync-Zeichen. Zb 0xFF
(in der Praxis nicmmt man eines, von dem man erwartet, dass es in den 
Daten nicht nicht oft vorkommen wird

> mein "2-Nibble-Anzeiger" ist 0x06.

wähle 0xFE

> Jetzt möchte ich das Zeichen 0x56
Wäre in deinem Fall kein Problem, weil 0x56 bei dir weder Sync-Zeichen 
noch Escape Zeichen ist. Mit 0x56 machst du also gar nichts. Das wird so 
gesendet wie alles andere.

Aber sagen wir mal, ich will 0xFF in den Daten senden.

Anstelle von 0xFF wird dann (in diesem Schema) gesendet
0xFE 0x0F 0x0F

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Reinhard Kern schrieb:
>> Du willst also immer einen Fehler provozieren, damit das folgende Paket
>> synchronisiert werden kann? Wie verträgt sich das mit der von dir
>> (unnötigerweise) geforderten Effektivität?
>
> was für ein Fehler provozieren?
>
> Wenn nur das halbe Packet, wegen einen kabel fehler, nicht bei client
> ankommt hat nicht mit Fehler provozieren zu tun?

Und wie synchronisierst du dich neu in den Datenstrom?

Antwort bei dir: Indem ich den Sender zwinge, nach einem Telegram eine 
Pause einzulegen, damit ich den Anfang des Pakets erkennen kann. (und 
ich rede von einem ununterbrochenem Datenstrom).
Und da sorgst du dich um Effizenz? Effizenz in einem System, in dem du 
dem Sender vorschreibst, dass er Pausen machen muss?

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:

> Wenn der PC zum client eine Anfrage schickt und darauf eine Antwort
> erwarten - wie lange willst du ihn denn warten lassen ohne Timeout?
>
> man muss also auf dem PC eine Timeout für eine Antwort festlegen

das ist sowieso unbestritten.
Dass man Timeouts vorsehen muss, ist kein Streipunkt.

Nur wenn die Synchronisierung in deinem Protokoll einzig und alleine 
darauf beruhen, dass die Timeouts das regeln, dagegen hab ich einen 
Einwand.


> es irgendwann weiter geht. Und in dieser Zeit erfolgt keine
> Datenübertragung auf dem Bus (PC warten ja). Und diese Lücke kann der
> client nutzen um seine Packet-State-Machine zurückzusetzen. Denn nach
> der Lücke kommt auf jeden Fall der Anfang von einem Packet.

Ach?
Bei dir vielleicht.
Bei mir nicht.
Bei mir kann nach einer Lücke alles mögliche kommen, weil der Sender 
dauersendet und die Putzfrau das Kabel wieder eingesteckt hat. Und nu?

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Und wie synchronisierst du dich neu in den Datenstrom?
>
> Antwort bei dir: Indem ich den Sender zwinge, nach einem Telegram eine
> Pause einzulegen, damit ich den Anfang des Pakets erkennen kann. (und
> ich rede von einem ununterbrochenem Datenstrom).
> Und da sorgst du dich um Effizenz? Effizenz in einem System, in dem du
> dem Sender vorschreibst, dass er Pausen machen muss?

noch einmal:
  Der Sender muss warten weil er keine Antwort bekommen hat.

Ich habe nicht geschrieben das der Sender nach jeden Packet eine Pausen 
machen muss. Die pause ergibt sich weil der client nicht antwortet.


Diese geht nur wenn der Bus so ausgelegt ist, das der Master Anfrage 
schickt und dann auf eine Antwort wartet.

von Joachim (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Sagen wir so:
> Du hast einen Fall konstruiert, bei dem diese Form des Escapens unsinnig
> ist.

Hallo Karl Heinz!

Das ist schon richtig, mein Beispiel war aber mit Absicht so gewählt. 
Ich wollte nur sicher gehen bzw. zeigen, daß das ja irgendwie nicht mit 
beliebigen Zeichen geht. Darum hab ich ja geschrieben, daß die 
"Sonderzeichen" größer als 0x0F sein müssen.
So wie bei dir, mit den von dir gewählten Codes ist das natürlich 
möglich :) .

Ich wollte nur nicht, daß das jemand ausversehen so macht, wie in meinem 
Beispiel und sich dann wundert, daß nix geht :) .

Gruß,
Joachim

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Bei mir kann nach einer Lücke alles mögliche kommen, weil der Sender
> dauersendet und die Putzfrau das Kabel wieder eingesteckt hat. Und nu?

er sendet ja nicht dauert.  Er will ja eine Antwort haben, also wartet 
er auch mal. Und wenn jemand das Kabel reinsteck während er gerade 
sendet (damit kommt nur der Rest vom Packet an) dann bekommt er keine 
Antwort.

Das nächste Senden vom Master ist dann wieder ein vollständiges packet.

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.