12 2005 108 110


K U R S
AVR GCC: kompilator C dla
mikrokontrolerów AVR, część 10
Zakres zmiennych, pliki nagłówkowe
Zgłębiając tajniki AVR GCC przechodzimy teraz do omówienia
sposobów deklarowania zmiennych oraz ich dopuszczalnych
zakresach. Jak pokazuje praktyka, zrozumienie tych
zagadnień ma duży wpływ na komfort pracy programisty
i  w konsekwencji  na jakość przygotowanego oprogramowania.
// deklaracja funkcji lokalnej dla mo-
int Myfunc1(char x,char y)
W praktyce zamiast rezygnować
dułu main
{
volatile char a,b;
z zalet optymalizacji lepiej jest kon-
// oraz definicja tej funkcji
char LocFunc(char Value)
a=x + y;
trolować istotne dla nas zmienne
{
b=x  y;
return Value + 2;
return (a*b);
przy pomocy używanego już słowa
}
}
kluczowego volatile. Informuje ono
a w module funkcje.c:
kompilator, żeby tak opisanej zmien- i oglądamy jak traktowane są
// deklaracja zmiennej lokalnej dla mo-
nej nie poddawać jakimkolwiek dzia- zmienne a oraz b przy wywoła- dułu funkcje
static char k=2;
łaniom optymalizującym i upraszcza- niach kolejno Myfunc oraz Myfun-
static char LocFunc(char Value);
jącym i wykonywać na niej wszyst- c1 (dobrze jest w tym celu dodat-
// deklaracja funkcji lokalnej dla mo-
dułu funkcje
kie operacje przewidziane w kodzie kowo włączyć w AvrStudio okienko
// oraz definicja tej funkcji
(chociaż z punktu widzenia optymali- podglądu pamięci danych jak na
char LocFunc(char Value)
zatora mogą one wyglądać na zbęd- rys. 23). Przekonamy się, że war- {
return Value + 10 +k;
ne). Główne zastosowanie tego me- tości chwilowe a i b zmieniają się
}
chanizmu to zabezpieczanie zmien- w zależności od tego, która funkcja Przy kompilacji stwierdzamy, że
nych używanych w przerwaniach (to aktualnie z nich korzysta. w tym przypadku nie występuje błąd
wynika bezpośrednio z nazwy: volati- Może w pierwszej chwili zdzi- wielokrotnej definicji. Wiąże się z tym
le  czyli ulotny, nietrwały  ozna- wić fakt, że w momencie wejścia również ukrycie powyższych lokal-
cza, że wartość zmiennej może być do funkcji Myfunc1 a oraz b zacho- nych nazw w oknie podglądu symboli
w każdej chwili uaktualniona przez wały wartości przypisane wewnątrz konsolidatora (rys. 24), wyszczególnio-
czynnik zewnętrzny  przerwanie  poprzedniej funcji (Myfunc)  prze- ne są tylko symbole globalne (okno
i nie można w związku z tym pomi- cież miały stracić ważność. Przyczy- podglądu symboli wywołujemy klawi-
nąć żadnej związanej z nią operacji ną jest prostota naszego przykładu. szem F8).
w głównej pętli programu), jednak Kompilator nie niszczy zmiennych Oczywiście, pomimo tego ukry-
często jest pomocny także w róż- lokalnych (np. przez wyzerowanie), cia zmienne k są fizycznie uloko-
nych innych sytuacjach. Sprawdzmy ale po prostu przestaje się nimi wane w pamięci SRAM (pod adre-
zaraz, że zmiana deklaracji na vola-  przejmować . Gdyby pomiędzy wy- sami 0x60 oraz 0x63 na rys. 25),
tile char a,b; (przy ponownym włą- wołaniami Myfunc i Myfunc1 pojawi- znajdziemy je też przeglądając plik
czeniu maksymalnej optymalizacji) ły się jakieś operacje wykorzystują- symboli Test03.smb. Użycie poszcze-
daje ten sam efekt: zmienne wędru- ce stos  a i b zostałyby nadpisane. gólnych adresów zależy od modu-
ją z obszaru rejestrów na stos. Jest Ponieważ jednak nic takiego nie łu, z którego się do naszej zmiennej
to pokazane na rys. 23. zachodzi wartości wstawione pod k odwołujemy (kod modułu main.
Zobaczmy jeszcze, że takie same adresy 0x45a i 0x45b pozostały nie c korzysta z adresu 0x63, natomiast
nazwy zmiennych mogą być z po- zmienione. moduł funkcje.c używa 0x60). Jeśli
wodzeniem użyte w innej funkcji Możliwość użycia takich samych zechcemy to prześledzić w AvrStu-
 w tym celu definiujemy sobie do- nazw zmiennych lub funkcji jest dio zauważymy, że po wstawieniu
datkowo: też czasem korzystna w odniesieniu do okienka podglądu zmiennej k
do poszczególnych modułów kodu
zródłowego. W C uzyskujemy to po-
przez ograniczenie zakresu ważności
zmiennej (funkcji) do pojedynczego
modułu  sprawia to słowo kluczo-
we static. Zadeklarujmy sobie takie
lokalne symbole: w module main.c
dopiszemy na przykład:
// deklaracja zmiennej lokalnej dla
modułu main
static char k=1;
Rys. 23. Podgląd zmiennych lokal- // funkcje:
Rys. 24. Tablica symboli pokazuje
nych na stosie static char LocFunc(char Value); tylko symbole globalne
Elektronika Praktyczna 12/2005
108
K U R S
strukcji należy raczej unikać (chyba,
że czytelność kodu postawimy na
absolutnie priorytetowym miejscu).
Wykorzystanie plików
nagłówkowych
Do używanych w projekcie plików
nagłówkowych *.h odwołujemy się
w dwojaki sposób: #include
Rys. 25. Przydział pamięci dla zmiennych lokalnych albo #include  file.h . Różnica leży
tylko w sposobie wyszukiwania przez
będzie ona opisana wartością i adre- kator const informuje kompilator, że kompilator na dysku pliku o podanej
sem zależnym od modułu, do które- jest to szablon tylko do odczytu): nazwie. Przy odwołaniu <> w pierw-
int Myfunc(char x,char y)
go wchodzimy pracą krokową. szej kolejności sprawdzane są własne,
{
const char Cyfry[] = 0123456789 ;
Podobnie jest z funkcjami  każdy systemowe zasoby plików avr gcc
static char a,b;
moduł odwołuje się do swojej wła- (a więc foldery avr\include i lib\gcc\avr\
a=2*x + y;
snej lokalnej definicji LocFunc. Język wersja\include), do których nie musi-
b=x + 2*y;
return (a+b+ Cyfry[1]);
C daje nam jeszcze jedną możliwość my podawać ścieżki. Dołączamy więc
}
łączącą właściwości powyższych przy- Wydawałoby się, że w trakcie takim zapisem wszystkie potrzebne
padków. Jeśli mianowicie użyjemy tworzenia ramki stosu dla funkcji w projekcie pliki avr libc. Konieczne
kwalifikatora static do zmiennej lo- podczas jej wywołania powinna być jest jednak zaznaczenie wejścia do
kalnej deklarowanej wewnątrz cia- powtórzona procedura taka sama jak ewentualnych subfolderów a więc np.
ła funkcji (automatycznej) uzyskamy dla zmiennych inicjalizowanych data #include . Użycie unikso-
następujacy efekt: zakres używania (przepisanie wartości z końca obsza- wego slasha / zamiast windowsowego
zmiennej pozostanie nadal ograni- ru kodu bezpośrednio na stos). Nie- backslasha \ nie jest błędem: kompi-
czony do ciała funkcji ale zarazem stety w tym przypadku avr gcc nie lator interpretuje go poprawnie nato-
zmiennej zostaje przydzielona na stałe postępuje optymalnie. Sprawdzmy to miast znacznie ułatwione jest przenie-
przestrzeń w obszarze danych SRAM. w AvrStudio  rys. 26. sienie projektu do środowiska unikso-
Po wyjściu z funkcji zmienna taka nie Okazuje się, że string Cyfry[] wego (jak Linuks).
jest zatem  jak poprzednio  nara- jest już w trakcie ogólnej inicjalizacji Forma   uruchamia wyszukiwanie
żona na zniszczenie (nadpisanie) ale również przepisywany na stałe do pliku od bieżącego folderu projektu
przechowuje ostatnio przypisaną war- obszaru data SRAM (podobnie jak oraz dodatkowych podanych kom-
tość  aż do ponownego wywołania wszystkie  zwykłe zmienne inicja- pilatorowi lokalizacji. W ten sposób
używającej ją funkcji. Wypróbujmy lizowane), gdzie spokojnie czeka na powołujemy się więc na własne po-
to zaraz przepisując nieco nasze po- wywołanie funkcji. Wtedy dopiero mocnicze pliki nagłówkowe projektu
przednie definicje: spod adresu w sekcji data jest prze- (jak dane.h w naszym przykładzie).
int Myfunc(char x,char y)
pisywany do ramki stosu. Dodatkowe ścieżki przeszukiwania
{
static char a,b;
Zamiast spodziewanych korzy- wprowadzamy przy pomocy opcji
a=2*x + y; ści mamy więc w efekcie wydłuże-  Iścieżka. AvrSide wspiera jedną po-
b=x + 2*y;
nie kodu wykonywalnego i żadnej mocniczą lokalizację, którą wpisuje-
return (a+b);
}
oszczędności RAM w porównaniu my w oknie edycyjnym dialogu Kon-
int Myfunc1(char x,char y)
{
z przypadkiem użycia tego strin- figuracja projektu > Ścieżki. Zazwy-
static char a,b;
ga jako zwykłej zmiennej globalnej czaj będzie to folder [\AvrSide\Myinc]
a=x + y;
b=x  y;
(ewentualnie lokalnej ale dla całego przewidziany na ogólne własne pliki
return (a*b);
} modułu). Widać więc, że takiej kon- nagłówkowe z ulubionymi typami,
Prowadząc krokowy debugging
jak na rys. 23 zobaczymy teraz jak
zmieniła się lokalizacja zmiennych
a i b: mają one przydzielony obszar
w sekcji bss. Opis a oraz b w okien-
ku podglądu zmienia się w trakcie
wchodzenia i opuszczania kolejnych
funkcji. Zauważmy, że biorąc pod
uwagę przydział pamięci zmienne
te nie różnią się obecnie od zwy-
kłych lokalnych czy nawet global-
nych. Natomiast znacznie poprawia
się czytelność kodu oraz jest redu-
kowana możliwość błędów wynika-
jących z powtórzenia nazw.
Zobaczmy jeszcze jak zachowają
się zmienne automatyczne inicjalizo-
wane. Jako przykład niech posłuży
łańcuch (string) z cyframi (kwalifi- Rys. 26. Zmienne lokalne funkcji w wersji inicjalizowanej
Elektronika Praktyczna 12/2005
109
K U R S
definicjami, pomocniczymi makrami domyślnie wewnątrz foldera projektu. stawie znalezionych dyrektyw dołą-
używanymi w wielu projektach. Za- Zaznaczenie opcji powoduje również czenia (#include) wszelkich otwartych
uważmy jednak, że narzucenie kon- przeszukanie plików w dodatkowych w projekcie (obecnych na zakładkach
kretnej pełnej ścieżki dostępu może lokalizacjach  jest to bardziej uni- edytora) plików nagłówkowych *.h.
sprawiać kłopoty przy przenoszeniu wersalne ale może spowalniać pracę Tabela ta jest pózniej sprawdzana
projektu na inną maszynę z inaczej AvrSide w przypadku nagromadzenia przy zapisywaniu zmian w pliku na-
zainstalowanym AvrSide  po prostu dużej liczby plików. główkowym  na tej podstawie są
takiej ścieżki może nie być co spo- Następne ułatwienie w pracy z pli- oznaczane jako zmienione (podkreśle-
woduje błędy. Dla takich przenośnych kami nagłówkowymi dotyczy zależ- nie nazwy pliku na zakładce edytora)
zastosowań przewidziałem dodatkowe ności. Mianem tym określamy w tym i włączane do polecenia Make odpo-
subfoldery foldera projektu [\lib] oraz przypadku ustalenie, w których plikach wiednie moduły zródłowe.
[\inc]. Posługują się one lokalizacją zródłowych *.c oraz *.s jest używa- Dla przyśpieszenia działania śro-
względną nie utrudniającą przeno- ny dany plik nagłówkowy. Zmiana dowiska aktualizacja tabeli zależno-
szenia. Utworzenie tych subfolderów w takim pliku nagłówkowym pociąga ści nie odbywa się samoczynnie;
jest dokonywane samoczynnie przy za sobą oczywiście zmiany w tych po wprowadzeniu w projekcie korekt
zapisaniu projektu z ustawioną opcją zródłach co wymaga ich ponowne- takich jak dodanie lub usunięcie
Konfiguracja projektu>Ścieżki>Używaj go przekompilowania. Jednak bez sa- pliku czy też dopisanie (usunię-
lokalnych podkatalogów lib/inc. Au- moczynnego wsparcia musielibyśmy cie) dołączenia nagłówka w kodzie
tomatycznie jest także przy wywoła- albo zawsze pamiętać, których plików zródłowym, należy użyć polecenia
niu kompilatora dodawana ścieżka do mogą dotyczyć zmiany albo zawsze menu Projekt>Aktualizuj zależności.
subfoldera [\inc]. po korekcie dowolnego nagłówka po- Nie musimy jednak tego każdorazo-
Z lokalizacją własnych plików na- nownie kompilować cały projekt (Bu- wo robić przy ładowaniu projektu
główkowych związana jest jeszcze ild), co przy rozbudowanych progra-  w tym przypadku utworzenie tabe-
jedna opcja AvrSide: Konfiguracja mach może znacznie wydłużać całą li jest automatyczne. Powyższe pole-
projektu>AvrSide>Szukaj deklaracji operację. Obsługa zależności w AvrSi- cenie jest także przydatne dla zre-
w folderach inc. Jest ona połączona de jest dosyć uproszczona: polega na setowania tabeli w razie wystąpienia
z mechanizmem podpowiedzi deklara- przeszukaniu pierwszych kilkunastu błędu, którego niestety nie udało mi
cji symbolu (F1). Przeszukanie plików wierszy kodu każdego zródła .c i .s się dotychczas zlokalizować, a który
w poszukiwaniu deklaracji odbywa się i utworzeniu tabeli zależności na pod- powoduje ciągłe oznaczenie plików
zródłowych jako zmienione pomimo
ich przekompilowania.
Należy też pamiętać, że powyż-
sza obsługa jest jednopoziomowa,
nie wspiera zagnieżdżonych dołą-
czeń (w nagłówku 1.h dołączamy 2.h
a z kolei 1.h jest dołączony do 3.c
 nasza uproszczona obsługa wykryje
zmianę w 1.h ale w 2.h już nie, ko-
nieczna jest komenda Build). Jak wi-
dać nie może się więc ona równać
z rozbudowanymi narzędziami kontroli
zależności używanymi w plikach ma-
kefile. Jednak dość dobrze zdaje eg-
zamin w przeciętnej wielkości amator-
skich projektach, dla których przede
wszystkim AvrSide powstało.
Często zbiór funkcji obsługujących
konkretne zadanie (np. obsługę urzą-
dzenia peryferyjnego) lokujemy w od-
dzielnym module zródłowym xx.c
a dostęp do niego realizujemy poprzez
skojarzony plik nagłówkowy o takiej
samej nazwie xx.h . AvrSide oferuje
dodatkowe skróty klawiszowe (SHIFT
+ F6 albo ALT + UP) do szybkiego
przełączania pomiędzy tak nazwaną
parą plików.
Jerzy Szczesiul, EP
jerzy.szczesiul@ep.com.pl
UWAGA!
Środowisko IDE dla AVR GCC opracowane
przez autora artykułu można pobrać ze
strony http://avrside.ep.com.pl.
Elektronika Praktyczna 12/2005
110


Wyszukiwarka