Napisano dnia 7.02.2021 r. o godzinie 8:00
Autor: Piotr Sperka
Witaj w trzeciej, i póki co ostatniej, części serii artykułów o bootloaderze dla STM32. W poprzednich dwóch częściach udało nam się stworzyć kompletny szkielet bootloadera oraz aplikacji użytkownika. Pomimo, że w przykładzie użyłem płytki z mikrokontrolerem STM32F207, to po nieznacznych modyfikacjach cały szkielet nada się również dla innych rodzin STM32. W ramach demonstracji stworzymy dzisiaj bardzo prosty bootloader wykorzystujący UART do zaprogramowania i pobrania kopii zapasowej programu. Oczywiście – można kwestionować sens tego programu, gdyż mikrokontrolery STM32 są wyposażone we wbudowany bootloader działający z UARTem i uruchamiany odpowiednią konfiguracją pinów BOOT. Uznałem jednak, że UART będzie najlepszy do celów czysto dydaktycznych i demonstracyjnych ze względu na swoją prostotę. Nadmieniam już na początku, że kod ten napisałem tylko po to, żeby do końca pokazać ideę działania bootloadera i nie jest on odporny na celowe próby zepsucia go.
Jak już wspomniałem we wstępie, do komunikacji z użytkownikiem wykorzystamy UART. Oczywiście, w wersji produkcyjnej warto byłoby dodać jakieś sprawdzanie błędów transmisji, jak choćby sumę kontrolną. Nie chcemy przecież pozwolić wgrać niepoprawnie przesłanych danych. Wykorzystamy transmisję z prędkością 115200 baud/s. Bootloader będzie obsługiwał komendy:
Każda komenda kończy się znakiem „\n”, czyli Line Feed. Co istotne, w programie nie ma echa, czyli w przypadku ręcznego wprowadzania komend z terminala (na przykład PuTTY), nie widzimy wprowadzanych znaków. Myślę, że jedyną komendą wymagającą szerszego omówienia jest komenda zapisu danych, czyli write. Zapis programu odbywa się w „paczkach” mających maksymalnie 1024 bajty. Dodatkowo, ich rozmiar powinien być podzielny bez reszty przez 4. Wysłanie paczki o zerowym rozmiarze zakańcza procedurę. Warto zauważyć, że warunek podzielności przez 4 jest zapewniony niejako automatycznie – STM32 jest mikrokontrolerem 32-bitowym, a więc każde słowo programu ma 4 bajty (lub wielokrotność). Na poniższym rysunku możesz zobaczyć, jak wygląda procedura zapisu programu.
Nie będę tutaj przedstawiał kodu obsługującego komunikację (możesz oczywiście zajrzeć do kodu źródłowego – wszystko dzieje się w funkcji main()). Nie jest on skomplikowany, ale za to dosyć rozwlekły. W skrócie – jeżeli przy starcie bootloadera pin B13 jest zwarty do masy, program wchodzi w nieskończoną pętlę nasłuchując komend. Wyjście z tej pętli możliwe jest jedynie poprzez reset – czy to przyciskiem, czy to komendą reset.
Do wgrywania lub pobierania pliku binarnego napisałem prosty skrypt w Pythonie 3, który również jest dostępny na repozytorium. Jego zadaniem jest odpowiednie parsowanie oraz wysyłanie i odbieranie danych. Zasadniczo można go uruchomić w trybie pobierania programu z mikrokontrolera, lub wgrywania nowego programu. Na początku musimy pamiętać o zresetowaniu mikrokontrolera w trybie bootloadera (zwierając PB13 do masy), a następnie możemy wywołać:
python main.py --port=COM3 --baud=115200 --ofile=out.bin
, aby zczytać program użytkownika do pliku out.bin, korzystając z portu COM3,
python main.py --port=COM3 --baud=115200 --ifile=blink.bin
, aby wgrać nowy program użytkownika umieszczony w pliku blink.bin.
W ten sposób wspólnie dotarliśmy do końca tej krótkiej serii poświęconej prostemu bootloaderowi dla STM32. Mam nadzieję, że w ten sposób udało mi się nieco przybliżyć Ci zasadę działania i budowania bootloaderów. Przedstawiony przykład ma tak naprawdę jedynie pokazać, że nasz szkielet bootloadera działa i zachęcić Cię do dalszych samodzielnych eksperymentów. Do rzeczywistych zastosowań moim zdaniem warto wykorzystać szybszy interfejs komunikacyjny, na przykład SPI, a przede wszystkim zaimplementować mechanizm sprawdzania poprawności transmisji. Jednak swoją prawdziwą siłę bootloader pokaże, gdy wyposażymy go w obsługę bardziej przyjaznych użytkownikowi końcowemu interfejsów. Mogą to być na przykład karta SD, USB, czy też Ethernet. Cały kod dostępny jest na moim GitHubie, i jak zawsze w przypadku jakichkolwiek uwag czy wątpliwości – zapraszam do kontaktu.
Do następnego razu!