niedziela, 21 października 2018

EFM32TG11B120F128GQ48 ARM Cortex-M0+ od Silicon Labs - SPI

W artykule przyjrzymy się  peryferium SPI w naszym procku. Przyda się nam ta wiedza m.in do uruchomienia wyświetlacza LCD opartego na ILI9163 i znajdującego się na mojej płytce developerskiej dla Silicona. Peryferium SPI ukryte jest w module USART (Universal Synchronous Asynchronous Receiver/Transmitter). Do dyspozycji mamy cztery moduły USART zatem i cztery SPI. W modułach tych znajdziemy również UART .



Dla przypomnienia jak wygląda 4-liniowe połączenie SPI :

Nas będzie na razie interesować wariant dwupinowy czyli CLK i MOSI ponieważ tyle  nam wystarczy do sterowania wyświetlaczem LCD.
Tera przyjrzyjmy się dla formalności jak wygląda struktura modułu USART :


Z rzeczy ciekawych to należy zauważyć , że bufory FIFO TX i RX są dwupoziomowe i 9 bitowe . Mamy możliwość scalania wirtualnego dwóch poziomów FIFO i obsługi ramek np 16 bitowych.

Żeby nie przynudzać za bardzo skupimy się na poszukiwaniu jak się dobrać do ustawień SPI i co musimy w minimalnym wymiarze ustawić. Od strony zegarowej wiemy, że na pewno będziemy musieli ustawić zegar dla GPIO i modułu USART. Schemat blokowy USART sugeruje, że będzie trzeba ustawić również bloczek o nazwie Baud Generator .Potem pewnie będzie trzeba skonfigurować piny w odpowiednie tryby i w jakiś cudowny sposób przyporządkować je do SPI. Dla linii MOSI wybieram pin PA0 a dla linii CLK wybieram pin PA2. Oba piny przyporządkowane są do modułu USART3 a skąd to wiem ano z tabelki z opisem funkcji pinów , którą znajdziemy w datasheet na stronie 99.
Kusi aby wszystkie powyższe czynności dokonać za pomocą biblioteki emlib. Ale ja idę na kompromis , część dotyczącą ustawień zegara zrobię za pomocą funkcji z biblioteki emlib, resztę robimy na rejestrach co by nie stracić z nimi kontaktu.

Na stronie 635 RM znajdziemy tabelkę ze spisem wszystkich rejestrów powiązanych z modułem USART . Zapoznanie się z zawartością rejestrów będzie pierwszym moim krokiem. Już pierwszy rejestr z brzegu USARTn_CTRL (Control Register) sugeruje, że coś w nim będziemy zmieniać. To coś to pierwszy bit rejestru opisany jako SYNC - USART Synchronous Mode, widzimy, że jego ustawienie przy starcie MCU wskazuje na tryb asynchroniczny. Taki tryb nam nie odpowiada bo SPI jest synchronicznym stworkiem. Czyli wiemy, że musimy w naszej konfiguracji SPI , dokonać tutaj modyfikacji. Zatem namalujmy obrazek modyfikacji rejestru USARTn_CTRL. Ale aby to zrobić musimy zerknąć w plik nagłówkowy jak opisany jest nasz rejestr. Stosowne definicje znajdziemy w pliku efm32tg11b_usart.h

/* Using synchronous (SPI) mode*/
  USART3->CTRL |=_USART_CTRL_SYNC_MASK ;

Drugim rejestrem w ,którym trzeba dokonać ważnego wpisu to rejestr USARTn_CMD - Command Register, tutaj włączamy tryb Master za pomoca ustawienia bitu MASTEREN - Master Enable i włączamy transmisję bitem TXEN - Transmitter Enable. Nurkujemy zatem w pliku nagłówkowym efm32tg11b_usart.h , szukamy opisu rejestru i tworzymy na tej podstawie wpis :

/* Enable Master (SPI) mode*/
USART3->CMD |=USART_CMD_MASTEREN | USART_CMD_TXEN;

Zwracam uwagę, że tym razem nie użyłem maski ale  ustawienia bitu za pomocą dodania operacji przesunięcia bitowego (0x1 << 4), taką możliwosc daje plik nagłówkowy albo użycie maski albo przesunięcia.W rejestrze USARTn_CMD warto jeszcze posłużyć się bitem CLEARTX - (Set to clear transmit buffer and the TX shift register), który oczyści nam bufor SPI . Zatem dodajemy wpis :

 

/* Clearing old transfers */
USART3->CMD |=USART_CMD_CLEARTX;

No coś tam powoli posuwamy się do przodu ale jeszcze przed nami kupa roboty :). Teraz zajmijmy się bloczkiem ze schematu blokowego USART opisanym jako Baud Generator Generator i spróbujmy coś tutaj porzeźbić. Najpierw przyjrzyjmy się co proponuje dokumentacja czyli RM :



Widzimy jakiś szalony wzór ale generalnie olejemy go. Ponieważ gdzieś w przykładach Silicona znalazłem jego  prostszą wariację, którą się posłużę w swoich rozważaniach. A wygląda ona tak :

br = 128 * (SPI_PERCLK_FREQUENCY / SPI_BAUDRATE - 2)

zegar jakim się posłużymy to 26MHz, SPI_BAUDRATE tu zaserwujemy sobie wartość 1MHz. Podstawmy zatem wartości :

br = 128 * (26 / 1 - 2) = 3072

Zatem  do rejestru (na razie nie wiem jakiego) od Baud Rate wpiszemy powyższą wartość. Warto wspomnieć ,że maksymalna prędkość dla SPI to połowa dostępnego zegara. Szukamy w tabelce Register Map modułu USART w RM miejsca gdzie można wpisać nasze wyliczone br. Jedynym sensownym miejscem wydaje się rejestr USARTn_CLKDIV - Clock Control Register i pole bitowe DIV - Fractional Clock Divider. Już bez nurkowania do pliku nagłówkowego efm32tg11b_usart.h , wykontycypujemy wpis :

/* SPI Baud Rate*/
USART3->CLKDIV  = 3072;

Dla porządku powinniśmy jeszcze wyłączyć przerwania od SPI bo nie bedzięmy z nich korzystać oraz wyczyścić flagi statusowe przerwań. Zatem szukamy w tabelce Register Map modułu USART adekwatnych rejestrów. Tu nie mamy problemy bo ich nazwa są aż nadto czytelne : USARTn_IEN - Interrupt Enable Register i USARTn_IFC - Interrupt Flag Clear Register, zaglądamy jak te rejestry wyglądają od środka i kontycypujemy wpisy :

/* disabling interrupts */
USART3->IEN  = 0;

/* Clear previous interrupts */
USART3->IFC = _USART_IFC_MASK; // tu korzystamy z gotowej maski z pliku nagłówkowego efm32tg11b_usart.h
 
Na razie idzie jak po maśle, przyznam, że bardzo przydaje się doświadczenie nabyte z PIC32MM.

Teraz zajmiemy się konfiguracją pinów a potem będziemy się zastanawiać jak je podpiąć do USART3. Do skonfigurowania mamy dwa piny dla linii MOSI  pin PA0 a dla linii CLK  pin PA2. Czyli oba piny muszą być wyjściami typu PUSHPULL. Majstrujemy zatem wpis ale posłużymy się biblioteką emlib dla skrócenia zapisu :

GPIO_PinModeSet(gpioPortA,       /* Port */
                   0,                                      /* Pin */
                   gpioModePushPull,      /* Mode */
                   0 );                                    /* Output value */

GPIO_PinModeSet(gpioPortA,       /* Port */
                   2,                                      /* Pin */
                   gpioModePushPull,      /* Mode */
                   0 );                                    /* Output value */


Teraz dumamy jak te piny podpiąć do USART3 ??? Na widelec bierzemy trzy ostatnie rejestry z tabelki Register Map dotyczącej modułu USARTx a są to :

USARTn_ROUTEPEN - I/O Routing Pin Enable Register
USARTn_ROUTELOC0 - I/O Routing Location Register
USARTn_ROUTELOC1 - I/O Routing Location Register

Zaglądamy do rejestru USARTn_ROUTEPEN i widzimy tam takie hasła jak CLK Pin Enable, TX Pin Enable czyli rozumiemy z tego, że dokonujemy tutaj aktywacji poszczególnych funkcjonalności na pinach ale nadal nie widzimy podpięcia naszych konkretnych pinów PA0 i PA2. Zaglądamy zatem do dwóch pozostałych rejstrów opisanych jako I/O Routing Location Register i próbujemy zrozumieć sens opisu bitów np. Location 0, Location 1 etc ni w ząb nam to nic nie mówi. Wertujemy dostępną dokumentację czyli RM i datasheet ,próbujemy coś sensownego znaleźć odnośnie hasła Location. Zaglądamy m.in w datasheet do tabelki opisanej jako Pin Definitions w rozdziale Alternate Funcionality Overview i widzimy tam kolumnę LOCATION :


Nas interesuje z tej tabelki wszystko co dotyczy pinu PA0 i PA2 i USART3. Piny te znajdujemy w wierszu dla US3_CLK i US3_TX, w kolumnie LOCATION 0-3
i widzimy coś takiego -  0: PA0 i 0: PA2. Domyślamy się , że cyferka 0 postawiona z przodu pinów oznacza nasze magiczne Location. Wracamy zatem do rejestru USARTn_ROUTELOC0 i do bitów opisanych jako CLKLOC i TXLOC, do tych bitów musimy wpisać wartość reprezentującą LOC0, fizycznie jest to cyferka 0. Czyli teoretycznie nic nie musimy wpisywać bo tak jest ustawiony rejestr po starcie MCU ,ale dla porządku to zrobimy. Dobrze doszliśmy zatem do tego jak przyporządkować nasze piny , zmajstrujmy zatem stosowne wpisy. Zaglądamy standardowo do pliku nagłówkowego efm32tg11b_usart.h i szukamy jak są opisane nasze rejestry.

/* Enable TX and CLK funcionality*/
USART3->ROUTEPEN |= USART_ROUTEPEN_TXPEN | USART_ROUTEPEN_CLKPEN ;

/* Set Location as TX and CLK */
USART3->ROUTELOC0 |= USART_ROUTELOC0_TXLOC_LOC0 | USART_ROUTELOC0_CLKLOC_LOC0 ;

I to by było na tyle jeśli chodzi o konfigurację SPI . Dla formalności jeszcze ustawmy zegar za pomocą funkcji z biblioteki emlib prosto, łatwo i przyjemnie :

 /* Enabling clock to USART 3*/
 CMU_ClockEnable(cmuClock_USART3, true);
 /* Enabling clock to GPIO*/
 CMU_ClockEnable(cmuClock_GPIO, true);

Esencją naszych zmagań z SPI będzie napisanie funkcji do wysyłania bajtu i stringa , poniżej propozycja takich funkcji . Działanie funkcji przetestujemy analizatorem stanów logicznych i to będzie zwieńczeniem artykułu.


efekt wysyłania bajtu po SPI, wysyłamy literkę A co 100 ms (program będzie dostępny na GitHubie) :



A tu efekt działania funkcji do wysyłania stringa po SPI, wysyłamy co 100 ms napis "Witaj SPI" :



W zasadzie wyczerpałem temat na tę chwilę. Muszę przyznać, że byłem zdziwony trochę , że konfiguracja SPI w MCU Silicona zatrybiła od pierwszego kopa, że nie umknęło żadne ustawienie w rejestrach ,pomimo, że robiliśmy "karkołomne" operacje na nich :) W sumie zapoznanie się z konfiguracją SPI i wyrzeźbienie jej bez pomocy biblioteki emlib było bardzo przyjemnym doznaniem transcendentalnym.

W linkach znajdziemy spakowany plik projektu. Plik ściągamy na dysk i rozpakowujemy w katalogu SimplicityStudio/v4_workspace. Potem importujemy do środowiska Siplicity Studio 4 wybierając opcję Existing Projects into Workspace. Szkoda , że środowisko Silicona nie ma wsparcia dla GitHuba tak jak MPLABX-IDE Microchipa.


Podczas uruchomienia wyświetlacza ILI9163 wyszedł jeden istotny aspekt  konfiguracyjny . Mianowicie chodzi o ustawienie bitu MSBF w rejestrze USARTn_CTRL - Control Register. Ustawienie to warunkuje tryb pracy SPI,  standardowym trybem jest most significant bit first. Drugi tryb to least significant bit first. Niektóre podzespoły potrzebują jednego trybu a inne drugiego , np MCP2517FD potrzebuje trybu drugiego a wyświetlacz ILI9163 trybu pierwszego. Aby SPI poprawnie działało z wyświetlaczem LCD ILI9163 musimy konfigurację USART uzupełnić dodatkowym wpisem :

 /* Data is sent with the most significant bit first*/
   USART3->CTRL |= USART_CTRL_MSBF;

Poniżej obrazek z zebraną do kupy konfiguracją SPI :




Pozdrawiam
picmajster.blog@gmail.com


Linki :
EFM32TG11 - Datasheet 

Brak komentarzy:

Prześlij komentarz