AVR-GCC_cz13.pdf
(
305 KB
)
Pobierz
ep_03_107-108_avr_gcc_cz13.indd
K U R S
AVR–GCC: kompilator C dla
mikrokontrolerów AVR, część 13
Obsługa obszarów pamięci
mikrokontrolerów AVR, część 1
Pamięci te są umieszczone w od-
dzielnych obszarach adresowych
i wyposażone w oddzielne sprzętowe
mechanizmy dostępu (architektura
typu
Harvard
) obsługiwane różny-
mi instrukcjami maszynowymi –
rys. 30
. W obszarze
RAM
dodatko-
wo wydzielone są sekcje rejestrów
roboczych i rejestrów wejść/wyjść
wyposażone w odrębny sposób ob-
sługi (m.in. występuje adresowanie
bitowe) –
rys. 31
.
Wszystko to jest „chlebem po-
wszednim” dla konstruktorów dobrze
zaznajomionych z budową mikrokon-
trolerów i programowaniem w assem-
blerze. Jednak z punktu widzenia ję-
zyka C sprawy się komplikują. Język
ten pochodzi ze świata dużych ma-
szyn o całkowicie odmiennej organi-
zacji pamięci (jeden obszar o wspól-
nym sposobie adresowania i dostępu
– czyli architektura
von Neumanna
)
i nie posiada żadnych standardo-
wych mechanizmów wspierających
powyższe zróżnicowanie.
Kompilatory C tworzone dla kon-
kretnych rodzin mikrokontrolerów są
od razu wyposażane w specyficzne
rozszerzenia obsługi różnych pamię-
ci. Na przykład w
SDCC
dla rodzi-
ny '51 klasyfikator
data
identyfikuje
podstawową pamięć danych,
idata
oznacza obszar rozszerzonej pamię-
Jedną z wielkich zalet mikrokontrolerów AVR (podobnie
zresztą jak układów wielu innych rodzin) jest integracja
w jednym układzie wszystkich potrzebnych rodzajów
pamięci (SRAM, EEPROM i Flash), co pozwala na znaczne
uproszczenie budowanych urządzeń. Ich obsługa nie zawsze
jest jednoznaczna, co sprawia duże trudności programistom,
zwłaszcza tym, którzy są przyzwyczajeni do korzystania z pełni
możliwości języka C.
ci wewnętrznej adreso-
wanej pośrednio,
xdata
– zewnętrzną pamięć
RAM, zaś
code
ozna-
cza umieszczenie stałej
(wyłącznie do odczytu)
ulokowanej w pamię-
ci programu. Przypisa-
nie zmiennej lub stałej
określonego klasyfikato-
ra pozwala kompilatoro-
wi w momencie jej uży-
cia na generację kodu
odpowiedniego dla za-
deklarowanego obszaru
pamięci (np. zastoso-
wanie instrukcji
MOVX
w przypadku zewnętrz-
nej pamięci '51). Na
ogół jest modyfikowana
też budowa wskaźników
tak, aby mieściły one dodatkową in-
formację o typie pamięci dla aktual-
nie wskazywanego obiektu.
Niestety AVR–GCC nie jest na-
rzędziem od podstaw dedykowanym
rodzinie AVR, ale jedynie jednym
z wielu portów uniwersalnego zesta-
wu kompilatorów GNU GCC. GCC
natomiast – tak samo jak nadmienio-
no powyżej o języku C – jest przy-
stosowany do ciągłego modelu pa-
mięci. Skłonienie go do współpracy
z tak nietypową dla niego platformą
sprzętową wymagało wielu wysiłków
i kompromisów. Nie udało się nieste-
ty do tej pory uzyskać pełnej wy-
gody użytkowania spotykanej w ko-
mercyjnych narzędziach – AVR–GCC
wymaga dodatkowo poznania kilku
specyficznych dla niego technik. Nie
jest to mocno uciążliwe, ale potrafi
zaskoczyć programistów przyzwycza-
jonych do PC lub innych kompilato-
rów C dla AVR.
Rys. 30. Rodzaje pamięci w AVR. Każ-
da z nich jest liniowym obszarem ad-
resowanym od zera. Stałe
RAMEND
,
E2END
i
FLASHEND
są zdefiniowane
w plikach nagłówkowych poszczegól-
nych typów mikrokontrolerów
Obsługa SFR oraz obszaru I/O
Ten temat był już wielokrotnie
omawiany w poprzednich odcinkach.
Obecnie AVR–GCC oferuje pełne
wsparcie dla takich operacji. Nie
są konieczne dawniejsze dodatkowe
makra typu
inp
czy
outp
– stosu-
jemy zwyczajne instrukcje przypisa-
nia. Przy tym optymalizator potrafi
samodzielnie określić możliwości
zaadresowania danego rejestru i wy-
brać najprostsze i najkrótsze instruk-
cje (
in
,
out
,
cbi
,
sbi
) – pokazywali-
śmy to na różnych przykładach. Je-
dynym mankamentem tego postępu
Elektronika Praktyczna 3/2006
107
Rys. 31. Struktura wewnętrznej pamięci RAM mikro-
kontrolerów AVR
K U R S
Rys. 32. Przemieszczanie sekcji da-
nych do zewnętrznej pamięci
– Wl,––section–start=.data=adres_
startowy
– Wl,––section–start=.bss=adres_
startowy
– Wl,––section–start=.noinit=adres_
startowy
albo (dla sekcji.
data
i.
bss
) wer-
sje skrócone:
– Wl,–Tdata=adres_startowy
– Wl,–Tbss=adres_startowy
W AvrSide wprowadzamy je
w polu edycyjnym dodatkowych
opcji w zakładce
Linker
okna kon-
figuracji projektu – ale bez pre-
fiksu –Wl, – AvrSide dodaje go
automatycznie (czyli np. wpisuje-
my po prostu:
–Tdata=adres
). Je-
śli używamy
makefile
dodajemy
potrzebną opcję do LDFLAGS. Na-
leży tylko pamiętać, że przesu-
nięcie sekcji przemieszcza samo-
czynnie wszystkie znajdujące się
za nią z zachowaniem domyślnej
kolejności (
rys. 32
). Korzyść z ta-
kiego przemieszczenia jest podwój-
na: otrzymujemy do dyspozycji
dużą przestrzeń na dane a zarazem
nie musimy się kłopotać o wiel-
kość stosu i potencjalne nadpisanie
przez stos części danych. Moż-
liwa jest także oczywiście ope-
racja odwrotna: przeniesienie do
zewnętrznej pamięci stosu (słu-
ży do tego dyrektywa kompilatora
–minit–stack=nnnn
albo zamien-
nie bezpośrednie zadeklarowanie
początku stosu dyrektywą linke-
ra
–defsym, __stack=nnnn
). Ale
w praktyce nie ma to większego
sensu, gdyż dostęp do stosu po-
przez zewnętrzną magistralę będzie
znacznie wolniejszy, co pogorszy
efektywność całego programu.
Przy takim przemieszczeniu
sekcji danych musimy samodziel-
nie zadbać o odpowiednio wcze-
sne uruchomienie magistrali
ext–
–ram
– musi być ona aktywna
już w momencie inicjalizacji da-
nych .
data
oraz zerowania ob-
szaru .
bss
. Zwykłe wpisanie kon-
figuracji magistrali na początku
naszego programu nie wystarczy,
gdyż (jak widzieliśmy w podglą-
dzie assemblera) kod inicjalizacji
(
__do_copy_data
oraz
__do_cle-
ar_bss
) wykonywany jest przed
wejściem do funkcji
main
. Musi-
my zmodyfikować jedną z sekcji
startowych (podział samoczynnie
tworzonych sekwencji rozpoczyna-
jących i kończących program na
sekcje startowe
init0
...
init9
i zamy-
kające
fini0
...
fini9
jest dokładnie
opisany w dokumentacji avr–libc)
– na ogół wykorzystujemy prze-
znaczoną dla użytkownika sekcję
init1
. Umieszczenie funkcji w okre-
ślonej sekcji realizuje atrybut
sec-
tion(nazwa).
Dodatkowo zastosu-
jemy znany juz atrybut
naked
,
który pozbawi funkcję samoczyn-
nie tworzonego prologu i epilogu.
W efekcie uzyskujemy bezpośred-
nie wstawienie kodu w potrzebnym
miejscu, np. tak (przykład dla
Atmega 128):
void EnableExtRam(void) __attribu-
te__ ((naked)) __attribute__ ((section
(„.init1”)));
void EnableExtRam(void)
{
XMCRA = _BV(SRW00);
XMCRB = _BV(XMBK);
MCUCR = _BV(SRE);
}
jest częściowy brak kompatybilności
ze starszymi projektami – jednak
dla rozpoczynających naukę nie bę-
dzie to żadną przeszkodą.
Obsługa pamięci danych
Sposób zarządzania pamięcią da-
nych także był już dość dokładnie
przedstawiony (podział na sekcje,
lokalizacja stosu itp.). Kompilator
posługuje się tą pamięcią jako do-
myślnym obszarem – nie musimy
stosować żadnych dodatkowych kla-
syfikatorów (oprócz wynikających
z zastosowanej sekcji – jak
NOINIT
,
czy wynikających ze struktury pro-
gramu – jak
static
czy
volatile
).
Także zawartość wskaźników do-
myślnie wskazuje na adresy w pa-
mięci danych (z wyjątkiem wskaź-
ników na funkcje odnoszących się
do adresów w obszarze kodu). Nie-
które układy z rodziny ATmega są
wyposażone w sprzętowy interfejs
zewnętrznej pamięci danych. Do-
stęp do zewnętrznej pamięci jest
zorganizowany bardzo prosto: od-
wołanie się programu do adresu
RAM wykraczającego poza pojem-
ność wbudowaną w kostkę powo-
duje samoczynne wygenerowanie
odpowiedniej sekwencji sygnałów
na magistrali
ext–ram
. Nie są więc
potrzebne (w przeciwieństwie do
np. '51) żadne oddzielne instrukcje
maszynowe. Wynika stąd od razu,
że AVR–GCC może taką pamięć
obsługiwać na zwykłych zasadach,
bez żadnych dodatkowych adapta-
cji. Jeśli jednak przypomnimy oma-
wiany wcześniej schemat domyślne-
go podziału RAM na sekcje (
.data
,
.
bss
,
.noinit
, stos) od razu stwier-
dzimy, że obszar zewnętrzny jest
ulokowany poza stosem i nie będzie
mógł być bezkolizyjnie wykorzysta-
ny przez linker. Mamy do dyspo-
zycji kilka sposobów uniknięcia
tej przeszkody. Jeden z najczęściej
stosowanych to poinstruowanie lin-
kera o nowym, pasującym do przy-
gotowanego sprzętu rozmieszczeniu
sekcji. Służą do tego odpowiednie
opcje linii komend AVR–GCC:
Sprawdźmy, że rzeczywiście od-
powiedni kod pojawił się zaraz za
wektorami przerwań. Zwróćmy też
uwagę, że w tym przypadku wy-
starcza sama deklaracja i definicja
funkcji – nigdzie w programie jej
jawnie nie wywołujemy – byłoby
to wręcz błędem, gdyż spowoduje
skok z powrotem do sekcji .
init1
praktycznie resetując program. Je-
śli zrobimy kilka eksperymentów
stwierdzimy także, że w sekcji .
init1
można spowodować błąd jawnym
przypisaniem zera (np.
XMCRA=0
).
Kompilator użyje rejestru zerowe-
go
r1
(
sts 0x006D, r1)
, który tutaj
może mieć jeszcze wartość przy-
padkową gdyż jest inicjalizowany
dopiero w ustawiającej stos sekcji.
init2
. W razie konieczności przenie-
śmy więc nasz kod do następnej
startowej sekcji użytkownika .
init3
.
Można też zamiennie dołączyć
do projektu mały moduł assemblero-
wy, który realizuje tylko to zadanie:
// uruchomienie ext–ram:
#include <avr/io.h>
.global MemInit
.section.init1,”ax”,@progbits
MemInit:
ldi r24,0x80
out _SFR_IO_ADDR(MCUCR),r24
ldi r24,_BV(SRW00)
sts XMCRA,r24
ldi r24,_BV(XMBK)
sts XMCRB,r24
ale tu już musimy sami pamię-
tać o rozmieszczeniu SFR w prze-
strzeniach
IO
oraz
extended–sfr
i stosować odpowiednie instrukcje
(
out
albo
sts
).
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.
108
Elektronika Praktyczna 3/2006
Plik z chomika:
DarkArchon
Inne pliki z tego folderu:
AVR-GCC_cz9.pdf
(502 KB)
AVR-GCC_cz8.pdf
(617 KB)
AVR-GCC_cz7.pdf
(289 KB)
AVR-GCC_cz6.pdf
(160 KB)
AVR-GCC_cz5.pdf
(447 KB)
Inne foldery tego chomika:
DataSheet
Elektronika dla Wszystkich
Elektronika Praktyczna
Pozostałe uP
Programy
Zgłoś jeśli
naruszono regulamin