3.22. SPI w kodzie

machine.SPI opakowuje sprzętowy kontroler SPI; linie CS to zwykłe wyjścia Pin zarządzane przez skrypt. Utwórz instancję SPI, podając id magistrali, żądaną szybkość zegara oraz (w razie potrzeby) tryb:

from machine import SPI, Pin

spi = SPI(0, baudrate=1_000_000, polarity=0, phase=0)
cs = Pin("P3", Pin.OUT, value=1)         # CS idle high

Parametr id wybiera, którego sprzętowego bloku SPI użyć; dostępne numery oraz piny SCK/MOSI/MISO, na które są mapowane, zależą od płytki (zobacz Płytki OpenMV). baudrate to częstotliwość SCK w hercach – rzeczywista szybkość osiągana przez sprzęt może być nieco niższa z powodu podziału zegara, co pokaże wypisana wartość obiektu SPI.

Pin CS jest tworzony z value=1, aby spoczywał w stanie dezaktywowanym. Każda transakcja aktywuje CS (ściąga go w stan niski), przesyła bajty i ponownie dezaktywuje CS (ściąga go w stan wysoki).

3.22.1. Odczyt, zapis, wymiana

Trzy metody obejmują typowe przypadki:

cs.value(0)
spi.write(b"\x10\x20\x30")              # send 3 bytes, ignore what comes back
cs.value(1)

cs.value(0)
data = spi.read(4)                      # read 4 bytes; sends 0x00 while reading
cs.value(1)

rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x9F\x00", rx)     # send 0x9F, 0x00; receive 2 bytes
cs.value(1)

write() to szybka ścieżka tylko do zapisu; kontroler wypycha bajty i odrzuca to, co peryferium odesłało na MISO. read() jest odbiciem lustrzanym – taktuje N impulsów SCK, wysyłając jednocześnie ustalony bajt wypełniający (0 domyślnie) na MOSI i zapisuje bajty z MISO. write_readinto() to forma pełnodupleksowa: wysyła bajty z jednego bufora i zapisuje równocześnie odebrane bajty z MISO do drugiego. Wiele peryferiów stosuje ten wzorzec – „wyślij bajt polecenia, a następnie odczytaj odpowiedź w kolejnym transferze” – więc obie operacje naturalnie mieszczą się w jednym wywołaniu write_readinto.

Większość peryferiów oczekuje, że linia CS pozostanie aktywna przez całą transakcję (od bajtów polecenia po bajty odpowiedzi), więc utrzymuj nawiasy cs.value(0) / cs.value(1) wokół całej sekwencji, a nie wokół każdego wywołania metody.

3.22.2. Typowy odczyt sensora

Wiele sensorów SPI organizuje swój stan jako zestaw wewnętrznych rejestrów i stosuje ten sam schemat wymiany: wyślij adres rejestru (z flagą odczytu/zapisu w najwyższym bicie), a następnie odczytaj lub zapisz bajty rejestru. Odczyt rejestru 0x0F na takim urządzeniu:

rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x8F\x00", rx)     # 0x80 = "read" flag, then reg 0x0F
cs.value(1)
register_value = rx[1]

Pierwszy bajt z MISO to śmieci (urządzenie w tym momencie wciąż odbierało polecenie); drugi bajt z MISO zawiera zawartość rejestru. Dokładny format bajtu polecenia – który bit jest flagą odczytu/zapisu, czy adres automatycznie się inkrementuje przy odczytach wielobajtowych – znajduje się w karcie katalogowej urządzenia.

3.22.3. Bit-banging

Powyższa instancja SPI korzysta ze sprzętowego bloku SPI: dedykowanego peryferium wewnątrz MCU z własnym rejestrem przesuwnym i generatorem zegara, który wytwarza przebiegi SCK / MOSI / MISO sprzętowo. Oprogramowanie po prostu przekazuje mu bajt; bity przemieszczają się po przewodzie bez dalszej pomocy CPU, pozostawiając procesor wolny do równoległego wykonywania innej pracy.

Alternatywą jest bit-banging: oprogramowanie wykonuje pętlę po każdym bicie i bezpośrednio przełącza piny GPIO, aby wytworzyć ten sam przebieg. Nie jest zaangażowane żadne peryferium sprzętowe – CPU ściąga SCK w stan niski, ustawia MOSI, ściąga SCK w stan wysoki, próbkuje MISO i tak dalej dla każdego bitu każdego bajtu. To zajmuje CPU na całą transakcję i działa wolniej, niż potrafi blok sprzętowy, ale działa na dowolnym pinie i nie wymaga, aby blok sprzętowy był wolny.

machine.SoftSPI to implementacja bit-bang tego samego API SPI:

from machine import SoftSPI, Pin

spi = SoftSPI(baudrate=500_000, polarity=0, phase=0,
              sck=Pin("P2"), mosi=Pin("P0"), miso=Pin("P1"))

Używaj jej, gdy urządzenie musi być na pinach, które nie są podłączone do sprzętowego bloku SPI, lub gdy wszystkie bloki sprzętowe są zajęte. 500 kHz to wygodny pułap na większości kamer; CPU pozostaje zajęte przez całą transakcję.