The modular mitmót system
Mitmót fakultatív labor I. Ismerkedés a fejlesztő eszközökkel Hardver közeli programozás – API készítés (példa) Egyszerű mintaalkalmazás
Dokumentációkód: 2006. szeptember
Budapesti Műszaki és Gazdaságtudományi Egyetem Méréstechnika és Információs Rendszerek Tanszék Beágyazott Információs Rendszerek csoport 2006. szeptember
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter (
[email protected]),Scherer Balázs (
[email protected]) 2006-09-25
1
Tartalom A mitmót honlap
3
A fejlesztői környezet Telepítés WinAVR Fordítás Segédprogramok, make AVR Libc standard C könyvtár Az Eclipse Workspace és perspektíva választás Meglévő project megnyitása és másolása Fordítás és letöltés
4 4 4 4 4 5 6 6 7 7
Hardver közeli programozás A forrástól a letölthető állományig – egyszerű mintaprogram A makefile-ról Alacsony szintű programozás Bevezető feladat: kis módosítás Információk a fordítás után keletkező gépi kódról Megszakítás kezelés API készítés
8 8 8 9 9 10 11 11
Komplex feladat Mitmót API mcu avr atmega128 api Magas szintű IO kezelés Specifikáció: A program megtervezése
12 12 12 12 14 14
Függelék – megoldások Bevezető feladat: kis módosítás Időzítő API Reflexidő mérése (reflex.c)
16 16 17 19
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter (
[email protected]),Scherer Balázs (
[email protected]) 2006-09-25
2
A mitmót honlap A mitmót honlap webcíme: http://bri.mit.bme.hu, ez elérhető a MIT portál Hallagtói információk pontja alól is. Itt – belépés után - bal oldalt a mitmót linket választva kapunk egy bővebb menüsort. Az e laborhoz kapcsolódó dokumentumok folyamatosan felkerülnek a Documents menü alá. A fejlesztői környezet telepíthető fájljai és a telepítési útmutató a Software/Eclipse és WinAVR linkre kattintva tölthetők le. A modulok leírásánál (Modules) a hardver dokumentációja mellett megtaláljuk a modulhoz készült C API dokumentációját és forráskódját. A laborok során bemutatott minta-projektek az MCU modul oldalára kerülnek fel.
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter (
[email protected]),Scherer Balázs (
[email protected]) 2006-09-25
3
A fejlesztői környezet Telepítés A laborban használt rendszer komponensei ingyenesen letölthetőek. Ezek – egymástól függetlenül is – folyamatos fejlesztés alatt állnak, így a mindenkori legújabb változatot használva kisebb-nagyobb eltéréseket tapasztalhatunk az itt leírtakhoz képest. Egy tesztelt, működő telepítő csomag (Windows rendszerhez) letölthető a mitmót honlapról, ahol részletes telepítési útmutatást is adunk. E mellett itt megadjuk az egyes szoftverek legfrissebb változatának elérhetőségét, illetve egy rövid leírást a programokról.
WinAVR Letölthető: http://winavr.sourceforge.net/download.html A laborban használt változat: 20060421 Fordítás Fordítást, linkelést, debugolást, futtatható állomány letöltését Ezek mindegyike (…) szolgáló programok [\bin]. parancssorból hívható és paraméterezhető, így tetszőleges grafikus ’front-end’ alól is használhatjuk őket, megfelelő beállítások után. • avr-gcc: fordítás, assembler, linkelés • avr-as: assembler • avr-ld: linker • avr-objdump: elemző • avr-gdb: GNU debugger (…) Segédprogramok, make A WinAVR csomag tartalmaz néhány, a Unix/Linux rendszerekből ismert segédprogramot. Ezek közül számunkra legfontosabb a fordítás, letöltés lépéseinek automatizálását szolgáló make parancs, amiről később még szólunk. Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter (
[email protected]),Scherer Balázs (
[email protected]) 2006-09-25
4
Megjegyzés: Hasznos tudni, hogy a teljes toolchain Linux környezetben fut, amit Windows alatt cygwin segítségével emulál. (Ez egy Windows-hoz készült ’Linux API’, bővebben lásd: www.cygwin.com ) AVR Libc standard C könyvtár Az általános C függvények, a legfontosabb hardver specifikus funkciók (IO műveletek, megszakításkezelés…) implementációja, illetve néhány ’segédfüggvény’. (Részletesen lásd [doc\avr-libc\avr-libc-user-manual1.4.4.pdf] Module Index fejezetét) Ezek mellé, fölé adhatunk még egyéb API függvényeket, és így biztosítható egy egységes, hardverfüggetlen interfész a felhasználó felé. Megjegyzés: A gyakorlatban sajnos ez az egységesség csak korlátozottan biztosítható: az AVR libc könyvtár folyamatos fejlesztés alatt áll, és különböző változatai nem feltétlenül kompatibilisek (pl. egy-egy függvény paraméterezése változhat). Ekkor a fordításkor figyelmeztető/hibaüzeneteket kapunk, amik alapján a hiba javítható. E mellett javasoljuk, hogy minden projekt/forráskód esetén tüntessük fel az általunk használt könyvtár vagy WinAVR verziószámát. (Ez történhet egyszerű ’komment’, vagy define előfordító direktíva formájában) Feladat: A könyvtár assembly és C forráskódokból fordított tárgykódú állományok (obj fájlok) gyűjteménye, lehetőségünk van arra, hogy ’belenézzünk’. Ehhez a WinAVR avr\lib könyvtárában indítsunk parancssort, és adjuk ki a következő utasítást: avr-ar -t libc.a
Ezzel kilistázzuk a könyvtárban lévő tárgykódú állományokat. Látható, hogy szinte minden függvényt külön object fájlban valósítottak meg, noha tudjuk, hogy egy-egy C modul több függvényt is tartalmazhat. Erre a minimális kódméret eléréséhez van szükség: a futtatható fájl készítésekor a linker a main függvényből indulva feltérképezi, mely függvényeket használja a programunk, és az ezeket tartalmazó tárgykódú állományokból áll elő a végeredmény. Ez azt jelenti, hogy ha egy object fájlból használunk egy függvényt, a teljes fájl (az esetleges nem használt függvényekkel együtt) bekerül a futtatható állományba.
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter (
[email protected]),Scherer Balázs (
[email protected]) 2006-09-25
5
Feladat: Vizsgáljuk meg, mi alapján tudja a fordító – ami alapvetően magas szintű, hardverfüggetlen programozást szolgál – értelmezni a hardver közeli parancsokat, például: DDRA=0xf0; A DDRA rövidítés az IO definíciók alapján oldható fel, amit a
következő sorral adunk meg: #include
//register defines
Ennek hatására kódunk elejére fordítás előtt bemásolódik a C:\WinAVR\avr\include\avr\io.h állomány, ami pedig – a makefile-ban megadott processzortípus alapján – az iom128.h fájlra hivatkozik. Ebben találjuk a következő sorokat: /* Data Direction Register, Port A */ #define DDRA _SFR_IO8(0x1A)
Az Atmega128 kontroller dokumentációjában megtalálhatjuk, hogy az A port irányregiszterének memóriacíme 0x1A. A fordítóhoz definiált _SFR_IO8(x) makró segítségével pedig közvetlenül írhatunk x memóriacímre egy 8 bites változót.
Az Eclipse Letölthető:
http://www.eclipse.org/downloads http://www.eclipse.org/cdt/downloads.php A laborban használt változat: Eclipse SDK 3.2, CDT 3.0.2 Workspace és perspektíva választás Az Eclipse indítása után meg kell adnunk azt a könyvtárat, ahol a project-einket tárolni fogjuk. Ez tetszőleges lehet, a laborba telepített gépeken az d:\student\eclipse\avrlab könyvtárat használjuk. A File/Switch Workspace menüpont kiválasztásával ez a beállítás bármikor megváltoztatható. A felhasználói felület, ún. perspektívákból áll. A C/C++ fejlesztéshez nekünk ki kell választani a a megfelelő perspektívát, amit a Window/Open Perspective/Other... almenüpont, majd az újonnan megjelenő kis ablakban a C/C++ perspektíva kiválasztásával tehetünk meg. Ennek hatására átalakul kicsit a felhasználói képernyő. Az Eclipse emlékezni fog a következő indításkor a kiválasztott perspektívára.
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
6
Meglévő project megnyitása és másolása Ha egy előre elkészített projectet szeretnénk megnyitni, akkor az azt tartalmazó könyvtárat célszerű a workspace alá másolni. Ezután a baloldali project navigatorban a jobb gomb megnyomására legördülő menüből, vagy a File menü alól az Import... pontot, majd innen értelemszerűen az General / Existing Projects into Workspace lehetőséget kell választani. A következő ablak Browse pontjára kattintva ki tudjuk választani az importálandó projectet tartalmazó könyvtárat. A megnyíló project leíró fájljai több olyan beállítást is tartalmaznak (például a Build parancsok, lásd később), amiket a szoftvercsomag installálása után csak egyszer kell helyesen beállítanunk, mert gyakorlatilag nem változnak. Újabb project létrehozásakor a Makefile-t (lásd később) is csak kis mértékben kell változtatni. Ezért célszerű az új projectet egy már meglévő másolásával létrehozni (a Navigator ablakban a meglévő projekt gyökerére kattintva használhatjuk a szokásos Copy-Paste parancsokat). Fordítás és letöltés Az Eclipse egy általánosan használható integrált fejlesztői felület (IDE, ’front-end’), ami legtöbbször valamilyen specifikus tool-cahin fölé épül. Esetünkben ez a WinAVR. Fordításhoz és letöltéshez a Project Navigatorban jobb gombbal kattintsunk a projekt gyökerére, majd válasszuk a Build Make Target… pontot. A felugró ablakból az all pontot választva lefordíthatjuk a programot, a program pontot választva pedig fordítás után az ISP interfészen keresztül le is tölthetjük. Az Edit gombra kattintva láthatjuk, hogy ilyenkor a háttérben annyi történik, hogy a WinAVR make parancsát meghívjuk a megfelelő bemeneti paraméterrel. A parancs kimenetét az Eclipse Console ablakjában olvashatjuk. A fordításhoz, letöltéshez szükséges összes információt a projekt részét képző Makefile szkript tartalmazza.
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
7
Hardver közeli programozás A forrástól a letölthető állományig – egyszerű mintaprogram A makefile-ról A Makefile egy olyan szkript amiben megadhatjuk a fordításhoz, letöltéshez szükséges programokat a megfelelő paraméterezéssel, a fordítás menetét és a forrásfájlokat. Ezután a make parancs meghívásával készíthetjük el programunkat. Importáljuk az avrlab06_1 nevű projektet, és nyissuk meg a hozzá tartozó Makefile-t. Azokat a részeket, amik egy-egy új projekt létrehozásakor valószínűleg változhatnak, kiemeltük a fájl elejére: ilyen például a main függvényt tartalmazó forrás modul neve, illetve az egyéb forrás állományok. Később megadjuk a processzor típusát, a fordító különböző beállításait, a fordító (avr-gcc) és letöltő program ( avrdude ) nevét… A fordítás menetét lépésenként adjuk meg: definiáljuk, mely paranccsal állítható elő a C forrásfájlból a tárgykódú állomány (%.o : %.c szakasz), majd ebből elf (Executable and Linkable format) állomány (%.elf : $(OBJ) szakasz), végül – például – a letölthető bináris fájl (%.hex : %.elf). Ezek után – a források megadása után – már csak annyit kell „közölni” a fájlban egy parancs – például all – definiálásakor, hogy „készítsd el a bináris állományt”. Ez adja ennek a struktúrának egy nagy előnyét: a megfelelő részlépések megadása után könnyen definiálhatunk új parancsokat. Így, ha például gépi kódú forrásfájlokat is hozzá szeretnénk adni a projektünkhöz, csupán annyit kell tenni, hogy a makefile-hoz, hozzáadjuk a gépi kódú fájlok fordítási szabályát (%.o : %.S). Ezután a korábban megírt all parancs minden további nélkül használható.
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
8
Alacsony szintű programozás Az első mintaprogram a kijelző modul LED-jeit gyújtja ki egymás után (leds.c) : #include
//register defines
int main(void) { DDRA=0xf0; unsigned char i=0x10; while(1) { PORTA=i; //MCU specific! i<<=1; if(!i) i=0x10; for(unsigned long int k=0 ;k<30000000; ++k); } }
Ez a program alacsony szinten valósítja meg funkcióját: a C nyelv gyakorlatilag csupán szintaktikai könnyítést jelent a gépi kódú programhoz képest. Ilyen típusú programozásra lehet szükség az API-k megírásakor, illetve egyes idő vagy kódméret kritikus alkalmazásoknál, amikor fontos, hogy az adott program(rész) minimális idő alatt lefusson, és/vagy kevés memóriát foglaljon. Hátránya, hogy természetesen hardverfüggő: nem hordozható és értelmezéséhez szükséges a hardver pontos ismerete. A fenti programot például csak akkor értelmezhetjük, ha tudjuk, hogy a kijlző modul LED-jei és gombjai az alábbi módon kapcsolódnak a mikrokontrollerhez: PORTA
L4 L3 L2 L1 B3 B2 B1 x
A PORTA regiszter 0-s bitje a jobbszélső, a B az egyes nyomógombokat (Button), míg az L az egyes LED-eket jelenti. Bevezető feladat: kis módosítás Változtassuk meg a mintaprogramot úgy, hogy csak egy adott gomb megnyomásakor léptessük a világító LED-et. Ehhez tudni kell, hogy a gombok ’low active’-ak, azaz értékük lenyomott állapotban nulla.
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
9
Információk a fordítás után keletkező gépi kódról A jelenleg alapértelmezett Makefile használata esetén a program fordításakor az elf (Executable and linking format) fájlból automatikusan generálódik az lss listafájl, ami két fő részből tevődik össze: A különféle memóriaszekciók felsorolása, amelyek leírják, hogy a program fizikailag hol helyezkedik el az adat- és programmemóriában. Mindegyik szekcióhoz a nevén és sorszámán kívül a következő paramétereket tünteti fel a lista: • size a szekció mérete • VMA (Virtual Memory Address) a szekció virtuális memóriacíme • LMA (Load Memory Address) a tényleges fizikai címe a szekciónak, ide fog letöltődni. (Beágyazott rendszerek esetén a VMA szinte mindig megegyezik az LMA-val, kivéve az inicializált adatmemória részt). • File off a szekció file-ban lévő offset-címről ad információt • Algn a minimális blokkméretet definiálja a szekcióhoz A fontosabb szekciók: • .text: a programkód a FLASH memóriában • .data: inicializált statikus adat, az SRAM memóriában (Az SRAM memória virtuális címe 0x800000) • .bss: inicializálatlan statikus és globális változók SRAM-ban. Ezeket a fordító automatikusan 0 értékre inicializálja. Ha ezt el akarjuk kerülni, a változót explicit módon .bss alszekciójába (.noinit) kell helyezni, ehhez deklarációnál a változó neve után __attribute__ ((section (".noinit")))
írandó. •
.eeprom: az EEPROM memóriába írt adat
A debug kezdetű szekciók a hibakeresés támogatására csak az .elf fájlban találhatók meg, a bináris kimenetben nem. A memória szekciók leírása az avr-libc dokumentáció Memory Sections fejezetében található. A szekciók leírása után a .text szekció kommentezett visszafejtett gépi kódját találjuk: az IT ugrótáblát, néhány segédfüggvényt, végül a fő programunkat.
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
10
Megszakítás kezelés Az IT kezelés tipikus példája a hardver közeli feladatoknak. Erre a C nyelv nem kínál szabványos megoldást, minden fejlesztői környezet, fordító esetében eltérő lehet. Az AVR libc különböző verzióiban sem egységes (a kompatibilitás érdekében a régebbi változatokban definiált függvények továbbra is használhatók, de a fordító figyelmeztet, hogy elavult módszert használunk.) Az IT kezelése egy speciális makró (ISR()) segítségével történik, ami a felhasználó elől rejtve biztosítja egyrészt az IT ugrótábla kitöltését, másrészt megszakítás esetén a kontextus váltást (azaz a regiszterek elmentését / visszatöltését.). Részletes leírás: [doc\avrlibc\avr-libc-user-manual-1.4.4.pdf p. 120]
Példát a következő fejezetben adunk.
API készítés Az API-k (Application Program Interface) egyrészt a programkód hardverfüggő részeinek elfedésére, másrészt gyakran használt funkciók megvalósítását szolgálják. Szerencsés esetben ezek már rendelkezésre állnak, és a fejlesztőnek elég magas szinten (akár hardver független kód formájában) implementálnia az alkalmazást. Beágyazott rendszer fejlesztése esetén azonban bármikor szükség lehet a hardvert közvetlenül kezelő modulok megírására. Célunk ennek segítése, egy ’Időzítő API’ megírásával. Ehhez szükségünk lesz a kontroller dokumentációjára, és az AVR libc dokumentációra. Segítségként elkészítettük az API fejlécét, illetve vázát (timer_api.c, timer_api.h). A header fájlban kommentben megadtuk az egyes függvények leírását, ami alapján a kód megírható. Ez a leírás szükséges, hisz az API felhasználója sok esetben – például ha a forrás fájlt könyvtárba ’rejtjük’ – csak a header fájlt látja. Ha a forrás elkészült, a Makefile SRC változójában meg kell adnunk, hogy ezt is le szeretnénk fordítani. Tesztelésként készítsük el a futófény program időzítő API-t használó változatát. Segítségként ennek törzsét is megadtuk (leds2.c). A fordításhoz természetesen be kell állítani a Makefile TARGET változóját.
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
11
Komplex feladat
Mitmót API A mitmót rendszer processzor moduljaihoz (AVR/ARM) készített API-k lehetővé teszik, hogy használatukkal valóban hordozható kódot írjunk. Az API-k és dokumentációjuk letölthetők a mitmót honlapról. A függvények részletes dokumentációját kérjük, itt olvassátok el. Az API jelenlegi változatát a használt Eclipse workspace avr_mitmot_api könyvtárában, könyvtárként (.a fájl) helyeztük el. Ezt a makefile-ban már megadtuk: LDFLAGS = -L. ../avr_mitmot_api/libmcu_avr_atmega128_api.a
Használatához csak az alábbi sort kell megadni a forrás fájlban: #include "../avr_mitmot_api/mcu_avr_atmega128_api.h"
mcu avr atmega128 api Az AVR processzor modulhoz készült API jelenlegi verziója a következő támogatásokat tartakmazza: • • • •
Gyakran használt standard könyvtárak (pl. stdio) importálása. Általános célú segédmakrók A mitmót busz GPIO lábainak kezelése, System LED kezelése UART, SPI és I2C kommunikáció
Magas szintű IO kezelés Mint tudjuk, C nyelven az IO eszközöket magas szinten fájlként kezeljük, amibe írhatunk, és ahonnan olvashatunk. Az stdio interfész is – amit pl. a printf és egyéb jól ismert C
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
12
függvények használnak – egy ilyen fájlhoz rendelhető. Az általunk használt fordító sajátossága, hogy az elsőként megnyitott FILE struktúrát értelmezi stdio interfészként. A FILE struktúra megnyitásánál meg kell adnunk hogyan valósul meg az elemi (egy bájtnyi) írási és olvasási művelet a használt eszköz esetében. Ezt az API-ban tesszük meg. Írás: írás a diagnosztikai soros portra: int UART1_put(char c, FILE *f); { loop_until_flag_is_set(UCSR1A, UDRE); UDR1 = c; return 0; }
Olvasás: olvasás a diagnosztikai soros portról. A fogadott karakter visszaküldése, illetve ENTER fogadása esetén korrekció (ENTER esetén a PC \r\n karaktereket küld, de ebből a könyvtári függvények – pl. a gets – a \n-t levágja. Echo esetén azonban ezt is vissza kell küldeni). char UART1_get(void) { loop_until_flag_is_set(UCSR1A, RXC); return UDR1; } int UART1_get_echo(FILE *f); //stdio handler uses this one { int i=UART1_get(); if(i=='\r'){ UART1_put(i); i='\n';} //gets cuts \n UART1_put(i); return i; }
Ezek után a soros port inicializálásakor létrehozhatjuk a FILE struktúrát: FILE *UART1_handler;
//device handler for stdio - set in UART1_Init
void UART1_Init(unsigned long baud_rate, void (*handler)(void)) {
… if(UART1_handler){fclose(UART1_handler); UART1_handler=0;} //if already opened (reinit): close dev. handler UART1_handler=fdevopen(&UART1_put,&UART1_get_echo,0); //open device handler
… }
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
13
Specifikáció: Készítsen programot, ami alkalmas az 1 másodpercnél rövidebb reflex idő ezredmásodperc felbontású mérésére. Az eredményt a soros portra írja ki. 5 másodpercenként gyújtsa ki véletlenszerűen az 1-3 LED-ek egyikét. Ha a felhasználó 1 másodpercen belül megnyomja a kigyújtott LED-nek megfelelő (1-3) gombot, a soros portra írja ki a LED kigyújtása és a gombnyomás közt eltelt időt (ezred másodpercben). Időtúllépés, illetve rossz gomb megnyomása esetén 999-et írjon ki.
A program megtervezése Ehhez a programhoz már nem adunk vázat, csak egy lehetséges megvalósítás tervét. Adjunk a projecthez egy új, reflex.c nevű forrásfájlt (jobb kattintás a project nevén a project navigátorban Æ New Æ Source file), és ebben valósítsuk meg az alábbiakat. Az ezredmásodpercek számolásához és az 5 másodpercenkénti LED kigyújtásához használjuk az elkészített időzítő API-t, a soros port kezeléséhez a mitmót MCU API-t! Ezeken kívül – véletlenszám generáláshoz – még szükségünk lesz az stdlib.h-ban definiált rand() függvényre. Az IT rutinban (void ms_elapsed(void)) elvégzendő feladatok: • sw_cntr1 szoftveres számláló növelése. Ebben számoljuk az eltelt időt ms-ban. sw_cntr1 • Ha eltelt 5 másodperc (sw_cntr1==5000) nullázása, és a fő ciklusban előállított (lsd. később) 0-2 közti véletlenszám (randnum) alapján véletlenszerűen egy LED kigyújtása. A LED-ek állapotát célszerű egy külön változóban (flags) is tárolni. A LED-ek beállítása megoldható az alábbi módon: flags=_BV(4+randnum); PORTA=flags;
Itt _BV(x) makró egy olyan maszkot állít elő, aminek x bitje egy, azaz a „klasszikus” (1<<x) utasításnak felel meg.
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
14
Időtúllépés, jó/rossz gomb megnyomása esetén hasonló műveleteket kell elvégeznünk, így ezeket érdemes egy segédfüggvénybe foglalni (void Done(int num)): • flags=0 – jelezzük, hogy a reflex-idő mérés megtörtént • írjuk ki a soros portra az eredményt • Oltsuk el a LED-eket • Generáljunk új (ál)véletlenszámot: randnum=(unsigned char)rand()%3;
• •
A főprogramban: Inicializáljuk a globális változókat, az A portot, a soros portot és az időzítő API-t. Végtelen ciklusban: ha még nem történt meg a mérés (flags) a. Időtullépés Æ Done(999); b. Gombnyomás történt: i. Jó gomb Æ Done(sw_cntr1); ii. Rossz gomb Æ Done(999); A gombok állapotának lekérése: unsigned char btns=0xf1 | PINA; btns=(~btns)<<3;
Ezután btns és flags változók összehasonlíthatók.
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
15
Függelék – megoldások Bevezető feladat: kis módosítás A for ciklusban megvalósított szoftveres késleltetés után végtelen ciklusban várakozzunk B1 gomb megnyomására. (A késleltetést nem hagyhatjuk el, hisz ekkor a LED egy gombnyomásra többször is „lépne”, mert a ciklus többször is lefut, miközben a gombot nyomva tartjuk): … for(unsigned long int k=0 ;k<30000000; ++k); while(PINA&0x02);
…
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
16
Időzítő API timer_api.h //Toolchain: WinAVR 20060421 #ifndef TIMER_API_H_ #define TIMER_API_H_ /********************************************************************* * Function: TickInit(unsigned int period_ms, void (*handler)(void)) * * PreCondition: * * Input: period_ms: desired IT period in ms //TODO: max. value? * handler: pointer to the IT handler function * * Output: none * * Side Effects: uses TIMER1 compare A * * Overview: Calls the handler function periodically * * * Note: To start timing call TickStart ********************************************************************/ void TickInit(unsigned int period_ms, void (*handler)(void));
/********************************************************************* * Function: TickStart * * PreCondition: Call to TickInit * * Input: none * * Output: none * * Side Effects: uses TIMER1 compare A * * Overview: Enables TIMER1 capture IT * * * Note: ********************************************************************/ void TickStart(void);
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
17
/********************************************************************* * Function: TickStop * * PreCondition: Call to TickInit * * Input: none * * Output: none * * Side Effects: uses TIMER1 compare A * * Overview: Disables TIMER1 capture IT * * * Note: ********************************************************************/ void TickStop(void); #endif /*TIMER_API_H_*/
timer_api.c #include "timer_api.h" #include #include
void (*Tick_IT_Handler)(void)=0; //pointer to IT handler function - set in TickInit ISR(TIMER1_COMPA_vect) { if(Tick_IT_Handler) Tick_IT_Handler();} //TODO: ISR vector?
void TickInit(unsigned int period_ms, void (*handler)(void)) { Tick_IT_Handler=handler; //TODO TCCR1A=0; //Normal port operation (IO pins not connected) TCCR1B=0x0b; //CTC mode prescale 64 --> T=8 us if cclk=8 MHz OCR1A=F_CPU/64000*period_ms; //Period of ITs sei(); //Enable global ITs //TODO end }
void TickStart(void) { //TODO TIMSK=0x10; //Output Compare IT TIMER1/A enabled //TODO end }
void TickStop(void) { //TODO TIMSK=0x00; //Output Compare IT TIMER1/A disabled //TODO end }
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
18
Reflexidő mérése (reflex.c) //Toolchain: WinAVR 20060421 #include "../avr_mitmot_api/mcu_avr_atmega128_api.h" #include "timer_api.h" #include <stdlib.h> //global unsigned unsigned unsigned
variables int sw_cntr1; char randnum=0; char flags=0;
//The IT handler function void ms_elapsed(void) { ++sw_cntr1; if(sw_cntr1==5000) { sw_cntr1=0; flags=_BV(4+randnum); PORTA=flags; } }
void Done(int num) { flags=0; printf("Ido: %d\r\n",num); PORTA=0; randnum=(unsigned char)rand()%3; } int main(void) { //init sw_cntr1=0; Done(0); DDRA=0xf0; UART1_Init(9600,NULL); TickInit(1, ms_elapsed); TickStart(); while(1) if(flags) //not done { if(sw_cntr1>999) Done(999); else { unsigned char btns=0xf1 | PINA; btns=(~btns)<<3; if(btns) //button pressed { if(btns==flags) Done(sw_cntr1); else Done(999); } }//else }//while }
Mitmót fakultatív labor I. BME MIT 2006. -D02a Csordás Péter ([email protected]),Scherer Balázs ([email protected]) 2006-09-25
19