Z80 jest jedną z najbardziej wdzięcznych historycznych platform do eksperymentu z kontrolerem czasu rzeczywistego. Sam CPU nadal nie ma ADC, PWM ani SPI, lecz rodzina Zilog obejmuje peryferia zaprojektowane jako spójny system: CTC generuje i mierzy czas, PIO obsługuje linie równoległe, SIO realizuje komunikację szeregową, a układ przerwań da się ułożyć priorytetowo. Rezultatem może być rzetelny HIL, stanowisko jednoosiowe albo ograniczony, nieuzbrojony demonstrator z zewnętrznym układem bezpieczeństwa.

Dlaczego Z80 jest wygodniejszy od wielu konstrukcji retro#

Z80 ma 16-bitową przestrzeń adresową pamięci i oddzielną przestrzeń portów I/O. Rejestry główne i alternatywne ułatwiają krótką obsługę przerwań, a tryb przerwań IM2 pozwala urządzeniom uczestniczyć w wyborze wektora. Najważniejsze jest jednak otoczenie CPU.

Z80 CTC zawiera cztery kanały counter/timer. Kanał może dzielić zegar, liczyć zewnętrzne impulsy i generować przerwania. Z80 PIO udostępnia dwa 8-bitowe porty z trybami wejścia, wyjścia, dwukierunkowym oraz bit-control. Z80 SIO zapewnia dwa kanały komunikacji szeregowej. Zamiast bit-bangować timer, port równoległy i UART w pętli, można przypisać każdemu zadaniu właściwy układ.

Minimalny zestaw sprzętowy#

 Z80 CPU ---- magistrala ---- ROM/Flash
    |             |---------- SRAM
    |             |---------- Z80 CTC ---- tick / capture
    |             |---------- Z80 PIO ---- handshake IMU / shutter opto
    |             +---------- Z80 SIO ---- telemetria / HIL
    |
 supervisor + watchdog ---- enable buforów wyjściowych

Do tego dochodzą dekoder adresu, generator zegara, reset, kondensatory odsprzęgające i translacja poziomów. Współczesna IMU 3,3 V nie może zostać bezpośrednio podłączona do starej logiki 5 V. Nawet jeśli poziom HIGH „wydaje się działać”, trzeba sprawdzić progi wejść, dopuszczalne napięcia oraz kierunek każdego sygnału.

Przykładowe porty z laboratorium są jedynie szablonem:

Port Urządzenie Funkcja
0x10–0x13 CTC tick, timestamp, pomiar czasu
0x20–0x23 PIO bank próbek, wyjście migawki, status
0x30–0x33 SIO ramki HIL i log
0x40–0x47 generator wyjść cztery komendy + status/heartbeat

Dekoder powinien zapewniać, że dokładnie jedno urządzenie odpowiada na cykl I/O. Stan PIO po resecie nie może przypadkowo włączyć optoizolatora ani generatora. Bufor wyjściowy otrzymuje osobny sygnał OUTPUT_ENABLE, domyślnie ściągnięty do stanu wyłączenia.

Podział kanałów CTC#

Cztery kanały CTC pozwalają rozdzielić zadania:

Kanał Rola Dlaczego
0 tick regulatora 100–250 Hz jedyny zegar uruchamiający pętlę
1 licznik czasu próbki mierzy wiek danych niezależnie od logiki programu
2 baud-rate clock SIO, jeśli potrzebny komunikacja nie zmienia okresu regulatora
3 pomiar szerokości impulsu / oscyloskopowy heartbeat diagnostyka WCET i watchdog

Dokładna stała czasowa zależy od zegara CPU, preskalera i trybu kanału. Wartość należy obliczyć z instrukcji CTC i potwierdzić analizatorem logicznym. Zakładanie, że tani oscylator ma idealną częstotliwość, prowadzi do dryfu timestampów względem komputera HIL.

ISR CTC ma tylko zwiększyć licznik i ustawić tick_pending. Jeżeli poprzedni tick nadal jest oczekujący, kod zwiększa deadline_miss. Nie wolno po prostu nadpisać flagi i udawać, że pętla działała 100 Hz.

PIO jako bezpieczny interfejs, nie generator wszystkiego#

PIO dobrze nadaje się do handshake z mostkiem IMU. Mostek wystawia DATA_READY, CPU odczytuje bank bajtów, a następnie potwierdza ACK. Tryb bit-control może wywołać przerwanie dla konkretnej kombinacji linii, ale w pierwszej wersji prostsze jest próbkowanie statusu w pętli tick.

Generowanie czterech przebiegów napędu programowymi zmianami PIO jest ryzykowne: czas zależy wtedy od przerwań i ścieżki kodu. PIO powinno przekazywać wartości do zewnętrznego generatora albo emulatora ESC. Zewnętrzny układ posiada timeout i zeruje kanały, jeżeli nie dostanie kolejnej kompletnej ramki.

Jedna linia PIO może sterować migawką przez transoptor. Jej wartość nie pochodzi bezpośrednio z komendy operatora, lecz z małej maszyny stanów: IDLE -> PULSE -> LOCKOUT -> IDLE. Reset lub stan safe wymuszają IDLE niezależnie od poprzedniego żądania.

SIO i ramki HIL#

Tekst CSV jest wygodny dla człowieka, lecz kosztowny dla Z80. Właściwa ramka HIL może mieć stałą długość:

SYNC  TYPE  SEQ  T_US[4]  GX[2] GY[2] GZ[2]  SET_R[2] SET_P[2] SET_Y[2]  FLAGS  CRC

Parser szuka bajtu synchronizacji, sprawdza typ i długość, odbiera payload, a na końcu porównuje CRC. Po błędzie nie interpretuje fragmentu jako kolejnej próbki, tylko wraca do szukania synchronizacji. Numer sekwencji wykrywa utratę i powtórzenie ramki.

Nadajnik logu działa z bufora kołowego. ISR SIO zabiera kolejny bajt, gdy rejestr jest pusty. Główna pętla nigdy nie czeka na transmisję. Po przepełnieniu bufora rośnie licznik log_drop; sterowanie pozostaje terminowe.

Regulator stałoprzecinkowy#

Rdzeń fc-lab-z80 stosuje tę samą reprezentację co wersje MCS-51 i 6502:

  • rate_tenth_dps oraz demand_tenth_dps: int16_t;
  • kp_q8: współczynnik pomnożony przez 256;
  • collective_q8: 0…255;
  • iloczyn pośredni: int32_t;
  • cztery wyjścia po saturacji: 0…255.

Z80 nie ma instrukcji sprzętowego mnożenia w klasycznym rdzeniu. Procedurę shift-add można przyspieszyć dla stałych współczynników, ale nie należy optymalizować przed pomiarem. Kluczowe są cztery liczby: czas minimalny, średni, maksimum i najdłuższa ścieżka przy nasyceniu.

Przykładowy regulator P rate:

e_roll = demand_roll - gyro_roll
u_roll = (e_roll * kp_roll_q8) >> 8

m1 = sat(collective + u_roll - u_pitch + u_yaw)
m2 = sat(collective - u_roll - u_pitch - u_yaw)
m3 = sat(collective - u_roll + u_pitch + u_yaw)
m4 = sat(collective + u_roll + u_pitch - u_yaw)

Pierwsza wersja celowo nie ma całki. Integral windup, reset integratora i skalowanie czasu powinny zostać dodane dopiero po zamknięciu pomiarów podstawowej pętli.

IMU: trzy warianty#

Programowe SPI#

PIO wystawia zegar i MOSI oraz czyta MISO. To dobre narzędzie do sprawdzenia rejestrów sensora na małej prędkości. Każdy bit wymaga wielu instrukcji I/O, dlatego pełny burst należy zmierzyć. Transakcja ma timeout; brak odpowiedzi nie może zatrzymać ticku.

Rejestr przesuwny i logika dyskretna#

Zewnętrzny rejestr może taktować SPI i przedstawić CPU gotowy bajt. Rozwiązanie zachowuje charakter retro, ale rośnie liczba układów oraz stanów brzegowych. Potrzebne są sygnały busy, done, error i reset.

Mostek współczesny#

Mały MCU czyta IMU przez SPI/DMA i wysyła do Z80 ramkę SIO albo udostępnia bank PIO. Jest to najbardziej niezawodne rozwiązanie laboratoryjne. Aby wynik pozostał uczciwy, filtracja i regulator działają na Z80, a mostek tylko pozyskuje, waliduje i timestampuje dane.

IM2 i priorytety przerwań#

Tryb IM2 pozwala utworzyć tablicę wektorów i przypisać urządzeniom osobne procedury. Sam fakt użycia IM2 nie rozwiązuje priorytetów. W rodzinie peryferiów Zilog łańcuch daisy-chain przekazuje IEI/IEO, więc fizyczna kolejność elementów wpływa na to, które przerwanie wygrywa.

Sensowna kolejność dla laboratorium to:

  1. timeout/safety lub wejście zewnętrznego supervisora;
  2. CTC tick pętli;
  3. zakończenie odbioru próbki SIO/PIO;
  4. nadajnik telemetrii;
  5. funkcje interfejsu użytkownika.

Każda ISR zapisuje tylko rejestry, które modyfikuje, i ma policzony maksymalny czas. Alternatywny zestaw rejestrów Z80 może skrócić obsługę jednego najważniejszego przerwania, ale nie wolno współdzielić go bez reguły między kilkoma ISR. Zagnieżdżanie przerwań dodaje trudne ścieżki czasowe; pierwsza wersja powinna go unikać.

Tablica testów przerwań wymusza jednocześnie CTC, odbiór SIO i opróżnianie nadajnika. Analizator sprawdza latencję ticku oraz kolejność potwierdzeń. Błąd polegający na skasowaniu flagi urządzenia przed zachowaniem danych może znikać przy pojedynczym źródle, a ujawniać się dopiero w takim zbiegu.

Rzeczywisty budżet cykli#

Dla zegara 4 MHz okres 10 ms obejmuje 40 000 taktów, ale instrukcje Z80 zajmują różną liczbę taktów T. Cykle oczekiwania pamięci i peryferiów dodatkowo zmniejszają budżet. Zamiast przeliczać cały program ręcznie, stosuje się dwa pomiary:

  • statyczne policzenie najdłuższych ścieżek krytycznych z listingu asemblera;
  • pomiar sprzętowy pinu LOOP_BUSY dla zestawu wektorów granicznych.

Budżet pętli nie może zużywać 100% okresu. Przykładowy podział 40 000 T:

Zadanie Limit Udział
odebranie i kontrola ramki 7 000 T 17,5%
skalowanie i filtr 8 000 T 20%
regulator trzech osi 8 000 T 20%
mikser i saturacja 4 000 T 10%
publikacja bufora 2 000 T 5%
health + migawka 2 000 T 5%
rezerwa na ISR/jitter 9 000 T 22,5%

To przykład metody, nie wynik benchmarku gotowego kodu. Rzeczywiste wartości zależą od kompilatora, pamięci i implementacji mnożenia. Jeżeli najgorszy przypadek nie mieści się w limicie, obniża się częstotliwość, upraszcza filtr albo przenosi transport do sprzętu — nie ukrywa overrunu.

Zasilanie, reset i fizyczna płyta#

Stary Z80 może być NMOS lub CMOS. Różnią się poborem, dopuszczalnym zegarem i zachowaniem wejść. Pełne oznaczenie części jest obowiązkowe. Płytę uruchamia się z zasilacza laboratoryjnego z limitem prądu, mierząc szynę przy skokach obciążenia oraz czas narastania resetu.

Supervisor powinien trzymać reset przy zbyt niskim napięciu. Oddzielny latch OUTPUT_ENABLE ma zostać wyzerowany sprzętowo i wymagać serii poprawnych heartbeatów do włączenia. Sam pull-down na linii CPU nie wystarcza, jeżeli bufor po drugiej stronie zasila się wcześniej.

Magistralę prowadzi się krótko, z kondensatorem 100 nF przy każdym układzie i kondensatorami zbiorczymi przy wejściu sekcji. Z80 z kilkoma układami DIP ma duże pętle prądowe; płytka stykowa nadaje się do wolnego monitora ROM, ale nie jest wiarygodnym nośnikiem do oceny szybkich zboczy i zakłóceń silnika.

Mostek 3,3 V oraz IMU otrzymują własną czystą sekcję zasilania. Linie między domenami przechodzą przez jawne translatory. Dwukierunkowy „automatyczny” translator przeznaczony do I²C może źle działać na push-pull SPI lub równoległej magistrali; typ dobiera się do kierunku i prędkości sygnału.

Wyzwalanie migawki#

W przewodowym selfie sticku naciśnięcie przycisku zmienia stan toru zestawu słuchawkowego; aplikacja telefonu mapuje zdarzenie przycisku, często głośności, na zdjęcie. Najbezpieczniej pozostawić oryginalną elektronikę sticka i dołączyć fototranzystor równolegle do jego przycisku. PIO steruje diodą transoptora przez rezystor, ale nie dzieli masy z telefonem.

Sekwencja firmware:

  1. przyjmuje żądanie tylko przy świeżych danych i aktywnym trybie laboratoryjnym;
  2. wykrywa zbocze 0 -> 1;
  3. aktywuje optoizolator na 120 ms;
  4. blokuje kolejne żądanie przez 500 ms;
  5. przy resecie, watchdogu lub błędzie natychmiast otwiera obwód;
  6. zapisuje czasy początku i końca impulsu w logu.

W przypadku aparatu z dedykowanym wejściem remote trzeba sprawdzić, czy ma osobne styki focus i shutter. Laboratorium realizuje pojedynczy styk migawki; rozszerzenie o focus wymaga osobnej maszyny stanów, dwóch izolowanych kanałów i dokumentacji producenta aparatu.

Plan testów sprzętowych#

  1. Uruchomić ROM monitor i sprawdzić RAM wzorami 00, FF, 55, AA.
  2. Zmierzyć tick CTC przez godzinę względem analizatora.
  3. Wywołać sztuczny najgorszy przypadek regulatora i zmierzyć szerokość pinu LOOP_BUSY.
  4. Zalać SIO telemetrią; regulator nie może wydłużyć okresu.
  5. Usunąć bajt z ramki IMU; wyjścia muszą przejść do zera.
  6. Zatrzymać CPU; zewnętrzny watchdog ma wyłączyć bufory.
  7. Przytrzymać żądanie migawki przez sekundę; powstaje jeden impuls.
  8. Odłączyć aplikację/aparat; brak potwierdzenia ma zostać zalogowany, a nie powtarzany bez końca.

Laboratorium fc-lab-z80#

Repozytorium w git/fc-lab-z80 zawiera przenośny rdzeń C, test jednostkowy, emulator ESC, scenariusz HIL, przykładowy log i port asemblerowy. Port definiuje miejsca CTC/PIO/SIO jako wartości przykładowe; konstruktor musi zmienić je zgodnie z dekoderem swojej płyty.

make test działa bez cross-kompilatora. Sprawdza bezpieczny start, kierunek roll, wygaśnięcie impulsu migawki i reakcję na starą próbkę. make hil generuje odpowiedź modelu napędu. Kompilacja dla prawdziwego Z80 jest osobnym etapem, bo składnia i format startowy zależą od z88dk, SDCC albo wybranego asemblera.

Cross-kompilator i granica między C a asemblerem#

Kod przenośny jest napisany w C11, aby identyczne wektory testowe działały na komputerze. Docelowy kompilator Z80 może jednak różnie traktować rozmiar int, konwencję wywołań, przesunięcie liczby ujemnej i koszt typu 32-bitowego. Port zaczyna się od testu założeń:

_Static_assert(sizeof(uint8_t) == 1, "byte");
_Static_assert(sizeof(int16_t) == 2, "word");
_Static_assert(sizeof(int32_t) == 4, "long");

Wartości ujemnych nie przesuwa się w prawo w kodzie zależnym od implementacji. Dzielenie przez 256 wykonuje się procedurą o jawnie zdefiniowanym zaokrągleniu. Ten sam plik wektorów zawiera dodatnie i ujemne błędy, wartości tuż przy zerze oraz granice nasycenia; wynik targetu jest porównywany bajt po bajcie z hostem.

Asembler ma sens w czterech miejscach:

  • procedura startowa, ustawienie stosu i IM2;
  • krótkie ISR CTC/SIO/PIO;
  • funkcja I/O używająca instrukcji IN/OUT;
  • zmierzony hotspot, na przykład mnożenie 16×8.

Cały regulator w asemblerze utrudnia testy i portowanie. Najpierw kompiluje się C z opcją generowania listingu, identyfikuje najdroższy fragment i zachowuje referencyjną implementację. Optymalizowany wariant musi przejść ten sam test dla wszystkich wektorów.

Build targetu powinien zapisywać wersję kompilatora, flagi, mapę pamięci, rozmiar sekcji i hash ROM-u. Dwa pliki o tej samej nazwie, zbudowane innymi opcjami optymalizacji, mogą mieć inny timing i nie powinny być traktowane jako ten sam firmware.

Diagnostyka na podstawie logu#

Przykładowy log w repozytorium jest krótki, lecz docelowy rekord diagnostyczny powinien obejmować:

Pole Co pozwala wykryć
seq utratę lub powtórzenie iteracji
ctc_time dryf i nieciągłość zegara
sample_seq, sample_age zamrożony mostek lub kolejkę starych danych
loop_ticks WCET i sporadyczne opóźnienia
irq_mask źródła przerwań aktywne w danej iteracji
gyro, setpoint znak osi i rzeczywisty błąd regulatora
correction, motor saturację oraz błąd miksera
safe_reason przyczynę, nie tylko sam stan zero
shutter_state request, pulse, lockout i wymuszone otwarcie
reset_cause watchdog, zasilanie albo reset zewnętrzny

Analiza rozpoczyna się od ciągłości czasu i numerów, dopiero później od przebiegu regulatora. Jeżeli dt skacze, oscylacje mogą wynikać z harmonogramu, a nie ze złego Kp. Jeżeli komendy są poprawne, ale model ESC nie odpowiada symetrycznie, problem leży w dynamice albo mapowaniu kanałów.

Logu nie zapisuje się synchronicznie do wolnej pamięci w każdym ticku. Rekord o stałej długości trafia do pierścienia RAM. SIO opróżnia go w tle, a komputer HIL zapisuje plik. Pole log_drop informuje o przepełnieniu; brak rekordu nigdy nie zostaje zamaskowany sztucznym powtórzeniem poprzedniej wartości.

Przegląd przed jakimkolwiek testem ruchowym#

Przegląd powinny wykonać dwie osoby albo jedna osoba według wydrukowanej checklisty. Sprawdza się rozdzielenie 5 V/3,3 V, orientację układów, stan wyjść przy odłączonym CPU, reakcję watchdoga i fizyczny wyłącznik zasilania aktuatora. Analizator logiczny ma potwierdzić tick oraz timeout bez polegania na komunikatach tego samego firmware.

Następnie wstrzykuje się kolejno: brak zegara CPU, brak CTC, zwarcie linii data-ready do 0 i 1, ciągły strumień SIO, błędne CRC, maksymalny gyro oraz reset w fazie impulsu migawki. Każda obserwacja jest dołączona do wersji ROM-u. Dopiero komplet oczekiwanych stanów bezpiecznych pozwala przejść ze sztucznego obciążenia na stanowisko mechaniczne.

Ocena#

Z80 z CTC/PIO/SIO jest bardziej kompletnym systemem czasu rzeczywistego niż sam 6502 lub goły 8051, ale płaci za to większą liczbą układów. Do HIL i nauki jest znakomity: wymusza jawne określenie źródeł przerwań, priorytetów, ramek i deadline. Do małego płatowca współczesny MCU będzie lżejszy, oszczędniejszy i prostszy.

Jeżeli celem jest zachowanie historycznej części, rozsądna architektura dzieli odpowiedzialność: Z80 liczy regulator lub steruje eksperymentem, a niezależny safety MCU pilnuje wieku próbki, watchdogów i fizycznych wyjść. Awaria jednego elementu nie może pozostawić ostatniej komendy na czas nieograniczony.

Powiązane tematy#

Przypisy#

  1. Zilog, Z80 CPU User Manual, architektura CPU, instrukcje i tryby przerwań.
  2. Zilog, Z80 CPU Peripherals User Manual, CTC, PIO, SIO i DMA.
  3. STMicroelectronics, STM32WB BLE HID Selfie, przykład urządzenia BLE HID używającego klawisza głośności jako migawki.

Źródła z centralnego rejestru

  1. Zilog: Z80 CPU User Manual [oficjalna dokumentacja CPU]
  2. Zilog: Z80 CPU Peripherals User Manual [oficjalna dokumentacja CTC, PIO, SIO i DMA]
  3. STMicroelectronics: STM32WB BLE HID Selfie [oficjalny przykład BLE HID używający klawisza głośności do wyzwalania zdjęcia]