3.19. UART im Code¶
machine.UART kapselt einen Hardware-UART-Kanal. Erstellen Sie einen mit der Bus-ID und einer Baudrate; alles andere hat sinnvolle Standardwerte:
from machine import UART
uart = UART(3, baudrate=115200)
Die id wählt aus, welcher Hardware-UART verwendet wird; der Wert hängt vom Board ab (siehe die OpenMV-Boards für die verfügbaren Busnummern und Pin-Zuordnungen auf einer bestimmten Kamera). Das Standard-Frame-Format ist 8 Datenbits, keine Parität, ein Stoppbit – das „8N1“, das jeder erwartet.
3.19.1. Schreiben und Lesen¶
Die UART-Ausgabe erfolgt über write():
uart.write("hello\n")
uart.write(b"\x01\x02\x03")
write akzeptiert entweder einen str (als UTF-8 kodiert) oder ein bytes / bytearray. Es kehrt sofort zurück, sobald die Bytes im TX-Puffer eingereiht sind; die Hardware schließt das Heraustakten im Hintergrund ab.
Das Lesen erfolgt über drei Methoden, je nach Bedarf:
n = uart.any() # bytes available to read right now
data = uart.read(8) # up to 8 bytes, or None on timeout
line = uart.readline() # bytes ending in '\n', or None on timeout
any() prüft den RX-Puffer ohne zu blockieren. read() liest eine feste Anzahl von Bytes und gibt None zurück, wenn vorher das Zeitlimit (konfigurierbar über das Argument timeout im Konstruktor) abläuft. readline() liest bis einschließlich des nächsten Zeilenumbruchs, nützlich für zeilenbasierte Protokolle.
Eine einfache Schleife, die alles zurücksendet, was sie empfängt:
uart = UART(3, baudrate=115200, timeout=100)
while True:
if uart.any():
data = uart.read()
uart.write(data)
read() ohne Längenargument liest weiter Bytes, bis die Empfangsleitung für das konfigurierte timeout ruhig bleibt, und gibt dann das Angesammelte zurück. Mit timeout=100 gibt jeder Aufruf hier einen Schwall von Bytes zurück – alles, was der Sender ohne eine Lücke von 100 ms zwischen den Bytes herausgetaktet hat. Ohne Zeitlimit hätte der Aufruf kein Signal dafür, dass der Sender fertig ist, und könnte unbegrenzt hängen.
3.19.2. Binärdaten mit struct¶
Ganzzahlen und Gleitkommazahlen über die Leitung zu senden, ist genau das, wofür das Modul struct da ist. Es packt Werte fester Breite mithilfe eines Formatstrings, der die Byte-Reihenfolge und den Typ jedes Feldes benennt, in ein bytes-Objekt:
import struct
uart.write(struct.pack("<lhb", count, temperature_x100, status))
Das führende "<" wählt die Little-Endian-Byte-Reihenfolge; "l" ist eine 32-Bit-Ganzzahl mit Vorzeichen, "h" ist eine 16-Bit-Ganzzahl mit Vorzeichen, "b" ist eine 8-Bit-Ganzzahl mit Vorzeichen. Die andere Seite entpackt mit dem gleichen Formatstring:
payload = uart.read(7) # 4 + 2 + 1 = 7 bytes
count, temperature_x100, status = struct.unpack("<lhb", payload)
Der Formatstring ist der Vertrag zwischen den beiden Enden. Jede Abweichung – falsche Byte-Reihenfolge, falsche Typgrößen, falsche Feldreihenfolge – erzeugt unsinnige Werte.
3.19.3. Pufferung und Lesen mit hoher Rate¶
Eine Polling-Schleife, die any() und read() aufruft, kommt mit dem meisten Datenverkehr von selbst zurecht, solange die Hauptschleife schnell genug iteriert, um den Empfangspuffer zu leeren, bevor er voll ist. Zwei Konstruktoroptionen sind wichtig, wenn die Rate steigt oder die Schleife stark beschäftigt ist.
rxbuf legt die Größe des Software-RX-Puffers fest. Der Standard liegt bei einigen hundert Bytes; bei Sensoren, die hunderte von Bytes pro Schwall ausgeben, oder wenn die Hauptschleife zwischen den Abfragen lange Arbeiten erledigt, verhindert ein größerer Puffer, dass eingehende Bytes verworfen werden, während die Schleife anderweitig beschäftigt ist:
uart = UART(3, baudrate=115200, timeout=100, rxbuf=4096)
Alles, was ankommt, während der Puffer voll ist, geht verloren; dimensionieren Sie rxbuf so, dass er die längste Lücke zwischen den Leerungen abdeckt.
Für anhaltend hohe Raten liest readinto() in einen vorab zugewiesenen Puffer, anstatt bei jedem Aufruf ein neues bytes-Objekt zurückzugeben:
buf = bytearray(256)
while True:
n = uart.readinto(buf)
if n:
process(buf, n)
Auf dem Lesepfad findet keine Zuweisung statt, was wichtig ist, wenn der Heap fragmentiert ist oder wenn die Zuweisungslatenz andernfalls das Inter-Byte-Timing-Budget überschreiten würde.