wtorek, 10 września 2019

SAM L10/L11 External Interrupt Controller (EIC)

Artykuł jest potrzebą chwili i powstał pod wpływem pisania biblioteki dla modułu radiowego SI4463. Podczas pisania biblioteki zaszła potrzeba użycia przerwania sprzętowego na pinie PA03 , który współpracuje z pinem IRQ modułu radiiowego RF. Niby sprawa trywialna ot wyczarujmy sobie przerwanie no ale kiedy poznajemy jakiś MCU i pierwszy raz stykamy się z jego modułem peryferyjnym  opierając się tylko na dokumentacji to takie zadanie może stać się wyzwaniem :) . W artykule pokażę  jak wyglądała moja droga do rozpoznania i w efekcie konfiguracji przerwania sprzętowego z wykorzystaniem modułu peryferyjnego EIC (External Interrupt Controller)

Przechodzę bez zbędnego pisania do konkretów czyli do konfiguracji przerwania w ATSAML10 . Sam jeszcze nie wiem jak to zrobić bo też jestem w trybie nauki, nawet pisząc ten artykuł . Spróbujmy przejść przez to razem. Potrzebujemy na pinie PA03 (podłączonym do IRQ modułu RF) zrealizować funkcję przerwania reagującego na zmianę stanu z H na L . Mając na uwadze , że moduł RF wystawia stan niski jeśli zajdzie jakiś event w module.

Pierwszą czynnością będzie zlustrowanie dokumentacji ,a konkretnie datasheet ATSAML10E16A jest to jedyny dokument jakim będę się posługiwać. W ATSAM nie ma manuala tak jak u innych producentów MCU z rdzeniem ARM. I to jest zaleta. Zaczynam od tabelki z której zorientuję się co mogę na pinie PA03 zrobić w zakresie przerwań.



Z tabelki widzimy , że pin PA03 możemy podpiąć do modułu EIC (External Interrupt Controller). Dodatkowe informację jakie musimy zapamietać z tabelki to literka kolumny A w której jest moduł EIC oraz EXTINT[3] przyporządkowany do naszego pinu PA03. Na razie nie czaimy bazy ale idziemy dalej. Na pewno będziemy musieli włączyć jakiś zegar dla modułu EIC. Znajduję w rozdziale o EIC w datasheet poniższą informację :



Stąd wiemy , że zegar dla szyny na której jest moduł EIC nazywa się CLK_EIC_APB i to jest szyna odchodząca od modułu MCLK , patrz poniższy rysunek :



Ten zegar musi być włączony ale z tabelki poniże wynika , że jest tak na starcie :



Więc nic tutaj nie trzeba robić. Ale , żeby nie było tak łatwo zostaje podłączenie modułu EIC do modułu zegara GCLK (Generic Clock Controller). Podeprę się tutaj obrazkiem z ustawienia jakie sobie na szybko poczyniłem celem zobrazowania zegara w aplikacji webowej ATMEL START :



Z obrazka powyższego wynika , że dla uproszczenia podepniemy się do Generatora nr 0 kanałem z EIC. Metodologia jest identyczna jak w przypadku każdego peryferium w tym SERCOM. Więc z tego punktu widzenia nie ma tutaj nic nowego, jest ten sam identyczny mechanizm.

Aby dokonać połączenia Generatora nr 0 kanałem z EIC. Musimy zajrzeć w rejestr PCHCTRLm bo za pomocą tego rejestru połączymy EIC z Generatorem nr 0 (moduł GCLK). Z tabelki poniżej wyznaczam indeks m dla rejestru PCHCTRLm :



Widzimy, że dla EIC numerek m to 3. Możemy zatem utworzyć pierwsze wpisy w rejestry aby skonfigurować zegar dla modułu EIC.

void Clock_EIC_init(void){
/*Start EIC Clock configuration*/
/*PCHCTRL3 --> GEN0 --> 0x0   / connect EIC to Generator nr 0*/
GCLK->PCHCTRL[3].bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val;
/*PCHCTRL3 --> CHEN --> 1  / switch on canal for clock EIC*/
GCLK->PCHCTRL[3].bit.CHEN = 1;

}

I to jest w/g mnie kompletna funkcja realizująca podpięcie modułu EIC do modułu zegara GCLK. Tak na marginesie w PIC32 Microchipa nie trzeba się pieprzyć z podpinaniem peryferiów do zegara. Tu taki kulawy z punktu widzenia użytkownika aspekt zegarowy jest domeną rdzeni ARM. W ATSAM-ach i tak jest kulturalnie bo przynajmniej szyna modułu MCLK jest na starcie zapięta do peryferiów, więc jeden problem mamy mniej.

No dobrze teraz musimy odpiąć się od modułu GPIO i podłączyć do modułu MUX, który jest pośrednikiem w połączeniu pin(pad) - peryferium. Patrz rysunek poniżej :



Zapis w rejestr jakim się posłużymy wygląda jak poniżej :

/*PORTA --> PINCFG3 --> PMUXEN --> 1 / connect PA03 to the module PMUX*/
PORT->Group[0].PINCFG[3].bit.PMUXEN = 1;


Group[0] - oznacza odwołanie do PORTA.

Teraz pozostało już tylko podpiąć się pinem PA03 do peryferium EIC. Robimy to poniższym zapisem :

/*PORTA --> PMUX1 --> PMUXO --> 0x0 / connect PA03 (odd pin 2*n+1) to the module EIC (put A)*/
/*Pin  3 = 2*n+1 stąd n = (3-1)/2 = 1 */
PORT->Group[0].PMUX[1].bit.PMUXO = 0x0;

Zaglądamy do fragmentu rejestru PMUX dotyczącego pinów nieparzystych PMUXO (pin PA03 jest nieparzysty):



Do rejestru PMUX bit PMUXO wpisujemy wartość 0x0 co oznacza literkę A z tabelki na początku rozważań o EIC.

Na tym koniec konfiguracji pinu PA03 i zegara dla modułu EIC. Przed nami aspekty związane z konfiguracją samego modułu EIC czyli takie sprawy jak reakcja na zmianę zbocza z H na L ,ewentualnie inne czynności jak włączenie przerwania , zerowanie flag i ciort wie co jeszcze .

Jakimś punktem zaczepienia będzie obrazek jak poniżej :



Widzimy tutaj jakieś filtry i cuda wianki na kiju , czas zatem popatrzeć sobie na spis rejestrów modułu EIC i przeczytać choćby ten rozdział.

Jestem po lekturze rozdziału o EIC. Z literatury dowiadujemy się , że sygnał wchodzący na pin, użyty z modułem EIC może być filtrowany i poddany mechanizmowi Debouncing-u . Na myśl przychodzą drgania styków i cały cyrk z filtrowaniem tego a tu proszę , mechanizm gotowy w pakiecie. No ale zakładam, że moduł radiowy RF to nie przycisk mechaniczny , więc na razie z tego dobrodziejstwa dla uproszczenia konfiguracji nie skorzystam.

Ustawiam reakcję przerwania na zmianę zbocza H na L. Rejestr w jakim to zrobię to CONFIG i pole bitowe SENSEx. Domyślam się tutaj, że x to wartość z nawiasu EXTINT[3], którą odczytaliśmy z tabelki na początku wątku o EIC. Co mamy wpisać w pole bitowe SENSE3 wynika z tabelki poniżej :



i jest to wartość 0x2 (FALL). Stwórzmy , więc fizyczny zapis do rejestru ,a skąd wiem jak to zrobić ? ano posłużyłem się genialnymi podpowiedziami środowiska SEGGER.

REG_EIC_CONFIG0 |= EIC_CONFIG_SENSE3_FALL;

W powyższy sposób , który jest intuicyjnie czytelny możemy dokonać wpisu w dowolny rejestr naszego ATSAM-ika. Brawo TY czyli ATSAM-ie. Aby uczcić tę chwilę zagryzam ptasim mleczkiem firmy WEDEL (made in Japan, firma LOTTE) w ilości sztuk 2 i dochodzę do wniosku, że życie jest zbyt krótkie aby marnować je jedząc tak chemiczny w składzie i gówniany w wydaniu Japończyków produkt. Prawdziwy nasz Polski WEDEL już dawno nie istnieje a wraz z nim umarło ptasie mleczko.

Wracamy do tematu. Mamy już ustawiony rodzaj reakcji na zmianę zbocza. Teraz rozglądamy się za czymś co włączy nam przerwania i za jakimś rejestrem czyszczącym flagę przerwania. Ostatnią czynnością będzie wykontycypowanie obrazu funkcji przerwania a w szczególności jej nazwy .

Szybki rzut okiem na tabelkę z rejestrami modułu EIC i wszystko już prawie wiemy. Dużą zaletą ATSAM-ów jest czytelność nazw rejestrów. Można szybko zaczaić o co kaman. Zatem włączenia modułu EIC realizujemy w rejestrze CTRLA bit ENABLE.



Fizyczny wpis do rejestru wyglądać będzie jak poniżej :


REG_EIC_CTRLA |= EIC_CTRLA_ENABLE;


Tak przy okazji jak chcemy wyłączyć moduł EIC i zresetować wszystkie jego rejestry to komenda jak poniżej :

REG_EIC_CTRLA |= EIC_CTRLA_SWRST; //Software Reset

Rejestr za pomocą , którego możemy kasować flagę przerwania i jednocześnie sprawdzać jej status to INTFLAG (Interrupt Flag Status and Clear). Kasowanie flagi wygląda następująco :

EIC->INTFLAG.reg |= EIC_INTFLAG_EXTINT(1<<3); //Clear EIC Flag EXTINT[3] (PA03)

Fizycznie operacja jaka jest tutaj wykonywana to nałożenie maski (0xFF) na rejestr tak aby nie zmieniać innych jego wartości i pomnożenie tej maski przez wynik przesunięcia (1<<3) bo chcemy zmienić tylko trzeci bit  w rejestrze. Te wszystkie operacje/makra/definicje zdefiniowane są w pliku dostarczanym przez producenta eic.h i stamtąd można czerpać wiedzę jak wyglądają wpisy w konkretny rejestr modułu EIC.

Jeszcze jedna sprawa aby nie umknęło. Oprócz włączenia samego modułu EIC potrzebna jest jeszcze operacja włączenia przerwania od konkretnej linii w naszym przypadku EXTINT[3]. Robimy to poniższym wpisem :

EIC->INTENSET.reg = (EIC_INTENSET_EXTINT((1<<3))); //Enable interrupt on EXTINT[3] (PA03)

Ponieważ powyższy wpis jest atomowy o czym świadczy końcówka SET dlatego przed nawiasem nie ma potrzeby stawiania sumy logicznej | .

I tak mi się wydaje, że doszedłem do końca konfiguracji modułu EIC. Pozostała jeszcze kwestia włączenia przerwania w module NVIC czyli module związanym z Core MCU i obsługującym przerwania . W każdym ARM-ie NVIC jest taki sam i posługuje się tymi samymi prawami. Zatem włączenie przerwania z punktu widzenia NVIC-a wygląda jak poniżej :

NVIC_EnableIRQ(EIC_3_IRQn); // Enable Interrupt on EIC module for EXTINT[3]

Jeśli ktoś przebrnął razem ze mną przez powyższą drogę i nie odczuł uszczerbku na psychice to wyrazy szacunku :) . Aż się chce krzyknąć ludzie dajcie mi 8-bitowca lub PIC32 abym nie zwariował :)

Na tym etapie jeszcze nie mam pojęcia czy moje dociekania w zakresie konfiguracji zadziałają.

Ostatnią rzeczą będzie poszukanie nazwy dla funkcji przerwania, posłużymy się tutaj dostępnymi nazwami handlerów. Ale to już będzie proste.

Nazw funkcji przerwań od peryferiów znajdziemy w pliku o nazwie ATSAML10E16A_Vectors. Plik jest w skrajnie prawym okienku Contents w katalogu System Files lub w katalogu o tej samej nazwie w naszym projekcie.


Na końcu tego pliku mamy Vector Table. Tam szukamy nazwy powiązanej z peryferium , które nas interesuje . Widzimy tutaj najbardziej pasującą nazwę : EIC_3_Handler. I to jest nazwa funkcji przerwania jaką zastosujemy w programie . Funkcja obsługi przerwania w naszym przypadku wygląda zatem jak poniżej :

/* Routime Interrupt */
void EIC_3_Handler(void){
    LED_TOG;

    /* Clear EIC Flag EXTINT[3] (pin PA03)*/
    EIC->INTFLAG.reg |= EIC_INTFLAG_EXTINT(1<<3);   
}

Jeszcze jedna sprawa o której należy wspomnieć. Można się poczuć zagubionym w pewnym aspekcie. Mianowicie , do linii lXTINT[3] , dołączony jest nie tylko pin PA03 ale też PA14 i PA24. Jak mamy rozpoznać od którego pinu konkretnie przyszło fizycznie przerwanie jeśli użyjemy tych trzech pinów na raz w projekcie dołączonych do wspólnej linii EXTINT[3] ?. W rejestrach modułu EIC nie znalazłem odpowiedzi na to nurtujące mnie pytanie. Sposób na jaki wpadłem aby to zrobić to podczas wywołania funkcji przerwania sprawdzić fizyczny stan pinów . Na przykład jeśli pin PA03 mamy ustawiony na reakcję na zmianę z High na Low to jeśli od tego pinu nastąpi wywołanie przerwania to na wejściu PA03 odczytamy wartość Low. Prosty i genialny sposób jak sobie poradzić jeśli producent nie przewidział obsługi takiego aspektu. Odczyt stanu pinu robimy np tak : REG_PORT_IN0 & PORT_PA03. A tak jeszcze w temacie samej konfiguracji pinu to nie musimy go ustawiać jako wejście bo zrobi to za nas moduł EIC.

I to by było na tyle w temacie. Przerwanie zaskoczyło od pierwszego kopa, aż sam byłem zaskoczony . Ale taki efekt jest jeśli przyłożymy się porządnie do zagadnienia a nie potraktujemy go po łebkach. Narobiłem się jak głupi osioł ale efekty pracy przyćmiewają wszelkie niedogodności :)

Ciekawi mnie zagadnienie możliwości sprzętowego Debouncing w ATSAM-ach,  jak by to współpracowało z mechanicznymi przyciskami ale to już temat na inny raz.

Na koniec obraz kompletnej funkcji inicjalizującej moduł EIC w środowisku SEGGER-a. Nie wspominam już o tym, że nie korzystam z ASF tylko rozmawiam z MCU za pomocą czystych wpisów w rejestry.



Jeszcze funkcja konfigurująca zegar dla modułu EIC :


A poniżej kod z konfiguracją pinu PA03 aby przysposobić go do współpracy z modułem EIC :

/*PORTA --> PINCFG3 --> PMUXEN --> 1 / connect PA03 to the module PMUX*/
PORT->Group[0].PINCFG[3].bit.PMUXEN = 1;

/*PORTA --> PMUX1 --> PMUXO --> 0x0 / connect PA03 (odd pin 2*n+1) to the module EIC (put A)*/
/*Pin  3 = 2*n+1 stąd n = (3-1)/2 = 1 */
PORT->Group[0].PMUX[1].bit.PMUXO = 0x0;

Podsumowując jestem zadowolony z efektów swojej pracy, cieszę się , że przechodząc trochę mozolną drogę, mój mózg pracował na wysokich obrotach i dzięki temu przybyło mi zapewne kilka nowych połączeń neuronowych :). Gdybym poszedł na łatwiznę i skorzystał z kobyły ASF ,nie uzyskałbym takiego efektu a i satysfakcja byłaby znacznie mniejsza. Poza tym poznając mechanizmy działania MCU od strony rejestrów nabywamy wartościowej wiedzy , która przyda się w każdym przypadku przechodząc np. na inne konstrukcje MCU np. innych producentów. Posługiwanie się bibliotekami typu ASF , HAL jest wygodne , ale kompletnie nas nie rozwija. Zachęcam do grzebania w rejestrach bo to kopalnia wartościowej wiedzy i dobry sposób na udane życie :)

Pozdrawiam
picmajster.blog@gmail.com




Linki :

ATSAML10E16A - datasheet 
EIC - strona dla developerów

Brak komentarzy:

Prześlij komentarz