Nagy Dávid Kristóf F66HYN
Bsc – Önálló laboratórium Inerciális szenzoregység alkalmazása Dokumentáció Feladat leírás: A feladatom egy szenzorpanel 3-dimenziós mágneses méréseinek kiolvasása szoftveres úton, melyeket 16 bites kettes komplemens formátumban tárol az érzékelő. A megoldást az i2c buszon keresztül kellett megvalósítani. Felhasznált eszközök:
Szenzor board: A témához használt hardverek egy NXP gyártmányú FRDM-STBC-AGM01 fejlesztő panel, melyen egy FXOS8700C 9-tengelyű digitális szenzor (gyorsulásmérő és magnetométer) és egy FXAS21002C 3tengelyű digitális giroszkóp helyezkedik el.
Mikrokontroller: A szenzorpanel egy Arduino MEGA 2560 mikrokontrollerhez illesztve használtam. A Mega 2560 egy ATmega2560 mikrokontrolleren alapuló fejlesztőpanel. Ez 54 digitális bemenettel / kimenettel rendelkezik. Ebből 15 használható PWM kimenetként továbbá 16 analóg bemenetet, 4 UART-ot (hardver soros port), egy 16 MHz-es kristály oszcillátort, USB-kapcsolatot, a hálózati csatlakozót, egy ICSP fejlécet, és a reset gombot foglalja magába. Egyszerűen csatlakoztatható a számítógéphez egy USB-kábellel.
Fejlesztő környezet: Az Arduino IDE 1.7.8-at használtam mely egy könnyen kezelhető felület a szoftver elkészítéséhez. Egy egyszerű kódszerkesztő és egy soros monitor a legfőbb elemei. A soros monitoron a számítógéphez USB-n keresztül csatlakoztatott fejlesztő panel által küldött adatok megjeleníthetők illetve akár adatokat is küldhetünk ezen keresztül. Megvalósítás:
A kiolvasandó adatok: A mért adatok a szenzor panel hexa 33-as címétől kezdődően a hexa 38-as címmel bezárólag az alábbi formátumban helyezkednek el a belső regiszterekben: az X tengely mentén mért adat felső byte-ját követi az X tengely mentén mért adat alsó byte-ja, az Y tengely mentén mért adat felső byteját követi az Y tengely mentén mért adat alsó byte-ja, a Z tengely mentén mért adat felső byte-ját követi az Z tengely mentén mért adat alsó byte-ja.
A kép illusztráció a szenzor adatlapjából a mágneses adatok elhelyezkedéséről. Az érzékelő az alábbi kép alapján meghatározott inercia rendszer szerint dolgozik. A felső sarokban lévő pötty által határozható meg az eszköz tényleges orientációja:
A szenzor inerciarendszere illusztráció.
I2C protokoll: Az I2C egy soros, 8 bit-es, kétirányú kommunikációs protokoll, amelynek sebessége normál üzemmódban 100kbit/s, gyors üzemmódban 400kbit/s. I2C busz csak két db kétirányú vezetékből áll (SDA és SCL), amelyre az összes eszköz is csatlakozik. Az egyik vezeték az (SDA –Serial DAta), amin a soros adatforgalom zajlik; a másik pedig az órajel vonal (SCL – Serial CLock), amit az I2C buszon történő kétirányú adatátvitel szinkronizálására használunk. Az SDA és az SCL vonalak az I2C buszon lévő minden eszközhöz hozzá vannak kötve (plusz természetesen a tápvezeték és a föld is). Az Arduino MEGA rendelkezik beépített i2c busszal. A busz SCL és SDA vonalakból áll melyek két-két digitális pin-re vannak kivezetve a panel oldalán. Ezek a digitális pin 20-SDA és 21-SCL, valamint ugyanezek a reset gomb felőli első két digitális pin (16,17). Ez utóbbiakra kapcsolódik a mikrokontrollerrel kompatibilis szenzorpanel.
Illusztráció a mikrokontroller és szenzor i2c busz kapcsolatáról. A kommunikáció a következőképpen működik:
Illusztráció az i2c műveletekről az érzékelő adatlapjából. A feladat során a mikrokontroller a mester és a szenzor panel a szolga. Ez azt jelenti, hogy a szenzor panel nem kezdeményez kommunikációt, nem állíthatja az adat vagy órajel vezetékeket a mester indítványozása nélkül. Továbbá a szenzor számára az órajelet az SCL vezeték jelenti, amit szintén a mester állíthat, vagyis esetünkben a mikrokontroller felelős a szenzor órajelének előállításáért.
Az i2c művelet előtt mind a kétirányú adatvezeték és az órajel vezeték is aktív magas állapotban van. Az indítási feltétel akkor teljesül, ha az órajel (SCL) magas állapota mellett az adatvezetéken (SDA) egy lefutó él érkezik. Ez csak úgy valósulhat meg, ha a mikrokontroller az SDA kimenetét logikai magas értékről logikai alacsonyra állítja. Az ezt követő 7 órajel periódusban a mester kiadja a megszólítandó szolga 7 bites gyárilag beállított egyedi címét.
A használt szenzor i2c címe az SA1 és SA0 pinek értékeinek megfelelően leolvasható az adatlapról. Az órajel alacsony értéke mellett a szenzor adatlapján megtalálható késleltetési idők betartásával adhat ki adatot (aktuális bitet) a mikrokontroller. Elsőként az MSB érkezik. A 8. bit pedig az i2c művelet típusát határozza meg. Ez ebben a pozícióban kötelezően nulla, vagyis a következő byte egy írási művelet operandusa lesz (a későbbiekben részletezve). Az ezt követő órajel periódusban a mester várja a szolga által küldött ACK (acknowledge) impulzust. Ennek formája a következő. A mester a kiadott 8. bit utáni órajel periódus elején „elengedi” az SDA vonalat tehát bemenetként kezeli, a felhúzó ellenállás miatt logikai magas szintre állítódik. Ha az adatok fogadása során nem történt hiba, a szenzor „felismerte” a saját címét, akkor a kétirányú SDA vonalat logikai nulla szintre állítja. Ez jelenti a mester számára, hogy a kommunikáció sikeres volt és folytatódhat. A következő 8 bit-et szintén a mester adja ki, melyek meghatároznak a szolga egy belső regisztercímét. Itt is az MSBt kell először küldeni. Hasonlóképpen a címhez, ezt a 8 bitet is egy ACK jellel nyugtázza a szolga, ha a bemeneti bufferébe sikeresen beíródott. Innentől lesz különböző az írás és olvasás művelete. Ha olvasási műveletet szeretnénk végrehajtani, akkor a megérkezett ACK jel után a mesternek egy úgynevezett ismételt indítási feltételt kell előállítani az i2c buszon. Ez azt takarja, hogy a következő órajel periódus alatt az órajel magas állapota mellett az SDA vonalon egy lefutó él érkezik. Az ezt követő 7 órajel periódusban újra kiadásra kerül a szolga i2c gyári címe, de az utolsó (8.) bit ezúttal 1es mivel egy olvasási műveletet szeretnénk végrehajtani az előzőleg kiválasztott regiszteren. A következő órajel periódusban a sikeres címzés esetén ACK jel érkezik a szolga áramkörtől, ezután viszont a mester továbbra is bemenetként kezeli az SDA vonalat. A következő 8 periódusban a szolga kiküldi az i2c buszra a megcímzett regiszterben lévő adatot, a mester által szolgáltatott órajelnek megfelelően. Eszerint az egyes adatbitek stabilak az SCL vezeték logikai magas állapota alatt. A mester ilyenkor mintavételezi az SDA vonalat. A mester ezután NAK jelet küld vagyis nem küld ACK jelet majd ezt követően egy lezáró feltételt küld. A lezáró feltétel az órajel magas állapota mellett az adatvonalon érkező felfutó élet jelenti. Ettől az írás művelet kissé eltér. Onnantól, hogy a művelethez szükséges regiszter címzése megtörtént egy írási művelet során a mester nem küld ismételt indítás feltételt és a szolga címének újraküldésére sincs szükség. A következő byte egyszerűen a regiszterbe írandó adat (8 bit) MSB-vel kezdve. Ezután e szolga a sikeres írás esetén egy újabb ACK jelet küld a következő órajel periódusban, majd a mester újra kimenetre állítja az SDA vonalat és egy a korábban meghatározott lezáró feltételt ad ki. Lehetőség van tovább úgynevezett „burst” üzemmódra is. Ez azt jelenti, hogy tetszőleges számú byte írása vagy olvasása történik egymás után. Ehhez szükséges, hogy a regiszterek, amelyek tartalmával dolgozni szeretnénk, sorban helyezkedjenek el (cím szerint). Kivétel ez alól a szenzor hibrid üzemmódja, melyet nem használok, ezért nem térek ki rá. Az ismételt
olvasáshoz a mester a kiolvasott első byte után egy ACK jelet küld a szolgának. Az olvasás egyészen addig folytatódik egyre növekvő regisztercímekről, amíg egy NAK és egy lezáró feltétel kiadásra nem kerül a mester részéről. Így tehát a kommunikációt csak a mester szakíthatja meg. A több byte-os írás esetében pedig addig áll fenn a kommunikáció, amíg a lezáró feltétel kiadásra nem kerül.
Inicializáció: A szenzor rendelkezik konfigurációs regiszterekkel, melyek segítségével a mérések paraméterei, egyéb funkciók és az állapotok állíthatók. Ezek az alábbi címeken találhatóak:
Az első és utolsó dolog mindig a 0x2A címen lévő CTR_REG1 írása, ugyanis ennek legalsó bitje határozza meg a szenzor állapotát, amelyet az alábbi ábra szemléltet.
Ha az alsó bit 0 értékű, akkor a szenzor standby állapotban van. Ekkor lehet a konfigurációs regiszterek tartalmát módosítani. A 0x5B címen lévő M_CTRL_REG1 alsó két bitje választja ki a működés típusát. Tekintve, hogy az FXOS8700CQ szenzor egy gyorsulásmérőt és egy magnetométert foglal magába a 00 bitkombináció kiválasztja csak a gyorsulásmérőt a 01 kombináció csak a magnetométert és az 11 pedig hibrid üzemmódot jelent. A 0x5C címen lévő M_CTRL_REG2 4-es pozícióján (LSB-től 5. bit) 1-es került, mert így lehet kikapcsolni a max/min detektálás funkciót, amire nem volt szükségem. Az alsó két bitpozícióra 11 kombináció kerül, amely a mágneses reset funkciót kapcsolja ki. A 0x5D címen lévő M_CTRL_REG3 regiszterbe 0x80 kerül. Így a 7-es pozíción lévő 1-es kikapcsolja az automatikus kemény vasas kompenzáló funkciót. Majd végezetül a 0x2A címen lévő CTRL_REG1 –be 0x39 kerül mely a kimeneti adat rátát 1.5625 Hz vagyis 640 ms periódusidejűnek határozza meg és a szenzort aktív állapotba állítja.
A program szerkezete: Az elkészített függvények: Az Arduino szoftver szerkezetét meghatározó függvények:
void setup() Egyszer fut le. void loop() Végtelen ciklusként pörög.
Saját függvények: void i2c_parameter() void timerIsr_reset() void timerIsr() void timerIsr_1read() void isr2() void display_function(uint8_t dataMSB_param, uint8_t dataLSB_param)
Felhasznált sztenderd függvények: pinMode();
Az egész számként átadott pin-t INPUT/OUTPUT paraméterrel bemenetnek vagy kimenetnek állítja. digitalWrite();
Az egész számként átadott pinre kiírja a második paraméterként átadott logikai értéket (HIGH,LOW) digitalRead();
Az egész számként átadott pinről beolvassa a logikai értéket (HIGH,LOW) Timer1.initialize();
Számláló inicializálása mikroszekundumban átadott egész értékkel Timer1.attachInterrupt();
A számlálóhoz hozzárendeli a név szerint paraméterként átadott ISR-t (interrupt service routine)
Általános lefutás: Az alábbi ábra összefoglalja a kezdeti és periodikus működést. Az Arduino szoftverek két alapvető komponense a setup() és a loop()függvények. Ebből értelem szerűen a setup() egyszer fut le és kezdeti értékek beállítására használható például. A loop() folyamatosan pörög végtelen ciklusként, ezt nem használja a kódom. A setup() függvény inicializálja az egyes Timer-t 1000 mikroszekundum periódusidővel. Ehhez csatlakoztatom az interrupt kiszolgáló rutinokat. Így tehát a periodikus működést az 1 milliszekundomonként meghívódó interrupt kiszolgáló függvények fogják megvalósítani. Először az isr_1write() függvény indul, mely egy állapotgép szerkezetű függvény. Ez minden egyes interrupt híváskor lefut egészen addíg míg az utolsó állapotába nem ér. Ekkor ugyanis meghívja az i2c_paraméter() nevű függvényt, amely átállít néhány globális változóban tárolt regisztercímet majd a számlálóhoz csatlakoztatja az egy byte-ot kiolvasó isr_1byte() interrupt kiszolgáló függvényt. Ez kiolvassa a megfelelő adatot,majd újból meghívja az i2c_parameter() függvényt. Ezzel gyakorlatilag megvalósítom a szenzor inicializálását. Az i2c_parameter() tehát egy író és egy olvasó függvény között kapcsolgat. Ezek az írásolvasás ciklusok csak 1 byte-on végződnek, és arra használom, hogy a megfelelő konfigurációs regiszterekbe bekerüljenek a megfelelő adatok. Az inicializáció végeztével már nem az 1-byteot olvasó függvény hívódik meg, hanem a 6 byte-nyi mágneses adatot kiolvasó megszakítás kezelő rutin. Ez is az előzőekhez hasonló állapotgép, csak hosszabb. Az utolsó állapotát elérve meghívja a timerIsr_reset() függvényt, mely a számlálóhoz az 1 byte-ot olvasó függvényt csatlakoztatja. A következő megszakításokra lefut, és ha a
0x32 címen lévő M_DR_STATUS mágneses adat státusz regiszterében nem nulla van, akkor visszavált a 6 byte-ot olvasó megszakítás kezelő rutinra. Ez pusztán annyit jelent, hogy a szenzor adatregisztereiben felülíródtak az értékek, új és friss adat kiolvasható. Ez periodikusan ismétlődik, amíg le nem választjuk a mikrokontrollert és a szenzort a számítógép USB csatlakozójáról.
Setup()
isr_1read()
isr_1write() 1.
Annyiszor fut le az írás és olvasás, amennyi a WRITE_CYCLE értéke
3. 2.
4.
i2c_parameter() Ha az összes WRITE_CYCLE lefutott
5
isr_6read( )
Csak akkor enged újabb adat olvasást, ha a státusz regiszter alapján frissültek a regiszterek
timerIsr_reset() isr_1read()
Globális változók: A következők volatile típusúak mert a megszakítás kezelő rutinok módosítják. volatile int state=0; volatile int state2=0; volatile int state3=0; Ezek a 3 állapotváltozó a 3 megszakítás kezelő függvényhez. volatile uint8_t dataMSB=0; volatile uint8_t dataLSB=0; A mágneses adat olvasása során ezekbe a változókba kerülnek a mérési eredmények. Mivel a szenzor byte-okkal dolgozik, elég 8 bites egész típus is. volatile int clock=0; A clock értelemszerűen az órajel periódus állapotát tartja számon, értéke 1-től 4-ig állítódik. volatile byte data=0; Az 1 byte-ot olvasó megszakítás kezelő függvény beolvasott adata kerül bele. int toggle2=0; int toggle=0; Az i2c_paraméterben használt flag típusú kapcsolgató változók. int display_cntr; A display_function() állapotváltozója, számon tartja, hogy x,y vagy z adatnál jár a kiolvasás és ennek megfelelően választja ki a soros monitorra kerülő tartalmat. int param_cntr; A megszakítás kezelő rutinokat felparaméterező i2c_parameter() függvény állapotát tartja számon. Eszerint vizsgálható, hogy hányadik konfigurációs írás történt meg. volatile int regadress2=0x2A; volatile int reg_data=0x00; byte regadress; A megszakításkezelő rutinok számára tárolt regisztercímek változói. byte adress_w=0x1E << 1; byte adress_r=(0x1E << 1) | 1; A szenzor panel gyári i2c címét tárolják, előállítva az olvasáshoz 1 utolsó bitet és íráshoz 0 utolsó bitet. int cfg_regadress[]={ 0x2A, 0x5B, 0x5C, 0x5D, 0x2A }; int cfg_data[]={ 0x00, 0x01, 0x13, 0x88, 0x39 }; Az első tömb a módosítandó konfigurációs regiszterek címeit tartalmazza sorban. A második a megfelelő helyen lévő regiszterhez tartozó inicializáló tartalmat tárolja. Azt, hogy ezekből hány van összesen, a WRITE_CYCLE makrónak kell értékül adni.
Setup: pinMode(SDA,OUTPUT); pinMode(SCL,OUTPUT); digitalWrite(SCL,HIGH); digitalWrite(SDA,HIGH); Ez a négy sor alapállapotba helyezi az i2c buszt. Itt fontos megjegyezni, hogy az Arduino mikrokontroller az SCL és SDA makrókat a megfelelő pinek számával helyettesíti, vagyis automatikusan kezeli az i2c buszt. Alapállapotban, amikor nincs kommunikáció a busz mindkét vonala logikai magas szinten van. Serial.begin(9600); while(!Serial); Ez a két sor inicializálja a soros monitort, hogy a fejlesztő környezetben megjeleníthetők legyen értékek. A clock változó 3-as állapotból fog indulni, tehát magas logikai szintről. clock=3; state=0; display_cntr=1; param_cntr=1; További kezdeti értékadás után inicializálja az 1-es időzítőt 1000 mikroszekundum időzítéssel. az attachInterrupt() sztenderd függvény csatlakoztatja az általam megírt megszakítás kezelő rutint az időzítő megszakításához. Timer1.initialize(1000); Timer1.attachInterrupt(isr2);
Egy ISR felépítése: Az ábrán könnyen figyelemmel kísérhető, mit jelent az állapotgép szerű megszakítás kezelő függvény. Az egyes órajel periódusokat tekintem állapotnak, és ezek határozzák meg sok-sok if-else típusú szerkezet segítségével az aktuális működést.
A szinkronizáló órajelet is a mesternek kell előállítania, tehát a megszakításkezelő rutinban implementáltam az órajel vonal vezérlését. Ehhez az alábbi ábra nyújt segítséget.
A nyilakkal jelzett időpontokban fut le a megszakítás kezelő rutin, a két nyíl között nem történik változás, az időzítő pörög, ha lejár, újra meghívódik a megszakítás kezelő rutin Egy megszakítás kezelő rutin szerkezetét egy switch-case típusú szerkezet adja, melynek feltétele az éppen aktuális órajel. Az időzítések legegyszerűbb kezeléséhez a számláló 4 megszakítást hoz létre, amelynek eredménye egyetlen órajel periódus lesz. Ezzel a periódus négy jól elkülönített részre osztódik. A legnagyobb előnye az, hogy az ábrán jelzett 1 illet 3 szakaszon az órajel stabil. Összefoglalva a megszakítások, folyamatosan érkeznek és a bekapcsolt megszakítás kezelő rutin rendre minden egyes alkalommal le is fut. A clock változó nyilvántartja az órajel állapotát, mely a kommunikáció időzítéséhez lesz szükséges, valamint a függvény minden második meghívás esetén ráolvas az SCL vonalra és megnegálja annak logikai szintjét. Ezek alapján érthetővé válik a következő felsorolás. A kódsorok formátuma, azért ilyen, mert lényegében egy-egy műveletből állnak egy feltétel vizsgálattal az elején és így követhető a működés. clock=3-as állapot: Azok a feladatok melyek az órajel magas (stabil) állapota mellett végzendők el a 3-as „case” alá kerülnek, ezek a tevékenységek a 9. oldal ábráján nyomon követhetők: if(state3==0){digitalWrite(SDA,LOW);} Start impulzus kiadása, az órajel magas állapota melletti lefutó élet jelent az adatvonalon. if(state3==19){digitalWrite(SDA,LOW);} Hasonlóan a start impulzushoz, az ismételt indítás művelete. if(state3>=29 && state3<=36){data= (data | (digitalRead(SDA) << (36-state3)));} A 29 és 36-os órajelperiódus között a szenzorpanel kiküldi az olvasandó 8 bites adatot az SDA vonalon. Mivel ez sorosan érkezik, bitenként tölti fel a megfelelő globális változót. if(state3==38){pinMode(SDA,OUTPUT);digitalWrite(SDA,HIGH);}//stop Lezáró feltétel. Az órajel magas állapota mellett a mester egy felfutó élet állít elő az SDA vonalon.
clock=1-es állapot: if(state3>=1 && state3<=8){mybit=(adress_w & (1 << (8-state3))); digitalWrite(SDA,mybit);} Az első 8 bit kiadása. Tartalmazza a szolga 7 bites i2c gyári címét, az utolsó bit nulla, jelezve a szolga számára, hogy a következő byte-ot el kell tárolnia. if(state3==9){if(digitalRead(SDA)){state3--;} else{ pinMode(SDA,OUTPUT);} } Az első ACK jel detektálása. Amíg a mintavételező függvény nem a Lehúzott logikai 0 értékkel tér vissza, addíg nem érkezik ACK jel és nem engedi folytatni a kommunikációt. if(state3>=10 && state3<=17){mybit= (regadress & (1 << (17-state3))); digitalWrite(SDA,mybit);} Második byte kiadása. Ez az i2c művelet leendő operandusa lesz, eltárolódik a szolga bemeneti pufferében. if(state3==18){if(digitalRead(SDA)){state3--;}else{ pinMode(SDA,OUTPUT);}} A második ACK jel detektálása. if(state3==19){digitalWrite(SDA,HIGH);} Az ismételt start feltétel előkészítése, az adatbemenet felhúzásával. if(state3>=20 && state3<=27){mybit=(adress_r & (1 << (27-state3))); digitalWrite(SDA,mybit);} Az olvasás műveletnél látható újracímzést végzi. Ezúttal az utolsó bit 1-es jelezve, hogy olvasási művelet következik. if(state3==28){if(digitalRead(SDA)){state3--;} } ACK jel detektálása 2-es állapot: digitalWrite(SCL, !(digitalRead(SCL))); Órajel élváltása. clock=4-es állapot: if(state3!=38){ digitalWrite(SCL, !(digitalRead(SCL)));} Órajel élváltása. Az utolsó állapotban letiltva, hogy a lezáró feltételt ki tudja adni a mester. (órajel magas állapota mellett felfutó él az SDA vonalon) if(state3==8){pinMode(SDA,INPUT_PULLUP);} if(state3==17){pinMode(SDA,INPUT_PULLUP);} if(state3==27){pinMode(SDA,INPUT_PULLUP);} ACK jelek detektálása előtt a mester elengedi az adatvezetéket. Mivel az i2c busz alaphelyzeben magas, ezért INPUT_PULLUP-ra állítja az SDA vonalat.
if(state3==38){Serial.println(data,HEX);} Végezetül az adat kiíratása a soros monitorra. Így épül fel egy egyszerű egy byte-os i2c kommunikációt szoftveresen megvalósító megszakítási rutin. Elkészítettem egy 1 byte-os író függvényt is. Ennek részletezésére itt nincs hely. A legfőbb különbség a 3. oldali diagramokon látszik. Nincs ismételt indítási feltétel, hiányzik az újracímzés. A mester csak kiküldi az írandó adatot az SDA vonalon majd egy ACK jel után lezárja a kommunikációt. A 6 byte-ot olvasó burst üzemmódú függvény viszont jóval hosszabb. Ilyenkor az első byte után, a mester nem zárja le a kommunikációt, hanem küld egy ACK jelet a szolga számára. Ennek következtében a szolga eggyel növeli a regiszter címet és kiküldi a következő byte-ot. Ez egészen addig folytatódik, amíg a mester nem küld ACK jelet (NAK) és le nem zárja a kommunikációt. Ez a függvény szerkezetét tekintve hasonló az 1 byte-ot olvasó társához, csak lényegesen hosszabb, több állapottal. Minden második lefutáskor meghívja a display_function() nevű függvényt, ami elkezdi összeállítani a byte-os adatokból a 16 bites adatot.
display_function() Ez a függvény a 6 byte-os nyers adatokat alakítja át és írja ki a soros monitorra. Minden második byte kiolvasása kor meghívódik a 6byte-ot olvasó megszakítás kezelő rutin által, ugyanis ekkor már rendelkezésre áll egy teljes adat. Paraméterként veszi át az MSB és LSB byte-ot. void display_function(uint8_t dataMSB_param, uint8_t dataLSB_param) { int Temp=0; if(display_cntr==1) Serial.print(" X: "); if(display_cntr==2) Serial.print(" Y: "); if(display_cntr==3) Serial.print(" Z: "); Számon tartja, hogy éppen X, Y, vagy Z tengelyű mérés érkezik, ez alapján ezt ki is írja a soros monitorra. int data=((dataMSB_param<<8)|(dataLSB_param)); // int16_t data = ((int16_t) dataMSB_param << 8 | dataLSB_param) >> 2; int andvalue=0x01; for(int i=1; i<=16; i++) { if((data & andvalue)!=0) { Temp+=andvalue; } andvalue*=2; }
Kettes komplemens- decimális előjeles átalakítás. if(display_cntr==3) Serial.println(Temp); else Serial.print(Temp); Adat kiíratása. if(display_cntr==3) display_cntr=1; else display_cntr++; return; }
i2c_parameter() void i2c_parameter() { switch(param_cntr){ case 1: regadress2=cfg_regadress[1]; reg_data=cfg_data[1]; break; case 2: regadress2=cfg_regadress[2]; reg_data=cfg_data[2]; break; case 3: regadress2=cfg_regadress[3]; reg_data=cfg_data[3]; break; case 4: regadress2=cfg_regadress[4]; reg_data=cfg_data[4]; break; default: break; } Ez a switch-case szerkezet felelős a konfigurációhoz szükséges regisztercímek és tartalom beállításáért. A globális változókat ezután a megfelelő író és olvasó megszakítást kezelő rutinok használják. Timer1.detachInterrupt(); Az előző megszakítás kezelő rutin lecsatlakoztatása.
clock=3; state2=0; digitalWrite(SCL,HIGH); digitalWrite(SDA,HIGH); Állapotváltozók és i2c busz alapállapotba állítása. if(WRITE_CYCLE!=param_cntr) { if(toggle==1 {regadress=cfg_regadress[param_cntr]; toggle=0; Timer1.attachInterrupt(timerIsr_1read); if(param_cntr<5) param_cntr++; } else if(toggle==0) { toggle=1; Timer1.attachInterrupt(isr2); } } else } regadress=0x33; Timer1.attachInterrupt(timerIsr); } } Ez az if szerkezet végzi a kapcsolgatást először a konfiguráció során az 1 byte-ot olvasó és 1 byte-ot író megszakítás kezelő rutinok között. Majd az inicializáció befejeztével a 6 byte-ot olvasó és az (státuszregisztert olvasó) 1 byte-ot olvasó között.
timerIsr_reset() Leválasztja az időzítő megszakításáról a 6 byte-ot olvasó megszakítás kezelő rutint. Alapállapotba állítja a megfelelő változókat és az i2c buszt. Timer1.detachInterrupt(); clock=3; state=0; digitalWrite(SCL,HIGH); digitalWrite(SDA,HIGH); regadress=0x32; Timer1.attachInterrupt(timerIsr_1read); Csatlakoztatja az időzítőhöz az 1 byte-ot olvasó megszakítás kezelő rutint. Ez a függvény a 6 byte-ot olvasó és az 1 byte-ot olvasó között kapcsolgat, annak megfelelően, ha az 1 byte (adat státusz regiszter értéke) alapján már új mágneses mérések állnak rendelkezésre.
Kiolvasott adatok: A szoftver hiba nélküli lefutásával folyamatos mágneses méréseket lehet folytatni +-1200 út méréshatárral. Az eredmények tized uT-ban értendők. Az alábbi két kép egy mérésről készült, amikor is a szenzorhoz az X tengelye mentén pozitív irányból egy mágnest közelítettem először az északi pólussal majd a déli pólussal. Látható hogy nem csak az X tengelyen mért értékek változtak a közelítés során. Ennek oka a mágneses mező alakja, valamint a nem elég pontos iránykövető közelítés.
Az alábbi két kép egy mérésről készült, amikor is a szenzorhoz az Y majd Z tengelye mentén pozitív irányból egy mágnest közelítettem először az déli pólussal majd a északi pólussal. A mérések tartalmazzák a Föld mágneses terét és egyéb mágneses zajokat. Azonban még ehhez képest is jelentős offszetet tapasztaltam. Ennek okát nem tudtam kideríteni. Okozhatja egy hiba a szoftverben, de lehet a beállítások vagy a szenzor sajátossága is.
Összefoglalás: A félév során hasznos ismereteket szereztem az érzékelők használatával kapcsolatban. Megismerkedtem az alapvető kommunikációs protokolljaikkal. Betekintést nyertem egy egyszerű feladat keretében a mikrokontrollerek és szenzorok alkalmazásába. Szoftverfejlesztés során programozói tapasztalatokra tehettem szert.
2016. május 25.