digitalni_mikrokontroleri

100
FAKULTET TEHNIČKIH NAUKA DEPARTMAN ZA ENERGETIKU, ELEKTRONIKU I TELEKOMUNIKACIJE KATEDRA ZA ELEKTRONIKU NOVI SAD TRG DOSITEJA OBRADOVIĆA 6 http://www.elektronika.uns.ac.rs email: [email protected] + _ (021) 485 2558 UPUTSTVO ZA LABORATORIJSKE VEŽBE IZ PREDMETA DIGITALNI MIKROKONTROLERI Fakultet tehničkih nauka Novi Sad Katedra za elektroniku februar 2008. mr Milan Nikolić mr Kalman Babković

Upload: marko-nedic

Post on 29-Dec-2015

98 views

Category:

Documents


5 download

DESCRIPTION

FAKULTET TEHNIČKIH NAUKADEPARTMAN ZA ENERGETIKU, ELEKTRONIKU I TELEKOMUNIKACIJEKATEDRA ZA ELEKTRONIKUsva prava pripadaju autoru

TRANSCRIPT

Page 1: Digitalni_Mikrokontroleri

FAKULTET TEHNIČKIH NAUKA DEPARTMAN ZA ENERGETIKU, ELEKTRONIKU I TELEKOMUNIKACIJE KATEDRA ZA ELEKTRONIKU NOVI SAD TRG DOSITEJA OBRADOVIĆA 6 h ttp://www.elektronika.uns.ac.rs email: [email protected]

+

_

(021) 485 2558

UPUTSTVO ZA LABORATORIJSKE VEŽBE IZ PREDMETA

DIGITALNI MIKROKONTROLERI

Fakultet tehničkih nauka Novi Sad Katedra za elektroniku februar 2008. mr Milan Nikolić mr Kalman Babković

Page 2: Digitalni_Mikrokontroleri

Mikrokontroleri 1

Katedra za elektroniku Sadržaj

Sadržaj: 1. ...........................................................................................................................................................3 Uvod2. ..................................................................................................4 Hardversko povezivanje mikrokontrolera

2.1. ..........................................................................................4 Ulazno/izlazni portovi mikrokontrolera2.2. ...............................................................................................................................8 Brojači i tajmeri2.3. .......................................................................................10 Povezivanje 7-segmentnih LED displeja2.4. ................................................................................11 Paralelno povezivanje 7-segmentnih displeja2.5. ....................................................................11 Redno (serijsko) povezivanje 7-segmentnih displeja2.6. .........................................................................12 Multipleksno povezivanje 7-segmentnih displeja2.7. .....................................................................................................14 Paralelno povezivanje tastature2.8. ..........................................................................................14 Redno (serijsko) povezivanje tastature2.9. ...............................................................................................15 Multipleksno povezivanje tastature2.10. ..................................................................................17 Galvansko razdvajanje periferijskih signala2.11. ...................................................................................................................18 Serijska komunikacija

2.11.1. ................................................................................................................18 RS232 Komunikacija2.11.2. ....................................................................................................18 RS485/RS422 Komunikacija2.11.3. .......................................................................................................19 SPI sinhrona komunikacija2.11.4. ...........................................................................................20 MicroWire sinhrona komunikacija2.11.5. .......................................................................................................20 I2C sinhrona komunikacija

2.12. .............................................................................................................21 Ulazno/izlazni ekspanderi2.13. ............................................................................................................................22 Deljenje portova2.14. .....................................................................................................................23 Reset kolo i oscilator

3. ...........................................................................................................................25 Softver mikrokontrolera3.1. ......................................................................................26 Organizacija, struktura i tipovi C fajlova

3.1.1. .....................................................................................................................31 Pitanja za proveru3.2. ........................................................................................................................31 Memorijski modeli

3.2.1. .....................................................................................................................34 Pitanja za proveru3.3. .........................................................................................34 Preprocesorske direktive C kompajlera

3.3.1. .....................................................................................................................39 Pitanja za proveru3.4. .............................................................................................................................39 Strukture i unije

3.4.1. .....................................................................................................................42 Pitanja za proveru3.5. .................................................................................42 Memorijsko mapiranje periferijskih jedinica

3.5.1. .....................................................................................................................45 Pitanja za proveru3.6. ................................................................................................................................45 Naredbe petlji

3.6.1. .....................................................................................................................48 Pitanja za proveru3.7. .......................................................................................................48 Naredbe uslovnog izvršavanja

3.7.1. .....................................................................................................................50 Pitanja za proveru3.8. ..........................................................................................................................50 Logičke operacije

3.8.1. .....................................................................................................................56 Pitanja za proveru3.9. ...........................................................................................................................................56 Prekidi

3.9.1. .....................................................................................................................62 Pitanja za proveru3.10. ..............................................................................................................63 Super petlja i FIFO baferi

3.10.1. .....................................................................................................................67 Pitanja za proveru3.11. ....................................................................................67 Softverske mašine konačnih stanja (FSM)

3.11.1. .....................................................................................................................72 Pitanja za proveru3.12. .............................................................72 Cooperative operativni sistem za rad u realnom vremenu

3.12.1. .....................................................................................................................76 Pitanja za proveru3.13. ..............................................................76 Preemptive operativni sistem za rad u realnom vremenu

3.13.1. .....................................................................................................................78 Pitanja za proveru3.14. .......................................................78 I/O podsistem operativnih sistema sa konkretnim primerima

3.14.1. ..................................................................................................78 I/O sistem operativnih sistema3.14.2. .......................................................................................79 I/O sistem pri korišćenju FEMTOS-a3.14.3. ...................................................................80 I/O podsistem na primeru PIC ploče za proširenja

3.14.3.1 .................................................................................................................80 Analogni ulazi3.14.3.2 ................................................................................................................81 Analogni izlazi3.14.3.3 ................................................................................81 8-cifarski 7-segmentni LED displej

Page 3: Digitalni_Mikrokontroleri

Mikrokontroleri 2

Katedra za elektroniku Sadržaj

3.15. ....................................................................................................................81 Paketna komunikacija3.15.1. .................................................................................................................81 Master/Slave uređaji3.15.2. ...........................................................................................................................82 Format paketa

4. ................................................................................................................................83 Laboratorijske vežbe4.1. ....................................................................................................83 Vežba 1: Organizacija C fajlova4.2. .........................................................................84 Vežba 2: Memorijski modeli, makro preprocesor4.3. ...............................................................................................................................84 Vežba 3: Unije4.4. ...............................................................................................................................85 Vežba 4: Petlje4.5. ...........................................................86 Vežba 5: Logičke operacije nad bitima (bitwise operacije)4.6. ............................................................................................................................87 Vežba 6: Prekidi4.7. .....................................................................................................................87 Vežba 7: FIFO baferi4.8. ....................................................................................................89 Vežba 8: Primena mašine stanja4.9. ....................................................................................90 Vežba 9: Korišćenje Cooperative RTOS-a4.10. ...................................................................................91 Vežba 10: Korišćenje Preemptive RTOS-a4.11. ....................................................................................91 Vežba 11: Ulazno/izlazni (I/O) Podsistem4.12. ............................................................................92 Vežba 12: Master-Slave paketna komunikacija

4.12.1. ...........................................................................................................92 Zahtevani format paketa5. ......................................................................................................................................................93 Dodaci

5.1. ....................................................................................93 Objašnjenja i dopune uz zadatak vežbe 115.2. ...............................................94 Aritmetika sa fiksnim zarezom na mikrokontrolerskim sistemima

5.2.1. ...................................................................................................94 Načini predstavljanja brojeva5.2.2. .....................................................................95 Osnove predstavljanja brojeva u fiksnom zarezu5.2.3. .............................................................................96 Apsolutna greška brojeva u fiksnom zarezu5.2.4. ...................................................................................96 Aritmetičke operacije u fiksnom zarezu5.2.5. .......................................................................96 Primer potprograma za rad sa fiksnim zarezom

5.3. ..........................................................................................97 Implementacija serijske komunikacije5.3.1. ...................................................................................................97 Baferi i globalne promenljive5.3.2. .............................................................................................................98 Inicijalizacija interfejsa5.3.3. .........................................................................98 Funkcija za servis prekida serijskog interfejsa5.3.4. .....................................................................98 Punjenje predajnog bafera i započinjanje predaje5.3.5. ................................................................................................99 Očitavanje primljenih podataka5.3.6. ......................................................................................99 Indikacija prijema i završetka predaje

Page 4: Digitalni_Mikrokontroleri

Mikrokontroleri 3

Katedra za elektroniku

1. Uvod

Da bi se mikroprocesor mogao iskoristiti, neophodno je dodavanje različitih vrsta digitalnih komponenti, kao što su memorije tipa ROM i RAM, adresni baferi, lečevi i dekoderi, brojači/tajmeri, kontroleri prekida, baferi i lečevi za digitalne ulaze i izlaze, kontroleri serijskih portova, A/D i D/A konvertori i slično. Ako se sve ove komponente, uključivši i mikroprocesor, integrišu u jedno integrisano kolo, dobija se mikrokontroler koji omogućava znatno smanjenje dimenzija uređaja u koji je ugrađen, uz mnogo veću pouzdanost i znatno nižu cenu. Prvi mikrokontroler, Intel 8048, nastao je davne 1976. godine. Danas postoji nekoliko hiljada tipova mikrokontrolera, razvijenih oko različitih familija mikroprocesora.

Osnovna ideja mikrokontrolera je namenska primena, u programabilinim elektronskim sklopovima

predviđenim za ugradnju (embedded sistem). Primeri primene ovakvih uređaja se mogu naći na svakom koraku, počevši od kućnih aparata (veš mašina, mikrotalasna pećnica, audio i video uređaji), preko autoindustrije (autokompjuter, autoradio), računarskih periferijskih komponenti (optički kompjuterski miš, USB memorijski modul), mobilnih telefona, pa do industrijskih uređaja (regulatori, PLC uređaji). Zbog velikog broja primena, različite familije i varijante unutar iste familije mikrokontrolera su veoma značajne, jer omogućavaju izbor mikrokontrolera koji najbolje odgovara postavljenim zahtevima, kao što su hardverske potrebe i programabilnost. Od posebnog značaja je i cena odabranog mikrokontrolera, pogotovo kada se uzmu u obzir velike proizvodne serije određenih tipova uređaja sa urađenim mikrokontrolerskim sistemima.

U okviru jedne familije mikrokontrolera, pojedinačni tipovi mikrokontrolera se dobijaju variranjem

broja i tipova hardverskih proširenja. Na primer, mikrokontroleri iste familije se mogu razlikovati po: Veličini internog ROM-a. Tipu internog ROM-a (MaskROM, OTPROM, EPROM, FlashROM). Veličini internog RAM-a. Broju tajmera/brojača. Tipu i broju serijskih kanala. Tipu paralelnih portova. Broju kanala i preciznosti A/D konvertora. Broju kanala, tipu i preciznosti D/A konvertora. Broju i tipu hardverskih jedinica posebne namene (USB, CAN, itd.).

Ubičajen postupak proizvođača pri projektovanju novog mikrokontrolera je izbor i kombinovanje

mikroprocesorskog jezgra i dodatnih hardverskih proširenja. Na primer, dva mikrokontrolera sa istim veličinama ROM i RAM memorije mogu se razlikovati u broju nožica, a samim tim i broju digitalnih i analognih linija. Sa druge strane, dva mikrokontrolera koji imaju isto kućište i hardverska proširenja, mogu se razlikovati samo u veličini ROM i RAM memorije. Posmatrajući proizvodni program nekih proizvođača, moglo bi se reći da se mikrokontroleri projektuju po principu Lego kockica. Kao mikroprocesorsko jezgro mikrokontrolera koriste se kako CISC (Complex Instruction Set Computer – mikroprocesor koji u jednoj mašinskoj instrukciji izvršava jednu ili više osnovnih instrukcija, na pr. 8051), tako i RISC (Reduced Instruction Set Computer – u jednoj mašinskoj instrukciji se izvršava jedna osnovna instrukcija, na pr. Microchip) arhitekture. Takođe, programska memorija i memorija podataka mogu biti jedinstvene (von Neumann, na primer Motorola, 68HC11), ili razdvojene (Harvard, na primer 8051, Microchip). Kao glavni podatak koji se uvek daje uz mikrokontroler je broj bita (širina) memorije za podatke, pa je tako 8051 8-bitni mikrokontroler. Ako je arhitektura Harvard tipa, tada se daje i broj bita (širina) programske memorije. Na primer, Microchip ima 8-bitne mikrokontrolere sa programskom memorijom širine 12, 14 i 16 bita. Zavisno od primene, na tržištu se mogu naći mikrokontroleri počevši od najjednostavnijih i u najmanjem pakovanju (8-bitni, samo 6 nožica), pa do vrlo složenih (16, 24, 32, 48, 64 bita), kao što su i mikrokontroleri specijalizovani za obradu audio, video i sličnih signala (DSP – Digital Signal Processor). Primeri korišćeni u ovom uputstvu se odnose uglavnom na jednostavnije varijante mikrokontrolera, pogotovo one koje se mogu naći na domaćem tržištu.

Page 5: Digitalni_Mikrokontroleri

Mikrokontroleri 4

Katedra za elektroniku

2. Hardversko povezivanje mikrokontrolera

Osnovni način povezivanja mikrokontrolera sa eksternim kolima je preko ulazno/izlaznih linija podeljenih po grupama (najčešće 8 linija u grupi), koje se obično nazivaju portovi. Kako je broj spoljašnjih fizičkih linija mikrokontrolera obično manji od ukupnog broja linija potrebnih za spoljašnje povezivanje svih internih hardverskih jedinica, ovi portovi su po pravilu multifunkcionalni i zavisno od konfiguracije preko internih registara, mogu imati različite namene. Iz ovoga se može zaključiti da nije moguće istovremeno korišćenje svih internih hardverskih kola, ili bar da nije moguće sva hardverska kola u potpunosti iskoristiti.

Među najčešćim periferijskim uređajima koji se koriste u mikroprocesorskim i mikrokontrolerskim

sistemima su displeji, tastature i serijski portovi. Svrha ovog teksta je upoznavanje studenata sa tipovima digitalnih ulaza i izlaza mikrokontrolera, kao i osnovnim načinima spajanja nekih periferijskih kola kao što su 7-segmentni displeji i grupe tastera. Takođe, prikazani su neki standardni tipovi serijskog prenosa podataka.

2.1. Ulazno/izlazni portovi mikrokontrolera

Ulazno/izlazni portovi mogu biti realizovani na dva načina, kao otvoreni drejn (Open Drain, često se koristi izraz otvoreni kolektor – Open Collector – iako se danas koristi CMOS tehnologija), ili kao totem pol (odnosno Push/Pull). Pojednostavljeni šematski prikaz ova dva tipa portova dat je na slici 1. Na primer, mikrokontroleri iz familije 8051 koriste portove sa otvorenim drejnom, dok mikrokontroleri proizvođača Microchip koriste totem pol portove.

mikrokontroler mikrokontroler

WRizlazni flip-flop

RP

RRQ1

izlazni flip-flop

Q1

Q2

VCC VCC

R1 flip-flop smera

WR

RP

RR

K1

K2

I/O I/O

a) Port tipa otvoren drejn b) Port tipa totem pol

WS

0 1

source

sink

source

sink

Slika 1: Ulazno/izlazni port mikrokontrolera

Oznake korišćene na slici su sledeće:

WR – signal upisa u izlazni flipflop RR – signal čitanja stanja izlaznog flipflopa RP – signal čitanja stanja spoljašnje nožice porta WS – signal upisa u flipflop smera I/O – spoljašnja nožica porta Q1 – izlazni prekidački tranzistor, spojen prema masi Q2 – izlazni prekidački tranzistor, spojen prema napajanju R1 – ekvivalentni otpornik realizovan tranzistorom (ne postoji uvek)

U varijanti porta sa otvorenim drejnom, slika 1a, mikrokontroler koristi tri kontrolne linije, RP, WR i RR. Izlazni flipflop, koji se kontroliše linijom WR, istovremeno se koristi za dve funkcije. Ako se u njega upiše logička nula, uključuje se izlazni tranzistor Q1 i spoljašnja I/O linija porta se dovodi takođe na logičku nulu. Međutim, ako se u flipflop upiše logička jedinica, izlazni tranzistor Q1 se isključuje, a logičku jedinicu na I/O liniji održava jedino otpornik R1. U ovom slučaju, spoljašnje kolo, priključeno na I/O liniju, može

Page 6: Digitalni_Mikrokontroleri

Mikrokontroleri 5

Katedra za elektroniku

forsirati logičku nulu ili logičku jedinicu, jer je I/O linija u stanju velike impedanse (R1 je tipično 50kΩ za familiju 8051). To znači da logička jedinica na izlazu porta istovremeno označava i da je port ulazni. Na slici 2 su prikazani primeri za izlazni i ulazni port.

mikrokontroler 8051

mikrokontroler 8051

1

Q1

VCC

R1

OUT

a) Izlazni port

0 1

Q1

VCC

R1

IN

b) Ulazni port

izlazno kolo

ulazno kolo

Slika 2: Izazni i ulazni port sa otvorenim drejnom

Za ulazni port (slika 2b) važno je napomenuti da tranzistor Q1 ni u jednom trenutku ne sme biti uključen, jer to može izazvati kratak spoj ukoliko spoljašnje ulazno kolo daje logičku jedinicu. Ovo je posebno važno kada se na port priključuje prekidač ili taster. Na slici 3 su dati primeri ispravnog i neispravnog povezivanja tastera na port sa otvorenim drejnom:

1

mikrokontroler 8051

1

Q1

VCC

R1

a) Pravilno povezivanje

0

VCC mikrokontroler 8051

Q1

VCC

R1

b) Nepravilno povezivanje

0

VCC

Re

Re

Slika 3: Povezivanje tastera/prekidača na port sa otvorenim drejnom

Spoj tastera (ili prekidača) prema slici 3b je veoma loše rešenje iz sledećih razloga:

1. Postoji opasnost od direktnog kratkog spoja između napajanja i mase, kroz taster i tranzistor Q1 u slučaju neke softverske greške (isprekidana linija).

2. Mikrokontroleri iz familije 8051 imaju reset ciklus koji dovodi sve portove u stanje logičke jedinice. Međutim, od momenta uspostavljanja napajanja potrebno je izvesno vreme da proradi oscilator čiji je rad neophodan za reset sekvencu. Kako se svi izlazni flipflopovi po pravilu inicijalno postavljaju na logičku nulu, to znači da je nakon uključenja napajanja potrebno izvesno vreme pre nego što svi portovi budu isključeni, odnosno postavljeni na logičku jedinicu. Ako je, u ovom kritičnom periodu, taster pritisnut, dolazi do kratkog spoja i izlazni tranzistor Q1 može biti trajno oštećen.

3. Spoljašnji otpornik Re prema masi mora biti znatno manje otpornosti nego interni otpornik R1 porta, da bi, pri isključenom tasteru, napon na razdelniku R1-Re bio znatno niži od najvišeg dozvoljenog nivoa za logičku nulu (oko 0.8V).

4. Za mikrokontroler 8051 najčešće se koriste ulazni logički nivoi kompatibilni sa TTL kolima, što znači da je logička nula najviše 0.8V, a logička jedinica najmanje 2V. Kako je margina šuma za logičku nulu (oko 0.8V) znatno manja od margine šuma za logičku jedinicu (oko 3V), to znači da spoljašnje kolo treba da ima znatno manju otpornost prema masi nego prema napajanju. Za taster se

Page 7: Digitalni_Mikrokontroleri

Mikrokontroleri 6

Katedra za elektroniku

može smatrati da ima otpronost nula u pritisnutom stanju, zbog čega je očigledno bolje rešenje povezivanje tastera prema masi nego prema napajanju.

U spoju tastera prema masi (slika 3a) ne postoji mogućnost kratkog spoja, jer su jedina dva elementa sa malim otporom (taster i tranzistor Q1) vezani paralelno. Iako 8051 ima interni (ekvivalentni) otpornik prema napajanju, obično se na liniju porta dodaje i spoljašnji otpornik (Re), tipično reda 10kΩ, koji smanjuje uticaj smetnji na rad mikrokontrolera, jer ista smetnja (sa istom energijom) na manjem otporniku pravi manji pad napona, tako da je manja verovatnoća da visok logički nivo padne ispod minimalno dozvoljene vrednosti. Pri povezivanju periferijskih uređaja na izlazni port sa otvorenim drejnom treba imati u vidu da je struja koju port može dati od napajanja prema izlazu (source current) daleko manja od struje koju port može da proguta prema masi (sink current), jer je otpornost uključenog izlaznog tranzistora Q1 mnogo manja od otpornosti ekvivalentnog optornika R1. Zbog toga se, u okviru kataloških podataka mikrokontrolera, definiše samo maksimalna struja koju jedna linija porta može progutati. Osim toga, u katalogu se definiše ukupna struja 8-bitnog porta (uvek manja od 8 puta struja pojedinačne linije porta), kao i ukupna struja svih portova, takođe manja od proizvoda broja 8-bitnih portova i ukupne struje 8-bitnih portova. Na primer, ako je maksimalna struja pojedinačne linije porta 20 mA, ukupna struja jednog 8-bitnog porta može biti do 80 mA, a ukupna struja za četiri porta može biti do 160 mA. Ove numeričke vrednosti nisu pravilo i treba ih shvatiti samo uslovno. Neki primeri povezivanja izlaznih kola na port sa otvorenim drejnom su dati na slici 4.

Ro1

mikrokontroler 8051

VCC

D1

Ro2

Rp1 Rp2

Qo1

Qo2

+

Slika 4: Primeri spajanja izlaznih kola na port sa otvorenim drejnom

Zbog znatno boljih strujnih mogućnosti pri logičkoj nuli na izlazu porta, LED D1 je povezana prema napajanju, uz obavezni otpornik Ro1 za ograničenje struje. U primeru na slici 4 koristi se LED sa malom strujom (Low Current LED, oko 2 mA), što je u granicama raspoložive struje porta kod standardnog 8051. Drugi primer je povezivanje PNP tranzistora Qo1, koji se na port povezuje preko otpornika Ro2 (obavezno!), koji ograničava struju baze tranzistora, uz otpornik Rp1 čija je svrha ubrzavanje isključenja Qo1 i povećanje margine smetnji invertora realizovanog tim tranzistorom. Treći primer prikazuje spajanje NPN tranzistora Qo2 (na pr. za pogon relea), za koji nije potreban redni otpornik prema portu, jer ne postoji potreba za ograničenjem struje baze (struja baze je određena paralelnom vezom otpornika Rp2 i internog otpornika R1 izlaznog porta, slika 1a; ovo je tzv. “direktna sprega“ prekidača Q1 i Qo2). Otpornik Rp2 se dodaje radi povećanja struje baze Qo2, da bi se obezbedilo zasićenje ovog tranzistora. Važno je napomenuti da je nivo logičke jedinice u ovom slučaju oko 0.7V (napon baza-emiter), jer je baza Qo2 direktno spojena sa portom.

Port sa otvorenim drejnom može se koristiti i za dvosmerni rad, u vezi poznatoj kao ožičeno I (Wired-And). U ovom slučaju, na istu fizičku liniju povezuje se više uređaja, pri čemu svi moraju biti tipa otvoreni drejn (ili kolektor), kao na slici 5 (O.D. označava kolo sa otvorenim drejnom/kolektorom). Ukoliko sva kola priključena na liniju porta (uključivši i mikrokontroler) postave na svoje izlaze logičku jedinicu (odnosno isključe svoje ekvivalentne Q1 tranzistore), tada će se na liniji pojaviti logička jedinica, koju obezbeđuje otpornik R1 (eventualno više otpornika paralelno). Ako bilo koje kolo na liniju postavi logičku nulu (odnosno uključi svoj ekvivalentni prekidač Q1 prema masi), tada se linija nalazi u stanju logičke nule. Ovo je ekvivalentno ponašanju logičkog I kola, pa odatle i naziv ovog načina povezivanja.

Page 8: Digitalni_Mikrokontroleri

Mikrokontroleri 7

Katedra za elektroniku

mikrokontroler 8051

1

Q1

VCC

R1

Izlazni port

0

O.D

kolo 1 kolo 2 kolo 3

O.D O.D

Slika 5: Povezivanje više kola po metodi ožičeno I

Portovi sa totem polom imaju dva flipflopa (slika 1b). Jedan flipflop ima istu funkciju kao i kod porta sa otvorenim drejnom, on određuje logičko stanje izlaza. Drugi flipflop određuje način rada, odnosno da li je port ulaznog ili izlaznog tipa. Logičke kapije K1 i K2 upravljaju tranzistorskim prekidačima Q1 i Q2, na osnovu stanja oba flipflopa. Ako je smer porta ulazni, tada su oba tranzistora isključena, a port se nalazi u stanju visoke impedanse. U slučaju izlaznog porta, jedan od dva tranzistora je uključen, zavisno od stanja izlaznog flipflopa. Za kontrolu smera porta koristi se dodatni flip flop sa pripadajućim kontrolnim signalom WS. Zavisno od tipa/familije mikrokontrolera, logička jedinica flipflopa smera može da znači ulazni ili izlazni tip porta, sto je uvek dato u kataloškim podacima proizvođača. Glavna osobina porta sa totem polom je veća struja koju port može dati (od napajanja prema izlazu). Slično kao i kod porta sa otvorenim drejnom, definiše se maksimalna struja jedne linije porta, jednog 8-bitnog porta i svih portova zajedno, ali u ovom slučaju se to odnosi kako na izlaznu (source), tako i na ulaznu (sink) struju. Za razliku od reset ciklusa kod mikrokontrolera iz familije 8051, kada se svi portovi postavljaju na logičku nulu u momentu uspostavljanja napajanja, a tek nakon izvesnog vremena budu postavljeni na logičku jedinicu (što znači da su kratkotrajno izlazni), mikrokontroleri sa totem pol portovima po pravilu nemaju ovaj problem, jer je inicijalno stanje flipflopa smera porta takvo da se portovi odmah postavljaju kao ulazni, u stanje visoke impedanse. Zbog toga ovde nije kritičan način povezivanja spoljašnjih kola niske otpornosti (tasteri, prekidači), pod uslovom da je softverski obezbeđeno održavanje ulaznog tipa porta. Ipak, i ovde postoje razlozi za povezivanje ovakvih komponenti prema masi, a ne prema napajanju:

Kada margine šuma za logičku nulu i jedinicu nisu iste (TTL ulazni nivoi). Za povezivanje tastera/prekidača potrebne su dve linije, jedna prema portu mikrokontrolera i druga

prema masi ili prema napajanju (zavisno od načina povezivanja). Razvođenje veze mase je svakako manje kritično, jer eventualni kratak spoj sa metalnim delovima uređaja neće napraviti štetu, pogotovo ako su i ovi metalni delovi povezani sa masom. Razvođenje napajanja je svakako lošije rešenje, iako ovde nije u pitanju mogućnost oštećenja izlaznog tranzistora porta, nego je pre reč o potencijalnom kratkom spoju napajanja.

Kada je reč o povezivanju izlaznih kola na port sa totem polom, način povezivanja zavisi od strujnih

mogućnosti porta, odnosno od toga koja je struja veća, izlazna (source) ili ulazna (sink). Preporučljivo je korišćenje logičkog nivoa koji obezbeđuje veću struju, ali treba uzeti u obzir i način povezivanja izlaznog kola, odnosno dužinu i vrstu veza prema izlaznom kolu (da li se vodi napajanje ili masa). U primeru sa slike 4, ovde je LED moguće povezati i prema masi, dok se tranzistor Qo2 povezuje slično tranzistoru Qo1, sa rednim otpornikom na izlazu porta, ali uz bazno-emiterski otpornik vezanim prema masi. Za oba tipa izlaznog stepena porta, otvoreni drejn i totem pol, od posebnog značaja su mogućnosti i načini softverskog pristupa portu. Ako se pogleda slika 1, uočljivo je da za upis izlaznog stanja porta postoji samo jedan signal, WR. Na slici 1b postoji dodatni signal WS, ali on određuje smer porta, a ne izlazno stanje. Međutim, za čitanje stanja porta postoji dva signala, RR za čitanje stanja izlaznog registra (flipflopa) i RP za čitanje stanja spoljašnje, fizičke linije porta. Razlog postojanja ovih signala krije se u softverskom modelu portova. Naime, kako su svi kontrolni registri za rad sa internim hardverom mapirani u okviru memorijskog prostora mikroprocesorske jedinice, širina (broj bita) ovih registara je određena brojem bita jedne reči

Page 9: Digitalni_Mikrokontroleri

Mikrokontroleri 8

Katedra za elektroniku

memorije u koju su registri mapirani, što je obično 1, 2 ili 4 bajta, odnosno 8, 16 ili 32 bita (u daljem tekstu, radi jednostavnosti, podrazumevaće se rad sa 8-bitnim mikrokontrolerima). Zbog toga se svim bitima jednog registra mora pristupiti istovremeno, čitanjem ili upisom jedne reči, širine jednog bajta. Ako su sve linije 8-bitnog porta deklarisane kao ulazne ili izlazne, tada se vrši samo čitanje (ulaza) ili upis (izlaza), po pravilu jednom mašinskom instrukcijom. Međutim, ako je port kombinovano podešen, odnosno jedan deo porta je ulazni, a drugi izlazni, tada se može postaviti pitanje sadržaja pročitanog sa porta. Ovo je posebno izraženo kod portova sa otvorenim drejnom (slika 1a), gde logička jedinica na izlazu porta istovremeno označava ulazni port. Problem nastaje kada se pročita stanje porta (RR), a zatim se isti sadržaj upiše u registar porta (WR). Ako je periferijska jedinica, koja je spojena na neku ulaznu liniju porta, u momentu čitanja porta forsirala logičku nulu, tada će ta nula biti i pročitana. Kada se isti (pročitani) sadržaj vrati na port, ova logička nula će se pojaviti na do tada ulaznoj liniji i ta će linija postati izlazna, što će potpuno blokirati rad ove linije kao ulazne. Ovaj problem se može eliminisati ako se za čitanje izlaznog porta koristi signal RR, a ne RP. Kako se čitanjem izlaznog flipflopa (RR) ne može dobiti stanje ulaznih linija, uvodi se pojam instrukcija koje modifikuju port u jednoj mašinskoj instrukciji (Read-Modify-Write, pročitaj-promeni-upiši). Neki primeri instrukcije ovog tipa su postavka (SETB P1.2), brisanje (CLR P1.2) ili inverzija (CPL P1.2) jednog bita porta. Instrukcije iz ove kategorije čitaju stanje izlaznih registara (RR), a ne stanje fizičkih linija porta, dok sve ostale instrukcije, čitaju stanje fizičkih linija porta (RP). Ako instrukcija RMW tipa nije primenljiva, ona se može zameniti sledećim nizom instrukcija: čitanje porta, promena odgovarajućih bita koji pripadaju izlazima, postavka svih bita ulaznog tipa na logičku jedinicu i upis dobijene reči na port. U ovom slučaju, korak u kome se ulazni biti pročitane reči forsiraju na logičku jedinicu, obezbeđuje da ulazne linije i dalje ostanu ulazne. Isti princip softverskog pristupa portovima koristi se i za portove tipa totem pol, iako ovde ne postoji problem promene smera porta sa ulaznog na izlazni. Naime, čitanjem porta i ponovnim upisom pročitanog sadržaja na port, stanje fizičkih linija se upisuje u izlazne flipflopove. Kako ovi flipflopovi ne mogu da utiču na stanje fizičke linije definisane kao ulaz, jedina promena će biti u stanju izlaznog registra, ali ne i na fizičkim linijama. Problem, ovoga puta manji, nastaje ako se namena porta dinamički menja između izlaznog i ulaznog tipa, pri čemu je potrebno zadržavati sadržaj izlaznih flipflopova. Dobar primer je simulacija izlaza tipa otvoren drejn pomoću izlaza tipa totem pol. Na slici 6 prikazana je blok-šema, koja u električnom smislu nije potpuno tačna, ali funkcionalno dobro opisuje način rada. U ovom slučaju izlazni flipflop se trajno drži u stanju logičke nule, a stanje fizičke linije porta se određuje flipflopom smera porta. Ako se smer podesi kao izlazni, tada se na izlazu pojavljuje logička nula kao posledica stanja izlaznog flipflopa. Kada se smer podesi kao ulazni, tada je izlazni stepen isključen i jedino spoljašnji otpornik, povezan prema napajanju, definiše logičku jedinicu. Ovakvo ponašanje u potpunosti odgovara ponašanju porta sa otvorenim drejnom i često se koristi u povezivanju po principu ožičeno I.

Totem pol

RP

Q1

VCC

Rs

Izlazni port

fiksno 0

O.D

kolo 1 kolo 2 kolo 3

O.D O.D

izlazni flip-flop

flip-flop smera

VCC

Slika 6: Totem pol port kao port sa otvorenim drejnom

2.2. Brojači i tajmeri

Imajući u vidu namenu mikrokontrolera, praktično je nemoguće zamisliti mikrokontroler koji nema neki pogodan način za precizno merenje vremena. Za ove svrhe se koriste brojači u najrazličitijim konfiguracijama, pri čemu se koriste interni ili eksterni izvori takta. Ukoliko je takt interni, brojač postaje tajmer, jer se ovaj takt dobija iz glavnog oscilatora, većinom kristalnog. Primer generičkog tajmera/brojača prikazan je na slici 7.

Page 10: Digitalni_Mikrokontroleri

Mikrokontroleri 9

Katedra za elektroniku

INT INT

PWM preskaler

brojač modula N

takt

preskaler

presetabilni brojač

takt

registar modula brojanja

preskalerpresetabilni

brojač

takt

registar modula brojanja

komparator

registar širine impulsa (PWM)

INT

a) Jednostavan brojač

a) Brojač promenljivog modula brojanja

a) Brojač promenljivog modula brojanja i PWM generator

Slika 7: Neke varijante brojača/tajmera mikrokontrolera Preskaler nije uvek prisutan, a ako postoji, obično ima jedan ili nekoliko fiksnih faktora deljenja. Brojač može imati fiksan (2N) ili varijabilan/programabilan moduo deljenja. Prekoračenje opsega brojanja je obično praćeno signalom prekida, što se koristi za generisanje preciznih vremenskih intervala. Uz brojač mogu postojati dodatni registri namenjeni za različite svrhe.

Najjednostavnija varijanta brojača/tajmera je prikazana na 7a). Ovde se brojač koristi u varijanti tajmera, sa fiksnim modulom brojanja (na primer 1:256), uz vrlo malo mogućnosti programiranja. Ako postoji, preskaler obično može da deli ulaznu frekvenciju sa nekoliko faktora, uglavnom sa vrednostima 2N, na primer 1, 2, 4, 8, 16, 32. Neki kontroleri, koji imaju ovakav tajmer, nemaju čak ni mogućnost generisanja prekida pomoću tajmera, ali je tada obično moguće čitanje trenutnog stanja brojača, što se može iskoristiti za približno određivanje vremenskih intervala. Primeri ovakvih brojača mogu se naći u najjednostavnijim mikrokontrolerima proizvođača Microchip.

U varijanti 7b), brojač može da radi kao delitelj sa programabilnim faktorom deljenja, za šta se

koristi dodatni registar modula brojanja. Svaki put kada dođe do prekoračenja opsega brojača, sadržaj dodatnog registra se prebacuje u glavni brojač, koji ovu vrednost koristi kao početnu. Ako brojač broji na gore, tada se može smatrati da je vrednost u registru modula brojanja u stvari negativna vrednost intervala brojanja, izražena u taktovima brojača. Svaki prelaz u stanje 0 (sa 111...111 na 000..000), osim ponovnog punjenja brojača, generiše i signal prekida mikrokontrolera, koji se ponavlja u programabilnom vremenskom intervalu. Ako se registar modula brojanja ne koristi, u potprogramu za obradu prekida brojač se može napuniti programski, čime se takođe dobija programabilan vremenski interval, koji u ovom slučaju nije potpuno precizan, zbog promenljivog vremena odziva na prekid. Vrlo često, dodatna mogućnost ovakvih brojača je prebacivanje sadržaja brojača u pomoćni registar (capture režim). Ovo ‘hvatanje’ stanja brojača se obično vezuje na spoljašnji događaj preko ulaznog porta i može se iskoristiti za merenje trajanja spoljašnjih signala. Primeri ovakvih brojača/tajmera se mogu naći u mikrokontrolerima iz familije 8051. Brojački sklop sa slike 7c), uz sve mogućnosti kao i varijanta pod 7b), ima mogućnost generisanja programabilnog PWM signala (Pulse-Width Modulation, impulno-širinska modulacija). Ova mogućnost se dobija pomoću dodatnog PWM registra i digitalnog komparatora. U toku jedne periode brojanja (što je perioda PWM signala), koji se podešava registrom modula brojanja, komparator generiše određeni logički nivo (na primer visok nivo) sve dok brojač ne dostigne vrednost u PWM registru. Od tog momenta pa sve do kraja intervala, izlazni signal komparatora generiše suprotan logički nivo (na primer nizak nivo). Promenom sadržaja PWM registra, perioda (kao i učestanost) izlaznog signala ostaje ista, ali se menja trajanje aktivnog logičkog nivoa, odnosno faktor ispune izlaznog signala. Ako se ovakav PWM signal propusti kroz niskopropusni filtar, dobija se analogni signal proprocionalan faktoru ispune, što se može iskoristiti kao jednostavan D/A konvertor. Primeri brojača/tajmera koji mogu da generišu PWM signal mogu se naći u većini današnjih mikrokontrolera (varijante 8051, mikrokontroleri proizvođača Microchip, Atmel itd.).

Page 11: Digitalni_Mikrokontroleri

Mikrokontroleri 10

Katedra za elektroniku

Jedna posebna varijanta tajmera su Watch Dog tajmeri. Ovakav tajmer se ponaša kao retrigerabilni monostabilni multivibrator, koji hardverski resetuje mikrokontroler ukoliko se ne osveži u određenom vremenskom intervalu. Svrha ovog tajmera je zaštita od pogrešnog izvršavanja programa, do koga može doći iz bilo kog razloga (greška u programu, uticaj smetnji). Softverski, instrukcije za osvežavanje ovog tajmera postavljaju se na mesta u programu kroz koja program mora redovno da prolazi, ukoliko nema nikakvih grešaka zbog kojih je zaštita i postavljena. Watch Dog tajmer (ili WDT) se najčešće programira fiksno, tako da ni jedna instrukcija programa ne može da ga uključi ili isključi, odnosno da promeni podešeni vremenski interval. Pri podešavanju ovog tajmera obično je vrlo malo mogućnosti na raspolaganju, što je i razumljivo s obzirom na njegovu svrhu.

2.3. Povezivanje 7-segmentnih LED displeja

Izgled 7-segmentnog displeja dat je na slici 8a. Iako je ovakav displej namenjen prvenstveno za prikaz cifara, na njemu je moguće prikazati i određeni broj slova, kao i neke posebne simbole, kao na slici 8b.

a

b

c

d

e

f g

dp

a) Izgled 7-segmentnog displeja

b) Izgled cifara i nekih slova i simbola koji se mogu prikazati na 7-segmentnom displeju

Slika 8: 7-segmentni displej

Osim 7 osnovnih segmenata koji se označavaju slovima ‘a’ do ‘g’, svaki displej ima i decimalnu tačku ‘dp’, pa je stvaran broj svetlećih elemenata 8. Ukoliko je na ovakvom displeju potrebno prikazivati znake koji nisu samo cifre, tada se mora predvideti povezivanje svakog pojedinačnog segmenta na mikrokontroler. Međutim, ako su dovoljne standardne cifre, broj linija povezivanja može smaniti na 4, korišćenjem dekodera 4/7seg, koji 4 ulazne linije konvertuje u 7-segmentne cifre. Neki tipovi ovakvih dekodera podržavaju i heksadecimalni prikaz, odnosno 10 standardnih cifara i slova ‘A’ do ‘F’. Pasivni LED displeji se proizvode u dve varijante, sa zajedničkom anodom i sa zajedničkom katodom (slika 9). Osim pasivnih, postoje i aktivni displeji sa ugrađenim dekoderima, ali se oni ređe koriste i ovde neće biti razmatrani. Kako su pasivni displeji u suštini grupa LED elemenata, sve što se u daljem tekstu odnosi na displeje direktno je primenljivo i na pojedinačne LED ili LED grupe. Direktno spajane jedne ili više LED na mikrokontroler (naravno, preko otpornika, kao na slici 4), moguće je samo uz poštovanje maksimalno dozvoljene struje porta mikrokontrolera. Zavisno od broja cifara 7-segmentnih displeja i znakova koje treba prikazivati, povezivanje ovih komponenti moguće je na više načina, kao što su paralelni, redni i multipleksni. U navedenim primerima korišćeni su displeji sa zajedničkom anodom (‘CA’), kada se segment aktivira logičkom nulom. Za displeje sa zajedničkom katodom (‘CC’) spajanje je slično, ali sa suprotnim polaritetima odnosno logikom. zajednička

anoda

a b c d e f g dp zajednička

katoda

a b c d e f g dp

Slika 9: Displeji sa zajedničkom anodom i katodom

Page 12: Digitalni_Mikrokontroleri

Mikrokontroleri 11

Katedra za elektroniku

2.4. Paralelno povezivanje 7-segmentnih displeja

Osnovna metoda paralelnog povezivanja displeja podrazumeva upravljanje displejem preko registara, slike 10a i 10b. Na ovim slikama 8-bitni registri mogu biti posebna kola, ali i portovi kontrolera, pri čemu u drugom primeru treba voditi računa o maksimalnoj struji priključaka portova. Na slici, izlomljenje linije ispod displeja označavaju obavezne redne otpornike za ograničenje struje svakog segmenta displeja.

8-bitni registar 8-bitni registar

8 data cs1 cs2

4/7seg dekoder

4/7seg dekoder

4/7seg dekoder

4/7seg dekoder 8-bitni

registar8-bitni registar

8-bitni registar

8-bitni registar

8data cs1 cs2 cs3 cs4

+5V +5V

a) Povezivanje preko 7-segmentnog dekodera b) Povezivanje za punu kontrolu svih 8 segmenata

Slika 10: Paralelno povezivanje 7-segmentnih displeja

U prvom primeru, na 8-bitne registre su priključeni 4/7 segmentni dekoderi, koji se mogu primenjivati uglavnom samo za brojčani prikaz zbog najviše 16 mogućih kombinacija segmenata, dok se u drugom primeru mogu postići potpuno proizvoljni simboli (svih 256 segmentnih kombinacija). Upis u registre (ako se koriste posebna kola) vrši se preko kontrolnih linija cs1..cs4, podacima preko magistrale podataka data. Minimalan broj fizičkih linija je 8+broj registara, pri čemu se broj linija cs može malo smanjiti korišćenjem dodatnog dekodera. Prednost ovakvog načina povezivanja je u vrlo brzom pristupu pri promeni sadržaja displeja, uz kontinualnu struju kroz LED segmente, sa retkim strujnim impulsnim udarima samo u momentima promene sadržaja, dok je mana veći broj fizičkih linija koje je potrebno provlačiti do svih registara, kao i veći broj otpornika segmenata. Primer registarskih kola koja se ovde mogu koristiti su 573 i 574 iz familija 74LS/HC/HCT.

U oba načina povezivanja, registri mogu biti mapirani u eksterni adresni memorijski prostor i tada

signali cs1..cs4 moraju biti generisani preko adresnog dekodera i signala upisa. Ako se sve kontrolne linije povežu na portove mikrokontrolera, tada se upis u registre vrši softverskim podešavanjem ovih linija. Prikazani displeji koriste zajedničku anodu, a segmenti se aktiviraju logičkom nulom. Svakako, moguća je i primena displeja sa zajedničkom katodom (aktiviranje logičkom jedinicom), uz izbor odgovarajućih dekodera/registara.

2.5. Redno (serijsko) povezivanje 7-segmentnih displeja

U ovom načinu povezivanja displeja koriste se serijsko/paralelni pomerački registri, kao što je prikazano na slici 11a, dok je na slici 11b dat primer odgovarajućeg kola. Sa svega tri fizičke linije moguće je kontrolisati veći broj displeja, ulančavanjem serijsko/paralelnih pomeračkih registara. Preko DOUT se prenose podaci, sinhrono sa taktom CLK. Kada se svi pomerački registri napune, tada se signalom STB sadržaj pomeračkih registara prebacuje u izlazne registre svih kola od jednom. Ova vrsta sinhrone komunikacije je u skladu sa SPI sinhronim serijskim protokolom (poglavlje 2.11.3) i može se veoma jednostavno implementirati, bez obzira da li primenjeni mikrokontroler ima ili nema odgovarajuću hardversku jedinicu. Prednost ovog načina povezivanja je u vrlo malom broju fizičkih linija kojima se pomerački registri povezuju, dok je mana sporije upravljanje displejima nego kod paralelnog povezivanja.

Page 13: Digitalni_Mikrokontroleri

Mikrokontroleri 12

Katedra za elektroniku

Serijsko/paralelni registar

8-bitni registar

8-bitni registar

8-bitni registar

+5V

a) Serijsko povezivanje displeja pomoću pomeračkih registara

8-bitni registar

DOUT

CLK STB

b) Primer pomeračkog registra

8-bitni izlazni registar STB

8-bitni serijsko/paralelni registar

SIN CLK SOUT

Slika 11: Redno povezivanje 7-segmentnih displeja

Ako mikrokontroler hardverski podržava SPI protokol, tada brzina ne mora da predstavlja problem i usporenje u odnosu na paralelnu vezu postaje zanemarljivo. Kao i kod paralelnog povezivanja, i ovde se radi sa kontinualnim strujama i retkim strujnim impulsima u momentima promene sadržaja displeja. Primer pomeračkih registara koji se ovde mogu koristiti su 4094 iz familije 4000 ili 74HC/HCT, kao i 595 iz familija 74LS/HC/HCT, a izbor zavisi od strujnih potreba displeja.

2.6. Multipleksno povezivanje 7-segmentnih displeja

Multipleksno povezivanje displeja podrazumeva povezivanje elemenata displeja u matricu, kao što je prikazano na slici 12. U ovom primeru su takođe primenjeni displeji sa zajedničkom anodom.

8-bitni registar

2-bitni registar i dekoder 2/4

VCC

data

cs1

cs2

8

Slika 12: Multipleksno povezivanje 7-segmentnih displeja

Katode svih ‘a’ segmenata su vezane zajedno na prvu izlaznu liniju 8-bitnog registra, katode svih

segmenata ‘b’ na drugu itd. Veza sa 8-bitnim registrom je ostvarena preko otpornika za ograničavanje struje (prikazano kao pravougaonik sa izlomljenom linijom). Anode pojedinačnih displeja se vezuju preko tranzistorskih prekidača na napajanje, pri čemu ni u jednom momentu nije uključeno više od jednog prekidača. Ako je jedan prekidač uključen, odgovarajući displej je aktivan, a svetle oni segmenti displeja čija se katoda (preko otpornika) nalazi na logičkoj nuli izlaza 8-bitnog registra. Brzim naizmeničnim uključivanjem jednog po jednog displeja stiče se utisak kontinualnog rada svih displeja.

Vremenski dijagram uključivanja displeja prikazan je na slici 13. Na slici se vidi i kratka pauza između dva uključenja, koja je potrebna radi pouzdanog isključivanja prekidačkih tranzistora a time i gašenja aktivnog displeja pre nego što se sadržaj 8-bitnog registra promeni u vrednost potrebnu za naredni aktivni displej. Ova pauza između dva aktiviranja koristi se za promenu sadržaja 8-bitnog registra. Treba napomenuti da je i ovde moguće oba prikazana registra puniti paralelno i serijski, ali se češće primenuje paralelni način.

Page 14: Digitalni_Mikrokontroleri

Mikrokontroleri 13

Katedra za elektroniku

displej 1 displej 2 displej 3 displej 4

T

τ

Slika 13: Vremenski dijagrami multipleksnog načina rada

Osnovni problem ovog načina vezivanja displeja je što displeji rade u impulsnom režimu, pri čemu

se pojedini segmenti napajaju impulsnom strujom Imax, odnosno srednjom strujom Imax * 1 / N, gde je N broj displeja. Da bi se ostvarila ista srednja (nominalna) struja kao kod paralelnog i serijskog načina povezivanja, struja svakog segmenta Imax mora biti N puta veća od zahtevane srednje struje.

struja

idealna karakteristika

karakteristikaosvetljaja

disipacija

osvetljaj

nominalna struja Slika 14: Karakteristika LED diode

Ako u toku rada dođe do blokade multipleksa tako da jedan displej ostane uključen, velika je verovatnoća uništenja aktivnih segmenata tog displeja, jer je struja segmenta u ovom načinu rada znatno iznad najveće dozvoljene struje za kontinualni režim rada, zbog čega je i znatno veća disipacija na diodi.

Dodatni problem stvara karakteristika LED segmenta zbog svoje nelinearnosti (slika 14), jer pri povećanju struje preko maksimalne dozvoljene trajne vrednosti sve više raste disipacija, a sve manje osvetljaj, odnosno opada efikasnost diode. Na primer, za trajnu nominalnu struja od 20 mA, ako se pri faktoru ispune 1/8 (za 8 displeja) koristi struja od 80 mA po segmentu, osvetljaj displeja će odgovarati kontinualnoj struji od oko 7 mA, a ne 10 mA (80 mA / 8), kao što bi se moglo očekivati.

U datom primeru (80 mA po segmentu), ukupna struja za displej kome su svi segmenti aktivni je 640 mA. Brze promene multipleksa mogu zbog toga da izazovu česte promene opterećenja napajanja i znatnije impulsne smetnje, pa je potrebno voditi računa o pravilnoj blokadi napajanja i načinu razvođenja veza napajanja i mase.

Prednost ovog načina povezivanja je u srednjem broju fizičkih linija i malom broju potrebnih kola, kao i u malom broju ograničavajućih otpornika (samo 8). Takođe, u ovoj varijanti mikrokontroler mora konstantno periodično da osvežava displej (uobičajeno pod prekidom), za razliku od jednokratnog upisa kao kod prethodnih varijanti, ali to može biti i prednost, jer je pojedinačni pristup (jedno aktiviranje) brz i vrlo malo vremenski opterećuje mikrokontroler. Kako spoljašnji 8-bitni registar u svakom trenutku sadrži podatak samo za jednu cifru displeja, u internoj memoriji mikrokontrolera mora postojati potpuna slika kompletnog displeja, na osnovu koje se vrši periodičan ispis pojedinih simbola. Kao što je rečeno, rad multipleksa se izvodi pod prekidom i nije vezan za funkcije koje manipulišu sadržajem displeja i koje direktno rade samo sa internom slikom sadržaja displeja. Osim toga, nikakvo drugo osvežavanje eksternih kola nije potrebno, što je inače obavezno u slučaju paralelnog (ili rednog) povezivanja displeja.

Page 15: Digitalni_Mikrokontroleri

Mikrokontroleri 14

Katedra za elektroniku

Jedan interesantan način multipleksnog povezivanja LED matrice (primenljivo na 7-segmentne displeje), poznat pod nazivom Charlieplexing (dizajner Charlie Allen, Maxim), prikazan je na slici 15. Ovaj metod obezbeđuje minimalan broj fizičkih veza – N linija omogućava povezivanje N * (N – 1) elemenata.

Slika 15: Charlieplexing metoda multipleksnog povezivanja displeja

Pri projektovanju multipleksnog povezivanja displeja i LED matrica, potrebno je konsultovati podatke proizvođača ovih komponenti, radi pravilnog određivanje maksimalne (impulsne) struje Imax, vremena uključenosti pojedinačnog displeja (τ na slici 13), kao i faktora ispune (τ/T).

2.7. Paralelno povezivanje tastature Paralelno spajanje tastature se realizuje slično paralelnom spajanju displeja, sa tom razlikom što se podatak o stanju čita a ne piše, slika 16. Umesto registara koriste se baferi, čiji se sadržaj čita odgovarajućim signalima CS1 i CS2. Kako su tasteri spojeni prema masi, pročitana logička nula znači da je taster pritisnut.

8-bitni bafer 8-bitni bafer

data CS1 CS2

VCC VCC

Slika 16: Paralelno spajanje tastature

Ako se umesto običnih bafera koriste invertori, tada logička jedinica označava pritisnut taster.

Ukoliko na mikrokontroleru postoji dovoljno slobodnih priključaka, baferi nisu neophodni i može se izvršiti direktno spajanje. Prednost ovog načina spajanja je vrlo brzo čitanje, a mana je veći broj linija spajanja sa mikrokontrolerom. Kola koja se mogu koristiti za bafere su na primer 541 iz familija 74LS/HC/HCT, a za invertore 540 iz istih familija.

2.8. Redno (serijsko) povezivanje tastature

Za serijsko povezivanje tastature koriste se takođe pomerački registri, ali paralelno/serijskog tipa, kao na slici 17. Slično povezivanju displeja, i ovde su dovoljne tri fizičke linije. Signal PL se koristi za učitavanje stanja tastera u pomerački registar, preko data se dobijaju podaci o stanju tastera, taktovano signalom CLK. Mali broj fizičkih linija je istovremeno i velika prednost ovog načina spajanja, uz manu sporijeg pristupa stanjima svim tastera. Imajući u vidu da realno ne postoji potreba brzog pristupa, može se reći da ova je mana često zanemarljiva. Ipak, ovaj način se ne koristi često, jer se za mali broj tastera koristi paralelni način sa direktnim spajanjem (bez dodatnih kola), dok se za veći broj tastera uglavnom koristi multipleksni način spajanja. Kao primeri kola koja se ovde mogu primeniti su standardni pomerački registri 165 i 166 iz familija 74LS/HC/HCT.

Page 16: Digitalni_Mikrokontroleri

Mikrokontroleri 15

Katedra za elektroniku

Kod rednog povezivanja tastature i displeja mogu se koristiti i I2C (poglavlje 2.11.5) ekspanderi

(poglavlje 2.12), ali se to ređe koristi.

8-bitni paralelno serijski registar data

PL CLK

VCC VCC

8-bitni paralelno serijski registar

Slika 17: Serijsko povezivanje tastature

Treba napomenuti da se u oba prethodna načina spajanja (paralelni i serijski) dobija podatak koji je jednoznačan, odnosno stanje koje se pročita u potpunosti odgovara stvarnom stanju tastature, što znači da ne postoje nikakva ograničenja u broju istovremeno pritisnutih tastera. Ovo omogućava da se potpuno slobodno kombinuju tasteri, prekidači, ali i neki drugi elementi kao što su optokapleri, pa u krajnjem slučaju i standardni digitalni ulazi, jer svaki pročitani bit tačno odgovara samo jednoj fizičkoj ulaznoj liniji u bafer odnosno pomerački registar.

2.9. Multipleksno povezivanje tastature

Multipleksni način povezivanja tastature je verovatno najčešći zbog malog broja dodatnih kola koja su potrebna za njegovu realizaciju. Kao što se vidi na slici 18, tasteri se spajaju u matricu NxM formata, koja kao rezultat ima N+M fizičkih linija. Ove linije su podeljene u dve grupe, M vertikala V i N horizontala H.

data CS1 data CS2

VCC

V1 V2 V3 V4

2-bitni registar i dekoder 2/4

N-bitni bafer

A B

C D

H2

H1

H3

Slika 18: Multipleksno povezivanje tastature formata 3x4

Vertikale se spajaju na izlaze dekodera tipa otvorenog drejna (kolektora), tako da je od svih vertikala u uvek samo jedna aktivna, i to na logičkoj nuli. Ova vertikala aktivira N tastera koji su na nju spojeni i daje im mogućnost da odgovarajuću horizontalu na koju su spojeni dovedu takođe na logičku nulu. Sve horizontale su spojene preko otpornika na napajanje, tako da horizontala na kojoj taster nije pritisnut, ostaje u stanju logičke jedinice. Kada se izvrši čitanje horizontalnih linija za sve pojedinačne vertikale, dobija se kompletna mapa stanja tastera. Ako dekoder nema izlaze tipa otvorenog drejna/kolektora, na njegove izlaze potrebno je staviti diode sa katodom prema dekoderu (na slici prikazano kružićem na izlazu vertikale V1), da bi se sprečilo kratko spajanje dva izlaza na različitim logičkim nivoima u slučaju pritiskanja više tastera od jednom, kao što je slučaj sa tasterima uokvirenim isprekidanom linijom (A, B, C i D). Diode treba da budu Schottky tipa zbog njihovog vrlo malog radnog napona ako su ulazi u mikrokontroler kompatibilni sa TTL nivoima (0.8V maksimalna logička nula), dok u slučaju CMOS kompatibilnosti (prag oko polovine napona

Page 17: Digitalni_Mikrokontroleri

Mikrokontroleri 16

Katedra za elektroniku

napajanja) mogu biti korišćene i standardne signalne diode. Postupak čitanja kompletnog stanja tastature je sledeći: Preko data linija se kontrolnim signalom CS2 upiše redni broj prve vertikale V2 u 2-bitni registar/dekoder, a zatim se, takođe preko data linija pročitaju stanja svih tastera na prvoj vertikali, otvaranjem bafera pomoću signala CS1. Postupak se ponavlja za sve vertikale dok se ne dobije kompletno stanje. U najčešćim primenama zaobilazi se N-bitni bafer i horizontale se direktno vezuju na ulaze mikrokontrolera, a takođe se izbegava i 2-bitni registar (odnosno T-bitni, zavisno od broja vertikala), dok se ostavlja samo dekoder. Naravno, moguće je i dekoder eliminisati ako ima slobodnih priključaka na mikrokontroleru. U ovom slučaju, ako su izlazi mikrokontrolera tipa totem pola, potrebno je simulirati izlaz tipa otvorenog drejna na već opisani način (slika 6). Mana ovog načina spajanja tastature je pojava virtuelno pritisnutih tastera. Ako su, na primer, pritisnuti tasteri označeni sa A, B i C, tada se, pri čitanju tastera na vertikali V3 dovodi logička nula na taster B, preko njega na horizontalu H2, zatim preko tastera A se stiže do vertikale V2 (iako ona nije aktivna) i na kraju, preko tastera C horizontala H3 dolazi na nivo logičke nule. Pri čitanju bafera postojaće nule na horizontalama H2 i H3, kao da su pritisnuti tasteri B i C, iako taster C nije pritisnut. Ovo se može izbeći ako se redno sa svakim tasterom ubaci dioda (takođe Schottky tipa), kada više nisu potrebne eventualne diode na izlazima dekodera. Naravno, to zahteva onoliko dodatnih dioda koliko ima i tastera, što komplikuje realizaciju. Ako se na istom uređaju istovremeno potrebni mulitpleksni displej i tastatura, koristi se zajedničko upravljanje vertikalama, čime se štedi na broju fizičkih linija mikrokontrolera. Softverski, oba drajvera se spajaju i realizuju pod jednom prekidnom funkcijom, čime se dobija i kvalitetnije softversko rešenje.

Multipleksna tastatura se može realizovati i na jedan poseban način, vezivanjem direktno na mikrokontroler koji ima ili izlaze sa otvorenim kolektorom, ili može da ih simulira na već opisani način (ekvivalent Charlieplexing metodi povezivanja LED matrice). Ova varijanta se izvodi u dimenziji N x N, sa najvećim brojem tastera do N x (N-1), kao na slici 19 za 12 tastera.

VCC

V1 V2 V3 V4 mikrokontroler

H1 H2 H3 H4

Slika 19: Minimalna forma multipleksnog povezivanja tastature

Ovaj način spajanja zahteva, osim tastera, samo još N otpornika i isto toliko dioda, takođe Schottky tipa. Princip rada je sledeći: Vertikala V1 se postavi na logičku nulu, dok se ostale vertikale podese kao ulazne linije. Preko diode na V1 horizontala H1 dobija logičku nulu, tako da se stanje tri tastera na toj horizontali može pročitati na vertikalnim ulazima V2, V3 i V4. Postupak se ponovi za sve vertikale, čime se dobija kompletno stanje tastature. Mana je ista kao i kod prethodne varijante, mogućnost pojave virtuelno pritisnutih tastera, ali se ovde ne preporučuje rešavanje tog problema dodavanjem rednih dioda na sve tastere zbog povećanja pada napona. I pored ove mane, ovaj način je svakako najjednostavniji, jer pored minimuma dodatnih komponenti, koristi najmanji mogući broj fizičkih linija.

Page 18: Digitalni_Mikrokontroleri

Mikrokontroleri 17

Katedra za elektroniku

2.10. Galvansko razdvajanje periferijskih signala

U mikrokontrolerskim sistemima često se javlja potreba za galvanskim razdvajanjem periferijskih signala od mikrokontrolerske jedinice, kao što su slučajevi u kojima su digitalna masa i masa periferijske jedinice su na različitim potencijalima, ili periferijska jedinica radi sa velikim strujama, zbog čega postoji opasnost od prenosa smetnji kroz masu na digitalni sistem. Zavisno od smera podataka, odnosno da li su u pitanju ulazi ili izlazi, za galvansko razdvajanje se koriste se različite komponente. Jedna od najčešće korišćenih komponenti ovog tipa je optosprežnjak (Opto Coupler – u daljem tekstu optokapler), sastavljen od LED diode i fototranzistora (ili fotootpornika, fotodiode, fotodiaka, fototriaka i slično). Neki primeri galvanskog razdvajanja digitalnih signala prikazani su na slici 20.

digitalni sistem

periferija

digitalnisistem

periferija

digitalni sistem

a) Optički razdvojen izlaz b) Optički razdvojen ulaz c) Relejni izlaz

Slika 20: Primeri galvanskog razdvajanja

Na slici su data tri primera, dva izlaza (a i c) i jedan ulaz (b). Radi jednostavnosti, u ovim primerima su izostavljeni obavezni otpornici i eventualne dodatne komponente. Konkretna realizacija galvanski razdvojenih izlaza zavisi od tipa izlaznog porta (otvoren drejn ili totem pol), aktivnog nivoa (nizak ili visok) i od periferijske jedinice (karakteristike ulaznog dela), dok su kod galvanski razdvojenih ulaza značajni aktivan nivo i karakteristike periferijskog signala. Tako na primer, imajući u vidu karakteristike mikrokontrolerskih portova (poglavlje 2.1), za portove sa otvorenim drejnom (kao što je 8051) optičko razdvajanje izlaza sa slike 20a može se realizovati na načine prikazane na slici 21.

VCC

µC

izlaz

+ periferija

ulaz

masa

a) Neinvertujuća konfiguracija

VCC

µC

izlaz

+ periferija

ulaz

masa

b) Invertujuća konfiguracija

R R

Slika 21: Optičko razdvajanje izlaza za portove sa otvorenim drejnom

Direktno povezivanje optokaplera (LED strane) na mikrokontroler moguće je samo ako izlazni port ima dovoljan strujni kapacitet za provođenje struje LED prema masi (red veličina 10-tak mA). U suprotnom, koriste se dodatni baferi kao strujni pojačavači. Najčešće korišćeni optokapleri imaju strujno pojačanje (odnos struja fototranzistora i LED) oko 0.5, što treba imati u vidu pri određivanju otpornika na obe strane optokaplera. Pri računanju otpornika za LED, može se smatrati da je pad napona na LED oko 1.6 do 1.8 V. Primer približnog proračuna LED i tranzistorskog otpornika dat je na slici 22, uz napomene: VCES i VOUT se zanemaruju; struja kroz LED je bar polovina nominalne vrednosti (5 mA za 10 mA optokaplere), a otpornost R2 treba da bude veća od proračunate vrednosti (na pr. dvostruko), radi pouzdanog zasićenja tranzistora.

I1 = (VCC1 – VD – VOUT) / R1

I2 = (VCC2 – VCES) / R2

A = I2 / I1 R2 ≥ R1 *

VCC1

R1

VOUT (0) GND2

VCC2

R2 I2 I1

VCC2A * (VCC1 – VD)

... struja kroz LED

... struja zasićenja

... pojačanje optokaplera ... granični odnos otpornosti

Slika 22: Primer proračuna optokaplerskih otpornika

Page 19: Digitalni_Mikrokontroleri

Mikrokontroleri 18

Katedra za elektroniku

2.11. Serijska komunikacija Tipovi serijske komunikacije koji će ovde biti ukratko prikazani su RS232, RS485, RS422, SPI, MicroWire i I2C. Prva tri tipa komunikacije se koriste za prenos podataka na kraća ili duža rastojanja, dok se preostala tri tipa koriste za komunikaciju između pojedinih digitalnih komponenti na jednoj ili više štampanih ploča.

2.11.1. RS232 Komunikacija RS232 je asinhrona serijska komunikacija namenjena za kraća rastojanja i manje brzine prenosa. Minimalni broj veza je masa plus jedna linija za svaki smer komunikacije. Komunikacija je dupleksna, što znači da komunikacioni uređaj može istovremeno da šalje i prima podatke. Na fizičkoj liniji definisana su tri moguća stanja, visoko stanje u opsegu od +3 do +15 V (po standardu maksimalno do 25V), nisko stanje u opsegu –3 do –15 V (odnosno –25V), i nedefinisano stanje u opsegu od –3 do +3 V. Predajni drajver i prijemni bafer su po pravilu invertorskog tipa, i koriste se za konverziju naponskih nivoa od 0-5V (TTL nivo) u +/- 12V i obrnuto. Format ove komunikacije na TTL strani je prikazan na slici 23.

S 1 2 3 4 5 6 7 8 E

Slika 23: Format RS232 serijske komunikacije Logička jedinica je pasivno stanje. Prvi bit ‘S’ je start bit na logičkoj nuli. Zatim slede biti podataka, najčešće 8, i na kraju stop bit ‘E’, koji je na logičkoj jedinici. Broj bita podataka može biti 5, 6, 7 ili 8, uz mogučnost uključivanja i 9-tog bita koji se uglavnom koristi kao bit parnosti prethodnih N bita, radi lakše detekcije greške u komunikaciji. Na fizičkoj liniji su nivoi obrnuti, odnosno neutralno stanje je nedostatak pozitivnog napona, start bit je pojava pozitivnog napona itd. U nekim slučajevima 9-ti bit može se koristiti i za posebne vidove komunikacije kao što je automatsko adresiranje, kada se on koristi za određivanje da li je preostalih 8 bita podatak ili adresa. Za komunikaciju RS232 se najčešće koriste brzine dobijene deljenjem 115200 sa celim brojem, kao na primer 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600 i 115200 Bd (Bauda), što označava brzinu broj prenetih bita u sekundi. Ako se koristi 8-bitna komunikacija, tada se za jedan bajt mora preneti 10 bita (8 + 1 start i 1 stop bit), pa se može približno smatrati da je brzina u bajtovima približna jednoj desetini brzine u Baudima. Da bi komunikacija bila moguća, predajnik i prijemnik moraju biti podešeni na istu brzinu, jer se takt ne prenosi posebno. Postoji mogućnost i automatskog određivanja brzine prenosa, ali se za to koriste određene softverske tehnike, koje baziraju na slanju određenog broja nul-bajtova pre stvarnog paketa, tako da prijemnik može izmeriti vreme proteklo od pojave start do pojave stop bita i na osnovu toga podesiti prijemnu brzinu. Softverska implementacija protokola komunikacije može biti raznolika i za to postoji niz različitih rešenja. Osnovna podela može se napraviti između prenosa ASCII karaktera (teksta) i binarnog prenosa, kada se prenose sve vrednosti u opsegu od 0 do 255. Način na koji se preneti podaci tumače zavise od primene i mogu biti i potpuno proizvoljni, zavisno od namene.

2.11.2. RS485/RS422 Komunikacija Komunikacija tipa RS485 i RS422 baziraju na prenosu podataka simetričnim vodom, odnosno

ukrštenom paricom sa protufaznim signalima u opsegu od 0 do 5 V. Za ove svrhe predajnik je izveden sa diferencijalnim izlazom, a prijemnik je u vidu diferencijalnog komparator sa malim histerezisom. Na slici 24 je prikazana veza predajnika i prijemnika ukrštenom paricom, kao i dva načina terminacije prenosnog voda. Osobina ovog tipa komunikacije je velika brzina prenosa na veća rastojanja. Po osnovnoj definiciji, maksimalan proizvod brzine prenosa i rastojanja je 100 Mbit-metara u sekundi, uz ograničenje brzine na najviše 10 Mbita u sekundi. Ograničenja maksimalne dužine takođe postoje i zavise od ukupne otpornosti voda. Za dobar prenos obavezna je pravilna terminacija voda na prijemnoj strani. Otpornost terminatora zavisi od karakteristične impedanse voda, a tipična vrednost terminacionog otpornika iznosi oko 100 Oma. Terminacioni otpornik se postavlja što bliže priključcima prijemnika, između obe fizičke linije.

Page 20: Digitalni_Mikrokontroleri

Mikrokontroleri 19

Katedra za elektroniku

Rt

R2

Rp

Rm

R1

+

+ –

Rt+ –

a) Veza predajnik-prijemnik b) Osnovna terminacija c) Terminacija sa zaštitom

prijemnik prijemnik

Slika 24: Komunikacija RS485/RS422

Na slici 24 je, osim osnovnog načina terminacije, prikazana i varijanta sa zaštitom od kratkog spoja i otvorene veze (pod c). U oba ova slučaja, na prijemniku je jasno definisan ulazni napon veći od napona ulaznog histerezisa, za razliku od varijante pod b), kada ulazni napon nije definisan i podložan je uticaju smetnji.

Komunikacija tipa RS422 je namenjena za komunikaciju između dve tačke, odnosno između jednog predajnika i jednog prijemnika, ali je moguće postojanje i više od jednog prijemnika. Za komunikaciju u oba smera mora se koristiti dve parice, po jedna za svaki smer. Za razliku od RS422, komunikacija tipa RS485 koristi jednu paricu za dvosmernu poludupleksnu komunikaciju. Pošto ovde jednu paricu koristi više predajnika, definiše se Master (glavni) predajnik, koji inicira komunikaciju prozivanjem pojedinih prijemnika pomoću različitih adresa, nakon čega prozvani uređaj preuzima komunikaciju i šalje potrebne informacije nazad do glavnog uređaja. Kako uvek postoji mogućnost da se u nekom trenutku više predajnika priključe na liniju, definiše se pojam kolizije na liniji, kao i metode za utvrđivanje i otklanjanje ove pojave. Osnovne razlike između RS422 i RS485 prikazane su na slici 25.

Slika 25: Razlike između RS422 i RS485 Slika 26: Način povezivanja linije mase

Osim pravilne terminacije, za ova dva tipa komunikacije važno je i pravilno spajanje masa, što je prikazano na slici 26.

Oba ova tipa komunikacije mogu biti zamena za komunikaciju tipa RS232 kada su potrebne veće brzine, duže veze, ili veći broj uređaja na istoj liniji. Sa druge strane, komunikacija RS422 se može koristiti i za sinhroni prenos, kada se, osim podataka, prenose takt i ostali neophodni signali. Među najčešće korišćenim drajverima i prijemnicima za ovaj tip komunikacije su AM26LS31 i AM26LS32, a proizvode se i u CMOS verziji sa smanjenom potrošnjom struje.

2.11.3. SPI sinhrona komunikacija SPI sinhrona komunikacija koristi 4 fizičke linije, SCLK (Serial CLocK), MOSI (Master-Out-Slave-In, od glavnog uređaja do periferije), MISO (Master-In-Slave-Out, od periferije ka glavnom uređaju) i CS (Chip Select). Komunikacija startuje obaranjem signala CS na logičku nulu, a zatim sledi prenos

Page 21: Digitalni_Mikrokontroleri

Mikrokontroleri 20

Katedra za elektroniku

podataka prvo preko linije MOSI (komanda prema periferiji), a zatim vraćanje podataka od periferije preko linije MISO, sinhrono sa taktom SCLK. Komunikacija se zaključuje vraćanjem signala CS na logičku jedinicu. Ukoliko je komanda koja je poslata periferiji takvog tipa da se ne očekuje odgovor, CS se vraća na logičku jedinicu odmah nakon poslate komande, u suprotnom je potrebno sačekati da podaci budu vraćeni glavnom uređaju. Komunikaciju vodi glavni uređaj čiji su signali CS i SCLK izlaznog tipa, kao i MOSI, dok je MISO ulaznog tipa. Primer čitanja podatka iz memorije 25C040 je prikazan na slici 27.

DATA IN

SCLK

MISO

MOSI

CS

CMD ADR DATA OUT

Napomena: CMD, ADR, DATA IN, DATA OUT su u grupama po 8 bita.

Slika 27: Primer sinhrone komunikacije SPI tipa Zavisno od komande, zadnji bajt preko MISO linije se ignoriše pri upisu u memoriju, a preko MOSI linije kada se memorija čita.

2.11.4. MicroWire sinhrona komunikacija MicroWire komunikacija je slična SPI sinhronoj komunikaciji i takođe koristi četiri fizičke linije, u ovom slučaju CS (Chip Select), CLK (CLocK), DI (Data In) i DO (Data Out). Ovde su linije DI i DO posmatrane sa strane periferije, odnosno EEProm memorije 93C56 za koje je opisana komunikacija na slici 28. Linija CS je na logičkoj nuli u neaktivnom stanju. Komunikacija startuje na prednju ivicu CLK, ako su CS i DI na logičkoj jedinici. Nakon toga, podaci se menjaju na svaku silaznu ivicu, a sempluju na svaku prednju ivicu CLK signala. U potrebnom broju taktova prvo se periferiji preko DI linije prenosi komanda koja ne mora biti u dužini N x 8 bita, nego može da zavisi od primenjene periferije. Komanda može da sadrži i podatak, a ako je komanda takvog tipa da se zahteva povratni podatak, periferija vraća traženi podatak nakon prijema komande, kontinualno sinhronizovano sa taktom CLK.

CLK

DI

DO

CS

Komanda, adresa, podatak

Podatak

Slika 28: Primer MicroWire komunikacije serijskog EEPROM-a 93C56 Mnogi mikrokontroleri, koji imaju hardversku podršku za SPI odnosno MicroWire komunikaciju, mogu se programirati za rad sa oba ova načina, jer su razlike između njih vrlo male.

2.11.5. I2C sinhrona komunikacija

Veoma rašireni način serijske komunikacije, razvijen od strane proizvođača Philips, koristi samo dve fizičke linije tipa otvoren drejn i naziva se I2C (I2C ili IIC – Inter Integrated Circuit), a koristi se za povezivanje integrisanih kola na malom rastojanju. Komunikacione linije su SCL (Serial CLock) i SDA

Page 22: Digitalni_Mikrokontroleri

Mikrokontroleri 21

Katedra za elektroniku

(Serial DAta). Zavisno od stanja jedne u trenutku promene stanja druge linije, definišu se momenti START i STOP komunikacije, kao i pravilo kada se menja i odabira podatak. Uslov za start komunikacije je prelaz sa 1 na 0 linije SDA, za vreme dok je SCL na logičkoj jedinici. Uslov za stop komunikacije je prelaz sa 0 na 1 linije SDA, za vreme dok je SCL na logičkoj jedinici. Promena stanja SDA linije radi postavljanja odnosno promene podatka se vrši isključivo dok je SCL na logičkoj nuli. Dijagram uslova startovanja i zaustavljanja komunikacije kao i postavljanja podatka dat je na slici 29.

SCL

SDA

START SET SET STOP

Slika 29: Princip rada I2C sinhrone komunikacije

Prema originalnom dizajnu, na I2C 2-bitnoj magistrali može postojati do 112 kola (7-bitno adresiranje, uz 16 rezervisanih adresa), ali se danas uglavnom koristi adresiranje sa 3 bita, dok preostala 4 bita označavaju tip kola. Bar jedno priključeno kolo mora biti glavni uređaj (Master, obično je to mikrokontroler), koji inicira komunikaciju, a ostala kola su periferije (Slave) koje odgovaraju na zahtev glavnog kola. Moguće je i postojanje više glavnih (Master) uređaja, kada se definiše i pojam kolizije, u slučaju istovremenog aktiviranja više Mastera. U primeru koji sledi date su neke komande za rad sa I2C memorijom 24C04A. Ovakvih kola može biti do 8 na jednoj I2C magistrali, zbog načina adresiranja.

Upis bajta: TIP: S C A ADR A DATA A P

VELIČINA: 1 8 1 8 1 8 1 1

Upis niza bajtova (Page mode): TIP: S C A ADR A DATA A DATA A DATA A DATA A P

VELIČINA: 1 8 1 8 1 8 1 8 1 8 1 8 1 1

Čitanje bajta sa tekuće adrese: TIP: S C A DATA N P

VELIČINA: 1 8 1 8 1 1

Čitanje bajta sa proizvoljne adrese: TIP: S C A ADR A S C A DATA N P

VELIČINA: 1 8 1 8 1 1 8 1 8 1 1

ADR – adresa unutar memorije S – Start (uslov startovanja) – pod ‘veličina’ ovde se podrazumeva prelazno stanje, a ne taktni interval C – Control (slanje kontrolnog bajta) A – Ack (‘0’ => odgovor prijemnika da je dobro primio prethodni podatak) N – No Ack (‘1’, ne očekuje se da prijemnik potvrdi prijem nakon slanja podatka) P – Stop (uslov zaustavljanja) – kao i kod Starta, i ovde ‘veličina’ označava prelazno stanje

Kontrolni bajt: 1 0 1 0 A2 A1 A0 R/W

R/W – 0=WRITE, 1=READ

2.12. Ulazno/izlazni ekspanderi

Za povezivanje displeja, LED matrica, tastature, ali i ostalih ulazno/izlaznih digitalnih periferijskih jedinica, mogu se koristiti kola za proširenje broja digitalnih linija. Neka kola su već predstavljena pri opisu serijskog povezivanja displeja (slika 11) i tastature (slika 17). Serijsko-paralelni i paralelno-serijski konvertori mogu se redno povezivati radi povećanja broja paralelnih izlaza odnosno ulaza. Takođe, moguće je spajanje kontrolnih signala tako da se za kompletan istovremeni serijski prenos za ulazna i izlazna kola

Page 23: Digitalni_Mikrokontroleri

Mikrokontroleri 22

Katedra za elektroniku

koriste samo 4 fizičke linije, kao na slici 30. Princip rada je sledeći: Dok je STB/PL na visokom nivou, izlazni podaci se prenose preko DOUT, a ulazni preko DIN. Kada je prenos završen, signal STB/PL se dovede na nizak nivo, čime se izvrši punjenje ulaznih pomeračkih registara sadržajem na digitalnim ulazima. Nakon toga, STB/PL se vraća na visok nivo, što izaziva prenost sadržaja izlaznih pomeračkih registara u njihove izlazne paralelne registre, čime se postavljaju i digitalni izlazi.

PL PL

STB STBizlazni registar

serijsko-paralelni 8-bitni registar

DOUT

izlazni registar

serijsko-paralelni 8-bitni registar

paralelno-serijski 8-bitni registar DIN

CLK

STB/PL

paralelno-serijski 8-bitni registar

digitalni izlazi

digitalni ulazi Slika 30: Proširenje broja digitalnih ulaza i izlaza pomoću pomeračkih registara

Zavisno od načina funkcionisanja kontrolnih signala (aktivan nizak ili visok nivo, odnosno rastuća ili opadajuća ivica), ovakav prenos sa četiri signala može da odgovara tipu serijske komunikacije SPI ili MicroWire, što hardverski podržavaju mnogi mikrokontroleri, pa je i programiranje ovakvih ulazno/izlaznih proširenja jednostavno. Umesto navedenog, veoma često se primenjuju I2C ekspanderska kola koja imaju određeni broj izlaznih ili ulaznih paralelnih linija, slika 31. Zavisno od strujnih karakteristika ovih kola, sa ili bez dodatnih drajvera (bafera) moguće je i povezivanje displeja kao izlaznih uređaja. Primer jednog ovakvog kola je PCF8574.

I2C kolo I2C kolo I2C kolo

SCL

SDA

Slika 31: Proširenje broja digitalnih ulaza i izlaza I2C kolima

2.13. Deljenje portova

Kao što je na početku rečeno, spoljašnji priključci portovi mikrokontrolera su u manjoj ili većoj meri multifunkcionalni, a konkretna namena im zavisi od spoljašnih veza i priključenih kola. Da bi sve ove veze funkcionisale ispravno, interne hardverske jedinice mikrokontrolera moraju biti pravilno konfigurisane. Šta i kako treba konfigurisati, zavisi od familije i konkretnog tipa mikrokontrolera, a predstavlja jedan od prvih koraka pri pisanju programa za mikrokontroler. Vrlo često i najmanji previd može dovesti do pogrešnog ili nepouzdanog rada, zbog čega treba detaljno proučiti programski model mikrokontrolera, odnosno sve njegove hardverske jedinice i kontrolno/upravljačke registre, kao i inicijalne vrednosti koje ovi registri dobijaju neposredno nakon reset ciklusa, nezavisno od toga da li se neka hardverska jedinica koristi ili ne. Na primer, u mikrokontroleru Microchip PIC 16F87x je inicijalno aktiviran A/D konvertor. Ako se on ne koristi i pri tom ne isključi, digitalne linije na portu deljenom sa A/D konvertorom neće ispravno funkcionisati.

Page 24: Digitalni_Mikrokontroleri

Mikrokontroleri 23

Katedra za elektroniku

2.14. Reset kolo i oscilator

Iako vrlo jednostavni, kolo za resetovanje i oscilator su od ključne važnosti za rad mikrokontrolera. Osnovna svrha reset kola je zadržavanje mikrokontrolera u reset fazi, sve dok oscilator u potpunosti ne proradi i kompletna inicijalizacija mikrokontrolera ne bude završena. Zavisno od familije mikrokontrolera, reset signal može biti aktivan na niskom ili visokom nivou. U ovom slučaju, niski i visoki nivo reset signala ne mora odgovarati graničnim naponskim nivoima logičkih signala mikrokontrolera, a za tačne vrednosti treba proveriti kataloške podatke proizvođača. Primeri oba tipa reset kola prikazani su na slici 32.

reset

VCC VCC

reset

a) Aktivan nizak nivo b) Aktivan visok nivo

C

C

R

D

D

R

Slika 32: Primeri reset kola za oba tipa reset signala

Osnovnu funkciju reset kola obavlja R-C član, koji održava reset signal aktivnim nakon uspostavljanja napajanja, u trajanju određenim RC konstantom i nivoom ulaznog praga reset signala. Dioda nije obavezna, ali je preporučljiva, jer obezbeđuje brzo pražnjenje kondenzatora pri nestanku napajanja, tako da je reset kolo vrlo brzo sposobno za novu reset sekvencu. Bez ove diode, ako je gubitak napajanja kratkotrajan i kondenzator se ne isprazni dovoljno, reset signal može biti suviše kratak ili potpuno izostati, što može dovesti mikrokontroler u neregularan režim rada, odnosno potpuno onemogućiti pravilan rad. Većina savremenih mikrokontrolera ima histerezisni reset ulaz (šmit-triger), tako da su ovako jednostavna reset kola potpuno zadovoljavajuća. U nekim slučajevima (na primer Microchip mikrokontroleri) reset kolo je moguće i potpuno izostaviti, ako je mikrokontroler ugrađeno reset kolo koje interno generiše reset signal u odgovarajućem trajanju. Kod nekih mikrokontrolera ulazni prag reset signala je vrlo nizak (ako je reset signal aktivan na niskom nivou), zbog čega prikazano RC kolo nije primenljivo. U tom slučaju, kao i kada je ostalim delovima mikrokontrolerskog sistema neophodan reset signal, koriste se namenska integrisana kola, koja mogu imati i dodatne mogućnosti, kao što su detekcija smetnji u napajanju (brown-out – pad napajanja ispod dozvoljene vrednosti), ugrađeni WatchDog tajmer, zaštićeni signal za selekciju baterijski napajane RAM memorije i slično. Slično reset kolu, i oscilator je za većinu mikrokontrolera vrlo jednostavan. Oscilator može biti realizovan na različite načine, zavisno od zahtevane tačnosti i stabilnosti radne frekvencije. Osnovno kolo oscilatora najčešće je ugrađeno u mikrokontroler, izuzev neke specifične komponente kao što su kristal i keramički rezonator. Oscilator može biti kristalni (kod većine mikrokontrolera), sa keramičkim rezonatorom, RC tipa i slično. Tako na primer, mikrokontroleri proizvođača Microchip uglavnom podržavaju sve navedene tipove oscilatora, uključivši i interni, fabrički podešen, RC oscilator. Kod ovih kontrolera, prilikom programiranja moguće je podesiti i tip oscilatora. Kada je reč o RC oscilatoru, treba imati u vidu da je on primenljiv samo ako mikrokontroler ne zahteva veliku tačnost i stabilnost frekvencije, kao što je to u slučaju tačnog merenja vremena, asinhorne serijske komunikacije i slično. Sa druge strane, mikrokontroleri bazirani na Intelovoj familiji 8051 uglavnom koriste samo kristalni oscilator, kao što je prikazano na slici 33a.

Uz spoljašnji kristal oscilatora obavezno se stavljaju i dva kondenzatora kapaciteta od 27 do 47 pF (tipično 33 pF), koji sa kristalom i internim invertujućim kolom formiraju Pirsov (Pierce) oscilator. Ovi kondenzatori veoma malo (zanemarljivo) utiču na frekvenciju oscilovanja, koja je tačno određena i odgovara frekvenciji kristala. Kristal i prateći kondenzatori se uvek montiraju što bliže priključcima mikrokontrolera.

Page 25: Digitalni_Mikrokontroleri

Mikrokontroleri 24

Katedra za elektroniku

8051

C2C1

X

napajanje

reset

oscilator

interni reset

prag reseta

reset sekvenca

a) Oscilator za 8051 b) Sekvenca pokretanja mikrokontrolera

Slika 33: Oscilator na bazi kristala (a) i prikaz sekvence pokretanja mikrokontrolera (b) Na slici 33b je prikazana sekvenca pokretanja mikrokontrolera. Sa uspostavljanjem napajanja aktivira se i spoljašnja reset linija. Oscilator zahteva izvesno vreme za dostizanje pune amplitude, nakon čega započinje i interna reset sekvenca (postavljanje svih registara na inicijalne vrednosti). Za pravilnu inicijalizaciju mikrokontrolera, ova sekvenca mora biti završena pre nego što spoljašnji signal dostigne prag reseta. Program mikrokontrolera započinje izvršavanje kada se dostigne ovaj prag, čime je reset ciklus i završen.

Page 26: Digitalni_Mikrokontroleri

Mikrokontroleri 25

Katedra za elektroniku

3. Softver mikrokontrolera

Imajući u vidu velike proizvodne serije uređaja sa namenskim mikrokontrolerskim (embedded) sistemima, veoma je značajna pojedinačna cena jednog mikrokontrolerskog sistema. U početku, konačna cena zavisi prvenstveno od troškova razvoja hardvera i softvera, ali se povećanjem broja proizvedenih uređaja ovaj deo cene brzo smanjuje. Kako broj proizvedenih uređaja raste, primarni deo cene uređaja postaje cena hardvera, odnosno pojedinačnih komponenti mikrokontrolerskog sistema. Ako je mikrokontroler dobro odabran, tada njegova cena ima značajni udeo u ceni hardvera, zbog čega i jeste neophodnan pravilan izabor ove komponente. Na primer, ako svim zahtevima odgovara dva tipa mikrokontrolera, koji se razlikuju samo u veličini interne memorije, tada je veoma važno pažljivo programiranje, čiji će rezultat biti manji program, koji može da stane u mikrokontroler sa manje memorije. Iako razlika u ceni između ovakva dva mikrokontrolera može biti mala, na velikom broju uređaja ova razlika postaje značajna. Ako se ovom razlikom cene nakon izvesnog broja uređaja mogu pokriti veći troškovi razvoja, tada svi naredni uređaji donose čistu dobit, zbog čega je kvalitetnije programiranje vrlo isplativo u dužem vremenskom periodu. Na primer, dva mikrokontrolera se razlikuju u ceni za 50 jedinica, a zbog manje memorije program ne može da stane u jeftiniju varijantu. Ako dodatnim radom programera, u trajanju od jednog meseca pri ceni od 150.000 jedinica, program bude smanjen tako da stane u jeftiniju varijantu, ovaj dodatni trošak će se pokriti sa prvih 3000 proizvedenih uređaja, dok će svi naredni uređaji donositi dobit od 50 jedinica po komadu proizvoda. Naravno, glavni uslov za program mikrokontrolera je zadovoljavajuća brzina izvršavanja, jer svi sistemi sa mikrokontrolerima moraju da rade u realnom vremenu. Očigledno je da je efikasno programiranje veoma bitno u razvoju mikrokontrolerskih uređaja, zbog čega i postoje značajne razlike u metodama programiranja mikrokontrolera i računarskih sistema opšte namene (kao što je PC). To je i glavni razlog zbog čega se za programiranje mikrokontrolera koriste asembler i programski jezik C, dok se izbegavaju drugi, složeniji programski jezici. Prednosti programskog jezika C, u odnosu na asembler, su velike, na primer:

Programski jezik C je u standardnom obliku primenljiv na svakom mikrokontroleru, dok je asembler specifičan za svaku familiju mikrokontrolera.

Sav posao oko razmeštanja promenljivih (varijabli) je automatizovan. C jezik je najvećim delom prenosiv (portabilan) na druge familije mikrokontrolera. Od svih programskih jezika, C jezik je najbliži asemblerskom načinu programiranja. Uz dobar C

kompajler i pažljivo pisanje programa, vrlo je teško napisati efikasniji program u asembleru. Programer je oslobođen većine detalja nezaobilazih u asembleru, tako da može da se posveti samoj

suštini algoritma programa. Programer koji poznaje C jezik treba da nauči samo manje razlike (specifičnosti) kada prelazi na

mikrokontroler neke nepoznate familije, dok se asembler mora kompletno naučiti. Svakako, velika je prednost programera koji poznaje i asembler, jer je često potrebno neke manje delove programa napisati u asembleru, pogotovo kada se radi o vremenski kritičnim sekcijama.

Prednosti C jezika u odnosu na asembler nisu toliko izražene kada je reč o DSP mikrokontrolerima, jer je njihov set asemblerskih komandi obično toliko specifičan i sveobuhvatan, da jedino prenosivost C programa ostaje kao značajnija prednost u odnosu na asembler. Zato se često C jezik kombinuje sa asemblerom radi iskorišćavanja prednosti oba jezika i dobijanja maksimalno efikasnog finalnog koda.

Kada je reč o nekim drugim, višim programskim jezicima, kompajlerima ili interpreterima1, može se reći da što je viši programski jezik, programiranje je lakše i sa mnogo više mogućnosti za programera, ali i sve neefikasnije za konačni program. Zbog toga su retki prevodioci (kompajleri) za C++, Pascal i slične programske jezike. Kao primer, može se navesti Propeler kolo firme Parallax, koje za programiranje koristi dva programska jezika, SPIN (interpreter) i asembler. Pri tome, program napisan u SPIN jeziku je u proseku oko 80 puta sporiji od asemblera.

1 Interpreter je program koji izvršava kodovane složene komande višeg programskog jezika, tako da se mikroprocesor sa interpreterskim programom ponaša kao neki drugi, složeniji procesor (virtuelna mašina).

Page 27: Digitalni_Mikrokontroleri

Mikrokontroleri 26

Katedra za elektroniku

U poređenju metoda programiranja na klasičnim računarima (kao što je PC) i mikrokontrolerima, ako se posmatra samo standardni C programski jezik, mogu se navesti neke razlike:

Na računaru nije od značaja kako se pišu aritmetički izrazi; na mikrokontrolerima može biti upadljivih razlika ako se složeniji izraz razbije u nekoliko jednostavnijih.

Na računaru se aplikacije ubacuju i izbacuju iz memorije po potrebi, a istovremeno može postojati više aplikacija; na mikrokontrolerima postoji samo jedna aplikacija, smeštena u ROM memoriju.

Na računaru se memorija za podatke alocira dinamički (određuje u toku rada), po potrebi; na mikrokontrolerima se sve memorijske potrebe znaju u momentu kompajliranja, pa nema potrebe za dinamičkom alokacijom, koja je i inače izuzetno neefikasna, zbog čega je treba izbegavati. Umesto dinamičke alokacije, mnogo je efikasnije koristiti strukture i unije.

Na računaru veličine pojedinih promenljivih nisu kritične i najefikasniji izbor je veličina koja odgovara širini memorije (na pr. 32 bita); na mikrokontroleru, zbog hroničnog nedostatka memorije, uvek se za promenljivu rezerviše minimalno potrebna širina reči.

Na računaru se konstantni nizovi nalaze u istoj memoriji kao i promenljive; na mikrokontroleru se konstantni nizovi čuvaju u ROM memoriji, opet zbog male RAM memorije.

Na računaru komande petlji (For, While i Do-While) se potpuno slobodno koriste; na mikrokontrolerima se vrlo pažljivo bira tip petlje, radi uštede na prostoru i dobijanja na brzini izvršavanja programa.

Na računaru nema posebne potrebe voditi računa u steku; na mikrokontroleru stek može biti kritičan pa treba voditi računa i o dubini poziva funkcija.

Završetkom izvršavanja aplikacije, na računaru se kontrola vraća operativnom sistemu; na mikrokontroleru postoji samo jedna aplikacija koja se nikad ne završava.

Dobro napisan program u C jeziku , u odnosu na primenjeni mikrokontroler, treba da ima sledeće osobine:

Program se može smestiti u raspoloživu ROM memoriju, pri čemu je ostavljen izvestan prostor za slučaj manjih izmena u programu.

Sve promenljive ostavljaju dovoljno slobodnog mesta za stek u RAM memoriji. Svi elementi programa mogu garantovano da izvrše postavljeni zadatak u zahtevanom vremenu. Program je dobro struktuiran i vrlo pregledno napisan. Gde god je to korisno, dodati su komentari, ali ne tako da se gubi preglednost programa. Nazivi funkcija i promenljivih su logični i proizilaze iz namene, ali nisu predugački. Svi parametri (konstante) programa za koje postoji pretpostavka da bi kasnije mogli biti promenjeni,

izdvojeni su i deklarisani na početku fajla u kome se koriste, ili u jednom zajedničkom fajlu namenjenom samo za te svrhe, uz prateće komentare.

Program je podeljen u logičke/funkcionalne celine, odnosno fajlove, koji se, ako je to moguće, mogu primeniti i u drugim projektima sa istim ili sličnim mikrokontrolerom.

Asemblerski delovi programa su minimizirani i izdvojeni u poseban fajl, tako da je olakšano prilagođenje programa drugom tipu mikrokontrolera.

Nezaobilazne specifičnosti C jezika, vezane za mikrokontroler, upotrebljene su preko makro komandi, smeštenim u poseban fajl, radi jednostavnije adaptacije na drugi mikrokontroler i kompajler.

3.1. Organizacija, struktura i tipovi C fajlova

Kao i većina ostalih programskih jezika, i u C jeziku program se najčešće piše iz više fajlova, koji se nazivaju moduli. Kompletan program se deli u module tako da svaki modul na neki način čini logičku celinu, bilo da su u pitanju funkcije vezane za određeni hardver (hardverski drajveri), ili set funkcija nekog algoritamskog bloka. Ako se sve funkcije, koje su direktno vezane za hardver mikrokontrolera, izdvoje u poseban modul (ili module), tada je i prenos programa na neki drugi mikrokontroler jednostavniji, jer treba adaptirati samo te, hardverski specifične funkcije. Slično tome, ako se na jednom mikrokontroleru neka spoljašnja hardverska jedinica zameni nekom drugom (na primer, umesto LED ugradi se LCD displej), tada je dovoljno promeniti samo modul hadrverskog drajvera, dok se ostali programski moduli ne menjaju.

Page 28: Digitalni_Mikrokontroleri

Mikrokontroleri 27

Katedra za elektroniku

Takođe, jednom napisan i proveren modul može se iskoristiti i u drugim programima. Osnovne prednosti podele programa na više modula/fajlova su lakše testiranje, dobra preglednost i lakša analiza manjih fajlova, mogućnost korišćenja modula u drugim projektima. Nekada su računari bili daleko sporiji, pa je svako prevođenje oduzimalo dosta vremena. Tada je podela na module bila dodatno važna zato što su se prevodili samo promenjeni moduli, a ne ceo program, čime se dobijalo na brzini. Zbog brzine današnjih računara, ovaj razlog više nije od primarnog značaja. Nakon podele programa u module, kako kompajler može da prevodi samo jedan fajl u određenom trenutku, može nastati problem ako se iz jednog modula poziva funkcija (ili promenljiva) koja se nalazi u drugom modulu, jer kompajler nema informaciju kako izgleda funkcija koju treba pozvati. Zbog toga se u C jeziku definišu dva tipa fajlova, C fajlovi, sa ekstenzijom .C i fajlovi zaglavlja (header fajlovi) sa ekstenzijom .H. U ovakvoj podeli, C fajlovi su osnovni i u njih se smeštaju svi elementi programa koji zauzimaju memoriju (funkcije i promenljive), odnosno definicije funkcija i promenljivih, dok su H fajlovi pomoćni i u njih se smeštaju samo deklaracije koje nemaju izvršnu vrednost i ne zauzimaju nikakvu memoriju, nego samo prenose informacije kompajleru, a koriste se u svim modulima kojima su ove informacije potrebne. Na ovaj način moduli mogu da koriste funkcije i promenljive koje su definisane u nekim drugim modulima. Deklaracije se smeju ponavljati sve dok su identične, kao što je to slučaj kada se u jednom modulu na početku napiše deklaracija funkcije, a kasnije i njena definicija (koja obuhvata i deklaraciju). Deklaracije funkcija sa istim nazivom ali različitim parametrima nisu dozvoljene (za razliku od nekih drugih programskih jezika – na pr. C++).

Primer podele C programa na više modula/fajlova, prikazana je na slici 34. U datom primeru koristi se tri modula, glavni.c, sabiranje.c i oduzimanje.c. U sva ova tri fajla nalaze se funkcije koje sadrže i telo funkcije (funkcije su definisane), što znači da će na tim mestima biti generisan konkretan mašinski kôd. Naime, u programskom jeziku C, celokupan izvršni deo programa (deo koji implementira određeni algoritam) sadržan je unutar imenovanih celina, odnosno funkcija. Sve funkcije su istog nivoa prioriteta, što znači da ne postoji nikakva hijerarhija, uz pravilo da se funkcija može koristiti tek nakon što je definisana deklaracijom ili definicijom (što je istovremeno i deklaracija). Opis izvršnog dela funkcije naziva se telo funkcije.

Kako u toku prevođenja modula glavni.c, unutar tog modula ne postoje definicije za funkcije iz

preostala dva modula, formirani su i fajlovi sabiranje.h i oduzimanje.h. U ova dva fajla su upisane deklaracije funkcija iz pripadajućih C fajlova, koje se prepoznaju po tome što nakon deklaracije funkcije ne postoji velika zagrada ‘’ (što bi bila definicija funkcije), nego se deklaracija završava simbolom tačka-zarez (;). Ovako napisana funkcija neće izazvati kreiranje kôda jer ne postoji telo funkcije, nego se samo označava da funkcija tog tipa, sa formalnim parametrima u navedenom broju i tipu, postoji u nekom modulu. Na osnovu toga, prevodilac može tačno da odredi način prenosa parametara i poziva funkcije, što i jeste zadatak deklaracije.

Zavisno od kompajlera, pokušaj poziva funkcije čija deklaracija ne postoji, može da izazove poruku upozorenja ili greške. Ako je u pitanju upozorenje, moguće je da sve faze prevođenja budu izvršene, uz generisanje izlaznog kôda (fajla). U takvim slučajevima, kompajler podrazumeva da je funkcija tipa int, uz proizvoljan broj i tip formalnih parametara. U većini slučajeva ova pretpostavka nije tačna, pa je zbog toga veoma važno analizirati sve dobijene poruke i otkloniti odgovarajuće greške. Takođe, ako se za funkciju bez formalnih parametara, umesto ključne reči void ne napiše ništa unutar zagrada u deklaraciji funkcije, neki kompajleri mogu prijaviti grešku. Zbog toga se preporučuje obavezno pisanje reči void, jer tada sigurno greška neće biti prijavljena. Kada je reč o formalnim parametrima, u deklaracijama funkcija se neki put koristi skraćeno pisanje, odnosno samo lista tipova (na pr. int exp_tabela(int,int); ), bez odgovarajućih naziva, koji su inače propisno napisani u okviru definicije funkcije (na primer int exp_tabela(int baza,int eksp); ). Ovo je loša praksa, jer spisak tipova parametara ništa ne govori programeru, dok pogodno izabrani nazivi parametara pomažu i podsećaju programera o njihovom značenju.

Page 29: Digitalni_Mikrokontroleri

Mikrokontroleri 28

Katedra za elektroniku

fajl: sabiranje.h

int saberi(int a,int b);

fajl: oduzimanje.h

int oduzmi(int a,int b);

fajl: sabiranje.c #include “sabiranje.h” int saberi(int a,int b) return (a + b);

fajl: oduzimanje.c #include “oduzimanje.h”

int oduzmi(int a,int b) return (a – b);

fajl: glavni.c #include “sabiranje.h” #include “oduzimanje.h” void main(void) int a; a = saberi(1,oduzmi(4,2)); ....

Slika 34: Podela C programa na module

U fajl glavni.c su komandom #include uključeni H fajlovi oba preostala modula, tako da kompajler ima tačne informacije o formatu funcija saberi i oduzmi (format parametara i tip funkcije), pa prevođenje može biti pravilno izvršeno. Pri prevođenju fajlova sabiranje.c i oduzimanje.c H fajlovi nisu neophodni, jer je svaka od funkcija u potpunosti definisana u C fajlu. Međutim, H fajl se uvek uključuje u pripadajući C fajl radi provere da li deklaracija u H fajlu odgovara stvarnoj funkciji u C fajlu, čime se smanjuje mogućnost grešaka. Na primer, ako je u H fajlu funkcija deklarisana kao int saberi(char a,char b); a u C fajlu kao int saberi(int a,int b) .... te dve funkcije nisu iste. Ako sabiranje.h nije uključeno u sabiranje.c, tada će kompajler izvršiti prevođenje, ali će pri izvršavanju programa nastupiti problemi zbog različitih formata parametara, jer se iz modula glavni.c ova funkcija poziva na jedan način, a u modulu sabiranje.c parametri se prihvataju na drugi način. Međutim, ako se sabiranje.h uključi u sabiranje.c, pri prevođenju modula sabiranje.c kompajler će otkriti razlike u deklaraciji i stvarnoj funkciji, prijaviće odgovarajuću grešku i omogućiće programeru da na vreme tu grešku i ispravi. Kao i deklaracije funkcija, u H fajlovima se mogu nalaziti i deklaracije promenljivih koje su inače definisane (što podrazumeva rezervisanje memorijskog prostora) u jednom, a koriste se u nekom drugom modulu. Za deklarisanje promenljive koristi se ključna reč extern:

extern char varijabla1; // ne zauzima se memorijski prostor extern unsigned int var2; // ne zauzima se memorijski prostor

Zahvaljujući ovoj ključnoj reči, ovako definisana promenljiva neće zauzeti memorijski prostor, kao što je to slučaj kod definicije promenljive, bez ove ključne reči extern:

char varijabla1; // rezerviše se memorijski prostor unsigned int var2; // rezerviše se memorijski prostor

Drugi način, bez ključne reči extern, piše se samo u C fajlu u kome se promenljiva primarno koristi (definicija promenljive), dok se prvi način, uz ključnu reč extern, piše u pripadajućem H fajlu (deklaracija promenljive).

Primer početničke greške je postavljanje definicije promenljive u H fajl, odnosno izostavljanje reči

extern u deklaraciji promenljive. Iako se radi o jednoj promenljivoj, kada se taj H fajl uključi u nekoliko različitih modula koji koriste navedenu promenljivu, ova definicija promenljive će se pojaviti u svakom od tih modula i rezervisaće poseban memorijski prostor. Promena vrednosti u jednom modulu neće uticati na vrednost te promenljive u ostalim modulima (jer su to različite promenljive istog naziva), što će verovatno dovesti do greške u programu. Ovakav tip greške linker može da primeti i prijavi odgovarajućom porukom,

Page 30: Digitalni_Mikrokontroleri

Mikrokontroleri 29

Katedra za elektroniku

zbog čega je važno detaljno pregledati sve poruke koje se dobijaju tokom svih faza u generisanju izlaznog fajla. Podela programa na više programskih modula/fajlova zahteva i odgovarajući način prevođenja. Naime, kako se prevođenje radi modul po modul, rezultat ovoga su pojedinačni, prevedeni fajlovi svakog modula (object – objektni fajlovi), ali ne i kompletan konačni program. Za spajanje svih ovih objektnih fajlova koristi se poseban program – linker. Ovaj program, osim objektnih fajlova programa, u konačni izlazni fajl uključuje i inicijalizacioni kôd, kao i standardne biblioteke koje su definisane u okviru projekta. Kompletan postupak prevođenja programa, od izvornog teksta programa do izlaznog fajla dat je na slici 35.

preprocesor kompajler

linker

izvorni kôd (moduli)

objektni fajlovi biblioteka

objektni fajlovi programa (ranije prevedeni moduli)

binarni ili heksadecimalni izlazni fajl

konvertor

objektni fajlovi programa (upravo prevedeni moduli)

Slika 35: Kompletan postupak prevođenja

Postupak prevođenja se izvršava u nekoliko faza:

1. Prevode se svi moduli koji su direktno (C modul) ili indirektno (neki od fajlova uključenih u C modul) menjani nakon poslednjeg prevođenja. Prevođenje se vrši u dva koraka:

a. Preprocesor zamenjuje sve #include definicije tako što umesto njih u izvorni tekst ubacuje navedene fajlove. Sve makro komande i konstante definisane direktivom #define se takođe zamenjuju stvarnim tekstom. Komande uslovnog kompajliranja (#define, #ifdef, #endif i slično) se proveravaju, tako da se u narednu fazu propušta samo izvorni tekst koji treba prevesti. Nakon preprocesiranja, kao rezultat se dobija jedinstven fajl (međufaza), koji ne sadrži ni jednu #include ili #define direktive.

b. Dobijeni tekst se prevodi u objektni fajl (kompajler). Dobijeni objektni fajlovi, kao i fajlovi biblioteka, predstavljaju mašinsku verziju izvornog teksta, uz dodatne informacije o svim referencama (adrese funkcija i promenljivih), jer sve adrese nisu poznate u momentu prevođenja, nego se moraju podesiti naknadno, u fazi povezivanja. Objektni fajlovi sadrže i sve informacije neophodne za eventualnu simulaciju programa.

2. Svi objektni fajlovi programa, potrebne biblioteke i inicijalni kôd se spajaju u jedinstven fajl. U toku ovog postupka, linker određuje konačne adrese svih funkcija i promenljivih i smešta ih prema optimalnom rasporedu. U primeru sa slike 34, pri pozivu funkcije saberi, kompajler je u objektni fajl smestio kôd za prenos parametara i mašinsku naredbu poziva funkcije saberi, ali bez konkretne adrese. Zadatak linkera je da odredi stvarnu (konačnu) adresu te funkcije i da prilagodi odgovarajuću mašinsku naredbu poziva potprograma.

3. Zavisno od tipa izlaznog formata procesa povezivanja (linkovanja), može biti potrebno konvertovanje dobijenog fajla u neki od standardnih formata (heksadecimalni tekst, binarni format), za šta se (opcionalno) koristi konvertor. Konačno, dobijeni fajl se može primeniti za programiranje mikrokontrolera ili rad simulatora.

Ako se posmatraju dve ključne faze generisanja izlaznog fajla, prevođenje i povezivanje, važno je znati šta i gde je u kojoj fazi vidljivo. U toku prevođenja, pošto se ovaj postupak izvodi nad pojedinačnim fajlovima, na svakom mestu u jednom C modulu vidljivo je sve ono što je do tog mesta definisano, uključivši i deklaracije dobijene kroz fajlove uključene direktivom #include. U toku povezivanja, kada se spajaju svi potrebni fajlovi na objektnom (prevedenom) nivou, vidljive su sve funkcije i promenljive koje nemaju prefiks static u modulu u kom su definisani. Ako se u više modula pojavi isti naziv (funkcije ili promenljive) bez ovog prefiksa, linker će ustanoviti da postoji više lokacija u memoriji sa istim nazivom, zbog čega će prijaviti grešku. Međutim, ako uz dati naziv postoji i prefiks static, taj naziv neće biti vidljiv linkeru, pa se neće pojaviti ni greške te vrste. Ukratko, svi nazivi sa prefiksom static se tretiraju kao lokalni u modulu u kom su definisani. Ovo takođe znači da u više modula mogu postojati isti nazivi, pod uslovom da imaju prefiks

Page 31: Digitalni_Mikrokontroleri

Mikrokontroleri 30

Katedra za elektroniku

static. Očigledno, naziv definisan kao static u nekom modulu nema smisla da se pojavljuje u odgovarajućem H fajlu, jer se ne može koristiti u drugom modulu zbog toga što neće biti vidljiv u toku povezivanja. Primena prefiksa static je veoma pogodna kada se kreiraju biblioteke. U tom slučaju, ako se svi nazivi (funkcije i promenljive), koji nisu publikovani u H fajlu, definišu kao static, jedino što će stvarno biti vidljivo u obe faze generisanja izlaznog fajla su nazivi deklarisani u H fajlu (ili fajlovima) biblioteke. Objektni fajlovi, koji su ulazni fajlovi za linker (faza povezivanja), osim mašinskog kôda prevedenih izvornih (tekst) fajlova, sadrže i veći broj dodatnih informacija, na primer:

Informacije o svim vidljivim nazivima (funkcija, promenljivih) koji nisu definisani kao static. Posebne su oznake svih mesta u mašinskom kôdu koja koriste apsolutne adrese, jer stvarne vrednosti

tih adresa (reference) određuje linker, tek nakon optimalnog slaganja svih modula i promenljivih u odgovarajući memorijski prostor.

Informacije o međusobnim vezama između pojedinih funkcija, koje omogućavaju da funkcije koje postoje u C modulu (pa tako i u objektnom fajlu) budu eliminisane iz finalnog fajla ukoliko se nigde ne koriste, odnosno nikada se ne pozivaju. Naravno, odgovarajuće informacije postoje i za promenljive.

Podaci neophodni za simulator (tj. debugger), kao što su: originalni naziv C modula od koga je nastao objektni fajl; nazivi i organizacija lokalnih promenljivih; redni brojevi svih linija C fajla na osnovu kojih je nastao mašinski kôd, uz odgovarajuće adrese; tipovi promenljivih, uključivši i formate struktura i unija i slično.

Nakon povezivanja (linkovanja), formira se izlazni fajl sa čistim mašinski kôdom, u kome su sve adrese podešene prema konačnom memorijskom rasporedu funkcija i promenljivih. Fajl biblioteke predstavlja jedan ili više objektnih fajlova spakovanih u jedinstven fajl, uz dodatne informacije koje koristi linker, a koje omogućavaju da se iz fajla biblioteke u konačni fajl uključe samo one funkcije i promenljive koje se stvarno i koriste.

Da bi se postupak prevođenja, povezivanja i eventualnog konvertovanja mogao odvijati integralno, na osnovu definisanih pravila, svi potrebni fajlovi forrmiraju C projekat. U okviru projekta se definiše lista modula (C fajlova), eventualni objektni fajlovi ranije prevedeni za koje ne postoji izvorni tekst, fajlovi biblioteka, spisak H fajlova, dokumentacioni fajlovi itd. Za ceo projekat se definišu globalni parametri, kao što je tip mikrokontrolera, memorijski model, parametri kompajlera, linkera i konvertora, naziv izlaznog fajla i slično. Osim standardnog izlaznog fajla direktno primenljivog u mikrokontroleru, ako modul koji sadrži glavnu funkciju main ne postoji, može se kreirati fajl biblioteke koja sadrži sve globalne funkcije i javne promenljive sadržane u modulima projekta. Ova biblioteka se može kasnije uključivati u druge projekte, čak i kada izvorni tekst fajl C modula ne postoji. Na primer, ako su u projekat uključena tri modula, za upravljanje displejem, tastaturom i serijskim portom, sve ove funkcije mogu da čine jedinstvenu biblioteku koja se direktno uključuje u druge projekte u fazi povezivanja (linkovanja). Na ovaj način su rešene sve standardne funkcije koje dolaze uz C kompajler, a koje se automatski, po potrebi, uključuju u izlazni fajl. Kompajler može da bude u formi IDE (Integrated Development Enviroment – integrisano razvojno okruženje) programa, ili kao skup izvršnih (exe) programa koji se moraju pojedinačno startovati. U prvom slučaju, IDE program obuhvata sve potrebne korake potrebne u toku generisanja izlaznog fajla, što podrazumeva proveru koje sve module treba prevoditi, pozive kompajlera, linkera i konvertora na podešeni način, kao i eventualno izvršenje simulatora radi testiranja kompletnog programa. U drugom slučaju, kada IDE ne postoji, obično se koristi specijalan program (make), koji, na osnovu tačno definisanih pravila opisanih u specijalnom fajlu, izvršava sve neophodne korake kompajliranja.

Kao što je ranije rečeno, programski jezik C namenjen za mikrokontrolere ima specifičnosti koje ga razlikuju od standardnog C jezika. Ove specifičnosti su direktno vezane za familiju mikrokontrolera i odnose se na dodatne tipove promenljivih i memorijske specifičnosti, odnosno načine adresiranja programa i podataka. Tipičan primer je tip promenljive bit. Ovaj tip ne postoji u standardnom C jeziku, bar ne kao poseban tip. Na primer, C jezik dozvoljava definisanje promenljivih čija je dimenzija manja od minimalne standardne širine (8-bitni char) na sledeći način:

typedef struct int bit0:1; int bit1:1;

Page 32: Digitalni_Mikrokontroleri

Mikrokontroleri 31

Katedra za elektroniku

.... int bit15:1; int_bits;

U ovom slučaju definisan je novi tip int_bits koji je 16-bitna vrednost (ako se umesto int korist char, tada se može definisati do 8 bita), podeljena na 16 pojedinačnih promenljivih širine po 1 bit. Međutim za pristup pojedinim bitima ovog tipa koristi se mašinski kôd koji nizom instrukcija simulira pristup pojedinačnom bitu. Kod mikrokontrolera koji podržavaju rad sa pojedinačnim bitima, u C jeziku je dodat novi tip promenljive bit, za koji se koriste specifične mašinske instrukcije, čime se dobija manji i brži kôd. Osim ovakvih specifičnih tipova, uvek su podržani i standardni tipovi:

char – jedan bajt, sa predznakom unsigned char – jedan bajt, bez predznaka int – dva bajta, sa predznakom unsigned int – dva bajta, bez predznaka long – četiri bajta, sa predznakom unsigned long – četiri bajta, bez predznaka float i double – broj bajtova zavisi od kompajlera, jer ovo nisu prirodni tipovi mikrokontrolera

(mašinskog jezika). Kod većine mikrokontrolera (izuzev DSP-a), izbegava se rad u tekućem zarezu, jer on zahteva značajno povećanje konačnog programa i dosta vremena za izvršavanje.

Kao proširenje standardnog C jezika, za svaku familiju mikrokontrolera postoje dodatne ključne reči koje omogućavaju maksimalno korišćenje mogućnosti mikrokontrolera. Tako se, za različite tipove memorije, koriste ključne reči za modifikaciju tipa memorije (modifikatori). Na primer, Keil kompajler za familiju 8051 koristi ključne reči data, idata, bdata, sfr, pdata, xdata,i code.

3.1.1. Pitanja za proveru

1. Šta je osnovna razlika između C i H fajlova? 2. Da li se veličina definisana pomoću ključne reči static, osim modula u kome je definisana, vidi u

ostalim modulima? 3. Zašto se H fajl sa deklaracijama funkcija uvek uključuje u C fajl u kome su te funkcije napisane? 4. U čemu je razlika između kompajlera i linkera? 5. Zbog čega u H fajlu, ispred deklaracije promenljive, mora biti napisana reč extern? 6. Šta su objektni fajlovi? 7. Da li je tip promenljive bit standardni tip C jezika?

3.2. Memorijski modeli Vrste i organizacija memorijskog prostora se mogu veoma razlikovati, kako između familija mikrokontrolera, tako i između varijanti unutar familije. Kao što je na samom početku već rečeno, memorijski prostori za program i podatke mogu biti razdvojeni (Harvard arhitektura, na primer 8051 i Microchip mikrokontroleri), ili jedinstveni (von Neumann arhitektura, na primer Motorola, Renesas, Zilog, Intel 80188 mikrokontroleri). Da bi se omogućilo efikasno manipulisanje memorijom, potrebno je definisati načine pristupa memoriji, odnosno memorijske modele mikrokontrolera, na nivou programskog jezika C, koji mogu zavisiti ne samo od mikrokontrolera, nego i od primenjenog C kompajlera. U slučaju jedinstvenog memorijskog prostora može postojati samo jedan ili više memorijskih modela, zavisno od mogućih načina adresiranja. Na primer, mikrokontroler Intel 80C188Ex (von Neumann arhitektura) ima istu memorijsku organizaciju kao i standardni mikroprocesor Intel 8088, odnosno koristi segmentiranu memoriju kapaciteta 1MB. Zavisno od veličine memorije predviđene za program i podatke, moguće su različite kombinacije pristupa memoriji, što određuje i različite memorijske modele (na primer, Borland TurboC kompajler koristi memorijske modele Tiny, Small, Medium, Compact, Large i Huge).

Page 33: Digitalni_Mikrokontroleri

Mikrokontroleri 32

Katedra za elektroniku

Primer 1: Mikrokontroler 8051, zbog razdvojene memorije za program i podatke, koristi nekoliko memorijskih modela za oba tipa memorije. Tako su, u Keil C kompajleru, definisani sledeći memorijski modeli: Programska memorija:

Small – Kompletan program je unutar bloka od 2 KB. Ovaj model je namenjen za vrlo male aplikacije i koristi isključivo 2-bajtne instrukcije skokova i poziva potprograma (AJMP i ACALL), umesto 3-bajtnih instrukcije, što znači i izvesnu uštedu u programskom memorijskom prostoru. Iako mala, ova ušteda može biti vrlo značajna, jer ovakvi programi mogu biti smešteni u mikrokontrolere sa programskom memorijom do 2KB.

Compact – Pojedinačne funkcije mogu zauzeti najviše do 2 KB, dok ceo program može zauzeti do 64 KB. Ovo znači da se svi skokovi i pozivi potprograma unutar jedne funkcije realizuju sa 2-bajtnim instrukcijama, dok se za pozive između funkcija koriste 3-bajtne instrukcije (LJMP i LCALL).

Large – svaka funkcija, kao i ceo program, može zauzeti prostor do 64 KB, a apsolutni skokovi i pozivi potprograma su 3-bajtni.

Memorija za podatke: Small – Svi podaci su u internoj memoriji. Kako se u istoj memoriji nalaze registarske banke i stek,

ovaj model podrazumeva i najmanje raspoloživog prostora za podatke. Compact – Svi podaci su u jednoj strani (Page), veličine 256 bajtova, u spoljašnjoj memoriji. Stek i

registarske banke ostaju u internoj memoriji, što znači da ima više prostora za podatke, ali je neophodna spoljašnja memorija. Mana ovog modela je u sporijem pristupu spoljašnjoj memoriji.

Large – Svi podaci su u spoljašnjoj memoriji (do 64 KB). Registarske banke i stek za adrese potprograma (instrukcije CALL i RET) su u internoj memoriji, ali se stek za prenos parametara i lokalne promenljive simulira u spoljašnjoj memoriji. Zbog čestog pristupa spoljašnjoj memoriji, ovaj memorijski model je i najsporiji.

Može se reći da kombinacija obe kategorije modela Small-Small predstavlja pravu suštinu namene mikrokontrolera, jer su tada (ako nema memorijski mapiranih periferija), svi portovi slobodni za korišćenje, a memorija je isključivo interna. Primer 2: Microchip mikrokontroleri srednje i niže klase (8-bitni), takođe sa razdvojenom memorijom za program i podatke, zbog ograničenog broja adresnih bita u instrukcijama, koriste princip podele memorije u banke. Hi-Tech C kompajler namenjen za ove mikrokontrolere nema mogućnost definisanja memorijskih modela. Za programsku memoriju, adresni prostor se tretira kao kontinualni prostor, pri čemu kompajler vodi računa o prelazima između pojedinih programskih banki, dok za podatke programer mora da koristi namenske ključne reči, kojima definiše u kojoj memorijskoj banci se podatak nalazi. Sa druge strane, Microchip C kompajler za familiju PIC18 mikrokontrolera, ima mogućnost definisanja memorijskih modela za program i podatke, zavisno da li je predviđeni memorijski prostor manji ili veći od 64 KB (ova familija ima mogućnost dodavanja spoljašnje memorije). Kada je u pitanju stek, mikrokontroleri (odnosno mikroprocesori) se mogu podeliti u dve kategorije. Prva kategorija podrazumeva varijantu steka manje veličine, prvenstveno namenjenog za instrukcije za rad sa potprogramima (CALL i RET – mašinski stek) i instrukcije za privremeno čuvanje manjeg broja podataka (PUSH i POP). U ovom slučaju, direktan pristup podacima na steku nije jednostavan i retko se koristi, ili je čak nemoguć (Microchip mikrokontroleri sa stekom dubine 2 ili 8 reči, nepristupačnim programeru). Druga kategorija podrazumeva veći stek, koji osim navedenih načina pristupa, ima i mogućnost pristupa podacima na steku registarskim adresiranjem uz dodatni ofset (registarsko relativno adresiranje). Primer ovog načina je mikroprocesor 8088/188 (odnosno mikrokontroler 80C188Ex), koji ima mogućnost pristupa steku preko namenskog registra BP, pri čemu se, u okviru instrukcije, može dodati i odgovarajući ofset, na primer:

MOV AX,[BP+10]

Kod ovakvih mikrokontrolera, lokalne promenljive, definisane u funkciji, čuvaju se na steku, uključivši i promenljive navedene u listi formalnih parametara, kao i neke pomoćne promenljive koje funkcija koristi, a nisu direktno vidljivi iz izvornog teksta funkcije. Ovaj način ne mora biti primenjen ukoliko sve promeljive mogu biti smeštene u registre mikroprocesora (registarske promenljive). Čuvanje lokalnih promenljivih na steku se često naziva stek metoda. Za razliku od ove metode, kod mikrokontrolera koji nemaju mogućnost registarskog relativnog adresiranja steka, koristi se metoda preklapanja (overlay metoda), u kojoj ključnu ulogu igra linker. Pri ovome je važna činjenica da lokalne

Page 34: Digitalni_Mikrokontroleri

Mikrokontroleri 33

Katedra za elektroniku

promenljive imaju važeću vrednost samo unutar funkcije, dok se pri izlasku iz funkcije ta vrednost gubi. Sa druge strane, ako dve funkcije nikad nisu istovremeno aktivne, tada njihove lokalne promenljive mogu da dele isti memorijski prostor. Radi razjašnjenja, funkcija je aktivna od momenta kada je pozvana, pa sve do momenta kada se izlazi iz funkcije, što znači da je aktivna i kada se iz te pozove neka druga funkcija. Na slici 36 su data tri primera poziva funkcija. U sva tri slučaja, funkcija main je uvek aktivna.

main()

func1()

func2()

func3()

main()

func1()

func2()

func3()

func4()

main()

func1() func2()

func4()

Primer a) Primer b) Primer c)

Slika 36: Primeri poziva funkcija u C jeziku

U daljoj analizi ova tri primera, pod pojmom ‘prostor’ podrazumevaće se adresni prostor potreban za smeštanje lokalnih promenljivih. Primer a) Funkcije func1 i func2 nikad nisu istovremeno aktivne, što znači da mogu deliti isti prostor. Fukcije func1 i func2 su istovremeno aktivne i one ne mogu deliti isti prostor, što važi i za funkcije func3 i func2. Primer b) Istovremeno su aktivne func1 i func2 u jednom, a func3 i func4 u drugom slučaju. To znači da funkcije func1 i func2 ne mogu koristiti isti prostor, kao ni funkcije func3 i func4. Međutim, ukupan prostor funkcija func1 i func2 može biti zajednički sa ukupnim prostorom funkcija func3 i func4. Primer c) Sve funkcije mogu biti istovremeno aktivne, zbog čega moraju koristiti zaseban prostor. Za optimalnu raspodelu memorijskog prostora potrebnog za lokalne promenljive, linker koristi stablo poziva funkcija (Call Tree, ili graf poziva – Call Graph), koje detaljno opisuje redosled poziva funkcija. Tokom ovog postupka, funkcije koje se nikad ne pozivaju (ne koriste se), neće se uzeti u obzir (Keil C kompajler će u tom slučaju izbaciti poruku upozorenja ‘UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS’). Primer raspodele memorije metodom preklapanja (overlay), za primer sa slike 36, dat je na slici 37.

Primer a) Primer b) Primer c)

main

func1 func3

func2

RAM

main

func1 func3

func2

RAM

func4

main

func1

func3

func2

RAM

Slika 37: Raspodela memorije lokalnih promenljivih metodom preklapanja

Verovatno najvažnija razlika između metode steka i metode preklapanja u određivanju memorijskog prostora za lokalne promenljive je u tome što metoda steka dozvoljava rekurzivne pozive funkcija, dok to u metodi preklapanja nije moguće. Razlog za to je što se, u metodi steka, svakim pozivom funkcije, rezerviše novi prostor na steku za lokalne promenljive, dok je u metodi preklapanja ovaj prostor uvek isti i novim pozivom već aktivne funkcije dotadašnji sadržaj memorije dodeljene lokalnim promenljivima se uništava novim sadržajem.

Page 35: Digitalni_Mikrokontroleri

Mikrokontroleri 34

Katedra za elektroniku

Način smeštanja lokalnih promenljivih zavisi od mikrokontrolera, kompajlera i memorijskih modela. Na primer, kod mikrokontrolera 8051, Hi-Tech kompajler u memorijskim modelima za podatke Large i Huge koristi stek metodu za lokalne promenljive (doduše, u pitanju je simulirani stek sa sporim pristupom), dok se u ostalim memorijskim modelima (Small i Medium) koristi metoda preklapanja. Keil C kompajler koristi memorijske modele Small (sve promenljive u internoj memoriji), Compact (sve promenljive u jednoj strani od 256 bajtova eksterne memorije) i Large (sve promenljive u eksternoj memoriji), pri čemu funkcija koristi stek metodu samo ako je definisana kao reentrant (funkcija koja može biti rekurzivna, odnosno više puta istovremeno pozvana). Ovakve funkcije u Small modelu treba izbegavati, jer je interna memorija mala i lako može doći do prekoračenja raspoloživog prostora. S obzirom na specifičnosti mikrokontrolera iz familije 8051 i ograničeni kapacitet interne memorije, u small memorijskom modelu treba imati u vidu sledeće:

1. Direktno adresiranje interne memorije omogućava pristup donjoj polovini internog RAM-a (128 bajtova) i SFR zoni (gornjih 128 bajtova), dok se indirektnim adresiranjem može pristupiti kompletnoj RAM zoni (svih 256 bajtova).

2. Za smeštanje promenljive tipa niza u verzijama mikrokontrolera sa 256 bajtova interne memorije, treba koristiti prefix idata, jer on obezbeđuje smeštanje ovakve promenljive u bilo koji deo interne memorije (svih 256 bajtova). Kako se nizovima svakako pristupa indirektno (preko indeksa), na ovaj način se ne povećava generisani kôd, niti se usporava rad, a moguće je maksimalno iskorišćenje interne memorije.

3. Pojedinačne konstante treba definisati direktivom #define, jer tada zauzimaju najmanje prostora (samo na mestu direktne primene).

4. Za konstantne nizove treba koristiti ključnu reč const i prefiks code, jer se tada ovakvi nizovi smeštaju u ROM memoriju, u kojoj ima znatno više mesta nego u internoj RAM memoriji.

3.2.1. Pitanja za proveru

1. Koji su mogući načini za smeštanje lokalnih promenljivih u memoriju? 2. Šta znači metoda preklapanja lokalnih promenljivih (overlay metoda)? 3. U Small memorijskom modelu (za podatke) Keil C kompajlera, koja je preporuka za smeštanje

nizova promenljivih i zašto?

3.3. Preprocesorske direktive C kompajlera Jedno veoma važno sredstvo programskog jezika C je makro preprocesor. On se koristi za definisanje konstanti, makro funkcija (funkcije koje ne zauzimaju memorijski prostor sve dok se ne primene) i uslovno prevođenje. Najvažnije ključne reči makro preprocesora su:

#include – Uključivanje spoljašnjeg fajla (najčešće H fajla) u tekući fajl. #define – Definisanje konstante, makro komande ili simbola za uslovno prevođenje. #undef – Ukidanje prethodno definisanog simbola. #if, #ifdef, #ifndef, #else, #elif i #endif – Direktive uslovnog prevođenja.

Postupak prevođenja izvornog teksta u objektni fajl izvodi se u dva koraka, preprocesiranje i prevođenje. Rezultat preprocesiranja može biti smešten u privremeni fajl ili u memoriju, zavisno od toga da li su preprocesor i prevodilac posebni programi ili integrisani u jedan program. U oba slučaja, za programera je preprocesiranje i prevođenje jedinstven postupak. Ulaz u preprocesor je jedan C fajl, koji može da sadrži ni jednu, jednu ili više navedenih direktiva. Rezultat preprocesiranja je ulazni C fajl, modifikovan tako da ne sardži više ni jednu ovakvu direktivu. Na primer, ako u C fajlu postoji direktiva #include, preprocesor izbacije ovu direktivu, a umesto nje ubacuje kompletan sadržaj fajla čiji je naziv dat kao parametar direktive, kao u primeru sa slike 38.

Page 36: Digitalni_Mikrokontroleri

Mikrokontroleri 35

Katedra za elektroniku

fajl: test.h

typedef unsigned char uchar;

fajl: test.c

#include “test.h” uchar var1; void main(void) while (1) var1++;

rezultat preprocesora (ulaz u prevodilac)

typedef unsigned char uchar; uchar var1; void main(void) while (1) var1++;

preprocesiranje

Slika 38: Preprocesiranje direktive #include

Ako unutar fajla (na slici 38 fajl test.h), koji se uključuje u osnovni C fajl, takođe postoji direktiva #include (uključivanje fajlova u više nivoa po dubini), i ona će takođe biti zamenjena, a postupak zamene se nastavlja sve dok se ne eliminiše i poslednja #include direktiva. Iz ovoga se može zaključiti da je moguće u jedan C fajl uključiti drugi, takođe C fajl. To svakako jeste moguće, ali predstavlja vrlo lošu praksu, jer tada C fajl koji se uključuje direktivom #include, ne može biti uključen u C projekat, odnosno ne može biti njegov deo, što projekat čini nepreglednim, jer više nije lako uočljivo koji C fajlovi čine projekat. Direktiva #define se koristi za definisanje makro funkcija, jednostavnih i kompleksnih. Jednostavna makro funkcija ima format

#define Makro_Naziv Makro_Sadržaj

Ovde se pod oznakom Makro_Naziv podrazumeva vezani niz alfa-numeričkih karaktera (uključivši i znak “_”) do prvog razmaka, dok se pod Makro_Sadržaj podrazumeva delimično proizvoljan niz karaktera, uključivši i razmake. Definicija direktive #define se završava krajem linije. Ako je potrebno produžiti opis makro funkcije i u narednu liniju (važi za sve linije u programu), tada se koristi simbol ‘\’ na kraju linije, kao oznaka da se definicija makro funkcije nastavlja u narednoj liniji. U toku rada, preprocesor pretražuje ceo tekst i na svim mestima na kojima pronađe isti niz karaktera kao što je i Makro_Naziv, ovaj niz karaktera se zamenjuje nizom karaktera Makro_Sadržaj. Na primer:

#define kristal_1 12 * 1000 Makro ili #define kristal_1 12 * \

1000 Primena a = kristal_1; Rezultat a = 12 * 1000;

U ovoj tabeli red Makro označava definiciju makro simbola, Primena je mesto u tekstu gde je makro simbol primenjen, a Rezultat je tekst koji se dobije nakon preprocesiranja (razvijanje makroa). Ako Makro_Sadržaj ne postoji, tada Makro_Naziv nema vrednost, ali se smatra definisanim. Kompleksna makro funkcija podrazumeva i postojanje argumenata u okviru definisanog simbola:

Makro #define Oduzmi(a,b) a-b Primena x = Oduzmi(y,17); Rezultat x = y-17;

I u ovom slučaju preprocesor izršava samo zamenu u izvornom tekstu. Kako argumenti a i b u makro funkciji Oduzmi mogu biti bilo šta, potrebno je obezbediti tačnu interpretaciju makro funkcije. Ako se ovako definisana makro funkcija Oduzmi primeni na tekst, dobiće se

Page 37: Digitalni_Mikrokontroleri

Mikrokontroleri 36

Katedra za elektroniku

Makro #define Oduzmi(a,b) a-b Primena x = 2 * Oduzmi(y,17); Rezultat x = 2 * y-17;

što je pogrešno, jer se će se prvo izvršiti množenje (zbog prioriteta operacija), a zatim sabiranje, što nije bila namera osnovnog izraza. Zbog toga je potrebno modifikovati definiciju makro funkcije Oduzmi:

Makro #define Oduzmi(a,b) (a-b) Primena x = 2 * Oduzmi(y,17); Rezultat x = 2 * (y-17);

što odgovara prvobitnom izrazu. Kako argumenti makro funkcije mogu biti proizvoljni, da bi se sprečile greške ovakvog tipa, veoma često se koriste dodatne zagrade za tačno određivanje prioriteta pri razvijanju makro funkcija. Ovo važi i za jednostavne makro funkcije (odnosno makro konstante), na primer:

Makro #define Kristal 18432 #define Baud_Rate 9600 #define Baud_Gen (((((Kristal) * 1000) /12) /32) / (Baud_Rate))

Primena TH1 = Baud_Gen; Rezultat TH1 = (((((18432) * 1000) /12) /32) / (9600));

U ovom slučaju, neće doći do greške čak i u slučaju da su prva dva parametra definisana na sledeći način:

Makro #define Kristal 18000 + 432 #define Baud_Rate 96 * 100 #define Baud_Gen (((((Kristal) * 1000) /12) /32) / (Baud_Rate))

Primena TH1 = Baud_Gen; Rezultat TH1 = (((((18000 + 432) * 1000) /12) /32) / (96 * 100));

što se inače može smatrati lošim načinom definisanja konstante (ne postoje zagrade za Kristal i Baud_Rate). U slučaju kompleksne funkcije, u prethodnom primeru makro funkcije Oduzmi, i pored dodatih zagrada, postoji mogućnost greške:

Makro #define Oduzmi(a,b) (a-b) Primena x = Oduzmi(y,-z); Rezultat x = y--z;

Prvi deo izraza će biti tretiran kao post-dekrement operacija y--, dok će promenljiva z biti tretirana kao greška (ekstra simbol). Međutim, u sledećem primeru kompajler neće prijaviti nikakvu grešku, ako je i parametar z iz gornjeg primera definisan kao makro, u ovom slučaju bez MakroSadržaja:

Makro #define Oduzmi(a,b) (a-b) #define Ofset

Primena x = Oduzmi(y,-Ofset); Rezultat x = y--;

Zbog toga je potrebno korigovati definiciju makro funkcije Oduzmi:

Makro #define Oduzmi(a,b) ((a)-(b)) Primena x = Oduzmi(y,-z); Rezultat x = (y)-(-z);

Na ovaj način je dobijena makro funkcija sa strožijom proverom, odnosno sa manjom verovatnoćom greške. Makro funkcija može da sadrži niz komandi koje čine jedinstven blok. Kako u trenutku pisanja makro funkcije nije poznat način na koji će se ona koristiti, potrebno je obezbediti da ovaj niz komandi ostane jedinstven u svim slučajevima. Na primer:

#define Tastatura() Ucitaj_Tastere(); \ Testiraj_Tastere(); \

Page 38: Digitalni_Mikrokontroleri

Mikrokontroleri 37

Katedra za elektroniku

Generisi_Kod_Tastera(); Zagrade uz reč Tastatura nisu potrebne i njihova svrha je samo u podsećanju programera da je u pitanju funkcija, bez obzira što to nije standardna, nego makro funkcija. Ako se ovakva makro funkcija primeni unutar petlje: while (uslov) Tastatura(); sve funkcije unutar bloka uokvirenog velikim zagradama će se izvršavati kao deo petlje While. Međutim, ako se ova makro funkcija definiše bez velikih zagrada (i bez odgovarajućeg simbola tačka-zarez):

#define Tastatura() Ucitaj_Tastere(); \ Testiraj_Tastere(); \ Generisi_Kod_Tastera(); tada će se unutar petlje izvršavati samo prva funkcija Ucitaj_Tastere, dok će preostale dve funkcije biti izvan petlje, jer ne postoji grupisanje ovih funkcija velikim zagradama. Vrlo često, zbog nepotpune kompatibilnosti različitih C kompajlera, ubacuje se jedno-prolazna petlja Do-While:

#define Tastatura() do Ucitaj_Tastere(); \ Testiraj_Tastere(); \ Generisi_Kod_Tastera(); while (0) Ovakva konstrukcija obezbeđuje pravilno tumačenje definicije makro funkcije u svim mogućim primenama. Zbog netačnog uslova u testu while (0 => False) postoji samo jedan prolaz kroz petlju, a prilikom prevođenja, zbog fiksnog uslova, petlja se odbacuje, odnosno ne proizvodi nikakav mašinski kôd. Sa druge strane, kompletna struktura od ključne reči do, sve do testa while (0), se ponaša jedinstveno i nedeljivo, što i jeste cilj ove konstrukcije. U tekstu, iza naziva makro funkcije Tastatura(), obavezan je simbol tačka-zarez, kao kod svih ostalih funkcija. Nakon zamene makro funkcije, simbol tačka-zarez dolazi iza while(0), što tačno odgovara sintaksi C jezika. Makro definicija koristi simbol ‘#’ (hash – ‘taraba’) za specijalne svrhe, što znači da unutar makro definicije ne može da postoji ni jedna preprocesorska direktiva. U Makro_Sadržaju, jedan hash simbol označava konverziju argumenta u string:

Makro #define NapraviString(a) #a Primena x = NapraviString(Tekst za probu); Rezultat x = “Tekst za probu”;

Dvostruki hash simbol se koristi za spajanje delova teksta u jednu reč:

Makro #define TajmerReg(tmr_deo,tmr_num) T##tmr_deo##tmr_num Primena x = TajmerReg(H,1); Rezultat x = TH1;

Jedan interesantan primer primene dvostrukog hash simbola je makro koji omogućava unos binarnih brojeva kod kompajlera koji tu opciju nemaju:

Makro #define HEX__(n) 0x##n##LU // 8-bitni binarni broj #define B8__(x) ((x&0x0000000FLU)?1:0) \ +((x&0x000000F0LU)?2:0) \ +((x&0x00000F00LU)?4:0) \ +((x&0x0000F000LU)?8:0) \ +((x&0x000F0000LU)?16:0) \ +((x&0x00F00000LU)?32:0) \ +((x&0x0F000000LU)?64:0) \ +((x&0xF0000000LU)?128:0) #define B8(d) ((unsigned char)B8__(HEX__(d))) // 16-bitni binarni broj #define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) \ + B8(dlsb)) // 32-bitni binarni broj #define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) \ + ((unsigned long)B8(db2)<<16) \ + ((unsigned long)B8(db3)<<8) \ + B8(dlsb))

Primena x = B16(10010110,01101001);

Page 39: Digitalni_Mikrokontroleri

Mikrokontroleri 38

Katedra za elektroniku

U okviru makro funkcije mogu se koristiti i serije izraza razdvojenih zarezom (comma expression), slično inicijalizacionom delu for petlje. Naime, u nizu izraza razdvojenih zarezom, svi izrazi se pojedinačno izračunavaju (sa leva na desno), pri čemu se svi rezultati, izuzev poslednjeg, odbacuju. Na primer, izraz x = ((a=b),(c=c+13),(d)); ima isti efekat kao a = b; c = c + 13; x = d; Ovo je moguće zbog toga, što i pridruživanje vrednosti promenljivoj ima vrednost (rezultat), i to onu koja je toj promenljivoj i pridružena: a = b = c = 0; sa redosledom izvršavanja a = (b = (c = 0)); Korišćenjem izraza sa zarezima moguće je formirati složenu makro komandu, koja poziva više funkcija i pri tome vraća odgovarajući rezultat. U narednoj tabeli je prikazan primer rada sa serijskim portom, pri čemu makro funkcija GetComChar poziva tri funkcije. Sve tri funkcije vraćaju neku vrednost, ali se prve dve vrednosti odbacuju (pravilo izraza sa zarezima), iako se funkcije pozivaju, dok se prihvata rezultat dobijen poslednjom funkcijom. Osim toga, dobijeni rezultat se pridružuje promenljivoj rec_char (primljeni karakter), a zatim se ista vrednost dodaje promenljivoj za kontrolni zbir sum. U dva primera primene makro funkcije, u drugom primeru se forsira prioritet, što nije neophodno, ali je ovde redosled izvršavanja očigledniji.

Makro #define GetComChar(s) (ComInit(),ComSend(s),ComGet()) Primena ili

char ComInit(void); char ComSend(char *s); char ComGet(void); ..... sum += rec_char = GetComChar(“Demonstracija”); ..... sum += (rec_char = GetComChar(“Demonstracija”));

Makro simboli definisani direktivom #define mogu se koristiti pri uslovnom prevođenju. Za ove svrhe koriste se dodatne direktive:

#if uslov – Ako je logički uslov zadovoljen (tačan), u izlazni tekst se uključuje blok (deo izvornog teksta) do odgovarajuće komande #elif, #else ili #endif.

#ifdef Makro_Naziv – Ako je makro Makro_Naziv definisan direktivom #define, u izlazni tekst se uključuje blok (deo izvornog teksta) do odgovarajuće komande #elif, #else ili #endif.

#ifndef Makro_Naziv – Ako makro Makro_Naziv nije definisan direktivom #define, u izlazni tekst se uključuje blok (deo izvornog teksta) do odgovarajuće komande #elif, #else ili #endif.

#elif uslov – Koristi se uz direktivu #if uslov. Ako uslov u #if uslov nije zadovoljen (tačan), u izlazni tekst se uključuje blok (deo izvornog teksta) do odgovarajuće komande #elif, #else ili #endif.

#else – U izlazni tekst se uključuje blok (deo izvornog teksta) do odgovarajuće komande #elif, #else ili #endif ako: Uslov u odgovarajućoj prethodnoj direktivi #if uslov ili #elif uslov nije zadovoljen; Makro_Naziv u odgovarajućoj prethodnoj direktivi #ifdef nije definisan; Makro_Naziv u odgovarajućoj prethodnoj direktivi #ifndef jeste definisan.

#endif – Zaključuje prethodno započetu direktivu za uslovno prevođenje #if, #ifdef ili #ifndef. Uslov je logički izraz koji se može razrešiti tokom prevođenja, što znači da može sadržavati samo konstante, makro funkcije i integralne logičko/aritmetičke operacije. Naredna tabela prikazuje primere primene osnovnih direktiva za uslovno prevođenje:

Primer 1: #if Primer 2: #ifdef Primer 3: #ifndef Primer 4: #if uslov1 ... #elif uslov2 ... #elif uslov3 ... #else ... #endif

#ifdef Makro_Naziv ... #else ... #endif

#ifndef Makro_Naziv... #else ... #endif

+- #ifdef Makro1 | +- #ifdef Makro2 | | ... | +> #endif +- #else | +- #ifdef Makro3 | | ... | +> #endif +> #endif

Page 40: Digitalni_Mikrokontroleri

Mikrokontroleri 39

Katedra za elektroniku

Prva tri primera prikazuju primene tri osnovna testa za uslovno prevođenje, dok je u četvrtom primeru prikazano uslovno prevođenje u dva nivoa. Zavisno od kompajlera, odnosno preprocesora, komentari unutar preprocesorskih direktiva mogu biti eliminisani pre ili posle razvoja makro komandi. Zbog toga se, u direktivi #define, preporučuje izbegavanje komentara, ili korišćenje višelinijskih komentara (uokvirenih kombinacijom /*...*/). Na primer, ako se komentari ne eliminišu pre razvijanja makro komandi, tada će se u sledećem primeru pojaviti greška,

Makro #define kristal 24 // frekvencija u MHz Primena x = kristal / 12;

y = 1; Rezultat x = 24 // frekvencija u MHz / 12;

y = 1;

jer je komentar iz makro konstante u ulaznom tekstu eliminisao deljenje i simbol tačka-zarez. U ovom slučaju, prevodilac će prijaviti grešku, jer se nakon broja 24 pojavljuje simbol y, odnosno novo pridruživanje, bez simbola tačka-zarez ili nekog simbola aritmetičke operacije. Međutim, u slučaju višelinijskog komentara ovakva greška se ne može dogoditi:

Makro #define kristal 24 /* frekvencija u MHz */ Primena x = kristal / 12;

y = 1; Rezultat x = 24 /* frekvencija u MHz */ / 12;

y = 1;

3.3.1. Pitanja za proveru

1. Za šta se koristi direktiva #define? 2. Šta je uslovno prevođenje i koje se direktive koriste za te svrhe? 3. Da li se preprocesorske direktive razrešavaju tokom preprocesiranja ulaznog teksta, ili tek

kasnije, u toku izvođenja programa na mikrokontroleru? 4. Da li makro funkcija zauzima memorijski prostor ako je definisana, ali nigde nije primenjena? 5. Da li se makro funkcija sme definisati u H fajlu? Obrazložiti.

3.4. Strukture i unije Osim osnovnih tipova promenljivih, programski jezik C, kao i većina drugih programskih jezika, ima mogućnost definisanja novih, složenih tipova. Za grupisanje više promenljivih, istog ili različitog tipa, u jedinstvenu celinu, koriste se strukture i unije, slika 39.

Deklaracija tipa typedef struct lokalno_ime tip1 var1; tip2 var2; ... tipN varN; ime_tipa;

typedef union lokalno_ime tip1 var1; tip2 var2; ... tipN varN; ime_tipa;

Definicija promenljive

ime_tipa x; ime_tipa *y; struct lokalno_ime z;

ime_tipa x; ime_tipa *y; union lokalno_ime z;

Primena x.var1 = 13; y->var2 = 22;

x.var = 13; y->var2 = 22;

Slika 39: Definisanje i primena struktura i unija

Page 41: Digitalni_Mikrokontroleri

Mikrokontroleri 40

Katedra za elektroniku

Svaki od navedenih tipova promenljivih unutar strukture/unije može biti osnovni, izvedeni ili složen tip promenljive, što podrazumeva i strukture i unije. Iako je način definisanja isti, strukture i unije su bitno različite. Sve promenljive unutar strukture zauzimaju poseban memorijski prostor, dok se u uniji promenljive preklapaju, odnosno zauzimaju isti memorijski prostor. Ukupna veličina strukture odgovara zbiru veličina svih pojedinačnih elemenata, dok ukupna veličina unije odgovara veličini najvećeg elementa. Može se reći da se elementi strukture u memoriji ređaju jedan pored drugog, dok se elementi unije ređaju jedan ispod, odnosno preko drugog, kao na slici 40:

var4

var3

var2

var1

var4

var3

var2

var1

a) Struktura (struct) b) Unija (union) Slika 40: Memorijska organizacija struktura i unija

Na slici 39, u polju primena, vide se načini pristupa elementu strukture/unije u slučaju kada je promenljiva definisana direktno (x.var1), ili preko pokazivača (y–>var1). U strukturi, promena jedne od promenljivih nema nikakvog uticaja na ostale promenljive. U uniji, promena bilo koje promenljive može da utiče na ostale promenljive koje imaju isti memorijski prostor kao i deo promenljive koji se menja. U primeru na slici 41 prikazane su dve unije, obe veličine četiri bajta:

Primer a) Primer b) Deklaracija tipa i definicija promenljive

typedef union long lg; char ch[4]; Q_BYTE; Q_BYTE qb;

typedef union int it; char ch[4]; Q_BYTE; Q_BYTE qb;

Primena qb.ch[3] = 0; qb.ch[3] = 0;

Slika 41: Primer primene unije U primeru 41a), oba elementa unije su iste veličine, četiri bajta. Ako se promeni bilo koji element niza ch, menja se i vrednost drugog elementa unije, lg. Međutim, u primeru 41b), elementi unije nisu iste veličine, jer je element it dvobajtni. Ako se promeni sadržaj poslednja dva bajta elementa (niza) ch (ch[2] ili ch[3]), element it se neće promeniti, jer deli isti prostor sa prva dva elementa niza ch (ch[0] i ch[1]). Primer unije, kao na slici 41a, može da se iskoristi radi pristupa pojedinačnim bajtovima višebajtne promenljive. U ovom slučaju, osnovna promenljiva je lg, tipa long (četvorobajtna), dok je promenljiva ch[4] niz od četiri pojedinačna bajta. Pristupom promenljivoj ch, mogu se menjati pojedinačni bajtovi promenljive lg. Međutim, za pravilno korišćenje ovakvog pristupa elementima unije, potrebno je znati način memorisanja višebajtnih promenljivih, odnosno kako su pojedinačni bajtovi smešteni u memoriji. Koriste se dva načina, Big Endian i Little Endian. U prvom načinu (Big Endian), na najnižu adresu (osnovnu adresu promenljive) smešta se bajt najveće težine (MSB – Most Significant Byte), dok se u drugom načinu (Little Endian) prvo smešta bajt najmanje težine (LSB – Least Significant Byte), kao na slici 42.

Adresa unije = N Primer unije sa slike 41a Promenljiva lg, tipa long (4 bajta) Promenljiva ch[4], tipa char

Adresa elementa: Big Endian Little Endian N + 3 1. bajt (LSB) 4. bajt (MSB) ch[3] N + 2 2. bajt 3. bajt ch[2] N + 1 3. bajt 2. bajt ch[1] N 4. bajt (MSB) 1. bajt (LSB) ch[0]

Slika 42: Način memorisanja višebajtnih promenljivih Koji se od ova dva načina koristi, zavisi od C kompajlera, ali i od toga da li, u paketu sa C kompajlerom, postoji i asembler (prevodilac), kao i koliko je taj asembler kompatibilan sa originalnim asemblerom

Page 42: Digitalni_Mikrokontroleri

Mikrokontroleri 41

Katedra za elektroniku

(proizvođača mikrokontrolera), jer se i u asembleru pojavljuje potreba za višebajtnom organizacijom i kod 8-bitnih mikrokontrolera (na primer komande DW – Define Word i DD – Define Double Word). Očigledno, zbog različitih načina pakovanja višebajtnih varijabli u memoriji, primena unije radi parcijalnog pristupa višebajtnoj promenljivoj nije preporučljiva, ako se zahteva prenosivost (portabilnost) izvornog kôda. U slučajevima kada je ovakav pristup potreban, a zahteva se i prenosivost kôda, mogu se koristiti direktive za uslovno prevođenje, tako da izvorni kôd podržava obe varijante memorijske organizacije, pri čemu se odgovarajuća varijanta bira modifikacijom odgovarajuće direktive #define. Na slici 43 su prikazana dva načina za rotiranje jednog bajta u levo. Primer 43a) prikazuje rotaciju korišćenjem unije, za oba načina organizacije višebajtnih promenljivih, dok je u primeru 43b) iskorišćeno pomeranje sa uslovom (testiranje najvišeg bita). Primer a) Primer b)

Deklaracije #define uchar unsigned char #define uint unsigned int typedef union uint w; uchar ch[2]; D_Byte;

#define uchar unsigned char

Funkcija rotacije

uchar lrot(uchar a) D_BYTE db; db.ch[0] = a; db.ch[1] = a; db.w <<= 1; #ifdef BIG_ENDIAN return db.ch[0]; #else return db.ch[1]; #endif

uchar lrot(uchar a) return (a & 0x80) ? (a<<1) | 1: (a<<1); // ili sledeća alternativa: uchar lrot2(uchar a) if (a & 0x80) return (a << 1) | 1; else return a << 1;

Slika 43: Rotacija jednog bajta u levo Osnovna razlika između ova dva primera je u tome, što se u primeru sa unijom ne koristi testiranje (i mašinski skok kao posledica komande testa). U varijanti sa testom, prikazane su dve funkcije, koje su skoro identične, iako funkcija lrot2 može biti za nijansu efikasnija od funkcije lrot. U konkretnom slučaju, primer sa unijom je znatno lošiji po efikasnosti od primera sa testom, a ovde je naveden iz edukativnih razloga. Međutim, ako se unija primeni za rotaciju 24-bitne reči, tada ova metoda može biti znatno efikasnija od metode sa testom. Na primer, u Keil C kompajleru za 8051 (koji koristi Big Endian), rotiranje 24-bitne reči pomoću unije (slično primeru 43a) je oko dva puta kraće nego rotiranje sa testom (na bazi primera 43b). Strukture i unije se koriste vrlo često i u razne svrhe. Kada su mikrokontroleri u pitanju, posebno je značajna primena unija kao metod za izbegavanje dinamičkog zauzimanja i oslobađanja memorije. Naime, funkcije za rad sa dinamičkom memorijom (malloc, realloc, free i slično) zahtevaju dosta dodatnog kôda i RAM memorije, ali i procesorskog vremena. Kako se, u mikrokontrolerskim sistemima, u većini slučajeva radi sa samo jednom aplikacijom, pri čemu su sve potrebe za memorijom unapred poznate, umesto dinamičke manipulacije sa memorijom moguće je korišćenje unija, čiji elementi odgovaraju složenim strukturama podataka za različite korake u programu, koji bi se inače dinamički zauzimali i oslobađali. Ovaj način rada sa memorijom je vrlo sličan overlay metodi za lokalne promenljive, ali ovaj put organizacija memorije je dužnost programera, a ne C prevodioca (tj. linkera). Glavna mana primene unija kao zamena dinamičkoj memoriji je u nešto većoj odgovornosti programera koji formira uniju (ili unije), međutim, to je donekle i prednost, je tada moguće bolje sagledati kakve su stvarne potrebe za RAM memorijom i da li primenjeni mikrokontroler raspolaže dovoljnim memorijskim kapacitetima. Prednost je svakako u uštedi ROM i RAM memorije, jer se tada funkcije i dodatne promenljive za rad sa dinamičkom memorijom ne koriste, dok se dobija na brzini rada, jer su svi memorijski blokovi unapred definisani i spremni za korišćenje. U primeru na slici 44 prikazan je jedan deo definicija struktura i unija, primenjenjih na mikrokontrolerskom sistemu koji preko serijskog porta prima i šalje komande sa podacima.

Page 43: Digitalni_Mikrokontroleri

Mikrokontroleri 42

Katedra za elektroniku

typedef struct char digital_out[2]; char check_sum;

char end_char; RX_CMD1; typedef struct

char analog_out[8]; char check_sum; char end_char; RX_CMD2; typedef struct

char relay; char check_sum; char end_char; RX_CMD3; typedef struct char file_num; unsigned int file_size; unsigned int file_check_sum; char check_sum; char end_char; RX_CMD4; typdef union RX_CMD1 rx_command1; RX_CMD2 rx_command2; RX_CMD3 rx_command3; RX_CMD4 rx_command4; RX_PACKET; typedef struct char start_char; unsigned char packet_length; char command; RX_PACKET rx_buf; RX_STRUCT; typedef union RX_STRUCT cmd_buffer; unsigned char byte_buffer[sizeof(RX_STRUCT)]; RX_BUFFER;

Slika 44: Primer primene struktura i unija kao zamena za rad sa dinamičkom memorijom

Nakon prijema komunikacionog paketa, primljeni podaci se tretiraju na različite načine, zavisno od primljene komande. Za svaku komandu koristi se odgovarajuća struktura, a sve komande, sa svojim podacima, dele isti prostor, zahvaljujući organizovanju ovih struktura u uniju. Na sličan način su organizovani i podaci pri slanju, koji ovde nisu prikazani. Alternativa ovakvom organizovanju komandi i podataka je dinamičko zauzimanje memorije u toku samog prijema paketa. Naime, nakon prijema komande dinamički se zauzme memorija za odgovarajuću strukturu. Nakon obrade komande, zauzeta memorija se oslobodi. Imajući u vidu potencijalne probleme kada je u pitanju rad sa dinamičkom memorijom, C prevodioci ove probleme rešavaju na različite načine. Tako na primer, Keil C kompajler za 8051 može da radi sa dinamičkom memorijom samu u eksternom adresnom prostoru (large memorijski model), dok u small memorijskom modelu dinamičku memoriju nije moguće koristiti.

3.4.1. Pitanja za proveru 1. U čemu je osnovna razlika između struktura i unija u programskom jeziku C? 2. Šta znače Big Endian i Little Endian?

3.5. Memorijsko mapiranje periferijskih jedinica Veoma često, i pored velikog broja familija i varijanti mikrokontrolera, javlja se potreba za nekim dodatnim hardverskim kolima, koja nisu sadržana u mikrokontroleru. Spajanje ovih kola na mikrokontroler se uglavnom vrši paralelno ili serijski. U slučaju paralelne veze, koja će se ovde razmatrati, periferijske jedinice su najčešće organizovane po principu spajanja memorije, što znači da koriste magistralu podataka i

Page 44: Digitalni_Mikrokontroleri

Mikrokontroleri 43

Katedra za elektroniku

signale za selektovanje, čitanje i pisanje. Eventualno, složenija kola mogu imati i dodatne adresne linije. Pri povezivanju ovih kola mogu se koristiti dva osnovna načina, standardno memorijsko mapiranje ili emuliranje potrebnih signala. U prvom slučaju, periferijsko kolo se povezuje isto kao i spoljašnja memorija (RAM), pri čemu mogu biti spojena oba signala pristupa (čitanje i pisanje), ili samo jedan od njih. Na primer, za ulazni bafer dovoljni su signali za selektovanje adrese i čitanje, dok je za izlazni registar (leč) dovoljno iskoristiti selekcioni signal i signal upisa. Glavna mana ovakvog spajanja je u tome što sve spoljašnje linije koje se koriste za adrese, podatke i kontrolne signale moraju biti potpuno funkcionalni i ne mogu se koristiti kao ulazno/izlazni portovi (deljene funkcije portova). U slučaju emulacije kontrolnih signala, iz grupe ulazno/izlaznih portova se zauzima samo minimalan broj fizičkih linija, tako da za druge svrhe preostaje više slobodnih ulazno/izlaznih linija. Mana emulacije je u tome, što se svi signali generišu softverski, što znači i manju brzinu pristupa periferijskom kolu, nešto više potrebnog mašinskog kôda i dodatno procesorsko vreme. Periferijsko kolo može imati mogućnosti čitanja (read-only, izlazni registar), upisa (write-only, ulazni bafer) ili obe (read-write). Pri tome, treća varijanta (read-write) podrazumeva dva tipa periferijskih kola, sa spojenim i razdvojenim ulazima i izlazima. Kola koja se mogu samo čitati ignorišu svaki pokušaj upisa podatka i ne zahtevaju nikakvu posebnu softversku podršku osim jednostavnog čitanja stanja. Kola koja imaju samo mogućnost upisa, zahtevaju dodatnu promeljivu u internoj memoriji za čuvanje tekućeg stanja. Pri tome sve modifikacije stanja se izvršavaju na ovoj slici, nakon čega se nova slika stanja upisuje u izlazno kolo. Kola koja podržavaju čitanje i upis mogu se tretirati kao standardna RAM memorija. Ovakva kola dozvoljavaju komande tipa pročitaj-promeni-upiši (RMW – Read-Modify-Write). Četvrta kategorija periferijskih kola nastaje spajanjem kola koja podržavaju samo čitanje i onih koja podržavaju samo upis. Ovakva kola nastaju kada se na istu adresu (isti signal adresnog dekodera) postavi jedno ulazno (koristi samo signal čitanja) i jedno izlazno kolo (koristi samo signal upisa). Iako se na podešenoj adresi mogu izvršiti obe operacije, čitanje i upis, primena RMW komandi ovde nije moguća, jer su ulazi nezavisni od izlaza. Primeri sve četiri kategorije ulaznih i izlaznih kola prikazani su na slici 45 (D.B. označava magistralu podataka).

izlazni registar

ulazni bafer

D.B.

upis izlaz

D.B.

čitanje ulaz

izlazni registar D.B.

upis izlaz

ulazni bafer

izlazni registar D.B.

upis izlaz

ulazni bafer

ulazčitanje čitanjea) Ulazno kolo

c) Memorijsko kolo b) Izlazno kolo d) Ulazno/izlazno kolo

* D.B. – magistrala podataka

Slika 45: Četiri kategorije ulaznih i izlaznih periferijskih kola

Od četiri navedene kategorije ulaznih i izlaznih kola, jedino kategorija sa slike 45c podržava RMW komande, pod uslovom da čitanje i upis koriste istu adresu. Komande tipa RMW se mogu podeliti u dve kategorije, komande C jezika i mašinske instrukcije. Uobičajeno je za C kompajler da C varijantu RMW komande direktno prevodi u mašinsku RMW instrukciju, na svim mestima gde je to moguće, pogotovo kada se radi sa internim registrima mikrokontrolera. Kako su ove instrukcije od velikog značaja kada se radi sa registrima mikrokontrolera, programer mora biti upoznat sa mašinskim RMW instrukcijama, kao i koje se C RMW naredbe direktno prevode u odgovarajuće mašinske RMW instrukcije. Neke od C RMW naredbi i njihovi ekvivalenti u asembleru prikazani su na slici 46.

Keil C kompajler za 8051 odgovarajuća mašinska instrukcija P1++; inc P1 P1--; dec P1 P1 &= vrednost; anl P1,vrednost P1 |= vrednost; orl P1,vrednost P1 ^= vrednost; xrl P1,vrednost ET1 = 1; setb ET1 ET1 = 0; clr ET1 ET1 = !ЕТ1; cpl ET1

Slika 46: Parovi C i mašinske RMW naredbe

Page 45: Digitalni_Mikrokontroleri

Mikrokontroleri 44

Katedra za elektroniku

Naredba tipa RMW u C jeziku ne znači automatski i RMW mašinsku instrukciju. Na primer, C naredbe x += y; x -= y; x *= y; x /= y; x %= y; x <<= y; x >>= y; nemaju svoje asemblerske ekvivalente, jer se u svim ovim naredbama koristi akumulator kao glavni registar. Za memorijsko mapiranje periferijskih kola, u Keil C kompajleru se koristi promenljiva na fiksiranoj adresi, u spoljašnjoj memoriji, na primer:

char xdata var _at_ adresa;

Ovde su: char – tip promenljive xdata – oznaka za spoljašnju memoriju (RAM) var – naziv promenljive _at_ – ključna reč (važi za Keil C kompajler) koja označava promenljivu na apsolutnoj adresi adresa – adresa promenljive u spoljašnjoj memoriji

Na primer: unsigned char xdata Red_Led _at_ 0x8000; unsigned char xdata Led_Switch _at_ 0x8003; Kada se koriste periferijska kola sa razdvojenim ulazima i izlazima, kao na slici 45d, ako se memorijski mapiraju ulazno i izlazno kolo sa različitim nazivima, ali na istoj adresi, prevodilac može prijaviti upozorenje ili grešku. Da bi se to izbeglo, može se koristiti sinomin definisan direktivom #define:

unsigned char xdata Red_Led _at_ 0x8000; #define Led_Switch Red_Led

Nakon ovoga, svaki upis se vrši u promenljivu Red_Led, a čitanje iz promenljive Led_Switch (koja u stvari ne postoji, jer se umesto nje takođe čita Red_Led). Stanje hardverskog kola, prvenstveno ulaznog, zavisi od spoljašnjih faktora. Kako se u C jeziku sve promenljive tretiraju kao memorijske vrednosti, dva uzastopna upisa sa različitim vrednostima u istu promeljivu mogu biti optimizovana tako da u konačnom kodu ostane samo poslednji od ova dva upisa. Slično, jednom pročitana vrednost promenljive može biti zadržana u internom registru ako do narednog čitanja nema ni jednog upisa, tako da se od dva čitanja promenljive u finalnom kodu zadrži samo prvo čitanje. Ukratko, u fazi optimizacije mogu biti eliminisane redundantne komande čitanja ili upisa promenljive. Pri radu sa hardverskim kolima ovo nije prihvatljivo, jer između dva čitanja može doći do promene stanja spoljašnjih signala. Da bi se obezbedio pravilan pristup memorijski mapiranim periferijskim jedinicama, koristi se ključna reč volatile:

unsigned char volatile xdata Red_Led _at_ 0x8000;

Ova ključna reč znači da se navedenoj promenljivoj ne može garantovati vrednost između dva pristupa. Kao posledica ovoga, prevodilac će isključiti optimizaciju u pristupu ovoj promenljivoj, čime će se obezbediti izvršenje svih navedenih naredbi čitanja i upisa, tačno onako kako je to navedeno u izvornom tekstu. Iz navedenih razloga za sve memorijski mapirana periferijska kola treba koristiti reč volatile. U nekim mikrokontrolerskim sistemima koristi se spoljašnja memorija sa baterijskim napajanjem. U ovakvoj memoriji se čuvaju podaci koji se menjaju u toku rada, ali moraju zadržati vrednost i nakon isključenja napajanja. Kako se u inicijalizacionom kôdu C programa (od reset vektora do funkcije main) briše kompletna RAM memorija, neki kompajleri imaju dodatnu mogućnost zaštite memorije od brisanja, pomoću ključne reči persistent. Podaci definisani na ovaj način se ne brišu u toku inicijalizacije, a rad sa njima podržan je dodatnim komandama za održavanje i proveru integriteta sadržaja. Primer kompajlera koji podržava ovaj način rada je Hitech C kompajler za 8051 i Microchip mikrokontrolere.

Page 46: Digitalni_Mikrokontroleri

Mikrokontroleri 45

Katedra za elektroniku

3.5.1. Pitanja za proveru 1. Da li se dinamičko zauzimanje i oslobađanje memorije može izbeći? Ako ne, zašto, a ako da, kako. 2. Koji tipovi periferijskih jedinica se mogu memorijski mapirati u adresni prostor mikrokontrolera? 3. Da li se memorijski mapirano periferijsko kolo uvek ponaša kao memorija? Obrazložiti. 4. Da li je za memorijsko mapiranje periferijskih kola obavezno postojanje kompletnih magistrala

adresa i podataka i kontrolnih signala upisa i čitanja? Obrazložiti. 5. Ako je memorijski mapirano memorijsko kolo izlazni registar bez mogućnosti čitanja stanja, šta je

potrebno uraditi za pravilan rad sa takvim kolom? 6. Kada je C naredba tipa RMW (Read-Modify-Write) primenljiva na memorijski mapirano kolo? 7. Kakva je razlika između RMW naredbi C jezika i asemblera?

3.6. Naredbe petlji

Za višestruko izvršavanje jedne ili više komandi, u programskom jeziku C se koriste komande petlji FOR, WHILE i DO–WHILE. Osnovni format ove tri komande je dat na slici 47:

FOR petlja WHILE petlja DO-WHILE petlja for (inicijalizacija;test;finalizacija) telo_petlje;

while (test) telo_petlje;

do telo_petlje; while (test);

telo_petlje – jedna C komanda, ili više C komandi uokvirenih velikim zagradama ...

Slika 47: Osnovni format komndi petlji FOR, DO i DO-WHILE

Prve dve petlje, FOR i WHILE, su funkcionalno iste i za njih važi da telo petlje može biti izvršeno nula ili više puta, zbog toga što je test uslova izvršavanja na početku petlje. Treća petlja, DO-WHILE, razlikuje se od prethodne dve petlje, jer se telo petlje može izvršiti najmanje jednom (ili više puta), pošto je test uslova izvršavanja na kraju petlje. Grafički prikaz sve tri petlje dat je na slici 48.

FALSE

TRUE

FALSE

TRUE FALSE

TRUE test ?

inicijalizacija

finalizacija

telo petlje

test ?

telo petlje

telo petlje

test ?

ulaz u petlju ulaz u petlju ulaz u petlju

kraj petlje kraj petlje kraj petlje

FOR petlja WHILE petlja DO-WHILE petlja Slika 48: Grafički prikaz petlji u C jeziku

Iz grafičkog prikaza lako je uočljiva sličnost između FOR i WHILE petlji. Ako se iz FOR petlje izostave inicijalizacioni i finalizacioni deo, dobija se WHILE petlja. Takođe, ako se ispred WHILE petlje ubaci inicijalizacioni deo, a na kraju tela WHILE petlje finalizacioni deo, dobija se petlja koja odgovara FOR petlji. U delovima za inicijalizaciju i finalizaciju FOR petlje, koristi se jedan izraz, ili više izraza razdvojenih zarezom (comma expression), pri čemu bilo koji od tri pomoćna elementa FOR petlje, inicijalizacija, test i finalizacija, može, ali i ne mora postojati. Ako se iz FOR petlje izostavi testiranje, podrazumeva se tačan

Page 47: Digitalni_Mikrokontroleri

Mikrokontroleri 46

Katedra za elektroniku

rezultat testa (TRUE), a petlja se bezuslovno izvršava. Korišćenjem ove osobine moguće je napraviti beskonačne petlje (slika 49):

FOR petlja WHILE petlja DO-WHILE petlja for (;;) ... // telo petlje

while (1) ... // telo petlje

do ... // telo petlje while (1);

Slika 49: Bezuslovne petlje Osnovna osobina petlji je postojanje skoka unazad. Imajući ovo u vidu, sve tri petlje mogu se

napisati i u dekomponovanoj formi, slika 50:

FOR petlja WHILE petlja DO-WHILE petlja inicijalizacija; labela: if (test == TRUE) telo_petlje; finalizacija; goto labela;

labela: if (test == TRUE) telo_petlje; goto labela;

labela: telo_petlje; if (test == TRUE) goto labela;

Slika 50: Dekomponovane petlje FOR, WHILE i DO-WHILE

Mnogi mikrokontroleri imaju mašinsku instrukciju DJNZ (Decrement Jump Not Zero), čiji je format najčešće

djnz var,lab

a koja znači “smanji promenljivu var za 1 i ako rezultat nije nula, skoči na labelu lab”. Promenljiva var može biti smeštena u memoriji ili registru, a dimenzija (broj bita) odgovara dimenziji memorije mikrokontrolera (8 bita za 8-bitne mikrokontrolere, 16 za 16-bitne itd.). Ovakva instrukcija može jako dobro da se uklopi u C petlju, posebno ako se radi o DO-WHILE petlji. Naime, samo ova petlja ima test posle tela petlje i skok ako je test uspešan (TRUE). U sledećem primeru su date tri jednostavne funkcije sa različitim petljama, pri čemu se broj ponavljanja definiše formalnim parametrom p (slika 51), sa prikazom asemblerske realizacije u Keil C kompajleru za 8051, sa optimizacijom nivoa 8. Formalni parametar p se prenosi preko registra R7.

FOR petlja WHILE petlja DO-WHILE petlja void func_FOR(char p) for (;p--;) P0 ^= 1;

void func_WHILE(char p) while (p--) P0 ^= 1;

void func_DOWHILE(char p) do P0 ^= 1; while (--p);

loop_lab: MOV R6,0x07 DEC R7 MOV A,R6 JZ ret_lab XRL P0,#0x01 SJMP loop_lab ret_lab: RET

loop_lab: MOV R6,0x07 DEC R7 MOV A,R6 JZ ret_lab XRL P0,#0x01 SJMP loop_lab ret_lab: RET

loop_lab: XRL P0,#0x01 DJNZ R7,loop_lab RET

Slika 51: Jednostavan primer parametriranih petlji Iako su sve tri petlje funkcionalno identične i ponavljaju jednu instrukciju (P0 ^= 1;) isti broj puta (p puta), vidi se da su FOR i WHILE petlja identične, jer FOR petlja nema inicijalizacioni i finalizacioni deo. Ono što je posebno uočljivo je razlika u realizaciji, jer je petlja DO-WHILE realizovana sa znatno manje mašinskih instrukcija i pomoću već spomenute DJNZ instrukcije. Treba uočiti da se u ovoj petlji koristi pre-

Page 48: Digitalni_Mikrokontroleri

Mikrokontroleri 47

Katedra za elektroniku

dekrement operacija unutar WHILE testa, sa pozivitivnom logikom (bez logičke inverzije testa), kao i da je telo petlje dovoljno malo, tako da je domet povratnog skoka u okviru opsega DJNZ instrukcije (razlika između adrese labele loop_lab i adrese prve naredne instrukcije RET je u okviru predznačne vrednosti relativnog skoka). Ako su ovi uslovi zadovoljeni, većina kompajlera će u višim nivoima optimizacije proizvesti isti ili vrlo sličan mašinski kôd. Osim smanjenja broja instrukcija, veoma je važno i brže izvršavanje petlje, što je od posebnog značaja kada se petlja nalazi unutar prekidne funkcije, ili u delovima programa koji zahtevaju maksimalno brzo izvršavanje.

Za sva tri tipa petlje postoje i dodatne komande break i continue, za prekidanje izvršavanja komandi koje se nalaze u telu petlje. Nakon prekida komandom break, potpuno se završava rad petlje, a tok programa se prenosi na prvu komandu posle petlje. Komanda continue, nakon prekida, preskače ostatak tela petlje i nastavlja izvršavanje petlje sa narednom iteracijom. Način rada ovih komandi je prikazan na slici 52.

for (y=1;y<10;) x += y; if (x==10) continue; if (x>20) break; y++;

while (y<10) x += y; if (x==10) continue; if (x>20) break; y++;

do x += y; if (x==10) continue; if (x>20) break; y++; while (y<10);

FOR petlja WHILE petlja DO-WHILE petlja

Slika 52: Način rada komandi break i continue unutar petlji

Kada se unutar petlje koristi komanda break, ne sme se zaboraviti da se ista komanda koristi i za prekid switch kontrolne strukture. Ako se unutar petlje nalazi i komanda switch, uokviru koje je i komanda break, tada break forsira kraj izvršavanja switch komande, a ne petlje. Slično važi i kada se unutar jedne nalazi druga petlja. Tada obe komande, continue i break, važe samo za petlju unutar koje se nalaze, a ne i za spoljašnju petlju.

Komande petlji mogu se koristiti za generisanje kašnjenja. Međutim, tako generisano kašnjenje ima određenih mana:

1. Duža kašnjenja nepotrebno blokiraju program. Umesto toga, treba koristiti vremenski interval na bazi hardverskog tajmera.

2. Kašnjenje na osnovu petlje može biti vrlo netačno, jer zavisi od primenjenog kompajlera i stepena optimizacije, kao i od komandi koje se nalaze ispred i iza petlje.

3. Kašnjenje pomoću petlje može biti narušeno i prekidima, ako su dozvoljeni i događaju se često, ili njihovo izvršavanje nije kratkotrajno.

4. Primena petlji za generisanje kašnjenja ima smisla samo za vrlo kratke vremenske intervale, kada prekid hardverskog tajmera nije primenljiv, zbog vremena koje se potroši na početku i na kraju prekidne funkcije. Umesto petlji, za vrlo kratka kašnjenja može se primeniti postupak ponavljanja identičnih komandi, pod uslovom da ponovljena komanda nema negativan uticaj na algoritam programa ili hardver. Na primer, ako se umesto jedne napišu tri komande

P1 = 3; P1 = 3; P1 = 3; ukupno trajanje ove tri komande (koje se prevode kao tri mašinske instrukcije) zavisi samo od frekvencije takta mikrokontrolera, a ne od kompajlera i stepena optimizacije.

5. Ako se, i pored navedenog, petlje koriste za generisanje dužih vremenskih intervala, onda takvo kašnjenje treba realizovati posebnom funkcijom sa dvostrukom petljom, pri čemu se unutrašnja petlja ponavlja konstanta broj puta, sa što tačnijim trajanjem, a broj ponavljanja spoljašnja petlje se određuje formalnim parametrom, u koracima trajanja unutrašnje petlje. Za ovakvu realizaciju kašnjenja neophodan je simulator sa merenjem vremena izvršavanja, tako da se intervali podešavaju iz nekoliko pokušaja, što je moguće tačnije.

Page 49: Digitalni_Mikrokontroleri

Mikrokontroleri 48

Katedra za elektroniku

Posebna primena DO-WHILE petlje je u makro funkcijama (spomenuto u vežbi 2), kada je potrebno formirati složenu makro funkciju, sastavljenu od više jednostavnih ili složenih standardnih ili makro konstrukcija, na primer

#define Tastatura() do Ucitaj_Tastere(); \ Testiraj_Tastere(); \ Generisi_Kod_Tastera(); while (0)

Kako se DO-WHILE uvek izvrši najmanje jednom, pri čemu navedeni uslov (0) znači da se petlja ne ponavlja, ova petlja se ponaša kao jedinstvena grupa C naredbi koja se izvrši samo jednom. Prednost ove konstrukcije je u tome, što se ona ponaša kao jedinstvena C naredba, koju je moguće direktno uključiti u bilo koji deo C programa ili makro funkcije, bez potrebe za dodavanjem zagradi. Mana ovakve makro funkcije je u tome što nije moguće vratiti vrednost (funkcija je tipa void).

3.6.1. Pitanja za proveru

1. Koji tipovi petlji postoje u programskom jeziku C i koliko se minimalno puta izvršava svaka od tih petlji (odnosno komande unutar tela petlje)?

2. U kom slučaju je FOR petlja identična WHILE petlji? 3. Navesti sve razlike između FOR i WHILE petlji. 4. Koje su razlike između WHILE i DO-WHILE petlji? 5. Koji uslovi moraju biti zadovoljeni da DO-WHILE petlja bude znatno efikasnija od WHILE petlje? 6. U kojim slučajevima ima smisla koristiti petlje za generisanje kašnjenja? 7. Zbog čega se izbegava korišćenje petlji za generisanje dugačkih kašnjenja? 8. Koji tip petlje je pogodan za korišćenje u makro funkcijama?

3.7. Naredbe uslovnog izvršavanja

Za uslovno izvršavanje jedne ili više komandi, u programskom jeziku C se koriste dva tipa komandi, jednostruki test IF-(THEN)-ELSE i višestruki test SWITCH. Osnovni oblik ovih komandi je prikazan na slici 53.

Primer a: IF-(THEN)-ELSE

Primer b: SWITCH

Primer c: Zamena SWITCH pomoću više IF komandi

if (uslov) komanda; else komanda; * umesto jedne komande može se koristiti niz komandi uokvirenih velikim zagradama ...

switch (vrednost) case konst_1: komanda1; break; case konst_2: komanda2; break; ..... case konst_N: komandaN; break default: komanda;

if (vrednost==konst_1) komanda1; else if (vrednost==konst_2) komanda2; else .... if (vrednost==konst_N) komandaN; else komanda; // default

Slika 53: Komande za uslovno izvršavanje C programa

Napomena: Ključna reč THEN u IF komandi ne postoji, a napisana je samo radi boljeg razumevanja.

Komanda IF-(THEN)-ELSE (slika 53a) testira navedeni uslov. Ako je ovaj uslov ispunjen (TRUE), izvršava se jedna ili više komand navedenih odmah posle uslova, dok se u suprotnom izvršava jedna ili više komandi, navedenih nakon ključne reči ELSE. Drugi deo ove komande (ELSE), ne mora se navoditi ako se ne koristi. Pod uslovom komande IF podrazumeva se proizvoljan logički izraz. Ako se ima u vidu da se, u C jeziku, i promenljiva koja nije logičkog tipa u logičkom izrazu tretira kao logička (FALSE ako je vrednost nula i TRUE za sve ne-nulte vrednosti), to znači da se kao uslov komande IF može navesti promenljiva bilo

Page 50: Digitalni_Mikrokontroleri

Mikrokontroleri 49

Katedra za elektroniku

kog tipa, logički ili aritmetički izraz, promenljiva sa komandom dodeljivanja, kao i konstanta. Pri tome, navođenje konstante će izazvati upozorenje prevodioca, jer takav izraz nema mogućnost uslovnog izvršavanja, pošto je uslov uvek isti, pa i komanda IF postaje besmislena. U ovakvim slučajevima treba proveriti napisani uslov, jer se često događa da se komparacija zameni pridruživanjem, na primer, ako se, umesto

if (a==5) .... napiše if (a=5) ....

test postaje besmislen, jer je uvek tačan (a dobija konstantnu vrednost 5, što je uvek različito od nule – TRUE). Iz ovih razloga je potrebno proveriti sva upozorenja kompajlera, a ne samo prijavljene greške.

Komanda SWITCH (slika 53b) testira vrednost promeljive ili izraza bilo kog tipa, poređenjem vrednosti sa navedenim konstantama. Naime, uz svaku ključnu reč case pridružuje se konstantna vrednost, tako da se kompletna komanda može tretirati kao više IF komandi (slika 53c), pri čemu se kao uslov IF komande navodi poređenje parametra vrednost i pojedinačnih konstanti (konst_1, konst_2, itd). Niz komandi koje pripadaju jednom slučaju (case), zaključuje se komandom break, koja izaziva iskakanje iz SWITCH komande. Ako se komand break izostavi, nakon izvršenja komandi navedenih u odgovarajućem slučaju (case) doći će do ‘propadanja’ programa do narednog slučaja (case), odnosno preći će se na izvršenje komandi u narednom slučaju. Ova osobina se povremeno koristi radi pojednostavljenja toka programa, ali nenamerno izostavljanje break naredbe može izazvati različite nepredviđene efekte. Primer:

verify_only = 0; switch (command) case _VERIFY: verify_only = 1; case _WRITE : write_command(verify_only);

U ovom primeru, funkcija write_command upisuje (verify_only==0) ili proverava (verify_only==1) blok podataka. Umesto da se komanda poziva dva puta, za svaki case po jednom, ali sa različitim parametrima, poziv komande se izvršava samo jednom, a “case _VERIFY:” menja inicijalnu vrednost kontrolne promenljive. Nakon poslednjeg slučaja case, break komanda nije potrebna, jer se tu komanda SWITCH i završava.

U određenim slučajevima, break naredba u komandi SWITCH može biti izostavljena radi pojednostavljenja programa i efikasnijeg kôda, kao što je prikazano u primeru na slici 54.

Primer a) Primer b) char Convert(char value) char retval; switch (value) case 1 : retval = 13; break; case 2 : retval = 27; break; case 3 : retval = 52; break; default: retval = 0; return retval;

char Convert(char value) switch (value) case 1 : return 13; case 2 : return 27; case 3 : return 52; default: return 0; /* break nije potrebno, jer return prekida funkciju */

Slika 54: Primer izostavljanja break komande u komandi SWITCH

Pri formiranju SWITCH komande sa većim brojem konstanti, treba težiti korišćenju konstanti u kontinualnom nizu (N, N+1, N+2,...), jer se time omogućava kompajleru formiranje tabele skokova, koja je najefikasnije rešenje SWITCH komande. U primeru na slici 55 je maksimalno optimizovan kôd za komandu SWITCH, jer se koristi efikasnije rešenje sa slike 54b, uz kontinualni niz konstanti. Pojedini delovi kôda su izostavljeni radi bolje preglednosti, a predstavljaju samo ponavljanje prikazanih delova.

Page 51: Digitalni_Mikrokontroleri

Mikrokontroleri 50

Katedra za elektroniku

Primer a) Primer b) Primer c) uchar convert(uchar par) switch (par) case 0: return 13; case 1: return 27; case 2: return 11; case 3: return 75; case 4: return 122; case 5: return 17; case 6: return 14; default: return 0;

convert: MOV A,R7 CJNE A,#7,test_range test_range: JNC default MOV DPTR,#jmp_table ADD A,ACC JMP @A+DPTR jmp_table: AJMP case_0 AJMP case_1 ...... AJMP case_6 AJMP default case_0: MOV R7,#13 RET case_1: MOV R7,#27 RET ..... case_6: MOV R7,#14 RET default: MOV R7,#0 RET

const uchar table[]= 13,27,11,75,122,17,14; uchar convert(uchar par) if (par>6) return 0; return table[par];

Slika 55: Maksimalno optimizovana SWITCH komanda i alternativno, tabelarno rešenje

Navedeni primer 55a ima prvenstveno edukativnu vrednost, jer se konkretan slučaj rešava još efikasnije primenom tabela (Look-Up tabele), kao u primeru 55c. Takođe, u primeru 55c se vidi da nakon return komande u IF komandi nije potrebna ključna reč ELSE, jer se funkcija prekida komandom return.

3.7.1. Pitanja za proveru

1. Navesti sve razlike između IF i SWITCH komande. 2. U kojim uslovima je SWITCH komanda efikasna?

3.8. Logičke operacije

Osim standardnih logičkih (Bulovih) izraza i promenljivih, programski jezik C koristi i logičke operacije na nivou pojedinačnih bita (bitwise) promenljivih koje su celobrojnog tipa. Za Bulove izraze važi da je ne-nulta aritmetička vrednost tačna (True), dok je nulta vrednost netačna (False). U aritmetičkom izrazu, Bulova promenljiva ili izraz može imati aritmetičku vrednost 0 (False), ili 1 (True). Zahvaljujući ovome, aritmetički i Bulovi izrazi i promenljive mogu se mešati u oba tipa izraza, Bulovom i aritmetičkom. Za razliku od logičkih operacija Bulovog tipa, operacije bitwise tipa se odnose na sve bite osnovnih celobrojnih veličina, što znači da se logička operacija izvodi istovremeno nad svim bitima. Kako su ovakve naredbe po pravilu direktno podržane mašinskim instrukcijama, vrlo su brze i zahtevaju minimum mašinskog kôda. Na slici 56 je prikazana bitwise AND operacija (logičko I nad pojedinačnim bitima), primenjena na dve promenljive var1 i var2, sa rezultatom smeštenim u var3. Sve tri promenljive su veličine po jedan bajt.

bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0var1

bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0var2

bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0var3

bit 7

AND

Slika 56: Primer bitwise AND operacije nad promenljivama širine jedan bajt

Svaki bit izlazne promenljive var3 se dobija kao rezultat logičke operacije I između para odgovarajućih bita promenljivih var1 i var2. Kod 8-bitnih mikrokontrolera, jedna bitwise logička operacija zahteva jednu mašinsku instrukciju za 8-bitne reči. Slično, kod N-bitnih mikrokontrolera, potrebna je jedna mašinska instrukcija za N-bitne reči, što znači da se N pojedinačnih logičkih operacija realizuje samo jednom

Page 52: Digitalni_Mikrokontroleri

Mikrokontroleri 51

Katedra za elektroniku

mašinskom instrukcijom. Isto važi i za bitwise logičke operacije ILI (OR), EKS-ILI (XOR) i negaciju (NOT). Na slici 57 su prikazane Bulove i bitwise logičke operacije u C jeziku i njihove realizacije u asembleru.

I (AND) ILI (OR) Eks-ILI (XOR) Negacija (NOT) Bulova operacija && || != ! C primer if (x && y)

cmd; if (x || y) cmd;

if (x != y) cmd;

if (!x) cmd;

ASM primer mov a,x jz kraj mov a,y jz kraj cmd: ... ; cmd kraj:

mov a,x jnz cmd mov a,y jz kraj cmd: ... ; cmd kraj:

mov a,x xrl a,y jz kraj cmd: ... ; cmd kraj:

mov a,x jnz kraj cmd: ... ; cmd kraj:

Bitwise operacija & | ^ ~ C primer z = x & y; z = x | y; z = x ^ y; z = ~x; ASM primer mov a,x

anl a,y mov z,a

mov a,x orl a,y mov z,a

mov a,x xrl a,y mov z,a

mov a,y cpl a mov z,a

Tabele istinitosti

y x z 0 0 0 0 1 0 1 0 0 1 1 1

y x z 0 0 0 0 1 1 1 0 1 1 1 1

y x z 0 0 0 0 1 1 1 0 1 1 1 0

x z 0 1 1 0

Slika 57: Primeri Bulovih i bitwise logičkih operacija

U bitwise logičkom izrazu sa dva ulazna parametra, jedan od parametara se može tretirati kao tzv. maska (na pr. y u tabelama istinitosti na slici 57). Zavisno od tipa logičke operacije, svrha maske je da:

I maska (AND) – propusti neke bite parametra (bit maske = 1), a ostale da obori na logičku nulu (bit maske = 0).

ILI maska (OR) – propusti neke bite parametra (bit maske = 0), a ostale da podigne na logičku jedinicu (bit maske = 1).

Eks-ILI maska (XOR) – promeni neke bite parametra (bit maske = 1), a ostale da propusti neizmenjene (bit maske = 0).

Negacija (NOT) menja sve bite parametra, a ekvivalent je Eks-ILI operaciji sa maskom u kojoj su sve jedinice. Iako negacija ima samo ulazni parametar, a ne i parametar maske, ona je pridružena ovom tipu operacija zbog toga što se koristi za promenu ulaznog parametra, kao i tri osnovne maske. Na slici 58 su prikazani efekti primene bitwise logičkih maski.

var1

maska

var2

AND

b7 b6 b5 b4 b3 b2 b1 b0

0 1 1 0 1 1 1 0

0 b6 b5 0 b3 b2 b1 0

var1

maska

var2

OR

b7 b6 b5 b4 b3 b2 b1 b0

0 1 1 0 1 1 1 0

b7 1 1 b4 1 1 1 b0

var1

maska

var2

XOR

b7 b6 b5 b4 b3 b2 b1 b0

0 1 1 0 1 1 1 0

b7 b6 b5 b4 b3 b2 b1 b0

var1

var2

NOT

b7 b6 b5 b4 b3 b2 b1 b0

b7 b6 b5 b4 b3 b2 b1 b0

Slika 58: Prikaz bitwise logičkih maski pomoću logičkih kola

Page 53: Digitalni_Mikrokontroleri

Mikrokontroleri 52

Katedra za elektroniku

Osnovna primena bitwise logičkih maski je u izdvajanju i promeni pojedinih bita iz ulaznog parametra. Na primer, ako su na niža četiri bita ulaznog porta mikrokontrolera priključena četiri tastera, njihovo stanje se može pročitati C komandom

tast = P1 & 0x0f;

koja, osim čitanja stanja porta, filtrira (propušta) samo ulazne linije na koje su priključeni tasteri, dok su preostala četiri pročitana bita forsirano postavljena na logičku nulu, čime je uticaj ova četiri bita porta na promenljivu tast eliminisan. Ako se ima u vidu da se tasteri najčešće povezuju prema masi, zbog čega je aktivno stanje tastera logička nula, često se pravi inverzija kojom se obezbeđuje da je aktivno stanje tastera, u odgovarajućoj promenljivoj, logička jedinica. Za te svrhe se koristi negacija:

tast = (~P1) & 0x0f;

Sada promenljiva tast ima četiri viša bita uvek na nuli (kao i u prethodnom slučaju), dok niža četiri bita odražavaju stanje tastera, pri čemu logička jedinica znači da je taster pritisnut, a logička nula da je otpušten (pozitivna logika). Nakon prevođenja, niz asemblerskih instrukcija (Keil C kompajler za 8051) će biti

mov a,P0 cpl a anl a,#0x0f mov tast,a

Vidi se da su za inverziju stanja i filtriranje I (AND) maskom dovoljne samo dve mašinske instrukcije, koje zauzimaju jako malo prostora i vrlo brzo se izvršavaju. U prikazanom аsemblerskom kôdu, negacija je realizovana mašinskom instrukcijom CPL.

Primena bitwise maski je veoma efikasna i pri kreiranju binarnog brojača modula 2n. Ako brojač ima n bita, uz stanja brojača u opsegu od 0 do 2n – 1, tada se ovakav brojač vrlo jednostavno realizuje, na primer, za brojač modula 16 (n = 4):

brojac++; // brojač na gore ili brojac-- za brojač na dole brojac &= 15; // ograničenje opsega brojanja do 24 – 1 = 15

odnosno u asembleru (Keil C kompajler za 8051):

inc brojac anl brojac,#15

U slučaju brojača koji broji naviše, nakon najveće moguće vrednosti sledi najmanja vrednost, dok kod brojača naniže, nakon početne (najmanje) sledi najveća vrednost. Za brojač od n bita, ukupan broj stanja je 2n. Ako je početno stanje brojača 0, tada je najveća vrednost brojača 2n – 1. Broj 2n je takav da ima jednu jedinicu na bit-poziciji n (pozicija najnižeg bita je 0, a najvišeg bita brojača je n – 1), dok su svi ostali biti na nuli. Kako je najveća vrednost brojača za jedan manja od 2n, to znači da je najveća vrednost brojača takva da je prvih n bita na logičkoj jedinici, dok su preostali (viši) biti na nuli. Na primer, u jednobajtnom brojaču sa 5 bita važi (slika 59):

dekadno binarno heksadecimalno n 5 – – 2n 32 0010 0000 20 2n – 1 31 0001 1111 1F najmanja vrednost 0 0000 0000 00 najveća vrednost 31 0001 1111 1F redosled brojanja naviše

0, 1, 2...31, 0, 1, 2... – –

redosled brojanja naniže

0, 31, 30, 29...1, 0, 31, 30... – –

Slika 59: Primer 5-bitnog brojača u jednobajtnoj promenljivoj

Sa slike se vidi da maska čija je vrednost 31 (2n – 1) direktno pokriva (odnosno propušta) tačno one bite brojača koji su aktivni (pripadaju brojaču), dok se preostali biti forsiraju u stanje logičke nule. Naravno, ako je broj bita brojača 8, 16 ili 32, tada nema smisla primenjivati masku, jer sama širina reči deluje kao maska,

Page 54: Digitalni_Mikrokontroleri

Mikrokontroleri 53

Katedra za elektroniku

pod uslovom da je za brojač odabran celobrojni tip odgovarajuće veličine (char, int ili long, bez predznaka – unsigned), jer ovde ne postoji višak bita koje treba forsirati u stanje logičke nule. Binarni brojač se može lako može realizovati u C jeziku na više načina. Prednost primene maski u formiranju binarnog brojača je vidljiva sa slike 60, gde su, osim varijante sa maskom, prikazane i dve standardne alternative. Uz C komande, prikazano je i asemblersko rešenje (Keil C kompajler za 8051, optimizacija 8), gde je promenljiva brojac 8-bitna globalna promenljiva, bez predznaka (unsigned char).

Osnovna varijanta Alternativa 1. Alternativa 2. brojac++; brojac &= 31;

brojac++; if (brojac>31) brojac = 0;

brojac = (brojac<31) ? (brojac+1) : 0;

inc brojac anl brojac,#0x1f

inc brojac mov a,brojac setb c subb a,#0x1f jc skip clr a mov brojac,a skip:

mov a,brojac clr c subb a,#0x1f jnc skip1 mov a,brojac inc a mov r7,a sjmp skip2 skip1: mov r7,#0 skip2: mov brojac,r7

Veličina: 5 bajtova Veličina: 12 bajtova Veličina: 17 bajtova Slika 60: Nekoliko rešenja binarnog brojača u C jeziku sa asemblerskom realizacijom

Korak brojača ne mora uvek biti jedan. Međutim, vrlo često se zahteva da se promena vrednosti brojača vrši po principu pravilnog kruga, bez obzira na korak brojanja, odnosno promene vrednosti. Na primer, ako brojač broji ugao u stepenima (celobrojno), nakon vrednosti 359 sledi vrednost 0. Međutim, ako je korak 2, posle 359 sledi vrednost 1. U ovakvim slučajevima varijanta brojača sa testom tipa

if (brojac>=K) brojac = 0;

gde je K moduo brojanja (360 u konkretnom slučaju) nije odgovarajuća i mora biti zamenjena sa

if (brojac>=K) brojac -= K;

Alternativno rešenje koristi mod operaciju:

brojac %= K;

međutim, ovaj način nije dobar jer zahteva interno deljenje radi dobijanja ostatka deljenja, što znači i dodatno procesorsko vreme. Ako moduo brojanja K ima vrednost 2n, tada primena bitwise maske može biti ubedljivo najefikasnija:

brojac &= K-1;

kao što je već opisano ranije. Radi poređenja, na slici 61 su prikazane varijante sa testom i mod operacijom za brojač sa modulom brojanja 40, za koji nije moguće primeniti metodu maske (Keil C kompajler za 8051).

Primer a) Primena testa Primer b) Primena mod operacije C primer if (x>=40)

x -= 40; x %= 40;

Asemblersko rešenje

mov a,x clr c subb a,#40 jc skip mov a,#0xD8 ; -40 add a,x mov x,a

mov a,x mov b,#40 div ab mov x,b

Page 55: Digitalni_Mikrokontroleri

Mikrokontroleri 54

Katedra za elektroniku

skip: Veličina (bajtova) 13 9 Trajanje u μs (na 12MHz) 5 sa preskokom, 8 bez preskoka 9

Slika 61: Realizacija brojača sa proizvoljnim modulom brojanja

Vidi se da se primenom testa postižu bolji rezultati što se tiče brzine izvršavanja, iako je izlazni kôd nešto veći. Međutim, treba imati u vidu da je promenljiva x u ovom slučaju 8-bitna, kao i da mikrokontroler 8051 ima mašinsku instrukciju 8-bitnog deljenja. Ako mikrokontroler nema mogućnost da deljenje izvrši u jednoj instrukciji, pogotovo ako uopšte nema mašinsku instrukciju deljenja, tada se vreme izvršenja u primeru 61b može drastično povećati. Princip primene maske za zadržavanje vrednosti u okviru opsega 2n nije striktno namenjeno samo za brojače, nego se može koristiti i u druge svrhe. Na primer, ako promenljiva predstavlja celobrojni ugao u stepenima, tada je za proračun trigonometrijskih vrednosti pogodno opseg ugla svesti na osnovni opseg od 0 do 359 stepeni, što je moguće izvršiti primenom jedne od metoda sa slike 61. Naravno, i ovde važi da je najbolje rešenje primena bitwise maske za vrednosti u opsegu od 0 do 2n. Na primer, ako jednostavni eho uređaj ima memorijski bafer od 32768 = 215 bajtova (odmeraka), indeksi za upis je pomeren za K odmeraka u odnosu na indeks za čitanje, a kašnjenje reprodukcije u odnosu na snimanje je K intervala odmeravanja. Ako je indeks čitanja osnovni indeks koji se povećava za 1, tada se indeks upisa dobija dodavanjem K na vrednost indeksa čitanja (oba indeksa su tipa unsigned int):

rd_index++; rd_index &= 32767; // 32767 == 0x7FFF (2n - 1) wr_index = (rd_index + K) & 32767;

U izrazu za indeks upisa (wr_index), bitwise operacija obezbeđuje zadržavanje vrednosti u zahtevanom opsegu, uz konstantni razmak u odnosu na indeks upisa, koji je realizovan kao brojač tipa 2n.

Bitwise maske se mogu koristiti kao zamena testovima komparacije u komandama IF, FOR, WHILE i DO-WHILE, u slučajevima kada je granicu testa moguće tačno definisati izdvajanjem jednog ili više bita. Na primer, ako je dozvoljena vrednost za neku promenljivu od 0 do 7, tada će se testom koji proverava sve bite osim najniža 3 bita (koji definišu navedeni opseg), lako odrediti da li je promenljiva unutar dozvoljenog opsega ili ne, slika 62:

Standardni test Test primenom I maske C test if (val > 7)

greska(); if (val & 0xf8) greska();

if (val & 0xfff8) greska();

tip testa 8-bitni 16-bitni 8-bitni 16-bitni

definicija uchar val; uint val; uchar val; uint val;

Asemblerski rezultat

mov a,val setb c subb a,7 jc skip lcall greska skip:

setb c mov a,val+1 subb a,#7 mov a,val subb a,#0 jc skip lcall greska :skip

mov a,val anl a,#0xf8 jz skip lcall greska skip:

mov a,val + 1 anl a,#0xf8 orl a,val jz skip lcall greska :skip

Veličina: 10 bajtova 14 bajtova 9 bajtova 11 bajtova Slika 62: Primena bitwise maski u logičkim testovima

Poređenja tipa ‘veće’, ‘veće ili jednako’, ‘manje’ i ‘manje ili jednako’ nije direktno podržano u mikrokontroleru 8051. Zbog toga test primenom maske sa slike 62 daje nešto kraći i brži kôd, što dodatno zavisi i od veličine promenljive (8, 16 ili 32 bita). Iz istog razloga se u testu petlji izbegavaju komparacije koje nisu direktno podržane mašinskim instrukcijama. Na primer, ako FOR petlja treba da se izvrši konstanta broj puta, pri čemu se indeks petlje ne menja nigde unutar tela petlje, može se primeniti poređenje tipa ‘jednako’, odnosno ‘različito’, kao na slici 63.

Primer a) Komparacija ‘manje’ Primer b) Komparacija ‘različito’ for (i=0;i<10;i++) komanda;

for (i=0;i!=10;i++) komanda;

Slika 63: Različiti načini testiranja uslova petlje

Page 56: Digitalni_Mikrokontroleri

Mikrokontroleri 55

Katedra za elektroniku

U primeru 63b se koristi činjenica da komanda ne menja vrednost promenljive i, koja zbog toga uvek dostiže vrednost definisanu u testu, zbog čega je test ‘različito’ moguće primeniti. Ovaj test je uvek podržan instrukcijama mikrokontrolera, za razliku od testa ‘manje’, koji može zahtevati više mašinskih instrukcija, zbog čega je izvršavanje nešto sporije i zahteva više memorijskog prostora. Ipak, moguće je da kvalitetan C kompajler generiše isti izlazni kôd u oba slučaja. Međutim, kako to zavisi od samog kompajlera, primer 63b je bolji, jer omogućava da i lošiji kompajler napravi optimalan izlazni kôd. Iz svega ovoga se može zaključiti da je preporučljiva analiza asemblerskog rezultata C kompajlera nekoliko osnovnih C kontrukcija (petlje, uslovne naredbe i komparacije), uvek kada se prelazi na novi C kompajler, jer se primenom pogodnih oblika C naredbi može znatno pomoći C kompajeru u efikasnoj optimizaciji izlaznog kôda.

Primer a) Primer b) Primer c) Promena jednog bita pomoću testa Promena više bita pomoću testa Promena primenom maske char change(char a,char mask) if (a & mask) a &= ~mask; else a |= mask; return a;

char change(char a,char mask) char i = 1; while (i) if (i & mask) if (a & i) a &= ~i; else a |=i; i <<= 1; return a;

char change(char a,char mask) a ^= mask; return a;

Slika 64: Modifikacija pojedinačnog bita celobrojne promenljive

Ako je potrebno promeniti jedan ili više bita u celobrojnoj reči, veoma efikasna je primena Eks-ILI (XOR) maske. Na slici 64 su prikazana dva rešenja za promenu jednog bita promenljive a, definisanog promenljivom mask. U slučaju primene testa (64a) moguće je menjati samo jedan bit promenljive a (promenljiva mask mora imati samo jedan bit na jedinici), jer u slučaju promene više bita od jednom rezultat IF testa može biti pogrešan (na pr. ako treba menjati dva bita, greška će se javiti ako su ta dva bita u promenljivoj a različiti, jer se sa promenu oba bita zahteva prolaz kroz različite IF grane). Zbog toga je, za promenu više bita metodom testa, potreban nešto složeniji algoritam (64b). Pri primeni Eks-ILI maske za promenu bita (XOR, slika 64c) moguće je menjati proizvoljan broj bita istovremeno, zbog čega je ovaj način u velikoj prednosti. Kao što se iz prethodnih primera može zaključiti, veoma često se bitwise maske koriste u naredbama tipa pročitaj-promeni-upiši (RMW – Read-Modify-Write) – komande koje osim znaka pridruživanja (jednakost), imaju i znak za neku od aritmetičkih ili logičkih (bitwise) operacija (prikazano na slici 46). Primenom RMW bitwise naredbe veoma efikasno se postavljaju, brišu, menjaju ili filtriraju jedan ili više bita celobrojne promenljive u jednom koraku, kao što je to u osnovnoj varijanti binarnog brojača sa slike 60. Za razliku od standardnih celobrojnih promenljivih (8, 16 i 32 bita, char, int i long), C kompajleri za mikrokontrolere koji podržavaju rad sa bit promenljivima, mogu na različite načine da tretiraju ovaj tip promenljivih. Naime, neki kompajleri bit promenljive tretiraju kao Bulov tip, ili kao celobrojni tip sa dve moguće vrednosti. Zbog toga je moguće da izraz

x ^= 1; // promena stanja bit-promenljive x

ne bude prihvaćen od strane kompajlera, dok je ispravno rešenje

x = !x;

pri čemu se ovo rešenje realizuje jednom mašinskom instrukcijom koja menja navedeni bit. Kada se radi o pridruživanju vrednosti bit promenljivoj, u osnovi važi pravilo kao i u ostalim slučajevima pridruživanja promenljive veće dimenzije promenljivoj manje dimenzije. Na primer, promenljiva tipa unsigned int se vrlo jednostavno pridružuje promenljivoj tipa unsigned char:

ucharvar = uintrvar;

Page 57: Digitalni_Mikrokontroleri

Mikrokontroleri 56

Katedra za elektroniku

Pri ovome, promenljiva ucharvar dobija vrednost nižeg bajta promenljive uintvar. Na sličan način, promenljivoj tipa bit moguće je pridružiti promenljivu sa većim brojem bita. Na primer, u pridruživanju

bitvar = charvar;

će promenljivoj bitvar (bit tipa) biti pridružen najniži bit iz 8-bitne promenljive charvar. Ako je bit promenljivoj potrebno pridružiti stvarnu logičku vrednost neke promenljive, tada se koristi komparacija:

bitvar = charvar!=0;

U ovom slučaju, bitvar dobija vrednost 1 ako charvar nije nula, a nulu ako je i charvar nula. Po pravilima mešanja Bulovih i aritmetičkih izraza, promenljiva bit tipa se može direktno pridružiti drugim tipovima celobrojnih promenljivih:

uintvar = bitvar;

U svakom slučaju, kada je reč o bit promenljivima, potrebno je konsultovati uputstvo za primenjeni C kompajler.

3.8.1. Pitanja za proveru

1. Navesti razlike između Bulovih i bitwise logičkih operacija. 2. Ako je mikrokontroler 16-bitni, koliko bita je moguće promeniti istovremeno u jednoj mašinskoj

instrukciji? 3. Koji uslovi treba da budu zadovoljeni za efikasnu realizaciju binarnog brojača? 4. Da li je potrebno primeniti bitwise logičku masku za binarni brojač sa 8, 16 i 32 bita? Obrazložiti. 5. Kako se najjedostavnije menja jedan ili više bita celobrojne promenljive? 6. Da li se bitwise logičke maske mogu primeniti u IF testu? Obrazložiti. 7. Kada je test ‘različito’ efikasniji od testa ‘manje’? 8. U kom slučaju se test ‘različito’ može primeniti u testu petlje?

3.9. Prekidi

Prekidi u programskom jeziku C nisu direktno podržani, jer se mehanizmi prekida veoma razlikuju za različite familije mikroprocesora i mikrokontrolera. Zbog toga je i način definisanja prekidnih funkcija specifičan, kako za svaku mikrokontrolersku familiju, tako i za različite C kompajlere. Zavisno od familije, mikrokontroler koji koristi prekide može podržavati jednu ili više prekidnih funkcija (potprograma), direktno ili indirektno adresiranih. Direktno adresiranje prekidne funkcije se koristi kod manjih (slabijih) mikrokontrolera, a podrazumeva da je prekidna funkcija smeštena na fiksnu adresu. Ako mikrokontroler podržava više različitih prekidnih funkcija, svaka od njih koristi tačno određenu adresu, fabrički definisanu. Jači mikrokontroleri koriste prekidne vektore, odnosno tabele sa adresama prekidnih funkcija, pri čemu funkcije mogu biti smeštene na proizvoljnoj adresi. Ovakve tabele mogu biti smeštene u ROM memoriji (fiksni prekidni vektori), ili u RAM memoriji, kada se prekidni vektori mogu dinamički podešavati (u toku izvršavanja programa). U ovom tekstu su opisani samo primeri prekida sa fiksnom adresom, odnosno sa prekidnim vektorima smeštenim u ROM memoriji (kao što su familija 8051 i Microchip mikrokontroleri srednje i niže klase).

Dva osnovna elementa u definisanju prekidne funkcije su početna adresa (vektor prekida) i mašinska

instrukcija za povratak iz prekida, jer su to i jedine stvarne razlike u odnosu da standardne C funkcije. Osim toga, svaka prekidna funkcija (ili prekidni potprogram u asembleru) mora sačuvati sadržaj ključnih registara. Kada je reč o C jeziku, na osnovu analize prekidne funkcije kompajler određuje koji se registri čuvaju i na koji način. Zbog izrazite specifičnosti načina definisanja prekidne funkcije, neophodno je konsultovati odgovarajuće uputstvo za primenjeni C kompajler. U tabeli na slici 65 su dati primeri načina definisanja prekidnih funkcija za neke kompajlere i mikrokontrolerske familije.

Page 58: Digitalni_Mikrokontroleri

Mikrokontroleri 57

Katedra za elektroniku

Keil C, 8051 void isr_func(void) interrupt 5 using 2

Hitech C, 8051 ROM_VECTOR(0x2b,isr_func) bank2 interrupt void isr_func(void)

SDCC, 8051 void isr_func(void) __interrupt (5) __using(2)

Hitech C, Microchip PIC 12/14 void interrupt isr_func(void)

Hitech C, Microchip PIC 16 void interrupt isr_func(void) @ 0x10

Renesas C, M16C // prekidni vektor se mora podesiti u asemblerskom inicijalnom kodu ! #pragma INTERRUPT isr_func void isr_func(void)

IAR C, M16C #pragma vector = TMRA2 __interrupt void isr_func(void)

Slika 65: Primeri definisanja prekidnih funkcija u C jeziku

U prikazanoj tabeli se vidi sa se za sve navedene mikrokontrolere osim za one iz srednje i niže klase Mikrochip mikrokontrolera (PIC 12 i 14), uz prekidnu funkciju definiše i njena adresa, jer svi ovi mikrokontroleri koriste više od jedne prekidne funkcije. Familije PIC12 i 14 koriste samo jednu prekidnu funkciju, pa za nju nije potrebno definisati adresu jer se ona uvek zna (i uvek je ista). Mikrokontroleri iz familije M16C (Renesas) koriste prekidne vektore, koji se, u prikazanom primeru, nalaze u ROM memoriji. Mikrokontroleri iz familije 8051 koriste četiri registarske banke sa po osam registara. Osnovni C program koristi standardno registarsku banku 0, što znači da su preostale tri banke slobodne. Kako je rad sa registrima iz aktivne banke vrlo efikasan, ovi registri se intenzivno koriste. U prekidnoj funkciji, ako se ne izvrši promena registarske banke, neophodno je sve ove registre sačuvati na steku, a pred kraj funkcije iz vratiti u početno stanje. Kako je za ove operacije neophodan dodatni kôd i dodatno vreme, u prekidnim funkcijama se po pravilu koristi jedna od preostale tri (slobodne) registarske banke. U primeru na slici 65, za 8051 i registarsku banku 2 u prekidnoj funkciji, Keil C kompajler koristi ključnu reč using, SDCC koristi __using, dok Hitech koristi ključnu reč bank2. U ovom slučaju, osim podešavanja odgovarajuće registarske banke, nema dodatnog kôda i gubitka procesorskog vremena. U primerima na slici 66 su prikazane dve prekidne funkcije (Keil C, 8051), od kojih prva koristi tekuću banku (primer 1), dok druga menja banku i koristi slobodnu banku 2 (primer 2). Ove funkcije koriste četiri lokalne promenljive smeštene u registrima, zbog čega se u prvom primeru stanja ovih registara moraju sačuvati na steku.

Primer 1: Prekidna funkcija bez promene banke Primer 2: Prekidna funkcija koja koristi banku 2 void isr_func(void) interrupt 1 uchar a,b,c,d; .... // telo funkcije

void isr_func(void) interrupt 1 using 2 uchar a,b,c,d; .... // telo funkcije push acc push psw mov psw,0x10 .... ; telo funkcije pop psw pop acc reti

push acc push psw mov psw,#0 push 0x04 ; R4 (a) push 0x05 ; R5 (d) push 0x06 ; R6 (c) push 0x07 ; R7 (b) .... ; telo funkcije pop 0x07 pop 0x06 pop 0x05 pop 0x04 pop psw pop acc reti

* Napomena: Prekidna funkcija se često naziva ISR, što je skraćenica od Interrupt Service Routine.

Slika 66: Prekidne funkcije bez i sa promenom registarske banke

Page 59: Digitalni_Mikrokontroleri

Mikrokontroleri 58

Katedra za elektroniku

Iz primera 66 se vidi način na koji se sadržaj registara čuva na steku. U standardnom kôdu, C koristi banku 0. Kako se, za sve registre iz sve četiri banke, tačno znaju adrese u internoj memoriji, a ne postoje mašinske instrukcije koje direktno smeštaju registre iz tekuće banke na stek, koristi se varijanta sa direktnim adresiranjem, odnosno na stek se smešta sadržaj interne memorije sa apsolutnih adresa koje odgovaraju registrima tekuće banke (push adr i pop adr). Iz istih razloga, odnosno zbog nepostojanja mašinskih instrukcija koje sadržaj iz jednog registra prebacuju u drugi, koristi se alternativa u kojoj se sadržaj sa apsolutne adrese jednog registra prebacuje u drugi registar. Zbog svega ovoga, za C funkciju je veoma važno koju registarsku banku koristi. Na primer, ako se iz prekidne funkcije zove neka standardna funkcije, a pri tome prekidna funkcija koristi neku od slobodnih banki (1 do 3), tada i pozvana funkcija mora koristiti istu banku. Keil C kompajler za ove svrhe koristi već spomenutu ključnu reč using, koja podrazumeva i ubacivanje odgovarajućeg kôda za podešavanje registarske banke (mov psw,0x10). Ako je odgovarajuća registarska banka već podešena, tada nema potrebe za ubacivanjem ovog dodatnog koda, ali je neophodno informisati kompajler da trenutno ne važi standardna banka 0, nego ona koja je podešena. Za te svrhe se koristi specifična pragma direktiva:

#pragma rb(2)

koja označava da sve funkcije napisane nakon ove direktive, koriste banku 2. Naravno, ovakve funkcije se mogu pozivati samo iz funkcije u kojoj je već podešena ova banka:

#pragma rb(2) uchar func1(uchar b)

.... void isr_func(void) interrupt 5 using 2 P1 = func1(P2);

U ovom slučaju, funkcija func1 se ponaša kao standardna funkcija, ali je interni asemblerski kôd takav da koristi registre iz banke 2. Pri tome, kôd za promenu banke postoji samo u funkciji isr_func, a ne i u func1. Na ovaj način izbegnuto je nepotrebno dupliranje asemblerskog kôda za podešavanje banke, jer je besmisleno podešavati već podešenu banku. Kada je reč o mikrokontroleru iz familije 8051, osnovne varijante ovog mikrokontrolera imaju dva, a neke složenije četiri nivoa prioriteta prekida. Kako neki prekid može prekinuti rad mikrokontrolera samo ako prekid istog ili višeg prioriteta nije aktivan, u varijanti sa dva prioriteta istovremeno su moguća samo dva prekida, jedan nižeg i jedan višeg prioriteta. U ovom slučaju, za sve prekide dovoljno je koristiti dve registarske banke, po jedna za svaki nivo prioriteta (na primer banke 2 i 3). Naime, ako prekid nižeg prioriteta koristi banku 2, drugi prekid istog prioriteta, koji takođe koristi banku 2, ne može biti prihvaćen, jer nije višeg prioriteta od trenutno aktivnog prekida. Kod mikrokontrolera sa četiri nivoa prioriteta, koristi se po jedna registarska banka za svaki od četiri prioriteta. Kako to znači da se koristi i registarska banka 0, koju koristi i osnovni program, ovu banku treba koristiti samo za prekide koji se najređe javljaju, jer kod njih nije kritično povećanje kôda i nešto duže izvršavanje zbog dodatnog kôda za sklanjanje registara (iz registarske banke) na stek. Ako se iz prekidne funkcije zove neka funkcija koja se može pozvati i iz osnovnog programa, takva funkcija mora biti definisana kao reentrant (pogledati vežbu 2). Kao što je već rečeno, ovakav tip funkcija se izbegava na mikrokontrolerima koji koriste overlay metodu za lokalne promenljive. Naime, mnogi kompajleri ne dozvoljavaju definisanje ovakvih funkcija, a ako dozvoljavaju, tada je neophodan dodatni mašinski kôd i dodatni prostor u internoj memoriji, što je vrlo nepogodno. Zbog toga, u slučajevima kada osnovni program i prekidna funkcija pozivaju istu funkciju, efikasnije je pisanje pozivane funkcije u dve verzije, za rad sa osnovnim programom i za rad sa prekidnom funkcijom. Drugi način je definisanje ovakve (pozivane) funkcije kao makro funkcija, koja se ne realizuje kao standardna funkcija, nego se direktno uključuje u kôd, na mestu gde je navedena. Uobičajeno je pravilo da se prekidna funkcija izvršava što brže, odnosno da se u prekidnoj funkciji realizuje samo deo kôda koji je stvarno neophodan za pravovremeno reagovanje na odgovarajući događaj. Ako se u prekidnoj funkciji ostvari kompletna obrada prekida, tada je u pitanju direktna obrada prekida.

Page 60: Digitalni_Mikrokontroleri

Mikrokontroleri 59

Katedra za elektroniku

Međutim, ako se u prekidnoj funkciji samo registruje razlog prekida, uz eventualnu brzu delimičnu obradu, a osnovnom programu se prepusti preostali deo obrade prekida koji nije vremenski kritičan, reč je o odloženoj obradi. Na primer, u prekidu se može pročitati stanje A/D konvertora, a analiza pročitane vrednosti se može izvršiti naknadno, u osnovnom programu, kada se mogu izvršiti razne vrste filtriranja, konverzije i slično. Rad sa prekidima na ovaj način (odložena obrada) omogućava pravovremeno reagovanje na više različitih i čestih prekida, bez opasnosti da će dugotrajna obrada pojedinačnog prekida u kraćem ili dužem vremenskom intervalu blokirati ostale programske blokove. Prekidne funkcije mogu da koriste globalne promenljive koje su pristupačne i iz ostalih programskih blokova (zajedničke promenljive). Kako prekid može nastati u bilo kom momentu, potrebno je obezbediti pravilan i pouzdan pristup ovim zajedničkim promenljivama, slika 67.

Osnovni program Prekidna funkcija Varijanta 1 Varijanta 2 DI(); // ili EA = 0; x = x*2 – y*(x – 2); EI(); // ili EA = 1;

bit temp = EA; EA = 0; x = x*2 – y*(x – 2); EA = temp;

void isr_func(void) interrupt 5 x++;

Slika 67: Pristup zajedničkim promenljivama

U ovom primeru se u osnovnom programu menja vrednost promenljive x. Kako ovakva promena zahteva nekoliko pristupa promenljivoj x, ako se između prvog i drugog dela aritmetičkog izraza dogodi navedeni prekid, konačni rezultat će biti pogrešan, jer promenljiva x u prvom i drugom pojavljivanju (u izrazu sa desne strane jednačine) nema istu vrednost. Međutim, ako se ispred navedenog izraza (varijanta 1) nalazi komanda za zabranu prekida (makro funkcija DI() – Disable Interrupt, kod 8051 ekvivalentno EA = 0;), tada tokom izračunavanja prekid ne može desiti, pa nema ni mogućnosti uticaja prekida na stanje promenljive x. Naravno, odmah nakon izračunavanja vrednosti navedena je i komanda za dozvolu prekida (makro funkcija EI() – Enable Interrupt, kod 8051 ekvivalentno EA = 1;), a vremenski interval u kome je prekid zabranjen treba da bude što kraći. Primena ove dve funkcije (DI i EI) u nekim slučajevima može biti problematična, prvenstveno zbog forsiranog dozvoljavanja prekida komandom EI. Problem nastaje ako je prekid u nekom drugom delu programa već bio zabranjen i ne sme biti dozvoljen u delu kôda sa slike 67. U tom slučaju se mora primeniti varijanta sa restauracijom stanja bita za dozvolu prekida, iako ovaj način zahteva dodatni mašinski kôd, kao što je prikazano u varijanti 2 sa slike 67. U datom primeru za dozvolu i zabranu prekida se koristi bit za globalnu kontrolu prekida, EA. Umesto njega, moguće je koristiti i bit za kontrolu specifičnog prekida za onu prekidnu funkciju koja koristi zajedničku promenljivu, tako da svi ostali prekidi nisu zabranjeni i mogu se normalno servisirati. Naravno, ovo važi samo za mikrokontrolere iz familije 8051. Za druge familije treba koristiti odgovarajuće bite za dozvolu i zabranu prekida.

Kada se radi o jednostavnom pristupu (obično pridruživanje) zajedničkoj promenljivoj iz osnovnog programa, postoji mogućnost pojave greške ako je širina promenljiva (broj bita) veća od osnovne širine mikrokontrolera. Na primer, ako je u pitanju 16-bitna promenljiva, a mikrokontroler je 8-bitni, tada se ovakvoj promenljivoj pristupa iz dva koraka (nekoliko mašinskih instrukcija). Ako se između ova dva koraka dogodi prekid koji menja vrednost promenljive, može doći do greške, kao što je prikazano na slici 68, kod pridruživanja jedne dvobajtne promenljive drugoj, takođe dvobajtnoj promenljivoj.

Primer: Komanda osnovnog programa y = x; Prekidna funkcija Vrednost x Vrednost y inicijalno 0x0100 0x????

korak 1. niži bajt y = niži bajt x; 0x0100 0x??00

prekid x--; 0x00FF 0x??00 1)

korak 2. viši bajt y = viši bajt x; 0x00FF 0x0000

korak 1. viši bajt y = viši bajt x; 0x0100 0x01??

prekid x--; 0x00FF 0x01?? 2)

korak 2. niži bajt y = niži bajt x; 0x00FF 0x01FF

Slika 68: Nastanak greške zbog nepravilnog pristupa zajedničkoj promenljivoj

Greška je u ovom slučaju očigledna. Naime, pre prekida, promenljiva x je imala vrednost 0x0100, a nakon prekida 0x00FF (primer 1), odnosno 0x01FF (primer 2). Rezultat koji je dobijen u promenljivoj y je 0x0000 (odnosno 0x01FF), što nije ni jedna od dve navedene očekivane vrednosti (0x0100 ili 0x00FF). Razlike u primerima 1 i 2 nastaju zbog razlike u redosledu prebacivanja pojedinačnih bajtova iz jedne u drugu

Page 61: Digitalni_Mikrokontroleri

Mikrokontroleri 60

Katedra za elektroniku

promenljivu. Ovakav problem ne postoji ako se pridruživanje može realizovati sa jednim čitanjem promenljive x, što bi bio slučaj da širina promenljiva odgovara osnovnoj širini podataka mikrokontrolera (u ovom slučaju je to 8-bitna promenljiva). Drugim rečima, neophodno je obezbediti pridruživanje vrednosti u neprekidnom nizu mašinskih instrukcija. Jedno rešenje je već prikazano na slici 67, kada je neprekidnost obezbeđena zabranom prekida. Alternativno rešenje je korišćenje pomoćne promenljive, kao na slici 69.

Osnovni program Prekidna funkcija isr_lock = 1; z = x2 * 2 – y * (x2 – 2);isr_lock = 0;

uint x2; bit isr_lock; void isr_func(void) interrupt 5 x++; if (!isr_lock) x2 = x;

Slika 69: Korišćenje pomoćne promenljive u pristupu zajedničkoj promenljivoj

U prikazanom primeru, promenljiva x ima svoj duplikat x2, čije se stanje ažurira u svakom prekidu, ali samo ako je to dozvoljeno pomoćnim bitom isr_lock. Prilikom preuzimanja vrednosti promenljive x, umesto ove uzima se vrednost pomoćne promenljive x2, koja prati (po vrednosti) promenljivu x. Kako je bit isr_lock za vreme pristupa aktivan, ažuriranje promenljive x2 se u prekidnoj funkciji neće izvršiti, čime je obezbeđena neprekidnost pristupa promenljivoj. Na žalost, ova metoda ima ograničene mogućnosti, jer ne obezbeđuje pridruživanje promenljive u suprotnom smeru (postavljanje vrednosti za x). Takođe, ako se prekid dogodi baš kada je pomoćni bit isr_lock aktivan, promenljiva x2 će biti ažurirana tek u narednom prekidu, ukoliko i tada ne bude aktiva bit isr_lock. Na slici 70 je prikazana varijanta koja ne koristi zabranju prekida, a omogućava dvosmerni pristup zajedničkoj promenljivoj. U ovom slučaju se pomoćni bit isr_lock koristi unutar prekida, za dozvolu ili zabranu modifikacije promenljive x. Mana ovog načina je u tome što se promena stanja unutar prekida ne realizuje (gubitak informacije o prekidu) ako je pomoćni bit isr_lock aktivan. U suštini, ako se za vreme aktivnosti isr_lock izvrši pridruživanje vrednosti promenljivoj x, tada se gubitak informacije o prekidu ne može smatrati manom. Naime, kako se ovakva situacija događa samo ako prekid nastane u toku modifikacije promenljive x van funkcije prekida, moguće je tretirati ovaj slučaj kao da se prekid dogodio neposredno pre modifikacije (pridruživanja) promenljive x, a kako pridruživanje svakako uništava dotadašnju vrednost x, tada ovaj prekid i nije neophodno registrovati. Radi podsećanja, u primeru sa slike 67 prekid se registruje, ali izvršava nakon pridruživanja, kada to i ima smisla.

Osnovni program Prekidna funkcija isr_lock = 1; x = x * 2 – y * (x – 2); isr_lock = 0;

bit isr_lock; void isr_func(void) interrupt 5 if (!isr_lock) x++;

Slika 70: Alternativa korišćenja pomoćne promenljive u pristupu zajedničkoj promenljivoj

Verovatno najčešće korišćeni prekidi su prekidi tajmera, koji se koriste za generisanje vremenskih intervala. Dve osnovne forme korišćenja tajmera za ove svrhe su generisanje periodičnih vremenskih intervala i pojedinačnih kašnjenja. Kada je reč o periodičnim intervalima, koji se koriste za periodično izvršavanje neke akcije, može se izvršiti podela na intervale u opsegu hardverskog tajmera (primer 1) i intervale koji prevazilaze opseg tajmera (primer 2), slika 71. U daljim primerima, radi kraćeg pisanja, za prekidne funkcije se neće posebno navoditi ključne reči za definisanje prekidnih funkcija. Umesto toga, prefiks isr_ u nazivu označava prekidnu funkciju.

Primer 1: Interval u opsegu hardverskog tajmera Primer 2: Interval van opsega hardverskog tajmera Direktno izvršavanje unutar prekidne funkcije

void isr_timer(void) // izvršavanje // akcije

void isr_timer(void) if (!(--tmr_val)) tmr_val = TIME1;

Page 62: Digitalni_Mikrokontroleri

Mikrokontroleri 61

Katedra za elektroniku

// izvršavanje // akcije

Odloženo izvršavanje, van prekidne funkcije void isr_timer(void) isr_flag = 1; .... if (isr_flag) isr_flag = 0; // izvršavanje // akcije

void isr_timer(void) if (!(--tmr_val)) tmr_val = TIME1; isr_flag = 1; .... if (isr_flag) isr_flag = 0; // izvršavanje // akcije

Slika 71: Realizovanje periodičnih akcija sa direktnom i odloženom obradom prekida

Direktno i odloženo izvršavanje zavise od trajanja intervala između dva prekida, trajanja izvršenja navedene akcije, kao i od važnosti pravovremenog reagovanja na prekid (objašnjeno u prethodnom tekstu). Kada je interval između dva prekida duži od maksimalnog opsega (intervala) tajmera, tada je neophodno uvesti dodatni, pomoćni brojač (kao što je tmr_val u primeru 2), kojim se produžava (u stvari umnožava) interval tajmera (primer 2). U datom primeru, ukupni interval između dva prekida je proizvod intervala tajmera i vrednosti TIME1. Takođe, u sva četiri navedena primera na slici 71 podrazumeva se da je hardverski tajmer podešen na automatski periodični rad. Ako to nije slučaj (na primer, tajmeri 0 i 1 u 8051, podešeni za 16-bitni rad), u prekidnu funkciju je potrebno dodati odgovarajući kôd za formiranje periodičnih intervala (na primer, ponovno punjenje 16-bitnih tajmera 0 i 1). U slučaju primene tajmera u generisanju pojedinačnih kašnjenja, najčešće se koristi periodični tajmer uz pomoćnu promenljivu kojoj se u prekidu smanjuje vrednost do nule, slika 72.

Prekidna funkcija tajmera Primer primene void isr_timer(void) if (tmr_val) if (!(--tmr_val)) isr_flag = 1;

.... isr_flag = 0; tmr_val = TIME1; .... while (!isr_flag); // izvršavanje akcije

Slika 72: Realizacija kašnjenja pomoću tajmerskog prekida

Kašnjenje započinje postavljanjem pomoćnih promenljivih isr_flag i tmr_val. Pri postavljanju vrednosti za tmr_val mora se voditi računa o metodi pridruživanja vrednosti (problem u korišćenju zajedničkih promenljivih). U prekidu, pomoćna promenljiva se smanjuje dok ne dostigne nultu vrednost, nakon čega se više ne menja. Kada je nulta vrednost dostignuta, registruje se kraj intervala promenljivom isr_flag. Ako je širina brojačke promenljive tmr_val manja ili jednaka osnovnoj širini reči mikrokontrolera, tada pomoćna promenljiva isr_flag nije potrebna, jer se umesto nje može koristiti vrednost tmr_val, slika 73.

Prekidna funkcija tajmera Primer primene void isr_timer(void) if (tmr_val) tmr_val--

.... tmr_val = TIME1; .... while (tmr_val); // izvršavanje akcije

Slika 73: Realizacija kraćih intervala kašnjenja

U ovom slučaju ne postoji ni problem pristupa zajedničkoj promenljivoj, jer se svi pristup realizuju jednom mašinskom intrukcijom. Zbog toga je preporučljiva realizacija dužih intervala primenom dodatnih pomoćnih

Page 63: Digitalni_Mikrokontroleri

Mikrokontroleri 62

Katedra za elektroniku

brojača (preskalera), tako da su širine svih brojačkih promenljivih u širini osnovne reči mikrokontrolera. Primer prekidne funkcije tajmera koja generiše različite vremenske intervale i kašnjenja je dat na slici 74.

void isr_timer(void) // 1 ms interval isr_flag_1ms = 1; if (!(--tmr_10ms)) tmr_10ms = 10; isr_flag_10ms = 1; if (delay_10ms) delay_10ms--; if (!(--tmr_100ms)) tmr_100ms = 10; isr_flag_100ms = 1; if (delay_100ms) delay_100ms--; if (!(--tmr_1s)) tmr_1s = 10; isr_flag_1s = 1; if (delay_1s) delay_1s--;

Slika 74: Prekidna funkcija tajmera kao generator vremenskih referenci U datom primeru, tajmer čija je prekidna funkcija opisana, podešen je na interval od 1 ms. Unutar prekidne funkcije, koristi se tri brojača (tzv softverski tajmeri) za intervale od 10 ms (tmr_10ms), 100 ms (tmr_100ms) i 1000 ms (tmr_1s). Svi brojači koriste prethodni kraći interval kao osnovnu referencu (preskaler), tako da su sve brojačke promenljive širine 8 bita, čime je obezbeđen pristup ovim promenljivama u jednoj mašinskoj instrukciji (iako za ovim pristupom nema potrebe van prekidne funkcije). Uz svaki ovakav brojač (softverski tajmer) pridružen je i odgovarajući indikator (isr_flag_1ms, isr_flag_10ms, isr_flag_100ms, isr_flag_1s), koji se koristi na način odloženog izvršavanja na slici 71. Slično softverskim tajmerima, koriste se i tri brojača kašnjenja, za kašnjenja sa osnovnim korakom od 10 ms (delay_10ms), 100 ms (delay_100ms) i 1000 ms (delay_1s). Kako su i promenljive brojača kašnjenja 8-bitne, maksimalna kašnjenja koja sa mogu ostvariti ovim su 2.55 s, 25.5 s i 255 s, a promenljive se koriste na način prikazan na slici 73. U nekim slučajevima, neophodno je posebno brzo reagovanje i obrada u prekidnoj funkciji, pogotovo ako ova funkcija treba jako često da izvrši neku jednostavnu obradu. Po potrebi, moguće je ovakvu prekidnu funkciju napisati u asembleru, jer i pored potencijalno kvalitetne optimizacije, može postojati mogućnost dodatnog optimizovanja na nivou asemblera. Kako takvu funkciju treba uključiti u projekat baziran na C fajlovima, veoma dobro rešenje je korišćenje sledećih koraka:

1. U posebnom modulu napiše se prekidna funkcija u C jeziku kojom se rešava postavljeni zadatak. 2. Napiše se odgovarajući H fajl sa odgovarajućim deklaracijama (ako ih ima). 3. Izvrši se prevođenje C modula sa prekidnom funkcijom u asemblerski format (fajl). 4. Dobijeni asemblerski se modifikuje (optimizuje). 5. Iz projekta se izbaci originalni C modul sa prekidnom funkcijom, a umesto toga se ubaci

modifikovani asemblerski fajl.

Prevođenjem C modula u asemblerski fajl dobijaju se sve potrebne prateće definicije, koje su neophodne za pravilno uključivanje asemblerskog fajla u C projekat, a koje bi inače ne bi bilo lako direktno podesiti.

3.9.1. Pitanja za proveru

1. Koje su osnovne razlike između obične i prekidne funkcije?

Page 64: Digitalni_Mikrokontroleri

Mikrokontroleri 63

Katedra za elektroniku

2. Da li se u funkcijama prekida mikrokontrolera 8051 koriste registarske banke 1, 2 i 3? Obrazložiti. 3. Kako se definiše prekidna funkcija u Keil C kompajleru za 8051? 4. Šta je potrebno učiniti da bi jedna funkcija u C jeziku mogla biti pozvana iz prekidne funkcije i iz

osnovnog programa? Da li se ovakve funkcije često koriste? Obrazložiti. 5. Opisati probleme koji mogu nastati ako se istoj promenljivoj, čija je širina veća od osnovne širine

mikrokontrolerske reči, pristupa iz prekida i iz osnovnog programa. Navesti najmanje dva načina kojima se ovi problemi izbegavaju i objasniti slučajeve u kojima je to primenljivo.

6. Koja je razlika između direktne i odložene obrade prekida. Kada se svaki od načina primenjuje? 7. Kako se, pomoću prekida hardverskog tajmera, generišu periodični vremenski intervali? 8. Kako se, pomoću prekida hardverskog tajmera, generišu pojedinačna kašnjenja?

3.10. Super petlja i FIFO baferi

Osnovni zahtev namenskog (embedded) mikrokontrolerskog sistema je rešavanje određenog hadrverskog zadatka u realnom vremenu. Pri tome, što je jednostavniji hardverski zadatak, to je jednostavniji i način njegovog softverskog rešavanja. Takođe, složeni problemi zahtevaju i znatno kompleksija softverska rešenja. Naravno, jednostavan zadatak se može rešiti i primenom složenih rešenja, što ne važi u obrnutom slučaju. Kada su u pitanju jednostavni zadaci, najlakše je primeniti princip rešavanja prostom petljom, u okviru koje se sekvencijalno realizuju pojedinačni delovi celokupnog zadatka, kao što je prikazano na slici 75a, u primeru programa za merenje i prikaz merenih rezultata.

Primer a) Primer b) Primer c) void main(void) int i = 0; while (1) pauza(100); if (kbhit()) obrada_tastature(); merenje(); i++; if (i==10) prikaz(); i = 0;

void main(void) int i = 0; while (1) if (kbhit()) obrada_tastature(); if (isr_100) isr_100 = 0; merenje(); if ((++i)==10) i = 0; prikaz_OK = 1; if (prikaz_OK) prikaz_OK = 0; prikaz();

void main(void) while (1) if (kbhit()) obrada_tastature(); if (isr_100) isr_100 = 0; merenje(); if (prikaz_OK) prikaz_OK = 0; prikaz();

Slika 75: Primer rešavanja hardverskog zadatka u jednoj petlji

U ovom primeru, na svakih 100 ms izvrši se provera tastature i eventualno procesiranje pritisnutog tastera, kao i merenje sa usrednjavanjem vrednosti, dok se u svakom desetom prolazu kroz petlju (1 sekunda) izvrši prikaz rezultata. Petlja u kojoj se realizuje kompletan hardverski zadatak mikrokontrolera, kao što je u ovom primeru, često se naziva i super-petlja (Super Loop). Osnovna karakteristika ove metode je sekvencijalno izvršavanje pojedinih programskih blokova, uključivši i eventualna kašnjenja (pauze). Pošto se izvršavanje pojedinačnih blokova izvodi ciklično, za ovu metodu se često koristi naziv Round Robin metoda. Jedina prednost ovog načina je jednostavnost, koja je primenljiva samo u vrlo jednostavnim slučajevima, kao što je i prikazani primer. Glavna mana ovog primera je funkcija pauze, u kojoj mikrokontroler ne može da radi ništa korisno, jer je u potpunosti posvećen čekanju isteka intervala od 100 ms. Kako i reagovanje na promene stanja tastera ulazi u sekvencu izvršavanja, to znači da je odziv na promene stanja tastature usporen, što

Page 65: Digitalni_Mikrokontroleri

Mikrokontroleri 64

Katedra za elektroniku

svakako nije dobro rešenje. Takođe, svako potencijalno proširenje navedenog algoritma, dodavanjem funkcija u cikličnu sekvencu, ograničeno je već postojećim zadržavanjem izvršenja petlje (pauza). Izvesno poboljšanje algoritma sa slike 75a može se dobiti uvođenjem pojma događaja (Event), pod kojim se podrazumeva softverska indikacija neke hardverske ili softverske promene. Indikator događaja ima dva moguća stanja i obično je veličine jednog bita, ili jednog bajta. Jedna vrsta ovakvih događaja je već opisana u vežbi 6, kada je bila reč o odloženim prekidima. U primerima odloženih tajmerskih prekida, promenljive sa prefiksom isr_flag (na primer, slika 74) u suštini predstavljaju događaj. Naravno, događaji nisu vezani samo za prekide, nego se mogu koristiti i u ostalim delovima programa, za signalizaciju i pokretanje odgovarajućih akcija. Modifikovani primer sa slike 75a, koji koristi događaje, prikazan je na slici 75b. U ovom slučaju definisana su dva događaja, od koji se prvi, isr_100, aktivira periodično, na svakih 100 ms u prekidu tajmera, a startuje funkciju merenja. Drugi događaj je prikaz_OK, koji se aktivira nakon 10 merenja, a koji startuje funkciju prikaza rezultata merenja. U primeru 75c, brojač za usrednjavanje je prebačen u funkciju merenje(), koja nakon svakih 10 izvršenih merenja aktivira događaj prikaz_OK. Ovako modifikovana super-petlja (75c) predstavlja znatno bolje rešenje od primera 75a, jer zahvaljujući korišćenju događaja, ni u jednom trenutknu ne blokira izvršavanje programa (nema nepotrebnog čekanja), a eventualno proširenje algoritma dodavanjem novih funkcija je vrlo jednostavno, ukoliko se koristi navedeni princip. Ova metoda se naziva i Event Driven (kontrola izvršavanja pomoću događaja). Ako se događaj testira samo radi aktiviranja jedne funkcije, tada je ovaj test moguće sprovesti unutar te funkcije, a ne u glavnoj petlji. Tako modifikovana varijanta, iz primera 75c, u kojoj su prikazane sve funkcije, uključivši i prekidnu funkciju tajmera, data je na slici 76.

void main(void) while (1) obrada_tastature(); merenje(); prikaz(); void merenje(void) if (isr_100) isr_100 = 0; .... // merenje if (brojac==10) brojac = 0; prikaz_OK = 1;

void obrada_tastature(void) if (kbhit()) ....// rad sa tastaturom void prikaz(void) if (prikaz_OK) prikaz_OK = 0; ....// prikaz rezultata void isr_timer(void) interrupt... /* interval == 1ms */ if (++timer==100) timer = 0; isr_100 = 1;

Slika 76: Testiranje događaja unutar funkcija koje ih koriste

Ovako modifikovana glavna (main) funkcija i njena glavna petlja najbolje odražavaju metodu cikličnog izvršavanja (Round Robin). U okviru programa može postojati više od jednog režima rada. Ako se primer sa slike 76 koristi za rad vage koja ima dva načina rada, za merenje do i preko 10 kg, pri čemu se koriste različite metode merenja, algoritam programa se može modifikovati na više načina, od kojih su dva prikazana na slici 77.

Primer a) Primer b) void main(void) while (1) obrada_tastature();

#define do_10kg 0 void main(void) char i = do_10kg;

Page 66: Digitalni_Mikrokontroleri

Mikrokontroleri 65

Katedra za elektroniku

if (do_10kg) merenje1(); else merenje2(); prikaz();

while (1) if (i==do_10kg) i = merenje_do_10kg(); else i = merenje_preko_10kg();

Slika 77: Realizacija programa sa dva algoritma U varijanti 77a, funkcije za obradu tastature i prikaz rezultata su zajedničke, zbog čega je dovoljno ubacivanje jedne IF naredbe za selekciju tipa merenja (ovde postoje dve funkcije merenja). Pri tome, kompletna struktura petlje je zadržana u formi kao na slici 76. U drugoj varijanti, 77b, algoritam u formi sa slike 76 je dupliran u funkcijama merenje_do_10kg() i merenje_preko_10kg(). Obe ove funkcije sadrže u sebi super-petlju kao što je glavna petlja sa slike 76, tako da je program sastavljen od glavne (u ovom slučaju selekcione) petlje i dve radne petlje. Varijanta 77b je pogodnija ako se i format prikaza razlikuje, odnosno kada su svi ili većina elemenata unutar jedne petlje različiti u odnosu na odgovarajuće elemente druge petlje. Obrada tastature je takođe izmeštena iz glavne petlje u pojedinačne funkcije merenja, tako da svaka od ove dve funkcije može, na osnovu obrađene tastature, prekinuti izvršavanje petlje i vratiti odgovarajući rezultat (na primer, novi režim rada) u glavnu petlju. Ako je potrebno bilo koji od navedenih algoritama (75 do 77) proširiti obradom nekog dodatnog spoljašnjeg uticaja, može nastati problem ukoliko je u pitanju spoljašnji događaj sa čestim ponavljanjem, koji zahteva složeniju obradu. Problem nastaje zbog toga, što se navedeni događaj može pojaviti u bilo kom trenutku, što znači i za vreme neke složenije obrade koja se odvija van glavne petlje, tako da se obrada spoljašnjeg događaja ne može izvršiti u pravom trenutku. Na primer, ako se koristi serijska komunikacija, a tokom izvršavanja funkcije merenja (u navedenim primerima) bude primljeno više serijskih podataka, ovi podaci mogu biti izgubljeni jer nisu prihvaćeni i obrađeni pravovremeno. Zbog toga se koriste memorijski baferi, u koje se informacije o spoljašnjim događajima smeštaju u trenutku dešavanja, ali ne i obrađuju (princip odložene obrade). Ako je ovakav memorijski bafer dovoljnog kapaciteta, u njega se mogu smestiti sve informacije o spoljašnjem događaju, do trenutka kada je obrada ovih informacija moguća. Memorijski baferi za ove namene obično su FIFO tipa (First-In-First-Out), što znači da će podatak koji je prvi ušao u bafer prvi i izaći iz njega i biti obrađen, odnosno, primljeni podaci će biti obrađeni po hronološkom redosledu. Kako se podaci upisuju i čitaju cirkularno (nakon upisa u poslednji element bafera, naredni element se upisuje u na prvo mesto u baferu), ovaj tip bafera se često naziva i cirkularni. Ako se glavna petlja iz primera sa slike 76 proširi serijskom komunikacijom (prijemom), tada se ova petlja može napisati u formi sa slike 78, uz prateće funkcije prijema serijskog porta i obrade primljenih podataka.

void main(void) while (1) obrada_tastature(); merenje(); prikaz(); obrada_serijskog_prijema();

void serijski_prijem (void) interrupt... // prijem podatka i stavljanje u // FIFO bafer

void obrada_serijskog_prijema(void) if (FIFO_nije_prazan) ....// obrada primljenih podataka

Slika 78: Proširenje glavne petlje obradom podataka serijske komunikacije

Naravno, umesto prikazane odložene obrade serijskog prijema, kompletna obrada prijema može biti u potpunosti realizovana u prekidnoj funkciji serijskog prijemnika (direktna obrada prekida), ali se to izbegava iz ranije navedenih razloga (razlike između direktne i odložene obrade prekida), pogotovo zbog toga što primljeni niz podataka dobija svoje pravo značenje tek kada se prijem kompletnog komunikacionog paketa (zaokružena celina primljenih podataka) u potpunosti završi. FIFO bafer se softverski može rešiti na više načina, od kojih je ovde predstavljen samo način sa indeksima. Ova metoda je veoma pogodna za slabije mikrokontrolere, jer se pogodnim biranjem veličine

Page 67: Digitalni_Mikrokontroleri

Mikrokontroleri 66

Katedra za elektroniku

bafera može ostvariti dovoljno efikasan konačni kôd. Osim toga, broj podataka smeštenih u bafer je posebna promenljiva koju ne treba posebno računati, tako da je informacija o tome da li je bafer prazan ili pun direktno raspoloživa. Treba napomenuti da ova metoda koristi FIFO bafer konstantne veličine (kao i neke metode sa pokazivačima), za razliku od nekih drugih metoda (sa pokazivačima i ulančanim nizovima), koje koriste bafere promenljive veličine i sa dinamičkim zauzimanjem memorije. U varijanti FIFO bafera fiksne veličine sa pokazivačima, koriste se dva pokazivača, koji pokazuju na naredni element pri upisu, odnosno čitanju. Oba pokazivača su izjednačena ako je bafer prazan, a broj elemenata u baferu se određuje razlikom pokazivača upisa i čitanja. Ako je ovakav FIFO bafer potpuno pun, tada takođe oba pokazivača pokazuju na isti element. Zbog toga se, u ovom slučaju, za bafer uvek rezerviše još jedan dodatni element, tako da se u bafer može smestiti najviše za jedan element manje od njegove veličine, čime se obezbeđuje da pokazivač upisa nikad ne može dostići (ciklično) vrednost pokazivača čitanja. U varijanti sa ulančanim nizovima, svaki element FIFO bafera sadrži element podatka ili pokazivač na podatak, kao i pokazivač na naredni element. Svakim novim upisom dinamički se kreira novi element i dodaje u listu, a čitanjem se postojeći element uklanja iz liste i iz memorije. Zbog svoje složenosti, ova metoda se ređe koristi, uglavnom u sistemima sa jačim mikroprocesorima i sa većim memorijskim kapacitetima. Osnovna varijanta FIFO bafera sa indeksima prikazana je na slici 79. Funkcije upisa i čitanja uključuju i proveru broja podataka u FIFO baferu, uz vraćanje odgovarajućeg rezultata (0 ili 1). Funkcija upisa u FIFO Funkcija čitanja iz FIFO uchar rd_index=0,wr_index=0, length=0; char buffer[MAX_SIZE]; char fifo_write(char data) if (length<MAX_SIZE) buffer[wr_index++] = data; if (wr_index>=MAX_SIZE) wr_index = 0; length++; return 0; // podatak je upisan return 1; // greška, bafer je pun

char fifo_read(char *data) if (length) *data = buffer[rd_index++]; if (rd_index>=MAX_SIZE) rd_index = 0; length--; return 0; // podatak je pročitan return 1; // greška, bafer je prazan

Slika 79: Osnovna varijanta FIFO bafera sa indeksima

Ako se za veličinu bafera izabere broj 2n, tada je ciklična promena oba indeksa znatno jednostavnija (ne zahteva IF test). U ovoj varijanti, maksimalno pojednostavljene funkcije za rad sa FIFO baferom su prikazane na slici 80.

Funkcija upisa i čitanja Primer primene uchar rd_index=0,wr_index=0, length=0; char buffer[MAX_SIZE]; // MAXSIZE = 2N void fifo_write(char data) buffer[wr_index++] = data; wr_index &= MAX_SIZE - 1; length++; char fifo_read(void) char tmp = buffer[rd_index++]; rd_index &= MAX_SIZE - 1; length--; return tmp;

.... // upis u FIFO if (length != MAX_SIZE) fifo_write(data); .... // čitanje iz FIFO if (length) data = fifo_read(); ....

Slika 80: Pojednostavljenje FIFO funkcija korišćenjem bitwise maski

Page 68: Digitalni_Mikrokontroleri

Mikrokontroleri 67

Katedra za elektroniku

Pogodno izabrana veličina bafera omogućava kreiranje oba indeksa u formi cirkularnih binarnih brojača, korišćenjem bitwise AND logičkih maski (vežba 5, brojači modula 2n). Iz funkcija upisa i čitanja izbačeni su i testovi za pun i prazan FIFO bafer. Umesto toga, testiranje se vrši na mestu poziva ove dve funkcije, jer se i u prethodnom slučaju (slika 79) testiranje na mestu poziva mora izvršiti, samo što se tada testira rezultat pozvane funkcije, a ne broj podataka u FIFO baferu. Konačni rezultat ovakve modifikacije je funkcionalno isti, ali je dobijeni kôd efikasniji, jer se sada funkcije upisa i čitanja ne pozivaju ako to nema smisla (zbog broja podataka u baferu). U primerima 79 i 80 je podrazumevana veličina FIFO bafera manja od 256 reči, zbog čega su promenljive rd_index, wr_index i length definisane kao uchar (unsigned char). Za veće vrednosti treba koristiti odgovarajuće tipove.

3.10.1. Pitanja za proveru

1. Šta se podrazumeva pod pojmom ‘Super-petlje’? 2. Navesti prednosti i mane izvršavanja programa u super-petlji. 3. Objasniti pojam događaja (Event) i način njegovog korišćenja. 4. Šta je FIFO bafer i kako se koristi? 5. Navesti i ukratko objasniti najmanje dva načina realizacije FIFO bafera.

3.11. Softverske mašine konačnih stanja (FSM) U analizi sekvencijalnih podataka, vrlo često se koristi princip mašine sa konačnim brojem stanja (FSM - Finite State Machine), ili kraće, mašine stanja. Osnovna ideja ove metode je podela algoritma za na faze, u kojima se vrši parcijalna analiza podataka (testiranje) i promena tekuće faze na osnovu rezultata analize. Na primer, prema NMEA protokolu za komunikaciju sa GPS modulom, ovaj modul šalje serijskim kanalom razne podatke u ASCII formatu. Jedan od paketa koji se šalje je i informacija o globalnom datumu i vremenu (UTC – Universal Time, Coordinated), koja ima format

$GPZDA,hhmmss.tt,dd,mm,yyyy,zz*cs<CR><LF>

pri čemu je hhmmss.tt vreme, a dd,mm,yyyy datum. Bez detaljnijeg ulaženja u navedeni format, ekstrakcija podataka iz ovog paketa može se vrlo jednostavno izvršiti primenom mašine stanja, kao što je prikazano na slici 81. Prikazani algoritam nije optimizovan i služi prvenstveno za demonstraciju metode mašine stanja.

a) Primer mašine stanja u C jeziku b) Grafički prikaz mašine stanja if (ch = read_com_char()) switch (state) case S0: if (ch==’$’) state = S1; break; case S1: if (ch==’G’) state = S2; else state = S0; break; case S2: if (ch==’P’) state = S3; else state = S0; break; case S3: if (ch==’Z’) state = S4; else state = S0; break; case S4: if (ch==’D’) state = S5; else state = S0; break; case S5: if (ch==’A’) state = S6; else state = S0; break; ....

S0

S1

S2

ch==’$’

ch==’G’

ch!=’G’

ch!=’P’

S3

ch==’P’

ch==’Z’ ch!=’Z’

S4 ch==’D’ch!=’D’

ch!=’$’

S4 ch==’A’ch!=’A’

Slika 81: Primena mašine stanja u analizi sekvence podataka

Page 69: Digitalni_Mikrokontroleri

Mikrokontroleri 68

Katedra za elektroniku

Osnovna ideja mašine stanja je da se stanje (promenljiva state) menja u zavisnosti od jednog ili više uslova (u ovom slučaju, to je vrednost bajta dobijenog iz ulazne sekvence serijskog porta), pri čemu stanje može ostati i nepromenjeno (na pr. S0 u navedenom slučaju). Osim osnovne strukture kao na slici 81a, kada se u pojedinačnim stanjima naredno stanje određuje na osnovu različitih uslova, svako stanje može sadržavati i prateće komande, kojima se realizuju određene akcije, vezane za tekuće stanje. Primer sa slike 81 može se donekle pojednostaviti, ukoliko je potrebno detektovati samo navedenu poruku iz NMEA protokola, slika 82.

const time_msg[6] = ‘GPZDA,’; switch (state) case S0: if (ch==’$’) state = S1; i = 0; break; case S1: if (ch==time_msg[i++]) if (i==6) state = S2; else state = S0; break;

S0

S1ch==’$’

ch==time_msg[i]

ch!=’$’

ch!=time_msg[i]

i==6

Slika 82: Pojednostavljena analiza konstantne sekvence Ovde je broj radnih stanja smanjen, zahvaljujući tome što se jedan deo konstantne sekvence analizira u bez promene stanja (S1), ali se pri tome koristi dodatna promenljiva (i). Primena mašina stanja nije ograničena samo na analizu sekvencijalnih podataka, nego je primenljiva na svim mestima u programu, gde se izvršavanje može realizovati nizom uslovljenih pojedinačnih koraka. Tako na primer, deo programa kojim se vrši merenje u primeru na slici 75b, može se realizovati na način prikazan na slici 83a. Na slici 83b je suštinski ista funkcija, ali zatvorena u beskonačnu petlju.

a) Funkcija realizovana metodom mašine stanja b) Funkcija realizovana u beskonačnoj petlji void merenje(void) switch(m_state) case 0: if (isr_100) isr_100 = 0; value = 0; cnt = 10; m_state++; break; case 1: start_merenja(); m_state++; break; case 2: if (kraj_merenja()) value += read_value(); if (!(--cnt)) m_state++; else m_state--; break; case 3: value /= 10; prikaz_OK = 1; m_state = 0;

void merenje(void) char cnt; while (1) while (!isr_100); isr_100 = 0; value = 0; for (cnt = 10;cnt;cnt--) start_merenja(); while (!kraj_merenja()); value += read_value(); value /= 10; prikaz_OK = 1;

Slika 83: Realizacija merenja sa slike 75b pomoću mašine stanja i kao samostalna petlja

Posmatrajući obe varijante, može se uočiti nezavisnost funkcije merenja, izuzev tri elementa, odnosno promenljive (isr_100, value i prikaz_OK), koje su i jedina veza ove funkcije sa ostalim delovima glavnog programa. Funkcija prikazana na slici 83b primenljiva je samo u varijanti konkurentnog programiranja, kada je moguće naizgled istovremeno izvršavanje više programskih blokova istovremeno (pseudo-paralelni rad). Ako se iz primera 75b preostala dva programska bloka (obrada tastature i prikaz rezultata) realizuju na sličan

Page 70: Digitalni_Mikrokontroleri

Mikrokontroleri 69

Katedra za elektroniku

način kao i funkcija merenja, tako da se dobije osnovna funkcija u formi sa slike 76 (funkcija main), program iz primera 76 se može grafički predstaviti kao što je to prikazano na slici 84a.

main

obrada tastature

merenje

prikaz

main

obrada tastature

merenje

prikaz

a) Primer programa sa mašinama stanja b) Primer programa sa beskonačnim petljama

Slika 84: Grafički prikaz programa sa slike 76

Ako se sve tri pomoćne funkcije sa slike realizuju sa beskonačnim petljama, prema primeru 83b, dobija se forma grafički prikazana na slici 84b. Kako svaka od ovih pojedinačnih funkcija izvršava neki deo programa koji se može tretirati kao celina, odnosno poseban zadatak, ovakve funkcije se često nazivaju taskovi (Task – zadatak), a za izvršavanje ovakvih funkcija potrebno je koristiti tzv. multitasking operativni sistem. Prednost ovakvih operativnih sistema je u tome, što se kompletan program može izdeliti na manje, funkcionalno zaokružene celine, koje se izvršavaju naizgled istovremeno, odnosno paralelno (tačnije, pseudo-paralelno), iako je sve realizovano na jednom mikroprocesoru (ili mikrokontroleru). Ako se uporede grafički prikazi 84a i 84b, može se videti da i način realizacije 84a omogućava privid paralelnog izvršavanja, pri čemu trajanje izvršavanja pojedinih funkcija (taskova) u svakom prolazu kroz mašinu stanja varira, zavisno od složenosti pojedinačnih koraka (stanja). Da bi se mašina stanja mogla efikasno primeniti na program koji je sličan radu sa multitasking operativnim sistemom, važno je da se svaki korak (stanje) mašine stanja dovoljno brzo izvršava. Osnovni razlozi za sporije izvršavanje unutar mašine stanja mogu biti petlje, poziv neke funkcije, ili složeniji matematički izraz. Zbog toga sve petlje koje se pojavljuju u okviru mašine stanja treba dekomponovati prema principima iz vežbe 4, slika 50. Primer dekomponovanja FOR petlje se vidi na slici 83, kada je FOR petlja sa slike 83b dekomponovana na više koraka (83a). Inicijalizacioni deo ove FOR petlje je sadržan u okviru stanja 0, dok je telo petlje sadržano u stanjima 1 i 2 (dva stanja su potrebna zato što se unutar FOR petlje nalazi i jedna WHILE petlja). Generalno, FOR petlja koristi najmanje dva stanja, jedno za inicijalizacioni deo petlje i drugo za testiranje, finalizacioni deo i telo petlje, dok je za WHILE i DO-WHILE petlje dovoljno jedno stanje, kao što je prikazano na slici 85.

Standardna petlja Petlja u mašini stanja FOR petlja

for (inicijalizacija;test;finalizacija) telo_petlje;

case x : inicijalizacija; state++; break; case x+1: if (test) telo_petlje; finalizacija; else state++; break;

WHILE petlja while (test) telo_petlje;

case x: if (test) telo_petlje; else state++; break;

DO-WHILE petlja do telo_petlje;

case x: telo_petlje; if (!test)

Page 71: Digitalni_Mikrokontroleri

Mikrokontroleri 70

Katedra za elektroniku

while (test); state++; break;

Slika 85: Korišćenje petlji u mašini stanja

Ako se iz mašine stanja poziva neka funkcija čije izvršavanje može duže trajati, tada i ovakve funkcije treba realizovati u formi mašine stanja, pri čemu se mora obezbediti indikacija da je pozvana funkcija u potpunosti završila svoju sekvencu, odnosno postavljeni zadatak. Na primer:

Pozvana funkcija je inicijalno u stanju 0 i to stanje ponovo dostiže kompletiranjem definisanih koraka. Rezultat ove funkcije je tekuće stanje mašine stanja, slika 86.

Osnovna funkcija Pozvana funkcija case x: if (sub_func()==0) state++; break;

char sub_func(void) ..... return sub_state;

Slika 86: Poziv funkcije iz mašine stanja u jednom koraku U ovom slučaju, u mašini stanja osnovne funkcije dovoljno je jedno stanje za poziv pod-funkcije, jer se stanje osnovne mašine stanja neće promeniti sve dok pod-funkcija u potpunosti ne završi svoj ciklus, odnosno dok se mašina stanja pozvane funkcije ne vrati u početno stanje. Ako osim pozvane funkcije, još neki uslov može izazvati promenu stanja u osnovnoj funkciji, može se desiti da se stanje u pozvanoj funkciji ne vrati na početnu vrednost, jer se i postupak u ovoj funkciji ne izvede do kraja. U tom slučaju mašina stanja u pod-funkciji može koristiti formalni parametar kao promenljivu stanja, slika 87. Na ovoj slici, u stanju x+1 osnovne funkcije, izlaz iz ovog stanja može nastupiti i kao rezultat promenljive dodatni_uslov, zbog čega mašina stanja funkcije sub_func može zaostati u nekom među-stanju. U navedenom primeru ovo je sprečeno tako što se poziv funkcije sub_func izvršava u dva koraka, prvo korakom x radi inicijalizacije, a zatim korakom x+1 radi izvršavanja.

Osnovna funkcija Pozvana funkcija case x : sub_state = 0; state++; break; case x+1: sub_state = sub_func(sub_state); if (sub_state==0 || dodatni_uslov!=0) state++; break;

char sub_func(char sub_state) ..... return sub_state;

Slika 87: Poziv funkcije iz mašine stanja u dva koraka

Promenljive svih mašina stanja uvek moraju biti globalne, jer moraju zadržati vrednost i nakon napuštanja funkcije u kojoj se koristi mašina stanja. Neki softverski mehanizmi, koji se inače koriste u multitasking operativnim sistemima, primenljivi su i na programe realizovane metodom mašine stanja:

Event (događaj) – Indikator da se neki događaj dogodio. Koristi samo dve vrednosti, najčešće 0 i 1, zbog čega je za događaj moguće koristiti bit promenljivu, ukoliko mikrokontroler podržava takav tip promenljivih. Promenljiva događaja se postavlja na 1 u delu kôda u kome se događaj dogodio, a testira se i poništava u delu kôda u kome se koristi (testira radi skretanja toka programa). U suštini, događaj je jednobitna varijana semafora.

Semaphore (semafor) – Promenljiva koja ima više od jedne vrednosti. U različitim delovima programa vrednost semafora se povećava ili smanjuje, a testiranje semafora daje informaciju o tome da li je vrednost semafora različita ili jednaka nuli. Na primer, FIFO bafer može da koristi princip semafora, pri čemu je semafor vezan za broj podataka u baferu.

Message (poruka) – Poruka je podatak koji se iz jednog dela programa (na primer taska) šalje nekom drugom tasku. Poruka može biti sastavljena od jedne ili više promenljivih standardnog C tipa (na primer, dve int promenljive, pokazivač ili slično), pri čemu i pošiljalac i primalac moraju tačno znati kakav je format poruke.

Mailbox (poštansko sanduče) – Bafer koji može da sakupi više poruka, koristeći princip FIFO bafera.

Page 72: Digitalni_Mikrokontroleri

Mikrokontroleri 71

Katedra za elektroniku

Vrlo često se događaj i semafor izjednačuju i tretiraju kao jednobitna vrednost. Takođe, poruke i poštansko sanduče mogu biti tretirani na isti način, tako da za pravo značenje ovih termina treba konsultovati uputstvo za operativni sistem koji se koristi. Kada se radi o multitasking operativnim sistemima namenjenim za srednje i niže klase mikrokontrolera, uglavnom se koriste događaji (nazvani Event ili Semaphore) i poruke (nazvane Message ili Mailbox). Primer funkcija za rad sa događajima prikazan je na slici 88.

Makro funkcije Standardne funkcije #define SetEvent(ev) do ev = 1; while(0) #define TestEvent(ev) ((tmp=(ev)),((ev)=0),tmp) bit ev_Timer,ev_Prikaz,tmp; .... SetEvent(ev_Timer); ... if (TestEvent(ev_Timer)) ....

#define ev_Timer 1 #define ev_Prikaz 2 char events; void SetEvent(char ev) events |= ev; char TestEvent(char ev) char tmp = events & ev; events &= ~ev; return (tmp!=0); .... SetEvent(ev_Timer); ... if (GetEvent(ev_Timer)) ...

Slika 88: Primer funkcija za rad sa događajima Program na bazi mašina stanja može biti realizovan u formi sličnoj jednostavnijem operativnom sistemu, uz slične mehanizme kontrole rada taskova, komunikacije između taskova i slično. Na primer, svi parametri za jedan task mogu biti složeni u odgovarajuću strukturu (TCB) kao na slici 89. Na slici su prikazane i neke osnovne funkcije ovakvog sistema (system.c), kao i primer osnovnog programa (app.c). system.c app.c typedef char (* TASK_FUNC)(char state); typedef struct TASK_FUNC tadr; char tstate; ... TCB; TCB tlist[MAX_TASK]; char tcount,taskID; void OSInit(void) tcount = 0; taskID = 0; void OSCreateTask(TASK_FUNC task) tlist[tcount].tadr = task; tlist[tcount++].tstate = 0; void OSRun(void) while (1) tlist[taskID].tstate = tlist[taskID].tadr(tlist[taskID].tstate); taskID = TaskScheduler(taskID);

char Task1(char state) switch(state) case 0: ... case 1: ... return (state); char Task2(char state) ... char Task3(char state) ... void main(void) Init(); OSInit(); OSCreateTask(Task1); OSCreateTask(Task2); OSCreateTask(Task3); OSRun();

Slika 89: Program na bazi mašina stanja, u formi operativnog sistema Sistemske funkcije su smeštene u modul system.c, dok se aplikacija nalazi u modulu app.c. Sistemska funkcija TaskScheduler, nakon analize parametara svih taskova, određuje koji će task biti aktiviran.

Page 73: Digitalni_Mikrokontroleri

Mikrokontroleri 72

Katedra za elektroniku

Program realizovan po principu mašine stanja ne zahteva nikakav dodatni kôd, koji je inače neophodan kada se koriste operativni sistemi (čak i najjednostavniji), zbog čega je ovaj metod programiranja pogodan na mikrokontrolerima koji imaju malo memorije. Prednost primene mašine stanja je u tome što se kompletan program realizuje slično multitasking operativnim sistemima, podelom programa na manje celine (taskove), uz mogućnost korišćenja većine mehanizama komunikacije između pojedinih taskova (događaji, poruke i slično). Jedan ciklus prolaza kroz sve taskove može biti znatno kraći kada se koriste mašine stanja, jer nema operativnog sistema koji dodatno troši procesorsko vreme. Najveća mana primene mašina stanja je u složenosti pisanja programa, jer se početna programska struktura mora vrlo pažljivo izdeliti na pojedinačne korake, odnosno stanja.

3.11.1. Pitanja za proveru

1. Šta je mašina stanja (FSM)? 2. Da li se mašina stanja može iskoristiti za analizu sekvencijalnih podataka? 3. Na koji način se više funkcija realizovanih u formi beskonačne petlje mogu zameniti mašinama

stanja? 4. Na koji način se standardne C petlje mogu implementirati u okviru mašine stanja? 5. Šta se dobija proširenjem super-petlje funkcijama na bazi mašina stanja? 6. Šta je task? 7. Navesti bar dva načina komunikacije između taskova.

3.12. Cooperative operativni sistem za rad u realnom vremenu

Program mikrokontrolera često je moguće podeliti na logičke celine, kao što je to prikazano u primeru primene mašine stanja. Pogodnost podele je u tome, što se ovakve logičke celine mogu lakše testirati nego kompletan program. Osim toga, ako se unapred precizno definišu međusobne veze pojedinih celina, kao i funkcionalnost svake logičke celine, pisanje programa se može podeliti na više programera, čime se ubrzava pisanje programa. Ako se logičke celine organizuju tako da se mogu realizovati u formi beskonačne petlje, dobija se program u formi sa slike 84b. Međutim, kako se kompletan program uglavnom izvršava na jednom mikrokontroleru, odnosno mikroprocesoru, koji nema mogućnost istovremenog (paralelnog) izvršavanja više programskih tokova, mora se primeniti neka metoda koja omogućava naizgled istovremeno izvršavanje (pseudo-paralelni rad), kao što je to i u slučaju primene mašine stanja. Za ove svrhe najčešće se koriste multitasking operativni sistemi za rad u realnom vremenu (RTOS – Real Time Operating System). Izraz multitasking znači da je program podeljen na logičke celine (zadatke – taskove), koji se izvršavaju u stilu multipleksa, tj. u svakom trenutku izvršava se samo jedan task, pri čemu se, po određenim pravilima, izvršavanje prebacuje sa jednog na drugi task, kao na slici 90 (aktivan task je posebno označen).

task 1

task 2

task 3

task 4operativni

sistem

Slika 90: Osnovni princip rada multitasking RTOS-a

Page 74: Digitalni_Mikrokontroleri

Mikrokontroleri 73

Katedra za elektroniku

Zavisno od načina koji se koristi za selekciju aktivnog taska, može se napraviti osnovna podela na:

Cooperative RTOS – Selektovanje aktivnog taska se vrši iz tekućeg taska, pozivom odgovarajuće funkcije operativnog sistema.

Preemptive RTOS – Selektovanje aktivnog taska se vrši forsirano, od strane operativnog sistema, a inicirano nekim hardverskim ili softverskim događajem, ili vremenskim intervalom.

Kako u kooperativnom sistemu nema forsirane promene aktivnog taska, suština ovog sistema je u ‘dobrovoljnoj’ kooperativnosti svih taskova, što znači da u svakom tasku treba dovoljno često pozivati neku od sistemskih funkcija kojom se menja aktivni task, kao što je u primeru programa sa tri taska na slici 91.

void task1(void) while(1) OS_Switch(); ....

void task2(void) while(1) OS_Switch(); ....

void task3(void) while(1) OS_Switch(); ....

Slika 91: Primer jednostavnog programa sa tri taska

Kao što se sa slike vidi, svi taskovi su u formi standardnih C funkcija, realizovani u beskonačnoj petlji. Kooperativnost taskova se ogleda u pozivanju funkcije OS_Switch, koja je (u ovom primeru) funkcija za promenu aktivnog taska. U okviru ovakve sistemske funkcije, koriste se različiti metodi za određivanje koji će task biti sledeći aktivan, odnosno koji će task nastaviti rad tamo gde je prethodno bio prekinut. Radi efikasnijeg utvrđivanja narednog aktivnog taska, za svaki task se koristi informacija o trenutnom stanju, na primer:

Active (aktivan) – Task je aktivan (upravo se izvršava). Ready (spreman) – Task nije aktivan, ali je spreman za rad i može se aktivirati. Wait (u čekanju) – Task je u stanju čekanja na neki događaj ili istek vremenskog intervala i nije

spreman za aktiviranje. Suspended (suspendovan) – Task je privremeno suspendovan i ignoriše se sve do ukidanja ovog

stanja. U svakom trenutku najviše jedan task može biti aktivan i to je onaj task koji se upravo izvršava. Pri analizi narednog aktivnog taska operativni sistem može aktivirati samo task koji je spreman za aktiviranje (Ready). Dodatni parametar za određivanje aktivnog taska može biti prioritet, ako je podržan operativnim sistemom. U ovom slučaju, za svaki task se definiše i prioritet, a pri određivanju narednog aktivnog taska, u slučaju da je više taskova u Ready stanju, biće aktiviran task najvišeg prioriteta. Konačno, ako ima više taskova spremnih za rad, sa istim prioritetom, najčešće se primenjuje Round Robin metoda, što znači da će taskovi koji su u Ready stanju i istog prioriteta biti aktivirani naizmenično. Osnovna karakteristika operativnih sistema za mikrokontrolere je što se na mikrokontrolerima uvek izvršava samo jedna aplikacija, koja može sadržavati više taskova. Sa druge strane, na složenim operativnim sistemima opšte namene moguće je izvršavanje više aplikacija istovremeno, pri čemu svaka aplikacija može biti sastavljena i iz više taskova (u ovom slučaju se koristi i naziv Thread umesto Task). Pri tome, jedna od karakteristika aplikacije je da se za svaku aplikaciju koristi poseban memorijski prostor, dok se za sve taskove unutar aplikacije koristi jedinstven memorijski prostor. Kako su mikrokontroleri po pravilu ograničeni sa memorijskim resursima, programi za mikrokontrolere se uobičajeno sastoje od samo jedne aplikacije koja je unapred isprogramirana i najčešće je smeštena u internoj ROM memoriji. Rad sa više taskova omogućava jednostavnije i efikasnije programiranje, ali sa donosi i neke probleme koji u klasičnim načinima programiranja (super-petlja, mašine stanja) nisu postojali. Ovo se prvenstveno odnosi na problem steka. Na primer, neka je program sastavljen od dva taska, Task1 i Task2, pri čemu svaki od taskova poziva po jednu funkciju nižeg nivoa, func_t1 i func_t2, kao na slici 92.

Page 75: Digitalni_Mikrokontroleri

Mikrokontroleri 74

Katedra za elektroniku

Stek

A1 B1 C1 D1

void Task1(void) while (1) ... OS_Switch(); ... func_t1(); .... void func_t1(void) .... OS_Switch(); ....

A2

B2C2

D2

void Task2(void) while (1) ... OS_Switch(); ... func_t2(); .... void func_t2(void) .... OS_Switch(); ....

S3 S2 S1

... ret_t2 ret_t1 SP ...

Slika 92: Primer programa sa dva taska

U prvom trenutku, aktivan je Task1, u tački A1 se aktivira Task2, a zatim u tački A2 drugog taska, ponovo se aktivira Task1. Na ovom mestu, stek se nalazi na poziciji S1, definisanoj pokazivačem steka SP. Nakon ovoga, iz prvog taska pozove se funkcija func_t1 (pozicija B2). Pozivom ove funkcije, stek se puni povratnom adresom na poziciji S2 (ret_t1 – adresa povratka u Task1). Unutar funkcije func_t1, poziva se operativni sistem (OS_Switch) na poziciji D1 i Task2 nastavlja sa izvršavanjem. U drugom tasku sada se poziva funkcija func_t2 (pozicija B2), zbog koje se pokazivač steka ponovo pomera (na poziciju S3), a na steku se sada nalazi i povratna adresa ret_t1 (adresa povratka u Task2). Konačno, u funkciji func_t2 poziva se operativni sistem (OS_Switch na poziciji D2), koji kontrolu vraća prvom tasku, odnosno funkciji func_t1. Funkcija func_t1 završava sa radom i povlači sa steka povratnu adresu (sa pozicije S3). Međutim, umesto povratne adrese ret_t1 unutar prvog taska (pozicija C1 u prvom tasku), na steku se nalazi adresa ret_t2, koja označava poziciju C2 u drugom tasku. Zbog toga se povratak iz funkcije func_t1 preusmerava na poziciju C2 drugog taska, a predviđeni tok izvršavanja programa se narušava i postaje nepredvidljiv. Kao što je već rečeno, u kooperativnom sistemu se promena aktivnog taska može izvršiti samo po posebnom zahtevu, odnosno pozivanjem odgovarajuće funkcije operativnog sistema. Imajući ovo u vidu, navedeni problem steka može se prevazići sledećim načinima:

1. Funkcije za promenu aktivnog taska se pozivaju isključivo u glavnoj funkciji taska. 2. Funkcije za promenu aktivnog taska mogu se pozivati i iz funkcija nižeg nivoa (funkcije koje su

pozvane iz funkcije taska) ako svaki task ima svoj sopstveni stek dovoljne veličine, Drugi način zahteva znatno više RAM memorije, jer stek za svaki task mora imati dovoljno prostora za kompletno stablo poziva svih funkcija iz osnovnog nivoa taska, uključivši i potreban prostor za povratne adrese (i eventualne dodatne parametre) svih prekida. Ako se koristi prvi način, to automatski znači da u kooperativnom sistemu sve funkcije nižeg nivoa (koje se pozivaju iz osnovnog nivoa task funkcije) moraju biti brze, odnosno ovakve funkcije ne smeju previše zadržavati procesor, jer se u suprotnom potpuno gubi osnovni smisao multitaskinga. U slučaju da neka funkcija nižeg nivoa ipak mora sadržavati petlje koje mogu imati dugo vreme izvršavanja, tada od takve funkcije treba napraviti novi task. Generalni problem koji se može pojaviti u primeni multitasking operativnog sistema na mikrokontrolerima sa malo RAM memorije potiče od metode preklapanja (overlay) lokalnih promenljivih. U klasičnom načinu programiranja, linker vrlo lako može da odredi stablo poziva funkcija, koje je i ključni element za metodu preklapanja. Međutim, multitasking operativni sistem zahteva određene manipulacije (što se prvenstveno odnosi na manipulacije sa povratnim adresama na steku), zbog čega i pri kreiranju stabla poziva funkcija može doći do greške, tako da se preklapanje izvrši na lokalnim promenljivama koje se inače ne smeju preklapati. Da bi se ovo izbeglo, moguće je koristiti više rešenja:

1. Sve lokalne promenljive treba deklarisati kao static, što automatski znači da ove promenljive zadržavaju svoju vrednost i nakon napuštanja funkcije u kojoj su definisane, kao i da su izuzete u metodi preklapanja promenljivih.

2. Umesto lokalnih promenljivih koriste se globalne promenljive.

Page 76: Digitalni_Mikrokontroleri

Mikrokontroleri 75

Katedra za elektroniku

3. Umesto lokalnih promenljivih koriste se globalne promenljive u formi unija, koje omogućavaju programirano preklapanje za funkcije koje nisu istovremeno aktivne (simulacija metode preklapanja).

4. Ako to kompajler (odnosno linker) dozvoljava, svaki task treba definisati kao posebno stablo poziva, pri čemu se funkcija taska nalazi na vrhu ovog stabla. Ovo omogućava primenu metode preklapanja unutar svakog stabla, ali bez preklapanja između različitih stabala poziva. Međutim, ova mogućnost nije standardna i za njeno koriščenje treba detaljno proučiti korisničko uputsvo za primenjeni kompajler.

Primer jednostavnog multitasking programa prikazan je na slici 93.

void main(void) OSInit(0); OSCreateTask(Task1); OSCreateTask(Task2); OSCreateTask(Task3); OSRun();

void Task1(void) static char led; while (1) RED = ~led; OS_Sleep(250); led++;

void Task2(void) static char led; while (1) YELLOW = ~led; OS_Sleep(200); led++;

void Task3(void) static char led; while (1) GREEN = ~led; OS_Sleep(150); led++;

Slika 93: Primer kooperativnog multitasking programa Napomena: Primer je baziran na malom multitasking operativnom sistemu FEMTOS, napisanom specijalno za ove vežbe.

Glavna funkcija (main) se ovde koristi samo za inicijalno postavljanje parametara i startovanje operativnog sistema. Pre svega, potrebno je pozvati funkciju OSInit, koja inicijalizuje operativni sistem, Zatim, svi taskovi programa se registruju pozivom funkcije OSCreateTask, nakon čega se operativni sistem startuje funkcijom OSRun. Svi taskovi su realizovani u formi standardne C funkcije sa beskonačnom petljom, u kojoj se poziva sistemska funkcija OS_Sleep. Ova funkcija prebacuje odgovarajući task u režim čekanja u navedenom trajanju (u ms), uz promenu aktivnog taska. U funkciji Task1, OS_Sleep(250) znači da se kontrola prepušta operativnom sistemu (i nekom drugom tasku), u trajanju od 250 ms, što znači da se Task1 neće ponovo aktivirati sve dok ne istekne navedeni interval od 250 ms. Kako sva tri taska koriste relativno dugačke intervale čekanja, najveći deo procesorskog vremena će biti potrošen na operativni sistem, odnosno na interne funkcije koje analiziraju trenutno stanje pojedinih taskova i određuju da li neki task treba aktivirati ili ne. Fukcionalno, kompletan program se ponaša kao tri nezavisne celine, od kojih svaka menja stanje LED grupe po isteku zadatog vremena. Crvene LED menjaju stanje svakih 250 ms, žute svakih 200 ms i zelene svakih 150 ms. Iako je pisanje programa uz primenu multitasking operativnog sistema očigledno jednostavnije nego u klasičnim formama (super-petlja, mašine stanja), pri programiranju se mora voditi računa o međusobnim zavisnostima pojedinih taskova. U primeru na slici 94 je prikazan slučaj koji dovodi do blokade programa:

void Task1(void) ... while (1) ... OS_WaitSem(ev_1,0); OSSetSem(ev_2); ...

void Task2(void) ... while (1) ... OS_WaitSem(ev_2,0); OSSetSem(ev_1); ...

Slika 94: Primer blokiranja multitasking programa

U ovom slučaju, Task1 prepušta kontrolu operativnom sistemu uz čekanje na semafor ev_1. Nakon toga, aktivira se Task2, koji takođe prepušta kontrolu operativnom sistemu uz čekanje na semafor ev_2. U funkciji OS_WaitSem, drugi parametar je alternativno vreme čekanja (TimeOut), nakon čijeg isteka task može biti aktiviran i ako se odgovarajući semafor nije aktivirao. U oba slučaja, ovo vreme čekanja je postavljeno na 0, što znači da čekanje na odgovarajući semafor nije ograničeno. Kako su sada oba taska u stanju čekanja,

Page 77: Digitalni_Mikrokontroleri

Mikrokontroleri 76

Katedra za elektroniku

sistem je blokiran, jer ni jedan task ne može nastaviti rad zbog toga što se ni jedan od dva semafora neće aktivirati. Ovaj problem je u literaturi poznat pod pojmom Dead-Lock. Ako je programer morao pažljivo da definiše sva moguća radna stanja u metodi programiranja sa mašinama stanja, u primeni multitasking operativnih sistema potrebno je pažljivo programiranje uslova aktiviranja taskova, da bi se izbegli slučajevi kao što je primer sa slike 94. Ako se napravi poređenje jednostavnih programa rešenih metodom mašine stanja i primenom multitasking operativnog sistema, može se zaključiti da primena mašine stanje daje efikasan i znatno manji kôd, kod koga se ne pojavljuju problemi sa stekom, ali je programiranje složenije, dok se primenom multitasking operativnog sistema pisanje programa znatno pojednostavljuje, uz veći izlazni kod i uz eventualne probleme sa stekom.

3.12.1. Pitanja za proveru

1. Šta je multitasking operativni sistem? 2. Dati grafički prikaz jednostavnog multitasking operativnog sistema. 3. Koje su osnovne razlike između Cooperative i Preemptive operativnih sistema? 4. Navesti razlike pri primeni mašine stanja i multitasking operativnog sistema. 5. Navesti potencijalne probleme steka u kooperativnom sistemu i moguća rešenja. 6. Koji problemi mogu da nastanu ako kompajler (linker) koristi metodu preklapanja (overlay) u

primeni multitasking operativnog sistema? Dati neke načine prevazilaženja ovih problema. 7. Navesti osnovne prednosti i mane u primeni multitasking operativnih sistema u odnosnu na klasične

načine pisanja programa. 8. Objasniti pojam Dead-Lock?

3.13. Preemptive operativni sistem za rad u realnom vremenu

Za razliku od operativnog sistema kooperativnog tipa, u Preemptive multitasking operativnim sistemima kooperativnost taskova nije neophodna, jer postoji forsirana izmena aktivnih taskova u okviru samog operativnog sistema. Ako se uporede ova dva tipa operativnih sistema, može se reći i da je Preemptive varijanta nadgradnja Cooperative varijante dodatnim mogućnostima, kojima je obezbeđeno da taskovi funkcionišu i u slučaju kada se u nekom tasku, u dužem vremenskom intervalu uopšte ne poziva operativni sistem. Dodatni mehanizmi za aktiviranje taska koji je spreman za rad mogu biti:

Aktiviranje iz prekidne funkcije sistemskog tajmera. Aktiviranje nakon nekog događaja koji prioritetni task dovodi u stanje Ready (spreman za rad).

Takođe, za svaki task može biti definisan prioritet, koji obezbeđuje da se uvek aktivira task koji je spreman za rad, a ima najviši prioritet. Ako su svi taskovi istog prioriteta, tada se može reći da se task izvršava do trenutka poziva sistemske funkcije za aktiviranje taska (Task-Scheduler, odnosno Context-Switcher), a najduže do prekida sistemskog tajmera koji takođe poziva funkciju za aktiviranje taska. Drugim rečima, svaki task ima TimeOut (ograničenje trajanja jednom radnog ciklusa). Na slici 95 je prikazan primer načina raspodele procesorskog vremena po taskovima.

Task

Vreme

1

2

3

T T T T

P3

T T

P1 P2 Slika 95: Vremenska raspodela rada taskova

Page 78: Digitalni_Mikrokontroleri

Mikrokontroleri 77

Katedra za elektroniku

Interval označen sa T (najčešće nazvan Time Slice) je interval definisan sistemskim tajmerom, koji obezbeđuje da se po isteku tog intervala rad tekućeg taska forsirano prekida, a kontrola se predaje narednom tasku koji je spreman za rad. U tačkama P1 i P2 tekući task poziva neku od sistemskih funkcija za promenu aktivnog taska (kao u Cooperative načinu rada), zbog čega se kontrola takođe predaje narednom spremnom tasku. Tačka P3 prikazuje reagovanje na pojavu događaja koji se očekuje u tasku 3, ako je ovaj task višeg prioriteta od ostalih taskova (u ostalim delovima slike podrazumeva se da svi taskovi imaju isti prioritet). Kada se radi sa taskovima različitih prioriteta, način izbora narednog aktivnog taska je od posebnog značaja. Osnovno pravilo je da se uvek bira task sa najvišim prioritetom, koji je spreman za aktiviranje (nalazi se u Ready stanju). Ako ima više takvih taskova, za selekciju aktivnog taska se najčešće koristi Round Robin metoda, odnosno ciklična selekcija, slika 96.

T1 P1

T2 P1

T4 P2

T3 P1

Slika 96: Smenjivanje taskova Round Robin metodom

U ovom primeru taskovi T1, T2 i T3 imaju niži prioritet (P1), dok task T4 ima viši prioritet (P2). Ukoliko četvrti task ni u jednom trenutku ne ulazi u stanje čekanja (vremenski interval ili neki događaj), tada ni jedan od preostala tri taska ne mogu biti aktivirana, jer su nižeg prioriteta, a task T4 je uvek ili aktivan, ili spreman za rad. Zbog toga se viši prioritet uobičajeno dodeljuje tasku koji treba brzo da reaguje na neki događaj, a najviše vremena provodi baš u čekanju na taj događaj, što daje šansu ostalim taskovima da normalno rade. Međutim, i u tom slučaju može se pojaviti problem, ukoliko izbor aktivnog taska uvek počinje od narednog taska u odnosu na tekući. Neka su u datom primeru, taskovi T1, T2 i T3 (niži prioritet) uvek u stanju Ready, odnosno koriste Time Slice za svoj rad (periodično aktiviranje), a neka task T4 treba da reaguje na spoljašnje hardverske impulse. Ako je trenutno aktivan task T1 (označeno na slici), a pre isteka radnog intervala (period T na slici 95) dođe do pojave spoljašnjeg impulsa, biće preskočeni taskovi T2 i T3, a aktiviraće se task T4. Nakon obrade impulsa, task T4 poziva operativni sistem i prepušta mu kontrolu, a sistemska funkcija za izbor aktivnog taska, pošto počinje analizu od prvog narednog taska, ponovo aktivirati task T1. Ako se ovakva sekvenca stalno ponavlja, odnosno spoljašnji impuls nastupa suviše često, taskovi T2 i T3 će se vrlo retko (ili nikad) aktivirati. Ovaj problem se može jednostavno prevazići ako se za izbor aktivnog taska koristi princip ulančane liste. U ovom slučaju, analiza pri izboru narednog aktivnog taska ne počinje od prvog narednog taska, nego od početka liste taskova, odnosno liste koja sadrži kontrolne blokove (TCB – Task Control Block). Kada se pronađe odgovarajući task, odnosno prvi task u listi koji ima najviši prioritet od svih taskova koji su u Ready stanju, taj task se proglašava aktivnim, a njegov TCB se vadi iz liste i prebacuje na kraj, kao što je prikazano na slici 97 (šrafiran je task koji prelazi u aktivno stanje):

T1, P1 T2, P1 T3, P1 T4, P2

T2, P1 T3, P1 T4, P2 T1, P1

T2, P1 T3, P1 T1, P1 T4, P2

T3, P1 T1, P1 T4, P2 T2, P1

1

2

3

4

Slika 97: Smenjivanje taskova po principu ulančane liste

Page 79: Digitalni_Mikrokontroleri

Mikrokontroleri 78

Katedra za elektroniku

Na slici su prikazana četiri koraka, sa pretpostavkom da se naizmenično aktiviraju task višeg prioriteta (T4) i jedan od taskova nižeg prioriteta (T1, T2 ili T3). Izbor aktivnog taska uvek počinje od početka liste (na slici sa leve strane), analiziraju se svi taskovi, a aktivira se task koji je spreman za rad, ima najviši prioritet, a nalazi se najbliže početku liste. Jedan način korišćenja prioriteta uz Round Robin metodu, u kome je izbegnuta mogućnost dugotrajnog mirovanja taska sa nižim prioritetom, je korišćenje proporcionalne vremenske raspodele. U ovom slučaju, procentualna raspodela procesorskog vremena po taskovima određena je nivoom prioriteta taskova. Na primer, svaki task radi kontinualno onoliko sistemskih tikova (odnosno prekida sistemskog tajmera), koliko iznosi prioritet tog taska (viši prioritet znači i više sistemskih tikova). Međutim, ovaj princip nije u skladu sa potrebama rada u realnom vremenu, kada je pravovremeno reagovanje na neki događaj od presudne važnosti (brzo aktiviranje prioritetnog taska), zbog čega se ovaj princip ne koristi u operativnim sistemima namenjenim za mikrokontrolere. Operativni sistem kooperativnog tipa može koristiti jedinstven stek, ako se sistemske funkcije za promenu aktivnog taska pozivaju samo iz osnovne funkcije taska (problem sa slike 92). U Preemptive operativnom sistemu ovo se ne može obezbediti, jer trenutak poziva funkcije za promenu taska nije predvidiv sa stanovišta toka izvršavanja programa, zbog čega do promena aktivnog taska može doći i van osnovne funkcije taska, odnosno u toku izvršavanja neke od funkcija nižeg nivoa. Zbog toga se, kod ovakvog operativnog sistema, mora obezbediti poseban stek za svaki task. Ovo znači i znatnu potrošnju RAM memorije, jer svaki pojedinačni stek mora imati dovoljnu veličinu da prihvati sve pozive funkcija (povratne adrese), počevši od osnovnog nivoa taska do najniže dubine u stablu poziva funkcija. Ako se koristi stek metoda za lokalne promenljive, onda to znači da i sve ove promenljive, iz svih funkcija u stablu poziva (po dubini) moraju imati obezbeđen prostor na lokalnom steku taska. Kada se računa dubina poziva funkcija, osim analize stabla poziva, moraju se uzeti u obzir i prekidi, koji takođe koriste stek tekućeg taska, kao i potencijalne bibliotečne i interne C funkcije (na primer, standardno množenje i deljenje u C jeziku može biti realizovano pozivom odgovarajućeg potprograma, u jednostrukoj ili višestrukoj dubini poziva). Ovo je i glavni razlog zbog čega se Preemptive operativni sistemi retko primenjuju na mikrokontrolere sa malo RAM memorije.

3.13.1. Pitanja za proveru

1. Šta je Preemptive multitasking operativni sistem? 2. Zbog čega taskovi u Preemptive operativnom sistemu moraju imati sopstvene stek memorijske

blokove, odnosno zbog čega se ne koristi jedinstven stek? 3. U kom slučaju je veća potrošnja RAM memorije, u Cooperative ili Preemptive načinu rada i zašto? 4. Ako su svi taskovi u Preemptive načinu rada istog prioriteta, da li se može dogoditi da se neki od

taskova nikad ne poziva? 5. Šta se dobija primenom taskova različitih prioriteta? 6. Ako su, u Preemptive operativnom sistemu sa Round Robin mehanizmom, taskovi različitih

prioriteta, da li se može dogoditi da se neki od taskova nikad ne poziva? Objasniti. 7. U slučaju da problem iz pitanja 6 postoji, na koji način se on može prevazići? 8. Da li je, u Preemptive operativnom sistemu, moguće napisati program sa više taskova koji pravilno

rade, iako ni u jednom trenutku ne pozivaju neku od sistemskih funkcija za promenu aktivnog taska?

3.14. I/O podsistem operativnih sistema sa konkretnim primerima

3.14.1. I/O sistem operativnih sistema

Osnovna svrha svakog sistema jeste da na osnovu određenog broja ulaznih podataka dâ određeni broj izlaznih veličina. Šta ti ulazi i izlazi predstavljaju, kao i njihov broj, zavisi od namene sistema. Obično su ulazne veličine izmerene vrednosti dobijene sa senzora koji mere konkretne fizičke veličine u nekom procesu, ili su to komande koje stižu od korisnika sistema. Izlazi su veličine pomoću kojih se upravlja

Page 80: Digitalni_Mikrokontroleri

Mikrokontroleri 79

Katedra za elektroniku

procesom ili obaveštava korisnik o stanju sistema. Pomenuti ulazi i izlazi čine ulazno-izlazni sistem računarskog sistema (U/I sistem, engl. input/output – I/O system).

Pod I/O sistemom se obično podrazumevaju i razne komunikacione linije (UART, I2C itd.), displeji, tastature, jedinice spoljašnje memorije i slično. Jednim imenom, sve se zovu periferijskim jedinicama ili jednostavno periferijama. Obuhvatajući sve pomenuto, I/O sistem može da dostigne prilično visok nivo složenosti i time se stvara potreba da operativni sistem sadrži ceo jedan podsistem posvećen njima – I/O podsistem. Tipična organizaciona šema savremenog operativnog sistema prikazana je na slici 98. Vidimo da se čitav sistem naslanja na hardver. Cilj je da se svi podsistemi naslonjeni na hardver učine u što je moguće većoj meri nezavisnim. Razlog za to je pojednostavljeni dalji razvoj sistema: ako se neki deo sistema izmeni ili proširi, potrebno je izmeniti samo deo koji je odgovoran za taj deo sistema, dok ostali delovi često ne zahtevaju promene. Drugi razlog je taj da neispravnost u jednom delu sistema ne mora da izazove neispravnost rada celog sistema. Time je olakšano i nalaženje grešaka u pojedinim delovima sistema. Jedan korak dalje u razvoju sistema jeste dodavanje još jednog sloja neposredno iznad hardvera – sloj za virtuelizaciju hardvera – čime se dodatno dobija na stabilnosti rada sistema i poboljšava se nezavisnost sistema od konkretnog hardvera koji je prisutan u sistemu.

HARDVER

UPRAVLJANJE PROCESIMA I PREKIDIMA

UPRAVLJANJE MEMORIJOM

I/O PODSISTEM

DRAJVERI

APLIKACIJA

SLOJ ZA VIRTUELIZACIJU

Slika 98: Tipičan savremeni operativni sistem

Može se primetiti da se I/O podsistem oslanja i na upravljanje procesima i prekidima kao i na drajvere. Drugim rečima, drajveri su upravljački programski blokovi koji neposredno pristupaju hardveru. Pošto su i drajveri programske celine, oni predstavljaju procese u sistemu, otuda oslanjanje i na upravljanje procesima i prekidima.

Sam korisnički softver je na vrhu ove strukture. Celokupnu komunikaciju sa hardverom obavlja posredstvom operativnog sistema. Uobičajeni način te komunikacije je pozivanjem potprograma samog operativnog sistema – uobičajeni naziv za te potprograme (funkcija u terminima jezika C) je sistemski pozivi. Treba napomenuti da je u slučaju ovakvih operativnih sistema direktan pristup hardveru, bez posredovanja operativnog sistema, zabranjen i najčešće i onemogućen pomoću zaštitnih mehanizama samog procesora (mikrokontrolera).

3.14.2. I/O sistem pri korišćenju FEMTOS-a

Nedostatak šeme sa slike 1 (pogotovo ako je prisutan i sloj za virtuelizaciju) jeste obavezan gubitak performansi sistema (stabilnost i jednostavnost na račun performansi). Zbog toga se kod malih mikrokontrolerskih sistema, sa skromnim resursima i procesorskom snagom, obično uvode značajna pojednostavljenja u odnosu na šemu sa slike 98. Jedna mogućnost koja se koristi sa real-time operativnim sistemom FEMTOS predstavljena je na slici 99.

Page 81: Digitalni_Mikrokontroleri

Mikrokontroleri 80

Katedra za elektroniku

HARDVER

UPRAVLJANJE TASKOVIMA –

FEMTOS

I/O PODSISTEM

DRAJVERI

DODATNI DRAJVERI I SERVISNE FUNCIJE PREKIDA

APLIKACIJA

Slika 99: Pojednostavljena struktura koja se koristi zajedno sa FEMTOS-om

U ovom slučaju I/O podsistem predstavlja skup funkcija unapred napisanih (i u čiju unutrašnju strukturu korisnik nema potrebe da zalazi) kojima se pojednostavljuje pristup periferijama prisutnim u sistemu. Prednost je u postizanju izvesne nezavisnosti od prisutnog hardvera. Ukoliko se on promeni, dovoljno je promeniti samo C modul koju upravlja tim promenjenim hardverom. Ostali delovi se ne moraju menjati i sistem i dalje ispravno funkcioniše jer način pozivanja I/O sistemskih poziva nije menjan. Promenjen je samo način njihovog rada, a rečeno je da korisnik sa tim i ne mora da bude upoznat.

3.14.3. I/O podsistem na primeru PIC ploče za proširenja

PIC ploča se dodaje na maketu koja je do sada korišćena na laboratorijskim vežbama. U ovom odeljku, ona će biti predstavljena do mere potrebne da se ilustruje I/O podsistem i uradi zadatak same vežbe. Za samu ploču postoji detaljno uputstvo koje sadrži i objašnjenja svih funkcija I/O podsistema. Ovaj dodatak omogućuje dodavanje sledećih elemenata u sistem:

8-cifarski 7-segmentni LED displej

Tastatura sa 16 tastera

Inteligentni LCD displej

4 alnalogna ulaza (sa mogućnošću podešavanja referentnih napona)

2 analogna izlaza

2 kvadraturna inkrementalna enkodera

Centralni element PIC ploče je Microchip PIC 16F877 mikrokontroler koji igra ulogu periferijskog kontrolera. To znači da se za bilo koju ulazno/izlaznu operaciju šalje zahtev periferijskom kontroleru koji dalje rukovodi operacijom. Sam glavni mikrokontroler u sistemu nema neposrednu kontrolu nad zahtevanom operaciojom. Ovakva periferijska jedinica naziva se inteligentnom.

Za gorenavedene ulaze i izlaze se na PIC ploču dodaju odgovarajuća proširenja. U ovoj vežbi, koristiće se analogni ulazi i izlazi kao i LED displej. U nastavku će biti dat kratak opis funkcija I/O podsistema kojima se sprovode operacije nad tim ulazima/izlazima, bez zalaženja u detalje koji nisu neophodni za uspešno izvođenje ove vežbe.

3.14.3.1 Analogni ulazi

Od ukupno četiri analogna ulaza koji su na raspolaganju u ovom sistemu, ne mogu svi istovremeno da se koriste. Postoji nekoliko režima rada kada se svakom ulazu dodeljuje drugačija funkcija. Npr. neki ulazi mogu biti konfigurisani kao digitalni ulazi ili izlazi, kao ulazi na koje se dovode referentni naponi važni za D/A konverziju ili kao analogni ulazi. Veoma je važno da se pre korišćenja bilo kog od njih tačno konfiguriše režim rada koji hoćemo da koristimo.

U ovoj vežbi, koristiće se dva analogna ulaza – prvi (0) i drugi (1). U taj režim rada uvodi poziv sledeće funkcije:

WriteAnalogConfig(0x5F);

Page 82: Digitalni_Mikrokontroleri

Mikrokontroleri 81

Katedra za elektroniku

Ovom funkcijom se bira da su prvi, drugi i četvrti kanal analogni ulazi, a da je treći kanal digitalni ulaz. Takođe se podešava da se naponi na analognim ulazima u opsegu od 0 do 5V konvertuju u osmobitnu binarnu reč. Treći i četvrti kanal neće biti korišćeni (u ovoj vežbi).

Pozivom sledeće funkcije, očitava se analogni napon na nekom od ulaza. Njena deklaracija (šablon) je sledeća:

uchar ReadAnalogInput(uchar channel);

Funkcija vraća osmobitnu neoznačenu vrednost koja predstavlja očitanu vrednost napona na nekom od analognih ulaza. Za izabrani režim rada binarna vrednost 0 odgovara nultom naponu, a 255 naponu nešto manjem (za jedan kvantizacioni nivo) od 5V. Parametrom channel se bira kanal koji se očitava – 0 odgovara prvom, a 1 drugom kanalu.

3.14.3.2 Analogni izlazi

Na raspolaganju su dva analogna izlaza. Nije potrebno posebno ih konfigurisati jer oni obavljaju svoju funkciju neprekidno. Opseg izlaznih napona je od 0 do 5V, a rezolucija je osmobitna. Šablon funkcije kojom se analogni izlaz postavlja je sledeći:

void WriteAnalogOutput(uchar channel,uchar value);

Parametrom channel bira se prvi (0) ili drugi (1) kanal, dok drugi predstavlja osmobitni binarni broj koji će se konvertovati u analogni napon (0 – 0V, 255 – 5V). Povratne vrednosti nema.

3.14.3.3 8-cifarski 7-segmentni LED displej

Ovaj displej predstavlja dosta složen izlazni uređaj čije mogućnosti neće biti u potpunosti predstavljene u ovom tekstu. Biće dat samo jedan veoma ograničen podskup mogućnosti. Koristiće se za prikaz maksimalno 8 karaktera, mada je uz pomeranje prikaza (skrol) u stanju da prikaže i više. Deklaracije (šabloni) i kratki opisi najvažnijih funkcija slede u nastavku.

void CursorOn(void);

void CursorOff(void);

Periferijski kontroler vodi računa o trenutnom poziciji ispisa na displeju. Ta pozicija može biti obeležena (trepćućim) kursorom, ali ne mora. Pomoću prethodne dve funkcije se pravi izbor. Ako se neijedna ne pozove nakon reseta sistema, podrazumeva se da je kursor uključen.

void ClrScr(void);

Briše ceo prikaz sa displeja i postavlja kursor na krajnju levu poziciju.

void Home(void);

Postavlja kursor na krajnju levu poziciju, a pritom ne briše prethodni sadržaj.

void GotoXY(uchar x);

Postavlja kursor na željenu poziciju – pozicija je određena parametrom x.

void WriteChar(uchar ch);

Ispisuje karakter čiji ASCII kod se prosledi kao parametar. Pomera poziciju ispisa na sledeće mesto desno.

3.15. Paketna komunikacija

3.15.1. Master/Slave uređaji

Često se javlja potreba za sinhronizovanim radom više odvojenih uređaja. Razlog za to je najčešće fizička udaljenost između delova sistema ili korisnika sistema, npr. telefonska mreža jedne zgrade ili sistem za prikazivanje tačnog vremena. Karakteristika im je postojanje jednog kontrolera sistema (master-a) i više

Page 83: Digitalni_Mikrokontroleri

Mikrokontroleri 82

Katedra za elektroniku

(izvršnih) uređaja koji nisu samostalni (slejv - slave). Master upravlja svim slejvovima i sinhronizuje njihov rad (slika 100).

MASTER

SLAVE SLAVE SLAVE SLAVE

Slika 100: Tipičan master/slave sistem

Neophodan element u celom sistemu je komunikaciona linija koja povezuje sve navedene elemente. Najčešće je to neki vid serijske komunikacije, a konkretni parametri veze određuju maksimalnu udaljenost do koje veza može da se koristi.

Dalje, mora da se definiše način komunikacije, tzv. protokol. On definiše vremenski sled – kad koji učesnik u komunikaciji šalje podatke i kada ih prima. Takođe određuje i format podataka koji se razmenjuju. Podaci su najčešće podeljeni u neprekidne blokove određene dužine i formata koje neki (samo jedan) od učesnika u određenom trenutku šalje. Uobičajeni naziv za ovakav blok je paket. Može se reći da se komunikacija odvija razmenom paketa između učesnika komunikacije.

Komunikacija može biti jednosmerna i dvosmerna. U prvom slučaju, slejvovi samo primaju komande od mastera, dok u drugom slučaju mogu da odgovaraju na komande mastera i da mu šalju podatke. Dvosmerna komunikacija je pouzdanija jer svaka komanda mastera izaziva odgovarajući odziv slejva i master je u mogućnosti da dijagnostikuje neispravnost nekog slejva, odnosno smetnje na komunikacionoj liniji.

Dvosmerna komunikacija može imati odvojene fizičke veze u oba smera, pri čemu je komunikacija moguća istovremeno u oba smera. Ovakva veza se naziva punom dupleks (full duplex) vezom. Kada se koristi ista veza u oba smera, u nekom trenutku moguća je komunikacija samo u jednom smeru. Ovakva veza je poznata pod imenom polu-dupleks (half duplex) veza.

3.15.2. Format paketa

Svi učesnici komunikacije moraju poznavati strukturu paketa. Tipično, svaki paket se sastoji iz dva dela – zaglavlja (header) i podataka (data) kao što je prikazano na slici 101. Po pravilu, zaglavlje dolazi ispred podataka, mada ima primera i drugačijih formata. Npr., zaglavlje može biti podeljeno na dva dela pri čemu podaci mogu biti u sredini između dva dela zaglavlja.

Zaglavlje po pravilu sadrži informacije o podacima koji su sadržani u delu sa podacima, tipično njihov broj, kontrolna suma, identifikacija uređaja sa kog podaci dolaze itd. Kod složenih komunikacionih protokola koji su implementirani u više slojeva (npr. TCP/IP mrežni protokol), svaki sloj komunikacije dodaje svoje zaglavlje. Nasuprot tome, kod veoma jednostavnih komunikacionih protokola sa kratkim paketima, ponekad je teško napraviti razliku između zaglavlja i podataka.

ZAGLAVLJE PODACI

PAKET

Slika 101: Tipičan format paketa

Page 84: Digitalni_Mikrokontroleri

Mikrokontroleri 83

Katedra za elektroniku

4. Laboratorijske vežbe

4.1. Vežba 1: Organizacija C fajlova Proučiti: Poglavlja 3 i 3.1.

Pomoću Keil razvojnog okruženja za mikrokontrolere iz familije 8051, kreirati novi projekat u direktorijumu grupe, pod nazivom ‘vezba1’. Izabrati mikrokontroler Atmel AT89C52 i podesiti sledeće parametre:

Memory Model: Small: variables in DATA Code Rom Size: Compact: 2K functions, 64K program

Izvršiti sledeće korake: 1. Kreirati tri nova fajla i snimiti ih pod nazivima ‘glavni1.c’, ‘sabiranje.c’ i ‘sabiranje.h’. Oba C fajla

dodati u projektnu listu fajlova. U prozoru projekta dodati grupu ‘H fajlovi’ i u tu grupu dodati fajl ‘sabiranje.h’.

2. U fajlu ‘sabiranje.c’ napisati funkciju saberi koja sabira dva int broja a i b, a rezultat takođe vraća kao int tip.

3. U fajl ‘sabiranje.h’ prebaciti deklaraciju funkcije saberi. 4. U fajlu ‘glavni1.c’ napisati main funkciju koja ima tri lokalne promenljive a, b i c, int tipa, kao i

beskonačnu while petlju. Unutar petlje, promenljivima a i b treba dodeliti proizvoljnu konstantu, a zatim ih kao parametre iskoristiti za poziv funkcije saberi, čiji rezultat treba staviti u c.

5. Na početku fajla ‘glavni1.c’ uključiti fajl ‘sabiranje.h’. 6. Kompajlirati projekat i otkloniti eventualne greške.

Ako je navedeni postupak protekao kako je predviđeno, prevođenje je izvršeno bez greške i može se preći na naredne korake.

7. Pozvati debugger (radi provere programa i otkrivanja eventualnih grešaka). Postaviti prekidnu tačku na poziv funkcije saberi, i startovati program. Otvoriti watch prozor za pregled promenljivih i podesiti vrednosti za a i b na 2 i -2. Izvršiti jedan korak (Step Over) i proveriti rezultat c.

8. Ponoviti postupak iz tačke 7 sa nekoliko različitih vrednosti korišćenjem proizvoljih predznaka. ********************* 9. Isključiti fajl ‘sabiranje.h’ u fajlu ‘glavni1.c’ komentarisanjem odgovarajuće linije. Startovati

kompajler i u build prozoru proveriti sve poruke. Ustanoviti šta ove poruke znače i zbog čega su nastale.

10. Vratiti fajl ‘sabiranje.h’ uklanjanjem komentara u fajlu ‘glavni1.c’, a u fajlu ‘sabiranje.c’, kao tipove parametara funkcije saberi, umesto int navesti unsigned char. Izvršiti prevođenje i proveriti poruke kompajlera u build prozoru. Utvrditi da li su registrovane greške. Ponoviti postupak iz tačaka 7 i 8 i proveriti rezultate. Da li funkcija saberi vraća dobar rezultat? Objasniti zašto.

********************* 11. U fajl ‘sabiranje.c’ uključiti odgovarajućom komandom fajl ‘sabiranje.h’. Izvršiti kompajliranje i

proveriti poruke kompajlera. Da li je kompajler prijavio greške? Zašto? 12. Podešavanjem parametara funkcije saberi otkloniti razloge grešaka pri kompajliranju i ponoviti

kompajliranje. Postupak ponoviti u slučaju grešaka. ********************* 13. Izbrisati fajl ‘sabiranje.c’ iz projektne liste fajlova. Izvršiti kompajliranje i protumačiti prijavljene

greške. ********************* 14. Vratiti fajl ‘sabiranje.c’ u projektnu listu fajlova. Unutar fajla ‘sabiranje.c’, funkciju saberi

deklarisati kao static. Kakve poruke sada prijavljuje kompajler?

Page 85: Digitalni_Mikrokontroleri

Mikrokontroleri 84

Katedra za elektroniku

4.2. Vežba 2: Memorijski modeli, makro preprocesor Proučiti: Poglavlja 3.2 i 3.3.

Pomoću Keil razvojnog okruženja za mikrokontrolere iz familije 8051, kreirati novi projekat u

direktorijumu grupe, pod nazivom ‘vezba2’. Izabrati mikrokontroler Atmel AT89C52 i podesiti sledeće parametre:

Memory Model: Small: variables in DATA Code Rom Size: Compact: 2K functions, 64K program

Izvršiti sledeće korake: 1. Kreirati nov fajl i snimiti ga pod nazivom ‘glavni2.c’, a zatim taj fajl dodati u projektnu listu fajlova. 2. Napisati funkciju zbir za izračunavanje zbira niza brojeva od 0 do N, iterativnim postupkom, za 32-

bitne brojeve bez predznaka. 3. Napisati main funkciju sa beskonačnom petljom, koja u petlji izračunava zbir niza za brojeve 0, 1, 2,

3... korišćenjem napisane funkcije zbir. Definisati globalne promenljive ‘i’ i ‘r’ tipa unsigned long, pri čemu promenljiva ‘i’ predstavlja rastuću vrednost parametra (broj članova niza), a ‘r’ rezultat funkcije zbir. Pratiti ponašanje steka (maksimalne vrednosti pokazivača steka) i promenljivih ‘i’ i ‘r’ nakon svakog poziva funkcije zbir. Utvrditi eventualno postojanje nepravilnosti u izvršavanju programa.

4. Iskomentarisati iterativnu funkciju zbir i napisati novu funkciju zbir koja je istog tipa i koristi isti ulazni parametar kao i prethodna funkcija, ali koristi rekurzivnu metodu za izračunavanje zbira niza brojeva (reentrant funkcija). Konsultovati uputstvo za Keil C51 i proučiti način rada rekurzivnog steka i pomoćne promenljive ?C_IBP.

5. Korišćenjem postojeće main funkcije ponoviti izračunavanje za brojeve od 0 na više i pratiti stanje maksimalne vrednosti pokazivača steka nakon svakog poziva funkcije zbir. Kao i u prethodnom slučaju, utvrditi eventualno postojanje nepravilnosti u izvršavanju programa, kao što je nepredviđeno anuliranje vrednosti globalne promenljive ‘i’. Analizom u disasemblerskom prozoru pratiti stanje promenljive ?C_IBP i utvrditi njenu vrednost u momentu pojave nepravilnosti rada programa. Uporediti to sa trenutnom i maksimalnom vrednošću pokazivača steka. Šta se iz ovoga može zaključiti?

6. Promeniti memorijski model za podatke na large i ponoviti postupak iz tačke 5. 7. Ukloniti komentar za iterativnu verziju funkcije zbir. Definisati makro simbol RE_ENT i iskoristiti

ovaj simbol za uslovno prevođenje jedne ili druge verzije funkcije zbir (rekurzivna varijanta ako je RE_ENT definisan).

8. Sa definisanim simbolom RE_ENT podesiti memorijski model (za podatke) na large i proveriti funkcionalnost kompletnog programa.

9. Iskomentarisati definiciju simbola RE_ENT, podesiti memorijski model (za podatke) na small i ponovo proveriti funkcionalnost programa.

10. Proučiti predefinisani makro simbol __MODEL__ i njegove moguće vrednosti. Iskoristiti ovaj simbol za uslovno definisanje simbola RE_ENT u slučajevima Compact i Large memorijskih modela za podatke. U opcijama kompajlera (Listing) uključiti generisanje preprocesorskog fajla. Za sva tri memorijska modela (podešavanjem u kompajlerskim opcijama) proveriti rezultat preprocesora, analiziranjem odgovarajućeg fajla (ekstenzija .I). Takođe proveriti funkcionalnost programa.

4.3. Vežba 3: Unije Proučiti: Poglavlja 3.4 i 3.5.

Pomoću Keil razvojnog okruženja za mikrokontrolere iz familije 8051, kreirati novi projekat u direktorijumu grupe, pod nazivom ‘vezba3’. Izabrati mikrokontroler Atmel AT89C52 i podesiti sledeće parametre:

Page 86: Digitalni_Mikrokontroleri

Mikrokontroleri 85

Katedra za elektroniku

Memory Model: Small: variables in DATA Code Rom Size: Compact: 2K functions, 64K program

Izvršiti sledeće korake u formiranju programa za maketu sa 24 LED komponente: 1. Kreirati nov fajl i snimiti ga pod nazivom ‘glavni3.c’, a zatim taj fajl dodati u projektnu listu fajlova. 2. U glavni fajl ubaciti H fajl za izabrani mikrokontroler (regx52.h). 3. Pomoću direktive #define, definisati sinonime za char, int i long bez predznaka (unsigned). 4. Kao novi tip deklarisati uniju opisanu na slici 41a, ali sa elementima bez predznaka (unsigned). 5. Definisati promenljivu ext_led, tipa deklarisane unije, i postaviti je u spoljašnji adresni prostor, na

adresu 0x8000. Pomoću direktive #define definisati alternativni naziv za ulazne prekidače, koji odgovaraju najnižem bajtu promenljive ext_led. U internoj memoriji definisati promenljivu int_led, istog tipa kao i ext_led. Direktivom #define definisati naziv LED, kao alternativu promenljivoj ext_led.

6. Napisati četiri funkcije za rotaciju u levo i desno 24-bitne reči (niža tri bajta) promenljive LED, prema principima sa slike 43a) (kao funkcija lrot) i b) (kao funkcija lrot2). Funkcija treba da ima jedan parametar koji određuje za koliko bita se vrši rotacija (od 0 do 23).

7. U glavnoj funkciji (main) postaviti inicijalnu (proizvoljnu) vrednost za niža 24 bita promenljive LED, a u beskonačnoj petlji, na osnovu učitanog stanja prekidača, rotirati 24-bitni deo promenljive LED levo ili desno, za 0 do 23 mesta, koristeći napisane funkcije za rotaciju metodom primene unije. Sa uključenom maketom u simulatoru, proveriti da li dolazi do rotacije simuliranih LED elemenata i izvesti odgovarajući zaključak.

8. U direktivi #define za naziv LED zameniti ext_led sa int_led, a u main funkciji, nakon svakog poziva funkcije za rotaciju, izvršiti pridruživanje sadržaja int_led u ext_led. Uključiti simulator i proveriti kako se sada ponašaju simulirani LED elementi. Izvesti odgovarajući zaključak.

9. U main funkciji, za svaku rotaciju pozvati uzastopce funkcije rotacije metodom primene unije i metodom testa. Pomoću prekidnih tačaka izvršiti merenje vremena izvršavanja svake od funkcija rotacije. Šta se može zaključiti?

10. Korake 8 i 9 ponoviti punjenjem programa na realnu maketu i proveriti da li se dobijaju isti rezultati kao i u simulaciji.

11. Prema principu za funkciju lrot na slici 43b), napisati makro funkciju za rotaciju reči veličine 1, 2 i 4 bajta u levo, pomoću standardne C funkcije sizeof i proveriti rad dobijene makro funkcije.

4.4. Vežba 4: Petlje Proučiti: Poglavlja 3.6 i 3.7.

Kao i u prethodnim vežbama, kreirati novi projekat ‘vezba4’, koristeći isti mikrokontroler i memorijske modele.

1. Napisati sve tri funkcije iz primera sa slike 51, kao i glavnu (main) funkciju, koja ima jednu lokalnu promenljivu veličine jedan bajt, inicijalne vrednosti 1. U beskonačnoj petlji glavne funkcije, pozivati jednu za drugom sve tri funkcije sa petljama, prosleđujući im navedenu lokalnu promenljivu, a nakon toga povećati vrednost lokalne promenljive za jedan. Proveriti u simulatoru rezultat prevođenja analizom asemblerskog rešenja za sve tri funkcije petlji. Izmeriti i vreme izvršavanja svake od funkcija za nekoliko različitih vrednosti broja ponavljanja. Zabeležiti rezultate.

2. U opcijama kompajlera isključiti optimizaciјu (nivo 0) i ponovo analizirati asemblerski rezultat, kao i brzine izvršavanja. Uočiti razlike.

3. U pojedinačnim funkcijama petlji, uvesti lokalne promenljive istog tipa kao i formalni parametar, kojima se pridružuje vrednost formalnog parametra pre startovanja petlje. Unutar petlje, koristiti lokalnu promenljivu umesto formalnog parametra. Ponoviti analizu asemblerskog rezultata za nivoe optimizacije 0 i 8.

4. Modifikovati FOR i WHILE petlje tako da koriste pre-dekrement umesto post-dekrement operacije. Šta je potrebno uraditi pre početka petlje, da bi rezultat funkcije bio identičan kao i pre modifikacije? Ponoviti analizu asemblerskog rezultata sa nivoom optimizacije 8.

5. Ponoviti postupak iz tačke 1, ali korišćenjem unsigned int tipova za sve promenljive.

Page 87: Digitalni_Mikrokontroleri

Mikrokontroleri 86

Katedra za elektroniku

6. Iz C fajla ukloniti funkcije petlji, a ubaciti funkciju za 24-bitnu rotaciju u levo korišćenjem unije, kao i sve neophodne deklaracije za LED ploču (može se iskoristiti C fajl iz vežbe 3).

7. Napisati funkciju za kašnjenje koja se sastoji iz dve petlje, jedna unutar druge, pri čemu se unutrašnja petlja ponavlja konstantan broj puta, dok broj ponavljanja spoljašnje petlje određuje formalni parametar. U simulatoru podesiti frekvenciju kristala na 18.432 MHz. Pozivom funkcije kašnjenja sa parametrom 1 iz glavne funkcije i podešavanjem konstantnog broja ponavljanja unutrašnje petlje u funkciji kašnjenja, podesiti trajanje izvršavanja unutrašnje petlje na približno 1 ms. Nakon podešavanja, proveriti tačnost kašnjenja od 100 ms i po potrebi korigovati konstantu unutrašnje petlje. Postupak ponoviti za nekoliko različitih dužih intervala i metodom usrednjavanja odrediti konačnu vrednost konstante unutrašnje petlje. Za merenje koristiti simulator i prekidne tačke u glavnoj funkciji. Cilj ovog postupka je da se dobije funkcija kašnjenja čiji formalni parametar određuje kašnjenje u milisekundama.

8. Pomoću funkcije kašnjenja napraviti 24-bitno rotiranje LED grupa u levo za po jedno mesto, sa podešljivom brzinom, određenom na osnovu stanja DIP prekidača LED ploče. Za ove svrhe treba iskoristiti 4 bita DIP prekidača, kojima se brzina podešava u koracima od po 100 ms, sa minimalnim intervalom takođe 100 ms (maksimalno trajanje je tada 1.6 sekundi).

9. U program dodati funkciju za 24-bitnu rotaciju u desno. Preostalih 4 bita DIP prekidača iskoristiti tako da se jedan bit koristi za određivanje smera rotacije, a preostala 3 bita za jedan od osam proizvoljno definisanih 24-bitnih LED formi (stanja), postavljenih korišćenjem posebne funkcije sa SWITCH komandom (kao u primeru 55a). Analizirati u asembleru SWITCH komandu. Proveriti program na realnom hardveru.

4.5. Vežba 5: Logičke operacije nad bitima (bitwise operacije) Proučiti: Poglavlje 3.8.

Kreirati novi projekat ‘vezba5’, koristeći isti mikrokontroler i memorijske modele.

1. U posebnim funkcijama formirati sva tri binarna brojača sa slike 60. Dodati i funkciju kao u alternativi 1, ali ubacivanjem komande za povećanje vrednosti (pre-inkrement) unutar IF testa. Svaka od ove četiri funkcije treba da ima svoju promenljivu brojača (uvek bez predznaka), čiji se tip globalno definiše (pomoću direktive #define ili deklaracijom novog tipa – typedef). Broj bita brojača, kao i masku brojanja, definisati #define direktivama.

2. Komandama uslovnog prevođenja, na osnovu broja bita, globalno određivati tip promenljive brojača (char, int ili long, uvek unsigned).

3. Iz glavne funkcije (main), u beskonačnoj petlji pozivati sve četiri brojačke funkcije. Analizirati asemblerska rešenja za brojače sa 5, 15 i 25 bita i odrediti veličinu funkcija u bajtovima. Za sve funkcije i sve module brojanja, izmeriti vreme izvršavanja postavljanjem prekidnih tačaka u glavnoj (main) funkciji (nakon poziva funkcija brojača, ubaciti i komandu P1 = P2; radi obezbeđenja pogodnog mesta za postavljanje prekidne tačke). Merenje ponoviti više puta (najmanje 5) i zapisati vremena izvršavanja u svim slučajevima. Napraviti tabelu rezultata za sve funkcije i module brojanja, sa minimalnim, maksimalnim i srednjim vremenima izvršavanja i veličinom kôda.

4. Ukloniti funkcije brojača iz programa, a ubaciti definicije za LED proširenje makete. Dodati i definicije odgovarajućih promenljivih u internoj memoriji. Za svaku od tri LED grupe formirati brojač, od 3, 5 i 7 bita. U beskonačnoj petlji glavne funkcije izvršavati brojanje i prikaz stanja na LED ploči makete. Nakon svakog koraka brojanja napraviti pauzu primenom FOR petlje, dovoljno dugačku da se brojanje može lako pratiti.

5. LED brojače izmeniti tako da su svi 4-bitni. Pri tome, LED brojači treba da pokrivaju različite LED podgrupe: prvi brojač koristi LED od 1 do 4, drugi od 3 do 6 i treći od 5 do 8. Preostalih 4 LED iz svake grupe treba da budu ugašene.

6. Formirati funkciju sa 8-bitnim brojačem koji se poziva u beskonačnoj petlji glavne funkcije, sa pauzom nakon svakog koraka brojanja. U ovoj funkciji, iz ovog brojača izdvojiti tri grupe bita i pridružiti ove grupe bita po jednoj LED grupi, primenom odgovarajućih maski. Pri tome. prva LED grupa treba da koristi bite brojača od 1 do 4, druga grupa od 3 do 6 i treća grupa od 5 do 8. U svakoj LED grupi, po 3 preostale LED treba da budu ugašene, dok četvrta neiskorišćena LED (LED 6 u

Page 88: Digitalni_Mikrokontroleri

Mikrokontroleri 87

Katedra za elektroniku

prvoj, LED 8 u drugoj i LED 1 u trećoj grupi) treba u svakom prolazu brojača da menja svoje stanje (primena Eks-ILI maske). Za sve tri LED grupe koristiti promenljive u internoj memoriji, kao sliku stvarnog stanja LED. Nakon svake promene ovih promenljivih, ažurirati stanje izlaznih LED grupa.

7. Napisati alternativnu funkciju za rešavanje problema iz tačke 6 (bez brisanja postojeće funkcije), ali bez korišćenja bitwise maski. Umesto toga, za svaku 8-bitnu grupu koristiti strukturu sa parcijalnim bit-grupama definisanu za svaku LED grupu posebno (slično primeru bit-strukture sa strane 30). Izvršiti analizu asemblerskih rešenja funkcija iz ove i prethodne tačke. Proveriti i vreme izvršavanja obe funkcije.

4.6. Vežba 6: Prekidi Proučiti: Poglavlje 3.9.

Kreirati novi projekat ‘vezba6’, koristeći isti mikrokontroler i memorijske modele. Osnovni zadatak vežbe je nezavisna kontrola (rotacija) tri grupe LED, koje su već korišćene u prethodnim vežbama.

1. Za svaku od tri LED grupe, osim odgovarajuće promenljive u eksternoj memoriji i njene slike u internoj memoriji, definisati jednobajtni softverski tajmer led_tmr_X (X = 1..3) i prateći jednobajtni registar led_per_X, koji sadrži interval softverskog tajmera.

2. Napisati prekidnu funkciju tajmera 2 (indeks prekida je 5) i podesiti ovaj tajmer na interval od 4 ms pri frekvenciji kristala od 18.432 MHz. Prekidna funkcija treba da koristi banku 2.

3. Sva tri softverska LED tajmera led_tmr_X treba da rade sa osnovnim taktom od 250 Hz (interval tajmera 2) i sa intervalom led_per_X.

4. Definisati dodatni softverski tajmer cnt u prekidnoj funkciji, koji deli frekvenciju tajmerskog prekida sa 4 (interval ovog softverskog tajmera je 16 ms). Na svaki prolaz kroz nultu vrednost ovog tajmera, povećati pomoćnu promenljivu intervala softverskog tajmera prve LED grupe (led_per_1) za jedan. Nakon dostizanja vrednosti 200, vratiti vrednost na 100. Istovremeno, smanjivati pomoćnu promenljivu intervala softverskog tajmera treće LED grupe (led_per_3) za jedan. Nakon dostizanja vrednosti manje od 90, vratiti vrednost na 190.

5. Ispred beskonačne petlje glavne funkcije (main) pozvati funkciju za inicijalizaciju koja treba da podesi tajmer 2 i početne vrednosti softverskih brojača led_tmr_X i led _per_X na 100. Inicijalno, sve tri LED grupe treba da imaju aktivnu sam jednu (prvu) LED.

6. U prekidnoj funkciji, uvek kada softverski tajmer led_tmr_X dostigne vrednost 0, osim njegovog punjenja iz odgovarajuće pomoćne promenljive, izvršiti rotaciju u levo za jedno mesto odgovarajuće LED grupe (rotira se promenljiva sa internom slikom stanja LED grupe) i upisati rezultat u LED grupu.

7. Umesto direktne obrade unutar prekidne funkcije za sva četiri tajmera, koristiti odloženu obradu prekida. Za te svrhe, umesto rotacije LED grupa u prekidnoj funkciji, realizovati ove rotacije u glavnoj petlji. Potrebno je definisati 3 bit promenljive led_isr_X (za svaku LED grupu po jednu), koje se postavljaju u prekidu, na mestu gde se u prethodnim koracima izvršavala rotacija (indikacija događaja premašaja softverskog tajmera). Na isti način, korišćenjem četvrte bit-promenljive, cnt_isr realizovati i promenu intervala prvog i trećeg softverskog tajmera pomoću četvrtog softverskog tajmera (promene opisane u tački 4).

4.7. Vežba 7: FIFO baferi Proučiti: Poglavlje 3.10.

U ovom zadatku se koriste tri grupe po 8 LED i serijska komunikacija. Prva LED grupa (crvene LED) treba da radi kao standardni 8-bitni brojač, koji broji na gore ili na dole. Druga grupa LED (žute LED) treba da prikazuje binarnu vrednost prve (crvene) LED grupe u formi Vu-metra (svetleća linija čija dužina zavisi od zadate vrednosti). Treća LED grupa (zelene LED) treba da prikazuje broj jedinica (aktivnih LED) iz prve grupe, takođe u formi Vu-metra. Primer jednog mogućeg stanja svih LED dat je na slici 102.

Page 89: Digitalni_Mikrokontroleri

Mikrokontroleri 88

Katedra za elektroniku

1. grupa – crvene LED

2. grupa – žute LED

3. grupa – zelene LED

7 6 5 4 3 2 1 0 bit-indeks

Slika 102: Primer stanja LED grupa u vežbi 7

Broj aktivnih LED u drugoj i trećoj grupi (žute i zelene LED) može biti od 0 do 8 (ukupno 9 stanja). Kako se brojčana vrednost prve grupe kreće od 0 do 255, korak promene srednje grupe je 255/9 = 28.333, odnosno 29 (prva naredna celobrojna vrednost). Ovo znači da je broj aktivnih LED u drugoj grupi N/29, gde je N trenutna vrednost za prvu grupu LED. Prva LED grupa broji binarno na više ili na niže, uz promenu stanja različitim brzinama, odnosno na svakih 10, 20, 50, 100, 200 i 500 ms. Odgovarajući (izabrani) interval se generiše u prekidu tajmera 2, aktiviranjem događaja TM2 (odloženi prekid). Nakon svake promene stanja, izračuna se broj aktivnih LED za drugu grupu (kao N/29). Ako se izračunato stanje razlikuje od prethodnog stanja, zapamti se (kao prethodno stanje) i aktivira se događaj GR2 (bit promenljiva). Osim toga, za novo stanje se računa i broj aktivnih LED u prvoj grupi. Ako se ovaj broj razlikuje od iste te vrednosti iz prethodnog koraka, u FIFO bafer za poruke upiše se novi broj aktivnih LED prve grupe, koji se istovremeno i zapamti kao vrednost iz prethodnog koraka. FIFO bafer za poruke je veličine 8 bajtova, a formira se prema primeru sa slike 80. Druga LED grupa treba da reaguje na događaj GR2. Ako je ovaj događaj aktivan, treba ga izbrisati i na osnovu odgovarajuće globalne promenljive (vrednost N/29) treba podesiti novu vrednost za drugu LED grupu. Treća LED grupa treba da proveri da li u FIFO baferu za poruke ima nekih poruka. Ako ima, poruka se pročita iz FIFO bafera i na osnovu nje se podesi vrednost za treću LED grupu. Da bi se što jednostavnije generisao odgovarajući interval u prekidu tajmera 2, ovaj tajmer treba podesiti na interval od 2 ms, uz dodavanje softverskog tajmera koji dodatno deli sa 5, 10 ... 250 (dovoljan je jedan bajt za softverski tajmer), čime se dobijaju potrebni intervali. Interval softverskog tajmera je podešljiv i smešten je u posebnu promenljivu. Serijska komunikacija se podešava na 9600 Bd i koristi prijemni FIFO bafer od 16 bajtova (u programu ukupno postoji dva FIFO bafera!). Ovaj bafer se takođe formira prema primeru sa slike 80, pri čemu je upis realizovan unutar prekidne funkcije serijskog porta, a čitanje se vrši u posebnoj funkciji za obradu serijskog porta. Komande koje se dobijaju preko serijskog porta su sledeće:

‘1’..’6’ – Izbor jedne od 6 mogući brzina za prvu LED grupu (interval softverskog tajmera), prema ranije navedenom redosledu (‘1’ => 10 ms, ‘2’ => 20 ms itd).

‘q’ – Prva LED grupa broji na više. ‘a’ – Prva LED grupa broji na niže. ‘w’ – Druga LED grupa prikazuje svetleću liniju sa desna na levo (kao na slici 102). ‘s’ – Druga LED grupa prikazuje svetleću liniju sa leva na desno (obrnuto u odnosnu na sliku 102). ‘e’ – Treća LED grupa prikazuje svetleću liniju sa desna na levo (kao na slici 102). ‘d’ – Treća LED grupa prikazuje svetleću liniju sa leva na desno (obrnuto u odnosnu na sliku 102).

Kodovi navedenih ASCII karaktera su sledeći:

ASCII ‘1’ ‘2’ ‘3’ ‘4’ ‘5’ ‘6’ ‘q’ ‘a’ ‘w’ ‘s’ ‘e’ ‘d’ decimalno 49 50 51 52 53 54 113 97 119 115 101 100 heksadecimalno 0x31 0x32 0x33 0x34 0x45 0x36 0x71 0x61 0x77 0x73 0x65 0x64

Komletan program treba da ima sledeće funkcije:

Page 90: Digitalni_Mikrokontroleri

Mikrokontroleri 89

Katedra za elektroniku

1. Funkcija za inicijalizaciju tajmera 2, serijskog porta i programskih promenljivih. 2. Funkcija prekida tajmera 2. 3. Funkcija prekida serijskog porta. 4. Funkcija za obradu prve LED grupe (crvene LED). 5. Funkcija za obradu druge LED grupe (žute LED). 6. Funkcija za obradu treće LED grupe (zelene LED). 7. Funkcija za obradu serijskog porta. 8. Glavna (main) funkcija, u okviru koje se, u glavnoj petlji, pozivaju tri funkcije za obradu LED grupa

i funkcija za obradu serijskog porta.

Primer nekih potrebnih funkcija je dat na slici 103.

sfr16 RCAP2 = 0xca; // 16-bitna deklaracija sfr16 TIMER2 = 0xcc; // 16-bitna deklaracija uchar com_rd,com_wr,com_len; uchar com_buf[16]; uchar com_read(void) // čitanje FIFO bafera uchar d = com_buf[com_rd++]; com_rd &= 15; com_len--; return d; void com_int(void) interrupt 4 using 3 uchar d; if (RI) RI = 0; d = SBUF; if (com_len!=16) com_buf[com_wr++] = d; com_wr &= 15; com_len++; TI = 0;

void init(void) // init com TMOD = 0x22; TH1 = 256-10; PCON = 0x80; SCON = 0x50; // init timer2 RCAP2 = -3072; // 2 ms TIMER2 = -3072; // Enable interrupts IE = 0x80 | 0x10 | 0x20; // start TR1 = 1; TR2 = 1; void main(void) init(); while(1) LED_grupa_1(); LED_grupa_2(); LED_grupa_3(); serial();

Slika 103: Neke od funkcija potrebne za vežbu 7

4.8. Vežba 8: Primena mašine stanja Proučiti: Poglavlje 3.11.

Kao i ranije, u ovom zadatku se radi sa tri LED grupe. Prva LED grupa (crvene LED) radi na tri različita načina:

1. Binarni brojač na više. Stanje brojača se menja na svakih 0.1 s. Prelaz na narednu fazu (način 2) se vrši nakon dva puna ciklusa brojanja.

2. Binarni brojač na više sa obrnutim redosledom u LED grupi (najniži bit brojača je vezan za najviši bit LED grupe). Stanje brojača se menja na svakih 0.1 s. Prelaz na narednu fazu (način 3) se vrši nakon dva puna ciklusa brojanja.

3. Svih 8 LED se istovremeno pali i gasi na svakih 0.2 s. Prelaz na početnu fazu (način 1) se vrši kada se aktivira događaj ev_LED1 (postavlja ga treća LED grupa).

Za okidač promena stanje LED grupe se koristi događaj ev_Timer2 koji se aktivira na svakih 0.1 s u prekidu tajmera 2.

Page 91: Digitalni_Mikrokontroleri

Mikrokontroleri 90

Katedra za elektroniku

U načinima 1 i 2, uvek kada se nakon povećanja stanja brojača dobije da su najniža 4 bita na nuli, aktivira se događaj ev_LED2. U načinu 3, događaj ev_LED2 se aktivira na svake 2 promene stanja LED (0.4s). Druga LED grupa (žute LED) radi na sledeća tri načina:

1. Vu-metar kao u vežbi 7, sa linijom koja počinje sa desne strane. Dužina linije se menja od 0 do 8, nakon čega se postupak ponavlja. Prelaz na narednu fazu (način 2) se vrši nakon 3 puna ciklusa promene linije od dužine 0 do dužine 8.

2. Vu-metar kao u vežbi 7, sa linijom koja počinje sa leve strane. Dužina linije se menja od 0 do 8, nakon čega se postupak ponavlja. Prelaz na narednu fazu (način 3) se vrši nakon 3 puna ciklusa promene linije od dužine 0 do dužine 8.

3. Svih 8 LED se istovremeno pali i gasi. Prelaz na početnu fazu (način 1) se aktivira događajem ev_LED4 (generiše ga LED grupa 3).

Okidač promene stanja LED grupe je događaj ev_LED2. U načinima 1 i 2, uvek kada dužina LED linije dostigne vrednost 4, aktivira se događaj ev_LED3. U načinu 3, događaj ev_LED3 se aktivira na svake 2 promene stanja LED. Treća LED grupa (zelene LED) radi na isti način kao i druga LED grupa, samo što se kao okidač promene stanja koristi događaj ev_LED3. Ova LED grupa generiše događaj ev_LED1 samo u tački 3, nakon 16 promena stanja LED, kao i događaj ev_LED4 nakon 7 promena stanja LED, a prelaz iz faze 3 u fazu 1 se izvršava nakon 32 promena stanja LED.

4.9. Vežba 9: Korišćenje Cooperative RTOS-a Proučiti: Poglavlje 3.12.

U Keil okruženju formirati novi projekat, a u direktorijum projekta prebaciti FEMTOS fajlove. U listu projektnih fajlova dodati femtos.c, kao i C fajl koji treba da sadrži glavnu (main) funkciju. Otvoriti fajl femtos.h i podesiti sledeće parametre:

#define OS_TIMER 2 /* koristi se tajmer 2 */ #define AUTO_RELOAD 0 /* nevažno za tajmer 2 */ #define STACK_SIZE 0 /* 0==kooperativni sistem */ #define OS_TIMER_BANK 1 /* banka tajmerskog prekida */ #define CRYSTAL 18432000UL /* frekvencija kristala u Hz */ #define OS_TIMER_INTERVAL 1000UL /* 1 ms */ #define MAX_TASK_COUNT 3 /* predviđeno tri taska */ #define MAX_EVENT_COUNT 8 /* do 4 semafora */

U glavnom C fajlu definisati konstante (prema zadatku vežbe 8):

#define ev_LED1 1 /* vrednost 0 nije dozvoljena */ #define ev_LED2 2 #define ev_LED3 3 #define ev_LED4 4 /* najviša vrednost može biti 8 (MAX_EVENT_COUNT) */

a osim fajla REGX52.H, komandom #include uključiti i fajl femtos.h.

Zadatak ove vežbe je isti kao i zadatak za vežbu 8, ali bez događaja ev_Timer2. Umesto toga, treba koristiti funkciju OS_Sleep sa odgovarajućim parametrom za interval pauze. Za svaku LED grupu treba formirati po jedan task, na primer:

void task1(void) ... while (1) ....

U svakoj funkciji taska, ako se koriste lokalne promenljive, treba ih deklarisati kao static.

Page 92: Digitalni_Mikrokontroleri

Mikrokontroleri 91

Katedra za elektroniku

Napomena: U ovoj vežbi se ne koristi fajl femtos_asm.c.

4.10. Vežba 10: Korišćenje Preemptive RTOS-a Proučiti: Poglavlje 3.13

Za ovu vežbu koriste se projekat i fajlovi koji su već formirani u vežbi 9. Nakon otvaranja projekta, izvršiti naredne korake:

1. Dodatno, u grupu projektnih fajlova treba dodati i fajl femtos_asm.c, za koji je potrebno posebno uključiti opcije:

a. Generate Assembler SRF File b. Assemble SRC File

2. Otvoriti fajl femtos.h i podesiti parametar STACK_SIZE na vrednost 8, čime se aktivira Femtos Preemptive način rada. Proveriti funkcionalnost programa u simulatoru i na maketi.

3. U fajlu femtos.h podesiti parametar MAX_TASK_COUNT na vrednost 4. 4. Definisati grupu plavih LED, koja je mapirana na adresu 0x8003 (plave LED su podržane samo u

simulatoru). 5. Formirati novi task (četvrti), u kome se, u beskonačnoj petlji, izvršavaju sledeći koraci:

a. 8-bitna vrednost za plave LED se iz promenljive u internoj memoriji prebacuje u LED registar.

b. Pomoću FOR petlje i 16-bitne promenljive bez predznaka, pravi se najduže moguće kašnjenje.

c. Vrednost promenljive za plave LED se povećava za 1. 6. U glavnoj (main) funkciji dopisati funkciju za kreiranje četvrtog taska. 7. Izvršiti kompajliranje i proveriti funkcionalnost programa u simulatoru. Zabeležiti i veličinu

potrošene RAM memorije. 8. U fajlu femtos.h vratiti parametar STACK_SIZE na vrednost 0, čime se aktivira Femtos

Cooperative način rada. Izvršiti kompajliranje i proveriti funkcionalnost programa. Uporediti veličinu potrošene RAM memorije sa vrednošću dobijenom u tački 7. Objasniti dobijeni rezultat.

4.11. Vežba 11: Ulazno/izlazni (I/O) Podsistem Proučiti: Poglavlje 3.14 (i sva pod-poglavlja). Koristeći operativni sistem FEMTOS i I/O podsistem za upravljanje periferijama koje kontroliše PIC ploča napisati program koji će simulirati rad analognog kvazintegratora.

1. Kreirati novi projekat i u njega osim glavnog C modula dodati i module femtos.c (koristiće se FEMTOS u kooperativnom modu) i pic.c (I/O podsistem za rad sa inteligentnom periferijom – PIC pločom).

2. Napisati dva taska koji će sa različitim vremenima odabiranja simulirati odziv analognog kvaziintegratora. Vremenska konstanta jednog će biti 5s, a drugog 2s. Vreme odabiranja prvog treba da bude 500ms, a drugog 100ms.

3. Ulazni signali za kvaziintegratore se dobijaju preko analognih kanala 0 i 1, na koje se spajaju izlazi funkcijskog generatora makete.

4. Rezultat prvog kvaziintegratora, normalizovan na vrednost od 0 do 10, prikazuje se na displeju, dok se rezultat drugog kvaziintegratora (u opsegu 0 do 255) vodi na osciloskop, preko analognog izlaza 0.

Za proračun izlaznog signala u funkciji ulaznog signala može se koristiti C izraz

y += a * (x – y);

gde parametar a ima vrednost 0.1 za prvi i 0.05 za drugi kanal.

Page 93: Digitalni_Mikrokontroleri

Mikrokontroleri 92

Katedra za elektroniku

4.12. Vežba 12: Master-Slave paketna komunikacija Proučiti: Poglavlje 3.15. Za izvođenje ove vežbe, neophodno je koristiti dve makete, pri čemu se jedna koristi kao master uređaj, a druga kao slejv uređaj. Makete se povezuju serijskom vezom. Master radi zajedno sa PIC pločom, dok slejv radi sa LED pločom.

Napisati softver i za master i za slejv sistem tako da master očitava prvi i drugi analogni ulaz svakih 500ms. Ako se konstatuje promena napona na ulazu, šalje se komanda slejv uređaju u kojoj je sadržana nova vredsnost izmerenog napona.

Slejv uređaj treba da primi komandu poslatu od strane mastera, interpretira je i pomoću LED-ova dâ indikaciju o veličini izmerenog napona. Koristiti VU-metar način indikacije (crvene LED za prvi, zelene LED za drugi kanal). Prikaz treba ažurirati odmah nakon prijema i interpretacije komande sa mastera bez dodatne vremenske sinhronizacije ili kašnjenja.

Komunikacija je jednosmerna i teče samo od mastera ka slejvovima.

Za izradu softvera koristiti C kompajler pod nazivom SDCC (Small Device C Compiler).

4.12.1. Zahtevani format paketa

Jednostavnosti radi, preporučuje se korišćenje paketa fiksne dužine. Potrebno je slejv obavestiti o tome kom je izlazu namenjen podatak koji se šalje i koji je to podatak. Radi se o kanalu 1 ili 2 i podatku od 0 do 8. Ta informacija bi mogla da se smesti i u jedan jedini osmobitni podatak, ali da bi se ilustrovao rad sa protokolima, zahteva se paket formata kao na slici 98.

Serijskom komunikacijom, prenose se osmobitne reči – bajtovi (postoje i varijante sa drugačijim brojem bita). U takvoj situaciji nemoguće je reći koji bajt predstavlja početak paketa. Ako u nekom trenutku dođe do prekida komunikacione veze, neki bajtovi neće biti primljeni i neminovno će doći do gubitka sinhronizacije, tj. prijemnik neće više moći da utvrdi kojim bajtom počinje paket. Ponovno uspostavljanje sinhronizacije mogalo bi biti komplikovano. Iz tog razloga, obično se šalje bajt unapred dogovorene vrednosti koji označava početak paketa (transmisije). Takođe se i kraj paketa često označava posebnim bajtom. Pošto je u ovom slučaju paket fiksne dužine, nije potrebno označavati kraj. Početak neće biti označen posebnim bajtom, nego će jedino početni bajt paketa imati 7. bit postavljen na 1. Svi ostali bajtovi će imati ovaj bit postavljen na 0.

byte_0: broj kanala

(0 ili 1)

byte_1: vrednost

(od 0 do 8)

byte_2: kontrolna suma

(~(byte_0+byte_1))&0x7F

Slika 104: Format paketa

Kontrolna suma je prisutna iz bezbednosnih razloga. Prijemnik računa kontrolnu sumu na osnovu ulaznih podataka (istim algoritmom kao i predajnik) i upoređuje sa trećim bajtom koji je pristigao. Ako se vrednosti ne poklapaju, smatra se da je došlo do greške u komunikaciji i prijemnik zanemaruje pristigli paket.

Page 94: Digitalni_Mikrokontroleri

Mikrokontroleri 93

Katedra za elektroniku

5. Dodaci

5.1. Objašnjenja i dopune uz zadatak vežbe 11

Na slici 105 prikazan je dijagram koji ilustruje rad analognog kvaziintegratora. Srednji blok predstavlja njegovu Laplasovu transformaciju (F(s)) u dva uobičajena oblika koja su ekvivalentna. Parametar τ predstavlja vremensku konstantu kvaziintegratora. U desnom bloku je data ilustracija tipičnog odziva na kojem se vidi da je najveći deo prelaznog procesa završen nakon 3τ vremena, odnosno može se smatrati da je dostignuta asimptota (barem vizuelno).

sssF

1

1

1

1)(

3τC

Slika 105: Kvaziintegrator – prenosna funkcija i odziv

Mikrokontroleri su digitalna kola i kao takva prirodno je da obavljaju diskretizaciju svih signala iz svoje okoline i po vrednosti i po vremenu. Drugim rečima, uzimaju odbirke signala iz okoline i ažuriraju svoje izlazne signale u određenim intervalima (diskretizacija po vremenu), a sa druge strane rade A/D i D/A konverziju nad ulaznim i izlaznim signalalima, što neminovno uvodi diskretizaciju i po vrednosti. Ako je broj bita kojima se odbirci predstavljaju dovoljno velik, greška usled konverzije postaje zanemarljivo mala, te diskretizaciju po vrednosti možemo zanemariti što će i u ovom razmatranju biti učinjeno.

U takvim uslovima, moguće je projektovati diskretni (u vremenu) sistem koji će dati odziv ekvivalentan analognom sistemu (slika 106). Za opis takvog sistema, uobičajeno je koristiti Z-transformaciju. Postoje metode koje prevode prenosnu funkciju iz kontinualnog (analognog) domena u diskretni po vremenu. Mora se imati u vidu da to prevođenje zavisi osim od polazne prenosne funkcije i od intervala odabiranja (intervala diskretizacije po vremenu). Takođe zavisi i od tzv. kola zadrške na izlazu – podrazumeva se da je na izlazu kolo zadrške nultog reda.

0

)(TTbz

azD

3τ C

A/D

D/A

T0

Slika 106: Diskretni sistem ekvivalentnog odziva – korišćena je Z-transformacija

Teorijskom analizom (u koju se ovde neće dalje ulaziti) može se doći do sledećih izraza:

0T

a ; ab 1

Izračunati parametri a i b za zadatak vežbe 11 dati su na slici 107.

SLUČAJ T0 τ a b 1 500ms 5s 0.1 0.9 2 100ms 2s 0.05 0.95

Slika 107: Veze između kontinualnih i diskretnih prenosnih funkcija

Ostaje još pitanje kako iskoristiti datu Z-transformaciju u konkretnom slučaju u mikrokontrolerskom softveru? Rešenje je zasnovano na sledećoj osobini Z-transformacije: obliku u Z-domenu z-1X(z) u

Page 95: Digitalni_Mikrokontroleri

Mikrokontroleri 94

Katedra za elektroniku

vremenskom domenu odgovara xn-1 – ovo praktično znači vrednost odbirka x u prethodnom intervalu odabiranja. Takođe xn je odbirak u trenutno aktuelnom intervalu odabiranja, a xn+1 odbirak u narednom intervalu odabiranja. Primer: naći algoritam izračunavanja narednog izlaznog odbirka (yn+1) na osnovu poznatog prethodnog izlaznog odbirka (yn) i prethodnog ulaznog odbirka (xn) za slučaj 1 sa slike 107. Prenosna funkcija je data na slici 106. Na osnovu nje sledi:

)(1

)()()()()(

)()(

1

1

zXbz

azzX

bz

azXzDzY

zX

zYzD

.

Da bi se na osnovu ovoga mogao izračunavati novi odbirak na izlazu, potreban je još jedan korak:

)()()()()1)(( 1111 zXazzYbzzYzXazbzzY .

Ako se ovo prevede u (diskretni) vremenski domen, tj. napiše kao veza narednog izlaznog odbirka i prethodnih izlaznih i ulaznih odbiraka, na osnovu gorepomenute osobine Z-transformacije se dobija:

11 nnn byaxy .

Ekvivalentna ovome je i forma

nnn byaxy 1

Obe forme imaju opravdanje: prva daje trenutni odbirak (n-ti) na osnovu prethodnih (n-1), a druga daje naredni odbirak (n+1) na osnovu trenutnih (n-tih) umesto trenutni na osnovu prethodnih. Algoritam na jeziku C koji implementira navedeni algoritam bi bio:

odbirak x,yp; while(1) x = uzmi_ulazni_odbirak(); yp = a*x + b*yp; prosledi_na_izlaz(yp); cekaj_T0_vremena();

Pošto važi da je

a + b = 1

moguće je C izraz

yp = a*x + b*yp;

zameniti sa jednostavnijim C izrazom

yp += a*(x – yp);

čime se smanjuje broj množenja. Tip podatka odbirak nije precizno definisan. Kakvog će tačno tipa biti određuje konkretna situacija u kojoj se algoritam primenjuje. Obično su u pitanju brojevi u fiksnom zarezu kod manjih mikrokontrolera. Ovaj tip podatka i osnovne operacije nad njima, obrađene su u odgovarajućem dodatku. Voditi računa da i navedene operacije + i * mogu zahtevati posebnu pažnju u zavisnosti od primenjenog tipa skladištenja brojeva.

5.2. Aritmetika sa fiksnim zarezom na mikrokontrolerskim sistemima

5.2.1. Načini predstavljanja brojeva

Mikrokontroler 8051 prilagođen je radu sa celim brojevima. Poseduje osmobitne registre koji mogu da skladište celobrojne numeričke vrednosti u opsegu od 0 do 255 ili od -128 do 127 kada se koriste za označene brojeve u komplementu dvojke. Ovaj se opseg bez većih problema može povećati korišćenjem dva osmobitna registra (216 celobrojnih vrednosti) ili četiri osmobitna registra (232 vrednosti), pri čemu je na isti

Page 96: Digitalni_Mikrokontroleri

Mikrokontroleri 95

Katedra za elektroniku

način moguće u komplementu dvojke predstaviti i negativne vredsnosti. Ovim proširivanjem, naravno, rastu zahtevi za skladišnim prostorom (memorijom) i usložnjava se samo izvođenje operacija (procesor ne podržava direktan rad sa višebajtnim vrednostima, pa je za svaku operaciju potrebno izvršiti veći broj elementarnih mašinskih instrukcija.

Kada se koristi C kompajler za pisanje programa, korišćenje višebajtnih promenljivih ne predstavlja poseban problem programeru. Kompajler poseduje skup veoma pažljivo napisanih funkcija sa rad sa višebajtnim promenljivama i primenjuje ih na svim mestima automatski, kada za tim postoji potreba. Međutim, ipak treba imati na umu kakve posledice to ima po veličinu generisanog programskog koda, količinu utrošene memorije i konačno i na performanse programa. Zbog svega toga, ni za jednu promenljivu ne treba koristiti promenljivu od više bita nego što je to zaista potrebno.

Često se javlja potreba za radom sa vrednostima manjim od 1. Za to pomenute celobrojne promenljive nisu direktno pogodne. Jezik C nudi rešenje u vidu promenljivih predstavljenih u pokretnom

zarezu – broj se predstavlja kao spoj mantise (m - po pravilu manje od 1) i eksponenta (e): . Primetimo da se ovakav način zapisivanja često koristi i u svakodnevnom računanju kada se predstavljaju veoma veliki ili veoma mali brojevi, ali uz korišćenje broja 10 za eksponencijalni deo. Pošto digitalni računari rade sa binarnom predstavom brojeva, broj 2 je u ovom slučaju prirodan izbor. Ovakvom predstavom brojeva postignut je veoma širok opseg realnih brojeva koji može da se predstavi, a da se pri tome ne gubi značajno na preciznosti. Ovaj format nije predmet ovog teksta tako da neće biti razmatrani dalji detalji. Važno je istaći da je zajednička karakteristika brojeva predstavljenih u pokretnom zarezu potreba za velikim skladišnim prostorom (u odnosu na celobrojne tipove) i složene matematičke operacije nad ovako predstavljenim brojevima. Savremeni mikroprocesori sadrže specijalizovani hardver za izvođenje operacija nad realnim brojevima, čije se performanse približavaju perfromansama hardvera koju izvodi operacije nad celobrojnim tipovima. Takav hardver je na mikrokontrolerima u većini slučajeva nedostupan. Zbog svega pomenutog, na mikrokontrolerskim sistemima, brojevi predstavljeni u pokretnom zarezu se izbegavaju, iako su podržani preko tipova float i double jezika C.

emR 2

Međutim, potreba za predstavljanjem ne-celobrojnih vrednosti javlja se u velikom broju zadataka,

pogotovo u obradi signala i sistemima automatskog upravljanja. Alternativa pokretnom zarezu je fiksni zarez. Direktna podrška ovakvom predstavljanju brojeva u jeziku C ne postoji, ali ipak ju je moguće koristiti prilagođavanjem celobrojnih tipova koji su već na raspolaganju i moguće je efikasno računati sa njima.

5.2.2. Osnove predstavljanja brojeva u fiksnom zarezu

Osnovna ideja leži u konverziji ne-celog broja (broja koju ima i razlomljeni deo, odnosno kako se to obično kaže ima određeni broj decimala) na neki način u celobrojnu vrednost, nad kojom mogu da se izvode celobrojne operacije. Veza sa originalnom vrednošću mora biti održana i to tako da se, što je moguće manje, izgubi na preciznosti prilikom izvođenja različitih aritmetičkih operacija.

Uobičajen način da se pretvaranje u celobrojnu vrednost uradi jeste da se originalni ne-celi broj

pomnoži sa nekom vrednošću. Na taj način deo razlomljenog dela broja (deo koji je manji od 1) prelazi u celobrojni deo. Deljenjem sa istim brojem ponovo dobijamo originalni broj. Primer 1: uzmimo decimalan broj 93.21. Ako ga pomnožimo sa 100, dobićemo (ceo broj) 9321 koji uopšte nema razlomljeni deo. Ponovnim deljenjem sa 100 dobijamo originalnu vrednost 93.21 od koje smo i krenuli. U ovom slučaju, ništa nismo izgubili na tačnosti. Primer 2: uzmimo isti decimalan broj kao u prethodnom primeru. Pomnožimo ga sa 10 i dobićemo ceo broj 932 (broj je ceo, tako da deo 0.1 odbacujemo). Deljenjem sa 10 dobijamo 93.2 koji ne odgovara broju od kojeg smo pošli, nego mu je samo približan. Dakle, predstavljanje brojeva u fiksnom zarezu može lako da dovede do gubitka preciznosti. Zašto se tako nešto dozvoljava? Broj 932 zahteva manje bita za skladištenje nego 9321. Uvek se traži kompromis između potrebnog skladišnog prostora i preciznosti. Primer 3: uzmimo isti decimalan broj kao u prethodnom primeru. Pomnožimo ga sa 64 i dobićemo broj 5965.44, odbacivanjem razlomljenog dela dobijamo 5965. Nakon što ga podelimo sa 64, dobijamo

Page 97: Digitalni_Mikrokontroleri

Mikrokontroleri 96

Katedra za elektroniku

93.203125. Gubitak na preciznosti je očigledan. Može se postaviti pitanje zašto se množi baš sa 64? Taj broj je 26, a množenje i deljenje eksponentima dvojke se može obaviti šiftovanjem, operacijom koja je daleko efikasnija kod mikrokontrolera od množenja i deljenja, pogotovo kod tipova promenljivih sa više od 8 bita. Primer 4: uzmimo isti decimalan broj kao u prethodnom primeru. Pomnožimo ga sa 32 i dobićemo broj 2982.72. Ako se broj uskladišti kao 2982, nakon deljenja sa 32 dobijamo 93.1875, a ako se broj uskladišti kao 2983, nakon deljenja dobijamo 93.21875. Vidimo de je drugi slučaj daje rezultat bliži originalu odakle sledi da je konverziju u ceo broj bolje raditi konverzijom u najbliži ceo broj, a ne odbacijanjem razlomljenog dela.

5.2.3. Apsolutna greška brojeva u fiksnom zarezu Iz prethodnih primera je jasno da predstavljanje brojeva u fiksnom zarezu neminovno unosi greške.

Granice te greške možemo lako odrediti. Greška nastaje zbog toga što nakon množenja originalne vrednosti izabranim brojem moramo uraditi konverziju u ceo broj. Broj koji je najdalje od prethodnog i narednog celog broja je svaki broj koji nakon množenja ima razlomljeni deo jednak 0.5. Tada je deo koji zanemarujemo najveći. Iz toga sledi zaključak da je najveće moguće odstupanje od originalne vrednosti (apsolutna greška) kada je broj predstavljen u fiksnom zarezu jednak

FfpF /5.0

gde je F broj sa kojim se množi i deli pri konverzijama. Za F=64, maksimalna apsolutna greška 0.0078125, za F=32 maksimalna greška je 0.015625. Očito, preciznost raste sa porastom F, ali isto tako i prostor potreban za skladištenje broja raste sa porastom F.

5.2.4. Aritmetičke operacije u fiksnom zarezu

Sabiranje Neka se sabiraju brojevi A i B. U fiksnom zarezu su to brojevi A*F i B*F. Njihovim sabiranjem se dobija: A*F+B*F=(A+B)*F. Jasno je da se konačna vrednost dobija sabiranjem dva broja predstavljena u fiksnom zarezu i njihovim deljenjem sa F. Drugim rečima nakon sabiranja dva broja predstavljena u fiksnom zarezu, i rezultat ostaje validan broj na isti način predstavljen u fiksnom zarezu.

Oduzimanje Sve rečeno za sabiranje, takođe važi i za oduzimanje.

Množenje Neka se množe brojevi A i B. U fiksnom zarezu su to brojevi A*F i B*F. Množenjem ta dva broja daje: (A*F)*(B*F)=(A*B*F)*F=(A*B)*(F*F). Dakle pravi rezultat može da se dobije deljenjem rezultata sa F*F, tj. F2. Ako rezultat treba da ostane u fiksnom zarezu, dovoljno je podeliti rezultat množenja sa F.

Deljenje Neka se deli broj A sa brojem B. U fiksnom zarezu su to brojevi A*F i B*F. Deljenje daje: (A*F)/(B*F)=(A/B)*(F/F)=(A*B). Dakle, pravi rezultat se dobija bez ikakve dalje intervencije, dok se rezultat u pokretnom zarezu dobija jednim dodatnim množenjem sa F.

Mora se primetiti da slučaj deljenja ipak nije tako jednostavan. Npr. ako je B veće od A, primenom gornjeg algoritma dobiće se rezultat 0, što je drastičan gubitak preciznosti. Odgovarajućim korekcijama to može da se izbegne, ali to izlazi iz okvira ovog poglavlja, pa o tome dalje neće biti reči, niti će biti data funkcija koja implementira deljenje. Većina algoritama ovu operaciju ni ne zahteva.

5.2.5. Primer potprograma za rad sa fiksnim zarezom

Priložene su funkcije za sabiranje, oduzimanje, promenu znaka i množenje brojeva u fiksnom zarezu. Svaki se broj skladišti kao označena long promenljiva. Pomoću makro definicije FRAC_BITS se bira koliko se bitova rezerviše za razlomljeni deo, odnosno bira se F u skladu sa prethodni objašnjenjima. F će biti broj za koji važi F=2FRAC_BITS. Nisu date funkcije za pretvaranje broja zadatog u standardnoj formi u broj sa pokretnim zarezom i obratno. Brojevi se moraju izračunati ili ručno (što je za konstante uobičajeno) ili je potrebno u programu predvideti dodatnu funkcionalnost za to.

#define FRAC_BITS 8

Page 98: Digitalni_Mikrokontroleri

Mikrokontroleri 97

Katedra za elektroniku

/* funkcija za sabiranje brojeva u fiksnom zarezu long add(long a, long b) return(a+b); /* funkcija za oduzimanje brojeva u fiksnom zarezu long sub(long a, long b) return(a-b); /* funkcija za promenu znaka broja u fiksnom zarezu long cpl(long a) return(-a); /* funkcija za mnozenje brojeva u fiksnom zarezu long mul(long a, long b) return((a*b)>>FRAC_BITS); /* ************************ ** primer koriscenja ** ************************ */ void korisnicka_funkcija() long a=37345L; // 145.88

// 145.32*256 = 37345.28 long b=3802L; // 14.85 // 14.85*256 = 3801.6 long c; c = add(a,b); c = mul(c,b); // c = (a+b)*b // c = 156440894 sadrzaj long promenljive // c = 2387.097656 dobijeni rezultat nakon konverzije u original // c = 2386.8405 tacan rezultat

5.3. Implementacija serijske komunikacije U nastavku je dato nekoliko funkcija koje obezbeđuju serijsku komunikaciju pomoću standardnog

mikrokontrolera 8051. Uz svaku funkciju, biće objašnjeno kako i kada se koristi. Prijem i predaja su implementirani pomoću FIFO bafera. Neće se zalaziti u princip rada serijskog interfejsa, o tome se može naći više u odgovarajućoj literaturi. Predstavljene funkcije predstavljaju samo jedno od mogućih rešenja.

5.3.1. Baferi i globalne promenljive Promenljive koje su date, po pravilu se definišu kao globalne promenljive. Razlog tome je što

moraju biti dostupne kako iz funkcija za servis prekida, tako i iz ostalih delova programa. Ne moraju se sve definisati i koristiti, osim ako se istovremeno koriste i predaja i prijem. BUF_LEN mora biti definisan kao vrednost 2N.

typedef unsigned char uchar; // ako nije ranije definisano #define BUF_LEN 8 // duzina bafera uchar r_ir,t_ir; // indeksi za prijem/predaju uchar r_ib,t_ib; // indeksi za prijem/predaju

Page 99: Digitalni_Mikrokontroleri

Mikrokontroleri 98

Katedra za elektroniku

uchar r_len,t_len; // broj podataka uchar rcv_buf[BUF_LEN]; // bafer za prijem uchar tsm_buf[BUF_LEN]; // bafer za predaju

5.3.2. Inicijalizacija interfejsa

Sledeća funkcija mora da se pozove u toku inicijalizacije sistema. Ona podešava i parametre komunikacije. Brzina se podešava na 9600 bita u sekundi, a komunikacija je osmobitna. Izmenama ove funkcije, to se može promeniti. U ovom primeru, kao izvor takta serijskog modula koristi se tajmer 2. Ako se koristi zajedno sa operativnim sistemom FEMTOS, treba voditi računa da on ne koristi isti tajmer za svoju vremensku sinhronizaciju.

void init_serial(void) TMOD &= 0x2F; TMOD |= 0x20; TH1 = 256-10; // interval tajmera PCON = 0x80; SCON = 0x50; // parametri interfejsa IE = 0x80 | 0x10; // prekid serijskog dozvoljen TR1 = 1; // tajmer 1 pokrenut

5.3.3. Funkcija za servis prekida serijskog interfejsa

Ovo je kompletna prekidna funkcija koja obavlja i prijem i predaju. Ukoliko ne postoji potreba za jednom od te dve operacije moguće je izostaviti odgovarajući deo: ako nije potreban prijem, može se izostaviti deo sa if(RI), a ako nije potrebna predaja, deo sa if(TI). Korišćena registarska banka (u ovom slučaju to je 3) može se po potrebi promeniti.

void com_int(void) interrupt 4 using 3 uchar d; if (RI) RI = 0; d = SBUF; if (r_len!=BUF_LEN) rcv_buf[r_ir++] = d; r_ir &= (BUF_LEN-1); r_len++; if (TI) TI = 0; if (t_len) d = tsm_buf[t_ir++]; t_ir &= (BUF_LEN-1); t_len--; SBUF = d;

5.3.4. Punjenje predajnog bafera i započinjanje predaje

Predaju je potrebno samo započeti. Slanje prvog elementa iz predajnog bafera odmah započinje, a za slanje svakog narednog, brine se funkcija za servis prekida serijskog inerfejsa. Kada se bafer isprazni, tj. i

Page 100: Digitalni_Mikrokontroleri

Mikrokontroleri 99

Katedra za elektroniku

poslednji bajt se pošalje, prekidna funkcija prestaje sa slanjem i potrebno je ponovo započeti novu predaju. Od sledeće dve funkcije, prva puni predajni bafer sa po jednim novim bajtom (ako se predajni bafer napuni, podaci koji nastave da se upisuju bivaju izgubljeni), a druga započinje slanje.

void com_write(uchar d) if (t_len!=BUF_LEN) tsm_buf[t_ib++] = d; t_ib &= (BUF_LEN-1); t_len++; void start_transmit() if (t_len) SBUF = tsm_buf[t_ir++]; t_ir &= (BUF_LEN-1); t_len--;

5.3.5. Očitavanje primljenih podataka

Sledeća funkcija čita najranije pristigli element iz prijemnog bafera koji još nije očitan i vraća ga. Ukoliko je bafer prazan, uvek vraća vrednost 0.

uchar com_read(void) uchar d; if(r_len) d = rcv_buf[r_ib++]; r_ib &= (BUF_LEN-1); r_len--; return d; return 0;

5.3.6. Indikacija prijema i završetka predaje

Ako je nešto primljeno, vrednost globalne promenljive r_len se uvećava, a čitanjem pristiglih bajtova iz bafera se smanjuje. Zbog toga, nulta vrednost promenljive r_len govori da nema neočitanih podataka, tj. bafer je prazan. Slično, o stanju na predaji govori promenljiva t_len. Ako je vrednost nula, prethodni transfer je završen i može da se započne novi. Najbolje je pre početka dodavanja novih bajtova u predajni bafer i započinjanja nove predaje sačekati da t_len postane jednak nuli (ili proveriti da li je već nula).