Da die asynchrone serielle Kommunikation auch heutzutage unter elektronischen Geräten weit verbreitet ist, sind viele von uns meiner Meinung nach von Zeit zu Zeit auf eine solche Frage gestoßen. Stellen Sie sich ein elektronisches Gerät D
und einen Computer vor, PC
die mit einer seriellen Leitung (RS-232 oder ähnlich) verbunden sind und Informationen kontinuierlich austauschen müssen . Dh PC
sendet jeweils einen Befehlsrahmen X ms
und D
antwortet jeweils mit Statusbericht / Telemetrie-Rahmen Y ms
(Der Bericht kann als Antwort auf Anforderungen oder unabhängig gesendet werden - spielt hier keine Rolle). Die Kommunikationsrahmen können beliebige Binärdaten enthalten . Angenommen, die Kommunikationsrahmen sind Pakete fester Länge.
Das Problem:
Da das Protokoll fortlaufend ist, verliert die empfangende Seite möglicherweise die Synchronisation oder "verbindet" sich einfach mitten in einem laufenden gesendeten Frame, sodass sie einfach nicht weiß, wo der Start des Frames (SOF) ist. Wenn die Daten aufgrund ihrer Position relativ zum SOF eine andere Bedeutung haben, werden die empfangenen Daten möglicherweise für immer verfälscht.
Die gewünschte Lösung
Zuverlässiges Begrenzungs- / Synchronisationsschema zur Erkennung der SOF mit kurzer Wiederherstellungszeit (dh die Neusynchronisation sollte nicht länger als 1 Frame dauern).
Mir sind die vorhandenen Techniken bekannt (und ich verwende einige):
1) Header / Checksumme - SOF als vordefinierter Bytewert. Prüfsumme am Ende des Frames.
- Vorteile: Einfach.
- Nachteile: Nicht zuverlässig. Unbekannte Wiederherstellungszeit.
2) Bytefüllung:
- Vorteile: Zuverlässige, schnelle Wiederherstellung, kann mit jeder Hardware verwendet werden
- Nachteile: Nicht so geeignet für rahmenbasierte Kommunikation mit fester Größe
3) 9. Bitmarkierung - stellen Sie jedem Byte ein zusätzliches Bit voran, während SOF mit 1
und die Datenbytes mit gekennzeichnet sind 0
:
- Vorteile: Zuverlässige, schnelle Wiederherstellung
- Nachteile: Benötigt Hardware-Unterstützung. Wird von den meisten
PC
Hard- und Softwarekomponenten nicht direkt unterstützt .
4) Markierung des achten Bits - Art der Emulation des oben genannten, wobei das achte anstelle des neunten Bits verwendet wird, wodurch für jedes Datenwort nur 7 Bit übrig bleiben.
- Vorteile: Zuverlässige, schnelle Wiederherstellung, kann mit jeder Hardware verwendet werden.
- Nachteile: Erfordert ein Kodierungs- / Dekodierungsschema von / zur herkömmlichen 8-Bit-Darstellung bis / von 7-Bit-Darstellung. Etwas verschwenderisch.
5) Timeout-basiert - Nehmen Sie an, dass das SOF das erste Byte ist, das nach einer definierten Leerlaufzeit kommt.
- Vorteile: Kein Datenaufwand, einfach.
- Nachteile: Nicht so zuverlässig. Funktioniert nicht gut mit schlechten Zeitgebungssystemen wie beispielsweise Windows-PCs. Möglicher Overhead des Durchsatzes.
Frage: Welche anderen möglichen Techniken / Lösungen gibt es, um das Problem anzugehen? Können Sie auf die Nachteile in der obigen Liste verweisen, die leicht umgangen werden können, um sie zu entfernen? Wie entwerfen (oder würden) Sie Ihr Systemprotokoll?
quelle
Antworten:
Meiner Erfahrung nach verbringt jeder viel mehr Zeit mit dem Debuggen von Kommunikationssystemen als jemals erwartet. Daher empfehle ich dringend, dass Sie immer dann, wenn Sie eine Auswahl für ein Kommunikationsprotokoll treffen müssen, die Option auswählen, die das Debuggen des Systems erleichtert, wenn dies überhaupt möglich ist.
Ich empfehle Ihnen, ein paar benutzerdefinierte Protokolle zu entwerfen - es macht Spaß und ist sehr lehrreich. Ich empfehle Ihnen jedoch auch, sich die vorhandenen Protokolle anzuschauen. Wenn ich Daten von einem Ort zum anderen übertragen müsste, würde ich mich sehr bemühen, ein bereits vorhandenes Protokoll zu verwenden, für das bereits ein großer Teil der Zeit für das Debuggen aufgewendet wurde.
Wenn Sie Ihr eigenes Kommunikationsprotokoll von Grund auf neu schreiben, stoßen Sie mit hoher Wahrscheinlichkeit auf viele der gleichen Probleme, die jeder hat, wenn er ein neues Protokoll schreibt.
Bei Good RS232-basierten Protokollen für die Embedded-to-Computer-Kommunikation sind ein Dutzend Protokolle für eingebettete Systeme aufgeführt. Welche entsprechen Ihren Anforderungen am ehesten?
Selbst wenn es ein Umstand unmöglich machte, ein bereits vorhandenes Protokoll genau zu verwenden, ist es wahrscheinlicher, dass ich etwas schneller zum Laufen bringe, indem ich mit einem Protokoll beginne, das fast den Anforderungen entspricht, und es dann optimiere.
schlechte Nachrichten
Wie ich schon sagte :
Leider kann kein Kommunikationsprotokoll all diese nützlichen Funktionen bieten:
Es würde mich überraschen und freuen, wenn ein Kommunikationsprotokoll all diese Merkmale aufweisen könnte.
gute Nachrichten
Oft macht es das Debuggen sehr viel einfacher, wenn ein Mensch an einem Textterminal eines der Kommunikationsgeräte ersetzen kann. Dies erfordert, dass das Protokoll relativ zeitunabhängig gestaltet wird (kein Timeout während der relativ langen Pausen zwischen von einem Menschen eingegebenen Tastenanschlägen). Außerdem sind solche Protokolle auf die Arten von Bytes beschränkt, die für einen Menschen leicht einzugeben und dann auf dem Bildschirm zu lesen sind.
Einige Protokolle erlauben das Senden von Nachrichten entweder im "Text" - oder "Binär" -Modus (und erfordern, dass alle möglichen Binärnachrichten eine "äquivalente" Textnachricht enthalten, die dasselbe bedeutet). Dies kann das Debuggen erheblich vereinfachen.
Einige Leute scheinen der Meinung zu sein, dass die Beschränkung eines Protokolls auf die Verwendung nur der druckbaren Zeichen "verschwenderisch" ist, aber die Einsparungen bei der Debugging-Zeit machen es oftmals lohnenswert.
Wie Sie bereits erwähnt haben, ist es wahrscheinlich, dass der Empfänger beim ersten Einschalten eines Empfängers falsch synchronisiert wird, wenn Sie zulassen, dass das Datenfeld ein beliebiges Byte enthält, einschließlich des Anfangs- und Endbytes des Headers Was wie ein Start-of-Header-Byte (SOH) im Datenfeld in der Mitte eines Pakets aussieht. Normalerweise erhält der Empfänger eine nicht übereinstimmende Prüfsumme am Ende dieses Pseudopakets (das normalerweise in der Mitte eines zweiten realen Pakets liegt). Es ist sehr verlockend, einfach die gesamte Pseudonachricht (einschließlich der ersten Hälfte dieses zweiten Pakets) zu verwerfen, bevor nach dem nächsten SOH gesucht wird - mit der Folge, dass der Empfänger für viele Nachrichten nicht synchronisiert werden kann.
Wie alex.forencich betonte, ist es für den Empfänger viel besser, Bytes am Anfang des Puffers bis zum nächsten SOH zu verwerfen. Dadurch kann der Empfänger (nachdem er möglicherweise mehrere SOH-Bytes in diesem Datenpaket durchgearbeitet hat) das zweite Paket sofort synchronisieren.
Wie Nicholas Clark hervorhob, hat das Consistent-Overhead-Byte-Stuffing (COBS) einen festen Overhead, der sich gut für Frames mit fester Größe eignet.
Eine Technik, die oft übersehen wird, ist ein dediziertes Byte für die Frame-Ende-Markierung. Wenn der Empfänger während einer Übertragung eingeschaltet wird, kann der Empfänger mithilfe eines speziellen Frame-Ende-Markierungsbytes schneller synchronisieren.
Wenn ein Empfänger in der Mitte eines Pakets eingeschaltet wird und das Datenfeld eines Pakets zufällig Bytes enthält, die als Paketanfang erscheinen (der Beginn eines Pseudopakets), kann der Sender eine Reihe einfügen Die Anzahl der Markierungsbytes für das Ende des Frames nach diesem Paket, damit solche Pseudo-Start-of-Packet-Bytes im Datenfeld die sofortige Synchronisierung und korrekte Dekodierung des nächsten Pakets nicht beeinträchtigen - selbst wenn Sie extrem unglücklich sind und die Prüfsumme von diesem Pseudopaket erscheint richtig.
Viel Glück.
quelle
Byte-Stopfschemata haben im Laufe der Jahre für mich großartig funktioniert. Sie sind nett, weil sie einfach in Software oder Hardware zu implementieren sind, Sie können ein Standard-USB-zu-UART-Kabel zum Senden von Datenpaketen verwenden und Sie erhalten garantiert eine gute Bildqualität, ohne sich Sorgen machen zu müssen Auszeiten, Hot-Swapping oder ähnliches.
Ich würde für eine Byte-Füllmethode in Kombination mit einem Längenbyte (Packet Length Modulo 256) und einer CRC auf Paketebene eintreten und dann UART mit einem Paritätsbit verwenden. Das Längenbyte stellt die Erkennung von fehlgeschlagenen Bytes sicher, was mit dem Paritätsbit gut funktioniert (da die meisten UARTs alle Bytes löschen, die die Parität nicht erfüllen). Dann gibt Ihnen die CRC auf Paketebene zusätzliche Sicherheit.
Haben Sie sich das COBS-Protokoll angesehen? Es ist ein genialer Weg, Byte-Stuffing mit einem festen Overhead von 1 Byte pro 254 übertragenen Bytes durchzuführen (plus Framing, CRC, LEN usw.).
https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
quelle
Ihre Option Nr. 1, SOH plus Prüfsumme, ist zuverlässig und stellt sich beim nächsten unbeschädigten Frame wieder her.
Ich gehe davon aus, dass Sie entweder die Länge einer Nachricht bereits kennen oder die Länge in den Bytes unmittelbar nach SOH codiert ist. Die Prüfbytes werden am Ende der Nachricht angezeigt. Sie benötigen außerdem einen empfangsseitigen Puffer für die Daten, der mindestens so lang ist wie Ihre längste Nachricht.
Jedes Mal, wenn Sie ein SOH-Byte am Anfang des Puffers sehen, ist dies möglicherweise der Beginn einer Nachricht. Sie durchsuchen den Puffer, um den Prüfwert für diese Nachricht zu berechnen und festzustellen, ob er mit den Prüfbytes im Puffer übereinstimmt. Wenn ja, sind Sie fertig; Andernfalls verwerfen Sie Daten aus dem Puffer, bis Sie zum nächsten SOH-Byte gelangen.
Beachten Sie, dass eine Meldung, die tatsächlich Datenfehler aufweist, von diesem Algorithmus verworfen wird - aber Sie würden das wahrscheinlich trotzdem tun. Wenn Ihr Überprüfungsalgorithmus eine Vorwärtsfehlerkorrektur enthält, können Sie jede potenzielle Nachrichtenausrichtung auf korrigierbare Fehler überprüfen.
Wenn die Nachrichten eine feste Länge haben, können Sie ganz auf das SOH-Byte verzichten - testen Sie einfach JEDE mögliche Startposition auf einen gültigen Prüfwert.
Sie können auch auf den Prüfalgorithmus verzichten und nur das SOH-Byte beibehalten. Dadurch wird der Algorithmus jedoch weniger deterministisch. Die Idee ist, dass für gültige Nachrichtenausrichtungen der SOH immer am Anfang einer Nachricht angezeigt wird. Wenn Sie eine falsche Ausrichtung haben, ist es unwahrscheinlich , dass das nächste Byte im Datenstrom ein anderes SOH ist (abhängig davon, wie oft SOH in den Nachrichtendaten angezeigt wird). Sie können die gültigen SOH-Bytes nur auf dieser Basis auswählen. (So funktioniert das Framing bei synchronen Telekommunikationsdiensten wie T1 und E1.)
quelle
Eine Option, die nicht erwähnt, aber weit verbreitet ist (insbesondere im Internet), ist die ASCII / Text-Codierung (die meisten modernen Implementierungen setzen UTF-8 voraus). Nach meiner Erfahrung hassen es Hardware-Leute, dies zu tun, aber Software-Leute bevorzugen dies eher als fast alles andere (meistens aufgrund der Unix-Tradition, alles textbasiert zu machen).
Der Vorteil der Textkodierung besteht darin, dass Sie nicht druckbare Zeichen zum Einrahmen verwenden können. Am einfachsten wäre es zum Beispiel, wenn Sie so etwas wie
0x00
den Beginn eines Frames und das0xff
Ende eines Frames angeben .Ich habe zwei Möglichkeiten gesehen, wie Daten als Text codiert werden:
Wenn ein Hardware- / Montagetechniker dazu aufgefordert wird, wird dies höchstwahrscheinlich als Hex-Codierung implementiert. Dies konvertiert einfach die Bytes in ihre Hex-Werte in ASCII. Der Aufwand ist groß. Grundsätzlich übertragen Sie zwei Bytes für jedes tatsächliche Datenbyte.
Wenn ein Software-Typ dazu aufgefordert wird, wird es wahrscheinlich als Base64-Codierung implementiert. Dies ist die De-facto-Codierung des Internets. Wird von E-Mail-MIME-Anhängen bis zur URL-Datencodierung verwendet. Der Overhead beträgt genau 33%. Viel besser als einfache Hex-Codierung.
Alternativ können Sie auf Binärdaten vollständig verzichten und Text übertragen. In diesem Fall besteht die gebräuchlichste Technik darin, Daten mit Zeilenvorschub abzugrenzen (entweder nur
"\n"
oder"\r\n"
). NMEA (GPS), Modem AT-Befehle und Adventech ADAM-Sensoren sind einige der häufigsten Beispiele hierfür.Alle diese textbasierten Protokolle / Frames haben die folgenden Vor- und Nachteile:
Profi:
Con:
"0"
(0x30) statt vier Bytes (0x00000000) "komprimieren". )sprintf()
Funktion)Persönlich überwiegen für mich die Vor- und Nachteile. Die einfache Fehlersuche allein zählt als 5 Punkte (so dass ein Punkt alleine bereits beide Nachteile überwiegt).
Dann gibt es nicht allzu sorgfältig durchdachte Lösungen, die oft von Software-Entwicklern stammen: Senden Sie verschlüsselte Daten, ohne über Frames nachzudenken.
Ich musste in der Vergangenheit mit Hardware kommunizieren, die unformatiertes XML gesendet hat. Das XML war der ganze Rahmen, den es gab. Glücklicherweise ist es ziemlich einfach, die Rahmengrenzen anhand der
<xml></xml>
Tags zu bestimmen. Das große Manko für mich ist, dass es mehr als ein Byte für das Framing verwendet. Außerdem kann es vorkommen, dass der Frame selbst nicht repariert wird, da das Tag möglicherweise Attribute enthält.<tag foo="bar"></tag>
Sie müssten also im schlimmsten Fall puffern, um den Frame-Start zu finden.Kürzlich habe ich Leute gesehen, die angefangen haben, JSON über serielle Schnittstellen zu senden. Bei JSON ist Framing bestenfalls eine Vermutung. Sie haben nur das
"{"
(oder"["
) Zeichen, um einen Frame zu erkennen, aber sie sind auch in den Daten enthalten. Am Ende benötigen Sie also einen Parser für rekursive Abstammung (oder zumindest einen Klammerzähler), um den Frame zu ermitteln. Zumindest ist es trivial zu wissen, ob der aktuelle Frame vorzeitig endet"}{"
oder"]["
in JSON ungültig ist und somit anzeigt, dass der alte Frame beendet wurde und ein neuer Frame gestartet wurde.quelle
<
und>
als Begrenzer E-Mail verwendet Zeilenumbrüche. Hinweis: Ja, E-Mail ist ein ordnungsgemäß eingerahmtes FormatWas Sie als "X-te Bit-Markierung" bezeichnen, kann in andere Codes verallgemeinert werden, die die Eigenschaft haben, die Daten um einen konstanten Bruchteil zu erweitern, so dass einige Codewörter als Begrenzer verwendet werden können. Oft bieten diese Codes auch andere Vorteile. CDs verwenden eine Modulation von acht bis vierzehn , die eine maximale Lauflänge von 0 Bits zwischen jeweils 1 garantiert. Andere Beispiele umfassen Vorwärtsfehlerkorrektur-Blockcodes , die zusätzliche Bits verwenden, um Fehlererkennungs- und Korrekturinformationen zu codieren.
Ein anderes System, das Sie nicht erwähnt haben, ist die Verwendung von Out-of-Band-Informationen, z. B. eine Chipauswahlleitung, um Transaktionen oder Pakete abzugrenzen.
quelle
Eine weitere Option ist die sogenannte Liniencodierung . Die Leitungscodierung verleiht dem Signal bestimmte elektrische Eigenschaften, die die Übertragung erleichtern (Gleichstromausgleich und maximale Lauflänge garantiert), und sie unterstützt Steuerzeichen für die Rahmenbildung und die Taktsynchronisation. Leitungscodes werden in allen modernen seriellen Hochgeschwindigkeitsprotokollen verwendet - 10M-, 100M-, 1G- und 10G-Ethernet, Serial ATA, FireWire, USB 3, PCIe usw. Übliche Leitungscodes sind 8b / 10b , 64b / 66b und 128b / 130b. Es gibt auch einfachere Leitungscodes, die keine Rahmeninformationen liefern, sondern nur die Gleichstrombalance und die Taktsynchronisation. Beispiele hierfür sind Machester und NRZ. Sie möchten wahrscheinlich 8b / 10b verwenden, wenn Sie schnell synchronisieren möchten. Die anderen Leitungscodes sind nicht für eine schnelle Synchronisierung ausgelegt. Wenn Sie einen Leitungscode wie den oben genannten verwenden, müssen Sie zum Senden und Empfangen benutzerdefinierte Hardware verwenden.
Was Ihre Option 5 betrifft, soll die Standard-RS232-Serie das Senden und Empfangen von Unterbrechungen unterstützen, wenn die Leitung ein paar Byte lang inaktiv ist. Dies wird jedoch möglicherweise nicht auf allen Systemen unterstützt.
Im Allgemeinen ist die einfachste und zuverlässigste Framing-Methode Ihre Option 1 in Kombination mit einem Längenfeld und einer einfachen CRC- oder Prüfsummenroutine. Die Dekodierungsroutine ist einfach: Verwerfen Sie Bytes, bis Sie ein Startbyte erhalten, lesen Sie das Längenfeld, warten Sie auf den gesamten Frame, überprüfen Sie die Prüfsumme und behalten Sie sie bei, wenn sie gut ist. Wenn die Prüfsumme schlecht ist, verwerfen Sie zunächst die Bytes aus dem Puffer, bis Sie ein Startbyte erhalten, und wiederholen Sie diesen Vorgang. Das Hauptproblem bei dieser Technik besteht darin, ein Start-of-Frame-Byte zu finden, das eigentlich kein Start-of-Frame-Byte ist. Um dieses Problem zu beheben, besteht eine Technik darin, Bytes mit demselben Wert wie der Beginn des Frame-Bytes mit einem anderen Steuerzeichen zu maskieren und das maskierte Byte dann so zu ändern, dass es einen anderen Wert hat. In diesem Fall müssen Sie dasselbe auch mit dem neuen Steuerbyte tun.
quelle