Mér˝o- és vezérl˝oberendezés megvalósítása ARM alapú mikrovezérl˝ovel és Linux-szal Fuszenecker Róbert 2007. augusztus 5.
Tartalomjegyzék 1. Köszönetnyilvánítás és felajánlás
2
2. Bevezetés
3
3. A hardver felépítése 3.1. A számítógép, mint mérésvezérl˝o . . . . . . . . . . . . . . . . 3.2. Az ARM alapú mikrovezérl˝ok . . . . . . . . . . . . . . . . . 3.3. ATMEL vagy NXP – „Te kit választanál?” . . . . . . . . . . . 3.4. A processzor modul . . . . . . . . . . . . . . . . . . . . . . . 3.5. Érzékel˝o, végrehajtó és beavatkozó szervek . . . . . . . . . . 3.5.1. 12 V-os izzó vezérlése digitális és analóg (PWM) jellel 3.5.2. Fényérzékel˝ok, fotodiódák . . . . . . . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
4 4 5 7 8 11 11 11
4. Fejleszt˝oi környezet 4.1. C fordító Linuxos PC-re . . . . . . . . . . . . . . . . . . . . . 4.2. C fordító ARM processzorra . . . . . . . . . . . . . . . . . . . 4.3. Az OpenOCD – feltölt˝o és nyomkövet˝o program . . . . . . . . 4.4. Adatátvitel megvalósítása soros porton keresztül . . . . . . . . . 4.5. A kommunikáció protokollja és annak megvalósítása C nyelven
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
14 14 15 16 18 20
5. Felhasználási területek 5.1. Vezérlés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.1. 12 V-os izzó be- és kikapcsolása . . . . . . . . . . . . . . . . . . . . 5.1.2. 12 V-os izzó be- és kikapcsolása id˝ozít˝o megszakítással . . . . . . . . 5.1.3. 12 V-os izzó fényerejének beállítása impulzusszélesség-modulációval 5.2. Mérésadatgy˝ujtés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3. Transzfer karakterisztika felvétele . . . . . . . . . . . . . . . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
30 30 30 34 35 37 42
6. Összefoglalás, végkövetkeztetés
46
7. Felhasznált irodalom
47
8. Felhasznált szoftverek
48
9. Mellékletek 9.1. JTAG csatlakozó kiosztása . . 9.2. ARM JTAG csatlakozó leírása 9.3. USB-JTAG kapcsolási rajza . . 9.4. JTAGKEY elvi rajza . . . . . 9.5. OOCD-LINK kapcsolási rajza
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
1
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
49 50 51 52 53 54
1. fejezet
Köszönetnyilvánítás és felajánlás Ezúton szeretném kifejezni köszönetemet konzulenseimnek, . . . . . . és Krüpl Zsolt okleveles villamosmérnöknek, akik áldozatos munkájukkal és nélkülözhetetlen szakmai tanácsaikkal segítettek a dolgozat megírásában. Szintén hálával tartozom a lektoroknak, Gagyi Endre okleveles villamosmérnöknek és Gnandt András okleveles villamosmérnöknek, akik erejükön felül teljesítve azon fáradoztak, hogy a dolgozat mindenfajta – szakmai, helyesírási és logikai – hibától mentesen kerülhessen az Olvasó elé.
A szabad szoftver mozgalom által készített operációs rendszer (Linux), a szövegformázó rendszer (LATEX2ε) és a többi segédprogram nélkül elképzelhetetlen lenne ezen m˝u megírása. Ezért felajánlom ezt a dolgozatot a közösség számára, hogy bárki – belátása szerint – használhassa mindenféle anyagi ellenszolgáltatás nélkül.
Ez a dokumentum szabad szoftver, szabadon terjeszthet˝o és/vagy módosítható a napokban megjelent GNU General Public License 3-ban leírtak szerint. Váljon ez a dokumentum minden él˝olény javára!
2
2. fejezet
Bevezetés Ezen dolgozat megírására azért vállalkoztam, hogy megmutassam az Olvasónak: egy általános célú irányítóberendezés (mérésadatgy˝ujt˝o és vezérl˝o) megépítése ma már nem okoz komoly kihívást egy elektronikában jártas szakembernek. A (mikorelektronikai) technológia lehet˝ové teszi, hogy mikrovezérl˝oink olyan számítási kapacitással rendelkezzenek, amivel az asztali és hordozható számítógépek rendelkeztek néhány évvel ezel˝ott. Egy grafikus kijelz˝ovel bíró, internetezésre és Java játékok futtatására alkalmas mobiltelefonban pontosan olyan processzor található, mint amilyet a mér˝oberendezésünk megépítéséhez választottam. Ez a processzor pedig az Acorn cég által útjára indított ARM 1 fejleszt˝oi prodzsekt terméke: az ARM7TDMI. Az ipar és a szórakoztató elektronika széles körben használja ezt a processzort, ezek találhatók az Apple iPod-okban, a HP PalmTop-jaiban, az Intel PDA-iban, a Nintendo GameBoy-okban, a legtöbb Nokia mobiltelefonban, és a Sirius m˝uholdvev˝oiben. Természetesen készülnek bel˝ole személyi számítógépek is: az A7000+ Odyssey a Castle Techologies Co-tól, az Acorn PC-i, vagy a IYONIX PC. A lista szinte végtelen, hiszen egy nagyon jól megtervezett processzorról beszélünk. De milyen méréstechnikai feladatok megoldására használhatók ezek az eszközök? Az adatlapok áttanulmányozása után könnyen beláthatjuk, hogy jónéhány mérési problémára megoldást nyújtanak, hiszen nagy részük rendelkezik több száz kSample/s2 ) sebesség˝u analóg–digitális és digitális–analóg átalakítóval, viszonylag nagy adatmemóriával, kommunikációs interfésszel (RS-232, CAN3 , SPI4 , Ethernet, USBUniversal Serial Bus – univerzális soros busz) és általános célú digitális ki- és bemenetekkel. A lista most sem teljes, de az itt felsorolt eszközök közül sem fogjuk mindegyiket felhasználni. A dolgozat els˝o részében a megfelel˝o mikrovezérl˝o5 kiválasztásáról, a mér˝o és vezérl˝o hardver kialakításáról olvashat az Olvasó, ezt követi a szoftver háttér (fejleszt˝oi környezet) ismertetése. Ezután a mikrovezérl˝oprogramozás legszebb részével, az adatátvitellel foglalkozunk. Végül néhány, a gyakorlatban is megvalósított példán keresztül mutatom be a legérdekesebb felhasználási területeket. Sajnos a dolgozat terjedelme nem teszi lehet˝ové, hogy minden, általam hasznosnak tartott témáról beszéljek, így csak a mérésadatgy˝ujtéssel, a vezérléssel, és a a transzfer karakterisztika felvételével tudunk részletesen foglalkozni. A dolgozat elolvasása során az Olvasó beláthatja, hogy az említett mikrovezérl˝ok összetettségük ellenére könnyen és hatékonyan felhasználhatók mindennapi munkánk során. Talán az olvasó is kedvet kap hasonló áramkörök készítéséhez, hogy az itt megszerzett tudását a gyakorlatba is átültethesse. Remélem, hogy ezen m˝u olvasása éppoly örömet okoz az Olvasónak, mint nekem jelentett a megírása.
A szerz˝o
1 Advanced
RISC Machines – fejlett, csökkentett utasításkészlet˝u gépek mintavételezés/másodperc, az átalakítás sebességér˝ol ad információt 3 Controller Area Network – mikrovezérl˝ ok helyi hálózata 4 Serial Peripheral Interface – soros periféria interfész, három vezetéket használ: órajel, adat és föld 5 ARM7TDMI-S processzor, memória és nagyszámú periféria áramkör 2 1000
3
3. fejezet
A hardver felépítése Ebben a részben – néhány blokkvázlat segítségével – megpróbáljuk megtervezni mér˝om˝uszerünk felépítését. Mivel egy összetett alkalmazásról van szó, ezért az áramkört máris két f˝o részre bonthatjuk: 1. a mikrovezérl˝ot és a kisegít˝o alkatrészeit (JTAG1 ) tartalmazó blokkra, valamint 2. a végrehajtókat, beavatkozókat és érzékel˝oket tartalmazó blokkra. Erre azért van szükség, hogy a mikrovezérl˝ot tartalmazó (processzor modul) blokk kapcsolási rajza ne legyen annyira összetett, hogy ne lehessen hozzá „szép” nyomtatott áramkört tervezni, másrészt rendkívül elegáns megoldás az, amikor a megoldandó feladatnak megfelel˝o blokkot egy szalagkábellel kötjük össze a processzor modullal.
A mér˝oberendezés blokkvázlata
3.1.
A számítógép, mint mérésvezérl˝o
Nézzük meg a különböz˝o blokkok felépítését! Szerencsénkre a számítógéppel nincsen különösebb gondunk, hiszen az többnyire adott, nem kell rajta változtatásokat eszközölni. Annyit azonban elvárhatunk t˝ole, hogy rendelkezzen párhuzamos (nyomtató) csatlakozóval, mert ezen keresztül tudjuk az elkészült programot feltölteni a mikrovezérl˝obe, és ezen keresztül követhetjük nyomon a program futását a mikrovezérl˝oben. Ez az IBM kompatibilis gépeken mindig egy 25 pólusú DSUB csatlakozó, pontosabban annak „female” (anya) változata. Ennek a portnak az az el˝onye, hogy nagyon könnyen programozható, és TTL2 szint˝u (0 V – 5 V) jelekkel dolgozik3 . Egy másik fontos követelmény a számítógéppel szemben a soros port megléte, mert ezen keresztül történik 1A
lefordított program feltöltésére és a mikrovezérl˝o m˝uködésének ellen˝orzésére (nyomkövetésre) szolgáló általános célú interfész logika 3 Az ARM alapú mikrovezérl˝ ok 3,3 V-os tápfeszültséggel m˝uködnek, így az általuk kiadott logikai „magas” szint 3,3 V, ami a TTL rendszerben már magas szintnek számít 2 Tranzisztor-tranzisztor
4
˝ 3.2. AZ ARM ALAPÚ MIKROVEZÉRLOK
a parancsok és mérési adatok átvitele a mikrovezérl˝o és a számítógép között. Nem várunk el a soros porttól túl sokat: mi is (csak) az iparban leggyakrabban alkalmazott 9600 8n1 átviteli konfigurációt (9600 bit/sec átviteli sebesség, 8 adatbit, nincs hibaellen˝orzés, 1 stop bit) fogjuk alkalmazni. Kialakítását tekintve ez egy 9 pólusú DSUB „male” (apa) csatlakozó. Hátránya ennek a rendszernek, hogy -15 V – +15 V-os jelszintekkel dolgozik, de azt a problémát egy illeszt˝o áramkörrel (a jól bevált MAX232 IC-vel) könnyen megoldhatjuk. Cserében viszont az áthidalható távolság több 10 m-re növekedhet, ellentétben a JTAG kábel 1 m-es maximális hosszával. A számítógép természetesen hasznavehetetlen egy megfelel˝o m˝uködtet˝o program nélkül. Err˝ol témáról a „Fejleszt˝oi környezet” részben talál további információkat, példákat az Olvasó.
3.2.
Az ARM alapú mikrovezérl˝ok
Az „ARM” nevet eredetileg mikroprocesszorokkal kapcsolatban használták, mert alig néhány éve még csak mikroprocesszorok készültek ARM maggal. Ezeket a mikroprocesszorokat aztán számítógépekbe ültették, így alakítva ki az Acorn, az Apple, vagy az Intel ARM alapú gépeit. Az áramköri lapka csak mint mikroprocesszor m˝uködött, és számos kiegészít˝o elemet (memória, megszakításvezérl˝o, I/O4 kezel˝o, busz illeszt˝o) kellett hozzá illeszteni. Az elmúlt néhány évben a technológia gyors fejl˝odése lehet˝ové tette, hogy egy szilícium lapkán kialakítható legyen egy teljes érték˝u számítógép – processzorral, memóriával, adat be-/kiviteli eszközökkel, és egyéb perifériákkal. Ezeket mikrovezérl˝oknek nevezzük. Nem a kis méretük miatt „mikro”-k, hanem azért, mert nem általános célú számítógépek, mint az asztali és hordozható számítógépeink, hanem bizonyos feladatcsoport megoldására tervezett kis teljesítmény˝u számítógépek. Azért ne becsüljük le ezeket a „mikro” eszközöket, hiszen így is jóval többre képesek, mint amit el tudnánk képzelni! Ezekben ugyanis egy teljes érték˝u 32 bites mikroprocesszor dolgozik, ami azt jelenti, hogy az egy lépésben feldolgozható adat hossza 32 bit. A természetes számok (integer, unsigned int) 0 és 4.294.967.295 (több, mint 4 milliárd!) vehetnek fel tetsz˝oleges értéket. Aki ezt is kevésnek találja, használhat lebeg˝opontos (float) számokat, ezekkel még a csillagászati számokat is könnyedén tudjuk ábrázolni. A lapkán kialakított memória már korántsem ennyire szívderít˝o: a program tárolására használható memória (FLASH – nem felejt˝o memória) 8 kbájt és 512 kbájt között változik, míg az adatok tárolására szolgáló gyors, de a tápfeszültség megsz˝unésekor „felejt˝o” (RAM) memória 2 kbájt és 128 kbájt között változik. Ezek az értékek típusonként eltér˝oek. Az általam javasolt mikrovezérl˝o (a legkisebb és egyben a legolcsóbb) 2 kbájt RAM-mal és 8 kbájt FLASH-sel rendelkezik. És ez elég-e? Igen, majd úgy írjuk a programot, hogy beleférjen!
Az ARM processzor blokkvázlata 4 Adat
be-/kivitel
5
˝ 3.2. AZ ARM ALAPÚ MIKROVEZÉRLOK
A f˝oprocesszor (ATM7TDMI main processor logic) bels˝o blokkvázlata
Ez persze nem minden: egy mai modern (2007. július) mikrovezérl˝o a processzoron és a memórián kívül még számos kiegészít˝o eszközzel rendelkezik. Ezeket perifériáknak nevezzük, mert általában a blokkvázlat szélén (perifériáján) kapnak helyet. Álljon itt néhány példa: • Analóg–digitális átalakítók: folytonos értékkészlet˝u jeleket (tipikusan feszültséget, áramot) alakítanak valamilyen számként (egész vagy fix-pontos) ábrázolt értékké; az ipari gyakorlatban 10 bites A/D átalakítókat használunk, ezek felbontása 0,1 % a teljes skálára vonatkoztatva • 16 és 32 bites számlálók: ezekkel vagy küls˝o eseményeket tudunk számolni (hányszor következtek be), vagy a mikrovezérl˝o órajeléb˝ol egy késleltetési (id˝ozítési) értéket tudunk képezni. Pl. mintavételezés minden 100. ms-ban: ekkor az id˝ozít˝o 100 ms-onként jelez a processzornak, hogy „ideje mintavételezni”! • UART: univerzális aszinkron adó-vev˝o; ezt fogjuk a számítógéppel való kommunikációhoz használni • I 2 C, vagyis Inter-IC kommunikáció: számos gyártó készít olyan eszközt (memóriák, PLL szintézerek5 , A/D átalakítók, DDS6 -ek), melyek ezen busz segítségével programozhatók. 2-vezetékes (+föld) átvitelt tesz lehet˝ové • SPI: Serial Peripheral Interface, azaz soros periféria interfész; hasonló az I 2 C-hez, de másként m˝uködik. Tipikusan MMC7 és SD kártyákkal történ˝o kommunikációhoz használjuk, de számos eszköz támogatja (digitális h˝omér˝ok, soros FLASH alapú memóriacsipek, mikrovezérl˝ok). 5 Frekvenciaszorzó 6 Direkt
digitális szintézer MP3 lejátszók, PDA-k kiegészít˝o memóriája
7 Fényképez˝ ogépek,
6
3.3. ATMEL VAGY NXP – „TE KIT VÁLASZTANÁL?”
• Megszakításvezérl˝o: küls˝o események felfüggeszthetik az aktuális program futását, és arra kényszeríthetik a processzort, hogy egy másik, el˝ore meghatározott programrészt hajtson végre. Annak lefutása után a processzor visszatér az eredeti feladatához, és ott folytatja, ahol abbahagyta. A megszakításvezérl˝o a küls˝o események rangsorolását, és a processzornak való „jelzést” végzi és segíti • Órajel-generátor: a mikroprocesszor „szívverését” szolgáltatja, minden esemény szinkron módon ehhez igazodik • GPIO: általános célú (digitális) adatbemenetek és kimenetek; ezek segítségével tudjuk például egy kapcsoló állását vizsgálni, vagy ezzel tudunk egy lámpát bekapcsolni • RTC: valós idej˝u óra (és kalendárium, szök˝oév-számítással – csak az LPC21xx-ben) • WDT: watchdog timer, olyan id˝ozít˝o, ami alaphelyzetbe állítja (RESET) a mikrovezérl˝ot, ha a szoftvere lefagy (Ez azért lehet hasznos, mert például egy m˝uholdat vagy egy Mars-járót nem lehet kézzel újraindítani, ehhez hardver támogatás kell) • USB: univerzális soros busz, tipikusan a számítógéppel való kommunikációhoz • Ethernet: hálózati kommunikációhoz; ezzel megvalósíthatjuk a teljes TCP/IP kommunikációt, így a mikrovezérl˝onket a Föld bármely pontjáról utasíthatjuk • CAN busz: controller area network; mikrovezérl˝ok hálózatba kötésére használatos, valósidej˝u kommunikációt valósíthatunk meg vele • PWM: id˝ozít˝o/számláló egységekkel (impulzusszélesség-modulációt alkalmazva) a digitális jelet analóg jellé alakíthatjuk. Pontos, de lassú eljárás Ezek után biztosan megérti a kedves Olvasó, hogy miért szeretik a mobiltelefonok, PDA-k, digitális fényképez˝ogépek és hálózati elemek gyártói az ARM alapú mikrovezérl˝oket alkalmazni termékeikben: a kiváló teljesítmény˝u processzor és a nagyszámú beépített elemkészlet igen jó választás beágyazott rendszereik számára.
Miel˝ott azonban az Olvasó ellátogatna az ARM cég honlapjára (www.arm.com), hogy mikrovezérl˝ot rendeljen, ˝ csak processzorok tervezésével és licenszek el kell mondanom, hogy az ARM cég nem gyárt mikrocsipeket. Ok (gyártási engedélyek) eladásával foglalkoznak. De akkor hol lehet ARM mikrovezérl˝ot beszerezni? – kérdezheti bárki. A lista szinte végtelen: világszerte majdnem 100 cég foglalkozik ARM lapka gyártással, t˝olük kell rendelni. Magyarországon több forrásból beszerezhet˝ok8 , arra azonban figyelnünk kell, hogy a gyártók csak a processzor gyártási engedélyét veszik meg, a perifériákat utólag illesztik hozzá – saját ízlésüknek és hagyományaiknak megfelel˝oen! Két mikrovezérl˝o-gyártó világcég (ATMEL9 és NXP10 ) termékeit hasonlítom össze a következ˝o fejezetben, majd objektíven eldöntjük, hogy az NXP LPC 2101 mikrovezérl˝oje a jobb (tényleg az a jobb). A mér˝oberendezés megépítéséhez is ezt a csipet fogjuk alkalmazni.
3.3.
ATMEL vagy NXP – „Te kit választanál?”
Ebben a részben azt fogjuk megállapítani, hogy céljaink megvalósításához melyik gyártó mikrovezérl˝oje a legmegfelel˝obb. Magyarországon viszonylag könny˝u választani, mert csak két gyártó (ATMEL és NXP) termékei kaphatók. A következ˝o táblázat a két gyártó egy-egy mikrovezérl˝ojének legfontosabb tulajdonságait tartalmazza, teret engedve a szubjektív tapasztalatnak is. Lássuk hát, hogy mit is tudnak azok a bizonyos mikrovezérl˝ok: 8 ATMEL:
MSC Budapest Kft. (950 Ft + ÁFA), NXP: FDH Kft. (966 Ft + ÁFA), RET Kft. (rendelésre) és Lomex Kft. (910 Ft + ÁFA)
9 www.atmel.com 10 www.nxp.com,
Philips alapította
7
3.4. A PROCESSZOR MODUL
Funkció Processzor gcc támogatás OpenOCD támogatás FLASH memória RAM A/D átalakító Számlálók, id˝ozít˝ok Valós idej˝u óra UART I 2C SPI Megszakításkezel˝o Órajel frekvenciája Tokozás Kiskereskedelmi ára
ATMEL AT91SAM7S32 ARM7TDMI Igen Igen 32 kbájt 8 kbájt 10 bites 3 x 16 bit (PWM kimenettel) Igen (32 bites egész szám) 1 db 1 db 1 db Igen 55 MHz (PLL nélkül), 220 MHz (PLL-lel) LQFP-48 950 Ft + ÁFA
NXP LPC2101 ARM7TDMI-S Igen Igen 8 kbájt 2 kbájt 10 bites 2 x 32 bit, 2 x 16 bit (PWM kimenettel) Igen (teljes naptár funkcióval) 2 db 2 db 2 db Igen 25 MHz (PLL nélkül), 70 MHz (PLL-lel) LQFP-48 966 Ft + ÁFA
Mikrovezérl˝o összehasonlító táblázat
Látható, hogy az ATMEL mikrovezérl˝oje több memóriával rendelkezik ugyan, de kevesebb perifériát (UART, naptár, I 2 C) épített bele a gyártó. Természetesen ez nem jelenti azt, hogy az NXP mikrovezérl˝oje jobb attól, hogy több perifériát tartalmaz. Személyes benyomásom mégis az, hogy az NXP jobban dokumentálja a mikrovezérl˝oit, több mintaprogramot ad, a mikrovezérl˝ok hardverét egyszer˝ubb programozni, a nyomkövetés is egyszer˝ubb, és talán ez a legfontosabb: kevésbé érzékeny a küls˝o „zavaró” hatásokra (csavarhúzó beejtése, lábak kézzel való érintése, stb. Ha a mostani mikrovezérl˝om mesélni tudna11 . . . Figyelembe véve az objektív tényeket és szubjektív tapasztalatokat, úgy t˝unik, hogy az NXP cég LPC2101 típusú mikrovezérl˝oje fogja a mér˝oberendezés „középpontját” alkotni.
3.4.
A processzor modul
Ez a fejezet szolgál arra, hogy megismerjük az LPC2101 mikrovezérl˝o köré építend˝o küls˝o áramköröket. Ilyen áramkör például a tápfeszültség el˝oállítására szolgáló stabilizátor, a mikrovezérl˝o újraindítását szolgáló RESET áramkör, vagy a program feltöltését és m˝uködésének elemzését lehet˝ové tev˝o JTAG interfész. Ezek szükséges és elégséges áramkörök, de saját igényeink szerint ki is egészíthetjük ezeket, például kijelz˝ovel, gombokkal, potenciométerekkel (jelen esetben ett˝ol eltekintünk, hiszen ez „csak” egy tesz áramkör, és nem a Paksi Atomer˝om˝unek fejlesztünk). Követelmények az áramkörrel szemben: • Biztosítania kell a mikrovezérl˝o m˝uködéséhez szükséges feltételeket, úgymint – stabilizált, túlfeszültség ellen védett tápfeszültség, max. 15 V bemen˝o feszültségig (tipikusan ipari 12 V) – nagypontosságú órajel a pontos id˝ozítésekhez, aszinkron soros átvitelhez (ezt egy 12 MHz frekvenciájú kvarc biztosítja) – a JTAG kivezetések, 10 pólusú szalagkábel csatlakozón keresztül – a megfelel˝o üzemmódba lépést beállító kapocspár (nyomkövetés vagy normál üzem) – hardver RESET kivezetés a mikrovezérl˝o esetleges kézi újraindításához • Elvárhatjuk, hogy az alkalmazáshoz szükséges alapvet˝o elemek is szerepeljenek a panelen: – (Token Ring–)RS-232 kommunikációt lehet˝ové tev˝o kivezetések és illeszt˝o áramkörök (MAX 232) 11 . . . akkor elmondaná, hogy ma majdnem elégett (ez szó szerint értend˝ o), a gyanta már szenesedett alatta, mégis m˝uködik. Eddig ezt az eszközt kínoztam meg legjobban, és kiválóan bírja.
8
3.4. A PROCESSZOR MODUL
– 10 pólusú szalagkábel csatlakozóra kivezetett ki- és bemenetek: ∗ 2 analóg bemenet ∗ 2 analóg (pontosabban PWM12 ) kimenet ∗ 4 vagy 5 digitális ki-/bemenet (szoftverb˝ol beállíthatók) Az fenti követelményrendszert az következ˝o kapcsolás maradéktalanul kielégíti. Talán az RS-232 illeszt˝o áramkört hiányolhatja az Olvasó: az nem ezen a rajzon kapott helyet, mert már régebben készítettem egy teljesen különálló, 4 pólusú tüskesoron keresztül csatlakoztatható áramkört, így feleslegesnek látom újra megépíteni. Az illeszt˝o rajzát is megtalálhatjuk a processzor modul rajza alatt.
A processzor modul kapcsolási rajza
Lássuk az RS-232 illeszt˝o áramkör kapcsolási rajzát is!
TTL – RS-232 illeszt˝o
Ezzel teljessé vált a processzor modul rajza. A következ˝o lista azt tartalmazza, hogy a kapcsolási rajz egyes elemei milyen funkciókat látnak el: 12 Impulzusszélesség-moduláció
9
3.4. A PROCESSZOR MODUL
• LPC210x: maga a mikrovezérl˝o. Látható, hogy több tápfeszültség-bemenettel rendelkezik, az azonos nev˝uek a tokozáson belül össze vannak kötve, ezért azok közül elegend˝o egyet bekötni • RESET: ez a jel a mikrovezérl˝ot alapállapotba állítja; a csatlakozó közvetlenül a mikrovezérl˝o RST bemenetére van kötve. A vezetéken megjelen˝o logikai alacsony szint újraindítja a mikrovezérl˝ot, ezért egy megfelel˝o érték˝u felhúzóellenállással gondoskodnunk kell arról, hogy normál üzemben logikai magas szinttel lássuk a mikrovezérl˝ot • JTAG csatlakozón keresztül tölthetjük a mikrovezérl˝o programját az eszközbe, és ezen keresztül végezhetjük el a program tesztelését is. Még számos hasznos funkciója van, ezzel a kés˝obbi fejezetekben ismerkedünk meg részletesen • Q1, C1 és C2: a mikrovezérl˝o órajel-generátorának küls˝o elemei, ezek határozzák meg az órajel frekvenciáját • RS-232 csatlakozó: a soros port jelilleszt˝o áramkörét csatlakoztathatjuk ide. A mikrovezérl˝o P0.0-ás lába a TXD13 , míg a P0.1-es láb az RXD14 . Err˝ol a tüskesorról csak 3,3 V-ot tudunk az illeszt˝o számára biztosítani, de a tapasztalat azt mutatja, hogy ez is elegend˝o a megfelel˝o m˝uködéshez • R2-es ellenállás: nagyon fontos. Ha a P0.14-es lábon alacsony logikai szint van a mikrovezérl˝o újraindításakor, akkor mindenképpen a beépített BOOT15 kód indul el, és a felhasználói program (a mi programunk) nem tud lefutni. Ezért ezt a lábat mindig logikai „1” szinten kell tartani, erre szolgál az ellenállás. Értéke 10 − 33 kΩ • GPIO16 csatlakozó: az analóg és digitális vezérl˝o kimenetek és bemenetek • DEBUG csatlakozó: a nyomkövet˝o üzemmód kiválasztására használatos kivezetések. Ha a mikrovezérl˝o újraindításakor a DBGSEL lábon logikai alacsony szint van, akkor a JTAG interfész használhatóvá válik. Magas szint esetén a mikrovezérl˝o összes portja használható, mint „közönséges” I/O port • Stabilizátor áramkörök (jobb fels˝o sarok): a mikrovezérl˝o m˝uködéséhez szükséges 3,3 V-os és 1,8 V-os tápfeszültséget szolgáltatják. Mindkét stabilizátor IC h˝omérséklet-kompenzált, és túláramvédelemmel rendelkezik Végezetül álljon itt mindkét áramkör nyomtatott áramköri terve. Természetesen, ha az Olvasó utánépíti az áramkört, nem kell a két áramkört külön-külön elkészítenie, nyugodt szívvel egyesítheti a két kapcsolási rajzot és NYÁK17 -tervet.
Processzor modul NYÁK-terve 13 Adat
kimenet bemenet 15 Gyári programozó szoftver 16 General Purpose Input-Output – általános célú bemenet-kimenet 17 Nyomtatott áramkör 14 Adat
10
˝ VÉGREHAJTÓ ÉS BEAVATKOZÓ SZERVEK 3.5. ÉRZÉKELO,
TTL – RS-232 illeszt˝o NYÁK-terve
3.5.
Érzékel˝o, végrehajtó és beavatkozó szervek
Önmagában a processzor modul értéktelen, mert ugyan ki tudja adni az utasítást, de semmi sincs, ami végrehajtaná azt. Az utasítás végrehajtásáért a végrehajtó szerv a felel˝os, ilymódon m˝uködtetve a beavatkozó szervet. Ebben a részben azzal ismerkedünk meg, hogy miképp lehet végrehajtó szervet illeszteni a mikrovezérl˝ohöz. Ugyancsak fontos kérdés, hogy milyen információ alapján adja ki a mikroprocesszor az utasítást a végrehajtó szervnek. Ha a külvilágból vett mérési eredmények, logikai értékek alapján, akkor szükségünk lesz valamire, ami érzékeli a külvilágban bekövetkezett változásokat. Erre valók az érzékel˝o szervek. Ezek illesztését is megismerjük a fejezet végére.
3.5.1.
12 V-os izzó vezérlése digitális és analóg (PWM) jellel
Az egyik legegyszer˝ubb végrehajtó szerv az elektronikus kapcsoló. Bemenete fogadja a processzortól érkez˝o jelet, és ett˝ol függ˝oen megváltoztatja a terhelése áramát vagy feszültségét. Mivel a mikroprocesszor nem rendelkezik (a hagyományos értelemben vett) digitális–analóg átalakítóval, ezért a mikrovezérl˝o PWM kimenetét használjuk a kapcsolandó áram értékének beállítására. Ez azt is jelenti, hogy az elektronikus kapcsoló mindig digitális jelet kap: néha folyamatosan be lesz kapcsolva, néha pedig csak 1-2 ms-ra (majd 1-1 ms-ra kikapcsoljuk, aztán megint be 1-2 ms-ra, aztán ki. . . ). A ki-be kapcsolgatás idejének arányával (vagyis az áramimpulzus szélességének megváltoztatásával = PULSE WIDTH MODULATION) állíthatjuk be a terhelésen átfolyó áram ÁTLAGértékét. Szerencsére a mikrovezérl˝o rendelkezik hardveres PWM támogatással, így igazából nekünk semmit sem kell kapcsolgatni, megteszi azt helyettünk egy megfelel˝oen beállított számláló. Elektronikus kapcsolóként egy MOSFET-et fogunk használni. Ennek el˝onye, hogy alacsony18 frekvencián nem igényel vezérl˝o teljesítményt, emellett akár 28 A-es áramot is képes károsodás nélkül kapcsolni. A választás az IRL 540-es típusra esett, mert ez már 3,3 V kapufeszültség hatására is kinyit. A típusjelzésben az „L” bet˝u arra utal, hogy logikai feszültséggel is vezérelhet˝o. A FET-en kívül már csak véd˝oelemek és csatlakozók találhatók az áramkörben. El˝obbiek feladata, hogy megvédjék a FET-et a mikrovezérl˝ot˝ol és a mikrovezérl˝ot a FET-t˝ol.
3.5.2.
Fényérzékel˝ok, fotodiódák
Ebben a fejezetben egy fényérzékel˝o elemet, vagy ha úgy jobban tetszik: egy egy képpontból álló szemet építünk a mikrovezérl˝o számára. Ez már egy kicsit bonyolultabb, mint a FET-es kapcsoló volt, hiszen ebben er˝osít˝o elemet kell elhelyeznünk a megfelel˝o érzékenység és linearitás elérése céljából. Láthatjuk, hogy semmi olyan elemet nem tartalmaz az áramkör, amit „otthon, a ház körül ne találnánk meg”. Remélem elnézi nekem az Olvasó, de az elektronikus kapcsoló olyan egyszer˝u – már-már primitív –, hogy nem szándékozom külön nyomtatott áramköri lapon elhelyezni a két áramkört, ezért most adom meg az egyesített elvi rajzot és NYÁK-tervet. Az egybeépítésnek megvan a maga el˝onye: a fényérzékel˝o fotodióda közvetlen kapcsolatban van a lámpával, így leegyszer˝usíthet számos mérést (könnyen végezhetnénk tranziens állapotbeli vizsgálatot, transzfer karakterisztika felvételt; hozzáteszem, hogy ez utóbbi mérést egy célszer˝uen kialakított, passzív hálózattal fogjuk elvégezni a 18 néhány
100 kHz
11
˝ VÉGREHAJTÓ ÉS BEAVATKOZÓ SZERVEK 3.5. ÉRZÉKELO,
könnyebb ellen˝orizhet˝oség kedvéért).
A végrehajtó-érzékel˝o modul kapcsolási rajza
A végrehajtó-érzékel˝o modul NYÁK-terve
Láthatjuk, hogy az érzékel˝o a következ˝o részekb˝ol épül fel: • fényérzékel˝o dióda (D5): ennek feladata, hogy a szivárgóáramát (ami eredetileg haszontalan, s˝ot káros) a beérkez˝o fotonok számával arányosan változtassa. A szivárgóáram értékéb˝ol következtetni tudunk a fényer˝osségére • áram-feszültség átalakító: a fotodióda szivárgóáramát alakítja mérhet˝o feszültséggé. Ez egy elektronikai alapkapcsolás, bármely szakkönyvben megtalálható. Érdekessége, hogy a kimeneti feszültségszintet (offszetet) egy potenciométerrel lehet beállítani (R2). Ez egyúttal a dióda el˝ofeszítését is változtatja. Mivel a dióda áramgenerátoros üzemmódban dolgozik, ez mit sem befolyásol m˝uködésén. Az R1-es potenciométer teremt kapcsolatot a szivárgóáram és a kimeneti feszültség között a már jól ismert Ohm törvény szerint: Uki = Isz · R1 ahol Isz a szivárgóáram értéke, és R1 a potenciométer ellenállása
12
˝ VÉGREHAJTÓ ÉS BEAVATKOZÓ SZERVEK 3.5. ÉRZÉKELO,
• ehhez persze hozzá kell számolni az offszet feszültség értékét, és azt, hogy a kimeneten egy 6,2 V-os Zénerdiódás szinteltoló is helyet kapott. Ez a szinteltoló és határoló az utolsó fokozat. Kimenete közvetlenül a mikrovezérl˝o bemenetére csatlakozik Ezzel elérkeztünk a hardver leírás végéhez, a következ˝o fejezetben a szoftver háttérrel fogunk megismerkedni.
13
4. fejezet
Fejleszt˝oi környezet Ebben a fejezetben arról lesz szó, hogy milyen szoftverkomponensek szükségesek a mikrovezérl˝o programjának (továbbiakban: firmware) és a személyi számítógép felhasználói programjának (továbbiakban: szoftver) el˝oállításához. Mivel a számítógép és a mikrovezérl˝o egyaránt 32 bites, vagyis az egy lépésben kezelhet˝o adatok 32 bitesek, ezért a forráskódok – ott, ahol ez lehetséges – megegyeznek egymással (els˝osorban header fájlok, kommunikációs rutinok). A XXI. században már nem vagyok hajlandó assembly nyelven programozni, ezért a programokat C fordítóprogram segítségével fogjuk elkészíteni. Szerencsére a GNU1 prodzsekt GCC2 -je fordít Intel ia32 és ARM architektúrára, így ugyanazt a fordítóprogramot használhatjuk mindkét esetben. A gcc szabad szoftver, szabadon beszerezhet˝o a http://www.gnu.org/gcc oldaltól. Célszer˝u a forráskódját letölteni, és azt lefordítani a célgépre. Ha így teszünk, szükségünk lesz a binutils3 programcsomagra, töltsük le azt is. Az elkészült programot valahogyan a mikrovezérl˝obe kell juttatni. Erre szolgál az OpenOCD4 . Természetesen ez is letölthet˝o az internetr˝ol. A bevezet˝o után nézzük meg röviden a felhasznált programok telepítését és használatát!
4.1.
C fordító Linuxos PC-re
Ha az Olvasó is Linuxot használ, akkor egyszer˝u dolga van, hiszen minden Linux disztribúciónak5 része a gcc6 . Ez a fordítóprogram a személyi számítógép számára készít futtatható állományokat, tehát a felhasználói programunk elkészítéséhez fog hozzájárulni. Telepítése rendkívül egyszer˝u, Debian alapú Linux disztribúciók esetén (Pl. Ubuntu) nem tartogat semmilyen meglepetést. Léteznek különböz˝o grafikus telepít˝oprogramok, mi viszont a legegyszer˝ubb módszerrel installáljuk fordítóprogramunkat: $ sudo apt-get install gcc make Ezzel a paranccsal utasítást adtunk a számítógépünknek, hogy töltse le és telepítse a C fordítót. Ha minden rendben megy, akkor néhány másodperc múlva ki is próbálhatjuk: $ gcc gcc: no input files A második sor a gcc válasza, vagyis egy hibaüzenet: nem adtunk meg forrásfájlt, amit fordíthatna. Ha eddig minden rendben történt, akkor nem lesz problémánk a számítógép programjának elkészítésével. Fontos azt is tudni, hogy ezzel a C fordítóval fogjuk a gcc-t lefordítani úgy, hogy az új fordító (keresztfordító) az ARM processzor számára generáljon futtatható állományt, ezért ennek megfelel˝o m˝uködése alapvet˝o fontosságú. 1 GNU’s
Not UNIX – a GNU nem UNIX Compiler Collection – GNU fordítógy˝ujtemény 3 Assembler, linker, objcopy, objdump és még sok hasznos program 4 Open On-Chip Debug 5 Terjesztéssel 6 C, C++, objektív C, java, fortran, ADA fordító 2 GNU
14
4.2. C FORDÍTÓ ARM PROCESSZORRA
4.2.
C fordító ARM processzorra
A már el˝oz˝oleg letöltött binutils és gcc forrást le kell fordítani. Ez nem túl bonyolult: • csomagoljuk ki a tömörített állományt: tar xjvf binutils-2.17.tar.bz2 • lépjünk be a binutils-2.17 könyvtárba: cd binutils-2.17 • végezzük el a konfigurálást: ./configure --target=arm-elf --prefix=/usr/local/arm • fordítsuk le a forrást: make (ez néhány percig tart) • telepítsük fel: sudo make install • csomagoljuk ki a másik tömörített állományt is: tar xjvf gcc-4.0.3.tar.bz2 • lépjünk be a gcc-4.0.3 könyvtárba, hozzuk létre az armgcc könyvtárat, és lépjünk bele: cd gcc-4.0.3; mkdir armgcc; cd armgcc • végezzük el a konfigurálást: export PATH+=/usr/local/arm/bin; ../configure --prefix=/usr/local/arm \ --target=arm-elf --enable-languages=c • fordítsuk le a forrást: make (ezután menjünk el teázni. . . ) • telepítsük fel: sudo make install A fordítás és telepítés után a keresztfordítónk (ami már ARM architektúrára fordít) a /usr/local/arm/bin könyvtárban található, és arm-elf-gcc névre hallgat. Ha ezzel is megvagyunk, akkor már majdnem tudunk programot írni ARM mikrovezérl˝ore. Egyetlen dolog, ami még ett˝ol elválaszt, az a mikrovezérl˝o regisztereinek nevét és helyét tartalmazó header fájl (mert memóriacímmel mégsem hivatkozhatunk a regiszterekre). A C fordítónak mindegy, de az emberek nem tudnak 32 bites számokat megjegyezni, ezért a 32 bites számokat (memóriacímeket) névvel látták el, amit már meg tudunk jegyezni. Ezek listáját tartalmazza a header fájl, nevezetesen az lpc2103.h Egy rövid idézet a fájlból:
/* Fast #define #define #define #define #define
General Purpose Input/Output (GPIO) */ FIODIR (*((volatile unsigned long *) FIOMASK (*((volatile unsigned long *) FIOPIN (*((volatile unsigned long *) FIOSET (*((volatile unsigned long *) FIOCLR (*((volatile unsigned long *)
0x3FFFC000)) 0x3FFFC010)) 0x3FFFC014)) 0x3FFFC018)) 0x3FFFC01C))
/* Memory Accelerator Module (MAM) */ #define MAMCR (*((volatile unsigned char *) 0xE01FC000)) #define MAMTIM (*((volatile unsigned char *) 0xE01FC004)) #define MEMMAP (*((volatile unsigned char *) 0xE01FC040)) /* Phase Locked Loop (PLL) */ #define PLLCON (*((volatile #define PLLCFG (*((volatile #define PLLSTAT (*((volatile #define PLLFEED (*((volatile
unsigned unsigned unsigned unsigned
char *) char *) short*) char *)
0xE01FC080)) 0xE01FC084)) 0xE01FC088)) 0xE01FC08C))
Természetesen a header fájl is letölthet˝o az internetr˝ol, s˝ot le is töltend˝o, hiszen els˝o dolgunk lesz minden programunkban, hogy beidézzük azt az els˝o sorban:
15
˝ ÉS NYOMKÖVETO ˝ PROGRAM 4.3. AZ OPENOCD – FELTÖLTO
#include "lpc2103.h"
Ha megszereztük a header fájlt is, akkor készen állunk, hogy megkezdjük a fejlesztést! M˝uköd˝o, teljes példákat a „Felhasználási területek” fejezetben talál az Olvasó. Ott részletesen leírok minden lépést, a forráskód megírásától a tesztelésig és a futtatásig.
4.3.
Az OpenOCD – feltölt˝o és nyomkövet˝o program
Az OpenOCD egy nyílt forráskódú program az ARM alapú mikrovezérl˝ok programjának feltöltésére és a program futásának ellen˝orzésére. Töltsük le az aktuális stabil vagy fejleszt˝oi verziót, és fordítsuk le az alábbiak szerint: • csomagoljuk ki a tömörített állományt: tar xjvf OpenOCD.tar.bz2 • lépjünk be a trunk könyvtárba: cd trunk • végezzük el a konfigurálást: ./bootstrap; ./configure \ --enable-parport vagy --enable-parport --enable-parport_ppdev Ez utóbbi (két) argument azért szükséges, mert alapértelmezés szerint nincs engedélyezve a párhuzamos (nyomtató) porton való kommunikáció. USB-s JTAG interfész esetén telepítsük fel a libusb és libftdi függvénykönyvtárakat is, majd a konfigurálásnál használjuk a --enable-ft2232_libftdi argumentumot (is)! • fordítsuk le a forrást: make (ez néhány másodpercig tart) • telepítsük fel: sudo make install A „kész” program a /usr/local/bin/openocd nevet viseli. Az OpenOCD-t úgy tervezte a programozója, hogy telnet7 program segítségével lehessen irányítani. Ez azért hasznos, mert a világ bármely pontjáról elérjük mikrovezérl˝onk programozó szoftverét. A másik érdekessége az OpenOCD-nek, hogy össze lehet kapcsolni a gdb-vel8 , így közvetlenül a C forrásunk segítségével ellen˝orizhetjük a mikrovezérl˝ot és a lefordított–feltöltött programot. Ez bizony nagyon nagy segítség bonyolultabb algoritmusok esetén. De nézzük el˝oször, hogy miként tudjuk a C fordítóval el˝oállított programunkat áttölteni a mikrovezérl˝obe. Ehhez el kell indítani az openocd-t. Az openocd egy szöveges konfigurációs fájlból veszi, hogy milyen paraméterekkel induljon. Egy lehetséges konfigurációs fájl így néz ki: telnet_port 4444 gdb_port 3333 daemon_startup reset interface parport parport_port 0x378 parport_cable hg8lhs jtag_speed 0 reset_config trst_and_srst jtag_device 4 0x1 0xf 0xe target arm7tdmi little run_and_halt 0 arm7tdmi-s_r4 working_area 0 0x40000000 0x2000 nobackup flash bank lpc2000 0x0 0x20000 0 0 lpc2000_v1 0 12000 calc_checksum 7 Távoli 8 GNU
belépés debugger – GNU nyomkövet˝o (hibamentesít˝o)
16
˝ ÉS NYOMKÖVETO ˝ PROGRAM 4.3. AZ OPENOCD – FELTÖLTO
Az els˝o két sor a telnet és a gdb hálózati TCP portját adja meg. Aztán beállítjuk, hogy az openocd indulásakor mi történjen: ebben az esetben alaphelyzetbe állítja a mikrovezérl˝ot. Ezt követi a programozó interfész megadása. Példánknál maradva ez a párhuzamos port, ami a 0x378-as I/O porton van. A kábel típusa „hg8lhs” lesz, vagyis egy általam definiált kiosztás. A párhuzamos portra csatlakozó JTAG9 programozó sebessége lehet 0 (valójában nincs szó semmilyen programozóról, így a sebesség értéke nem befolyásol semmit, de szintaktikailag szükséges). Beállítjuk a RESET viselkedést is: mivel nekünk van TRST10 és SRST11 kivezetésünk is, ezért mindkett˝ore igényt tartunk. A következ˝o sorban azt határozzuk meg, hogy a legalacsonyabb JTAG szinten milyen hosszú a parancs regiszter, és milyen parancs szolgál az eszköz azonosítására. A processzor megadása kötött formát követ. A FLASH memória definiálása sem hagy sok lehet˝oséget a változtatásra, egyedül a „12000” értéket tudjuk módosítani, az ugyanis a kvarckristály frekvenciája kHz mértékegységben.
Mentsük ezt egy „config” nev˝u fájlba, erre fogunk majd az openocd indításakor hivatkozni. Csatlakoztassuk a mikrovezérl˝ot12 , zárjuk rövidre a DBGSEL kapcsolót (anélkül nem lép DEBUG13 üzemmódba), indítsuk újra a mikrovezérl˝ot a RESET átkötéssel, és adjuk ki a következ˝o parancsot:
$ sudo openocd -f config Info: openocd.c:86 main(): Open On-Chip Debugger (2007-05-30 17:45 CEST) Amennyiben üdvözl˝o szöveg után az OpenOCD nem tér vissza hibaüzenettel, akkor minden sikeres volt. Ha nem ezt látjuk, akkor egy üzenet tájékoztat minket a hiba okáról. Ennek oka többnyire a mikrovezérl˝o tápfeszültségének hiánya, a DBGSEL átkötés rövidre nem zárása, esetleg hibásan kivitelezett JTAG kábel. Ezeken kívül persze a hibák száma végtelen, de hely hiányában nem tudom az összes hibát (és azok megoldását) tételesen felsorolni. Innent˝ol kezdve két dolgot kell tehetünk: a telnet program segítségével belépünk az openocd-be, majd elindítjuk a gdb-t. Ez utóbbi lépés nem feltétlenül szükséges, véleményem szerint az openocd több lehet˝oséget biztosít a hibafelderítésre, ezért a gdb használatát még röviden sem tárgyalom. Ezt a feladatot az Olvasóra bízom.
$ telnet localhost 4444 Trying 127.0.0.1... Connected to localhost. Escape character is ’^]’. Open On-Chip Debugger > > flash banks #0: lpc2000 at 0x00000000, size 0x00020000, buswidth 0, chipwidth 0 > flash info 0 #1: lpc2000 at 0x00000000, size 0x00020000, buswidth 0, chipwidth 0 #0: 0x00000000 (0x2000kB) erase state unknown, protected #1: 0x00002000 (0x2000kB) erase state unknown, protected #2: 0x00004000 (0x2000kB) erase state unknown, protected #3: 0x00006000 (0x2000kB) erase state unknown, protected #4: 0x00008000 (0x2000kB) erase state unknown, protected #5: 0x0000a000 (0x2000kB) erase state unknown, protected #6: 0x0000c000 (0x2000kB) erase state unknown, protected #7: 0x0000e000 (0x2000kB) erase state unknown, protected #8: 0x00010000 (0x2000kB) erase state unknown, protected #9: 0x00012000 (0x2000kB) erase state unknown, protected 9 Joint
Test Action Group – általános célú feltölt˝o, ellen˝orz˝o és hibaelhárító hardver és a hozzá tartozó protokoll RESET – cél alaphelyzetbe állítás 11 System RESET – rendszer alaphelyzetbe állítás, ez a teljes áramkört újraindítja 12 A JTAG kábel bekötése a kapcsolási rajz bal alsó sarkában található. Az OpenOCD párhuzamos porti kiosztása a trunk/src/jtag/parport.c fájlból deríthet˝o ki, de mellékletként is megtalálható a dolgozathoz csatolva 13 Nyomkövetés 10 Target
17
4.4. ADATÁTVITEL MEGVALÓSÍTÁSA SOROS PORTON KERESZTÜL
#10: 0x00014000 (0x2000kB) erase state unknown, protected #11: 0x00016000 (0x2000kB) erase state unknown, protected #12: 0x00018000 (0x2000kB) erase state unknown, protected #13: 0x0001a000 (0x2000kB) erase state unknown, protected #14: 0x0001c000 (0x2000kB) erase state unknown, protected #15: 0x0001e000 (0x2000kB) erase state unknown, protected lpc2000 flash driver variant: 1, clk: 12000 > poll target state: halted target halted in ARM state due to debug request, current mode: System cpsr: 0x000000df pc: 0x00000474 > Az id˝onként felbukkanó prompthoz14 a következ˝o parancsokat gépelhetjük be: targets flash banks flash info 0 flash erase 0 0 7 flash write 0 program.bin 0 armv4_5 disassemble 0 30 reg poll halt resume cím step cím reset soft_reset_halt mdw, mdh, mdb mww, mwh, mwb dump_image data.bin 0x40000000 2048
listát ad az elérhet˝o eszközökr˝ol. Ebb˝ol úgyis csak egy lesz, az „arm7tdmi (little endian)” információt ad az elérhet˝o FLASH bankokról (memóriaterületekr˝ol) információt ad a 0. bankról törli a 0. bank els˝o 8 szektorát (0–7-ig) a „program.bin” lefordított programot a 0. bankba írja 0 kezd˝ocímmel az els˝o 30 utasítást visszafordítja binárisról assembly nyelvre listát készít a regiszterek aktuális állapotáról megmutatja a processzor állapotát (fut-e vagy áll valahol) leállítja a program futását folytatja a program futását (cím címt˝ol) végrahajtja a következ˝o utasítást (a cím címt˝ol kezdve) alapállapotba állítja a processzort, és elindítja a program végrehajtását alapállapotba állítja a processzort, de felfüggeszti a program végrehajtását. A 0. memóriacímnél várakozik. memóriaterületeken található szavak (32 bites), félszavak (16 bites) és bájtos adatok értékének listázása memóriaterületeken található szavak (32 bites), félszavak (16 bites) és bájtos adatok értékének módosítása RAM tartalom (pl. mérési adatok) mentése a „data.bin” nev˝u fájlba
A felsorolt parancsokkal azután könnyedén tölthetünk fel programot, futatthatjuk azt, vagy végrehajthatjuk lépésenként, a regiszterek és memóriatartalmak értékének ellen˝orzése mellett. Használatuk némi gyakorlatot igényel, de néhány óra alatt jártasságot szerezhetünk benne.
4.4.
Adatátvitel megvalósítása soros porton keresztül
Ahhoz, hogy a számítógép, mint mérésvezérl˝o utasításokat adhasson a mikrovezérl˝onek, szükséges, hogy valamiféle adatátvitel jöjjön létre a két eszköz között. Ennek egyik legegyszer˝ubb, és ezért legelterjedtebb módja az RS-232 aszinkron soros átvitel. Aszinkron, mert nem visszük át az órajelet, így azt mindkét fél maga állítja el˝o az adatfolyam alapján, másrészt soros, mert a bitek nem egyszerre, hanem egymás után kerülnek át egyik gépr˝ol a másikra. A hardvert leíró részben már láttuk, hogy rendkívül egyszer˝u összekábelezni a két berendezést: az egyik készülék TDX lábát a másik RXD lábára kell kötni, és viszont. Természetesen szükségünk lesz egy földvezetékre, így összesen 3 ér szükséges a kommunikációhoz. 14 Készenléti
jel, „>”
18
4.4. ADATÁTVITEL MEGVALÓSÍTÁSA SOROS PORTON KERESZTÜL
Az adatátvitel paramétereit (sebesség, adatbitek száma, hibaellen˝orzés) szoftveres úton állíthatjuk be: az ipari gyakorlatnak megfelel˝oen válasszuk a 9600 bit/sec sebesség˝u kommunikációt 8 bit adathosszal, hibaellen˝orzés nélkül és 1 stop bittel (9600 8N1). A következ˝o programrészlet azt mutatja be, hogy egy Linux-os gépen mindezt hogyan tehetjük meg a legegyszer˝ubben: FILE *setup_usart0(char *tty_name) { FILE *tty; // Soros port fájl azonosítója struct termios term; // Beállítások tty = fopen(tty_name, "r+");
// Port megnyitása
if (tty == NULL) { return NULL; }
// Hiba esetén üzenet a felhasználónak
tcgetattr(fileno(tty), &term); // Beállítások lekérdezése cfsetispeed(&term, B9600); // Vétel sebessége: 9600 bit/sec cfsetospeed(&term, B9600); // Adás sebessége: 9600 bit/sec term.c_cflag &= ~(CSIZE|PARENB); // Beállítások módosítása term.c_cflag |= CS8; // 8 adatbit, nincs h. e. tcsetattr(fileno(tty), TCSANOW, &term); // Beállítások mentése return tty;
// Fájl leíró visszaadása
} Mivel egy Linux-os gépen minden eszköz egy fájl, ezért a soros portot is úgy kezeljük, mint egy fájlt. Írhatunk bele, olvashatunk bel˝ole. . . Amit írunk, az megjelenik a soros port kimenetén, és átkerül a másik eszköz vételi regiszterébe. Amit pedig olvasunk, az a soros porton keresztül érkezett egy másik eszközt˝ol.
Adat küldése Linux alatt: fwrite(packet, sizeof(struct Packet), 1, fflush(tty);
tty);
Adat fogadása: fread(packet, sizeof(struct Packet), 1,
tty);
Ez kevés a kommunikációhoz, hiszen még csak bájtokat vittünk át, tehát következ˝o lépésként a bájtokból meghatározott formátumú csomagot kell készíteni, ami tartalmazza a forrás és a célállomás azonosítóját, a parancsot és az adatokat. Err˝ol részletesen a következ˝o fejezetben olvashatunk. Azt se felejtsük el, hogy az adatátvitel mindig legalább két fél között jön létre (hacsak nem adattárolásról van szó, mert akkor a két fél többnyire azonos, csak id˝oben eltolva), tehát meg kell ismerkednünk a mikrovezérl˝o soros kommunikációs interfészének programozásával is. Ez sem sokkal összetettebb, hiszen pontosan ugyanazokat a beállításokat kell alkalmazni (különben nem jön létre a kapcsolat). Mivel a mikrovezérl˝on nem fut operációs rendszer, ami fájlként emulálja a soros portot, ezért itt más szemléletet kell alkalmaznunk. Létezik egy adó regiszter, ha ebbe írunk, akkor azt a hardver automatikusan elküldi az ellenállomásnak, a vev˝o regiszterben pedig az ellenállomástól vett adatot találjuk. Lássunk példát a paraméterek beállítására! void setup_usart0(unsigned int divisor) { if (divisor == 0) divisor = 20; /* Alapértelmezés: 9600 bit/sec 12 MHz-nél */ PINSEL0 &= ~0xf; PINSEL0 |= 0x5;
/* TXD és RXD lábak az UART-hoz rendelve */ 19
4.5. A KOMMUNIKÁCIÓ PROTOKOLLJA ÉS ANNAK MEGVALÓSÍTÁSA C NYELVEN
U0FCR = 0x7; U0LCR = 0x83; U0DLL = divisor; U0DLM = 0; U0LCR = 0x3;
/* Átmeneti puffertár engedélyezése */ /* 8 bites szóhossz engedélyezése, nincs hibaellen˝ orzés, 1 stop bit */ /* 20_dec ----> BITSEBESSÉG = 9373 bit/sec [hiba = 2.34%] */ /* Bitsebesség: 9600 bit/sec */ /* Adó-vev˝ o engedélyezése */
} Mint már említettem, bájtok küldése és fogadása egy-egy regiszter írásával, illetve olvasásával érhet˝o el. Erre mutat példát az alábbi kódrészlet:
Adat küldése mikrovezérl˝ovel: while(!(U0LSR & (1 << 6))); U0THR = packet_ptr[c];
// Várakozás, amíg foglalt a vonal // Adat beírása az átmeneti tárba
Adat fogadása mikrovezérl˝ovel: while(!(U0LSR & 1)); packet_ptr[c] = U0RBR;
4.5.
// Várakozás a bejöv˝ o adatra // Adat olvasása az átmeneti tárból
A kommunikáció protokollja és annak megvalósítása C nyelven
Most, hogy már tudunk 1 bájtot küldeni és fogadni, ideje, hogy a bájtokat csomagba szervezzük. Ez azért fontos, mert egy bájt nem elegend˝o minden információ célba juttatására, ezért a teljes adatmennyiséget több bájt segítségével visszük át. Minden bájt meghatározott pozícióban helyezkedik el, és mindegyik meghatározott feladatot lát el. Ahhoz, hogy pontosan megértsük a rendszer m˝uködését, meg kell értenünk, hogy hogyan néz ki egy TokenRing– RS-232 hálózat.
TokenRing–RS-232 hálózat topológiája
A gy˝ur˝u topológiában elhelyezett állomások egyenrangúak, kivéve egyet, ami egyenrangúbb a többinél: ez a mérésvezérl˝o. Az állomások azonosítóval rendelkeznek, a mérésvezérl˝o a 0-s, a többi állomás azonosítóját 1-tól kezdve a mérésvezérl˝o osztja ki a gy˝ur˝u körüljárási irányával megegyez˝o irányban haladva. A gy˝ur˝u lényege, hogy aki megkapja a csomagot, tovább is küldi, legfeljebb „apróbb” módosításokat eszközöl benne. Ez úgy lehetséges, hogy az egyik eszköz kimenete (TXD) a következ˝o eszköz bementére (RXD) van
20
4.5. A KOMMUNIKÁCIÓ PROTOKOLLJA ÉS ANNAK MEGVALÓSÍTÁSA C NYELVEN
kötve. Esetünkben sincs ez másként, de a hálózatunkban mindössze 2 állomás van, így nem is nevezhet˝o igazán hálózatnak. Ett˝ol függetlenül mindent úgy fogunk kezelni, mintha több állomás lenne a gy˝ur˝uben, így kés˝obb több mér˝oberendezést is össze tudunk kötni. A kommunikáció során alkalmazott csomag a következ˝o formátumú: • a célállomás dinamikus azonosítója: ezt az azonosítót minden eszköz ellen˝orzi: – ha nem nulla, akkor nem neki szól a csomag, csökkenti az azonosító értékét, és továbbküldi a teljes csomagot – ha nulla, akkor neki szól a csomag, végrehajtja az utasítást, az azonosító értékét -1-re (0xFF) módosítja (csökkenti, csakúgy, mint a „nem nulla” esetben), és az adat részben válaszüzenetet (nyugta, hibajelzés, mért adat, stb.) helyez el. Ezt követ˝oen továbbküldi a csomagot • a feladó azonosítója: mérésvezérl˝o osztja ki a gy˝ur˝u felépítése során, a kommunikáció ideje alatt változhat (a mérésvezérl˝o mindig a 0-s azonosítójú) • az utasítás 8 bites kódja • ellen˝orz˝o összeg: értékét úgy kell kiszámolni, hogyha csomag bájtjait (8 db) összeadjuk, és elosztjuk 256-tal, akkor a maradék éppen 0-t adjon • 4 darab adatbájt, ami kezelhet˝o 1 darab 32 bites számként, vagy akár külön-külön is (mi az el˝obbi megoldást fogjuk alkalmazni) Lássuk, hogyan néz ki a csomagot leíró C struktúra! struct Packet { unsigned char unsigned char unsigned char unsigned char unsigned int
dst_id; src_id; command; chsum; data;
// Mivel az ia32 és az ARM is little-endian // (beállítástól függ˝ oen), ezt megtehetem
} A teljesség kedvéért definiálnunk kell még a command mez˝o lehetséges értékeit és paramétereit. Az els˝o oszlop az utasítás számmal kifejezett értéke, a második a neve, a harmadik a paraméter neve vagy értéke, míg a negyedik az utasítás rövid leírása. Érték 0 1 2
Parancs neve COMM_HELLO COMM_SET_ID COMM_GET_IOMASK
Paraméter
3
COMM_SET_IOMASK
maszk
4 5 6
COMM_GET_DIGIO COMM_SET_DIGIO COMM_GET_ANALOG
7
COMM_SET_ANALOG
válasz: státusz érték bemenet azonosító válasz: analóg érték MSB: kimenet azonosító periódus « 16 + érték
azonosító válasz: maszk
A parancsok kódja
Az el˝obbi táblázatnak megfelel˝o C fájl (command.h) tartalma: 21
Leírás üres üzenet a gy˝ur˝u feltérképezéséhez állomás dinamikus azonosítójának beállítása annak lekérdezése, hogy melyik láb kimenet és melyik bemenet (1: kimenet, 0: bemenet, bit pozíció a mikrovezérl˝o eredeti kiosztását követi) annak beállítása, hogy melyik láb a kimenet, és melyik bemenet digitális bemenetek állapotának lekérdezése digitális kimenetek állapotának beállítása analóg bemenet állapotának lekérdezése analóg kimenet állapotának beállítása érték: PWM magas (H) részének hossza
4.5. A KOMMUNIKÁCIÓ PROTOKOLLJA ÉS ANNAK MEGVALÓSÍTÁSA C NYELVEN
#ifndef __COMMMANDS_H__ #define __COMMANDS_H__ #define #define #define #define #define #define #define #define
COMM_HELLO COMM_SET_ID COMM_GET_IOMASK COMM_SET_IOMASK COMM_GET_DIGIO COMM_SET_DIGIO COMM_GET_ANALOG COMM_SET_ANALOG
0 1 2 3 4 5 6 7
#endif A csomagorientált átvitelhez szükségünk lesz csomagot küld˝o és fogadó C függvényekre. Ezek alapvet˝oen nem bonyolultak, viszont a személyi számítógép és a mikrovezérl˝o ezen a szinten már annyira különbözik egymástól, hogy célszer˝unek látszott külön-külön algoritmusokat létrehozni. Nézzük el˝oször a személyi számítógép függvényeit! A soros port átviteli paramétereit beállító programrésszel már megismerkedtünk, s˝ot az adatküldés és fogadás is ismert el˝ottünk. A következ˝o lépés a teljes csomagot elküldeni képes kód megalkotása. A forrásprogramot igyekeztem megjegyzésekkel ellátni, ez segít a m˝uködés megértésében.
int send_packet(FILE *tty, struct Packet *packet) { // 8 bájtnyi adat "írása" a soros portot kezel˝ o fájlba fwrite(packet, sizeof(struct Packet), 1, tty); // Átmeneti tárak ürítése fflush(tty); // Egy kis üzenet a felhasználónak, könnyíti a hibakeresést printf("\n\nKÜLDÖTT ------------------------\n"); printf("Cél:\t\t%02X\n", packet->dst_id); printf("Forrás:\t\t%02X\n", packet->src_id); printf("Parancs:\t%02X\n", packet->command); printf("Összeg:\t\t%02X\n", packet->chsum); printf("Adat:\t\t%08X", packet->data); fflush(stdout); return 0; A küld˝o függvény egyszer˝uen csak kiírja a teljes – 8 bájtos – csomagot a kimenetre. A felhasználónak kell gondoskodnia arról, hogy a csomag minden mezeje valós információt tartalmazzon! A csomag fogadását végz˝o függvény sajnos már nem ennyire egyszer˝u, hiszen ennek a hibaellen˝orzést is el kell végeznie. Feladata továbbá, hogy a nem nekünk szóló csomagokat továbbküldje a következ˝o állomásnak (a cél-azonosító értékének csökkentése után), valamint dobja el a válaszként érkezett, tehát a gy˝ur˝un teljesen körbeért csomagot. Az „eldobás” jelen esetben nem azt jelenti, hogy nem foglalkozunk vele, hiszen a válaszüzenet adatot, mérési eredményt tartalmazhat, tehát fel lehet dolgozni. int receive_packet(FILE *tty, struct Packet *packet) { int c; unsigned char *packet_ptr = (unsigned char *) packet; unsigned char sum; // Beolvassuk a következ˝ o 8 bájtos csomagot fread(packet, sizeof(struct Packet), 1, tty);
22
4.5. A KOMMUNIKÁCIÓ PROTOKOLLJA ÉS ANNAK MEGVALÓSÍTÁSA C NYELVEN
// Hibamentesítést segít˝ o üzenetek a felhasználónak printf("\n\nFOGADOTT -----------------------\n"); printf("Cél:\t\t%02X\n", packet->dst_id); printf("Forrás:\t\t%02X\n", packet->src_id); printf("Parancs:\t%02X\n", packet->command); printf("Összeg:\t\t%02X\n", packet->chsum); printf("Adat:\t\t%08X", packet->data); fflush(stdout); // A 8 bájt összege 0 kell, hogy legyen for (c = 0, sum = 0; c < 8; c++) sum += packet_ptr[c]; // Ha tényleg 0 a bájtok összege... if (sum == 0) { printf("\n\nEllen˝ orizve."); // Ha a cél-azonosító már 0-ra csökkent, akkor nekünk // szól a csomag, "jelezni" kell a feldolgozó rutinnak if (packet->dst_id == 0) { printf("\nParancs csomag érkezett."); return 1; } // // // // if
Ha a forrás-azonosító megegyezik a saját (dinamikus) azonosítónkkal, akkor a csomagot mi küldtük, tehát ez egy válasz csomag. Jelezzük ezt is, hátha fontos adatot tartalmaz (packet->src_id == 0) { printf("\nVálasz csomag érkezett."); return 2;
} printf("\nIsmeretlen csomag! Továbbítva..."); // Ha nem mi küldtük, és nem nekünk jött, akkor // csökkentjük a cél-azonosító értékét, és továbbküldjük packet->dst_id--; packet->chsum++; send_packet(tty, packet); // De err˝ ol is jelentést adunk return 3; } else { // Ha ide jutottunk, akkor az ellen˝ orz˝ o összeg hibás: // a csomag hibás, más protokoll került a gy˝ ur˝ ube, // vagy kiestünk a szinkronból. Jobb, ha nem csinálunk // semmit, csak továbbküldjük, de jelezzük a feldolgozó // rutinnak printf("\n\nÉrvénytelen csomag!"); send_packet(tty, packet); return -1; } return 0; }
23
4.5. A KOMMUNIKÁCIÓ PROTOKOLLJA ÉS ANNAK MEGVALÓSÍTÁSA C NYELVEN
Ezeken kívül még egy rövid rutin kapott helyet a forráskódban. Feladata, hogy a csomag (szándékolt) megváltoztatása után kiszámítsa az ellen˝orz˝o összeg új értékét. Valójában ez csak egy rövid ciklus, de nagyon hasznos. void calc_checksum(struct Packet *packet) { unsigned char sum; unsigned char *packet_ptr = (unsigned char *) packet; int c; // Ez a mez˝ o ne "szóljon bele" a végeredménybe packet->chsum = 0; // Összeadjuk a 8 bájtot (valójában csak 7-et) for (c = 0, sum = 0; c < 8; c++) sum += packet_ptr[c]; // Ezek ellentettje az ellen˝ orz˝ o összeg packet->chsum = -sum; } Az el˝obb bemutatott függvények a személyi számítógép szoftverének részét képezik. A mikrovezérl˝o kódja nagyon hasonlóan m˝uködik, hiszen pontosan meghatároztuk, hogy miként m˝uködik a hálózat. Kisebb eltérések mutatkozhatnak a forrásprogramok között, de ezek nem alapvet˝oek. Ennek szemléltetése céljából álljon itt a mikrovezérl˝o hálózatkezel˝o algoritmusainak forráskódja is! int send_packet(struct Packet *packet) { int c; unsigned char *packet_ptr = (unsigned char *) packet; // 8 bájtnyi adat "írása" a soros portot kezel˝ o fájlba for (c = 0; c < 8; c++) { // Várakozunk, amíg az el˝ oz˝ o adat még az átmeneti tárban van while(!(U0LSR & (1 << 6))); // A megfelel˝ o adatbájt küldése U0THR = packet_ptr[c]; } return 0; } A következ˝o kódrészlet a fogadó algoritmust ismerteti. A megjegyzések hasonlósága nem a véletlen m˝uve. int receive_packet(struct Packet *packet, unsigned char my_id) { int c; unsigned char *packet_ptr = (unsigned char *) packet; unsigned char sum; // Beolvassuk a következ˝ o 8 bájtos csomagot for (c = 0; c < 8; c++) { while(!(U0LSR & 1)); packet_ptr[c] = U0RBR; } sum = 0; // A 8 bájt összege 0 kell, hogy legyen for (c = 0; c < 8; c++) 24
4.5. A KOMMUNIKÁCIÓ PROTOKOLLJA ÉS ANNAK MEGVALÓSÍTÁSA C NYELVEN
sum += packet_ptr[c]; // Ha tényleg 0 a bájtok összege... if (sum == 0) { // Ha a cél-azonosító már 0-ra csökkent, akkor nekünk // szól a csomag, "jelezni" kell a feldolgozó rutinnak if (packet->dst_id == 0) return 1; // // // // if
Ha a forrás-azonosító megegyezik a saját (dinamikus) azonosítónkkal, akkor a csomagot mi küldtük, tehát ez egy válasz csomag. Jelezzük ezt is, hátha fontos adatot tartalmaz (packet->src_id == my_id) return 2;
// Ha nem mi küldtük, és nem nekünk jött, akkor // csökkentjük a cél-azonosító értékét, és továbbküldjük packet->dst_id--; calc_checksum(packet); send_packet(packet); return 3; } else { // Ha ide jutottunk, akkor az ellen˝ orz˝ o összeg hibás. // Vagy a csomag hibás, vagy más protokoll került a gy˝ ur˝ ube, // vagy kiestünk a szinkronból. Jobb, ha nem csinálunk // semmit, csak továbbküldjük, de jelezzük a feldolgozó // rutinnak send_packet(packet); return -1; } return 0; } Végezetül lássunk egy lehetséges – de nagyon egyszer˝u – példát az el˝obb ismertetett függvények használatára. Ezek a programok m˝uköd˝oképesek: a hálózatban részt vev˝o állomások számának megállapítására, és a dinamikus azonosítók kiosztására használhatók. Kezdjük a számítógép f˝oprogramjával: #include
#include #include <stdio.h> // A már régebben leírt függvények prototípusa #include "usart.h" // Parancskódok #include "commands.h" // // // //
A hálózat elemeinek számát állapítja meg: Hánnyal csökkent a cél-azonosító A cél-azonosító kezdeti értékének nagynak kell lennie, nehogy valamelyik állomás neki szóló parancscsomagnak értelmezze
int get_chain_length(FILE *tty) { struct Packet packet = {0xff, 0, COMM_HELLO, 1, 0}; 25
4.5. A KOMMUNIKÁCIÓ PROTOKOLLJA ÉS ANNAK MEGVALÓSÍTÁSA C NYELVEN
// El˝ okészített csomag küldése send_packet(tty, &packet); // Válaszcsomag vétele receive_packet(tty, &packet); // A csökkenés mértéke -> állomások száma return 0xff - packet.dst_id; } /*************************************************************/ /* A f˝ oprogram */ /*************************************************************/ int main() { FILE *tty; struct Packet packet; int length, id; // Soros port "megnyitása", paraméterbeállítás if ((tty = setup_usart0("/dev/ttyS0")) == NULL) { perror("controller:setup_usart"); return 1; } // Üzenet a felhasználónak, állomások számának megállapítása printf("\n\nGy˝ ur˝ u hossza: %d", length = get_chain_length(tty)); // Dinamikus azonosítók kiosztása printf("\nAzonosítók kiosztása..."); for (id = 1; id <= length; id++) { // Csomag összeállítása packet.dst_id = id - 1; packet.src_id = 0; packet.command = COMM_SET_ID packet.data = id; // Ellen˝ orz˝ o összeg kiszámítása calc_checksum(&packet); // Csomag küldése send_packet(tty, &packet); // Válasz vétele, igazából nem foglalkozunk vele receive_packet(tty, &packet); } // Soros port "lezárása" fclose(tty); printf("\n\n"); return 0; } A számítógép f˝oprogramja által küldött csomagokat a mikrovezérl˝o f˝oprogramja dolgozza fel: // Hardverfügg˝ o header fájlok #include "lpc2103.h" #include "../LPC210x/library.h" 26
4.5. A KOMMUNIKÁCIÓ PROTOKOLLJA ÉS ANNAK MEGVALÓSÍTÁSA C NYELVEN
#include "commands.h" // A f˝ oprogram és a megszakításkezel˝ o prototípusa PROTO_MAIN(); PROTO_IRQ(); // Adatszegmens kezdetének jelzése egy vicces hexadecimális számmal // 0xdeadbeef: DEAD BEEF (kimúlt szarvasmarha) unsigned int __temp__ = 0xdeadbeef; // Csomag helyének feltöltése véletlen adatokkal (helyfoglalás) struct Packet packet = {0x04, 0x03, 0x02, 0x01, 0x55aaff00}; // Legyen a dinamikus ID-m 1, aztán majd úgyis kapunk újat // a számítógépt˝ ol unsigned char my_id = 0x1; /**********************************************************/ /* A f˝ oprogram */ /**********************************************************/ int main() { // Vermek beállítása (makró) SETUP_STACKS(); // Minden port kimenet IODIR = 0xffffffff; // Jellegzetes bitminta a kimenetekre, m˝ uködés ellen˝ orzésére IOPIN = 0xaaaaaaaa; // Soros port paramétereinek beállítása setup_usart0(0); // Dinamikus azonosító beállítása (az el˝ obbi nem elég!) my_id = 0x1; while(1) { // Ha van csomag a vételi tárban, akkor feldolgozzuk // Ha nincs, akkor csinálhatunk mást is... if (detect_packet()) { // Csomag kiolvasása az átmeneti tárból // A függvény visszatérési értéke: // 0: Elvileg sosem fordul el˝ o // -1: Ellen˝ orz˝ o összeg hibás // 1: Nekünk szóló parancs csomag // 2: Egy régebbi parancscsomagunkra érkezett válasz, // ami körbeért a gy˝ ur˝ un; választ tartalmazhat! // 3: Továbbküldend˝ o csomag érkezett (továbbküldte) // Ha nekünk szóló parancs csomag érkezett if (receive_packet(&packet, my_id) == 1) { // Válogatás a parancskódok alapján switch (packet.command) { // Gy˝ ur˝ u felderítése case COMM_HELLO: break;
27
4.5. A KOMMUNIKÁCIÓ PROTOKOLLJA ÉS ANNAK MEGVALÓSÍTÁSA C NYELVEN
// Dinamikus azonosító beállítása // Erre a csomag adatmezejét a vicces 0xDEADBEEF-re // módosítjuk (az ellen˝ orz˝ o összegre vigyázunk!) case COMM_SET_ID: my_id = (unsigned char) (packet.data & 0xff); packet.dst_id = 0xff; packet.data = 0xdeadbeef; calc_checksum(&packet); send_packet(&packet); break; } } } } } // Megszakítás-kiszolgáló rutin, most nem használjuk void irq() { } Ha az el˝obb idézett forráskódokat megfelel˝oen lefordítjuk, összef˝uzzük és lefuttatjuk, akkor a számítógép szoftvere – együttm˝uködve a mikrovezérl˝ovel – a következ˝o eredményt szolgáltatja: KÜLDÖTT -----------------------Cél: FF Forrás: 00 Parancs: 00 Összeg: 01 Adat: 00000000 FOGADOTT ----------------------Cél: FE Forrás: 00 Parancs: 00 Összeg: 02 Adat: 00000000 Ellen˝ orizve. Válasz csomag érkezett. Gy˝ ur˝ u hossza: 1 Azonosítók kiosztása... KÜLDÖTT -----------------------Cél: 00 Forrás: 00 Parancs: 01 Összeg: FE Adat: 00000001 FOGADOTT ----------------------Cél: FF Forrás: 00 Parancs: 01 Összeg: C8 Adat: DEADBEEF
28
4.5. A KOMMUNIKÁCIÓ PROTOKOLLJA ÉS ANNAK MEGVALÓSÍTÁSA C NYELVEN
Ellen˝ orizve. Válasz csomag érkezett. Látható, hogy a program el˝oször küldött egy „általános” csomagot, ebb˝ol megállapította a gy˝ur˝u hosszát: eredményül 1-et kapott. Ezt követ˝oen elkezdte az eszközöknek (1 db) kiosztani az azonosítókat: sikerességér˝ol a „0xDEADBEEF” (elhullott szarvasmarha) üzenet tájékoztat. A fejezetben felépített vázra fogjuk felépíteni a mér˝okészülékünk többi funkcióját, ezért feltétlen hasznosnak tartom a leírtak megfelel˝o megértését és elfogadását. Ha az Olvasó bizonytalannak érzi magát, vagy kétség gyötri, lapozzon vissza bátran, s frissítse fel ismereteit, hiszen a következ˝o részekben hivatkozni fogok erre az anyagra.
29
5. fejezet
Felhasználási területek Ebben a részben néhány példán keresztül megismerkedhetünk egy mikrovezérl˝ob˝ol, egy személyi számítógépb˝ol és néhány egyszer˝u áramkörb˝ol álló mérési összeállítás felhasználási területeivel. Megnézzük, hogy az el˝obb felsorolt elemekb˝ol hogyan építhet˝o mérésadatgy˝ujt˝o és vezérl˝o készülék. A rendelkezésre álló kevés számú elem ellenére az alkalmazási területek széles skáláját igyekszem bemutatni, de sajnos számtalan olyan mérési-vezérlési elv létezik, amit – id˝obeli és terjedelmi okokból – nem áll módomban tárgyalni. Ha tisztelt Olvasó bizonyos szakterületben jobban el kíván mélyülni, akkor a rendelkezésre álló szakirodalomhoz kell folyamodnia. Az els˝o részben részletesen megismerkedhetünk a legegyszer˝ubb vezérlési feladatokkal és megoldásukkal. Ez a fejezet inkább azért hasznos, mert segítségével könnyen áttekinthetjük a mikrovezérl˝o teljes szoftverhátterének m˝uködését.
5.1.
Vezérlés
Automatikából így tanultuk: „A vezérlés olyan nyílt hatásláncú irányítástechnikai tevékenység, amely során a beavatkozás nem közvetlenül a szabályozott jellemz˝o megváltozásának eredményeképpen jön létre, hanem bizonyos küls˝o és egyéb jellemz˝ok logikai kapcsolatának hatására. Dönt˝oen diszkrét jelekkel dolgozik, de használhat analóg és digitális jeleket is.” Ez azt jelenti, hogy nem lesz szükségünk érzékel˝ore: a vezérléshez a mikrovezérl˝o egyik kimeneti portját a végrehajtó szerv (elektronikus kapcsolóját irányító) bemenetére kötjük.
Vezérlés egyszer˝usített rajza
A következ˝o három alfejezetben ezt a mérési összeállítást használjuk. A helyesen megépített végrehajtó– érzékel˝o áramkörön nem szükséges semmilyen változtatást végrehajtani, mert az pontosan megfelel az el˝obbi ábrának.
5.1.1.
12 V-os izzó be- és kikapcsolása
Ez a feladat több célt szolgál: egyrészt megismerkedünk a mikrovezérl˝o „vezérl˝oként” való alkalmazásával, másrészt – lévén a m˝uködtet˝o szoftver igen egyszer˝u – gondosan áttanulmányozhatjuk a programozás lépéseit. 30
5.1. VEZÉRLÉS
Kezdjünk is hozzá! Szükségünk lesz egy C fordítóra, ami ARM architektúrára fordít. Err˝ol már a „Fejleszt˝oi környezet” fejezetben volt szó, így ezzel nem kívánok foglalkozni. Ezen kívül el kell készítenünk a forrásprogramot, valamint egy olyan fájlt, ami megkönnyíti a fordítást (Makefile). Ez utóbbi arra szolgál, hogy forgatókönyvként szolgáljon a make1 számára. A C forráskód (main.c) tartalma a következ˝o: // A mikrovezérl˝ o hardver regiszterei #include "lpc2103.h" // Hasznos rutinokat tartalmazó függvénykönyvtár (ld. melléklet) #include "../LPC210x/library.h" // A f˝ oprogram és a megszakításkezel˝ o prototípusa. Tartalma: // #define PROTO_MAIN() int main(void) __attribute__ ((noreturn)) \ // __attribute__ ((naked)); // #define PROTO_IRQ() void irq(void) __attribute__ \ // ((interrupt ("IRQ"))); PROTO_MAIN(); PROTO_IRQ(); // ********************************************************* // A f˝ oprogram // ********************************************************* int main() { // Helyi változó, egy számláló értéket tartalmazza int c; // ELS˝ O LÉPÉS: mikrovezérl˝ o vermének (visszatérési címeinek, és // helyi változóinak helye) beállítása. SETUP_STACKS(); // Minden I/O port legyen kimenet IODIR = 0xffffffff; // ciklus, 0-tól "végtelenig", "c" értéke egyesével növekedik for(c = 0;; c++) { // Ha az alulról számolva 16. bitje 1 érték˝ u, akkor... if (c & 32768) // ... a lámpa húnyjon ki! IOPIN = 0; else // De ha nem nulla, akkor világítson fényesen! IOPIN = 0xffffffff; } } // Megszakítás-kiszolgáló rutin, sosem fut le void irq() { } A mikrovezérl˝o legels˝o „teend˝oit” egy assembly nyelv˝u fájl (crt.S2 ) határozza meg, ami nem csak az els˝o néhány utasítást tartalmazza, hanem el˝o is készíti a mikrovezérl˝ot a C nyelven írt kód futtatására. Ennek forrását láthatjuk a következ˝o néhány sorban: 1A
fordítást több lépésben végz˝o program – C RunTime, .S (Source) – forrás
2 CRT
31
5.1. VEZÉRLÉS
# A KÓD FELHASZNÁLÁSÁHOZ EL KELL TÁVOLÍTANI A #-VAL KEZD˝ OD˝ O SOROKAT, MERT # A FORDÍTÓPROGRAM NEM TUDJA ÉRTELMEZNI AZOKAT. .text .align 4 .global _init .type _init, %function .global _start .type _start, %function # A 0-s címen kezd˝ odjön a kód .org 0 # Kötelez˝ o címkék, a program belépési pontja _init: _start: # RESET után kezdje futtatni a "main()" függvényt (f˝ oprogram) b main # Minden más esetben (hibák, kivételek) ugorjon egy végtelen ciklusra b _stop b _stop b _stop b _stop # Ide az OpenOCD egy ellen˝ orz˝ o összeget ír, innen tudja a mikrovezérl˝ o # BOOT kódja, hogy a memóriában valós felhasználói program van. nop # Megszakításkezel˝ o címének olvasása a megszakítás-kezel˝ ot˝ ol, és ugrás a # kapott címre. Ezt a címet mi állíthatjuk be a "main()" függvényben. ldr pc, [pc, #-0xFF0] # FIQ-t nem használunk, ekkor is ugorjon a cégtelen ciklusra b _stop # Végtelen ciklus, nem módosítja a regisztereket, könnyebb a hibakeresés, # ha "áll" a processzor _stop: b
_stop
A fordításhoz ne felejtsük el elkészíteni a Makefile-t! # Az általunk elkészített C fordító (keresztfordító ARM-ra) helye PREFIX = /usr/local/arm/bin # A fordító neve CC = $(PREFIX)/arm-elf-gcc # A fordító paraméterei CFLAGS = -Wall -g -O3 -mlittle-endian -marm -mcpu=arm7tdmi-s # A linker, ami összeállítja a részenként lefordított kódot: # crt.S main.c ---> crt.o main.o ---> program.elf ---> program.bin LD = $(PREFIX)/arm-elf-ld # A linker paraméterei: a futtatható kód a 0 kezd˝ ocímre kerüljön # Az adat pedig 0x40000000-re, vagyis 1 GBájtra # F˝ uzze még hozzá a library.a-ból a szükséges függvényeket # (most nincs ilyen, de majd lesz) 32
5.1. VEZÉRLÉS
LDFLAGS = -Ttext 0 -Tdata 0x40000000 ../LPC210x/library.a # Segédprogramok (és paramétereik) az adatkonverzióhoz OBJCOPY = /usr/local/arm/bin/arm-elf-objcopy OCFLAGS = -j .text -O OBJDUMP = /usr/local/arm/bin/arm-elf-objdump ODFLAGS = -j .text -j .data -dS # A programrészek (C forrásfájlok) felsorolása OBJS = main.o # Mit kell tennie a make-nak fordításkor: takarítás, # CRT fordítása, programrészek fordítása all: clean crt.o $(OBJS) # Majd ezek összef˝ uzése egy fájllá (speciális formátum) $(LD) -o program.elf crt.o $(OBJS) $(LDFLAGS) # Konvertálás bináris formába (az OpenOCD ezt fogadja el) $(OBJCOPY) $(OCFLAGS) binary program.elf program.bin # Listing fájl készítése: a C forrásfájl sorait a nekik megfelel˝ o # assembly kóddal együtt listázza, memóriacímekkel, adatokkal... # Nagyon hasznos! $(OBJDUMP) $(ODFLAGS) program.elf > program.list # Takarításkor töröljön minden core, .o, ... vég˝ u fájlt! clean: rm -f *core *.o *.hex *.bin *.list *.img *.elf Ha minden fájlt (Makefile, crt.S, main.c, lpc2103.h, library.h3 és library.a) hiba nélkül el˝okészítettünk, akkor a fordítást a következ˝oképpen végezhetjük el: $ make rm -f *core *.o *.hex *.bin *.list *.img *.elf /usr/local/arm/bin/arm-elf-gcc -c -o crt.o crt.S /usr/local/arm/bin/arm-elf-gcc -Wall -g -O3 -mlittle-endian -marm -mcpu=arm7tdmi-s -c -o main.o main.c /usr/local/arm/bin/arm-elf-ld -o program.elf crt.o main.o -Ttext 0 -Tdata 0x40000000 ../LPC210x/library.a /usr/local/arm/bin/arm-elf-objcopy -j .text -O binary program.elf program.bin /usr/local/arm/bin/arm-elf-objdump -j .text -j .data -dS program.elf > program.list Az OpenOCD-t úgy írták meg, hogy bináris fájlokkal dolgozzon, ezért nekünk a program.bin-re lesz szükségünk. Adjunk tápfeszültséget a mikrovezérl˝onek, csatlakoztassuk a JTAG kábelt, zárjuk rövidre a DBGSEL átkötést, végezzünk hardver RESET-et (ez kell a DEBUG üzemmódba lépéshez), indítsuk el az openocd-t a már megismert módon, telnet-eljünk bele, végül adjuk ki a memóriatörlési és írási parancsot: hg8lhs@buddhayana:~$ telnet localhost 4444 Trying 127.0.0.1... Connected to localhost. Escape character is ’^]’. Open On-Chip Debugger > flash erase 0 0 7 erased sectors 0 through 7 on flash bank 0 in 0s 514536us > flash write 0 program.bin 0 3 Ezek a fájlok most nem mind vesznek részt a fordításban, de a kés˝ obbiekben kiemelt jelent˝osséggel fognak bírni. A szükséges módosításokat a megfelel˝o id˝oben közölni fogom.
33
5.1. VEZÉRLÉS
wrote 148 byte from file program.bin to flash bank 0 at offset 0x00000000 > resume 0 Target 0 resumed > A legutolsó utasítás (resume 0) el is indítja a program végrehajtását a 0 címt˝ol (a memória legelejét˝ol) kezdve. Ha minden sikeres volt (ennek esélye a nullával egyenl˝o, de ha mégis m˝uködik, akkor valami nincs rendben – szokták mondani a szakemberek), akkor a lámpa kb. 60-szor . . . 120-szor villan fel percenként.
5.1.2.
12 V-os izzó be- és kikapcsolása id˝ozít˝o megszakítással
Ha végiggondoljuk, az el˝obbi program egész idejében csak számol, számol, és nagyon ritkán kapcsolja ki vagy be a lámpát. Idejének nagy részében olyan dolgot végez, aminek számunkra nem sok haszna van (ha nem számolna, akkor nem is villogna a lámpa, de maga a számolás nem hasznos m˝uvelet). Mi lenne, ha számolásra inkább egy céleszközt, egy számlálót használnánk? Akkor a processzornak lenne ideje mással is foglalkozni. A megoldás nem ennyire egyszer˝u, de azért nem is bonyolult. Ebben a fejezetben olyan mintaprogramot készítünk, amely egy számlálót használ: ha a számláló elér egy el˝ore beállított értéket, akkor megszakítást generál. A megszakítás hatására a processzor felfüggeszti aktuális tevékenységét, „megjegyzi”, hogy hol hagyta abba, lefuttatja a megszakítás-kiszolgáló rutint (ami kikapcsolja vagy bekapcsolja a lámpát annak aktuális állapotától függ˝oen), majd visszatér eredeti feladatához, mintha semmi sem történt volna. Szerencsére csak a C forrásállományban kell változtatást eszközölnünk, mert a Makefile és a crt.S állomány elég általános ahhoz, hogy kés˝obb is bármikor felhasználhassuk. #include "lpc2103.h" #include "../LPC210x/library.h" PROTO_MAIN(); PROTO_IRQ(); // Globális változó, amit megtartja értékét a megszakítási // rutin lefutása után is. Értéket kell neki adni, hogy // a C fordító helyes kódot generáljon! Ez nem a fordító // hibája, hanem a bináris formátumú fájl miatt kell ezt a // trükköt alkalmazni. Másik megoldás az 1GBájt méret˝ u // bináris fájl lenne, de azt azért mégsem kellene.. unsigned int r0 = 0x55; int main() { SETUP_STACKS(); IODIR = 0xffffffff; // Megszakításvezérl˝ o beállítása // 1. id˝ ozít˝ o_0 megszakításának engedélyezése // 2. megszakításkezel˝ o rutin címének megadása // 3. megszakítások prioritásának (els˝ obbségének) // megadása. Egy darab megszakításunk van, tehát // nem számít VICIntEnable = 0x10; VICVectAddr0 = (unsigned long) irq; VICVectCntl0 = 0x20 | 4; // Id˝ ozít˝ o_0 beállítása T0CTCR = 0; T0PR = 0; // Mely érték elérésekor keletkezzen megszakítás 34
5.1. VEZÉRLÉS
T0MR0 = 1000000; // Keletkezzen megszakítás! T0MCR = 3; // Számlálás indítása (triggerelés) T0TCR = 1; // // // // // // // // // //
Processzor megszakításfogadásának engedélyezése Ez a library-ben található függvény (interrupts.c): void inline enable_interrupts() { // ARM mód, IRQ engedélyezve, FIQ tiltva, // System módban asm("mov r0, #0x5f"); // Gépi állapotregiszter módosítása asm("msr cpsr, r0"); }
enable_interrupts(); // Nem csinál semmit a processzor, most nem. for(;;); } /********************************************************/ /* A megszakítás-kiszolgáló rutin */ /********************************************************/ void irq() { // Ha r0 változó legalsó bitje 1, akkor... if ((r0++) & 1) // Lámpa világít! IOPIN = 0xffffffff; else // Ha nem nulla, akkor kialuszik. IOPIN = 0x00000000; // Számláló megszakítás nyugtázása T0IR = 1; // Megszakításvezérl˝ o megszakítás-kérésének nyugtázása // Ez is egy függvény a library-ben: // // void inline acknowledge_interrupt() { // VICVectAddr = 0xff; // } acknowledge_interrupt(); } A kód fordítása, feltöltése és futtatása ugyanúgy végzend˝o, mint az el˝obbi esetben.
5.1.3.
12 V-os izzó fényerejének beállítása impulzusszélesség-modulációval
Most, hogy már rendelkezünk a lámpa villogtatásához szükséges tudással, meg kell ismerkednünk a lámpa fényerejének változtatásával is. Célunk, hogy impulzusszélesség-modulációval úgy módosítsuk a lámpán (és a FET-en) átfolyó áram átlagértékét, hogy a kívánt fényer˝ot elérjük. Ehhez nem kell bonyolult programot írnunk, mert a mikrovezérl˝o rendelkezik PWM képességgel.
35
5.1. VEZÉRLÉS
A PWM alapja is egy számláló, ami N-ig számol (N értékét mi határozzuk meg). A számlálás kezdetekor a megfelel˝o (M2.0) PWM kimenet logikai alacsony szinten van, így a lámpa nem világít. Ha a számláló értéke elér egy (általunk el˝ore beállított) M értéket (M < N), akkor a PWM kimenet magas szintre vált. Ha a számláló elérte N-t, akkor kezdi elölr˝ol (0-ról) a számolást, és a PWM kimenetet alacsony szintre állítja. Az áram átlagértéke: Iavg = I0 ·
N −M N
ahol I0 a lámpa névleges árama (teljesen nyitott FET esetén folyik). A program beállítja N értékét 3300-ra, majd M értékét lassan 0-ról 3300-ra növeli, így a lámpa egyre halványabban világít. Ezután az M értékét lassan 0-ra csökkenti, hogy a lámpa újra fényesen világítson. Jó ha tudjuk: ha a kimeneti feszültség max. értéke 3,3 V, és N = 3300-at választunk, akkor M = 1-hez éppen 3300–1 mV = 2999 mV tartozik, M = 2-höz pedig 3300–2 mV = 2998 mV. . . A C forrásban a „3300 − M ” kivonást el˝ore elvégezzük, így az M értéke a fényer˝osség értékével egyenesen arányos. A PWM használatához szükséges C forráskód a következ˝o: #include "lpc2103.h" #include "../LPC210x/library.h" PROTO_MAIN(); PROTO_IRQ(); unsigned int r0 = 0x55; unsigned int value = 1; int main() { SETUP_STACKS(); // Minden port legyen kimenet, tulajdonképpen mindegy IODIR = 0xffffffff; // Írunk valamit a kimenetre, valójában nem használjuk IOPIN = 0xaaaaaaaa; for (;;) { // M értéke 0 és N = 3300 között változik (növekedik)... for(value = 0; value < 3300; value++) { // Ha a kimeneti feszültség max. értéke 3,3 V, és // N = 3300-at választunk, akkor M = 1-hez 1 mV tartozik set_pwm_20(value, 3300); // Várunk kicsit for (r0 = 0; r0 < 512; r0++); } // ... M csökken. for(; value > 0; value--) { set_pwm_20(value, 3300); for (r0 = 0; r0 < 512; r0++); } } } void irq() { } A set_pwm_20(int M, int N) függvény a library (függvénykönyvtár) pwm.c fájljában található: 36
˝ 5.2. MÉRÉSADATGYUJTÉS
void inline set_pwm_20(int value, int period) { PINSEL0 &= ~(3 << 14); // Engedélyezi a MAT2.0 kimenetet PINSEL0 |= (2 << 14); T2TC = T2IR = T2CTCR = 0; T2PR = 0;
// Id˝ ozít˝ o órajelének forrása a PCLK // El˝ oosztó tiltása
T2MR0 T2MR3 T2MCR T2EMR
T2PWMCON = 1;
// // // // // //
T2TCR = 2; T2TCR = 1;
// Törli a számláló értékét // Engedélyezi a számlálást
= = = =
(period - value); period; (1 << 10); (2 << 4) | (1 << 10);
Kimenet átlagértékének beállítása (M) Periódusid˝ o (N) beállítása Ha eléri a számláló N-t, újraindul Ha a számláló eléri M-et, akkor 1-be vált a MAT2.0, és kigyullad a lámpa Engedélyezi a PWM üzemmódot
} Az ebben a részben megismert példák nem használták a soros kommunikációt, mert a vezérlési feladatok olyan egyszer˝uek voltak, hogy nem lett volna értelme túlbonyolítani azokat. A következ˝o részben felhasználjuk az el˝oz˝o fejezetben megírt kommunikációs rutinokat.
5.2.
Mérésadatgyujtés ˝
Ebben a fejezetben arról olvashatunk, hogy hogyan lehet az eddig megszerzett tudást mérésadatgy˝ujt˝o berendezés építésére felhasználni. Maga a mérésadatgy˝ujt˝o nem egy bonyolult készülék, egyetlen feladata, hogy a mérésvezérl˝o által meghatározott id˝oben megmérje az analóg bemenetén lév˝o jel nagyságát (feszültségét), és ezt válaszként tudassa a mérésvezérl˝ovel. A mérésvezérl˝o (számítógép) végzi az eredmények tárolását, értékelését és megjelenítését. Mivel a mérésvezérl˝onek jeleznie kell azt, hogy szüksége van a mért értékre, ezért valamilyen kommunikációra lesz szükség a számítógép és a mikrovezérl˝o között. A TokenRing–RS-232 hálózatunk erre a célra éppen megfelel. Könny˝u kiválasztani, hogy milyen mennyiséget mérjünk, mert érzékel˝onk a fényer˝osség megállapítására alkalmas. Legyen tehát a mért jellemz˝o a szobámban tapasztalható fényer˝osség (a fotodióda felületére érkez˝o fotonok számával arányos fizikai mennyiség – legyen az bármi is).
A méréshez három dologra lesz szükségünk: 1. a mér˝ohardverre (processzor modul és érzékel˝o) 2. a mikrovezérl˝o programja (firmware) 3. a személyi számítógép programja (szoftvere) Az els˝o tétel könnyen biztosítható, mert a processzor modul és a végrehajtó-érzékel˝o modul rendelkezésünkre áll. Ha helyesen építettük meg az áramkört, akkor az módosítás nélkül használható a mérés során. A kommunikációhoz ne felejtsük el csatlakoztatni a TTL–RS-232 illeszt˝ot is!
37
˝ 5.2. MÉRÉSADATGYUJTÉS
A mérésadatgy˝ujtés egyszer˝usített rajza
A mikrovezérl˝o szoftvere alig bonyolultabb, mint idáig. Ezt az eszközt olyan jól tervezte meg a gyártó, hogy a hardver programozása nagyon könnyen elvégezhet˝o. Lássuk, hogy hogyan! #include "lpc2103.h" #include "../LPC210x/library.h" #include "commands.h" PROTO_MAIN(); PROTO_IRQ(); unsigned int __temp__ = 0xdeadbeef; struct Packet packet = {0x04, 0x03, 0x02, 0x01, 0x55aaff00}; unsigned char my_id = 0x1; int main() { SETUP_STACKS(); IODIR = 0xffffffff; IOPIN = 0xaaaaaaaa; setup_usart0(0); my_id = 0x1; while(1) { if (detect_packet()) { if (receive_packet(&packet, my_id) == 1) { switch (packet.command) { case COMM_HELLO: break; case COMM_SET_ID: my_id = (unsigned char) (packet.data & 0xff); packet.data = 0xdeadbeef; break; case COMM_GET_ANALOG: packet.data = get_adc3(); break; } packet.dst_id = 0xff; calc_checksum(&packet); send_packet(&packet); } 38
˝ 5.2. MÉRÉSADATGYUJTÉS
} } } Egyetlen különbség a már megismert kommunikációs algoritmushoz képest az, hogy feldolgozzuk a nekünk szóló COMM_GET_ANALOG parancsot, és válaszként az AD0.3 bemeneten mérhet˝o feszültség értékét adjuk vissza (a packet.data alsó 10 bitjén). A get_adc3() alprogram a library függvénykönyvtár adc.c állományában kapott helyet. Forráskódja igen egyszer˝u: int get_adc3() { // Bemenetek kiválasztása PINSEL0 &= ~(3 << 20); PINSEL0 |= 3 << 20; // AD0.3-as csatorna, 10 bit, kezdje a mérést MOST! ADCR = (1 << 3) | (2 << 8) | (1 << 21) | (1 << 24); // Megvárjuk, amíg végez while (!(ADGDR & 0x80000000)); // Visszaadjuk az eredményt az alsó 10 biten return (ADGDR >> 6) & 0x3ff; } A listázott két forrásból kit˝unik, hogy a mikrovezérl˝o csak akkor mér, ha megfelel˝oen kitöltött csomag segítségével a mérésvezérl˝o erre utasítja. Az eredményt pedig válaszcsomag formájában közli a számítógéppel. A vezérl˝o szoftvere ennek megfelel˝oen a következ˝o: // A szükséges, szabványos C függvények prototípusa #include #include #include <stdio.h> #include <stdlib.h> #include <string.h> // Saját kommunikációs függvényeink prototípusa #include "usart.h" // Parancsok kódja #include "commands.h" // Mennyi ideig mérjen a m˝ uszer (2 * 86400 s = 2 nap) #define MAX_TIME 2*86400 int meresadatgyujtes(FILE *tty, char *filename) { FILE *data; int time; struct Packet packet; // Adatfájl megnyitása, hiba esetén üzenetet küld a felhasz// nálónak, és kilép a programból if ((data = fopen(filename, "w")) == NULL) { perror("controller:meresadatgyujtes():fopen()"); exit(2); } // Két nap... 39
˝ 5.2. MÉRÉSADATGYUJTÉS
for (time = 0; time < MAX_TIME; time++) { // Vár 1 másodpercet sleep(1); // Kitölti a csomag mez˝ oit // DST_ID = 0, mert az els˝ o elérhet˝ o mikrovezérl˝ onek üzen packet.dst_id = 0; // SRC_ID = 0, vagyis a mérésvezérl˝ o packet.src_id = 0; // Analóg érték lekérdezése packet.command = COMM_GET_ANALOG; // Ennek nincs jelent˝ ossége, majd a mér˝ oberendezés átírja packet.data = 0; // Helyes ellen˝ orz˝ o összeg nélkül nem fogadja el a mikro// vezérl˝ o a csomagot calc_checksum(&packet); // Üzenet küldése send_packet(tty, &packet); // Válaszüzenet fogadása, ami úgyis csak a mikrovezérl˝ ot˝ ol // jöhet receive_packet(tty, &packet); // Mérési eredmény mentése az adatfájlba fprintf(data, "%d %d\n", time, packet.data); // Pufferek ürítése fflush(data); } return 0; } int main(int argc, char **argv) { FILE *tty; struct Packet packet; int length, id; // Soros port paramétereinek beállítása if ((tty = setup_usart0("/dev/ttyS0")) == NULL) { perror("controller:main():setup_usart()"); return 1; } // Gy˝ ur˝ u hosszának megállapítása a dinamikus ID-k kiosztásához printf("\n\nGy˝ ur˝ u hossza: %d", length = get_chain_length(tty)); // Dinamikus azonosítók kiosztása printf("\nAzonosítók kiosztása..."); for (id = 1; id <= length; id++) {
40
˝ 5.2. MÉRÉSADATGYUJTÉS
packet.dst_id = id - 1; packet.src_id = 0; packet.command = COMM_SET_ID; packet.data = id; calc_checksum(&packet); send_packet(tty, &packet); receive_packet(tty, &packet); } // Ha az els˝ o paraméter egy "m" bet˝ u, és utána megadtunk egy // fájlnevet, akkor kezd˝ odhet a mérésadatgy˝ ujtés (2 napig!!!) if ((argc >= 3) && !strcmp(argv[1], "m")) meresadatgyujtes(tty, argv[2]); // Ha végeztünk, akkor fájlok bezárása, és kilépés fclose(tty); printf("\n\n"); return 0; } A program használatához annyit mindenképpen tudni érdemes, hogy bemenetként két paramétert vár, az els˝o egy „m” (mérés) bet˝u, míg a második egy fájlnév. Ez utóbbiba menti a program a mért értékeket. Ha minden megfelel˝oen m˝uködik, akkor hasonló eredményt láthatunk a terminál ablakban (a küldött és fogadott csomagok nyomkövetési információi): KÜLDÖTT -----------------------Cél: 00 Forrás: 00 Parancs: 06 Összeg: FA Adat: 00000000 FOGADOTT ----------------------Cél: FF Forrás: 00 Parancs: 06 Összeg: B5 Adat: 00000145 Ellen˝ orizve. Válasz csomag érkezett. A paraméterként megadott adatállományban pedig gy˝ulnek-gy˝ulnek a mérési eremények (els˝o 15 másodperc): 0 774 1 325 2 802 3 347 4 758 5 357 6 754 7 887 8 395 9 760 10 357 11 760 12 415 41
5.3. TRANSZFER KARAKTERISZTIKA FELVÉTELE
13 790 14 331 Ha a mérési eredményeket feldolgozzuk (zajsz˝urés), és egy megjelenít˝o programmal (pl. a gnuplot-tal) ábrázoltatjuk, akkor hasonló eredményt kapunk:
A mért adatok diagramja
A „nyers” (feldolgozatlan) mérési adatok diagramja
A fejezet lezárásaként vonjuk le a tanulságot: nappal világos van, este pedig sötét. Lám, most már méréssel is bebizonyítottuk azt a „természeti törvényt”, amit eddig csak csalóka érzékszerveinkkel tudtunk megfigyelni.
5.3.
Transzfer karakterisztika felvétele
Ha utasítjuk a mikrovezérl˝ot, hogy analóg kimenetén produkáljon megadott érték˝u egyenfeszültséget, és ezt a jelet egy négypólus bemenetére kapcsolva mérjük a négypólus válaszát, akkor felrajzolhatjuk annak transzfer karakte42
5.3. TRANSZFER KARAKTERISZTIKA FELVÉTELE
risztikáját4 . Minden programozási tudás rendelkezésünkre áll, már csak néhány sorral kell kiegészítenünk a mikrovezérl˝o és a számítógép forráskódját. A számítógép 0 V-tól 3,3 V-ig változtatja a mikrovezérl˝o analóg kimeneti feszültségét (például 1 mV-os lépésközzel), és minden léptetésnél megméri a vizsgált négypólus válaszát (megfelel˝o id˝ot hagyva a tranziens – átmeneti – jelenségek lecsengésére). A mikrovezérl˝o kódjában a következ˝o változtatásokat kell elvégezni: switch(packet.command) { ... case COMM_SET_ANALOG: // Állítsa be az analóg kimenet értékét set_pwm_20(packet.data & 0xffff, packet.data >> 16); // Szokásos válasz: elhullott tehén packet.data = 0xdeadbeef; break; ... A mérésvezérl˝o szoftverében több módosítanivalónk lesz: egy új függvény jelenik meg transzfer néven. Ez a programrész a már el˝obb elmondott m˝uveleteket végzi el, és közben a mérési adatokat a megadott fájlba menti. int transzfer(FILE *tty, char *filename) { FILE *data; int value; struct Packet packet; // Megnyitja az adatfájlt, hiba esetén üzenetet küld if ((data = fopen(filename, "w")) == NULL) { perror("controller:transzfer():fopen()"); exit(3); } // "value" értékét 0 mV és 3300 mV között változtatja (növeli) for (value = 0; value <= MAX_VALUE; value++) { // vár, amíg lecsengenek a tranziensek usleep(DELAY); // El˝ okészíti a feszültségbeállító csomagot packet.dst_id = 0; packet.src_id = 0; packet.command = COMM_SET_ANALOG; packet.data = value | (MAX_VALUE << 16); calc_checksum(&packet); // És elküldi send_packet(tty, &packet); receive_packet(tty, &packet); // Tranziensek lecsengése usleep(DELAY); // Lekérdez˝ o csomag összeállítása packet.dst_id = 0; packet.src_id = 0; 4A
bementre adott egyenfeszültség és a kimeneten mérhet˝o egyenfeszültség között ad függvénykapcsolatot (statikus üzemben)
43
5.3. TRANSZFER KARAKTERISZTIKA FELVÉTELE
packet.command = COMM_GET_ANALOG; packet.data = 0; calc_checksum(&packet); // Analóg lekérdez˝ o csomag küldése, válasz fogadása send_packet(tty, &packet); receive_packet(tty, &packet); // Mérési adatok kiírása fájlba fprintf(data, "%d %d\n", value, packet.data); fflush(data); } return 0; } ... // Program használata: ./controller t data/transzfer.txt if ((argc >= 3) && !strcmp(argv[1], "t")) transzfer(tty, argv[2]); ... Egy elkészített eszköz transzfer karakterisztikáját mutatja a következ˝o ábra. A négypólus kapcsolási rajzát nem adom meg, felépítésének kitalálását az Olvasóra bízom.
A transzfer karakterisztika
44
5.3. TRANSZFER KARAKTERISZTIKA FELVÉTELE
A „nyers” (feldolgozatlan) transzfer karakterisztika
45
6. fejezet
Összefoglalás, végkövetkeztetés A bevezet˝oben hangot adtam annak a szándékomnak, hogy bemutassam: egy modern mikrovezérl˝o általános célú mér˝om˝uszerként való alkalmazása nem jelent komoly kihívást, sokkal inkább érdekes–izgalmas tanulási folyamatot. Remélem, sikerült ugyanezt a véleményt kialakítanom a kedves Olvasóban is. Sajnos még számos mérést elvégezhettünk volna, de terjedelmi okokból – és nem kevésbé id˝ohiány miatt – erre nem kerülhetett sor. Bízom benne, hogy az Olvasó érdemesnek találta a m˝uvet az elolvasásra, sok új ismeretre, tapasztalatra tett szert a dolgozat elolvasása (és esetleg az áramkörök utánépítése) során. Ha bármilyen megjegyzése,javaslata van a dolgozattal kapcsolatban, esetleg kétség gyötri, bátran vegye fel velem a kapcsolatot a Fuszenecker Róbert email-címen.
46
7. fejezet
Felhasznált irodalom • ARM7TDMI Technical Reference Manual (Rev 3), ARM Limited, 2001. • UM10161 Volume 1: LPC2101/02/03 User Manual, Philips Semiconductors, 2006. január 12. • AT91 ARM Thumb-based Microcontrollers, ATMEL Corporation, 2006. november 22.
47
8. fejezet
Felhasznált szoftverek A dolgozat megírásához és a programok elkészítéséhez a következ˝o szoftvereket használtam fel:
• Ubuntu Linux 6.06 (Dapper Drake) [Linux kernel 2.6.15-27-k7] • VIM — VI IMproved version 6.4.6 • LATEX2ε (pdfeTeX, Version 3.141592-1.21a-2.2) • The GIMP 2.2.11 • aspell (International Ispell Version 3.1.20 (but really Aspell 0.60.4)) • Dia 0.94 • Eagle 4.16r2 for Linux, Light Edition
• binutils 2.17 (using BFD version 2.17) • gcc 4.0.3 • openocd – Open On-Chip Debugger (2007-05-30 17:45 CEST)
48
49
9.1. JTAG CSATLAKOZÓ KIOSZTÁSA
9. fejezet
Mellékletek 9.1.
JTAG csatlakozó kiosztása
50
9.2. ARM JTAG CSATLAKOZÓ LEÍRÁSA
9.2.
ARM JTAG csatlakozó leírása
51
9.3. USB-JTAG KAPCSOLÁSI RAJZA
9.3.
USB-JTAG kapcsolási rajza
52
9.4. JTAGKEY ELVI RAJZA
9.4.
JTAGKEY elvi rajza
53
9.5. OOCD-LINK KAPCSOLÁSI RAJZA
9.5.
OOCD-LINK kapcsolási rajza
54