Lista parametrów, jakie przyjmuje fbnkcja Ważne aics*. jedynie wymienienie ich typów
wskaźnik na funkcję
Mimo tego. że już kolejny raz posłużymy się wskaźnikami, nie przedstawiam jeszcze pełne go opisu zagadnienia. Omówienie wskaźników nie jest jednak konieczne przy sposobie, w jaki z nich korzystamy. Teraz chce jedynie, żebyś oswoił się z samą ideą zmiennej, która po prostu przechowuje adres, pod którym w pamięci znajduje się inna zmienna lub, co ciekawe, także funkcja. Wskaźnik na funkcję od strony czysto maszynowej jest 16-bitową liczbą przechowującą adres, pod jakim znajduje się dana procedura Tworząc taki wskaźnik, musimy poinformować kompilator, jakie parametry musi poumieszczać w rejestrach przed wykonaniem skoku w odpowiednie miejsce.
Zmienna typu wskaźnikowego jest deklarowana przez dodanie znaku gwiazdki między jej nazwą a typem. Na przykład: int* wskaznik_na_int J Wskaźnik na funkcję tworzymy według następującego przepisu:
Typ zwracanej przez funkcję wanofci ■—|
SJazwB wskaźnika. Jest to nazwa tworzonej zmiennej. Nie ma nic wspólnego z nazwą ńiakcji
Przypisanie takiemu wskaźnikowi odpow iedniej wartości jest dziecinnie proste. W :ym celu posługujemy się nazwą wybranej funkcji. Na przykład:
VO! d(*proc)(uintl 6_~); proc = WyswietlDEC;
Funkcja mus: mieć identyczny zestaw' parametrów oraz identyczny typ zwracanej wartości. Inaczej kompilator zgłosi błąd.
Jeśli już przypisaliśmy takiemu wskaźnikowi odpowiednią wartość, wywołanie wskazywanej przez niego funkcji jest intuicyjne i proste: VOld(*proc) (uir.tl 6_t); proc = WyswietlDEC; proc(lOO);
Uważaj. Przy tworzeniu wskaźnika na funkcję wszystkie tc „nadużyte” nawiasy są konieczne. C hyl już za to krytykow any. Zabawność sytuacji polega na tym, że 1nt *funkcja(vo1d) to funkcja zwracająca wskaźnik do int, z kolei: int (*funkcja)(void) to wskaźnik na funkcję zwracającą int.
Listing 18
Funkcje odpowiedzi na naciśnięcie przycisku.
void pius(void)
{
-H-g__Licznik J WyswietlDEC(g_Licznik);
>
void Minus(void)
{
—g_Licznik;
WyswietlDEC(g Licznik);
>
Listing 19
Modyfikacja pętli głównej.
WyswietlDEC(g_Licznik);
f°r(;;)
{
OfcslozPrzycisk(l«SWl, Plus); 0fcslczPrzyciskCl«SW2, Minus);
}
- H t K L A M A -
' JSmm
c ELECTRONICS j
SPltZKUAŻ CZI2ŚCI elektuonh:znyi:u
Hurt:
01- 985 Warszawa, ul. Dzicrżoniowska 9A tal O 22 RM 30 60. fa*: Ol..22 865 30 50 DETAL:
02- 585 Warszawa, Al. Niepodległości 84 id.: 0...22 844 44 22. lcl./fax: 0...22 844 09 92 02-620 Warszawa, ul. Puławska 132 td7fox: O. .22 848 44 95. id.: 0...22 844 44 43 40-032 Katowice, ul. Dąbrowskiego 1
td.: 0...32 251 24 25. lel/fa*: 0..32 251 58 44
jak to ma miejsce w wygodnej procedurze DEBOUNCE, umożliwić przesłanie informacji, jaką funkcję należy wywołać przy naciśnięciu klawisza, konieczne będzie poznanie informacji na temat wskaźników na funkcje. Odpowiednie informacje znajdziesz w ostatniej ramce ABC... C. W aktualnym kodzie możesz całkowicie usunąć zawartość pętli głównej programu - przybierze zupełnie inną postać. Na listingu 17 przedstawiam funkcję, która zajmie się obsługą naciśnięcia i przytrzymania przycisku. Zauważ, że jest ona ograniczona o tyle, że wszystkie przyciski muszą znajdować się na jednym wybranym porcie - tak jest u nas w istocie. Umożliwia za to wybranie dowolnego wyprowadzenia danego portu. Podana jako parametr funkcja będzie wywoływana raz, gdy przycisk zostanie naciśnięty, a następnie za każdym razem gdy „będzie powtarzany” przy przytrzymaniu.
Zauważ, że we wnętrzu wykorzystałem pętlę do-while, zamiast while. W tym przypadku, od teoretycznej strony, nie ma to znaczenia, jednak okazuje się, że przy takiej składni program zajął nieco mniej miejsca w pamięci.
Zajmijmy się teraz napisaniem funkcji odpowiedzialnych za odpowiedź na naciśnięcie klawisza pierwszego i drugiego. Pokazuje je listing 18 i myślę, że ich działanie nie wymaga komentarza. Pamiętaj jedynie, aby umieścić ich definicje powyżej miejsca wykorzystania - inaczej konieczna będzie ich deklaracja. W tym przypadku umieszczamy je powyżej funkcji main. Nie ma znaczenia, żc naprawdę wywoływać je będziemy z poziomu funkcji ObslozPrrycisk. Funkcja ta odwołuje się do swojego zdefiniowanego wskaźnika funkcji, zupełnie nie interesując się tym, do czego on się z kolei odwołuje.
Mamy już wszystkie potrzebne elementy. Możemy połączyć je w całość, co oczywiście odbędzie się w pętli głównej. Zmianę pokazuje listing 19. Zauważ, że przy takiej konstrukcji funkcja WyswietlDEC jest wywoływana tylko wtedy, gdy jest to konieczne, nie tak jak poprzednio, gdzie jej wywołania były nadmiarowe. Ponadto, po kompilacji przekonasz się, że program zajmuje mniej miejsca w pamięci programu. Udało się to dzięki temu, że usunęliśmy, wspomniany wcześniej, powtarzający się fragment kodu.
Dziś zapoznaliśmy się z pętlami, zmiennymi lokalnymi, zaczęliśmy obsługiwać klawiaturę, co wiązało się z odczytem danych z portów' mikrokontrolera. Poznałeś także sposób wykorzystania wskaźników na funkcje, co z reguły jest uznawane za zagadnienie dla zaawansowanych - świat mikrokontrolerów jednak rządzi się innymi zasadami niż świat dużych maszyn typu PC i tutaj będziemy korzystać z tej przydatnej możliwości dość często, czasem nawet w prostych programach.
Zgodnie z obietnicą zamykamy już wyświetlacz multipleksowany. W następnej części zajmiemy się prostą obsługą wyświetlacza alfanumerycznego LCD. Poznamy makra oraz, koniecznie, zajmiemy się obsługą łańcuchów znakowych czyli napisów. Będzie to ostatnia część kursu prowadzona z wykorzystaniem BASCOM-owej płytki testowej. W przygotowaniu jest już sterownik który da nam znacznie większe możliwości.
Radosław Koppel
Sprzedaż wysyłkowo PEŁNA OFERTA W NTERNICtE
wwwMnrnmk.comjA, słown*#*łwn*.<om.pj
46 Wrzesień 2005 Elektronika dla Wszystkich