3.17. Bitfolyamok és impulzusmérés

Egyes eszközöknek pontosan időzített impulzusmintákra van szükségük, nem pedig állandó frekvenciájú PWM-jelre. Egy WS2812 RGB LED minden bitet egy meghatározott szélességű impulzusként kódol; egy HC-SR04 ultrahangos távolságmérő egy visszhang-impulzussal válaszol, amelynek szélessége az oda-vissza repülési idő; egy IR-távirányító egy fejlécet küld, amelyet az adatbitek be-ki kapcsolási sorozatként követnek.

A machine modul két függvényt kínál erre a fajta időzítéspontos GPIO-ra:

  • A bitstream() egy impulzussorozatot küld, külön időzítéssel a 0 és az 1 bitekhez.

  • A time_pulse_us() mikroszekundumban méri egy beérkező impulzus szélességét.

3.17.1. Bitfolyam küldése

A machine.bitstream() egy lábat, egy kódolást, egy időzítési specifikációt és a küldendő bájtokat veszi át. A leggyakoribb kódolás (0) a magas-alacsony impulzusszélesség-moduláció: minden bit egy bizonyos szélességű magas impulzus, amelyet egy másik szélességű alacsony impulzus követ, ahol a 0 és az 1 bitek eltérő időzítésűek.

Két egymásra rakott hullámforma. A felső egy 0 bitet mutat: egy rövid magas impulzust (high_0), amelyet egy hosszabb alacsony periódus (low_0) követ. Az alsó egy 1 bitet mutat: egy hosszabb magas impulzust (high_1), amelyet egy rövidebb alacsony periódus (low_1) követ.

A magas-alacsony impulzusszélesség-kódolás: egy 0 és egy 1 is egy magas fázisból áll, amelyet egy alacsony fázis követ, eltérő szélességekkel.

A kanonikus példa a WS2812 (NeoPixel) RGB LED, amely 800 kHz-en várja a biteket, a következő időzítésekkel:

  • 0: 400 ns magas, majd 850 ns alacsony.

  • 1: 800 ns magas, majd 450 ns alacsony.

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

Az MCU a kért szélességekre bit-bangeli az impulzusokat; az ehhez elég gyors kamerákon az időzítés néhány tíz nanoszekundumon belüli pontosságú.

Figyelem

A bitstream() a teljes átvitel idejére letiltja az összes megszakítást, hogy pontosan kézben tarthassa az impulzusidőzítést. A hívás időtartama lineárisan skálázódik a bájtok számával – WS2812 időzítésnél (bájtonként körülbelül 10 µs) 100 bájt küldése a CPU-t körülbelül 1 ms-ra szünetelteti, 1000 bájt 10 ms-ra, 10000 bájt pedig teljes 100 ms-ra. Bármi, ami hívásonként néhány száz bájton túlmegy, észrevehető lefagyásokat kockáztat – bontsd a hosszú frissítéseket kisebb darabokra, úgy hogy a hívás minden darab között visszatérjen, hogy a szkript többi része futhasson.

Megjegyzés

Kifejezetten WS2812 / NeoPixel szalagok meghajtásához a neopixel modul a bitstream() függvényt egy magasabb szintű felületbe csomagolja, amely kezeli a színsorrend átrendezését és a tömeges szalagfrissítéseket. Akkor nyúlj közvetlenül a bitstream() függvényhez, amikor a protokoll nem WS2812, de még mindig egy magas-alacsony PDM alakba illeszkedik.

3.17.2. Beérkező impulzus mérése

A machine.time_pulse_us() egy lábon egyetlen impulzus szélességét méri. Megvárja, amíg a vonal eléri a megadott szintet (az impulzus kezdetét), majd megméri, mennyi ideig marad a vonal azon a szinten (az impulzus vége), és visszaadja az időtartamot mikroszekundumban.

A klasszikus felhasználás egy HC-SR04 ultrahangos távolságérzékelő. A kamera egy 10 µs-os trigger-impulzust küld, majd megvárja, amíg a visszhang-láb visszaad egy impulzust, amelynek szélessége a hang oda-vissza ideje:

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)

A harmadik argumentum a mikroszekundumban megadott időkorlát, amely külön-külön vonatkozik az „impulzus kezdetére várás” és az „impulzus végére várás” szakaszra. Időtúllépéskor a függvény egy negatív értéket ad vissza, amely azonosítja, melyik szakasz hiúsult meg: -2, ha az impulzus soha nem indult el, -1, ha elindult, de soha nem ért véget az ablakon belül.

Az időkorlát mindkét fele számít a valós érzékelőknél. Egy HC-SR04-nek egy-két ezredmásodpercbe telhet, mire elindítja a visszhangját, és maga a visszhang távoli tárgyaknál több tíz ezredmásodperc hosszú is lehet. A timeout_us méretezése a szükséges maximális tartományhoz korlátok között tartja a mérést.