====== 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, połączenie z układem
- 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:
/*
* main.c
*
* Created on: 26-02-2011
* Author: Maciek
* Najprostszy program
*/
//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;
//Nieskończona pętla
//W C 0 oznacza fałsz a każda inna liczba to prawda
while(1)
{
PORTB ^= 1; //przełącz pin 0 portu B (0->1 lub 1->0)
_delay_ms(1000); //Czekaj 1 sekundę
}
}
===== Porty wejścia wyjścia =====
Najbardziej podstawowym elementem mikroprocesora są porty wejścia/wyjścia, umożliwiają one podstawową komunikację programu z peryferiami układu. Pozwalają na proste operacje (zapalenie diody, włączenie brzęczyka, załączenie tranzystora, przekaźnika itd.) jak również na bardziej skomplikowane operacje (przetwarzanie C/A((cyfrowo-analogowe)), realizację interfejsów). Większość pinów (wyprowadzeń, nóżek układu) jest kontrolowana przez porty I/O((Input/Output - wejścia/wyjścia)) (oczywiście piny posiadają różne funkcje, wiele z nich może pracować jako I/O lub pełnić inną funkcję. Patrz nota katalogowa układu rozdział 12.3 __Alternate Port Functions__), są niezwykle uniwersalnym narzędziem.\\
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 (LO, 0) Prąd wpływa do układu, napięcie = potencjałowi masy
* 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,PORTx,PINx (analogicznie dla wszystkich 8 [0->7] pinów). Port składa się z 8 przerzutników czyli bitów informacji i można go interpretować jako 8 bitową liczbę. Porty są dostępne przez szynę adresową a ich modyfikacja w C jest bardzo prosta, wystarczy użyć ich nazwy jak zmiennej (Patrz przykłady poniżej).
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->PB7)
* 6 pinów portu C (PC0->PC5)((Można skonfigurować pin reset jako port PC6 wtedy mamy do dyspozycji 7 pinów portu C ale na razie warto zapomnieć o takiej możliwości.))
* 8 pinów portu D (PD0->PD7)
To co tutaj napisałem to minimum wiedzy. Portom I/O poświęcony jest cały 12 rozdział noty katalogowej, warto go całego przeczytać i spróbować zrozumieć.
===== 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 ====
uint8_t # 8 bitowa liczba bez znaku 0->255 (unsigned char)
int8_t # 7 bitowa liczba ze znakiem -128->127 (signed char)
uint16_t # 16 bitowa liczba bez znaku (unsigned int)
int16_t # 15 bitowa liczba ze znakiem (signed int)
uint32_t # 32 bitowa liczba bez znaku (unsigned long int)
int32_t # 31 bitowa liczba ze znakiem (signed long int)
char* # Standardowy string w C
==== Zapis liczb ====
Poniższe przypadki są jednoznaczne:
uint8_t liczba = 0xCD #Zapis hexadecymalny
uint8_t liczba = 0b11001101 #Zapis binarny
uint8_t liczba = 205 #Zapis dziesiętny
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:uint8_t i = 0b00000010; //dziesiętnie 2
i=(i<<3);
//W wyniku operacji i będzie równe 0b00010000 czyli 16
Przesunięcie w lewo o 1 miejsce daje w wyniku to samo co mnożenie liczby przez 2, przesunięcie w lewo o 3 miejsca daje to samo co mnożenie przez 8 (2^3). Przesuwanie bitów jest szybsze od mnożenia! (Jeżeli liczba nie mieści się w rejestrze tracimy najstarsze bity)
* Przesunięcie bitowe w prawo np:uint8_t i = 0b00100000; //dziesiętnie 32
i=(i>>4);
//W wyniku operacji i będzie równe 0b00000010 czyli 2
Analogicznie do powyższego przykładu można dzielić (Tracimy część ułamkową!)
* Zapalanie bitu (set bit) LICZBA |= (1<
* Gaszenie bitu (clear bit) LICZBA &= ~(1<
* Przełączanie bitu (toggle bit) LICZBA ^= (1<
==== 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).
/*
* main.c
*
* Created on: 2-03-2011
* Author: Maciek
* Najprostszy zegar binarny
*/
#include
#include
//Knfiguracja sprzętowa: do portu B podpięte są 4 diody do pinów 0-3
int main()
{
DDRB = 0x0F; //Modyfikacja rejestru DDRB: Ustaw piny 0-3 portu B jako wyjście
PORTB = 0x00; //Modyfikacja rejestru PORTB: Ustaw początkowo wszystkie piny portu B na stan niski
//Nieskończona pętla
while(1)
{
PORTB++; //Inkrementacja wartości rejestru
_delay_ms(1000); //Czekaj 1 sekundę
}
// 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)
}
/*
* main.c
*
* Created on: 2-03-2011
* Author: Maciek
* Kolejny przykład
*/
#include
#include
//Knfiguracja sprzętowa: do portu B podpięte są 4 diody do pinów 0-3
//Dobrze jest na początku okrelić połączenia w poniższy sposób, w razie zmiany
//konfiguracji sprzętowej łatwiej jest zaminić dane w jednym miejscu niż w 50 :)
#define LED_DIR DDRB
#define LED_P PORTB
int main()
{
//Inicjalizacja portów
LED_DIR = 0x0F;
LED_P = 0x00;
//Zapal pierwszą diodę
LED_P |= (1<<0);
_delay_ms(1000);
//Zapal 3 diodę
LED_P |= (1<<2);
_delay_ms(1000);
//Przełącz (zgaś) pierwszą diodę
LED_P ^= (1<<0);
_delay_ms(1000);
// Zgaś 3 diodę
LED_P &=~(1<<2);
//Nieskończona pętla
for(;;);
}
==== Zadania ===
* Zrealizuj efekt podobny do "[[http://www.youtube.com/watch?v=s9do4hZhaSo|Oka Cylona]]"
* 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, na czas programowania należy odłączyć wszystko co jest podpięte pod te złącza.**
* 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ą!