piątek, 28 stycznia 2022

STM32G0 - Bare Metal - SPI

Praca organiczna na rejestrach daje najwięcej satysfakcji. Nie ma innej drogi aby dobrze poznać nasz MCU . Skupiam się w swoim poznawaniu STM-ów na jednej rodzinie STM32G0 , opartej na podrasowanym rdzeniu Cortex M0+. Z tego punktu widzenia jest to wyjątkowy MCU. Ponieważ za pomocą SPI będę rozmawiał z MAX7219 zastosowanym w moim zegarku, dlatego postanowiłem pokazać jak uruchomić moduł SPI w STM32G0, korzystając wyłącznie z rejestrów (bez HAL). Są po drodze małe kruczki z którymi osoby początkujące w programowaniu na rejestrach w STM32, mogą sobie nie poradzić. Dlatego uważam, że ten wpis jest arcy-potrzebny.

STM32G0 posiada dwa moduły SPI . Maksymalna prędkość to 32 Mbit/s . Moduł SPI1 doposażony jest w interfejs I2S co w połączeniu z DAC może posłużyć nam jako centrum rozrywki muzycznej.

Moja baza narzędziowa to :

  • płytka mojej konstrukcji dla STM32G071KBT6,
  • programator J-LINK EDU Mini firmy Segger,
  • środowisko SEGGER IDE,
  • analizator stanów logicznych.

Konfigurację modułu SPI zaczynam od włączenia dla SPI1 zegara bo na tym numerku SPI będę działał.


Kolejnym krokiem jest konfiguracja pinów dla modułu SPI1 . Wybrane piny to PA1 (SCK) i PA2 (MOSI). Z MISO nie korzystam, wysyłam tylko dane . Dodatkowo konfiguruję pin PA8 dla diody LED na mojej płytce . Minimalizuję zapisy do rejestrów uwzględniając stany domyślne w rejestrach.



Poniżej pokazuję funkcję konfiguracyjną modułu SPI1 w trybie Master. I nad tą funkcją trochę podywagujemy.



Prędkość SPI1 ustawiłem do testów na 2 MHz tak aby mój wolny analizator stanów logicznych poprawnie obrazował przebiegi. Jak nie zapomnę to docelowo przestawię to na 8 MHz.

Kluczowym zagadnieniem w poprawnej konfiguracji modułu SPI w STM32G0 jest właściwe ustawienie modułu NSS, który pełni m.in funkcję takiego typowego CS-select. Są dwa tryby pracy tego modułu : sprzętowy i programowy. Sprzętowy oznacza, że musimy skonfigurować zewnętrzny pin lub wiele pinów i odpowiednio ustawiać na nich stany a programowy jest wewnętrznie ustawiany w/g potrzeb przy pomocy rejestrów SSI i SSOE.


Standardowo moduł NSS ustawiamy w trybie programowym. Aby w takim przypadku prawidłowo zainicjalizować moduł SPI musimy koniecznie dokonać poniższej sekwencji zapisów do rejestrów (przy domyślnym ustawieniu SSOE):

Kolejne zagadnienie to rejestr SPI1->CR2 i pole DS w którym ustawiamy ile bitów ma być transmitowanych. Możemy ustawić wartość od 4 bitów do 16 bitów. Ciekawostką przyrodniczą jest tutaj to, że ustawienia te nie działają tak jakbyśmy oczekiwali albo tak jak powinny działać zgodnie z opisem. Po prostu to nie działa. Dane do wysłania wpisywane są do 16-bitowego rejestru / bufora SPI1->DR. Jakbyśmy nie ustawili w DS ilości transmitowanych bitów to i tak zostanie wysłane 16-bitów w magistralę.  Firma STM ma tendencję do ograniczania wolności i wymuszania na programiście jedynie słusznych idei a taką jest tutaj transfer 16-bitowy. Aby wysłać np. 8-bitów trzeba się napocić i wprost tego nie zrobimy ale do tego dojdziemy później.

Poniżej funkcja wysyłająca 8-bitów po magistrali SPI1 :


Zaznaczyłem powyżej krytyczny fragment funkcji wysyłającej , w którym zapisujemy do rejestru/bufora  DR daną 8-bitową. Ponieważ jak pisałem wcześniej ,rejestr jest 16-bitowy , więc przypisanie do niego danej 8-bitowej domyślnie zostanie zrzutowane do 16-bitów, da nam to efekt 16-pulsów zegara i wysłanie 16-bitów w magistralę SPI. Aby poprawnie wysłać 8-bitów ,musimy dokonać operacji zapisu do młodszej połowy rejestru a starszą połowę 8-bitową nie tykać jakimkolwiek zapisem. Tutaj z pomocą przychodzą nam wskaźniki. Zapis jaki widzimy przed SPI1->DR. powoduje, że za pomocą wskaźnika typu uint8_t wskazujemy na zrzutowany do uint8_t  SPI->DR. Można też próbować rozwiązać ten problem za pomocą unii i rozbicia DR na dwa fragmenty 8-bitowe. Jeśli chcemy odebrać daną 8-bitową z rejestru DR to musimy identyczny zapis zastosować .

Poniżej dla uzupełnienia jeszcze funkcja wysyłająca dane z bufora, czyli jeśli chcemy wysłać ciąg danych 8-bitowych.



Na samym końcu pokażę jak używamy funkcji wysyłającej, ale przed tym jeszcze zobaczmy jak sobie upraszczać kod np. do sterowania diodą LED :



Na koniec plik main.c i test funkcji wysyłającej. Co 0.5 s wysyłana jest liczba 0x77 :



Efekt jaki powinniśmy zobaczyć na analizatorze :


A poniżej to samo ale bez rozwiązania problemy z zapisem 8-bitów do rejestru DR :



Jak widzimy programując na rejestrach peryferia, nie zawsze może nam pójść wszystko gładko, szczególnie kiedy dokumentacja nie ułatwia nam tego. Rozwiązując jednak takie problemy bardzo efektywnie uczymy się i rozwijamy nasz mózg , oddalając się od perspektywy Alzhaimera. Korzystając z biblioteki HAL nawet nie mielibyśmy pojęcia o problemie związanym z zapisem 8-bitowej danej do rejestru DR i byśmy żyli dalej w błogiej niewiedzy a Alzhaimer dopadnie nas wtedy szybko :).

STM32G0 jest fajnym MCU , coraz bardziej się do niego przekonuję. Podrasowanie rdzenia na 64MHz  jest fajnym pomysłem .Cenię w nim to, że można m.in dla tej serii korzystać bezpłatnie z IDE Keila do 256 kB kodu. 



Pozdrawiam

PICmajster
picmajster.blog@gmail.com




3 komentarze:

  1. sprawdziłem na G070 i rzeczywiście bez zmuszenia rzutowaniem do 8bit i tak zawsze wypuszczał ten G(ad)070 16 taktów zegara. Podziękowania za uratowanie od giga flustracji

    OdpowiedzUsuń
    Odpowiedzi
    1. O ! fajnie Witek , że sobie szperasz po serii G0 od STM-a . STM nie rozwija już wielu serii np. F4, F3, F0, F1. A G0 i G4 jest i będzie w przyszłości rozwijana, warto zatem tej serii poświecić uwagę.

      Usuń
  2. Pojawiły się na naszym rynku nowe MCU w rodzinie G0, STM32G0Bx z 256/512 Flash i 144 RAM. Można na nich stawiać naprawdę duże aplikacje z wykorzystaniem RTOS-a. Jeszcze raz podkreślę, warto moim zdaniem inwestować swój czas w tę rodzinę MCU, tym bardziej , że jest to relatywnie tania rodzina, więc dla hobbysty jest to idealne połączenie.

    OdpowiedzUsuń