STM32Butterfly2: obsługa GPIO (General Purpose Input Output) w STM32F107 oraz likwidacja drgań styków joysticka za pomocą kontrolera NVIC i timerów

Aplikacja wykorzystuje do swojego działania diody LED (2 sztuki podłączone katodami do pinów 0 i 1 portu B mikrokontrolera (?C) oraz 2 sztuki podłączone w sposób analogiczny do pinów 4 i 5 portu C ?C) i joystick (podłączony do pinów 7-11 portu C ?C) zainstalowane w zestawie ewaluacyjnym STM32Butterfly2, ale połączenia pokazane na rysunku 1 można wykonać na dowolnym innym zestawie z mikrokontrolerem STM32 (także STM32Butterfly). Joystick w chwili naciśnięcia przycisku zwiera odpowiednią linię ?C z masą sygnałową.

 

Rys. 1. Schemat elektryczny ilustrujący podłączenie peryferii do mikrokontrolera STM32F107 (fragment schematu elektrycznego zestawu STM32Butterfly2)

Rys. 1. Schemat elektryczny ilustrujący podłączenie peryferii do mikrokontrolera STM32F107 (fragment schematu elektrycznego zestawu STM32Butterfly2)

 

Założenia aplikacji

  1. Naciśnięcie joysticku do góry wywołuje zaświecenie wszystkich LED-ów.
  2. Naciśnięcie joysticku w dół wywołuje wygaszenie wszystkich LED-ów.
  3. Jednokrotne naciśnięcie joysticku w prawo powoduje zaświecenie kolejnej diody.
  4. Jednokrotne naciśnięcie joysticku w lewo powoduje wygaszenie ostatnio zaświeconej diody.
  5. Przytrzymanie joysticku w lewo lub w prawo powtarza akcję przeznaczoną dla tego przycisku z okresem równym około 1 s.

Schemat funkcjonalny programu

Na rysunku 2 znajduje się schemat funkcjonalny omawianej aplikacji. Naszą analizę rozpoczniemy od przeanalizowania konfiguracji portów (element 2 na schemacie). W kolejnym punkcie zajmiemy się elementami (7-9). Ostatnim elementem będzie omówienie funkcji odpowiedzialnych za eliminowanie zjawiska drgania styków i uruchomienie układów czasowych i przerwań ?C.

 

Rys. 2. Algorytm działania przykładowej aplikacji

Rys. 2. Algorytm działania przykładowej aplikacji

 

Opis programu

Konfiguracja portów GPIO

Do konfiguracji portów wejścia wyjścia wykorzystywana jest funkcja StartFunctions_GPIO_Conf() (listing 1) znajdująca się w pliku StartFunctions.c. Jest ona wywoływana w funkcji main przed wejściem programu w pętlę nieskończoną.
Funkcja rozpoczyna swoje działanie od inicjalizacji sygnału zegarowego dla portu B mikrokontrolera, w następnej linii następuje deklaracja struktury GPIO_InitStructure, która jest typu GPIO_InitTypeDef. W Liniach 78-81 następuje wypełnienie struktury wartościami pozwalającymi na inicjalizację pinów 0 i 1 portu B do pracy, jako wyjścia push-pull z częstotliwością 50 MHz. Linia 81 odpowiada za przesłanie struktury dotyczącej konfiguracji portu oraz oznaczenia portu do funkcji GPIO_Init(), która jest odpowiedzialna za inicjalizację portu na podstawie przekazanych danych.
W liniach 84-89 jest inicjalizowana część portu C odpowiedzialna za obsługę joysticka. Ponieważ joystick w stanie spoczynku wystawia na port mikrokontrolera logiczną „1” a w stanie zadziałania logiczne „0” to te piny portu C ustawiamy, jako wejście bez jakiegokolwiek wewnętrznego podciągnięcia. W obrębie linii 92-95 następuje inicjalizacja dwóch pinów odpowiedzialnych za diody podpięte do portu C.

 

List. 1. Listing funkcji StartFunctions_GPIO_Conf()

 

Sterowanie diodami

Do sterowania diodami wykorzystano zmienną LED_register, na której czterech najmłodszych bitach odzwierciedlany jest stan LED-ów (dla wszystkich zapalonych 1111B dla dwóch zapalonych 0011B itd.). Omawiany w tym punkcie kod programu znajduje się na listingu 2. W linii 56 sprawdzany jest stan wyprowadzenia 9 portu C (jeśli joystick jest skierowany do góry funkcja GPIO_ReadInputDataBit zwróci 0, po negacji otrzymujemy jedynkę logiczną co powoduje wywołanie kodu za funkcją if -wprowadzenie do zmiennej licznik samych zer) ?C. Analogicznie w linii 57 sprawdzany jest stan wyprowadzenia 8 portu C (gdy joystick skierowany jest w górę zmienna licznik wypełniana jest na pierwszych czterech bitach jedynkami).

 

List. 2. Fragment programu odpowiadający za sterowanie LED

 

Za faktyczne ustawienie stanu LED-ów odpowiada funkcja GPIO_WriteBit, jest ona wywołana 4 krotnie (dla każdej diody). Jako pierwszy argument przyjmuje port którego bit ma być ustawiony, jako drugi – pin, który ma być ustawiony, trzecim argumentem jest stan logiczny który ma być zapisany do danego portu.
W celu otrzymania stanu logicznego odpowiadającego interesującej nas diodzie maskujemy zmienną licznik jedynką na interesującej nas pozycji (np. licznik&0x04 spowoduje ustawienie wszystkich oprócz trzeciego bitu (0x04=0100B) na wartość równą 0) a następnie przesuwamy interesujący nas stan logiczny na pozycję najmniej znaczącego bitu. Jako ostatni element następuje rzutowanej zmiennej licznik (typu uint16_t) na zmienną typu BitAction.

Drgania styków

Niestety, nie jesteśmy w stanie w analogiczny sposób zrealizować załączania pojedynczych LED-ów (wszystkie diody zapaliły by się jednocześnie, ponieważ bezwładność ręki ludzkiej jest dużo większa niż czas, który jest potrzebny procesorowi na sprawdzenie stanu wejścia. W trakcie jednego kliknięcia stan pinu zostałby sprawdzony kilkukrotnie). Innym rozwiązaniem naszego problemu mogło by być wprowadzenie opóźnienia w pętli głównej programu zaraz po sprawdzeniu stanu wyprowadzenia, prowadzi to jednak do zatrzymania wszelkich obliczeń wykonywanych przez procesor na ten czas. Kolejnym rozwiązaniem wydawałoby się zgłaszanie przerwania od portu zewnętrznego i na podstawie tego przerwania zmiana stanu określonej LED. Jest to także rozwiązanie niewystarczające, ponieważ w trakcie jednego naciśnięcia styku sygnał podawany na p

in ?C zmienia się kilkukrotnie w bardzo krótkim okresie czasu. Rozwiązaniem powszechnie stosowanym jest wykorzystanie przerwania połączonego z timerem, na potrzeby tego przykładu układ ten urozmaicono o układ drugiego timera. Diagram zaproponowanego rozwiązania znajduje się na rysunku 3.

 

Rys. 3. Sposób likwidacji drgań styków joysticka

Rys. 3. Sposób likwidacji drgań styków joysticka

 

W funkcji obsługi przerwania zewnętrznego (listing 3) następuje wyczyszczenie flagi informującej o linii, z której nadeszło przerwanie (Line10 odpowiada przyciskowi w prawo, Line11 przyciskowi w lewo). Następnie ustawienie szesnastobitowego rejestru CC timera2 na wartość 0xFFFF, wyzerowanie licznika (linia 48 i 56) wystartowanie układu licznikowego(linia 49 i 57) i uruchomienie przerwań od tego układu (linie50 i 58).

 

List. 3. Funkcja obsługi przerwania zewnętrznego

 

W funkcji obsługi przerwania od timera2 (listing 4) następuje sprawdzenie czy przerwanie nadeszło od układu Capture Compare odpowiedzialnego za zliczanie impulsów zegarowych (linia 9), następnie czyszczenie flagi i wyłączenie przerwań od timera2 oraz samego timera2 (linie 11-13). Następnie następuje sprawdzenie stanu przycisku, jeśli nadal naciśnięty jest przycisk w lewo zmienna Left_click ustawiana jest na 1, jeśli naciśnięty jest przycisk w prawo zmienna Right_click jest ustawiana na „1”. Następnie startowany jest timer3 (analogicznie do timera 2 tylko ze znacznie dłuższym czasem – około 1 s).
Funkcja obsługi timera3 wyłącza timer3 i włącza timer2. Jeśli przycisk nadal jest wciśnięty po nadejściu przerwania od timera2 zostanie zapalona kolejna dioda.

 

List. 4. Funkcja obsługi przerwania od timera2

 

Część kodu odpowiadająca za uzupełnianie zmiennej LED_register w wypadku naciśnięcia przycisku w lewo lub w prawo jest przedstawiona na listingu 5.

 

List. 5. Fragment programu odpowiadający za uzupełnianie zmiennej LED_register w przypadku naciśnięcia przycisku w lewo lub w prawo

 

Jeśli zmienna Left_click ma wartość 1 (została ustawiona przez timer2) i LED_register nie jest przepełniony następuje przesunięcie bitów o 1 w lewo i dodanie jedynki na pozycji reprezentującej najmniej znaczący bit. Analogicznie, jeśli nastąpiło kliknięcie w prawo następuje sprawdzenie czy LED_register jest większy niż zero i przesunięcie bitów licznika w prawo.
Jan Rachoń
Jan.Rachon@gmail.com

Do pobrania

Autor: