86_88.pdf

(97 KB) Pobierz
C dla mikrokontrolerów 8051, część 10
K U  R S
Wiêkszoæ urz¹dzeñ wykonanych na mikrokontrolerach
wymaga zastosowania jakiego interfejsu zapewniaj¹cego komunikacjê
z u¿ytkownikiem. W poprzednich odcinkach kursu zajmowalimy siê
prezentacj¹ wyników przy pomocy wywietlacza LED czy LCD. Czasami
jednak trzeba równie¿ wprowadziæ pewne parametry - na przyk³ad wartoæ
czasu, napiêcia itp. Mo¿na to zrobiæ na wiele sposobów, lecz jeden z nich
przyj¹³ siê wiele lat temu i króluje do dzi - klawiatura.
czêæ 10
Obs³uga klawiatury w C, czêæ 1
Ten odcinek w ca³oci powiêco-
ny bêdzie obs³udze pojedynczych kla-
wiszy, klawiatur, zawrzemy w nim
przegl¹d najczêciej stosowanych roz-
wi¹zañ. Zaczniemy od prostych me-
tod wykrywania naciniêcia pojedyn-
czego klawisza, poprzez proste kla-
wiatury wielostykowe, przedstawimy
tak¿e zagadnienia obs³ugi klawiatur
matrycowych, a tak¿e dzia³aj¹cych
z wykorzystaniem przerwañ.
w przyk³adzie. Pojedynczy przycisk pod-
³¹czony zosta³ do portu P1.2 mikrokon-
trolera z rodziny 8051 (AT89C2051). Ty
mo¿esz zrobiæ to tak samo, mo¿esz
równie¿ wykorzystaæ inn¹ liniê portu.
Zwróæ tylko uwagê na to, czy nie ma
przypadkiem koniecznoci do³¹czenia
do tej linii rezystora pullup . Potrzebne
informacje mo¿na znaleæ w dokumen-
tacji mikrokontrolera. W przyk³adzie
u¿ywa³em AT89C2051, który posiada
wewnêtrzny rezystor pullup do³¹czony
do linii P1.2.
Przycisk pod³¹czy³em w taki spo-
sób, aby w momencie jego naciniê-
cia linia portu P1.2 zwierana by³a do
masy. Dlaczego? Otó¿ w normalnej
sytuacji, gdy port pracuje jako wej-
ciowy, linia ta jest pod³¹czona do
dodatniego napiêcia zasilania przez
rezystor pullup . W konsekwencji mik-
rokontroler sprawdzaj¹c stan bitu
P1.2 odczyta stan wysoki. Mo¿na go
zmieniæ (pamiêtajmy, ¿e linia pracuje
jako wejciowa) podaj¹c stan niski,
tak aby zmieni³ siê potencja³ wymu-
szany przez rezystor. Podanie stanu
niskiego odpowiada zwarciu linii
P1.2 do masy. To jeden z powodów.
Drugi, to mog¹ce pojawiæ siê zak³ó-
cenia. W przyjêtej konfiguracji znacz-
nie ³atwiej je wyeliminowaæ, ponie-
wa¿ impedancja linii w stanie niskim
jest wielokrotnie mniejsza ni¿ w sta-
nie wysokim. Tyle o pod³¹czeniu, te-
raz warto by wspomnieæ o w³aciwo-
ciach fizycznych styków prze³¹cznika.
Podczas zwierania styków prze-
³¹cznika, mo¿e pojawiæ siê ich drga-
nie, co zilustrowano na rys. 2 . Jak
³atwo zauwa¿yæ, stan niski na wej-
ciu portu pojawi siê co najmniej
kilkakrotnie i jeli mikrokontroler bê-
dzie wystarczaj¹co szybki, a zadanie
klawisza polegaæ ma np. na zwiêk-
szeniu jakiego parametru wartoci
o 1 przy jego naciniêciu, to mo¿e siê
okazaæ, ¿e pojedyncze naciniêcie
klawisza owocuje przyrostem warto-
ci zmiennej o kilka czy nawet kilka-
nacie kroków. We wspó³czenie pro-
dukowanych miniaturowych prze³¹cz-
nikach zjawisko drgania styków zo-
sta³o prawie ca³kowicie wyeliminowa-
ne, jednak jego wystêpowanie zale-
¿eæ bêdzie od konstrukcji mechanicz-
nej prze³¹cznika - poniewa¿ tej ni-
gdy nie mo¿emy byæ pewni, lepiej
siê przed nim zabezpieczyæ.
Lekarstwem na to zjawisko jest
prosty zabieg: reakcja na wciniêty
klawisz (czy to opadaj¹ce zbocze, czy
poziom niski sygna³u), odczekanie
przez czas 20...50 ms i ponowny od-
czyt stanu portu. Jeli nie zmieni³
siê on, to oznacza, ¿e klawisz nadal
jest wciniêty i u¿ytkownik oczekuje
reakcji na wciniêcie przycisku. Jeli
odczytany stan jest wysoki, to ozna-
czaæ mo¿e, ¿e albo odebrano zak³ó-
cenie, albo przypadkowo naciniêto
klawisz. Tê prost¹ zasadê dzia³ania
wykorzystuje program umieszczony na
list. 1 . Stan klawisza sygnalizowany
jest przez bit 3 portu P1. Bit ten
przyjmuje poziom logiczny niski, gdy
klawisz jest wciniêty.
Testowanie stanu
pojedynczego przycisku
Na pocz¹tek zajmiemy siê testo-
waniem stanu pojedynczego przycis-
ku. Wbrew pozorom nie jest to za-
danie ³atwe, mimo i¿ tak mo¿e siê
wydawaæ na pocz¹tku. Program musi
uwzglêdniæ fizyczne w³aciwoci sty-
ku, zabezpieczyæ siê przed przypad-
kowym jego wciniêciem lub zewnêt-
rznym zak³óceniem. Na podstawie ob-
s³ugi pojedynczego przycisku omówi-
my podstawowe zasady odczytu sta-
nu prze³¹cznika. Funkcje omawiane
dalej bêd¹ tylko rozwiniêciem oma-
wianej tutaj funkcji podstawowej.
Na rys. 1 pokazano sposób pod³¹-
czenia klawisza wykorzystywany
Rys. 1. Pod³¹czenie pojedynczego
klawisza do mikrokontrolera
wykorzystywane w przyk³adzie 1
Rys. 2. Typowy przebieg sygna³u na
wejciu mikrokontrolera po
naciniêciu przycisku
86
Elektronika Praktyczna 3/2003
32571017.003.png 32571017.004.png 32571017.005.png 32571017.006.png
K U  R S
List. 1. Prosty program odczytuj¹cy stan klawisza pod³¹czonego do P1.2
/* prosty program demonstracyjny odczyt pojedynczego klawisza
klawisz w³¹czony pomiêdzy masê a port P1.2, stan aktywny = L;
rezonator kwarcowy 8MHz */
Zaznaczam - jest to jedna z naj-
prostszych mo¿liwych funkcji obs³ugi
klawisza. W wiêkszoci sytuacji jest
wystarczy, ma jednak tê wadê, ¿e po
jego wciniêciu oczekuje na zwolnie-
nie blokuj¹c tym samym wykonywa-
nie innych zadañ, o ile nie s¹ one
obs³ugiwane przez przerwania.
#include <reg51.h>
//do³¹czenie definicji rejestrów mikrokontrolera
sbit PortKey = P1^2;
//definicja bitu portu klawisza
sbit Active = P1^3;
//port przyjmuje stan niski, gdy klawisz
//jest wciniêty
//opónienie oko³o 1 milisekundy dla kwarcu 8MHz
void Delay(unsigned int time)
{
Funkcja autorepeat klawisza
Ka¿dy u¿ytkownik komputera PC
zna funkcjê autorepeat klawiatury.
Polega ona na powtarzaniu kodu
wciniêtego klawisza. Zrealizujemy ta-
k¹ funkcjê w programie dla mikro-
kontrolera. Ustalmy najpierw metodê.
Wydaje mi siê, ¿e sporód wielu
sposobów obs³ugi klawiszy, najbar-
dziej naturalnymi s¹:
1. Po wciniêciu jednokrotne wy-
s³anie kodu wciniêtego klawisza,
brak reakcji jeli klawisz nie zosta³
zwolniony.
2. Po wciniêciu wys³anie kodu
klawisza i jeli nie zosta³ on zwol-
niony powtarzanie kodu klawisza co
pewien ustalony odstêp czasu.
3. Po wciniêciu wys³anie kodu
klawisza i jeli nie zosta³ on zwol-
niony - powtarzanie kodu klawisza
pocz¹tkowo wolno a po wykonaniu
pewnej liczby powtórzeñ - znacznie
szybciej.
W tym odcinku kursu wykonamy
program, który zrealizuje w praktyce
dzia³anie klawiatury w sposób numer
3. Z racji tego, ¿e liczba klawiszy nie
ma wiêkszego znaczenia dla zrozu-
mienia zasady dzia³ania, zanim prze-
jdziemy do bardziej zaawansowanych
rozwi¹zañ, pos³u¿ymy siê (podobnie
jak poprzednio) rozwi¹zaniem wyko-
rzystuj¹cym pojedynczy przycisk. Tu
jedna uwaga: to tylko propozycja roz-
wi¹zania. Pisz¹c programy czêsto za-
uwa¿ysz, ¿e jeden problem mo¿na
rozwi¹zaæ na kilka mo¿liwych sposo-
bów.
Aby funkcja automatycznego po-
wtarzania naciniêtego klawisza mog-
³a dzia³aæ, nie wolno zatrzymywaæ
programu i oczekiwaæ na zmianê sta-
nu przycisku. Mikrokontroler musi
nieprzerwanie (nie bierz tego dos³ow-
nie) wykonywaæ program sprawdzaj¹c
stan klawisza i sprawdzaj¹c warunki
dla funkcji autorepeat. W zwi¹zku
z tym nie wolno nam u¿yæ konstruk-
cji while (!PortKey) z poprzedniego
przyk³adu. Trzeba zastosowaæ zupe³-
nie inn¹ metodê: program przyk³ado-
wy liczy liczbê cykli odczytu stanu
przycisku i w zale¿noci od jej war-
toci podejmuje odpowiedni¹ akcjê.
unsigned int j;
while (time >= 1)
//wykonanie pêtli FOR zajmuje oko³o 1 msek.
{
//pêtla jest powtarzana TIME razy
for (j=0; j<65; j++);
time--;
}
}
//funkcja - reakcja na naciniêcie klawisza
//tylko ustawienie stanu portu wyjciowego
void KeyPressed(void)
{
Active = 0;
}
//funkcja - reakcja, gdy klawisz nie jest wciniêty
//tylko ustawienie stanu portu wyjciowego
void KeyNotPressed(void)
{
Active = 1;
}
//pocz¹tek programu g³ównego
void main(void)
{
while (1)
//pêtla nieskoñczona
{
if (!PortKey)
{
Delay(20); //opónienie 20ms
if (!PortKey) //ponowny odczyt klawisz i podjêcie akcji,
{
//jeli nadal wciniêty
KeyPressed();
while (!PortKey); //oczekiwanie na zwolnienie klawisza
}
}
KeyNotPressed(); //akcja, gdy klawisz nie jest wciniêty
}
}
Pocz¹tek programu zawiera dekla-
racje bitów portu: bit 2 pracuje jako
wejciowy - do niego jest pod³¹czo-
ny klawisz, bit 3 pracuje jako wyj-
ciowy - jego stan zmienia siê w za-
le¿noci od stanu klawisza. Funkcja
void KeyPressed() zawiera polecenia
wykonywane w sytuacji, gdy klawisz
jest wciniêty i odwrotnie: funkcja
void KeyNotPressed() zawiera polece-
nia wykonywane, gdy klawisz jest
zwolniony. W³aciwy odczyt klawisza
zosta³ zdefiniowany w programie
g³ównym i sprowadza siê do testowa-
nia stanu pojedynczego bitu portu
przy pomocy polecenia if (jeli). Wy-
ra¿enie if (!PortKey) oznacza: jeli
zmienna PortKey ma wartoæ nie ze-
ro, to wykonaj polecenia zawarte
pomiêdzy nawiasami klamrowymi.
Czyli: odczekaj 20 milisekund, odczy-
taj ponownie stan bitu portu. Jeli
nadal jest on równy stanowi niskie-
mu, to wykonaj funkcjê odpowiadaj¹-
c¹ wciniêtemu klawiszowi. Zadaniem
pêtli while (!PortKey) jest oczekiwa-
nie a¿ klawisz zostanie zwolniony.
while (1) //pêtla nieskoñczona,
{
//w niej odczyt klawisza
if (!PortKey)
{
Delay(20); //opónienie 20ms
if (!PortKey)
//ponowny odczyt klawisz
//i podjêcie akcji,
{
KeyPressed();
//jeli nadal wciniêty
while (!PortKey);
//oczekiwanie na zwolnienie
//klawisza
}
}
KeyNotPressed();
//akcja, gdy klawisz nie jest
//wciniêty
}
Elektronika Praktyczna 3/2003
87
32571017.001.png
K U  R S
List. 2. Program odczytuje stan klawisza pod³¹czonego do P1.2 wyposa¿ony w
rozbudowan¹ funkcjê autorepeat
/* prosty program demonstracyjny odczyt pojedynczego klawisza
z funkcj¹ automatycznego powtarzania.
klawisz w³¹czony pomiêdzy masê a port P1.2, stan aktywny = L;
rezonator kwarcowy 8MHz */
//czy licznik jest wiêkszy
//od warunku dla
{ //wolniejszego autorepeat?
KeyPressed(); //tak - wykonaj
//fragment kodu
Delay(500); //d³u¿sze opónienie
#include <reg51.h>
//do³¹czenie definicji rejestrów mikrokontrolera
#define Time_1 900
//time 1 = oko³o 15 ms
}
else
if (Counter == Time_1)
//czy licznik osi¹gn¹³ wartoæ,
//gdy uznajemy klawisz
{ //za wciniêty?
KeyPressed(); //tak - wykonaj
//fragment kodu
#define Time_2 9000
//oko³o 1,5 sekundy
#define Time_3 9020
//po 20 wykonaniach pêtli dla Time_2
unsigned int Counter = 0;
//deklaracja zmiennej licznika
sbit PortKey = P1^2;
//definicja bitu portu klawisza
sbit Active = P1^3;
//port przyjmuje stan niski, gdy klawisz
//jest wciniêty
//opónienie oko³o 1 milisekundy dla kwarcu 8MHz
void Delay(unsigned int time)
{
}
unsigned int j;
}
else
Counter = 0;
//zeruj licznik, jeli zwolniono
//klawisz
while (time >= 1)
//wykonanie pêtli FOR zajmuje oko³o 1 msek.
{
//pêtla jest powtarzana TIME razy
for (j=0; j<65; j++);
time-;
}
}
//funkcja - reakcja na naciniêcie klawisza
//tylko ustawienie stanu portu wyjciowego
void KeyPressed(void)
{
Na pocz¹tku program sprawdza
stan klawisza. Jeli jest on wciniêty,
to wartoæ zmiennej Counter zwiêksza-
na jest o 1 i rozpatrywana przez kon-
strukcjê if . W przeciwnym przypadku
zmiennej nadawana jest wartoæ 0.
Zadaniem if jest zdecydowanie jaka
akcja zostanie podjêta w zale¿noci od
wartoci zmiennej Counter . Sta³a Ti-
me_1 to iloæ pojedynczych operacji
odczytu bitu przycisku, po której
uznaje go za wciniêty. Jej wartoæ
w g³ównej mierze zale¿y od szybkoci
mikrokontrolera i musi byæ dobrana
indywidualnie dla budowanego uk³a-
du. Sta³a Time_2 , to iloæ przebiegów
cykli odczytu, dla której uznajê wa-
runek dla wolniejszego powtarzania
za spe³niony. Wykonywany jest wów-
czas fragment programu zawieraj¹cy
instrukcjê Delay(500) powoduj¹c¹ prze-
rwê ok. 0,5 sekundy pomiêdzy powtó-
rzeniami odczytu klawisza. Jeli war-
toæ zmiennej Counter osi¹gnie war-
toæ sta³ej Time_3 , to wykonywany
jest fragment programu z instrukcj¹
Delay(100) : czas pomiêdzy powtórze-
niami akcji w³aciwej dla wciniêtego
przycisku jest piêciokrotnie krótszy.
Instrukcja Counter- s³u¿y do zabezpie-
czenia przed przekroczeniem wartoci
dopuszczalnej dla danego typu zmien-
nej Counter .
Do automatycznego powtarzania
kodu klawisza wrócimy jeszcze przy
okazji budowy menu. W tym momen-
cie, proponujê wykorzystaæ program
list. 2 aby zobaczyæ jak nastawy
wartoci Time_1 , Time_2 Time_3
wp³ywaj¹ na pracê programu wyko-
nywanego przez mikrokontroler.
Jacek Bogusz, AVT
jacek.bogusz@ep.com.pl
Active = Æounter;
}
//funkcja - reakcja, gdy klawisz nie jest wciniêty
//tylko ustawienie stanu portu wyjciowego
void KeyNotPressed(void)
{
Active = 1;
}
//pocz¹tek programu g³ównego
void main(void)
{
while (1)
//pêtla nieskoñczona
{
if (!PortKey)
{
Counter++; //tak, zwiêksz licznik
if (Counter > Time_3) //czy licznik wiêkszy od warunku dla
{
//szybszego autorepeat?
KeyPressed(); //tak - wykonaj fragment kodu
Counter-;
Delay(100);
//krótsze opónienie
} else;
if (Counter > Time_2) //czy licznik wiêkszy od warunku dla
{
//wolniejszego autorepeat?
KeyPressed(); //tak - wykonaj fragment kodu
Delay(500);
//d³u¿sze opónienie
} else;
if (Counter == Time_1) //czy licznik osi¹gn¹³ wartoæ, gdy
{
//uznajemy klawisz za wciniêty?
KeyPressed(); //tak - wykonaj fragment kodu
}
}
else;
{
Counter = 0; //licznik jest zerowanym, gdy klawisz
KeyNotPressed(); //zostaje zwolniony
}
}
}
Oto czêæ programu napisanego w jê-
zyku C, w którym pozostawi³em tyl-
ko naprawdê niezbêdne fragmenty ko-
du ród³owego:
//czy licznik jest wiêkszy
//od warunku dla
{ //szybszego autorepeat?
KeyPressed(); //tak - wykonaj
//fragment kodu
if (!PortKey)
//czy naciniêto klawisz?
{
Counter-;
Delay(100); //krótsze opónienie
}
else
if (Counter > Time_2)
Counter++; //tak, zwiêksz licznik
if (Counter > Time_3)
88
Elektronika Praktyczna 3/2003
32571017.002.png
Zgłoś jeśli naruszono regulamin