Kurs AVR GCC, cz 4


XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 1 of 27
Artykuł pochodzi ze strony XYZ HOBBY ROBOT (xyz.isgreat.org)
Kurs AVR-GCC cz.4(v2)
17.08.2009 ABXYZ
Dużo wody upłynęło Wisłą do morza
od czasu ukazania się trzeciej część
niniejszego kursu i wreszcie jest
część czwarta. Ostatnio omawiane
były: zmienne, operatory, pętle
i instrukcje sterujÄ…ce jak: if, switch.
W tej części kursu przerobimy
tablice, struktury i funkcje, czyli
dalszy ciąg podstaw języka C. Jak
poprzednio, wpierw omówię
wymienione tematy, a następnie
zestawimy układ z AVRem
i uruchomimy kilka przykładowych programów. W tej części kursu
będziemy bawić się przyłączając do AVRa klawiaturę telefoniczną
i piezo-buzzer(bez generatora).
Tablice
Gdy w programie mamy większą ilość danych jednego typu, to
zamiast tworzyć wiele pojedynczych zmiennych, lepiej będzie
utworzyć tablicę. W tablicy można przechowywać jednocześnie
wiele zmiennych jednakowego typu. Wszystkie elementy tablicy sÄ…
ponumerowane, dostęp do dowolnej zmiennej w tablicy uzyskuje
siÄ™ podajÄ…c nazwÄ™ tablicy i numer elementu.
W języku C tablicę przed pierwszym użyciem należy wcześniej
zdefiniować. Tablicę definiuje się pisząc kolejno: typ danych, nazwę
tablicy i objętą parą nawiasów kwadratowych [] liczbę elementów
tablicy. Definicję należy zakończyć średnikiem.
int main(void)
{
/* Definicja tablicy 'pomiar' o 64 elementach typu char */
char pomiar[64];
/* Definicja tablicy 'wysokosc' o 32 elementach typu int */
int wysokosc[32];
/* Definicja tablicy 'temperatura' o 16 elementach typu
double */
double temperatura[16];
Elementami tablic mogą być wartości typów: char, int, float, double
oraz: struktury, unie, pola bitowe i wskazniki o których będzie
mowa w następnej części kursu. Elementami tablicy mogą być też
same tablice; w taki sposób tworzy się w języku C tablice
wielowymiarowe.
/* 2 elementowa tablica 40 elementach tablic typu char */
char bufor[2][40];
/* Tablica trójwymiarowa */
char jakas_tablica[4][8][3];
Definiując tablicę można jednocześnie tablicę inicjalizować
wartościami początkowymi. W tym celu dopisuje się na końcu
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 2 of 27
definicji: operator = , a po nim, objęte parą nawiasów klamrowych
{}, wartości początkowe kolejnych elementów tablicy rozdzielone
przecinkami.
/* Definicja tablicy z jednoczesną inicjalizacją wartościami
początkowymi dwóch pierwszych elementów tablicy */
char tablica[4] = {0x01,0x02};
/* Definicja tablicy z jednoczesną inicjalizacją wartościami
początkowymi wszystkich czterech elementów. W takim przypadku
można nie wpisywać w definicji ilości elementów tablicy,
kompilator sam to wyliczy. */
char tablica[] = {0x01,0x02,0x04,0x08};
/* Definicja z inicjalizacjÄ… tablicy dwuwymiarowej */
char inna_tablica[][2] = { 0x31,0x02,
0xf1,0x02,
0x41,0xf2,
0xf1,0xc2,
0x11,0xa2 };
W programie elementami tablicy posługujemy się w identyczny
sposób jak pojedynczymi zmiennymi. Każdy element tablicy ma
nazwę składającą się z nazwy tablicy oraz objętego parą nawiasów
kwadratowych indeksu(numeru elementu). Elementy tablicy
numerowane są zaczynając od zera, a nie od jedynki, należy to
koniecznie zapamiętać.
/* Przypisanie pierwszemu elementowi tablicy wartości 23.5 */
temperatura[0] = 23.5;
/* Przypisanie drugiemu elementowi tablicy wartości 22.0 */
temperatura[1] = 22.0;
/* Przypisanie ósmemu elementowi tablicy wartości 17.5 */
temperatura[7] = 17.5;
/* Przypisanie jakiejs_zmiennej wartości czwartego elementu
tablicy */
jakas_zmienna = temperatura[3];
Trzeba też pamiętać, że jeżeli zdefiniujemy tablicę n elementową
i spróbujemy zapisać coś pod indeksem równym lub większym n to
kompilator nie zgłosi błędu, ale skutkować to może nieprawidłowym
działaniem programu.
Przy operacjach na tablicach szczególnie użyteczna może być
instrukcja pętli for, gdzie licznik pętli będzie indeksem tablicy.
unsigned char i,j;
int tablica_1[16];
int tablica_2[16];
int tablica_3[10][10];
/* Kolejnym elementom tablicy przypisane zostaną wartości:
0,10,20,30..150 */
for(i = 0; i < 16; i++)
tablica_1[i] = i * 10;
/* Kopiowanie zawartości tablica_1 do tablica_2 */
for(i = 0; i < 16; i++)
tablica_2[i] = tablica_1[i];
/* Wypełnienie tablicy dwuwymiarowej */
for(i = 0; i < 9; i++)
for(j = 0; j < 9; j++)
tablica_3[i][j] = i * j;
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 3 of 27
Na tym etapie nauki, te informacje na temat tablic mam wystarczÄ….
Struktury
Strukturą w języku C nazywa się grupę zmiennych oznaczoną jedną
nazwą. Na przykład, aby opisać okrąg (figurę geometryczną), którą
program będzie rysował na ekranie wyświetlacza graficznego,
potrzebujemy co najmniej trzech wartości: dwóch współrzędnych
położenia środka okręgu oraz długość promienia. Dla jednego
egzemplarza okręgu można by utworzyć w pamięci trzy zmienne
o nazwach: x, y, r. Jednak, gdy przyjdzie potrzeba zapisać
w pamięci większą liczbę okręgów, lepiej w tym celu będzie
zdeklarować odpowiednią strukturę.
Deklaracja struktury informuje kompilator z jakich zmiennych
będzie składać się nowy typ struktury. Deklaracja struktury
rozpoczyna się od słowa struct i nazwy nowego typu struktury. Po
nazwie, pomiędzy parą nawiasów klamrowych {}, deklaruje się
zmienne składowe struktury. Cała instrukcja deklaracji kończy się
średnikiem.
/* Deklaracja struktury typu 'Okrag' */
struct Okrag
{
int x;
int y;
unsigned int r;
};
Sama deklaracja struktury nie tworzy jeszcze zmiennych w pamięci,
tworzy jedynie nowy typ. Aby utworzyć pamięci egzemplarz
struktury danego typu należy ją zdefiniować, w podobny sposób jak
definiuje się zmienne. Definicja zaczyna się od słówka struct, dalej
podaje się nazwę typu zdeklarowanego wcześniej struktury i nazwę
nowego egzemplarza struktury lub nazwy kilku egzemplarzy
oddzielone przecinkami; na końcu deklaracji stawia się średnik.
/* Definicja trzech struktur typu 'Okrag' o nazwach:
okrag_1, okrag_2, okrag_3 */
struct Okrag okrag_1, okrag_2, okrag_3;
Należy zapamiętać różnice między deklaracją a definicją: Deklaracja
nie rezerwuje miejsca w pamięci, a jedynie tworzy nowy typ
struktury. Dopiero definicja tworzy w pamięci konkretne
egzemplarze danego typu struktury. DeklarujÄ…c nowy typ struktury
można jednocześnie utworzyć(zdefiniować) jeden lub kilka jej
egzemplarzy; w tym celu, na końcu deklaracji, między nawiasem
zamykającym } a średnikiem, dopisuje się nazwę tworzonego
egzemplarza struktury lub nazwy kilku egzemplarzy rozdzielone
przecinkami, przykład:
/* Deklaracja struktury typu 'prostokÄ…t' z jednoczesnym
tworzeniem jej trzech egzemplarzy */
struct prostokat
{
int x;
int y;
unsigned int width;
unsigned int height;
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 4 of 27
} prostokat1, prostokat2, prostokat3;
Dostęp do zmiennych składowych struktury uzyskuje się podając
kolejno: nazwÄ™ egzemplarza struktury, operator kropka i nazwÄ™
składowej użytej w deklaracji struktury, przykłady:
/* Deklaruje strukturę bez podania nazwy typu (tak też
można) i jednocześnie tworzy dwa egzemplarze struktury
tego typu. */
struct
{
int x;
int y;
} punkt1, punkt2;
/* Dostęp do zmiennych składowych struktury
uzyskuje się używając operatora kropki. */
punkt1.x = 17;
punkt1.y = 20;
punkt2.x = 10;
punkt2.x = 18;
/* Składowymi struktury posługujemy się podobnie jak
zmiennymi prostego typu */
jakas_ zmienna = punkt1.x + punkt2.x;
inna_zmienna = punkt1.y + punkt2.y;
Struktury mogą wchodzić w skład innych struktur, w przykładzie
poniżej struktura typu Punkt jest jest zagnieżdżona w strukturze
typu Okrag.
/* Deklaracja struktury typu 'Punkt' */
struct Punkt
{
int x;
int y;
};
/* Deklaracja struktury typu 'Okrag' z jednoczesnym
utworzeniem trzech egzemplarzy tego typu struktury */
struct Okrag
{
struct Punkt o;
unsigned int r;
}okragA, okragB, okragC;
/* Dostęp do zmiennych składowych struktury uzyskuje
się używając operatora kropki. */
okragC.o.x = okragA.o.x + okragB.o.x;
Definiując strukturę można jednocześnie inicjalizować jej składowe
wartościami początkowym. W tym celu, w definicji, po nazwie nowo
tworzonego egzemplarza struktury, wstawia siÄ™ operator '=', i dalej,
między nawiasami klamrowymi {}, wartości początkowe kolejnych
składowych struktury rozdzielone przecinkami.
/* Definiując struktury można je jednocześnie inicjalizować
wartościami początkowymi. */
struct punkt p1={12,33}, p2={20,30};
Także można inicjalizować definiowaną strukturę zawartością
składowych innego egzemplarza struktury tego typu, przykład:
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 5 of 27
/* Definicja struktury z jednoczesnÄ… inicjalizacjÄ…
składowych */
struct punkt p3 = p1;
Używając operatora = można strukturę skopiować, tzn. przypisać
składowym jednego egzemplarza struktury wartości odpowiednich
składowych innego egzemplarza.
/* Kopiowanie struktury */
p2 = p1;
Funkcje
Program składa się z ciągu instrukcji zapisanych w pamięci
i wykonywanych jedna po drugiej. Obok instrukcji, które realizują
konkretne zadania, na przykład włączają i wyłączają diodę LED,
istnieją też specjalne instrukcje sterujące przebiegiem programu jak
np.: if-else, switch, for, while; które były tematem poprzedniej
części kursu.
Obok wspomnianych instrukcji sterujących istnieje możliwość
tworzenia podprogramów. Zwykle dłuższe programy dzielone są na
odrębne fragmenty, nazywane podprogramami lub procedurami.
Spośród podprogramów wyróżnić można podprogram pierwszy
(główny), od którego pierwszej instrukcji rozpoczyna się działanie
całego programu. Umieszczając w programie specjalną instrukcję -
instrukcję wywołania podprogramu - można wykonać wybrany
podprogram.
Przebieg przykładowego programu składającego się z podprogramu głównego i dwóch
podprogramów. Cały program zaczyna się od pierwszej instrukcji podprogramu głównego.
A jaki może być pożytek z podziału programu na podprogramy ? Na
przykład, jeśli jakiś dłuższy fragment kodu powtarza się w wielu
miejscach programu, oczywistym rozwiązaniem będzie umieścić ten
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 6 of 27
fragment w odrębnym podprogramie. W wyniku otrzymamy
znacznie krótszy program o przejrzystej strukturze. W tak
napisanym programie łatwiej jest wprowadzać zmiany. Oczywiście
sposób podziału programu nie bywa przypadkowy, podprogramy
zwykle realizujÄ… konkretne zadania np. odczyt czujnika
temperatury, włączenie sygnalizacji albo uruchomienie
serwomechanizmu.
W języku C podprogramy nazywa się funkcjami. Każdy program
w języku C musi zawierać funkcje o nazwie "main" - każda funkcja
ma nadanÄ… nazwÄ™ (identyfikator). Funkcja main jest wspomnianym
wcześniej podprogramem pierwszym(głównym), od którego
pierwszej instrukcji zaczyna się wykonanie całego programu.
/* Najprostszy program-całość umieszczona w funkcji main */
#define F_CPU 1000000L
#include
/* Definicja funkcji main */
int main(void)
{
/* Instrukcje naszego programu */
}
Obok funkcji main programista może tworzyć(definiować) własne
funkcje, można też wykorzystywać gotowe funkcje z biblioteki
standardowej języka C, dostarczonej razem z kompilatorem. Razem
z kompilatorem avr-gcc wykorzystuje się bibliotekę AVR Libc, która
pełni rolę biblioteki standardowej, dopasowanej do możliwości 8-
bitowych mikrokontrolerów AVR. W przykładach z kursu często
używana jest funkcja z biblioteki AVR Libc o nazwie _delay_ms.
Funkcja ta wprowadza do programu opóznienie o zadanym
w milisekundach okresie czasu; w avr-libc dostępna jest także
podobna funkcja _delay_us, dla której długość opóznienia podaje
siÄ™ w mikrosekundach.
Instrukcja wywołania funkcji składa się z nazwy funkcji i objętej
parą nawiasów okrągłych listy argumentów, i kończy się
średnikiem. Argumenty funkcji to dane przekazywane do funkcji,
jak na przykład wartość opóznienia w funkcji _delay_ms. Jeśli
funkcja oczekuje kilku argumentów, kolejne argumenty oddziela się
przecinkami. Gdy funkcja nie oczekuje żadnego argumentu, to
uruchamiajÄ…c jÄ…, po nazwie funkcji wstawiamy "pustÄ…" parÄ™
nawiasów okrągłych ().
/* Funkcje _delay_ms, _delay_us oczekują przy wywołaniu
jednego argumentu typu double -wartości opóznienia */
/* Argumentem funkcji jest wartość stała 80 */
_delay_ms(80);
/* Argumentem funkcji będzie wartość wyrażenia
jakas_zmienna+20 */
_delay_us(jakas_zmienna + 20);
/* Funkcje mogą oczekiwać jednego lub większej
liczby argumentów albo żadnego */
/* Jeśli jakaś funkcja oczekuje kilku argumentów, kolejne
argumenty oddziela siÄ™ przecinkiem. */
jakas_funkcja(10, 17.3, jakas_zmienna);
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 7 of 27
/* Inna funkcja nie oczekuje żadnego argumentu */
inna_funkcja();
Po zakończeniu działania funkcje mogą zwracać dane. Na przykład
funkcja rand z biblioteki standardowej zwraca wartość typu int,
wartością tą jest liczba pseudolosowa z przedziału od 0 do
RAND_MAX(0x7FFF). Jeżeli funkcja zwraca wartość, wtedy całe
wyrażenie: nazwa_funkcji(argumenty_funkcji) posiada wartość
i typ, podobnie zmienne. Wtedy, przykładowo, można instrukcję
wywołania funkcji "nazwa_funkcji(argumenty_funkcji)" postawić po
prawej stronie operatora przypisania, żeby zwróconą przez funkcję
wartość przypisać od zmiennej; albo umieścić w warunku instrukcji
if-else.
/* Funkcja rand, obliczajÄ…ca liczbÄ™ losowÄ…, zwraca
wartość typu int */
jakas_ zmienna = rand() + 100;
/* Jeśli wartość zwrócona przez funkcje rand będzie większa
od 10, wtedy warunek w instrukcji if będzie spełniony */
if( rand() > 10)
{
Funkcja może zwracać tylko wartość jednej zmiennej lub nie
zwracać niczego.
WÅ‚asnÄ… funkcjÄ™ tworzy(definiuje) siÄ™ wpisujÄ…c kolejno: typ
zwracanej wartości, nazwę nowej funkcji, listę parametrów funkcji
objętą parą nawiasów okrągłych (); deklaracje kolejnych
parametrów oddziela się przecinkami. W definicji funkcji argumenty
nazywa się parametrami. Następnie, między parą nawiasów
klamrowych {}, umieszcza siÄ™ instrukcja po instrukcji kod funkcji.
/* Definicja funkcji */
typ nazwa_funkcji(parametry)
{
/* Instrukcje wewnÄ…trz funkcji */
}
Jeśli w definicji funkcji jako zwracany typ wstawi się słówko void,
oznaczać to, że funkcja nie będzie niczego zwracać; a jeśli
wstawimy void w miejsce listy parametrów, to funkcja nie będzie
oczekiwać żadnych argumentów. W naszych krótkich programach
definicje funkcji będą umieszczane w pliku przed funkcją main.
/* Definicja funkcji które niczego nie zwraca i nie oczekuje
żadnych argumentów */
void io_init(void)
{
DDRD = 0x0f;
PORTD = 0xf0;
DDRB = 0x0f;
}
/* Definicja funkcji main */
int main(void)
{
/* Wywołanie funkcji io_init */
io_init();
Poniżej znajduje się przykład definicji funkcji, która przyjmuje dwa
argumenty typu unsigned int i nie zwraca niczego. Wartości
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 8 of 27
argumentów dostępne są wewnątrz funkcji w specjalnie tworzonych
zmiennych. W chwili wywołania funkcji, tworzone są zmienne
o typach i nazwach jakie znajdują się na liście parametrów.
Zmienne te są inicjalizowane wartościami argumentów podanych
przy wywołaniu funkcji. W przykładzie poniżej, w funkcji beep
dostępne są dwie zmienne o nazwach: frequency i duration
zawierające wartości wysłanych do funkcji argumentów.
/* Definicja funkcji beep */
void beep(unsigned int frequency, unsigned int duration)
{
unsigned int i,t,n;
t = 125000/frequency;
n = (250UL*duration)/t;
PORTB |= 0x01;
PORTB &= ~0x02;
for(i=0; i < n; i++)
{
PORTB ^= 0x01;
PORTB ^= 0X02;
_delay_loop_2(t);
}
}
/* Definicja funkcji main */
int main(void)
{
int f = 440, d = 250;
/* Przykłady wywołanie funkcji beep */
beep(1000,1000);
/* Do funkcji zostaną przekazane wartości zmiennych f i d */
beep(f,n);
Kolejny przykład to definicja funkcji, która zwraca wartość typu
unsigned char i oczekuje argumentu typu unsigned char. Tu nowÄ…
rzeczą jest instrukcja return, wymusza ona natychmiastowy powrót
z funkcji. Jeżeli funkcja coś zwraca, wtedy po prawej stronie
instrukcji return wstawia się wyrażenie, którego wartość zostanie
zwrócona po wyjściu z funkcji. Jak już pisałem, gdy funkcja coś
zwraca, instrukcja wywołania funkcji, całe wyrażenie:
"nazwa_funkcji(argumenty_funkcji)" ma przypisaną wartość
zwracanÄ… przez funkcjÄ™.
/* Funkcja zmienia wartość argumentu na bajt w kodzie BCD */
unsigned char bin2bcd(unsigned char bin)
{
/* Jeśli bin > 99, to funkcja zwraca wartość argumentu bin */
if(bin>99) return bin;
/* Funkcja zwraca wartość wyrażenia po prawej stronie
instrukcji return */
return bin/10<<4 | bin%10;
}
/* Wywołanie funkcji bin2bcd, zwrócona przez funkcję
wartość 0x99 zostanie przypisane zmiennej */
jakas_zmienna = bin2bcd(99);
Tablice przekazywane są do funkcji w odmienny sposób, niż
zmienne. Jeśli przekazuje się do funkcji argumenty nie będące
tablicami, to w momencie wywołania funkcji tworzone są zmienne
o nazwach parametrów funkcji, zmienne te inicjalizowane są
wartościami argumentów podanymi w instrukcji wywołania funkcji.
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 9 of 27
Natomiast, jeśli argumentem funkcji jest tablica, to w momencie
wywołania funkcji, nie jest tworzona kopia tej tablicy; wewnątrz
funkcji wszystkie działania przeprowadzane są na oryginalnej
tablicy podanej jako argument w instrukcji wywołania funkcji.
Przykład:
/* Definicja funkcji */
void funkcja(int n, int tablica[])
{
/* Wypełnia tablicę */
for(; n; n--)
tablica[n-1] = n;
}
int main(void)
{
/* Definicja tablicy */
int tablica[16];
int n = 16;
/* Zmienne przekazywane są do funkcji przez wartość, a tablice
przez wskaznik. Po wykonaniu funkcji wartość zmiennej
n nie zmieni się, natomiast tablica zostanie wypełniona */
funkcja(n, tablica);
PodsumowujÄ…c, argumenty do/z funkcji przekazywane sÄ… przez
wartość, z wyjątkiem tablic które przekazywane są poprzez
wskaznik. Wskazniki omówię szczegółowo w kolejnej części kursu.
Zakres widoczności zmiennych
Zmienne mogą być deklarowane wewnątrz funkcji(zmienne lokalne)
albo poza wszystkimi funkcjami(zmienne globalne). Zmienne
deklarowane poza wszystkimi funkcjami, na poczÄ…tku pliku, istniejÄ…
przez cały czas działania programu i są dostępne we wszystkich
funkcjach w pliku. Zmienne deklarowane wewnÄ…trz funkcji tworzone
są w momencie wywołania funkcji i istnieją jedynie do momentu
zakończenia działania funkcji. Zmienne deklarowane wewnątrz
funkcji dostępne są tylko w obrębie funkcji, w której zostały
zdeklarowane. W różnych funkcjach mogą istnieć zmienne o tej
samej nazwie.
/* Zmienna dostępna we wszystkich funkcja w całym pliku */
int zmienna_globalna ;
/* Definicja funkcji */
void funkcja(int n, int tablica[])
{
/* Zmienna dostępna tylko w tej funkcji */
double zmienna_lokalna;
zmienna_globalna = 17;
}
/* Definicja innej funkcji */
void inna_funkcja(char s )
{
/* Zmienna dostępna tylko w tej funkcji */
double zmienna_lokalna;
zmienna_lokalna = zmienna_globalna;
}
Jeśli w funkcji zostanie utworzona zmienna o nazwie identycznej
z nazwÄ… istniejÄ…cej zmiennej utworzonej poza funkcjami, wtedy,
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 10 of 27
w funkcji, pod tą nazwą dostępna będzie zmienna deklarowane
w funkcji.
/* Zmienna dostępna we wszystkich funkcja w całym pliku */
int jakas_zmienna ;
/* Definicja funkcji */
int jakas_funkcja(void)
{
/* Zmienna dostępna tylko w tej funkcji */
int jakas_zmienna;
/* Zapis nastÄ…pi do zmiennej zdefiniowanej w tej funkcji. */
jakas_zmienna = 17;
}
Zmienne deklarowane wewnÄ…trz funkcji tworzone sÄ… w chwili
wejścia do funkcji i istnieją tylko do momentu wyjścia z funkcji.
Zawartość tych zmiennych po wyjściu z funkcji jest bezpowrotnie
tracona. Jeżeli zależy nam, aby zmienna deklarowana wewnątrz
funkcji istniała przez cały okres działania programu, i aby pomiędzy
kolejnymi wywołaniami funkcji zawartość tej zmiennej nie była
tracona, to należy deklaracje tej zmiennej poprzedzić słówkiem
static, takie zmienne nazywa siÄ™ zmiennymi statycznymi. Zmienne
deklarowane poza wszystkimi funkcjami otrzymują wartość
początkową równą zero, natomiast zmienne deklarowane wewnątrz
funkcji mają nieokreśloną, przypadkową wartość początkową, ale
nie zmienne statyczne, te również otrzymują wartość początkową
równą zero.
/* Definicja funkcji */
int licz(int n)
{
/* Definicja statycznej zmiennej licznik, która będzie
istnieć przez cały okres działania programu */
static int licznik;
licznik += n;
return licznik;
}
int main(void)
{
/* Funkcja zwróci wartość 2 */
jakas_zmienna = licz(2);
/* Funkcja zwróci wartość 5 */
jakas_zmienna = licz(3);
Kod BCD
W elektronice cyfrowej nierzadko wykorzystywany jest kod BCD
(ang. Binary-Coded Decimal), czyli dziesiętny zakodowany
dwójkowo. My będziemy przyłączać do mikrokontrolera 7-
segmentowe wyświetlacze LED poprzez układy 7447(dekoder kodu
BCD na kod wyświetlacza 7-segmentowego). Zaletą kodu BCD jest
prostota: posługujemy się systemem dziesiętnym i każda cyfra
(0,1..9) liczby zapisywana jest na czterech bitach.
bity liczba (system dziesiętny)
0000 0000 00
0000 0001 01
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 11 of 27
bity liczba (system dziesiętny)
0000 0010 02
0000 0011 03
0000 0100 04
0000 0101 05
0000 0110 06
0000 0111 07
0000 1000 08
0000 1001 09
0001 0000 10
0001 0001 11
0001 0010 12
0001 0011 13
0001 0100 14
. .
. .
. .
1001 0111 97
1001 1000 98
1001 1001 99
Kodowanie BCD
Elementy i schematy
Zależnie od przykładu, będziemy do mikrokontrolera atmega16
przyłączać: osiem diod LED, dwa 7-segmentowe wyświetlacze LED,
klawiaturę telefoniczną, dwa przekazniki oraz brzęczyk(bez
generatora, sam przetwornik piezo).
Cały schemat podzieliłem na kilka części:
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 12 of 27
Schemat 4.1 - sposób przyłączenia do układu atmega16 zasilania, resetu i złącza
programatora. Kliknij w obrazek, żeby powiększyć.
Segmenty wyświetlacza LED potrzebują więcej prądu niż
pojedyncze diody LED, więc tym razem każdy z wyświetlaczy LED
będzie przyłączony do mikrokontrolera poprzez układ scalony 7447
(dekoder kodu BCD na kod wyświetlacza 7-segmentowego).
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 13 of 27
Schemat 4.2 Dwa 7-segmentowe wyświetlacze LED przyłączone do mikrokontrolera
poprzez układy 7447 (dekoder kodu BCD na kod wyświetlacza 7-segmentowego). Kliknij
w obrazek, aby powiększyć.
Fot. 4.1 Dwa 7 segmentowe wyświetlacze LED wraz z dekoderami 7447 umieszczone na
osobnej płytce.
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 14 of 27
Schemat 4.3 Schemat klawiatury 3x4 i sposób jej przyłączenia do portów we/wy mC
atmega16. Kliknij w obrazek, żeby powiększyć.
Fot. 4.2 Klawiatura wymontowana z aparatu telefonicznego.
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 15 of 27
Fot. 4.3 Klawiatura wykonana z przycisków micro swich na płytce uniwersalnej.
Schemat. 4.4 Sposób przyłączenia przetwornika piezo.
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 16 of 27
Fot. 4.4 Buzzer bez generatora ( przetwornik piezo - membrana z puszkÄ… ) wylutowany ze
starego, popsutego kalkulatora.
Fot. 4.5 Przetwornik piezo - membrana przylutowana do płytki obwodu drukowanego.
Schemat 4.5 Sposób przyłączenia przekazników.
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 17 of 27
Przykładowe programy
Przygotowałem cztery przykładowe programy, do uruchomienia
jako ćwiczenia. Do przykładów dołączone są animacje pokazującą
efekt działania programów oraz krótki opis. Wszystkie przykłady są
raczej proste, nikt nie powinien mieć problemów ze zrozumieniem
jak działają.
Poniżej znajduje się szkielet programu, wszystkie przykłady
napisane są według tego wzoru.
/**** PLIKI NAGAÓWKOWE ****/
#include
#include
#define F_CPU 1000000L
/**** ZMIENNE GLOBALNE ****/
/**** DEFINICJE FUNKCJI ****/
/* Inicjalizacja, konfiguracja sprzętu */
void init(void)
{
}
/* Inne funkcje */
/**** POCZTEK PROGRAMU ****/
/* Definicja funkcji main */
int main(void)
{
/* Deklaracje zmiennych */
/* Inicjalizacja */
init();
/* Główna pętla programu */
for(;;)
{
}
}
Szkielet prostego programu.
Pozytywka elektroniczna.
Ten przykładowy programik odgrywa krótką melodyjkę z pomocą
buzzera, nagranie dzwięku:
W programie najwięcej pracuje funkcja beep, która generuje sygnał
prostokątny na wyprowadzeniach PB0 i PB1, gdzie przyłączony jest
buzzer (przetwornik piezo). Funkcja ta przyjmuje dwa argumenty:
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 18 of 27
częstotliwość sygnału w hercach i dułgość czasu trwania sygnału
w milisekundach. Generowanie dzwięku odbywa się programowo,
bez użycia układów czasowych AVRra. Do uzyskania krótkich
opóznień w programie używana się funkcji: void _delay_loop_2
(unsigned int __count) z biblioteki avr-libc, która wprowadza
opóznienie czterech cykli procesora na jednostkę. Uwaga! Funkcja
beep będzie działać właściwie tylko dla uC AVR pracującego
z częstotliwością 1MHz.
Inna funkcja, o nazwie play, odgrywa melodyjkę dzwięk po dzwięku
wywołując funkcję beep. Funkcja play oczekuje argumentów:
tablicy dzwięków oraz indeks pierwszego i ostatniego dzwięku.
Elementami tablicy dzwięków mają być tablice o dwóch elementach
typu int: częstotliwość podana w hercach i długość trwania dzwięku
w milisekundach. W programie zdefiniowano, poza funkcjami,tablicÄ™
dzwięków o nazwie koziolek. Tablice definiowane poza funkcjami
dostępne są bezpośrednio we wszystkich funkcjach w całym pliku.
Jednak w przykładzie tablica jest przekazywana do funkcji jako
jeden z argumentów, dzięki temu struktura programu jest bardziej
przejrzysta, a funkcja jest bardziej uniwersalna.
/*
KURS AVR-GCC cz.4
Program, z pomocÄ… buzzera (przetwornika piezo),
odgrywa krótką melodyjkę.
układ atmega 1MHz
PB0 -> R(330Ohm) -> BUZZER -> PB1
*/
#define F_CPU 1000000L
#include
#include
/**** ZMIENNE GLOBALNE ****/
/*
Tablica dzwięków:
częstotliwść(Hz), czas_trwania(ms), częstotliwość, ...
*/
int koziolek[][2]={
523,125,
587,125,
659,250,
698,125,
659,125,
587,250,
523,250,
1047,250,
784,250,
523,250,
1047,250,
784,250,
523,250,
1047,250,
784,1000 };
/**** DEFINICJE WAASNYCH FUNKCJI ****/
/* Konfiguruje porty we/wy uC */
void init(void)
{
/* PB0,PB1 - wyścia */
DDRB = 0x03;
PORTB = 0x00;
}
/*
Funkcja generuje sygnał prostokątny na wyprowadzeniach PB0 i PB1,
gdzie przyłączony jest buzzer. Funkcja przyjmuje argumenty:
częstotliwość(Hz) sygnału i dułgość czasu trwania sygnału (ms).
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 19 of 27
*/
void beep(unsigned int frequency, unsigned int duration)
{
unsigned int i,t,n;
t = 125000/frequency;
n = (250UL*duration)/t;
PORTB |= 0x01;
PORTB &= ~0x02;
for(i=0; i < n; i++)
{
PORTB ^= 0x01;
PORTB ^= 0X02;
_delay_loop_2(t);
}
}
/*
Odgrywa melodyjkę dzwięk po dzwięku. Jako argumentów funkcja
oczekuje tablicy dzwięków oraz numerów pierwszego i ostatniego
dzwięku. Elementami tablicy dzwięków są tablice o dwóch elementach
typu int (częstotliwość w Hz i długość trwania dzwięku w ms).
*/
void play(int nots[][2], unsigned int start, unsigned int stop)
{
int n;
for(n=start; n <= stop; n++)
beep(nots[n][0], nots[n][1]);
}
/**** POCZTEK PROGRAMU ****/
/* Definicja głównej funkcji */
int main(void)
{
/* Konfiguracja sprzętu */
init();
/* Nieskończona pętla */
while (1)
{
/* Gra dwukrotnie ten sam "kawałek" */
play(koziolek,0,15);
play(koziolek,0,15);
/* Chwila spokoju :) */
_delay_ms(6000);
}
return 0;
}
Listing 4.1 Program "Pozytywka elektroniczna".
Klawiaturka "telefoniczna".
W tym przykładzie program odczytuje w pętli stan klawiatury
i wyświetla numer ostatniego wciśniętego przycisku.
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 20 of 27
Animacja prezentuje sposób działania programu "Klawiatura telefoniczna".
W programie kluczową rolę odgrywa funkcja read_keypad, która
sprawdza kolejno wszystkie przyciski klawiatury i zwraca numer
pierwszego wciśniętego przycisku, albo zero, jeśli żaden przycisk
nie został wciśnięty. Czytanie klawiatury odbywa się w następujący
sposób: Wybierane są kolejno wiersz po wierszu kalawiatury i dla
każdego wiersza testowane są przyciski kolumna po kolumnie.
Każdy przycisk klawiatury jednym wyprowadzeniem przyłączony
jest do jednej z czterech linii wierszy i drugim wyprowadzeniem do
jednej z trzech linii kolumn. Linie wierszy klawiatury przyłączone są
do wyjść P0..PD3 uC AVR, a linie kolumn do wejść PD4..PD6.
Początkowo wszystkie cztery wyjścia ustawiana są na stan wysoki
napięcia. Wiersz, którego przyciski mają być testowane, wybiera się
wymuszając na tej linii stan niski. Wejścia uC, do których
przyłączone są linie kolumn klawiatury, są wewnętrzne podciągnięte
przez rezystor do napięcia zasilania, więc normalnie występuje na
wejściach stan wysoki. Gdy w wybranym wierszu zostanie wciśnięty
przycisk, wtedy linia wiersza zewrze siÄ™ z liniÄ… kolumny, i na linii
kolumny też pojawi się stan niski.
Sposób testowania przycisków klawiatury.
/*
KURS AVR-GCC cz.4
Klawiatura telefonu
układ atmega16 (1MHz)
*/
#define F_CPU 1000000L
#include
#include
/**** DEFINICJE WAASNYCH FUNKCJI ****/
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 21 of 27
/* Konfiguracja sprzętu */
void init(void)
{
/* Konfiguruj portu A jako wyjścia */
/* Wyświetlacz */
DDRA = 0xFF;
PORTA = 0xFF;
/* Klawiaturka PD0..PD7 */
DDRD = 0x0f;
PORTD = 0x7f;
}
/*
Funkcja zmienia bajt w kodzie binarnym na bajt
zakodowany w BCD
*/
unsigned char bin2bcd(unsigned char bin)
{
if(bin>99) return bin;
return bin/10<<4 | bin%10;
}
/*
Funkcja sprawdza kolejno wszystkie przyciski klawiatury
i zwraca numer pierwszego wciśniętego przycisku, albo zero,
gdy żaden przycisk nie został wciśnięty.
*/
unsigned char read_keypad(void)
{
unsigned char row,col,key;
for(row=0x7e,key=1; row>=0x77; row=(row<<1|0x1)&0x7f)
{
PORTD = row;
for(col=0x10; col< 0x80; col<<=1, key++)
if(!(PIND & col)) return key;
}
return 0;
}
/**** POCZTEK PROGRAMU ****/
/* Definicja funkcji main */
int main(void)
{
unsigned char key;
/* Konfiguracja portów we/wy */
init();
/* Nieskończona pętla */
for(;;)
if(key = read_keypad())
PORTA = bin2bcd(key);
}
Listing 4.2 Program "Klawiatura telefoniczna".
Zamek na szyfr
W tym przykładzie wykorzystywane są: klawiatura (PD0..PD7),
bzzer(PB0,PB1), i sześć diod led (PA0..PA5); linia PC0 steruje
blokadą rygla zamka. Aby otworzyć zamek należy wybrać na
klawiaturze sześć właściwych cyfr. Wciskając przycisk z gwiazdką
można skasować wszystkie wprowadzone cyfry i rozpocząć
wpisywanie kodu od początku. Przy wciśnięciu każdej cyfry zapala
się kolejna dioda led i słychać krótki sygnał dzwiękowy. Po
wprowadzeniu właściwego kodu natychmiast następuje
odblokowanie rygla zamka. Otwarcie zamka sygnalizowane jest
migotaniem wszystkich sześciu diod i sygnałem dzwiękowym
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 22 of 27
o zmieniającej się częstotliwości. Wpisanie niewłaściwego kodu
sygnalizowane jest wygaszeniem wszystkich sześciu diod LED
i długim, "niskim", dzwiękiem.
W przykładzie wykorzystywane są omówione wcześnie funkcje beep
i read_keypad.
Animacja prezentuje działanie programu "Zamek na szyfr".
/*
KURS AVR-GCC cz.4
Zamek na szyfr (schemat i opis działania w artykule)
układ atmega16 (1MHz)
*/
/**** PLIKI NAGAÓWKOWE ****/
#define F_CPU 1000000L
#include
#include
/**** DEFINICJE FUNKCJI ****/
/* Inicjalizacja i konfiguracja sprzętu */
void init(void)
{
/* Konfiguruje portów we/wy */
/* PA0..PA6 - diody led */
DDRA = 0x3F;
PORTA = 0x3F;
/* PB0,PB1 - piezo buzzer */
DDRB = 0x03;
PORTB = 0x00;
/* Klawiaturka */
DDRD = 0x0f;
PORTD = 0x7f;
/* PC0 - rygiel zamka */
DDRC = 0x01;
PORTC = 0x00;
}
/*
Funkcja sprawdza kolejno wszystkie przyciski klawiatury
i zwraca numer pierwszego wciśniętego przycisku, albo zero,
jeśłi żaden przycisk nie został wciśnięty.
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 23 of 27
*/
unsigned char read_keypad(void)
{
unsigned char row,col,key;
/*
Wybiera wiersz po wierszu i testuje przyciski kolumna po
kolumnie, zmienna key przechowuje numer przycisku 1-12.
*/
for(row=0x7e,key=1; row>=0x77; row=(row<<1|0x1)&0x7f)
{
PORTD = row;
for(col=0x10; col< 0x80; col<<=1, key++)
if(!(PIND & col)) return key;
}
/* Jeśli żaden z przycisków nie został wciśnięty,
wyjście z kodem zero */
return 0;
}
/*
Funkcja generuje sygnał prostokątny na wyprowadzeniach PB0 i PB1,
gdzie przyłączony jest buzzer. Funkcja przyjmuje argumenty:
częstotliwość(Hz) sygnału i dułgość czasu trwania sygnału (ms).
*/
void beep(unsigned int frequency, unsigned int duration)
{
unsigned int i,t,n;
t = 125000/frequency;
n = (250UL*duration)/t;
PORTB |= 0x01;
PORTB &= ~0x02;
for(i=0; i < n; i++)
{
PORTB ^= 0x01;
PORTB ^= 0X02;
_delay_loop_2(t);
}
}
/**** POCZTEK PROGRAMU ****/
/* Definicja funkcji main */
int main(void)
{
/* Deklaracje zmiennych */
unsigned char i,j,key,err=0;
/* 6 cyfrowy klucz */
unsigned char pass[] = {7,8,7,8,7,8};
/* Konfiguracja i inicjalizacja sprzętu */
init();
/* Główna pętla programu */
for(;;)
{
/* Blokada rygla zamka */
PORTC &= ~0x01;
/* Wygaszenie diod led */
PORTA |= 0x3f;
/* Zeruje licznik błędów */
err = 0;
/* Odczyt i porównanie z wzorem 6 cyfr klucza */
for(i=0; i<6; i++)
{
/* Oczekiwanie na wciśnięcie przycisku klawiatury */
while(!(key = read_keypad()));
/* Jeśli wciśnięto przycisk gwiazdka *, to kasowanie
wprowadzonych cyfr */
if(key == 10) break;
/* Sygnalizacja postępu wprowadzania kolejnych cyfr klucza */
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 24 of 27
PORTA &= ~(1< beep(880,100);
/* Oczekiwanie na zwolnienie przycisku */
while(read_keypad());
/* Zatrzymanie aż wygasną drgania styków przycisku */
_delay_ms(80);
/* Każda wprowadzona cyfra klucza jest porównywana z
wzorem w tablicy "pass", jeśli nie pasuje,
licznik błędów w zmiennej "err" jest zwiększany o jeden */
if(key != pass[i]) err++;
/* Jeśli wprowadzona cyfra klucza jest ostatnią */
if(i == 5)
{
/* Jeśli wprowadzono właściwy klucz */
if(!err)
{
/* Zwolnienie blokady rygla zamka */
PORTC |= 0x01;
/* Sygnalizacja otwarcia zamka */
for(j=0; j<6; j++)
{
PORTA ^= 0x3f;
beep(880,200);
PORTA ^= 0x3f;
beep(440,200);
}
}
else
{
/* Sygnalizacja błędu */
beep(200, 600);
PORTA |= 0x3f;
}
}
}
}
}
Listing 4.3 Program "Zamek na szyfr"
Wyłącznik czasowy
Jak w tytule punktu, wyłącznik czasowy. Wybiera się na klawiaturze
liczbę sekund 1-99 i następnie, wciskając przycisk gwiazdka
uruchamia siÄ™ odliczanie od wybranej liczby do zera. Dwucyfrowe
liczby wprowadza się wciskając daw przyciski w krótkim odstępie
czasu, pierwszą - dziesiątki, drugą - jedności.
Animacja prezentuje działanie programu "Wyłącznik czasowy"
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 25 of 27
/*
KURS AVR-GCC cz.4
Wyłącznik czasowy (schemat i opis działania w artykule)
układ atmega16 (1MHz)
*/
#define F_CPU 1000000L
#include
#include
/**** DEFINICJA WAASNYCH FUNKCJI ****/
/* Inicjalizacja, konfiguracja sprzętu */
void init(void)
{
/* Konfiguruje portów we/wy */
/* Wyświetlacz */
DDRA = 0xFF;
PORTA = 0xFF;
/* Klawiaturka */
DDRD = 0x0f;
PORTD = 0x7f;
/* Przekaznik */
DDRC = 0x01;
PORTC = 0x00;
}
/*
Funkcja zmienia 8-bit wartość w kodzie dwójkowym
na wartość w kodzie BCD
*/
unsigned char bin2bcd(unsigned char bin)
{
if(bin > 99) return bin;
return bin/10<<4 | bin%10 ;
}
/*
Funkcja sprawdza kolejno wszystkie przyciski klawiatury
i zwraca numer pierwszego wciśniętego przycisku, albo zero,
gdy żaden przycisk nie został wciśnięty.
*/
unsigned char read_keypad(void)
{
unsigned char row,col,key;
/*
Wybiera wiersz po wierszu i testuje przyciski kolumna po
kolumnie, zmienna key przechowuje numer przycisku 1-12.
*/
for(row=0x7e,key=1; row>=0x77; row=(row<<1|0x1)&0x7f)
{
PORTD = row;
for(col=0x10; col< 0x80; col<<=1, key++)
if(!(PIND & col)) return key;
}
/* Jeśli żaden z przycisków nie został wciśnięty,
wyjście z kodem zero */
return 0;
}
/**** POCZTEK PROGRAMU ****/
/* Definicja funkcji main */
int main(void)
{
/* Deklaracje zmiennych */
unsigned char i,key;
signed char v = 0;
/* Tablica kodowania przycisków klawiatury */
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 26 of 27
unsigned char codetab[]= {0xff,1,2,3,4,5,6,7,8,9,0,0,0};
/* Konfiguracja, inicjalizacja sprzętu */
init();
/* Główna pętla programu */
for(;;)
{
/* Funkcja read_keypad zwraca wartość (kod przycisku) różną
od zera, gdy wykryje wciśnięcie przycisku na klawiaturze. */
key = read_keypad();
/* Wprowadzenie liczby sekund (0-99) czasu zliczania */
if(key && key < 10)
{
v = bin2bcd(codetab[key]);
/* Wyświetla pierwszą cyfrę */
PORTA = v;
/* Opóznienie dla wygaśnięcia drgań styków przycisku */
_delay_ms(20);
/* Oczekiwanie na zwolnienie przycisku */
while(read_keypad()){};
_delay_ms(20);
/* W pętli, 20 razy odczytuje klawiaturę oczekując
wprowadzenia drugiej cyfry */
for(i=20; i>0; i--)
{
if((key = read_keypad()))
{
_delay_ms(20);
/* Składa obie wprowadzone cyfry w jedną liczbę */
v = v*10 + codetab[key];
/* Przesuwa pierwszą cyfrę wyświetlacza pozycję na lewo */
PORTA <<= 4;
/* Wyświetla drugą cyfrę */
PORTA |= codetab[key];
/* Czeka na zwolnienie przycisku */
while(read_keypad()){};
_delay_ms(20);
break;
}
/* Dodatkowe opóznienie między kolejnymi odczytami
klawiatury*/
_delay_ms(50);
/* Wygasza pierwszą cyfrę na wyświetlaczu, jeśli
wprowadzono liczbÄ™ jednocyfrowÄ… */
if(i == 1) PORTA |= 0XF0;
}
}
/* Wciśnięcie przycisku z gwiazdką, gdy wcześnie wprowadzona
liczba sekund była większa od zera, rozpoczyna odliczanie */
else if (key == 10 && v)
{
PORTC |= 0x01; // włącza przekaznik
/* Odliczanie od v do zera, zmienna v zawiera wprowadzonÄ…
wcześniej liczbę sekund */
do{
_delay_ms(960); // Prawie sekunda opóznienia
v--;
/* Wyświetla aktualny stan licznika */
PORTA = bin2bcd(v);
}while(v>0);
PORTC &= ~0x01; // wyłącza przekaznik
}
}
}
Listing 4.4 Program "Wyłącznik czasowy".
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010
XYZ Hobby Robot-Kurs AVR-GCC, cz.4 Page 27 of 27
W następnej części kursu..
W następnej części kursu tematem przewodnim będą tekst i
działania na tekście. Kolejnym tematem będzie podział kodu
zródłowego programu na oddzielnie kompilowane pliki. Napiszę też
kilka zdań na temat preprocesora języka C. W części praktycznej
będziemy bawić się alfanumerycznym wyświetlaczem LCD
HD44780, przyłączymy też AVRa do komputera PC poprzez port
szeregowy rs232.
Kurs AVR-GCC cz.5
Copyright © 2009-2010 ABXYZ - Wszelkie prawa zastrzeżone
http://hobby.abxyz.bplaced.net/index.php?pid=4&aid=7&pv 13/06/2010


Wyszukiwarka

Podobne podstrony:
Kurs AVR GCC cz 5
Kurs AVR GCC, cz 3
Kurs AVR GCC cz 2
Kurs AVR GCC cz 3
Kurs AVR GCC cz 1
Kurs AVR GCC, cz 1
Kurs AVR GCC, cz 5
Kurs AVR GCC, cz 2
Kurs AVR GCC cz 4
Kurs AVR GCC Wyświetlacz LCD od Nokii310
XYZ Hobby Robot Kurs AVR GCC Gamepad od PlayStation
Kurs AVR GCC Dekoder RC5
Using the EEPROM memory in AVR GCC
AVR GCC w Linuksie przykład instalacji ze źródeł
Zestaw uruchomieniowy do procesorow rodziny AVR i 51, cz 2

więcej podobnych podstron