mikrokontrolery.net

 

   

Programowanie mikrokontrolerów 8051 w języku C - część 2

W drugiej części kursu programowania zapoznamy się z obsługą klawiatury.W tym celu musimy podłączyć do układu z poprzedniej części kursu, do portu P3, 8 przycisków w sposób pokazany na ponizszym rysunku :

Klawiatura ta działa w następujący sposób : po naciśnięciu przycisku wyprowadzenie portu, do którego jest podłączony przycisk, jest zwierane do masy, co wymysza na wyprowadzeniu portu stan niski. Port musi zawierać wewnętrzne rezystory podciągajęce linie portu do szyny zasilającej. W przeciwnym razie będziumy musieli dołączyć zewnętrzne rezystory podciągające. W typowych mikrokontrolerach rodziny 8051 rezystory te są wbudowane w każdy port, za wyjątkiem portu P0 a także w bardzo popularnym AT89C2051 za wyjątkiem linii P1.0 i P1.1. Tak wiec przed podłączeniem klawiatury nalezy się upewnić, że port do którego chcemy podłaczyc klawiaturę posada rezystory podciągające.

Pierwszy program wykorzystujący klawiaturę jest bardzo prosty i zamieszczony poniżej :

// dołączenie pliku nagłówkowego
// zawierajacego definicje rejestrow
// wewnetrznych procesora
#include <8051.h>
// główna funkcja programu
void main(void)
{
// pętla nieskończona
while(1)
// przepisanie do portu P0 stanu linii portu P3
P0 = P3;
}

Cały program opiera się w zasadzie na nieustannym przepisywaniu stanu linii portu P3 do portu P0. Efektem tego jest zapalanie odpowiedniej diody LED po naciśnięciu dowolnego przycisku. Zauważmy, że naciśniecie przycisku wywołuje wymuszenie na odpowiednim wyprowadzeniu portu P3 stanu niskiego, a zapalenie diody LED jest spowodowane ustawieniem na odpowiednim wyprowadzeniu portu P0 również stanu niskiego. Dzięki temu możemy po prostu przepisać stan linii portu P3 do portu P0, bez zadnych dodatkowych zabiegów. Jest to zrealizowane przez instrukcję :

P0 = P3;

Nie są wymagane żadne dodatkowe zmienne pośredniczące w przesyłaniu danych z portu P3 do portu P0.

Instrukcja warunkowa if
Teraz poznamy bardzo ważną instrukcję warunkową if. Służy ona do warunkowego wykonania fragmentu programu. Najprostsza wersja instrukcji if wygląda następująco :

if(warunek)
instrukcja;

Jesli warunek ma wartość true (prawda) to wykonywana jest instrukcja, w przeciwnym razie instrukcja nie będzie wykonana.

Przykład zastosowania instrukcji warunkowe if jest pokazany poniżej :

// dołączenie pliku nagłówkowego
// zawierajacego definicje rejestrow
// wewnetrznych procesora
#include <8051.h>
// główna funkcja programu
void main(void)
{
// Pętla nieskończona
while(1)
{
// jeśli P3.0 jest w stanie niskim
if(P3_0 == 0)
// to ustaw na P0.0 stan niski
P0_0 = 0;
// jesli P3.1 jest w stanie niskim
if(P3_1 == 0)
// to ustaw na P0.0 stan wysoki
P0_0 = 1;
}
}

Zapis P3_0 odpowiada zapisowi P3.0, czyli określa pin 0 portu P3. Przyjrzyjmy sie teraz dokładniej instrukcji if :

if(P3_0 == 0)
P0_0 = 0;

W nawiasach okrągłych po słowie if umieszczono warunek. Warunkiem musi być wyrażenie zwracające wartość logiczną, czyli prawda lub fałsz. W naszym przykładzie dokonujemy sprawdzenia, czy wyprowadzenie P3.0 jest w stanie niskim. Jeśli tak, to oznacza to, że naciśnięty został przycisk podłączony do tego wyprowadzenia. Należy podjąć wtedy odpowiednie działanie, czyli ustawić wyprowadzenie P0.0 w stan niski. Początkujących adeptów programowania w jezyku C może zadziwić znak "==", czyli operator porównania. Jest on inny niż w językach Basic lub Pascal i z początku bardzo łatwo się myli z operatorem przypisania "=". Instrukcja if może także wyglądać następująco:

if(warunek)
instrukcja1;
else
instrukcja2;

Słowo else (w przeciwnym przypadku) umieszczone przed instrukcja2 mówi, że instrukcja2 zostanie wykonana tylko wtedy, gdy warunek instrukcji if będzie niespełniony, czyli będzie wartości false. W sytuacji, gdy w przypadku spełnienia danego warunku wykonanych ma być kilka instrukcji, to należy blok tych instrukcji ująć w nawiasy klamrowe {}:

if(warunek)
{
instrukcja1;
instrukcja2;
instrukcja3;
}

Instrucka iteracyjna for
Zapoznamy się teraz z kolejną niezwykle użyteczna instrukcją - z instrukcją for. Służy ona do realizowania wszelkiego rodzaju pętli. Ogólna postać instrukcji for wygląda następujaco :

for(inicjalizacja zmiennej licznikowej; warunek; modyfikacja zminnej licznikowej)

inicjalizacja zmiennej licznikowej - jest to przypisanie do zmiennej licznikowej jej wartości początkowej
warunek - warunek, który określa kiedy pętla ma być wykonywana
modyfikacja zmiennej licznikowej - odpowiednie zmodyfikowanie zmiennej licznikowej (inkrementacja, dekrementacja lub cokolwiek innego)

W przykładowym programie wykorzystamy pętlę for do wygenerowania pewnego opóźnienia. Funkcja generująca opóźnienie (a raczej przerwę w wykonywaniu programu) jest bardzo przydatna przy współpracy mikrokontrolera z wolniejszymi układami peryferyjnymi, gdzie trzeba czekać np. na zakończenie pomiaru, itp. W naszym programie wykorzystamy funkcję opóźniającą do generowania prostego efektu świetlnego na diodach LED. Kod programu przedstawiony jest poniżej :

// dołączenie pliku nagłówkowego
// zawierajacego definicje rejestrow
// wewnetrznych procesora
#include <8051.h>

// definicja funkcji opóźniającej
void czekaj(unsigned char x)
{
// deklaracja dwóch zmiennych pomocniczych
unsigned char a, b;
// potrójnie zagnieżdzona pętla for
// ta pętla zostanie wykonana x-razy
for( ; x > 0; x--)
// ta 10 razy
for(a = 0; a < 10; ++a)
// a ta 100 razy
for(b = 0; b < 25; ++b);
}

// główna funkcja programu
void main(void)
{
// pętla nieskończona
while(1)
{
// zapal odpowiednią kombinace diod LED
P0 = 0x55;
// odczekaj pewien okres czasu
czekaj(250);
// zapal inną kombinację diod LED
P0 = 0xAA;
// odczekaj pewien okres czasu
czekaj(250);
}
}

Po raz pierwszy stworzyliśmy własną funkcję. Zgodnie z tym, co napisałęm w pierwszej części kursu, funkcja czekaj nie zwraca zadnej wartości (void) i wymaga jednego parametru typu unsigned char (liczba 8-bitowa bez znaku). Parametrem tym będzie żadana przez nas długość opóźnienia (mniej-więcej w milisekundach). Przyjrzyjmy sie pierwszej pętli for :

for( ; x > 0; x--)

Brakuje tutaj części inicjalizacji zmiennej licznikowej, ponieważ tą zmienną jest parametr przekazywany do funkcji. Gdybyśmy w tym miejscy zainicjalizowali zmienną x, to przekazywany parametr zostałby zamazany. W pętli for może brakować dowolnego elementu - może nawet pętla for wyglądać następująco :

for( ; ; ; )

W takim przypadku pętla ta będzie wykonywana bez końca.

Nasza funkcja opóźniająca składa się z trzech zagnieżdzonych pętli for. Wwyniku tego łączny czas wykonywania tych pętli jest iloczynem powtórzeń każdej pętli. Dokłądny czas opóźnienia trudno jest określić, ponieważ ze wzgledu na rózne techniki optymalizacji kodu przez kompilator nie jest znany dokłądny czas wykonywania jednej pętli. Można co prawda odczytać z pliku *.asm generowanego przez kompilator jakie instrukcje zostały uzyte do realizacji tych pętli i określić dokładny czas ich wykonania, ale nie mamy gwarancji, że po zmianie bądź warunku pętli, badź wartości początkowych oraz końcowych kompilator nie zastosuje innego kodu. Tak więc generowanie opóźnień za pomocą pętli jest przydatne tylko przy generowniu przyblizonych opóźnień. Do odmierzania dokładnych odcinków czasu należy zastosować wewnętrzne timery mikrokontrolera.

 

 

 

 

 

 

 

 

 

 

 

 
 
 
 

(c) 2004-2008 Radosław Kwiecień
Polityka prywatności