piątek, 18 stycznia 2019

ATSAML21 - ustawiamy zegar na inną niż startowa wartość.

Po starcie, ATSAML21 ustawiony jest na pracę z wewnętrznym zegarem o wartości 4 MHz. Ponieważ jesteśmy pod wpływem filmu Szybcy i Wściekli, dlatego ta szybkość nas nie satysfakcjonuje. Spróbujemy zatem ustawić zegar wewnętrzny na szybszy wariant, ale nie na najszybszy, co by nam nie zdmuchneło czapek z głów. Zaproponuję zmianę ustawień zegara na jedną z dostępnych wartości 16 MHz. Maksymalna możliwa szybkość do uzyskania z zegara wewnętrznego to 48 MHz. Zegar będziemy ustawiać za pomocą wpisów do rejestrów bez używania bibliotek ASF i środowiska ATMEL STUDIO. Na poczętek przyjrzyjmy się jak w aplikacji webowej ATMEL START przedstawiony jest schemat blokowy zegara (patrz obrazek w nagłówku)

W sumie nie wygląda groźnie ale i tak nie wiemy dokładnie o co chodzi. Ale to nic jak to się mówi nie święci garnki lepili. Jedynym dokumentem , którym bedę się podpierał to datasheet. Przypomnę , że w ATSAM-ach nie ma podziału na datasheet i manual, tu jest jeden wspólny dokument i powiem , że jest to bardzo wygodne rozwiązanie. Zerknijmy na bardziej szczegółowy obrazek blokowy zegara :


Wyróżnić możemy na nim trzy główne bloki funkcjonalne :

  • blok źródła : OSCCTRL i OSCK32CTRL
  • blok zegara asynchronicznego: GCLK (Generic Clock Controller)
  • blok zegara synchronicznego: MCKL (Main Clock)

Blok źródła nie wymaga komentarza. Najbardziej rozbudowany blok GCLK służy do generowania zegara asynchronicznego dla peryferiów, które takiego zegara potrzebują do działania. Blok ten składa się z generatorów i kanałów. Generatorów mamy maksymalnie 8 szt a kanałów 35 szt. Do jednego generatora można podpinać dowolną ilość kanałów a każdy kanał sparowany jest z jednym konkretnym peryferium. Takie kombinacje alpejskie powodują , że moduł zegara jest bardzo elastyczny i można go w szerokim zakresie konfigurować, ograniczeni jesteśmy praktycznie tylko wyobraźnią :). Wyobraźmy sobie np , że sterujemy wieloma peryferiami a każde  z nich inną częstotliwością zegara.
Tu warto wspomnieć , że w ATMEL START kanały są niewidoczne dla użytkownika na schemacie blokowym i taki biedny żuczek używający tego narzędzia nawet nie będzie wiedział o ich istnieniu.
Poniżej obrazek ilustrujący w uproszczeniu idee dystrybucji zegara przy pomocy generatorów i kanałów :

Blok MCKL jest źródłem zegara synchronicznego niezbędnego do życia core MCU i peryferiów podpiętych do szyny ABP i AHB, które takiego zegara potrzebują. Blok ten napędzany jest przez Generator nr 0 z bloku GCKL.

Poniżej obrazek jak wygląda przykładowa dystrybucja zegara dla peryferium SERCOM0 :



W zasadzie taka mała garść informacji na początek nam wystarczy o budowie zegara.
Teraz skupimy się na możliwości zmiany parametrów pracy zegara wewnętrznego. Moduł z tym zegarem opisany jest jako OSC16M i znajduje się w bloku OSCCTRL. Zawsze dochodzę do istoty rzeczy od szerszej perspektywy do węższej od ogółu do szczegółu. Takie podejście jeszcze nigdy mnie nie zawiodło. Pisząc artykuł z reguły sam się uczę zagadnienia z którym  wcześniej nie miałem do czynienia.
Skoro znamy nazwę modułu zegara wewnętrznego (OSC16M) i wiemy w jakim bloku się znajduje (OSCCTRL) to już mamy z górki. Poszukamy adekwatnego rejestru do zmiany parametrów pracy tego bloku i spróbujemy to zaaplikować w programie.
W datasheet odszukujemy rozdział o bloku OSCCTRL i wchodzimy od razu w tabelkę ze spisem rejestrów :


W tabelce widzimy rejestr o nazwie OSC16MCTRL, nazwa jest na tyle czytelna, że od razu domyślamy się do czego to służy. Zaglądamy zatem do środka rejestru


Struktura wewnętrzna nie wygląda na skomplikowaną. Przeglądamy opisy dla poszczególnych pól . Szukamy czegoś czym można pospieszyć zegar wewnętrzny.
Znajdujemy pole opisane FSEL (Oscillator Frequency Selection) i bingo.


Tabelka nie wymaga komentarza, aby ustawić zegar wewnętrzny na 16 MHz musimy do pola bitowego FSEL wpisać wartość 0x11. Ale ,hola hola jak wpisać 0x11 do pola dwubitowego ??!!! widzimy ,że to raczej niemożliwe. Ewidentny babol , prawdłowy zapis jakby co to 0b11. Powiem szczerze takie błędy nie robią już na mnie wrażenia bo wiem , że są one chlebem powszednim u wszystkich producentów.  Zastanówmy się czy to wszystko ?? Tak na chłopski rozum po ustawieniu zegara musi mieć on jakiś czas na ustabilizowanie swojej pracy , nazwijmy to rozruchem roboczym. Musi zatem być jakaś sygnalizacja czy zegar wyszedł z fazy rozruchu roboczego i pracuje stabilnie. Wracamy do tabelki ze spisem rejestrów (patrz wyżej) i szukamy czegoś stosownego. Widzimy tam rejestr STATUS, zaglądamy do środka.


W strukturze rejestru STATUS od razu rzuca nam się w oczy pole OSC16MRDY

Trzeba przyznać , że nazwy rejestrów w ATSAM-ach są bardzo czytelne i można szybko zorientować się o ich przeznaczeniu. Wchodzimy do opisu pola :
 

Opis jak widzimy jest bardzo czytelny i jednoznacznie pokazuje, że jeśli w polu tym odczytamy wartość 1 to będzie to oznaczać , że zegar pracuje stabilnie. Musimy zatem po ustawieniu prędkości pracy w rejestrze OSC16MCTRL, sprawdzić tę flagę i poczekać aż będzie ustawiona.
Mamy zatem zakończoną fazę poszukiwawczą, wiemy gdzie i co ustawić. Teraz druga faza czyli rozpoznanie jak zmienić ustawienia i pola bitowe w programie za pomocą plików nagłówkowych producenta.

Trzeba tu wspomnieć, że takie grzebanie po rejestrach zajmuje trochę czasu szczególnie kiedy nasz MCU jest rozbudowany to fakt. Ale wraz z doświadczeniem w takim grzebaniu, proces ten jest coraz efektywniejszy i szybszy. Sam widzę po sobie , że taka zabawa rozwija znakomicie mózg. Stąd mogę postawić tezę , że programowanie MCU za pomocą rejestrów może chronić nas przed Alzheimer-em a programowanie bibliotekami typu HAL nie da nam żadnego efektu w tym względzie.

Odpalamy środowisko SEGGER-a. Posłużymy sie moim projektem HELLO WORLD dostępnymy na GitHubie.
Czyli zakładam , że mamy już odpalony projekt z migająca diodą LED na porcie PB11 i z wykorzystaniem SysTic-a czyli takiego prostego timerka zaimplementowanego w każdym rdzeniu ARM. Zaglądamy sobie w pliki nagłówkowe w katalogu Dependencies. Pliki jak widzimy są przyporządkowane nazwami do poszczególnych peryferiów naszego MCU. Znajdujemy tam plik o nazwie oscctrl.h, znajdują się tutaj opisy , definicje umożliwjące dostęp bezpośredni do rejestrów w tym module. W sumie możemy nawet nie zaglądać do tych opisów aby zorientować się jak możemy zapisać dane do pól rejestrów. Znamy już ogólną konwencję zapisu , która zawsze zaczyna się od nazwy modułu w którym jest nasz rejestr, a dalsze nazwy i postać zapisu podpowie nam środowisko SEGGERA

Wpisujemy zatem z dużej litery OSCCTRL i po wpisaniu znaczka -> (operator wskaźnika na element struktury) środowisko poda nam propozycję dalszego zapisu a w tych propozycjach szukamy nazwy naszego rejestru OSC16MCTRL. Następnie wpisujemy kropeczkę po której znowu propozycja co dalej wpisać a będzie to reg lub bit wybieramy bit.

Łączny zapis jak dotychczas wykontycypowaliśmy wygląda tak :

OSCCTRL->OSC16MCTRL.bit  

Słowo bit oznacza, że odwołujemy się nie do całego rejestru ale do wybranego bitu lub pól bitowych . Konsekwencją tego zapisu jest to , że wstawiając kropkę po  bit otrzymamy podpowiedź z nazwami pól bitowych naszego rejestru , zobaczymy tam równiez nasze pole FSEL. Zatem zapis nasz ostatecznie rozwija się do poniższej postaci :

OSCCTRL->OSC16MCTRL.bit.FSEL = 0b11; // set the OSC16M value to 16MHz

W pliku nagłówkowym oscctrl.h znajdziemy definicje, które zastąpią nam wartość 0b11 słowem OSCCTRL_OSC16MCTRL_FSEL_16_Val , zwiększa nam to czytelność kodu. Rozwińmy zatem zapis do postaci ostatecznej :

OSCCTRL->OSC16MCTRL.bit.FSEL = OSCCTRL_OSC16MCTRL_FSEL_16_Val ; // set the OSC16M value to 16MHz

Jak widzimy wygląda to trochę jak dialekt kantoński ale Chińczycy nie mają z tym problemu i się porozumiewają w takim języku. Skoro oni mogą to czemu my nie. W wielu przypadkach jest tak, że taki zapis "kantoński" jest najkrótszym możliwym zapisem operacji na rejestrach w programie a napewno najbardziej optymalnym z punktu widzenia działania MCU. W przypadku bibliotek typu HAL zapisy puchną a ich abstrakcja rośnie logarytmicznie. I może przenośność kodu jest większa ale programiści robią sie przez to "głupsi" bo bliżej im do "małp" klepiących wyuczone regułki :) A jak nazwa regułki się zmieni to "małpa" już jest zagubiona. Efekt "małpy" nie występuje natomiast w programowaniu stylem "kantońskim" :).

Powyżej pokazałem mniej więcej drogę jaką się poruszam w celu rozwiązywania zagadnień związanych z programowaniem na rejestrach. Mnie osobiście programowania na rejestrach daje ogromną satysfakcję.

No dobrze zostało nam jeszcze zagadnienie związane z odczytem statusu naszego zegara czyli odczytanie bitu OSC16MRDY w rejestrze STATUS i poczekaniem aż ten bit będzie ustawiony na 1. Zrobimy to takim zapisem :

while(!OSCCTRL->STATUS.bit.OSC16MRDY);

Pętla zostanie opuszczona jak bit OSC16MRDY będzie miał wartość 1. Proste jak świński ogon.

Trzeba przyznać, że podpowiedzi środowiska SEGGER'a ogromnie przyspieszają pracę z rejestrami.

Teraz pytanie jak sprawdzić czy nasz zapis do pola FSEL rejestru OSC16MCTRL był skuteczny, ja proponuję podejrzeć to uruchamiając program w trybie debugging. Mamy tam komfortowy i szybki podgląd wszystkich rejestrów.

Powyższe zapisy i ustawienie rejestru są skuteczne i działają co sprawdziłem. 

Projekt z GitHuba ściągamy lokalnie na dysk za pomocą komendy (Linux w konsoli):

git clone https://github.com/PICmajster/ATSAML21_HELLO_WORLD.git

i dodajemy w środowisku SEGGER za pomocą opcji File --> Open Solution
A tak na marginesie odkryłem niedawno, że w środowisku SEGGER-a jest zamplementowana obsługa GitHuba trzeba to tylko skonfigurować w opcjach.

Pozdrawiam
picmajster.blog@gmail.com



Linki :
Projekt HELLO_WORLD - na GitHub dla środowiska SEGGER
IDE SEGGER 
ATSAML21G18B - strona producenta  
Artykuł o ATSAML21

Brak komentarzy:

Prześlij komentarz