K U R S
AVR GCC: kompilator C
mikrokontrolerów AVR, część 4
Kontynuujemy cykl artykułów, których zadaniem jest przedstawienie podstaw
oraz praktycznych zasad programowania mikrokontrolerów AVR w języku C
z użyciem kompilatora avr-gcc. Oczywiście wybór kompilatora AVR-GCC może
się jednym podobać, a innym nie. Postaramy się jednak uzasadnić, że nie
jest to zły wybór.
#if WARUNEK1
Blok kodu1
Dyrektywy kompilacji nek może być stałą liczbową, stałą
#elif WARUNEK2
warunkowej znakową albo dowolnym spełniają- Blok kodu 2
#else
Na chwilę przerwiemy omawianie cym reguły C wyrażeniem (operacją
Blok kodu 3
#endif
typów zmiennych i obejrzymy do- arytmetyczną, logiczną, bitową). If
kładniej zastosowane polecenie kom- jest realizowane gdy WARUNEK !=0. W naszym przykładzie makro FLO-
pilacji warunkowej #if #else #endif. W wyrażeniach możemy także spraw- AT zdefiniowaliśmy za pomocą dy-
Dyrektywy kompilacji warunkowej dzać zdefiniowanie makra służy do rektywy #define FLOAT na początku
nie wchodzą w skład wynikowego tego oddzielny operator defined. Za- pliku zródłowego. W przypadku ko-
kodu programu. Są analizowane na pis #ifdef MACRO będzie więc rów- nieczności zastosowania makra w wie-
samym początku przez preprocesor, noważny z #if defined(MACRO). lu plikach wygodniej będzie umieścić
Dyrektywa może być pojedyncza:
który zgodnie z nimi modyfikuje kod definicję w jakimś wspólnym pliku
#ifdef MACRO
zródłowy poddawany następnie kom- Blok kodu.
nagłówkowym. Jeszcze inną możliwo-
#endif
pilacji (sięgnijmy do wcześniejszego albo złożona: ścią jest dopisanie definicji do opcji
#ifdef MACRO
ogólnego opisu działania AVR GCC).
Blok kodu 1 (blok uwzględniany, gdy MA- wywołania kompilatora. W okienku
CRO zdefiniowane)
Powyższy zapis powoduje wstawienie edycyjnym dodatkowych opcji Avr-
#else
Blok kodu 2 (blok uwzględniany, gdy MA-
do kodu tylko bloku zgodnego z na- Side (zakładka Kompilator) wpiszmy
CRO nie zdefiniowane)
#endif
rzuconym warunkiem (zdefiniowane D FLOAT i sprawdzmy, że działanie
makro) i pomija blok niezgodny. Pomi- Nieco bardziej kłopotliwe jest będzie takie samo (rys. 11). Po zmia-
mo dosyć podobnej składni dyrektywa sprawdzenie kilku oddzielnych wa- nie samych opcji (bez zmiany treści
nie ma więc nic wspólnego z progra- runków. Wymaga to zagnieżdżenia kodu) dla ponownego przekompilo-
mową instrukcją warunkową if() else; kolejnych instrukcji if: wania użyjmy polecenia Build (CTR-
#if WARUNEK1
wykonywaną dopiero w trakcie działa- L+SHIFT+F9) gdyż polecenie Make
Blok kodu 1 (WARUNEK1 spełniony)
#else // (WARUNEK1 niespełniony)
nia programu. (F9) pominie kompilację nie zmienio-
#if WARUNEK2
Blok kodu 2 (WARUNEK1 niespełniony, WA-
Dyrektywy warunkowe mogą mieć nego pliku zródłowego.
RUNEK2 spełniony)
#else
kilka odmian. Warunek może być Jednym z typowych zastosowań
Blok kodu 3 (WARUNEK1 niespełniony
sformułowany jak powyżej: #ifdef i WARUNEK2 nie spełniony) może być wstawienie do kodu frag-
#endif // zakończenie obsługi WARUNEK2
MACRO wtedy sprawdza po prostu mentów używanych tylko przy uru-
#endif // zakończenie obsługi WARUNEK1
czy MACRO zostało wcześniej zdefi- Dla ułatwienia wprowadzono do- chamianiu i testowaniu aplikacji. Po-
niowane (lub odwrotnie gdy użyjemy datkowy operator elif z jego po- wszechne jest też stosowanie w pli-
#ifndef MACRO). Bardziej uniwersalną mocą zapiszemy to samo znacznie kach nagłówkowych warunku:
#ifndef PLIK
formą jest #if WARUNEK gdzie waru- prościej:
#define PLIK
Treść pliku nagłówkowego.
#endif
Zabezpiecza to przed omyłkowym
wielokrotnym wstawieniem treści pli-
ku do kodu dyrektywami #include,
po pierwszym wstawieniu makro
PLIK jest już zdefiniowane i każda
następna próba zostaje zablokowana.
Checkboxy DEBUG i HAPSIM po-
wodują zdefiniowanie odpowiednio
makr DEBUG i HAPSIM, które są po-
mocne w kodzie przy uruchamianiu
i symulacji z użyciem Hapsima.
Zmienne logiczne, flagi bitowe,
obsługa linii wejść/wyjść
Zmienne logiczne to zmienne
przyjmujące tylko dwie wartości:
Rys. 11. Definiowanie makra w opcjach wywołania kompilatora prawda i fałsz. Są wygodne pod-
Elektronika Praktyczna 6/2005
103
K U R S
ską zanegowaną (z wartością wszyst-
kich bitów zmienioną na przeciw-
ną: ~0000 1000 = 1111 0111):
1100 1010 & ~0000 1000 = 1100 1010 &
1111 0111 = 1100 0010
Jeśli nie potrzebujemy konkret-
nego stanu bitu, a chcemy tylko
Rys. 12. Podpowiedz deklaracji makra w AvrSide zmienić jego wartość na przeciw-
ną posłużymy się maską w operacji
czas rozpatrywania rozmaitych roz- z listy poprzez zaznaczenie pozycji ex or:
1100 0010 ex or 0000 1000 = 1100 1010
gałęzień warunkowych. W języku i klawisz DEL, całą listę zerujemy
1100 1010 ex or 0000 1000 = 1100 0010
C wydzielenie tych zmiennych ma natomiast skrótem CTRL+DEL). Pa-
charakter raczej umowny i służący miętajmy tylko, że długość bufo- Maski potrzebne w powyższych
przejrzystości kodu. Każda, bowiem ra listy jest ograniczona po jego działaniach uzyskujemy przesuwając
wartość niezerowa jest traktowa- wypełnieniu dalsze wpisy nie będą w lewo liczbę jeden (czyli z usta-
na jako logiczna prawda , a zero dokonywane. wionym bitem nr 0) o ilość miejsc
jest równoważne z logicznym fał- Zmienne typu bool są dosyć roz- zgodną z lokalizacją bitu poddawa-
szem . Np. w nieskończonej pętli rzutne zajmują cały bajt tylko po nego działaniu:
0000 0001 << 1 = 0000 0010
naszego pierwszego testu użyliśmy to, aby określić stan jednego bitu
0000 0001 << 5 = 0010 0000
po prostu warunku while (1) je- (gdyż true i false to odpowiednio po
dynka jest zawsze prawdą i pętla prostu 1 i 0). Dlaczego więc nie uży- i ewentualnie poddając je negacji
będzie zawsze wykonana. Właśnie wać tak popularnych w rodzinie 51 (użyta w powyższych przykładach
dla przejrzystości zdefiniowany zo- flag (zmiennych jednobitowych), któ- maska będzie więc wyrażona jako
stał dodatkowy typ bool oraz jego re maksymalnie oszczędzają pamięć? 1<<3).
wartości true oraz false. Aby je Na przeszkodzie stoją następujące Biblioteka avr libc przewidziała
wykorzystać musimy dołączyć sys- względy: dla tworzenia masek pomocnicze
temowy plik nagłówkowy stdbool. h AVR nie ma przeznaczonego do makro _BV(numer_bitu) jest ono
(#include
). Wtedy mo- ogólnego stosowania obszaru pamię- całkowicie równoważne z zapisem
żemy używać bardzo czytelnego ci adresowanej bitowo (nie można (1<i jednoznacznego zapisu (np. bool tu uwzględniać rejestrów I/O, które wane zamiennie według własnych
mybool=true;). Typ bool nie jest mają całkiem inne przeznaczenie, po preferencji (użycie _BV wymaga
niestety rozumiany przez AvrStudio, trzy takie rejestry ogólnego przezna- dołączenia na początku kodu syste-
z czego można jednak łatwo wy- czenia mają tylko niektóre najnowsze mowego pliku io.h, #include brnąć definiując własny typ zgodny Atmegi), io.h>). Systemowe pliki nagłówkowe
z 1 bajtowym char (i pozostawiając AVR GCC niestety nie obsługuje opisu poszczególnych mikrokontrole-
nazewnictwo true false ze stdbo- dość popularnej w innych kompilato- rów zawierają także nazwy poszcze-
ol.h). Użyjemy do tego bardzo po- rach wygodnej składni dostępu do gólnych bitów w rejestrach SFR, co
żytecznego operatora typedef. Skład- poszczególnych bitów (np. zmienna. pozwala podczas konfiguracji SFR
nia jest bardzo prosta: X , PORTA.2 itp.) wg obecnych na użycie symboli zgodnych z do-
typedef określenie_typu własna_
informacji nie zanosi się tutaj na kumentacją Atmela zamiast niewiele
nazwa_nowego_typu;
szybką zmianę. mówiących numerów bitu.
Zapiszmy więc w naszym kodzie Jednak wbrew tym ogranicze- Zróbmy kilka testowych działań
np.: niom wcale nie musimy z flag bi- bitowych na zmiennej long z uży-
typedef char boolean;
towych rezygnować. Wymaga to ciem różnych masek (w oknie pod-
W ten sposób określiliśmy wła- tylko zastosowania nieco innych glądu AvrStudio ustawmy format na
sny nowy typ boolean całkowicie (ale standardowych dla C) metod. hex żeby łatwo ocenić wynik), np.:
volatile unsigned long flagi;
zgodny z char ale wyróżniający się Działania na pojedynczych bitach
w kodzie oddzielną nazwą którego będziemy wykonywać za pomocą (Ponownie zwróćmy uwagę na
teraz możemy używać do definio- operatorów bitowych (iloczyn and deklarację volatile, która nie pozwa-
wania zmiennych logicznych, np.: &, suma or | i suma wyłączna la optymalizatorowi na usunięcie
boolean mybool = true;
ex or ^ nie pomylmy jej z po- z kodu operacji pośrednich, nie ma-
AvrStudio poprawnie identyfi- pularnym zapisem potęgowania!) jących wpływu na końcową wartość
kuje typy zdefiniowane za pomocą oraz tzw. masek bitowych. Maska zmiennej).
flagi |= _BV(5);
typedef. Natomiast AvrSide pozwala to po prostu liczba całkowita o po-
flagi |= _BV(12);
na dołączenie nowej nazwy do li- trzebnym rozmiarze (char, int, long)
sty słów kluczowych, a tym samym z ustawionymi (1) tylko określonymi Oczywiście dla czytelności mo-
jej odpowiednie kolorowanie w ko- bitami. Dla zapalenia jednego wy- żemy w każdej chwili zdefiniować
dzie (po zaznaczeniu nazwy np. branego bitu w zmiennej sumujemy własną nazwę dla konkretnego bitu
dwukrotnym kliknięciem na niej ją bitowo z maską o zgodnym roz- i używać jej zamiast liczby:
#define Flaga1 7
używamy skrótu CTRL+K). Li- miarze, w której tylko ten bit jest
flagi |= _BV(Flaga1);
flagi &=~ _BV(Flaga1);
sta dodatkowych słów kluczowych ustawiony, np. dla bitu nr 3:
1100 0010 or 0000 1000 = 1100 1010
jest dostępna w dialogu Ustawienia Zwróćmy przy okazji uwagę na
na zakładce AvrSide (w tym miej- Dla zgaszenia tego samego bitu kilka pułapek:
scu można zbędne słowa usuwać używamy iloczynu zmiennej z ma- BV zwraca domyślnie typ si-
Elektronika Praktyczna 6/2005
104
K U R S
wycofane z avr libc bit_is_set(sfr, bit) sprawdza
(rozdział Deprecated ustawienie (1) bitu nr bit w reje-
List w podręcznku) strze sfr
w zamian pojawiła się bit_is_clear(sfr, bit) to samo
niedostępna wcześniej tylko dla bitu zgaszonego (0)
możliwość użycia SFR loop_until_bit_is_set(sfr, bit) pę-
bezpośrednio w instruk- tla oczekiwania na ustawienie (1)
cjach przypisania. Nie bitu nr bit w rejestrze sfr
będziemy więc pisać loop_until_bit_is_clear(sfr, bit)
np. outp (0x55, DDRB); to samo tylko oczekiwanie na zga-
ale po prostu DDR- szenie (0) bitu.
Rys. 13. Automatyczna lista pól unii / struktury B=0x55; (należy za- Pętle oczekujące należy stosować
uważyć, że ceną tego rozważnie, jeśli wprowadzimy
gned int, przesunięcie o 15 pozycji postępu mogą być czasem nieste- warunek, który nigdy nie zaistnieje,
(ustawiony najstarszy bit) powoduje ty kłopoty ze starymi projektami). zatrzymamy cały program (ewen-
potraktowanie wyniku jako liczby Zwróćmy uwagę, że kompilator sa- tualnie zresetujemy mikrokontroler
ujemnej rozwiązaniem jest rzuto- modzielnie wykonuje rozróżnienie o ile jest włączony watchdog).
wanie typu na wymagany: flagi |= pomiędzy rejestrami IO a rejestrami Przy okazji należy podkreślić, żeby
(unsigned int) _BV(15 ; rozszerzonymi i stosuje odpowiednie nie mylić makra z funkcją, chociaż są
Przy przesunięciach większych instrukcje. Np. jeśli zmienimy typ często bardzo podobne w składni.
niż 15 otrzymujemy ostrzeżenie procesora na Atmega 128 (domyśl- Funkcja jest wywoływana dy-
o przekroczeniu rozmiaru typu. na w AvrSide Atmega 8 nie używa namicznie w trakcie działania pro-
Dzieje się tak dlatego, że jedynka rozszerzonych SFR) i wypróbujemy gramu z chwilowymi, nieznanymi
w wyrażeniu (1 << n) jest domyśl- zapis do portu B (przestrzeń IO) w chwili kompilacji, wartościami
nie traktowana jako int o szeroko- oraz G (adres rozszerzony 0x65) argumentów. Makro jest wykonane
ści 16 bitów. Makro _BV nie da dostaniemy kod: (rozwinięte) jednorazowo w trakcie
PORTB=0x55;
sobie już z tym przypadkiem rady, kompilacji przez preprocesor, a wsta-
252: 25 e5 ldi r18, 0x55 ;
użyjmy jawnego przesunięcia z od- 85
wiane argumenty muszą być z góry
254: 28 bb out 0x18, r18 ;
powiednim rzutowaniem typu: flagi 24 określone w kodzie.
PORTG=0x55;
|= (unsigned long)1<<16; Dyrektywy definiujące oraz makra
256: 20 93 65 00 sts 0x0065, r18
Jest to o tyle mniej istotne, że Dla portu B użyty został skróco- pozwalają bardzo mocno poprawić
_BV() zostało w zasadzie przezna- ny rozkaz out. Dla portu G byłby czytelność kodu programu. Na przy-
czone do obsługiwania 8 bitowych on nieprawidłowy i kompilator stosuje kład podłączmy do linii PB0 i PB1
rejestrów SFR gdzie takie problemy zwykły zapis do pamięci sts. Podob- dwa ledy, zielony i czerwony, zasila-
nie wystąpią jednak dobrze ilu- nie jest przy obsłudze pojedynczych ne z V przez rezystory ograniczające
cc
struje różnego rodzaju niespodzian- linii, np. dla ustawiania i gaszenia: czyli zapalane przy niskim stanie
PORTB |=_BV(PB2);
ki związane z typami i zakresami linii. Zamiast cały czas pamiętać
252: c2 9a sbi 0x18, 2 ; 24
PORTG |=_BV(PG2);
zmiennych. o tej konfiguracji i każdorazowo uży-
254: 80 91 65 00 lds r24, 0x0065
W podobny sposób sprawdzamy 258: 84 60 ori r24, 0x04 ; 4 wać uniwersalnych instrukcji opisa-
25a: 80 93 65 00 sts 0x0065, r24
stan bitu: tworzymy iloczyn zmien- nych powyżej, po prostu zdefiniujmy
nej i odpowiedniej maski i kontrolu- oraz potrzebne operacje odpowiednio je
PORTB &=~_BV(PB2);
jemy czy jest równy zero czy nie. nazywając (nazwy makr zwyczajowo
252: c2 98 cbi 0x18, 2 ;
Na przykład w instrukcji warunko- 24
pisze się dużymi literami):
PORTG &=~_BV(PG2);
#define LED_Z PB0
wej możemy bezpośrednio skorzy- 254: 80 91 65 00 lds r24, 0x0065
#define LED_CZ PB1
258: 8b 7f andi r24, 0xFB
#define ZAPAL_Z (PORTB &=~_BV(LED_Z))
stać z reguły, że każda wartość nie- ; 251
#define ZGAS_Z (PORTB |= _BV(LED_Z))
25a: 80 93 65 00 sts 0x0065, r24
zerowa odpowiada logicznej praw- #define PRZELACZ_Z (PORTB ^= _BV(LED_
Z))
dzie: albo dla sprawdzania stanu li- #define ZAPAL_CZ (PORTB &=~_BV(LED_CZ))
#define ZGAS_CZ (PORTB |= _BV(LED_CZ))
if(flagi & _BV(3)) { coś wykonu- nii:
#define PRZELACZ_CZ (PORTB ^= _BV(LED_
if(PINB & _BV(PB2)) PORTA |= _BV(PA0); CZ))
jemy } wykonanie nastąpi przy
252: b2 99 sbic 0x16, 2 ; 22
254: d8 9a sbi 0x1b, 0 ; 27
ustawionym bicie 3 w zmiennej Wpis w kodzie np. ZGAS_CZ;
if(PING & _BV(PG2)) PORTA |= _BV(PA1);
flagi (przy okazji obejrzyjmy wyge- 256: 80 91 63 00 lds r24, 0x0063
jest jednoznaczny i czytelny, a do-
25a: 82 fd sbrc r24, 2
nerowany kod, przekonamy się, że 25c: d9 9a sbi 0x1b, 1 ; 27 datkowo zmniejsza ryzyko wystą-
dla typu long jest on mocno skom- Powyższe operacje możemy dla pienia błędu przy wielokrotnym
plikowany więc w miarę możliwo- nabrania wprawy prześledzić w Avr- użyciu. Dodatkowym ułatwieniem
ści w praktyce ograniczajmy rozmiar Studio, które w pełni wspiera obsłu- stosowania jest system podpowie-
zmiennych używanych w takim celu; gę oraz podgląd linii we/wy portów dzi AvrSide: klawisz F1 przy karet-
dla flagi typu char kod jest już kró- (potrzebna będzie oczywiście rów- ce ustawionej na nazwie symbolu
ciutki i przejrzysty). nież zmiana procesora albo utwo- wyświetla okienko z jego deklaracją
Identyczne reguły zalecane są rzenie nowej sesji). (rys. 12), natomiast skrót SHIFT
przy obsłudze linii wejść / wyjść W avr libc (\include\avr\sfr_defs.h) + F1 wyszukuje wszystkie miejsca
portów mikrokontrolera (i ogólnie znajdziemy kilka dodatkowych (zre- występowania symbolu w kodzie.
przy dostępie do rejestrów SFR). alizowanych za pomocą opisanych Język C dostarcza jeszcze jeden
Popularne dawniej pomocnicze ma- powyżej operacji bitowych) makr sposób używania flag są to pola
kra sbi, cbi, outp, inp są obecnie obsługi linii: bitowe. Zadeklarowanie flag jest
Elektronika Praktyczna 6/2005
105
K U R S
w tym przypadku niestety bardziej jemy zmienną typu Flags to może- bardzo krótki i efektywny:
MojeFlagi.Bits.Flag3=true;
skomplikowane (szczegółowe infor- my się odwoływać zarówno jednora-
64: 80 91 78 00 lds r24, 0x0078
68: 84 60 ori r24, 0x04 ; 4
macje o stosowanych tu strukturach zowo do całego bajtu poprzez pole
6a: 80 93 78 00 sts 0x0078, r24
oraz uniach znajdziemy w każdym Byte (może być to przydatne przy
podręczniku C): operacjach wspólnych np. szybkim Jak widać pozornie zawikłany me-
Najpierw definiujemy typ struk- wyzerowaniu wszystkich flag) jak chanizm jest ostatecznie bardzo efek-
tury z potrzebną liczbą jednobito- i do poszczególnych flag (bitów) po- tywny i przejrzysty w działaniu jed-
wych flag (np. 8, wtedy flagi zaj- przez pola Bits.FlagX: nocześnie maksymalnie oszczędzając
Flags MojeFlagi;
mują dokładnie jeden bajt: pamięć danych. Ma on jednak jeden
//..................
typedef struct
MojeFlagi.Byte=0; // wyzerowanie istotny w naszym środowisku uru-
{
wszystkich flag
unsigned char Flag1:1;
MojeFlagi.Bits.Flag1 = true;// ustawie- chomieniowym mankament: AvrStu-
unsigned char Flag2:1;
nie pierwszej flagi
unsigned char Flag3:1;
MojeFlagi.Bits.Flag5 = false; // wyze- dio nie potrafi (przynajmniej w chwili
unsigned char Flag4:1;
rowanie 5. flagi
unsigned char Flag5:1; pisania artykułu) obsługiwać pól bito-
unsigned char Flag6:1;
W takich zapisach znów dopo- wych. Jeśli więc zależy nam na pod-
unsigned char Flag7:1;
unsigned char Flag8:1;
może system podpowiedzi AvrSide glądzie zmiennych logicznych w trak-
} FlagBits;
(zamiast przy każdym polu wyświetlający po kropce listę wy- cie debugowania użyjemy ich w wer-
wpisywać oddzielnie typ unsigned boru dostępnych w unii/strukturze sji tradycyjnej. Można też ewentualnie
możemy w opcjach kompilacji za- pól (rys. 13) (dla działania wymaga zanim zespół Atmela wprowadzi
znaczyć pozycję zmienne z polami zapisania pliku po zadeklarowaniu odpowiednie udoskonalenia do Avr-
bitowymi domyślnie bez znaku ). unii lub struktury). Studio oglądać bezpośrednio pole
Następnie definiujemy typ unii, Dodatkowo możemy nazwać każ- Byte unii w zapisie heksadecymalnym,
w której jedną z możliwych zawar- dą flagę w czytelny sposób (#defi- ale nie jest to zbyt wygodne.
tości jest nasza struktura z polami ne MOJA_FLAGA MojeFlagi.Bits.Flag1 Jerzy Szczesiul, EP
bitowymi a drugą (zamienną) zwy- itp.) i używać jej wtedy jak każdej jerzy.szczesiul@ep.com.pl
czajny bajt bez znaku: innej zmiennej logicznej (MOJA_
typedef union
FLAGA = true; if(MOJA_FLAGA) {}). UWAGA!
{
FlagBits Bits; Środowisko IDE dla AVR-GCC opracowane
Jeśli zajrzymy do wygenerowanego
uchar Byte;
przez autora artykułu można pobrać ze
} Flags; kodu, to stwierdzimy, że pomimo
strony http://avrside.ep.com.pl.
Jeśli teraz z programie zadeklaru- skomplikowanych deklaracji jest on
PRENUMERAT ELEKTRONIKI PRAKTYCZNEJ
NAJWYGODNIEJ ZAMAWIAĆ SMS-EM!
Wyślij SMS o treści PREN na numer 0695458111,
my oddzwonimy do Ciebie
i przyjmiemy -aTwoje jzamówienie.
(koszt SMS według Two ej taryfy).
Elektronika Praktyczna 6/2005
106
Wyszukiwarka
Podobne podstrony:
AVR GCC kompilator C dla mikrokontrolerów AVR, część 12
AVR GCC kompilator C dla mikrokontrolerów AVR, część 11
AVR GCC kompilator C dla mikrokontrolerów AVR, część 3
Płytka testowa dla mikrokontrolerów AT89S oraz AVR
więcej podobnych podstron