[NOWY FRAMEWORK] Microchip Harmony dla PIC32, część 2
Po skonfigurowaniu taktowania możemy przystąpić do napisania pierwszego programu wykonującego jakąś czynność. Nie będzie to bardzo ambitne zadanie, bo spróbujemy zapalić diodę LED umieszczoną na module PIC32 USB Starter KIT II.
W module są umieszczone 3 diody LED połączone do linii portów RD0, RD1 i RD2, oraz 3 przyciski podłączone do linii RD6, RD7 i RD13. Jeżeli producentem mikrokontrolera, modułu ewaluacyjnego i oprogramowania jest ta sama firma, to często się zdarza, że moduły ewaluacyjne są w jakiś tam sposób wspierane. Do tego celu w MHC służy opcja konfiguracyjna BSP Configuration (rysunek 11).
Rys. 11. Konfiguracja wsparcia modułu PIC32MX USB start Kit 2
Po skonfigurowaniu BSP PIC32MX USB Starter Kit2 i wygenerowaniu plików przez MHC w projekcie pojawią się pliki bsp_sys_init.c i bsp_config.h. W pliku bsp_sys_init.c umieszczono funkcje inicjujące linie portów sterujących LED oraz funkcje sterujące diodami i czytające stany wejść podłączonych do przycisków. Można skorzystać z tych funkcji, lub użyć funkcji bibliotecznych MPLAB Harmony.
Ponieważ obsługa portów ma ścisły związek ze sprzętem, to musi być umieszczona w warstwie PLIB.
W dokumentacji Harmony można znaleźć dokładny opis wszystkich funkcji obsługi portów z warstwy Peripherial Library PLIB. W zasadzie nic nie stoi na przeszkodzie, by tych funkcji użyć i wszystko będzie działało. Ale pamiętamy, że mocno akcentowana idea programowania w tym środowisku nie zaleca stosowania funkcji PLIB przez naszą warstwę aplikacji. Ma to ścisły związek z możliwością dostępu w jednym czasie do tego samego zasobu (portów) przez różne moduły. Oczywiście w tak prostej aplikacji jak zapalenie jednej diody taki konflikt nie będzie możliwy, ale warto od początku stosować zalecane zasady.
Przed zapalenie diody LED trzeba ustawić linie RD0, RD1 i RD2 jako wyjściowe. W mikrokontrolerach Microchipa po zerowaniu wszystkie linie portów są domyślnie ustawiane jako wejściowe, a linie które są multipleksowanymi wejściami przetwornika ADC są dodatkowo ustawione jako wejścia analogowe. Do ustawiania kierunku przepływu danych jednej linii portu wykorzystywane są funkcje:
- PLIB_PORTS_PinDirectionOutputSet dla warstwy Peripherial,
- SYS_PORTS_PinDirectionSelect dla warstwy drivera urządzeń.
Ustawienie kierunku linii można wykonać w procedurze inicjalizacji aplikacji, kiedy aktywny jest stan APP_STATE_INIT maszyny stanów (listing 5).
List. 5. Inicjalizacja RD0…RD2 jako linie wyjściowe
switch (appData.state ) { /* Stan inicjalizacji aplikacji */ case APP_STATE_INIT: { /* ustawienie linii RD0 jako wyjściowej*/ SYS_PORTS_PinDirectionSelect(PORTS_ID_0, SYS_PORTS_DIRECTION_OUTPUT, PORT_CHANNEL_D ,BSP_LED_1); /* ustawienie linii RD1 jako wyjściowej*/ SYS_PORTS_PinDirectionSelect(PORTS_ID_0, SYS_PORTS_DIRECTION_OUTPUT, PORT_CHANNEL_D ,BSP_LED_2); /* ustawienie linii RD2 jako wyjściowej*/ SYS_PORTS_PinDirectionSelect(PORTS_ID_0, SYS_PORTS_DIRECTION_OUTPUT, PORT_CHANNEL_D ,BSP_LED_3); }
Argumentami funkcji SYS_PORTS_PinDirectionSelect są:
- PORTS_ID_0 identyfikacja modułu portu,
- SYS_PORTS_DIRECTION_OUTPUT – linia ustawiana jako wyjście,
- PORT_CHANNEL_D – kierunek linii portu PORTD,
- BSP_LED_1 – identyfikacja linii w porcie.
Definicje linii BSP_LED_1 jest zapisana w pliku bsp_sys.init. Funkcja ustawiania linii w bibliotece PLIB wygląda podobnie:
PLIB_PORTS_PinDirectionOutputSet(PORTS_ID_0, PORT_CHANNEL_D, PORTS_BIT_POS_1);
Teraz sobie zdefiniujemy kolejne 2 stany maszyny stanów w pliku app.h (APP_STATES) – listing 6.
List. 6. Definicja nowych stanów maszyny stanów
typedef enum { /* stany maszyny stanów warstwy aplikacji */ APP_STATE_INIT=0, /*stan inicjalizacji */ APP_LED_ON, /*stan zapalenia diody LED i */ APP_NOP, /*stan nic nie rób */ } APP_STATES;
Stan APP_LED_ON ma za zadanie wprowadzić maszynę stanów sekwencje zapalenia diody LED. Kiedy uaktywnimy stan APP_NOP to nasza aplikacja nie będzie nic robiła. W kolejnym kroku modyfikujemy funkcję APP_Tasks dodając 2 kolejne stany (listing 7).
List. 7. Zmodyfikowana funkcja APP_Tasks
void APP_Tasks (void ) { /* Testowanie stanów maszyny stanów aplikacji */ switch (appData.state ) { /* Stan inicjalizacji aplikacji */ case APP_STATE_INIT: { SYS_PORTS_PinDirectionSelect(PORTS_ID_0, SYS_PORTS_DIRECTION_OUTPUT, PORT_CHANNEL_D ,BSP_LED_1); SYS_PORTS_PinDirectionSelect(PORTS_ID_0, SYS_PORTS_DIRECTION_OUTPUT, PORT_CHANNEL_D ,BSP_LED_2); /* Po inicjalizacji stan do zapalenia LED*/ appData.state=APP_LED_ON; break; } case APP_LED_ON : { /* Zapalenie diod LED1 i LED2 */ SYS_PORTS_PinSet(PORTS_ID_0,PORT_CHANNEL_D ,BSP_LED_1); SYS_PORTS_PinSet(PORTS_ID_0,PORT_CHANNEL_D ,BSP_LED_2); /* Po zapaleniu diod LED nic nie rób */ appData.state=APP_NOP; } case APP_NOP: { /* Stan nic nie rób */ ; } default: { break; } } }
W trakcie inicjalizacji aplikacji wprowadzany jest stan APP_STATE_INIT, w którym ustawiamy linie RD0 (BSP_LED_1) i RD1 (BSP_LED_2) jako wyjściowe i na końcu ustawiamy stan APP_LED_ON. W następnym cyklu pooling’u aktywny jest stan APP_LED_ON i wykonywane jest wystawienie na liniach RD0 i RD1 stanu wysokiego przez funkcję SYS_PORTS_PinSet. Po tej czynności jest wprowadzany stan APP_NOP. Ponieważ w „obsłudze” tego stanu nie ma żadnej zmiany stanu na inny, to maszyna pozostaje w stanie APP_NOP w nieskończoność.
Pakiet Harmony zawiera wiele funkcji obsługujących układ portów, od konfiguracji kierunku przesyłania danych, poprzez ustawienia typu PULLUP, do czytania stanów linii wejściowych i sterowania liniami wyjściowymi. Dla tak prostych zadań jak na przykład wystawienie stanów na linię portu funkcja drivera portu wywołuje tylko funkcję biblioteczną układów peryferyjnych PLIB (listing 8).
List. 8. Funkcja SYS_PORTS_PinSet
bitPos ); } void SYS_PORTS_PinSet(PORTS_MODULE_ID index, PORTS_CHANNEL channel, PORTS_BIT_POS bitPos ) { PLIB_PORTS_PinSet(index, channel,
Po prostym zadaniu jakim jest zapalenia diody LED spróbujemy teraz zmodyfikować nasz program tak, aby dioda zaczęła migać. Żeby to zrealizować trzeba będzie odliczać opóźnienia. Można to zrobić programowo, ale lepiej jest wykorzystać układ licznikowy.
Mikrokontroler PI32MX795F512L ma wbudowanych 5 układów czasowo licznikowych: Timer1….Timer5. Wszystkie liczniki są 16 bitowe, ale układy Timer2 i Timer3, oraz Timer4 i Timer5 można łączyć w pary uzyskując liczniki 32 bitowe. My użyjemy 32 bitowego licznika Timer4/5. Jego schemat blokowy został pokazany na rysunku 12. Tak jak każdy układ peryferyjny Timer 4/5 musi przed użyciem zostać skonfigurowany. Żeby to zrobić programista musi sięgnąć do opisu rejestrów konfiguracyjnych SFR. W MPLAB Harmony konfiguracja Timera, ale także drivera urządzenia może być przeprowadzona z poziomu wtyczki MHC.
Rys. 12. Schemat blokowy licznika Timer4/5