sobota, 27 lipca 2019

SI4463 - transciver RF firmy Silicon Labs do zastosowań bateryjnych, konwersja biblioteki dla ATSAML10 firmy Microchip.

W tym wpisie zajmę się konwersją biblioteki transcivera radiowego SI4463 napisaną dla ekosystemu PIC32 do ekosystemu ATSAML. W szczególe bibliotekę napisaną dla PIC32MM przekonwertujemy do ATSAML10E16. Stosowny artykuł w przypadku PIC32MM poczyniłem tutaj . Trasciver SI4463 zaskoczył mnie bardzo pozytywnie przy testach wewnątrz-budynkowych. W rozległym piętrowym obiekcie z żelbetonowymi przeszkodami nie było najmniejszych problemu z nawiązaniem łączności i przesyłem danych. Ani jedna ramka nie została utracona na 100 przesłanych pomimo ,że moduły były przedzielone nawet i kilkoma kondygnacjami o grubych stropach. Postanowiłem zatem kontynuować bliski kontakt z tymi modułami ale tym razem z perspektywy mikrokontrolera opartego o rdzeń M23 firmy Microchip

Na wstępie rzecz odbiegająca od tematyki tego artykułu ale wydaje mi się , że ciekawa, dlatego prezentuję ją tu i teraz .  Wpadła w moje ręce statystyka z 2019 r popularności architektur MCU na naszym rynku :


Miło wiedzeć, że produkty Microchipa mają aż tak wysokie noty w tej statystyce. A ciekawe, że NXP , która jest pociotkiem STM-a (STM ma duże udziały w NXP) staje się marginesem "społecznym" , patrz MCU ColdFire. Dla mnie to zawsze było szemrane towarzystwo technologiczne :)


Wracamy do tematyki artykułu :

Moja baza sprzętowa  to :
W pierwszej kolejności zerkam na pinologię modułu RF SI4463 , źródłem informacji jest schemat  mojej płytki developerskiej :



Podłączenie modułu RF na mojej płytce wygląda następująco :

Signal SI4463 ---> MCU ATSAML10E16
      
    MOSI                    --> PA10
     MISO                    --> PA09
     SCK                      --> PA11
     nSEL                    --> PA08
     SDN                     --> PA02
     IRQ                      --> PA03
     GPIO0                  --> PA16

Jednym z elementów dostosowania biblioteki jest konfiguracja pinów. Najpierw próbuję ustalić niezbędne dane do konfiguracji pinów pod kątem pracy w trybie SPI. Skupiam zatem uwagę na pinach PA09 , PA10, PA11. Poszukiwania zaczynam od datasheet i tabelki na stronie 21


Muszę ustalić na podstawie powyższej tabelki do jakiego numeru SERCOM podepniemy nasze piny i jakie mają numery PAD-ów oraz jaka literka znajduje się w nagłówku kolumny. Te trzy rzeczy są niezbędne do konfiguracji pinów dla SPI. Z tabelki odczytujemy ,że nasze piny PA09 , PA10, PA11 są obsługiwane przez SERCOM1 numery PAD-ów to :

PA09 - PAD[1]
PA10 - PAD[2]
PA11 - PAD[3]

Literka w nagłówku kolumny to C. Mając powyższe informacje możemy przystąpić do konfiguracji pinów w trybie SPI. W przypadku pinów skonfigurowanych do pracy z SPI nie jest potrzebne ustawianie ich czyli nie musimy konfigurować w module PORT typu i kierunku pinu. Kontroler SPI ustawi to automatycznie poza naszą percepcją tzn MOSI i SCK będzie cyfrowym wyjściem a MISO cyfrowym wejściem.

Teraz warto sobie przypomnieć jak wygląda dystrybucja sygnału do fizycznego pada pinu . Podeprę się tutaj schematem blokowym  :


Na schemacie widzimy dwa bloki PORT i PORTMUX . Na starcie wszystkie pady pinów są podpięte do modułu PORT a pośredniczy w tym moduł PORTMUX , który zatrzaskuje połączenie pomiędzy modułem PORT a padem pinu. Jeśli chcemy do fizycznego pada pinu podpiąć sygnał z jakiegoś peryferium np. SERCOM to w tym przypadku musimy odłączyć moduł PORT od pada pinu a ustanowić w module PORTMUX kros pomiędzy źródłem sygnału peryferyjnego a fizycznym padem pinu. Od strony rejestrów jak wynika ze schematu powyższego aby odłączyć się od modułu PORT trzeba ustawić bit PMUXEN w rejestrze PINCFG na 1. Drugim krokiem jest wskazanie źródła sygnału peryferyjnego np. SERCOM i jak widać ze schematu musimy tego szukać w  rejestrze PMUX.

Najpierw odłączmy nasze piny PA09 , PA10, PA11 (które chcemy podpiąć do SERCOM1) od modułu PORT robimy to następująco :

/*PORTA --> PINCFG09 --> PMUXEN --> 1 / connect PA09 to the module PMUX*/
PORT->Group[0].PINCFG[9].bit.PMUXEN = 1;
/*PORTA --> PINCFG10 --> PMUXEN --> 1 / connect PA10 to the module PMUX*/
PORT->Group[0].PINCFG[10].bit.PMUXEN = 1;
/*PORTA --> PINCFG11 --> PMUXEN --> 1 / connect PA11 to the module PMUX*/
PORT->Group[0].PINCFG[11].bit.PMUXEN = 1;
 

Group[0] w powyższych zapisach reprezentuje PORTA 

Jakby chciał tak słowami opisać powyższe zapisy to np rozważmy pierwszy  z nich : W module PORT odwołujemy się do PORTA (Group[0]) dla którego w rejestrze PINCFG odwołującego się do pinu 9 ustawiamy bit PMUXEN na 1.

Mamy odłączone nasze trzy piny PA09 , PA10, PA11 od modułu PORT teraz musimy dokonać krosu peryferium SERCOM1 w module PORTMUX z padami naszych pinów. Ustawień dokonujemy w rejestrze PMUXn. Rejestry PINCFG i PMUXn znajdziemy w tabelce na stronie 634 w datasheet. Datasheet jest interaktywny więc z tabelki przeniesiemy się bezpośrednio do opisu w/w rejestrów.

Rejestr PMUXn gdzie n od 0 do 15. Składa się z dwóch pól bitowych
PMUXO[3:0] i PMUXE[3:0]. Jeśli nasz pin ma numerek parzysty to wpisów dokonujemy w polu PMUXE jeśli nieparzysty w polu PMUXO. Wartość jaką musimy wpisać w jedno z tych pól (adekwatnie do numeru pinu) to literka C (literka z nagłówka kolumny - patrz tabelka na początku artykułu). Literka C jest reprezentowana za pomocą liczby 0x02 co wynika z tabelki poniżej :


Wartość 0x2 musimy wpisać w pole  PMUXO lub PMUXE w rejestrze PMUXn odpowiednio dla każdego pinu PA09 , PA10, PA11.

Rozważmy przykład bo na razie zdaje sobie sprawę z zasłony ciemności jaka rozpostarła się nad rozważanym zagadnieniem :) Na początku sam miałem z tym lekki kłopocik ale dokumentacja dla ATSAM-ów jest dobra i można to zrozumieć bez dzwonienia o pomoc na 112.

Bierzemy na widelec pin PA09 i preparujemy dla tego pinu zapisy w rejestrze PMUXn. Nasz pin jest nieparzysty ,wiec wpis będziemy robić w polu PMUXO Pierwszym krokiem będzie ustalenie numerku PMUXn, mamy do dyspozycji wartości 0..15. Dla nieparzystego pinu wyliczamy numerek ze wzoru :

dla PA09 (n = 9), 
PMUX = 2*n+1 stąd n = (9 - 1)/2 = 4

Zatem dla PA09 adekwatnym rejestrem PMUXn jest PMUX4 . Wpis w rejestr PMUX4 dla pinu PA09 jaki musimy zrobić wygląda następująco :

/*PORTA --> PMUX4 --> PMUXO --> 0x2 / connect PA09 (odd pin 2*n+1) to the module SERCOM1 (put C)*/
PORT->Group[0].PMUX[4].bit.PMUXO = 0x2;

Liczba 0x2 reprezentuje jak pamiętamy literkę C. Jeśli ktoś stracił wątek to nic :) kwestia zrozumienia za n-tym razem.

Prześledźmy teraz ustawienie rejestru PMUXn w przypadku pinu o numerze parzystym PA10. W tym przypadku wpisu będziemy dokonywać w pole bitowe PMUXE.

dla PA10 (n = 10), 
PMUX = 2*n stąd n = 10/2 = 5  (wzorek na n ulega lekkiej zmianie)

Zatem dla PA10 adekwatnym rejestrem PMUXn jest PMUX5 . Wpis w rejestr PMUX5 dla pinu PA10 jaki musimy zrobić wygląda następująco :


/*PORTA --> PMUX5 --> PMUXE --> 0x2 / connect PA10 (even pin 2*n) to the module SERCOM1 (put C)*/
PORT->Group[0].PMUX[5].bit.PMUX
E = 0x2;


Analogicznie robimy dla pinu  PA11 (nieparzysty).

/*PORTA --> PMUX5 --> PMUXO --> 0x2 / connect PA11 (odd pin 2*n+1) to the module SERCOM1 (put C)*/
PORT->Group[0].PMUX[5].bit.PMUXO = 0x2;


Przebrneliśmy przez etap konfiguracji pinów dotyczący podpięcia padów PA09 , PA10, PA11 do modułu peryferyjnego SERCOM1.

Kolejny etap  to konfiguracja modułu SERCOM1 dla naszych pinów PA09 , PA10, PA11 tak aby na poszczególnych pinach była odpowiednia funkcjonalność SPI. Tutaj do ustawień służy rejestr CTRLA w module SERCOM i podmodule SPI. Interesują nas konkretnie pola bitowe DIPO i DOPO. Tabelka z rejestrem jest na stronie 772 w datasheet.

Naszym punktem wyjścia do ustawień tutaj jest informacja podana na początku artykułu , przypomnę : 


PA09 - PAD[1]  -> MISO
PA10 - PAD[2]  -> MOSI
PA11 - PAD[3]  -> SCK


Zaglądamy najpierw do opisu pola bitowego DIPO w którym ustalamy funkcjonalność dla MISO:



Widzimy, że aby ustawić na naszym pinie PA09 funkcjonalność MISO musimy dokonać wpisu w pole DIPO = 0x1 (bo PA09 - PAD[1])

Przechodzimy do pola bitowego DOPO opis poniżej

 
Tutaj wpisujemy w pole DOPO wartość 0x1  co sprawi, że na pinie PA10 pojawi się funkcjonalność MOSI a na pinie PA11 funkcjonalność SCK.

Wpisy w rejestry jakimi posłużymy się w programie będą wyglądały następująco :

/*CTRLA --> DIPO -->  0x1 / in mode MASTER allocate PAD[1] to the MISO*/
SERCOM1->SPI.CTRLA.bit.DIPO = 0x1;


/*CTRLA --> DOPO -->  0x1 / in mode MASTER allocate PAD[2] to the MOSI and PAD[3] to the CLK*/
SERCOM1->SPI.CTRLA.bit.DOPO = 0x1;


I to już koniec konfiguracji pinów do pracy  z SPI. Zreasumujmy ogólne czynności jakie zostały wykonane :
  1. odpięcie pinów  PA09 , PA10, PA11 od modułu PORT
  2. skrosowanie w/w pinów z peryferium SERCOM1
  3. przyporządkowanie pinom poszczególnych funkcjonalności SPI.
Ponieważ uznałem , że powyższa konfiguracja jest cokolwiek trudna do ogarnięcia dla osób zaczynających z ATSAM-ami , dlatego krok po kroku ją opisałem.  
ATSAM-y były dla mnie wyzwaniem intelektualnym :), myślałem , że polegnę na nich z racji ich "skomplikowanej" obsługi i niebanalnej specyfiki. Życie zweryfikowało moje wyobrażenia i okazało się , że wszystko jest dla ludzi, człowiek to wymyślił to i człowiek to zrozumie :). Jedynym dokumentem jakim się posługiwałem w poznaniu ATSAM-ów był datasheet, nie wszystko za pierwszym razem rozumiałem ale za 10-tym już tak. Jeśli ktoś chciałby się na ATSAM-y przesiąść np z ekosystemu 8-bitowego ATMEL-a. To radziłbym najpierw wejść w PIC32 (bo niski próg wejścia i łatwo) a potem płynnie przeskoczyć na ATSAM-y.

Jeden z pinów modułu RF , IRQ musimy ustawić od strony MCU jako wejście (Input). Jest to dokładnie pin PA03. Specyfika budowy wewnętrznej I/O ATSAM-a wymaga tutaj odprawienia pewnych czarów. Pokażę w celach dydaktycznych jak to zrobić. Naszym celem jest konfiguracja pinu PA03 jako wejście podciągnięte do stanu wysokiego. Zaczniemy od tabelki z datasheet na str. 629



i do tego uzupełnienie w postaci rysunku :


Zauważmy, że jest opcja ustawienia pinu jako wejścia i wyjścia jednocześnie.
Z powyższych obrazków wynika , że aby skutecznie ustawić pin jako wejście podciągnięte do stanu wysokiego ,musimy dokonać ustawień w czterech miejscach (trochę dużo) : DIR , INEN, PULLEN, OUT. Poniżej pokazuję jak to zrobić w programie :

/****************************************************************************
  Setting the Input and  Pull Up see page 629 in datasheet
****************************************************************************/

PORT->Group[0].DIRCLR.reg = PORT_PA03; //Set pin PA03 direction to Input
PORT->Group[0].PINCFG[3].bit.INEN = 1 ; //Set pin PA03 Input Enable
PORT->Group[0].PINCFG[3].bit.PULLEN = 1 ;
//Set pin PA03 PULL Enable
PORT->Group[0].OUTSET.reg = PORT_PA03 ;
//Set pin PA03 PULL UP uff :)

Jak widzimy trochę sporo tych zapisów a im ich więcej tym łatwiej o błąd. Ale nie ma co się martwić,  w przyszłości będziemy programować w środowisku ,któremu wskażemy ogólnie funkcjonalność jaką chcemy uzyskać a program wygeneruje się automatycznie. Przyszłość informatyków jest nie jasna w tym kontekście, idą pojazdy autonomiczne a co za problem "programistę" autonomicznego stworzyć. Myślę , że to kwestia czasu tylko :). Cieszmy się , że możemy jeszcze coś podłubać sami własnymi rękoma i prowadzić sami pojazdy , cieszmy się z rzeczy prostych :)

Na wszelki wypadek pokażę jak próbować się dostać do danych pinu ustawionego jako wejście (Input):

#define PA03_GetValue            (REG_PORT_IN0 & PORT_PA03) 

lub

#define PA03_GetValue            (PORT->Group[0].IN.reg & PORT_PA03)

Wartość 0 w słowie IN0 oznacza, że odwołujemy się do portu A, zapis dla portu B i PB03 wyglądałby tak :

REG_PORT_IN1 & PORT_PB03

lub

PORT->Group[1].IN.reg & PORT_PB03


W ATSAML10E16 wszystkie piny mamy na porcie A.

No dobrze mamy skonfigurowane piny do pracy z SPI. Teraz pytanie o zegar czy przypadkiem moduł SERCOM1 , który użyczył nam funkcjonalności SPI dla naszych pinów PA09 , PA10, PA11 nie trzeba przypahttps://www.linuxmint.com/start/tina/dkiem potraktować jakimś zegarem ?? Odpowiem krótko tak trzeba inaczej nasze SPI nie zadziała. Sam zresztą złapałem się niedawno ,kiedy zapomniałem uruchomić zegar dla SERCOM-a i drapałem się chwilę w głowę czemu mi urządzenie nie działa :) potem się cieszyłem jak głupi do sera, że matoł ze mnie jak zapomniałem o takich podstawach :)
Ponownie w celach dydaktycznych pokażę jak ustawić zegar dla SERCOM1. Zauważmy, że artykuł o module SI4463 a tu ile trzeba się "natrudzić" aby w ogóle dojść do tego tematu :)

Zabawy zegarem zawsze zaczynam od schematu blokowego zegara jaki zobaczymy w aplikacji webowej ATMEL START 
Jak zerkniemy na schemat to łatwiej nam ogarnąć co trzeba zrobić aby skonfigurować zegar. Tworzę zatem sobie nowy projekt w aplikacji webowej ATMELA z MCU ATSAML10E16Ai po utworzeniu przechodzę w zakładkę CLOCK , pojawia obrazek jak poniżej :



Do schematu blokowego dodaję komponent SPI (numerek bez znaczenia) i podpinam zegary CORE i SLOW tego modułu do Generatora nr 0 :


Mając przed oczyma powyższy schemat blokowy mogę sobie wyobrazić (znając specyfikę zegara w ATSAM-ach)  co należy zrobić aby zegar dla wybranego SERCOM skonfigurowanego dla SPI uaktywnić. W sumie są to dwie rzeczy, które musimy zrobić :

  1. podłączyć zegar CORE i SLOW modułu SERCOM via SPI do Generatora nr 0. (utworzyć połączenie - kanał)
  2. włączyć utworzony kanał dla zegara CORE i SLOW modułu SERCOM via SPI
Pokazuję jak to zrobić :

Najpierw zajmiemy się utworzeniem połączenia (kanału) dla zegara CORE i SLOW z Generatorem nr 0, fizycznie dokonujemy tego w module zegara GCKL w rejestrze PCHCTRLm gdzie m szukamy w  tabelkach poniżej :




/*PCHCTRL10 --> GEN --> 0x0   / connect SERCOM1 Slow to Generator nr 0*/
GCLK->PCHCTRL[10].bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val;

/*PCHCTRL12 --> GEN --> 0x0   / connect SERCOM1 Core toPICbus Generator nr 0*/
GCLK->PCHCTRL[12].bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val;

a teraz włączenie utworzonych powyżej kanałów :

/*PCHCTRL10 --> CHEN --> 1  / switch on canal for clock SERCOM1 Slow*/
GCLK->PCHCTRL[10].bit.CHEN = 1;

/*PCHCTRL12 --> CHEN --> 1  / switch on canal for clock SERCOM1 Core*/
GCLK->PCHCTRL[12].bit.CHEN = 1;

gdzie :
GCLK_PCHCTRL_GEN_GCLK0_Val = 0 (bo odwołujemy się do Generatora nr 0)


Może wydawać się na początku, że ustawienie zegara w ATSAM-ach jest cokolwiek skomplikowane ale zapewniam , że tak nie jest. Zdecydowanie bardziej upierdliwie to wygląda w STM32. Zresztą o czym tu mówić na początku wszystko wydaje się trudne i skomplikowane ale ludzie nawet języka Chińskiego są w stanie się nauczyć to co tu narzekać , że coś jest trudne. Ścisnąć mocno pośladki i napierać do skutku aż w mózgu klapki się pootwierają. Moim zdaniem  naprawdę warto poznać ATSAM-y , to bardzo intrygujące i nietuzinkowe MCU.

Powyżej przedstawiłem aspekty związane z konfiguracją ATSAML, które moim zdaniem mogą na początku przygody sprawiać jakieś problemy. Wiedza, którą prezentuję na moim blogu w obszarze MCU ATSAM jest unikalna , nie znalazłem w necie jej odpowiednika. Dlatego zachęcam do korzystania i własnych eksperymentów :)

Pierwsze testy biblioteki mam za sobą. O ile sama biblioteka działa o tyle natrafiłem po drodze na problemy , które na pozór wyglądały na sprzętowe. Mianowicie  pin IRQ modułu radiowego RF cały czas wystawiał mi sygnał niski co blokowało  działanie programu. Na początku myślałem, że to jakiś problem sprzętowy ale przyczyna leżała w aktywnym przerwaniu CMD_ERROR, które sam jak baran włączyłem. Domyślnie jest wyłączone i zalecam tak zostawić. Aczkolwiek przerwanie od CMD_ERROR miałem również włączone w bibliotece dla PIC32MM i tam nie było z tym problemu. Muszę w chwili wolnej dojść co jest przyczyną generowania tego przerwania związanego z procesem przetwarzania komendy, na razie jednak nie przeszkadza to w poprawnym działaniu biblioteki bo wszystko wygląda na to, że działa. Przy sprzętowym CTS problem zniknął przy włączonym przerwaniu CMD_ERROR no i gitara.

W akcie desperacji nie mogąc na poczatku znaleźć przyczyny problemów zrobiłem płytkę rozszerzenie do złącza PICbus, która wygląda tak :



Czyli moduł radiowy SI4463 możemy sobie osadzić w rozszerzeniu PICbus , które znajduje się na każdej mojej płytce developerskiej. Zmieniłem tym samym połączenia pomiędzy MCU a SI4463. Dodałem dwie diody do sygnalizowania transmisji i odbioru przy okazji.
Dodatkową zaletą będzie mozliwość wykorzystania modułu z dowolną moją płytką developerską np dla PIC32MM lub płytkami Microchip'a wyposażonymi w złącze mikroBUS. Całe to zamieszanie jest dla mnie cenną nauką na przyszłość . Plik Gerbera dla płytki roszerzenia będzie dostępny  tutaj

Płytki już mam. W międzyczasie musiałem iść na operację przepukliny , więc wszytko się rozciągnęło w czasie. Płytka PICBus z modułem SI4463 i to wszystko wpięte do mojej płytki developerskiej dla ATSAML10/11 wygląda jak poniżej (pudełko od Microchipa a obok owoc dzikiej róży z mojego ogródka) :


Zrobiła się taka "kanapka" ale ma to swoją zaletę w tym, że moduł radiowy jest wysoko wyniesiony co sprzyja lepszej propagacji fal. Poza tym jak wspominałem wcześniej otrzymałem uniwersalną płytkę , którą zapnę do każdego modelu mojej i nie tylko mojej płytki ze złączem PICbus , kompatybilnym ze złączem Mikroelektroniki.

Nowe podejście do wpięcia modułu RF w płytkę developerską wymusiło zmianę rozkładu pinów.  Pinologia dla płytki roszerzenia jest następująca :

Signal SI4463 ---> MCU ATSAML10E16
       
MOSI                    --> PA18
MISO                    --> PA17
SCK                      --> PA19
nSEL                     --> PA05
SDN                      --> PA03
IRQ                       --> PA02
GPIO0                   --> PA08
GPIO1                   --> PA10


dodatkowo diody LED dla TX i RX znajdujące się na płytce PICbus :

 LED TX     --> PA17
 LED RX     --> PA09

Pierwszym testem modułu RF w układzie "kanapki" :) było sprawdzenie działania diod LED . Aby tego dokonać potrzebujemy skonfigurować piny PA17 i PA09 jako wyjścia (Output). Robimy to poniższym atomowym zapisem :

PORT->Group[0].DIRSET.reg = PORT_PA09 | PORT_PA16; //Set pin PA09,PA16 direction to Output for PICbus LED TX and RX

Teraz zapalamy diody LED poniższymi zapisami (jeden z krótszych wariantów):

REG_PORT_OUTSET0 = PORT_PA09
REG_PORT_OUTSET0 = PORT_PA16;

Powyższe zapisy ustawiają stany wysokie na PORTA ("0") na pinach PA09 i PA16.  Jeśli chcemy ustawić stany niskie to zamiast OUTSET0 posłużymy się OUTCLR0, jeśli chcemy toglować pinem to OUTTGL0. To wszytko są zapisy atomowe. Test zapalenia diod przebiegł pomyślnie.

Kończę na razie wątek dodatkowej płytki rozszerzenia dla modułu RF i wracam do dedykowanego rozwiązania czyli modułu RF wpiętego gniazdo dla SI4463 na mojej płytce dla ATSAML10. Docelowo biblioteka będzie napisana w części sprzętowej dla tego dedykowanego gniazda na płytce.

W dedykowanym złączu dla modułu RF mam podpięty tylko jeden z czterech dostępnych pinów GPIO a jest to dokładnie GPIO0, od strony MCU na pinie PA16 . Trochę się sfrajerzyłem zbytnim minimalizmem w złączu , bo piny GPIO w module SI4463 mają miliony ciekawych zastosowań i trybów pracy (postanowiłem zrobić nową wersję płytki ver 3.2 dla ATSAML10 w której na złączu RF będą podpięte GPIO0..2, płytka jest już dostępna na GitHub). W mojej bibliotece dla SI4463 wykorzystam ten jeden dostępny w złączu pin do sprzętowego potwierdzania pomyślnego przetworzenia operacji czyli CTS. Normalnie CTS jest realizowane programowo i jak zerkałem na inne biblioteki na GitHubie to nikt nie odwarzył się realizować CTS sprzętowo. Ja to zrobiłem chyb  pierwszy. Sprzętowy CTS jest znacznie szybszy od programowego , pomiar dla komendy Part_Info wykazał , że sprzętowy CTS jest o 100 % szybszy od programowego. Delikatny problemik polegał na tym, że sam proces inicjalizacji modułu RF wymaga dla pierwszej komendy POWER UP , programowego CTS-a a dopiero w drugiej komendzie inicjalizującej GPIO_PIN_CFG (plik radio_config_si4463.h) pojawia się możliwość konfiguracji pinów GPIO modułu RF. Poradziłem sobie w ten sposób , że powołałem w pliku si4463.c zmienną switch_CTS za pomocą , której przełączam się na sprzętowy CTS po wykonaniu funkcji inicjalizującej modułu RF. Taki zabieg prawdopodobnie nie byłby potrzebny gdybym miał w złączu do dyspozycji pin GPIO1, który jak wynika z dokumentacji jest skonfigurowany na starcie na CTS, ale jest to tylko jakaś enigmatyczna wzmianka ,więc nie wiem czy pewna. 
W płytce rozszerzeniu dla gniazda PICbus dałem dwa piny GPIO0 i GPIO1 to tak na marginesie.

Tak jeszcze na marginesie , to potwierdzanie pomyślnie przetworzonej operacji czyli CTS może wydawać się cokolwiek upierdliwe ale dzięki temu moduł RF działa pewnie i przewidywalnie. A z dotychczasowych doświadczeń z modułem SI4463 wiem , że można na nim polegać bo działa bardzo stabilnie i pewnie.

Podczas pierwszych testów biblioteki dla modułu RF wyszło mi  zagadnienie związane ze zbyt długą obsługą wyświetlania danych na LCD. Czego efekt odczułem w ten sposób, że odbierałem co drugą nadaną ramkę. Bo podczas kiedy MCU był zajęty obsługą wyświetlacza LCD przychodziła ramka , która  nie została obsłużona. Efekt ten nie uwidocznił mi się w przypadku testowania biblioteki na PIC32MM i wyświetlaczu DOGM162. Tu wspomnę, że majszybszy wyświetlacz z jakim miałem do czynienia dotychczas to model DOGM204
Przypomina to o marności innobytu i o uwzględnianiu zależności czasowych w obsłudze sprzętu :) . Właściwym kierunkiem w rozwiązaniu problemiku jest oczywiście obsługa odbioru transmisji w przerwaniu sprzętowym a nie sprawdzanie stanu pinu IRQ modułu RF w pętli głównej, tak jak to w przypływie radosnej twórczości zrobiłem.


Mamy zatem dobry przyczynek ku temu aby zorientować się jak skonfigurować przerwanie w ATSAML10. Pin PA03 obsługuje nam na zasadzie sprawdzania stanu , obsługę zdarzeń modułu RF. Jeśli coś się dzieje w module RF co podlega sygnalizacji na pinie IRQ modułu RF zostanie wystawiony stan niski. Wyjście IRQ mamy podpięte do PA03 w MCU. Po wykryciu stanu niskiego realizujemy obsługę zdarzenia. Jak to robię w programie bez użycia przerwania w przypadku odbioru RX pokazuje obrazek poniżej :


Po krótce co robi powyższa sentencja. W pętli głównej programu sprawdzamy stan pinu IRQ. Jeśli jest na tym pinie stan niski przechodzimy do funkcji SI4463_Get_Interrupt(), która pobiera nam do bufora inter_buff obraz stanów wszystkich flag sygnalizacyjnych z modułu RF. Kolejnym krokiem jest sprawdzenie czy flaga PACKET RX PEND od sygnalizacji odebranej wiadomości RX jest ustawiona : ( inter_buff[2] & 0x10), flaga ta jest umiejscowiona w trzecim bajcie na 4 bicie. Wynika to ze struktury strumienia danych jakie  otrzymamy z modułu RF po wydaniu komendy GET_INT_STATUS


Po sprawdzeniu flagi PACKET RX PEND (jeśli jest ustawiona) przechodzimy do sprawdzenia flagi CRC_ERROR_PEND tu zależy nam na tym aby flaga nie była ustawiona bo sygnalizuje nam błąd w sumie CRC odebranej wiadomości czyli wiadomość została uszkodzona. Jeśli CRC jest oki przechodzimy do analizy odebranej ramki. Funkcja SI4463_RX_FIFO ( receive_buff, SI4463_Get_Packet_Info()  ), pobiera nam odebraną ramkę z bufora sprzętowego modułu RF do bufora lokalnego receive_buff. Argument w postaci funkcji SI4463_Get_Packet_Info() pobiera nam przesłaną w ramcę długość pola danych czyli wiemy ile danych do bufora lokalnego mamy pobrać. Potem inkrementujemy zmienną licznik , która symbolizuje nam ilość poprawnie odebranych wiadomości i przechodzimyu do nieszczęsnej sekcji wyświetlania danych na LCD. W tym momencie jak przyjdzie kolejna ramka to zostanie "przeoczona" przez naszą metodę odbioru. Dla ciekawości powiem , że czas obsługi wyświetlania danych na LCD zajmuje 0.48 s !!!!! i w tym momencie zatęskniłem za ultra szybkim wyświetlaczu DOGM204. Rozumiem teraz potrzebę isnienia takich konstrukcji dwurdzeniowych jak dsPIC33CH
gdzie jeden rdzeń obsługuje nam interfejs użytkownika a drugi logikę "biznesową" :)

Jeszcz na koniec omawiania skrawka kodu powyżej. Bardzo ważna sentencja na końcu :

           SI4463_Clear_RX_FIFO();
           SI4463_RX_Start( 0, 0 ); 
           SI4463_Get_Interrupt(inter_buff);
//get interrupt status and clear

Po każdym odbiorze udanym czy nie (CRC złe). Musimy wyczyścić bufor FIFO odbiorczy w module RF. Uruchomić ponownie tryb RX jeśli chcemy zostać na nasłuchu. Można to akurat skonfigurować w rejestrach w jaki tryb wchodzimy po odebraniu lub nadaniu wiadomości. Ostatnią czynnością jest pobranie flag statusowych z modułu RF, to pobranie jest równocześnie resetem tych flag , które należy przed oczekiwaniem na kolejne operacje wyzerować.

Powyżej przedstawiłem wersję blokującą obsługi odbioru ramki przez moduł RF. Poniżej wariant jedynie słuszny czyli wersja nieblokująca wykorzystująca przerwanie na pinie PA03 , podłaczonym do pinu IRQ modułu RF. W tym przypadku otrzymamy efekt wyświetlania licznika odebranych ramek z przeskokiem. To znaczy jeśli odbierane ramki będą przychodziły szybciej niż wyświetlacz LCD nadąży odświeżyć ekran to zobaczymy na ekranie wartość licznika odebranych ramek nie po kolei 1.2.3....30 tylko np. 1..3..5...30.
Dla nas jednak priorytetem jest fakt aby wszystkie ramki zostały odebrane a sprawa ich wyświetlania z przeskokiem jest sprawą drugorzędną.



Kodu już nie tłumaczę bo po krótce opsałem go w wersji blokującej. Różnica jest tu taka, że całą obsługę odbioru ramki pakujemy do funkcji obsługi przerwania. A w pętli głównej pozostaje odświeżanie danych na wyświetlaczu LCD jeśli przyszły nowe dane.

Teraz omówię luźne zagadnienie dotyczące regulacji mocy w nadajniku. Po prostu napiszemy sobie funkcję , która tę moc zmniejszy nam np. do +5dB. Po resecie moduł startuje z mocą maskymalną +20 dB. Nie zawsze taka pełna moc jest potrzebna bo chcemy np. zaoszczędzić na prądzie a mniejsza moc i tak pokrywa nasze potrzeby. Na szybko sprawdziłem, że jedna kondygnacja w dużym domu z grubym stropem i łączność po przeciwległych stronach budynku na +5dB przechodzi 100 na 100 poprawnych ramek. Ale oprócz mocy ważne jest dobre skonfigurowanie toru radiowego co czyniłem w tym artykule o SI4463.

Poszukajmy sobie najpierw w spisie komend API (Si4463 revC2A Command/Property API Documentation - dostępne na stronie producenta)  coś stosownego do ustawienia mocy. Znajduję odpowiednie "coś" w spisie Property (właściwościach) :


Property o nazwie PA_PWR_LVL Właściwość ta od środka wygląda tak :


Widzimy, że właściwość PA_PWR_LVL wymaga podania tylko jednego parametru DDAC z zakresu od 0 do 0x7F ale nie widać jak ta wartość jest skorelowana z mocą w dB. Szukamy tej informacji w datasheet i znajdujemy taki o to wykres :


Z wykresu widzimy, że nie ma zależności liniowej pomiędzy wartością podaną w  PA_PWR_LVL a mocą. Taka specyfika modułu ale w niczym nam to nie przeszkadza. Odczytujemy wartość jaką należy wpisać do PA_PWR_LVL aby uzyskać założoną moc. Wybieram np. wartość mocy ok +5dB
, wartość jaką odczytuję to 10 (hex 0x0A) i to musimy przesłać do modułu z parametrem 10. Jak fizycznie zrealizować taki wpis do modułu.
Zerknijmy jakie kolejne bajty trzeba wysłać do modułu i co one oznaczają.

PA_PWR_LVL ---> 0x11, 0x22, 0x01, 0x00, 0x0A

Bajt nr 1 = 0x11 --> kod komendy SET_PROPERTY (Sets the value of one or more properties), znajdziemy ją w spisie komend. Komenda do ustawienia właściwości jednej lub kilku.
Bajt nr 2 = 0x22 --> Group ID (ID Property PA_PWR_LVL)
Bajt nr 3 = 0x01 --> Number
Bajt nr 4 = 0x00 --> Start ID
Bajt nr 5 = 0x0A --> DDAC (nasz parametr dla mocy +5dB)


Poniżej jak fizycznie będzie wyglądała nasza prosta funkcja do ustawienia mocy w programie :

//------------------------------- SI4463_Power_TX -------------------------
void SI4463_Set_Power_TX( uint8_t power ){ 
//value power see datasheet page 36, Figure 11
uint8_t send_stream[] = { SI4463_CMD_SET_PROPERTY, 0x22, 0x01, 0x00, power };
SI4463_Send_With_CTS( send_stream, 5 );
}


a poniżej wywołanie funkcji w programie i ustawienie mocy +5dB :

SI4463_Set_Power_TX(0x0A); // 0x0A about 5 dB see datasheet page 36, Figure 11 for TX

Powyżej pokazałem jak wygląda obsługa właściwości Property modułu RF. W ten identyczny i prosty sposób możemy sobie zarządzać dowolnym Property. SI4463 jest bardzo przyjemny w obsłudze , na początku może trochę być niezrozumiały podział na Command i Property ale mgła niezrozumienia szybko opada i pozostaje przyjemność korzystania z tego dobrodziejstwa.

Jest wiele ciekawych aspektów związanych z SI4463,  takie jak choćby Maski (Mask) i Filtry (Match). Mamy tutaj 4 Maski i 4 Filtry , które możemy stosować na odbieranej ramce. Możemy tak jak w CAN adresować wiadomości czy filtrować adresy czy maski adresów. Do konfiguracji tych aspektów służy grupa Property o numerze 0x30.


Czas już kończyć ten artykuł bo za bardzo się wydłuża. Cel został z powodzeniem osiągnięty, nie miałem szczególnej trudności przeportować bibliotekę z PIC32MM na ATSAML10 . Podołałem wyzwaniu i z tego się cieszę. Ludzie nie bójcie się wyzwań , stawiajcie sobie cele wyżej niż niżej :).

Na GitHub umieszczę dwa oddzielne projekty dla IDE SEGGERA jeden dla nadawania pakietu danych złożonych z trzech pól : Pole długości przesyłanej wiadomości + Pole z danymi (dane to słowo DATA) + Pole sumy CRC (dla dwóch powyższych pól). W zmiennej globalnej "licznik" wpisujemy ilość pakietów , które zostaną wysłane. Po wysłaniu, program już nic nie robi , na LCD będzie inkremetowana zmienna "licznik" w takt każdej poprawnie wysłanej ramki. W drugim oddzielnym projekcie mamy odbiór danych . Tutaj oczekujemy na transmisję z drugiego modułu. Na LCD po każdym poprawnym odebranym pakiecie z poprawną sumą CRC zostanie inkremetowana liczba i wyświetlana treść odebranej wiadomości czyli słowo DATA. Program nie jest specjalnie ambitny ale starałem się w prosty sposób pokazać podstawowe operacje w nadawaniu i odbiorze.

Docelowym zamierzeniem będzie ogarnięcie komunikacji w układzie jeden Master - on, wiele Slave - on/off. Taka struktura komunikacji w praktyce byłaby bardzo przydatna np jeśli chcemy mieć wiele punktów pomiarowych zasilanych bateryjnie i zbierać te dane w punkcie centralnym a potem dalej to wrzucić np do chmury.

Biblioteka gotowa. Zapraszam do używania. Projekt z GitHub ściągamy lokalnie na dysk za pomocą komendy (Linux w konsoli):

git clone https://github.com/PICmajster/ATSAML10_SI4463_TX.git
drugi projekt
git clone https://github.com/PICmajster/ATSAML10_SI4463_RX.git

i dodajemy w środowisku SEGGER za pomocą opcji File --> Open Solution

W kolejnym wpisie dotyczącym SI4463 zajmę się trybem uśpienia i wybudzaniem. Jeśli chodzi o aspekt Power Saving to moduł radiowy SI4463 jest the best. Pobór na poziomie 50 nA nie jest żadnym problemem dla tego modułu. Zsumowanie Power Saving ATSAML10 i SI4463 może dać rewelacyjne osiągi na co po cichu liczę.



Linki:

SI4463 Board Module
SI4463 - datasheet 
SI4463 - strona producenta 
SI4463_TX Project on GitHub
SI4463_RX Project on GitHub
 

5 komentarzy:

  1. ważne informacje parafialne dla pic32mm jest wersja D dokumentacji http://ww1.microchip.com/downloads/en/DeviceDoc/PIC32MM0256GPM064-Family-Data-Sheet-DS60001387D.pdf
    porównajcie z wersją C , bo są spore różnice

    OdpowiedzUsuń
  2. Wiesz Witek ty to jesteś gość ja bym nawet nie zwrócił uwagi , że się literka w wydaniu dokumentacji zmieniła :)

    OdpowiedzUsuń
  3. Też na to nie wpadłem bo w większości stron MC jest wersja c , dopóki na forum mi nie odpisał gość https://www.microchip.com/forums/m1085634.aspx

    OdpowiedzUsuń
  4. Fajnie, że gość to zauważył na forum Microchipa. Udało się ten CLC uruchomić w końcu ???

    OdpowiedzUsuń
  5. na str 347 datasheet dla PIC32MM jest historia zmian jak widzę.

    OdpowiedzUsuń