Was ist UART?

UART ist die Abkürzung für “Universal Asynchronous Receiver / Transmitter”, ein Protokoll für den seriellen Austausch von Daten zwischen zwei Geräten.

Dabei haben beide Geräte die Anschlüsse:

  • TX für Senden
  • RX für Empfangen

Man verbindet jeweils TX des einen Geräts mit RX des anderen Geräts. Daneben verbindet man GND der beiden Geräte miteinander.

Für eine Verbindung müssen Sender und Empfänger die gleichen Einstellungen haben für:

  • Baudrate
  • Anzahl der Datenbits
  • Stoppbit
  • Paritätsbit

Microcontroller wie der Raspi Pico, der ESP32 und der Arduino haben eine oder mehrere UART-Schnittstellen, ebenso der Raspberry Pi. Welchen GPIO-Ports für UART verwendet werden können, kann sieht man in der jeweiligen Pinbelegungs-Übersicht.

An PCs kann man an die USB-Schnittstelle einen USB-UART-Adapter anschließen und über diesen mit einem anderen Gerät kommunizieren.

Serielle Schnittstelle unter Linux mit USB-UART-Adapter

Unter Linux muss man zunächst den Namen der Schnittstelle (die Device-Datei) herausfinden. Hierzu gibt man folgendes vor und nach dem anstecken des Adapters auf der Kommandozeile ein:

$ ls /dev/ttyACM* /dev/ttyUSB* /dev/ttyAMA*

Der Device-Name der nach dem Anstecken dazukommt, ist der richtige. Wichtig ist zudem, daß der aktuelle Nutzer in der Linux-Gruppe dialout enthalten ist.

Für die Kommunikation über die serielle Schnittstelle gibt es spezielle Programme unter Linux beispielsweise Picocom und Minicom, unter Windows PuTTY.

Nutzung von Picocom unter Linux

Zunächst muss Picocom installiert werden, z.B. mit

$ sudo apt-get install picocom

Der Aufruf von Picocom geht dann wie folgt:

$ picocom -b 9600 -c /dev/ttyUSB0

Dabei wird mit dem Parameter -b die Baudrate angegeben, mit -c das lokale Echo angeschaltet, dass heisst, dass Eingaben im Terminal auch dort angezeigt werden. /dev/tty/USB0 ist die wie oben ermittelte Device-Datei.

Eingaben in Picocom werden nun über die serielle Schnittstelle an die Gegenstelle gesendet, Daten die von der Gegenstelle gesendet werden, werden in Picocom angezeigt.

Mit den Optionen imap, omap und emap kann man festlegen, ob bestimmte Zeichen Sonderzeichen nach dem Empfang, vor dem Versand oder für die lokale Anzeige geändert werden (zum Beispiel CR LF oder DEL BS). Details dazu findet man in der Manpage.

Picocom kann dadurch beendet werden, dass man Strg gedrückt hält und nacheinander a und x drückt.

UART Programmierung in Micropython

In Micropython kann man UART nutzen, in dem man ein Objekt der Klasse UART erzeugt. Details sind hier beschrieben: https://docs.micropython.org/en/latest/library/machine.UART.html

Beim Erzeugen des Objekts der Klasse UART gibt man die ID und die Baudrate und ggf. die genutzten Pins an. Beim Raspi Pico gibt es beispielsweise zwei UART-Schnittstellen, UART0 und UART1. UART0 kann an die Pins 0/1 (default), 12/13 oder 16/17 angeschlossen werden, UART1 an die Pins 4/5 oder 8/9. Folgendes Programm erzeugt ein Objekt für UART1 an den Pins 0 und 1 und ein Objekt für UART1 an den Pins 4 und 5.

from machine import UART, Pin
uart0 = UART(0, 9600)
uart1 = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))

Weiterer optionaler Parameter ist timeouot der Timeout in ms, der standardmäßig auf 0 steht.

Man kann sich die aktuellen Parameterwerte des UART-Objekts auch anschauen:

print(uart)

UART(0, baudrate=9600, bits=8, parity=None, stop=1, tx=0, rx=1, txbuf=256, rxbuf=256, timeout=10000, timeout_char=2, invert=None)

Zum Senden von Daten nutzt man den write-Befehl:

uart0.write('Hello world')

Für den Empfang von Daten gibt es die Methode read, der man optional die Anzahl der zu lesenden Bytes übergeben kann. Gibt man keine Anzahl an, werden soviele Bytes wie möglich gelesen. Das Lesen endet, wenn die angegebene Anzahl der Bytes oder der gesetzte Timeout erreicht wurde. Konnten keine Zeichen gelesen werden, gibt die Methode None zurück, ansonsten die gelesenen Bytes als Byte-Objekt.

Mit der Methode any erhält man die Anzahl der Bytes, die gelesen werden können:

print(uart0.any())

Mit folgendem Code werden beispielsweise kontinuierlich einzelne Bytes gelesen und ausgegeben:

while True:
    text = uart0.read(1)
    if text != None:
        print(text)

Mit nachfolgendem Beispielcode werden solange Zeichen über die Schnittstelle eingelesen bis ein bestimmtes Endzeichen (im Beispiel Carriage Return) ankommt.

def read_uart_until(termination_char):
    bytes_read = b''
    while True:
        if uart.any():
            bytes_read += (uart.read(1))
            try:
                string_read = bytes_read.decode('utf-8')
                if string_read.endswith(termination_char):
                    break
            except UnicodeError:
                # read next byte 
                pass
        sleep(0.01)
    return string_read[:-1]
while True:
    eingabe = read_uart_until("\r")
    print(eingabe)

Hierfür wird in einer Schleife mit der Methode any geprüft, ob Daten an der Schnittstelle vorliegen. Falls ja wird jeweils ein Bytes gelesen und an die bisherigen Bytes angehängt. Danach wird die Umwandlung in einen String versucht. Ist dies erfolgreich, wird geprüft, ob das letzte Zeichen dem gewählten Endzeichen entspricht. Falls ja wird die Schleife abgebrochen. Ein UnicodeError tritt dann auf, wenn noch nicht alle zu einem Zeichen gehörenden Bytes gelesen wurde, was z.B. bei Umlauten möglich ist. In diesem Fall wird dann gleich das nächste Byte gelesen.

UART am Raspberry Pi

https://www.electronicwings.com/raspberry-pi/raspberry-pi-uart-communication-using-python-and-c

UART Programmierung in Python

https://pyserial.readthedocs.io/en/latest/shortintro.html

Weitere Infos

https://medium.com/geekculture/serial-connection-between-raspberry-pi-and-raspberry-pico-d6c0ba97c7dc