3.19. UART em código¶
machine.UART encapsula um canal UART de hardware. Construa um com o id do barramento e uma taxa de baud; tudo o resto tem valores predefinidos razoáveis:
from machine import UART
uart = UART(3, baudrate=115200)
O id seleciona qual o UART de hardware a utilizar; o valor depende da placa (consulte Placas OpenMV para os números de barramento disponíveis e as atribuições de pinos numa determinada câmara). O formato de fotograma predefinido é 8 bits de dados, sem paridade, um bit de paragem – o «8N1» que todos esperam.
3.19.1. Escrita e leitura¶
A saída UART passa por write():
uart.write("hello\n")
uart.write(b"\x01\x02\x03")
write aceita uma str (codificada em UTF-8) ou um bytes / bytearray. Regressa imediatamente após os bytes serem colocados na fila do buffer de TX; o hardware termina de os transmitir em segundo plano.
As leituras são feitas através de três métodos, dependendo do que é necessário:
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() verifica o buffer de RX sem bloquear. read() lê um número fixo de bytes, devolvendo None se o tempo limite (configurável através do argumento timeout no construtor) expirar primeiro. readline() lê até incluir o próximo carácter de nova linha, útil para protocolos baseados em linhas.
Um ciclo simples que ecoa tudo o que recebe:
uart = UART(3, baudrate=115200, timeout=100)
while True:
if uart.any():
data = uart.read()
uart.write(data)
read() sem argumento de comprimento continua a ler bytes até a linha de receção ficar silenciosa durante o timeout configurado, devolvendo então o que foi acumulado. Com timeout=100, cada chamada aqui devolve uma rajada de bytes – tudo o que o emissor transmitiu sem uma pausa de 100 ms entre bytes. Sem tempo limite, a chamada não teria sinal de que o emissor terminou e poderia bloquear indefinidamente.
3.19.2. Dados binários com struct¶
O envio de inteiros e números de vírgula flutuante pelo fio é para isso que o módulo struct serve. Empacota valores de largura fixa num objeto bytes utilizando uma cadeia de formato que indica a ordem dos bytes e o tipo de cada campo:
import struct
uart.write(struct.pack("<lhb", count, temperature_x100, status))
O "<" inicial escolhe a ordem de bytes little-endian; "l" é um inteiro de 32 bits com sinal, "h" é um inteiro de 16 bits com sinal, "b" é um inteiro de 8 bits com sinal. O outro lado desempacota com a mesma cadeia de formato:
payload = uart.read(7) # 4 + 2 + 1 = 7 bytes
count, temperature_x100, status = struct.unpack("<lhb", payload)
A cadeia de formato é o contrato entre as duas extremidades. Qualquer incompatibilidade – ordem de bytes errada, tamanhos de tipo errados, ordem de campos errada – produz valores sem sentido.
3.19.3. Buffering e leituras a alta velocidade¶
Um ciclo de sondagem que chama any() e read() acompanha a maior parte do tráfego por si só, desde que o ciclo principal itere com rapidez suficiente para esvaziar o buffer de receção antes de ele ficar cheio. Duas opções do construtor são importantes quando a velocidade aumenta ou o ciclo está ocupado.
rxbuf define o tamanho do buffer de RX em software. O valor predefinido é algumas centenas de bytes; para sensores que emitem centenas de bytes por rajada, ou quando o ciclo principal executa trabalho longo entre sondagens, aumentar o buffer evita que os bytes recebidos sejam descartados enquanto o ciclo está ocupado:
uart = UART(3, baudrate=115200, timeout=100, rxbuf=4096)
Tudo o que chega quando o buffer está cheio é perdido; dimensione rxbuf para cobrir o maior intervalo entre esvaziamentos.
Para velocidades altas sustentadas, readinto() lê para um buffer pré-alocado em vez de devolver um novo objeto bytes em cada chamada:
buf = bytearray(256)
while True:
n = uart.readinto(buf)
if n:
process(buf, n)
Não ocorre nenhuma alocação no caminho de leitura, o que é importante quando o heap está fragmentado ou quando a latência de alocação excederia o orçamento de temporização entre bytes.