Das Titelbild dieses Beitrags ist von dooder – de.freepik.com

Die serielle asynchrone Kommunikation ist eine der häufigsten Formen der Kommunikation zwischen zwei elektronischen Geräten. Wir wollen uns in diesem Beitrag anschauen, welche Arduino-Bibliotheken es gibt und checken, wie gut diese funktionieren.

Asynchrone serielle Kommunikation

Bei der asynchronen seriellen Kommunikation benötigt man nur zwei Leitungen (plus Masse), um zwei elektronische Systeme zu verbinden. Und tatsächlich war dies die Art und Weise, wie Fernschreibsysteme in den frühen Tagen der digitalen Kommunikation miteinander verbunden wurden. Ein paar Jahre später wurden Fernschreiber als E/A-Geräte an Computer angeschlossen. Heutzutage wird diese Art der Kommunikation häufig zwischen verschiedenen elektronischen Geräten verwendet. Eine Leitung ist für ausgehende Signale (TX), die andere für eingehende Signale (RX).

Anschließen eines (Vintage-)Fernschreibers an einen (Vintage-)Computer

Mit dieser Konfiguration ist es möglich, gleichzeitig zu senden und zu empfangen (dies wird auch als Vollduplex-Kommunikation bezeichnet). Man kann es noch einfacher haben und nur eine Signalleitung verwenden. Dann kann jedoch jeweils nur eine Partei senden und die andere muss empfangen (was als Halbduplex bezeichnet wird). Das debugWIRE-Protokoll, das für das Hardware-Debugging der kleineren AVR-MCUs verwendet wird, verwendet einen solchen Mechanismus. Es verwendet die RESET-Leitung, um zwischen dem Hardware-Debugger und der MCU zu kommunizieren.

Übertragen eines Bytes

Die charakteristische Eigenschaft der asynchronen Kommunikation besteht darin, dass es kein Taktsignal gibt, das angibt, wann die Daten auf der Leitung gültig sein sollen (wie es bei den synchronen I2C – und SPI-Protokollen der Fall ist). Das bedeutet, dass die beiden kommunizierenden Parteien wissen müssen, welche Kommunikationsgeschwindigkeit verwendet wird, und sie müssen diese Geschwindigkeit beim Lesen und Schreiben von Daten einhalten.

Es ist jedoch nicht nur die Geschwindigkeit, sondern auch das Format, auf das man sich einigen muss. Heutzutage überträgt man normalerweise einen sogenannten Frame, indem man zuerst ein Startbit (eine logische Null) sendet, gefolgt vom Datenbyte (8 Bit), ohne ein Paritätsbit anzuhängen, gefolgt von einem Stopbit (einem logischen). Dies wird als 8N1-Format bezeichnet. Darüber hinaus ist die übliche Interpretation, dass die Übertragung mit dem am wenigsten signifikanten Bit beginnt (es ist Little-Endian). Zeichnet man die Übertragung eines Bytes mit einem Logikanalysator auf, kann es wie folgt aussehen.

Übertragung von 0x55 im 8N1-Format bei 115200 bps

Der Ruhezustand ist, dass sich die Leitung im hohen Zustand befindet. Das Startbit (ab ca. 12 μs) ist immer ein Nullbit. Dann werden die Bits des Datenbytes, in diesem Fall 0x55, rückwärts übertragen, d.h. mit dem am wenigsten signifikanten Bit zuerst. Nachdem 8 Bit übertragen wurden, wird die Übertragung durch das Stopbit beendet, das immer ein Einsbit ist. Danach könnte ein neues Byte übertragen werden oder die Leitung kann im Ruhezustand bleiben.

Auf der Empfängerseite wartet man auf eine fallende Flanke, die den Beginn des Startbits signalisiert. Man wartet dann 1,5 Bit-Zeiten, bevor man den Zustand des ersten (am wenigsten signifikanten) Bits abtastet. Danach wartet man immer eine Bitzeit, um den Bitwert in der Mitte der Bitzeit abzutasten.

Mögliche Timing-Probleme

Es kann jedoch vorkommen, dass das Timing etwas daneben liegt. Der Grund dafür kann sein, dass der Systemtakt nicht genau ist oder weil das „universelle asynchrone Empfänger- und Sendegerät“ (kurz UART) nicht die richtige Taktrate erzeugen kann. Wenn man beispielsweise eine AVR-MCU mit 16 MHz taktet und mit einer Bitrate von 115200 bps kommunizieren will, muss man eine Bitrate wählen, die entweder 3,5 % zu langsam oder 2,1 % zu schnell ist (siehe WormFood’s AVR Baud Rate Calculator). Der Arduino-Core ist übrigens 2,1 % zu schnell.

Und was ist mit dem Systemtakt? Glücklicherweise verwendet der Arduino Uno einen Resonator, der eine Genauigkeit von 1000 ppm (= 0,1 %) oder besser haben sollte. Und tatsächlich ist es so, wie das Bild zeigt.

Messen des Arduino Uno Systemtakts

Verwendet man jedoch den internen RC-Oszillator einer AVR-MCU, dann hat man nur eine garantierte Genauigkeit von ± 10 %. In allen MCUs, die ich gesehen habe, waren es jedoch ± 2 %. Mit Kalibrierung kann man das auf ± 1 % reduzieren.

Was sind also die Konsequenzen für die asynchrone Kommunikation, wenn eine Partei die Bits schneller oder langsamer sendet, als die empfangende Partei es erwartet? Die gute Nachricht ist, dass man nur einen Frame berücksichtigen muss, denn nachdem ein Frame empfangen wurde, wird das Timing mit dem nächsten Startbit neu gestartet. Dies bedeutet, dass sich Fehler nicht über mehrere Frames fortpflanzen.

Das nächste Bild zeigt die Übertragung des Bytes 0x55 mit drei verschiedenen Geschwindigkeiten. Die mittlere Spur entspricht dabei dem Bitstrom mit der richtigen Übertragungsgeschwindigkeit von 115200 bps. Die obere Spur zeigt, was passiert, wenn die Übertragungsgeschwindigkeit 5 % zu langsam ist, die untere Spur zeigt eine 5 % zu schnelle Übertragung.

Was passiert, wenn das Timing 5% abweicht?

Wie man sehen kann, pflanzt sich der Fehler im Laufe der Zeit fort. Da der Wert des Bits in der Mitte der Bitzeit bestimmt wird, kann man bei einer Abweichung von 5 % immer noch den richtigen Wert des letzten Bits (wo sich die gestrichelte Linie befindet) bestimmen, vorausgesetzt, dass die mittlere Spur das Timing der empfangenden Seite widerspiegelt. 5 % ist dabei offenbar nicht die maximal mögliche Abweichung. Wie viel Abweichung wird also zu der Situation führen, dass wir einen akkumulierten Fehler von 50 % in der Mitte des achten Bits haben, d.h. nach 8,5 Bitzeiten?

x\% \times 8,5 = 50\%

Das Lösen der Gleichung ergibt x = 5,88. Alles, was besser als 5,88 % ist, sollte also theoretisch in Ordnung sein. Im Tutorial zu Clock Accuracy Requirements for UART Communications Protocol von Analog Devices wird jedoch argumentiert, dass man in den meisten Szenarien die Anstiegs- und Abfallzeiten des Signals nicht ignorieren kann. Es wird argumentiert, dass in „schlimmen“ Umgebungen nur die mittleren 50 % der Bitzeit als stabil angenommen werden kann, während in „normalen“ Szenarien 75 % einer Bitzeit als stabil angenommen werden darf. Weiterhin wird davon ausgegangen, dass man überprüfen möchte, ob das Stopbit tatsächlich ein Einsbit ist. Mit diesen Annahmen reduziert sich der akzeptable relative Fehler auf 2,6 % für die „schlimme“ Umgebung und 3,9 % für die „normale“ Umgebung.

Leider gibt es mehr Quellen für Kommunikationsfehler, die durch falsches Timing erzeugt werden. Eine davon ist die Interrupt-Latenz, die von Interrupt-Diensten erzeugt wird, z. B. dem Timer-Überlauf-Interrupt, der Millisekunden zählt. Wie wir in dem zitierten Blogbeitrag gesehen haben, dauert dieser Interrupt 6,625 μs, was ein ziemlicher Brocken ist, wenn wir mit 57600 bps kommunizieren, wo die Bitzeit 17,36 μs beträgt. Wenn man asynchrone Kommunikation in Software implementiert, dann setzt man auf Interrupts, die in diesem Fall um fast 7 μs verzögert sein können! Daher kann es in solchen Szenarien ratsam sein, den Timer-Überlauf-Interrupt zu deaktivieren.

Ein letztes Problem kann sein, dass bei der Implementierung der asynchronen Kommunikation in Software der Empfang eines Bytes in einer Interrupt-Routine erfolgen muss. Das bedeutet, dass es sehr wenig Zeit gibt, das empfangene Byte zu verarbeiten, nämlich nur die Bitzeit für das Stopbit. Und dies kann sehr schnell zu einem Pufferüberlaufproblem führen.

Serielle Kommunikationsbibliotheken für den Arduino

Wenn man den Arduino Uno verwendet, benutzt man normalerweise das Serial-Objekt, um asynchron zu kommunizieren. Dies Objekt ist eine Instanz der Klasse HardwareSerial. Der Hardware-UART erledigt die meiste Arbeit und erst wenn ein Byte empfangen wurde oder ein Byte gesendet werden kann, wird ein Interrupt ausgelöst. Die Interrupt-Service-Routine zum Empfangen von Daten verwendet 5 μs, die Interrupt-Routine für das Senden des nächsten Bytes benötigt im schlimmsten Fall 8,75 μs.

Da der Uno nur einen Hardware-UART besitzt, muss oft ein Software-UART eingesetzt werden. Wenn man die kleineren ATtinys verwendet, von denen manche überhaupt keinen Hardware-UART haben, führt kein Weg daran vorbei, einen Software-UART zu verwenden. Die Standardlösung ist die SoftwareSerial-Bibliothek.

Es gibt jedoch drei Probleme. Zunächst ist es notwendig, dass die fallende Kante des Startbits so genau wie möglich erkannt wird. Dies geschieht über den Pin-Change-Interrupt an der Empfangsleitung. Andere Interrupts sind also kontraproduktiv. Sie können dazu führen, dass der empfangene Bitstrom falsch interpretiert wird, wenn das Startbit zu spät erkannt wird. Wenn beispielsweise Daten mit einer Bitrate von 57600 empfangen werden, beträgt die Bitzeit 17,4 μs. Wird der Millis-Interrupt kurz vor der fallenden Kante eines Startbits ausgelöst, verzögert sich die Erkennung des Startbits um 6,6 μs, was bereits einem Drittel der Bitzeit entspricht. Zusammen mit anderen Abweichungen kann das leicht dazu führen, dass der Datenstrom falsch interpretiert wird.

Zweitens erfordert das Senden und Empfangen von Daten ein genaues Timing, und aus diesem Grund sind Interrupts während dieser Zeit deaktiviert und es können keine anderen Dinge passieren. Da die Empfangsroutine in das Stopbit hinein wartet, steht nur eine halbe Bitzeit für die Verarbeitung eines empfangenen Bytes zur Verfügung. Wenn zu viele Bytes in kurzer Zeit empfangen werden, kann das dann zu einem Überlauf des Empfangspuffers (von 64 Bytes) führen.

Drittens können selbst langsame Bitraten zu Problemen führen. Wenn man zum Beispiel mit 1200 bps kommuniziert, dann beträgt die Bitzeit 833 μs, und so werden Interrupts für mindestens das 9,5-fache der Bitzeit, d.h. 7,9 ms, blockiert. Dies bedeutet, dass der millis-Interrupt, der jede Millisekunde ausgelöst wird, nicht rechtzeitig bedient werden kann.

Es gibt mindestens zwei Alternativen zu SoftwareSerial. Eine ist picoUART, ein sehr minimalistischer Software-UART, von dem ich die Version 1.2.0 verwende. Die Bibliothek benutzt nur eine minimale Menge an Code, ist aber extrem genau im Timing. Im Gegensatz zu SoftwareSerial müssen jedoch die Ein-/Ausgangspins und die Kommunikationsgeschwindigkeit zur Übersetzungszeit festgelegt werden. Ähnlich wie bei SoftwareSerial ist fast die gesamte Frame-Zeit für Interrupts gesperrt. Der Empfang von Daten kann entweder durch aktives Warten auf neue Daten oder durch Interrupts erfolgen. Im letzteren Fall gibt es nur einen Ein-Byte-Empfangspuffer, was leicht dazu führen kann, dass ein Datenbyte verloren geht.

Unser letzter Kandidat ist die AltSoftSerial-Bibliothek, die eine völlig andere Methode verwendet als die Bit-Banging-Technik der letzten beiden Bibliotheken. Es verwendet die Eingangserfassungsfunktion (engl. Input Capture) von Timer 1 auf dem ATmega328P, um den Zeitpunkt zu erfassen, zu dem eine Signalflanke auftritt. Und dies geschieht auf interruptgesteuerte Weise, sodass die Interrupt-Latenz deutlich kürzer ist als das 9,5-fache der Bitzeit ist. Es wird optimistisch behauptet, dass es 2-3 μs sind. Es stellt sich jedoch heraus, dass es im schlimmsten Fall 16 μs sein können. Dies ist immer noch viel besser als das, was die anderen Methoden fordern, kann aber für höhere Bitraten problematisch sein. Darüber hinaus wird behauptet, dass die Bibliothek eine Interrupt-Latenz von fast einer Bitzeit tolerieren kann. Zusammen mit seinen eigenen 16 μs ist das definitiv zu optimistisch. Die Generierung der zu übertragenden Bytes erfolgt über die Ausgabevergleichsfunktion (engl. Output Compare) desselben Timers in einer Interrupt-gesteuerten Weise. Im Vergleich zu den beiden Bit-Banging-Methoden benötigt diese Bibliothek also deutlich weniger MCU-Zyklen. Dafür gibt es natürlich einen Preis: Der Ein- und Ausgangspin ist fest und man kann die PWM-Funktionalität der mit Timer 1 verbundenen Pins nicht nutzen.

Welches ist also die beste Alternative? SoftwareSerial ist die flexibelste. Es können beliebige Pins als Eingang und Ausgang verwendet werden. Und man kann sogar mehr als eine SoftwareSerial-Instanz erzeugen, aber nur eine kann jeweils aktiv sein. picoUART ist die Bibliothek mit dem kleinsten Speicherbedarf und mit beeindruckender Timing-Genauigkeit. Sie passt offensichtlich gut zu den kleineren ATtinys. Schließlich ist AltSoftSerial, das sich auf Timer anstelle von Verzögerungsschleifen verlässt, sehr genau und verbraucht die geringste Anzahl von Rechenzyklen.

Im nächsten Abschnitt werfen wir einen Blick darauf, mit welchen Kommunikationsgeschwindigkeiten die Bibliotheken zuverlässig umgehen können und wie viel Abweichung in der Bitrate sie tolerieren.

Stresstests der verschiedenen seriellen Bibliotheken

Wie genau ist das Timing beim Senden und wie tolerant sind die Bibliotheken beim Empfang von Daten? Um die Genauigkeit der Bitrate beim Senden zu messen, habe ich meinen Saleae Logikanalysator verwendet, um die generierten Bitraten zu messen. Zum Stresstest der Empfangsfunktionalität habe ich ein FT232R-Board verwendet, das von einem Python-Skript gesteuert wird.

Werfen wir zunächst einen Blick auf die Übertragungsbitraten (hier wird wieder der Dezimalpunkt statt des Dezimalkommas benutzt).

BitrateHardware-
Serial
Software-
Serial
pico-
UART
AltSoft-
Serial
12000.0%0.0%0.0%0.0%
24000.0%0.0%0.0%0.0%
48000.0%0.0%0.0%-0.1%
9600+0.2%0.0%0.0%-0.1%
19200+0.2%-0.3%+0.1%-0.1%
38400+0.2%-0.7%0.0%-0.1%
57600+2.2%-0.7%0.0%-0.1%
115200+2.2%-0.7%0.0%-0.1%
230400-3.6%-4.3%+0.5%____
78120.0%-0.2%+0.1%-0.1%
156250.0%-0.3%0.0%-0.1%
312500.0%0.0%+0.1%-0.1%
625000.0%-1.4%+0.1%-0.1%
125000-0.1%-2.8%+0.1%-0.1%
250000-0.1%-6.7%-0.1%____
500000-0.1%-12.4%-0.2%____
1M-0.1%-22.0%-0.1%____
Communication speed deviation when transmitting

Die Tabelle enthält ein paar interessante Einträge. Erstens ist es selbst für den Hardware-UART nicht immer möglich, eine Bitrate zu erzeugen, die nahe an der nominellen Bitrate liegt. Für 57600 und 115200 bps ist die tatsächliche Bitrate 2,2 % zu schnell. Schlimmer noch, für 230400 bps ist der UART 3,6 % zu langsam, was problematisch ist. Der Grund für diese Abweichungen wurde bereits genannt: Der AVR-Baudratengenerator kann nicht alle Bitraten erzeugen. Die nächste interessante Beobachtung ist, dass SoftwareSerial vermutlich nicht mit Bitraten höher als 115200 bps verwendet werden sollte. Bei AltSoftSerial ist bei 125000 bps Schluss. Der klare Gewinner scheint picoUART zu sein.

Wie schlagen sich die Bibliotheken, wenn sie Daten empfangen werden sollen? Ich habe das folgende Arduino-Programm (etwas vereinfacht) verwendet, um die Leistung von SoftwareSerial zu testen. Für die anderen Bibliotheken sieht es ähnlich aus. Man beachte, dass ich nicht die available()– Methode verwende, sondern das Ergebnis einfach lese und ignoriere, wenn es kleiner als Null ist. Das ist der schnellste Weg, um ankommende Bytes zu empfangen.

unsigned long baud=115200;

#include <SoftwareSerial.h>
SoftwareSerial UART = SoftwareSerial(8, 9);
const int RTS=12; // RTS line

void setup() {
  // TIMSK0 = 0;
  pinMode(LED_BUILTIN; OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);   
  UART.begin(baud);
}

void loop() {
  byte expect = 0;
  int inp;

  while (1) {
    inp = UART.read();
    if (inp >= 0) {
      if (inp != expect++) {
        digitalWrite(LED_BUILTIN, LOW);   
	pinMode(DTR; OUTPUT); // pull DTR low
	while (1);
      }
    }
  }
}

Die zu empfangenden Daten werden von einem Python-Skript generiert, die über ein FT232R-Board an den Arduino Uno geschickt werden. Der FT232R kann fast jede gewünschte Bitrate erzeugen. Es wird eine Bitrate ausgewählt, die so nah wie möglich an einer ist, die zu einem ganzzahligen Ergebnis führt, wenn 24 Millionen durch die Bitrate geteilt werden. Hier ist das (vereinfachte) Skript. Man ruft es mit den folgenden Parametern auf:

  • <bps> – die Bitrate;
  • <stopbits> – Anzahl der Stopbits, normalerweise 1; kann auf 2 gesetzt werden, wenn die Kommunikation verlangsamt werden soll;
  • <num> – Anzahl der Bytes, die für jeden Geschwindigkeitsschritt gesendet werden sollen;
  • <dir> – Änderungsrichtung für Geschwindigkeitsschritte, kann ‚+‘ oder ‚-‚ sein
  • <startstept> – die Startabweichung, z. B. 3.9, was eine Abweichung von 3,9 % bedeutet.

Das Skript ändert die Bitrate systematisch in Promille-Schritten und stoppt, wenn das Arduino-Programm die CTS-Linie nach unten zieht, weil ein unerwartetes Byte empfangen wurde.

#!/usr/bin/env python3
import serial
import sys
import time

serialport = '/dev/cu.usbserial-XXXXX' # specfiy your serial port here

def usage():
    print("serialgen.py <bps> <stopbits> <num> <dir> <startstep>")
    exit()

if len(sys.argv) != 6: usage()

bps = int(sys.argv[1])
addstep = bps/100
stopbits = float(sys.argv[2])
maxwrite = int(sys.argv[3])
if sys.argv[4] == '+': direction = 1
else: direction = -1
step = float(sys.argv[5])*direction

outbyte = 0;
while (1):
    dev = bps+(step*addstep)
    print("bps:", bps, " deviation:", "%4.1f" % (step,),  
          " is:", int(dev))
    ser = serial.Serial(serialport, int(dev), stopbits=stopbits)
    time.sleep(0.05)
    i = 0;
    while i < maxwrite:
        ser.write(outbyte.to_bytes(1,'big'))
        i += 1
        outbyte = (outbyte + 1) % 256
    time.sleep((maxwrite+5)*10.1/(bps+(step*addstep))) 
    # otherwise bytes get dropped:
    if ser.cts:
        print("Failure!")
        exit()
    ser.flush()
    time.sleep(0.3) # otherwise bytes get dropped
    ser.close()
    step += 0.1*direction

Für einige Bitraten kam es zu Übertragungsfehlern, wenn der millis-Interrupt aktiv war. Das ist nicht überraschend, da die Interrupt-Latenz von 6,6 μs bei 115200 bps nahe bei der Bitzeit liegt. Um trotzdem ein aussagekräftiges Ergebnis zu erhalten, habe ich den Interrupt deaktiviert und in der Ergebnistabelle unten mit ‚#‘ markiert. Manchmal war die Zeit zwischen dem Lesen von zwei Bytes zu kurz. Ich habe etwas mehr Zeit eingeräumt, indem ich ein Sendeformat mit 2 Stopbits verwendet habe. Dies ist durch einen Stern in der Tabelle gekennzeichnet.

Für jede Bibliothek ist die maximale negative und positive relative Abweichung angegeben, die die Bibliothek toleriert hat. Es wurde mit mindestens 10.000 Bytes getestet. Für alle Bitraten über 10.000 bps wurde mit 100.000 Bytes getestet. Und für alle Bitraten, die höher als 100.000 bps sind, wurden 1 Million Bytes verwendet. Die angegebenen Prozentsätze sind diejenigen, die toleriert werden, während die nächsthöhere (oder niedrigere) Bitrate zu einem Fehler führte. Zu beachten ist, dass insbesondere bei höheren Bitraten der Schritt zur nächsten Bitrate recht groß sein kann (z.B. 0,5 % bei Bitraten um 100.000 bps). Schließlich sollte man beachten, dass ich die blockierende Variante von picoUART verwendet habe, die Interrupts blockiert, sobald die Leseroutine aufgerufen wird.

BitrateHardwareSerialSoftwareSerialpicoUARTAltSoftSerial
1200-5.9%+2.2%-5.6%+5.6%-5.7%+5.5%-5.8%+5.5%
2400-5.8%+2.2%-5.6%+5.7%-5.7%+5.4%-5.8%+5.4%
4800-5.9%+2.1%-5.4%+5.6%-5.7%+5.4%-5.8%+5.4%
9600-5.7%+2.3%-5.2%+5.7%-5.7%+5.3%-5.8%+5.3%
19200-5.6%+2.2%-4.8%+5.2%-5.6%+5.4%-5.7%+5.5%
38400-5.6%+2.2%-4.1%+4.3%-5.6%+5.3%-5.5%+5.2%
57600-3.6%+5.4%-2.5%+4.1%-5.6%+5.3%-2.6%
+4.2%
115200-3.5%+4.3%-0.6%+6.4%#-5.1%+5.2%-1.8%+5.1%#*
230400-8.5%-1.9%________-4.3%+5.3%#________
7812-5.9%+2.1%-5.4%+5.6%-5.7%+5.4%-5.8%+5.3%
15625-5.7%+2.1%-5.1%+5.2%-5.6%+5.3%-5.8%+5.3%
31250-5.7%+2.3%-4.7%+4.8%-5.5%+5.4%-5.7%+5.3%
62500-5.7%+2.3%-3.3%+3.6%-5.5%+5.3%-2.4%+3.6%
125000-5.9%+2.2%-2.5%+7.8%#*-5.6%+5.2%________
250000-4.9%+2.4%________-4.8%+4.3%#________
500000-4.0%+2.1%________-3.9%+4.4%#________
1M-4.1%+1.7%________________________
Possible speed deviations when receiving data
('#' = no millis interrupt, '*' = 2 stop bits)

Es gibt eine Reihe interessanter Ergebnisse in dieser Tabelle. Erstens ist offensichtlich, dass der Hardware-UART die robusteste Variante ist. Allerdings ist völlig unklar, warum der Hardware-UART kein symmetrisches Toleranzintervall hat. Warum wird bei nominalen 1200 bps eine Bitrate, die 5,9 % langsamer ist, toleriert, aber keine Bitrate, die 2,3 % schneller ist? Außerdem kann der Hardware-UART bei 230400 bps überhaupt keine Bytes mit der Nominalgeschwindigkeit empfangen! Zwei Arduino Unos könnten dennoch problemlos kommunizieren, weil sie den gleichen Fehler hätten.

Zweitens sieht SoftwareSerial für alles bis zu 57600 bps ganz OK aus. Bei 112500 bps muss jedoch der millis-Interrupt deaktiviert werden, man muss auf zwei Stopbits umschalten, und außerdem liegt die erzeugte Bitrate (die -0,7 % daneben liegt) außerhalb des Toleranzintervalls des Empfängers. Wenn man also zwei Systeme mit SoftwareSerial bei 115200 bps kommunizieren lassen will, wird es vermutlich zu Problemen kommen.

Drittens scheint picoUART die robusteste Lösung zu sein, selbst bei hohen Bitraten. Man sollte jedoch beachten, dass ich die Polling-Variante verwendet habe, die alle Interrupts blockiert. Mit der Interrupt-gesteuerten Version würde es vermutlich Probleme bei höheren Bitraten geben, weil der ISR mehr Zyklen benötigt und der millis-Interrupt zu Problemen führen könnte. Schließlich ist es auch die am wenigsten flexible Lösung, da man Bitraten und E/A-Pins zur Übersetzungszeit festlegen muss.

Viertens ist AltSoftSerial nicht so robust, wie ich gedacht hätte. Bei Bitraten von 56700 bps und weniger ist es ein definitives Plus, dass man mehr Rechenzyklen für das Anwenderprogramm hat als bei anderen Softwarelösungen. Bei 57600 bps sollte man jedoch wahrscheinlich den Timer-Interrupt (und wahrscheinlich auch andere Interrupts) deaktivieren, da die kombinierte Worst-Case-Interrupt-Latenz sehr nahe an einer Bitzeit liegt. 115200 bps sind schließlich nur dann machbar, wenn alle Interrupts ausgeschaltet sind und zwei Stopbits verwendet werden.

Views: 198