SPI jest synchronicznym interfejsem szeregowym najczęściej używanym między mikrokontrolerem flight controllera a szybkim IMU, pamięcią flash, przetwornikiem lub innym układem na tej samej płytce. Zapewnia dużą przepustowość i prostą ramkę, lecz nie definiuje jednego kompletnego protokołu urządzenia. Tryb zegara, znaczenie chip select, kolejność bitów, adresowanie, dummy cycles i maksymalna częstotliwość pochodzą z datasheetu konkretnego układu.

Spis treści#

Sygnały i topologia#

Typowy interfejs ma:

  • SCK — zegar generowany przez kontroler;
  • MOSI — dane od kontrolera do urządzenia;
  • MISO — dane od urządzenia do kontrolera;
  • CS/NSS — wybór urządzenia, zwykle aktywny stanem niskim.

Nazwy controller/peripheral zastępują historyczne master/slave. SPI nie ma adresów w warstwie elektrycznej. Każde urządzenie otrzymuje osobny CS albo jest częścią specjalnego łańcucha daisy-chain.

SCK i MOSI są wspólne dla wielu urządzeń, MISO może być wspólne tylko wtedy, gdy niewybrane układy przechodzą w high-Z. CS jest połączeniem punkt-punkt. Topologia najlepiej działa na krótkich ścieżkach PCB; nie jest domyślnie magistralą kablową.

Pełny dupleks#

SPI przesuwa bity w obu kierunkach jednocześnie. Każdy takt wysyła bit MOSI i odbiera bit MISO. „Odczyt” wymaga więc wysłania bajtów dummy, a „zapis” generuje odebrane bajty, które kontroler musi opróżnić, aby nie dostać overrun.

W peryferium są osobne rejestry przesuwne TX/RX i bufory lub FIFO. ST AN5543 podkreśla, że zdarzenia nadawania i odbioru nie kończą się dokładnie w tej samej chwili co DMA. Program musi znać znaczenie flag TX complete, end-of-transfer i busy dla konkretnej wersji SPI.

Tryb half-duplex na jednej linii istnieje w niektórych MCU, ale wymaga bezpiecznej zmiany kierunku GPIO. Konflikt dwóch wyjść może uszkodzić dane lub układ.

CPOL i CPHA#

CPOL określa poziom spoczynkowy SCK, CPHA — zbocze próbkowania względem początku okresu. Powstają cztery tryby 0–3. Urządzenie musi widzieć tryb zgodny z datasheetem.

Zły CPHA może przesunąć ramkę o bit. Przy małym zegarze układ czasem „działa”, bo marginesy wejść przypadkowo obejmują złe zbocze. Po zwiększeniu częstotliwości, zmianie temperatury lub sondy oscyloskopu błąd wraca.

Konfiguracja jest własnością urządzenia, nie całej magistrali. Arbiter przed każdym transferem ustawia tryb, prescaler, rozmiar słowa i kolejność bitów, ale tylko wtedy, gdy SPI jest w stanie pozwalającym na rekonfigurację.

Analizator logiczny ustawiony w złym trybie może zdekodować fałszywe bajty, mimo że układy komunikują się poprawnie. Najpierw ogląda się analogowe zbocza i specyfikację.

Chip select#

CS wyznacza granice transakcji i często resetuje wewnętrzny parser urządzenia. Minimalny czas przed pierwszym zegarem, po ostatnim zegarze oraz przerwa między transakcjami są parametrami timingowymi.

Niektóre układy wymagają utrzymania CS nisko przez cały burst adres+dane. Automatyczne pulsowanie NSS między bajtami przerwie auto-increment. Inne urządzenia wykorzystują zbocze CS do zatwierdzenia zapisu.

Po resecie MCU CS powinny mieć rezystory utrzymujące stan nieaktywny zanim GPIO zostaną skonfigurowane. Pływający CS podczas narastania zasilania może wprowadzić urządzenie w nieokreślony stan.

CS sterowany programowo musi respektować fakt, że zakończenie DMA TX nie zawsze oznacza wysłanie ostatniego bitu. Zwolnienie przed flagą EOT/TXC uszkodzi końcówkę ramki.

Ramka urządzenia#

SPI nie definiuje komendy read/write. Częsty format to:

[adres z bitem R/W] [dummy] [dane0] [dane1] ...

Ale bit odczytu może być MSB, osobna komenda, a auto-increment osobnym bitem. Niektóre sensory zwracają pierwszy poprawny bajt dopiero po dummy cycle. Inne mają banki rejestrów.

Sterownik nie powinien zawierać „uniwersalnego” spi_read_register zakładającego jeden format. Transport może przesłać bufor, a warstwa urządzenia buduje konkretną transakcję.

Kolejność bitów zwykle jest MSB-first, lecz kontroler może obsługiwać LSB-first. Endianowość wielobajtowych pól jest niezależna od kolejności bitów w bajcie.

Częstotliwość i timing#

Maksymalny SCK wynika z minimum limitów MCU, urządzenia, level shiftera i PCB. Datasheet może podawać inną wartość dla odczytu i zapisu lub dla fazy inicjalizacji.

Krytyczne parametry:

  • setup/hold MOSI względem zbocza próbkowania;
  • opóźnienie clock-to-output MISO;
  • czas propagacji ścieżki i translatora;
  • skew SCK–dane;
  • rise/fall time;
  • czas CS setup/hold.

Dla odczytu kontroler wysyła SCK, urządzenie reaguje, a MISO wraca. Budżet obejmuje dwukierunkową drogę i opóźnienie układu. Przy wysokiej częstotliwości sample delay w MCU może poprawić margines, ale wymaga pomiaru.

Najwyższy prescaler działający na jednym egzemplarzu w temperaturze pokojowej nie jest walidacją. Testuje się narożniki napięcia, temperatury i tolerancji.

Wiele urządzeń#

Współdzielenie SCK/MOSI/MISO oszczędza piny, ale tworzy wspólny zasób czasowy i elektryczny. Każda dodatkowa gałąź zwiększa pojemność oraz stub. Urządzenie o niskim maksymalnym SCK wymusza rekonfigurację per transfer.

Szybkie IMU o deterministycznym deadline warto oddzielić od wolnej pamięci lub zewnętrznego złącza. Długi zapis flash nie może blokować data-ready IMU. Jeśli magistrala jest wspólna, transfery muszą być dzielone na wywłaszczalne porcje albo planowane między deadline’ami.

Nie wolno przełączać CS dwóch urządzeń jednocześnie. Jeśli oba sterują MISO, wystąpi contention. Test prądu i oscyloskop mogą ujawnić konflikt, którego analizator cyfrowy nie pokazuje.

MISO i stan wysokiej impedancji#

Niewybrane urządzenie powinno zwolnić MISO. Datasheet podaje czas od dezaktywacji CS do high-Z. Następny CS nie może zostać aktywowany wcześniej, jeśli wyjścia mogłyby się nakładać.

Niektóre układy nie zwalniają MISO w określonym trybie, mają pin SDO używany też do wyboru adresu albo wymagają konfiguracji. Takich urządzeń nie można bezpośrednio łączyć na wspólnej linii.

Rezystor pull-up/down może ustalić poziom pływającej MISO, lecz nie rozwiązuje konfliktu. Odczyt 0xFF lub 0x00 może oznaczać brak urządzenia, ale także poprawne dane.

Transakcja rejestrowa#

Bezpieczne API jest asynchroniczne i ma deadline:

SpiStatus spi_transfer(Device *dev,
                       const uint8_t *tx, uint8_t *rx,
                       size_t length, uint64_t deadline_us);

Opis urządzenia zawiera tryb, częstotliwość, CS i wymagane czasy. Wynik rozróżnia timeout, overrun, underrun, błąd DMA i anulowanie.

Read-modify-write rejestru jest ryzykowne dla bitów write-one-to-clear i statusów. Maski writable biorą się z datasheetu. Po zapisie konfigurację odczytuje się z maską pól stabilnych.

Bank rejestrów jest globalnym stanem układu. Sekwencja wyboru banku i odczytu musi być atomowa względem innych użytkowników sterownika.

FIFO i burst#

Burst zmniejsza narzut CS/adresu i zapewnia spójny snapshot kolejnych pól. Auto-increment może zatrzymywać się na granicy banku. Licznik FIFO bywa zmienny podczas odczytu, więc potrzebna jest procedura producenta.

Najpierw odczytuje się rozmiar, ogranicza do pojemności bufora, następnie wykonuje burst. Przepełnienie FIFO jest zdarzeniem jakości danych, nie tylko licznikiem debug.

Parser powinien wykryć niepełny rekord, zły header i nieznaną długość. Po utracie synchronizacji FIFO się opróżnia, a estymator otrzymuje informację o luce.

Duży burst blokuje magistralę. Watermark dobiera się z budżetu latency. Dla 8 kHz IMU czekanie na dziesiątki rekordów może być nieakceptowalne dla rate loop.

Data-ready i czas próbki#

SPI przenosi wynik pomiaru, ale samo nie informuje, kiedy fizycznie powstała próbka. Szybkie IMU udostępniają pin DRDY albo przerwanie watermark FIFO. Zbocze powinno trafić do wejścia przerwaniowego, a przy wysokich wymaganiach — do timera input capture. Timestamp z chwili rozpoczęcia DMA zawiera już oczekiwanie na arbitra i jitter oprogramowania.

DRDY edge -> hardware timestamp -> enqueue read request
arbiter SPI -> burst FIFO -> DMA complete -> validate record
sample.time = timestamp_DRDY lub czas odtworzony z FIFO

Jeżeli kolejny DRDY wystąpi przed zakończeniem odczytu, sterownik nie powinien bezwarunkowo uruchamiać drugiego DMA na tych samych buforach. Może zwiększyć licznik pending, odczytać kilka rekordów z FIFO albo oznaczyć missed sample. Wybór zależy od semantyki pinu: pulse, latched interrupt, data-ready level lub watermark.

Przerwanie kasuje się w kolejności podanej w datasheet. W niektórych układach odczyt rejestru statusu kasuje źródło, w innych dopiero odczyt danych. Skasowanie przed zabezpieczeniem timestampu może połączyć dwie próbki; nieskasowanie tworzy lawinę ISR.

Przy wielu sensorach na jednym SPI ich DRDY mogą pojawić się jednocześnie. Arbiter zna deadline każdego odczytu i maksymalny czas oczekiwania, a nie tylko kolejność zgłoszeń. W logu warto zachować:

  • numer i timestamp zdarzenia DRDY;
  • czas rozpoczęcia i zakończenia transferu;
  • liczbę rekordów FIFO;
  • overflow, missed DRDY i deadline miss;
  • identyfikator generacji sensora po resecie.

Pozwala to oddzielić opóźnienie próbkowania od opóźnienia magistrali. Bez tych danych pozorny jitter żyroskopu może pochodzić z kolejki SPI.

Start sensora także wymaga synchronizacji. Po resecie układ może przez określony czas nie odpowiadać, zwracać stare dane albo wymagać sekwencji wyboru interfejsu. CS i SCK utrzymuje się w stanach zalecanych przez producenta, czeka na boot, odczytuje identyfikator, zapisuje konfigurację, odczytuje ją zwrotnie i dopiero potem dopuszcza próbki do estymatora.

DMA#

DMA ogranicza koszt CPU, lecz konkuruje o bus matrix i pamięć. AN5543 wskazuje, że arbitraż DMA też ma latency; nie można zakładać nieskończonej przepustowości.

Pełny dupleks wymaga skonfigurowania obu kierunków. Kolejność uruchamiania musi zapobiec utracie pierwszego odebranego bajtu. Callback TX może nadejść przed rzeczywistym końcem SCK, więc zakończenie transakcji opiera się na właściwej fladze peryferium.

Bufory muszą żyć do callbacku. Double buffering rozdziela transfer i dekodowanie. ISR tylko zapisuje wynik oraz budzi zadanie; nie parsuje dużego FIFO.

Timeout powinien anulować oba kanały, wyczyścić flagi i przywrócić znany stan CS/SPI. Ponowne użycie częściowo aktywnego DMA prowadzi do rzadkich błędów.

Zakończenie transferu#

„DMA complete”, „TX FIFO empty”, „shift register empty” i „bus idle” to różne zdarzenia. Starsze STM32 używają m.in. flag BSY/TXE/RXNE, nowsze EOT/TXC/TXP/RXP. Kod musi odpowiadać wersji peryferium.

Przed zmianą trybu, wyłączeniem zegara lub podniesieniem CS należy mieć pewność, że ostatni bit i wymagany hold time minęły. Z drugiej strony polling flagi o zmiennej semantyce może się zawiesić — zawsze potrzebny deadline.

Po transferze RX FIFO musi zostać opróżnione, a overrun skasowany zgodnie z kolejnością z reference manual. Niepoprawne czyszczenie flag może usunąć następną informację.

Cache i własność bufora#

Na MCU z cache DMA nie musi widzieć najnowszych danych CPU, a CPU może czytać starą linię po zapisie DMA. Przed TX robi się clean, po RX invalidate, z uwzględnieniem granic linii cache. Alternatywą jest region non-cacheable.

Bufor nie powinien dzielić linii cache z innymi zmiennymi, bo invalidate może utracić niezwiązany zapis. Wyrównanie i rozmiar są częścią projektu.

Własność przechodzi stanami FREE → DMA_OWNED → READY → CONSUMER → FREE. Bez tego callback może nadpisać dane parsowane przez zadanie.

Integralność sygnałowa#

SPI na PCB jest linią transmisyjną, gdy czas narastania jest krótki względem propagacji ścieżki — nawet przy umiarkowanym SCK. Liczy się edge rate, nie tylko częstotliwość zegara.

Zasady:

  • ciągła płaszczyzna odniesienia;
  • krótkie SCK i minimalne stuby;
  • brak przejść nad szczelinami masy;
  • rezystor szeregowy blisko źródła dla tłumienia;
  • kontrola przesłuchu od mostków mocy;
  • właściwy drive strength i slew rate GPIO;
  • lokalne odsprzęganie urządzenia.

Zbyt silny driver zwiększa ringing i EMI. Zbyt duży rezystor wydłuża zbocze i narusza setup. Dobór potwierdza się oscyloskopem przy odbiorniku, nie przy MCU.

Długiego kabla nie należy traktować jak ścieżki SPI. Brak różnicowej warstwy fizycznej, wspólna masa i wiele szybkich linii ograniczają odporność. Dla zewnętrznych modułów lepszy bywa CAN lub RS‑485.

Poziomy napięć#

VOH/VOL nadajnika muszą spełnić VIH/VIL odbiornika dla całego zakresu zasilania. „Oba układy są 3,3 V” nie wystarcza, jeśli jeden pin nie jest 5 V tolerant albo domena IO jest 1,8 V.

Translator musi obsługiwać kierunek każdej linii i szybkość. Automatyczne dwukierunkowe translatory przeznaczone do open-drain I²C często nie nadają się do push-pull SPI. MISO ma przeciwny kierunek niż SCK/MOSI/CS.

Przy niezasilonym urządzeniu sygnał może zasilać je pasożytniczo przez diody. Sekwencja power domains wymaga izolacji albo ustawienia GPIO high-Z.

Obsługa błędów#

SPI samo nie ma ACK, adresowego arbitrażu ani standardowego CRC. Sukces kontrolera oznacza tylko wygenerowanie taktów. Integralność zapewnia warstwa urządzenia:

  • ID i wartości resetowe;
  • CRC, jeśli obsługiwane;
  • sequence/header FIFO;
  • odczyt zwrotny konfiguracji;
  • sanity limits;
  • timestamp i monotoniczność;
  • timeout i liczniki overrun.

Nie powtarza się bezmyślnie komendy, która mogła zostać wykonana. Dla zapisu flash lub resetu trzeba znać idempotencję. Odczyt można zwykle powtórzyć po ponownym ustawieniu CS.

Po serii błędów zmniejszenie SCK jest użyteczne diagnostycznie. Jeśli pomaga, bada się margines timingowy i PCB zamiast pozostawiać nieudokumentowany workaround.

Arbiter i czas rzeczywisty#

Każdy transfer ma koszt:

t_bus ≈ (liczba_bitów / f_SCK) + t_CS + t_software

Do tego dochodzi oczekiwanie na magistralę i DMA. Harmonogram zna deadline data-ready IMU oraz maksymalny nieprzerwany transfer innych klientów.

Priorytetowa kolejka bez możliwości przerwania trwającego burstu nadal cierpi priority inversion. Długi zapis pamięci należy porcjować. CS nie może zostać przerwany w połowie transakcji, więc fragmentacja musi zachodzić na granicach dozwolonych przez urządzenie.

Metryki obejmują wait time, transfer time, deadline misses i occupancy. Średnie obciążenie 20% nie gwarantuje braku kolizji szczytowych.

Diagnostyka#

Oscyloskop sprawdza poziomy, ringing, setup/hold, CS i relację do zasilania. Analizator logiczny dekoduje długie sekwencje, ale nie widzi marginalnych poziomów i contention.

Procedura:

  1. zweryfikuj zasilanie oraz CS idle;
  2. ustaw mały SCK i odczytaj ID;
  3. obejrzyj CPOL/CPHA analogowo;
  4. porównaj bajty z datasheetem;
  5. zwiększaj SCK, mierząc margines;
  6. aktywuj DMA i obciąż CPU/pamięć;
  7. uruchom inne urządzenia na magistrali;
  8. testuj temperaturę i napięcie;
  9. wstrzyknij timeout/reset na stanowisku;
  10. sprawdź liczniki i recovery.

Wzorzec 0xAA/0x55 ujawnia przejścia bitów, ale urządzenie rejestrowe może nie pozwalać na loopback. Wtedy używa się kontrolowanego rejestru scratch lub zewnętrznego urządzenia testowego.

Lista uruchomieniowa#

  • Tryb i kolejność bitów pochodzą z datasheetu.
  • Częstotliwość mieści się w budżecie obu stron i PCB.
  • CS respektuje setup/hold oraz granice burstu.
  • MISO wszystkich urządzeń przechodzi w high-Z.
  • RX jest obsługiwany również podczas zapisu.
  • DMA ma poprawne cache maintenance i lifetime bufora.
  • Transfer ma deadline i recovery.
  • Konfiguracja jest okresowo weryfikowana.
  • FIFO ma ochronę overflow i parser długości.
  • Timestamp opisuje próbkę, nie callback.
  • Arbiter respektuje deadline szybkiego sensora.
  • Log zawiera błędy, latency i reset generation.

Powiązane tematy#

Przypisy#

  1. STMicroelectronics, AN5543: Guidelines for enhanced SPI communication — wersje peryferium, DMA, FIFO, zakończenie transferu i błędy przepływu.
  2. TDK InvenSense, ICM‑42688‑P — przykład timingów oraz rejestrowego interfejsu szybkiego IMU.
  3. Bosch Sensortec, BMI088 — przykład niezależnych interfejsów części gyro i accel.

Utworzono: 15 sierpnia 2026. Ostatnia aktualizacja: 15 sierpnia 2026. Źródła zweryfikowano: 15 sierpnia 2026.

Źródła z centralnego rejestru

  1. ST AN5543: Guidelines for enhanced SPI communication on STM32 MCUs and MPUs [nota aplikacyjna producenta]
  2. TDK InvenSense: ICM-42688-P [datasheet]
  3. Bosch Sensortec: BMI088 [datasheet]
  4. STMicroelectronics: STM32H743/753 documentation [producent]