W O J S K O W A A K A D E M I A T E C H N I C Z N A im. Jarosława Dąbrowskiego ZAKŁAD AWIONIKI I UZBROJENIA LOTNICZEGO Przedmiot: MIKROELEKTRONIKA SAMOCHODOWA Ćwiczenie laboratoryjne: Badanie układów wejścia-wyjścia systemu mikroprocesorowego wprowadzanie danych przy użyciu klawiatury WARSZAWA 2008
1. Cel ćwiczenia Celem ćwiczenia jest zapoznanie studentów z budową, zasadą działania i obsługą programową portów wejścia-wyjścia, jako podstawowych układów peryferyjnych mikrokontrolerów. 2. Opis zestawu uruchomieniowego ZL2AVR W skład zestawu uruchomieniowego ZL2AVR czyli systemu mikroprocesorowego z mikrokontrolerem ATmega8 wchodzą następujące elementy składowe: Lp. produkt zdjęcie 1 Zestaw uruchomieniowy z mikrokontrolerem AVR ATmega8 (ZL2AVR) 2 Programator ISP dla mikrokontrolerów AVR (ZL2PRG) 3 Alfanumeryczny wyświetlacz LCD 2x16 4 Zasilacz niestabilizowany 3...12V 500mA Charakterystyka głównych elementów zestawu: 1. Zestaw uruchomieniowy ZL2AVR. Pozwala na opracowywanie różnorodnych projektów, które można przetestować zanim zostanie dla nich zaprojektowana płytka drukowana. Możliwe jest również projektowanie specjalizowanych sterowników mikroprocesorowych zbudowanych z wykorzystaniem zestawu ZL2AVR. 2. Programator ZL2PRG dla mikrokontrolerów AVR. Programator jest wyposażony w interfejs umożliwiający programowanie pamięci już po zamontowaniu w systemie (ISP). Zbudowany jest w oparciu o dwustronną płytkę drukowaną, a jego atutem jest możliwość współpracy z wieloma bezpłatnymi programami sterującymi jego pracą, m.in. programator obsługiwany jest przez oprogramowanie Bascom AVR. 3. Alfanumeryczny wyświetlacz LCD. Jego zastosowanie umożliwia prezentację danych zobrazowujących wyniki działania opracowywanego oprogramowania.
4. Zasilacz niestabilizowany 3..12V 500mA. Zasilacz umożliwia ustawienie napięcia wyjściowego za pomocą obrotowego przełącznika. Schemat ideowy układu uruchomieniowego ZL2AVR przedstawia rys. 1. Rys.1. Schemat ideowy układu uruchomieniowego ZL2AVR Ze schematu wynika, że w układzie uruchomieniowym są dostępne elementy, z których można utworzyć wiele różnych układów mikroprocesorowych, bez konieczności lutowania. Charakterystyka elementów znajdujących się na układzie zamieszczona jest poniżej: 1. Mikrokontroler ATmega8 (U 1 ) może być taktowany wewnętrznym sygnałem zegarowym lub z zastosowaniem zewnętrznego rezonatora kwarcowego X1, dołączonego przez zworki JP2, JP3. Wszystkie wyprowadzenia mikrokontrolera są dostępne na podwójnych końcówkach. Elementy L1, C3, C4, tworzą filtr napięcia zasilającego wewnętrzny przetwornik analogowo cyfrowy A/C. 2. S5, R24, C5, JP1 zapewniają zerowanie mikrokontrolera. Zerowanie można przeprowadzić przyciskiem S5. Zwora JP1 umożliwia dołączenie zewnętrznego obwodu zerującego do mikrokontrolera. W mikrokontrolerze linia zerowania RST może być wykorzystana jako zwyczajna linia I/0 portu C. 3. Złącze Z7 jest to złącze programatora ISP,wraz ze złączem Z2 służy do podłączania programatora. 4. Wyświetlacz LCD o organizacji 2x16 znaków umożliwia prezentacje danych zobrazowujących wyniki działania opracowywanego oprogramowania. 5. Cztery wyświetlacze siedmiosegmentowe LED ze wspólną anodą przeznaczone są do prezentowania danych liczbowych.
6. Interfejs szeregowy RS232 zrealizowany z użyciem konwertera poziomów MAX232 umożliwia komunikację z komputerem PC oraz innymi układami mikroprocesorowymi. 7. Konwerter I 2 C na 8-bitowy port I/0 zrealizowany na układzie PCF8574 może pracować zarówno z liniami wejściowymi, jak i wyjściowymi, również w trybie mieszanym. Adresy konwertera są ustalone na stałe. Umożliwia wykonywanie eksperymentów z wykorzystaniem interfejsu I 2 C oraz zwiększa liczbę portów o dodatkowy port 8-bitowy. 8. Złącza Z5, Z6 (I 2 C) umożliwiają dołączenie do układu zewnętrznych urządzeń sterowanych magistralą I 2 C. Rezystory R27 i R28 są konieczne do poprawnej pracy magistrali. 9. Port wyjściowy dużej mocy zbudowany jest z wykorzystaniem układu ULN2803A. Umożliwia sterowanie urządzeniami pobierającymi znaczny prąd. 10. Diody sygnalizacyjne LED (D1...D8) umożliwiają sygnalizację zdarzeń stwierdzonych przez zaprojektowany układ mikroprocesorowy. Rezystory R1 R8 ograniczają prądy płynące przez diody LED. 11. Złącze Z10 (1-Wire) umożliwia dołączanie elementów sterowanych za pomocą interfejsu 1-Wire. 12. Odbiornik transmisji danych w podczerwieni U7 umożliwia sterowanie układem mikroprocesorowym za pomocą pilota. 13. Złącze Z11 (Servo) służy do dołączania mechanizmu modelarskiego. 14. Przyciski ogólnego przeznaczenia S1...S4 w zależności od wartości napięcia podawanego na wejścia portów mikrokontrolera będą po ich przyciśnięciu podawać poziom niski, bądź wysoki. 15. Złącze Z1 umożliwia dołączenie do płytki klawiatury PC ze złączem PS2. 16. Złącza Z4, Z8, Z9 (AUX) na złącza te zostały wyprowadzone napięcia zasilające płytkę oraz linie zasilające A1-A6, które umożliwiają dołączenie do płytki elementów zewnętrznych. 17. Potencjometr P2 umożliwia podanie na wejście przetwornika analogowo cyfrowego A/C zawartego w mikrokontrolerze napięcia z zakresu 0 do 5 V. 18. Podstawka U2 można w nią włożyć dowolny układ 14 pinowy. 19. Podstawka U3 można w nią włożyć dowolny układ 16 pinowy. Układy zawarte na płytce zasilane są napięciem +5 V, które jest stabilizowane za pomocą stabilizatora U6. Dioda D9 sygnalizuje włączenie zasilania płytki uruchomieniowej. Wszystkie elementu układu uruchomieniowego mikrokontrolera należy ze sobą łączyć za pomocą przewodów. Przewodów nie należy lutować, ponieważ są wyposażone w odpowiednie wtyki. 3. Obsługa klawiatury Ważnymi elementami towarzyszącymi mikrokontrolerom, są oprócz wyświetlaczy, przyciski oraz klawiatury. Za pomocą tych elementów jest możliwe wpisanie do mikrokontrolera odpowiednich wartości lub przełączanie jego trybów pracy. Za ich pomocą operator może wprowadzać dane lub modyfikować wartości konfigurowalnych parametrów programu. Przyciski do mikrokontrolera można dołączyć bezpośrednio do jego wyprowadzeń lub dołączyć je matrycowo. Klawiatury w układzie matrycowym trzeba odczytywać poprzez sekwencyjne ustawianie, np. niskiego poziomu na liniach wierszy i odczytywanie kolejno linii kolumn. Do obsługi klawiatury w układzie matrycowym potrzebna jest mniejsza liczba linii mikrokontrolera. Jeżeli jest potrzebna bardzo rozbudowana klawiatura, to można
dołączyć do mikrokontrolera komputerową klawiaturę, która ma ponad 101 klawiszy. Język Bascom oferuje do obsługi takiej klawiatury specjalne instrukcje, wskutek czego obsługa klawiatury jest bardzo łatwa. W tym rozdziale przedstawiono dwa przykłady obsługi przycisków: dołączonych bezpośrednio do wyprowadzeń (linii portów) mikrokontrolera, a także dołączonych w układzie klawiatury matrycowej 2x2, przeglądanej w podprogramie obsługi przerwania od przepełnienia licznika Timer0. 3.1. Wykorzystanie do obsługi przycisków instrukcji Debounce Ponieważ styki klawiatury są elementami mechanicznymi, ich obsługa może się wydać trudna i sprawiać trochę problemów. Problemy te wynikają z faktu, że po naciśnięciu przycisku jego styki przez pewien czas drgają powodując powstanie serii impulsów zamiast czystego zwarcia lub rozwarcia. Drgania styków zazwyczaj nie trwają dłużej niż 25 ms. Jeżeli mikrokontroler będzie bardzo często sprawdzał stan linii, do której dołączono przycisk, to zamiast pojedynczego naciśnięcia może wykryć serię naciśnięć, co może być przyczyną niepoprawnej pracy programu. Najczęściej stosowany i najprostszy sposób rozwiązania tego problemu polega na odczytywaniu przycisku (klawiatury) z opóźnieniem. Po wykryciu naciśnięcia przycisku odmierzane jest zazwyczaj opóźnienie około 25 ms, po czym ponownie jest sprawdzany stan przycisku, który można już uznać za stabilny, jeżeli jest dalej naciśnięty. W języku Bascom jest instrukcja Debounce umożliwiająca wykrycie naciśnięcia przycisku (przycisków) na wybranych liniach portów mikrokontrolera. Zapewnia ona odczytanie stanu przycisku (choć nie tylko jednego) z zastosowaniem programowej eliminacji drgań styków. Składnia tej instrukcji jest następująca: Debounce Pinx.y, stan, etykieta [,Sub] gdzie: Pinx.y linia portu, która będzie sprawdzona, np. Pinb.5; stan 0, gdy sygnałem aktywnym naciśnięcia ma być poziom niski, a 1 gdy poziom wysoki; etykieta etykieta określająca miejsce skoku po naciśnięciu przycisku; Sub po naciśnięciu nastąpi skok do podprogramu o podanej etykiecie (skok do podprogramu jak z zastosowaniem instrukcji Gosub) Parametr Sub umożliwia skok do etykiety podprogramu, z którego będzie możliwy powrót instrukcją Return. Instrukcja Debounce po wykryciu zmiany stanu linii portu do stanu określonego parametrem stan, po 25 ms sprawdza ponownie stan tej linii (eliminuje to drganie styków). Jeśli stan się nie zmienił, to następuje skok do podanej etykiety. Jeżeli będą różne stany (parametr stan będzie różny od stanu linii portu), to będą wykonane kolejne instrukcje programu zawarte po tej instrukcji. Instrukcja ta nie wstrzymuje bowiem działania programu. Przy przytrzymaniu przycisku instrukcja Debounce zostanie wykonana tylko jeden raz. Jej ponowne wykonanie będzie możliwe po puszczeniu oraz ponownym naciśnięciu przycisku. Każda instrukcja Debounce korzysta z 1 bitu pamięci RAM, do zapamiętania stanu linii portu. Dla każdej linii portu jest przydzielany osobny bit. Jeżeli w programie jest potrzebne oczekiwanie na naciśnięcie przycisku, to można skorzystać z instrukcji Bitwait, która wstrzymuje działanie programu dopóki na określonej linii portu pojawi się oczekiwany stan (np. przy naciśnięciu przycisku).
Dla instrukcji Debounce jest możliwa zmiana czasu opóźnienia (domyślnie 25 ms) na inny. Służy do tego instrukcja konfigurująca Config Debounce = czas, gdzie czas jest wartością w milisekundach. Aby sprawdzić działanie oraz wykorzystanie instrukcji Debounce, do mikrokontrolera można dołączyć dwa przyciski oraz dwie diody LED zgodnie ze schematem na rys.1 i przygotować program z instrukcją. Rys.1. Schemat dołączenia do mikrokontrolera przycisków oraz diod LED. Przycisku mogą być dołączone do linii PB1 oraz PB2, a diody do PB4 oraz PB5 mikrokontrolera. Przyciśnięcie przycisku będzie powodować podanie na linię wejściową niskiego poziomu napięcia. Tak więc instrukcja Debounce powinna reagować na poziom niski na danej linii. Napisany przykładowy program dla mikrokontrolera będzie powodował, że po naciśnięciu przycisku S1 będzie zmieniał się na przeciwny stan diody D1, a naciśnięcie przycisku S2 będzie powodowało zmianę stanu diody D2 na przeciwny. Poniżej przedstawiono przykład odczytu dwóch przycisków za pomocą instrukcji Debounce: Program obsługi przycisków S1, S2 za pomocą instrukcji Debounce $REGFILE = m8def.dat informuje kompilator o pliku dyrektyw mikrokontrolera $CRYSTAL = 8000000 informuje kompilator o częstotliwości oscylatora taktującego mikrokontroler Config Pinb.1 = Intput linia PB1 jako wejściowa Config Pinb.2 = Intput linia PB2 jako wejściowa Config Pinb.4 = Output linia PB4 jako wyjściowa Config Pinb.5 = Output linia PB5 jako wyjściowa Led1 Alias Portb.5 przypisanie nazwy Led1 portowi Portb.5 Led2 Alias Portb.4 przypisanie nazwy Led2 portowi Portb.4 S1 Alias Pinb.2 przypisanie nazwy S1 portowi Portb.2 S2 Alias Pinb.1 przypisanie nazwy S2 portowi Portb.1 Set Portb.1 Set Portb.2 dołączenie do linii PB1 rezystora podciągającego dołączenie do linii PB2 rezystora podciągającego Do nieskończona pętla Do Loop Debounce S1, 0, Pr1, Sub jeśli naciśnięty przycisk S1
to skok do podprogramu Pr1 Debounce S2, 0, Pr2, Sub jeśli naciśnięty przycisk S1 to skok do podprogramu Pr1 Loop koniec pętli programu End koniec programu Pr1: Toggle Led1 Return Pr2: Toggle Led2 Return podprogram Pr1 zmiana na przeciwny stanu wyjścia sterującego diodą D1 powrót z podprogramu podprogram Pr2 zmiana na przeciwny stanu wyjścia sterującego diodą D2 powrót z podprogramu W pierwszej kolejności w programie linie PB1 oraz PB2 są konfigurowane jako wejściowe, a linie PB4 oraz PB5 jako wyjściowe. Następnie do wykorzystywanych linii portów przypisano aliasy, które oznaczają elementy, jakie do tych linii dołączono. Instrukcje Set Portb.1 oraz Set Portb.2 dołączają do linii PB1 oraz PB2 rezystory podciągające. Należy takie rezystory dołączyć, gdyż przy rozwartych przyciskach byłaby na tych liniach stan nieokreślony, a rezystory podciągające wymmuszają na nich poziomy wysokie. Dalej, w nieskończonej pętli Do Loop umieszczono dwie instrukcje Debounce odczytujące odpowiednio stan przycisku S1 oraz S2. Po naciśnięciu przycisku linii wejściowej będzie poziom niski, dlatego parametr stan instrukcji Debounce ma wartość 0. Po wykryciu przez te instrukcje poziomu niskiego nastąpią skoki do podprogramów oznaczonych etykietą Pr1 dla S1, oraz Pr2 dla S2, gdyż w instrukcjach Debounce zastosowano parametr Sub. Po naciśnięciu przycisku S1wykonane zostaną instrukcje w podprogramie Pr1, czyli zostanie zmieniony na przeciwny stan linii PB5 (Led1). Po naciśnięciu przycisku S2 wywołany zostanie podprogram Pr2 i zmieniony zostanie na przeciwny stan linii PB4 (Led2). Przytrzymanie wciśniętego przycisku nie powoduje żadnych zmian. Dopiero po jego puszczeniu i ponownym naciśnięciu wykonane zostaną od początku odpowiednie podprogramy. Instrukcja Debounce jest nieodpowiednia, gdy chcemy aby wartość określanej zmiennej zwiększyła się przy dłuższym przyciśnięciu przycisku, a nie po każdym jego naciśnięciu. Można jednak przygotować obsługującą przyciski przykład poprzez zastosowanie instrukcji warunkowej If Then, która zapewni pełną kontrolę nad działaniem przycisku i funkcjami, jakie mają być nim wybrane. 3.2. Zastosowanie instrukcji warunkowej If Then do odczytywania stanów przycisków Do odczytywania stanu przycisku można zastosować własną procedurę z instrukcją warunkową If Then. W tym przykładzie przedstawiono sposób obsługi przycisków z wykorzystaniem tej instrukcji oraz instrukcji Bitwait. Aby prawidłowo wykonać czynności po wciśnięciu przycisku należy: wykryć naciśnięcie przycisku; odczekać 25 ms w celu eliminacji drgań styków; ponownie sprawdzić, czy przycisk jest nadal wciśnięty; jeżeli wciśnięty, to wykonać związane z tym funkcje;
czekać na puszczenie przycisku. W przedstawionym poniżej przykładzie programu, w odpowiedzi na naciśnięcie przycisków S1 i S2 będą zmieniane stany diod D1 i D2 dołączonych do mikrokontrolera zgodnie z rys.1. Naciśnięcie przycisku zmienia na przeciwny stan przypisanej mu diody LED. Program umożliwia także, przy włączaniu mikrokontrolera, wywołanie innej funkcji, w której jest realizowane migotanie diod LED. Aby wywołać tę funkcję należy przed włączeniem zasilania nacisnąć przyciski S2 i przytrzymać je: Program obsługi przycisków S1, S2 za pomocą instrukcji warunkowej In Then $REGFILE = m8def.dat informuje kompilator o pliku dyrektyw mikrokontrolera $CRYSTAL = 8000000 informuje kompilator o częstotliwości oscylatora taktującego mikrokontroler Config Pinb.1 = Intput linia PB1 jako wejściowa Config Pinb.2 = Intput linia PB2 jako wejściowa Config Pinb.4 = Output linia PB4 jako wyjściowa Config Pinb.5 = Output linia PB5 jako wyjściowa Led1 Alias Portb.5 przypisanie nazwy Led1 portowi Portb.5 Led2 Alias Portb.4 przypisanie nazwy Led2 portowi Portb.4 S1 Alias Pinb.2 przypisanie nazwy S1 portowi Portb.2 S2 Alias Pinb.1 przypisanie nazwy S2 portowi Portb.1 Set Portb.1 Set Portb.2 dołączenie do linii PB1 rezystora podciągającego dołączenie do linii PB2 rezystora podciągającego początek programu If S1 = 0 And S2 = 0 Then Jeśli naciśnięte jednocześnie Przyciski S1 i S2 to Waitms 25 opóźnienie 25ms dla eliminacji drgań If S1 = 0 And S2 = 0 Then ponowne sprawdzenie stanu przycisków S1 oraz S2 i jeśli są dalej naciśnięte to: Do wejście do nieskończonej pętli Toggle Led2 zmiana na przeciwny stanu linii PB4 Toggle Led1 zmiana na przeciwny stanu linii PB5 Waitms 500 opóźnienie 500ms Loop Do If S1 = 0 Then Jeśli naciśnięte jednocześnie Przyciski S1 to Waitms 25 opóźnienie 25ms dla eliminacji drgań If S1 = 0 Then ponowne sprawdzenie stanu przycisku S1 jest dalej naciśnięty, jeśli tak to Toggle Led1 zmiana na przeciwny stanu linii PB5 Bitwait S1, Set oczekiwanie na puszczenie przycisku S1
koniec warunków Return powrót z podprogramu If S2 = 0 Then Jeśli przyciśnięty przyciski S2 to Waitms 25 opóźnienie 25ms dla eliminacji drgań If S2 = 0 Then ponowne sprawdzenie czy przycisk S2 dalej jest wciśnięty, jeśli tak to: Toggle Led2 zmiana na przeciwny stanu linii PB4 Bitwait S2, Set oczekiwanie na puszczenie przycisku S2 Loop End Na początku programu, za pomocą instrukcji If Then, jest sprawdzany stan przycisków S1 i S2. Jeżeli oba są naciśnięte, to wykonane zostaną instrukcje zależne od spełnienia tego warunku. Po opóźnieniu 25 ms ponownie sprawdza się, czy S1 oraz S2 są jednocześnie naciśnięte. Jeżeli tak, to program wchodzi do nieskończonej pętli Do Loop, w której co 500 ms będzie zmieniany na przeciwny stan linii portu, do których zostały dołączone diody D1 i D2. Dołączone do nich diody będą zapalały się i gasły. Jeżeli przy włączaniu zasilania nie zostały naciśnięte jednocześnie przyciski S1 i S2, to opisane wyżej instrukcje zostaną pominięte i będą wykonywane instrukcje w drugiej nieskończonej pętli Do Loop. W tej pętli sprawdzany jest stan przycisku S1. Jeżeli naciśnięcie przycisku S1 zostanie poprawnie zinterpretowane, to zmieniony zostanie stan linii PB5 na przeciwny. Dalej za pomocą instrukcji oczekującej Bitwait zrealizowane jest oczekiwanie na naciśnięcie przycisku S1, czyli oczekiwanie na pojawienie się na linii z dołączonym z poziomu wysokiego (puszczony przycisk). Instrukcja Bitwait umożliwia oczekiwanie na ustawienie lub wyzerowanie określonego bitu. Składnia tej instrukcji jest następująca: Bitwait bit, Set Reset gdzie: bit nazwa zmiennej bitowej lub bitu w porcie mikrokontrolera. Instrukcja powoduje wstrzymanie działania programu, dopóki określony bit nie zostanie ustawiony w stan 1 (parametr Set) lub w stan 0 (parametr Reset). Wróćmy do programu. Po stwierdzeniu puszczenia przycisku instrukcja Bitwait zakończy swe działanie. Kolejne instrukcje w pętli Do Loop odpowiedzialne są za obsługę przycisku S2, po jego naciśnięciu stan linii PB4 jest zmieniany na przeciwny. Wykrywanie naciśnięcia wielu przycisków lub dłuższego ich przytrzymania można wykorzystać do wywołania w programie procedur konfigurujących dany układ. W omawianym i poprzednim przykładzie należy zauważyć, że stan linii jest odczytywany z rejestru PINx, a nie z rejestru PORTx, gdzie x to symbol danego przycisku. Jeżeli naciśnięcie przycisków będzie powodować podanie na wejście poziomu wysokiego, to należy wyłączyć rezystory podciągające i dołączyć zewnętrzne rezystory ściągające do masy, aby w stanie spoczynku (gdy nie jest naciśnięty przycisk linii wejściowej panował poziom niski, wymuszony przez rezystor ściągający.
3.3. Obsługa klawiatury matrycowej 2 x 2 W sytuacji, gdy mamy niewiele wolnych linii mikrokontrolera, a jest potrzeba użyć wielu przycisków, można zastosować połączenie przycisków w układzie matrycowym. W połączeniu matrycowym, przyciski są dołączone do mikrokontrolera podobnie jak diody matrycowego wyświetlacza LED. Jeżeli zastąpimy w takim wyświetlaczu diody przyciskami, to uzyskamy klawiaturę matrycową. Jeżeli musimy zastosować 9 przycisków, można je połączyć w matrycę o 3 kolumnach i 3 wierszach. Do jej obsługi jest potrzebnych 6 linii mikrokontrolera, a nie 9, jak przy dołączeniu każdego przycisku do jednej linii portu. Obsługa takiej klawiatury przebiega podobnie jak wyświetlacza matrycowego LED, ale kolumnami sterują linie wyjściowe, a wiersze są odczytywane przez linie wejściowe mikrokontrolera. Jeżeli naciśnięcie przycisku ma powodować podanie na wejście poziomu niskiego, to obsługa klawiatury jest następująca. Na pierwszą kolumnę w danej chwili należy podać poziom niski, to naciśnięto przycisk. Który przycisk naciśnięto można zidentyfikować po numerze wiersza, w którym odczytano poziom niski. W kolejnych krokach jest sprawdzany stan przycisków kolumny 2, a następnie 3, znowu pierwszej, itd. (dla klawiatury matrycowej 3x3). Tak jak w przypadku multipleksowanych wyświetlaczy, multipleksowanie klawiatur matrycowych można zrealizować na wiele sposobów. Sposób zaprezentowany w tym przykładzie jest tylko jednym z możliwych. Tak jak przy multipleksowaniu wyświetlaczy, odczytywanie klawiatury dobrze jest umieścić w podprogramie obsługi przerwania, aby było realizowane poza programem głównym. W tym przykładzie zilustrowano odczytywanie stanu przycisków połączonych w matrycę 2x2, czyli będą obsługiwane 4 przyciski. Oczywiście zastosowanie połączenia matrycowego przycisków ma sens, jeżeli przycisków jest więcej niż 2. Przy 4 przyciskach są potrzebne 4 linie, czyli tyle samo ile jest potrzebnych przy bezpośrednim dołączeniu przycisków do mikrokontrolera. Rys.2. Schemat dołączenia do linii portu mikrokontrolera klawiatury matrycowej 2x2.
Przykładowy program obsługuje klawiaturę w przerwaniu zgłaszanym od Timer a. W przypadku klawiatury nie jest potrzebne zgłaszanie przerwania tak często, jak w przypadku wyświetlaczy. Wystarczy, że przerwanie obsługujące klawiaturę matrycową będzie zgłaszane rzadziej niż co 20 ms. W przykładzie przerwanie zgłaszane jest co 25 ms. Procedurę obsługi klawiatury 2x2 napisano tak, aby stan przycisków zapisany w zmiennej nie był zakłócany. Ponieważ styki przycisków drgają, aby pominąć to zjawisko, gdy zostanie wykryty naciśnięty przycisk, stan jest zapamiętywany w dodatkowej zmiennej. Jeżeli po ponownym przerwaniu, czyli za około 25 ms przycisk dalej ma taki sam stan (równy zapisanemu w dodatkowej zmiennej), to oznacza, że przycisk naciśnięto, a nie jest to zakłócenie. Dopiero wtedy do zmiennej określającej stan przycisku jest wpisywany jego kod. Taki sposób działania podprogramu obsługi przerwania pozwala na pominięcie drgań styków przycisków. Przykład dołączenia klawiatury matrycowej 2x2 do mikrokontrolera przedstawiono na rys.2. Zgodnie z tym schematem, do linii PB0 oraz PB1 (skonfigurowanych jako wejściowe) dołączono wiersze matrycy klawiatury, natomiast kolumny dołączono do PB2 i PB3, skonfigurowanych jako wyjściowe. Przy poziomie 0 na linii PB2 będzie możliwy odczyt przycisków kolumny 1, a przy poziomie 0 na PB3 kolumny 2. Do mikrokontrolera dołączono także wyświetlacz alfanumeryczny LCD, na którym będzie wyświetlany kod naciśniętego przycisku. Program obsługujący klawiaturę 2x2 przedstawia się następująco: Program obsługi klawiatury matrycowej 2x2 w przerwaniu zgłaszanym od przepełnienia Timer a 0 8 MHz/1024/200= =około 39 Hz. $REGFILE = m8def.dat informuje kompilator o pliku dyrektyw mikrokontrolera $CRYSTAL = 8000000 informuje kompilator o częstotliwości oscylatora taktującego mikrokontroler Config Pinb.0 = Intput linia PB0 jako wejściowa Config Pinb.1 = Intput linia PB1 jako wejściowa Config Pinb.2 = Output linia PB2 jako wyjściowa Config Pinb.3 = Output linia PB3 jako wyjściowa Config Lcd = 16 * 2 konfigurowanie typu wyświetlacza LCD Config Lcdpin = Pin, Db4 = Portc.3,Db5 = Portc.2 Db6 = Portc.1, Db7 = Portc.0, E = Portc.4, Rs = Portc.5 konfigurowanie linii mikrokontrolera, do których dołączono LCD On Timer0 Mult_kl Dim Temp1 As Byte Dim Temp2 As Byte Dim Przycisk As Byte Dim I As Byte W1 Alias Pinb.0 W2 Alias Pinb.1 konfigurowanie przerwania od przepełnienia Timer0, skok do podprogramu Mult_kl zmienna pomocnicza Temp1 zmienna pomocnicza Temp2 zmienna, do której będzie wpisywany kod naciśniętego przycisku zmienna licznikowa przypisanie nazwie Pinb.0 nazwy W1 przypisanie nazwie Pinb.1 nazwy W2
Kol1 Alias Portb.2 Kol2 Alias Portb.3 Enable Interrupts Enable Timer0 Load Timer0, 200 Set Portb.0 Set Portb.1 Set Kol1 Set Kol2 Do Cls Lcd Przycisk Waitms 100 Loop End przypisanie nazwie Portb.2 nazwy Kol1 przypisanie nazwie Portb.3 nazwy Kol2 odblokowanie globalnego systemu przerwań odblokowanie przerwania od przepełnienia Timer0 wpisanie do licznika wartości początkowej 56, bo 256-200=56 dołączenie do linii PB0 rezystora podciągającego dołączenie do linii PB1 rezystora podciągającego ustawienie linii Kol1 ustawienie linii Kol2 wejście do nieskończonej pętli czyszczenie LCD wyświetlenie na LCD stanu zmiennej Przycisk opóźnienie 100ms koniec programu Mult_kl: podprogram przerwania, w którym jest obsługiwana klawiatura Load Timer0, 200 wpisanie do licznika wartości początkowej 56 For I = 1 To 2 pętla wykonywana 2 razy If I = 1 Then jeśłi I = 1, to Reset Kol1 zerowanie linii Kol1 Else w przeciwnym razie Set Kol1 ustawienie linii Kol1 Reset Kol2 i zerowanie linii Kol2 If W1 = 0 Or W2 = 0 Then jeżeli przy I = 1 lub I =2 wejścia W1 lub W2 mają stany niskie naciśnięty przycisk to Temp1 = Pinb And &B00000011 zapisanie do zmiennej Temp1 wartości dwóch najmniej znaczących bitów rejestru PINB (linie W1 i W2) Exit For opuszczenie pętli For Next Else Temp1 = 0 wyzerowanie zmiennej Temp1 Next I zwiększenie o 1 wartości I If Temp2 = Temp1 Then jeśli wartości w Temp2 oraz Temp1 są sobie równe, to Przycisk = Temp1 zapisanie kodu naciśniętego przycisku do zmiennej Przycisk If I = 2 Then jeśli I = 2, to
Przycisk = Przycisk + 2 End OF Else Temp2 = Temp1 Set Kol1 Set Kol2 Return dodanie do zmiennej Przycisk wartości 2, gdyż odczytano stany przycisków drugiego wiersza zapisanie wartości Temp1 do do zmiennej Temp2 powrót z podprogramu przerwania Najpierw linie, do których dołączono wiersze klawiatury skonfigurowano jako wejściowe, a linie, do których dołączono klawiatury jako linie wyjściowe. Dalej jest konfigurowany wyświetlacz LCD oraz Timer0 z podziałem preskalera po 1024. Następnie konfigurowane jest przerwanie od przepełnienia licznika Timer a 0 po jego wystąpieniu następuje skok do podprogramu wskazywanego przez etykietę Mult_kl. Dalej definiowane są zmienne Temp1 oraz Temp2, które są zmiennymi wykorzystywanymi w podprogramie obsługi przerwania, do tymczasowego przechwytywania kodu naciśniętego klawisza w celu ominięcia drgań styków. Do zmiennej Przycisk w podprogramie obsługi przerwania jest wczytywany kod klawisza. Kody czterech klawiszy będą to wartości od 1 do 4. Przy niewciśniętych klawiszach zmiennej Przycisk jest wpisywana wartość 0. Dla linii sterujących klawiaturą utworzono aliasy W1 i W2, przypisane do linii wejściowych (identyfikują wiersze klawiatury) oraz Kol1 i Kol2 przypisane do linii wyjściowych (identyfikują kolumny klawiatury). Następnie odblokowano przerwania globalne od licznika i Timer0. Do Timer0 załadowano instrukcją Load wartość 200, która jest liczbą impulsów po zliczeniu których licznik wygeneruje przerwanie. Policzymy po jakim czasie zostanie wygenerowane przerwanie przy podziale preskalera przez 1024, częstotliwości rezonatora kwarcowego 8MHz i liczbie impulsów do odliczenia równej 200. 1/8 MHz = 125 khz czyli przerwanie będzie wykonywane co 125 ns * 1024 * 200 26 ms. Następnie do linii wejściowych mikrokontrolera będących wejściami wierszy matrycy klawiatury zostają, poprzez wykonanie instrukcji Set Portb 0 oraz Set Portb1 dołączone rezystory podciągające, aby linie nie miały nieokreślonych stanów, gdy nie przyciśnięto żadnego przycisku. Dalej ustawiane są poziomy wysokie na liniach wyjściowych o aliasach Kol1 i Kol2 i rozpoczyna się pętla For Next, która może być wykonana dwa razy. W zależności od wartości zmiennej I obsługiwana jest pierwsza kolumna albo druga kolumna klawiatury. Jeżeli I = 1, to ustawiany jest poziom niski na linii dołączonej do pierwszej kolumny klawiatury. Jeżeli nie naciśnięto żadnego przycisku tej kolumny, to warunek (W1 = 0 Or W2 = 0) nie będzie prawdziwy. Warunek ten będzie prawdziwy jedynie wtedy, gdy na liniach wiersza W1 lub W2 będzie poziom niski. Który świadczy o naciśnięciu przycisku. Przy niespełnieniu tego warunku, do zmiennej Temp1 jest wpisywana wartość 0. Następnie pętla For Next zostaje wykonana dla I = 2, co spowoduje ustawienie poziomu wysokiego na linii Kol1 i poziomu niskiego na linii Kol2. Obsługiwana jest więc 2 kolumna klawiatury. Jeżeli nie naciśnięto żadnego przycisku to zmiennej Temp1 zostaje wpisana wartość 0. Po wykonaniu kolejnych instrukcji do zmiennej Przycisk zostanie wpisana wartość 0, co oznacza, ze nie naciśnięto żadnego przycisku. W przypadku, gdy linia W1 lub W2 zmieniła poziom z wysokiego na niski (przyciśnięcie przycisku), wykonane zostaną instrukcje po spełnieniu warunku (W1 =0 Or W2 = 0),
czyli do zmiennej Temp1 załadowane zostaną z rejestru wejściowego PINB dwa najmniej znacząc bity. Pozostałe bity wpisane do zmiennej Temp1, z użyciem operatora And zostaną wyzerowane. Po wpisaniu do zmiennej Temp1 stanu linii W1 i W2 zostaje wykonana instrukcja Exit For, która umożliwia wcześniejsze opóźnienie pętli. Jeżeli bowiem po sprawdzaniu kolumny 1 wykryto naciśnięcie przycisku, to nie jest już konieczne sprawdzanie kolejnych kolumn. Po wykonaniu instrukcji Exit For zostaje sprawdzony warunek, czy wartość zmiennej Temp2 jest równa zmiennej Temp1. Jeżeli nie są równe, to do zmiennej Temp2 jest wpisywana wartość Temp1 i podprogram obsługi przerwania jest kończony. Jeżeli przycisk jest nadal wciśnięty, to w następnym przerwaniu (za około 25 ms) zostanie znowu wykryty naciśniety przycisk i wtedy warunek (Temp2 = Temp1) będzie spełniony i do zmiennej Przycisk zostanie zapisany kod naciśniętego klawisza. Po wciśnięciu przycisku jego kod jest więc wpisywany do zmiennej Przycisk dopiero w drugim przerwaniu. Do rozpoznania, które przyciski wciśnięto, tzn. do której kolumny należą, została wykorzystana zmienna I. Jeżeli zmienna ta, po wykryciu naciśnięcia przycisku, miała wartość 1, to będą rozpoznawane klawisze kolumny pierwszej o kodach 1 i 2. Jeżeli rozpoznanie naciśniętego przycisku wykryto przy I = 2, to oznacza, że naciśnięto któryś z przycisków kolumny 2 i do zmiennej Przycisk zostaje dodana wartość 2. Naciskanym przyciskom będą więc odpowiadały kody od 1 do 4. Aby zilustrować, w pętli głównej programu co 100 ms na wyświetlaczu LCD jest wypisywana wartość zmiennej Przycisk, do której jest ładowany kod naciśnietego przycisku. Przed każdym wyświetleniem wartości zmiennej Przycisk, wyświetlacza jest czyszczony instrukcją Cls. Warto wykorzystywać klawiatury matrycowe, gdy mamy mało wolnych linii mikrokontrolera, a potrzebnych jest wiele przycisków. Gdy w układzie stosowane są multipleksowane wyświetlacze LED, obsługę klawiatury można włączyć do podprogramu sterowania tymi wyświetlaczami, zmniejszając tym samym liczbę wykorzystywanych linii mikrokontrolera. Język Bascom ma także funkcję Getkbd(), która sprawdza stan klawiatury matrycowej i zwraca numer wciśniętego klawisza do zmiennej. Funkcja ta umożliwia tylko obsługę klawiatur matrycowych 4 x 4 lub 4 x 6. Dla klawiatur matrycowych o innych rozmiarach trzeba napisać własne procedury obsługi, np. wzorując się na zaprezentowanym przykładzie. 3.4. Obsługa komputerowej klawiatury AT Gdy w jakiejś aplikacji jest potrzebna klawiatura o wielu przyciskach, to można do mikrokontrolera dołączyć tanią komputerową klawiaturę AT. W języku Bascom instrukcja, dzięki której można z łatwością obsłużyć taką klawiaturę. W tym punkcie zaprezentowano przykład obsługi takiej klawiatury. Znaki naciskanych klawiszy będą wyświetlane na wyświetlaczu alfanumerycznym LCD. Klawiatury AT mają złącza w dwóch odmianach: DIN5 lub PS/2 (DIN6). W tabeli 1 przedstawiono funkcje styków złączy DIN5 oraz PS/2. Linia DATA jest linią dwukierunkową, którą są przesyłane do i z kontrolera klawiatury. Linia CLK jest również dwukierunkowa. Źródłem sygnału zegarowego jest zawsze klawiatura, ale można na tej linii wymusić poziom niski. Jeżeli na linii poziom niski będzie trwał dłużej niż 10 ms, to kontroler klawiatury przełączy się w tryb odbioru danych z mikrokontrolera. Na rys.3 przedstawiono schemat dołączenia klawiatury AT do linii PB2, PB3 mikrokontrolera. Dwie pozostałe linie klawiatury dołączono do napięć zasilających. Do
mikrokontrolera dołączono także wyświetlacz LCD, aby zobrazować działanie dołączonej klawiatury AT. Rys.3. Schemat dołączenia klawiatury AT do mikrokontrolera. Tabela 1. Funkcje styków złączy AT (DIN5) oraz PS/2 (DIN6) Do obsługi klawiatury są wykorzystywane dwie instrukcje: konfigurująca Config Keyboard i funkcja odczytu klawiatury Getatkbd(). Dzięki tym instrukcjom można obsługiwać klawiaturę AT nie znając jej protokołu transmisji. Przykładowy program obsługi klawiatury AT przedstawiono poniżej: Program obsługi klawiatury AT PC $REGFILE = m8def.dat informuje kompilator o pliku dyrektyw mikrokontrolera $CRYSTAL = 8000000 informuje kompilator o częstotliwości oscylatora taktującego mikrokontroler Config Lcd = 16 * 2 konfiguracja typu wyświetlacza LCD
Config Lcdpin = Pin, Db4 = Portc.3, Db5 = Portc.2, Db6 = Portc.1, Db7 = Portc.0, E = Portc.4, Rs = Portc.5 konfiguracja linii mikrokontrolera do których dołączono LCD Config Keyboard = Pinb.2, Data = Pinb.3, Keydata = Keydata konfiguracja linii portu do których dołączono klawiaturę AT Dim Kod_k1 As Byte zmienna do której wpisywany jest przekonwertowany kod naciśnietego klawisza Cls Do czyszczenie LCD nieskończona pętla Do Loop Kod_k1 = Getatkbd() odczytanie danej klawiatury i zapisanie kodu klawisza do zmiennej Kod_k1 If Kod_k1 > 0 Then jeśli odczytywany kod ma wartość większą niż 0, to If Kod_k1 > 31 and Kod_k1 < 127 Then jeśli warunek jest spełniony to Lcd Chr(Kod_k1) wyświetlenie na LCD kodu znaku o kodzie ASCII zawartm w Kod_k1 If Kod_k1 = 44 Then Cls If Kod_k1 = 13 Then LowerLine Loop End czyszczenie wyświetlacza przeniesienie kursora do drugiej linii wyświetlacza LCD koniec programu tablica konwersji kodów odczytanych z klawiatury na kod ASCII Keydata: tablica kodów Keydata klawisze normalnie małe litery Data 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Data &H5E, 0, 0, 0, 0, 0, 0, 113, 49, 0, 0, 0, 122, Data 115, 97, 119, 50, 0, 0, 99, 120, 100, 101, 52, Data 51, 0, 0, 32, 118, 102, 116, 114, 53, 0, 0, 110, Data 98, 104, 103, 121, 54, 7, 8, 44, 109, 106, 117, 55, Data 56, 0, 0, 44, 107, 105, 111,48, 57, 0, 0, 46, 45, Data 108, 48, 112, 43, 0, 0, 0, 0, 0, 0, 92, 0, 0, 0, Data 0, 13, 0, 0, 92, 0, 0, 0, 48, 44, 50, 53, 54, 56, Data 0, 0, 0, 43, 51, 45, 42, 57, 0, 0, klawisze z Shift duże litery Data 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Data 0, 0, 0, 0, 0, 0, 81, 33, 0, 0, 0, 90, 83, 65, Data 87, 34, 0, 0, 67, 88, 68, 69, 0, 35, 0, 0, 32, 86,
Data 70, 84, 82, 37, 0, 0, 78, 66, 72, 71, 89, 38, 0, Data 0, 76, 77, 74, 85, 47, 40, 0, 0, 59, 75, 73, 79, Data 61, 41, 0, 0, 58, 95, 76, 48, 80, 63, 0, 0, 0, 0, Data 0, 0, 96, 0, 0, 0, 0, 13, 94, 0, 42, 0, 0, 0, 62, Data 0, 0, 0, 8, 0, 0, 49, 0, 52, 55, 0, 0, 0, 0, 48, Data 44, 50, 53, 54, 56, 0, 0, 0, 43, 51, 45, 42, 57, 0, Po uruchomieniu tego programu, ilustrującego obsługę klawiatury AT, każde naciśnięcie klawisza (o kodach ASCII znaku od 32 do 127), spowoduje wyświetlenie na wyświetlaczu odpowiadającego mu znaku. Na przykład naciśnięcie klawisza z literą a spowoduje wyświetlenie na wyświetlaczu litery a (z klawiszem Shift będą wyświetlane duże litery). Naciśniecie klawisza Del powoduje wyczyszczenie wyświetlacza LCD, a klawisza Enter przejście kursora do drugiej linii wyświetlacza. Na początku programu, oprócz instrukcji konfigurującej wyświetlacz LCD, konfigurowana jest także klawiatura AT za pomocą instrukcji Config Keyboard. Instrukcja ta współpracuje z funkcją Getatkbd() odczytu naciśniętego klawisza oraz określa linie portów mikrokontrolera, do których dołączono klawiaturę AT. Składnia instrukcji konfigurującej klawiaturę AT jest następująca: Config Keyborad = Pinx.y, Data = pinx.y, Keydata = tabela_kodow gdzie: Keyborad linia portu, do której jest dołączona linia sygnałowa CLK klawiatury; Data linia portu, do której jest dołączona linia sygnałowa DATA klawiatury, Keydata etykieta określającą adres tablicy przeliczeniowej kodów klawiszy. Klawiatura AT nie przesyła kodów ASCII naciśniętego klawisza, tylko zwraca numer umowny tego klawisza, na przykład klawisza Esc zwraca numer klawisza (kod 76). Dlatego w instrukcji Config Keyboard należy podać adres (poprzez podanie etykiety) tablicy przeliczeniowej, zapisanej w bloku instrukcja Data na końcu programu. Zawarta w programie tablica przeliczeniowa o etykiecie Keydata zawiera kody ASCII znaków odpowiadające poszczególnym numerom (kodom) klawiszy klawiatury AT. Na przykład, naciśnięcie klawisza e o numerze 24 jest konwertowane na kod ASCII 101, który odpowiada właśnie literze e. Do odczytania numeru naciśniętego klawisza i przeliczonego wg tablicy na kod ASCII służy funkcja Getatkbd(). Składnia tej funkcji jest następująca: zmienna = Getatkbd() gdzie: zmienna zmienna typu Sting lub Byte, do której jest przypisywany odczytany znak lub jego kod ASCII funkcja zwraca zero, gdy nie naciśnieto żadnego klawisza Funkcja oczekuje na odebranie znaku z klawiatury, czyli wstrzymuje działanie programu. Przerwanie pętli oczekiwania może nastąpić przez ustawienie zmiennej Err, na przykład w podprogramie obsługi przerwania. Funkcja Getatkbd() używa dwóch bitów w
rejestrze R6: bity 4 i 5 są używane jako flagi stanu klawiszy Shift i Ctrl. Klawisze specjalne klawiatury nie są rozpoznawane przez tę funkcję. W programie definiowana jest zmienna Kod_kl, do której jest wpisywany kod ASCII naciśniętego klawisza. Następnie po wykonaniu czyszczenia LCD instrukcją Cls są wykonywane instrukcje w nieskończonej pętli Do Loop. W pierwszej kolejności, w tej pętli jest wykonywana funkcja odczytująca i przeliczająca według tablicy numer naciśniętego klawisza na jego kod ASCII, zapisywany w zmiennej Kod_k1. Następnie jest sprawdzane czy wartość zmiennej Kod_k1 jest większa od 0. Jeżeli tak, to oznacza, że naciśnięto klawisz. Następnie jest sprawdzany zakres kodu ASCII klawisza. Jeżeli jest on większy od 31, a mniejszy od 127, to są to kody ASCII znaków, które można wyświetlić na LCD. Jeżeli kod odczytanego znaku zawiera się w tym zakresie, to jest on zamieniany przez funkcję Chr na odpowiadający mu znak, który następnie zostaje wyświetlony na LCD. Jeżeli wartość kodu nie odpowiada znakom, które można wyświetlić na LCD, to nie jest ona przesyłana do LCD. Jeżeli otrzymany kod ma wartość 44, to oznacza, że naciśnięto klawisz Del i wówczas wyświetlacz LCD zostaje wyczyszczony poprzez wykonanie instrukcji Cls. Następnie za pomoc instrukcji warunkowej If Then sprawdzane jest, czy wartość zmiennej Kod_k1 wynosi 13 (kod ASCII klawisza Enter). Jeżeli naciśnięto klawisz Enter, to jest wykonywana instrukcja Lowerline, która ustawia kursor wyświetlacza na początku drugiej linii.