Jak korzystać z dokumentacji do STM32 – biblioteka HAL i LL
Osoby zaczynające naukę programowania STM32 bardzo często sięgają po biblioteki HAL-a. Ich popularność to nie przypadek – bogate wsparcie od producenta, duża ilość materiałów dostępnych w sieci, ale przede wszystkim łatwy start sprawiają, że nowi adepci bardzo chętnie używają tej biblioteki. I to nie tylko do szybkiego prototypowania. Narzędzie, jakim jest konfigurator STM32CubeMX pozwala w przystępny sposób wdrożyć się w dość rozbudowane układy. Na początku najłatwiej jest nam znaleźć jakieś poradniki albo kurs, gdzie poznamy podstawy stosowania HAL w przykładach. Co jednak, jeżeli na jakiś temat, który nas interesuje, nie możemy znaleźć odpowiednich materiałów? Albo chcielibyśmy zapoznać się ze wszystkimi funkcjonalnościami biblioteki dotyczącej konkretnego interfejsu, aby wiedzieć, jakie możliwości wyboru właściwie mamy? Z pomocą przychodzi nam dokumentacja producenta.
Co znajdziemy w dokumentacji do HAL i LL
We wstępie wspomniałem o bibliotece HAL-a, ale właściwie dokumentacja, o której będę pisał, odnosi się do dwóch typów bibliotek – HAL (Hardware Abstraction Layer) oraz LL (Low-Layer).
HAL Library to biblioteka wysokopoziomowa zapewniająca API (Application Programming Interfaces) do interakcji z układami peryferyjnymi mikrokontrolera. Zawiera interfejsy do inicjalizacji i konfiguracji, zarządzania transferami danych w trybie odpytywania, obsługi przerwań lub DMA i zarządzania błędami komunikacji.
Low-Layer to zestaw funkcji zorientowanych na sprzęt, jaki oferuje STM32. Sterowniki odzwierciedlają możliwości mikrokontrolera i zapewniają operacje atomowe na poziomie rejestrów. Biblioteka LL nie wymaga, jak to jest w przypadku HAL-a, dodatkowych zasobów w postaci pamięci do obsługi buforów, liczników czy wskaźników. Pozwala programować na rejestrach, dając do dyspozycji użytkownikowi zestaw nazw przyjaznych dla programisty.
Informacją istotną dla osób programujących mikrokontrolery zawodowo jest to, że obie biblioteki zostały napisane w standardzie ANSI-C, czyli są niezależne od zastosowanego kompilatora czy IDE. Poza tym kod HAL i LL jest zgodny ze standardem MISRA C®:2004.
Gdzie znajdziemy dokumentację do HAL i LL
Biblioteki HAL i LL zostały napisane dla każdej z serii mikrokontrolerów STM32 tzn., że pisząc na różne układy z danej serii korzystamy z tej samej biblioteki. Zatem dokumentacje też będzie wspólna dla całej serii. Dla przykładu, używana przeze mnie płytka Nucleo-L476RG wykorzystuje dokumentację do HAL i LL dla L4/L4+. Ze względu na to, że biblioteki te są powiązane ściśle z całym pakietem STM32Cube, właściwego PDF-a dla serii L4 należy szukać, wpisując frazę „STM32CubeL4”. Następnie przechodzimy do zakładki „Documentation” i w rozdziale „User Manuals” znajdziemy dokument „Description of STM32L4/L4+ HAL and low-layer drivers 8.0„.
Biblioteka HAL
Jak już wspomniałem, biblioteka HAL-a dostarcza wysokopoziomowy interfejs programowania. Cechą charakterystyczną biblioteki jest to, że jest portowalna między różnymi mikrokontrolerami z rodziny STM32, co oznacza, że znając bibliotekę HAL-a jesteśmy w stanie programować wszystkie mikrokontrolery STM32 przy użyciu tego samego nazewnictwa. Tak właściwie, to HAL Library jest podzielona na dwie części:
- Generic Driver – są to podstawowe funkcje wspólne dla wszystkich układów z rodziny STM32 i dostępne na każdym mikrokontrolerze. W ich skład wchodzą bazowe operacje takie, jak komunikacja UART, I2C, SPI, timery, ADC czy GPIO.
- Extension Driver – funkcje rozszerzające specyficzne dla danej serii lub układu. Funkcje te będą niedostępne dla niektórych mikrokontrolerów i stanowią rozbudowę podstawowych możliwości biblioteki HAL-a. Przykładem może być generowanie sygnałów komplementarnych PWM, ADC w trybie wstrzykiwanym czy używanie wbudowanego bufora FIFO w komunikacji UART.
Używając bibliotek HAL-a mamy również zapewnioną kompatybilność z systemem RTOS. Dodatkowo biblioteka udostępnia trzy tryby komunikacji dla każdego z interfejsów: polling (czyli komunikacja w trybie blokującym), przerwania i DMA. Bez problemu możemy korzystać z kilku instancji interfejsu, czyli np. jednocześnie używać USART1 i USART2. HAL udostępnia także obsługę błędów transmisji.
W dokumentacji w rozdziale 3. pt. „Overview od HAL drivers” znajdziemy dokładny opis konstrukcji biblioteki, w tym nazewnictwo plików, zależności między nimi, nazewnictwo funkcji (prefiks HAL) oraz spis plików biblioteki z zaznaczeniem, dla których układów są one dostępne. Producent opisuje tutaj zasadę działania mechanizmu przerwań i callback-ów, makra biblioteki (z prefiksem __HAL) oraz sposób obsługi błędów. Warto zapoznać się z tymi informacjami. Stanowią one dobry przewodnik po bibliotece dla zaczynających naukę, ale także chcących poznać jej pełne możliwości. W dokumencie znajdziemy również liczne przykłady kodów pokazujących, jak powinna wyglądać inicjalizacja interfejsów, czy obsługa błędów.
W kolejnych rozdziałach (dla PDF-a dla serii L4 od rozdziału 6. do 79.) znajdziemy dokładny opis funkcjonalności dla poszczególnych układów peryferyjnych. Opisane tam zostały elementy (pola) struktur stosowanych do obsługi danego peryferia, możliwości, opis procesu inicjalizacji i deinicjalizacji oraz konfiguracji przy użyciu biblioteki HAL. Znajdziemy także opisy wszystkich dostępnych funkcji. Jak zaznaczyłem wcześniej, podzielone zostały ona na dwa działy: Generic Driver i Extension Driver.
Biblioteka LL
Drugim elementem bibliotek dostępnych dla programistów STM32 jest warstwa Low-Layer. Jest zestawem funkcji skupiających się na dostarczeniu programiście przyjaznego interfejsu z bezpośrednim dostępem do rejestrów. Są przeznaczone raczej dla osób bardziej doświadczonych, dłużej zajmujących się mikrokontrolerami, ponieważ wymagają sporej wiedzy o działaniu mikrokontrolera. W przeciwieństwie do warstwy HAL, interfejsy LL nie są dostarczane dla urządzeń peryferyjnych, w których zoptymalizowany dostęp nie jest kluczową funkcją. Nie są dostępne także dla interfejsów wymagających ciężkiej konfiguracji oprogramowania lub złożonego stosu wyższego poziomu (takich jak USB czy Ethernet).
W przypadku bibliotek LL, wszystkie funkcje zaczynają się od prefiksu LL, a w nazwach plików znajdziemy frazę „_ll_” np. stm32l4xx_ll_system.h. W rozdziale „Overview of low-layer drivers” znajdziemy dokładny opis nazewnictwa analogiczny do warstwy HAL. Ze względu na to, że warstwa LL stanowi niejako most pomiędzy rejestrami a programistą, nie mamy tutaj tak rozbudowanych funkcjonalności jak callbacki, obsługa błędów czy inicjalizacja. Programista musi zadbać o to sam.
W rozdziałach od 80. do 107. znajdziemy opis LL dla poszczególnych układów peryferyjnych. Przy korzystaniu z LL warto również zapoznać się z tabelą w rozdziale 108. która przedstawia porównanie nazw rejestrów z odpowiadającymi im nazwami warstwy Low-Layer. Choć nazewnictwo LL jest intuicyjne, czasami znalezienie funkcji odpowiedzialnej za ustawienie danego rejestru może być problematyczne. Wtedy rozdział „Correspondence between API registers and API low-layer driver functions” może okazać się szybkim rozwiązaniem.
Łączenie HAL i LL
Warstwa HAL może współpracować z warstwą LL, jednak należy uważać, aby nie stosować ich dla tej samej instancji. Można np. z powodzeniem używać HAL dla USART1, a LL dla USART2, jednak mieszanie HAL i LL dla jednej instancji nie jest zalecane. Stosowanie LL może bowiem spowodować nadpisanie danych w warstwie HAL i niewłaściwe jej działanie.
Przy używaniu jednocześnie HAL i LL dla danej instancji należy zwrócić szczególną uwagę, aby warstwa LL nie nadpisywała pól inicjalizacji biblioteki HAL. Poza tym bez problemu można stosować mieszanie funkcji HAL i LL, jeżeli operujemy na elementach nie korzystających z struktur np. GPIO, RCC czy Flash.
PDF to nie wszystko
Chociaż w dokumentacji znajdziemy liczne przykłady oraz trochę kodu, na początku nauki poszukujemy gotowych programów, które moglibyśmy wrzucić na płytkę i przeanalizować ich działanie. Producent mikrokontrolerów STM32 i w tym obszarze nie pozostawia nas bez pomocy. Pakiet STM32Cube, którego szukaliśmy, aby pobrać dokumentację do HAL i LL, zawiera liczne przykłady dostępne na różne płytki deweloperskie producenta. Możemy je pobrać bezpośrednio ze strony producenta w zakładce „Get Software”.
Warto korzystać z najnowszej wersji, chociaż czasami możemy chcieć pobrać starszą wersję ze względu na kompatybilność z jakąś inną biblioteką np. X-CUBE-AI czy TouchGFX.
Jeżeli korzystamy z STM32CubeIDE i uruchamialiśmy już jakiś projekt z danej serii, paczkę tą możemy znaleźć w repozytorium CubeMX (domyślnie C:\Users\_nazwa_użytkownika_\STM32Cube\Repository”).
W paczce znajdziemy dokumentację opisującą strukturę plików HAL i LL, pliki biblioteki, bibliotek dodatkowych, jak USB, FreeRTOS czy FatFs oraz liczne przykłady. W folderze „Projects” mamy dostępne projekty dla większości płytek ewaluacyjnych STMicroelectronics – zarówno Discovery, jak i Nucleo czy EVAL.
Wśród przykładów znajdziemy:
- Applications – przykładowe aplikacje np. z użyciem FreeRTOS-a
- Demonstrations – projekty demonstracyjne z użyciem konkretnego zestawu płytek rozszerzeń, np. wyświetlaczem
- Examples – przykłady z wykorzystaniem biblioteki HAL
- Examples_LL – przykłady z wykorzystaniem biblioteki LL
- Examples_MIX – przykłady z wykorzystaniem biblioteki HAL i LL, czyli kiedy i jak możemy mieszać obie biblioteki
- Templates – szablon dla projektu z HAL pod różne środowiska programistyczne
- Templates_LL – szablon dla projektu z LL pod różne środowiska programistyczne
Podsumowanie
W artykule przedstawiłem dokumentację do bibliotek HAL i LL, do której, mam wrażenie, programiście sięgają dość rzadko. Chociaż plik PDF jest długi, rozdziały są podzielone w sposób bardzo przejrzysty, co sprawia, że znalezienie szukanej przez nas funkcji czy opisu do układu peryferyjnego jest bardzo łatwe. Dodatkowo do bibliotek mamy dedykowane przykłady. Czasami są one dość stare i nieaktualizowane, ale pomimo to dają pogląd, jak należy korzystać z danej funkcji czy np. systemu callback-ów. Mam nadzieję, że materiał pozwoli osobom zaczynającym naukę lepiej zapoznać się z zasadą działania bibliotek i różnicami między nimi.