pl:avrc:art:0x02
Differences
This shows you the differences between two versions of the page.
pl:avrc:art:0x02 [2012/09/29 13:45] – created mkucia | pl:avrc:art:0x02 [2012/09/29 19:27] (current) – mkucia | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== 0x02 Migająca dioda ====== | ||
+ | Migająca dioda to typowa aplikacja "Hello world" w świecie MCU. Pozwala ona przetestować toolchain czyli zestaw narzędzi za pomocą których wygenerujemy i wgramy kod. Pozwala ona również sprawdzić czy układ działa. | ||
+ | |||
+ | ===== Wymagania ===== | ||
+ | |||
+ | - Działający programator, | ||
+ | - Skonfigurowane środowisko | ||
+ | - Diody LED, odpowiednie rezystory | ||
+ | - Dobra znajomość języka C | ||
+ | - Znajomość praw logiki Boolea, podstawowych operacji NOT, AND, OR, XOR | ||
+ | |||
+ | ===== Pierwszy program ===== | ||
+ | Do pinu PB0 należy podpiąć przez odpowiedni rezystor diodę LED (w dowolnym kierunku). Stwórz nowy projekt i w pliku main.c wklej poniższy kod: | ||
+ | <code c> | ||
+ | /* | ||
+ | * main.c | ||
+ | * | ||
+ | | ||
+ | | ||
+ | | ||
+ | */ | ||
+ | |||
+ | //io.h zawiera informacje o portach w układzie (np DDRB, PORTB) | ||
+ | #include < | ||
+ | //delay.h zawiera podprogram _delay_ms | ||
+ | #include < | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | DDRB = 0xFF; | ||
+ | PORTB = 0x00; | ||
+ | |||
+ | // | ||
+ | //W C 0 oznacza fałsz a każda inna liczba to prawda | ||
+ | | ||
+ | { | ||
+ | PORTB ^= 1; // | ||
+ | _delay_ms(1000); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | ===== Porty wejścia wyjścia ===== | ||
+ | Najbardziej podstawowym elementem mikroprocesora są porty wejścia/ | ||
+ | |||
+ | Praktycznie porty układu mogą znajdować się w 3 stanach (Są trójstanowe): | ||
+ | * Wysokim (HI, 1) Prąd wypływa z układu, napięcie = napięciu zasilania | ||
+ | * Niskim | ||
+ | * Wysokiej impedancji (Hi-Z, floating) prąd nie wpływa ani nie wypływa. Można wyobrażać sobie jakby wyprowadzenie nie było do niczego podłączone lub jakby rezystancja wewnętrzna dążyła do nieskończoności. | ||
+ | |||
+ | Co więcej za pomocą portu możemy: | ||
+ | * Wpisywać dane (write) uC ustawia wyprowadzenie w określony stan logiczny. | ||
+ | * Wczytywać dane (read) uC sprawdza w jakim stanie logicznym znajduje się wyprowadzenie. | ||
+ | |||
+ | Sterowaniem zachowania portów zajmują się 3 rejestry: | ||
+ | * DDRx - Data Direction Register - kontroluje kierunek działania portu (wejście czy wyjście) | ||
+ | * PORTx - Pozwala na ustawianie stanu logicznego portu (HI/LO) | ||
+ | * PINx - Pozwala odczytywać stan logiczny portu | ||
+ | Każdy z powyższych rejestrów jest 8 bitowy. W przypadku układu ATmega8 zamiast x możemy wpisać B, C lub D (takie porty posiada układ - patrz nota). Pinowi numer 0 odpowiadają zerowe bity rejestrów DDRx, | ||
+ | |||
+ | Możliwe stany portów I/O przedstawia poniższa tabela: | ||
+ | ^ DDR ^ PORT ^ I/O ^ Komentarz ^ | ||
+ | | 0 | 0 | Input | Floating | | ||
+ | | 0 | 1 | Input | Hi!((Pomimo że wejście, będzie pracował również jak wyjście w stanie Hi (podciągnięcie pod Vcc))) | | ||
+ | | 1 | 0 | Output| Lo | | ||
+ | | 1 | 1 | Output| Hi | | ||
+ | |||
+ | |||
+ | Układ ATmega8 posiada: | ||
+ | * 8 pinów portu B (PB0-> | ||
+ | * 6 pinów portu C (PC0-> | ||
+ | * 8 pinów portu D (PD0-> | ||
+ | |||
+ | To co tutaj napisałem to minimum wiedzy. Portom I/O poświęcony jest cały 12 rozdział noty katalogowej, | ||
+ | ===== Programowanie w C ===== | ||
+ | Na początku w ramach przypomnienia trochę podstawowych informacji dotyczących zapisu w C oraz trochę informacji specyficznych dla C AVR. | ||
+ | ==== Typy danych ==== | ||
+ | <code c> | ||
+ | uint8_t | ||
+ | int8_t | ||
+ | |||
+ | uint16_t # 16 bitowa liczba bez znaku (unsigned int) | ||
+ | int16_t | ||
+ | uint32_t # 32 bitowa liczba bez znaku (unsigned long int) | ||
+ | int32_t | ||
+ | |||
+ | char* # Standardowy string w C | ||
+ | </ | ||
+ | ==== Zapis liczb ==== | ||
+ | Poniższe przypadki są jednoznaczne: | ||
+ | <code c> | ||
+ | uint8_t liczba = 0xCD #Zapis hexadecymalny | ||
+ | uint8_t liczba = 0b11001101 | ||
+ | uint8_t liczba = 205 # | ||
+ | </ | ||
+ | Warto umieć płynnie zamieniać liczby pomiędzy tymi 3 systemami liczbowymi. | ||
+ | ====Podstawowe operacje na bitach==== | ||
+ | * Bity numerujemy od 0 np. 8 bitowa liczba ma bity od 0 do 7. Znaczenie bitów rośnie od prawej do lewej. | ||
+ | * Przesunięcie bitowe w lewo np:<code c> | ||
+ | i=(i<< | ||
+ | //W wyniku operacji i będzie równe 0b00010000 czyli 16 </ | ||
+ | * Przesunięcie bitowe w prawo np:<code c> | ||
+ | i=(i>> | ||
+ | //W wyniku operacji i będzie równe 0b00000010 czyli 2 </ | ||
+ | |||
+ | * Zapalanie bitu (set bit) <code c>LICZBA |= (1<< | ||
+ | //To samo co: | ||
+ | LICZBA = LICZBA | (1<< | ||
+ | // | to logiczna operacja OR | ||
+ | // | ||
+ | uint8_t i = 0; | ||
+ | i |= (1<< | ||
+ | //W wyniku operacji i będzie równe 0b00001000 | ||
+ | </ | ||
+ | * Gaszenie bitu (clear bit) <code c>LICZBA &= ~(1<< | ||
+ | // & to logiczna operacja AND | ||
+ | // ~ to logiczna operacja NOT | ||
+ | // | ||
+ | uint8_t i = 0xFF; | ||
+ | i &= ~(1<< | ||
+ | //W wyniku operacji i będzie równe 0b11111011 | ||
+ | </ | ||
+ | * Przełączanie bitu (toggle bit) <code c>LICZBA ^= (1<< | ||
+ | //^ to logiczna operacja XOR | ||
+ | // | ||
+ | uint8_t i = 0b10101010; | ||
+ | i ^= (1<< | ||
+ | i ^= (1<< | ||
+ | //W wyniku operacji i będzie równe 0b10101100 | ||
+ | </ | ||
+ | ==== Przykłady ==== | ||
+ | W poniższych przykładach diody zapalane są stanem logicznym 1 czyli łączymy anodę przez rezystor do układu a katodę do masy. Połączenie odwrotne spowoduje że diody będą zapalane stanem logicznym niskim (anoda do VCC, katoda do uC). | ||
+ | <code c>/* | ||
+ | * main.c | ||
+ | * | ||
+ | | ||
+ | | ||
+ | | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | DDRB = 0x0F; // | ||
+ | PORTB = 0x00; // | ||
+ | |||
+ | // | ||
+ | | ||
+ | { | ||
+ | PORTB++; | ||
+ | _delay_ms(1000); | ||
+ | } | ||
+ | |||
+ | // Lepiej nie dopuszczać programu żeby wychodził z main (stosować nieskończoną pętle) | ||
+ | // Dopoki nasz program nie korzysta z przerwań w wypadku braku pętli kompilator sam ją doda, ale z instrukcją | ||
+ | // blokującą przerwania! (Stan na 2011, wcześniej mogło być inaczej) | ||
+ | } | ||
+ | </ | ||
+ | <code c> | ||
+ | /* | ||
+ | * main.c | ||
+ | * | ||
+ | | ||
+ | | ||
+ | | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // | ||
+ | |||
+ | //Dobrze jest na początku okrelić połączenia w poniższy sposób, w razie zmiany | ||
+ | // | ||
+ | #define LED_DIR DDRB | ||
+ | #define LED_P PORTB | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | // | ||
+ | LED_DIR = 0x0F; | ||
+ | LED_P = 0x00; | ||
+ | |||
+ | //Zapal pierwszą diodę | ||
+ | LED_P |= (1<< | ||
+ | _delay_ms(1000); | ||
+ | |||
+ | //Zapal 3 diodę | ||
+ | LED_P |= (1<< | ||
+ | _delay_ms(1000); | ||
+ | |||
+ | // | ||
+ | LED_P ^= (1<< | ||
+ | _delay_ms(1000); | ||
+ | |||
+ | // Zgaś 3 diodę | ||
+ | LED_P & | ||
+ | |||
+ | // | ||
+ | for(;;); | ||
+ | } | ||
+ | </ | ||
+ | ==== Zadania === | ||
+ | * Zrealizuj efekt podobny do " | ||
+ | * Zrealizuj program zapalający diodę na coraz krótsze odcinki czasu | ||
+ | ==== Uwagi ==== | ||
+ | * **Podłączenie programatora nie zakłóci pracy urządzenia (wysoka impedancja na złączu programatora) ale obecność czegokolwiek na pinach mosi miso sck na pewno zakłóci proces programowania, | ||
+ | * Chwilowa obciążalność prądowa portu I/O w układzie ATmega8A to 40 [mA]((Nota katalogowa 25.1 __Absolute Maximum Ratings__)) nie powinno się obciążać wyjścia prądem ciągłym większym niż 30 [mA]! | ||
+ | * Nie wiesz czegoś? Sprawdź notę katalogową! | ||