3.19. L’UART en code¶
machine.UART encapsule un canal UART matériel. Construisez-en un avec l’identifiant du bus et un débit en bauds ; tout le reste a des valeurs par défaut raisonnables :
from machine import UART
uart = UART(3, baudrate=115200)
Le paramètre id sélectionne quel UART matériel utiliser ; la valeur dépend de la carte (voir la Cartes OpenMV pour les numéros de bus disponibles et l’affectation des broches sur une caméra donnée). Le format de trame par défaut est de 8 bits de données, sans parité, un bit d’arrêt – le « 8N1 » auquel tout le monde s’attend.
3.19.1. Écriture et lecture¶
La sortie UART passe par write() :
uart.write("hello\n")
uart.write(b"\x01\x02\x03")
write accepte soit une str (encodée en UTF-8), soit un objet bytes / bytearray. Il revient immédiatement une fois les octets mis en file dans le tampon TX ; le matériel termine de les transmettre en arrière-plan.
Les lectures se font via trois méthodes, selon le besoin :
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() vérifie le tampon RX sans bloquer. read() lit un nombre fixe d’octets, renvoyant None si le délai d’attente (configurable via l’argument timeout du constructeur) expire d’abord. readline() lit jusqu’au prochain saut de ligne inclus, ce qui est utile pour les protocoles orientés ligne.
Une boucle simple qui renvoie en écho tout ce qu’elle reçoit :
uart = UART(3, baudrate=115200, timeout=100)
while True:
if uart.any():
data = uart.read()
uart.write(data)
read() sans argument de longueur continue à lire des octets jusqu’à ce que la ligne de réception reste silencieuse pendant le timeout configuré, puis renvoie ce qui a été accumulé. Avec timeout=100, chaque appel ici renvoie une rafale d’octets – tout ce que l’émetteur a transmis sans intervalle de 100 ms entre les octets. Sans délai d’attente, l’appel n’aurait aucun signal indiquant que l’émetteur a terminé et pourrait se bloquer indéfiniment.
3.19.2. Données binaires avec struct¶
L’envoi d’entiers et de flottants sur la ligne est précisément le rôle du module struct. Il empaquette des valeurs de largeur fixe dans un objet bytes à l’aide d’une chaîne de format qui indique l’ordre des octets et le type de chaque champ :
import struct
uart.write(struct.pack("<lhb", count, temperature_x100, status))
Le "<" initial choisit l’ordre des octets petit-boutiste ; "l" est un entier signé de 32 bits, "h" est un entier signé de 16 bits, "b" est un entier signé de 8 bits. L’autre côté désempaquète avec la même chaîne de format :
payload = uart.read(7) # 4 + 2 + 1 = 7 bytes
count, temperature_x100, status = struct.unpack("<lhb", payload)
La chaîne de format est le contrat entre les deux extrémités. Toute incohérence – mauvais ordre des octets, mauvaises tailles de type, mauvais ordre des champs – produit des valeurs absurdes.
3.19.3. Mise en mémoire tampon et lectures à haut débit¶
Une boucle d’interrogation qui appelle any() et read() suit la plupart du trafic par elle-même, tant que la boucle principale itère assez vite pour vider le tampon de réception avant qu’il ne se remplisse. Deux options du constructeur comptent lorsque le débit augmente ou que la boucle est occupée.
rxbuf définit la taille du tampon RX logiciel. La valeur par défaut est de quelques centaines d’octets ; pour les capteurs qui émettent des centaines d’octets par rafale, ou lorsque la boucle principale effectue un long traitement entre deux interrogations, agrandir le tampon évite que les octets entrants soient perdus pendant que la boucle est occupée ailleurs :
uart = UART(3, baudrate=115200, timeout=100, rxbuf=4096)
Tout ce qui arrive pendant que le tampon est plein est perdu ; dimensionnez rxbuf pour couvrir le plus long intervalle entre deux vidanges.
Pour des débits élevés soutenus, readinto() lit dans un tampon préalloué au lieu de renvoyer un nouvel objet bytes à chaque appel :
buf = bytearray(256)
while True:
n = uart.readinto(buf)
if n:
process(buf, n)
Aucune allocation ne se produit sur le chemin de lecture, ce qui importe lorsque le tas est fragmenté ou lorsque la latence d’allocation dépasserait sinon le budget de timing inter-octets.