TT#9 Bootloader wbudowany w STM32

Aktualizacja oprogramowanie wymaga użycia kodu, który będzie odbierał dane z nowym wsadem przez jakiś interfejs i umieszczał je we właściwym miejscu w pamięci. Do tego służy program ładujący, czyli bootloader. Częstą praktyką jest umieszczanie takiego kodu napisanego samodzielnie na początku pamięci flash. Jednak w STM32 producent dostarcza układy z już wbudowanym bootloaderem. W dzisiejszym materiale pokaże, jak go uruchomić i używać.

Materiał jest częścią Tips and Tricks – serii artykułów dotyczących ciekawostek o STM32

czyli przydatnych, choć rzadko opisywanych elementów z ekosystemu STM32.

W obecnych czasach rzadko kiedy raz przygotowane oprogramowanie pozostaje w takiej samej formie przez cały cykl życia produktu. Czasami aktualizacja podyktowana jest błędem niewykrytym w trakcie testów produkcyjnych, innym razem nowe wymagania po stronie klientów dyktują zmiany i konieczność wprowadzenia dodatkowych funkcjonalności.

Ale w jaki sposób dostarczyć do mikrokontrolera nową wersję programu? Przecież rzadko kiedy zdarza się, że użytkownik ma dostęp do programatora i samodzielnie umieści wsad w taki sposób, jak robimy to zazwyczaj na produkcji. Jeżeli mamy dostępną komunikację bezprzewodową, możemy soft zaktualizować zdalnie przez WiFi lub Bluetooth. W jeszcze innych przypadkach możemy podłączyć produkt do komputera i umieścić w pamięci nową wersję programu przez USB. Wszystko zależy od specyfiki urządzenia. Niemniej jednak za każdym razem potrzebujemy oprogramowania, które spowoduje, że wgranie nowego pliku do pamięci będzie możliwe. Do tego służy bootloader, czyli program ładujący.

Osoby zajmujące się programowaniem mikrokontrolerów, które zaczynały od mniejszych układów (np. popularnych AVR-ów) znają problematyką bootloadera bardzo dobrze. Dzięki niemu możliwe jest m.in. tak łatwe wgrywanie wsadów na Arduino. Bootloader to nieduży program umieszczany na początku pamięci mikrokontrolera, który w zależności od konfiguracji uruchamia docelowy program znajdujący się w pamięci tuż za nim lub pozostaje w trybie ładowania nowego programu w miejsce starego. To on odpowiada za właściwe zarządzanie adresami pamięci tak, aby docelowy program był umieszczany zawsze w dobrym miejscu.

Mikrokontrolery STM32 mają jednak taki bootloader umieszczony w pamięci układu już na etapie produkcji układu. Dzięki temu w wielu przypadkach nie musimy samodzielnie pisać go od nowa, a wystarczy prawidłowo skorzystać z tego fabrycznego.

Bootloader w STM32 umieszczany jest w pamięci systemowej, czyli System Memory. Jak uruchomić mikrokontroler w ten sposób, aby startował z wybranego obszaru, opisywałem w materiale TT#8 Konfiguracja rozruchu – BOOT w STM32. Obsługa bootloadera jest możliwa za pomocą różnych interfejsów w zależności od wybranego układu – może to być interfejs UART, I2C, SPI, CAN lub USB. Szczegóły dotyczące konkretnych rodzin i serii znajdziemy w dokumentacji „STM32 microcontroller system memory boot mode„.

Oprócz sposobu wykorzystującego prawidłową konfigurację pinów BOOT, użytkownik może uruchomić bootloader wykonując skok do pamięci systemowej z kodu użytkownika. Przed przejściem do bootloadera użytkownik powinien:

  • Wyłącz wszystkie zegary peryferyjne
  • Wyłącz używane PLL
  • Wyłącz przerwania
  • Wyczyść oczekujące przerwania

Po starcie z pamięci systemowej inicjalizowane są podstawowe układy peryferyjne mikrokontrolera niezbędne do działania bootloadera. Następnie w pętli sprawdzane jest, czy wystąpiła inicjalizacja bootloadera na jednym z dostępnym interfejsów. Przykładowo przez interfejs UART inicjalizujemy bootloader wysyłając komendę 0x7F, przy I2C konkretnym adresem urządzenia, zaś dla USB wykrycie przewodu. Przykładowy schemat blokowy przepływu programu dla STM32L47x przedstawia diagram poniżej.

Następnie, w zależności od używanego interfejsu, wywołujemy komendy kasowania pamięci, zapisania konkretnego obszaru czy obsługę innych instrukcji specjalnych, jak blokowanie pamięci przed odczytem. Szczegóły odnośnie każdego z interfejsów obsługiwanych przez bootloader znajdziemy w dokumentacjach:

Wyjście z trybu bootloadera następuje po załadowaniu programu i wywołaniu resetu mikrokontrolera. Oczywiście w momencie resetowania tryb BOOT powinien być już skonfigurowany tak, aby powodował start z pamięci głównej mikrokontrolera.

Praktyczny przykład – aktualizacja przez interfejs UART

Ale dość o teorii. Zobaczmy jak zaktualizować oprogramowanie przy pomocy wbudowanego bootloadera na przykładzie płytki Nucleo-L476RG z wykorzystaniem interfejsu UART.

Do tego celu będziemy potrzebowali płytki Nucleo, zewnętrznego konwertera USB-UART oraz aplikacji STM32CubeProgrammer. Zamiast zewnętrznego konwertera USB-UART można wykorzystać port VCOM udostępniony przez ST-Link, ale lepiej będzie widoczne przy zewnętrznym konwerterze, że dane nie „idą” przez programator. Co do aplikacji STM32CubeProgrammer – użyjemy softu od producenta, bo tak będzie najprościej. Można oczywiście napisać własną obsługę wysyłania danych do bootloadera korzystając z dokumentacji, do których link umieściłem kilka zdań powyżej.

Przechodząc do konkretów, uruchamiamy aplikację STM32CubeProgrammer. Przed połączeniem musimy wprowadzić mikrokontroler w tryb boot-owania z pamięci System Memory, gdzie znajduje się fabryczny bootloader.

W przypadku układu STM32L476RG musimy podciągnąć pin BOOT0 (pin 59) oraz ustawić bit nBOOT1 jako 1 (negacja BOOT1). Drugi element powinien być domyślnie ustawiony zgodnie z naszymi oczekiwaniami, ale warto sprawdzić np. za pomocą STM32CubeProgrammera.

Teraz podciągamy BOOT0 do 3,3 V i resetujemy mikrokontroler. Mamy uruchomiony tryb bootloadera. Zgodnie z tym co opisałem w pierwszej części artykułu, następnie musimy zainicjalizować komunikację przez UART. Ustawiamy konfigurację UART – numer portu konwertera, prędkość (nie wyższa niż 115200 bps) oraz parzystość Even. Po kliknięciu Connect bootloader powinien przejść w tryb pracy z UART.

Kolejno przechodzimy do zakładki Erase&Programming. Najpierw musimy wyczyścić pamięć układu. Następnie wybieramy plik „.bin”, który chcemy wgrać i klikamy „Start Programming”.

Jeżeli proces aktualizacji przez bootloader przebiegnie pomyślnie, otrzymamy komunikat „File download complete”. Teraz wystarczy podpiąć BOOT0 pod GND (albo w przypadku płytki Nucleo odłączyć podciągnięcie po 3,3 V, ponieważ na płytce domyślnie BOOT0 jest podciągnięcie do GND) i zresetować układ. Nowy program powinien wystartować.

To tyle w temacie wbudowanego w STM32 bootloadera. Warto o nim wiedzieć i z niego korzystać, ponieważ ma spore możliwości i zapewnia stabilne działanie. Sporo osób, nie będąc świadomym istnienia fabrycznego bootloadera, spędza sporo czasu na pisaniu własnego programu ładującego (choć w niektórych przypadkach może być to konieczne). Mam nadzieję, że teraz unikniesz niepotrzebnej dodatkowej pracy 🙂

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *