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ów0i1.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.
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.