Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych Autor artykułu: mgr Jerzy Wałaszek |
©2015 mgr
Jerzy Wałaszek
|
Port B |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Porty pełnią w mikrokontrolerach funkcję bram,
poprzez które mikroprocesor komunikuje się ze światem
zewnętrznym. Różne typy mikrokontrolerów posiadają różną liczbę
linii portów wejścia/wyjścia:
Porty oznaczane są kolejnymi literami alfabetu. Mamy zatem PORTA, PORTB, PORTC i PORTD. Na zewnątrz obudowy wyprowadzone są linie portów, które oznaczamy PA0...PA7, PB0...PB7, itd. Mikrokontroler ATTINY13, którym początkowo będziemy się zajmować, posiada jedynie PORTB, z którego dostępne jest sześć linii PB0...PB5. Linia PB5 posiada specjalną funkcję resetowania mikrokontrolera, zatem nie można jej używać jako linii wejścia (ponieważ stan niski na tej linii wymuszałby resetowanie – można to wyłączyć przez ustawienie tzw. fusebitów, czyli bitów bezpiecznikowych, lecz po tej operacji mikrokontrolera nie da się już zaprogramować w zwykły sposób, gdyż linia RESET traci swoją funkcję i staje się zwykłą linią portu we/wy). Najlepiej zostawić tę linię w spokoju (w aplikacji łączymy ją z plusem zasilania przez opornik 4,7kΩ...10kΩ). Dodatkowo linie portów pełnią funkcje linii przesyłu danych w czasie programowania mikrokontrolera (pełnią też inne funkcje, co omówimy w dalszej części kursu). Linie te posiadają dodatkowe nazwy. Poniższe nazwy dotyczą interfejsu ISP (ang. In-System Programming), czyli programowania mikrokontrolera bezpośrednio w docelowym układzie elektronicznym:
Mikrokontrolery stosują system programowania zwany ISP (ang. In System Programming). Polega on na tym, że mikrokontroler może być programowany bez wyjmowania go z układu aplikacyjnego. Wystarczy dostarczyć do odpowiednich końcówek opisane powyżej sygnały z programatora. Na czas programowania programator przejmuje kontrolę nad mikrokontrolerem. Po zaprogramowaniu programator zwalnia linie programujące. Mikrokontroler rozpoczyna wtedy wykonywanie programu. ISP jest znaczącym ułatwieniem. W pierwszych mikrokontrolerach układ należało umieścić w podstawce programatora, zaprogramować, po czym wstawić do obwodu aplikacyjnego. W trakcie uruchamiania bardziej skomplikowanej aplikacji czynność taką wykonywało się dziesiątki razy, co często mogło doprowadzić do mechanicznego uszkodzenia mikrokontrolera. Przy ISP mikrokontroler pozostaje cały czas w swoim układzie aplikacyjnym i rozpoczyna pracę bezpośrednio po zakończeniu procesu programowania. Teraz zajmiemy się portem B w ATTINY13 (informacje tutaj podane dotyczą również pozostałych portów w innych mikrokontrolerach). Każda z linii PB0...PB5 może pracować jako wejście lub wyjście danych. Jeśli linia pracuje jako wejście, to stan tej linii jest określany przez urządzenie zewnętrzne. Jeśli linia pracuje jako wyjście, to mikroprocesor określa jej stan. Zatem pierwszym zadaniem programu będzie odpowiednie skonfigurowanie linii portu jako wejście lub wyjście danych. Do tego celu służy rejestr kierunku DDRB (ang. Data Direction Register B). Rejestr posiada osiem bitów, lecz tylko 6 z nich jest aktywnych, ponieważ PORTB posiada w ATTiny13 tylko 6 aktywnych bitów.
Rejestr kierunku danych portu B: DDRB Każdy z bitów DDB0...DDB5 steruje kierunkiem odpowiadającej mu linii PB0...PB5. Z powyższego rysunku wynika, że wszystkie linie przyjmują początkowy stan 0 (czyli stan po resecie mikrokontrolera lub po włączeniu zasilania). Stan 0 bitu DDBx oznacza, że odpowiednia linia PBx pracuje jako wejście, czyli czyta dane z zewnątrz. Bity DDBx posiadają status R/W (ang. Read/Write). Oznacza to, że mikroprocesor może wpisywać do nich informację (W) lub odczytywać informację przechowywaną przez rejestr (R). Stan bitów 7 i 6 mikroprocesor może jedynie odczytywać, nie może ich zmienić (i tak nie mają tu przydzielonej funkcji). Załóżmy, że chcemy ustawić linie PB0 i PB1 jako wyjścia danych, a pozostałe linie jako wejścia. Musimy zatem wpisać do rejestru DDRB liczbę binarną 00000011:
Liczba 00000011 w rejestrze DDRB ustawia PB0 i PB1 jako wyjścia, a pozostałe linie PB2...PB5 jako wejścia
Kolejnym rejestrem dostępnym dla mikroprocesora jest rejestr wyjścia danych PORTB. Rejestr ten również zawiera 6 bitów aktywnych:
Rejestr wyjścia danych portu B: PORTB Bity rejestru PORTB współpracują z bitami rejestru DDRB oraz z wyjściowymi liniami PBx mikrokontrolera w sposób następujący:
Zwróć uwagę na ostatnią opcję. Pozwala ona wykorzystywać linię PBx do odczytu stanu przycisków bez stosowania zewnętrznych oporników, co upraszcza aplikację:
DDBx = 0, PORTBx = 1 Ostatnim rejestrem jest rejestr wejścia danych PINB. Umożliwia on odczyt stanu linii PBx.
Rejestr wejścia danych portu B: PINB Bez względu na stan bitu DDBx odczyt bitu PINBx daje zawsze stan logiczny linii PBx. Natomiast zapis bitu o wartości 1 do PINBx powoduje, iż bit PORTBx zmienia wartość na przeciwną (jeśli linia PBx pracuje jako wyjście, tzn. DDBx = 1, to stan tego wyjścia zmienia się odpowiednio z 0 na 1 lub z 1 na 0). Ta funkcja jest dostępna tylko w mikrokontrolerach z rodziny Tiny. W rodzinie Mega należy stosować inne operacje, które opisujemy w dalszej części kursu. |
Ćwiczenia z portem B |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Po części teoretycznej przyszedł czas na nieco
ćwiczeń praktycznych, które utrwalą nam zdobytą wiedzę o
portach. Zbuduj na płytce stykowej następujący układ
(do odpowiednich końcówek układu ATTINY13 podłącz sygnały z
programatora, nie będziemy o tym już więcej przypominali):
Otwórz środowisko Eclipse i stwórz nowy projekt AVR Cross Target Application dla ATTiny13 (jak to zrobić, opisujemy w poprzednim rozdziale, nie zapomnij wybrać odpowiedniego programatora dla swojego projektu). W projekcie utwórz nowy plik źródłowy. Eclipse umieści w tym pliku jedynie komentarz nagłówkowy:
W naszym programie chcemy uzyskać dostęp do rejestrów DDRB, PORTB i PINB. W tym celu należy dołączyć do programu tzw. plik nagłówkowy (ang. header file). W plikach nagłówkowych umieszcza się definicje różnych obiektów, których nie definiuje sam język C. Do programu dopisz:
Teraz dopisujemy szablon funkcji main, który omówiliśmy na początku tego rozdziału:
W układzie elektronicznym do linii PB0 jest podłączona dioda LED, której świeceniem będzie sterował mikrokontroler. Linia ta musi pracować jako wyjście danych. Należy zatem ustawić na 1 bit DDB0 w rejestrze kierunku DDRB.
Rejestr kierunku danych portu B: DDRB Przed pętlą while dopisujemy polecenie:
Do rejestrów zapisujemy dane przy pomocy instrukcji:
nazwa_rejestru = wartość;
Po każdej instrukcji w języku C umieszczamy średnik. To ważne, jeśli opuścisz chociaż jeden średnik, program nie zostanie skompilowany i kompilacja zakończy się błędem. W programie będą nam potrzebne opóźnienia. Opóźnienie tworzy funkcja _delay_ms(liczba milisekund). Funkcja stanie się dostępna po dołączeniu pliku nagłówkowego util/delay.h:
Nasz układ elektroniczny z mikrokontrolerem jest bardzo prosty, co nie znaczy, że nie będziemy mogli dla niego stworzyć kilku pouczających programów. Zaczynamy od prostego migacza. Dioda LED ma się zapalać na 1 sekundę, po czym gasnąć również na jedną sekundę. Aby zapalić diodę, mikrokontroler ustawia linię PB0 w stan wysoki 1. Aby ją zgasić, ustawia linię PB0 w stan niski 0. Pomiędzy tymi działaniami wprowadzamy opóźnienie 1 sekundy, czyli 1000 milisekund:
Zapisz swój program na dysku (Ctrl+S), a następnie skompiluj go, klikając ikonę młotka na pasku narzędziowym Eclipse. Jeśli nie popełniłeś błędu, to w oknie konsoli na spodzie ekranu otrzymasz raport kompilacji. Zużycie pamięci mikrokontrolera jest dla tego programu następujące:
AVR Memory Usage ---------------- Device: attiny13 Program: 82 bytes (8.0% Full) (.text + .data + .bootloader) Data: 0 bytes (0.0% Full) (.data + .bss + .noinit)
Przesyłamy teraz wynik kompilacji do mikrokontrolera. Uruchamiamy AVRDUDE (Ctrl+Alt+U). Znów, jeśli nie popełniłeś błędu w połączeniu programatora z mikrokontrolerem, w oknie konsoli otrzymasz raport:
avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.00s avrdude: Device signature = 0x1e9007 avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed To disable this feature, specify the -D option. avrdude: current erase-rewrite cycle count is 4 (if being tracked) avrdude: erasing chip avrdude: warning: cannot set sck period. please check for usbasp firmware update. avrdude: reading input file "002.hex" avrdude: input file 002.hex auto detected as Intel Hex avrdude: writing flash (82 bytes): Writing | ################################################## | 100% 0.08s avrdude: 82 bytes of flash written avrdude: verifying flash memory against 002.hex: avrdude: load data flash data from input file 002.hex: avrdude: input file 002.hex auto detected as Intel Hex avrdude: input file 002.hex contains 82 bytes avrdude: reading on-chip flash data: Reading | ################################################## | 100% 0.05s avrdude: verifying ... avrdude: 82 bytes of flash verified avrdude done. Thank you. avrdude finished Po przesłaniu programu mikrokontroler podejmuje jego wykonywanie i dioda LED zaczyna migać. Program zajął 82 bajty pamięci FLASH. Zachodzi pytanie, czy da się go skrócić? Mikrokontrolery z serii Tiny posiadają specjalną funkcję: jeśli zapis zostanie dokonany do rejestru PINB, to spowoduje on odwrócenie stanu bitów na przeciwne. Rejestr PINB normalnie służy do odczytu stanu linii portu B, zatem zapis do niego nie ma normalnego sensu. Zapamiętaj to sobie. Funkcja ta pozwoli nam nieco skrócić program:
Po wprowadzeniu zmian w tekście programu zapisz go na dysku, bo inaczej będziesz wciąż kompilował poprzedni program. Skompiluj program i sprawdź końcówkę raportu kompilacji:
Device: attiny13 Program: 66 bytes (6.4% Full) (.text + .data + .bootloader) Data: 0 bytes (0.0% Full) (.data + .bss + .noinit)
Program zajmuje teraz 66 bajtów, a nie 82. A robi dokładnie to samo, co poprzedni. Wniosek z tego jest taki, że należy dokładnie poznać funkcje swojego kontrolera, gdyż pozwoli to pisać krótsze programy. Ale my na początku nie musimy tak aptekarsko oszczędzać, wróćmy zatem do pierwszej wersji programu, lecz zmodyfikujmy opóźnienia:
Teraz dioda błyska co około sekundę (wewnętrzne odmierzanie czasu przez mikrokontroler nie jest zbyt precyzyjne, zegarów w każdym razie na tym nie buduj). W kolejnym programie stworzymy kilka nowych funkcji. Funkcje te będą realizowały proste zadania: mrugnięcie szybkie (kropka), mrugnięcie dłuższe (kreska) oraz mruganie dla liter S i O w alfabecie Morse'a. Następnie wykorzystamy te funkcje do stworzenia sygnalizatora SOS. W programie wykorzystaliśmy informacje o interwałach czasowych w kodzie Morse'a, które bez problemu znajdziesz w sieci. Podstawą miary czasu jest czas trwania kropki. Tutaj przyjęliśmy 1/10 sekundy. Kreska trwa trzykrotnie dłużej od kropki. Odstępy czasowe pomiędzy kropkami i kreskami w znaku wynoszą tyle samo, co czas trwania kropki. Odstępy pomiędzy kolejnymi literami trwają tyle co kreska. Odstępy pomiędzy kolejnymi słowami trwają 7 okresów kropki.
Tutaj właśnie objawia się potęga układów z mikrokontrolerem. Dzięki jego uniwersalności zmiana programu powoduje, że ten sam układ zaczyna wykonywać inne zadania. Nowe funkcje posiadają następującą budowę: void nazwa(void) { treść; } Słówko void przed nazwą funkcji informuje kompilator, że dana funkcja nic nie zwraca. Słówko void w nawiasach za nazwą funkcji informuje z kolei, że funkcja nie potrzebuje żadnych parametrów do swojego działania. Ważna jest kolejność
definicji. Funkcja musi być zdefiniowana przed użyciem w
programie. Najpierw utworzyliśmy funkcje kropka() i kreska().
Następnie utworzyliśmy funkcje S() i O(), które w swoim kodzie
wywołują odpowiednią liczbę razy poprzednio zdefiniowane funkcje
kropka() i kreska(). Wywołanie funkcji powoduje wykonanie
zawartego w niej kodu i powrót do miejsca, z którego funkcja
została wywołana. Aby wywołać funkcję, w programie umieszczamy
jej nazwę wraz z nawiasami, które tutaj są akurat puste,
ponieważ funkcje nie potrzebują parametrów – tymi zagadnieniami
zajmiemy się w dalszej części kursu. Teraz poćwiczymy odczyt danych. Zbuduj na płytce stykowej poniższy układ:
W układzie pojawił się przycisk W, który jest podłączony do linii PB0. Dioda LED podłączona jest to linii PB1. Linia PB0 będzie pracowała jako wejście danych. Wewnętrznie podepniemy ją do opornika podciągającego. Linia PB1 będzie pracowała jako wyjście danych. Konfiguracja rejestrów sterujących portem B jest następująca:
Rejestr kierunku danych portu B: DDRB
Rejestr danych portu B: PORTB W rejestrze kierunku DDRB ustawiamy na 1 bit DDB1, aby linia PB1 pracowała jako wyjście. W rejestrze PORTB ustawiamy na 1 bit PORTB0, aby do linii PB0 (pracującej jako wejście, ponieważ DDB0 = 0) został podłączony opornik podciągający. Uruchom środowisko Eclipse i utwórz sobie nowy projekt dla ATTINY13 (możesz też skasować poprzedni program i tworzyć nowy w starym projekcie, to zostawiam już twojej decyzji). W edytorze wpisz:
Przed pętlą while umieszczone są dwie instrukcje, które przygotowują port B do pracy w naszym układzie. Do rejestrów zapisujemy liczby w postaci binarnej, ponieważ odzwierciedla ona najlepiej stan poszczególnych bitów. W języku C stała binarna rozpoczyna się od dwóch znaków 0b, za którymi umieszczamy kolejne bity liczby od najstarszego do najmłodszego. Zamiast liczb binarnych moglibyśmy użyć odpowiadających im liczb dziesiętnych, lecz wtedy czytelność operacji spadłaby znacznie. Porównaj: 0b011100 i 28 obie stałe mają dokładnie taką samą wartość liczbową, lecz w stałej binarnej 0b011100 od razu widzimy stan poszczególnych bitów, czego nie daje nam stała dziesiętna 28. Później pokażemy jeszcze bardziej czytelny sposób ustawiania bitów w rejestrach. Jeśli nie wiesz nic o liczbach binarnych, to przeczytaj dokładnie poniższe artykuły z naszego serwisu:
Znajomość systemu dwójkowego jest absolutnie niezbędna przy programowaniu mikrokontrolerów, ponieważ będziesz pracował z bitami. Napiszemy program, który systematycznie odczytuje stan przycisku i, jeśli przycisk będzie wciśnięty, to zapali diodę LED. Jeśli przycisk nie będzie wciśnięty, dioda LED pozostanie zgaszona. W takim programie napotykamy na pewną trudność: jak zbadać stan określonego bitu rejestru? Otóż do tego celu wykorzystujemy operację logiczną & (AND, koniunkcja, iloczyn logiczny). W języku C operacja & działa na poszczególnych bitach argumentów. Można ją opisać poniższą tabelką:
Bit wyniku operacji & jest równy 1, jeśli oba bity a i b miały stan wysoki 1. W przeciwnym razie bit wyniku przyjmuje stan 0. Gdy chcemy sprawdzić stan określonego bitu, tworzymy tzw. maskę bitową, w której wszystkie bity są wyzerowane za wyjątkiem bitu na testowanej pozycji, który ma wartość 1. Jeśli teraz wykonamy operację & wartości bitowej z tą maską, to wszystkie bity wyniku, dla których bity maski mają wartość 0, zostaną wyzerowane. Natomiast na pozycji, gdzie w masce jest bit o stanie 1 otrzymamy 0, jeśli badany bit ma wartość 0 lub 1 w przypadku przeciwnym. Na przykład chcemy badać stan bitu nr 3. Tworzymy maskę z ustawionym bitem nr 3:
maska = 0b00001000
Dla badanej wartości 0b11010101 otrzymamy:
Otrzymaliśmy wynik równy zero, czyli badany bit posiadał stan 0. Dla badanej wartości 0b11011101 otrzymamy:
Otrzymaliśmy wynik różny od zera, czyli badany bit miał stan 1. Zatem operacja rejestr & maska pozwoli nam wydzielić odpowiedni bit rejestru. Co dalej? Teraz w zależności od stanu tego bitu powinniśmy wykonać różne zadania. To wymaga podjęcia decyzji. Do podejmowania decyzji w języku C służy instrukcja warunkowa if. Posiada składnię:
if(wyrażenie) operacja1; else operacja2; if(wyrażenie) operacja1; else operacja2; if(wyrażenie) { dowolny ciąg operacji1; } else { dowolny ciąg operacji2; }
Instrukcja if działa w sposób następujący: Najpierw zostaje wyliczone wyrażenie, które umieszczamy w nawiasach za if. Jeśli wyrażenie ma wartość różną od zera, to zostaje wykonana operacja 1 lub ciąg operacji1 w klamerkach. Po wykonaniu tej operacji instrukcja if nie wykonuje już operacji2. Mikrokontroler przechodzi do wykonania pierwszej instrukcji za if. Jeśli wyrażenie ma wartość 0, to operacja 1 zostaje pominięta i mikrokontroler wykonuje tylko operację2. Dzięki tej instrukcji mikrokontroler może postępować w sposób "inteligentny". Zależnie od zastanych warunków wykonuje odpowiednie operacje. Wracając do naszego programu, umieść w pętli while następujące instrukcje:
Zapisz program na dysku, skompiluj i prześlij wynik do mikrokontrolera. Jeśli nie popełniłeś błędu, to po naciśnięciu przycisku W dioda LED powinna się zaświecić. Zwolnienie przycisku gasi diodę. Zwróć uwagę, że przy zapisie do PORTB zachowujemy cały czas stan ostatniego bitu, który kontroluje dołączaniem opornika podciągającego. Sprawdź, co się stanie, jeśli bit ten nie będzie ustawiany na 1 (zmień odpowiednio swój program). W takim przypadku linia PB0 będzie "wisiała w powietrzu" i zacznie się zachowywać jak antena, zbierając wszelkie zakłócenia z okolicy. Mikrokontroler ATTiny13 zbudowany jest z tranzystorów polowych, a te, jak pamiętasz, posiadają bardzo dużą oporność wejściową. Układ przestanie działać prawidłowo, chyba że podłączysz do nóżki PB0 opornik zewnętrzny 1...10k.
Następny program mruga cyklicznie diodą LED. Jeśli jednak zostanie wciśnięty przycisk W, to mruganie znacznie przyspiesza. W tym programie instrukcja warunkowa będzie decydowała o opóźnieniu pomiędzy zmianami stanu portu PB1, który steruje świeceniem diody LED.
Podnosimy poprzeczkę. Następny program ma zapalać diodę LED po naciśnięciu przycisku i gasić ją po ponownym naciśnięciu przycisku. Brzmi prosto, lecz wcale takie proste nie jest. Musimy rozwiązać kilka problemów. Po pierwsze, jak wykrywać kolejne naciśnięcia przycisku? Następnie należy pamiętać, że styki przycisków mechanicznych wykonują mikrodrgania, co powoduje powstanie szybkiego ciągu impulsów. Mikrokontroler jest dosyć szybki i może potraktować takie drgania jako kilkakrotne naciśnięcie przycisku. Algorytm będzie następujący:
K1: Czytamy port B
K2: Jeśli linia PB0 = 1, to wracamy do kroku K1 K3: Zmieniamy stan linii PB1 na przeciwny K4: Odczekujemy 10 ms aż styki przycisku przestaną drgać K5: Czytamy port B K6: Jeśli linia PB0 = 0, to wracamy do kroku K5 K7: Odczekujemy 10 ms aż styki przycisku przestaną drgać K8: Koniec
Takie operacje musimy umieścić w pętli while. Przyjrzyj się temu dobrze i upewnij się, że rozumiesz wszystko. W tym algorytmie mamy dwie pętle w krokach K1, K2 oraz w krokach K5, K6. Pierwsza pętla czeka aż przycisk zostanie wciśnięty, czyli aż linia PB0 przyjmie stan 0. Po wykryciu naciśnięcia przycisku wykonujemy operację związaną z przyciskiem (tutaj będzie to zapalenie bądź zgaszenie diody LED). Następnie musimy odczekać pewien czas, aż styki przycisku przestaną wykonywać drgania. Teraz następuje druga pętla, która czeka na zwolnienie przycisku, czyli aż linia PB0 wróci do stanu 1. Gdy tak się stanie, pętla zostaje przerwana i znów odczekujemy krótki czas na wygaśnięcie drgań przycisków. Teraz możemy kontynuować pętlę główną i czekać na kolejne naciśnięcie przycisku. Program jest następujący:
Do oczekiwania na naciśnięcie lub zwolnienie przycisku wykorzystujemy tutaj pętle while. A dlaczego nie instrukcję if? Początkującym często mylą się te instrukcje. Różnica jest zasadnicza: pętla while testuje swój warunek, jeśli jest spełniony, wykonuje instrukcję, po czym wraca na początek i znów testuje warunek. Działania te są powtarzane w kółko, aż warunek osiągnie wartość 0. Wtedy pętla zostaje przerwana i mikrokontroler wykonuje kolejną operację w programie. Instrukcja warunkowa if też testuje swój warunek i jeśli ma wartość różną od zera to wykonuje jedną z dwóch operacji. Jednakże instrukcja if nigdy nie wraca na swój początek. Działa jednorazowo. A przecież mikrokontroler musi cierpliwie czekać, aż użytkownik naciśnie przycisk. Należy cyklicznie sprawdzać stan linii PB0, aby wykryć moment tego naciśnięcia. Tutaj właśnie używamy instrukcji pętli warunkowej:
while(PINB & 0b000001);
Pętla ta oblicza wyrażenie w nawiasach. Jeśli jest prawdziwe (czyli PB0=1), to wraca na początek i znów wylicza wyrażenie. Dlaczego? Ponieważ po nawiasie zamykającym wyrażenie nie umieściliśmy żadnej instrukcji do wykonania wewnątrz pętli. To nie błąd. Tak właśnie ma być. Celem tej pętli jest cykliczne sprawdzanie, czy linia PB0 ma wartość 1, co oznacza, że przycisk W nie jest wciśnięty. Wykonanie programu "zatrzymuje się" w tym miejscu do czasu, aż PB0 przyjmie stan 0. Wtedy wyrażenie w nawiasach przestanie być prawdziwe i pętla zostanie przerwana. Po wyjściu z pętli mikrokontroler zmienia na przeciwny stan linii PB0, po czym odczekuje 10 ms na wygaśnięcie drgań styków. To ważne. Jeśli nie zaczekasz, to dalsza część programu może zawieść. Druga pętla:
while((PINB & 0b000001) == 0);
Wykonuje zadanie odwrotne do pętli pierwszej. Czeka na zwolnienie przycisku, czyli aż linia PB0 wróci do stanu 1. Wyjaśnienia wymaga zawartość nawiasów instrukcji while. Umieściliśmy tam wyrażenie porównawcze. Wyrażenie to jest prawdziwe, gdy wynik PINB & 0b000001 jest równy zero. W języku C jest więcej takich wyrażeń i możesz je stosować w instrukcjach while oraz if:
|
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe