Wie funktioniert die serielle Kommunikation auf dem Arduino?

16

In Bezug auf die Arduino Uno, Mega2560, Leonardo und ähnliche Boards:

  • Wie funktioniert die serielle Kommunikation?
  • Wie schnell ist seriell?
  • Wie verbinde ich einen Sender mit einem Empfänger?

Bitte beachten Sie: Dies ist eine Referenzfrage.

Nick Gammon
quelle
Dies ist möglicherweise für Puffer auf beiden Seiten eines Nano interessant, der mit einem Raspian-System verbunden ist, auf dem ein Python-Datenlogger ausgeführt wird, und der nur ein reguläres USB-Programmierkabel zwischen den beiden verwendet: arduino.stackexchange.com/questions/11710/…
SDsolar

Antworten:

16

Asynchrone serielle Kommunikation (normalerweise als serielle Kommunikation bezeichnet) wird zum Senden von Bytes von einem Gerät zu einem anderen verwendet. Ein Gerät kann eines oder mehrere der folgenden sein:

  • Arduino
  • PC
  • Geographisches Positionierungs System
  • RFID-Kartenleser
  • LCD Bildschirm
  • Modem
  • Andere

Taktrate und Abtastung von Daten

Im Gegensatz zu SPI / USB / I2C hat die serielle Kommunikation kein Taktsignal. Der Abtasttakt ist eine vereinbarte Abtastrate (bekannt als die Baudrate). Sowohl der Sender als auch der Empfänger müssen so konfiguriert sein, dass sie dieselbe Rate verwenden. Andernfalls empfängt der Empfänger bedeutungslose Daten (da die Bits nicht mit derselben Rate abgetastet werden, mit der sie gesendet wurden).

Die Übertragung ist asynchron, was im Grunde bedeutet, dass Bytes jederzeit mit unterschiedlichen Lücken zwischen ihnen gesendet werden können. Diese Grafik zeigt ein einzelnes Byte, das gesendet wird:

Serielle Kommunikation - Senden eines Bytes

Die obige Grafik zeigt den übertragenen Buchstaben 'F'. In ASCII ist dies 0x46 (in hex) oder 0b01000110 (in binär). Die am wenigsten signifikant (niedrige Ordnung) Bit wird zuerst übertragen, so dass in der obigen Grafik , um die Bits zu sehen in der Reihenfolge der Ankunft: 01100010.

Die "Leerlauf" -Zeit zwischen Bytes wird als kontinuierliche "1" -Bits übertragen (effektiv wird die Übertragungsleitung kontinuierlich hochgehalten).

Um den Beginn eines Bytes anzuzeigen, wird das Startbit immer durch Ziehen der Linie nach unten angezeigt, wie in der Grafik dargestellt. Sobald der Empfänger das Startbit sieht, wartet er auf das 1,5-fache der Abtastzeit und tastet dann die Datenbits ab. Es wartet 1,5 mal damit es:

  • Überspringt das Startbit
  • Samples auf halbem Weg durch das nächste Bit

Wenn die Baudrate beispielsweise 9600 Baud beträgt, beträgt die Abtastrate 1/9600 = 0.00010416Sekunden (104,16 µs).

Somit wartet der Empfänger bei 9600 Baud nach dem Empfang eines Startbits auf 156,25 us und tastet dann alle 104,16 us ab.

Bit-Timing starten

Der Zweck des Stoppbits besteht darin, sicherzustellen, dass definitiv ein 1-Bit zwischen jedem Byte liegt. Wenn ein Byte ohne das Stoppbit mit einer Null endet, ist es für die Hardware unmöglich, den Unterschied zwischen diesem und dem Startbit des nächsten Bytes zu erkennen.

Um die obige Ausgabe auf einem Uno zu erzeugen, könnten Sie diesen Code schreiben:

void setup()
  {
      Serial.begin(9600);
      Serial.print("F");
  }

void loop ()
  {
  }

Anzahl der Datenbits

Um Übertragungszeit zu sparen (früher), durften Sie unterschiedlich viele Datenbits angeben. Die AtMega-Hardware unterstützt Datenbits mit einer Nummerierung von 5 bis 9. Je weniger Datenbits Sie senden können, desto weniger Informationen können Sie senden, aber desto schneller.


Paritätsbits

Sie können optional ein Paritätsbit haben. Dies wird bei Bedarf berechnet, indem die Anzahl der Einsen im Zeichen gezählt wird und dann sichergestellt wird, dass diese Anzahl ungerade oder gerade ist, indem das Paritätsbit nach Bedarf auf 0 oder 1 gesetzt wird.

Beispielsweise können Sie für den Buchstaben "F" (oder 0x46 oder 0b01000110) sehen, dass es dort 3 gibt (in 01000110). Somit haben wir bereits eine ungerade Parität. Das Paritätsbit wäre also wie folgt:

  • Keine Parität: weggelassen
  • Gerade Parität: eine 1 (3 + 1 ist gerade)
  • Ungerade Parität: a 0 (3 + 0 ist ungerade)

Das Paritätsbit erscheint, falls vorhanden, nach dem vorletzten Datenbit vor dem Stoppbit.

Wenn der Empfänger nicht das richtige Paritätsbit erhält, spricht man von einem "Paritätsfehler". Dies weist darauf hin, dass ein Problem vorliegt. Möglicherweise sind Sender und Empfänger so konfiguriert, dass sie unterschiedliche Baudraten (Bitraten) verwenden, oder es gab Rauschen auf der Leitung, das eine Null in eine Eins verwandelte, oder umgekehrt.

Einige frühe Systeme verwendeten auch "Mark" -Parität (wobei das Paritätsbit unabhängig von den Daten immer 1 war) oder "Space" -Parität (wobei das Paritätsbit unabhängig von den Daten immer 0 war).


9-Bit-Übertragung

Einige Kommunikationsgeräte verwenden 9-Bit-Daten. In diesen Fällen wird das Paritätsbit in das 9. Bit umgewandelt. Es gibt spezielle Techniken zum Senden dieses 9. Bits (die Register sind 8-Bit-Register, so dass das 9. Bit an einer anderen Stelle abgelegt werden muss).


Anzahl der Stoppbits

Frühe Geräte tendierten dazu, elektronisch etwas langsamer zu sein. Um dem Empfänger Zeit für die Verarbeitung des eingehenden Bytes zu geben, wurde manchmal festgelegt, dass der Sender zwei Stoppbits sendet. Dies fügt im Grunde genommen mehr Zeit hinzu, wenn die Datenleitung hoch gehalten wird (eine weitere Bitzeit), bevor das nächste Startbit erscheinen kann. Diese zusätzliche Bitzeit gibt dem Empfänger Zeit, das letzte eingehende Byte zu verarbeiten.

Wenn der Empfänger keine logische 1 erhält, wenn das Stoppbit sein soll, wird dies als "Rahmenfehler" bezeichnet. Dies weist darauf hin, dass ein Problem vorliegt. Möglicherweise sind Sender und Empfänger so konfiguriert, dass sie unterschiedliche Baudraten (Bitraten) verwenden.


Notation

In der Regel wird die serielle Kommunikation folgendermaßen angegeben: Geschwindigkeit, Anzahl der Datenbits, Art der Parität und Anzahl der Stoppbits.

9600/8-N-1

Das sagt uns:

  • 9600 Bits pro Sekunde
  • 8 Datenbits
  • Keine Parität (stattdessen sehen Sie vielleicht: E = gerade, O = ungerade)
  • 1 Stoppbit

Es ist wichtig, dass sich Absender und Empfänger einig sind. Andernfalls ist es unwahrscheinlich, dass die Kommunikation erfolgreich ist.


Pin-outs

Der Arduino Uno verfügt über die digitalen Pins 0 und 1 für die Hardware-Seriennummer:

Serielle Arduino Uno-Pins

Um zwei Arduinos Sie tauschen Tx und Rx wie folgt aus :

Zwei Arduinos miteinander verbinden


Geschwindigkeit

Ein breiter Geschwindigkeitsbereich wird unterstützt (siehe Grafik unten). "Standard" -Geschwindigkeiten sind normalerweise ein Vielfaches von 300 Baud (z. B. 300/600/1200/2400 usw.).

Andere "nicht standardmäßige" Geschwindigkeiten können durch Einstellen der entsprechenden Register gehandhabt werden. Die HardwareSerial-Klasse erledigt dies für Sie. z.B.

Serial.begin (115200);  // set speed to 115200 baud

Als Faustregel gilt, dass Sie unter der Annahme, dass Sie 8-Bit-Daten verwenden, die Anzahl der Bytes schätzen können, die Sie pro Sekunde übertragen können, indem Sie die Baudrate durch 10 dividieren (aufgrund des Startbits und des Stoppbits).

Somit können Sie bei 9600 Baud 960 Bytes ( 9600 / 10 = 960) pro Sekunde übertragen.


Baudratenfehler

Die Baudrate des Atmega wird durch Herunterteilen der Systemuhr und anschließendes Hochzählen auf eine voreingestellte Zahl erzeugt. Diese Tabelle aus dem Datenblatt zeigt die Registerwerte und Fehlerprozentsätze für einen 16-MHz-Takt (wie den auf dem Arduino Uno).

Baudratenfehler

Das U2Xn-Bit wirkt sich auf den Taktteiler aus (0 = Division durch 16, 1 = Division durch 8). Das UBRRn-Register enthält die Nummer, bis zu der der Prozessor zählt.

In der obigen Tabelle sehen wir also, dass wir 9600 Baud von einem 16-MHz-Takt wie folgt erhalten:

16000000 / 16 / 104 = 9615

Wir teilen durch 104 und nicht durch 103, da der Zähler relativ zu Null ist. Der Fehler liegt hier also in 15 / 9600 = 0.0016der Nähe der obigen Tabelle (0,02%).

Sie werden feststellen, dass einige Baudraten eine höhere Fehlermenge aufweisen als andere.

Laut Datenblatt liegt die maximale Fehlerquote für 8 Datenbits im Bereich von 1,5% bis 2,0% (siehe Datenblatt für weitere Details).


Arduino Leonardo

Arduino Leonardo und Micro verfolgen einen anderen Ansatz für die serielle Kommunikation, da sie direkt über USB und nicht über den seriellen Port mit dem Host-Computer verbunden sind.

Aus diesem Grund müssen Sie warten, bis Serial "bereit" ist (wenn die Software eine USB-Verbindung herstellt).

void setup()
  {
      Serial.begin(115200);
      while (!Serial)
      {}  // wait for Serial comms to become ready
      Serial.print("Fab");
  }

void loop ()
  {
  }

Wenn Sie jedoch tatsächlich über die Pins D0 und D1 kommunizieren möchten (anstatt über das USB-Kabel), müssen Sie Serial1 anstelle von Serial verwenden. Du machst das so:

void setup()
  {
      Serial1.begin(115200);
      Serial1.print("Fab");
  }

void loop ()
  {
  }

Spannungspegel

Beachten Sie, dass der Arduino TTL-Pegel für die serielle Kommunikation verwendet. Dies bedeutet, dass es erwartet:

  • Ein "Null" -Bit ist 0V
  • Ein "Eins" -Bit ist + 5V

Ältere serielle Geräte, die zum Anschluss an die serielle Schnittstelle eines PCs entwickelt wurden, verwenden wahrscheinlich RS232-Spannungspegel, und zwar:

  • Ein "Null" -Bit ist +3 bis +15 Volt
  • Ein "Eins" -Bit ist -3 bis -15 Volt

Dies ist nicht nur in Bezug auf TTL-Pegel "invertiert" (eine "Eins" ist negativer als eine "Null"), der Arduino kann auch keine negativen Spannungen an seinen Eingangspins verarbeiten (noch positive Spannungen über 5 V).

Daher benötigen Sie eine Schnittstellenschaltung für die Kommunikation mit solchen Geräten. Nur für den Eingang (zum Arduino) reichen ein einfacher Transistor, eine Diode und ein paar Widerstände aus:

Puffer invertieren

Für die bidirektionale Kommunikation müssen Sie in der Lage sein, negative Spannungen zu erzeugen, sodass eine komplexere Schaltung erforderlich ist. Zum Beispiel wird der MAX232-Chip dies in Verbindung mit vier 1-µF-Kondensatoren tun, um als Ladungspumpenkreise zu fungieren.


Software Serial

Es gibt eine Bibliothek namens SoftwareSerial, mit der Sie serielle Kommunikationen (bis zu einem gewissen Punkt) in Software und nicht in Hardware durchführen können. Dies hat den Vorteil, dass Sie für die serielle Kommunikation unterschiedliche Pin-Konfigurationen verwenden können. Der Nachteil ist, dass die serielle Ausführung in Software prozessorintensiver und fehleranfälliger ist. Weitere Informationen finden Sie unter Software Serial .


Mega2560

Das Arduino "Mega" verfügt über 3 zusätzliche serielle Hardware-Ports. Sie sind auf der Platine als Tx1 / Rx1, Tx2 / Rx2, Tx3 / Rx3 gekennzeichnet. Sie sollten nach Möglichkeit anstelle von SoftwareSerial verwendet werden. Um diese anderen Ports zu öffnen, verwenden Sie die Namen Serial1, Serial2, Serial3 wie folgt:

Serial1.begin (115200);  // start hardware serial port Tx1/Rx1
Serial2.begin (115200);  // start hardware serial port Tx2/Rx2
Serial3.begin (115200);  // start hardware serial port Tx3/Rx3

Interrupts

Sowohl das Senden als auch das Empfangen unter Verwendung der HardwareSerial-Bibliothek verwenden Interrupts.

Senden

Wenn Sie a ausführen Serial.print, werden die Daten, die Sie drucken möchten, in einem internen "Sendepuffer" abgelegt. Wenn Sie über 1024 Byte oder mehr RAM verfügen (z. B. auf dem Uno), erhalten Sie einen 64-Byte-Puffer, andernfalls erhalten Sie einen 16-Byte-Puffer. Wenn der Puffer Platz hat, Serial.printkehrt der Code sofort zurück und verzögert den Code nicht. Wenn kein Platz vorhanden ist, wird "blockiert", bis der Puffer so weit geleert ist, dass Platz vorhanden ist.

Wenn dann jedes Byte von der Hardware übertragen wird, wird ein Interrupt aufgerufen (der "USART, Data Register Empty" -Interrupt) und die Interruptroutine sendet das nächste Byte aus dem Puffer aus dem seriellen Port.

Empfang

Wenn ankommende Daten empfangen werden, wird eine Interruptroutine aufgerufen (der "USART Rx Complete" -Interrupt) und das ankommende Byte wird in einen "Empfangs" -Puffer (mit der gleichen Größe wie der oben erwähnte Sendepuffer) gestellt.

Wenn Sie anrufen Serial.available, erfahren Sie, wie viele Bytes in diesem Empfangspuffer verfügbar sind. Beim Aufruf wird Serial.readein Byte aus dem Empfangspuffer entfernt und an Ihren Code zurückgegeben.

Auf Arduinos mit 1000 Bytes oder mehr RAM gibt es keine Eile, Daten aus dem Empfangspuffer zu entfernen, vorausgesetzt, Sie lassen sie nicht voll werden. Wenn es voll ist, werden alle weiteren eingehenden Daten verworfen.

Beachten Sie, dass es aufgrund der Größe dieses Puffers keinen Sinn macht, auf das Eintreffen einer sehr großen Anzahl von Bytes zu warten. Beispiel:

while (Serial.available () < 200)
  { }  // wait for 200 bytes to arrive

Dies wird niemals funktionieren, da der Puffer nicht so viel aufnehmen kann.


Tipps

  • Stellen Sie vor dem Lesen immer sicher, dass Daten verfügbar sind. Zum Beispiel ist das falsch:

    if (Serial.available ())
      {
          char a = Serial.read ();
          char b = Serial.read ();  // may not be available
      }

    Der Serial.availableTest stellt nur sicher, dass Sie über ein Byte verfügen , der Code versucht jedoch, zwei zu lesen. Es kann funktionieren, wenn sich zwei Bytes im Puffer befinden. Andernfalls wird -1 zurückgegeben, was beim Drucken wie 'ÿ' aussieht.

  • Beachten Sie, wie lange das Senden von Daten dauert. Wie oben erwähnt, werden bei 9600 Baud nur 960 Bytes pro Sekunde übertragen, sodass der Versuch, 1000 Messwerte von einem analogen Port bei 9600 Baud zu senden, nicht sehr erfolgreich ist.


Verweise

Nick Gammon
quelle
In der 1. Grafik: Bei den Pfeilen sieht es so aus, als würde das Stoppbit zuerst gesendet. Wenn Sie Rx / Tx und die Richtung der Pfeile austauschen, würde ich denken, dass es weniger verwirrend ist.
ott--
Es sollte von links nach rechts gelesen werden (so wie dieser Satz) und daher passieren die Dinge auf der linken Seite zuerst. Sagen Sie es so: Auf einem Oszilloskop würden Sie die Spur so sehen.
Nick Gammon
Ok mit der Oszilloskop Erklärung kaufe ich das. :-)
ott--
Allerdings habe ich gedacht, dass Ihr Punkt viel Sinn macht. Was denken andere? Wäre es klarer, wenn die Pfeile vertauscht und ich Rx / Tx ausgetauscht hätte?
Nick Gammon
1
@ linhartr22 Ich habe es geändert, um "bedeutungslose Daten" zu lesen, was wahrscheinlich näher ist.
Nick Gammon