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_dpsorazdemand_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:
- timeout/safety lub wejście zewnętrznego supervisora;
- CTC tick pętli;
- zakończenie odbioru próbki SIO/PIO;
- nadajnik telemetrii;
- 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_BUSYdla 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:
- przyjmuje żądanie tylko przy świeżych danych i aktywnym trybie laboratoryjnym;
- wykrywa zbocze
0 -> 1; - aktywuje optoizolator na 120 ms;
- blokuje kolejne żądanie przez 500 ms;
- przy resecie, watchdogu lub błędzie natychmiast otwiera obwód;
- 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#
- Uruchomić ROM monitor i sprawdzić RAM wzorami
00,FF,55,AA. - Zmierzyć tick CTC przez godzinę względem analizatora.
- Wywołać sztuczny najgorszy przypadek regulatora i zmierzyć szerokość pinu
LOOP_BUSY. - Zalać SIO telemetrią; regulator nie może wydłużyć okresu.
- Usunąć bajt z ramki IMU; wyjścia muszą przejść do zera.
- Zatrzymać CPU; zewnętrzny watchdog ma wyłączyć bufory.
- Przytrzymać żądanie migawki przez sekundę; powstaje jeden impuls.
- 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#
- Kontroler lotu na elektronice 8-bitowej
- 6502, Commodore i Atari jako kontroler
- Flight controller na 8051
- Własny flight controller: od Arduino do STM32
- Watchdog w flight controllerze
- Interfejsy UART w UAV
Przypisy#
- Zilog, Z80 CPU User Manual, architektura CPU, instrukcje i tryby przerwań.
- Zilog, Z80 CPU Peripherals User Manual, CTC, PIO, SIO i DMA.
- 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
- Zilog: Z80 CPU User Manual [oficjalna dokumentacja CPU]
- Zilog: Z80 CPU Peripherals User Manual [oficjalna dokumentacja CTC, PIO, SIO i DMA]
- STMicroelectronics: STM32WB BLE HID Selfie [oficjalny przykład BLE HID używający klawisza głośności do wyzwalania zdjęcia]