24. Lektion: UART

Aus Attraktor Wiki

Wechseln zu: Navigation, Suche

Was ist ein UART?

UART steht für Universal Asynchronous Receiver Transmitter. Es handelt sich hierbei um die wohl älteste Schnittstelle in der Computerwelt. Sie wurde z.B. schon bei den Fernschreibern eingesetzt. Ursprünglich war sie für die Datenübertragung im Telefonnetz entwickelt worden.
Es gibt 2 Normen für diese serielle Schnittstelle:

  • V.24 von der ITU-T (Internatioale Fernmeldebehörde)
  • RS232 von der EIA (Electronic Industries Association)

inzwischen sind diese Normen identisch.

RS232

Uart timing.svg.png
Das Bild zeigt das Timing sehr anschaulich.

  • Der Ruhezustand ist 1
  • Die Übertragung eines Bytes wird mit einem Startbit (0) eingeleitet.
  • Dann folgen die Datenbits (5...9).
  • Anschließend kann ein optionales Paritybit gesendet werden.
  • Beendet wird die Übertragung eines Bytes mit dem Stopbit (1)
    • Das Stopbit kann 1, 1.5 oder 2 Bit lang sein.

Die Übertragungsgeschwindigkeit wird in Baud (Bits/Sekunde) angegeben.

Übliche Bitraten
Bitrate Bitdauer
50 bit/s 20,0 ms
110 bit/s 9,09 ms
150 bit/s 6,67 ms
300 bit/s 3,33 ms
1.200 bit/s 833 µs
2.400 bit/s 417 µs
4.800 bit/s 208 µs
9.600 bit/s 104 µs
19.200 bit/s 52,1 µs
38.400 bit/s 26,0 µs
57.600 bit/s 17,4 µs
115.200 bit/s 8,68 µs
230.400 bit/s 4,34 µs
460.800 bit/s 2,17 µs
921.600 bit/s 1,08 µs
2.000.000 bit/s 500 ns
3.000.000 bit/s 333 ns

Parity-Bit

Das Parity-Bit ist eine einfache Fehlerprüfung.

even
das Parity-Bit ergänzt die Anzahl High-Bits zu einer geraden Zahl.
odd
das Parity-Bit ergänzt die Anzahl High-Bits zu einer ungeraden Zahl.


Pegel

Die Spannungspegel sind wie folgt zugeordnet:

  • Logisch 0 - Space = +12V
  • Logisch 1 - Mark = -12V

Die Spannung kann 3...15V betragen.

In der Microcontrollertechnik wird der Aufwand der positiven und negativen Spannungen nicht realisiert. Hier wird die Betriebsspannung (5V, 3V3) als Logisch 1 und GND als Logisch 0 verwendet. Das wird als TTL bezeichnet, in Anlehung an die digitale TTL Famile, die mit 5 Volt arbeitet.

Synchron/Asynchron

Synchron

Bei einer synchronen Übertragung werden Daten und Takt parallel übertragen, so dass die Daten immer mit dem Takt synchron sind. Selbst bei langen Übertragungen werden die Daten immer zum richtigen Zeitpunkt abgetastet. Beispiele hier für sind SPI und I2C.

Asynchron

Bei der asynchronen Übertragung wird kein Takt übertragen. Deshalb muss das Timing von Sender und Empfänger sehr genau sein. Da sich ein Auseinanderlaufen des Timings bei Sender und Empfänger nicht vermeiden lässt, muss bei der asynchronen Übertragung immer wieder eine Synchronisierung zwischen Sender und Empfänger stattfinden. Dazu dient das Startbit. Wenn es vom Empfänger dedektiert wird beginnt sein Timing bei Null. Die Übereinstimmung vom Sender- und Empfängertiming muss dann nur noch << 8% sein.

Die Klasse UART in Micropython

Die Klasse UART befindet sich im Modul machine. Sie enthält folgende Methoden:

>>> dir(machine.UART)
['__class__', '__name__', 'any', 'read', 'readinto', 'readline', 'write', '__bases__', '__dict__', 'CTS', 'INV_RX', 'INV_TX', 'RTS', 
'deinit', 'flush', 'init', 'sendbreak', 'txdone']
>>> 

Eine Instanz erzeugen

machine.UART(id, ...)
Hiermit wird eine Instanz der Klasse UART erzeugt. Es können folgende Parameter übergeben werden:
baudrate
is the clock rate.
bits
is the number of bits per character, 7, 8 or 9.
parity
is the parity, None, 0 (even) or 1 (odd).
stop
is the number of stop bits, 1 or 2.

Weitere Schlüsselwort Parameter sind:

tx
specifies the TX pin to use.
rx
specifies the RX pin to use.
rts
specifies the RTS (output) pin to use for hardware receive flow control.
cts
specifies the CTS (input) pin to use for hardware transmit flow control.
txbuf
specifies the length in characters of the TX buffer.
rxbuf
specifies the length in characters of the RX buffer.
timeout
specifies the time to wait for the first character (in ms).
timeout_char
specifies the time to wait between characters (in ms).
invert
specifies which lines to invert.
    • 0 will not invert lines (idle state of both lines is logic high).
    • UART.INV_TX will invert TX line (idle state of TX line now logic low).
    • UART.INV_RX will invert RX line (idle state of RX line now logic low).
    • UART.INV_TX | UART.INV_RX will invert both lines (idle state at logic low).
UART.init(baudrate=9600, bits=8, parity=None, stop=1, *, ...)
Ändert die Einstellungen einer vorhandenen Instanz.
UART.deinit()
Schaltet die UART-Instanz aus. Sie lässt sich mit init() nicht wieder einschalten. Sie muss neu angelegt werden.

Datenfluß Kontrolle

flow gibt an, welche Hardware-Flusssteuerungssignale verwendet werden sollen. Der Wert ist eine Bitmaske.

0
will ignore hardware flow control signals.
UART.RTS
will enable receive flow control by using the RTS output pin to signal if the receive FIFO has sufficient space to accept more data.
UART.CTS
will enable transmit flow control by pausing transmission when the CTS input pin signals that the receiver is running low on buffer space.
UART.RTS | UART.CTS
will enable both, for full hardware flow control.

Methoden zum Datentransfer

UART.any()
Gibt eine ganze Zahl zurück, die die Anzahl der Zeichen zählt, die ohne Blockierung gelesen werden können. Sie gibt 0 zurück, wenn keine Zeichen verfügbar sind, und eine positive Zahl, wenn es Zeichen gibt. Die Methode kann auch dann 1 zurückgeben, wenn mehr als ein Zeichen zum Lesen verfügbar ist.
UART.read([nbytes])
Zeichen lesen. Wenn nbytes angegeben ist, werden maximal so viele Bytes gelesen, andernfalls werden so viele Daten wie möglich gelesen. Es kann früher zurückkehren, wenn eine Zeitüberschreitung erreicht wird. Die Zeitüberschreitung ist im Konstruktor konfigurierbar.
Rückgabewert: ein Byte-Objekt, das die eingelesenen Bytes enthält. Bei Zeitüberschreitung wird nichts zurückgegeben.
UART.readinto(buf[, nbytes])
Liest Bytes in den buf. Wenn nbytes angegeben ist, werden höchstens so viele Bytes gelesen. Andernfalls werden höchstens len(buf) Bytes gelesen. Es kann früher zurückkehren, wenn eine Zeitüberschreitung erreicht wird. Die Zeitüberschreitung ist im Konstruktor konfigurierbar.
Rückgabewert: Anzahl der gelesenen und in buf gespeicherten Bytes oder None bei Timeout.
UART.readline()
Liest eine Zeile, die mit einem Newline-Zeichen endet. Sie kann früher zurückkehren, wenn eine Zeitüberschreitung erreicht wird. Die Zeitüberschreitung ist im Konstruktor konfigurierbar.
Rückgabewert: die gelesene Zeile oder Keine bei Zeitüberschreitung.
UART.write(buf)
Schreibt den Puffer mit Bytes auf den Bus.
Rückgabewert: Anzahl der geschriebenen Bytes oder Keine bei Timeout.
UART.sendbreak()
Senden einer Unterbrechungsbedingung auf dem Bus. Dadurch wird der Bus für eine längere Zeit als für die normale Übertragung eines Zeichens erforderlich auf einen niedrigen Pegel gebracht.
UART.flush()
Waits until all data has been sent. In case of a timeout, an exception is raised. The timeout duration depends on the tx buffer size and the baud rate. Unless flow control is enabled, a timeout should not occur.
    • Note
    • For the rp2, esp8266 and nrf ports the call returns while the last byte is sent. If required, a one character wait time has to be added in the calling script.
UART.txdone()
Tells whether all data has been sent or no data transfer is happening. In this case, it returns True. If a data transmission is ongoing it returns False.
    • Note
    • For the rp2, esp8266 and nrf ports the call may return True even if the last byte of a transfer is still being sent. If required, a one character wait time has to be added in the calling script.
UART.irq()
ist im Pico nicht implementiert.

UART im Raspberry Pi Pico W

In Microcontrollern sind häufig USART's implementiert. Das sind UART's die auch synchrone Übertragungen ermöglichen.
Es gibt im Pico 2 UARTs, UART0 und UART1. Die möglichen Pins für die UARTs zeigen die Tabellen.

Pins für UART0
TX RX
0 1
12 13
16 17
Pins für UART1
TX RX
4 5
8 9

Versuchsaufbau mit dem Demoboard

Um auf dem Demoboard die serielle UART-Schnittstelle zu testen, muss an den Expansion-Anschlüssen eine Stecker- oder Buchsenleiste eingelötet werden. Dann müssen folgende Anschlüsse miteinander verbinden werden:
GP 0 -> GP 5
GP 1 -> GP 4
Demoboard_UART

Eine UART-Instanz erzeugen

Die default Einstellungen für UARTs sind:

>>> print(UART(0))
    UART(0, baudrate=115200, bits=8, parity=None, stop=1, tx=0, rx=1, txbuf=256, rxbuf=256, timeout=0,
 timeout_char=1, invert=None)

>>> print(UART(1))
    UART(1, baudrate=115200, bits=8, parity=None, stop=1, tx=4, rx=5, txbuf=256, rxbuf=256, timeout=0,
 timeout_char=1, invert=None)

Eine Instanz von UART hat folgende Eigenschaften:

>>> from machine import UART
>>> uart = UART(0)
>>> print(uart)
    UART(0, baudrate=115200, bits=8, parity=None, stop=1, tx=0, rx=1, txbuf=256, rxbuf=256, timeout=0,
 timeout_char=1, invert=None)

Wie man oben sieht ist die Initialisierung unter Verwendung der Defaultparameter sehr einfach.

Für unsere weiteren Versuche muss immer zuerst folgender Code ausgeführt werden:

import machine
uart0 = machine.UART(0)
uart1 = machine.UART(1)

Senden

Im einfachsten Fall wird einfach ein String zur Übertragung angegeben:

>>> uart0.write('Hallo!')
    6

Zahlen können nicht so einfach übertragen werden. Sie müssen erst in einen String umgewandelt werden:

>>> uart0.write(123456)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: object with buffer protocol required

>>> uart0.write(str(123456))
    6

Empfangen

>>> import machine
    uart0 = machine.UART(0)
    uart1 = machine.UART(1)

>>> uart0.write('Hallo!')
    6
>>> uart1.read()
    b'Hallo!'

# oder:
>>> uart1.read().decode('utf-8')
    'Hallo!'

Für die Übertragung muss der String in eine Bytefolge (Bytestring - b' ') umgewandelt werden. Das macht die .write() Methode intern. Die .read() Methode wandelt diesen aber nicht wieder zurück.
Für die Umwandlung von Bytes zurück in einen String gibt es zwei Wege:

# mit str()
>>> str(b'Hallo!', 'utf-8')
    'Hallo!'

# mit decode()
>>> b'Hallo!'.decode('utf-8')
'Hallo!'

Bytes in Python

Bytes haben in Python, anders als in anderen Programmiersprachen, eine besondere Aufgabe. Sie dienen nur zur Aufbereitung von Werten für die serielle Datenübertragung. Hierbei werden die Werte Byteweise übertragen und die Werte dazu in Bytes zerlegt.
Mit den Funktionen/Methoden bytes(), bytearray(), .to_bytes(), .from_bytes() oder .encode() und .decode() werden Werte in eine Reihe von Bytes übersetzt:

Zur Umwandlung von Bytes in String oder Integer stehen die Funktionen .decode(), str() und int.from_bytes() zur Verfügung.

int.from_bytes() hat drei mögliche Parameter. Währen das 2. und 3. in CPython als Keyword-Argument übergeben wird, sind in Micropython alle drei Position-Arguments.

>>> bytes = b'\xAA\x00\x00\x00'
    integer = int.from_bytes(bytes, 'little', True)
    print(integer)
170

Bytes und Strings

Möglichkeiten Strings in eine Bytefolge zu konvertieren:

>>> text = 'Hallo Micropython auf dem Pico W.'

>>> st1 = bytes(text, 'utf-8')
>>> st1
b'Hallo Micropython auf dem Pico W.'

>>> st2 = bytearray(text, 'utf_8')
>>> st2
bytearray(b'Hallo Micropython auf dem Pico W.')

>>> st3 = text.encode('utf-8')
>>> st3
b'Hallo Micropython auf dem Pico W.'

Von einer Bytefolge zurück zum String:

>>> rt1 = str(st1, 'utf-8')
>>> rt1
'Hallo Micropython auf dem Pico W.'

>>> rt2 = st1.decode('utf-8')
>>> rt2
'Hallo Micropython auf dem Pico W.'

# geht auch vom bytearray:
>>> rt3 = str(st2, 'utf-8')
>>> rt3
'Hallo Micropython auf dem Pico W.'

>>> rt4 = st2.decode('utf-8')
>>> rt4
'Hallo Micropython auf dem Pico W.'

Wenn String verarbeitet werden muss immer die Codierung angegeben werden! Bei Micropython ist das meist 'utf-8'.

Bytes und Integer

Integer in Bytearray/Bytes umwandeln.

>>> bytearray(5)
bytearray(b'\x00\x00\x00\x00\x00')

>>> bytearray([5])
bytearray(b'\x05')

>>> bytearray((5,))
bytearray(b'\x05')

>>> x = 5

>>> x.to_bytes(5, 'big')
b'\x00\x00\x00\x00\x05'

>>> x.to_bytes(1, 'big')
b'\x05'

>>> 5.to_bytes(1, 'big')
Traceback (most recent call last):
  File "<stdin>", line 1
SyntaxError: invalid syntax for number

vom Bytearray zurück zum Integer

>>> y = bytearray(5)
>>> y
bytearray(b'\x00\x00\x00\x00\x00')

>>> y[0] = 7
>>> y
bytearray(b'\x07\x00\x00\x00\x00')

>>> int.from_bytes(y, 'big')
30064771072
>>> hex(int.from_bytes(y, 'big'))
'0x700000000'

Bytes und Listen

Liste in bytearray und zurück: (Werte: 0 ... 255)

>>> x = [1,2,3,4,5]

>>> y = bytearray(x, 'big')

>>> y
bytearray(b'\x01\x02\x03\x04\x05')

>>> list(y)
[1, 2, 3, 4, 5]

Aber Achtung:

>>> int.from_bytes(y, 'big')
4328719365
>>> hex(int.from_bytes(y, 'big'))
'0x102030405'

Wenn man nicht aufpasst wird aus der Liste eine Zahl!

Links:


Navigation

Zurück zur "Micropython Kurs 2023 Teil 2" Startseite
Zurück zur "Micropython Kurs 2023" Startseite
Zurück zur Programmieren Startseite
Zurück zur Wiki Startseite

Diese Seite wurde zuletzt am 29. Januar 2024 um 15:38 Uhr geändert. Diese Seite wurde bisher 1.776 mal abgerufen.