Distortos to system operacyjny czasu rzeczywistego (RTOS) napisany w C++ z myślą o procesorach ARM Cortex-M, a szczególnie STM32. Pisałem już o nim przy okazji ciekawych projektów C++ Embedded i łazika na NASA Space Apps. Aplikacja na STM w łaziku chyba jednak będzie za prosta, żeby dawać do niej RTOSa, ale ostatnio zacząłem przepisywać Micromouse do C++ i tam już się nada. Źródła distortosa możecie znaleźć na GitHubie, a dokumentację na stronie projektu. Jest on jeszcze w dosyć wczesnej fazie rozwoju, dlatego nie posiada aż tak rozbudowanej dokumentacji i przykładów. Może to być pewną barierą na początku, jednak z czasem na pewno to się zmieni.
Po co nam C++ w RTOSie?
Największą zaletą RTOSa napisanego w C++ jest wykorzystanie RAII (Resource Acquisition is Initialization). Ktoś kiedyś powiedział, iż RAII jest idealnym podsumowaniem C++. Idealnie łączy w sobie ohydę nic nie mówiącej nazwy z niesamowicie przydatną funkcjonalnością. No ale do rzeczy – RAII polega na tym, iż operacje związane z zabieraniem i oddawaniem zasobu są umieszczone w konstruktorze i destruktorze. Dzięki temu mamy pewność, iż zasób zostanie oddany w każdej ścieżce wyjścia z funkcji. Najlepiej pokazać to na przykładzie:
void function_using_mutex() { os::Lock lock(getMutex()); ... //some processing if (...) { ... //some more processing return; } }Dzięki RAII mamy pewność, iż mutex zostanie oddany zarówno jeżeli program wyjdzie z funkcji na końcu, jak i w ifie. Dzięki temu nie musimy pamiętać o dodawaniu zwalniania mutexu w każdym możliwym miejscu.
Inne przydatne elementy C++ do wykorzystania w RTOSie to przeciążanie funkcji i templaty. Dzięki templatom możemy na przykład przekazać do funkcji rozmiar stosu dla wątku w czasie kompilacji. W Distortosie wygląda to następująco:
constexpr size_t STACK_SIZE = 1024; constexpr uint8_t PRIORITY = 1; auto thread = distortos::makeAndStartStaticThread(PRIORITY, taskFunction);Przy okazji widzimy tutaj użycie auto do typu zmiennej, które pozwala nam pisać bardziej odporny na zmiany kod i ukrywać szczegóły, które w danym kontekście nie są interesujące.
Konfiguracja i kompilacja
W opisie projektu na GitHubie mamy instrukcję z rozpisanymi krokami potrzebnymi do konfiguracji i kompilacji systemu. Musimy:
- Ściągnąć repo
- Wygenerować konfigurację dla naszego HW, albo użyć jednej z istniejących.
- Uruchomić cmake w celu wygenerowania plików do builda.
- Dopasować konfigurację używając cmake-gui.
- Uruchomić build przy użyciu Ninja.
Ja w kroku 2 potrzebowałem konfiguracji dla STM32F401RB. Wygenerowałem ją używając komendy:
python scripts/generateBoard.py source/chip/STM32/STM32F4/chipYaml/ST_STM32F401RB.yaml -o generated/chipWywołałem ją z głównego katalogu distortosa. Wywołanie z wyższego katalogu (np. z poziomu głównego katalogu projektu używającego distortos) powodowało błędy w skrypcie.
Następnie w kroku 3 podaję cmake’owi ścieżkę do wygenerowanego pliku toolchain:
cmake .. -DCMAKE_TOOLCHAIN_FILE={PATH_TO_DISTORTOS}/generated/chip/Toolchain-ST_STM32F401RB.cmake -GNinjaPróbowałem bawić się cmakiem, aby potrafił sam sobie wygenerować plik Toolchain i wykorzystać go dalej, ale na razie mi się nie udało. o ile nie podamy tego pliku na początku, CMake używa domyślnego toolchaina.
Następnie w cmake-gui możemy wybrać opcje distortosa, które nas interesują. Jest ich całkiem sporo, dlatego próba zarządzania nimi z poziomu plików .cmake jest dużo trudniejsza.
Dzięki tej konfiguracji możemy na przykład wyklikać, czy chcemy sam scheduler, czy również inicjalizację zegara albo drivery peryferiów.
Następnie w kroku 5 wywołujemy ninję i budujemy distortosa do biblioteki statycznej, a także unit testy i hw testy. Hw testy możemy od razu wgrać sobie na płytkę i zobaczyć czy wszystko działa. Jak chcemy jakieś inne przykładowe programy na Distortosie, możemy zobaczyć oddzielne repo z przykładami. Na razie jest tam kilka różnych sposobów świecenia LEDami.
Co zawiera distortos?
Distortos to nie tylko scheduler, to cały framework do STM32. Zawiera także między innymi:
- Skrypty linkera i pliki startupowe.
- Stuby dla syscalli z newliba.
- Drivery do peryferiów.
Automatyczne generowane skrypty linkera i pliki startupowe stanowią dużą wartość. ST w swoich przykładach z not aplikacyjnych zwykle daje tylko skrypty, które nadają się do C. Brakuje niektórych sekcji do C++, a szczególnie do inicjalizacji statycznych obiektów. W distortosie te sekcje są i działają. Co więcej mamy makra umożliwiające przesunięcie niektórych funkcji inicjalizacyjnych przed funkcję main. Dlatego na przykład inicjalizacja RCC i niektórych peryferiów może odbywać się tam. Nie wnikałem w to dokładnie, ale chyba funkcja main jest częścią wątku MainThread i jest wywoływana już po starcie schedulera.
Drivery do peryferiów są na razie w trakcie tworzenia. Istnieją już drivery dla GPIO, USART, DMA, SPI, ale dużo peryferiów nie jest wspieranych. I nie ma w tym nic dziwnego biorąc pod uwagę wszystkie rodzaje STM32 i ich peryferia. Na razie testowałem tylko driver GPIO i całkiem przyjemnie się go używa:
//PA0 - PWM pin for right motor //PA1 - PWM pin for left motor distortos::chip::configureAlternateFunctionPin(distortos::chip::Pin::pa0, distortos::chip::PinAlternateFunction::af1); distortos::chip::configureAlternateFunctionPin(distortos::chip::Pin::pa1, distortos::chip::PinAlternateFunction::af1);Jak ktoś potrzebuje pełnych bibliotek do peryferiów w C++, powinien się zainteresować projektem Modm.
Podsumowanie
Na razie to tyle o ile chodzi o moje początki z distortosem. Napisałem do tej pory tylko kilka prostych programów wykorzystujących StaticThready tworzone tak jak w przykładzie kodu wyżej. Żeby omówić bardziej zaawansowane funkcje muszę jeszcze trochę go poznać. Jednak już teraz mogę powiedzieć, iż projekt jest interesujący i stanowi fajną alternatywę np. dla FreeRTOSa. Przy okazji warto pamiętać, iż na razie distortos pozostało w wersji dosyć eksperymentalnej i pewnie będzie się zmieniać na lepsze.