Między niebem a ziemią, czyli sterowniki peryferiów.

W drugim artykule na tym blogu, napisałem, iż naturą systemów wbudowanych jest wzajemne przenikanie się sprzętu i oprogramowania. Wynika z tego, że każda funkcjonalność w naszym urządzeniu będzie odwoływać się w mniejszym lub większym stopniu do układów peryferyjnych mikrokontrolera. Na początek trochę teorii…

Teoria

Istnieje kilka sposobów na powiązanie świata software’u ze sprzętem. Pierwszym, często spotykanym w projektach początkujących adeptów, jest odwoływanie się do sprzętu bezpośrednio z poziomu aplikacji. Przywołując przykład terminala z poprzedniego artykułu, widzimy, że można “dobrać się” bezpośrednio do rejestrów portu szeregowego (UART) w celu odczytania odebranego bajtu, jak również można pisać bezpośrednio na wyświetlacz, używając np. GPIO. Podejście takie ma liczne wady – powtarzające się fragmenty kodu, słaba czytelność i brak przenośności między platformami.

W drugim podejściu wprowadzamy pośrednią warstwę abstrakcji sprzętu. Innymi słowy, zamiast pobierać dane bezpośrednio z peryferiów, odwołujemy się do nich przez funkcję interfejsu, jaki udostępnia warstwa abstrakcji. Ponownie spójrzmy na przykładu terminala. Można wyobrazić sobie ogólną funkcję PobierzBajt(), która odwoła się do portu szeregowego w naszym imieniu i zwróci ostatnio odebrany znak. Zaletą takiego podejścia jest odseparowanie kodu aplikacji od kodu platformowego, który sięga do sprzętu. Zapewnia to lepszą czytelność kodu oraz jego przenośność. Dobrze zaprojektowany kod aplikacji wymagać wówczas będzie jedynie przeniesienia na inną platformę. Wciąż jednak jesteśmy w pośredni sposób powiązani ze sprzętem, co utrudnia m.in. współdzielenie zasobów między wieloma aplikacjami.

Trzecie podejście całkowicie odcina warstwę aplikacji od sprzętu. Wszelkie zapytania i żądania trafiają do demonów/serwisów, które to dopiero przekazują je niżej, do warstwy abstrakcji. Rozwiązany zostaje w ten sposób problem współdzielenia zasobów. Nie można także zapominać o kwestiach bezpieczeństwa. W dwóch pierwszych podejściach aplikacja jest w stanie wykonać dowolną operację, na którą pozwala udostępniony interfejs sprzętu. W przypadku błędu nic jej nie powstrzymuje przed podjęciem akcji, która potencjalnie może doprowadzić do błędnego działania czy nawet awarii całego systemu. Do warstwy usług można także oddelegować część funkcjonalności, którą wcześniej zawierała aplikacja. Dobry przykład stanowią tu wszelkie protokoły komunikacyjne.

Praktyka

Zdecydowaliśmy się wykorzystać w projekcie procesor SMT32F407. Podobnie jak inni producenci mikrokontrolerów, tak firma STMicroelectronics dostarcza dla układów z rodziny STM32 wygodną bibliotekę HAL (ang. Hardware Abstraction Layer), której użyjemy.

Jak to? Jaki profesjonalista używa HAL’a? Tylko pisanie po rejestrach!

No właśnie… W środowisku entuzjastów mikrokontrolerów biblioteka HAL budzi dość skrajne emocje. Wiele osób, zwłaszcza z dużym doświadczeniem w programowaniu, krytykuje ją za nadmierną zasobożerność, przesadne skomplikowanie, ograniczone możliwości i sporą ilość błędów. Z drugiej strony osoby rozpoczynające dopiero przygodę z STM’ami chwalą prostotę konfiguracji i łatwość użycia jaką zapewnia (zazwyczaj do momentu, w którym nie natrafią na jeden z bugów 🙂 ).

Dlaczego więc użyjemy HAL’a?

Mimo wszystkich swoich słabości i problemów jakie niesie, ma on jedną, ogromną zaletę – pozwala na prawdę łatwo i szybko uruchomić potrzebne układy peryferyjne. Na etapie prototypu, kiedy tak na prawdę dopiero poszukujemy najlepszych rozwiązań, nie musimy tracić cennego czasu na zagłębianie się w niuanse konfiguracji danego urządzenia. Ot wołamy odpowiednią funkcję Init i gotowe. Nawet jeśli za jakiś czas okaże się, iż nie będzie nam już ono potrzebne lub przeniesiemy funkcjonalność w inne miejsce, nie straciliśmy wiele.
Dopiero w momencie kiedy całość systemu nabierze ostatecznych kształtów, można będzie przystąpić do optymalizacji, czyli w gruncie rzeczy zastąpienia HAL’a w krytycznych miejscach czymś bardziej adekwatnym, np. odwołaniami bezpośrednio do rejestrów.
W przyszłości postaram się poświęcić problemom optymalizacji nieco więcej miejsca. A być może nawet cały dzień 🙂

Posted in Sto dni w kolorze.