Miesięczne archiwum: Wrzesień 2015

Objaśnienia instrukcji użytych w lekcji 1

W tym miejscu chciałam Wam wyjaśnić pewne kwestie, które są niezbędne do pełnego zrozumienia lekcji 1 dot. programowania. Postanowiłam ująć to w osobnym poście, aby nie wprowadzać zbyt wielu informacji na początek w obawie, że pierwsza lekcja stałaby się zbyt skomplikowana.

We „wstępie…” pisałam, że oprócz podstaw elektroniki do nauki programowania będzie Wam potrzebna znajomość systemów liczbowych, o których pisałam tutaj. Teraz nieco rozwinę tą kwestię wyjaśniając zapis:

DDRD |= 1<<0;

Jak już wiemy rejestr DDRD określa czy nóżki należące do portu D mają być wejściami czy wyjściami.

piny atmegi8 z rejestrami

Nóżek ATmegi8 należących do portu D jest 8, są to nóżki: PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7 (na schemacie powyżej są one oznaczone kolorem niebieskim).

Domyślnie wszystkie nóżki mikrokontrolera są wejściami, co możemy zapisać w ten sposób:

DDRD = 0b00000000

(0b to specyficzne dla kompilatora AVR-GCC oznaczenie, które niejako tłumaczy mikrokontrolerowi, że oto teraz użyjemy liczby zapisanej w postaci binarnej)

Każde z zer występujących po przedrostku „0b” oznacza inną nóżkę portu D. Zero leżące maksymalnie po prawej stronie oznacza nóżkę PD0, a zero maksymalnie po lewej nóżkę PD7.

dod1

W lekcji pierwszej programowania diodę LED podłączaliśmy pod nóżkę drugą ATmegi8, oznaczoną jako nóżka PD0. Dlatego zależało nam, aby nóżka PD0 była wyjściem, a nie wejściem. W tym celu wystarczy zmienić odpowiednie zero na jedynkę:

DDRD = 0b00000001

W tym miejscu możecie zarzucić mi, że przecież w lekcji 1 programowania nie stosowaliśmy takiego zapisu! To prawda, zapisaliśmy to w ten sposób:

DDRD |= 1<<0;

Skąd taki zapis? W tak prostym i krótkim programie jak nasz, może rzeczywiście łatwiej i prościej byłoby użyć „DDRD = 0b00000001″, ale celowo użyłam „trudniejszego” zapisu, ponieważ jest on bardziej przydatny podczas pisania dłuższych, skomplikowanych programów, a poza tym „prostszego” zapisu „nie rozumieją” inne kompilatory oprócz AVR-GCC. Dlatego wydaje mi się, że lepiej od razu przyzwyczaić się do takiego zapisu.

Teraz wyobraźmy sobie, że jesteśmy w trakcie pisania dłuższego programu, w którym zdefiniowaliśmy już część nóżek należących do portu D, a teraz chcemy tylko zdefiniować nóżkę PD0 jako wyjście. Gdybyśmy użyli zapisu: „DDRD = 0b00000001″ wszystkie zaprogramowane dotychczas nóżki portu D zmieniłyby się w wejścia, a tylko ostatnia nóżka PD0 byłaby wyjściem. Dzieki zapisowi „DDRD |= 1<<0″ nóżka PD0 zmieni się w wyjście, a wszystkie pozostałe nóżki pozostaną niezmienione, niezależnie czy wcześniej zaprogramowaliśmy je jako wejścia, wyjścia czy może, tak jak to ma miejsce w naszym przypadku, pozostawiliśmy je domyślnie wejściami.

Co właściwie oznacza zapis „|= 1<<0″?

Dwa znaki „<<” oznaczają przesunięcie w lewą stronę. W naszym przypadku logiczne „1” przesuwamy o zero miejsc, więc jedynka pozostanie na miejscu ostatnim, czyli oznaczającym nóżkę PD0.

Gdyby w lekcji 1 dioda LED została podłączona np. do nóżki PD2, nasz zapis wyglądałby następująco:

DDRD |=1<<2

Logiczne jeden przesunęłoby się o dwa miejsca i znalazło na trzecim miejscu od prawej (DDRD = 0b00000100).

Ta pionowa kreska „|” oznacza funkcję logiczną OR, czyli sumę. Każemy naszemu programowi dodać te logiczne zera i jedynkę do domyślnych ustawień nóżek rejestru portu D:

dod2

Jak wyglądałby wynik dodawania gdyby zamiast domyślnych ustawień dla portu D, mielibyśmy inne, np. takie: DDRD = 0b10101100?

dod3Jak widzicie w wyniku dodawania poprzednie ustawienia dla portu D nie uległy zmianie za wyjątkiem ostatniej nóżki PD0, która z logicznego zera (wejścia) zmieniła się w logiczne 1 (wyjście).

Na marginesie dodam, że w standardzie w języku C nie ma możliwości zapisania liczby systemem binarnym (wyjątkiem są tu niektóre kompilatory, np. AVR-GCC, które umożliwiają wprowadzenie liczby binarnie z przedrostkiem 0b, np. 0b00000001), ale jak najbardziej można użyć zapisu heksadecymalnego, czyli szesnastkowego. W tym celu taką liczbę należy poprzedzić przedrostkiem „0x”, np. 0x01.

Teraz przyjrzyjmy się bliżej instrukcji wpisanej w pętle „while”:

PORTD |=_BV(PD0);

Znów mamy tu do czynienia z operatorem „|”, czyli sumą, ale co oznacza: „_BV(PD0)”? Otóż „_BV(numer_bitu)” to pomocnicze makro, czyli swoiste polecenie rozumiane przez kompilator. „_BV(numer_bitu) oznacza to samo co omawiane przed chwilą „1<<(numer_bitu)”. Który zapis wybierzecie zależy od Was.

W nawias następujący po „_BV” możecie wpisać zarówno nazwę nóżki mikrokontrolera (PD0) lub odpowiadający jej numer bitu (1), pamiętając, że bity numerujemy odpowiednio: PD0 to bit 1, PD1 to bit 2, …, PD7 to bit 8.

Instrukcja „PORTD |=_BV(PD0)” oznacza, że do domyślnie ustawionych lub zdefiniowanych przez nas wcześniej w programie wartości logicznych na poszczególnych nóżkach mikrokontrolera program ma dodać logiczne 1 do nóżki PD0 (bit 1).

dod4Na nóżce PD0 pojawi się logiczne 1, co spowoduje wyłączenie diody LED.

Kolejna instrukcja zawarta w pętli „while” to:

PORTD &=~_BV(PD0);

Znak „&” to operator oznaczający iloczyn (AND), a „~” to negacja.

Makrodefinicję  „_BV(PD0)” możemy zapisać jako 0b00000001. Po jej zanegowaniu (znak ~ na przedzie) otrzymamy 0b11111110. Operator „&” każe nam to pomnożyć z ustawieniami portu D (wprowadzonymi uprzednio instrukcją „PORTD |=_BV(PD0)”), przez co w konsekwencji otrzymamy 0b00000000.

dod5Na nóżce PD0 pojawi się logiczne 0, co spowoduje włączenie diody LED.

Więcej o operacjach bitowych znajdziecie tutaj.