Java na STM32: sposób na interfejsy GUI w systemach embedded
Graficzny interfejs użytkownika (GUI) stał się istotnym elementem systemów wbudowanych, ponieważ to właśnie on ma bezpośredni wpływ na komfort obsługi urządzenia przez końcowego odbiorcę. Projektowanie takiego interfejsu nie jest jednak proste – wymaga zrozumienia i uwzględnienia wielu różnych aspektów tego zagadnienia. Znaczna złożoność wynika ze zmiennych wymagań podczas projektowania i użytkowania systemu, jego dynamicznego działania, jak również typowych ograniczeń systemu wbudowanego w postaci rozmiaru pamięci oraz wydajności procesora. Problemem jest również kompilacja na różne platformy.
Rozwiązania obiektowe
Faktem jest, że języki zorientowane obiektowo zostały szybko zaadoptowane do realizacji GUI, ponieważ interfejs zasadniczo składa się z wielu elementów zorganizowanych w luźno połączone struktury.
Wiele organizacji zajmujących się interfejsem korzysta z rozwiązania w języku Smalltalk opisanego w publikacji “Design Patterns – Elements of Reusable Object Oriented Software” autorstwa Ericha Gamma i Addison Wesley. Projekt GUI powinien być oparty na koncepcji MVC (Model-Widok-Kontroler), ponieważ jest to najlepsze znane rozwiązanie do projektowania elastycznych i łatwych w utrzymaniu interfejsów. Model przechowuje dane, które są aktualizowane przez Kontroler na skutek działań użytkownika za pomocą obiektu graficznego (widżetu). Gdy dane modelu ulegną zmianie, zleca on aktualizację Widoku, aby wyświetlić nowy wygląd widżetu. Wszystkie elementy są luźno powiązane, zatem nowy widżet oparty na istniejącym modelu może zostać dodany bez wpływu na pozostałe części systemu.
Rys. 1. Schemat koncepcji MVC
MVC wykorzystuje kilka wzorców projektowych, takich jak Obserwator, wzorzec Kompozytu i jego metody, Strategia i Dekorator. Schemat MVC można zrealizować w języku C++. Język ten ma jednak kilka wad, które nie dają znaczącej przewagi nad tradycyjnym C w systemach wbudowanych. Do tych wad należą:
- Nieostra granica między programowaniem strukturalnym w C a programowaniem obiektowym w C++
- Mała gęstość kodu
- Konieczność korzystania ze wskaźników
- Brak automatycznego zarządzania pamięcią
Zalety języka Java
- Wirtualizacja
Technologia Java została wymyślona w latach 90. jako alternatywa dla istniejących języków – łatwiejsza, niż Smalltalk i bezpieczniejsza od C++. Tym samym Java odziedziczyła składnię po C/C++, dzięki czemu programiści C nie mają problemów z jej przyswojeniem. Java definiuje również bezpieczne środowisko wykonania, które obejmuje ściśle określoną semantykę i środowisko wykonania zawierające wirtualny procesor z bezpieczną listą rozkazową.
Tworzenie interfejsu użytkownika doskonale współgra z charakterystyką Javy. Obiektowy charakter pozwala zaprojektować architekturę z luźno połączonymi strukturami. Java oferuje także inne cechy, które pozwalają odciążyć programistów odpowiedzialnych za złożone GUI, będącego dynamicznym systemem sterowanym zdarzeniami. Trzy najważniejsze funkcje to automatyczne zarządzanie pamięcią za pomocą wirtualnego procesora i odśmiecania pamięci (garbage collector), sprawdzanie wyjątków w czasie wykonania – na przykład odwołanie do adresu spoza tablicy, a także obsługa zasobów sprzętowych i przenośność kodu binarnego, dzięki czemu skompilowany program można wykorzystać w wielu różnych projektach.
- Symulacja oprogramowania wbudowanego
Symulacja oprogramowania zawsze stanowiła trudne zadanie dla aplikacji przeznaczonych na mikrokontroler, ponieważ może ona napotkać wiele problemów, takich jak prawdziwa liczba cykli wykonania instrukcji, funkcjonalność i testowanie z użyciem urządzenia docelowego.
Drogie narzędzia do symulacji mogą być w stanie obsłużyć wszystkie te obszary jednocześnie, łącząc modele symulacji Verilog i zestaw narzędzi programistycznych wspieranych przez wydajne urządzenia, jednak proste symulatory zwykle są niewystarczające, ponieważ brakuje w nich dobrze zdefiniowanych modeli symulacji.
Java daje możliwość określenia nowego celu dla systemu symulacji. Ponieważ Java jest zestandaryzowaną platformą z dobrze określonym modelem wykonania i interfejsem programistycznym (API), istnieje jednoznaczny podział na platformę wykonania (sprzęt i wirtualny procesor) oraz właściwą aplikację. Ponieważ platforma wykonania może zostać w pełni przetestowana i scharakteryzowana osobno, symulator może skupić się wyłącznie na samej aplikacji.
Rys. 2. Platformy systemu wbudowanego i symulacji Javy
Rozumiejąc koncepcję platformy wirtualnej Java, można łatwo i w przystępny sposób stworzyć bliźniaczą platformę, która pracuje na stacji roboczej i realizuje symulację. Platformy mogą symulować takie właściwości, jak na przykład kolejkowanie w systemie czasu rzeczywistego. Mogą również stanowić dodatki, których stacje robocze nie oferują, na przykład panel przedni (wyświetlacz LCD, przyciski, diody), czujniki i układy wykonawcze, specjalne protokoły komunikacji itp.
Ponieważ kod aplikacji jest wykonywany na obu platformach, symulowana platforma staje się potężnym narzędziem do testowania GUI od najwyższego poziomu (pokrycie testu funkcjonalnego) do najniższego (na poziomie binarnym). Pozwala zatem na analizę zużycia pamięci aplikacji na etapie projektowania.
- Dodawanie graficznego interfejsu do aplikacji w C
Aby wzbogacić istniejącą aplikację w języku C o interfejs graficzny napisany w Javie, należy wykonać trzy kroki:
- Umieścić maszynę wirtualną Javy w istniejącym środowisku oprogramowania
- Połączyć sprzęt odpowiedzialny za grafikę z graficznymi bibliotekami Javy
- Połączyć natywny kod aplikacji (C/asm) z interfejsem graficznym w Javie zgodnie z koncepcją MVC
Integracja Javy z natywnym kodem C/asm musi być prosta, aby zagwarantować programistom łatwą i bezpieczną drogę łączenia kodu Javy z dobrze znanym i stabilnym kodem aplikacji. Istniejące sterowniki i funkcje aplikacji muszą być dostępne dla nowego kodu Javy. Jednocześnie narzut na wywołanie funkcji oraz alokację zasobów pamięci i procesora powinien pozostać niewielki.