3.19. UART in code

machine.UART omhult één UART-hardwarekanaal. Maak er een aan met de bus-id en een baudrate; al het overige heeft redelijke standaardwaarden:

from machine import UART

uart = UART(3, baudrate=115200)

De id selecteert welke hardware-UART gebruikt wordt; de waarde hangt af van het board (zie de OpenMV-boards voor de beschikbare busnummers en pintoewijzingen op een bepaalde cam). Het standaard frameformaat is 8 databits, geen pariteit, één stopbit – het “8N1” dat iedereen verwacht.

3.19.1. Schrijven en lezen

UART-uitvoer gaat via write():

uart.write("hello\n")
uart.write(b"\x01\x02\x03")

write accepteert ofwel een str (gecodeerd als UTF-8) ofwel een bytes / bytearray. Het keert direct terug zodra de bytes in de TX-buffer zijn geplaatst; de hardware klokt ze op de achtergrond verder naar buiten.

Lezen gebeurt via drie methoden, afhankelijk van wat er nodig is:

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() controleert de RX-buffer zonder te blokkeren. read() leest een vast aantal bytes en retourneert None als de timeout (configureerbaar via het timeout-argument in de constructor) eerst verstrijkt. readline() leest tot en met de volgende newline, handig voor regelgebaseerde protocollen.

Een eenvoudige lus die alles teruggeeft wat hij ontvangt:

uart = UART(3, baudrate=115200, timeout=100)

while True:
    if uart.any():
        data = uart.read()
        uart.write(data)

read() zonder lengteargument blijft bytes lezen totdat de ontvangstlijn gedurende de geconfigureerde timeout stil blijft, en retourneert dan wat er is verzameld. Met timeout=100 retourneert elke aanroep hier één burst van bytes – alles wat de zender naar buiten klokte zonder een gat van 100 ms tussen bytes. Zonder een timeout zou de aanroep geen signaal hebben dat de zender klaar is en zou hij voor onbepaalde tijd kunnen blijven hangen.

3.19.2. Binaire data met struct

Het verzenden van gehele getallen en floats over de draad is waar de struct-module voor dient. Hij verpakt waarden met vaste breedte in een bytes-object met behulp van een formaatstring die de bytevolgorde en het type van elk veld benoemt:

import struct

uart.write(struct.pack("<lhb", count, temperature_x100, status))

De leidende "<" kiest little-endian bytevolgorde; "l" is een 32-bits signed integer, "h" is een 16-bits signed integer, "b" is een 8-bits signed integer. De andere kant pakt uit met dezelfde formaatstring:

payload = uart.read(7)        # 4 + 2 + 1 = 7 bytes
count, temperature_x100, status = struct.unpack("<lhb", payload)

De formaatstring is het contract tussen de twee uiteinden. Elke mismatch – verkeerde bytevolgorde, verkeerde typegroottes, verkeerde veldvolgorde – produceert onzinwaarden.

3.19.3. Bufferen en lezen met hoge snelheid

Een polling-lus die any() en read() aanroept, houdt het meeste verkeer zelf bij, zolang de hoofdlus snel genoeg itereert om de ontvangstbuffer te legen voordat hij vol is. Twee constructoropties zijn van belang wanneer de snelheid toeneemt of de lus het druk heeft.

rxbuf stelt de grootte van de software-RX-buffer in. De standaard is een paar honderd bytes; voor sensoren die honderden bytes per burst uitzenden, of wanneer de hoofdlus tussen polls lang werk doet, voorkomt het vergroten van de buffer dat binnenkomende bytes worden gedropt terwijl de lus elders bezig is:

uart = UART(3, baudrate=115200, timeout=100, rxbuf=4096)

Alles wat binnenkomt terwijl de buffer vol is, gaat verloren; bemeet rxbuf zo dat het de langste tussenpoze tussen het legen dekt.

Voor aanhoudend hoge snelheden leest readinto() in een vooraf toegewezen buffer in plaats van bij elke aanroep een nieuw bytes-object te retourneren:

buf = bytearray(256)

while True:
    n = uart.readinto(buf)
    if n:
        process(buf, n)

Er vindt geen allocatie plaats op het leespad, wat van belang is wanneer de heap gefragmenteerd is of wanneer de allocatielatentie anders het inter-byte-timingbudget zou overschrijden.