You are on page 1of 87

UNIVERZITET U SARAJEVU

ELEKTORTEHNIKI FAKULTET SARAJEVO

Akvizicija podataka pomou


modula Arduino UNO u okviru
Matlab okruenja
- Zavrni rad -

Mentor:

Student:

Doc. dr Samim Konjicija

Marko Lali

Saetak
Data Acquisition Toolbox je dio MATLAB-a koji omoguava koritenje ureaja za akviziciju podataka.
Pomou posebnih DLL-ova, korisnici mogu proiriti mogunosti toolboxa tako da omogue ureaje
koji nisu podrani. Arduino ureaji imaju programabilan mikroprocesor sa mogunosti analognog i
digitalnog ulaza i izlaza. U ovom radu je predstavljen sistem koji pretvara Arduino ureaj u ureaj za
akviziciju podataka i omoguava MATLAB-u njegovo koritenje pomou Data Acquisition Toolboxa.
Sistem se sastoji od etiri komponente: program za Arduino, servis za pristup funkcijama Arduino
ureaja, ve pomenuti DLL za toolbox i GUI aplikacija za upravljanje ureajima. Demonstracija
akvizicije analognog signala i analognog izlaza koristei ovaj sistem je takoer predstavljena u radu.

Abstract
Data Acquisition Toolbox is a part of MATLAB which enables the use of data acquisition hardware
devices. Through custom DLLs, users can extend the toolbox to enable devices not supported by
default. Arduino devices feature a programmable microprocessor with analog and digital input/output
capabilities. A system which turns the Arduino board into a data acquisition device, complete with
MATLAB Data Acquisition Toolbox integration, is presented in this paper. The system is made of four
components: a program for the Arduino, a service for accessing an Arduino board's functions, the
aforementioned DLL for the toolbox and a GUI application for device management. A demonstration of
analog data acquisition and analog data output which uses this system is also presented.

Sadraj
Postavka zavrnog rada..............................................................................................................................6
Uvod...........................................................................................................................................................7
1. Data Acquisition Toolbox.......................................................................................................................8
1.1. Uvod...............................................................................................................................................8
1.2. Generalno o Data Acquisition Toolboxu........................................................................................8
1.3. Arhitektura Data Acquisition Toolboxa..........................................................................................8
1.3.1. M-fileovi...............................................................................................................................10
1.3.2. Data Acquisition Engine.......................................................................................................10
1.3.3. Adaptor DLL.........................................................................................................................10
1.4. Analog Input.................................................................................................................................12
1.5. Analog Output..............................................................................................................................16
1.6. Digital I/O.....................................................................................................................................18
1.7. Dodatne funkcije Data Acquisition Toolboxa..............................................................................19
1.7.1. daqhwinfo.............................................................................................................................19
1.8. Zakljuak......................................................................................................................................20
2. Arduino.................................................................................................................................................21
2.1. Uvod.............................................................................................................................................21
2.2. Arduino Platforma........................................................................................................................21
2.3. Arduino Uno.................................................................................................................................21
2.3.1. Atmel ATmega328 procesor.................................................................................................21
2.3.2. Analogno-digitalni konvertor................................................................................................22
2.3.3. Analogni izlaz.......................................................................................................................23
2.3.4. Timeri....................................................................................................................................23
2.3.5. Serijska komunikacija...........................................................................................................24
2.4. Zakljuak......................................................................................................................................26
3. Implementacija.....................................................................................................................................27
3.1. Uvod.............................................................................................................................................27
3.2. Generalni opis...............................................................................................................................27
3.3. Problem sinhronizacije niti...........................................................................................................29
3.3.1. Win32 API: uzajamno iskljuivanje.....................................................................................29
3.3.2. CriticalSection klasa.............................................................................................................30
3.3.3. Lock klasa.............................................................................................................................30
3.4. Meuprocesna komunikacija........................................................................................................31
3.4.1. Named pipe...........................................................................................................................31
3.4.1.1. Win32 API za komunikacjiu preko named pipeova......................................................31
3.4.1.2. PipeCommunicator klasa..............................................................................................33
3.4.2. Window Messages................................................................................................................34
3.4.2.1. Win32 API: Window i WindowMessage......................................................................34
3.4.2.2. MessageWindow klasa..................................................................................................36
3.4.2.3. MessageWindowSender klasa.......................................................................................40
3.5. EtfArduinoService proces............................................................................................................41
3.5.1. EtfArduinoService klasa.......................................................................................................42
3.5.2. Komunikacija sa Arduinom..................................................................................................44
3.5.2.1. Win32 API: Serijska komunikacija...............................................................................44
3

3.5.2.2. SerialCommunicator klasa............................................................................................46


3.5.2.3. ArduinoDevice klasa.....................................................................................................47
3.5.3. DeviceMap klasa..................................................................................................................53
3.5.4. Obrada zahtjeva....................................................................................................................55
3.5.4.1. RequestHandler.............................................................................................................55
3.5.4.2. RequestHandlerFactory.................................................................................................55
3.5.4.3. RequestHandlerFactoryImpl klasa................................................................................56
3.5.4.4. Konkretne RequestHandler klase..................................................................................56
3.5.5. Glavni program EtfArduinoService servisa..........................................................................58
3.6. Adaptor implementacija...............................................................................................................60
3.6.1. COM interfejs.......................................................................................................................60
3.6.2. Adaptor klasa........................................................................................................................61
3.6.3. CetfarduinoAin klasa............................................................................................................63
3.6.4. CetfarduinoAout klasa..........................................................................................................68
3.6.5. CetfarduinoDio klasa............................................................................................................69
3.7. Arduino Firmware........................................................................................................................71
3.7.1. Analogni ulaz........................................................................................................................71
3.7.2. Analogni izlaz.......................................................................................................................73
3.7.3. Digitalni izlaz........................................................................................................................73
3.7.4. Glavna petlja.........................................................................................................................74
3.8. EtfArduinoConfig - GUI aplikacija..............................................................................................75
3.9. Zakljuak......................................................................................................................................77
4. Demonstracija......................................................................................................................................78
4.1. Uvod.............................................................................................................................................78
4.2. Setup.............................................................................................................................................78
4.3. Akvizicija sinusnog signala..........................................................................................................79
4.4. Akvizicija trouglastog signala......................................................................................................80
4.5. Akvizicija signala oblika etvrtke................................................................................................81
4.6. Akvizicija promjene napona na NTC otporniku...........................................................................82
4.7. Analogni izlaz...............................................................................................................................83
4.8. Zakljuak......................................................................................................................................85
Zakljuak..................................................................................................................................................86
Literatura..................................................................................................................................................87

Sadraj slika
Slika 1: Data Acquisition Toolbox arhitektura...........................................................................................9
Slika 2: Komunikacija Data Acquisition Enginea i adaptor DLL-a.........................................................11
Slika 3: Aliasing pri koritenju Data Acquisition Toolbox objekata........................................................13
Slika 4: Dijagram sekvence - proces akvizicije analognog signala.........................................................15
Slika 5: Dijagram sekvence - proces analognog izlaza............................................................................17
Slika 6: Rezultat izvravanja daqhwinfo funkcije bez parametara..........................................................19
Slika 7: Grafik koji pokazuje vrijeme (s) potrebno za izvravanje iteracije petlje koja alje 2 bajta na
serijski izlaz u odnosu na redni broj petlje...............................................................................................25
Slika 8: Komunikacija komponenti sistema.............................................................................................28
Slika 9: Dijagram klasa EtfArduinoService komponente........................................................................41
Slika 10: Poruka greke ukoliko nije registrovan ureaj s traenim ID-em............................................64
Slika 11: EtfArduinoConfig aplikacija: otkriven port, ureaj neregistrovan...........................................76
Slika 12: EtfArduinoConfig aplikacija: poruka o uspjenoj registraciji ureaja.....................................76
Slika 13: EtfArduinoConfig aplikacija: poruka o neuspjenoj registraciji ureaja..................................76
Slika 14: EtfArduinoConfig aplikacija: ureaj registrovan.....................................................................77
Slika 15: Grafik koji prikazuje podatke dobivene akvizicijom sinusoidnog signala frekvencije 100 Hz
..................................................................................................................................................................79
Slika 16: Oitanje osciloskopa za sinusoidni signal frekvencije 100 Hz.................................................79
Slika 17: Grafik koji prikazuje podatke dobivene akvizicijom signala oblika trougla frekvencije 100 Hz
..................................................................................................................................................................80
Slika 18: Oitanje osciloskopa za signal oblika trougla frekvencije 100 Hz...........................................80
Slika 19: Grafik koji prikazuje podatke dobivene akvizicijom signala oblika etvrtke frekvencije 100
Hz.............................................................................................................................................................81
Slika 20: Oitanje osciloskopa za signal oblika etvrtke frekvencije 100 Hz.........................................81
Slika 21: Grafik podataka dobivenih akvizicijom naponskog signala na krajevima NTC otpornika......82
Slika 22: Laboratorija za vrijeme izvravanja akvizicije vrijednosti napona sa NTC otpornika.............82
Slika 23: Osciloskop prikazuje rezultat postavljanja analognog izlaza na vrijednost 0 V.......................83
Slika 24: Osciloskop prikazuje rezultat postavljanja analognog izlaza na vrijednost 1.25 V..................83
Slika 25: Osciloskop prikazuje rezultat postavljanja analognog izlaza na vrijednost 2.5 V....................84
Slika 26: Osciloskop prikazuje rezultat postavljanja analognog izlaza na vrijednost 3.75 V..................84
Slika 27: Osciloskop prikazuje rezultat postavljanja analognog izlaza na vrijednost 5 V.......................85

Univerzitet u Sarajevu
Elektrotehniki fakultet Sarajevo
Odsjek za raunarstvo i informatiku

Postavka zavrnog rada


Tema

Akvizicija podataka pomou modula Arduino UNO u okviru Matlab


okruenja
Cilj
Razviti driver za Data Acquistion Toolbox koji omoguava koritenje modula Arduino
UNO kao akvizicijskog ureaja.
Opis
Analizirati i opisati Matlab Data Acquisition Toolbox i kreiranje drivera za hardverske
ureaje za akviziciju podataka. Razviti driver za Arduino UNO, koji omoguava da se u
okviru Data Acquisition Toolbox-a koristi analogni ulaz, PWM izlaz i digitalni izlaz
modula Arduino UNO.
Oekivani rezultati:
Razvije driver za Arduino UNO i demonstrirane mogunosti Arduino modula kao ureaja
za akviziciju podataka.
Polazna literatura:
1. S. Konjicija: "Predavanja sa kursa Praktikum automatike"
2. Matlab Data Acquisition Toolbox User's Guide, The Mathworks Inc., 2011
3. Getting Started with Arduino, http://arduino.cc/en/Guide/HomePage
Mentor

Doc. dr Samim Konjicija, dipl. ing. el.

Uvod
Data Acquisition Toolbox je dio MATLAB-a koji omoguava koritenje ureaja za akviziciju podataka.
Arduino ureaji su bazirani na mikroprocesorima koji imaju mogunosti analognog i digitalnog ulaza i
izlaza. U ovom radu su razmatrane mogunosti Arduino ureaja da rade kao jeftini akvizicioni moduli
te je razvijen sistem pomou kojeg je mogua integracija sa MATLAB-ovim Data Acquisition
Toolboxom kako bi nain upotrebe bio identian kao i nain na koji se koristi hardver prvenstveno
namijenjen za akviziciju.
U prvom poglavlju izvrena je analiza Data Acquisition Toolboxa. Predstavljene su njegove
mogunosti, nain upotrebe i arhitektura. Od arhitekture zavisi i razvoj adaptera za Arduino ureaje
koji bi sluili kao akvizicioni moduli. Obraena je i komunikacija komponenti toolboxa, kako bi se
steklo razumijevanje procesa koji se odvijaju pri upotrebi tog alata.
U drugom poglavlju je predstavljena Arduino platforma pri emu je posebna panja data Arduino Uno
ureaju koji je koriten pri implementaciji sistema. Dat je opis svih osobina ureaja bitnih za
implementaciju akvizicije i komunikaciju sa raunarom. Na osnovu toga, odreena su i ogranienja
koja moraju biti zadovoljena.
Tree poglavlje je posveeno implementaciji sistema. Objanjen je dizajn komponenti sistema zajedno
sa njihovom interakcijom i kooperacijom. Zatim je svaka od komponenti razmatrana samostalno.
Predstavljena je implementacija komponente s osvrtom na namjenu svake klase, koritene bibliotene
funkcije i algoritme.
etvrto poglavlje sadri demonstraciju koritenja razvijenog sistema. Dobiveni rezultati su prikazani i
uporeeni sa oekivanim. Ovo predstavlja nain provjere konane upotrebne vrijednosti sistema.

1. Data Acquisition Toolbox


1.1. Uvod
U ovom poglavlju je predstavljen MATLAB-ov Data Acquisition Toolbox. To obuhvata nain na koji se
koristi te njegovu arhitekturu obzirom da od te dvije stvari zavisi i implementacija adaptora, to je
zadatak rada.

1.2. Generalno o Data Acquisition Toolboxu


Data Acquisition Toolbox MATLAB-u omoguava povezivanje sa ureajima za akviziciju podataka.
Izmeu ostalog, ono to je mogue postii koristei toolbox je konfiguracija akvizicionog hardvera i
preuzimanje podataka sa ureaja. Preuzete podatke je zatim mogue koristiti u standardnom MATLAB
okruenju za analizu, pohranjivanje ili bilo koju drugu namjenu. Takoer je mogue vriti i slanje
podataka preko analognih ili digitalnih izlaznih kanala koji postoje na akvizicionim karticama.
Korisnik ovim mogunostima pristupa koristei funkcije toolboxa koje su podijeljene u sljedee
kategorije:

funkcije za upravljanje analognim ulazom (analog input);


funkcije za upravljanje analognim izlazom (analog output);
funkcije za upravljanje digitalnim ulazom/izlazom (digital I/O).

Data Acquisition Toolbox podrava ureaje mnogih proizvoaa opreme za akviziciju podataka, kao
to su National Instruments, Measurement Computing, Advantech i sl. Ipak, ukoliko ureaj nije
podran, mogue je razviti poseban driver koji se integrie u toolbox ime se postie podrka za
dodatne ureaje.

1.3. Arhitektura Data Acquisition Toolboxa


MATLAB-ov Data Acquisition Toolbox se sastoji iz tri dijela:

M-fileovi MATLAB funkcije za pristup funkcionalnostima akvizicionih ureaja;

Data Acquisition Engine MEX-file koji upravlja MATLAB objektima za pristup


akvizicionim ureajima: njihovim kreiranjem, postavljanjem svojstava, podacima vezanih uz
njih;

Adaptori DLL-ovi koji omoguavaju engineu komunikaciju sa razliitim hardverskim


ureajima za akviziciju.

Saradnja dijelova je organizovana tako da svaki nivo pristupa uslugama nivoa ispod njega, pri emu
postoji jasna podjela odgovornosti meu njima. Na dijagramu ispod je prikazana komunikacija ovih
komponenti.

Slika 1: Data Acquisition Toolbox arhitektura

1.3.1. M-fileovi
M-fileovi su MATLAB funkcije koje korisniku omoguavaju spajanje i komunikaciju sa hardverskim
ureajima za akviziciju. Za obavljanje bilo kojeg akvizicionog zadatka, potrebno je koristiti ove
funkcije.
Sve funkcije koje Data Acquisition Toolbox definie se mogu pronai korstei naredbu help('daq') u
MATLAB okruenju. Neke od najznaajnijih su:

Funkcije za kreiranje MATLAB objekata za kontrolu podsistema ureaja za akviziciju:


analoginput, analogoutput, digitalio;

Funkcije za preuzimanje prikupljenih ili slanje izraunatih podataka: getdata, putdata;

Funkcije za postavljanje ili preuzimanje konfiguracijskih vrijednosti objekata: get, set.

Implementacija M-fileova se zasniva na komunikaciji sa Data Acquisition Engineom, tj. pozivanju


njegovih funkcija.

1.3.2. Data Acquisition Engine


MEX-fileovi (MATLAB executable) su dijeljene biblioteke koje se mogu uitati i izvravati unutar
MATLAB funkcija, bez obzira to su implementirane u drugim programskim jezicima.
Engine je MEX-file koji sadri funkcije koje upravljaju objektima za akviziciju, njihovim svojstvima i
podacima. Data Acquisition Toolbox M-fileovi zatim koriste funkcije definisane u engineu u svojoj
implementaciji.
Osim toga, engine prima i brine se za ispravno obraivanje svih dogaaja koji se signaliziraju pri
komunikaciji sa ureajem za akviziciju. Neki od njih su potreba za spaavanjem primljenih podataka ili
podataka koje treba proslijediti prema ureaju. Bitno je naglasiti da se ove radnje odvijaju u pozadini,
tj. u isto vrijeme je mogue koristiti MATLAB okruenje za izvravanje drugih funkcija.
Kako bi Data Acquisition Engine komunicirao sa ureajem za akviziciju, koristi Adaptor DLL koji
odgovara tom ureaju.

1.3.3. Adaptor DLL


Adaptor DLL slui za komunikaciju sa ureajem za akviziciju kome prenosi podatke i konfiguracijske
vrijednosti te od kojeg prima podatke koji su rezultat akvizicije. Adaptor je, dakle, interfejs preko kojeg
Data Acquisition Engine komunicira sa ureajem.

10

Sam adaptor i engine komuniciraju preko COM (Component Object Model) interfejsa. Za svaki ureaj,
koristei COM interfejs, engine kreira odgovarajui adaptor objekat. Adaptor objekat, na engineov
zahtjev za pristup odreenom podsistemu, kreira objekat specifian za taj podsistem. Engine za potrebe
upravljanja tim podsistemom komunicira direktno sa objektom podsistema. Komunikaciju s ureajem
mora implementirati svaki od objekata podsistema.

Slika 2: Komunikacija Data Acquisition Enginea i adaptor DLL-a

11

1.4. Analog Input


Nain na koji se koristi analog input podsistem ureaja za akviziciju koristei Data Acquisition
Toolbox je prikazan u sljedeem dijelu koda:
ai = analoginput('mcc', 0);
addchannel(ai, 1);
ai.SampleRate = 1000;
ai.SamplesPerTrigger = 5000;
start(ai)
wait(ai, 10);
[data, t] = getdata(ai, ai.SamplesAcquired);
plot(t, data)
delete(ai)

U ovom primjeru, korisnik vri akviziciju signala u trajanju od 5 sekundi, sa frekvencijom uzorkovanja
od 1000 Hz. Zatim crta grafik tog signala.
Pozivom analoginput funkcije, M-file zahtijeva od enginea da kreira MATLAB objekat pomou kojeg
e korisniku biti omogueno koritenje analog input podsistema mcc ureaja sa ID-om 0. Pri kreiranju
tog objekta, engine, koristei adaptor definisan za taj ureaj (iji je naziv prvi parametar funkcije),
kreira specijalni adaptor objekat za analog input podsistem pomou kojeg e moi vriti komunikaciju
sa samim hardverom i pridruuje ga novokreiranom MATLAB objektu.
Korisnik nije svjestan ovog detalja, ali ipak utie na njegovo koritenje toolboxa. Naime, ukoliko se
izvri jednostavno kopiranje MATLAB analoginput objekta, npr. sa
ai1 = ai

Rezultat e biti taj da su oba ta objekta povezana sa istim adaptor objektom za komunikaciju sa
ureajem za akviziciju. Drugim rijeima, dolazi do pojave koja se u programiranju naziva aliasing kada
ai1 i ai predstavljaju samo referencu na isti interni objekat te bilo koja promjena pomou jednog od
njih se odraava i na drugi. Slika ispod poblie ilustrira ovu pojavu:

12

Slika 3: Aliasing pri koritenju Data Acquisition Toolbox objekata


Funkcija addchannel dodaje novi ulazni kanal na MATLAB objekat za analognu akviziciju. Engine,
kada dobije naredbu od M-filea, pomou adapter objekta za analog input signalizira ureaju da je
potrebno da vri akviziciju sa navedenih kanala.
Naredne dvije komande postavljaju osobine SampleRate i SamplesPerTrigger MATLAB analoginput
objekta. Ove osobine odreuju koja e biti frekvencija uzimanja uzoraka ulaznog signala prilikom
akvizicije, te koji je ukupan broj samplova koji se trebaju prikupiti za vrijeme jednog izvravanja
akvizicije. Osobina SamplesPerTrigger moe biti postavljena na vrijednost Inf, to znai da vrijeme
prikupljanja podataka nije ogranieno, ve korisnik mora zaustaviti koritenjem funkcije stop. Za
postavljanje ovih vrijednosti, opet je zaduen engine.
Prilikom postavljanja osobina, engine nije obavezan komunicirati sa ureajem ukoliko je u pitanju neka
osobina koja ne utie na izvravanje akvizicije. Adaptor objekti za podsisteme imaju poseban nain da
signaliziraju engineu koje su te osobine za ije promjene ele biti obavijeteni. O ovome e biti vie
rijei u poglavlju Implementacija.
Komanda start signalizira engineu da je potrebno zapoeti sa akvizicijom. Nakon to se pozove ova
funkcija, MATLAB program nastavlja sa izvravanjem, obzirom da je od tog trenutka engine zaduen
za komunikaciju sa ureajem. Kae se da je start poziv asinhron. Engine je taj koji prima i spaava
podatke koje ureaj vraa raunaru.
Dok je aktivna akvizicija, osobina Started analoginput objekta je postavljena na vrijednost true.
Ipak, iako funkcija start zapoinje akviziciju, spaavanje oitanih vrijednosti od strane enginea ne
poinje u tom trenutku. U zavisnosti od postavke osobine TriggerType objekta analoginput, spaavanje
moe poeti u istom trenutku kada i akvizicija (Immediate postavka), kada sam korisnik, pozivajui
funkciju trigger, to zatrai (Manual postavka) ili kada se detektuje neki dogaaj koji indicira da
spaavanje podataka treba zapoeti (Software postavka).
13

Dogaaji koji mogu izazvati poetak spaavanja podataka mogu biti prelazak oitane vrijednosti na
nekom ulaznom kanalu preko ili ispod postavljene granice, ulazak oitane vrijednosti unutar
postavljenog intervala ili rastua, tj. opadajua, ivica signala. U ovom sluaju, sam Data Acquisition
Engine je taj koji se brine da e korisnik dobiti ispravne podatke, adaptor nije svjestan da se neki
podaci koje vraa odbacuju. Dogaaj koji e zapoeti spaavanje podataka se bira postavljanjem
svojstva TriggerCondition MATLAB analoginput objekta.
Treba napomenuti da neki ureaji osim navedene tri opcije za TriggerType omoguavaju i Hardware
opciju. U tom sluaju, spaavanje podataka treba da pone kada sam ureaj primi i obradi odreen
signal od drugog hardverskog ureaja; engine vie nije taj koji se za to brine. Na taj nain, reakcija
moe biti bra, ali je potrebno da to hardver akvizicionog modula podrava.
Funkcija wait slui kada korisnik eli da program zaustavi svoje izvravanje sve dok odreeni
analoginput objekat ne zavri sa svojim izvravanjem ili dok ne protekne vrijeme navedeno kao
parametar (u sekundama).
Pomou getdata funkcije, korisnik zahtijeva od enginea da vrati podatke prikupljene za vrijeme
akvizicije. Drugi parametar funkcije je broj samplova koji se ele preuzeti. Funkciju je mogue pozivati
i dok je MATLAB analoginput objekat u aktivnom stanju. Svaki poziv funkcije uklanja vraene
podatke iz enginea. Osim samih podataka, za svaki od samplova, funkcija vraa i apsolutno vrijeme od
poetka akvizicije u kojem je taj sample uzet.
Preuzeti podaci se nalaze u klasinim MATLAB matricama te to znai da ih je mogue dalje koristiti u
bilo kojim MATLAB funkcijama za obradu podataka. Primjer u ovom sluaju je funkcija plot koja e
nacrtati grafik preuzetog signala.
Poziv funkcije delete oslobaa sve resurse koje je analoginput objekat zauzeo. Engine je zaduen za
oslobaanje tih resursa. Nakon poziva ove funkcije, vie nije mogue koristiti ovaj objekat te za
koritenje istog ureaja je potrebno ponovo pozvati konstruktorsku funkciju analoginput.
Ispod se nalazi dijagram sekvence koji prikazuje kako se odvija komunikacija svih dijelova Data
Acquisition Toolboxa u ovom primjeru.

14

Slika 4: Dijagram sekvence - proces akvizicije analognog signala


15

1.5. Analog Output


Nain na koji se koristi analog output podsistem ureaja za akviziciju koristei Data Acquisition
Toolbox je prikazan u sljedeem dijelu koda:
ao = analogoutput('mcc', 1);
addchannel(ao, 1);
ai.SampleRate = 500;
data = sawtooth(linspace(0, 4 * pi, 500));
putdata(ao, data)
start(ao)
delete(ao)

U ovom primjeru, na analogni izlaz ureaja se alje signal u trajanju od 1 sekunde, oblika pile,
amplitude 1 volta i perioda 0.5 s.
Funkcija analogoutput, na isti nain kao i ve opisana analoginput, funkcija kreira MATLAB objekat
analogoutput koji slui da korisnik pristupi podsistemu za analogni izlaz mcc ureaja s ID-om 1.
Naredne dvije naredbe takoer rade na isti nain kao i ve opisane funkcije kod analognog ulaza.
Funkcija putdata alje engineu podatake koje e biti potrebno da ureaj poalje na svoje izlazne kanale.
Podaci su predstavljeni standardnom MATLAB matricom realnih brojeva, koja je u ovom sluaju
generisana naredbom prije.
Pozivom start funkcije za MATLAB analogoutput objekte, engine osigurava da se podaci koji su
prethodno dodani alju na ureaj. Poziv ove funkcije je asinhron, pa korisnik moe nastaviti sa radom
dok e ureaj dobivati ispravne podatke za svoj izlaz od Data Acquisition Enginea.
Delete funkcija radi isto to i u sluaju analoginput objekta.
Ispod se moe vidjeti dijagram sekvence koji prikazuje nain komunikacije komponenti Data
Acquisition Toolboxa za prethodno dati primjer.

16

17
Slika 5: Dijagram sekvence - proces analognog izlaza

1.6. Digital I/O


Slijedi primjer koritenja podsistema za digitalni ulaz i izlaz:
dio = digitalio('mcc');
addline(dio, 0, 'Out');
v = 0;
for i = 1 : 1000
pause(0.1)
putvalue(dio, v)
v = ~v;
end
delete(dio)

Ovaj primjer svakih 100 ms mijenja stanje nekog digitalnog izlaznog porta ureaja.
Funkcija digitalio je konstruktorska funkcija za MATLAB digitalio objekat koji slui za pristup tom
podsistemu i radi na isti nain kao i ve opisane konstruktorske funkcije.
Funkcija addline mijenja funkciju addchannel za podsistem za digitalni ulaz i izlaz, tj. signalizira
ureaju da doda dodatnu liniju koja e biti koritena za ulaz ili izlaz, u zavisnosti od parametra. Svaka
linija je povezana sa portom, ali je mogue adresirati svaku individualnu liniju pomou cijelog broja
koji predstavlja njen ID. Svaki port moe vie od jedne linije, a najee je to 1, 8 ili 16 linija. Ipak,
ponaanje prilikom slanja izlaza na neku liniju zavisi od ureaja do ureaja, tj. neki ureaji kada se
postavi neka vrijednost na jednu od linija porta, postave tu vrijednost za sve ostale linije porta, a drugi
omoguavaju individualno mijenjanje vrijednsoti na svakoj liniji.
Funkcija putvalue slui da na odreeni ureaj poalje digitalne vrijednosti. Ukoliko postoji vie linija,
binarna reprezentacija proslijeenog parametra vrijednosti odreuje koje linije e biti u logikom
HIGH stanju, a koje u logikom LOW.
Postoji i funkcija getvalue koja oitava vrijednosti svih linija i vraa cijeli broj ija binarna
reprezentacija predstavlja stanje svake od linija.
Funkcija delete, kao i za sve ostale podsisteme, vri oslobaanje zauzetih resursa.
Ono to je bitno za sistem za digitalni ulaz i izlaz Data Acquisition Toolboxa je da objekte tipa digitalio
nije mogue pokrenuti. Drugim rijeima, itanje vrijednosti i slanje vrijednosti je uvijek pod kontrolom
korisnika te nikad ne prelazi pod kontrolu enginea, kao to je to sluaj kod analoginput i analogoutput
objekata toolboxa.

18

1.7. Dodatne funkcije Data Acquisition Toolboxa


1.7.1. daqhwinfo
Funkcija daqhwinfo ima vie namijena.
Prva je kada se pozove bez parametara i tada daje generalne informacije o Data Acquisition Toolboxu:
njegovu verziju, naziv, verziju MATLAB-a i listu svih instaliranih adaptora to predstavlja listu
dozvoljenih parametara za konstruktorske funkcije toolboxa.

Slika 6: Rezultat izvravanja daqhwinfo funkcije bez parametara


Drugi sluaj je kada se proslijedi string parametar koji predstavlja naziv nekog od adaptora. Taj poziv
daje informacije o samom adaptoru: lokacija DLL-a pomou kojeg je implementiran, verziju, njegov
naziv, a najbitnije, listu svih instaliranih ureaja s kojim adaptor moe komunicirati zajedno sa
njihovim ID-ovima. To su jedini ID-ovi koji su dozvoljeni za koristiti prilikom poziva konstruktorskih
funkcija za dati adaptor.
Na kraju, funkciji daqhwinfo je mogue proslijediti i neki od MATLAB objekata Data Acquisition
Toolboxa, tj. analoginput, analogoutput ili digitalio objekat. U tom sluaju, funkcija vraa informacije
specifine samo za podsistem na koji se odnosi dati objekat.
19

Neke od najbitnijih informacija koje se dobiju u tom sluaju su lista svih kanala koji su dostupni tom
podsistemu na samom ureaju zajedno sa ID-evima koji se mogu proslijediti addchannel funkciji kako
bi im se pristupilo.

1.8. Zakljuak
U ovom poglavlju je predstavljen Data Acquisition Toolbox, njegov nain koritenja i arhitektura, to
podrazumijeva komponente i nain njihove komunikacije. Iz analize arhitekture se vidi da je potrebno
implementirati DLL koji sadri COM komponente s kojima e komunicirati Data Acquisition Engine
kako bi se podrali Arduino ureaji za akviziciju direktno kroz toolbox. Takoer, moe se primijetiti da
podsistem za digitalni ulaz i izlaz ima znaajno manji broj podranih funkcionalnosti.

20

2. Arduino
2.1. Uvod
U ovom poglavlju je predstavljena Arduino platforma njen hardverski i softverski dio. Posebno je
razmatran Arduino Uno, ureaj na kojem je izvrena implementacija zadatka. Prikazane su mogunosti
i ogranienja od kojih zavisi planirana implementacija sistema za akviziciju.

2.2. Arduino Platforma


Arduino je open source platforma za prototipiranje elektronikih sistema zasnovana na fleksibilnom i
jednostavnom za koritenje hardveru i softveru. Arduino ureaji se sastoje od Arduino tampane
ploice, iji dizajn je open source, mikroprocesora iz Atmel AVR porodice i podrke za ulaz ili izlaz.
Softver se sastoji od kompajlera i razvojnog okruenja za Arduino jezik koji je pojednostavljenje C++
jezika prilagoeno za jednostavnije koritenje funkcionalnosti AVR procesora. Uz to, Arduino ureaji
imaju preinstaliran boot loader koji omoguava jednostavan prenos novih programa na ureaj sa
raunara na kojem su kompajlirani, po cijenu neto smanjenog ukupno raspoloivog memorijskog
prostora za program.

2.3. Arduino Uno


Za svrhe ovog projekta, koriten je Arduino Uno ureaj iz Arduino porodice. Uno je baziran na Atmel
ATmega328 procesoru i ima 14 digitalnih pinova koji se mogu koristiti za ulaz ili izlaz, 6 analognih
ulaza, oscilator frekvencije 16 MHz i USB konektor. Ureaj je mogue napajati eksternim naponom od
7-12V (baterija ili adapter) ili direktno preko USB porta raunara na koji je spojen.

2.3.1. Atmel ATmega328 procesor


ATmega328 procesor ini srce Arduino Uno ureaja to znai da svaki aspekt funkcionalnosti zavisi od
mogunosti ovog procesora.
ATmega328 je 8-bitni RISC procesor sa maksimalnom frekvencijom 20 MHz. Sadri 32 registra opte
namjene, podrku za interne i eksterne prekide (interrupte), 10-bitni analogno-digitalni konvertor i 23
linije namijenjene za ulaz ili izlaz.
Na raspolaganju ima tri tipa memorije, svaka razliite namjene i kapaciteta:

Flash memorija 32KB od ega 0.5KB zauzima boot loader. U flash memoriji se spaava
izvrni kod programa koji procesor treba da izvrava.
21

EEPROM persistentna memorija, tj. prostor u kojem programi mogu spasiti vrijednosti koje
se trebaju sauvati i kada se ureaj ugasi. Pristup ovoj memoriji je veoma spor te je namijenjen
za minimalnu koliinu podataka. Kapacitet je 1 KB.

SRAM RAM memorija, kapaciteta 2KB, gdje program spaava vrijednosti varijabli za
vrijeme svog izvravanja.

2.3.2. Analogno-digitalni konvertor


Analogno-digitalni konvertor (ADC) ima rezoluciju 10-bita, to znai da je mogue detektovati 1024
diskretna nivoa ulaznog naponskog signala. ADC koristi algoritam uzastopnih aproksimacija. Ovaj
algoritam je zasnovan na binarnom pretraivanju gdje se u svakom koraku odreuje jedan bit digitalne
vrijednosti.
U prvom koraku, najznaajniji bit digitalne vrijednosti se postavlja na 1, a ulazna vrijednost se, pomou
ugraenog komparatora, poredi sa polovinom referentnog napona koji predstavlja maksimalnu
vrijednost koju ureaj moe prepoznati. Ukoliko komparator indicira da je ulazna vrijednost manja,
najznaajniji bit postaje 0, a proces se nastavlja na isti nain postavljanjem sljedeeg bita na 1 i
dijeljenjem referentnog napona sa 4.
Ovaj proces nakon 10 koraka daje digitalnu aproksimaciju ulaznog naponskog signala. Najmanja
promjena napona koju ADC moe detektovati, uzimajui 5V kao referentni napon, se moe dobiti kao:

Za ovaj proces, konvertoru je potreban sinhronizacijski signal (clock). Za jednu analogno-digitalnu


konverziju je potrebno 13 ciklusa sinhronizacijskog sata, pa od frekvencije sata zavisi i brzina
izvravanja konverzije. Ipak, maksimalna frekvencija pri kojoj ne dolazi do gubitka preciznosti
konverzije je 1 MHz, to znai da je vrijeme potrebno za konverziju ogranieno na minimalno 13 s.
ATmega328 procesor omoguava koritenje signala sata samog procesora za ovu namjenu. Pomou
specijalnog sklopa unutar konvertora, frekvencija signala sata se moe podijeliti odreenom
vrijednosti, koja se naziva faktor skaliranja (prescaler), kako bi se za signal sinhronizacije analognodigitalne konverzije dobila nia frekvencija. Uzimajui u obzir da je frekvencija sata 16 MHz za
koriteni Arduino Uno ureaj, faktor skaliranja je potrebno postaviti na vrijednost 16 kako bi se
postigla frekvencija od 1 MHz.
Odabir faktora skaliranja se vri upisom odgovarajue vrijednosti u za to predviene bite ADPS[2:0]
registra ADCSRA. Iz data sheeta procesora se moe vidjeti da za faktor skaliranja 16, treba postaviti
navedene bite na vrijednosti:
ADPS2 = 1
ADPS1 = 0
ADPS0 = 0

22

ADC sadri i multiplekser pomou kojeg se moe odabrati koji od 6 raspoloivih ulaznih portova e se
konvertovati u digitalnu vrijednost. Odabir porta se vri upisom odgovarajue vrijednosti u bite
MUX[3:0] registra ADMUX.

2.3.3. Analogni izlaz


Arduino Uno nema sklop za digitalno-analognu konverziju pa za postizanje analognog izlaza koristi
pulse width modulation (PWM) tehniku.
PWM radi tako to se prosjena vrijednost napona koja dolazi do potroaa kontrolie slanjem
digitalnih impulsa (maksimalna naponska vrijednost ili minimalna naponska vrijednost) velikom
frekvencijom. to je due vrijeme u kojem je na izlazu maksimalna vrijednost, to je srednja vrijednost
napona vea. Na taj nain, odabirom odgovarajueg broja impulsa vrijednosti 1, postiu se razliite
srednje vrijednosti napona, tj. analogne vrijednosti.
Ipak, da bi se mogao prepoznati oblik signala, potrebno je izvriti njegovo filtriranje, u to se ovdje
nee ulaziti jer je izvan teme ovog rada.
Na Arduino platformi za koritenje analognog izlaza je obezbijeena globalna funkcija analogWrite
koja prima vrijednost od 0 do 255 koju treba pretvoriti u analognu vrijednost na izlaznom portu koji je
takoer zadat kao parametar. Vrijednost 255 odgovara maksimalnoj vrijednosti izlaza 5V.

2.3.4. Timeri
Timeri ili brojai su specijalni registri ija se vrijednost automatski inkrementuje odreenom
frekvencijom koja zavisi od frekvencije sata i faktora skaliranja koji je primijenjen na tu frekvenciju.
Kada doe do overflowa vrijednosti u registru, ATmega328 procesoru se signalizira hardverski prekid
(interrupt). Postavljanjem odgovarajue vrijednosti faktora skaliranja, poetne vrijednosti u registru te
u zavisnosti od veliine registra, ovaj mehanizam moe biti koriten za sinhrono generisanje dogaaja,
obzirom da je procesor obavezan prei u rutinu za obradu prekida kada se on javi.
ATmega328 sadri 3 timera. Od toga, Timer0 i Timer2 imaju registre veliine 8 bita, a Timer1 registar
veliine 16 bita.
Na Arduino platformi, svi ovi timeri su koriteni za implementaciju PWM izlaza, obzirom da je za tu
svrhu potrebna sinhrona generacija signala. Uz to, Timer0 je koriten za imlementaciju funkcija koje
mjere vrijeme: milis, micros, delay, delayMicroseconds. Ukoliko korisnik eli koristiti neki od timera,
izgubit e neku od funkcionalnosti Arduino ureaja.
Faktor skaliranja za Timer0 je postavljen na 64, to znai da je frekvencija inkrementovanja vrijednosti
tog brojaa 250 kHz, to odgovara periodu od 4s. Iz tog razloga, funkcija micros, koja vraa broj
mikrosekundi od poetka rada ureaja, ima rezoluciju od 4s, tj. vraena vrijednost je uvijek djeljiva s
4.

23

2.3.5. Serijska komunikacija


ATmega328 omoguava serijsku komunikaciju s drugim ureajima preko pinova 0 (RX) i 1 (TX).
Kako Arduino Uno za spajanje sa raunarom koristi USB, sadri dodatni mikroprocesor, Atmega8U2
koji je programiran da radi kao USB-serijski konvertor. Pinovi 0 i 1 ATmega328 procesora su spojeni
na ovaj procesor koji je zatim zaduen da generie odgovarajui signal za USB. Na raunaru, Arduino
Uno se registruje kao virtualni COM port te se komunikacija moe postii potpuno transparentno
koristei API za serijsku komunikaciju.
Sam Atmega328 podrava baud vrijednosti (broj prenesenih bita u jedinici vremena) do 2 Mbaud s
procentom greke od 0%. Ipak, nakon testova sprovedenih tokom izrade ovog rada, utvreno je da zbog
dodatnog procesiranja koje zahtijeva konverzija USB u serijski signal, nakon vrijednosti od 256 kbaud,
dolazi do pojave greaka u prenosu podataka, pa je najvea efektivna brzina prenosa koju je mogue
postii koristei Arduino Uno 256 kbaud.
Za programiranje serijske komunikacije je ostavljena mogunost koritenja manipulacije registara
procesora, ali Arduino platforma implementira specijalanu klasu HardwareSerial i instancu klase Serial
kojoj svaki Arduino program ima pristup. Pomou metoda objekta Serial, mogue je izvriti slanje i
oitavanje vrijednosti.
Najbitnije metode su:

begin slui za inicijalizaciju komunikacije te kao parametar prima baud koji treba koristiti;

write metoda za slanje proslijeenog bajta na izlaz. Ono to je bitno za ovu metodu je da je
njeno izvravanje asinhrono ne eka da se vrijednost zaista poalje na izlaz, ve se ona upisuje
u interni FIFO buffer iz kojeg se, kada je podsistem za serijski izlaz procesora spreman,
proslijeuje sljedea vrijednost na izlaz. Ukoliko je buffer pun, metoda blokira izvravanje sve
dok se ne oslobodi mjesto, tj. dok se bajt iz buffera ne poalje na izlaz;

read ita sljedei bajt iz buffera vrijednosti primljenih preko serijskog porta. Vraenu
vrijednost uklanja iz buffera. Ukoliko nema neproitanih vrijednosti, vraa -1;

available vraa broj neproitanih bajta primljenih preko serijskog porta;

peek vraa sljedei bajt iz buffera vrijednosti primljenih preko serijskog porta, bez uklanjanja
vrijednosti iz buffera. Ukoliko nema neproitanih vrijednosti, vraa -1.

Iako je write funkcija po svojoj prirodi asinhrona, najmanji period kreiranja podataka koji e se slati
preko serijskog porta je jednak vremenu za prenos tih podataka iz buffera preko serijskog interfejsa.
Razlog za to je to bi se u sluaju proizvodnje podataka bre od vremena potrebnog za prenos tih
podataka, buffer punio bre nego to bi se praznio, to znai da nakon nekog vremena dolazi do
situacije da je buffer pun te da funkcija write mora da eka da se dovoljan broj podataka poalje kako bi
se novi podaci mogli dodati u njega. Vrijeme potrebno da se eka je upravo jednako vremenu za slanje
podataka, pa se iz toga moe zakljuiti da proizvodnja podataka ne moe biti bra od vremena
24

potrebnog za njihovu transmisiju.


Za prenos 1 bajta podataka je potrebno izvriti prenos 10 bita, obzirom da serijski protokol zahtijeva
upotrebu jednog start bita i (barem) jednog stop bita. Tako, pri baud vrijednosti od 256000, za prenos 2
bajta podataka, oekivano vrijeme prenosa je:

Ispod se moe vidjeti grafik koji pokazuje vrijeme potrebno za izvravanje petlje na Arduino Uno
ureaju koja, koristei write funkciju, alje 2 bajta. Funkcija micros je koritena za odreivanje razlike
izmeu vremena u kojem je izvrena svaka iteracija petlje. Moe se uoiti trenutak kada se buffer
napuni te od kada vrijeme potrebno za dolazak u sljedeu iteraciju postaje jednako vremenu za prenos 2
bajta.

Slika 7: Grafik koji pokazuje vrijeme (s) potrebno za izvravanje


iteracije petlje koja alje 2 bajta na serijski izlaz u odnosu na redni
broj petlje
Iz ovoga se moe zakljuiti da je najmanji mogui period sampliranja, ukoliko se ti podaci ele slati na
raunar, jednak 80s to je ekvivalentno maksimalnoj frekvenciji sampliranja od 12.5 kHz.

25

2.4. Zakljuak
U ovom poglavlju je predstavljena Arduino platforma. Na osnovu analize dostupne memorije i brzine
prenosa podataka s ureaja na raunar, moe se zakljuiti da je maksimalna frekvencija uzorkovanja
prilikom procesa akvizicije analognog signala 12.5 kHz. Znajui da je rezolucija analogno-digitalnog
konvertora 10 bita, dolazi se do zakljuka da je maksimalan broj diskretnih naponskih nivoa koje
Arduino moe prepoznati 1024.
Obzirom da je analogni izlaz implementiran pomou PWM tehnike, analogni izlaz Arduino
akvizicione kartice bez dodatnog filtera nee imati valni oblik koji se oekuje.

26

3. Implementacija
3.1. Uvod
U ovom poglavlju su predstavljene sve komponente sistema koje su potrebne kako bi se postigla
traena funkcionalnost Arduino ureaja kao akvizicionog modula. Najprije je dat opis svrhe svake
komponente i naina njihove saradnje. Iza toga slijedi opis metoda komunikacije tih komponenti.
Po tom, svaka od komponenti je detaljno opisana, njen dizajn i implementacija. Sve bitne klase su
predstavljene i krucijalni dijelovi implementacije metoda objanjeni.

3.2. Generalni opis


Da bi se postigao cilj implementiranja adaptora za Data Acquisition Toolbox, osim samog DLL-a kojeg
e koristiti Data Acquisition Engine, razvijene su jo tri komponente koje kroz meusobnu kooperaciju
sistemu omoguavaju ispravan rad. Svaki od dijelova ima odgovornost koja se ne preklapa ni sa jednim
drugim dijelom.
Sve etiri komponente su:

EtfArduinoFirmware program za Arduino ureaj koji mu omoguava izvravanje


funkcionalnosti akvizicione kartice i komunikacije s raunarom.

EtfArduinoService proces predstavlja servis koji klijentima omoguava pristup


funkcionalnostima Arduino ureaja za akviziciju (Arduino sa EtfArduino firmwareom).
Zaduen je za svu komunikaciju s ureajem koritenje usluga ovog procesa je jedini nain da
se pristupi mogunostima ureaja. Ovaj proces je jedini koji brine o direktnoj komunikaciji s
ureajem, tj. protokolima koje je potrebno koristiti kako bi se komunikacija obavila.
Klasa EtfArduinoService je implementirana da bi se koritenje servisa olakalo svim procesima
koji imaju potrebu za njegovim uslugama.

EtfArduinoConfig GUI aplikacija slui da se Arduino ureaji koji imaju instaliran


odgovarajui firmware registruju za koritenje, tj. da se servisu signalizira da treba da omogui
komunikaciju sa tim ureajem.

etfarduino.dll DLL koji implementira adaptor za MATLAB-ov Data Acquisition Toolbox


koji omoguava Data Acquisition Engineu da koristi Arduino ureaje. Koristi usluge
EtfArduinoService procesa kako bi komunicirao s ureajem i proslijeivao mu komande.

27

Iz ovog opisa se moe vidjeti da svaka od komponenti ima potrebu da komunicira sa makar jednom
drugom komponentom.
Obzirom da su u pitanju odvojeni procesi, za postizanje komunikacije EtfArduinoService servisa sa
etfarduino.dll objektima i EtfArduinoConfig aplikacijom, potrebno je upotrijebiti neki mehanizam
meuprocesne komunikacije. U ovom projektu to su named pipes i window messages.
Za komunikaciju samog ureaja sa raunarom, tj. EtfArduinoService servisom, je potrebno koristiti
neki vid komunikacije meu razliitim ureajima. U ovom projektu, koritena je komunikacija preko
(virtualnog) serijskog porta.
DLL sa engineom komunicira tako to implementira COM interfejs koji engine zahtijeva.

Slika 8: Komunikacija komponenti sistema

28

3.3. Problem sinhronizacije niti


Prije prelaska na opis implementacije pojedinih komponenti sistema, bit e dat opis odreenih klasa
koje e biti koritene kao pomone (utility) klase pri implementaciji nekih veih i kompleksnijih
dijelova.
Jedan od najbitnijih problema koji se javlja pri razvoju kompleksnih sistema je postizanje ispravnog
funkcionisanja vie niti unutar jednog procesa. Koristei vienitnost moe se puno dobiti na efikasnosti,
ali, ukoliko se ne preduzmu potrebne mjere, moe doi do problema koje je izuzetno teko otkriti.
Jedna od tih mjera je sinhronizacija ili serijalizacija niti. To podrazumijeva koritenje odgovarajuih
mehanizama kako bi se osiguralo da vie niti ne izvrava neki dio programa u isto vrijeme. Ukoliko
jedna nit ue u taj dio programa (koji se naziva kritina sekcija), sve ostale moraju ekati dok ta nit ne
izae iz kritinog dijela. Najee je tako neto potrebno postii kada se pristupa dijeljenom resursu
kao to su hardverski ureaji, objekti dijeljeni meu nitima i sl. Ovo se jo naziva uzajamno
iskljuivanje (mutual exclusion).

3.3.1. Win32 API: uzajamno iskljuivanje


Za postizanje uzajamnog iskljuivanja Win32 API nudi vie razliitih opcija. U implementaciji ovog
projekta je odabrano koritenje critical section objekata. To su objekti koji obezbjeuju sinhronizaciju
niti unutar istog procesa, tj. objekat nije mogue dijeliti izmeu razliitih procesa kao to je to sluaj sa
mutex objektima.
Funkcije za koritenje critical section objekta su:

InitializeCriticalSection inicijalizira critical section objekat iji je pokaziva proslijeen kao


parametar funkcije.

EnterCriticalSection niti koriste ovu funkciju kako bi zahtijevali iskljuivo vlasnitvo nad
critical section objektom iji je pokaziva proslijeen kao parametar. Ukoliko neka druga nit
posjeduje objekat u trenutku poziva funkcije, poziv blokira izvravanje niti sve dok objekat ne
postane slobodan i pree u vlasnitvo trenutne niti.

LeaveCriticalSection funkcija koritena kako bi nit napustila vlasnitvo critical section


objekta i time omoguila drugim nitima da nastave svoje izvravanje, tj. preuzmu vlasnitvo
nad tim objektom. Ukoliko nit zavri svoje izvravanje bez poziva ove funkcije, critical section
objekat ostaje permanentno u zakljuanom stanju, tj. sve ostale niti koje ele preuzeti vlasnitvo
e osatati permanentno blokirane.

DeleteCriticalSection funkcija oslobaa sve resurse koje critical section objekat zauzima.
Nakon poziva ove funkcije vie nije mogue koristiti objekat za sinhronizaciju.

29

Kako bi koritenje ovog sinhronizacijskog mehanizma bilo olakano te kako bi se sprijeile mogue
greke i problemi nastali zbog neoslobaanja vlasnitva nad critical section objektima uslijed baenih
izuzetaka, implementirane su dvije pomone klase.

3.3.2. CriticalSection klasa


class CriticalSection
{
public:
CriticalSection();
~CriticalSection();
void enter();
void leave();
private:
CRITICAL_SECTION cs;
};

Wrapper klasa oko CRITICAL_SECTION objekta koja pomou svojih metoda Enter i Leave nudi
funkcionalnost preuzimanja i oslobaanja vlasnitva nad critical section objektom. Te metode su
implementirane preko poziva EnterCriticalSection i LeaveCriticalSection funkcija.
Konstruktor i destruktor se brinu o ispravnom inicijaliziranju i oslobaanju resursa asociranih sa
critical section objektom tako to pozivaju funkcije InitializeCriticalSection i DeleteCriticalSection.

3.3.3. Lock klasa


class Lock
{
public:
Lock(CriticalSection& criticalSection);
~Lock();
private:
void acquire();
void release();
CriticalSection& criticalSection;
};

Klasa definie objekte koji u svom konstruktoru primaju CriticalSection objekat i pokuavaju preuzeti
vlasnitvo nad njim pozivajui metodu enter tog objekta. Vlasnitvo nad objektom se oslobaa u
desktruktoru objekta pozivom leave metode CriticalSection objekta.
Na ovaj nain je obezbijeeno da nit oslobodi vlasnitvo u trenutku izlaska iz bloka u kojem je
deklarisan Lock objekat, bez obzira da li je u pitanju normalno izvravanje ili je baen neoekivani
izuzetak.

30

3.4. Meuprocesna komunikacija


Kao to je pomenuto na poetku poglavlja, za komunikaciju meu procesima su koriteni mehanizmi
named pipe i poruka prozorima (window messages). Slijedi opis svake od tih metoda, naina na koji su
podrani kroz WinAPI te naina na koji su koriteni u ovom projektu.

3.4.1. Named pipe


Named pipe predstavlja jednosmjerni ili duplex komunikacioni kanal izmeu pipe servera i jednog ili
vie pipe klijenata. Sve instance named pipe objekta dijele isto ime, ali svaka od njih ima zasebne
buffere to znai da svim klijentima omoguava zasebnu metodu komunikacije sa serverom.
Named pipe omoguava procesima da razmjenjuju velike koliine podataka pa je iz tog razloga
odabran kao jedan od mehanizama meuprocesne komunikacije u ovom projektu.

3.4.1.1. Win32 API za komunikacjiu preko named pipeova


Win32 API definie nekoliko funkcija pomou kojih se implementira komunikacija koristei named
pipe. Najbitnije, tj. one koritene u implementaciji ovog projekta su:

CreateNamedPipe kreira novu instancu named pipea datog imena. Vraa handle na
novokreiranu instancu pomou kojeg je mogue pristupiti ostalim funkcionalnostima te
instance.
Ime pipea mora biti dato u formatu \\.\pipe\IME_PIPEA.
Ovom funkcijom se takoer postavljaju atributi instance named pipea koja se kreira.
Postavljanjem atributa moda otvaranja instance (open mode) na PIPE_ACCESS_DUPLEX
dozvoljava se dvosmjerna komunikacija izmeu klijenta i servera.
Mod komunikacije preko instance pipea se moe postaviti na tzv. bajt mod i message mod.
Razlika je u tome to se u message modu razliite operacije pisanja na named pipe mogu
raspoznati i u njihovoj cijelosti itati, to dovodi do lakeg razdvajanja nevezanih poruka, bez
potrebe za slanjem specijalnih znakova koji bi odvajali razliite poruke. Ovaj mod se postavlja
zadavanjem PIPE_TYPE_MESSAGE i PIPE_READMODE_MESSAGE flagova parametra pipe
mode.

ConnectNamedPipe omoguava serverskom procesu named pipe komunikacije da eka da se


na instancu kreiranu pozivom funkcije CreateNamedPipe spoji klijent. Poziv ove funkcije
blokira nit koja je izvrila poziv sve dok se klijent ne spoji kada funkcija vraa BOOL
vrijednost kojom govori da li je konekcija uspjeno izvrena.

31

CreateFile funkcija omoguava klijentskom procesu da se povee na neku instancu named


pipea datog imena koja je ve kreirana i otvorena za klijentske konekcije (prethodne dvije
funkcije).
Funkcija prepoznaje da je u pitanju otvaranje konekcije sa named pipe instancom na osnovu
formata imena filea koji se proslijedi.
Funkcija vraa handle pomou kojeg se moe pristupiti klijentskoj strani pipea.

SetNamedPipeHandleState funkcija koja se koristi za izmjenu moda komunikacije pipea.


Nakon to se klijent spoji na instancu named pipea, handle koji vraa CreateFile funkcija
omoguava bajt mod itanja. Postavljanjem PIPE_READMODE_MESSAGE flaga za
parametar mode ove funkcije, mod se prebacuje na message.

WriteFile pomou ove funkcije klijent i server vre upis podataka koje druga strana moe
itati. Funkciji kao parametre prime handle na pipe instancu, pokaziva na buffer iz kojeg treba
slati podatke i broj bajta koje je potrebno poslati.
Kada je handle koji je proslijeen funkciji u message modu, primalac je u stanju automatski
razdvojiti podatke poslane razliitim pozivima ove funkcije.
Funkcija blokira izvravanje niti iz koje je pozvana sve dok se svi podaci ne poalju nakon ega
vraa BOOL vrijednost koja je indikator uspjenosti slanja podataka.

ReadFile pomou ove funkcije klijent i server itaju podatke koje je druga strana poslala
preko instance named pipea iji je handle dat kao parametar.
Kada je mod komunikacije named pipea postavljen na tip message, funkcija bez obzira na
proslijeeni broj bajta koje treba proitati, vraa samo onoliko podataka koliko je poiljalac
poslao pri pozivu WriteFile funkcije.
Ukoliko ne postoje neproitane poruke na strani pipea s koje je pozvana funkcija, blokira se
izvravanje niti sve dok druga strana ne izvri slanje poruke.

TransactNamedPipe kada je mod komunikacije na instanci named pipea postavljen na


message, mogue je koristiti ovu funkciju kako bi se u jednoj operaciji poslali podaci na drugu
stranu pipea, a zatim saekao i proitao odgovor na tu poruku.
Izvravanje operacije se na zavrava prije nego to se proita odgovor i smjesti u odgovarajui
buffer iji pokaziva je proslijeen.

DisconnectNamedPipe funkcija diskonektuje serversku stranu named pipea od klijentskog


procesa. Ukoliko je u trenutku poziva funkcije klijentska strana otvorena, funkcija uzrokuje
njeno zatvaranje. Nakon toga, svaki pokuaj klijenta da pristupi named pipeu vraa greku.

32

Instanca named pipea nad kojom je pozvana funkcija se moe otvoriti za konekciju drugog
klijenta koristei ConnectNamedPipe funkciju nad istim handleom.

CloseHandle pozivanjem ove funkcije nad handleom povezanim sa klijentskom ili


serverskom stranom pipea, zatvara se komunikacioni kanal.

3.4.1.2. PipeCommunicator klasa


Ova klasa slui da sakrije detalje kreiranja named pipe instanci, primanja klijentskih konekcija, kao i
primanja i slanja poruka preko kreirane veze. Javni interfejs klase otkriva koje funkcije klasa podrava:
public:
typedef int message_t;
static DWORD const bufferSize = 512;
PipeCommunicator(LPCWSTR pipeName);
~PipeCommunicator();
bool Connect();
bool SendMessage(LPCVOID buffer, DWORD bufferSize);
bool RetrieveMessage(LPVOID buffer, DWORD bufferSize, LPDWORD bytesRead);

Konstruktor objekta kreira novu instancu named pipea pod imenom koje mu je proslijeeno kao
parametar. Ovo je postignuto pozivom WinAPI funkcije CreateNamedPipe.
Vrijednosti parametara poziva funkcije su takvi da se kreira named pipe koji radi u message modu
prenosa podataka.
hPipe = CreateNamedPipe(
pipeName,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
bufferSize,
bufferSize,
0,
NULL);

//
//
//
//
//
//
//
//
//
//

pipe name
read/write access
message type pipe
message-read mode
blocking mode
max. instances
output buffer size
input buffer size
client time-out
default security attribute

Destruktor objekta zatvara serversku stranu named pipe konekcije.


Metoda Connect eka da se klijent spoji na named pipe instancu koja je kreirana. To je postignuto
pozivom ConnectNamedPipe WinAPI funkcije.
Metoda blokira izvravanje threada u kojoj je pozvana sve dok se neki klijent ne konektuje. Povratna
vrijednost je tipa bool i oznaava da li je konekcija uspjeno izvrena ili ne.
Metoda SendMessage prima pokaziva na buffer i broj bajta koje je potrebno poslati. Metoda pozivom
WriteFile funkcije vri prenos podataka od servera prema klijentu.
33

Izvravanje niti koja poziva ovu metodu se blokira sve dok se cijela poruka ne poalje. Povratna
vrijednost je tipa bool i oznaava da li je poruka uspjeno poslana.
Metoda RetrieveMessage prima pokaziva na buffer u koji se trebaju smjestiti proitani podaci, broj
bajta koje treba proitati i pokaziva na DWORD u koji se moe smjestiti broj bajta koji su zaista
proitani.
Izvravanje niti koja poziva ova funkciju se blokira sve dok se poruka ne proita. Povratna vrijednost je
tipa bool i oznaava da li je poruka uspjeno proitana.

3.4.2. Window Messages


Windows operativni sistem implementira sistem proslijeivanja poruka aplikacijama kako bi Windows
aplikacije bile event-driven, tj. kako bi mogle da odgovaraju na razliite dogaaje u sistemu. Dogaaji
na koje aplikacija moe odgovarati ukljuuju korisniki ulaz, promjenu veliine prozora aplikacije i sl.
Ovaj sistem radi tako to operativni sistem alje poruku (Message) nekom prozoru (Window) aplikacije.
Svaki prozor registruje callback funkciju koja e se pozvati kada aplikacija primi poruku koja je
namijenjena za taj prozor. Callback funkcija je zaduena da na osnovu primljene poruke preduzme
odgovarajuu akciju. Svaka poruka sadri dvije vrijednosti koje mogu nositi dodatne informacije o
poruci i koje mogu uticati na to ta callback funkcija radi.
Poruke za aplikaciju se smijetaju u message queue iz kojeg se mogu preuzeti na FIFO nain koristei
WinAPI funkcije. Uobiajeni nain za preuzimanje poruka je da se u nekoj niti aplikacije izvrava
petlja koja konstantno provjerava ima li novih poruka u message queueu te ako ima pokree
odgovarajue callback funkcije. Ova petlja se naziva message loop.
Ono to je znaajno je da poruke prozoru aplikacije ne mora slati samo operativni sistem. Svi programi,
s dovoljnim nivoom privilegije, mogu poslati poruku u message queue bilo kojeg prozora. Ova
injenica, kao i to da je mogue kreirati prozore bez grafikog prikaza, je iskoritena kako bi se
implementirala meuprocesna komunikacija i u ovom projektu.

3.4.2.1. Win32 API: Window i WindowMessage

RegisterClass funkcija registruje window klasu koja e u budunosti biti koritena u


pozivima CreateWindow funkcije.
Funkcija kao parametar prima pokaziva na WNDCLASS strukturu koja definie sve parametre
prozora te klase, od kojih su najbitniji callback funkcija koja e se izvriti kada prozor primi
poruku i tekstualno ime prozora.
Funkcija vraa identifikator registrovanog prozora koji se moe koristiti pri pozivu funkcije
CreateWindow.

34

CreateWindow funkcija kreira novi prozor tipa proslijeene Window klase. Takoer postavlja
parametre prikaza kreiranog prozora.
Kao identifikator Window klase koja treba biti koritena za kreiranje prozora, moe se
proslijediti ili tekstualno ime klase ili vrijednost koju je vratila RegisterClass funkcija.
Funkcija vraa handle tipa HWND pomou kojeg se moe identifikovati novokreirani prozor.

FindWindow funkcija pronalazi prozor ija klasa ima tekstualno ime ili identifikator koji je
proslijeen kao parametar i vraa handle (HWND) pomou kojeg se moe referencirati.

GetMessage funkcija preuzima sljedeu neobraenu poruku iz message queuea aplikacije i


stavlja je u MSG strukturu iji pokaziva je proslijeen kao parametar.

DispatchMessage funkcija, na osnovu podataka iz MSG strukture iji pokaziva joj je


proslijeen kao parametar, poziva odgovarajuu callback funkciju povezanu s prozorom za koji
je poruka namijenjena.
Povratna vrijednost funkcije je vrijednost koju vrati callback.

DestroyWindow funkcija unitava prozor iji handle prima kao parametar. alje prozoru
Windows predefinisanu poruku WM_DESTROY koja signalizira callback proceduri prozora da
izvri oslobaanje svih resursa koje je prozor zauzeo. Ovu funkciju moe pozvati samo thread
koji je i kreirao prozor pozivom funkcije CreateWindow.

UnregisterClass funkcija deregistruje klasu prozora ime se oslobaa memorija koju


definicija tog prozora zauzima. Parametar funkcije je tekstualno ime prozora ili identifikator
koji je vratila RegisterClass funkcija.

RegisterWindowMessage registruje novu poruku koja se moe slati prozorima (window


message) na nivou sistema iji je identifikator jedinstven. Identifikator je povratna vrijednost
funkcije i moe se koristiti pri slanju poruka prozorima.
Ukoliko dvije razliite aplikacije pozovu funkciju sa istom vrijednosti imena poruke, obje
aplikacije dobivaju istu vrijednost ime se postie mogunost komunikacije tih aplikacija
pomou registrovane poruke.

PostMessage funkcija stavlja message, iji identifikator i parametri su proslijeeni funkciji,


na kraj message queuea koji je povezan sa prozorom iji handle je proslijeen. Funkcija ne eka
da poruka bude obraena.

35

3.4.2.2. MessageWindow klasa


Pomou objekta klase MessageWindow, adaptor prima i obrauje poruke od procesa koji je zaduen za
komunikaciju sa Arduino ureajima EtfArduinoService servis. Ove poruke slue da obavijeste
adaptor o stanju servisa.
Jedina poruka koja je trenutno implementirana u projektu je signal da je servis prikupio cijeli buffer
podataka od ureaja pri analognoj akviziciji te da je spreman za novi zahtjev za preuzimanje buffera
podataka.
Klasa implementira singleton design pattern, obzirom da je mogue kreirati samo jedan prozor koji bi
obraivao poruke od servisa, bez obzira na broj akvizicionih podsistema koje MATLAB-ov Data
Acquisition Engine kreira.
Ovaj prozor prilikom obrade primljene poruke odreuje za koji podsistem je poruka namijenjena i
poziva metodu objekta povezanog s tim podsistemom predvienu za obradu tog dogaaja. Kako bi
pronaao odgovarajui objekat za dati podsistem, MessageWindow objekat ima std::map objekat koji
uva mapiranja para (ID ureaja, podsistem ureaja) na pokaziva na odgovarajui objekat podsistema.
Kada podsistem zapone aktivnost koja e zahtijevati poruke od servisa, registruje se pri
MessageWindow klasi pozivom odgovarajue metode kojoj proslijedi pokaziva na sebe. Po zavretku
aktivnosti, kada prestane potreba za primanjem signala od servisa, podsistem se deregistruje iz skupa
podsistema koje MessageWindow treba obavjetavati.
Javni interfejs sa privatnim lanovima i pomonim funkcijama je:
class MessageWindow {
public:
// Destructor
virtual ~MessageWindow();
// Public member functions
bool AddDevice(UINT deviceId, UINT subsystem, LPVOID device);
bool RemoveDevice(UINT deviceId, UINT subsystem);
// Public static member functions
static MessageWindow& getInstance();
private:
typedef LPVOID DevicePtr;
struct DeviceIdentificator {
UINT deviceId;
UINT subsystem;
DeviceIdentificator(UINT deviceId, UINT subsystem) :
deviceId(deviceId), subsystem(subsystem) { }
bool operator< (DeviceIdentificator const& rhs) const {
return deviceId < rhs.deviceId;
}
};
typedef std::map<DeviceIdentificator, DevicePtr> map_t;
// Constructors
MessageWindow();
// Private members
HWND mHandle;

36

};

HANDLE hThread;
map_t devices;
CriticalSection cs;
// Private member functions
bool StartMessageLoopThread();
bool CreateMessageWindow();
DevicePtr GetDevice(UINT deviceId, UINT subsystem);
// Private static members
static TCHAR const* mClassName;
// A Window Message registered with the OS
static UINT WM_ETFARDUINO;
// Subsystem identifier constants
static UINT const AI_SUBSYSTEM = 1;
static UINT const AO_SUBSYSTEM = 2;
// Private static member functions
static TCHAR const* className();
static LRESULT CALLBACK WindowProc(HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp);
static DWORD WINAPI ThreadProc(LPVOID param);

Kao to se moe vidjeti, konstruktor klase se nalazi u privatnom dijelu kako bi se zabranilo kreiranje
objekata tipa MessageWindow vanjskim korisnicima klase.
Statika javna metoda getInstance vraa referencu na jedinu instancu objekta koja postoji za vrijeme
izvravanja procesa.
MessageWindow& MessageWindow::getInstance() {
// Singleton pattern
static MessageWindow mInstance;
return mInstance;
}

Statiki privatni atribut className uva tekstualno ime klase prozora.


Statiki privatni atribut WM_ETFARDUINO predstavlja identifikator poruke koja se registruje pri
operativnom sistemu za komunikaciju adaptora i servisa.
UINT MessageWindow::WM_ETFARDUINO = RegisterWindowMessage(TEXT("WM_ETFARDUINO"));

Konstruktor registruje klasu prozora i pozivom pomone privatne metode StartMessageLoop, zapoinje
thread u kojem se izvrava message loop.
Poziv WinAPI funkcije RegisterClass u konstruktoru:
WNDCLASS classStruct = {
0,
&MessageWindow::WindowProc,
0,
0,
0,
0,
0,
0,
0,

//
//
//
//
//
//
//
//
//

style
WndProc
cbClsExtra
cbWndExtra
hInstance
hIcon
hCursor
hbrBackground
lpszMenuName

37

className
// lpszClassName
};
if (!RegisterClass(&classStruct)) {
throw TEXT("Fatal error! Unable to register the MessageWindow class.");
}

Nit u kojoj se izvrava message loop je i nit koja kreira prozor pozivajui CreateMessageWindow
metodu klase MessageWindow nad objektom iji je pokaziva proslijeen kao parametar. Ovo je iz
razloga to samo nit koja kreira prozor moe zvati funkciju DestroyWindow. Snippet koda ispod
prikazuje statiku funkciju ThreadProc koja predstavlja opisanu nit.
DWORD WINAPI MessageWindow::ThreadProc(LPVOID param) {
// Thread treba najprije da registruje prozor kojem ce se
// proslijedjivati poruke
MessageWindow& msgWin = *((MessageWindow*)param);
msgWin.CreateMessageWindow();
// Sada zapoceti message loop
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return 0;
}

Metoda CreateMessageWindow, kao to je ve reeno, kreira prozor:


bool MessageWindow::CreateMessageWindow() {
mHandle = CreateWindow(
className,
0,
WS_CAPTION,
0, 0, 0, 0,
0,
0,
0,
0);
return true;
}

//
//
//
//
//
//
//
//

className
title
style
x, y, cx, cy
parent
menu
instance
create struct

Iz snippeta koda konstruktora, koji je dat iznad, vidi se da je callback procedura koja se izvrava za
obradu poruka koje stignu prozoru statika metoda WindowProc klase MessageWindow.
Ukoliko prozor primi poruku koju je poslao servis moe je prepoznati po jedinstvenom identifikatoru
WM_ETFARDUINO, zbog svojstva RegisterWindowMessage funkcije koja je ve objanjena. Za
obradu te poruke, pronalazi se odgovarajui objekat podsistema kojem je namijenjena poruka i poziva
metoda predviena za obradu dogaaja. Vrijednosti koje su potrebne za pronalazak podsistema su ID
ureaja i ID podsistema koji su poslani u prvom (wParam) i drugom (lParam) parametru messagea.
if (msg == WM_ETFARDUINO) {
// Preuzimanje parametara
UINT deviceId = (UINT)wp;
UINT subsystem = (UINT)lp;
// Pronalazak podsistema

38

if (subsystem == AI_SUBSYSTEM) {
MessageWindow& msgWin = MessageWindow::getInstance();
CetfarduinoAin* ain =
(CetfarduinoAin*) msgWin.GetDevice(deviceId, subsystem);
if (ain != 0) {
ain->ReceivedMessage(wp, lp);
}
}

Ostale poruke koje je potrebno obraditi su rijeene na sljedei nain:


switch (msg)
{
case WM_CLOSE:
DestroyWindow(msgWindow);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(msgWindow, msg, wp, lp);
}

Vidi se da poruka WM_CLOSE izaziva poziv WinAPI funkcije DestroyWindow, a da WM_DESTROY


poruka dovodi do zaustavljanja message loopa.
Privatna ugnijedena klasa DeviceIdentificator slui kao pomona klasa iji su objekti kljuevi pomou
kojih se identifikuju i pronalaze pokazivai na objekte podsistema.
Metode AddDevice, RemoveDevice i GetDevice, slue za manipulaciju std::map objektom devices, koji
uva sve pokazivae na objekte podsistema. Metode su meusobno sinhronizovane koristei
CriticalSection i Lock objekte kako bi se pristup objektu devices zatitio od istovremenog pristupa iz
razliitih niti procesa.
bool MessageWindow::RemoveDevice(UINT deviceId, UINT subsystem) {
Lock lock(cs);
devices.erase(DeviceIdentificator(deviceId, subsystem));
return true;
}

39

3.4.2.3. MessageWindowSender klasa


MessageWindowSender je klasa koju koristi EtfArduinoService proces kako bi poslao odgovarajue
notifikacije adaptoru.
class MessageWindowSender {
public:
// Constructors
MessageWindowSender(UINT deviceId, int subsystem);
// Destructor
virtual ~MessageWindowSender();
// Public member functions
bool NotifyBufferFull();
// Public static members
static int const SUBSYSTEM_AI = 1;
static int const SUBSYSTEM_AO = 2;
private:
// Private members
HWND mHandle;
UINT const deviceId;
int const subsystem;
// Private static members
static TCHAR const* className;
static UINT WM_ETFARDUINO;
};

Obzirom da su notifikacije predviene da se alju kao poruke prozoru, klasa sadri privatne statike
atribute className i WM_ETFARDUINO koji definiu tekstualno ime klase prozora i identifikator
poruke koju alje servis, respektivno.
TCHAR const* MessageWindowSender::className = TEXT("EtfArduinoMessageWindow");
UINT MessageWindowSender::WM_ETFARDUINO = RegisterWindowMessage(TEXT("WM_ETFARDUINO"));

Bitno je primijetiti da je ime klase postavljeno na istu vrijednost kao to je to i u klase MessageWindow
koja registruje prozor.
Konstruktor MessageWindowSender objekta pomou WinAPI funkcije FindWindow pronalazi kreirani
prozor koji oekuje da primi poruke. Kao parametre konstruktor prima ID ureaja i podsistema za
kojeg e sve njegove poruke biti namijenjene.
Metoda NotifyBufferFull pomou PostMessage funkcije u message queue prozora stavlja poruku koja
oznaava da je servis popunio cijeli buffer podataka koje je primio od Arduino ureaja.
if (!PostMessage(mHandle, WM_ETFARDUINO, (WPARAM) deviceId, (LPARAM) subsystem)) {
return false;
}

Iz snippeta se vidi da je identifikator poruke WM_ETFARDUINO, prvi parametar poruke ID ureaja, a


drugi parametar identifikator podsistema.

40

3.5. EtfArduinoService proces


Ovo je proces koji slui za direktnu komunikaciju sa Arduino ureajima. Ostali procesi koji ele pristup
funkcionalnostima Arduino ureaja za akviziciju im mogu pristupiti samo preko usluga ovog procesa.
Za komunikaciju sa klijentima koristi mehanizam meuprocesne komunikacije named pipe. Klijenti,
znajui ime pipea se spajaju na servis, a zatim, slanjem poruka kroz instancu pipea na koju su spojeni,
zahtijevaju usluga od ovog servisa. Za olakano koritenje usluga servisa, implementirana je klasa
EtfArduinoService koja detalje komunikacije sakriva iza svog interfejsa.
Servis koristi i drugu metodu meuprocesne komunikacije koja je ranije predstavljena, poruke
prozorima. Ta metoda je koritena kada sam proces ima potrebu da obavijesti klijente o dogaajima
koji su se desili. Potreba za ovim dolazi iz injenice da neke operacije moraju biti asinhrone, pa je onda
jedini nain da se pozivalac obavijesti o njihovom zavretku ili statusu, neki vid meuprocesne
komunikacije. Tu su poruke prozorima izuzetno pogodne jer nije potrebno brinuti o implementaciji
sinhronizacije reda poruka, ve se Windows operativni sistem za to brine.
Ispod se moe vidjeti dijagram klasa koritenih u implementaciji komponente.

Slika 9: Dijagram klasa EtfArduinoService komponente


41

Moe se uoiti da je koriten abstract factory dizajn pattern za implementaciju klasa za procesiranje
zahtijeva klijenata. Razlog za to je vea modularnost i time laka proirivost servisa dodatnim
funkcionalnostima, ukoliko za tim bude postojala potreba.

3.5.1. EtfArduinoService klasa


Ova klasa slui da od svog korisnika sakrije detalje komunikacije klijentskog procesa sa
EtfArduinoService server procesom. Ono to klasa otkriva su metode koje predstavljaju
funkcionalnosti koje server nudi. Pozivom odgovarajue metode, klijent pristupa tim
funkcionalnostima.
Javni interfejs klase je:
public:
// Constructors
EtfArduinoService();
// Destructor
virtual ~EtfArduinoService();
// Public member functions
bool setResponseBufferSize(DWORD responseBufferSize);
bool GetBufferedData(
UINT deviceId,
LPVOID ptrBuffer,
DWORD bufferSize);
bool StartAcquisition(UINT deviceId);
bool StopAcquisition(UINT deviceId);
bool SetAcquisitionBufferSize(UINT deviceId, DWORD bufferSize);
bool SetSampleRate(UINT deviceId, DWORD sampleRate);
bool CheckDeviceRegistered(UINT deviceId, LPTSTR serialPortName);
bool RegisterDevice(UINT& deviceId, LPCWSTR serialPortName);
bool GetRegisteredDeviceIds(std::vector<UINT>& deviceIds);
unsigned short
GetSingleValue(UINT deviceId);
bool SendDigitalValue(UINT deviceId, int line, int value);
bool PutSingleValue(UINT deviceId, int channel, int value);

Konstruktor klase poziva OpenPipeConnection privatnu metodu koja se pokuava spojiti na postojeu
named pipe instancu na kojoj je server predvidio komunikaciju sa klijentima. Ukoliko je konekcija
neuspjena, pri buduim pozivima metoda kojima je potrebna mogunost komunikacije sa serverom,
ponovo se pokuava uspostaviti konekcija kako bi se za sigurno utvrdilo da komunikacija nije mogua
prije vraanja poruke greke.
U snippetu koda koji se nalazi ispod se vidi nain na koji se klijent pokuava spojiti na server.
hPipe = CreateFile(
PIPENAME,
GENERIC_READ |
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);

// pipe name
// read and write access
//
//
//
//
//

no sharing
default security attributes
opens existing pipe
default attributes
no template file

42

if (hPipe != INVALID_HANDLE_VALUE)
break;
if (GetLastError() != ERROR_PIPE_BUSY) {
hPipe = 0;
return false;
}
if (!WaitNamedPipe(PIPENAME, 20000)) {
hPipe = 0;
return false;
}

Ukoliko je konekcija uspjena, mod komunikacije se postavlja na message.


DWORD dwMode = PIPE_READMODE_MESSAGE;
BOOL fSuccess = SetNamedPipeHandleState(
hPipe,
// pipe handle
&dwMode,
// new pipe mode
NULL,
// don't set maximum bytes
NULL);
// don't set maximum time

Privatna metoda SendMessage vri slanje podataka iz buffera koji joj je proslijeen kao parametar
prema serveru.
BOOL fSuccess = WriteFile(
hPipe,
// pipe handle
lpvMessage,
// message
cbToWrite,
// message length
&cbWritten,
// bytes written
NULL);
// not overlapped
// Make sure the message got through the pipe before returning.
FlushFileBuffers(hPipe);

Privatna metoda ClosePipeConnection najprije serveru alje specijalnu poruku kojom signalizira kraj
komunikacije, a zatim zatvara klijentsku stranu NamedPipe instance.
Sve javne metode, koje klijentskom procesu otkrivaju neku od funkcionalnosti servera, prolaze kroz
istu sekvencu koraka pri implementaciji, obzirom da je zadatak svake od njih jednostavno poslati
odgovarajuu poruku serveru i, eventualno, preuzeti odgovor.
Najprije, formira se buffer u kojem e se nai podaci koji se trebaju poslati prema serveru. Buffer je niz
alociran na stacku iji su elementi tipa message_t. Tip message_t je definisan kako bi svaki niz koji je
predvien da sadri podatke koji se alju serveru bio lako prepoznatljiv, ali i kako bi se taj tip mogao u
budunosti lako izmijeniti, ukoliko za tim bude postojala potreba. Trenutno je ovaj tip alias za tip int.
Buffer se puni podacima tako to se na prvo mjesto u nizu stavlja kod operacije koja se zahtijeva od
servera. Klasa kao privatne statike lanove sadri vrijednosti svih kodova koji odgovaraju operacijama
ije izvravanje podrava:

43

static
static
static
static
static
static
static

message_t
message_t
message_t
message_t
message_t
message_t
message_t

const
const
const
const
const
const
const

GET_BUFFERED_DATA = 1;
START_ACQUISITION = 2;
STOP_ACQUISITION = 3;
GET_SINGLE_VALUE = 4;
CHECK_DEVICE_REGISTERED = 5;
REGISTER_DEVICE = 6;
GET_REGISTERED_DEVICE_IDS = 7;

static message_t const


static message_t const

SET_ACQUISITION_BUFFER_SIZE = 10;
SET_SAMPLE_RATE = 11;

static message_t const

SEND_DIGITAL_VALUE = 20;

static message_t const

PUT_SINGLE_VALUE = 30;

static message_t const

DISCONNECT_CLIENT = 255;

Nakon toga, u bufferu slijede svi parametri koje server zahtijeva od klijenta za uspjeno izvravanje
traene komande.
Nakon toga, poruka se alje preko otvorene instance NamedPipe-a koja je pridruena objektu. Ukoliko
je u pitanju operacija koja zahtijeva odgovor od servera, koristi se TransactNamedPipe funkcija koja
automatski nakon slanja podataka eka i odgovor. S druge strane, ako se izvrava operacija kojoj nije
potreban odgovor od servera, podaci se jednostavno alju pomou funkcije WriteFile.
Na kraju, preuzeti odgovor se postavlja u za to predviene strukture koje su proslijeene metodi.
Metode vraaju bool koji uvijek predstavlja da li je izvravanje uspjeno okonano.

3.5.2. Komunikacija sa Arduinom


3.5.2.1. Win32 API: Serijska komunikacija
Komunikacija sa serijskim portovima je u Win32 API-u podrana pomou standardnih funkcija za
upravljanje fileovima. Pri tome, serijski portovi imaju specijalna imena pomou kojih sistem
prepoznaje koji serijski port je u pitanju.
Prije poetka komunikacije sa otvorenim serijskim portom, potrebno je izvriti i odreenu
konfiguraciju svojstava porta.

CreateFile otvara serijski port ije ime je proslijeeno kao parametar funkcije. Imena
serijskih portova moraju biti u formatu COMx, gdje je x jednocifren broj.
Mogue je odrediti da li je e port biti koriten za pisanje (GENERIC_WRITE), itanje
(GENERIC_READ) ili obje funkcije.
Pri pozivu funkcije obavezno je proslijediti parametar OPEN_EXISTING obzirom da serijski
portovi ve postoje, tj. njihovo kreiranje nije dozvoljeno.

44

Postavljanjem flaga FILE_FLAG_OVERLAPPED u odgovarajuem parametru, port se otvara u


overlapped modu, ime se definie mogunost istovremenog izvravanja operacija itanja i
pisanja na istom portu.
Funkcija vraa handle na otvoreni serijski port. Ukoliko poziv nije uspjean, vraena vrijednost
e biti INVALID_HANDLE_VALUE.

SetCommState postavlja parametre serijskog porta bitne za uspostavljanje uspjene


komunikacije. Kao parametar prima handle na serijski port koji je potrebno konfigurisati i
pokaziva na DCB strukturu koja definie sve potrebne parametre koje je potrebno postaviti.
Polja DCB strukture koja treba postaviti su:
BaudRate brzina prenosa podataka;
ByteSize broj bita u bajtima koji se alju i primaju;
StopBits broj stop bitova koje treba koristiti;
Parity koritena ema pariteta za detekciju greaka.

SetCommTimeouts postavlja parametre serijske komunikacije koji se tiu timeouta, tj.


vremena koje je dozvoljeno ekati da se pojedina operacija na serijskom portu izvri. Funkcija
kao parametar prima handle na serijski port koji se konfigurie i pokaziva na
COMMTIMEOUTS strukturu koja definie sve potrebne vrijednosti.
Polja strukture COMMTIMEOUTS koja je potrebno definisati su:
ReadIntervalTimeout vrijeme u milisekundama koje je dozvoljeno ekati izmeu
prijema bajtova prije nego to se operacija itanja zavri;
ReadTimeoutMultiplier vrijeme u milisekundama koje predstavlja vrijeme ekanja
za svaki bajt koji je zahtijevan u operaciji itanja.
ReadTotalTimeoutConstant vrijeme u milisekundama koje je ukupno dozvoljeno
provesti itajui poruku sa serijskog porta. Suma ove vrijednosti i prethodne definie
ukupno vrijeme koje je dozvoljeno iskoristiti za itanje;
WriteTimeoutMultiplier i WriteTimeoutConstant predstavljaju isto to i prethodne
dvije vrijednosti samo za operacije pisanja na serijski port.

WriteFile funkcija alje vrijednosti iz proslijeenog buffera na serijski port. Prima broj
bajtova koje je potrebno ispisati.
Povratna vrijednost funkcije je BOOL koji oznaava da li je operacija uspjeno izvrena.
45

Takoer, funkcija zapisuje broj uspjeno ispisanih bajtova na poziciju odreenu proslijeenim
pokazivaem.
Ukoliko je postavljen overlapped mod za port na koji se pokuavaju poslati podaci, funkcija ne
eka na kraj izvravanja prenosa svih podataka, ve se izvravanje nastavlja odmah nakon
njenog poziva. Tako, mogue je da povratna vrijednost bude FALSE, ali da je prenos podataka
jo uvijek u toku. Ta situacija se otkriva tako to WinAPI funkcija GetLastError vrati kod koji
odgovara greci ERROR_IO_PENDING.
Iz tog razloga, funkcija kao parametar prima pokaziva na OVERLAPPED strukturu koja sadri
Event objekat koji e biti signaliziran kada se operacija uspjeno zavri.

ReadFile funkcija vri itanje vrijednosti sa serijskog porta i upisuje ih u buffer koji je
proslijeen kao parametar. Prima broj bajtova koje je potrebno uitati.
Broj uspjeno uitanih bajtova se zapisuje na poziciju odreenu proslijeenim pokazivaem.
Funkcija vraa BOOL vrijednost koja oznaava da li je itanje datog broja bajtova uspjeno
izvreno.

CloseHandle zatvara handle na serijski port ime se oslobaaju resursi i prestaje


komunikacija sa portom. Omoguava se drugim aplikacijama da zaponu komunikaciju sa istim
portom.

3.5.2.2. SerialCommunicator klasa


SerialComunicator klasa slui da detalje komunikacije sa odreenim serijskim portom sakrije iza svog
javnog interfejsa:
public:
SerialCommunicator(LPCWSTR serialPortName);
virtual ~SerialCommunicator();
bool SendMessage(LPCVOID buffer, DWORD bufferSize);
bool RetrieveMessage(LPVOID buffer, DWORD bufferSize, LPDWORD bytesRead);

Konstruktor objekta se brine da se uspostavi komunikacija sa serijskim portom, ije je ime proslijeeno
kao parametar. To radi pozivom privatne metode InitializeSerial koja otvara port i postavlja
odgovarajue parametre komunikacije.
Pri otvaranju porta, specificiran je FILE_FLAG_OVERLAPPED kako bi se omoguilo istovremeno
itanje i pisanje podataka na serijskom portu.

46

hSerial =
CreateFile(serialPortName,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);

Brzina prenosa podataka, baud rate, je postavljena na 256000, istu vrijednost koju postavlja i Arduino
firmware, kako bi komunikacija izmeu raunara i ureaja bila sinhronizirana.
dcbSerialParams.BaudRate = CBR_256000;

Destruktor vri zatvaranje otvorenog serijskog porta kako bi se pri unitavanju objekta oslobodili svi
resursi koji su zauzeti.
Metoda SendMessage kao parametre prima pokaziva na buffer i veliinu buffera u bajtima.
WinAPI funkciji WriteFile se proslijeuju odgovarajui parametri, ukljuujui i pokaziva na
OVERLAPPED strukturu u kojoj se nalazi Event koji e primiti signal okonanja prenosa podataka.
Funkcija eka na kraj prenosa podataka pozivajui GetOverlappedResult funkciju ime se postie da se
izvravanje nee nastaviti sve dok se slanje podataka ne zavri.
Povratna vrijednost SendMessage metode je bool vrijednost koja oznaava da li je prenos uspjeno
zavren ili ne.
Metoda RetrieveMessage prima pokaziva na buffer, broj bajta koje je potrebno proitati i pokaziva na
DWORD u koji se moe upisati ukupni broj bajta koji su proitani.
Slino kao to je i sluaj kod metode SendMessage kreira se i OVERLAPPED struktura kojoj e biti
signaliziran kraj procesa itanja podataka sa serijskog porta.
Metoda vraa bool vrijednost koja oznaava da li je itanje uspjeno izvreno.

3.5.2.3. ArduinoDevice klasa


Pomou ArduinoDevice klase, mogue je pristupati funkcionalnostima Arduino kartice bez potrebe za
poznavanjem naina komunikacije izmeu raunara i same kartice. Sama ArduinoDevice klasa je
abstraktna bazna klasa, pa je za implementaciju metoda koje ona specificira potrebno definisati
konkretnu klasu izvedenu iz nje. Time je postignuto da su svi detalji u vezi s formatom poruka koje se
razmjenjuju sadrani samo u implementaciji metoda konkretnih ArduinoDevice klasa. Zahvaljujui
ovom dizajnu, sasvim je jednostavno podrati drugi metod komunikacije sa Arduino ureajima.

47

Javni interfejs klase ArduinoDevice otkriva koje funkcije su sve podrane:


public:
typedef unsigned short response_t;
ArduinoDevice(UINT deviceId);
ArduinoDevice(UINT deviceId, DWORD bufferSize);
virtual ~ArduinoDevice();
virtual
virtual
virtual
virtual
virtual
virtual
virtual
virtual

bool StartAcquisition() = 0;
bool StopAcquisition() = 0;
response_t GetSingleValue() = 0;
bool SetSampleRate(int sampleRate) = 0;
bool SetBufferSize(int bufferSize) = 0;
bool SendDigitalValue(int line, int value) = 0;
bool PutSingleValue(int channel, int value) = 0;
response_t* GetNextBuffer() = 0;

DWORD GetBufferSize() const;


UINT GetDeviceId()
const;
virtual LPCTSTR GetPortName() const = 0;

Protected lanovi su predvieni da budu koriteni od strane izvedenih klasa.


protected:
UINT deviceId;
DWORD bufferSize;
BufferQueue bufferQueue;
static DWORD const DEFAULT_BUFFER_SIZE = 64;

ArduinoDeviceSerial klasa je izvedena iz ArduinoDevice abstraktne bazne klase i implementira iste


virtualne metode tako to za komunikaciju s ureajem koristi serijski port uz pomo
SerialCommunicator klase.
Privatni dio klase ArduinoDeviceSerial je:
private:
TCHAR serialPortName[10];
int const tickFrequency;
volatile LONG acquisitionActive;
SerialCommunicator serial;
CriticalSection cs;
ArduinoDeviceSerial(ArduinoDeviceSerial const&);
ArduinoDeviceSerial& operator=(ArduinoDeviceSerial const&);

Konstruktor inicijalizira privatni SerialCommunicator objekat proslijeujui mu ime serijskog porta


koje je asocirano s ArduinoDevice objektom. Time se otvara i zauzima serijski port za komunikaciju
sve dok se ne pozove destruktor samog ArduinoDevice objekta nakon kojeg e biti uniten i
SerialCommunicator.
U desktruktoru nema potrebe poduzimati nikakvu aktivnost za oslobaanje resursa, obzirom da tako
neto nije potrebno ni za jedan lan klase.

48

Privatni lan acquisitionActive je LONG vrijednost koja se moe nai u tri stanja. Kada ima vrijednost
0, oznaava da proces analogne akvizicije za dati Arduino ureaj nije aktivan. Vrijednost 1 znai da je
proces zapoeo i trenutno aktivan, a vrijednost 2 predstavlja stanje u kojem je poslan zahtjev za
prestankom akvizicije, ali se jo uvijek eka prestanak preuzimanja novih podataka od ureaja.
Obzirom da vie razliitih threadova programa moe pozivati metode objekta koje imaju potrebu da
provjere ili promijene stanje varijable, potrebno je osigurati sinhronizovan pristup njenoj vrijednosti. S
tim ciljem, prvo to se treba osigurati je da itanje i pisanje bude zavreno kao jedan jedinstven
nedjeljiv korak bez mogunosti da se vrijednost u memoriji nae u nevalidnom stanju (atomic
operacija). Ovo je zagarantovano injenicom da se itanje i pisanje rijei (word) uvijek izvrava u
jednom nedjeljivom koraku.
Druga stvar koja je potrebna je biti siguran da se itanje vrijednosti varijable izvrava prije svih
memorijskih operacija koje dolaze nakon nje u programu (read-acquire operacija), kao i da se pisanje
vrijednosti varijable izvrava nakon svih memorijskih operacija koje dolaze ispred u programu (writerelease). Ovo je potrebno jer, bez posebno preduzetih mjera, moderni procesori, uslijed pokuaja
optimizacije izvravanja programa, vre izmjenu redoslijeda pristupa memoriji to, u sluaju pristupa
istoj vrijednosti iz razliitih niti programa, moe dovesti do itanja ili pisanja neispravnih vrijednosti.
Iako standard C++ jezika to ne spominje, Microsoft Visual C++ kompajler od verzije 2005 garantuje
da sve operacije itanja i pisanja volatile kvalificiranih varijabli imaju ve navedene osobine readacquire, tj. write-release operacija. To je razlog iz kojeg je acquisitionActive potrebno oznaiti i kao
volatile.
Metoda StartAcquisition slui da zapone proces akvizicije analognog signala. Najprije je potrebno
postaviti vrijednost acquisitionActive na 1 kako bi od tog trenutka sve ostale operacije objekta bile u
stanju prepoznati da je akvizicija aktivna.
Ipak, mogunost postoji da je sama metoda StartAcquisition pozvana u trenutku u kojem je aktivan
proces akvizicije, pa je njeno izvravanje onemogueno. Ovo znai da prije postavljanja nove
vrijednosti treba uraditi provjeru trenutne i samo u sluaju da je ta vrijednost 0, postaviti novu. Oito,
ova operacija mora biti nedjeljiva (atomic) kako mogui context switch nakon itanja vrijednosti
varijable, a prije upisa nove, ne bi doveo do neispravnih rezultata u nastavku izvravanja. Ovakva
operacija se naziva compare-and-swap (CAS).
Za tu svrhu, WinAPI obezbjeuje funkciju InterlockedCompareExchange koja za novu vrijednost
objekta iji je pokaziva proslijeen, postavlja vrijednost drugog parametra, samo u sluaju da je stara
vrijednost jednaka treem parametru. Funkcija vraa vrijednost koju je zatekla, pa se tako moe
odrediti da li je operacija izvrena ili ne u sluaju da je povratna vrijednost razliita od zadnjeg
parametra operacija nije izvrena.
if (InterlockedCompareExchange(&acquisitionActive, 1, 0) != 0) {
// Another thread already holds the acquisition active!
return false;
}

Iz snippeta koda s poetka metode StartAcquisition se vidi upotreba ove funkcije.


49

Nakon to je metoda uspjeno postavila indikator da je akvizicija aktivna, potrebno je poslati poruku
Arduino ureaju s komandom da zapone s vraanjem oitanih vrijednosti analognog signala. To je
postignuto slanjem jednog bajta koji sadri vrijednost ASCII znaka '1'. Ukoliko poruka nije uspjeno
poslana, izvravanje se prekida i signalizira se da proces akvizicije nije aktivan.
if (!serial.SendMessage(buffer, sizeof buffer)) {
acquisitionActive = 0;
return false;
}

Iza toga, program ulazi u petlju ije se izvravanje nastavlja sve dok se status indikatora
acquisitionActive ne izmijeni pozivom odgovarajue metode za zaustavljanje akvizicije. Petlja ita
vrijednosti koje Arduino ureaj vraa, stavlja ih u red preuzetih buffera te alje signal adaptoru da su
novi podaci spremni za preuzimanje.
while (acquisitionActive == 1) {
DWORD bytesRead = 0;
response_t* values = new response_t[bufferSize];
if (serial.RetrieveMessage(
values,
bufferSize * sizeof(response_t), &bytesRead)) {
bufferQueue.Push(values);
} else {
delete[] values;
}
// Send notification that this device has filled another buffer.
msgWinSender.NotifyBufferFull();
}

Po izlasku iz petlje, alje se poruka Arduino ureaju koja oznaava da treba prestati vriti analognu
akviziciju, pa time i slanje podataka raunaru. Tek poslije toga se indikator acquisitionActive vraa na
vrijednost 0 koja oznaava da je novi proces akvizicije mogue zapoeti.
buffer[0] = '2';
bool status = serial.SendMessage(buffer, sizeof buffer);
acquisitionActive = 0;
return status;

Poziv metode StartAcquisition blokira thread iz kojeg je poziv izvren, za sve vrijeme trajanja
akvizicije, to je potencijalno veliki vremenski period. Iz tog razloga, potrebno je osigurati da se poziv
izvri iz niti koja je namijenjena iskljuivo za izvravanje ove metode kako ne bi dolo do velikog
kanjenja u obradi drugih podataka.
StopAcquisition metoda slui da se proces akvizicije zaustavi. Ona postavlja indikator
acquisitionActive na vrijednost 2 to e izazvati da petlja koja vri preuzimanje vrijednosti u sljedeoj
iteraciji prestane s radom. Izmjena se vri samo ako trenutna vrijednost pokazuje da je akvizicija
aktivna. Slina kao i pri poetku akvizicije, za ovu svrhu je potrebno koristiti compare-and-swap
operaciju.

50

bool ArduinoDeviceSerial::StopAcquisition() {
// Sets the flag to indicate the acquisition should stop
if (InterlockedCompareExchange(&acquisitionActive, 2, 1) != 1)
return false;
return true;
}

GetSingleValue metoda slui za preuzimanje jedne vrijednosti analognog signala (jedan sample) sa
Arduino ureaja. Uz potrebne provjere validnosti i sinhronizacije izvravanja operacije, ureaju se alje
poruka od jednog bajta s ASCII vrijednosti koja odgovara karakteru '4'. Odmah nakon toga se trai
odgovor jedna unsigned short vrijednost (2 bajta) koja predstavlja traeni uzorak. Preuzeta vrijednost
se vraa kao povratna vrijednost funkcije.
U sluaju bilo koje greke pri izvravanju metode, vraena vrijednost je jednaka 0xFFFFFFFF to je
znak da ispravna vrijednost nije preuzeta, obzirom da je Arduino ADC ogranien na 10 bita, tj. najveu
vrijednost 1023.
SetSampleRate metoda vri izraun potrebne vrijednosti perioda koju je potrebno poslati Arduino
ureaju kako bi brzina sampliranja odgovarala traenom sample rateu. Ureaju se, nakon koda
operacije, alju bajti od kojih se sastoji izraunata vrijednost, poevi od najmanje znaajnog bajta
(LSB) do najbitnijeg bajta.
char buffer[1 + 4];
buffer[0] = '3';
buffer[1] = period;
buffer[2] = period >> 8;
buffer[3] = period >> 16;
buffer[4] = period >> 24;

// Truncated to least significant byte

Redoslijed slanja bajta je izuzetno bitan kako bi ureaj mogao ispravno rekonstruisati poslanu
vrijednost.
SetBufferSize metoda nema potrebu za komuniciranjem s Arduino ureajem, ve samo mijenja internu
vrijednost veliine buffera koji e puniti vrijednostima koje dolaze s Arduina. Ovu operaciju nije
mogue izvriti za vrijeme trajanja procesa akvizicije te se u tom sluaju vraa false.
SendDigitalValue metoda slui za izvravanje funkcije digitalnog izlaza Arduino ureaja. Njeno
izvravanje prati istu sekvencu koraka kao SetSampleRate obzirom da je u pitanju samo slanje poruke
ureaju bez potrebe za ekanjem odgovora. Poruka je veliine 3 bajta: kod poruke, identifikator
digitalnog izlaza, vrijednosti 0 ili 1 koja oznaava da li treba postaviti izlaz na low ili high vrijednost.
PutSingleValue metoda izvrava funkcionalnost slanja jedne analogne vrijednosti na izlaz Arduina.
Jedina razlika u odnosu na prethodnu metodu je da je drugi bajt poruke identifikator analognog izlaza,
a trei bajt moe primiti sve vrijednosti od 0 do 255, obzirom da je rezolucija Arduino analognog izlaza
8 bita.
GetNextBuffer metoda slui da pozivaocu vrati pokaziva na prvi sljedei nepreuzeti buffer podataka
koji su preuzeti s Arduino ureaja. U sutini, vraa se najstariji buffer obzirom da je struktura podataka
koja ih uva red (queue). Bitno je napomenuti da je predvieno da pozivalac nakon preuzimanja ovog
51

pokazivaa preuzima i potpunu odgovornost za eventualno oslobaanje resursa koje buffer zauzima
pozivom delete[] operatora.
ArduinoDevice::response_t* ArduinoDeviceSerial::GetNextBuffer() {
// No need to copy the data to another array since after this call
// the ownership of the buffer will be transferred to the caller
// anyway. (The caller will have to free some memory in any case.)
response_t* buffer = bufferQueue.Pop();
return buffer;

}
BufferQueue klasa
Logian izbor strukture podataka za uvanje buffera koji se trebaju vratiti adaptoru je red (queue),
obzirom da se bufferi obavezno moraju vratiti onim redoslijedom kojim su i stigli ureaju. Ipak,
std::queue definisan u standardnoj biblioteci jezika C++ ima ozbiljan problem koji ga ini nedovoljno
dobrim izborom implementacije reda. U pitanju je injenica da pristupi redu (pisanje i itanje) nisu
sinhronizovani izmeu razliitih threadova, obzirom da C++ standard ni ne poznaje pojam threada.
Ukoliko bi dolo do istovremenog izvravanja vie metoda std::queue klase, ponaanje programa je
nedefinisano (undefined behavior) i moe voditi do korupcije podataka.
Rjeenje problema je implementacija wrapper klase oko std::queue objekta koja sinhronizuje sve
pristupe redu koristei uzajamno iskljuive kritine sekcije. Ve predstavljene klase CriticalSection i
Lock su koritene u tu svrhu.
Javni interfejs klase pokazuje koje metode su podrane, a u privatnom dijelu se mogu vidjeti
std::queue i CriticalSection objekti koji su koriteni za implementaciju:
class BufferQueue
{
public:
typedef unsigned short* buffer_t;
BufferQueue();
~BufferQueue();
void Push(buffer_t value);
buffer_t Pop();
void Clear();
private:
std::queue<buffer_t> queue;
CriticalSection cs;
};

Push, Pop i Clear imaju poznatu semantiku dodavanja elementa na kraj reda, brisanja elementa s
poetka reda i brisanja svih elemenata u redu. Implementacija svake od metoda na poetku inicijalizira
Lock objekat nad CrticalSection objektom koji je lan instance klase. Time je postignuta serijalizacija
metoda tog objekta, tj. nemogue je da se u istom trenutku izvrava vie od jedne metode objekta bez
obzira na to iz kojeg threada je pozvana.

52

void BufferQueue::Push(unsigned short* value) {


Lock lock(cs);
queue.push(value);
}

Snippet koda za prikaz koritenja Lock objekta s svrhom postizanja uzajamnog iskljuivanja metoda
BufferQueue objekta.
Dodatno je predvieno da je objekat vlasnik svih buffera koji se dodaju sve do trenutka kada se izbrie
iz reda pozivom metode Pop. Nakon toga, pozivalac koji je preuzeo vrijednost je obavezan osloboditi
memoriju. Ovo znai da metoda Clear, kao i destruktor objekta, mora osloboditi memoriju koju bufferi
zauzimaju pozivom delete[] operatora.
void BufferQueue::Clear() {
Lock lock(cs);
while (!queue.empty()) {
unsigned short* buffer = queue.front();
delete[] buffer;
queue.pop();
}
}

3.5.3. DeviceMap klasa


Kako bi bilo mogue pronai sve ArduinoDevice objekte koji su povezani sa Arduino ureajima za rad
sa sistemom akvizicije opisanim u ovom radu, za njihovo uvanje je potrebna struktura podataka koja
na osnovu ID-a ureaja vraa traeni objekat.
std::map je C++ opcija za uvanje key, value parova kakvi su iznad opisani. Ipak, std::map je dio
standardne biblioteke, pa, slino kao to je i sluaj sa std::queue pri implementaciji BufferQueue klase,
poziv njegovih metoda je bez mehanizma sinhronizacije i moe dovesti do neoekihvanih rezultata i
pada programa ukoliko se metode pozivaju konkurentno iz razliitih threadova. To je situacija koja se
moe razumno oekivati u opisanom sluaju uvanja ArduinoDevice objekata. Iz tog razloga,
DeviceMap klasa implementira metode koje sinhronizuju pozive std::map metoda.
Javni interfejs zajedno sa privatnim strukturama koritenim za implementaciju:
class DeviceMap
{
public:
DeviceMap();
~DeviceMap();
// Inserts a device into the map and returns its ID
UINT Insert(LPCWSTR serialPortName);
void Remove(UINT deviceId);
ArduinoDevice* Find(UINT deviceId);
void GetAllRegisteredDevices(std::vector<UINT>& devices);
private:
std::map<UINT, ArduinoDevice*> devices;
UINT nextId;
CriticalSection cs;
};

53

DeviceMap klasa ipak nije samo wrapper oko std::map objekta koji dozvoljava thread-safe pristup.
Metoda Insert, prije umetanja novog key, value para u std::map objekat, pronalazi koji je sljedei ID
ureaja koji je mogue koristiti te na kraju vraa tu vrijednost kao rezultat izvravanja. Time je
postignuto da je DeviceMap objekat taj koji se brine o dodjeljivanju ID-eva novim ureajima koji se
registruju. Takoer, ta metoda kreira sam ArduinoDevice objekat na osnovu proslijeenih i izraunatih
parametara.
UINT DeviceMap::Insert(LPCWSTR serialPortName) {
Lock lock(cs);
std::pair<std::map<UINT, ArduinoDevice*>::iterator, bool> ret =
devices.insert(std::pair<UINT, ArduinoDevice*>(nextId, new
ArduinoDeviceSerial(nextId, serialPortName)));
++nextId;
return nextId - 1;
}

Kao to se moe vidjeti, postizanje meusobnog iskljuivanja metoda DeviceMap objekta je postignuto
koritenjem Lock i CriticalSection klasa na ve opisani nain.
Find metoda objekta vraa pokaziva na ArduinoDevice objekat koji odgovara proslijeenom ID-u.
Ukoliko se ne pronae registrovan ureaj s datim ID-om, vraa se null pointer.
GetAllRegisteredDevices metoda u std::vector ija je referenca proslijeena kao parametar stavlja IDeve svih registrovanih ureaja.
DeviceMap objekat je vlasnik svih ArduinoDevice objekata koje interno kreira i uva pa je njen zadatak
da izvri oslobaanje resursa koji ti objekti zadravaju. Destruktor DeviceMap objekta poziva delete
operator nad svim postojeim objektima. Ovo ponaanje se vidi iz snippeta koda iz destruktora klase
koji se nalazi ispod.
for (std::map<UINT, ArduinoDevice*>::iterator it = devices.begin();
it != devices.end();
++it) {
delete it->second;
}

Metoda Remove takoer eksplicitno oslobaa resurse koje zauzima objekat koji je potrebno
deregistrovati.
void DeviceMap::Remove(UINT deviceId) {
Lock lock(cs);
std::map<UINT, ArduinoDevice*>::const_iterator it = devices.find(deviceId);
if (it != devices.end())
delete it->second;
devices.erase(it);
}

54

3.5.4. Obrada zahtjeva


3.5.4.1. RequestHandler
RequestHandler je abstraktna bazna klasa koja definie metodu koju svaki konkretni objekat, koji e
vriti obradu zahtijeva pristiglih EtfArduinoService servisu, mora implementirati.
class RequestHandler
{
public:
RequestHandler(std::tr1::shared_ptr<PipeCommunicator> pipe);
virtual ~RequestHandler();
enum Status { OK, FAIL, EXIT };
// Pure virtual function
virtual Status run() = 0;
protected:
std::tr1::shared_ptr<PipeCommunicator> pipe;
};

Metoda run je ista virtualna metoda, to i ini klasu abstraktnom.


Definisane su i tri vrijednosti koje ta metoda moe vratiti: OK, FAIL i EXIT, koje, redom, signaliziraju
da je operacija uspjeno izvrena, operacija neuspjeno izvrena te da klijent od kojeg je zahtjev doao
zatvorio komunikaciju.
Konstruktor definie da RequestHandler objekti trebaju primiti pokaziva na PipeCommunicator
objekat pomou kojeg mogu vratiti odgovor klijentu, ukoliko je to potrebno.
Destruktor abstraktne bazne klase je obavezno virtualan.

3.5.4.2. RequestHandlerFactory
RequestHandlerFactory je abstraktna bazna klasa koja definie metode koje svaka konkretna
RequestHandlerFactory klasa mora implementirati.
class RequestHandlerFactory {
public:
typedef int request_t;
RequestHandlerFactory(DeviceMap& devices);
virtual ~RequestHandlerFactory();
// Pure virtual member function
virtual std::auto_ptr<RequestHandler> GetRequestHandler(request_t const* const
buffer) = 0;
protected:
// Request codes. All concrete Factories will use them!
static request_t const GET_BUFFERED_DATA = 1;
static request_t const START_ACQUISITION = 2;
static request_t const STOP_ACQUISITION = 3;
static request_t const GET_SINGLE_VALUE = 4;
static request_t const CHECK_DEVICE_REGISTERED = 5;

55

static request_t const REGISTER_DEVICE = 6;


static request_t const GET_REGISTERED_DEVICE_IDS = 7;
static request_t const SET_ACQUISITION_BUFFER_SIZE = 10;
static request_t const SET_SAMPLE_RATE = 11;
static request_t const SEND_DIGITAL_VALUE = 20;
static request_t const PUT_SINGLE_VALUE = 30;
static request_t const DISCONNECT_CLIENT = 255;
DeviceMap& devices;

};

U javnom dijelu interfejsa klase je deklarisana ista virtualna metoda GetRequestHandler koja vraa
pokaziva na RequestHandler objekat. Koriten je std::auto_ptr pametni pokaziva kako bi se
automatski oslobodili resursi koje zauzima objekat. Time su izbjegnuti potencijalni leakovi resursa
uslijed baenog izuzetka obzirom da e RequestHandler objekat biti uniten pri izlasku std::auto_ptr
objekta iz scopea.
Kao parametar metoda prima pokaziva na buffer koji sadri pristigli zahtjev na osnovu kojeg je
potrebno odluiti koji konkretni RequestHandler objekat treba kreirati.
Protected dio klase sadri kodove svih zahtijeva za koje je mogue kreirati konkretni RequestHandler
objekat.

3.5.4.3. RequestHandlerFactoryImpl klasa


Ova klasa je derivirana iz abstraktne bazne klase RequestHandlerFactory i implementira metodu
GetRequestHandler.
Obzirom da prvi element buffera zahtjeva (ulazni parametar metode) uvijek sadri kod zahtjeva,
metoda u switch-case bloku dekodira zahtjev i kreira odgovarajui konkretni objekat kojem se
proslijeuju svi potrebni parametri.

3.5.4.4. Konkretne RequestHandler klase


Za svaki od zahtijeva iji kod je definisan u RequestHandlerFactory klasi kreiran je jedan konkretni
RequestHandler:

GetBufferedDataRequestHandler zahtjev za preuzimanjem zadnjeg buffera podataka


prikupljenih od Arduino ureaja u procesu analogne akvizicije;

StartAcquisitionRequestHandler zahtjev za poetkom akvizicije analognog signala nekog


ureaja;
56

StopAcquisitionRequestHandler zahtjev za obustavljanjem analogne akvizicije;

GetSingleValueRequestHandler zahtjev za preuzimanjem jedne vrijednosti analognog signala


Arduino ureaja (jedan sample);

CheckDeviceRegisteredRequestHandler zahtjev za provjeru da li postoji registrovan ureaj s


ID-om koji je dat u zahtjevu;

RegisterDeviceRequestHandler zahtjev za registracijom novog ureaja na serijskom portu ije


ime je dato u zahtjevu;

GetRegisteredDevicesRequestHandler zahtjev kojim klijent trai listu ID-eva svih


registrovanih ureaja;

SetAcquisitionBufferSizeRequestHandler zahtjev kojim se postavlja veliina buffera kojeg


servis treba da vraa klijentu za vrijeme procesa akvizicije analognog signala;

SetSampleRateRequestHandler zahtjev za postavljanjem nove frekvencije sampliranja


signala;

SendDigitalValueRequestHandler zahtjev za slanjem digitalne vrijednosti na izlaz Arduino


ureaja;

PutSingleValueRequestHandler zahtjev za slanjem analogne vrijednosti na izlaz nekog


Arduino ureaja;

DisconnectRequestHandler zahtjev kojim klijent oznaava servisu da od tog momenta nee


biti vie zahtjeva, tj. da prekida konekciju.

Implementacija run metode svake od klasa poziva potrebne metode objekata ArduinoDevice,
DeviceMap i PipeCommunicator kako bi uspjeno odgovorila na sve zahtjeve.
Za klasu StartAcquisitionRequestHandler treba naglasiti da pri pozivu run metode kreira novu nit u
kojoj e se izvravati akvizicija analognog signala s ureaja koja se pokree pozivom metode
StartAcquisition ArduinoDevice objekta. Inae, nit koja obrauje zahtjeve tog podsistema bi bila
blokirana
i
ne
bi
bilo
mogue
obraditi
zahtjev
za
prestankom
akvizicije
(StopAcquisitionRequestHandler). Funkcija koju nit izvrava kao parametar prima pokaziva na
ArduinoDevice objekat ija akvizicija treba da zapone.
DWORD WINAPI StartAcquisitionRequestHandler::AnalogAcquisitionProc(LPVOID lpvParam) {
ArduinoDevice& device = *(ArduinoDevice*)lpvParam;
device.StartAcquisition();
return 0;
}

57

3.5.5. Glavni program EtfArduinoService servisa


Entry point, tj. main funkcija EtfArduinoService procesa u beskonanoj petlji kreira instance named
pipea preko kojeg se klijenti mogu spojiti na servis. Kada se neki klijent uspjeno konektuje, kreira se
nova nit koja e sluiti za rjeavanje svih zahtijeva koji budu pristizali od tog klijenta. Ime named
pipea sadri GUID generisan iz Visual Studio okruenja kako bi se osiguralo jedinstveno ime na
sistemu.
for (;;)
{
PipeCommunicator* pipe =
new PipeCommunicator(_T(
"\\\\.\\pipe\\etfarduino-{522590F9-C51E-4711-B6E2-9CECC7C3FD91}"));
// Wait for the client to connect
if (pipe->Connect())
{
// Create a thread for this client.
HANDLE hThread = CreateThread(
NULL,
// no security attribute
0,
// default stack size
InstanceThread,
// thread proc
(LPVOID) pipe,
// thread parameter
0,
// not suspended
NULL);
// no thread ID
if (hThread == NULL) {
// Thread creation failed, pipe ownership still in this thread!
delete pipe;
} else {
CloseHandle(hThread);
}
} else {
// The client could not connect, so close the pipe.
delete pipe;
}
}

Funkciji novokreirane niti se proslijeuje pokaziva na PipeCommunicator objekat koji, kako je ve


objanjeno, slui za komunikaciju preko instance named pipe objekta. Vlasnitvo nad objektom prelazi
toj funkciji, a time i odgovornost da se oslobode resursi koje on zauzima.
Ukoliko kreiranje threada ne uspije ili se klijent neuspjeno konektuje, resursi se oslobaaju prije
sljedee iteracije petlje.
Nit koja usluuje klijenta eka poruke koje pristiu na instancu named pipe objekta, koristei
PipeCommunicator iji pokaziva je proslijeen pri kreiranju niti. Svaki zahtjev se obrauje tako to se
proslijedi GetRequestHandler metodi RequestHandlerFactory objekta. Povratna vrijednost, pokaziva
na RequestHandler objekat, se preuzima i poziva se njegova run metoda.

58

Opisani postupak se moe vidjeti i kroz snippet koda koji se nalazi ispod:
std::tr1::shared_ptr<PipeCommunicator> pipe((PipeCommunicator*) lpvParam);
std::vector<PipeCommunicator::message_t> request(PipeCommunicator::bufferSize);
PipeCommunicator::message_t* pchRequest = &request[0];
std::auto_ptr<RequestHandlerFactory> factory(
new RequestHandlerFactoryImpl(
registeredDevices,
pipe));
for (;;) {
DWORD cbBytesRead = 0;
bool success =
pipe->RetrieveMessage(
pchRequest,
PipeCommunicator::bufferSize * sizeof(PipeCommunicator::message_t),
&cbBytesRead);
if (!success || cbBytesRead == 0) {
if (GetLastError() == ERROR_BROKEN_PIPE) {
break;
}
continue;
}

std::auto_ptr<RequestHandler> handler(
factory->GetRequestHandler(pchRequest));
if (handler.get() != 0) {
RequestHandler::Status const status =
handler->run();
if (status == RequestHandler::Status::EXIT) break;
}

Iz ovoga se vidi mo koritenog factory design patterna za implementaciju novog zahtjeva potrebno
je samo implementirati novu konkretnu klasu deriviranu iz RequestHandler abstraktne bazne klase te
dodati odgovarajui dio koda u konkretnoj RequestHandlerFactoryImpl klasi kojim se kreiraju ti
objekti. U kodu niti koja usluuje klijenta nita nije potrebno modifikovati.

59

3.6. Adaptor implementacija


3.6.1. COM interfejs
Kao to je spomenuto u poglavlju Data Acquisition Toolbox, svaki adaptor mora biti implementiran
tako da se komunikacija sa Data Acquisition Engineom odvija preko COM (Component Object Model)
interfejsa.
COM je Microsoftova tehnologija koja omoguava kreiranje softverskih komponenti koje je mogue
koristiti neovisno od platforme na kojoj je uraena implementacije te komponente. COM definie
standard koji komponentama omoguava da komuniciraju sa drugim objektima. Jedino ogranienje
programskog jezika koji moe biti koriten za implementaciju COM komponenti je da je jezik u stanju
pristupati memorijskim lokacijama na osnovu pokazivaa te da moe vriti pozive funkcija na osnovu
pokazivaa na funkcije.
COM je zasnovan na principima objektno-orijentisanog programiranja. Svaki COM objekat
(komponenta) implementira odreene interfejse. Interfejsi definiu koje operacije moraju biti podrane
od strane objekata koji ih implementiraju. Operacije nekog interfejsa sa nazivaju metodama interfejsa.
Jedini nain da se pristupi operacijama nekog COM objekta je preko pokazivaa na neki od njegovih
interfejsa. COM objekti se definiu pomou COM klasa.
Pokaziva na COM interfejs nekog objekta predstavlja pokaziva na niz koji sadri pokazivae na
funkcije. Pozivom tih funkcija se vri izvravanje metode objekta. Kompajleri za jezike C i C++
omoguavaju pozivanje tih funkcija po imenu, bez potrebe da se vri eksplicitno indeksiranje
pomenutog niza pokazivaa, pa tako nije potrebno poznavanje tanog redoslijeda metoda u nizu.
COM interfejsi i COM klase se identifikuju pomou 128-bitnih jedinstvenih globalnih identifikatora
(GUID) koji se nazivaju IID i CLSID. COM sistem na odreenom raunaru je obavezan uvati
informacije o tome koje DLL ili EXE datoteke sadre kod sa implementacijom neke COM klase te koje
sve interfejse implementira odreena klasa. Takoer, za svaku komponentu treba uvati i njeno
tekstualno ime, tj. identifikaciju, pored GUID-a. Na Windows operativnom sistemu ove informacije se
uvaju u registryu.
Pri prvom uitavanju DLL-a ili pokretanju EXE-a koji implementira neke COM komponente, potrebno
je izvriti registraciju te komponente na sistemu koristei odgovarajue API funkcije.
Svaki objekat je obavezan da implementira interfejs IUnknown. Bitna metoda koju taj interfejs
zahtijeva je QueryInterface preko koje je mogue dobiti pokaziva na drugi interfejs istog objekta.
Kako bi otkrivanje svih COM objekata koji podravaju neku funkcionalnost bilo olakano, postoje
COM kategorije. COM kategorije su identifikovane GUID-om koji se naziva CATID. Svaki COM
objekat moe pripadati jednoj ili vie kategorija.

60

Klijenti COM objekata, koristei predefinisane WinAPI funkcije, od sistema mogu zahtijevati
pokaziva na neki interfejs odreenog objekta, poznavajui odgovarajue CLSID i IID vrijednosti.
Mogue je i dobiti listu svih objekata (njihovih CLSID-eva i tekstualnih imena) na sistemu koji
pripadaju nekoj kategoriji, ukoliko je poznat njen CATID, te je na taj nain klijent u stanju odabrati
koju implementaciju eli koristiti.
COM sadri jo mnoge definicije i standarde, ali za svrhu ovog rada, nije potrebno ulaziti u dodatne
detalje.
Kako bi se definisanje COM interfejsa i klasa, zajedno s interfejsima koje e podravati, olakalo,
Microsoft je kreirao Microsoft Interface Description Language (MIDL). Opise COM komponenti iz
MIDL jezika poseban kompajler prevodi u C kod koji se zatim moe prevesti u binarni izvrni kod.
Visual C++ takoer olakava kreiranje DLL-a koji sadri COM komponentu, to podrazumijeva
olakanu izmjenu vrijednosti u registryu koristei za to predviene klase i predefinisane funkcije koje
sistem moe pozvati kako bi pristupio pokazivaima na interfejse objekata definisanih u DLL-u.

3.6.2. Adaptor klasa


Kao to je reeno u objanjenju konstruktorsih funkcija Data Acquisition Toolboxa, prilikom kreiranja
nekog MATLAB Data Acquisition Toolbox objekta (analoginput, analogoutput, digitalio) Data
Acquisition Engine prima ime adaptora kojeg treba koristiti za komunikaciju sa odgovarajuim
hardverom. Tada, engine od sistema zahtijeva listu svih komponenti koji pripadaju kategoriji s CATIDem: {6FE55F7B-AF1A-11D3-A536-00902757EA8D}.
Na osnovu tekstualnih imena vraenih komponenti, pronalazi onu koja odgovara imenu datom u pozivu
konstruktorske funkcije. Na osnovu CLSID-a pridruenog tom imenu, pomou odgovarajuih funkcija
kreira COM objekat i pristupa mu preko pokazivaa na ImwAdaptor interfejs.
ImwAdaptor je interfejs koji je definisao i registrovao sam Data Acquisition Engine prilikom svoje
instalacije. Ovaj interfejs specificira koje sve metode adaptori moraju implementirati.
U ovom projektu, klasa Cetfarduinoadapt je ta koja implementira potrebne metode.
MIDL definicija klase je:
[

uuid(013D390C-EA84-4d05-907F-15B5903A2952),
helpstring("etfarduinoadapt Class")

]
coclass etfarduinoadapt
{
[default] interface ImwAdaptor;
};

61

Kako bi implementacija adaptora u C ili C++-u bila olakana, MathWorks uz Data Acquisition Toolbox
obezbjeuje i daqmex.h header datoteku koja sadri definicije svih COM interfejsa vezanih za tooblox
kao C struktura ili C++ abstraktnih baznih klasa. Ova datoteka je dobivena kompajliranjem definicije
datih interfejsa pomou MIDL jezika.
Tako pri implementaciji Cetfarduinoadapt klase treba navesti da je izvedena iz ImwAdaptor klase koja
je definisana upravo u daqmex.h.
Tri metode koje definie ovaj interfejs su: OpenDevice, AdaptorInfo i TranslateError.
Metoda AdaptorInfo vraa informacije vezane za adaptor i ureaje s kojima moe komunicirati koje
MATLAB preuzima pri pozivu funkcije daqhwinfo za taj adaptor. Dakle, potrebno je vratiti ime
adaptora, ime DLL-a adaptora, niz registrovanih ureaja s kojima moe komunicirati, tj. koji mogu biti
koriteni u procesu akvizicije te podsisteme kojima je mogue pristupiti na tim ureajima.
Metoda prima pokaziva na interfejs IPropContainer te pozivanjem njegove metode put_MemberValue
definie pomenute vrijednosti. Primjer koda se vidi ispod gdje se definie naziv adaptora.
HRESULT Cetfarduinoadapt::AdaptorInfo(IPropContainer * Container)
{
HRESULT hRes = Container->put_MemberValue(L"adaptorname", variant_t(ConstructorName));
if (!(SUCCEEDED(hRes))) return hRes;
// ... nastavak metode za ostale informacije ...
}

OpenDevice metoda kreira objekat vezan uz odreeni podsistem ureaja: analogni ulaz, analogni izlaz
ili digitalni ulaz/izlaz i vraa pokaziva na taj objekat. Podsistem koji treba kreirati se identifikuje na
osnovu parametra koji prima funkcija koji je IID jednog od interfejsa ImwInput, ImwOutput, ImwDIO
koji redom odgovaraju navedenim podsistemima. Ove interfejse takoer definie Data Acquisition
Toolbox, a adaptor DLL klase CetfarduinoAin, CetfarduinoAout, CetfarduinoDio implementiraju ove
interfejse.
Dio koda koji kreira podsistem za analogni ulaz i vraa pokaziva na njega je:
bool Success = false;
CComPtr<ImwDevice> pDevice;
if (InlineIsEqualGUID(__uuidof(ImwInput),riid))
{
CComObject<CetfarduinoAin>* Ain;
CComObject<CetfarduinoAin>::CreateInstance(&Ain);
RETURN_HRESULT(Ain->Open((IDaqEngine*)pIEngine, id));
pDevice = Ain;
Success = true;
}

62

3.6.3. CetfarduinoAin klasa


Ve pomenuta klasa CetfarduinoAin implementira metode interfejsa ImwInput koje su potrebne kako bi
engine pristupao podsistemu za analogni ulaz ureaja za koje je adaptor DLL namijenjen.
Kako je i ova klasa COM komponenta, njena MIDL definicija je:
[

uuid(DF13DDD3-AB25-4470-A5CA-0CFCE6A4E1C5),
helpstring("etfarduinoAin Class")

]
coclass etfarduinoAin
{
[default] interface IetfarduinoAin;
interface ImwDevice;
interface ImwInput;
};

Moe se vidjeti da klasa implementira jo jedan interfejs, ImwDevice. To je Data Acquisition Toolbox
interfejs koji moraju implementirati svi podsistemi, a definie metode koje nisu specifine ni za jedan
od podsistema.
Slijedi opis svih metoda implementiranih u klasi.
Open metode ImwDevice interfejsa. Ovu metodu poziva metoda OpenDevice, adaptor objekta kada
je potrebno kreirati vezu sa analognim podsistemom nekog ureaja.
Ova metoda je zaduena da izvri provjeru postojanja ureaja koji se pokuava koristiti, a zatim i
inicijalizaciju komunikacije. Jo je bitno da postavi vrijednosti svojstava podsistema, kao to su
podrazumijevane vrijednosti za SampleRate, SamplesPerTrigger i sl.
Metoda prima dva parametra; prvi je pokaziva na IDaqEngine interfejs pomou ijih metoda ovaj
objekat moe komunicirati sa Data Acquisition Engineom. Drugi parametar je ID ureaja s kojim se
eli zapoeti komunikacija.
Za provjeru postojanja ureaja, implementacija Open metode koristi metodu CheckDeviceRegistered
klase EtfArduinoService koja je ve opisana. CetfarduinoAin objekti imaju, kao privatni lan, objekat
ovog tipa koji je koriten za vrijeme cijelog ivota objekta.
bool registered =
service.CheckDeviceRegistered(DeviceId, portName);
if (!registered) {
char tempMessage[255];
sprintf(tempMessage, "etfarduino: Device %d not found.", DeviceId);
return CComCoClass<ImwDevice>::Error(CComBSTR(tempMessage));
}

63

Statika funkcija Error interfejsa ImwDevice omoguava podsistemima adaptora da vrate odreene
poruke greke koje se pojavljuju u MATLAB okruenju.

Slika 10: Poruka greke ukoliko nije registrovan ureaj s


traenim ID-em
Osobine koje se zatim postavljaju u funkciji su:

ID ureaja;
Minimalna i maksimalna vrijednost za SampleRate pokuaj postavljanja vrijednosti izvan
ovih granica automatski korisniku javlja greku;
Ime ureaja uvijek u formatu etfarduino (Device x), gdje je x ID ureaja;
Granice u kojima se nalazi ulaz postavlja se na 0 do 5V, obzirom da je to ogranienje Arduino
ureaja;
Tip podataka koji e biti koriten za vrijednosti koje ureaj vraa u ovom sluaju unsigned
short 2 bajtna nepredznaena vrijednost koja odgovara Arduino tipu unsigned int;
Broj bita rezolucije engine na osnovu ovog podatka i granica ulaza, automatski vri
konverziju primljenih digitalnih vrijednosti od ureaja u analogne;
Postavljaju se podrazumijevane vrijednosti za SampleRate i SamplesPerTrigger ove
vrijednosti, prema uputama Data Acquisition Enginea, se trebaju postaviti tako da bi
podrazumijevano vrijeme akvizicije dobiveno na osnovu ove dvije osobine bilo 1 sekunda.
Podrazumijevane vrijednosti se postavljaju na 1000.

Na kraju, metoda signalizira engineu da objekat eli prima obavijesti kada se vrijednost neke od
osobina promijene. Ovo je potrebno kako bi se ureaj mogao obavijestiti o nekim izmjenama koje utiu
na nain njegovog rada ili kako bi sam adaptor objekat izvrio izmjene nekih privatnih svojstava na
osnovu novih vrijednosti.
Metoda ovu signalizaciju obavlja koristei predefinisani ATTACH_PROP makro:
ATTACH_PROP(SamplesPerTrigger);

Ovaj makro zahtijeva da je mogu pristup objektu tipa TRemoteProp s istim nazivom kao proslijeeni
naziv osobine uz prefiks slova 'p'. Pomou ovog objekta bit e pristupano vrijednostima osobine.
Objekat je definisan kao privatni atribut CetfarduinoAin objekata kako bi pristup bio mogu iz bilo koje
metode:
TRemoteProp<double> pSamplesPerTrigger;

SetDaqHwInfo je pomona metoda za definisanje vrijednosti osobina podsistema. Poziva je samo


Open metoda.

64

ChildChange metoda se poziva kada


SetProperty je metoda koju engine poziva kada se promijeni neka od osobina za koju je zahtijevano da
se primaju obavijesti o izmjeni pri pozivu Open metode.
Prima dva parametra od kojih je prvi long tipa i slui da se identifikuje koja osobina je izmijenjena, a
drugi predstavlja pokaziva na novu vrijednosti.
Pomou makroa USER_VAL mogue je dobiti long identifikator TRemoteProp objekata i na osnovu
toga utvrditi za koju osobinu je pozvana metoda.
U implementaciji, samo dvije osobine se razmatraju: SampleRate i ClockSource.
Pri izmjeni SampleRatea potrebno je izvriti provjeru da li je nova vrijednost validna. Obzirom da
period uzorkovanja na Arduino ureaju mora biti djeljiv s 4 mikrosekunde, onda postoje i ogranienja
na validne SampleRateove. Standardno ponaanje adaptora za akvizicione ureaje je da umjesto traene
vrijednosti, postave SampleRate na najbliu moguu. Izmjena vrijednosti od strane SetProperty metode
izaziva automatsku poruku upozorenja korisnicima u MATLAB okruenju.

0.5;

if (User == USER_VAL(pSampleRate)) {
double newSampleRate = *val;
if (pClockSource == CLOCKSOURCE_SOFTWARE) {
return CswClockedDevice::SetProperty(User, NewValue);
}
// Calculate a sample rate which is compatible with the board
int const factor = (static_cast<double>(tickFrequency) / newSampleRate) +

int const period = 4 * factor;


newSampleRate = (1. / period) * 1000 * 1000; // us -> Hz
if (newSampleRate > 12500) {
// Issue warning of max sample rate
_engine->WarningMessage(CComBSTR("etfarduino: Sample rates higher than
12,500 are not supported by etfarduino."));
newSampleRate = 12500;
}
pSampleRate = newSampleRate;
*val = pSampleRate;
}

ClockSource osobina specificira da li e se koristiti interni sat ureaja za uzorkovanje (Hardware) ili e
biti koriten tzv. softverski sat (Software). Softverski sat radi tako to Data Acquisition Engine, nakon
to protekne odgovarajui period, automatski poziva metodu GetSingleValue ovog objekta. Na ovaj
nain korisnik je ogranien na maksimalnu frekvenciju uzorkovanja 500 Hz.
Pri izmjeni ClockSource osobine potrebno je izvriti izmjenu granica za SampleRate, obzirom na
upravo pomenuto ogranienje. Takoer, potrebno je promijeniti podrazumijevane vrijednosti za
SampleRate i SamplesPerTrigger i trenutne vrijednosti ne smije se dozvoliti da se objekat nae u
nekonsistentnom stanju nakon izmjene ClockSourcea.

65

if (User == USER_VAL(pClockSource)) {
if ((long)(*val) == CLOCKSOURCE_SOFTWARE) {
// Software clocking chosen
// First, change the SampleRate to SW clocked defaults
double softwareClockedDefault = 100;
pSampleRate.SetDefaultValue(CComVariant(softwareClockedDefault));
pSampleRate = softwareClockedDefault;
pSampleRate.SetRange(
CComVariant((double)MIN_SW_SAMPLERATE),
CComVariant((double)MAX_SW_SAMPLERATE));
EnableSwClocking();
pSamplesPerTrigger->put_DefaultValue(
CComVariant(softwareClockedDefault));
pSamplesPerTrigger = softwareClockedDefault;
} else {
// Na isti nain se postavljaju vrijednosti za Hardware Clocking
}
}

ChildChange je metoda koju engine poziva pri dodavanju, izmjeni ili brisanju kanala na analoginput
objektu.
Prima dva parametra; prvi je bitmaska koja daje tip promjene koja se vri, a drugi pokaziva na
strukturu NESTABLEPROP koja opisuje kanal koji se mijenja.
Trenutno je podran samo jedan kanal, pa ukoliko se pokuava dodati dodatni kanal nakon prvog, vraa
se greka.
if ((typeofchange & CHILDCHANGE_REASON_MASK) == ADD_CHILD) {
if (_nChannels == 1) {
return Error(CComBSTR("etfarduino: Nije podrzano vise od jednog kanala
po AnalogInput objektu"));
}
}

SetChannelProperty je metoda koja se poziva kada engine eli izmijeniti neku vrijednost osobine
kanala objekta. Osobina koja je bitna za implementaciju CetfarduinoAin klase je opseg ulaznih
vrijednosti. Ukoliko se pokua izmijeniti, u ovoj metodi se pokua nai podrani opseg koji najbolje
odgovara traenom. Obzirom da je jedini mogu opseg 0-5V, to e uvijek biti rezultat pokuaja izmjene
opsega.
GetSingleValue metoda, kao to je ve pomenuto, slui da engineu vrati trenutnu vrijednost s
analognog ulaza i koristi se samo kada je ClockSource postavljen na Software.
Prima dva parametra; prvi je redni broj kanala s kojeg se eli preuzeti vrijednost, a drugi pokaziva na
mjesto u memoriji na koje treba staviti oitanu vrijednost.
Implementacija se zasniva na pozivanju metode GetSingleValue nad EtfArduinoService objektom koji
je privatni lan ove klase.
66

HRESULT CetfarduinoAin::GetSingleValue(int chan, RawDataType *value)


{
*value = (RawDataType) service.GetSingleValue(DeviceId);
return S_OK;
}

Start se poziva kada korisnik, koristei MATLAB funkciju start, engineu poalje signal da eli
zapoeti s akvizicijom. Spaavanje vrijednosti u engineu poinje tek kada se pozove trigger funkcija u
zavisnosti od postavke osobine TriggerType, pa zbog toga je jedini zadatak ove metode da izvri
inicijalizaciju svih potrebnih pretpostavki za obavljanje zadatka akvizicije.
Implementacija najprije provjerava da li je ClockSource postavljen na Software. U tom sluaju, posao
se preputa ve implementiranoj klasi koja dolazi uz Data Acquisition Engine.
Inae, koristei metode EtfArduinoService objekta, servisu za komunikaciju s ureajem se signaliziraju
vrijednosti SampleRate i veliine buffera koja e biti koritena za vrijeme akvizicije.
Adaptor je duan engineu vraati podatke u bufferima koje moe dobiti od njega i ija veliina je fiksna
za vrijeme jednog zadatka akvizicije. Kada se prikupi podaci za jedan cijeli buffer, potrebno je poslati
signal engineu da je buffer napunjen. Iz tog razloga je potrebno servisu dati do znanja koja je veliina
buffera, tj. koliko podataka je potrebno vraati adaptoru.
Trigger metoda zapoinje prikupljanje podataka s ureaja i njihovo slanje engineu. Ova metoda
obavezno mora biti asinhrona jer engine mora nastaviti sa radom nakon njenog poziva. Iz tog razloga,
ono to metoda radi je to da alje signal servisu za komunikaciju s ureajem da treba poeti te se
registruje kao sistem zainteresovan za primanje obavjetenja servisa preko ve objaenjenog
mehanizma meuprocesne komunikacije koritenjem Window poruka.
Metoda se registruje pozivom metode AddDevice nad singleton objektom tipa MessageWindow kojoj
proslijedi ID ureaja s kojim eli komunicirati, tip podsistema (analogni ulaz) i pokaziva na sebe.
Zatim, pomou EtfArduinoService objekta zapoinje prikupljanje podataka na samom ureaju.
msgWindow.AddDevice(DeviceId, subsystemId, this);
if (!service.StartAcquisition(DeviceId)) {
return CComCoClass<ImwDevice>::Error(_T("etfarduino: Problem starting
acquisition."));
}

ReceivedMessage je metoda koja e biti pozvana kada MessageWindow primi poruku od servisa za
komunikaciju s ureajem koja je namijenjena podsistemu s kojim je objekat povezan.
Zadatak metode je preuzeti podatke koji su spremni u servisu i poslati ih engineu u odgovarajuoj
buffer strukturi. Buffer strukturu se preuzima metodom GetBuffer IDaqEngine interfejsa, a, nakon to je
napunjen odgovarajuim podacima, vraa engineu pozivom metode PutBuffer.
Preuzimanje podataka od servisa za komunikaciju s ureajem je postignuto jednostavnim pozivom
metode GetBufferedData nad EtfArduinoService objektom.
67

Buffer struktura postavljanjem bita BUFFER_IS_LAST u Flags polju daje adaptoru do znanja da proces
akvizicije treba biti zavren. StartPoint polje predstavlja ukupni redni broj prvog uzorka u bufferu od
poetka akvizicije.
BUFFER_ST* pBuffer = 0;
_engine->GetBuffer(0, &pBuffer);
service.GetBufferedData(DeviceId, pBuffer->ptr, pointsPerBuffer);
pBuffer->StartPoint = m_samplesThisRun * _nChannels;
pBuffer->ValidPoints = samplesToFetch * _nChannels;
m_samplesThisRun += pBuffer->ValidPoints / _nChannels;
if ((pBuffer->Flags & BUFFER_IS_LAST) || pBuffer->ValidPoints < pointsPerBuffer) {
mustStop = true;
}
// Konacno vratiti buffer engineu
_engine->PutBuffer(pBuffer);

3.6.4. CetfarduinoAout klasa


Klasa CetfarduinoAout implementira metode interfejsa ImwOutput koje su potrebne kako bi engine
pristupao podsistemu za analogni izlaz ureaja za koje je adaptor DLL namijenjen. Implementira i
ImwDevice interfejs koji je obavezan za sve podsisteme.
Kako je i ova klasa COM komponenta, njena MIDL definicija je:
[

uuid(F5D02ED5-BAA4-49f1-AE9B-F63F0E82A1C5),
helpstring("etfarduinoAout Class")

]
coclass etfarduinoAout
{
[default] interface IetfarduinoAout;
interface ImwDevice;
interface ImwOutput;
};

Metode Open, SetDaqHwInfo, SetChannelProperty i ChildChange imaju isto znaenje kao i u sluaju
analoginput objekta, pa ovdje nee biti ponovo objanjavani.
Obzirom na ogranienja Arduino ureaja u vezi sa analognim izlazom, ova komponenta podrava samo
softverski sat za upravljanje periodom slanja vrijednosti na izlaz (ClockSource osobina moe biti samo
Software).
Metoda PutSingleValue slui da omogui engineu da na izlaz ureaja poalje jednu analognu
vrijednost. Engine je koristi pri implementaciji izlaz koristei softverski sat.
Prima dva parametra; prvi je kanal na koji treba poslati vrijednost, a drugi upravo vrijednost koju treba
postaviti. Bitno je da je primljena vrijednost ve pretvorena iz analogne u digitalnu vrijednost na
osnovu poznatih podataka o opsegu izlaznog kanala i broju bita rezolucije digitalno-analognog
konvertora ureaja.
68

Implementacija jednostavnim pozivom PutSingleValue metode EtfArduinoService objekta izvrava


potrebnu operaciju.
HRESULT CetfarduinoAout::PutSingleValue(int chan, RawDataType value)
{
if (!service.PutSingleValue(DeviceId, chan, value)) {
return CcomCoClass<ImwDevice>::Error(CComBSTR("etfarduino: Analog output failed."));
}
return S_OK;
}

3.6.5. CetfarduinoDio klasa


Klasa CetfArduinoDio implementira metode interfejsa ImwDio koje su potrebne kako bi engine
pristupao podsistemu za digitalni ulaz/izlaz ureaja za koje je adaptor DLL namijenjen. Implementira i
ImwDevice interfejs koji je obavezan za sve podsisteme.
Kako je i ova klasa COM komponenta, njena MIDL definicija je:
[

uuid(5EF1299B-0EA2-4039-AB34-012FD8F978F2),
helpstring("etfarduinoDio Class")

]
coclass etfarduinoDio
{
[default] interface IetfarduinoDio;
interface ImwDevice;
interface ImwDIO;
};

Metode Open i SetDaqHwInfo opet imaju istu svrhu kao i u prethodna dva sluaja.
Metode specifine za ImwDio interfejs su ReadValues, WriteValues, SetPortDirection.
Engine koristi WriteValues da na digitalni izlaz ureaja s kojim je povezan objekat poalje potrebne
vrijednosti. Implementacija se svodi na poziv metode SendDigitalValue EtfArduinoService objekta.
for (int i = 0; i < NumberOfPorts; ++i) {
if (!service.SendDigitalValue(DeviceId, PortList[i], Data[i])) {
return CComCoClass<ImwDevice>::Error(CComBSTR("etfarduino: Digital write failed."));
}
}

Pomou SetPortDirection se implementira mogunost izmjene smjera odreenog porta, tj. da li e biti
koriten za ulaz ili izlaz. U ovom projektu nije predviena mogunost izmjene smjera porta, pa metoda
samo vraa greku.

69

HRESULT CetfarduinoDio::SetPortDirection(LONG Port, ULONG DirectionValues)


{
// For now, there is no possiblity to change any port direction
return CcomCoClass<ImwDevice>::Error(
CComBSTR("etfarduino: Port direction change is not supported."));
}

70

3.7. Arduino Firmware


Arduino firmware program, koji omoguava koritenje Arduino ureaja kao akvizicionog modula u
saradnji sa ostalim komponentama sistema razvijenog kroz ovaj projekat, ima zadatak da na osnovu
primljenih komandi preko serijskog komunikacionog porta izvrava odreene zahtjeve.

3.7.1. Analogni ulaz


Za podrku analognog ulaza, Arduino implementira podrku za sljedee komande:

Poetak akvizicije komanda iji je kod bajt 49 koji odgovara ASCII vrijednosti znaka '1'.
Zapoinje prikupljanje podataka s analogno-digitalnog konvertora, frekvencijom koja je zadata,
i njihovo slanje raunaru.

Kraj akvizicije komanda s kodom ija vrijednost odgovara ASCII vrijednosti znaka '2'.
Zaustavlja proces akvizicije podataka i njihovog slanja raunaru.

Postavljanje perioda akvizicije komanda s kodom ija vrijednost odogovara ASCII


vrijednosti znaka '3'. Osim koda, ita jo 4 bajta s serijskog ulaza koji predstavljaju vrijednost
perioda akvizicije u mikrosekundama. Period treba da bude broj djeljiv sa 4 inae e se
oekivani period razlikovati od onog kojeg e Arduino koristiti.

Preuzimanje jedne analogne vrijednosti komanda s kodom ija vrijednost odogovara ASCII
vrijednosti znaka '4'. Komanda vri oitavanje trenutne vrijednosti s analognog kanala i tu
vrijednost odmah alje nazad preko serijskog porta.

Kako bi se implementirao proces akvizicije analognog signala s tano specificiranim periodom,


koriten je Timer1 ATmega328 procesora. Faktor skaliranja je postavljen na 64 to znai da je period s
kojim se inkrementuje broja 4s. To znai da Arduino podrava samo one postavke perioda koje su
djeljive sa 4. Dio koda koji vri inicijalizaciju timera se moe vidjeti u sljedeem isjeku.
cli();
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = (period >> 2) - 1;
TCCR1B |= (1 << WGM12);
// CTC mode
TCCR1B |= (1 << CS11) | (1 << CS10);
// 64 prescaler (str. 135)
TIMSK1 &= ~(1 << OCIE1A); // disable timer interrupt
sei();

71

Kada je potrebno izmijeniti period akvizicije, Arduino postavlja vrijednost registra OCR1A na
vrijednost nakon koje treba da se izvri rutina za obradu prekida tog timera. Ova vrijednost se rauna
po sljedeoj formuli.

Gdje je T period akvizicije u mikrosekundama, a Ttimer period inkrementovanja timera u


mikrosekundama. Od kolinika te dvije vrijednosti je potrebno oduzeti 1, obzirom da se prekid javlja
kada broja pree postavljenu vrijednost.
Dio koda koji to radi je:
OCR1A = (period >> 2) - 1;

Obrada komandi za poetak i kraj akvizicije jednostavno aktiviraju, tj. deaktiviraju pokretanje rutine za
obradu ovog prekida.
case '1': {
TCNT1 = 0;
// reset the counter
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
return;
}
case '2': {
TIMSK1 &= ~(1 << OCIE1A); // disable timer compare interrupt
return;
}

U samoj rutini, Arduino vri sampliranje ADC-a, nakon ega se postavlja vrijednost boolean indikatora
da postoji vrijednost koja se treba proslijediti raunaru. Rutina ne vri slanje te vrijednosti obzirom da
svaka rutina za obradu prekida mora biti to kraa kako ne bi onemoguila ispravno rjeavanje drugih
prekida koji se jave za to vrijeme.
Potreba za to kraim vremenom izvravanja ove rutine je razlog koritenja direktne manipulacije
registrima za akviziciju vrijednosti signala, umjesto Arduino funkcije analogRead koja ima puno vei
overhead.
Kod za inicijalizaciju analogno-digitalnog konvertora gdje se specificira da treba vriti oitavanje
analognog ulaza A0, Arduino ureaja, koristiti referentni napon od 5V te faktor skaliranja 16 se vidi
ispod:

72

ADMUX = 0;
ADMUX |= (1 << REFS0);
// Enable the ADC
ADCSRA |= (1 << ADEN);
// Set the prescale factor to 16 (100 == ADPS bits)
ADCSRA |= (1 << ADPS2);
ADCSRA &= ~(1 << ADPS1);
ADCSRA &= ~(1 << ADPS0);

Ispod se moe vidjeti snippet koda sa rutinom za obradu prekida Timera1.


ISR(TIMER1_COMPA_vect)
// timer compare interrupt service routine
{
// Do a conversion
ADCSRA |= (1 << ADSC);
// Start conversion
// This blocks for 13us 208 cycles wasted
while (bit_is_set(ADCSRA, ADSC)) ;
analogSample.buffer[0] = ADCL;
analogSample.buffer[1] = ADCH;
toSend = true;
}

Zadnja komanda koju je potrebno podrati je vraanje jedne analogne vrijednosti ija implementacija
koristi isti nain preuzimanja analogne vrijednosti kao to je sluaj u upravo navedenoj rutini za obradu
prekida te tu vrijednost alje na serijski izlaz.

3.7.2. Analogni izlaz


Za funkcionalnost analognog izlaza, potrebno je implementirati samo jednu komandu, s kodom koji
odgovara vrijednosti ASCII znaka '6', koja dobiveni bajt, pomou funkcije analogWrite proslijeuje na
analogni izlaz.
case '6': {
// the read method does not block, so have to manually wait
// for all the parameters to arrive
while (Serial.available() < 2) ;
// Ignoring the channel parameter for now
Serial.read();
unsigned char const val = Serial.read();
// Send the second parameter directly to the digitalWrite function
analogWrite(analogOutPin, val);
return;
}

3.7.3. Digitalni izlaz


Komanda za podrku digitalnog izlaza ima kod koji odgovara ASCII vrijednosti znaka '5' i njegova
implementacija se razlikuje od upravo navedene implementacije analognog izlaza samo u tome da se
koristi funkcija digitalWrite umjesto analogWrite:

73

case '5': {
// the read method does not block, so have to manually wait
// for all the parameters to arrive
while (Serial.available() < 2) ;
// Ignoring the line parameter for now
Serial.read();
unsigned char const val = Serial.read();
// Send the second parameter directly to the digitalWrite function
digitalWrite(digitalOutPin, val);
return;
}

3.7.4. Glavna petlja


Petlja koja se izvrava sve dok je Arduino ureaj ukljuen, definisana koristei pomonu funkciju
Arduino platforme, loop, u svakoj iteraciji najprije provjeri da li je potrebno izvriti slanje neke
vrijednosti na serijski izlaz, a zatim da li postoji neproitana vrijednost sa serijskog ulaza. U sluaju da
postoji, vrijednost se preuzima, dekodira i izvrava jedan od ve objanjenih metoda rjeavanja
komandi.
void loop() {
if (toSend) {
toSend = false;
Serial.write(analogSample.buffer[0]);
Serial.write(analogSample.buffer[1]);
}
// peek() je mnogo brza funkcija od available!
if (Serial.peek() != -1) {
char code = Serial.read();
switch (code) {
// ...
// Vec objasnjeni i pokazani case blokovi
// ...
}
}
}

74

3.8. EtfArduinoConfig - GUI aplikacija


GUI aplikacija slui kao alat za registraciju Arduino ureaja koji sadre odgovarajui program za
komunikaciju sa EtfArduinoService servisom.
Ova aplikacija je implementirana koristei .NET C++/CLI jezik. Razlog za ovaj izbor je injenica da se
na taj nain moe pristupiti .NET framework opcijama za jednostavnije kreiranje GUI-a, a u isto
vrjieme koristiti ve implementirana EtfArduinoService klasa za komunikaciju sa servisom.
Aplikacija pri pokretanju, koristei odgovarajue funkcije EtfArduinoService klase provjerava koji
ureaji su ve registrovani. Ova operacija se izvrava u posebnom threadu kako ne bi dolo do
blokiranja GUI threada. Svi pronaeni registrovani ureaji se stavljaju u za to predvien list box na
formi.
Osim toga, pronalaze se i svi serijski portovi koji postoje na sistemu, koristei statiku metodu
GetPortNames .NET klase System::IO::Ports::SerialPort.
Upravo opisani kljuni detalji implementacije aplikacije se mogu vidjeti u snippetu koda iz
EtfArduinoConfig aplikacije.
private: delegate void ListUpdateDelegate(int deviceId, String^ portName);
private: void DeviceDetectThread() {
// Go through IDs 0 to 10 and check if there are registered devices...
EtfArduinoService service;
for (int i = 0; i <= 10; ++i) {
TCHAR serialPortName[10];
if (service.CheckDeviceRegistered(i, serialPortName)) {
// Delegate the form update to the GUI thread
this->BeginInvoke(
gcnew ListUpdateDelegate(this, &Form1::AddToDeviceList),
i,
gcnew String(serialPortName));
}
}
}
private: void InitDeviceDetectThread() {
ThreadStart^ start = gcnew ThreadStart(this, &Form1::DeviceDetectThread);
Thread^ t = gcnew Thread(start);
t->Start();
}
private: void DetectSerialPorts() {
try {
serialPorts = System::IO::Ports::SerialPort::GetPortNames();
} catch (Exception^ exc) {
MessageBox::Show("Problem detecting ports...");
return;
}
}

75

Svi serijski portovi na kojima nije registrovan niti jedan ureaj se stavljaju u drugi list box.
Odabirom nekog od tih serijskih portova iz liste i pritiskom na dugme za registraciju ureaja, izvrava
se poziv metode RegisterDevice EtfArduinoService objekta.
Omogueno je i osvjeavanje liste registrovanih ureaja pritiskom na za to predvieno dugme.
Screenshoti koji prikazuju izvravanje aplikacije u razliitim fazama se nalaze ispod.

Slika 11: EtfArduinoConfig aplikacija: otkriven port, ureaj neregistrovan

Slika 12: EtfArduinoConfig aplikacija:


poruka o uspjenoj registraciji ureaja

Slika 13: EtfArduinoConfig


aplikacija: poruka o neuspjenoj
registraciji ureaja

76

Slika 14: EtfArduinoConfig aplikacija: ureaj registrovan

3.9. Zakljuak
U ovom poglavlju su predstavljene sve komponente i njihove implementacije.
Zahvaljujui named pipe mehanizmu meuprocesne komunikacije razliiti procesi su u stanju
zahtijevati usluge od EtfArduinoService servisa. Zbog dizajna koji koristi objektno-orijentisane tehnike
i dizajn patterne, dodati podrku za nove funkcionalnosti bilo u vidu novih komandi ili ureaja je
izuzetno olakano.
Zbog modularnosti koja je postignuta razdvajanjem procesa za upravljanje ureajima i samog DLL
adaptora, nije potrebno mijenjati kod DLL-a ukoliko doe do promjene u implementaciji funkcija
specifinih za Arduino ureaj, kao to je komunikacija sa raunarom.
Program za Arduino je zbog upotrebe timera i mehanizma prekida u stanju da generie uzorke ulaznog
signala s tano onom frekvencijom koja je zadata. Ipak, ogranienje postoji u tome da period
uzorkovanja izraen u mikrosekundama mora biti broj djeljiv sa 4.

77

4. Demonstracija
4.1. Uvod
U ovom poglavlju su predstavljeni rezultati dobiveni koritenjem razvijenog sistema u laboratorijskim
uslovima. Kroz odgovarajue grafike i slike, prikazano je kako se rezultati prikupljeni akvizicijom
signala razliitih oblika odnose sa oitanjima osciloskopa za te iste signale. Takoer, pokazan je i rad
analognog izlaza na bazi PWM tehnike. Prije svega, opisani su i potrebni koraci za pokretanje i
instalaciju sistema ukoliko se posjeduju kompajlirane datoteke.

4.2. Setup
Da bi koritenje sistema za akviziciju bilo mogue, potrebno je da je zadovoljeno nekoliko preduslova.
Najprije, ukoliko etfarduino adaptor nije prethodno koriten na raunaru, potrebno je izvriti njegovu
registraciju pri Data Acquisition Toolboxu kako bi engine bio u stanju locirati DLL s implementacijom
potrebnih interfejsa. Ovo se postie koritenjem komande daqregister u MATLAB okruenju:
daqregister etfarduino.dll
Pri tome etfarduino.dll se mora nalaziti u MATLAB putu ili trenutnom radnom direktoriju. Moe se
proslijediti i apsolutna putanja do DLL datoteke. Ukoliko je u pitanju Windows Vista ili noviji Windows
operativni sistem, ova operacija mora biti izvrena s uzdignutim privilegijama, to se postie
pokretanjem MATLAB-a u Administrator modu (desni klik, Run as Administrator).
Sljedei uslov je da je pokrenut servis za komunikaciju sa ureajem EtfArduinoService.exe.
Na kraju, iako je ovo dovoljno kako bi adaptor radio, kako bi bilo mogue kreirati bilo koji objekat za
akviziciju unutar Data Acquisition Toolboxa, potrebno je izvriti registraciju Arduino ureaja koristei
EtfArduinoConfig GUI aplikaciju.
Naravno, Arduino ureaj mora biti ispravno spojen na raunar s instaliranim EtfArduinoFirmware
programom.

78

4.3. Akvizicija sinusnog signala


Za demonstraciju rada sistema najprije je izvrena akvizicija sinusnog signala frekvencije 100 Hz
generisanog pomou generatora funkcija. Akvizicija je obavljena s frekvencijom uzorkovanja od 2 kHz
i u trajanju 20 ms.
MATLAB kod kojim je ovo postignuto je:
ai = analoginput('etfarduino');
addchannel(ai, 0);
ai.SampleRate = 2000;
ai.SamplesPerTrigger = ai.SampleRate * (20 / 1000);
start(ai);
wait(ai, 5);
[data, t] = getdata(ai);
plot(t, data);

% 20 ms

Ispod se moe vidjeti usporedba dobivenog rezultata akvizicije s oitanjem osciloskopa.

Slika 15: Grafik koji prikazuje podatke dobivene


akvizicijom sinusoidnog signala frekvencije 100
Hz

Slika 16: Oitanje osciloskopa za sinusoidni


signal frekvencije 100 Hz

Moe se zakljuiti da i frekvencija i amplituda odgovaraju oekivanoj.

79

4.4. Akvizicija trouglastog signala


Zatim je izvrena akvizicija signala oblika trougla frekvencije 100 Hz. Koritena frekvencija
uzorkovanja je takoer 2 kHz te vrijeme trajanja akvizicije 20 ms.
Ispod se nalaze slike na kojima se moe vidjeti usporedba rezultata dobivenog akvizicijom i oitanja
osciloskopa.

Slika 17: Grafik koji prikazuje podatke dobivene


akvizicijom signala oblika trougla frekvencije 100
Hz

Slika 18: Oitanje osciloskopa za signal oblika


trougla frekvencije 100 Hz

80

4.5. Akvizicija signala oblika etvrtke


Sljedee je uraena akvizicija signala oblika etvrtke frekvencije 100 Hz. Koritena frekvencija
uzorkovanja je 2 kHz i vrijeme trajanja akvizicije 20 ms.
Ispod se moe vidjeti usporedba rezultata dobivenog akvizicijom signala i oitanja osciloskopa.

Slika 19: Grafik koji prikazuje podatke dobivene


akvizicijom signala oblika etvrtke frekvencije 100
Hz

Slika 20: Oitanje osciloskopa za signal oblika


etvrtke frekvencije 100 Hz

81

4.6. Akvizicija promjene napona na NTC otporniku


Sljedei eksperiment se sastojao od akvizicije naponske vrijednosti na krajevima NTC otpornika koji je
zatim izloen grijanju i hlaenju kako bi dolo do izmjene oitavanog napona.
Frekvencija uzorkovanja je postavljena na 1 kHz, a vrijeme trajanja akvizicije 1 min.
Rezultujui grafik signala se moe vidjeti ispod.

Slika 21: Grafik podataka dobivenih akvizicijom naponskog


signala na krajevima NTC otpornika

Slika 22: Laboratorija za vrijeme izvravanja akvizicije


vrijednosti napona sa NTC otpornika
82

4.7. Analogni izlaz


Za demonstraciju rada analognog izlaza, uraeno je slanje 5 razliitih vrijednosti na izlaz te oitavanje
rezultujueg signala na osciloskopu. Signal je u obliku digitalnih pulseva, to je zbog ve objanjenog
ogranienja sistema analognog izlaza Arduino ureaja.
Prva vrijednost koja je poslana je 0V. Na slici ispod se moe vidjeti oitanje osciloskopa koje pokazuje
da je signal konstantno u stanju 0V, to je i oekivano.

Slika 23: Osciloskop prikazuje rezultat


postavljanja analognog izlaza na vrijednost 0
V
Druga vrijednost je bila 1.25V, to predstavlja jednu etvrtinu maksimalne vrijednosti od 5V. irina
pulseva, kako je ve objanjeno bi tada trebala biti jednaka etvrtini perioda. Na slici ispod se vidi
oitanje osciloskopa pri ovoj vrijednosti izlaza.

Slika 24: Osciloskop prikazuje rezultat


postavljanja analognog izlaza na
vrijednost 1.25 V
83

Sljedea vrijednost je 2.5V, tj. polovina maksimalne vrijednosti od 5V. Ispod se nalazi oitanje
osciloskopa.

Slika 25: Osciloskop prikazuje rezultat postavljanja


analognog izlaza na vrijednost 2.5 V
Za predzadnju vrijednost je uzeto 3.75V, to je tri etvrtine maksimalne vrijednosti od 5V. Oitanje
osciloskopa se nalazi na sljedeoj slici.

Slika 26: Osciloskop prikazuje rezultat postavljanja analognog


izlaza na vrijednost 3.75 V

84

Na kraju, na izlaz je poslana vrijednost 5V te se rezultat koji je prikazan na osciloskopu moe vidjeti
ispod.

Slika 27: Osciloskop prikazuje rezultat postavljanja


analognog izlaza na vrijednost 5 V

4.8. Zakljuak
U ovom poglavlju su predstavljeni rezultati koritenja razvijenog sistema. Usporedbom grafika signala
dobivenih analognom akvizicijom i onih na osciloskopima, moe se zakljuiti da je odstupanje
minimalno. Uoena odstupanja se mogu objasniti relativno niskom frekvencijom uzorkovanja.
Eksperimentom s akvizicijom napona s krajeva NTC otpornika je utvreno da je Arduino mogue
koristiti i u situacijama gdje je potrebno da akvizicija traje dui vremenski period te da prati sporije
promjene. Na kraju, analogni izlaz je proizveo PWM valne oblike koji su i oekivani za koritene
vrijednosti.

85

Zakljuak
Kroz rad je razmatrana mogunost koritenja Arduino ureaja kao akvizicionog modula.
Utvreno je da je najvea mogua frekvencija uzorkovanja koristei Arduino 12.5 kHz. Razlog za to se
pronalazi u dva aspekta, koliina memorije koju ureaj posjeduje i brzina prenosa podataka prema
raunaru. Nedovoljan kapacitet memorije dovodi do nemogunosti spaavanja podataka koji se
prikupljaju na sam ureaj. Maksimalna brzina prenosa koju je bilo mogue postii, 256 kbaud, dovodi
do navedenog ogranienja.
Za razvoj sistema je bilo potrebno razviti DLL sa COM komponentama koje su u stanju komunicirati
sa Data Acquisition Engineom. Ta komunikacija se zasniva na implementaciji predefinisanih COM
interfejsa, tj. njegovih metoda. Osim DLL-a, za postizanje modularnosti i efikasnosti, implementiran je
i proces EtfArduinoService koji predstavlja servis koji klijentima omoguava pristup funkcionalnostima
Arduino ureaja za akviziciju. Objektno-orijentisani principi dizajna koji su koriteni omoguavaju
lako proirenje servisa dodatnim funkcionalnostima.
Za sam Arduino je razvijen program koji reaguje na primljene komande u dogovorenom formatu i vraa
odgovarajue rezultate. Pri akviziciji, koritenje prekida je omoguilo postizanje perioda akvizicije koji
tano odgovara zadatom, pod uslovom da je period, izraen u mikrosekundama, broj djeljiv s 4. PWM
tehnika omoguava podrku analognog izlaza.
Kroz poglavlje Demonstracija moe se primijetiti da je sistem uspjeno iskoriten pri uslovima koji su
vladali u tim eksperimentima. Odstupanje dobivenih vrijednosti od oitanja osciloskopa je bilo
minimalno. Odreeno izoblienje grafika koje se javilo na nekim od signala je rezultat niske
frekvencije uzorkovanja. Akvizicija dueg trajanja je uspjeno izvrena i rezultat je pokazao kako je
mogue vriti akviziciju signala s sporijom promjenom. Analogni, tj. PWM, izlaz je takoer
demonstriran da radi kako je i oekivano.
Zbog ovoga, razvijeni sistem se moe koristiti kao zamjena za skuplje akvizicione module sve dok nije
potrebno vriti akviziciju signala izuzetno visokih frekvencija i dok je 10-bitna rezolucija ulaznih
vrijednosti dovoljna.
Bez obzira na to, budue unaprjeenje sistema bi se moglo nastaviti u vie pravaca. Jedan od njih je
uvesti specifinu podrku za druge Arduino ureaje koji ne bi bili ogranieni na 256 kbaud kao to je
Uno zbog svog Atmega8u2 procesora koji vri pretvorbu serijskog izlaza ATmega328 procesora u USB
signal.
Druga mogunost je istraiti druge vidove komunikacije raunara i Arduino ureaja, kao to su
Bluetooth, Ethernet, Wi-Fi i sl. Mogue je da bi se na taj nain mogla postii vea brzina prenosa
podataka to bi omoguilo i veu maksimalnu frekvenciju uzorkovanja. Obzirom na dizajn
EtfArduinoService servisa, za ovo bi bilo potrebno samo izvriti dodatnu implementaciju
ArduinoDevice abstraktne klase.
Jo jedna mogunost je koristiti ne-Arduino ureaje. Bilo koji ureaj koji podrava vid komunikacije sa
raunarom i analogni ulaz i izlaz bi se mogao podrati u sistemu. Primjer bi bili ATmega328 zasnovani
sklopovi koji za komunikaciju koriste serijski izlaz procesora i serijski port raunara, gdje bi brzina
komunikacije mogla biti 2 Mbaud. I u ovom sluaju, koriteni dizajn dozvoljava ovakva unaprjeenja.
86

Literatura
[1]

Arduino Team. ArduinoUno documentation. http://arduino.cc/en/Main/ArduinoBoardUno.


Preuzeto august, 2012.

[2]

Atmel Corporation (2012). ATmega48A/PA/88A/PA/168A/PA/328/P [DATASHEET]. Atmel


Corporation.

[3]

Atmel Corporation (2006). AVR120: Characterization and Calibration of the ADC on an AVR.
Atmel Corporation.

[4]

Atmel Corporation (2003). AVR131: Using the AVR's High-speed PWM. Atmel Corporation.

[5]

Box, Don (1998). Essential COM. Addison Wesley Longman, Inc.

[6]

Denver, Allen (1995). Serial Communications. http://msdn.microsoft.com/enus/library/ff802693.aspx. MSDN Library. Preuzeto august, 2012.

[7]

Microsoft Corporation. Interprocesss Communications. http://msdn.microsoft.com/enus/library/windows/desktop/aa365574(v=vs.85).aspx. Preuzeto august, 2012.

[8]

Microsoft Corporation. Microsoft COM: Component Object Model Technologies.


http://www.microsoft.com/com/. Preuzeto august, 2012.

[9]

The MathWorks, Inc. (2011). Data Acquisition Toolbox Adaptor Kit User's Guide. The
MathWorks, Inc.

[10] The MathWorks, Inc. MATLAB documentation. http://www.mathworks.com/help/techdoc/.


Preuzeto august, 2012.
[11] The MathWorks, Inc. (2011). Data Acquisition Toolbox User's Guide. The MathWorks, Inc.
[12] The MathWorks, Inc. MATLAB documentation. http://www.mathworks.com/help/techdoc/.
Preuzeto august, 2012.

87

You might also like