Der Einplatinencomputer Raspberry Pi bietet durch den Ethernet-Port (Raspberry Pi Modell B/B+/2) und die GPIO-Pins zahlreiche Möglichkeiten zur Kommunikation und Ansteuerung diverser Schaltungen und Bauelemente. Die GPIO-Pins können dabei als digitale Ein- und Ausgänge genutzt werden und unterstützen eine Reihe von gängigen Schnittstellen. Eine davon ist die serielle UART-Schnittstelle. UART kommt bei der Kommunikation zwischen Mikrocontrollern und Computern häufig zur Anwendung. In diesem Artikel wird gezeigt, wie man die UART-Schnittstelle am Raspberry Pi zugänglich macht und in einem C-Konsolenprogramm verwendet.
UART aktivieren
Unter Raspbian wird die UART-Schnittstelle standardmä0ig als serielle Konsole bereitgestellt. Damit wir die UART-Schnittstelle in unseren eigenen Anwendungen nutzen können können, müss die Funktion der seriellen Konsole deaktiviert werden. Dazu editieren wir die Datei /boot/cmdline.txt mit nano.
sudo nano /boot/cmdline.txt
Aus der Datei /boot/cmdline.txt entfernen wir folgenden Teil:
console=ttyAMA0,115200 kgdboc=ttyAMA0,115200
Ist dies erledigt sollte anschließend der im Bild gezeigte Ausdruck noch vorhanden sein. Nach dem editieren kann die Datei gespeichert und geschlossen werden.
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
Anschließend bearbeiten wir die Datei /etc/inittab.
sudo nano /etc/inittab
Am Ende der Datei befindet sich eine Zeile mit folgendem Inhalt:
T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
Diese Zeile müssen wir durch einfügen des Zeichens # (Raute) am Zeilenanfang auskommentieren.
Haben wir die Zeile erfolgreich auskommentiert, kann die Datei mit der Tastenkombination Strg+X, Y und Enter gespeichert und geschlossen werden. Damit dir Einstellungen übernommen und aktiviert werden muss der Raspberry Pi neugestartet werden.
sudo reboot
Jetzt wurde die UART-Schnittstelle auf dem Pi aktiviert und kann verwendet werden.
UART Raspberry Pi: Beschaltung und Pinbelegung
Soll eine Kommunikation vom Raspberry Pi zu einem anderen kompatiblem Gerät via UART aufgebaut werden, so müssen die entsprechenden UART-GPIO-Pins am Raspberry Pi beschalten werden. Zur Kommunikation benötigen wir lediglich 3 Pins am Pi:
- Pin 8: TxD, Sendeleitung
- Pin 10: RxD, Empfangsleitung
- Pin 6: GND, Masse
Zur Kommunikation mit einem anderen Gerät wird der Sendepin mit dem Empfangspin des Kommunikationspartnes und umgekehrt verbunden. Außerdem wird eine Verbindung zwischen 2 GND-Pins (Masse) hergestellt.
Achtung! Die UART-Schnittstelle des Raspberry Pi arbeitet mit einem Pegel vin 3,3 Volt. Viele andere Geräte hingegen nutzen einen Pegel von 5 Volt. Will man zwischen diesen Geräten eine UART-Kommunikation aufbauen, so muss ein Pegelwandler dazwischen geschalten werden, um den Pi nicht zu beschädigen oder zu zerstören.
UART: Senden und Empfangen in C
Nachdem wir UART aktiviert haben, kann dieses beliebig genutzt werden. Neben einigen Tools die eine Kommunikation via UART ermöglichen, kann man in vielen gängigen Programmiersprachen das Senden und Empfangen realisieren. In diesem Tutorial zeigen wir ein kleines Beispielprogramm in der Sprache C, welches Nachrichten via UART senden und empfangen kann. Dabei handelt es sich lediglich um einen möglichen Ansatz.
Da wir mit einem Raspberry Pi gleichzeitig senden und empfangen, können wir problemlos den Sende- und Empfangspin direkt verbinden (kurzschließen).
Zu Beginn verbinden wir uns mittels SSH mit dem Raspberry Pi und erstellen eine neue C-Quellcode-Datei mit dem Editor nano. Alternativ kann die Datei an einem anderen Computer mit einem entsprechendem Editor erstellt und via USB-Stick oder über FTP auf den Pi übertragen werden.
sudo nano uart.c
Im folgenden wird der Quellcode-Ansatz vorgestellt.
Quellcode in C
Zunächst wird der C-Quellcode gesamt vorgestellt. Dieser bindet notwendige Bibliotheken ein und initialisiert die UART-Schnittstelle. Anschließend wird eine Nachricht, also einige Bytes, über den TX-Pin gesendet und gleich wieder über den RX-Pin empfangen.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> int main() { int uart0_filestream = -1; uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY); if (uart0_filestream == -1) { printf("[ERROR] UART open()\n"); } struct termios options; tcgetattr(uart0_filestream, &options); options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; options.c_iflag = IGNPAR; options.c_oflag = 0; options.c_lflag = 0; tcflush(uart0_filestream, TCIFLUSH); tcsetattr(uart0_filestream, TCSANOW, &options); // Bytes senden unsigned char BUF_TX[20]; unsigned char *TX; TX = &BUF_TX[0]; *TX++ = 'R'; *TX++ = 'a'; *TX++ = 's'; *TX++ = 'p'; *TX++ = 'b'; *TX++ = 'e'; *TX++ = 'r'; *TX++ = 'r'; *TX++ = 'y'; *TX++ = ' '; *TX++ = 'P'; *TX++ = 'i'; if (uart0_filestream != -1) { int out = write(uart0_filestream, &BUF_TX[0], (TX - &BUF_TX[0])); // if (out < 0) { printf("[ERROR] UART TX\n"); } else { printf("[STATUS: TX %i Bytes] %s\n", out, BUF_TX); } } // if uart0 sleep(1); // Bytes empfangen if (uart0_filestream != -1) { unsigned char BUF_RX[50]; int rx_length = read(uart0_filestream, (void*)BUF_RX, 50); if (rx_length < 0) { printf("[ERROR] UART RX\n"); } else if (rx_length == 0) { printf("[ERROR] UART RX - no data\n"); } else { BUF_RX[rx_length] = '\0'; printf("[STATUS: RX %i Bytes] %s\n", rx_length, BUF_RX); } //rx_length check } //if uart0 close(uart0_filestream); return 0; }
Der Quellcode kurz erklärt
Mit der Funktion open() initialisieren wir die UART-Schnittstelle und legen einige Eigenschaften fest. In unserem Fall muss der Funktion der Pfad gegeben werden, über welchen die UART-Schnittstelle ansprechbar ist. Außerdem initialisieren wir UART im nichtblockierendem Schreib- und Lesemodus, d.h. wir ignorieren den Status von DCD.
uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY);
Weiterhin legen wir die Baudrate auf 9.600 bit/s fest und irgnorieren Parity-Fehler.
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; options.c_iflag = IGNPAR; options.c_oflag = 0; options.c_lflag = 0;
Für eine konkrete Anwendung sollten alle Eigenschaften entsprechend gesetzt und mit dem Kommunikationspartner abgestimmt werden. Sowohl unter Wikipedia, als auch bei Mikrocontroller.net kann die genaue Funktionsweise von UART nachgelesen werden. Weiterhin gibt es im Artikel von Netzmafia eine detaillierte Übersicht über die einzelnen Optionen beim initialisieren der UART-Schnittstelle.
Im weiteren Verlauf ermöglichen wir das Senden von Bytes über den TX-Pin. Dabei erstellen wir eine Variable vom Datentyp / Name char BUF_TX[20]. Mit Hilfe eines Zeigers TX schreiben wir in die erzeugte Variable die Zeichenkette Raspberry Pi, welche wir anschließend senden wollen. Die Funktion write() übernimmt dabei das Senden, d.h. das Schreiben von Bytes auf die Leitung. Der Funktion muss die initialisierte UART-Schnittstelle (Filedeskriptor), als auch das Start- und das End-Byte übergeben werden. Der Integer out zählt dabei die geschriebenen Bytes.
// Bytes senden unsigned char BUF_TX[20]; unsigned char *TX; TX = &BUF_TX[0]; *TX++ = 'R'; *TX++ = 'a'; *TX++ = 's'; *TX++ = 'p'; *TX++ = 'b'; *TX++ = 'e'; *TX++ = 'r'; *TX++ = 'r'; *TX++ = 'y'; *TX++ = ' '; *TX++ = 'P'; *TX++ = 'i'; if (uart0_filestream != -1) { int out = write(uart0_filestream, &BUF_TX[0], (TX - &BUF_TX[0])); // if (out < 0) { printf("[ERROR] UART TX\n"); } else { printf("[STATUS: TX %i Bytes] %s\n", out, BUF_TX); } }
Im nächsten Zug werden die Bytes empfangen. Für das Empfangen von Bytes wird normalerweise die Funktion read() verwendet. Dieser wird der Filedeskriptor, ein Buffer und die maximale Anzahl an zu empfangenen Bytes übergeben. Der Integer rx_length Zählt dabei die ankommenden Bytes. Diese Zahl muss anschließend überprüft werden: Ist die Anzahl der Bytes kleiner als Null, dann ist irgendetwas falsch gelaufen. Ist diese gleich oder größer Null, dann wurde keine Bytes (0) oder mehrere empfangen.
if (uart0_filestream != -1) { unsigned char BUF_RX[50]; int rx_length = read(uart0_filestream, (void*)BUF_RX, 50); if (rx_length < 0) { printf("[ERROR] UART RX\n"); } else if (rx_length == 0) { printf("[ERROR] UART RX - no data\n"); } else { BUF_RX[rx_length] = '\0'; printf("[STATUS: RX %i Bytes] %s\n", rx_length, BUF_RX); } //rx_length check } //if uart0
Quellcode Testen
Den nun erstellten Quellcode haben wir wie beschrieben entweder mit dem Editor nano generiert, oder manuell von einem anderen Computer auf den Pi übertragen in das verzeichnis /home/pi. Jetzt ist es Zeit für einen Test der Konsolenanwendung. Dazu müssen wir das Programm mit gcc kopilieren.
sudo nano gcc -o uart uart.c
Wenn das Testprogramm ordnungsgemäß arbeitet, dann sollte im ersten Moment die Ausgabe der Nachricht Raspberry Pi und die Anzahl der gesendeten Bytes erscheinen. Anschließend wird die Nachricht gelesen und es erfolgt eine RX-Ausgabe mit den gleichen Daten. Zum ausführen geben wir folgenden Befehl ein.
sudo ./uart
Im folgenden Bild ist die entsprechende Terminalausgabe dargestellt.
UART senden und empfangen mit Minicom
Alternativ zu einem eigenem Quellcode kann das Tool Minicom zum senden und empfangen via UART genutzt werden. Zum installieren geben wir folgenden Befehl ein.
sudo apt-get install minicom
Zum Ausführen des Tools wird der anschließende Befehl verwendet. Dabei wird mit dem Parameter -b die Baudrate und mit dem Parameter -D das Verzeichnis der UART-Schnittstelle definiert.
sudo minicom -b 115200 -D /dev/ttyAMA0 -o
Kommunikation zwischen 2 RPis mit Minicom
Theoretisch könnte man jetzt an einem Pi den Sende- und Empfangspin kurzschließend und das was man reinschreibt gleich wieder empfangen. An dieser stelle soll jetzt aber eine Nachricht von einem Pi auf einen anderen Pi gesendet werden. Beide Raspberry Pis müssen dazu entsprechend UART aktiviert und Minicom installiert haben. Die Verbindung wird wie folgt im Ausgeschalteten Zustand verkabelt:
Der Sendepin wird mit dem Empfangspin des anderen Gerätes verbunden und umgekehrt. Außerdem wird eine Verbindung zwischen 2 GND-Pins (Masse) hergestellt.
Ist Minicom installiert und die Verkabelung erledigt, dann kann wie gerade vorgestellt auf beiden Pis Minicom gestartet werden.
sudo minicom -b 115200 -D /dev/ttyAMA0 -o
Quellen: Wikipedia, Mikrocontroller.net, Netzmafia
Hallo,
mein Problem ist, dass wenn ich die Zeile ausführe ich in ein leeren Editor weitergeleitet werde. Per str + X komme ich dann zum Editor wo sich mein Code befindet und ausführen möchte mein Raspi 3 auch nicht.
Hi Tony, Deine Seite ist echt klasse!
Hast Du auch schon Erfahrung mit dem Funk Antennen Modul 4 Pi gesammelt?
Auch bekannt unter dem Namen FAM4PI.
Viele Grüße
Frank
Leider noch nicht. Das Modul war mir so noch nicht bekannt. Werde ich mir mal anschauen :) Danke für den Hinweis.
Hallo Tony, ich habe da eine Frage: wie könnte man ein Programm realisieren das von Windows PC via USB Kabelverbindung mit dem RPI kommuniziert ? also sowohl Daten liest und sendet ?
für Info wäre ich sehr dankbar, Gruss Peter
Hallo Peter,
eine Möglichkeit wäre ein USB-Seriell-Konverter mit dem USB auf UART übersetzt wird. Da sollte es schon was fertiges geben. Gängige ICs sind meiner Meinung nach der CP2102 und der FT232RL. Bei einigen fertigen Konverter-Modulen habe ich die schon verbaut gesehen.
Hallo Tony
Wenn man anstelle den ASCII einen hex code übertragen möchte was muss man dann beachten?
Also anstelle ‘R’ den HEX Code von R also 0x52
Danke für deine Antwort
Hallo, das ist jetzt schon ein paar Jahre älter, aber immernoch interessant und daher möchte ich mich bedanken und gleichzeitig auf ein paar Dinge hinweisen.
Ein Pegelwandler ist zwar eine Option, die besste Variante wäre aber eine Entkopplung mittels Optokoppler. Gute Erklärungen gibt es im Zusammenhang mit MIDI, wo meist der 6N138 empfohlen wird, mit dem ich sehr gute Ergebnisse auch bei höheren als den gewöhnlichen UART-Takten hatte. Durch anpassen der Wiederstände kann man dann wahlweise den 5v oder 3V3 Anschluss des RPis benutzen.
Auf den gleichen Pins also dem Device ttyAMA0 wird auch der serielle Anschluss herausgeführt. Mit dem Programm raspi-config sollte man daher sicherstellen, dass UART an und serial ausgeschaltet ist, damit uns dort keine Pakete an andere Handler kommen.
Ansonsten vielen Dank!