Wie ausfallsicher ist \ n \ r als Stoppbytes?


8

In meiner UART-Kommunikation muss ich das Startbyte und das Stoppbyte der gesendeten Nachricht kennen. Das Startbyte ist einfach, das Stoppbyte jedoch nicht so sehr. Ich habe am Ende meiner Nachricht zwei Stoppbytes implementiert, nämlich \ n und \ r (10 und 13 Dezimalstellen). UART funktioniert nur mit Byte-Werten von 0 bis 255. Wie ausfallsicher ist das? Ich kann mir mit geringer Wahrscheinlichkeit vorstellen, dass meine Nachricht die Werte "10 und 13" nacheinander enthält, wenn sie nicht die Stoppbytes sind.

Gibt es einen besseren Weg, dies umzusetzen?


7
Um beliebige Daten zu senden, müssen Sie entweder Pakete oder Byte-Stuffing verwenden. In Ihrem Fall beträgt die Wahrscheinlichkeit, dass das Muster an einer bestimmten Stelle erscheint, 1/65536. Was zu 1 wird, wenn Sie einen ausreichend langen zufälligen Datenstrom haben.
Oldfart

4
Können Sie bitte den Kontext angeben? Stoppbits sind Teil der UART-Kommunikation, aber Stoppbytes? Dies klingt nach einem reinen Softwareproblem und hängt davon ab, was von Sender und Empfänger vereinbart wurde.
Warren Hill

2
@MariusGulbrandsen Wenn Ihre Daten wirklich willkürlich sind und nicht ausschließlich aus Text bestehen (denken Sie an ASCII), funktioniert die Nullterminierung nicht. Sie müssen ein Paket implementieren.
RamblinRose

4
Übrigens: Es ist üblich, den Wagenrücklauf vor den Zeilenvorschub zu stellen : "\x0D\x0A".
Adrian McCarthy

3
@AdrianMcCarthy Ich denke, der Sinn der Umkehrung besteht darin, die Wahrscheinlichkeit zu minimieren, dass es sich um eine gültige Sequenz handelt. Das heißt, zwei Windows-Zeilenenden in einer Reihe würden Ihnen geben, \r\n\r\nwelche die \n\rSequenz in der Mitte enthält ...
Mike Caron

Antworten:


14

Es gibt verschiedene Möglichkeiten, dies zu verhindern:

  • Stellen Sie sicher, dass Sie in Ihren regulären Nachrichten niemals eine 10/13-Kombination senden (also nur als Stoppbytes). ZB 20 21 22 23 24 25 senden:

20 21 22 23 24 25 10 13

  • Escape 10 und 13 (oder alle Nicht-ASCII-Zeichen mit einem Escape-Zeichen, z. B. 20 21 10 13 25 26 senden: (siehe Kommentar von / Credits für: DanW)

20 21 1b 10 1b 13 25 26

  • Definieren Sie beim Senden von Nachrichten ein Paket. Wenn Sie beispielsweise eine Nachricht 20 21 22 23 24 25 senden möchten, fügen Sie stattdessen die Anzahl der zu sendenden Bytes hinzu. Das Paket lautet also:

<nr_of_data_bytes> <data>

Wenn Ihre Nachrichten maximal 256 Byte groß sind, senden Sie:

06 20 21 22 23 24 25

Sie wissen also nach dem Empfang von 6 Datenbytes, dass dies das Ende ist. Sie müssen danach keine 10 13 senden. Und Sie können 10 13 innerhalb einer Nachricht senden. Wenn Ihre Nachrichten länger sein können, können Sie 2 Bytes für die Datengröße verwenden.

Update 1: Eine andere Möglichkeit, Pakete zu definieren

Eine andere Alternative besteht darin, Befehle zu senden, die eine bestimmte Länge haben und viele Abweichungen aufweisen können, z

10 20 30 (Befehl 10, der immer 2 Datenbytes enthält)

11 30 40 50 (Befehl 11, der immer 3 Datenbytes enthält)

12 06 10 11 12 13 14 15 (Befehl 12 + 1 Byte für die Anzahl der folgenden Datenbytes)

13 01 02 01 02 03 ... (Befehl 13 + 2 Bytes (01 02 für 256 + 2 = 258 Datenbytes, die folgen)

14 80 90 10 13 (Befehl 14, gefolgt von einer ASCII-Zeichenfolge, die mit 10 13 endet)

Update 2: Schlechte Verbindungs- / Byteverluste

Alle oben genannten Funktionen funktionieren nur, wenn die UART-Zeile Bytes korrekt sendet. Wenn Sie zuverlässigere Versandmethoden verwenden möchten, gibt es auch viele Möglichkeiten. Nachfolgend einige:

  1. Senden einer Prüfsumme innerhalb des Pakets (überprüfen Sie Google auf CRC: Cyclic Redundancy Check). Wenn der CRC in Ordnung ist, weiß der Empfänger, dass die Nachricht in Ordnung gesendet wurde (mit hoher Wahrscheinlichkeit).
  2. Wenn eine Nachricht erneut gesendet werden muss, muss ein Bestätigungsmechanismus (ACK / Antwort) verwendet werden (z. B. der Absender sendet etwas, der Empfänger empfängt beschädigte Daten, sendet einen NACK (nicht bestätigt), der Absender kann dann erneut senden.
  3. Zeitüberschreitung: Falls der Empfänger nicht rechtzeitig eine Bestätigung oder einen NACK erhält, muss eine Nachricht erneut gesendet werden.

Beachten Sie, dass alle oben genannten Mechanismen einfach oder so kompliziert sein können, wie Sie es möchten (oder müssen). Beim erneuten Senden einer Nachricht ist auch ein Mechanismus zum Identifizieren von Nachrichten erforderlich (z. B. Hinzufügen einer Sequenznummer zum Paket).


1
"Stellen Sie sicher, dass Sie in Ihren regulären Nachrichten niemals eine 10/13-Kombination senden (also nur als Stoppbytes)." - Sie haben nicht gesagt , wie die Daten zu senden , die sich eine 10/13 Kombination enthalten - Sie müssen es entkommen. "20 10 13 23 10 13" könnte also als "20 1b 10 1b 13 23" mit 1b als Fluchtzeichen gesendet werden.
Dan W

1
Beachten Sie, dass Sie bei Verwendung eines vorgeschlagenen Längenfelds Probleme bekommen, wenn Ihre serielle Verbindung fehlerhaft ist und ein einzelnes Byte verliert. Alles wird nicht mehr synchron sein.
Jonas Schäfer

@DanW Wenn Sie das erste oder 2 Byte als Anzahl der Datenbytes verwenden, spielt es keine Rolle, ob 10 oder 13 Teil dieser Daten sind ... 20 10 13 23 10 13 kann also als 06 20 10 13 23 gesendet werden 10 13 wobei 06 die Anzahl der folgenden Datenbytes ist.
Michel Keijzers

@MichelKeijzers - ja, aber das ist die zweite Lösung, die Sie erwähnen. Bei Ihrer ersten Lösung fehlt eine Erklärung der Escape-Sequenzen, um zu verhindern, dass die Stoppbytes übertragen werden.
Dan W

Beide Ansätze funktionieren und werden häufig verwendet, haben jedoch unterschiedliche Vor- und Nachteile, die Sie bei Bedarf hinzufügen können, obwohl dies über das hinausgeht, was das OP verlangt.
Dan W

13

Wie ausfallsicher ist \ n \ r als Stoppbytes?

Wenn Sie senden, senden Sie beliebige Daten -> wahrscheinlich nicht ausfallsicher genug.

Eine übliche Lösung ist die Verwendung von Escape:

Definieren wir, dass die Zeichen 0x02 (STX - Frame-Start) und 0x03 (ETX - Frame-Ende) innerhalb des übertragenen Datenstroms eindeutig sein müssen. Auf diese Weise können Anfang und Ende einer Nachricht sicher erkannt werden.

Wenn eines dieser Zeichen innerhalb des Nachrichtenrahmens gesendet werden soll, wird es durch das Präfixieren eines Escapezeichens (ESC = 0x1b) und Hinzufügen von 0x20 zum ursprünglichen Zeichen ersetzt.

Originalzeichen ersetzt durch

0x02 -> 0x1b 0x22  
0x03 -> 0x1b 0x23  
0x1b -> 0x1b 0x3b  

Der Empfänger kehrt diesen Vorgang um: Jedes Mal, wenn er ein Escape-Zeichen empfängt, wird dieses Zeichen gelöscht und das nächste Zeichen von 0x20 subtrahiert.

Dies erhöht nur den Verarbeitungsaufwand, ist jedoch zu 100% zuverlässig (vorausgesetzt, es treten keine Übertragungsfehler auf, die Sie überprüfen können / sollten, indem Sie zusätzlich einen Prüfsummenmechanismus implementieren).


1
Gute Antwort. Das für ASCII-Protokolle verwendete allgemeine Escape-Zeichen war '\x10'DLE (Data Link Escape). Einige der Wikipedia-Seiten weisen darauf hin, dass DLE häufig umgekehrt verwendet wurde: Das nächste Byte war eher ein Steuerzeichen als ein Datenbyte. Nach meiner Erfahrung ist das im Allgemeinen die entgegengesetzte Bedeutung für eine Flucht.
Adrian McCarthy

2
Eine Sache, auf die Sie hier achten sollten, ist, dass sich Ihre Puffergröße im schlimmsten Fall verdoppelt. Wenn der Speicher sehr knapp ist, ist dies möglicherweise nicht die beste Lösung.
TechnoSam

1
@Rev Was ist der Grund für das Hinzufügen von 0x20 zum Originalcharakter? Würde das Fluchtschema ohne das nicht genauso gut funktionieren?
Nick Alexeev

1
@NickAlexeev: Es ist einfacher / schneller, die tatsächlichen Rahmengrenzen zu identifizieren, wenn Sie ein anderes Vorkommen der reservierten Zeichen aus dem Stream entfernen. Auf diese Weise können Sie den Frame-Empfang und das Frame-Parsing (einschließlich des Un-Escaping) trennen. Dies kann besonders relevant sein, wenn Sie einen sehr langsamen Controller ohne FIFO und / oder hohe Datenraten haben. Sie können also einfach die eingehenden Bytes (zwischen STX / ETX) bei ihrem Eintreffen in den Frame-Puffer kopieren, den Frame als abgeschlossen markieren und die Verarbeitung mit niedrigerer Priorität durchführen.
Rev1.0

@TechnoSam: Guter Punkt.
Rev1.0

5

Sie wissen, ASCII hat bereits Bytes für diese Funktionen.

  • 0x01: Beginn der Überschrift - Startbyte
  • 0x02: Beginn des Textes - Ende der Header, Beginn der Nutzlast
  • 0x03: Ende des Textes - Ende der Nutzlast
  • 0x04: Übertragungsende - Stoppbyte
  • 0x17: Ende des Übertragungsblocks - Nachricht wird im nächsten Block fortgesetzt

Es enthält auch Codes für verschiedene Zwecke innerhalb der Nutzlast.

  • 0x1b: Escape (Escape des nächsten Zeichens - Verwendung in der Nutzlast, um anzuzeigen, dass das nächste Zeichen nicht zu den Strukturen gehört, die die in Ihrem Protokoll verwendeten Codes beschreiben)
  • 0x1c, 0x1d, 0x1e, 0x1f: Datei-, Gruppen-, Datensatz- und Einheitentrennzeichen - wird als gleichzeitiges Stopp- und Startbyte für Teile hierarchischer Daten verwendet

Ihr Protokoll sollte die feinste Granularität von ACK (0x06) und NAK (0x15) angeben, damit negativ bestätigte Daten erneut übertragen werden können. Bis zu dieser feinsten Granularität ist es ratsam, unmittelbar nach einem (nicht entkappten) Startindikator ein Längenfeld zu haben, und (wie in anderen Antworten erläutert) ist es ratsam, jedem (nicht entkappten) Stoppindikator einen CRC zu folgen.


Ich werde beliebige Daten senden. Ich denke, es war verwirrend, "\ n \ r" in meiner Frage zu verwenden, wenn ich keine ASCII-Daten sende. Obwohl mir diese Antwort gefällt, ist sie sehr informativ beim Senden von ASCII über UART
CK am

@MariusGulbrandsen: Solange Ihr Protokoll festlegt, wo sich die Nutzdaten befinden und welche Codes in den einzelnen Nutzdatenabschnitten maskiert werden müssen, können Sie alles senden, nicht nur Textdaten.
Eric Towers

4

UART ist von Natur aus nicht ausfallsicher - wir sprechen hier von der Technologie der 1960er Jahre.

Die Ursache des Problems liegt darin, dass UART nur einmal pro 10 Bit synchronisiert wird, sodass zwischen diesen Synchronisierungsperioden viel Kauderwelsch übertragen werden kann. Im Gegensatz zu beispielsweise CAN, das jedes einzelne Bit mehrmals abtastet.

Jeder in den Daten auftretende Doppelbitfehler beschädigt einen UART-Frame und wird unentdeckt übergeben. Bitfehler in Start / Stopp-Bits können in Form von Überlauffehlern erkannt werden oder nicht.

Unabhängig davon, ob Sie Rohdaten oder Pakete verwenden, besteht daher immer die Wahrscheinlichkeit, dass durch EMI verursachte Bitflips zu unerwarteten Daten führen.

Es gibt zahlreiche Möglichkeiten der "traditionellen UART-Quacksalberei", um die Situation geringfügig zu verbessern. Sie können Synchronisationsbytes, Synchronisationsbits, Paritätsbits und Doppelstoppbits hinzufügen. Sie können Prüfsummen hinzufügen, die die Summe aller Bytes zählen (und diese dann invertieren - warum nicht), oder Sie können die Anzahl der binären als Prüfsumme zählen. All dies ist weit verbreitet, äußerst unwissenschaftlich und mit einer hohen Wahrscheinlichkeit fehlender Fehler. Aber das war es, was die Leute von 1960 bis 1990 taten und vieles mehr seltsame Dinge wie diese leben heute weiter.

Der professionellste Weg, um mit der sicheren Übertragung über UART umzugehen, ist eine 16-Bit-CRC-Prüfsumme am Ende des Pakets. Alles andere ist nicht sehr sicher und es besteht eine hohe Wahrscheinlichkeit, dass Fehler fehlen.

Auf Hardwareebene können Sie dann das Differential RS-422 / RS-485 verwenden, um die Robustheit des Getriebes drastisch zu verbessern. Dies ist ein Muss für eine sichere Übertragung über größere Entfernungen. UART auf TTL-Ebene sollte nur für die Kommunikation an Bord verwendet werden. RS-232 sollte nicht für andere Zwecke als zur Abwärtskompatibilität mit alten Sachen verwendet werden.

Insgesamt ist Ihr Fehlererkennungsmechanismus umso effektiver, je näher er an der Hardware liegt. In Bezug auf die Effektivität addieren sich die Differenzsignale am meisten, gefolgt von der Überprüfung auf Rahmen- / Überlauffehler usw. CRC16 fügt einige hinzu, und dann fügt "traditionelle UART-Quacksalberei" ein wenig hinzu.


7
Dieser Rat ist ziemlich tangential - Sie haben die gestellte Frage nicht wirklich beantwortet. Insbesondere können Ihre vorgeschlagenen Lösungen andere Probleme lösen, aber sie lösen nicht das Grundproblem der Frage auf dieser Seite , bei der es sich um eine Verwechslung zwischen Framing Byes und Payload Byes handelt. Ihr Vorschlag würde höchstens gültige Daten ablehnen , in die ein Framing-Byte aufgrund eines CRC oder eines ähnlichen Fehlers eingebettet ist, ohne dass dies kommuniziert werden kann.
Chris Stratton

3
In der Tat macht diese Antwort es noch schlimmer. Das Original hatte nur Datenbytes und Stoppbytes. Dies fügt eine dritte Kategorie hinzu, CRC-Bytes. Und wie hier dargestellt, können diese jeden Wert annehmen, einschließlich {10,13}.
MSalters

1
@MSalters: Der CRC kann ASCII-codiert hexadezimal sein, um dieses Problem zu vermeiden. Ein weiterer Trick, den ich bei RS485 gesehen habe, besteht darin, Bit 7 im Start- / Adressbyte zu setzen.
Transistor

Zu "CAN, das jedes einzelne Bit mehrmals abtastet." : Die tatsächliche Abtastung des Bitwerts erfolgt nur einmal pro Bit. Worauf beziehen Sie sich hier? Eine Art Fehlerprüfung, wie vom Absender? Uhrensynchronisation?
Peter Mortensen

Das Invertieren der Prüfsumme wurde durchgeführt, so dass das Summieren des gesamten Datenblocks zu einer Null führen würde, die etwas einfacher zu codieren und etwas schneller auszuführen ist. Außerdem ist CRC viel besser, als Sie es sich vorstellen. Schlagen Sie es in der Wikipedia nach.
Werkzeugschmiede

0

... Ich kann mir mit geringer Wahrscheinlichkeit vorstellen, dass meine Nachricht die Werte "10 und 13" nacheinander enthält, wenn sie nicht die Stoppbytes sind.

Eine Situation, in der ein Teil der Daten gleich der Abschlusssequenz ist, sollte beim Entwerfen des Formats eines seriellen Datenpakets berücksichtigt werden. Eine andere zu berücksichtigende Sache ist, dass jedes Zeichen während der Übertragung beschädigt werden oder verloren gehen kann. Ein Startzeichen, ein Stoppzeichen, ein Datennutzlastbyte, eine Prüfsumme oder ein CRC-Byte sowie ein Vorwärtsfehlerkorrekturbyte sind nicht immun gegen Korruption. Der Framing-Mechanismus muss erkennen können, wenn ein Paket beschädigte Daten enthält.

Es gibt verschiedene Möglichkeiten, dies alles anzugehen.

Ich gehe davon aus, dass Pakete nur mit den seriellen Bytes umrahmt werden. Handshake-Linien werden nicht zum Einrahmen verwendet. Zeitverzögerungen werden nicht für die Gestaltung verwendet.

Paketlänge senden

Senden Sie die Länge des Pakets am Anfang anstelle des Abschlusszeichens am Ende.

Profis: Payload wird in einem effizienten Binärformat gesendet.

Nachteile: Sie müssen die Paketlänge zu Beginn der Übertragung kennen.

Entkomme den Sonderzeichen

Entfliehen Sie beim Senden der Nutzdaten den Sonderzeichen. Dies wurde bereits in einer früheren Antwort erläutert .

Vorteile: Der Absender muss die Länge des Pakets zu Beginn der Übertragung nicht kennen.

Nachteile: Etwas weniger effizient, je nachdem, wie viele Nutzlastbytes maskiert werden müssen.

Payload-Daten sind so codiert, dass sie keine Start- und Stoppzeichen enthalten können

Die Nutzdaten des Pakets sind so codiert, dass sie keine Start- oder Stoppzeichen enthalten können. Normalerweise erfolgt dies durch Senden von Nummern als ASCII- oder Hex-ASCII-Darstellung.

Profis: Mit gängigen Terminalprogrammen lesbar. Es ist kein Code erforderlich, um das Entkommen zu handhaben. Sie müssen die Länge des Pakets zu Beginn der Übertragung nicht kennen

Nachteile: Geringere Effizienz. Für ein Byte Nutzdaten werden mehrere Bytes gesendet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.