3.17. Strumienie bitów i pomiar impulsów

Niektóre urządzenia potrzebują precyzyjnie odmierzanych wzorców impulsów, a nie sygnału PWM o stałej częstotliwości. Dioda RGB WS2812 koduje każdy bit jako impuls o określonej szerokości; ultradźwiękowy dalmierz HC-SR04 odpowiada impulsem echa, którego szerokość to czas przelotu w obie strony; pilot na podczerwień wysyła nagłówek, po którym następują bity danych w postaci sekwencji włącz-wyłącz.

Moduł machine ma dwie funkcje do tego rodzaju GPIO o precyzyjnym odmierzaniu czasu:

  • bitstream() wysyła ciąg impulsów z osobnym czasem dla bitów 0 i 1.

  • time_pulse_us() mierzy szerokość nadchodzącego impulsu w mikrosekundach.

3.17.1. Wysyłanie strumienia bitów

machine.bitstream() przyjmuje pin, kodowanie, specyfikację czasów oraz bajty do wysłania. Najpopularniejsze kodowanie (0) to modulacja czasu trwania impulsu typu wysoki-niski: każdy bit to impuls wysoki o jednej szerokości, po którym następuje impuls niski o innej szerokości, przy czym bity 0 i 1 mają odmienne czasy.

Dwa ułożone jeden nad drugim przebiegi. Górny przedstawia bit 0:  krótki impuls wysoki (high_0), po którym następuje dłuższy  okres niski (low_0). Dolny przedstawia bit 1: dłuższy  impuls wysoki (high_1), po którym następuje krótszy okres niski  (low_1).

Kodowanie czasu trwania impulsu typu wysoki-niski: 0 i 1 składają się każde z fazy wysokiej, po której następuje faza niska, o odmiennych szerokościach.

Kanonicznym przykładem jest dioda RGB WS2812 (NeoPixel), która oczekuje bitów przy 800 kHz z następującymi czasami:

  • 0: 400 ns wysoki, następnie 850 ns niski.

  • 1: 800 ns wysoki, następnie 450 ns niski.

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 generuje impulsy metodą bit-bang o żądanych szerokościach; na kamerach wystarczająco szybkich do tego czas jest dokładny z dokładnością do dziesiątek nanosekund.

Ostrzeżenie

bitstream() wyłącza wszystkie przerwania na czas całej transmisji, aby zachować precyzyjną kontrolę nad czasem impulsów. Czas trwania wywołania skaluje się liniowo wraz z liczbą bajtów – przy czasach WS2812 (około 10 µs na bajt) wysłanie 100 bajtów wstrzymuje CPU na około 1 ms, 1000 bajtów na 10 ms, a 10000 bajtów na pełne 100 ms. Cokolwiek powyżej kilkuset bajtów na wywołanie grozi zauważalnymi zawieszeniami – dziel długie aktualizacje na mniejsze fragmenty, tak aby wywołanie zwracało sterowanie między fragmentami i reszta skryptu mogła działać.

Informacja

W szczególności do sterowania paskami WS2812 / NeoPixel moduł neopixel opakowuje bitstream() w interfejs wyższego poziomu, który obsługuje przestawianie kolejności kolorów i zbiorcze aktualizacje pasków. Sięgaj po bitstream() bezpośrednio wtedy, gdy protokół nie jest WS2812, ale nadal pasuje do kształtu PDM wysoki-niski.

3.17.2. Pomiar nadchodzącego impulsu

machine.time_pulse_us() mierzy szerokość pojedynczego impulsu na pinie. Czeka, aż linia osiągnie określony poziom (początek impulsu), następnie mierzy, jak długo linia pozostaje na tym poziomie (koniec impulsu), i zwraca czas trwania w mikrosekundach.

Klasycznym zastosowaniem jest ultradźwiękowy czujnik odległości HC-SR04. Kamera wysyła impuls wyzwalający o szerokości 10 µs, następnie czeka, aż pin echa zwróci impuls, którego szerokość to czas przelotu dźwięku w obie strony:

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)

Trzeci argument to limit czasu w mikrosekundach, stosowany osobno do „oczekiwania na rozpoczęcie impulsu” oraz „oczekiwania na zakończenie impulsu”. Po przekroczeniu limitu funkcja zwraca wartość ujemną identyfikującą, która faza zawiodła: -2, jeśli impuls nigdy się nie rozpoczął, -1, jeśli się rozpoczął, ale nigdy nie zakończył w obrębie okna.

Obie połowy limitu czasu mają znaczenie dla rzeczywistych sensorów. HC-SR04 może potrzebować jednej do dwóch milisekund na rozpoczęcie echa, a samo echo dla odległych obiektów może trwać dziesiątki milisekund. Dobranie timeout_us do maksymalnego potrzebnego zasięgu utrzymuje pomiar w ograniczonych ramach.