Kurs AVR GCC cz 4 2

background image

Artykuł pochodzi ze strony XYZ HOBBY ROBOT (

xyz.isgreat.org

)

Kurs AVR-GCC cz.4(v1)

17.08.2009 ABXYZ

Artykuł jest wciąż jeszcze w trakcie edycji, więc wszelkie uwagi,
podpowiedzi i komentarze będą bardzo pomocne.

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 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 wskaźniki 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

1 z 23

background image

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;

2 z 23

background image

Na tym etapie nauki, te informacje na temat tablic mam wystarczą.

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
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

3 z 23

background image

pierwszej instrukcji zaczyna się wykonanie całego programu.

/* Najprostszy program-całość umieszczona w funkcji main */

#define F_CPU 1000000L
#include <avr/io.h>

/* 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óźnienie o zadanym
w milisekundach okresie czasu; w avr-libc dostępna jest także
podobna funkcja _delay_us, dla której długość opóźnienia 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óźnienia 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óźnienia */

/* 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);

);

);

);

/* 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.

4 z 23

background image

/* 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
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;

;

;

;

5 z 23

background image

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.
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;
}

}

}

}

6 z 23

background image

int

main(

(

(

(

void

)

)

)

)

{

{

{

{

/* Definicja tablicy */

int

tablica[

16

];

int

n =

16

;

/* Zmienne przekazywane są do funkcji przez wartość, a tablice
przez wskaźnik. 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
wskaźnik. Wskaźniki 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,
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

;

7 z 23

background image

}

}

}

}

Zmienne deklarowane wewnątrz funkcji tworzone są w chwili
wywołania funkcji i przestają istnieć w momencie powrotu
z funkcji, przechowywane dane są tracone. Przy każdym wywołaniu
funkcji zmienne tworzone są od nowa. Jeśli zależy nam, aby
zmienna deklarowane w funkcji istniała przez cały okres działania
programu i dane w zmiennej nie były tracone po wyjściu z funkcji,
to należy deklaracje zmiennej poprzedzić słówkiem static; takie
zmienne nazywa się statycznymi.

/* 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

)

)

)

);

Zmienne deklarowane poza funkcjami mają początkową wartość
zero; natomiast zmienne deklarowane w funkcja mają przypadkową
wartość początkową z wyjątkiem zmiennych statycznych, których
mają wartość początkową zero.

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

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

8 z 23

background image

bity

liczba (system dziesiętny)

0001 0010

12

0001 0011

13

0001 0100

14

.

.

.

.

.

.

1001 0111

97

1001 1000

98

1001 1001

99

Kodowanie BCD

Elementy i schematy

Celem uruchamiana kilku przykładowych programów zbudujemy
kompletny "komputer", będzie on miał wyświetlacz, klawiaturę i
oczywiście dźwięk:), sercem komputera będzie układ atmega16. Do
mikrokontrolera podłączymy klawiaturę od telefonu i dwa
7-segmentowe wyświetlacze LED oraz buzzer(bez generatora, sam
przetwornik piezo). Segmenty wyświetlacza LED potrzebują więcej
prądu niż pojedyncze diody LED, dlatego każdy z wyświetlaczy
będzie przyłączony do mikrokontrolera poprzez układ 7447
(dekoder kodu BCD na kod wyświetlacza 7-segmentowego).

Cały schemat podzieliłem na cztery fragmenty:

Schemat 4.1 - sposób przyłączenia do układu atmega16 zasilania, resetu i złącza

9 z 23

background image

programatora. Kliknij w obrazek, żeby powiększyć.

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.

10 z 23

background image

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.

11 z 23

background image

Fot. 4.3 Klawiatura wykonana z przycisków micro swich.

Fot. 4.4 Buzzer bez generatora ( przetwornik piezo - membrana z puszką ) wylutowany ze

starego, popsutego kalkulatora.

12 z 23

background image

Fot. 4.5 Przetwornik piezo - membrana przylutowana do płytki obwodu drukowanego.

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 NAGŁÓWKOWE ****/

#include <avr/io.h>
#include <util/delay.h>
#define F_CPU 1000000L

/**** ZMIENNE GLOBALNE ****/

/**** DEFINICJE FUNKCJI ****/

/* Inicjalizacja, konfiguracja sprzętu */

void

init(

(

(

(

void

)

)

)

)

{

{

{

{

}

}

}

}

/* Inne funkcje */

/**** POCZĄTEK PROGRAMU ****/

/* Definicja funkcji main */

int

main(

(

(

(

void

)

)

)

)

{

{

{

{

/* Deklaracje zmiennych */

/* Inicjalizacja */

init();

();

();

();

/* Główna pętla programu */

for

(;;)

(;;)

(;;)

(;;)

{

{

{

{

}

}

}

}

}

}

}

}

Szkielet prostego programu.

Pozytywka elektroniczna.

13 z 23

background image

Ten przykładowy programik odgrywa krótką melodyjkę z pomocą
buzzera, nagranie dźwię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:
częstotliwość sygnału w hercach i dułgość czasu trwania sygnału
w milisekundach. Generowanie dźwięku odbywa się programowo,
bez użycia układów czasowych AVRra. Do uzyskania krótkich
opóźnień w programie używana się funkcji: void
_delay_loop_2(unsigned int __count) z biblioteki avr-libc, która
wprowadza opóźnienie 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ę dźwięk po dźwięku
wywołując funkcję beep. Funkcja play oczekuje argumentów:
tablicy dźwięków oraz indeks pierwszego i ostatniego dźwięku.
Elementami tablicy dźwięków mają być tablice o dwóch elementach
typu int: częstotliwość podana w hercach i długość trwania dźwięku
w milisekundach. W programie zdefiniowano, poza funkcjami,tablicę
dźwię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 <avr/io.h>
#include <util/delay.h>

/**** 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

,

,

,

,

14 z 23

background image

523

,

,

,

,

250

,

,

,

,

1047

,

,

,

,

250

,

,

,

,

784

,

,

,

,

250

,

,

,

,

523

,

,

,

,

250

,

,

,

,

1047

,

,

,

,

250

,

,

,

,

784

,

,

,

,

1000

};

};

};

};

/**** DEFINICJE WŁASNYCH 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).
*/

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 dźwię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

]);

]);

]);

]);

}

}

}

}

/**** POCZĄTEK 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".

15 z 23

background image

W tym przykładzie program odczytuje w pętli stan klawiatury
i wyświetla numer ostatniego wciśniętego przycisku.

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.

16 z 23

background image

Sposób testowania przycisków klawiatury.

/*
KURS AVR-GCC cz.4
Klawiatura telefonu (schemat i opis w artykule)

układ atmega16 (1MHz)

*/

#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>

/**** DEFINICJE WŁASNYCH FUNKCJI ****/

/* 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

;

;

;

;

}

}

}

}

/**** POCZĄTEK PROGRAMU ****/

/* Definicja funkcji main */

int

main(

(

(

(

void

)

)

)

)

{

{

{

{

/* Inicjalizacja, konfiguracja sprzętu */

init();

();

();

();

/* Nieskończona pętla */

for

(;;)

(;;)

(;;)

(;;)

if

(

(

(

(key =

=

=

= read_keypad())

())

())

())

PORTA =

=

=

= bin2bcd(

(

(

(key);

);

);

);

17 z 23

background image

}

}

}

}

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ł dźwię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 dźwiękowym
o zmieniającej się częstotliwości. Wpisanie niewłaściwego kodu
sygnalizowane jest wygaszeniem wszystkich sześciu diod LED
i długim, "niskim", dźwię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 NAGŁÓWKOWE ****/

#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>

/**** DEFINICJE FUNKCJI ****/

/* Inicjalizacja i konfiguracja sprzętu */

void

init(

(

(

(

void

)

)

)

)

{

{

{

{

/* Konfiguruje portów we/wy */

/* PA0..PA6 - diody led */

DDRA =

=

=

=

0x3F

;

;

;

;

18 z 23

background image

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.
*/

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);

);

);

);

}

}

}

}

}

}

}

}

/**** POCZĄTEK 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();

();

();

();

19 z 23

background image

/* 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 */

PORTA &=

&=

&=

&= ~(

~(

~(

~(

1

<<

<<

<<

<<i);

);

);

);

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.

20 z 23

background image

Animacja prezentuje działanie programu "Wyłącznik czasowy"

/*
KURS AVR-GCC cz.4
Wyłącznik czasowy (schemat i opis działania w artykule)

układ atmega16 (1MHz)
*/

#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>

/**** DEFINICJA WŁASNYCH FUNKCJI ****/

/* Inicjalizacja, konfiguracja sprzętu */

void

init(

(

(

(

void

)

)

)

)

{

{

{

{

/* Konfiguruje portów we/wy */

/* Wyświetlacz */

DDRA =

=

=

=

0xFF

;

;

;

;

PORTA =

=

=

=

0xFF

;

;

;

;


/* Klawiaturka */

DDRD =

=

=

=

0x0f

;

;

;

;

PORTD =

=

=

=

0x7f

;

;

;

;


/* Przekaźnik */

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

)

)

)

)

21 z 23

background image

{

{

{

{

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

;

;

;

;

}

}

}

}

/**** POCZĄTEK PROGRAMU ****/

/* Definicja funkcji main */

int

main(

(

(

(

void

)

)

)

)

{

{

{

{

/* Deklaracje zmiennych */

unsigned

char

i,

,

,

,key;

;

;

;

signed

char

v =

=

=

=

0

;

;

;

;

/* Tablica kodowania przycisków klawiatury */

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óźnienie 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óźnienie 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)

)

)

)

22 z 23

background image

{

{

{

{

PORTC |=

|=

|=

|=

0x01

;

;

;

;

// włącza przekaźnik

/* Odliczanie od v do zera, zmienna v zawiera wprowadzoną

wcześniej liczbę sekund */

do

{

{

{

{

_delay_ms(

(

(

(

960

);

);

);

);

// Prawie sekunda opóźnienia

v--;

--;

--;

--;

/* Wyświetla aktualny stan licznika */

PORTA =

=

=

= bin2bcd(

(

(

(v);

);

);

);

}

}

}

}

while

(

(

(

(v>

>

>

>

0

);

);

);

);

PORTC &=

&=

&=

&= ~

~

~

~

0x01

;

;

;

;

// wyłącza przekaźnik

}

}

}

}

}

}

}

}

}

}

}

}

Listing 4.4 Program "Wyłącznik czasowy".

W następnej części kursu..

W następnej części kursu tematami będą: struktury, unie i pola
bitowe; stałe tekstowe i tablice znaków; wskaźniki; preprocesor
języka C; podział programu na wiele plików.

© 2009 ABXYZ Wszelkie prawa zastrzeżone

23 z 23


Wyszukiwarka

Podobne podstrony:
Kurs AVR GCC cz 1
Kurs AVR GCC, cz 2
Kurs AVR GCC cz 2
Kurs AVR GCC, cz 5
Kurs AVR GCC, cz 1
Kurs AVR GCC cz 3
Kurs AVR GCC, cz 4
Kurs AVR GCC, cz 3
Kurs AVR GCC cz 5
Kurs AVR GCC cz 1

więcej podobnych podstron