81_84.PDF

(119 KB) Pobierz
C dla mikrokontrolerów 8051, część 1
K U  R S
Przedstawiamy pierwsz¹ czÍúÊ kursu jÍzyka C†
część 1
dla mikrokontrolerÛw. Zamieszcono w†nim przyk³ady wziÍte z†praktyki
inøynierskiej, w†zwi¹zku z†czym ich praktyczna wartoúÊ jest bardzo
duøa.
Przygotowuj¹c juø od kilkunastu
lat programy, dostrzeg³em pewien
trend, ktÛry jest widoczny nie tylko
w†úrodowiskach projektowania opro-
gramowania zarÛwno dla ìduøychî
komputerÛw, jak rÛwnieø dla mikro-
kontrolerÛw jednouk³adowych.
JÍzyki programowania staj¹ siÍ co-
raz ³atwiejsze do opanowania. Oferu-
j¹ wiele gotowych procedur, ktÛrych
uøycie jest bardzo ³atwe. Doskona³ym
przyk³adem takiego oprogramowania
jest Bascom, w†ktÛrym rzeczy skom-
plikowane i†trudne staj¹ siÍ bardzo
³atwe. Bascom jednak ma jedn¹ pod-
stawow¹ wadÍ - tworzy kod, ktÛry
jest uniwersalny a†przez to nie opty-
malny, w†zwi¹zku z†czym zajmuje
duøo pamiÍci. Oczywiúcie, nowoczes-
ne mikrokontrolery maj¹ tej pamiÍci
coraz wiÍcej, jednak ma to takøe in-
ne reperkusje. Sam bardzo czÍsto ko-
rzystam z†takich jÍzykÛw, jak Bas-
com, zw³aszcza jeúli szybko potrze-
bujÍ mieÊ program, w†stosunku do
ktÛrego nie mam zbyt wielkich wy-
magaÒ. Jednak stoj¹c przed zadania-
mi ìpowaøniejszymiî, nieodmiennie
korzystam z†jÍzyka C i†asembler'a.
Asembler jest jÍzykiem, ktÛry
zmusza do uczenia siÍ programowa-
nia praktycznie od podstaw. Daje
krÛtkie kody wynikowe i†umoøliwia
w†zasadzie dowoln¹ optymalizacjÍ ko-
du programu. Jednak kaødy, kto kie-
dykolwiek napisa³ program w†tym jÍ-
zyku wie, øe bardzo trudno szuka siÍ
w†nim b³ÍdÛw, a†operacje nawet po-
zornie proste trzeba wykonaÊ uøywa-
j¹c wielu rozkazÛw. Jednym s³owem
- programy pisane w†asemblerze po-
ch³aniaj¹ wiele czasu, ktÛrego nie za-
wsze mamy w†nadmiarze, szczegÛlnie
gdy koÒczymy projekt.
WiÍkszoúci wad asemblera pozba-
wiony jest jÍzyk C: zwarty i†czytelny
kod ürÛd³owy (pod warunkiem, øe
celowo czegoú w†nim nie ukrywamy),
bardzo oszczÍdny kod wynikowy -
úrednio rÛønica pomiÍdzy programem
napisanym w†C i†asemblerze, to oko-
³o 15% zajÍtoúci pamiÍci na korzyúÊ
asemblera (to jednak zaleøy od do-
úwiadczenia programisty), ³atwa loka-
lizacja b³ÍdÛw i†przez to nie tak wie-
le czasu potrzebnego na uruchomie-
nie programu. Z†doúwiadczenia wiem,
øe wiÍkszoúÊ - zw³aszcza pocz¹tkuj¹-
cych programistÛw - przystÍpuj¹c do
wykonania programu liczy tylko czas
potrzebny na jego napisanie i†zupe³-
nie lekcewaøy potrzebny na jego uru-
chomienie. To bardzo duøy b³¹d. Je-
den z†moich znajomych mawia, øe
jeúli zapytasz programistÍ ile potrze-
buje czasu i†odpowie ci, øe na przy-
k³ad 1†miesi¹c, to tworz¹c harmono-
gram realizacji ca³ego projektu, nale-
øy do czasu podanego przez progra-
mistÍ dodaÊ jeszcze 2-krotn¹ jego
iloúÊ, czyli w†tym przypadku 3†mie-
si¹ce. Wiem z†praktyki, øe tak jest.
Celem tego artyku³u nie jest nau-
czenie programowania w†jÍzyku C.
Jest na ten temat mnÛstwo ksi¹øek,
miÍdzy innymi - juø klasyka infor-
matyki - pary autorÛw Brian Kernig-
ham i†Dennis Ritchie ìJÍzyk ANSI
Cî, traktuj¹ca o†standardach, ktÛre
musz¹ byÊ przestrzegane w†kaødym
z†dialektÛw jÍzyka C, bez wzglÍdu na
jego przeznaczenie. To jest opis stan-
dardu - z†tej ksi¹øki moøna nauczyÊ
siÍ podstaw. Do standardu, kaødy
z†autorÛw kompilatora jÍzyka C†do³¹-
cza rozszerzenia. Moim celem jest
opisanie pewnych rozszerzeÒ jÍzyka
C, specyficznych dla úrodowiska,
w†ktÛrym tworzone s¹ aplikacje.
W†artykule przedstawiÍ narzÍdzie,
ktÛre dobrze znam - pakiet progra-
mÛw firmy Raisonance. Jest to zin-
tegrowane úrodowisko do tworzenia
programÛw dla mikroprocesorÛw z†ro-
dziny 8051, a†rÛwnieø XA firmy Phi-
lips i†ST62 firmy ST Microelectro-
nics, najpopularniejsza w†naszym kra-
ju rodzina mikrokontrolerÛw.
zrozumia³ej dla mikroprocesora, czyli
- w†duøym uproszczeniu - z†jÍzyka C
do jÍzyka asembler. Jeúli przeúledzi-
my uwaønie listÍ rozkazÛw asemble-
ra dla 8051, zauwaøymy, øe na przy-
k³ad rozkazy skokÛw mog¹ zajmowaÊ
w†pamiÍci 2 lub 3 bajty ( AJMP
i† JMP ). Podobnie z†rozkazami wywo-
³ania podprogramÛw CALL i† ACALL .
Rozkazy 2-bajtowe wystÍpuj¹ wÛw-
czas, gdy adres docelowy znajduje
siÍ w†obrÍbie tego samego 2kB bloku
pamiÍci programu, a†3-bajtowe gdy
w†obrÍbie 64kB. Przedstawi³em to
bardzo skrÛtowo, jednak nasuwaj¹ siÍ
nastÍpuj¹ce wnioski. Jeúli pamiÍÊ jest
ma³a, to rozkazy skokÛw mog¹ byÊ
krÛtsze i†moøemy w†ten sposÛb
zmniejszyÊ wielkoúÊ programu wyni-
kowego! I†to jest w³aúnie jedna
z†przes³anek do utworzenia rÛønych
modeli kompilacji i†rÛønych modeli
pamiÍci. DziÍki nim kompilator
ìorientuje siÍî, jaki kod tworzyÊ
i†w†jaki sposÛb go optymalizowaÊ.
Naleøy tutaj wspomnieÊ, øe kom-
pilator firmy Raisonance obs³uguje
rÛwnieø programy o†³¹czonych mode-
lach pamiÍci. Moøna wiÍc napisaÊ
ca³y program korzystaj¹c z†modelu
SMALL, a†tylko niektÛre procedury
przy zastosowaniu modelu LARGE
i†zmiennych umieszczonych w†pamiÍ-
ci zewnÍtrznej. Oczywiúcie moøna tak
postÍpowaÊ przy spe³nieniu pewnych
warunkÛw. Jednak - szczegÛlnie jeúli
stawiasz pierwsze kroki w†jÍzyku C†-
proponujÍ nie ³¹czyÊ rÛønych modeli
pamiÍci.
Zaczynamy - okreúlenie
wielkoúci pamiÍci
Zaimplementowany w†pakiecie
Raisonance jÍzyk RC-51 oferuje 5†rÛø-
nych modeli kompilacji dla 3†wiel-
koúci pamiÍci programu. Zanim przy-
st¹piÍ do omÛwienia szczegÛ³Ûw,
chcia³bym krÛtko wyjaúniÊ sk¹d wziÍ-
³a siÍ idea rÛønych modeli kompila-
cji i†modeli wielkoúci pamiÍci.
Rol¹ kaødego kompilatora jÍzyka
programowania wysokiego poziomu (a
do takich naleøy jÍzyk C) jest prze-
t³umaczenie kodu programu z†postaci
zrozumia³ej dla cz³owieka, do postaci
Korzystanie z†pamiÍci, czyli
zmienne i†sta³e oraz sposoby
ich umieszczania
w†przestrzeni adresowej
mikroprocesora
Dla programisty bardzo waøne
jest aby zmienne znajdowa³y siÍ
w†úciúle okreúlonym miejscu pamiÍ-
ci, pod znanym adresem. RC-51 wy-
posaøono w†instrukcje umoøliwiaj¹ce
w³aúnie takie swobodne umieszcza-
Elektronika Praktyczna 6/2002
81
32262523.001.png
K U  R S
Tab. 1. Modele pamięci
Model
LcdVideoRam, LcdReadCtrl, LcdRead;
Domyślny typ
Wymagana
Lokalizacja stosu Uwagi
pamięci danych
zewn. pamięć
danych
/* adresy poszczególnych kolumn
klawiatury */
at 0x34 pdata char Column1;
at 0x2C pdata char Column2;
at 0x1C pdata char Column3;
TINY
Wewnętrzna, do 256 bajtów * * *
idata
Zwłaszcza dla mikro−
procesorów serii 8x75x
firmy Philips
SMALL Wewnętrzna, do 256 bajtów Nie
idata lub pdata Możliwe umieszczanie
zmiennych w pamięci
zewnętrznej
/* zmienne robocze */
data Nastawy Bufor;
char PRG;
unsigned int Pozycja;
int Zero = 45;
COMPACT Zewnętrzna, do 64kB
Tak
idata lub pdata Taki sam jak LARGE,
jednak kod wynikowy
jest mniejszy
LARGE Zewnętrzna, do 64kB
Tak
idata lub pdata Doskonały dla dużych
aplikacji
Jest to czÍsto spotykany przypa-
dek, gdy nie jest istotne gdzie w†pa-
miÍci danych znajd¹ siÍ zmienne wy-
korzystywane w†czasie pracy programu
oraz stos, ale waøne jest pod jakimi
adresami znajduj¹ siÍ pod³¹czone do
mikrokontrolera elementy pamiÍci ze-
wnÍtrznej. Oczywiúcie taka deklaracja
powoduje, øe program bÍdzie pobiera³
dane PDATA uøywaj¹c instrukcji
asemblera MOVX A,@R0 . Gdyby
zmienne zadeklarowane zosta³y jako
XDATA , wÛwczas odczytywane by³y-
by instrukcj¹ MOVX A,@DPTR .
ZwrÛÊmy jeszcze uwagÍ na spo-
sÛb deklaracji adresÛw, pod ktÛrym
znajduj¹ siÍ rejestry wyúwietlacza
LCD. Taka metoda zapisu adresÛw
oznacza, øe LcdCtrlRegister znajdzie
siÍ pod adresem 0x00, LcdVideoRam
pod adresem 0x01, LcdReadCtrl -
0x02 i†tak dalej. Moøna w†ten spo-
sÛb umieszczaÊ w†okreúlonym miejs-
cu pamiÍci nie tylko zmienne i†sta³e,
ale rÛwnieø funkcje.
HUGE Zewnętrzna, do 64kB
Tak
pdata
Adres powrotu i para−
metry są zapamiętywa−
ne w pamięci zew.
nie zmiennych i†sta³ych. Niestety,
nie ma øadnego obowi¹zuj¹cego
standardu - prawdopodobnie pocho-
dz¹ce od innych producentÛw kom-
pilatory C bÍd¹ wymaga³y zupe³nie
innej sk³adni.
PamiÍÊ mikrokontrolera 8051 zo-
sta³a podzielona na nastÍpuj¹ce ob-
szary:
- DATA - bezpoúrednio adresowany
obszar wewnÍtrznej pamiÍci RAM
(128 bajtÛw),
- IDATA - poúrednio adresowany ob-
szar wewnÍtrznej pamiÍci RAM
mikrokontrolera (128 lub 256 baj-
tÛw),
- BDATA - obszar pamiÍci RAM,
w†ktÛrym moøliwe jest adresowanie
pojedynczych bitÛw
- SFR - obszar rejestru funkcji spe-
cjalnych w†pamiÍci RAM ( special
function registers ),
- SBIT - obszar pojedynczego bitu
w†obrÍbie rejestru funkcji specjal-
nych SFR, na przyk³ad sbit OV =
PSW^2,
- PDATA - tak samo, jak XDATA
z†tym, øe adres dostÍpu do pamiÍ-
ci zewnÍtrznej jest 8-bitowy,
- XDATA - zewnÍtrzna pamiÍÊ da-
nych, przy czym adres dostÍpu 16-
bitowy (64kB),
- CODE - pamiÍÊ programu; moøe to
byÊ wewnÍtrzna, albo zewnÍtrzna
pamiÍÊ ROM mikrokontrolera.
Wyøej wymienione s³owa kluczo-
we jÍzyka RC-51 nosz¹ nazwÍ kwali-
fikatorÛw obszaru pamiÍci ( space qu-
alifier ). Innym s³owem kluczowym,
úciúle z†nimi zwi¹zanym, jest AT.
SprÛbujmy teraz powi¹zaÊ zdoby-
te juø informacje w†ca³oúÊ. SprÛbuj-
my dopasowaÊ zmienne (takøe sta³e)
i†modele kompilacji do ürÛd³a nasze-
go programu. Jako pierwszy niech
pos³uøy nam przyk³ad programu na-
pisanego dla mikrokontrolera jedno-
uk³adowego, ktÛry wykorzystuje tylko
i†wy³¹cznie swoje zasoby wewnÍtrzne
- nie ma w†przestrzeni adresowej do-
³¹czonych øadnych urz¹dzeÒ ze-
wnÍtrznych.
#pragma TINY (lub SMALL)
at 0x20 data char ZMIENNA1;
Przede wszystkim okreúlamy, øe
nasz program zmieúci siÍ w†pamiÍ-
ci wewnÍtrznej mikroprocesora
AT89C2051, ktÛry posiada 2kB pa-
miÍci programu. Informuje o†tym mo-
del pamiÍci zdefiniowany za pomoc¹
polecenia #pragma TINY . Jako druga
nastÍpuje dyrektywa dla kompilatora,
aby zmienn¹ o†nazwie ZMIENNA1
umieúci³ pod adresem 20H w†pamiÍ-
ci danych. Podobnie postÍpujemy
w†przypadku pamiÍci programu:
at 0x1000 code char tablica[5]=
{'H','E','L','L','O'};
Powyøsza instrukcja umieszcza
w†pamiÍci programu CODE pod adre-
sem 1000H napis HELLO. Oto jesz-
cze inne przyk³ady deklaracji
zmiennych:
code at 0x1000 int ZapisDoEEProm
(char x);
code at 0x00FF void UstawBit (void);
Bardzo uøytecznym kwalifikatorem
przestrzeni jest moim zdaniem sbit .
DziÍki niemu mamy na przyk³ad
moøliwoúÊ powi¹zania poszczegÛlnych
bitÛw portÛw wyjúciowych mikrokon-
trolera z†ich nazw¹ symboliczn¹.
U³atwia to bardzo analizÍ programu
i†rÛwnieø jego pisanie.
char code patterns[10] = {
0x09,0xAF, 0x1A, 0x8A,0xAC,0xC8,
0x48,0x8F,0x08,0x88 };
char code digits[6] = {
0xFE,0xFD,0xFB,0xF7,0xEF,0xDF };
unsigned int data display[6] = {
0xFF,0xFF,0xFF,0x00,0x9,0x09 };
/* funkcje poszczególnych bitów
portu p2 */
sbit Wejscie_1 = P2^4;
sbit Wyjscie_Start = P2^3;
sbit Wyjscie_Kontrola = P2^2;
sbit Wyjscie_Docisk = P2^1;
sbit Wyjscie_Blad = P2^0;
Rozpatrzmy inny przypadek, rÛw-
nieø bardzo czÍsto spotykany w†pra-
ktyce: pamiÍÊ programu, stos oraz
zmienne wewn¹trz mikroprocesora,
a†na zewn¹trz w†przestrzeni adresowej
(adres 8-bitowy) pod³¹czone s¹ pew-
ne urz¹dzenia zewnÍtrzne, takie jak:
wyúwietlacz, klawiatura, port danych
urz¹dzenia pomiarowego. Oto przyk³a-
dy deklaracji:
Zapis ìWyjscie_Start = 1;î jest
rÛwnowaøny instrukcji SETB P2.3
a†jest bardziej czytelny.
Przerwania
S³owo kluczowe interrupt powo-
duje, øe funkcja traktowana jest przez
kompilator jako obs³uguj¹ca przerwa-
#pragma SMALL;
/* adresy rejestrów wyświetlacza */
at 0x00 pdata char LcdCtrlRegister,
82
Elektronika Praktyczna 6/2002
32262523.002.png
K U  R S
Tab. 2. Modele (rozmiary) pamięci
programu
Rozmiar
Opis
strukcji using . S³uøy ona do zmiany
banku rejestrÛw. CzÍsto bowiem zda-
rza siÍ tak, øe nasz program g³Ûwny
uøywa banku rejestrÛw (R0...R7) do
realizacji rÛønych zadaÒ. Sk³adania
polecenia using jest nastÍpuj¹ca:
char generic *p1;
// wskaźnik do dowolnego obszaru
// danych
pdata char *klawiatura;
// wskaźnik do 8-bitowego adresu
// urządzenia zewnętrznego
xdata char *pamiec_danych;
// wskaźnik do zewnętrznej pamięci
pamięci
programu
SMALL 1. Rozmiar programu nie może
przekroczyć 2kB
2. Wywołania CALL mają postać
ACALL a JMP − AJMP
COMPACT 1. Rozmiar programu nie może
przekraczać 64kB
2. Rozmiar funkcji nie może być
większy niż 2kB
3. CALL to LCALL, JMP to AJMP
void Przerwanie_Timera0 (void)
interrupt 1 using 1
int Pomnóż_Liczby (char x,y) using 2
W†powyøszym przyk³adzie * jest
operatorem adresowania poúredniego.
Zastosowany do wskaünika podaje za-
wartoúÊ wskazywanego obiektu. W†jÍzy-
ku C†istnieje jeszcze inna metoda przy-
pisania wskazaÒ do okreúlonej zmien-
nej. Jednoargumentowy operator &
zwraca adres zmiennej (uwaga: moøna
go stosowaÊ tylko do obiektÛw zajmu-
j¹cych pamiÍÊ zmiennych oraz tablic).
To, co najbardziej decyduje
o†elastycznoúci jÍzyka C†-
wskaüniki
Wskaünik ( pointer ) to zmienna,
ktÛra zawiera adres innej zmiennej.
Uøycie wskaünikÛw prowadzi do
bardziej efektywnego kodu niø
otrzymywany innymi metodami. Jed-
n¹ z†niepoø¹danych cech wskaüni-
kÛw jest to, øe powoduj¹ czÍsto
tworzenie zupe³nie niezrozumia³ych
programÛw. Dzieje siÍ tak zw³aszcza
wtedy, gdy wskaüniki stosowane s¹
w†sposÛb ìnieostroønyî. £atwo jest
dla przyk³adu utworzyÊ wskaünik,
ktÛry bÍdzie wskazywa³ nie wiado-
mo co i†nie wiadomo jakiego typu.
Jednak przy przestrzeganiu pewnych
zasad, wskaüniki staj¹ siÍ jednym
z†najmocniejszych mechanizmÛw jÍ-
zyka C.
Kompilator RC-51 oferuje nam 2
typy wskaünikÛw. Pierwszy, to
wskaünik obszaru pamiÍci deklarowa-
nego za pomoc¹ wczeúniej omawia-
nych kwalifikatorÛw (takich jak CO-
DE czy DATA ). Drugi umoøliwia
wskazanie dowolnej danej w†jakim-
kolwiek obszarze adresowania mikro-
kontrolera. Waøne jest, øe jeúli
wskaünik przypisany jest do obiektu
zakwalifikowanego do okreúlonego
obszaru pamiÍci, to rÛwnieø zostaje
przypisany do tego w³aúnie obszaru
pamiÍci, w†ktÛrym umieszczony jest
wskazywany obiekt. Drugi typ jest
specyficznym typem wskaünika, roz-
szerzeniem wprowadzonym przez fir-
mÍ i†nosi nazwÍ generic . Dlaczego -
mÛg³by ktoú zapytaÊ - nie stosowaÊ
wy³¹cznie wskaünikÛw typu generic ?
Oczywiúcie, moøna to zrobiÊ. G³Ûw-
n¹ jednak przyczyn¹ wprowadzenia
takiego podzia³u jest to, øe typowo
wskaünik generic zajmuje 3†bajty, na-
tomiast kwalifikowany do pewnego
obszaru pamiÍci mikrokontrolera -
2†bajty. Wskaünik typu generic za-
wiera dodatkowo 1†bajt przeznaczo-
ny na kod wskazywanego obszaru
pamiÍci.
LARGE
1. Rozmiar programu nie może
przekraczać 64kB
2. CALL to LCALL, JMP to AJMP
nie. Przy jej definiowaniu wymagana
jest znajomoúÊ numeru przerwania -
kolejnoúci w†tablicy wektorÛw prze-
rwaÒ. Adres funkcji zostanie automa-
tycznie wyliczony. Przeanalizujmy
przyk³adow¹ procedurÍ obs³ugi prze-
rwania generowanego przez Timer 0 .
char x = 1, y = 2, z[5];
char *wskaznik;
ip = &x;
//zmienna wskaznik wskazuje na x
y = *wskaznik;
//teraz zmienna y ma wartość 1
*wskaznik = 0;
//teraz zmienna x ma wartość 0
wskaznik = &z[0]
//zmienna wskaznik wskazuje na
//pierwszy element tablicy z
void Przerwanie_Timera0 (void)
interrupt 1
{
TR0 = 0; // zatrzymanie timera 0
TH0 = 0; // odświeżenie
// zawartości rejestrów
TL0 = 0x1F;
licznik++;// zwiększenie
// zmiennej licznik o 1
TR0 = 1; // ponowne uruchomienie
// timera 0
W†deklaracji wskaünika powinno
byÊ naúladowanie elementu, do ktÛ-
rego odnosi siÍ wskazanie.
Zastosowanie wskaünikÛw znacz-
nie upraszcza program. Dobrze znana
jest programistom koniecznoúÊ prze-
kazywania parametrÛw do procedur.
W†wielu jÍzykach programowania pa-
rametry przekazuje siÍ wymieniaj¹c
d³ug¹ ich listÍ. Zajmuje to bardzo
duøo miejsca w†pamiÍci RAM. W†jÍ-
zyku C tÍ listÍ parametrÛw, ³¹cznie
z†d³ugimi tablicami (buforami da-
nych), moøna przekazaÊ przez proste
wskazanie na adres w†pamiÍci, gdzie
ta zmienna siÍ znajduje. Jest to je-
den z†g³Ûwnych powodÛw, dla ktÛre-
go program napisany w†jÍzyku C mo-
øe byÊ wykonywany bardzo szybko,
przy oszczÍdnym gospodarowaniu sto-
sem mikrokontrolera.
}
Odpowiedni wektor przerwania
obliczany jest jako 3+numer_przerwa-
nia x†8 .
Wartoúci 3†i†8†s¹ przyjmowane ja-
ko domyúlne dla rodziny 8051. Jeúli
zachodzi potrzeba ich zmiany, moø-
na to zrobiÊ poprzez modyfikacjÍ IN-
TVECTOR oraz INTERVAL (sta³e
kompilatora). Program, ktÛry jest wy-
konywany po RESET nosi nazwÍ
main i†do niego zawsze odnosi siÍ
wektor przerwania numer 0. Jest to
program g³Ûwny. Kompilator sam dba
o†to, aby wektor przerwania numer
0†zawsze odnosi³ siÍ do miejsca
w†pamiÍci programu, gdzie umiesz-
czony jest kod wynikowy main .
Jaki jest skutek umieszczenia
w†przyk³adzie s³owa kluczowego in-
terrupt ? Po pierwsze instrukcja LJMP
Przerwanie_Timera0 jest umieszczana
w†tablicy wektorÛw przerwaÒ pod ad-
resem 0BH. Po drugie, przy jej uru-
chomieniu zapamiÍtywana jest zawar-
toúÊ rejestrÛw A, B, PSW, DPH, DPL
(na stosie). Po trzecie, na koÒcu pro-
cedury zamiast rozkazu RET, umiesz-
czone zostaje RETI.
Omawiaj¹c procedurÍ obs³ugi
przerwaÒ naleøy wspomnieÊ o†in-
Wskaüniki i†argumenty
funkcji
W†jÍzyku C†parametry wywo³ania
funkcji przekazywane s¹ przez war-
toúÊ. Na skutek tego funkcja (dzia³a-
nia wewn¹trz tzw. cia³a funkcji) nie
ma dostÍpu do argumentÛw naleø¹-
cych do wywo³uj¹cego j¹ podprogra-
mu. Jedyny sposÛb pozwalaj¹cy na
wykonanie dzia³aÒ na zmiennych po-
chodz¹cych z†innego podprogramu, to
przekazanie jako argumentÛw wywo-
³ania funkcji, wskaünikÛw do tych
zmiennych.
data char[10] Tablica =
{0,1,2,3,4,5,6,7,8,9}
char generic *Tablica;
// wskaźnik do obszaru DATA,
// wskazuje elementy
// zmiennej Tablica
Elektronika Praktyczna 6/2002
83
32262523.003.png
K U  R S
Ope− Przykład Opis
rator użycia
Operatory arytmetyczne
+ x + y Dodawanie
− x − y Odejmowanie
* x * y Mnożenie
% 10 % 3 Reszta z dzielenia
(w przykładzie: wy−
nik = 1)
Operacje arytmetyczne
+= x += 10 x = x + 10
−= x −= 10 x = x − 10
*= x *= 3 x = x * 3
/= x /= 23 x = x / 23 (dzielenie)
%= x %= 2 reszta z dzielenia
przez 2
^= x ^= 2 x = x XOR 2
|= x |= 0xF0 x = x OR F0H
<<= x <<= 2 x = x << 2
(przes. 2 razy w le−
wo)
>>= x >>= 4 x = x >> 4
(przes. 4 razy pr−
awo)
Operatory logiczne
> if (x > 23) Znak większości
>= If (x >= 10) Znak większe−równe
< if (x < 10) Znak mniejszości
<= if (x <= y) Znak mniejsze−rów−
ne
== if (x == 0) Porównanie
!= if (x != 0) Znak różności
Operacje logiczne
|| if (x || 0x11 = 0x11) Suma logiczna (OR)
&& if (x && 0x80 = 0) Iloczyn logiczny
(AND)
Do tego potrzebujemy tylko 2†baj-
ty na przekazanie parametrÛw, bo-
wiem nasze zmienne leø¹ w†obszarze
pamiÍci DATA. A†poza tym dzia³ania
wewn¹trz funkcji s¹ wykonywane
bezpoúrednio na zmiennych programu
g³Ûwnego. Øadnych ìpoúrednikÛwî,
np. w†formie stosu, nie potzreba.
Porty I/O, dzia³ania na
portach
Korzystaj¹c z†portÛw I/O mikro-
kontrolera w†programach napisanych
w†jÍzyku C†naleøy kierowaÊ siÍ tymi
samymi zasadami, co w†jÍzyku asem-
bler. To znaczy porty, ktÛre wymaga-
j¹ ustawienia (poziomy wysokie)
przed odczytem z†nich danych, mu-
sz¹ byÊ w†ten stan ustawione. Nic
nie stanie siÍ bez naszego udzia³u,
niczego kompilator nie zrobi za nas.
Gdy o†tym pamiÍtamy, to korzystanie
z†portÛw jest bardzo ³atwe. Moøna
testowaÊ wartoúci bitÛw, przypisywaÊ
zmienne i†sta³e, wykonywaÊ rÛøne in-
ne operacje. Podobnie jak w†asemble-
rze, jeúli operacja wymaga zmian sta-
nu portu (przesuniÍÊ bitÛw i†podob-
nych operacji), lepiej jest utworzyÊ
zmienn¹ bÍd¹c¹ kopi¹ stanu portu
i†na niej wykonywaÊ dzia³ania. Potem
wystarczy tylko przypisaÊ do danego
portu wartoúÊ zmiennej - unikniemy
w†ten sposÛb b³ÍdÛw zwi¹zanych
z†zak³Ûceniami jakie mog¹ wyst¹piÊ
na wyprowadzeniach portu.
- odczyt stanu portu:
1. zmienna = P1
// (0..3 - w zależności od
// mikrokontrolera)
2. P1 = P1 | 0xFF;
// gdy port pracuje jako
// wejściowy, dobrze jest jego
// bity ustawić na “1”:
// zmienna = P1;
- zapis do portu:
1. P1 = 0xAA;
// (numer portu zależny od
// aplikacji),
2. P1 = P1 | 0x01;
// ustawienie bitu P1.0
3. P1 = P1 & 0xFE;
// wyzerowanie bitu P1.0
4. P1^0 = 1;
// odpowiednik rozkazu SETB P1.0
5. P1^0 = 0;
// odpowiednik rozkazu CLR P1.0
- testowanie stanu bitu:
P1 = P1 | 0x01;
// ustawienie P1.0 na “1”
if (P1^0 = 1....
// jeden ze sposobów testowania
//stany P1.0
Kilka s³Ûw o†arytmetyce
wskaünikÛw
Zasady arytmetyki wskaünikÛw
s¹ bardzo proste. Jeúli wskaünik
T†pokazuje pocz¹tek tablicy elemen-
tÛw typu int i†sam rÛwnieø jest ty-
pu *int , to wÛwczas operacja
T=T+1; (inaczej T++;) przesunie
wskazanie na nastÍpny element tab-
licy. Do adresu wskazania nie zo-
stanie dodana liczba 1†lecz 2, po-
niewaø zmienna typu int ma 2 baj-
ty d³ugoúci. Wszystkie operacje na
wskaünikach s¹ automatycznie do-
stosowywane do rozmiaru wskazy-
wanych obiektÛw.
Do operacji wskaünikowych nale-
ø¹: przypisanie wskaünikÛw do
obiektÛw tego samego typu, dodawa-
nie i†odejmowanie wskaünika i†licz-
by ca³kowitej, odejmowanie b¹dü po-
rÛwnywanie dwÛch wskaünikÛw
z†elementami tej samej tablicy oraz
przypisanie wskaünikowi wartoúci
0†lub przyrÛwnanie wskaünika do 0.
Wszystkie inne operacje na wskaüni-
kach s¹ nielegalne. Nie wolno doda-
waÊ do siebie dwÛch wskaünikÛw
(nawet tego samego typu) ani ich
mnoøyÊ, dzieliÊ, przesuwaÊ w†prawo
b¹dü w†lewo, sk³adaÊ z†maskami
(operacje AND i†OR), ani teø doda-
waÊ do nich liczb typu float i† doub-
le . Nie wolno nawet - z†wyj¹tkiem
wskaünika typu *void - wskaünika
obiektu jednego typu przypisaÊ bez
przekszta³cenia (rzutowania) do
obiektÛw innego typu.
Wskaüniki w†jÍzyku C†to temat
bardzo obszerny. Zainteresowanych
poszerzeniem swojej wiedzy oraz
praktycznymi zastosowaniami wskaü-
nikÛw, zapraszam do korespondencji
ze mn¹ oraz lektury materia³Ûw ürÛd-
³owych na ten temat.
!
!x
Negacja logiczna
(NOT)
Operatory bitowe
|
P1 = P1 | 0xF0 Suma bitowa
&
P1 = P1 & 0xF0 Iloczyn bitowy
^
P1 = P1 ^ 0xAA Bitowe wyłącznie−
lub
<< A = A << 4
Przesunięcie w lewo
4 razy
>> A = A >> 2
Przesunięcie w pr−
awo 2 razy
~
~0x77
Dopełnienie jedyn−
kowe
void Zamien (int *Tx, int *Ty)
{
To, co przysparza mi zawsze
najwiÍcej k³opotÛw w†jÍzyku
C†- operatory
Kolejnymi elementami jÍzyka C†s¹
operatory podstawowych operacji: do-
dawania, odejmowania, sumy i†iloczy-
nu logicznego i†tak dalej. Osobiúcie
zawsze mia³em k³opoty z†ich zapa-
miÍtaniem. Dlatego teø, zw³aszcza dla
pocz¹tkuj¹cych, zdecydowa³em siÍ je
zestawiÊ w†tabeli. Skopiujcie j¹ sobie,
bo jeszcze nie raz do niej wrÛcicie.
Zw³aszcza analizuj¹c programy pisa-
ne przez kogoú innego.
Po tym nieco przyd³ugim wpro-
wadzeniu najwyøszy czas aby zapre-
zentowaÊ przyk³ady zastosowaÒ. BÍ-
dzie moøna wÛwczas najlepiej zoba-
czyÊ, w†jaki sposÛb wykonuje siÍ
dzia³ania na bitach, zmiennych, por-
tach itp. Pokaøemy to w†kolejnej
czÍúci kursu.
Jacek Bogusz, AVT
jacek.bogusz@ep.com.pl
int temp;
temp = *Tx;
*Tx = *Ty;
*Ty = temp;
}
void main(void)
{
int A = 10, B = 20;
Zamien(&A, &B);
//zamiana wartości zmiennych A i B
}
Dodatkowe informacje
Ewaluacyjna wersja pakietu firmy Raisonance
znajduje siê na CD-EP6/2002B.
84
Elektronika Praktyczna 6/2002
32262523.004.png
Zgłoś jeśli naruszono regulamin