Jan 22, 2024
Was schiefgehen könnte: Asynchronous Serial Edition
Es ist die einfachste Sache der Welt – einfache, unkomplizierte serielle Daten. Es ist das Fallback-Kommunikationsprotokoll für fast jedes eingebettete System da draußen, und deshalb ist es eines, das Sie wirklich wollen
Es ist die einfachste Sache der Welt – einfache, unkomplizierte serielle Daten. Es ist das Fallback-Kommunikationsprotokoll für fast jedes eingebettete System da draußen, und deshalb ist es eines, mit dem man unbedingt arbeiten möchte, wenn die Chips ausfallen. Und doch! Wenn Sie es am meisten brauchen, stellen Sie möglicherweise fest, dass selbst die asynchrone Serielle ein paar Stunden Debugging-Zeit kosten kann und ein paar graue Haare auf Ihrer Kopfhaut hervorruft.
In diesem Artikel werde ich die meisten (alle?) Dinge behandeln, die bei asynchronen seriellen Protokollen schief gehen können, und wie man diese nützlichste Datenübertragungsmethode diagnostiziert und debuggt. Das Ziel besteht darin, Ihnen bewusst zu machen, was schiefgehen kann, damit Sie das Problem in wenigen Minuten systematisch beheben können, anstatt ein paar Stunden zu verschwenden.
Stellen Sie sich vor, Sie haben acht Datenbits, die Sie mir elektronisch senden möchten. Wenn zwischen uns acht Drähte (plus Masse) liegen, können Sie einfach Ihre acht Schalter umlegen und an jeden Draht hohe oder niedrige Spannungen legen. Wenn ich am anderen Ende ein paar LEDs habe, lese ich einfach ab, welche aufleuchten, und fertig. Aber acht Drähte sind viel Kupfer. Stattdessen entscheiden Sie sich dafür, ein Bit nach dem anderen zu senden und dabei nur eine Leitung (plus Masse) zu verwenden. Das ist die Essenz der seriellen Kommunikation – Bits werden in Reihe gesendet, indem die Spannung an einem Kabel im Laufe der Zeit präzise variiert wird.
Klingt einfach, aber jetzt müssen wir einige Entscheidungen treffen. Wie schnell senden Sie jedes Bit? Stellt eine leuchtende LED eine 1 oder eine 0 dar? Woher weiß ich, wann Ihre Nachricht beginnt oder endet? Und schließlich benötigen wir zwei Kabel, wenn wir uns gegenseitig Daten senden wollen. Woher wissen wir, welches ich weitersende und welches Sie weitersenden? Bei jeder dieser Entscheidungen kann es passieren, dass etwas schiefgeht und sich Fehler einschleichen.
Der letzte Punkt, welche Kabel Daten in welche Richtung übertragen, sorgt überraschenderweise häufig für Verwirrung und ist daher ein guter Ausgangspunkt, um mit dem Debuggen zu beginnen.
„RX“ und „TX“ stehen für „receive“ bzw. „transmit“. Die meisten seriellen Kommunikationssysteme verfügen jeweils über eines. Oft läuft die Einrichtung etwa so ab: Sie verbinden „GND“ eines Geräts mit „GND“ des anderen Geräts. Möglicherweise teilen sie sich auch eine Stromschiene, sodass Sie „VCC“ des einen mit „VCC“ des anderen verbinden. Und dann verbinden Sie nach und nach „RX“ auf einem Gerät mit „RX“ auf dem anderen.
Und das ist Fehler Nummer eins. Beide Geräte erwarten den Empfang von Daten auf ihrer „RX“-Leitung, also sitzen beide einfach da und warten, während die beiden „TX“-Leitungen am Ende übereinander kommunizieren. Nein, der „richtige“ Weg besteht darin, den „RX“-Port eines Geräts mit dem „TX“-Port des anderen Geräts zu verbinden und umgekehrt. Das ist einfach logisch, oder? Um Sie daran zu erinnern, wird „TX“ manchmal mit „TXD“ beschriftet, wobei das „D“ für „Gerät“ steht, und das soll Sie daran erinnern, dass Sie die Dinge aus der Perspektive dieses Geräts betrachten.
Wie auch immer Sie es nennen, die Verbindung eines Ports namens „TX“ mit einem Port namens „RX“ verursacht Probleme in modernen CAD-Programmen, in denen Sie das Netzwerk und nicht die einzelnen Ports benennen. Wie nennt man ein Kabel, das die „GND“-Pins beider Geräte verbindet? „GND“ ist ein guter Name. Wie nennt man das Kabel, das „TX“ mit „RX“ verbindet? Wie wäre es mit dem, der „RX“ mit „TX“ verbindet? Es herrscht Verwirrung.
(Beachten Sie, dass SPI, das seine eigenen Probleme hat, auf die wir beim nächsten Mal eingehen werden, diese Leitungen „Master in, Slave out“ und „Master out, Slave in“ nennt. Die Leitungsnamen sind konsistent, und wenn Sie wissen, um welches Gerät es sich handelt Sie sehen sofort, in welche Richtung die Daten fließen. Das ist viel besser.)
Die erste Frage, die Sie sich beim Debuggen stellen sollten, ist also, ob Sie die Signalleitungen ordnungsgemäß gekreuzt haben oder nicht. Und selbst wenn ja, versuchen Sie trotzdem, sie auszutauschen, denn selbst wenn Sie nicht verwirrt sind, können Sie nicht sicher sein, dass der Techniker vor Ihnen nicht verwirrt war. (Wir haben es gesehen.)
Wir haben die Verkabelung geklärt. Wie sieht es also mit der Geschwindigkeit aus, mit der Sie Daten senden (und empfangen)? Das ist wichtig, denn wenn Sie eine Zeit lang eine hohe Spannung an Ihrem Kabel sehen, müssen Sie wissen, wie viele Bits diese „Währung“ darstellen sollte. Wenn ich Ihnen vier Nullen schicke, sehen Sie doppelt so lange eine konstante Spannung, als wenn ich Ihnen zwei schicke, aber wir müssen uns auf eine Zeitbasis einigen, damit Sie sicher sein können, dass ich nicht nur zwei oder acht Nullen geschickt habe .
Die Anzahl der pro Sekunde gesendeten Bitsignale wird Baudrate genannt und darauf müssen wir uns einfach einigen. Das bedeutet, dass sowohl der Sender als auch der Empfänger über ziemlich genaue Uhren verfügen müssen, damit sie die gleiche Zeit einhalten können.
Halten Sie Ausschau nach Baudraten wie 2400, 9600, 38400 und 115200. Wenn Sie die Baudrate Ihres Zielgeräts nicht kennen, kann es nicht schaden, sie alle auszuprobieren.
Es gibt einen cleveren Trick, den Sie oder Ihr Gerät anwenden können, wenn Sie die Baudrate nicht im Voraus kennen. Wenn Sie einige Datenbytes empfangen, können Sie verfolgen, wie lange die Spannung auf der Leitung konstant ist, und den kleinsten gemeinsamen Nenner ermitteln. Wenn Sie beispielsweise eine hohe Spannung für 208 μs, dann eine niedrige Spannung für 104 μs und schließlich eine hohe Spannung für 312 μs sehen, ist es eine gute Wette, dass eine Bitperiode 104 μs lang ist, was 9600 Baud entspricht. Wenn es eher 8 μs sind, sind das 115.200 Baud. Gelöst.
Sie haben die „RX“- und „TX“-Leitungen richtig verstanden und die Baudrate ermittelt, sodass Sie auf dem besten Weg sind, Daten zu empfangen und zu senden. Es stellt sich nun die Frage, wie es zu interpretieren ist. Anders ausgedrückt: Ist eine Hochspannung eine 1 oder eine Hochspannung eine 0?
Man würde nicht glauben, dass das verwirrend wäre, aber leider hat sich die Geschichte gegen uns verschworen. RS-232, der früher beliebteste serielle Standard, verwendete positive und negative Spannungen (von 3 V bis 15 V und von -3 V bis -15 V), um 0 bzw. 1 zu signalisieren. Ja, das ist richtig. Eine 1 wird mit einer negativen Spannung gesendet, die höhere Spannung entspricht einer 0.
Schnitt in die Gegenwart, wo einseitige Signalisierung häufiger vorkommt. Heutzutage wird die höhere Spannung (3,3 oder 5 V) als 1 und die niedrige Spannung (0 V) als Null angenommen. Die Antwort auf die Frage, wie man die Spannungen als Zahlen interpretiert, lautet also: Es kommt darauf an. Die moderne, aber immer noch RS-232-artige Signalisierung verwendet 0 V und 5 V als 1 und 0, während die serielle TTL-Signalisierung genau das Gegenteil bewirkt.
Die gute Nachricht ist, dass es möglich ist, diese beiden Fälle mit einer LED (oder einem Multimeter, wenn Sie möchten) zu unterscheiden. Sowohl RS-232- als auch TTL-Systeme beginnen damit, dass der „TX“-Port eines Geräts standardmäßig einen Pegel 1 sendet. Wenn die „TX“-Leitung im Leerlauf hoch ist, handelt es sich um ein TTL-System. Wenn der Leerlauf niedrig ist, wird höchstwahrscheinlich die RS-232-Polarität verwendet.
Wenn Sie ein FTDI-USB-zu-Seriell-Kabel oder eines der Klongeräte wie den CP2102 oder den … haben, sind Sie zu 100 % im TTL-Seriell-Land. Gute Nachrichten. Wenn Sie jedoch eine Schnittstelle zu einem anderen Gerät benötigen, das RS-232-Pegel verwendet, müssen Sie noch ein wenig arbeiten.
Hier ist eine RS-232-zu-TTL-Konverterschaltung, die für bescheidene Baudraten funktioniert und sogar die Spannungspegelverschiebung für Sie übernimmt. Sie können also Ihre 3,3 V empfindlichen ESP8266-Schaltkreise an einen alten Zeilendrucker mit -15 V bis 15 V anschließen und alles wird einwandfrei funktionieren. Dies ist eines für Ihren Werkzeuggürtel. Es ist nicht streng konform, da es nicht auf -12 V (oder was auch immer) umschaltet, aber es sorgt für die richtige Polarität und funktioniert mit den meisten Geräten.
Wenn Sie eine Schnittstelle zu alten RS-232-Geräten benötigen, können Sie sich einen Chip (MAX-232 oder gleichwertig) besorgen, der die höheren Spannungen für Sie erzeugt. Wenn Sie einen RS-232-Konverter aufbrechen, sehen Sie manchmal einen seriellen USB-TTL-Chip gepaart mit einem MAX-232. (Die billigen invertieren einfach das Signal und sind nicht besser als die obige Zwei-Transistor-Schaltung. Sie wurden gewarnt.)
Sie können nicht einfach Spannungen über das Kabel senden. Sie müssen wissen, wann das Signal beginnt und endet und nach welchen Daten Sie suchen müssen. Zusammenfassend wird dies als Framing bezeichnet. Die meisten seriellen Systeme verwenden die gleichen „8N1“-Frames, aber wenn dies nicht der Fall ist, lohnt es sich, darüber Bescheid zu wissen. Diese drei Zeichen entsprechen der Anzahl der gleichzeitig gesendeten Bits, dem Paritätsbit bzw. der Anzahl der Stoppbits. Nehmen wir das auseinander.
Die Anzahl der pro Paket gesendeten Datenbits ist selbsterklärend und die meisten seriellen Protokolle senden Daten byteweise, sodass dies normalerweise kein Problem darstellt. Aber auf wirklich alten Geräten sieht man manchmal sieben Bits – ASCII verwendet schließlich nur sieben Bits. Wie auch immer, die Anzahl der Bits pro Paket beträgt „8“ in „8N1“.
Denken wir nun über die „Start“- und „Stopp“-Bits nach. Da der „TX“-Port des sendenden Geräts (und der „RX“-Port des Empfängers) im Leerlauf-High (für TTL) ist, können Sie Ihre Datenübertragung nicht mit einer 1 beginnen – wie würden Sie feststellen, ob sie gesendet wurde? Ein immer niedriges Startbit beginnt also mit dem Datenpaket.
Wenn Sie ein Byte senden, endet es für lange Zeit mit der Leitung hoch, und es ist ziemlich einfach zu erkennen, wo es endet. Wenn Sie zwei Bytes senden und das zweite mit einem niedrigen Startbit beginnt, müssen Sie am Ende des ersten Pakets mindestens ein hohes Stoppbit senden. (Wenn Sie ein Byte senden, verschmilzt das Stoppbit mit dem Hintergrund-High-Zustand der TX-Leitung.) Wenn Sie mitzählen, sind das mindestens zehn Signale, um acht Bits zu senden: ein Startbit und mindestens ein Stoppbit . Einige Systeme senden jedoch zwei Stoppbits, sodass Sie dies erneut angeben müssen. Die Anzahl der Stoppbits ist die „1“ in „8N1“.
Hier wird die Nachricht „*\n“ – Stern- und Zeilenvorschub (ASCII 10) – mit einem bzw. zwei Stoppbits gesendet. Im Binärformat ist das 00101010 00001010. Die Stoppbits (eins bzw. zwei) erscheinen zwischen diesen beiden Bytes, und vor jedem gibt es auch ein einzelnes Startbit. Versuchen Sie, das herauszufinden.
Und das bringt uns zum „Paritätsbit“ in der Mitte, das hoch oder niedrig ist, je nachdem, ob die Anzahl der Einsen in den Daten gerade oder ungerade ist. Und die Wahl, gerade als 1 oder ungerade als 1 zu kodieren, ist willkürlich. Um das Paritätsbit als Fehlererkennungsmechanismus nutzen zu können, müssen Sie wissen, welches welches ist. Daher wird es als „N“ für keine Parität oder „E“ oder „O“ für gerade bzw. ungerade Parität angegeben. Wenn ein Paritätsbit vorhanden ist, wird es nach den Daten direkt vor dem/den Stoppbit(s) hinzugefügt. Das Paritätsbit macht die Anzahl der Einsen im Byte gerade bzw. ungerade, was dem Fehlererkennungsschema entspricht. Wenn Sie gerade Parität verwenden und drei Einsen einschließlich des Paritätsbits sehen, wissen Sie, dass ein Übertragungsfehler vorliegt.
Es gibt noch eine letzte Verwirrung, die Sie glücklicherweise fast nie sehen werden: das Problem der Endianness. Die seriellen numerischen Daten können entweder mit dem niedrigstwertigen Bit zeitlich zuerst oder mit dem höchstwertigen Bit gesendet werden. Die gute Nachricht ist, dass bei seriellen TTL- und RS-232-Daten fast immer das niederwertigste Bit zuerst (oder „Little-Endian“) gesendet wird, aber einige andere serielle Protokolle, wie etwa die Internetprotokolle, senden ihre seriellen Daten zuerst mit dem Big-End. Die schlechte Nachricht ist, dass Zielfernrohre eingehende Daten von links nach rechts anzeigen und wir Zahlen mit dem höchstwertigen Bit zuerst schreiben, sodass Sie beim Lesen der Zielfernrohraufnahmen die Bitmuster in Ihrem Kopf umkehren müssen. (Diese vier Nullen im Zeilenvorschubzeichen sollen Ihnen bei der Orientierung helfen.)
Mittlerweile haben Sie alle Signale geklärt. Man könnte meinen, dass nichts mehr schief gehen könnte. Aber warte! Da sich die serielle Kommunikation im Laufe der Zeit weiterentwickelt hat, gibt es zwei (vielleicht drei) Möglichkeiten, das Ende einer Datenzeile zu signalisieren. Das Problem mit den Zeilenenden ist bekannt, wenn Sie Textdateien auf Unix-, Windows- und älteren MacOS-Rechnern kopiert haben. Jeder verwendet (natürlich) einen anderen Standard. Die Verwirrung zwischen diesen drei Traditionen hat auch die Welt der eingebetteten Geräte erfasst. Wenn das empfangende Programm auf eines davon wartet und Sie das andere senden, weiß es nicht, wann Sie fertig sind, und bleibt einfach stehen.
Die Kurzfassung besagt, dass Sie möglicherweise einen Zeilenvorschub (LF, ASCII 10) oder einen Wagenrücklauf (CR, ASCII 13) oder beides (CR+LF) senden müssen, bevor der andere Endpunkt antwortet. Bei den meisten Terminalprogrammen können Sie dies sowohl für das Senden als auch für den Empfang im Handumdrehen festlegen, so dass eine manuelle Fehlerbehebung keine große Sache ist. Wenn Sie sich jedoch bereits nicht sicher sind, was Ihr Mikrocontroller-Code tut, und nichts davon sehen können, was Sie optimieren können, kommt Ihnen möglicherweise nicht der Gedanke, dass Sie ein Problem mit dem Zeilenende haben. Und natürlich hindert niemanden daran, sein eigenes spezifisches Zeilenendezeichen in seinem Protokoll zu verwenden. Seufzen.
Damit ist unser Leitfaden zur Fehlerbehebung bei seriellen Leitungen abgeschlossen, und ich habe so ziemlich alle möglichen Variablen behandelt: die richtigen Leitungen finden, die richtige Baudrate auswählen, die Leitungspolarität (TTL- oder RS-232-Stil) herausfinden, Daten Länge, Stoppbits, Parität und das Zeilenende. Das ist eine Menge, die auf einmal schief gehen kann, wenn man nur versucht, ein paar Daten aus einem undurchsichtigen Mikrocontrollersystem herauszuholen. Wenn Sie jedoch alle möglichen Faktoren kennen, haben Sie einen Halt – eine Checkliste, mit der Sie sicherstellen können, dass alles so funktioniert, wie Sie es sich vorstellen.
Meistens ist es nicht so schlimm. Bei einer der Standard-Baudraten werden Sie auf 8N1 stoßen. Stellen Sie sicher, dass Ihre Drähte gekreuzt sind, und testen Sie die Spannungen auf den Übertragungsleitungen, um die Parität festzustellen. Dann können Sie mit verschiedenen Baudraten herumspielen. Wenn Sie dadurch nicht gerettet werden, versuchen Sie es mit den Zeilenenden. Und wenn Sie immer noch nicht weiterkommen, holen Sie das Zielfernrohr heraus und untersuchen Sie die Signale.
Ich hoffe, das hilft und viel Spaß beim Debuggen! Als nächstes nehmen wir uns SPI vor.