3.17. Flussi di bit e misurazione di impulsi¶
Alcuni dispositivi necessitano di pattern di impulsi temporizzati con precisione anziché di un segnale PWM a frequenza costante. Un LED RGB WS2812 codifica ogni bit come un impulso di larghezza specifica; un telemetro a ultrasuoni HC-SR04 risponde con un impulso di eco la cui larghezza è il tempo di volo di andata e ritorno; un telecomando IR invia un’intestazione seguita da bit di dati come sequenze di acceso-spento.
Il modulo machine dispone di due funzioni per questo tipo di GPIO a temporizzazione precisa:
bitstream()invia un treno di impulsi con una temporizzazione separata per i bit0e1.time_pulse_us()misura la larghezza di un impulso in ingresso in microsecondi.
3.17.1. Inviare un flusso di bit¶
machine.bitstream() accetta un pin, una codifica, una specifica di temporizzazione e i byte da inviare. La codifica più comune (0) è la modulazione della durata dell’impulso alto-basso: ogni bit è un impulso alto di una certa larghezza seguito da un impulso basso di un’altra, con i bit 0 e 1 aventi temporizzazioni distinte.
La codifica della durata dell’impulso alto-basso: un 0 e un 1 consistono ciascuno di una fase alta seguita da una fase bassa, con larghezze distinte.¶
L’esempio canonico è il LED RGB WS2812 (NeoPixel), che si aspetta i bit a 800 kHz con queste temporizzazioni:
0: 400 ns alto, poi 850 ns basso.1: 800 ns alto, poi 450 ns basso.
from machine import Pin, bitstream
pin = Pin("P7", Pin.OUT)
# (high_0, low_0, high_1, low_1) in nanoseconds
timing = (400, 850, 800, 450)
# one LED: GRB order, three bytes per LED (red shown here)
bitstream(pin, 0, timing, b"\x00\xff\x00")
L’MCU genera gli impulsi via bit-banging alle larghezze richieste; sulle cam abbastanza veloci la temporizzazione è accurata entro decine di nanosecondi.
Avvertimento
bitstream() disabilita tutti gli interrupt per l’intera trasmissione in modo da mantenere un controllo preciso sulla temporizzazione degli impulsi. La durata della chiamata cresce linearmente con il numero di byte – alla temporizzazione del WS2812 (circa 10 µs per byte), inviare 100 byte mette in pausa la CPU per circa 1 ms, 1000 byte per 10 ms e 10000 byte per ben 100 ms. Qualsiasi cosa oltre qualche centinaio di byte per chiamata rischia di causare blocchi percettibili – suddividi gli aggiornamenti lunghi in blocchi più piccoli, facendo ritornare la chiamata tra un blocco e l’altro così che il resto dello script possa girare.
Nota
Per pilotare in particolare le strisce WS2812 / NeoPixel, il modulo neopixel incapsula bitstream() in un’interfaccia di livello più alto che gestisce il riordino dell’ordine dei colori e gli aggiornamenti in blocco della striscia. Ricorri direttamente a bitstream() quando il protocollo non è il WS2812 ma rientra comunque in una forma PDM alto-basso.
3.17.2. Misurare un impulso in ingresso¶
machine.time_pulse_us() misura la larghezza di un singolo impulso su un pin. Attende che la linea raggiunga il livello specificato (l’inizio dell’impulso), poi misura per quanto tempo la linea rimane a quel livello (la fine dell’impulso) e restituisce la durata in microsecondi.
L’uso classico è un sensore di distanza a ultrasuoni HC-SR04. La camera invia un impulso di trigger di 10 µs, poi attende che il pin di eco restituisca un impulso la cui larghezza è il tempo di andata e ritorno del suono:
import time
from machine import Pin, time_pulse_us
trigger = Pin("P7", Pin.OUT, value=0)
echo = Pin("P8", Pin.IN)
while True:
trigger.value(1)
time.sleep_us(10)
trigger.value(0)
width = time_pulse_us(echo, 1, timeout_us=30_000)
if width > 0:
# sound at ~343 m/s; round trip / 2 / 343 = distance (m)
distance_cm = (width * 343) / 2 / 10_000
print(distance_cm, "cm")
time.sleep_ms(100)
Il terzo argomento è il timeout in microsecondi, applicato separatamente all“«attesa dell’inizio dell’impulso» e all“«attesa della fine dell’impulso». Allo scadere del timeout la funzione restituisce un valore negativo che identifica quale fase è fallita: -2 se l’impulso non è mai iniziato, -1 se è iniziato ma non è mai terminato entro la finestra.
Entrambe le metà del timeout contano per i sensori reali. Un HC-SR04 può impiegare da uno a due millisecondi per avviare la sua eco, e l’eco stessa può durare decine di millisecondi per oggetti lontani. Dimensionare timeout_us alla portata massima necessaria mantiene la misurazione limitata.