Professional Documents
Culture Documents
Zawartość
AVR-GCC - wstęp ..................................................................................................................... 4
AVR-GCC - dystrybucja WinAVR............................................................................................ 4
Praca z kompilatorem ......................................................................................................... 5
AVR-GCC - kompilator ............................................................................................................. 5
AVR-GCC - kompilacja prostych programów........................................................................... 6
AVR-GCC - program make........................................................................................................ 7
Plik sterujący (makefile) .................................................................................................... 8
AVR-GCC - programowanie układu ........................................................................................ 13
Programator AVRprog (AVR910) ................................................................................... 13
Programator STK200 ....................................................................................................... 14
Oprogramowanie .............................................................................................................. 15
AVR-GCC - dostęp do zasobów mikrokontrolera ................................................................... 16
Rejestry specjalne ............................................................................................................. 16
Najważniejsze funkcje zawarte w pliku "avr/io.h" .......................................................... 16
AVR-GCC - wejście i wyjście binarne .................................................................................... 18
Rejestry............................................................................................................................. 19
Praktyczny sposób dostępu do wyprowadzeń układu ...................................................... 20
Podciąganie wejścia do logicznej jedynki ........................................................................ 20
Programy przykładowe .................................................................................................... 21
AVR-GCC - port szeregowy .................................................................................................... 23
Inicjalizacja ...................................................................................................................... 24
Wysyłanie znaku .............................................................................................................. 25
Odbiór znaku .................................................................................................................... 25
Program przykładowy ...................................................................................................... 26
AVR-GCC - pamięć SRAM ..................................................................................................... 28
Operacje na zmiennych .................................................................................................... 29
Program przykładowy ...................................................................................................... 30
AVR-GCC - pamięć programu (FLASH) ................................................................................ 32
Najważniejsze funkcje zawarte w pliku avr/pgmspace.h ................................................. 33
Tworzenie stałych w pamięci programu .......................................................................... 33
Czytanie stałych z pamięci programu .............................................................................. 33
Tworzenie tablic w pamięci programu ............................................................................. 33
Czytanie wartości z tablic w pamięci programu .............................................................. 33
Tworzenie łańcucha znaków ............................................................................................ 33
Czytanie łańcucha znaków ............................................................................................... 34
Program przykładowy ...................................................................................................... 34
AVR-GCC - pamięć EEPROM ................................................................................................ 36
Najważniejsze funkcje zawarte w pliku avr/eeprom.h..................................................... 36
1
Program przykładowy ...................................................................................................... 36
AVR-GCC - obsługa przerwań ................................................................................................ 38
Program przykładowy ...................................................................................................... 39
AVR-GCC - licznik/czasomierz TIMER 0 .............................................................................. 40
Tryb licznika .................................................................................................................... 41
Tryb czasomierza ............................................................................................................. 43
AVR-GCC - licznik/czasomierz TIMER 1 .............................................................................. 45
Tryb licznika .................................................................................................................... 45
Tryb czasomierza ............................................................................................................. 46
Tryb porównywania ......................................................................................................... 47
Tryb przechwytywania ..................................................................................................... 49
Tryb PWM - modulowana szerokość impulsu ................................................................. 50
AVR-GCC - licznik/czasomierz TIMER 2 .............................................................................. 52
Tryb czasomierza ................................................................................................................. 53
Program przykładowy ...................................................................................................... 53
Tryb porównywania ............................................................................................................. 56
Program przykładowy ...................................................................................................... 56
Program przykładowy ...................................................................................................... 58
AVR-GCC - komparator analogowy........................................................................................ 58
Programy przykładowe .................................................................................................... 59
AVR-GCC - przetwornik analogowo/cyfrowy ........................................................................ 61
Programy przykładowe .................................................................................................... 63
AVR-GCC - układ Watchdog .................................................................................................. 65
Najważniejsze funkcje zawarte w pliku avr/wdt.h ........................................................... 66
Program przykładowy ...................................................................................................... 66
AVR-GCC - tryby zmniejszonego poboru mocy ..................................................................... 68
Najważniejsze funkcje zawarte w pliku avr/sleep.h ........................................................ 68
Program przykładowy ...................................................................................................... 68
AVR-GCC - opcje wywoływania narzędzi .............................................................................. 71
AVR-GCC - opis funkcji biblioteki avr-libc ............................................................................ 76
Lista plików nagłówkowych ................................................................................................ 76
avr/crc16.h ............................................................................................................................ 77
avr/delay.h ............................................................................................................................ 77
avr/eeprom.h ......................................................................................................................... 77
avr/ina90.h ............................................................................................................................ 78
avr/interrupt.h ....................................................................................................................... 78
avr/io.h.................................................................................................................................. 79
avr/io[MCU].h ...................................................................................................................... 79
avr/parity.h ........................................................................................................................... 79
avr/pgmspace.h ..................................................................................................................... 79
avr/sfr_defs.h ........................................................................................................................ 81
avr/signal.h ........................................................................................................................... 81
avr/sleep.h ............................................................................................................................ 83
2
avr/timer.h ............................................................................................................................ 84
avr/twi.h................................................................................................................................ 84
avr/wdt.h ............................................................................................................................... 84
ctype.h .................................................................................................................................. 85
errno.h .................................................................................................................................. 86
inttypes.h .............................................................................................................................. 86
math.h ................................................................................................................................... 87
setjmp.h ................................................................................................................................ 88
stdlib.h .................................................................................................................................. 89
string.h .................................................................................................................................. 90
AVR-GCC - kompilacja środowiska ze źródeł ........................................................................ 91
Pakiet binutils ................................................................................................................... 92
Pakiet gcc-core ................................................................................................................. 92
Pakiet avr-libc .................................................................................................................. 93
3
AVR-GCC - wstęp
Kilka lat temu firma Atmel wprowadziła nową rodzinę 8 bitowych mikrokontrolerów
zbudowanych w architekturze RISC (o zredukowanej liście rozkazów), z których
większość wykonuje się w pojedynczym takcie zegara, posiadające bardzo rozbudowane
peryferia i łatwo programowalne w systemie docelowym (pięć przewodów łącznie z
zasilaniem). Wydawać by się mogło, że nowa rodzina mikrokontrolerów z zupełnie nową
niekompatybilną z innymi rodzinami listą rozkazów nie zostanie dobrze przyjęta na rynku.
Jednak stało się inaczej - dzięki mądrej polityce firmy Atmel, która udostępnia za darmo
narzędzia uruchomieniowe (np. AVR Studio), wielu entuzjastów mikrokontrolerów z
łatwością może testować swoje własne konstrukcje z tymi układami. Jednak wśród tych
narzędzi brakowało czegoś w dzisiejszych czasach ważnego a mianowicie kompilatora
języka wysokiego poziomu - nie zawsze mamy tyle czasu i chęci, żeby pisać każdy
program w asemblerze (ucząc się nowej listy rozkazów i technik programowania) i później
długo go testować. W tym momencie przychodzi na myśl zastosowanie języka, który byłby
blisko związany ze sprzętem (dostęp do rejestrów, peryferii, pamięci itp.) i jednocześnie
można byłoby w nim pisać programy na dosyć wysokim poziomie abstrakcji (bez
ukierunkowywania się na konkretny sprzęt). Tutaj na myśl przychodzi język C. Jest to
najbardziej rozpowszechniony język programowania komputerów. Spotyka się jego
implementacje na najróżniejsze maszyny: od prostych mikroprocesorów po potężne
superkomputery. Jednak jeżeli posiadamy już darmowe oprogramowanie do uruchamiania
na pewno pomyślimy sobie: czy nie ma również jakiegoś darmowego kompilatora języka
C na mikrokontrolery AVR? Okazuje się, że jest i to bardzo dobry. Wywodzi się z rodziny
GCC (GNU Compiler Collection), nazywa się AVR-GCC. Pierwotnie pracował pod
systemami unixowymi jak FreeBSD czy Linux (zresztą na takich systemach ciągle są
rozwijane jego nowe wersje), ale posiadając wersje źródłowe (bez problemu dostępne w
internecie na zasadzie "otwartego źródła") każdy chętny może "przenieść" go pod swój
ulubiony system operacyjny. Jednak przeciętnego użytkownika najbardziej interesuje
szybki efekt i tu z pomocą przychodzą tzw. "dystrybucje" zawierające kompilator z całym
szeregiem narzędzi i bibliotek.
4
Praca z kompilatorem
avr-gcc -v
W rezultacie powinien się nam wyświetlić tekst zawierający m.in wersję kompilatora:
AVR-GCC - kompilator
Na kompilator AVR-GCC składa się wiele programów, są to:
• avr-addr2line - tłumaczy adresy w plikach wynikowych na numery linii w plikach
źródłowych
• avr-ar - tworzy, modyfikuje i wyciąga dane z archiwów
• avr-as - asembler
• avr-cpp - preprocessor C
• avr-gcc - kompilator
• avr-ld - linker (konsolidator)
• avr-nm - wyświetla nazwy symboliczne z plików wynikowych
• avr-objcopy - kopiuje i tłumaczy pliki objektowe (może "pięknie" deasemblować)
• avr-objdump - wyświetla różne informacje z plików objektowych
• avr-ranlib - generuje indeks do archiwum
• avr-size - wyświetla listę rozmiarów sekcji
• avr-strings - wyświetla łańcuchy ("drukowalne" znaki) z pliku
• avr-strip - usuwa symbole z plików wynikowych
• avr-libc - standardowa biblioteka funkcji dla kompilatora.
Ogólne określenie kompilator obejmuje cały zestaw narzędzi, bibliotek, plików
nagłówkowych, które łącznie pozwalają przetworzyć kod programu stworzony w C i
assemblerze do wynikowej postaci binarnego kodu maszynowego ładowanego do pamięci
flash mikrokontrolera. Przetwarzanie takie obejmuje następujące etapy:
• wykonanie na tekście programu dyrektyw preprocesora (np. wstawienie plików
(dyrektywy #include), zastąpienie fragmentów odpowiednimi definicjami (dyrektywy
#define));
• kompilacja modułów C do postaci plików assemblerowych;
5
• asemblacja plików assemblerowych do postaci relokowalnego kodu maszynowego / tj.
bez przydzielonych konkretnych adresów etykiet, skoków itp.;
• konsolidacja (linkowanie) czyli połączenie przygotowanych plików relokowalnych,
dołączenie kodu wywoływanych funkcji bibliotecznych i ustawienie wszystkiego
kolejno w przestrzeni adresowej programu z przydzieleniem konkretnych wartości
adresów; wynikiem jest plik w formacie elf zawierający wszelkie potrzebne
informacje o projekcie - wynikowy kod programu, zawartość obszaru eeprom, dane
dla debuggera itd.;
• utworzenie na podstawie pliku elf potrzebnych wyjściowych plików w odpowiednich
formatach (zawartość flash i eeprom w postaci bin lub hex, plik debuggera coff dla
potrzeb symulacji);
• pobranie z pliku elf dodatkowych użytecznych informacji o projekcie, jak np. zajętość
poszczególnych obszarów pamięci.
Właściwy program avr-gcc wykonuje dwa pierwsze etapy - czyli przetworzenie kodu C na
kod assemblerowy z uwzględnieniem ustawionych opcji, optymalizacji itp. Dalej sprawą
zajmują się specjalizowane narzędzia (binutils): assembler avr-as, linker avr-ld i szereg
innych: avr-objcopy, avr-objdump, avr-ar itd.).
Oczywiście wszystko należy uruchomić w odpowiedniej kolejności, z podaniem
potrzebnych argumentów i wymaganych opcji. Tradycyjnie służy do tego bardzo
uniwersalny i wszechstronny manager procesów make.
Istnieją dwie drogi prowadzące do przygotowania działającego środowiska kompilatora:
• pobranie źródeł z internetu, kompilacja i instalacja oprogramowania
• pobranie z internetu "dystrybucji" zawierającej skompilowane i (zazwyczaj)
przetestowane programy oraz jego instalacja
Każda z tych metod ma swoje "za" i "przeciw". Pierwsza gwarantuje, że będziemy mogli
używać najnowszych wersji narzędzi i bibliotek. Ponadto możemy pracować z tymi
narzędziami pod kontrolą swojego ulubionego systemu operacyjnego. Jest to jednak
okupione znacznie dłuższą i bardziej czasochłonną metodą instalacji nie gwarantującą
zawsze pełnego sukcesu.
Stosując drugą metodę efekt osiągamy niemal natychmiastowo. Otrzymujemy środowisko
jakie posiada zapewne wiele innych osób na świecie. Okupione jest to jednak mniejszą
elastycznością w pozyskiwaniu nowych wersji programów i bibliotek. Jednak dla
praktyków, którzy chcą wykorzystywać narzędzia a nie je tworzyć jest to najlepsze
rozwiązanie.
opcja -O ihex powoduje, że plik wyjściowy będzie miał format Intel HEX,
opcja -R .eeprom wyłącza z przetwarzania sekcję z zawartością przeznaczoną dla pamięci
EEPROM mikrokontrolera, a.out - plik wejściowy, program.hex - plik wyjściowy. Po
wykonaniu powyższego polecenia otrzymujemy plik .hex przeznaczony dla programatora
układu.
Aby w przyszłości za każdym razem nie wypisywać z klawiatury tylu komend, można
stworzyć plik wsadowy np. o nazwie avr-build.bat z następującą treścią:
@echo off
avr-gcc -mmcu=%1 %2.c
avr-objcopy -O ihex -R .eeprom a.out %2.hex
7
-p powoduje wypisanie makrodefinicji i reguł transformacji
-s wyłącza wypisywanie treści polecenia przed jego wykonaniem
Opcje można ze sobą łączyć. Np.: polecenie {make -np} powoduje wypisanie wszystkich
reguł i makrodefinicji oraz ciągu poleceń jakie powinne być wykonane, aby uzyskać
żądany cel.
Jest to pomocne w sytuacji, gdy chcemy sprawdzić poprawność definicji zawartych w
pliku sterującym bez uruchamiania długotrwałej kompilacji wielu plików.
Plik sterujący zawiera definicje relacji zależności, które mówią w jaki sposób i z jakich
elementów należy stworzyć cel (program, bibliotekę, lub plik obiektowy) i wskazują pliki,
których zmiany implikują wykonanie powtórnej kompilacji poszczególnych celów. Plik
sterujący może również zawierać zdefiniowane przez programistę reguły transformacji. W
pliku makefile znakiem komentarza jest znak # (hash) umieszczony na początku linii.
Poniżej przedstawiłem skonstruowany przezemnie plik makefile, w którym w zasadzie
wystarczy zmienić tylko nazwę programu TARGET i typ mikrokontrolera MCU.
# typ mikrokontrolera
MCU = atmega163
# Dodatkowe biblioteki
#
# Minimalna wersja printf
#LDFLAGS += -Wl,-u,vfprintf -lprintf_min
#
# Zmiennoprzecinkowa wersja printf (wymaga biblioteki matematycznej)
8
#LDFLAGS += -Wl,-u,vfprintf -lprintf_flt
#
# Biblioteka matematyczna
#LDFLAGS += -lm
include $(USRLIB)/avr_make
include $(USRLIB)/avr_make
jest włączany kolejny plik będący zbiorem reguł i makrodefinicji wspólnych dla każdego
projektu.
Oto jego listing:
# Opcje kompilatora
CFLAGS += -g
CFLAGS += -funsigned-char
CFLAGS += -funsigned-bitfields
CFLAGS += -fpack-struct
CFLAGS += -fshort-enums
CFLAGS += -Wall
CFLAGS += -Wstrict-prototypes
CFLAGS += -Wa,-ahlms=$(<:.c=.lst)
CFLAGS += -I$(USRLIB)
CFLAGS += -O$(OPT)
# Opcje asemblera
ASFLAGS = -Wa,-ahlms=$(<:.asm=.lst),-gstabs
# Opcje linkera
LDFLAGS += $(TARGET).a
LDFLAGS += -Wl,-Map=$(TARGET).map,--cref
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
9
AR = avr-ar
REMOVE = rm -f
COPY = cp
# ---------------------------------------------------------
# ---------------------------------------------------------
# ---------------------------------------------------------
# ---------------------------------------------------------
# ---------------------------------------------------------
# ---------------------------------------------------------
# Domyślne wywołanie
all: begin sizebefore \
$(TARGET).a \
$(TARGET).elf \
$(TARGET).lss \
$(TARGET).hex \
$(TARGET).eep \
$(TARGET).cof \
sizeafter finished end
# ---------------------------------------------------------
# Wyświetlanie tekstów.
begin:
@$(BEGIN)
finished:
@$(FINISH)
end:
@$(END)
# ---------------------------------------------------------
10
@if [ -f $(TARGET).elf ]; then echo Size before:; $(ELFSIZE);fi
sizeafter:
@if [ -f $(TARGET).elf ]; then echo Size after:; $(ELFSIZE);fi
# ---------------------------------------------------------
# ---------------------------------------------------------
COFFCONVERT=$(OBJCOPY) --debugging \
--change-section-address .data-0x800000 \
--change-section-address .bss-0x800000 \
--change-section-address .noinit-0x800000 \
--change-section-address .eeprom-0x810000
%.cof: %.elf
$(COFFCONVERT) -O $(COFFOUT) $< $@
# ---------------------------------------------------------
%.eep: %.elf
$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" --
change-section-lma .eeprom=0 -O ihex $< $@
# ---------------------------------------------------------
# ---------------------------------------------------------
# ---------------------------------------------------------
# ---------------------------------------------------------
# ---------------------------------------------------------
11
%.o : %.asm
$(CC) -c $(ALL_ASFLAGS) $< -o $@
# ---------------------------------------------------------
# ---------------------------------------------------------
# Czyści projekt.
clean: begin clean_list finished end
clean_list :
$(REMOVE) $(SRC:.c=.s)
$(REMOVE) $(SRCLIB:.c=.s)
$(REMOVE) $(SRCLIB:.c=.lst)
$(REMOVE) $(OBJLIB)
$(REMOVE) $(OBJ)
$(REMOVE) $(LST)
$(REMOVE) $(TARGET).a
$(REMOVE) $(TARGET).hex
$(REMOVE) $(TARGET).eep
$(REMOVE) $(TARGET).obj
$(REMOVE) $(TARGET).cof
$(REMOVE) $(TARGET).elf
$(REMOVE) $(TARGET).map
$(REMOVE) $(TARGET).a90
$(REMOVE) $(TARGET).sym
$(REMOVE) $(TARGET).lnk
$(REMOVE) $(TARGET).lss
# ---------------------------------------------------------
install:
avrdude -p $(MCU) -c $(PROG) -U flash:w:$(TARGET).hex -U
eeprom:w:$(TARGET).eep
# ---------------------------------------------------------
# Zależności
$(TARGET).a : $(CONFIG)
$(TARGET).o : $(TARGET).c $(CONFIG)
12
AVR-GCC - programowanie układu
Jeśli kompilacja projektu przebiegnie poprawnie, można plik wynikowy (ten z
rozszerzeniem .hex) wpisać do pamięci programu mikrokontrolera. Tutaj nie powinno być
problemu, gdyż opublikowano bardzo dużo układów programatorów - od bardzo prostych
składających się tylko z wtyczki do portu równoległego aż do bardziej skomplikowanych,
niekiedy zawierających wbudowany mikrokontroler sterujący jego pracą, ale za to
pozwalające na programowanie różnych funkcji (np. przeprogramowywanie tzw. fuse bits
- blokowanie programowania szeregowego, uaktywnianie wewnętrznego generatora RC,
przyśpieszanie startu mikrokontrolera po włączeniu zasilania itp.), których (zwykle) nie
można zaprogramować prostymi programatorami (tzw. szeregowymi). Jednak do
szybkiego uruchamiania oprogramowania najlepsze są właśnie te proste programatory
szeregowe (ISP - ang. in system programming - programowanie w systemie docelowym
bez wyjmowania układu z podstawki).
Firma ATMEL ustandaryzowała złącza służące do programowania układów. W chwili
obecnej są stosowane złącza 6 i 10 stykowe. Ich schematy przedstawiono na rysunku:
13
Opis (PDF) znajduje się na stronie WWW producenta. Niestety główną przeszkodą w jego
skonstruowaniu może być konieczność zaprogramowania mikrokontrolera AT90S1200,
który jest jego głównym elementem. Programator jest zasilany napięciem pobieranym z
systemu, w związku z czym podczas korzystania z niego nie trzeba stosować dodatkowego
zasilacza.
W sieci można znaleźć wiele ciekawych "wariacji" na temat tego programatora jedną z
nich jest AVR910 na AT90S2313.
Programator STK200
Innym rozwiązaniem programatora ISP jest STK200 również firmowany przez firmę
Atmel lecz konstrukcję tego programatora jak również starterkitu STK200 opracowała
firma Kanda. Jest to programator szeregowy komunikujący się z komputerem za pomocą
pomocą portu równoległego (LPT), obsługiwany jest przez bardzo dużą liczbę programów.
Schemat elektryczny programatora STK200 pokazano na rysunku:
14
Układ IC1 spełnia rolę separatora linii wejść/wyjść interfejsu drukarkowego Centronics od
systemu, w którym znajduje się programowany mikrokontroler. Programator jest zasilany
napięciem pobieranym z systemu, w związku z czym podczas korzystania z niego nie
trzeba stosować dodatkowego zasilacza.
Oprogramowanie
make program
PROG = stk200
program:
avrdude -p $(MCU) -c $(PROG) -e -m flash -i $(TARGET).hex
avrdude -p $(MCU) -c $(PROG) -m eeprom -i $(TARGET).eep
PROG = stk200
program:
uisp -v -dprog=$(PROG) --erase --upload if=$(TARGET).hex
uisp -v -dprog=$(PROG) --segment=eeprom --upload if=$(TARGET).eep
15
przy czym zmienna TARGET oznacza nazwę projektu a MCU - typ mikrokotrolera.
Po szczegóły dotyczące parametrów wywołania i konfiguracji programów uisp i avrdude
odsyłam do ich dokumentacji.
Rejestry specjalne
16
sbi (sfr, bit )
Instrukcja ta służy do ustawiania wskazanego bitu bit w rejestrze sfr. Pozostałe bity nie są
zmieniane. Przykład:
lub
Obie funkcje robią dokładnie to samo czyli ustawiają 3 bit w PORTB. Zwróćmy uwagę na
fakt, że w drugim przykładzie jako numeru bitu użyto PB3 - w ten sposób możemy
zwiększyć czytelność programu.
lub
Obie funkcje robią dokładnie to samo czyli kasują 3 bit w rejestrze PORTB.
17
lub poprzez przypisanie:
res = SREG;
Czyta rejestr SREG i wpisuje jego wartość do res.
inw ( sfr )
Instrukcja ta służy do czytania wartości 16 bitowej w rejestrze sfr. Tymi rejestrami są np.:
ADC, ICR1, OCR1A, OCR1B, TCNT1 itp. Zgodnie ze specyfikacją kontrolerów AVR ich
8 bitowe "połówki" muszą być odczytane w odpowiedniej kolejności aby poprawnie
została przeczytana 16 bitowa wartość. Właśnie inw wyręcza nas w pamiętaniu o tej
konieczności. Przykład:
res = TCNT1;
outb(PORTB, 0xFF);
PORTB = 0xFF;
OCR1A = 0xAAAA;
18
Rejestry
Tutaj sytuacja się trochę (pozornie) komplikuje. Do każdego z fizycznych portów (tzn.
tych dostępnych z zewnątrz układu) przypisane są trzy rejestry. Ale dzięki temu możemy
niemal dowolnie konfigurować każdą końcówkę układu związaną z portami, tzn. ustalać
kierunek, podciągać wejścia do zasilania, odłączać od reszty układu elektronicznego itp.
Rejestr kierunku danych (ang. Data Direction Register X, gdzie X jest oznaczeniem
znakowym portu np. DDRA jest rejestrem kierunku dla portu A). Ustalenie kierunku
odbywa się wg zasady:
Przykład:
DDRB = _BV(7)|_BV(6)|_BV(5)|_BV(4);
lub
DDRB = 0xF0;
Rejestr Danych Portu X (gdzie X jest oznaczeniem znakowym portu np. PORTA jest
rejestrem danych dla portu A). Na przykład aby załadować do rejestru PORTB wartość
0xAA należy użyć następującego kodu:
PORTB = 0xAA;
Odczyt z portu X (gdzie X jest oznaczeniem znakowym portu np. PINA jest rejestrem
odczytu portu A). Odczyt z tego portu daje nam fizyczny stan na końcówkach - oczywiście
pod warunkiem wcześniejszego ustalenia kierunku poprzez wpisanie zer w odpowiednie
bity rejestru kierunku.
Przykład:
19
Praktyczny sposób dostępu do wyprowadzeń układu
Trzy rejestry dla jednego fizycznego portu mogą być dla wielu zbyt dużą uciążliwością
szczególnie w przypadku, gdy z jakiegoś powodu musimy zmienić port, do którego
podłączamy jakieś urządzenie - wtedy w całym programie musieli byśmy dokonywać
zmian dotyczących trzech portów - łatwo więc o pomyłkę. Można jednak wykorzystać
fakt, że wszystkie opisywane tu rejestry łączy pewna zależność: w przestrzeni adresowej
układu znajdują się one "koło siebie". Załóżmy że piszemy procedury obsługi np.
wyświetlacza LCD, będzie on podłączony do jednego fizycznego portu np. PORTB.
Wystarczy umieścić w programie odpowiednie makrodefinicje a cała operacja stanie się
banalnie prosta. Przykład:
Po napisaniu takich makrodefinicji w dalszej części programu już nie posługujemy się
nazwami w rodzaju: PORTB, PINB, DDRB ale: LCD_PORT_O, LCD_PORT_I,
LCD_PORT_D. Ma to również jeszcze jedną zaletę - jeśli będziemy chcieli zmienić w
programie port do którego ma być przyłączone obsługiwane urządzenie wystarczy zmiana
tylko jednej linijki np.:
na:
Bardzo interesującą cechą układów AVR (szczególnie dla używających w swej praktyce
układów MCS-51) jest możliwość "podciągania" wejść do logicznej jedynki bez użycia
zewnętrznych rezystorów. Robi się to w ten sposób, że przy wpisanym zerze do bitu
kierunku DDRx (ustawienie bitu portu jako wejście) należy wpisać jedynkę na ten sam bit -
ale do portu PORTx. Zostanie to zilustrowane na przykładzie:
Tutaj celowo użyto dwóch instrukcji, jednak praktycznie można użyć tylko ostatniej
"podciągającej" wejście gdy mamy pewność, że wcześniej nie był ustawiany dany bit
ustalający kierunek w pocie (DDRx) - po starcie mikrokontrolera wszystkie bity związanie
z portami fizycznymi są wyzerowane czyli porty są ustawione na odczyt danych.
20
Programy przykładowe
21
powoduje zatrzymanie programu do momentu, aż na bicie 7 rejestru PINB pojawi się stan
niski. W efekcie program czeka na naciśnięcie przycisku podłączonego pomiędzy linię
PB7 mikrokontrolera a masę. Następnie instrukcja sbi(PORTB,0) wpisuje jedynkę do bitu
0 rejestru PORTB co w efekcie powoduje pojawienie się stanu wysokiego na
wyprowadzeniu PB0. W efekcie gasi diodę LED podłączoną do linii PB0. Dalej ponownie
pojawia się instrukcja loop_until_bit_is_clear(PINB,7) i pętla się zamyka - program
przechodzi ponownie do wykonywania instrukcji cbi(PORTB,0) itd.
Po zaprogramowaniu układu mikrokontrolera powinna zapalić się dioda LED podłączona
do linii PB0. Naciśnięcie przycisku podłączonego do linii PB7 powinno spowodować
lekkie zmniejszenie jasności świecenia diody (pojawia się tam fala prostokątna o
wypełnieniu 50\%). Gdy przestaniemy naciskać przycisk dioda zapali się lub zgaśnie w
zależności od tego, w którym miejscu był program w momencie zwolnienia przycisku.
Załóżmy jednak, że w przyszłości będziemy chcieli podłączyć diodę LED do innej
końcówki układu, bo np. w ten sposób uprościmy płytkę drukowaną dla układu. Podobnie
może być z przyciskiem. I co wtedy? Tutaj być może jeszcze nie będzie wielkiego
problemu, ot zmienimy wpisy w paru linijkach kodu i po kłopocie!
Jednak gdy kod źródłowy rozrośnie się powiedzmy do kilkuset a nawet kilku tysięcy linii
to tak napisany program może być bardzo trudny do późniejszej modyfikacji, a o pomyłkę
będzie bardzo łatwo. Warto więc zawczasu nabrać kilku dobrych nawyków. Po pierwsze -
korzystajmy z makrodefinicji (to nie pochłania pamięci programu mikrokontrolera!).
Program źródłowy jest większy i wygląda na bardziej skomplikowany lecz wielkość
programu wynikowego nie zmienia się w stosunku do tego z powyżsego listingu.
22
}
}
Makrodefinicjami:
gdzie symbolowi LED_PORT jest przypisywana wartość znajdująca się w linii po nim
czyli PORTB. W ten sposób zmieniając napis PORTB np. na PORTD spowodujemy, że
symbol LED_PORT przyjmie wartość PORTD. Teraz w dalszej części programu będziemy
się posługiwać symbolem LED_PORT. W kolejnym wierszu programu znajduje się
definicja:
#define LED_BIT 0
23
wejście szeregowe. Transmisja może się odbywać w trybie full-duplex, gdyż układ ten
posiada dwa niezależne rejestry transmisyjne. Układ posiada także własny układ taktujący,
co zwalnia liczniki-czasomierze z generowania tego sygnału. Jest to znaczne rozszerzenie
możliwości układu UART w stosunku do popularnej rodziny kontrolerów 8051.
Jego główne cechy to:
Uwaga!
Ze względu na duże rozbieżności w nazewnictwie rejestrów i ich bitów kontrolujących
port szeregowy opis będzie dotyczył "klasycznych" układów AVR np. AT90S2313 i
AT90S8515 - w układach ATMega istnieją ich odpowiedniki o często rozszerzonej
funkcjonalności.
Inicjalizacja
F_CPU
UBRR = ---------------- - 1
(UART_BAUD * 16)
24
gdzie:
F_CPU - częstotliwość zegara mikrokontrolera w Hz,
UART_BAUD - prędkość transmisji w b/s.
Powyższą formułę można umieścić w programie jako makrodefinicje np.
Gdy mamy ustaloną prędkość transmisji należy zezwolić na transmisję i/lub odbiór
znaków (w zależności od potrzeb). Robi się to przez ustawianie odpowiednich bitów w
rejestrze UCR (RXEN - zezwolenie na odbiór, TXEN - zezwolenie na nadawanie) np.
poprzez wpisanie takiego kodu:
UCR = _BV(RXEN)|_BV(TXEN);
W tym momencie mamy zainicjowany interfejs szeregowy - pora więc z niego skorzystać.
Wysyłanie znaku
Odbiór znaku
Podobnie jak w przypadku nadajnika, główną częścią części odbiorczej jest rejestr
przesuwający oraz połączony z nim rejestr bufora UDR. Jak wspomniano na początku
25
część odbiorcza jest wyposażona w układ eliminujący zakłócenia jakie mogą wystąpić
podczas transmisji bitów. Mechanizm jego działania jest prosty. Polega on na
wielokrotnym próbkowaniu stanu linii RxD. Jeśli logika sterująca stwierdzi, że co najmniej
dwie ostatnie z trzech próbek – w środku “okna” dla każdego z bitów – są identyczne, stan
linii jest uznawany za ważny i bit trafia do rejestru przesuwającego.
Gdy mamy już procedurę służącą do wysyłania danych przydała by się do niej
komplementarna - czyli odbierająca znaki oto i ona:
Program przykładowy
26
#define LED_PORT_I PIN(LED_PORT) // rejestr wejściowy
// _UCR_
#ifdef UCR
#define _UCR_ UCR
#endif
#ifdef UCSRB
#define _UCR_ UCSRB
#endif
#ifdef UCSR0B
#define _UCR_ UCSR0B
#endif
// _USR_
#ifdef USR
#define _USR_ USR
#endif
#ifdef UCSRA
#define _USR_ UCSRA
#endif
#ifdef UCSR0A
#define _USR_ UCSR0A
#endif
// Definicje funkcji
27
int main(void) // program główny
{
UART_init(); // inicjalizacja portu szeregowego
28
urządzeń, ponieważ możemy w zasadzie w dowolnej chwili skorzystać z linii używanych
do komunikacji z pamięcią np. w celu obsłużenia innych urządzeń. W pamięci SRAM
przechowywane są wszystkie zmienne. Deklaracje często używanych całkowitych typów
danych znajdują się w pliku avr/inttypes.h. Stałe są tworzone poprzez podanie słowa
kluczowego const. Taka "zmienna" posiada atrybut tylko do odczytu i nie może być
zmieniana - jest stałą przechowywaną w pamięci SRAM.
W tabeli przedstawiono predefiniowane typy zmiennych wg standardu ISO C99 stworzone
po to, aby ułatwić pisanie programów i było łatwiejsze szybkie zorientowanie się w ilości
bajtów (bitów) zajmowanych przez daną zmienną.
Są też stosowane bardziej skrócone nazwy w rodzaju u08, s08, u16 itp. Jak widać są one
krótsze a równie łatwo można się zorientować ile miejsca w pamięci będą zajmowały
zmienne. Oto definicje takich typów danych:
Operacje na zmiennych
Tworzenie zmiennej
Tworzenie zmiennej to prosta operacja, którą można wykonać na wiele sposobów np.:
lub
uint_8 val = 8;
lub
u08 val = 8;
29
korzyść
z zastosowania krótszej nazwy typu - po prostu mniej pisania a efekt taki sam!
lub
lub
val = 10;
val++;
Po wywołaniu obu powyższych instrukcji zmienna o nazwie val przyjmie wartość 11.
Stałe deklaruje się bez podania typu za pomocą słowa kluczowego const.
Program przykładowy
30
zachowanie zmiennych. Komunikacja z użytkownikiem będzie zrealizowana tak jak w
poprzednim przykładzie za pomocą łącza szeregowego.
Jednak same funkcje komunikacji szeregowej powinny zostać "ukryte" aby nie zaciemniać
głównego programu. W związku z tym funkcje związane z portem szeregowym
przeniesiono do osobnych plików i umieszczono w oddzielnym katalogu (../lib), tak aby i
inne programy mogły z nich korzystać. Funkcje biblioteczne umieszczone w tym katalogu
zostały omówione tutaj.
31
putint(val2); // wyświetl val2
na linię w rodzaju:
32
• 8 bitowy: prog_char
• 16 bitowy: prog_int
• 32 bitowy: prog_long
• 64 bitowy: prog_long_long
Na przykład tak:
jest również możliwe utworzenie otwartej tablicy (bez określenia wielkości - kompilator
robi to za nas) ...
33
char LINE2[] __attribute__ ((progmem)) = "to jest drugi wiersz";
inny przykład:
tworzy łańcuch w pamięci programu i zwraca wskaźnik do niego w text1.
Zmienna lastchar zawiera teraz 43 znak z łańcucha LINE1 ponieważ w języku C wszystkie
tablice są indeksowane od zera.
Program przykładowy
Naszym celem będzie napisanie programu, który pokaże zachowanie stałych i tablic
umieszczonych w pamięci programu. Komunikacja z użytkownikiem będzie zrealizowana
tak jak w poprzednim przykładzie za pomocą łącza szeregowego.
34
}
35
while(1); // pętla nieskończona
}
Program przykładowy
36
zrealizowana tak jak w poprzednich przykładach za pomocą terminala podłączonego do
łącza szeregowego. Podczas jego testowania należy zwrócić uwagę na powstały w wyniku
kompilacji plik .eep zawierający dane przeznaczone do wpisania w pamięć EEPROM
mikrokontrolera.
37
val2 = eeprom_read_byte((uint8_t*)E2END); // odczyt eeprom spod adresu
E2END
UART_putint(val2,RADIX);
jest zdefiniowana tablica o nazwie ver[] również w pamięci EEPROM zawierająca ciąg
znaków z datą i czasem kompilacji programu. Dostęp do tych zmiennych jest możliwy
przez specjalne funkcje dostępu do pamięci EEPROM. W kolejnych liniach mamy
zdefiniowaną funkcję UART_putstr_E(*s), która służy do wysyłania przez port zeregowy
łańcucha znajdującego się w pamięci EEPROM. Dalej znajduje się program główny -
funkcja main(), w której są zadeklarowane zmienne: buffer, val1 z zainicjowaną wartością
oraz val2. Po zainicjowaniu portu szeregowego przez funkcję UART_init() następuje
wypisanie tekstu:
1. Odczyt z pamięci EEPROM eeprom_val ->
val2 = eeprom_read_byte((u08*)&eeprom_val);
SIGNAL (nazwa_uchwytu)
{
// Instrukcje tu zawarte będą wykonywane jako obsługa przerwania
}
INTERRUPT (nazwa_uchwytu)
{
// Instrukcje tu zawarte będą wykonywane jako obsługa przerwania
}
Program przykładowy
Naszym celem będzie napisanie programu, który pokaże w możliwie najprostszy sposób
wykorzystanie przerwań. Komunikacja z użytkownikiem będzie zrealizowana za pomocą
przycisków podłączonych z jednej strony do masy a z drugiej do linii INT0 i INT1
mikrokontrolera oraz diod LED podłączonych do portu B.
SIGNAL (SIG_INTERRUPT0)
39
{
PORTD = 0x1d; // zgaś diode LED
}
SIGNAL (SIG_INTERRUPT1)
{
PORTD = 0x0d; // zapal diode LED
}
GIMSK = _BV(INT0)|_BV(INT1);
//włącz obsługę przerwań Int0 i Int1
MCUCR = _BV(ISC01)|_BV(ISC11);
// włącz generowanie przerwań przez
// opadające zbocze na Int0 i Int1
40
które następnie należy obsłużyć. Również program może odczytywać jego wartość w
dowolnym momencie (tzw. polling) i na tej podstawie podejmować odpowiednie działania.
Tryb licznika
W tym trybie są zliczane zmiany stanu na końcówce T0. Zmiany stanu na końcówce T0 są
synchronizowane z częstotliwością zegarową CPU.Aby te zmiany były zauważone,
minimalny czas pomiędzy tymi zmianami musi być większy od okresu zegara CPU. Stan
na wejściu T0 jest próbkowany w czasie narastającego zbocza wewnętrznego sygnału
zegarowego CPU. Aby włączyć zliczanie impulsów należy ustawić odpowiednią
kombinację bitów w rejestrze TCCR0.
Programy przykładowe
uint8_t led;
sbi(TIFR,TOV0);
// jeśli wpiszemy 1 do bitu TOV0
// to ten bit zostanie skasowany
// przy następnym przepełnieniu licznika 0
}
}
41
instrukcją w tej pętli jest ustawienie bitu TOV0, które w efekcie powoduje jego
skasowanie.
Bardziej złożony przykład na wykorzystanie licznika 0 do zliczania impulsów na wejściu
T0. Dzięki wykorzystaniu przerwań można całkowicie oddzielić zliczanie impulsów od
programu głównego. Obsługa tego programu polega na częstym podawaniu impulsów na
wejście T0 mikrokontrolera (np. poprzez zwieranie go z masą) i wysyłaniu dowolnego
znaku na port szeregowy z terminala. Na terminalu otrzymany wyniki działania programu.
SIGNAL (SIG_OVERFLOW0)
{
overflow++; // inkrementuj licznik przepełnień
}
int main(void)
{
UART_init(); // inicjalizacja portu szeregowego
DDRB = 0xFF; // wszystkie linie PORTB jako wyjścia
// nawet linia PB0 - wejście T0
PORTB = 0xFF; // wszystkie linie PORTB w stan wysoki
TIMSK = _BV(TOIE0); // włącz obsługę przerwań T/C0
TCNT0 = 0x00; // wartość początkowa T/C0
TCCR0 = _BV(CS01)|_BV(CS02);
// wyzwalanie z T0 opad. zboczem
PRINT_VALUE(PSTR("overflow="),overflow);
// overvlow na UART
PRINT_VALUE(PSTR("TCNT0="),TCNT0);
// TCNT0 na UART
42
PRINT_VALUE(PSTR("licznik (overflow*256)+TCNT0="),licznik);
// licznik na UART
Tryb czasomierza
Programy przykładowe
// Testowanie timera 0 (polling)
uint8_t led;
uint8_t state;
TIFR = _BV(TOV0);
// jeśli 1 wpiszemy do bitu TOV0 to
// ten bit powinien zostac skasowany
// przy następnym przepełnieniu licznika 0
}
}
43
linię PB0 jako wejście z uwagi na jej alternatywną funkcję: wejście licznika T0. Następnie
jest ustawiana wartość początkowa licznika TCNT0 na 0. W kolejnej instrukcji poprzez
ustawienie bitu TOIE0 w rejestrze TIMSK zostało włączone generowanie przerwań od
przepełnienia licznika TCNT0. Podobnie poprzez ustawienie bitów CS01 i CS02 w
rejestrze TCCR0 zostało włączone taktowanie licznika z wejścia T0. Przed wejściem do
głównej pętli programu instrukcją sei() została włączona obsługa przerwań. Pierwszą
instrukcją w głównej pętli jest UART_getchar() czekająca na dowolny znak z portu
szeregowego. Po odebraniu dowolnego znaku z tego portu obliczana jest wartość licznika
impulsów na wejściu T0 mikrokontrolera wg wzoru:
jednak mnożenie przez 256 łatwiej i szybciej mikrokontroler wykona stosując przesuwanie
w lewo o 8 bitów a dodawanie poprzez sumę logiczną. W związku z powyższym wartość
licznika jest obliczana w następujący sposób:
licznik=(overflow<<8)|(inb(TCNT0));
SIGNAL (SIG_OVERFLOW0)
{
TCNT0 = T0_INIT; // przeładuj timer 0
if (overflow>0)
overflow--; // dekrementuj
tbi(PORTD,PD5); // przełącz stan LED na PB1
}
int main(void)
{
DDRD = 0xFF; // wszystkie linie PORTD jako wyjścia
PORTD = 0xFF; // wygaś diody LED
TIMSK = _BV(TOIE0); // włącz obsługę przerwań T/C0
44
TCNT0 = T0_INIT; // wartość początkowa T/C0
TCCR0 = _BV(CS00)|_BV(CS02); // preskaler 1024
Rejestry licznika/czasomierza 1
Tryb licznika
W tym trybie są zliczane zmiany stanu na końcówce T1. Zmiany stanu na końcówce T1 są
synchronizowane z częstotliwością zegarową CPU. Aby te zmiany były zauważone,
minimalny odstęp czasu pomiędzy tymi zmianami musi być większy od okresu zegara
CPU. Stan na wejściu T1 jest próbkowany w czasie narastającego zbocza wewnętrznego
sygnału zegarowego CPU. Aby włączyć zliczanie impulsów należy ustawić odpowiednią
kombinację bitów w rejestrze TCCR1B.
45
Bity rejestru TCCR1B określające zliczanie impulsów zewnętrznych przez licznik 1
Tryb czasomierza
Programy przykładowe
Naszym celem będzie napisanie programów, które pokażą w możliwie najprostszy sposób
wykorzystanie licznika/czasomierza 1 do zliczania impulsów pochodzących z
wewnętrznego preskalera. W obu przypadkach do komunikacji z użytkownikiem będą
wykorzystane diody LED podłączone do portu B. Pierwszy przykład przedstawia
wykorzystanie cyklicznego sprawdzania jego stanu (polling) natomiast drugi realizuje
dokładnie to samo ale poprzez obsługę przerwania.
uint8_t led;
uint8_t state;
46
TCNT1 = 0xFF00; // wartość początkowa T/C1
TIFR = _BV(TOV1);
// jeśli ustawimy bit TOV1 to
// ten bit zostanie skasowany
// przy następnym przepełnieniu licznika 1
}
}
uint8_t led;
SIGNAL (SIG_OVERFLOW1)
{
PORTC = ~led++; // wyświetl na LED-ach
TCNT1 = 0xFF00; // przeładuj timer 1
}
int main(void)
{
DDRC = 0xFF; // wszystkie linie PORTC jako wyjścia
TIMSK = _BV(TOIE1); // włącz obsługę przerwań T/C1
TCNT1 = 0xFF00; // wartość początkowa T/C1
TCCR1A = 0x00; // włącz tryb czasomierza T/C1
TCCR1B = _BV(CS10)|_BV(CS12);
// preskaler ck/1024
sei(); // włącz obsługę przerwań
while(1); // pętla nieskończona
}
Tryb porównywania
47
0 | 0 | Licznik 1 odłączony od końcówki OC1B
0 | 1 | Przełączanie stanu końcówki OC1B na przeciwny
1 | 0 | Zerowanie stanu końcówki OC1B
1 | 1 | Ustawianie stanu końcówki OC1B
Program przykładowy
Naszym celem będzie napisanie programu, który pokaże nam zastosowanie trybu
porównywania.
Będzie to generator przebiegu prostokątnego o regulowanej częstotliwości i wypełnieniu
przebiegu.
Wyjściem generatora jest PB0. Częstotliwość reguluje się zwierając do masy linie PD3 lub
PD4.
Wypełnienie regulujemy zwierając do masy linie PD5 lub PD6.
48
if (bit_is_clear(PIND,PD3)) // jeżeli zwarto PD3 z masą
delay-=0x80; // zmniejsz delay o 128
loop_until_bit_is_set(PIND,PD3);
// czekaj na zwolnienie PD3
}
}
Tryb przechwytywania
Program przykładowy
49
#include <avr/io.h> // dostęp do rejestrów
#include <avr/interrupt.h> // funkcje sei(), cli()
#include <avr/signal.h> // definicje SIGNAL, INTERRUPT
#include "uart.h" // obsługa portu szeregowego
int main(void)
{
UART_init(); // inicjalizacja portu szeregowego
TCCR1B = _BV(ICNC1)|_BV(CS10)|_BV(CS12);
// opadające zbocze i filtracja zakłóceń na ICP
// taktowanie T1 CK/1024
UART_putstr_P(CLEAR);
PRINT_VALUE(PSTR("TCNT1 = 0x"),TCNT1);
// TCNT1 na UART
PRINT_VALUE(PSTR("ICR1 = 0x"),ICR1);
// ICR1 na UART
}
}
Kiedy zostanie wybrany tryb pracy licznika jako PWM (ang. Pulse Width Modulation)
czyli modulacja szerokości impulsów. Czasomierz 1 może być używany jako 8, 9 lub 10
bitowy samobieżny modulator PWM. Tryb PWM ustawiany jest przez ustawianie bitów
PWM10 i PWM11 znajdujących się w rejestrze TCCR1A.
50
Licznik może zliczać od 0x0000 do wybranej granicy (8 bitowy - 0x00FF, 9 bitowy -
0x01FF, 10 bitowy - 0x03FF), kiedy ją przekroczy liczy z powrotem od zera i powtarza ten
cykl w nieskończoność. Kiedy wartość licznika zrówna się z wartością rejestru
porównującego (OCR1A, OCR1B) daje następujący efekt na wyjściach OC1A i OC1B w
zależności od ustawień jak w tabeli:
Gdzie: x to A lub B
Program przykładowy
Naszym celem będzie napisanie programu, który pokaże działanie wyjścia PWM.
Urządzeniem wyjściowym będzie dioda LED wraz z rezystorem 470 om podłączona
między końcówkę OC1A a zasilanie.
Sterowanie programem umożliwiają dwa przyciski podłączone między końcówki PD2 i
PD3 a masę.
#ifdef __AVR_AT90S2313__
#define PWM_out(value) OCR1=value
#endif
51
#if defined(__AVR_AT90S4414__) || defined(__AVR_AT90S8515__) || \
defined(__AVR_AT90S4434__) || defined(__AVR_AT90S8535__) || \
defined(__AVR_ATmega163__) || defined(__AVR_ATmega16__)
sbi(DDRD,PD5); // ustawienie kierunku wyjscia PWM
#endif
#ifdef __AVR_AT90S2313__
sbi(DDRB,PB3); // ustawienie kierunku wyjscia PWM
#endif
TCCR1A = _BV(COM1A1)|_BV(COM1A0)|_BV(PWM10)|_BV(PWM11);
// czasomierz 1 w trybie 10 bitowego
PWM
TCCR1B = _BV(CS00); // czasomierz 1 taktowany F_CPU
Rejestry licznika/czasomierza 2
Nazwa | Funkcja
--------------------------------------
52
TCCR2 | Rejestr kontrolny
TCNT2 | Wartość
OCR2 | Rejestr porównujący
ASSR | Asynchroniczny rejestr statusu
Tryb czasomierza
Program przykładowy
53
// j.w. spacje
// zliczanie czasu
// w oparciu o ATMEL Application Note AVR134
SIGNAL(SIG_OVERFLOW2) // obsługa przerwania od licznika 2
{
if (++t.second==60) // inkrementuj sekundy i sprawdź czy
jest ich 60
{ // jeśli tak to:
t.second=0; // wyzeruj licznik sekund oraz
if (++t.minute==60) // inkrementuj licznik minut i sprawdź czy
jest ich 60
{ // jeśli tak to:
t.minute=0; // wyzeruj licznik minut oraz
if (++t.hour==24) // inkrementuj licznik godzin i sprawdź
czy jest ich 24
{ // jeśli tak to:
t.hour=0; // wyzeruj licznik godzin oraz
if (++t.date==32) // inkrementuj licznik dni i sprawdź czy
jest ich 32
{ // jeśli tak to:
t.month++; // inkrementuj licznik miesięcy
t.date=1; // ustaw dzień na 1
}
else if (t.date==31) // jeżeli dzień równa się 31
{ // to sprawdź czy są to miesiące: 4, 6,
9, 11
if ((t.month==4) || (t.month==6) ||
(t.month==9) || (t.month==11))
{ // jeśli tak to:
t.month++; // inkrementuj licznik miesięcy
t.date=1; // ustaw dzień na 1
}
}
else if (t.date==30) // jeżeli dzień równa się 30
{ // to sprawdź czy
if (t.month==2) // jest to miesiąc 2 (luty)
{ // jeśli tak to:
t.month++; // inkrementuj licznik miesięcy
54
t.date=1; // ustaw dzień na 1
}
}
else if (t.date==29) // jeżeli dzień równa się 29
{ // to sprawdź czy:
if ((t.month==2) && (not_leap())) // miesiąc 2 i rok
nieprzestępny
{ // jeśli tak to:
t.month++; // inkrementuj licznik miesięcy
t.date=1; // ustaw dzień na 1
}
}
if (t.month==13) // jeśli miesiąc wynosi 13
{ // to:
t.month=1; // ustaw miesiąc na 1 (styczeń)
t.year++; // inkrementuj licznik lat
}
}
}
}
}
55
last_second=t.second; // zapamiętaj obecną sekundę
UART_putstr_P(HOME); // ustaw kursor na początku
PRINT_VALUE(PSTR("Rok = "),t.year); // wyświetl rok
PRINT_VALUE(PSTR("Miesiac = "),t.month); // wyświetl miesiąc
PRINT_VALUE(PSTR("Dzien = "),t.date); // wyświetl dzien
PRINT_VALUE(PSTR("Godzina = "),t.hour); // wyświetl godzinę
PRINT_VALUE(PSTR("Minuta = "),t.minute); // wyświetl minutę
PRINT_VALUE(PSTR("Sekunda = "),t.second); // sekundę
}
}
}
Tryb porównywania
Jeśli chcemy wyzerować licznik gdy zachodzi równość z rejestrem porównującym należy
ustawić bit CTC2 w rejestrze TCCR2.
Program przykładowy
Naszym celem będzie napisanie programu, który pokaże nam zastosowanie trybu
porównywania.Będzie to generator przebiegu prostokątnego o regulowanej częstotliwości i
wypełnieniu przebiegu. Wyjściem generatora jest PB0. Częstotliwość reguluje się
zwierając do masy linie PD3 lub PD4. Wypełnienie regulujemy zwierając do masy linie
PD5 lub PD6.
56
#include <avr/signal.h> // definicje SIGNAL, INTERRUPT
#include <avr/delay.h> // funkcje opóźniające
57
w nieskończoność. Kiedy wartość licznika zrówna się z wartością rejestru porównującego
OCR2 daje następujący efekt na wyjściu OC2 w zależności od ustawień bitów jak w tabeli
\ref{pwm2wyj}.
Praca wyjścia PWM (bity rejestru TCCR2)
Program przykładowy
// Testowanie timera 2 w trybie samobieżnego PWM
TCCR2 = _BV(COM21)|_BV(COM20)|_BV(PWM2)|_BV(CS20);
// czasomierz 2 w trybie PWM
// taktowany F_CPU
58
licznika/czasomierza 1. Oprócz tego, komparator analogowy może wygenerować
przerwanie.
Można wybrać czy przerwanie ma być generowane przez wyjście komparatora zboczem
narastającym,
opadającym lub obydwoma. Ta funkcja jest kontrolowana przez bity ACIS0 i ACIS1
rejestru ACSR
Programy przykładowe
Aby przetestować działanie komparatora należy podłączyć do jego wejść prosty układ,
który wymusi różne napięcia na jego wejściach np. do wejścia AIN0 podłączmy
symetryczny dzielnik napięcia dający na wyjściu połowę napięcia zasilania. Do wejścia
AIN1 podłączmy końcówkę suwaka potencjometru, którego krańcowe wyprowadzenia są
podłączone pomiędzy zasilanie a masę. Ze względu na dużą rezystancję wejściową
komparatora, wartości rezystancji rezystorów i potencjometru nie są krytyczne i mogą
zawierać się w granicach od 1k do 1M.
Przykład z poniższego listingu ilustruje działanie komparatora analogowego. Stan wyjścia
komparatora jest cyklicznie sprawdzany i na jego podstawie sterowana jest dioda LED
podłączona do linii PB7.
int main(void)
{
sbi(DDRB, PB7); // linia PB7 jako wyjście (LED)
while(1) // pętla nieskończona
{
if (bit_is_set(ACSR, ACO)) // jeżeli na wyjściu komparatora
jest 1
cbi(PORTB, PB7); // zapal diodę na PB7
else // w przeciwnym wypadku
sbi(PORTB, PB7); // zgaś ją
}
}
SIGNAL(SIG_COMPARATOR)
{
tbi(PORTB, PB7); // zmień stan diody na PB7
}
int main(void)
{
59
sbi(DDRB, PB7); // linia PB7 jako wyjście (LED)
ACSR = _BV(ACIE); // komparator analogowy generuje przerwanie
// przy każdej zmianie stanu na jego wyjściu
sei(); // włącz obsługę przerwań
while(1); // pętla nieskończona
}
#ifdef __AVR_AT90S2313__
# define C_PIN PB1 // końcówka kondensatora i rezystora
#else
# define C_PIN PB3 // końcówka kondensatora i rezystora
#endif
60
sbi(DDRB, C_PIN); // ustaw linię kondenstaora (- komparatora)
jako wyjście
delay10us(); // i czekaj na rozładowanie kondensatora
cbi(DDRB, C_PIN); // ustaw linię kondenstaora (- komparatora)
jako wejście
sbi(DDRB, R_PIN); // podłącz linię rezystora (rozpocznij
ładowanie kondensatora)
TCCR1B = _BV(CS10)|_BV(ICNC1);
// licznik 1 taktowany F_CPU
// wraz z filtracją zakłóceń z wejścia
przechwytywania
// i przechwytywaniem za pomocą opadającego
zbocza
while(busy); // czekaj na przerwanie od przechwytywania
lub przepełnienia
}
TIMSK = _BV(TOIE1)|_BV(TICIE1);
// włącz przerwania licznika 1
// od przepełnienia i przechwytywania
61
Przetwornik analogowo/cyfrowy w mikrokontrolerze integruje przetwarzanie sygnałów
analogowych i cyfrowych w jednym układzie. Posiada on rozdzielczość 10 bitów (1024
poziomy). Pracuje na zasadzie sukcesywnej aproksymacji. Aby można było mierzyć
wartości sygnałów analogowych z wielu źródeł, na wejściu przetwornika umieszczono
multiplekser analogowy. W danej chwili tylko jeden z kilku sygnałów analogowych może
być zamieniony na postać cyfrową. Rejestr ADMUX z załadowaną odpowiednią wartością
wskazuje, który z tych kanałów jest połączony z przetwornikiem.
Wartość Używany
ADMUX kanał
--------------------
0 kanał 0
1 kanał 1
2 kanał 2
3 kanał 3
4 kanał 4
5 kanał 5
6 kanał 6
7 kanał 7
Przykład:
fosz = 4MHz
50kHz < fad < 200kHz
-> prescale = 32
fad = fosz/32 = 4000000/32 = 125000 = 125 kHz
50kHz < 125kHz < 200kHz
-> ADPS0 = 1, ADPS1 = 0, ADPS2 = 1
Rejestr ADCSR
62
2 | ADPS2 | preskaler
3 | ADIE | zezwolenie na generowanie przerwania przez przetwornik na
zakończenie przetwarzania
4 | ADIF | znacznik przerwania z przetwornika - jeśli jest ustawiony
to konwersja A/D została zakończona
5 | ADFR | przetwarzanie samodzielne (Free Run)
6 | ADSC | start konwersji - jeśli jest ustawiony ten bit oraz ADEN
7 | ADEN | włączenie przetwornika (AD Enable)
Programy przykładowe
63
szeregowy mikrokontrolera. Wynik przetwarzania jest wysyłany również na port
szeregowy oraz 8 bardziej znaczących bitów wyniku jest prezentowanych na diodach LED
podłączonych do PORTB.
64
#include <avr/io.h> // dostęp do rejestrów
#include <avr/interrupt.h> // funkcje sei(), cli()
#include <avr/signal.h> // definicje SIGNAL, INTERRUPT
#include "uart.h" // obsługa portu szeregowego
65
czasomierzem a generatorem znajduje się preskaler, którym można ustalić jak często ma
być generowany sygnał restartujący mikrokontroler. Aby jednak nie dopuścić do
restartowania mikrokontrolera należy co pewien czas zerować czasomierz układu nadzoru.
Biblioteka avr-libc dołączona do kompilatora zawiera funkcje ułatwiające używanie
układu Watchdog. Do programu należy dołączyć nagłówek avr/wdt.h.
wdt_enable(timeout)
Załącza układ Watchdog z czasem zdefiniowanym jako timeout. Jako timeout można użyć
predefiniowanych wartości zamieszczonych w tabeli:
Stała Czas
-------------------
WDTO_15MS 15 ms
WDTO_30MS 30 ms
WDTO_60MS 60 ms
WDTO_250MS 250 ms
WDTO_500MS 500 ms
WDTO_1S 1000 ms
WDTO_2S 2000 ms
Przykład:
wdt_enable(WDTO_500MS);
Należy pamiętać, że czasy te podane są tylko orientacyjnie i mogą się różnić od podanych
nawet kilkukrotnie! Niższe napięcie zasilania mikrokontrolera oznacza wydłużenie czasu
zadziałania układu Watchdog.
wdt_reset()
Powoduje kasowanie czasomierza układu Watchdog. Kiedy układ Watchdog jest aktywny,
powyższą funkcję należy wywoływać odstępach czasu mniejszych od czasu zadziałania
układu Watchdog.
wdt_disable()
Powoduje wyłączenie układu Watchdog.
Program przykładowy
66
#define WDT_ENABLE // określa czy używamy Watchdoga
SIGNAL (SIG_OVERFLOW0)
{
// WDR(); // reset licznika Watchdog
// nie powinno się resetować Wdoga
// w fukcjach obsługi przerwań !!!
}
void long_loop(void)
{
uint16_t i,j,k; // zmienne lokalne dla pętli
UART_putstr_P(PSTR("long_loop - start\n\r"));
for(i=0;i<0xFFFF;i++) // pierwsza pętla
{
for(j=0;j<0xFFFF;j++) // druga pętla
{
for(k=0;k<0xFFFF;k++) // trzecia pętla
{
// WDR(); // reset licznika Watchdog
}
// WDR(); // reset licznika Watchdog
}
WDR(); // reset licznika Watchdog
}
UART_putstr_P(PSTR("long_loop - stop\n\r"));
}
int main(void)
{
UART_init(); // inicjalizacja portu szeregowego
UART_putstr_P(PSTR("Program wystartował !!!\n\r"));
#ifdef WDT_ENABLE
// wdt_enable(WDTO_2S); // watchdog na czas ok 2s
// wdt_enable(WDTO_1S); // watchdog na czas ok 1s
wdt_enable(WDTO_500MS); // watchdog na czas ok 0,5s
// wdt_enable(WDTO_250MS); // watchdog na czas ok 0,25s
// wdt_enable(WDTO_120MS); // watchdog na czas ok 0,12s
// wdt_enable(WDTO_60MS); // watchdog na czas ok 0,06s
// wdt_enable(WDTO_30MS); // watchdog na czas ok 0,03s
// wdt_enable(WDTO_15MS); // watchdog na czas ok 0,015s
#endif
67
AVR-GCC - tryby zmniejszonego poboru
mocy
Mikrokontrolery AVR są wyposażone w układy pozwalające na wejście w tryby pracy ze
zmniejszonym poborem mocy.
Oczywiście nie ma nic zupełnie za darmo i odbywa się to różnymi kosztami jak np.
zmniejszona szybkość pracy, wyłączenie niektórych urządzeń peryferyjnych (komparator
analogowy, przetwornik analogowo/cyfrowy itp.).
set_sleep_mode(mode)
Przygotowuje mikrokontroler do wprowadzenia w tryb uśpienia przez ustawienie
odpowiednich bitów w rejestrze SMCR lub MCUCR (sprawdź w dokumentacji
mikrokontrolera): SM0, SM1, SM2.
Poniżej przedstawiono możliwe tryby obniżonego poboru mocy:
• SLEEP_MODE_IDLE
• SLEEP_MODE_PWR_DOWN
• SLEEP_MODE_PWR_SAVE
• SLEEP_MODE_ADC
• SLEEP_MODE_STANDBY
• SLEEP_MODE_EXT_STANDBY
Przykład:
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
sleep_mode()
Wprowadza mikrokontroler w tryb uśpienia wybrany wcześniej za pomocą funkcji
set_sleep_mode(mode).
Program przykładowy
68
#define PWR_save bit_is_clear(PINB,PINB0)
// zliczanie czasu
// w oparciu o ATMEL Application Note AVR134
SIGNAL(SIG_OVERFLOW2) // obsługa przerwania od licznika 2
{
if (++t.second==60) // inkrementuj sekundy i sprawdź czy
jest ich 60
{ // jeśli tak to:
t.second=0; // wyzeruj licznik sekund oraz
if (++t.minute==60) // inkrementuj licznik minut i
sprawdź czy jest ich 60
{ // jeśli tak to:
t.minute=0; // wyzeruj licznik minut oraz
if (++t.hour==24) // inkrementuj licznik godzin i
sprawdź czy jest ich 24
{ // jeśli tak to:
t.hour=0; // wyzeruj licznik godzin oraz
if (++t.date==32) // inkrementuj licznik dni i sprawdź czy
jest ich 32
{ // jeśli tak to:
t.month++; // inkrementuj licznik miesięcy
t.date=1; // ustaw dzień na 1
}
else if (t.date==31) // jeżeli dzień równa się 31
{ // to sprawdź czy są to miesiące: 4, 6,
9, 11
if ((t.month==4) || (t.month==6) ||
(t.month==9) || (t.month==11))
69
{ // jeśli tak to:
t.month++; // inkrementuj licznik miesięcy
t.date=1; // ustaw dzień na 1
}
}
else if (t.date==30) // jeżeli dzień równa się 30
{ // to sprawdź czy
if (t.month==2) // jest to miesiąc 2 (luty)
{ // jeśli tak to:
t.month++; // inkrementuj licznik miesięcy
t.date=1; // ustaw dzień na 1
}
}
else if (t.date==29) // jeżeli dzień równa się 29
{ // to sprawdź czy:
if ((t.month==2) && (not_leap())) // miesiąc 2 i rok
nieprzestępny
{ // jeśli tak to:
t.month++; // inkrementuj licznik miesięcy
t.date=1; // ustaw dzień na 1
}
}
if (t.month==13) // jeśli miesiąc wynosi 13
{ // to:
t.month=1; // ustaw miesiąc na 1 (styczeń)
t.year++; // inkrementuj licznik lat
}
}
}
}
if (PWR_save) // jeśli układ ma być w trybie
obniżonego poboru
sleep_mode(); // to wprować go w ten tryb
}
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
// przygotuj układ do obniżonego poboru mocy
70
for(temp0=0;temp0<0x0040;temp0++)
for(temp1=0;temp1<0xFFFF;temp1++);
// czekaj na ustabilizowanie się generatora
32768 Hz
-mmcu=architektura
Kompiluje kod dla podanej architektury. Przez avr-gcc rozpoznawane są następujące
architektury:
• avr1 - "Prosty" rdzeń CPU, tylko programy w asemblerze
• avr2 - "Klasyczny" rdzeń CPU, do 8 KB ROM
• avr3 - "Klasyczny" rdzeń CPU, więcej niż 8 KB ROM
71
• avr4 - "Rozszerzony" rdzeń CPU, do 8 KB ROM
• avr5 - "Rozszerzony" rdzeń CPU, więcej niż 8 KB ROM
Domyślnie kod jest generowany dla architektury avr2. Zauważmy, że kiedy używamy -
mmcu=architecture a nie -mmcu=typ_MCU, plik nagłówkowy avr/io.h nie będzie
realizował swej funkcji dopóki nie zdecydujemy się, na konkretny typ MCU. Typ MCU
wybiera się podobnie.
-mmcu=typ_MCU
W tabeli przedstawiono zestawienie wszystkich typów MCU rozpoznawanych przez
kompilator avr-gcc (a dokładniej bibliotekę avr-libc).
arch typ_MCU makrodefinicja
----------------------------------
avr1 at90s1200 __AVR_AT90S1200__
avr1 attiny11 __AVR_ATtiny11__
avr1 attiny12 __AVR_ATtiny12__
avr1 attiny15 __AVR_ATtiny15__
avr1 attiny28 __AVR_ATtiny28__
avr2 at90s2313 __AVR_AT90S2313__
avr2 at90s2323 __AVR_AT90S2323__
avr2 at90s2333 __AVR_AT90S2333__
avr2 at90s2343 __AVR_AT90S2343__
avr2 attiny22 __AVR_ATtiny22__
avr2 attiny24 __AVR_ATtiny24__
avr2 attiny25 __AVR_ATtiny25__
avr2 attiny26 __AVR_ATtiny26__
avr2 attiny261 __AVR_ATtiny261__
avr2 attiny44 __AVR_ATtiny44__
avr2 attiny45 __AVR_ATtiny45__
avr2 attiny461 __AVR_ATtiny461__
avr2 attiny84 __AVR_ATtiny84__
avr2 attiny85 __AVR_ATtiny85__
avr2 attiny861 __AVR_ATtiny861__
avr2 at90s4414 __AVR_AT90S4414__
avr2 at90s4433 __AVR_AT90S4433__
avr2 at90s4434 __AVR_AT90S4434__
avr2 at90s8515 __AVR_AT90S8515__
avr2 at90c8534 __AVR_AT90C8534__
avr2 at90s8535 __AVR_AT90S8535__
avr2 at86rf401 __AVR_AT86RF401__
avr2 attiny13 __AVR_ATtiny13__
avr2 attiny2313 __AVR_ATtiny2313__
avr3 atmega103 __AVR_ATmega103__
avr3 atmega603 __AVR_ATmega603__
avr3 at43usb320 __AVR_AT43USB320__
avr3 at43usb355 __AVR_AT43USB355__
avr3 at76c711 __AVR_AT76C711__
avr4 atmega48 __AVR_ATmega48__
avr4 atmega8 __AVR_ATmega8__
avr4 atmega8515 __AVR_ATmega8515__
avr4 atmega8535 __AVR_ATmega8535__
avr4 atmega88 __AVR_ATmega88__
avr4 at90pwm2 __AVR_AT90PWM2__
avr4 at90pwm3 __AVR_AT90PWM3__
avr5 at90can32 __AVR_AT90CAN32__
avr5 at90can64 __AVR_AT90CAN64__
avr5 at90can128 __AVR_AT90CAN128__
avr5 at90usb646 __AVR_AT90USB646__
avr5 at90usb647 __AVR_AT90USB647__
avr5 at90usb1286 __AVR_AT90USB1286__
72
avr5 at90usb1287 __AVR_AT90USB1287__
avr5 atmega128 __AVR_ATmega128__
avr5 atmega1280 __AVR_ATmega1280__
avr5 atmega1281 __AVR_ATmega1281__
avr5 atmega16 __AVR_ATmega16__
avr5 atmega161 __AVR_ATmega161__
avr5 atmega162 __AVR_ATmega162__
avr5 atmega163 __AVR_ATmega163__
avr5 atmega164p __AVR_ATmega164P__
avr5 atmega165 __AVR_ATmega165__
avr5 atmega168 __AVR_ATmega168__
avr5 atmega169 __AVR_ATmega169__
avr5 atmega32 __AVR_ATmega32__
avr5 atmega323 __AVR_ATmega323__
avr5 atmega324p __AVR_ATmega324P__
avr5 atmega325 __AVR_ATmega325__
avr5 atmega3250 __AVR_ATmega3250__
avr5 atmega329 __AVR_ATmega329__
avr5 atmega3290 __AVR_ATmega3290__
avr5 atmega406 __AVR_ATmega406__
avr5 atmega64 __AVR_ATmega64__
avr5 atmega640 __AVR_ATmega640__
avr5 atmega644 __AVR_ATmega644__
avr5 atmega644p __AVR_ATmega644P__
avr5 atmega645 __AVR_ATmega645__
avr5 atmega6450 __AVR_ATmega6450__
avr5 atmega649 __AVR_ATmega649__
avr5 atmega6490 __AVR_ATmega6490__
avr5 at94k __AVR_AT94K__
-mint8
Domyślnie typ int jest 16 bitowy. Opcja ta przestawia typ int na typ 8 bitowy. Z uwagi, że
nie jest on normalnie obsługiwany przez bibliotekę avr-libc, nie powinno się używać tej
opcji bez wyraźnej potrzeby.
-mno-interrupts}
Generuje kod, który zmienia wskaźnik stosu bez wyłączania przerwań. Normalnie, stan
rejestru SREG jest przechowywany w rejestrze tymczasowym, obsługa przerwań jest
zablokowana podczas zmiany wskaźnika stosu i jest odtwarzany stan rejestru SREG.
-mcall-prologues
Używa wywoływania podprogramów dla prologów i epilogów funkcji. Powoduje
oszczędzanie miejsca w pamięci programu, nieznacznie tylko zwiększając czas wykonania
funkcji.
-minit-stack=nnnn
Ustala wskaźnik stosu na nnnn. Domyślnie wskaźnik stosu jest symbolem __stack, który
jest utalony na RAMEND poprzez kod inicjujący program.
-mtiny-stack
Powoduje zmianę tylko mniej znaczących 8 bitów wskaźnika stosu.
-mno-tablejump
Nie generuje tablicy skoków. Domyślnie, tablica skoków jest używana do optymalizacji
instrukcji switch. Kiedy jest wyłączona, w to miejsce są wstawiane sekwencje
73
porównujące. Tablice skoków zwykle powodują szybsze wykonywanie programu, lecz w
pewnych przypadkach konstrukcja switch zawierająca wiele skoków do jednej np.
domyślnej etykiety może spowodować większe użycie pamięci programu.
-mshort-calls
Wymusza używanie instrukcji rjmp/rcall (ograniczony zakres adresowania) w
mikrokontrolerach z pamięcią flash o pojemności większej niż 8kB. W architekturach avr2
i avr4 (mniej niż 8 kB pamięci flash), jest zawsze w użyciu. Natomias architektury avr3 i
avr5, wywołania funcji i skoki to miejsc poza bieżącą funkcją powinny używać instrukcji
jmp/call które umożliwiają wykonywanie skoków w całym zakresie pamięci, ale zajmują
więcej miejsca w pamięci flash i dłużej się wykonują.
-mrtl
Wypisuje wewnętrzne wyniki kompilacji zwane "RTL" jako komentarze w generowany
kod asemblera. Przydatne podczas debugowania avr-gcc.
-msize
Wypisuje adres, rozmiar i inne dotyczące wyrażenia jako komentarze w generowany kod
asemblera. Przydatne podczas debugowania avr-gcc.
-mdeb
Wypisuje wiele informacji przydatnych do debugowania na wyjście strumienia błędu
(stderr).
-On
Poziom optymalizacji n. Zwiększanie n zwiększa poziom optymalizacji. Poziom
optymalizacji równy 0 jest równoznaczny z jej brakiem.
-Wa,assembler-options, -Wl,linker-options
Przenosi opcje do asemblera lub linkera.
-g
Generuje informacje dla debugera avr-gdb.
-c
Powoduje zatrzymanie po etapie asemblacji, wyniki są umieszczane w pliku z
rozszerzeniem .o.
-E
Powoduje zatrzymanie po etapie prekompilacji, wyniki są wypisywane na ekran.
-o nazwa
Powoduje zmianę nazwy programu wynikowego na podaną przez użytkownika; np.: avr-
gcc -o prog main.c, powoduje nadanie nazwy prog zamiast standardowej a.out.
-S
Powoduje zatrzymanie po etapie generowania kodu asemblera, wyniki są umieszczane w
pliku z rozszerzeniem .s. Opcją -o możemy podać inną nazwę (rozszerzenie).
-H
74
Wypisz nazwę każdego używanego pliku nagłówkowego.
-ansi
Tekst źródłowy musi być w pełni zgodny z normą ANSI języka C.
-traditional
Toleruje starsze konstrukcje języka C, z tzw. wersji języka K&R (opis znajdujesię w
książce autorów języka B.W.Kernighan'a i D.M.Ritchie'go)
-v
Wypisywanie (na standardowym wyjściu dla błędów), komend wywoływanych podczas
kolejnych etapów kompilacji. Wypisuje również numer wersji programu sterującego
kompilatorem, preprocesora oraz właściwego kompilatora.
-fsyntax-only
Sprawdź kod pod kątem błędów składniowych i nie rób nic poza tym.
-include file
Najpierw przetwarza plik file (np. kompiluj najpierw file).
-imacros file
Najpierw przetwarzaj plik file, wypisz wyniki na wyjście, zanim zaczniesz przetwarzać
resztę plików.
-idirafter dir
Dodaj katalog dir jako drugi katalog do przeszukiwania dla plików nagłówkowych. Jeśli
plik nagłówkowy nie został znaleziony w żadnym ze wskazanych wcześniej katalogów,
kompilator przeszukuje ten katalog.
-nostdinc
Nie szukaj w katalogu ze standardowymi plikami nagłówkowymi, szukaj tylko w
katalogach wskazanych przez `-I' i w katalogu bieżącym.
-I dir
Dodaje katalog dir do listy katalogów przeszukiwanych ze wzgledu na pliki nagłówkowe.
-L dir
Dodaje katalog dir do listy katalogów przeszukiwanych przy użyciu przełącznika `-l'.
-Dmacro
Użycie opcji jest równoznaczne z umieszczeniem linii #define makro na początku pliku
zawierającego tekst źródłowy.
-Dmacro=defn
Zdefiniuj makro macro jako defn.
-Umacro
Użycie opcji jest równoznaczne z umieszczeniem linii #undef makro na początku pliku
zawierającego tekst źródłowy.
75
-Wall
Wypisuje ostrzeżenia dla wszystkich sytuacji, które pretendują do konstrukcji, których
używania się nie poleca i których użycie jest proste do uniknięcia, nawet w połączeniu z
makrami.
-funsigned-char
Ustala domyślny typ zmiennych typu char na unsigned.
-funsigned-bitfields
Ustala domyślny typ pól bitowych, gdy brak deklaracji signed albo unsigned. Użycie opcji
-traditional włącza także -funsigned-bitfields, w przeciwnym razie domyślnie jest -fsigned-
bitfields tak jak typ int.
-Wstrict-prototypes
Ostrzega, jeśli jakaś funkcja jest zadeklarowana lub zdefiniowana bez określenia typów
argumentów.
-fpack-struct
"Pakuje" struktury powodując utworzenie mniejszego kodu.
76
ctype.h Funkcje testujące wartości typów znakowych
errno.h Obsługa błędów
inttypes.h Definicje różnych typów całkowitych
math.h Różne funkcje matematyczne
setjmp.h Zawiera funkcje długich skoków (long jumps)
stdio.h Standardowa biblioteka wejścia/wyjscia
stdlib.h Rozmaite funkcje standardowe
string.h Funkcje operujące na łańcuchach
avr/crc16.h
avr/delay.h
avr/eeprom.h
int eeprom_is_ready()
77
Zwraca wartość różną od 0 jeżeli EEPROM jest gotowy na następną operację (bit EEWE
w rejestrze EECR jest równy 0).
avr/ina90.h
Ten plik nagłówkowy zawiera kilka funkcji i makr do łatwiejszego przenoszenia aplikacji
z kompilatora IAR C do avr-gcc. Jednak nie powinno się go używać do pisania aplikacji
„od początku”.
avr/interrupt.h
sei()
Włącza przerwania. Makro.
cli()
Wyłącza przerwania. Makro.
78
avr/io.h
avr/io[MCU].h
Definicje rejestrów I/O dla odpowiedniego typu mikrokontrolera, gdzie [MCU] jest
tekstem określającym typ w rodzaju 2313, 8515 itp. Zobacz do dokumentacji
mikrokontrolera. Tych plików nie należy włączać do pisanych programów – robi to za nas
avr/io.h na podstawie parametru -mmcu=typ_MCU przekazanego do kompilatora np. w
pliku makefile.
avr/parity.h
parity_even_bit(val)
avr/pgmspace.h
79
Kopiuje n znaków z jednego ciągu do drugiego. Jako wynik zwraca wskaźnik do dest. Jest
odpowiednikiem funkcji memcpy() z tą różnicą, że łańcuch src znajduje się w pamięci
programu.
80
char* strncpy_P(char* dest, PGM_P src, size_t n)
Kopiuje nie więcej niż n bajtów z src do dest. Jako wynik zwraca wskaźnik do dest. Jest
odpowiednikiem funkcji strncpy() z tą różnicą, że łańcuch src znajduje się w pamięci
programu.
avr/sfr_defs.h
_BV(x)
Zwraca wartość bitu (bit value) x. Zdefiniowany jako (1 << x). Makro.
inb(sfr)
Czyta bajt z sfr. Makro.
outb(sfr, val)
Wpisuje val do sfr. Makro. Odwrotnie jak inb(sfr).
cbi(sfr, bit)
Kasuje bit w sfr. Makro.
sbi(sfr, bit)
Ustawia bit w sfr. Makro.
bit_is_set(sfr, bit)
Zwraca wartość różną od 0, jeżeli bit w sfr jest ustawiony, w przeciwnym wypadku 0.
Makro.
bit_is_clear(sfr, bit)
Zwraca wartość różną od 0, jeżeli bit w sfr jest skasowany, w przeciwnym wypadku 0.
Makro.
loop_until_bit_ist_set(sfr, bit)
Wstrzymuje działanie programu (wykonuje pętlę) dopóki bit w sfr jest ustawiony. Makro.
loop_until_bit_is_clear(sfr, bit)
Wstrzymuje działanie programu (wykonuje pętlę) dopóki bit w sfr jest skasowany. Makro.
avr/signal.h
Definiuje nazwy uchwytów dla przerwań, które znajdują się na początku pamięci FLASH.
Oto one:
SIG_INTERRUPT0 do SIG_INTERRUPT7
81
Uchwyty funkcji obsługi przerwań zewnętrznych od 0 do 7. Przerwania o numerach
większych od 1 są dostępne tylko w niektórych układach ATmega.
SIG_OUTPUT_COMPARE2
Uchwyt funkcji obsługi przerwania od porównania licznika 2.
SIG_OVERFLOW2
Uchwyt funkcji obsługi przerwania do przepełnienia licznika 2.
SIG_INPUT_CAPTURE1
Uchwyt funkcji obsługi przerwania od przechwytywania licznika 1.
SIG_OUTPUT_COMPARE1A
Uchwyt funkcji obsługi przerwania od porównania licznika 1 (A).
SIG_OUTPUT_COMPARE1B
Uchwyt funkcji obsługi przerwania od porównania licznika 1 (B).
SIG_OVERFLOW1
Uchwyt funkcji obsługi przerwania do przepełnienia licznika 1.
SIG_OUTPUT_COMPARE0
Uchwyt funkcji obsługi przerwania od porównania licznika 0.
SIG_OVERFLOW0
Uchwyt funkcji obsługi przerwania do przepełnienia licznika 0.
SIG_SPI
Uchwyt funkcji obsługi przerwania SPI.
SIG_UART_RECV
Uchwyt funkcji obsługi przerwania UART(0) – odbiór znaku.
SIG_UART1_RECV
Uchwyt funkcji obsługi przerwania UART1 – odbiór znaku. UART1 jest dostępny w
niektórych układach ATmega.
SIG_UART_DATA
Uchwyt funkcji obsługi przerwania UART(0) – pusty rejestr danych.
SIG_UART1_DATA
Uchwyt funkcji obsługi przerwania UART1 – pusty rejestr danych. UART1 jest dostępny
tylko w niektórych układach ATmega.
SIG_UART_TRANS
Uchwyt funkcji obsługi przerwania UART(0) – zakończenie transmisji.
SIG_UART1_TRANS
Uchwyt funkcji obsługi przerwania UART1 – zakończenie transmisji. UART1 jest
dostępny tylko w niektórych układach ATmega.
82
SIG_ADC
Uchwyt funkcji obsługi przerwania ADC – zakończenie przetwarzania.
SIG_EEPROM
Uchwyt funkcji obsługi przerwania EEPROM – gotowość.
SIG_COMPARATOR
Uchwyt funkcji obsługi przerwania z komparatora analogowego.
SIGNAL(signame)
Używany do definicji uchwytu sygnału dla signame.
INTERRUPT(signame)
Używany do definicji uchwytu przerwania dla signame.
Dla uchwytu zdefiniowanego w SIGNAL(), dodatkowe przerwania są bezwarunkowo
zabronione, natomiast w uchwycie INTERRUPT(), pierwszą (bezwarunkowo) instrukcją
jest sei, i występujące w tym czasie przerwania mogą być obsługiwane.
avr/sleep.h
#define SLEEP_MODE_ADC
Redukcja zakłóceń z przetwornika analogowo/cyfrowego.
#define SLEEP_MODE_EXT_STANDBY
Rozszerzony tryb gotowości (Extended Standby).
#define SLEEP_MODE_IDLE
Tryb bezczynny (Idle).
#define SLEEP_MODE_PWR_DOWN
Wyłączenie zasilania (Power Down).
#define SLEEP_MODE_PWR_SAVE
Oszczędzanie zasilania (Power Save).
#define SLEEP_MODE_STANDBY
Tryb gotowości (Standby).
void sleep_mode(void)
Wprowadza kontroler w tryb uśpienia na podstawie wcześniej wybranego trybu za pomocą
funkcji set_sleep_mode().
83
Aby uzyskać więcej informacji, zobacz do dokumentacji mikrokontrolera.
avr/timer.h
void timer0_stop()
Zatrzymuje Timer 0 poprzez wyzerowanie rejestru TCNT0.
void timer0_start()
Startuje Timer 0 poprzez wpisanie 1 w rejestr TCNT0.
avr/twi.h
avr/wdt.h
wdt_reset()
Powoduje kasowanie czasomierza układu Watchdog.
wdt_enable(timeout)
Ustawia odpowiedni timeout i uruchamia układ watchdoga. Zobacz do dokumentacji
Atmel AVR. Wartość timeout może przyjmować jedną z predefiniowanych wartości:
WDTO_15MS
WDTO_30MS
WDTO_60MS
84
WDTO_250MS
WDTO_500MS
WDTO_1S
WDTO_2S
wdt_disable()
Wyłącza układ watchdoga.
ctype.h
85
int toascii(int __c);
Zamienia __c na 7 bitowy znak ASCII.
errno.h
Obsługa błędów.
int errno;
Przechowuje systemowy kod błędu
inttypes.h
86
math.h
M_PI = 3.141592653589793238462643
Liczba PI.
M_SQRT2 = 1.4142135623730950488016887
Pierw. kwadr. z 2
double cos(double x)
Zwraca cosinus z x.
double fabs(double x)
Zwraca absolutną wartość z x.
double sin(double x)
Zwraca sinus z x.
double sqrt(double x)
Zwraca pierwiastek kwadratowy z x.
double tan(double x)
Zwraca tangens z x.
double floor(double x)
Zwraca większą wartość całkowitą mniejszą niż x.
double ceil(double x)
Zwraca mniejszą wartość całkowitą większą niż x.
double exp(double x)
Zwraca e^x.
double cosh(double x)
Zwraca cosinus hiperboliczny z x.
double sinh(double x)
Zwraca sinus hiperboliczny z x.
87
double tanh(double x)
Zwraca tangens hiperboliczny z x.
double acos(double x)
Zwraca arcus cosinus z x.
double asin(double x)
Zwraca arcus sinus z x.
double atan(double x)
Zwraca arcus tangens z x. Wyjście między -PI/2 i PI/2 (włącznie).
double log(double x)
Zwraca logarytm naturalny z x.
double log10(double x)
Zwraca logarytm dziesiętny z x.
double square(double x)
Zwraca x2.
double inverse(double x)
Zwraca 1/x.
UWAGA. Aby skorzystać z tych funkcji należy włączyć do projektu bibliotekę libm.a.
setjmp.h
88
stdlib.h
typedef struct {
int quot;
int rem;
} div_t;
typedef struct {
long quot;
long rem;
} ldiv_t;
typedef int (*__compar_fn_t)(const void *, const void *);
Używane w funkcjach porównujących np. qsort().
void abort();
Skutecznie przerywa wykonywanie programu przez wprowadzenie MCU w nieskończoną
pętlę.
89
Zwalnia pamięć wskazywaną przez ptr, która była wcześniej zaalokowana funkcją
malloc().
string.h
90
znaków s1 jest mniejsze od s2. Zero jeśli są równe. Większą od zera jeśli s1 jest większe
od s2.
strdupa( s );
Duplikuje s, zwracając identyczny łańcuch. Makro.
strndupa( s, n );
Zwraca zaalokowaną kopię n batów z s. Makro.
91
wyboru dwa środowiska: "Cygwin" i "MinGW". "Cygwin" jest środowiskiem
kompatybilnym z systemami unixowymi (standard Posix) natomiast "MinGW" jest tzw.
"minimalistyczną" wersją programów GNU dostosowaną specjalnie do systemu MS
Windows (nazwa MinGW pochodzi od: Minimalist GNU for Windows).
Opis instalacji i używania powyższych i innych środowisk zostanie pominięty - polecam
skorzystanie z bogatej bazy wiedzy znajdującej się w internecie.
Pakiet binutils
cd binutils*
opcja --prefix oznacza ścieżkę, w której będą instalowane programy jeśli jej nie podamy
zostanie użyta domyślna (zazwyczaj będzie to /usr/local), opcja --disable-nls oznacza
wyłączenie wsparcia dla innych wersji językowych. Po wykonaniu powyższej instrukcji
zostanie utworzony plik make file i będzie można przeprowadzić właściwą kompilację
poprzez wydanie polecenia:
make
Gdy kompilacja zostanie zakończona należy pakiet zainstalować czyli umieścić pliki
wynikowe w miejscu podanym przez opcję -prefix skryptu configure. Wydajemy
polecenie:
Pakiet gcc-core
92
configure --target=avr --prefix=/avrgcc --disable-nls --enable-
languages=c
make
Gdy kompilacja zostanie zakończona należy pakiet zainstalować czyli umieścić pliki
wynikowe
w miejscu podanym przez opcję -prefix skryptu configure. Wydajemy polecenie:
make install
Pakiet avr-libc
doconf --prefix=/avrgcc
domake
Gdy kompilacja zostanie zakończona należy pakiet zainstalować czyli umieścić pliki
wynikowe
w miejscu podanym przez opcję -prefix skryptu doconf. Wydajemy polecenie:
domake install
93