Einleitung
In diesem Artikel geht es um serielle Kommunikation mit dem Raspberry Pi, inklusive Hintergründe und Grundlagen. Ein zweiter Artikel zeigt wie man das ganze praktisch anwendet - UART zu UART, und RS485 als Schnittstelle zwischen zwei Pi's.
Der Raspberry Pi hat dazu zwei spezielle Hardwareeinheiten, die über die GPIO Pins zugänglich sind: den PL011 UART und den miniUART. Ich erläutere, wie man diese Pins ansteuert und nutzt, sowie wie man einen RS485 Transceiver daran anbindet.
Ich spreche im folgenden vom Empfänger und Sender. Die Kommunikation ist meistens (außer bei Halfduplex RS485) bidirektional, d.h. beide Partner sind jeweils Sender UND Empfänger gleichzeitig. Dabei erfolgt die Kommunikation im sogenannten Vollduplex Modus. Das was ich schreibe gilt also für beide Kommunikationspartner, jeweils in unterschiedliche Richtung. Es reicht dass wir uns eine Seite der Kommunikation anschauen, da die andere genau gleich läuft (einfach nur in die Gegenrichtung).
Grundlagen
Bei serieller Kommunikation nutzen wir eine Datenleitung, um seriell Daten zu übermitteln. Die einzelnen Bits werden nacheinander, also in Serie, übermittelt. Die alternative dazu wäre parallele Kommunikation - wie beispielsweise bei den früher benutzten Parallelports für Drucker. Die meisten modernen Schnittstellen sind seriell (bspw. USB, SPI, I2C, SATA, CAN usw. usf. ...), da es bei höheren Geschwindigkeiten sehr schwierig wäre, parallele Pins zu synchronisieren. Serielle Kommunikation bietet auch den Vorteil, dass wir weniger Leitungen benötigen, was die Kabel dünner und günstiger macht.
In Computern liegen die Daten intern als Bytes vor - 1 Byte besteht aus 8 bit. Mit 1 Byte (8 bits) kann man 256 Zeichen darstellen. Üblicherweise fängt man die Nummerierung bei 0 an, d.h. es gibt Zeichen 0 - 255.
Auf folgender Webseite kann man bspw. die Belegung dieser Zeichen, falls sie als ASCII interpretiert werden einsehen:
Einige davon sind nicht-druckbare Zeichen (bspw. für neue Zeile, etc.); andere sind Sonderzeichen - bspw. Umlaute, usw. Die Zeichen ab 128 können je nach Codepage anders interpretiert werden. Damit kann man beispielsweise kyrillische Zeichen, usw. darstellen (die richtige Codepage vorausgesetzt). Die Codepage bedeutet einfach, man einigt sich darauf, was die Zeichen darstellen.
UTF-8 nutzt hingegen ein bis vier bytes zum Kodieren von einem Zeichen. Es ist zu ASCII abwärts kompatibel: die Zeichen 0-127 entsprechen dem ASCII Encoding.
Was ich damit sagen möchte ist, dass ein Byte, oder 8 bit, die Grundeinheit für Kommunikation darstellt. Wir übertragen, speichern und "denken" in Bytes am Computer.
Über die serielle Schnittstelle übertragen wir bit-weise, aber jeweils 1 byte am Block. Die Aufgabe des UARTs im Raspberry Pi, Mikrocontroller, PC, Arduino oder was auch immer wir einsetzen, ist die Daten aus dem Byte in bits umzuwandeln und nacheinander zu senden. Dabei kommt typischerweise eine Sendeleitung je Richtung zum Einsatz - der UART hat einen Sendeteil (TX für Transmit), und einen Empfangsteil (RX für Receive). Die serielle Kommunikation über einen UART hat im Gegensatz zu SPI und I2C keine zusätzliche Takt/Clockleitung für den Sendetakt. Auf der Gegenseite empfängt der UART der Gegenseite die einzelnen Bits und setzt sie wieder zu einem Byte zusammen.
UART steht dabei für Universal Asynchronuous Receiver Transmitter.
Bildquelle: Wikipedia, CC-BY-SA, by IngenieroLoco
Wie man in dem Bild sieht, werden bit0 bis bit7 (also insgesamt 8 bits) nacheinander übertragen.
Aber was ist das start bit und das stop bit?
Dabei handelt es sich um das sogenannte "Data framing". Die Nutzdaten (unser zu übertragendes Byte) werden dabei von dem start bit und dem stop bit eingerahmt. Das Start bit ist immer "0" bzw. low, das stop bit immer "1" bzw. high. Das passiert bei jedem einzelnen Byte das wir übertragen.
Es geht hier darum, dass der Empfänger merken soll, wann gesendet wird, bzw. wann keine Kommunikation erfolgt, und wann die Leitung unterbrochen ist ("break detection"), sowie dass der Empfänger sicher mehrere hintereinander gesendete Zeichen unterscheiden kann und sich nicht desynchronisiert.
Der Reihe nach:
Als Relikt aus der Telegrafie ist der Standard-Zustand im Leerlauf ohne Kommunikation beim UART "high", d.h. eine logische 1. Der UART zieht also auf Senderseite den GPIO Pin (TX) des Raspberry Pi's auf +3,3 V. Dieser Pin ist normalerweise mit dem Eingangspin (RX) auf der Empfängerseite verbunden, so dass hier dauerhaft +3,3V anliegen.
Falls die Leitung unterbrochen ist, d.h. am RX-pin der Empfängerseite der Zustand dauerhaft eine logische 0 bzw. GND ist, kann der UART der Empfängerseite das detektieren und dem System melden.
Hinweis: nur der PL011 UART ist zur "break detection" imstande. Da der miniUART das stop bit nicht prüft, würde er bei kaputter Leitung fälschlicherweise 0x00 ausgeben, da er wiederholt low von der "Leitung" liest. Siehe: https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf Seite 10-11
Wenn der Sender ein Byte senden möchte, zieht er die Leitung auf GND - das ist das Start Bit. Danach werden die einzelnen Bits nacheinander übermittelt (0 als low, d.h. Leitung liegt auf GND und 1 als high, d.h. Leitung auf +3,3V im Falle des Pi). Anschließend zieht der Sender für das stop bit die Leitung wieder auf high, +3,3V - den Ursprungszustand. Das ganze zusammen nennt man ein Frame.
Damit wird außerdem sichergestellt, dass zumindest ein Übergang in der Übertragung passiert. Wenn wir eine lange Reihe von 0en übertragen würden, wären das ohne die Start und Stop bits alles Zustände mit 0 V. Dann könnte es leicht sein dass Sender und Empfänger sich desynchronisieren - der Sender würde beispielsweise 800 Nullen senden, und der Empfänger 798 zählen, da er nicht genau weiß wann sie jeweils anfangen sondern sich ausschließlich auf seine eingebaute Uhr verlassen muss. Durch das Start und Stop Bit kann der Empfänger sich immer wieder synchronisieren, mindestens ein Übergang der Leitung pro gesendetem Byte ist garantiert.
Da das Senden des Start-Bits jederzeit erfolgen kann, mit beliebigen Pausen zwischen dem letzten Stop bit und dem neuen Start-Bit, wird diese Art der Kommunikation als asynchron bezeichnet.
Erweiterte Grundlagen
Die gerade besprochene Basis wird jetzt konkretisiert und erweitert.
PuTTY, zum Beispiel, gibt einem die Möglichkeit für Kommunikation über die serielle Schnittstelle die nötigen Parameter zu setzen. Diese Parameter werden im folgenden erklärt.
Es gibt für serielle Kommunikation über den UART folgende Parameter, die auf beiden Seiten (Empfänger und Sender) eingestellt werden müssen:
Geschwindigkeit
Bit Rate in bit/s, bspw. 115200 bit/s - jedes Bit "dauert" dann 8,68 microsec.
Zahl der Datenbits
In der Einleitung habe ich vom Standard gesprochen, in welchem Fall wir bei jedem gesendeten Frame ein Byte, also 8 bit an Nutzdaten übertragen. Es gibt jedoch auch andere Möglichkeiten:
- 5 bit - zum Beispiel als Baudot Code
- 6 bit - selten genutzt
- 7 bit - für das "echte" ASCII (nur mit den ersten 0-127 Zeichen)
- 8 bit - am weitesten verbreitet
- 9 bit - selten genutzt
Zahl der Stop Bits
Die Stop bits werden am Ende jedes Zeichens bzw. als Abschluss des Frames gesendet. Sie erlauben dem Empfänger das Ende zu detektieren, und sich zu synchronisieren. Üblicherweise wird ein Stop Bit verwendet, es gibt aber auch 1 1/2 oder 2 Stop bits. Das war zum Beispiel für die langsamen elektromechanischen Teleprinter notwendig.
Parity
Die Parity ist eine einfache Möglichkeit, Fehler bei der Übertragung zu prüfen. (Aber nicht korrigieren zu können!) Diese Fehler können beispielsweise durch äußere Störeinwirkungen (elektromagnetische Störungen auf schlecht geschirmten Leitungen) erfolgen, der Empfänger detektiert fälschlicherweise eine 1 wo eine 0 sein sollte oder umgekehrt.
Bei der Parity wird dabei die Zahl der logischen 1er im übertragenen Byte, inklusive des Paritybits gezählt. Das Paritybit wird so gesetzt (also 0 oder 1) dass die Zahl der logischen 1er immer entweder gerade oder ungerade ist (je nachdem ob ODD oder EVEN Parity eingesetzt wird). Falls ein "Bitflip" vorliegt, und eine vorher nicht dagewesene 1 detektiert wird, bzw. eine 1 zu einer 0 geworden ist, kann der Empfänger das also merken. Leider werden zwei bzw. andere gerade Anzahlen an Bitflips nicht gemerkt!
Falls ein Parity Bit vorgesehen wird, wird es zusätzlich nach dem letzten Daten-Bit, und vor dem Stop-Bit im Frame mit übertragen:
Bildquelle Wikipedia: CC-BY SA 4.0 User: Chris828 (leicht modifiziert - ohne Spannungen)
Es gibt folgende Möglichkeiten die Parity zu konfigurieren:
- None: es wird kein Parity Bit mitgesendet
- Odd: die Zahl der "logischen 1er" muss ungerade sein
- Even: die Zahl der "logischen 1er" muss gerade sein
- Mark: das Parity Bit wird immer auf 1 gesetzt
- Space: das Parity Bit wird immer auf 0 gesetzt
Mark und Space werden normalerweise nicht eingesetzt, da sie nicht zur Fehlererkennung beitragen.
Unabhängig vom Parity bit können wir auf einer höheren Schicht der Kommunikation weitere Fehlermechanismen, bzw. Mechanismen zur Wiederanforderung der Daten vereinbaren.
Flow Control
Ebenfalls am besten aus der historischen Sicht zu verstehen ist Flow Control.
Es geht hier darum, dass der Empfänger den Sender informiert, wann er bereit ist für weitere Daten. Beispielsweise könnte der Empfänger eine gewisse Zeit benötigen, um die Daten zu verarbeiten, oder sein Empfangsbuffer ist voll, und er kann keine weiteren Daten annehmen ohne welche zu verlieren. Zum Beispiel ein Drucker, der Zeile für Zeile langsam druckt - und nicht nachkommt, wenn die Daten zu schnell geschickt werden.
Es gibt Hardware Flow Control und Software Flow Control, oder Flow Control kann abgeschaltet werden.
Für Hardware Flow Control werden zwei zusätzliche Steuerleitungen benötigt:
- RTS = Ready to send
- CTS = Clear to send
am Pi sind diese zwei zusätzlichen Pins für beide UARTs (PL011 und miniUART) verfügbar.
Die Idee dahinter ist, dass der Sender RTS auf GND herunterzieht, wenn er senden möchte. Der Empfänger zieht im Gegenzug CTS auf GND, und signalisiert damit dass er bereit ist Daten vom Sender zu akzeptieren.
Möglich ist auch, statt RTS den Pin als RTR zu verwenden. In diesem Fall wird RTS immer als "gesetzt" angenommen (d.h. der Empfänger sollte immer sich empfangsbereit halten). RTR dient jetzt als Indikation für die Empfangsbereitschaft des Senders (für die Kommunikation in die Gegenrichtung).
Hinweis: Ich nutze hier das Wort Sender für den Computer ("DTE, Data Terminal Equipment"), da die Terminologie dem Computer das RTR bzw RTS Signal zuweist, während der Empfänger/ DCE = Data Communication Equipment das CTS Signal zugewiesen hat.
Im einfachsten Kommunikationsfall können wir auf Flow Control verzichten.
Software Flow Control:
Beim Software Flow Control werden zwei besondere Zeichen vereinbart, bspw. die ASCII Kontrollzeichen XON/XOFF.
Standardmäßig startet der Empfänger im "bereit" Modus, und empfängt Daten. Falls seine Buffer überfüllt zu werden drohen, sendet er dem Sender (auf der anderen Leitung, bei der der Empfänger selbst der Sender ist) ein XOFF Zeichen. Sobald er wieder empfangen kann, sendet er ein XON Zeichen.
Software Flow Control benötigt weniger Kabel, allerdings muss sichergestellt sein, dass die XON/XOFF Zeichen nur für diesen Zweck verwendet werden, und falls bspw. binäre Daten übertragen werden entsprechend "escaped" werden.
Putty's Flow Control Einstellungsmöglichkeiten.
- None: es wird kein Flow Control verwendet. Sinnvoll ist daher die Datenrate so zu setzen, dass der Empfänger die Daten sicher empfangen und verarbeiten kann, bzw. mit Pausen von Senderseite zu arbeiten, oder in höheren Protokollschichten Mechanismen für erneuten Versand vorzusehen
- XON/XOFF: Software Flow Control
- RTS/CTS: Hardware Flow Control mit RTS und CTS
- DSR/DTR: Hardware Flow Control mit DSR und DTR (nicht Standard, aber bei manchen Geräten) - am Pi nicht verfügbar.
RS232
RS232 ist ein Standard für serielle Kommunikation zwischen Geräten (bspw. Computer und Modem). Bei diesem Standard wird die elektrische Schnittstelle definiert, und es gibt Übereinkünfte über die Steckverbinder.
Die entsprechende aktuelle Norm stammt aus dem Jahr 1997 und heißt EIA/TIA-232-F. Das RS von RS-232 steht für "Recommended Standard". Diese früher weit verbreitete Schnittstelle wurde u.a. von USB abgelöst und wird in moderne Computer nicht mehr eingebaut. Sie ist aber in vielen Geräten als bspw. Diagnoseschnittstelle verbaut, und spielt gerade in der Industrie immer noch eine Rolle.
Wichtig ist, dass der Pi mit seinen UART Pins nicht direkt mit einem RS232 Gerät verbunden werden darf, wegen anderer Signalpegel! Mehr dazu gleich.
Heutzutage gebräuchlich ist bei RS232 der SUB-D 9-pin Stecker, beim Computer männlich:
CC-BY-SA von Afrank99, via Wikipedia
Der Stecker ist auf eine bestimmte Art und Weise belegt - wobei nicht alle Pins geschaltet werden müssen:
CC-0 von En3rGy via Wikipedia
Wichtig ist, dass die RX und TX Pins bei Sender (Computer) und Empfänger (bspw. Modem oder zweiter Computer) jeweils gekreuzt sein müssen, damit die Daten empfangen werden. Falls zwei Computer miteinander verbunden werden, werden daher sogenannte Nullmodem-Kabel eingesetzt. (Nullmodem, weil kein Modem dazwischen hängt), bei denen die RX und TX Leitungen gekreuzt sind.
Pinbelegung am Computer / Sender 1:
- 1: DCD (Data Carrier Detect)
- 2: RxD / RX (Receive Data) - Empfang an Computer 1, Sendelinie für Computer 2, bzw. Empfänger
- 3: TxD / TX (Transmit Data) - Senden für Computer 1, Empfang für Computer 2 bzw. Empfänger
- 4: DTR (Data Terminal Ready)
- 5: GND - Die Signale werden gegen diese Referenz gemessen
- 6: DSR (Data Set Ready)
- 7: RTS oder RTR (siehe oben, Hardware Flow Control) - Computer 1 ist bereit Daten zu empfangen
- 8: CTS (Clear To Send) - Empfänger bzw. Computer 2 ist bereit Daten zu empfangen (Hardware Flow Control, siehe oben)
- 9: RI (Ring Indicator)
In allen Fällen benötigt werden zur Kommunikation RX, TX und GND.
Die digitalen Signale werden durch den UART erzeugt, müssen allerdings durch einen RS232 Transceiver-Chip auf die richtigen Pegel gebracht werden.
RS232 nutzt dabei für die Datenleitungen (RX und TX) für eine logische 1 eine Spannung zwischen -3 V und -15 V, und für eine logische 0 eine Spannung von +3 V bis +15V.
Signale zwischen -3V und +3V gelten als undefiniert.
Bei den Steuerleitungen (RTS, CTS, etc.) ist der aktive Zustand (d.h. asserted) +3V bis +15V und der inaktive -3V bis -15V. Das entspricht einem Ziehen auf "GND" bzw. eine logische 0 des UARTs, entsprechend einer logischen 0 auf den Datenleitungen.
Um diese bereits erwähnten Spannungen am Sender auch bei Spannungsabfällen über das Kabel, Stecker usw. zu erreichen, muss der Empfänger mindestens eine Spannung von +5V bzw. -5V aufbauen. Üblich sind sogar +12V bzw. -12V.
Solche Spannungen würden die empfindlichen GPIO Pins am Pi grillen.
Diese sind nur für 0 - 3.3V ausgelegt.
Es muss daher ein spezieller Chip angeschlossen werden, um Kommunikation über RS232 zu ermöglichen.
Dazu gibt es entweder spezielle HATs, oder man kann sich einen Chip auf einem Breadboard aufbauen.
Interessanterweise ist es nicht unbedingt nötig eine 12 V Spannungsquelle zu haben, da manche Chips bereits integrierte Ladungspumpen haben. Ein solcher Chip ist der MAX3225E. Im sogenannten DIP Package ist dieser Chip auch verfügbar (breadboard freundlich).
Hier ist ein Schaltdiagramm für den MAX3225E:
Quelle: Datenblatt von Maxim
Dabei wird T1OUT als TxD Pin auf der RS-232 Seite genutzt, und T2OUT als RTS bzw RTR Signal.
R1IN ist RxD auf RS232 Seite, R2IN CTS.
Auf der Pi Seite werden die Pins entsprechend an die UART Pins beschaltet:
T1IN = TXD & T2IN = RTS (die zwei Sendesignale)
R1OUT = RXD & R2OUT = CTS (die zwei Empfangssignale)
Der Chip kann mit 3,3V versorgt werden und generiert sich die benötigten Spannungen für die RS232 Schnittstelle mittels Ladungspumpe selbst. Dazu benötigt er externe Kondensatoren als Zwischenspeicher für die Ladungen. Details siehe Datenblatt von Maxim.
Der Nachteil von RS232 ist dass die Signale nicht differentiell, sondern asymmetrisch ("unbalanced") übertragen werden. Sie sind auf sogenannte Gleichtaktstörungen empfindlich, die beispielsweise durch elektromagnetische Störer auf den Leitungen entstehen können. Damit ist unter anderem die maximale Länge der Leitungen limitiert. Mehr Störsicherheit bietet durch differenzielle Signale RS485.
RS232 ist eine Punkt-zu-Punkt Verbindung. Es werden genau zwei Geräte miteinander verbunden.
RS485
RS-485 auch als EIA/TIA-485 bekannt adressiert einige der Probleme mit RS232 für eine erhöhte Stabilität der Übertragung. Daher kommt es vor allem in Industrieanwendungen zum Einsatz.
Es kommen differentielle ("balanced") Signale zum Einsatz, und es können mehrere Kommunikationspartner auf dem gleichen Netzwerk miteinander sprechen. Für die differenziellen Signale werden zwei Leitungen benötigt (A und B). Eine weitere Leitung (SC / G / reference pin) als GND-Referenz ist in manchen Fällen ebenso vorhanden.
Die Signalpegel (Spannungen) sind dabei auch niedriger als bei RS-232. Spannungen zwischen -7 und +12 V sind erlaubt, der minimale Spannungslevel um ein Signal zu erkennen liegt für den Empfänger bei +/- 200 mV. Die tatsächlich auf dem Bus anliegende Spannung hängt typischerweise von der Betriebsspannung des Treibers ab (bspw. 5 V oder 3,3 V beim Pi.)
RS-485 unterstützt Datenraten von bis zu 10 Mbit/s über kurze Entfernungen (typischerweise bis zu 12 m), und bei geringeren Datenraten bis zu 1,2 km Entfernung. Als Faustregel gilt: die Geschwindigkeit in bit/s * Länge in m sollte 10^8 nicht überschreiten, ein 50 m langes Kabel kann also mit maximal 2 Mbit/s betrieben werden. Es sind standardmäßig bis zu 32 Teilnehmer auf der Leitung möglich, bei Einsatz spezieller Transceiver-Bausteine auch mehr.
Auch RS-485 benutzt UARTs als Eingang für RS-485 Treiber, die das UART Signal in RS-485 konforme differentielle Signale umsetzen. Dabei ist der binäre Zustand der Leitung 1 oder "MARK", wenn die Leitung A (auch: TX-/RX-/D-) im Hinblick auf die Leitung B (auch: TX+/RX+/D+) negativ ist, und 0 oder "SPACE" wenn B im Hinblick auf A negativ ist. Der RS-485 Empfänger wandelt das Eingangssignal entsprechend wieder um.
Bild: Wikipedia, CC-BY-SA 3.0 Royvegard
Das Bild zeigt die Zustände der Leitungen für asynchrone Kommunikation mit Hilfe von zwei UARTs, an denen auf beiden Seiten RS-485 Transceiver angeschlossen sind. Aus dem Idle Modus ("Mark") wird die Leitung, wie beim UART diskutiert, auf den SPACE Zustand gezogen (eine 0) - das Start Bit. Anschließend erfolgt eine Übertragung von in diesem Fall 8 bits, und das Stop Bit (auch als Mark - der Zustand der logischen 1, wie beim UART). Danach ist die Leitung erneut im Ruhezustand. Durch mindestens einen Übergang pro gesendeten Frame kann sichergestellt werden, dass der Empfänger-UART nicht mit dem Zählen der empfangenen gleichen Bits durcheinander kommt und verrutscht.
Da die Treiber bei RS-485 selektiv auf die gemeinsame Leitung aufgeschaltet werden können (bspw. Pin "Driver Enable" beim Texas Instruments SN65HVD1782 ), ist bidirektionale Kommunikation auch über nur ein Leitungspaar möglich (sogenannter "Half-Duplex"-Modus bei RS485). Diese zwei Leitungen werden üblicherweise, um Störungen zu minimieren, miteinander verdrillt - "Twisted Pair".
Es gibt auch Implementationen wo zwei Leitungspaare genutzt werden, dabei wird eine Master / Slave Architektur (ein Master, mehrere Slaves) aufgebaut.
RS-485 spezifiziert nur den physikalischen Layer. Es gibt kein Kommunikationsprotokoll und keine Stecker vor, nur ein elektrisches Interface. Einige andere Protokolle, beispielsweise Modbus, und Profibus, basieren auf RS-485 und definieren Stecker, Datenübertragungsformate, etc.
Ein wichtiger Punkt ist bei dem Bus die Topologie. Es werden hier am Hauptbus über kurze Stichleitungen die einzelnen Teilnehmer angeschlossen. Der Bus sollte zur Vermeidung von Reflexionen an beiden Seiten mit Abschlusswiderständen versehen werden. Typischerweise werden dazu 120 Ω zwischen A und B geschaltet. Bei sehr kurzen Leitungslängen (z.B. für Testversuche auf dem Breadboard) oder sehr geringen Datenraten kann man den Abschlusswiderstand weglassen, da die fehlerfreie Datenübertragung durch die Reflexionen in diesen Fällen weniger stark behindert wird.
Die DMX-Schnittstelle, die zur profesionellen Lichtsteuerung in zum Beispiel Theatern benutzt wird, basiert auf RS-485.
Further Reading
- https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter
- https://de.wikipedia.org/wiki/Universal_Asynchronous_Receiver_Transmitter
- https://en.wikipedia.org/wiki/Serial_port
- https://en.wikipedia.org/wiki/RS-232
- https://en.wikipedia.org/wiki/Software_flow_control
- https://en.wikipedia.org/wiki/RS-485
- https://de.wikipedia.org/wiki/EIA-485
- Transceiver der bis zu 100 Mbps und den Profibus unterstützt: https://www.renesas.com/eu/en/www/doc/datasheet/isl3259e.pdf
3 Kommentare
Luciano
Endlich ein Artikel, der die serielle Kommunikation umfassend und verständlich erklärt ohne abzuschweifen. Vielen Dank dafür!
Han Eric
sehr überschaubar und verständlich, vielen DANK
Ron
Super Artikel, sehr verständlich geschrieben. Vielen Dank dafür!