Professional Documents
Culture Documents
Mentor:
Student:
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
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
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.
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.
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.
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:
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.
11
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
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
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
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
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.
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.
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.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
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;
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
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.
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.
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.
28
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.
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.
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.
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
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.
31
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.
32
Instanca named pipea nad kojom je pozvana funkcija se moe otvoriti za konekciju drugog
klijenta koristei ConnectNamedPipe funkciju nad istim handleom.
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
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.
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.
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.
35
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;
}
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;
}
//
//
//
//
//
//
//
//
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);
}
}
39
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;
}
40
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.
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;
}
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;
SET_ACQUISITION_BUFFER_SIZE = 10;
SET_SAMPLE_RATE = 11;
SEND_DIGITAL_VALUE = 20;
PUT_SINGLE_VALUE = 30;
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.
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
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.
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.
47
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;
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;
}
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;
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
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();
}
}
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.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
};
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.
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
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
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.
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
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.
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;
64
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) +
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
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);
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
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
70
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.
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.
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.
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);
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.
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;
}
74
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.
76
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
% 20 ms
79
80
81
Sljedea vrijednost je 2.5V, tj. polovina maksimalne vrijednosti od 5V. Ispod se nalazi oitanje
osciloskopa.
84
Na kraju, na izlaz je poslana vrijednost 5V te se rezultat koji je prikazan na osciloskopu moe vidjeti
ispod.
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]
[2]
[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]
[6]
Denver, Allen (1995). Serial Communications. http://msdn.microsoft.com/enus/library/ff802693.aspx. MSDN Library. Preuzeto august, 2012.
[7]
[8]
[9]
The MathWorks, Inc. (2011). Data Acquisition Toolbox Adaptor Kit User's Guide. The
MathWorks, Inc.
87