3.17. Fluxuri de biți și măsurarea impulsurilor

Unele dispozitive au nevoie de modele de impulsuri sincronizate cu precizie, mai degrabă decât de un semnal PWM de frecvență constantă. Un LED RGB WS2812 codifică fiecare bit ca un impuls de o lățime specifică; un telemetru ultrasonic HC-SR04 răspunde cu un impuls de ecou a cărui lățime este timpul de zbor dus-întors; o telecomandă IR trimite un antet urmat de biți de date sub formă de secvențe pornit-oprit.

Modulul machine are două funcții pentru acest tip de GPIO cu sincronizare precisă:

  • bitstream() trimite un tren de impulsuri cu o sincronizare separată pentru biții 0 și 1.

  • time_pulse_us() măsoară lățimea unui impuls de intrare în microsecunde.

3.17.1. Trimiterea unui flux de biți

machine.bitstream() primește un pin, o codificare, o specificație de sincronizare și octeții de trimis. Cea mai comună codificare (0) este modularea duratei impulsului de tip high-low: fiecare bit este un impuls high de o lățime urmat de un impuls low de altă lățime, biții 0 și 1 având sincronizări distincte.

Două forme de undă stivuite. Cea de sus arată un bit 0: un impuls high scurt (high_0) urmat de o perioadă low mai lungă (low_0). Cea de jos arată un bit 1: un impuls high mai lung (high_1) urmat de o perioadă low mai scurtă (low_1).

Codificarea de tip high-low a duratei impulsului: un 0 și un 1 constau fiecare dintr-o fază high urmată de o fază low, cu lățimi distincte.

Exemplul canonic este LED-ul RGB WS2812 (NeoPixel), care așteaptă biți la 800 kHz cu aceste sincronizări:

  • 0: 400 ns high, apoi 850 ns low.

  • 1: 800 ns high, apoi 450 ns low.

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")

MCU generează impulsurile prin bit-banging la lățimile cerute; pe camerele suficient de rapide pentru aceasta, sincronizarea este precisă până la zeci de nanosecunde.

Atenționare

bitstream() dezactivează toate întreruperile pe durata întregii transmisii, pentru a putea menține un control precis asupra sincronizării impulsurilor. Durata apelului crește liniar cu numărul de octeți – la sincronizarea WS2812 (aproximativ 10 µs per octet), trimiterea a 100 de octeți întrerupe CPU-ul pentru circa 1 ms, 1000 de octeți pentru 10 ms, iar 10000 de octeți pentru întregi 100 ms. Orice depășește câteva sute de octeți per apel riscă blocaje vizibile – împarte actualizările lungi în bucăți mai mici, apelul returnând între fiecare bucată astfel încât restul scriptului să poată rula.

Notă

Pentru controlul benzilor WS2812 / NeoPixel în special, modulul neopixel încapsulează bitstream() într-o interfață de nivel mai înalt care gestionează rearanjarea ordinii culorilor și actualizările în masă ale benzii. Apelează direct bitstream() atunci când protocolul nu este WS2812, dar se încadrează totuși într-o formă PDM de tip high-low.

3.17.2. Măsurarea unui impuls de intrare

machine.time_pulse_us() măsoară lățimea unui singur impuls pe un pin. Așteaptă ca linia să atingă nivelul specificat (începutul impulsului), apoi măsoară cât timp rămâne linia la acel nivel (sfârșitul impulsului) și returnează durata în microsecunde.

Utilizarea clasică este un senzor de distanță ultrasonic HC-SR04. Camera trimite un impuls de declanșare de 10 µs, apoi așteaptă ca pinul de ecou să returneze un impuls a cărui lățime este timpul dus-întors al sunetului:

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)

Al treilea argument este timeout-ul în microsecunde, aplicat separat la „așteaptă ca impulsul să înceapă” și „așteaptă ca impulsul să se termine”. La timeout, funcția returnează o valoare negativă care identifică ce fază a eșuat: -2 dacă impulsul nu a început niciodată, -1 dacă a început, dar nu s-a terminat niciodată în interiorul ferestrei.

Ambele jumătăți ale timeout-ului contează pentru senzorii reali. Un HC-SR04 poate dura una până la două milisecunde pentru a-și începe ecoul, iar ecoul în sine poate avea zeci de milisecunde pentru obiecte îndepărtate. Dimensionarea lui timeout_us la raza maximă necesară menține măsurarea limitată.