A szövegben levő programokat megcsináltam. ZIP-fájlokban benne vannak az ASM és a HEX fájlok.
Proteus-ban ki vannak próbálva és működnek.
Proteus-ban ki vannak próbálva és működnek.
Click to set custom HTML
01_első.zip | |
File Size: | 0 kb |
File Type: | zip |
02_bevitel_kezeles.zip | |
File Size: | 0 kb |
File Type: | zip |
03_feltétel_kezelés.zip | |
File Size: | 0 kb |
File Type: | zip |
04_forgat.zip | |
File Size: | 0 kb |
File Type: | zip |
05_forgat_02.zip | |
File Size: | 0 kb |
File Type: | zip |
06_timer.zip | |
File Size: | 0 kb |
File Type: | zip |
07_timer_02.zip | |
File Size: | 0 kb |
File Type: | zip |
08_irány_timerrel.zip | |
File Size: | 1 kb |
File Type: | zip |
Tartalom
PIC
Üdvözöllek Kedves Látogató!
Itt a PIC-el kapcsolatos dolgokat próbálok leírni.
Mielőtt továbbmennék, szeretném megemlíteni, hogy én is tanulom a programozást.
Igaz, hogy csináltam LED-villogókat, zenelejátszókat, ezek alapjához kaptam segítséget.
Ezért inkább azt szeretném leírni, hogy én honnan szedem össze a tanulmányaimhoz szükséges dolgokat.
Nagyon sok leírás van a NET-en, de vagy haladóknak szól, vagy ha kezdőkhöz,
az meg hosszan tárgyalja a PIC- működését.
A fentiek miatt nagyon sokat keresgéltem, hogy találjak egy érthetően leírt blogot.
Általában minden leírás LED villogtatással indul, utána már az LCD kijelzőkkel foglalkozik.
Milyen nyelven programozzunk?
Sok évvel ezelőtt találtam egy nagyon jó leírást, ez az Assembly nyelvvel foglalkozik.
Ezen az oldalon : http://www.mulder82.atw.hu/linkek.php
Sajnos ez az oldal már több éve nem működik.
********************************************************
Mi az a PIC?
Alapfogalmak a PIC-ről
Kezdeti lépések
Mi az a PIC?
A PIC mikrokontroller a Microchip cég által kifejlesztett igen hasznos eszköz. Egyetlen IC, de egy komplett digitális számítógépet foglal magába. A tananyag során a PIC16F84 kerül bemutatásra, mert ezt a legegyszerűbb programozni. Ha már ennek a kezelését elsajátítottuk, a továbbiakban nem lesz nehéz átállni másik típusra. A PIC-hez egy PC-s szoftverrel lehet programot készíteni, ugyanígy egy megfelelő áramkörrel lehet felprogramozni. Az áramkör rajza és az égető megtalálható a PIC letöltési oldalon, a fejlesztői környezet (MPLAB) pedig letölthető a Microchip honlapjáról, a http://www.microchip.com-ról (sajnos 10 MB-os terjedelme miatt nem kerülhetett fel a honlapomra). Érdemes még letölteni az angol nyelvű dokumentációt, mert a teljes assembly parancslista megtalálható benne részletezve (30430c.pdf).
PIC
Üdvözöllek Kedves Látogató!
Itt a PIC-el kapcsolatos dolgokat próbálok leírni.
Mielőtt továbbmennék, szeretném megemlíteni, hogy én is tanulom a programozást.
Igaz, hogy csináltam LED-villogókat, zenelejátszókat, ezek alapjához kaptam segítséget.
Ezért inkább azt szeretném leírni, hogy én honnan szedem össze a tanulmányaimhoz szükséges dolgokat.
Nagyon sok leírás van a NET-en, de vagy haladóknak szól, vagy ha kezdőkhöz,
az meg hosszan tárgyalja a PIC- működését.
A fentiek miatt nagyon sokat keresgéltem, hogy találjak egy érthetően leírt blogot.
Általában minden leírás LED villogtatással indul, utána már az LCD kijelzőkkel foglalkozik.
Milyen nyelven programozzunk?
Sok évvel ezelőtt találtam egy nagyon jó leírást, ez az Assembly nyelvvel foglalkozik.
Ezen az oldalon : http://www.mulder82.atw.hu/linkek.php
Sajnos ez az oldal már több éve nem működik.
********************************************************
Mi az a PIC?
Alapfogalmak a PIC-ről
Kezdeti lépések
Mi az a PIC?
A PIC mikrokontroller a Microchip cég által kifejlesztett igen hasznos eszköz. Egyetlen IC, de egy komplett digitális számítógépet foglal magába. A tananyag során a PIC16F84 kerül bemutatásra, mert ezt a legegyszerűbb programozni. Ha már ennek a kezelését elsajátítottuk, a továbbiakban nem lesz nehéz átállni másik típusra. A PIC-hez egy PC-s szoftverrel lehet programot készíteni, ugyanígy egy megfelelő áramkörrel lehet felprogramozni. Az áramkör rajza és az égető megtalálható a PIC letöltési oldalon, a fejlesztői környezet (MPLAB) pedig letölthető a Microchip honlapjáról, a http://www.microchip.com-ról (sajnos 10 MB-os terjedelme miatt nem kerülhetett fel a honlapomra). Érdemes még letölteni az angol nyelvű dokumentációt, mert a teljes assembly parancslista megtalálható benne részletezve (30430c.pdf).
Alapfogalmak a PIC-ről
A PIC16F84 két párhuzamos IO porttal rendelkezik (egy 5 és egy 8 bitessel), melyekkel kapcsolatot tud teremteni a külvilággal. A két port bitjeit külön-külön beállíthatjuk, hogy input vagy output adatforgalmat bonyolítsanak le. Órajelet egy rezgőkvarccal adhatunk a PIC-nek, ami maximum 10 MHz lehet. A programok időzítését segíti, hogy a PIC minden egyes utasítása egy órajel ciklus alatt végrehajtódik, kivéve az ugró és elágazó utásítások, melyek két órajel ciklust igényelnek.
Bevezetésként ennyit elég tudnunk, a többit majd "munka közben" tárgyaljuk.
A PIC16F84 két párhuzamos IO porttal rendelkezik (egy 5 és egy 8 bitessel), melyekkel kapcsolatot tud teremteni a külvilággal. A két port bitjeit külön-külön beállíthatjuk, hogy input vagy output adatforgalmat bonyolítsanak le. Órajelet egy rezgőkvarccal adhatunk a PIC-nek, ami maximum 10 MHz lehet. A programok időzítését segíti, hogy a PIC minden egyes utasítása egy órajel ciklus alatt végrehajtódik, kivéve az ugró és elágazó utásítások, melyek két órajel ciklust igényelnek.
Bevezetésként ennyit elég tudnunk, a többit majd "munka közben" tárgyaljuk.
Kezdeti lépések
Először is készítsünk el egy, a PIC feltöltéséhez szükséges áramkört. Alant a COM-84 nevű, JDM kompatíbilis áramkör rajza található, mely magyarra lefordítva a legegyszerűbb áramkör, ami PIC16F84-et tud programozni. Ezzel azonban vannak problémák, így érdemes megtekinteni további PIC programozó áramköröket, melyek rajzai megtalálhatók a PIC főoldalon.
Először is készítsünk el egy, a PIC feltöltéséhez szükséges áramkört. Alant a COM-84 nevű, JDM kompatíbilis áramkör rajza található, mely magyarra lefordítva a legegyszerűbb áramkör, ami PIC16F84-et tud programozni. Ezzel azonban vannak problémák, így érdemes megtekinteni további PIC programozó áramköröket, melyek rajzai megtalálhatók a PIC főoldalon.
A PIC helyére csak egy IC foglalatot forrasszunk be, hiszen felprogramozás után ki szeretnénk venni az áramkörből a PIC-et! Másodszor legyen egy próbapanelünk, amivel ki tudjuk próbálni a felprogramozott mikrokontrollert:******************************************************
Ugyanúgy mint az előbb, az áramkörbe a PIC helyett egy IC foglalatot rakjunk, hogy később a PIC-et átrakhassuk a programozóba. G1 és G2 helyekre nyomógombokat rakjunk!
Az áramkörök teszteléséhez égessük a PIC-be a tesztfájlt. Ha mindent jól csináltunk, akkor egy futófény-effektet kapunk.
A következő számban már mi fogjuk programozni a PIC-et!
Ugyanúgy mint az előbb, az áramkörbe a PIC helyett egy IC foglalatot rakjunk, hogy később a PIC-et átrakhassuk a programozóba. G1 és G2 helyekre nyomógombokat rakjunk!
Az áramkörök teszteléséhez égessük a PIC-be a tesztfájlt. Ha mindent jól csináltunk, akkor egy futófény-effektet kapunk.
A következő számban már mi fogjuk programozni a PIC-et!
- 2. szám: Az MPLAB beállítása és az első program
Tartalom
Az MPLAB beállítása
Az első program
A program lefordítása és hibakeresése (debuggolása)
Beégetés Az MPLAB beállítása
Úgy gondolom, az MPLAB merevlemezre való telepítése nem igényel különösebb instrukciót, így kezdjük rögtön a beállításával. Indítsuk el a Start menü -> Programok ->Microchip MPLAB -> MPLAB helyről. Ha Windows 3.1 vagy 3.11-et használunk, akkor a Programkezelő Microchip MPLAB programcsoportjából indítsuk az MPLAB ikont. Ekkor a program főablakát láthatjuk.
Nyissunk meg egy új projektet a Project/New project menüponttal. Ekkor a program az új projekt nevét fogja kérni. Névnek állítsuk be hogy "tan.pjt", könyvtárnak pedig mindegy hogy mit, de a kiválasztott könyvtár legyen üres az áttekinthetőség kedvéért. A legjobb, ha külön erre a célra létrehozunk egy új könyvtárat. Ha ezzel megvagyunk, nyomjunk OK-t
Tartalom
Az MPLAB beállítása
Az első program
A program lefordítása és hibakeresése (debuggolása)
Beégetés Az MPLAB beállítása
Úgy gondolom, az MPLAB merevlemezre való telepítése nem igényel különösebb instrukciót, így kezdjük rögtön a beállításával. Indítsuk el a Start menü -> Programok ->Microchip MPLAB -> MPLAB helyről. Ha Windows 3.1 vagy 3.11-et használunk, akkor a Programkezelő Microchip MPLAB programcsoportjából indítsuk az MPLAB ikont. Ekkor a program főablakát láthatjuk.
Nyissunk meg egy új projektet a Project/New project menüponttal. Ekkor a program az új projekt nevét fogja kérni. Névnek állítsuk be hogy "tan.pjt", könyvtárnak pedig mindegy hogy mit, de a kiválasztott könyvtár legyen üres az áttekinthetőség kedvéért. A legjobb, ha külön erre a célra létrehozunk egy új könyvtárat. Ha ezzel megvagyunk, nyomjunk OK-t
Ezután a projekt jellemzőit kell megadnunk. A Development Mode mezeje melletti Change gombra klikkeljünk rá. Ekkor megadhatjuk, hogy milyen PIC-hez akarunk fejleszteni. Mi most a PIC16F84-gyel foglalkozunk, ezért állítsuk be az alábbiakat:
- MPLAB-SIM Simulator
- Processor: 16F84
A többi beállítással nem kell foglalkoznunk, nyomjunk OK-t.
Klikkeljünk az Add Node gombra.
- MPLAB-SIM Simulator
- Processor: 16F84
A többi beállítással nem kell foglalkoznunk, nyomjunk OK-t.
Klikkeljünk az Add Node gombra.
A fájlnévhez írjuk be hogy "elso.asm", könyvtárnak pedig válasszuk ki ugyanazt a könyvtárat, amit az előbb is megadtunk.
Ha ezzel készen vagyunk, nyomjunk OK-t.
Ha ezzel készen vagyunk, nyomjunk OK-t.
Kész beállítás panel ezek után így kell, hogy kinézzen:
Jelöljük ki az "elso [.hex]" sort, majd klikkeljünk a Node Properties-re.
Jelöljük ki az "elso [.hex]" sort, majd klikkeljünk a Node Properties-re.
Töltsük ki a táblázatot úgy, hogy ezt kapjuk:
Nyomjunk OK-t. Majd a projekt beállítása ablakon is nyomjuk egy OK-t.
Nyomjunk OK-t. Majd a projekt beállítása ablakon is nyomjuk egy OK-t.
Visszakerültünk a főképernyőre. Válasszuk ki a Project/Save Project menüpontot, hogy elmentsük a beállításokat.
Válasszuk ki a File/New menüpontot, majd a File/Save As-t. Fájlnévnek írjuk be hogy "elso.asm", a könyvtárnak pedig válasszuk ki a szokásos projektkönyvtárunkat, ha nem az lenne kijelölve. Ezt OK-zzuk le. Az ablak fejlécében meg is jelenik a fájl neve. Méretezzük az ablakut úgy, hogy kényelmesen elférjen alá egy másik ablak is. Innentől ezt az ablakot programablaknak fogjuk hívni, mert a programunkat ebbe írjuk majd.
Válasszuk ki a Window/Watch Windows/New Watch Window menüpontot. A megjelenő ablak tetején levő Symbol sorba írjunk egy "w"-t, majd klikkeljünk az Add gombra. Ugyanígy írjuk be hogy "portb", majd újfent klikkeljünk az Add gombra.
Végül nyomjunk Close-ot.
Válasszuk ki a File/New menüpontot, majd a File/Save As-t. Fájlnévnek írjuk be hogy "elso.asm", a könyvtárnak pedig válasszuk ki a szokásos projektkönyvtárunkat, ha nem az lenne kijelölve. Ezt OK-zzuk le. Az ablak fejlécében meg is jelenik a fájl neve. Méretezzük az ablakut úgy, hogy kényelmesen elférjen alá egy másik ablak is. Innentől ezt az ablakot programablaknak fogjuk hívni, mert a programunkat ebbe írjuk majd.
Válasszuk ki a Window/Watch Windows/New Watch Window menüpontot. A megjelenő ablak tetején levő Symbol sorba írjunk egy "w"-t, majd klikkeljünk az Add gombra. Ugyanígy írjuk be hogy "portb", majd újfent klikkeljünk az Add gombra.
Végül nyomjunk Close-ot.
Az új ablakban, amit a főképernyőre kaptunk, megjelennek az általunk kiválasztott regiszterek. Ez az ablak a Watch ablak, ami futás közben a regiszterek állapotát mutatja. (Ez hibakeresésnél nagyon hasznos. Márpedig hibákat mindig ejt az ember programozás közben.) Később további regisztereket is felveszünk ebbe. Húzzuk a Watch ablakot a programablak mellé vagy alá, hogy mindkettőt látni lehessen. Példaként itt van hogy én hogy csináltam, de akárhogyan máshogy is átrendezhetjük, akár a Window/Tile Horizontal ill. Tile Vertical menüpont segítségével is.
Ha az elrendezéssel készen vagyunk, válasszuk ki a Project/Save Project menüpontot, hogy beállításainkat véglegesítsük. Befejeztük a konfigurálást, kezdhetünk is programozni.Az első programHa elkészítettük az előző részben tárgyalt PIC próbapanelt és égetőpanelt, valamint beleégettük a PIC-ünkbe a teszt.hex file-t, akkor először is sikeresen kitaláltuk hogyan kell kezelni az égetőprogramot (amit a későbbiekben majd az első program végén tárgyalunk is), másrészt elgondolkodhattunk azon, hogy milyen jó lenne ha magunk is tudnánk ilyet írni. Miért is ne, írjunk egy ilyet!
Programozás közben tartsuk be az alábbi szabályokat, mert egyrészt átláthatóbbá teszi a programot, másrészt az MPLAB szólni fog miatta ha megszegjük őket:
1. Címke csak az első oszlopba kerülhet,
2. utasítás csak a másodikba,
3. operandus pedig csak a harmadikba (és logikusan csak utasítás után).
4. Megjegyzéseket pontosvesszővel kell bevezetni és bárhol állhatnak.
5. Ez pedig csak ajánlott: Az oszlopokat TAB-bal válasszuk el.
Rögtön az első kérdés ami felmerül bennünk: Hogy mi van? Mi ez az egész? Mi az hogy operandus, miféle oszlopok, hova kell címke?
Mindjárt láthatjuk miről van szó.
Először is gépeljük be az alábbi programrészletet a programablakba, aztán jön a magyarázat:
(Begépeléskor nyomjunk egy TAB-ot azoknál a soroknál, ahol jól láthatóan beljebb kezdődik a szöveg. Ugyanígy az "ENDC" sortól kezdve az összes üres helynél TAB-ot használjunk, ne space-t!)
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_OFF
CBLOCK 0x0C
T1
T2
ENDC
ORG 0
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'00000000'
MOVWF TRISB
BCF STATUS,RP0 ;BANK0
Egyre bonyolultabb? Lássuk hogy mik ezek a kínai dolgok. Nem kell megijedni, a fenti kódrészletet egy ideig nem szükséges megértenünk, csak az MPLAB-nak ad további instrukciókat, valamint a PIC-et állítja be. Egy jó ideig a programjaink ezekkel a sorokkal kezdődnek majd. (Ettől függetlenül az elejét egy kicsit nagyító alá vesszük.)
Kezdjük először is a fogalmakkal: Láthatjuk, hogy a sorok java része beljebb keződik (pontosan egy TAB-nyi távolságra a sor elejétől), ezek a második oszlopban vannak. A "TRISA", "TRISB", stb. pedig a harmadik oszlopban. Ezek az operandusok, vagyis az utasításokhoz rendelt paraméterek. Ha ránézünk a kódra, már rögtön láthatjuk is, hogy miért "oszlop" a nevük. A TAB billentyű segítségével oszlopokba rendezhetjük az egyes elemeket. Megfigyelhetjük, hogy van még egy ";BANK1" és egy ";BANK0" rész is. Mint feljebb említettem, ezek az ún. kommentek, vagyis ami a pontosvessző után áll azt, az MPLAB nem veszi figyelembe. Így rakhatunk magunknak megjegyzéseket a forráskódba, ill. hibakeresés közben ezzel iktathatunk ki egy-egy sort. Ezek után lássuk, hogyan épül fel egy sor:
<címke> <utasítás> <operandus>
Most pedig egy picit nézzük meg a kód elejét! Az első három sorral nem kell foglalkoznunk.
Az azt követő sorok már érdekesebbek.
CBLOCK 0x0C
T1
T2
ENDC
Ha programoztunk már valaha valamilyen nyelven, akkor röviden annyit mondhatunk, hogy ez itt a T1 és T2 nevű változók deklarálása, vagyis hogy mit fogunk használni a programban. A változókat a PIC esetében regisztereknek nevezzük. Ha még sosem foglalkoztunk más programnyelvvel, akkor ezt úgy kell felfogni, hogy veszünk egy-egy darabot a memóriából és elkereszteljük őket T1, valamint T2-nek. Innentől pedig bármilyen értéket beleírhatunk 0 és 255 között, valamint ezt vissza is olvashatjuk, amikor csak akarjuk. A CBLOCK után álló 0x0C azt jelzi, hogy a PIC hanyadik regiszterétől kezdve foglalja le nekünk T1 és T2 regisztereket a program. Ez lehetőleg mindig 0C legyen (A "0x" bevezetés hexadecimális értéket jelöl), mert az az előttiek más célokra vannak fenntartva.
A további részekben van maga a program, amit már a PIC futtatni fog. Itt beállítjuk a PIC 5 bites A portját fogadásra (input), a 8 bites B portját pedig kivitelre (output). Feltűnhet, hogy az utasításokban igen sok W szerepel. Van ugyanis egy W nevű regiszterünk, ez más néven az akkumulátor. Ez a regiszter a PIC legközpontibb regisztere. Ennek a segítségével végez műveleteket, a műveletek erdeménye is ide kerül (ha nem adunk meg mást), valamint az utasítások és különböző regiszterek között is ezzel vihetünk át adatokat. A sok állítgatás után éppen itt az ideje, hogy begépeljünk valami hasznosat is. Akkor hát rajta!
VISSZA: MOVLW B'10000000'
MOVWF PORTB
CALL DELAY
MOVLW B'01000000'
MOVWF PORTB
CALL DELAY
MOVLW B'00100000'
MOVWF PORTB
CALL DELAY
MOVLW B'00010000'
MOVWF PORTB
CALL DELAY
MOVLW B'00001000'
MOVWF PORTB
CALL DELAY
MOVLW B'00000100'
MOVWF PORTB
CALL DELAY
MOVLW B'00000010'
MOVWF PORTB
CALL DELAY
MOVLW B'00000001'
MOVWF PORTB
CALL DELAY
MOVLW B'00000000'
MOVWF PORTB
CALL DELAY
MOVLW B'00000001'
MOVWF PORTB
CALL DELAY
MOVLW B'00000010'
MOVWF PORTB
CALL DELAY
MOVLW B'00000100'
MOVWF PORTB
CALL DELAY
MOVLW B'00001000'
MOVWF PORTB
CALL DELAY
MOVLW B'00010000'
MOVWF PORTB
CALL DELAY
MOVLW B'00100000'
MOVWF PORTB
CALL DELAY
MOVLW B'01000000'
MOVWF PORTB
CALL DELAY
MOVLW B'10000000'
MOVWF PORTB
CALL DELAY
MOVLW B'00000000'
MOVWF PORTB
CALL DELAY
GOTO VISSZA
Jó hosszú, de nem érthetetlen, lévén, hogy majdnem ugyanaz ismétlődik egyfolytában: egy MOVLW, egy MOVWF és egy CALL utasítás. Ráadásul az operandus csak a MOVLW-nél változik.
Mit is csinál ez a rész? Az elején van egy VISSZA címke, melyre a programrészlet végén hivatkozunk (GOTO VISSZA). A GOTO parancs hatására a program a megadott címkétől fog folytatódni. (Jelen esetben a VISSZA címkétől, vagyis a programrészlet végén visszaugrunk az elejére). Ezután jön a három ismétlődő sor:
MOVLW B'01000000'
MOVWF PORTB
CALL DELAY
A MOVLW parancs feladata az, hogy az operandust töltse be az akkumulátorba (W regiszterbe). Azt mondtuk, hogy a regiszterek 0 és 255 között kaphatnak értéket. Akkor mi ez a B'01000000'? Nos, ez egy bináris érték. Az MPLAB lehetővé teszi, hogy a számokat megadhassuk hexadecimális, decimális, vagy bináris formátumban. Ezt többféleképpen is tehetjük, néhány példa:
H'40' vagy 0x40 vagy 40 - hexadecimális 40
D'64' vagy .64 - decimális 64
B'01000000' - bináris 01000000
A fenti példák mind ugyanazt az értéket adják vissza, csak más számrendszerben adtuk meg őket. Arra ügyeljünk ha hexadecimális számot előtag nélkül adunk meg (tehát csak 40-et és nem pl. 0x40-et), hogy ha a szám betűvel kezdődik (pl. C0), akkor egy nullát feltétlenül elé kell írnunk (Tehát pl. 0F0)! Felmerülhet a kérdés, hogy miért nem írtuk be akkor egyszerűen azt, hogy MOVLW .64? Nos azért nem, mert ezt az értéket kívánjuk kiküldeni a B portra, ahova a LED-ek csatlakoztatva vannak, aztán így egyszerűbb megadni: a bináris érték 8 helyiértéke megfelel a 8 LED-nek. Amelyik helyiérték 1, ott a LED világítani fog, amelyik 0, ott nem fog világítani. A fenti érték - miután kiküldtük a B portra - tehát a második LED-et kigyújtja.
A következő parancs (MOVWF) az akkumulátor tartalmát (melybe az imént raktuk a bináris értékünket) kiküldi az operandusban megadott regiszterbe, jelen esetben a B portra. Már most elgondolkodhattunk azon, hogy ha már ilyen minimális különbségek vannak az utasítások között - és ez még csak a kezdet -, akkor mi lesz később? Nos, igazából azok vannak előnyben, akik tudnak angolul. Ezek a furcsán kinéző utasítások ugyanis angol rövidítések. A MOV kezdetű utasítások az angol "move" (mozgatás) szóból származnak, tehát valamit valahova mozgatnak. Hogy mit hova, azt az utasítás vége mondja meg:
L - az operandusként megadott konstans
F - az operandusként megadott regiszter
W - az akkumulátor
Tehát a MOVWF PORTB az az akkumulátor tartalmát tölti be a PORTB-be, a MOVFW PORTB meg éppen fordítva, vagyis a PORTB-t olvassa vissza az akkumulátorba.
Láthatjuk, hogy a két MOV utasítás mindössze annyit tett, hogy a B portra kiküldött egy konstans értéket. De akkor miért kellett közbeiktatni az akkumulátort? Nem lehetett volna egy olyan utasítást használni, hogy MOVLF? A válasz az, hogy nem. Egy utasítás ugyanis csak egy operandust tartalmazhat, így viszont kettőt kellett volna megadni (a bináris értéket és a PORTB-t). Van egy speciális eset, amikor aritmetikai műveleteknél - pl. egy összeadás esetén - megmondhatjuk hogy a végeredmény az operadusban levő regiszterbe (ha az operandus egy regiszter) vagy az akkumulátorba kerüljön. Ekkor ezt az operandus mellé kell írni vesszővel elválasztva.
További információt a MOV utasításokról a letölthető dokumentum 62. oldalán találhatunk.
Menjünk tovább. Egy CALL DELAY utasítást láthatunk. A CALL utasítással meghívhatunk egy szubrutint. A szubrutinok lényege, hogy hívásukkor lefutnak úgy, mintha egy GOTO utsaítással ugrottunk volna oda, majd a rutin végén visszatér a futás arra a pontra, ahonnan hívtuk a rutint. Így a szubrutint a program bármely pontjáról meghívhatjuk (és mint a CALL DELAY utasítások számából látható, meg is hívjuk párszor a DELAY-t), mindig ugyanoda fog visszatérni, ahonnan hívták. A DELAY szubrutin feladata, hogy várakozik egy rövid ideig. Erre szükségünk van, hiszen miután kigyújtottunk egy LED-et, hagyni kell azt egy kicsit égni, mielőtt a következőre mennénk. Feltűnhet persze, hogy ha a CALL úgy működik először mint a GOTO, akkor kéne valami DELAY címkét látnunk. Valóban, a várakoztató rutint még nem írtuk meg. Nos, akkor jöjjön az!
DELAY: MOVLW d'150'
MOVWF T1
DEL: MOVLW d'255'
MOVWF T2
DEL1: NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
DECFSZ T2,f
GOTO DEL1
DECFSZ T1,f
GOTO DEL
RETURN
END
Na ebben már van pár címke. Meg pár ismerős utasítás is, de javarészt azért ismeretlen. A MOVLW - MOVWF párost már ismerjük (Konstans érték akkumulátorba, akkumulátor regiszterbe), vagyis T1 és T2 regisztereket feltöltjük kezdőértékekkel. Itt már decimális számokat használtunk, nem egyszerűsítené a feladatunkat a bináris vagy hexadecimális értékek használata. A NOP utasítás feladata, hogy ne csináljon semmit. Abszolút semmit. Mindössze azért kell (és ennyi), hogy amíg ezeken átfut a program, eltelik egy kis idő. A DECFSZ már egy kicsit érdekesebb (és bonyolultabb). Ennek az a feladata, hogy az operandusban megadott regisztert (itt csak regiszter lehet az operandus) eggyel csökkentse. Ha pedig ez a regiszter nulla értékű lesz a csökkentéstől (tehát 1 volt az értéke), akkor a következő utasítást ugorja át a program. (Itt fennáll az operandusok speciális esete, vagyis meg kell adni az operandus mellé, hogy T2 eggyel csökkentett értéke hova kerüljön. "f" esetén vissza T2-be, "w" esetén pedig az akkumulátorba. Ez utóbbi esetben logikusan T2 értéke változatlan marad.)
A GOTO DEL1 hatására visszaugrunk a DEL1 címkére. Látható, hogy addig fogunk visszaugrani, amíg a DECFSZ T2,f utasítás le nem nulláza T2 regisztert. Ekkor ugyanis átugorjuk a GOTO DEL1 utasítást.
A DECFSZ T1,f utasítás ugyanígy fog működni. Minthogy T1 még nem 1, ezért visszaugrunk a DEL címkére. Hoppá, itt viszont T2 megint felveszi a kezdeti értékét, tehát az előző ciklus újra lefut. Aztán megint csökken egyet T1, majd a ciklus újra lefut. Mindezt pontosan 150-szer teszi meg, minthogy addig nem tudunk továbblépni, amíg T1 nem nullázódik le és DECFSZ T1,f tovább nem engedi. Hasonlóan az előző, DECFSZ T2,f-hez, T1 kinullázódásakor átlépünk a GOTO DEL utasításon.
A RETURN utasítás segítségével visszatérünk arra a pontra, ahonnan egy CALL-lal hívtuk a szubrutint. Az END utasítás már nem a szubrutinhoz tartozik. Ez egy formai megkötés, hogy minden programnak ezzel kell végződnie.
Készen is vagyunk az első programmal. A teljes, megírt programot letölthetjük innen.
Programozás közben tartsuk be az alábbi szabályokat, mert egyrészt átláthatóbbá teszi a programot, másrészt az MPLAB szólni fog miatta ha megszegjük őket:
1. Címke csak az első oszlopba kerülhet,
2. utasítás csak a másodikba,
3. operandus pedig csak a harmadikba (és logikusan csak utasítás után).
4. Megjegyzéseket pontosvesszővel kell bevezetni és bárhol állhatnak.
5. Ez pedig csak ajánlott: Az oszlopokat TAB-bal válasszuk el.
Rögtön az első kérdés ami felmerül bennünk: Hogy mi van? Mi ez az egész? Mi az hogy operandus, miféle oszlopok, hova kell címke?
Mindjárt láthatjuk miről van szó.
Először is gépeljük be az alábbi programrészletet a programablakba, aztán jön a magyarázat:
(Begépeléskor nyomjunk egy TAB-ot azoknál a soroknál, ahol jól láthatóan beljebb kezdődik a szöveg. Ugyanígy az "ENDC" sortól kezdve az összes üres helynél TAB-ot használjunk, ne space-t!)
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_OFF
CBLOCK 0x0C
T1
T2
ENDC
ORG 0
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'00000000'
MOVWF TRISB
BCF STATUS,RP0 ;BANK0
Egyre bonyolultabb? Lássuk hogy mik ezek a kínai dolgok. Nem kell megijedni, a fenti kódrészletet egy ideig nem szükséges megértenünk, csak az MPLAB-nak ad további instrukciókat, valamint a PIC-et állítja be. Egy jó ideig a programjaink ezekkel a sorokkal kezdődnek majd. (Ettől függetlenül az elejét egy kicsit nagyító alá vesszük.)
Kezdjük először is a fogalmakkal: Láthatjuk, hogy a sorok java része beljebb keződik (pontosan egy TAB-nyi távolságra a sor elejétől), ezek a második oszlopban vannak. A "TRISA", "TRISB", stb. pedig a harmadik oszlopban. Ezek az operandusok, vagyis az utasításokhoz rendelt paraméterek. Ha ránézünk a kódra, már rögtön láthatjuk is, hogy miért "oszlop" a nevük. A TAB billentyű segítségével oszlopokba rendezhetjük az egyes elemeket. Megfigyelhetjük, hogy van még egy ";BANK1" és egy ";BANK0" rész is. Mint feljebb említettem, ezek az ún. kommentek, vagyis ami a pontosvessző után áll azt, az MPLAB nem veszi figyelembe. Így rakhatunk magunknak megjegyzéseket a forráskódba, ill. hibakeresés közben ezzel iktathatunk ki egy-egy sort. Ezek után lássuk, hogyan épül fel egy sor:
<címke> <utasítás> <operandus>
Most pedig egy picit nézzük meg a kód elejét! Az első három sorral nem kell foglalkoznunk.
Az azt követő sorok már érdekesebbek.
CBLOCK 0x0C
T1
T2
ENDC
Ha programoztunk már valaha valamilyen nyelven, akkor röviden annyit mondhatunk, hogy ez itt a T1 és T2 nevű változók deklarálása, vagyis hogy mit fogunk használni a programban. A változókat a PIC esetében regisztereknek nevezzük. Ha még sosem foglalkoztunk más programnyelvvel, akkor ezt úgy kell felfogni, hogy veszünk egy-egy darabot a memóriából és elkereszteljük őket T1, valamint T2-nek. Innentől pedig bármilyen értéket beleírhatunk 0 és 255 között, valamint ezt vissza is olvashatjuk, amikor csak akarjuk. A CBLOCK után álló 0x0C azt jelzi, hogy a PIC hanyadik regiszterétől kezdve foglalja le nekünk T1 és T2 regisztereket a program. Ez lehetőleg mindig 0C legyen (A "0x" bevezetés hexadecimális értéket jelöl), mert az az előttiek más célokra vannak fenntartva.
A további részekben van maga a program, amit már a PIC futtatni fog. Itt beállítjuk a PIC 5 bites A portját fogadásra (input), a 8 bites B portját pedig kivitelre (output). Feltűnhet, hogy az utasításokban igen sok W szerepel. Van ugyanis egy W nevű regiszterünk, ez más néven az akkumulátor. Ez a regiszter a PIC legközpontibb regisztere. Ennek a segítségével végez műveleteket, a műveletek erdeménye is ide kerül (ha nem adunk meg mást), valamint az utasítások és különböző regiszterek között is ezzel vihetünk át adatokat. A sok állítgatás után éppen itt az ideje, hogy begépeljünk valami hasznosat is. Akkor hát rajta!
VISSZA: MOVLW B'10000000'
MOVWF PORTB
CALL DELAY
MOVLW B'01000000'
MOVWF PORTB
CALL DELAY
MOVLW B'00100000'
MOVWF PORTB
CALL DELAY
MOVLW B'00010000'
MOVWF PORTB
CALL DELAY
MOVLW B'00001000'
MOVWF PORTB
CALL DELAY
MOVLW B'00000100'
MOVWF PORTB
CALL DELAY
MOVLW B'00000010'
MOVWF PORTB
CALL DELAY
MOVLW B'00000001'
MOVWF PORTB
CALL DELAY
MOVLW B'00000000'
MOVWF PORTB
CALL DELAY
MOVLW B'00000001'
MOVWF PORTB
CALL DELAY
MOVLW B'00000010'
MOVWF PORTB
CALL DELAY
MOVLW B'00000100'
MOVWF PORTB
CALL DELAY
MOVLW B'00001000'
MOVWF PORTB
CALL DELAY
MOVLW B'00010000'
MOVWF PORTB
CALL DELAY
MOVLW B'00100000'
MOVWF PORTB
CALL DELAY
MOVLW B'01000000'
MOVWF PORTB
CALL DELAY
MOVLW B'10000000'
MOVWF PORTB
CALL DELAY
MOVLW B'00000000'
MOVWF PORTB
CALL DELAY
GOTO VISSZA
Jó hosszú, de nem érthetetlen, lévén, hogy majdnem ugyanaz ismétlődik egyfolytában: egy MOVLW, egy MOVWF és egy CALL utasítás. Ráadásul az operandus csak a MOVLW-nél változik.
Mit is csinál ez a rész? Az elején van egy VISSZA címke, melyre a programrészlet végén hivatkozunk (GOTO VISSZA). A GOTO parancs hatására a program a megadott címkétől fog folytatódni. (Jelen esetben a VISSZA címkétől, vagyis a programrészlet végén visszaugrunk az elejére). Ezután jön a három ismétlődő sor:
MOVLW B'01000000'
MOVWF PORTB
CALL DELAY
A MOVLW parancs feladata az, hogy az operandust töltse be az akkumulátorba (W regiszterbe). Azt mondtuk, hogy a regiszterek 0 és 255 között kaphatnak értéket. Akkor mi ez a B'01000000'? Nos, ez egy bináris érték. Az MPLAB lehetővé teszi, hogy a számokat megadhassuk hexadecimális, decimális, vagy bináris formátumban. Ezt többféleképpen is tehetjük, néhány példa:
H'40' vagy 0x40 vagy 40 - hexadecimális 40
D'64' vagy .64 - decimális 64
B'01000000' - bináris 01000000
A fenti példák mind ugyanazt az értéket adják vissza, csak más számrendszerben adtuk meg őket. Arra ügyeljünk ha hexadecimális számot előtag nélkül adunk meg (tehát csak 40-et és nem pl. 0x40-et), hogy ha a szám betűvel kezdődik (pl. C0), akkor egy nullát feltétlenül elé kell írnunk (Tehát pl. 0F0)! Felmerülhet a kérdés, hogy miért nem írtuk be akkor egyszerűen azt, hogy MOVLW .64? Nos azért nem, mert ezt az értéket kívánjuk kiküldeni a B portra, ahova a LED-ek csatlakoztatva vannak, aztán így egyszerűbb megadni: a bináris érték 8 helyiértéke megfelel a 8 LED-nek. Amelyik helyiérték 1, ott a LED világítani fog, amelyik 0, ott nem fog világítani. A fenti érték - miután kiküldtük a B portra - tehát a második LED-et kigyújtja.
A következő parancs (MOVWF) az akkumulátor tartalmát (melybe az imént raktuk a bináris értékünket) kiküldi az operandusban megadott regiszterbe, jelen esetben a B portra. Már most elgondolkodhattunk azon, hogy ha már ilyen minimális különbségek vannak az utasítások között - és ez még csak a kezdet -, akkor mi lesz később? Nos, igazából azok vannak előnyben, akik tudnak angolul. Ezek a furcsán kinéző utasítások ugyanis angol rövidítések. A MOV kezdetű utasítások az angol "move" (mozgatás) szóból származnak, tehát valamit valahova mozgatnak. Hogy mit hova, azt az utasítás vége mondja meg:
L - az operandusként megadott konstans
F - az operandusként megadott regiszter
W - az akkumulátor
Tehát a MOVWF PORTB az az akkumulátor tartalmát tölti be a PORTB-be, a MOVFW PORTB meg éppen fordítva, vagyis a PORTB-t olvassa vissza az akkumulátorba.
Láthatjuk, hogy a két MOV utasítás mindössze annyit tett, hogy a B portra kiküldött egy konstans értéket. De akkor miért kellett közbeiktatni az akkumulátort? Nem lehetett volna egy olyan utasítást használni, hogy MOVLF? A válasz az, hogy nem. Egy utasítás ugyanis csak egy operandust tartalmazhat, így viszont kettőt kellett volna megadni (a bináris értéket és a PORTB-t). Van egy speciális eset, amikor aritmetikai műveleteknél - pl. egy összeadás esetén - megmondhatjuk hogy a végeredmény az operadusban levő regiszterbe (ha az operandus egy regiszter) vagy az akkumulátorba kerüljön. Ekkor ezt az operandus mellé kell írni vesszővel elválasztva.
További információt a MOV utasításokról a letölthető dokumentum 62. oldalán találhatunk.
Menjünk tovább. Egy CALL DELAY utasítást láthatunk. A CALL utasítással meghívhatunk egy szubrutint. A szubrutinok lényege, hogy hívásukkor lefutnak úgy, mintha egy GOTO utsaítással ugrottunk volna oda, majd a rutin végén visszatér a futás arra a pontra, ahonnan hívtuk a rutint. Így a szubrutint a program bármely pontjáról meghívhatjuk (és mint a CALL DELAY utasítások számából látható, meg is hívjuk párszor a DELAY-t), mindig ugyanoda fog visszatérni, ahonnan hívták. A DELAY szubrutin feladata, hogy várakozik egy rövid ideig. Erre szükségünk van, hiszen miután kigyújtottunk egy LED-et, hagyni kell azt egy kicsit égni, mielőtt a következőre mennénk. Feltűnhet persze, hogy ha a CALL úgy működik először mint a GOTO, akkor kéne valami DELAY címkét látnunk. Valóban, a várakoztató rutint még nem írtuk meg. Nos, akkor jöjjön az!
DELAY: MOVLW d'150'
MOVWF T1
DEL: MOVLW d'255'
MOVWF T2
DEL1: NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
DECFSZ T2,f
GOTO DEL1
DECFSZ T1,f
GOTO DEL
RETURN
END
Na ebben már van pár címke. Meg pár ismerős utasítás is, de javarészt azért ismeretlen. A MOVLW - MOVWF párost már ismerjük (Konstans érték akkumulátorba, akkumulátor regiszterbe), vagyis T1 és T2 regisztereket feltöltjük kezdőértékekkel. Itt már decimális számokat használtunk, nem egyszerűsítené a feladatunkat a bináris vagy hexadecimális értékek használata. A NOP utasítás feladata, hogy ne csináljon semmit. Abszolút semmit. Mindössze azért kell (és ennyi), hogy amíg ezeken átfut a program, eltelik egy kis idő. A DECFSZ már egy kicsit érdekesebb (és bonyolultabb). Ennek az a feladata, hogy az operandusban megadott regisztert (itt csak regiszter lehet az operandus) eggyel csökkentse. Ha pedig ez a regiszter nulla értékű lesz a csökkentéstől (tehát 1 volt az értéke), akkor a következő utasítást ugorja át a program. (Itt fennáll az operandusok speciális esete, vagyis meg kell adni az operandus mellé, hogy T2 eggyel csökkentett értéke hova kerüljön. "f" esetén vissza T2-be, "w" esetén pedig az akkumulátorba. Ez utóbbi esetben logikusan T2 értéke változatlan marad.)
A GOTO DEL1 hatására visszaugrunk a DEL1 címkére. Látható, hogy addig fogunk visszaugrani, amíg a DECFSZ T2,f utasítás le nem nulláza T2 regisztert. Ekkor ugyanis átugorjuk a GOTO DEL1 utasítást.
A DECFSZ T1,f utasítás ugyanígy fog működni. Minthogy T1 még nem 1, ezért visszaugrunk a DEL címkére. Hoppá, itt viszont T2 megint felveszi a kezdeti értékét, tehát az előző ciklus újra lefut. Aztán megint csökken egyet T1, majd a ciklus újra lefut. Mindezt pontosan 150-szer teszi meg, minthogy addig nem tudunk továbblépni, amíg T1 nem nullázódik le és DECFSZ T1,f tovább nem engedi. Hasonlóan az előző, DECFSZ T2,f-hez, T1 kinullázódásakor átlépünk a GOTO DEL utasításon.
A RETURN utasítás segítségével visszatérünk arra a pontra, ahonnan egy CALL-lal hívtuk a szubrutint. Az END utasítás már nem a szubrutinhoz tartozik. Ez egy formai megkötés, hogy minden programnak ezzel kell végződnie.
Készen is vagyunk az első programmal. A teljes, megírt programot letölthetjük innen.
A program lefordítása és hibakeresése (debuggolása)A programot a Project/Build All vagy a CTRL+F10 billentyűkkel fordíthatjuk le. Ekkor bejön az MPASMWIN nevű program ablaka és elkészíti nekünk a végleges kódot. Ezután visszatérünk az MPLAB főképernyőjére, ahol egy új ablak jelenik meg, a hibákat felsorolva. Ha mindent jól gépeltünk be, akkor ezt kell hogy kapjuk: 11-es kép
Ezután kezdhetünk debuggolni ha akarunk, vagyis lépésenként megnézni hogy mi is történik az egyes sorok lefutása után. Az MPLAB teljesen emulálni tudja a PIC-et, így még beégetés előtt megtekinthetjük mit is csinál a program. Ehhez tegyük aktívvá a programablakot (klikkeljünk rá egyszer) és nyomjunk F6-ot. Így a forráskódban a program belépési pontjára ugrunk (ahol ténylegesen indul a program) és inverzben látjuk azt a sort, amelyik következőnek végre fog hajtódni.
Ha az F7-et megnyomjuk, akkor hajtódik végre az utasítás és ugrunk a következőre. Nyomjuk meg párszor az F7-et és azt tapasztalhatjuk, hogy a watch ablak mindig mutatja a benne levő regiszterek aktuális értékét. Ha valamelyik regiszter megváltozott az előző lépés óta, azt pirossal tűnteti fel. Zavaró lehet, hogy egy jó ideig bináris értékeket ír a program az akkumulátorba, de a watch ablak hexadecimálisan írja ki. A kijelzés átírásához klikkeljünk a watch ablak bal felső sarkában levő ikonra (Windows 3.1 és 3.11 alatt a bal felső sarokban levő mínusz jelre) és válasszuk ki az Edit Watch menüpontot. Válasszuk ki a "w"-t, majd nyomjuk meg a Properties gombot. Válasszuk ki a Binary opciót, majd nyomjunk OK-t.
Ugyanezt tegyük meg a "PORTB" regiszterrel is. Ha azzal is megvagyunk, nyomjunk Close-t az Edit Watch Symbol ablakon és vissztérünk a főképernyőre.
Most már a kijelzés tökéletes. Minthogy a PORTB regisztert is átállítottuk binárisra, láthatjuk hogy - virtuálisan - éppen melyik LED világít és melyik nem. Tanulámnyozzuk, hogy hogyan dolgoznak az egyes utasítások. Érdemes felvenni a watch ablak listájába még a T1 és T2 regisztereket, hogy jobban láthassuk a DELAY szubrutin működését. Ehhez tegyük aktívvá a watch ablakot és nyomjunk rajta egy Insert billentyűt. A felugró menüből pedig válasszuk ki a T1-et, majd klikkeljünk az Add gombra. Ezután válasszuk ki a T2-t és megint klikkeljünk az Add gombra. Végül zárjuk be az ablakot a Close gombbal. A már megismert módon megváltoztathatjuk a kijelzést - ezúttal ajánlott decimálisra, mert abban jobban láthatjuk a két regiszter csökkenését. Nézegessük a program futását, érdemes kísérletezgetni. A programot újraindítani az F6 billentyűvel tudjuk.
Ugyanezt tegyük meg a "PORTB" regiszterrel is. Ha azzal is megvagyunk, nyomjunk Close-t az Edit Watch Symbol ablakon és vissztérünk a főképernyőre.
Most már a kijelzés tökéletes. Minthogy a PORTB regisztert is átállítottuk binárisra, láthatjuk hogy - virtuálisan - éppen melyik LED világít és melyik nem. Tanulámnyozzuk, hogy hogyan dolgoznak az egyes utasítások. Érdemes felvenni a watch ablak listájába még a T1 és T2 regisztereket, hogy jobban láthassuk a DELAY szubrutin működését. Ehhez tegyük aktívvá a watch ablakot és nyomjunk rajta egy Insert billentyűt. A felugró menüből pedig válasszuk ki a T1-et, majd klikkeljünk az Add gombra. Ezután válasszuk ki a T2-t és megint klikkeljünk az Add gombra. Végül zárjuk be az ablakot a Close gombbal. A már megismert módon megváltoztathatjuk a kijelzést - ezúttal ajánlott decimálisra, mert abban jobban láthatjuk a két regiszter csökkenését. Nézegessük a program futását, érdemes kísérletezgetni. A programot újraindítani az F6 billentyűvel tudjuk.
BeégetésPIP02 (Csak a COM84 égetőhöz!)
Lássuk, hogyan működik a program, mikrokontrollerrel! A letölthető PIP02 égetőprogram DOS alapú, de gond nélkül működik Windows alatt is, amennyiben számítógépünk órajele nem haladja meg az 500 MHz-et. Ha ennél gyorsabb gépünk van, akkor használjuk a valamivel bonyolultabb IC-Prog szoftvert. (Ez fut 500 MHz alatt és felett is, de csak Windows alatt.) Az IC-Prog útmutatója a következő fejezetben található.
A PIP02-t kicsomagolás után indítsuk a START.BAT fájlal. Ha a PIC égető nem a COM2 portra csatlakozna, akkor ezt módosítsuk a START.BAT fájlon belül egy szövegszerkesztő segítségével. Az égetőprogramot előre beállítottam PIC16F84-re és a megfelelő égetőre (melynek kapcsolási rajza az előző számban található meg), így nem lesz semmi gondunk vele.
Miután a programot elindítottuk, válasszuk ki a File/Load menüpontot, vagy egyszerűen nyomjunk F3-at. Keressük meg a projektkönyvtárunkat. A lefordítás során generált nekünk az MPLAB egy ELSO.HEX nevű fájlt. Ezt jelöljük ki (vagy írjuk be felülre a nevét) és nyomjunk Open-t.
Lássuk, hogyan működik a program, mikrokontrollerrel! A letölthető PIP02 égetőprogram DOS alapú, de gond nélkül működik Windows alatt is, amennyiben számítógépünk órajele nem haladja meg az 500 MHz-et. Ha ennél gyorsabb gépünk van, akkor használjuk a valamivel bonyolultabb IC-Prog szoftvert. (Ez fut 500 MHz alatt és felett is, de csak Windows alatt.) Az IC-Prog útmutatója a következő fejezetben található.
A PIP02-t kicsomagolás után indítsuk a START.BAT fájlal. Ha a PIC égető nem a COM2 portra csatlakozna, akkor ezt módosítsuk a START.BAT fájlon belül egy szövegszerkesztő segítségével. Az égetőprogramot előre beállítottam PIC16F84-re és a megfelelő égetőre (melynek kapcsolási rajza az előző számban található meg), így nem lesz semmi gondunk vele.
Miután a programot elindítottuk, válasszuk ki a File/Load menüpontot, vagy egyszerűen nyomjunk F3-at. Keressük meg a projektkönyvtárunkat. A lefordítás során generált nekünk az MPLAB egy ELSO.HEX nevű fájlt. Ezt jelöljük ki (vagy írjuk be felülre a nevét) és nyomjunk Open-t.
Ha még eddig nem tettük volna meg, helyezzük a PIC-et az égetőbe, ügyelve a helyes polaritásra! Ezután válasszuk ki a Device/Program menüpontot, vagy egyszerűen nyomjunk F5-öt. Az égetés megkezdődik. Eközben egy folyamatjelző mutatja, hány százalékig van meg az égetés.
Az égetés végén ha egy emelkedő "füttyentést" hallunk és nem kapunk semmilyen üzenetet, akkor az égetés sikeres. (Ha teljesen biztosra akarunk menni, akkor F6-ra visszaolvassa az égetőprogram a PIC-ből az adatokat és leellenőrzi hogy a betöltött fájlal megegyeznek-e. Siker esetén ugyanezt a hangeffektet kapjuk, szintén üzenet nélkül.)
Ha csak egy rövidebb sípolást kapunk és egy "Error Programming Device" üzenetet, akkor az égetés nem sikerült. Ilyenkor ellenőrizzük a PIC égetőben való polaritás-helyességet, valamint sokszor segít ha először kitöröljük a Device/Erase menüponttal a PIC tartalmát. (Ez utóbbi esetben mindig a "füttyentés" hangot hallatja a program, függetlenül a sikerességtől.) Ha ez sem segít, próbáljuk meg újraindítani vagy DOS alól futtatni a programot.
A fütty után a PIC-et kivehetjük az égetőből és a próbapanelen kipróbálhatjuk. A PIC16F84-et újra lehet programozni, tehát nem kell félnünk attól, hogy "véglegesen" írtunk bele valamit, mint egy írható CD-nél. (A PIC 16C84 ellenben csak egyszer írható, ezért ügyeljünk arra, hogy milyet veszünk!)
Az égetés végén ha egy emelkedő "füttyentést" hallunk és nem kapunk semmilyen üzenetet, akkor az égetés sikeres. (Ha teljesen biztosra akarunk menni, akkor F6-ra visszaolvassa az égetőprogram a PIC-ből az adatokat és leellenőrzi hogy a betöltött fájlal megegyeznek-e. Siker esetén ugyanezt a hangeffektet kapjuk, szintén üzenet nélkül.)
Ha csak egy rövidebb sípolást kapunk és egy "Error Programming Device" üzenetet, akkor az égetés nem sikerült. Ilyenkor ellenőrizzük a PIC égetőben való polaritás-helyességet, valamint sokszor segít ha először kitöröljük a Device/Erase menüponttal a PIC tartalmát. (Ez utóbbi esetben mindig a "füttyentés" hangot hallatja a program, függetlenül a sikerességtől.) Ha ez sem segít, próbáljuk meg újraindítani vagy DOS alól futtatni a programot.
A fütty után a PIC-et kivehetjük az égetőből és a próbapanelen kipróbálhatjuk. A PIC16F84-et újra lehet programozni, tehát nem kell félnünk attól, hogy "véglegesen" írtunk bele valamit, mint egy írható CD-nél. (A PIC 16C84 ellenben csak egyszer írható, ezért ügyeljünk arra, hogy milyet veszünk!)
IC-Prog
Ha IC-Prog-ot használunk, akkor az alábbi leírás segítségével állíthatjuk be a programot, ill. égethetünk vele.
Először is csomagoljuk ki a fájlokat egy könyvtárba, majd futtassuk az ICPROG.EXE fájlt. Minthogy első alkalommal indítjuk a programot, figyelmeztetni fog, hogy be kell konfigurálnunk. Nyomjunk OK-t a figyelmeztetésre, majd a megjelenő konfigurációs ablakban állítsuk be a menüpontokat az alábbi ábra szerint (A COM portot természetesen a saját gépünknek megfelelően állítsuk be): 16-os kép
Ha IC-Prog-ot használunk, akkor az alábbi leírás segítségével állíthatjuk be a programot, ill. égethetünk vele.
Először is csomagoljuk ki a fájlokat egy könyvtárba, majd futtassuk az ICPROG.EXE fájlt. Minthogy első alkalommal indítjuk a programot, figyelmeztetni fog, hogy be kell konfigurálnunk. Nyomjunk OK-t a figyelmeztetésre, majd a megjelenő konfigurációs ablakban állítsuk be a menüpontokat az alábbi ábra szerint (A COM portot természetesen a saját gépünknek megfelelően állítsuk be): 16-os kép
A JDM a COM84-gyel kompatíbilis égető. Amennyiben más kapcsolást használunk, természetesen annak megfelelően kell beállítani az égető típusát.
Nyomjunk OK-t. Ha NT alapú Windows-t használunk (ide tartozik a Windows NT, Windows 2000 és Windows XP), akkor valószínűleg kapunk pár hibaüzenetet. Ezekkel egyelőre ne foglalkozzunk (okézzuk mindet), következő lépésben oldjuk meg azt, hogy a program hibátlanul menjen.
A következő lépést csak NT alapú Windows-t használóknak kell megtenni!
Válasszuk ki a Settings/Options menüpontot, a megjelenő ablakban pedig a Misc fület. Itt pipáljuk ki az Enable NT/2000/XP driver jelölőnégyzetet! A program saját maga újraindítását fogja kérni, erre válaszoljunk Yes-szel
A program újraindul, majd megerősítést kér a driver feltelepítésére. Erre is válaszoljunk Yes-szel. Most már a program működik NT alatt is.
Mostantól a Win 95/98/Me-vel rendelkezők is csinálják:
A jobb-felső sarokban levő legördülőmenüből (melyben SDA3506 van éppen kijelölve) válasszuk ki a PIC16F84-et. Az ablak kissé átalakul, mostantól teljesen be van állítva a program PIC16F84 égetésre. Próbáljuk ki a teszt.hex fájllal!
Nyomjunk OK-t. Ha NT alapú Windows-t használunk (ide tartozik a Windows NT, Windows 2000 és Windows XP), akkor valószínűleg kapunk pár hibaüzenetet. Ezekkel egyelőre ne foglalkozzunk (okézzuk mindet), következő lépésben oldjuk meg azt, hogy a program hibátlanul menjen.
A következő lépést csak NT alapú Windows-t használóknak kell megtenni!
Válasszuk ki a Settings/Options menüpontot, a megjelenő ablakban pedig a Misc fület. Itt pipáljuk ki az Enable NT/2000/XP driver jelölőnégyzetet! A program saját maga újraindítását fogja kérni, erre válaszoljunk Yes-szel
A program újraindul, majd megerősítést kér a driver feltelepítésére. Erre is válaszoljunk Yes-szel. Most már a program működik NT alatt is.
Mostantól a Win 95/98/Me-vel rendelkezők is csinálják:
A jobb-felső sarokban levő legördülőmenüből (melyben SDA3506 van éppen kijelölve) válasszuk ki a PIC16F84-et. Az ablak kissé átalakul, mostantól teljesen be van állítva a program PIC16F84 égetésre. Próbáljuk ki a teszt.hex fájllal!
Nyissuk meg a TESZT.HEX-et a File/Open menüparanccsal, vagy a nyitott mappa ikonnal!
Ha az égetőt csatlakoztattuk és a PIC is benne van, akkor klikkeljünk a második IC ikonra (a villámosra), vagy válasszuk ki a Command/Program All menüpontot, vagy egyszerűen nyomjunk F5-öt. A program rákérdez, hogy valóban fel akarjuk-e programozni a PIC-et. Nyomjunk Yes-t. (Érdekességképpen törlésnél ezt nem kérdezi meg a program, hanem azonnal töröl.)
Ha az égetőt csatlakoztattuk és a PIC is benne van, akkor klikkeljünk a második IC ikonra (a villámosra), vagy válasszuk ki a Command/Program All menüpontot, vagy egyszerűen nyomjunk F5-öt. A program rákérdez, hogy valóban fel akarjuk-e programozni a PIC-et. Nyomjunk Yes-t. (Érdekességképpen törlésnél ezt nem kérdezi meg a program, hanem azonnal töröl.)
Az égetés három fázisból áll: Először a program kerül be a memóriába, aztán a 64 bájtos EEPROM (későbbiekben lesz annak a használatáról is szó), majd végül a beállítások. (Watchdog Timer, kódvédelem stb., szintén később lesz róluk szó)
Ezután visszaolvassa a PICből az adatokat a program, ellenőrizve ezzel, hogy sikerült-e az égetés. Az eredményről egy kis ablakban tájékoztat.
Ezután visszaolvassa a PICből az adatokat a program, ellenőrizve ezzel, hogy sikerült-e az égetés. Az eredményről egy kis ablakban tájékoztat.
PIC - 3. szám: Bevitelkezelés
Tartalom
Program a bevitelkezelésre és a bevitel-szimulálás
Program a bevitelkezelésre és a bevitel-szimulálás
Bevinni adatot a PIC-be hasonlóképpen tudunk, mint ahogyan kivittük az előző számban. Csak ezúttal a PORTA-t használjuk és nem írni fogunk bele, hanem olvasni belőle.
Példaképpen készítsünk egy olyan programot, ami beolvassa a kapcsolók állását, majd azoknak megfelelően kigyújtja az első és/vagy második LED-et. (A többi LED-et most nem használjuk.) A kapcsolókat a PORTA első és második lábára kötöttük (RA0 és RA1), tehát ezeket kell először beolvasnunk, majd kiírnunk a PORTB-re, ahova is a LED-eket kötöttük. Kezdjünk is neki!
Első lépésként vegyünk elő valamilyen fájlmenedzsert (pl. Windows Intéző, Windows Commander, stb.), majd az előző leckében elkészült "elso.asm" programunkról készítsünk egy másolatot ugyanabba a könyvtárba "masodik.asm" néven. Nyissuk meg az MPLAB-ot. Ha nem használtuk az előző lecke óta, megkérdezi hogy az utolsó projekten ("tan.pjt") akarunk-e dolgozni. Azon akarunk, tehát nyomjuk meg a Yes gombot. Ha nem kapunk ilyen kérdést, vagy azóta már dolgoztunk más projekten is, akkor a Project/Open project menüponttal (vagy a CTRL+F2-vel) nyissuk meg a "tan.pjt" fájlt. Zárjuk be az "elso.asm" ablakát, majd a File/Open menüponttal nyissuk meg a "masodik.asm"-et. Méretezzük át az új ablakot úgy, hogy ne takarjon semmit sem. Ha megvagyunk, klikkeljünk a Project/Edit Project menüpontra, vagy nyomjunk CTRL+F3-at. Jelöljük ki az "elso [.asm]" sort, majd nyomjuk meg a Delete Node gombot.
Ezután nyomjuk meg az Add Node gombot és a megjelenő panelen nyissuk meg a "masodik.asm" fájlt. Az MPLAB ekkor figyelmeztet minket, hogy a végleges fájl neve meg fog változni. Nyomjuk meg az OK gombot először a figyelmeztető üzeneten, aztán az Edit Project ablakban. Tanácsos most elmenteni a projektet a Project/Save Project menüponttal.
Megjegyzés: Eredetileg egy projekt egy programhoz szól, az egyszerűség kedvéért mi azonban végig ugyanabban a projektben fogunk dolgozni, hogy ne kelljen újra meg újra beállítani a projekt tulajdonságait.
Töröljük ki a CBLOCK...ENDC részt. Ezután menjünk a VISSZA címkéhez és töröljünk ki onnantól lefele mindent
Megjegyzés: Eredetileg egy projekt egy programhoz szól, az egyszerűség kedvéért mi azonban végig ugyanabban a projektben fogunk dolgozni, hogy ne kelljen újra meg újra beállítani a projekt tulajdonságait.
Töröljük ki a CBLOCK...ENDC részt. Ezután menjünk a VISSZA címkéhez és töröljünk ki onnantól lefele mindent
Ezután mentsük el a fájlt a File/Save menüponttal, vagy a CTRL+S billentyűkkel. Most annyit tettünk, hogy átvettük az előző programból az inicializációs részt. Megcsonkított programunk aljára gépeljük be az alábbi részt:
VISSZA MOVFW PORTA
MOVWF PORTB
GOTO VISSZA
END
A kód magáért beszél. Egy MOVFW utasítással beolvassuk PORTA tartalmát az akkumulátorba (emlékezzünk hogyan kell értelmezni a MOV után álló két karaktert!), majd MOVWF utasítással kiírjuk azt a PORTB-re. Ezután visszaugrunk az elejére. A programot CTRL+F10-zel le tudjuk fordítani és a már megismert F7-es léptetéssel nyomon követni hogy hogyan működne egy PIC-ben. A watch ablakba vegyük fel a PORTA regisztert is, ekkor láthatjuk hogy a kapcsolók milyen pozicióban vannak. (Emlékeztetőül: tegyük aktívvá a watch ablakot, majd nyomjuk meg az Insert billentyűt. Itt válasszuk ki vagy gépeljük be a PORTA-t, majd nyomjuk meg az ADD gombot. Vagy a Properties gombbal is felvehetjük, ezzel rögtön át is tehetjük a kijelzést binárisra. Végül nyomjuk meg a Close-t.)
Kísérletképpen nézzük meg, hogyan viselkedne a program a különböző kapcsolóállásokra. Ugyebár F7-tel remekül láthatjuk hogyan működik a program, de a PORTA-t változtatni kéne futás közben is. Ehhez készítenünk kell egy szimulációs fájlt, melyben leírjuk hogy az egyes időpillanatokban hogyan változnak a portokon a bitek. Válasszuk ki a File/New menüpontot, az új szövegablakba meg gépeljük be az alábbi sorokat: (TAB-ot használjunk az oszlopok elválasztására!)
STEP RA1 RA0
10 0 0
15 0 1
20 1 0
25 1 1
A STEP oszlop jelzi, hogy hanyadik lépésnél kell életbe lépnie a változásnak. Az RA1 és RA0 pedig a PORTA első két lábára kötött kapcsolókat állítja be virtuálisan.
Ezután a File/Save As menüponttal mentsük el a munkamappánkba "bevitel.sti" néven! (Az "sti" kiterjesztés nagyon fontos!) Méretezzük át és rakjuk az ablakot olyan helyre, ahol nem zavar. Most válasszuk ki a Debug/Simulator Stimulus/Pin Stimulus/Enable menüpontot, majd válasszuk ki a "bevitel.sti" fájlt.
Ez utóbbi akcióval azt értük el, hogy mostantól hibakeresés közben az MPLAB figyelembe veszi a szimulációs fájlunkat. Most kezdjük el léptetni F7-tel a programot. Láthatjuk, hogy a megfelelő időpontokban átvált PORTA értéke, és a program működése szerint átíródik a LED-ekre kötött PORTB is. Remek! Égessük is bele a PIC-ünkbe hogy láthassuk élőben is! (A szimulációs fájl nem szól bele a fordításba.)
Hoppá, ez valahogyan élőben nem működik! Pedig az MPLAB-ban még minden rendben volt. Két jelenséggel nézünk most szembe, ha minden igaz: a LED akkor világítanak ha nem nyomjuk a gombokat, valamint (ez nem biztos hogy előjön) a többi LED közül is pár világít.
Na akkor vegyük nagyító alá a problémát! A kapcsolók azért működnek fordítva, mert elektronikusan így kötöttük be őket. Vagyis a PORTA folyamatosan "1"-et kap, csak akkor kap "0"-t, ha megnyomjuk a megfelelő lábra kapcsolt gombot. Tehát ez rendben van. Na de mi van a második jelenséggel? Nos, ott szimplán az a helyzet, hogy a PORTA többi lába nincs sehova sem bekötve, így a PIC felélesztésekor azok akármilyen állapotot felvehetnek. Ezt a jelenséget maszkolással tudjuk kiküszöbölni. A maszkolást úgy képzeljük el, ahogyan a valóságban is kinéz. Amikor egy felíratot festenek nagyüzemben, azt rendszerint úgy teszik, hogy a felületre ráhelyeznek egy műanyaglapot (maszkot), amiből ki van vágva a felirat. Ezután lesprézik festékkel az egészet és a lap elvétele után csak a felirat marad hátra a felületen. Így működik ez itt is. Először beolvassuk a PORTA tartalmát, melyet egy maszkon átviszünk. A maszk a számunkra lényegtelen biteket nullára állítja (függetlenül hogy mi volt az értéke), a többit érintetlenül hagyja. Nekünk ez arra lesz jó, hogy a PORTA-nak az első két bitén kívül az összes többi bit legyen nulla, így csak azok a LED-ek fognak működni, amelyek ténylegesen kapnak vezérlést. egészítsük ki programunkat az alábbi ANDLW sorral:
VISSZA MOVFW PORTA
ANDLW B'00000011'
MOVWF PORTB
GOTO VISSZA
END
Az ANDLW utasításban az "LW" ugyanazt jelenti mint a MOV utasításoknál. Az ANDLW ún. "és" műveletet végez az akkumulátor és az operandus között. Hogy ez pontosan mit is jelent, egyelőre nem kell foglalkoznunk vele, mindössze annyi a lényeg, hogy az akkumulátornak minden olyan bitét nullázza, ahol az operandusban is nulla van. (A többit meg békén hagyja.)
Például:
W értéke: B'00110011'
ANDLW operandusa: B'00000011'
W új értéke: B'00000011'
vagy
W értéke: B'10111010'
ANDLW operandusa: B'00000011'
W új értéke: B'00000010'
vagy
W értéke: B'11110100'
ANDLW operandusa: B'00000011'
W új értéke: B'00000000'
Látható a példákból hogy hogyan működik ez az utasítás. Tehát ha beiktatjuk a programba, a többi LED garantáltan nem fog világítani, csak az első kettő.
Ha már ilyen ügyesen kiküszöböltük ezt a problémát, oldjuk meg a másikat is, vagyis hogy akkor világítson egy LED, ha megnyomunk egy gombot és ne akkor ha elengedjük. Ehhez a XORLW utasítást alkalmazzuk, ami ún. "kizáró vagy" műveletet végez az akkumulátor és az operandus között.
Ha az operandus csupa egyesekből áll (vagyis decimálisan 255 az értéke), akkor az akkumulátor minden bitét megfordítja. Más néven ahol eddig "1" volt, oda nulla kerül és ahol "0" volt, oda egy kerül.
Programunk tehát így változik:
VISSZA MOVFW PORTA
XORLW B'11111111'
ANDLW B'00000011'
MOVWF PORTB
GOTO VISSZA
END
Így a program már tökéletesen fog működni a PIC-ben.
A kész programot letölthetjük innen.
Kis háttér-információ a logikai műveletekről:
Az alap logikai műveletek az AND (és), OR (vagy), XOR (kizáró vagy), valamint a NOT (nem).
Ezek úgy működnek mint bármilyen matematikai művelet, csak bitekkel. A NOT kivételével mindegyik két taggal dolgozik. Íme az "igazságtáblázatuk", vagyis hogy milyen eredményt produkálnak a különböző esetekben:
AND
1. tag 2. tag eredmény
0 0 0
0 1 0
1 0 0
1 1 1
OR
1. tag 2. tag eredmény
0 0 0
0 1 1
1 0 1
1 1 1
XOR
1. tag 2. tag eredmény
0 0 0
0 1 1
1 0 1
1 1 0
NOT
tag eredmény
0 1
1 0
Vagyis így lehet megfogalmazni ezek szabályait:
AND: az eredmény csak akkor lehet "1", ha mindkét tag értéke "1".
OR: az eredmény csak akkor lehet "1", ha legalább az egyik tag értéke "1".
XOR: az eredmény csak akkor lehet "1", ha csak és kizárólag az egyik tag értéke "1".
NOT: az eredmény mindig a tag ellentéte. (Itt csak egy tag lehet.)
PIC - 4. szám: Feltételkezelés
Tartalom
A BTFSx utasítások
Program a feltételes elágazásra
Értékvizsgálat és a STATUS regiszter
A BTFSx utasítások
Aki már programozott valaha is valamilyen programnyelven, az tudja hogy a programok attól fognak "gondolkodni", ha feltételek alapján tudnak dönteni, hogy merre tovább. Már pedig ha pl. egy hőszabályzó áramkört készítünk PIC-kel, (amit azért egy jó ideig biztosan nem fogunk) akkor nem árt ha a program el tudja dönteni - a hőmérséklet alapján, ami számadatként áll rendelkezésére -, hogy mikor kell be- és kikapcsolni a fűtést.
A feltételkezelésre PIC-ben a BTFSC és BTFSS (Bit Test File, Skip If Clear/Set) utasítások állnak rendelkezésre. A gyakorlattól eltérően két paraméterük van, egy regiszter és egy szám 0 és 7 között. Példa a BTFSC működésére:
BTFSC PORTA,7
Ennél az utasításnál a PIC megnézi, hogy PORTA utolsó helyiértékű bitje 0-e. Ha az, akkor átugorja a soron következő utasítást, ha egy, akkor rendesen fut tovább.
Például:
MOVLW d'15'
BTFSC PORTA,7
MOVLW d'20'
MOVWF PORTB
Először az akkumulátor értéke 15 lesz. Majd ha PORTA hetedik bitje nulla volt, akkor kihagyjuk a következő (MOVLW d'20') utasítást és 15 kerül ki a PORTB-re. Ha viszont PORTA hetedik bitje egy volt, akkor az akkumulátor felveszi a 20 értéket és az íródik ki a PORTB-re.
Persze a legtöbbször roppant kevés nekünk, hogy az egyik elágazásnak csak egy utasításnyi helye van. Ezért hasznosabb, ha a BTFSC után rögtön egy GOTO utasítást helyezünk el, így az "egyutasításos" feltételnél több utasítást is végrehajthatunk. Például ha XYZ regiszter negyedik bitje 0, akkor kerüljön 15 a PORTA-ra, de ha XYZ negyedik bitje 1, akkor kerüljön 20 a PORTB-re:
BTFSC XYZ,4
GOTO IDE
MOVLW d'15' ; ide akkor kerül a vezérlés, ha XYZ 4. bitje 0.
MOVWF PORTA
GOTO TOVABB
IDE MOVLW d'20' ; ide pedig akkor, ha XYZ 4. bitje 1.
MOVWF PORTB
TOVABB ...
A BTFSS annyiban különbözik a BTFSC-től, hogy a feltétele fordítva dolgozik. Vagyis akkor ugorja át a soron következő utasítást, ha a paraméterében megadott bit 1 és nem 0.
VISSZA MOVFW PORTA
MOVWF PORTB
GOTO VISSZA
END
A kód magáért beszél. Egy MOVFW utasítással beolvassuk PORTA tartalmát az akkumulátorba (emlékezzünk hogyan kell értelmezni a MOV után álló két karaktert!), majd MOVWF utasítással kiírjuk azt a PORTB-re. Ezután visszaugrunk az elejére. A programot CTRL+F10-zel le tudjuk fordítani és a már megismert F7-es léptetéssel nyomon követni hogy hogyan működne egy PIC-ben. A watch ablakba vegyük fel a PORTA regisztert is, ekkor láthatjuk hogy a kapcsolók milyen pozicióban vannak. (Emlékeztetőül: tegyük aktívvá a watch ablakot, majd nyomjuk meg az Insert billentyűt. Itt válasszuk ki vagy gépeljük be a PORTA-t, majd nyomjuk meg az ADD gombot. Vagy a Properties gombbal is felvehetjük, ezzel rögtön át is tehetjük a kijelzést binárisra. Végül nyomjuk meg a Close-t.)
Kísérletképpen nézzük meg, hogyan viselkedne a program a különböző kapcsolóállásokra. Ugyebár F7-tel remekül láthatjuk hogyan működik a program, de a PORTA-t változtatni kéne futás közben is. Ehhez készítenünk kell egy szimulációs fájlt, melyben leírjuk hogy az egyes időpillanatokban hogyan változnak a portokon a bitek. Válasszuk ki a File/New menüpontot, az új szövegablakba meg gépeljük be az alábbi sorokat: (TAB-ot használjunk az oszlopok elválasztására!)
STEP RA1 RA0
10 0 0
15 0 1
20 1 0
25 1 1
A STEP oszlop jelzi, hogy hanyadik lépésnél kell életbe lépnie a változásnak. Az RA1 és RA0 pedig a PORTA első két lábára kötött kapcsolókat állítja be virtuálisan.
Ezután a File/Save As menüponttal mentsük el a munkamappánkba "bevitel.sti" néven! (Az "sti" kiterjesztés nagyon fontos!) Méretezzük át és rakjuk az ablakot olyan helyre, ahol nem zavar. Most válasszuk ki a Debug/Simulator Stimulus/Pin Stimulus/Enable menüpontot, majd válasszuk ki a "bevitel.sti" fájlt.
Ez utóbbi akcióval azt értük el, hogy mostantól hibakeresés közben az MPLAB figyelembe veszi a szimulációs fájlunkat. Most kezdjük el léptetni F7-tel a programot. Láthatjuk, hogy a megfelelő időpontokban átvált PORTA értéke, és a program működése szerint átíródik a LED-ekre kötött PORTB is. Remek! Égessük is bele a PIC-ünkbe hogy láthassuk élőben is! (A szimulációs fájl nem szól bele a fordításba.)
Hoppá, ez valahogyan élőben nem működik! Pedig az MPLAB-ban még minden rendben volt. Két jelenséggel nézünk most szembe, ha minden igaz: a LED akkor világítanak ha nem nyomjuk a gombokat, valamint (ez nem biztos hogy előjön) a többi LED közül is pár világít.
Na akkor vegyük nagyító alá a problémát! A kapcsolók azért működnek fordítva, mert elektronikusan így kötöttük be őket. Vagyis a PORTA folyamatosan "1"-et kap, csak akkor kap "0"-t, ha megnyomjuk a megfelelő lábra kapcsolt gombot. Tehát ez rendben van. Na de mi van a második jelenséggel? Nos, ott szimplán az a helyzet, hogy a PORTA többi lába nincs sehova sem bekötve, így a PIC felélesztésekor azok akármilyen állapotot felvehetnek. Ezt a jelenséget maszkolással tudjuk kiküszöbölni. A maszkolást úgy képzeljük el, ahogyan a valóságban is kinéz. Amikor egy felíratot festenek nagyüzemben, azt rendszerint úgy teszik, hogy a felületre ráhelyeznek egy műanyaglapot (maszkot), amiből ki van vágva a felirat. Ezután lesprézik festékkel az egészet és a lap elvétele után csak a felirat marad hátra a felületen. Így működik ez itt is. Először beolvassuk a PORTA tartalmát, melyet egy maszkon átviszünk. A maszk a számunkra lényegtelen biteket nullára állítja (függetlenül hogy mi volt az értéke), a többit érintetlenül hagyja. Nekünk ez arra lesz jó, hogy a PORTA-nak az első két bitén kívül az összes többi bit legyen nulla, így csak azok a LED-ek fognak működni, amelyek ténylegesen kapnak vezérlést. egészítsük ki programunkat az alábbi ANDLW sorral:
VISSZA MOVFW PORTA
ANDLW B'00000011'
MOVWF PORTB
GOTO VISSZA
END
Az ANDLW utasításban az "LW" ugyanazt jelenti mint a MOV utasításoknál. Az ANDLW ún. "és" műveletet végez az akkumulátor és az operandus között. Hogy ez pontosan mit is jelent, egyelőre nem kell foglalkoznunk vele, mindössze annyi a lényeg, hogy az akkumulátornak minden olyan bitét nullázza, ahol az operandusban is nulla van. (A többit meg békén hagyja.)
Például:
W értéke: B'00110011'
ANDLW operandusa: B'00000011'
W új értéke: B'00000011'
vagy
W értéke: B'10111010'
ANDLW operandusa: B'00000011'
W új értéke: B'00000010'
vagy
W értéke: B'11110100'
ANDLW operandusa: B'00000011'
W új értéke: B'00000000'
Látható a példákból hogy hogyan működik ez az utasítás. Tehát ha beiktatjuk a programba, a többi LED garantáltan nem fog világítani, csak az első kettő.
Ha már ilyen ügyesen kiküszöböltük ezt a problémát, oldjuk meg a másikat is, vagyis hogy akkor világítson egy LED, ha megnyomunk egy gombot és ne akkor ha elengedjük. Ehhez a XORLW utasítást alkalmazzuk, ami ún. "kizáró vagy" műveletet végez az akkumulátor és az operandus között.
Ha az operandus csupa egyesekből áll (vagyis decimálisan 255 az értéke), akkor az akkumulátor minden bitét megfordítja. Más néven ahol eddig "1" volt, oda nulla kerül és ahol "0" volt, oda egy kerül.
Programunk tehát így változik:
VISSZA MOVFW PORTA
XORLW B'11111111'
ANDLW B'00000011'
MOVWF PORTB
GOTO VISSZA
END
Így a program már tökéletesen fog működni a PIC-ben.
A kész programot letölthetjük innen.
Kis háttér-információ a logikai műveletekről:
Az alap logikai műveletek az AND (és), OR (vagy), XOR (kizáró vagy), valamint a NOT (nem).
Ezek úgy működnek mint bármilyen matematikai művelet, csak bitekkel. A NOT kivételével mindegyik két taggal dolgozik. Íme az "igazságtáblázatuk", vagyis hogy milyen eredményt produkálnak a különböző esetekben:
AND
1. tag 2. tag eredmény
0 0 0
0 1 0
1 0 0
1 1 1
OR
1. tag 2. tag eredmény
0 0 0
0 1 1
1 0 1
1 1 1
XOR
1. tag 2. tag eredmény
0 0 0
0 1 1
1 0 1
1 1 0
NOT
tag eredmény
0 1
1 0
Vagyis így lehet megfogalmazni ezek szabályait:
AND: az eredmény csak akkor lehet "1", ha mindkét tag értéke "1".
OR: az eredmény csak akkor lehet "1", ha legalább az egyik tag értéke "1".
XOR: az eredmény csak akkor lehet "1", ha csak és kizárólag az egyik tag értéke "1".
NOT: az eredmény mindig a tag ellentéte. (Itt csak egy tag lehet.)
PIC - 4. szám: Feltételkezelés
Tartalom
A BTFSx utasítások
Program a feltételes elágazásra
Értékvizsgálat és a STATUS regiszter
A BTFSx utasítások
Aki már programozott valaha is valamilyen programnyelven, az tudja hogy a programok attól fognak "gondolkodni", ha feltételek alapján tudnak dönteni, hogy merre tovább. Már pedig ha pl. egy hőszabályzó áramkört készítünk PIC-kel, (amit azért egy jó ideig biztosan nem fogunk) akkor nem árt ha a program el tudja dönteni - a hőmérséklet alapján, ami számadatként áll rendelkezésére -, hogy mikor kell be- és kikapcsolni a fűtést.
A feltételkezelésre PIC-ben a BTFSC és BTFSS (Bit Test File, Skip If Clear/Set) utasítások állnak rendelkezésre. A gyakorlattól eltérően két paraméterük van, egy regiszter és egy szám 0 és 7 között. Példa a BTFSC működésére:
BTFSC PORTA,7
Ennél az utasításnál a PIC megnézi, hogy PORTA utolsó helyiértékű bitje 0-e. Ha az, akkor átugorja a soron következő utasítást, ha egy, akkor rendesen fut tovább.
Például:
MOVLW d'15'
BTFSC PORTA,7
MOVLW d'20'
MOVWF PORTB
Először az akkumulátor értéke 15 lesz. Majd ha PORTA hetedik bitje nulla volt, akkor kihagyjuk a következő (MOVLW d'20') utasítást és 15 kerül ki a PORTB-re. Ha viszont PORTA hetedik bitje egy volt, akkor az akkumulátor felveszi a 20 értéket és az íródik ki a PORTB-re.
Persze a legtöbbször roppant kevés nekünk, hogy az egyik elágazásnak csak egy utasításnyi helye van. Ezért hasznosabb, ha a BTFSC után rögtön egy GOTO utasítást helyezünk el, így az "egyutasításos" feltételnél több utasítást is végrehajthatunk. Például ha XYZ regiszter negyedik bitje 0, akkor kerüljön 15 a PORTA-ra, de ha XYZ negyedik bitje 1, akkor kerüljön 20 a PORTB-re:
BTFSC XYZ,4
GOTO IDE
MOVLW d'15' ; ide akkor kerül a vezérlés, ha XYZ 4. bitje 0.
MOVWF PORTA
GOTO TOVABB
IDE MOVLW d'20' ; ide pedig akkor, ha XYZ 4. bitje 1.
MOVWF PORTB
TOVABB ...
A BTFSS annyiban különbözik a BTFSC-től, hogy a feltétele fordítva dolgozik. Vagyis akkor ugorja át a soron következő utasítást, ha a paraméterében megadott bit 1 és nem 0.
Program a feltételes elágazásra
Alakítsuk át már meglévő programunkat úgy, hogy legyen benne feltételes utasítás!
Indítsuk az MPLAB-ot a TAN.PJT projektünkkel, majd készítsük fel az új programra az előző számban megismert módszerrel. Azaz először töröljük ki a kódot a VISSZA címkéig, majd mentsük el más néven a programot a File/Save As segítségével. Névnek adjuk meg: FELTETEL.ASM. Ezután CTRL+F3-mal vagy a Project/Edit Project menüponttal állítsuk be hogy a FELTETEL.ASM lesz a lefordítandó állomány.
Először csak annyit írjunk meg, hogy az első gomb állásától függően vagy az első LED világít és a második nem, vagy a második LED világít és az első nem. Valahogy így:
Gomb
LED1
LED2
felengedve
világít
nem világít
lenyomva
nem világít
világít
A program utolsó sora után tegyünk ki egy VISSZA címkét és lássunk is hozzá a programozáshoz!
A program pofon egyszerűen fog működni:
1. Megnézzük, hogy milyen az állapota a használt gombnak (PORTA,0)
2. Ha nincs lenyomva, akkor LED1 be, LED2 ki.
3. Ha le van nyomva, akkor LED1 ki, LED2 be.
Vagyis:
VISSZA BTFSS PORTA,0
GOTO LENYOM
BSF PORTB,0
BCF PORTB,1
GOTO VISSZA
LENYOM BCF PORTB,0
BSF PORTB,1
GOTO VISSZA
END
A BTFSS utasítás megvizsgálja a gombot, mely a PORTA 0-dik bitjén van. Ha a gomb le van nyomva (azaz a bit értéke 0), a program futása a következő sorral folytatódik, ahol is a GOTO utasítás a LENYOM címkére ugrasztja a programot. Ha a gomb nincs lenyomva, a GOTO utasítást átlépjük. A BSF és BCF utasítások állítják be a LED-eket. (A BCF ki-, a BSF bekapcsol egyet) Mindkét esetben egy GOTO VISSZA utasítás újra kezdi az ellenőrzést.
Az előző számban megismert "Pin Stimulus" segítségével érdemes emulálnunk a gomb különböző állásait, majd egyesével végiglépkedni a sorokon F7-tel, ezzel figyelve a BTFSS működését. Továbbá próbáljuk ki, hogy mi történik akkor, ha a BTFSS utasítást BTFSC-re cseréljük! (Azonban lecserélés után ne felejtsünk el újrafordítani!)
A kész példát le is tölthetjük innen.
Alakítsuk át már meglévő programunkat úgy, hogy legyen benne feltételes utasítás!
Indítsuk az MPLAB-ot a TAN.PJT projektünkkel, majd készítsük fel az új programra az előző számban megismert módszerrel. Azaz először töröljük ki a kódot a VISSZA címkéig, majd mentsük el más néven a programot a File/Save As segítségével. Névnek adjuk meg: FELTETEL.ASM. Ezután CTRL+F3-mal vagy a Project/Edit Project menüponttal állítsuk be hogy a FELTETEL.ASM lesz a lefordítandó állomány.
Először csak annyit írjunk meg, hogy az első gomb állásától függően vagy az első LED világít és a második nem, vagy a második LED világít és az első nem. Valahogy így:
Gomb
LED1
LED2
felengedve
világít
nem világít
lenyomva
nem világít
világít
A program utolsó sora után tegyünk ki egy VISSZA címkét és lássunk is hozzá a programozáshoz!
A program pofon egyszerűen fog működni:
1. Megnézzük, hogy milyen az állapota a használt gombnak (PORTA,0)
2. Ha nincs lenyomva, akkor LED1 be, LED2 ki.
3. Ha le van nyomva, akkor LED1 ki, LED2 be.
Vagyis:
VISSZA BTFSS PORTA,0
GOTO LENYOM
BSF PORTB,0
BCF PORTB,1
GOTO VISSZA
LENYOM BCF PORTB,0
BSF PORTB,1
GOTO VISSZA
END
A BTFSS utasítás megvizsgálja a gombot, mely a PORTA 0-dik bitjén van. Ha a gomb le van nyomva (azaz a bit értéke 0), a program futása a következő sorral folytatódik, ahol is a GOTO utasítás a LENYOM címkére ugrasztja a programot. Ha a gomb nincs lenyomva, a GOTO utasítást átlépjük. A BSF és BCF utasítások állítják be a LED-eket. (A BCF ki-, a BSF bekapcsol egyet) Mindkét esetben egy GOTO VISSZA utasítás újra kezdi az ellenőrzést.
Az előző számban megismert "Pin Stimulus" segítségével érdemes emulálnunk a gomb különböző állásait, majd egyesével végiglépkedni a sorokon F7-tel, ezzel figyelve a BTFSS működését. Továbbá próbáljuk ki, hogy mi történik akkor, ha a BTFSS utasítást BTFSC-re cseréljük! (Azonban lecserélés után ne felejtsünk el újrafordítani!)
A kész példát le is tölthetjük innen.
Értékvizsgálat és a STATUS regiszter
Most már a PIC-ünk gondolkodik is, nem csak egyszerűen végrehajt egymást követő utasításokat. Azonban a későbbiekben tapasztalni fogjuk, hogy sokszor nem elég egy regiszter valamely bitjének vizsgálata, hanem a teljes regiszter értéke alapján kell döntést hoznia a programnak. Például mi van, ha egy olyan programot akarunk írni, ami folyamatosan növeli SZAML regiszter értékét, és minden LED-et bekapcsol, ha SZAML értéke eléri a 15-öt? SZAML regiszter egyetlen bitjének vizsgálata ehhez nem elég. Bár ha furfangosak vagyunk, akkor megoldható a probléma nyolc BTFSx utasítással, melyek egyenként végigtesztelik a biteket. Ennél azért egyszerűbben is megoldható a probléma, egy logikai cselfogással. A kizáró vagy művelet (XOR) különlegessége, hogy ha két ugyanolyan szám között végezzük el, akkor nullát ad eredményül.(És mellesleg csak ebben az esetben ad vissza nullát) Hogy ez miért jó nekünk? Ideje megismernünk a STATUS regisztert!
A STATUS regiszter értéke minden egyes végrehajtott parancs után felveszi a végrehajtás állapotát. Minden egyes bitje valami másnak az állapotát jelzi. Éppen ezért ezeket jelzőbiteknek hívjuk. Az MPLAB a STATUS regiszter mindegyik bitjének ad egy nevet, így nem szükséges megjegyeznünk, hogy egy-egy állapotjelző bit melyik helyiértéken van, hanem elég csak a bit elnevezését tudnunk. A továbbiakban is csak a nevük alapján fogjuk azonosítani ezeket. (A kép a PIC16F84 hivatalos dokumentációjából származik, ami letölthető erről a honlapról is.)
Ami most nekünk kell, az a Z (Zero, azaz nulla) jelzőbit. Ennek az a feladata, hogy ha az utolsó végrehajtott utasításban végzett számítás eredménye nulla lett, akkor felveszi az 1 értéket. Minden egyéb esetben nullát ad vissza.
Akkor hát kapcsoljuk össze, amit eddig megtudtunk! Úgy csinálhatunk elágazást teljes regiszterérték alapján, hogy
1. XOR műveletet végzünk a kérdéses regiszter és az összehasonlítandó érték között. (A fenti példában SZAML és 15 között)
2. Ha SZAML értéke éppen 15 volt, akkor a XOR művelet eredménye nulla lesz, és ettől a Z jelzőbit felveszi az 1 értéket.
3. Egy BTFSx utasítással megvizsgáljuk a Z jelzőbit értékét és ez alapján elágazhatunk.
Még jobban összefoglalva: ha a két érték (amit összehasonlítunk) egyenlő, akkor Z=1 lesz.
Példa:
CLRF SZAML
VISSZA INCF SZAML
MOVFW SZAML
XORLW .15
BTFSS STATUS,Z
GOTO VISSZA
MOVLW h'ff'
MOVWF PORTB
GOTO VISSZA
END
A példa a PIC-ben nem sokat mutatna, hiszen a SZAML regiszter a másodperc törtrésze alatt elszámol 15-ig, így gyakorlatilag csak annyit látnánk, hogy bekapcsoljuk az áramkört és a LED-ek azonnal kivilágítanak. Ettől függetlenül a példa letölthető innen, kiegészítve a bevezető részekkel (beleértve a SZAML regiszter deklarációját is).
Ezek után próbáljunk készíteni magunktól egy olyan programot, ami egy egyirányú futófényt valósít meg. A gombok állásától függően menjen a fény. Tehát ha a G1 gombot megnyomjuk, akkor jobbról balra fusson, G2-re pedig balról jobbra. A következő számban benne lesz a megoldás, valamint ennek egyszerűsítése a forgató utasítássokkal és a STATUS regiszter C jelzőbitével.
PIC - 5. szám: Bitforgatás
Ez a rész összpontosít a maradék dolgokra, amit még tudnunk érdemes, mielőtt egy szinttel feljebb lépünk, a megszakításokhoz. Először az előző szám „házi feladatára” lássunk valami megoldást. A feladatra számos megoldás kínálkozik. Legegyszerűbb, ám a leghosszabb is, ha két rutin írunk, melyből az egyik balra, a másik jobbra futtatja a fényt, egy rövid kódrész pedig a kettő között kapcsol, a gombok állásának megfelelően. Ezen belül is több lehetőség kínálkozik, például egymás után állítgathatjuk a LED-eket konstans értékekkel, vagy valami logika szerint is "tolhatjuk" a fényt. Először az első megoldásnak essünk neki - ami leginkább az első programunkra fog emlékeztetni - majd próbáljuk ki kissé fejlettebb technikával is!
VISSZA: BTFSS PORTA,0
GOTO BALRA
BTFSS PORTA,1
GOTO JOBBRA
GOTO VISSZA
JOBBRA: MOVLW B'10000000'
MOVWF PORTB
CALL DELAY
MOVLW B'01000000'
MOVWF PORTB
CALL DELAY
MOVLW B'00100000'
MOVWF PORTB
CALL DELAY
MOVLW B'00010000'
MOVWF PORTB
CALL DELAY
MOVLW B'00001000'
MOVWF PORTB
CALL DELAY
MOVLW B'00000100'
MOVWF PORTB
CALL DELAY
MOVLW B'00000010'
MOVWF PORTB
CALL DELAY
MOVLW B'00000001'
MOVWF PORTB
CALL DELAY
GOTO VISSZA
BALRA: MOVLW B'00000001'
MOVWF PORTB
CALL DELAY
MOVLW B'00000010'
MOVWF PORTB
CALL DELAY
MOVLW B'00000100'
MOVWF PORTB
CALL DELAY
MOVLW B'00001000'
MOVWF PORTB
CALL DELAY
MOVLW B'00010000'
MOVWF PORTB
CALL DELAY
MOVLW B'00100000'
MOVWF PORTB
CALL DELAY
MOVLW B'01000000'
MOVWF PORTB
CALL DELAY
MOVLW B'10000000'
MOVWF PORTB
CALL DELAY
GOTO VISSZA
A CALL utasítások által hivatkozott DELAY rutin a már eddig megismert késleltető rutin egy az egyben. Ezt most helytakarékosság miatt nem közöltem le újra, de ha el akarjuk készíteni a programot, akkor szükségünk lesz rá. A lefordításra kész .asm fájl – amelyben már a DELAY is benne van – letölthető innen.
Ezzel a megoldással van azért némi probléma. Az egyik legnagyobb gond az, hogy csak egyszer fut végig, utána újra meg kell nyomni valamelyik gombot. (Vagy folyamatosan nyomva kell tartani.) Ezen persze könnyen lehet segíteni, mindössze a tesztelő részt kell átírnunk, hogy a gombok le nem nyomott állapotában ismételje meg az utolsó futást. A másik probléma pedig, hogy a kód nagyon hosszú. Ha profi PIC programot készítünk, bizony hamar tapasztalni fogjuk, hogy nagyon kevés memóriával gazdálkodhatunk, így ahol csak lehet, rövidítsünk a kódon! Futófényünkhöz egy igen remek megoldás van a rövidítésre, a forgatóutasítások használata. A forgatóutasítások annyit tesznek, hogy a megadott regiszter tartalmát eggyel jobbra vagy balra csúsztatják. Például ha egy regiszter értéke binárisan 00110010, akkor abból jobbra forgatás után 00011001 lesz. Ezen felül a jobboldalt „lecsúszott” bit (jelent esetben egy „nulla” bit), átkerül a másik oldalra. Pont ez kell nekünk! Gyakorlatilag egy utasítás helyettesíti minden szenvedésünket. (Pontosabban kettő, mert a másik irányba való forgatáshoz másik utasítás kell.) Sajnos még van egy dolog, ami megkeseríti az életünket. De ezzel egyelőre ne foglalkozzunk! Balra az RLF (Rotate Left File), jobbra az RRF (Rotate Right File) utasítás forgat.
A következő lesz a terv: a program elején a kapcsolótesztelő ciklust módosítjuk, hogy ne várjon a gomblenyomással, hanem menjen tovább a legutolsó billentyűlenyomás szerint a program, ha nincs lenyomott gomb. Ezután magukat a JOBBRA és BALRA címkéjű rutinokat kicseréljük a pársoros RLF és RRF utasításos megoldásra.
Először is töröljünk ki mindent a VISSZA címkéig! Bevezetésképpen állítsunk be egy értéket PORTB-nek, hiszen a későbbiekben csak forgatni fogjuk a már meglévő tartalmát. Ezen felül vezessünk be egy IRANY regisztert, ami a kapcsoló kiértékelésekor fogja mutatni, hogy merre menjünk tovább, ha nem volt gomblenyomás. Gépeljük be az alábbi sorokat közvetlenül az utolsó sor (vagyis a BCF utasítás) után:
MOVLW B'10000000'
MOVWF PORTB
CLRF IRANY
Mint látható, még nem helyeztünk el VISSZA címkét. A VISSZA címke csak ez után fog jönni, hiszen a PORTB-t nem akarjuk minden egyes ciklusban 10000000-ra állítani, ahogyan az IRANY regisztert sem törölni. Azonban az új regiszter felvételét jelezni kell a CBLOCK direktívánál is. Ezután a CBLOCK rész így kell, hogy kinézzen:
CBLOCK 0x0C
T1
T2
IRANY
ENDC
Nézzük, miként tudjuk megoldani a gombok problémáját! Szögezzük le, hogy ha IRANY értéke 0, akkor balra mutat, ha 1, akkor pedig jobbra! Ezután a kapcsolótesztelés működjön az alábbiak szerint:
1. Olvassuk be IRANY értékét.
2. A gombok állásának megfelelően írjunk bele 0-t, 1-et, vagy ha nincs gomb lenyomva, akkor írjuk vissza a régi értékét.
3. IRANY értékének megfelelően ugorjunk a JOBBRA vagy BALRA címkére.
Mindezt valahogy így, amit hozzá is tehetünk programunkhoz:
VISSZA: MOVFW IRANY
BTFSS PORTA,0
MOVLW 00
BTFSS PORTA,1
MOVLW 01
MOVWF IRANY
XORLW 01
BTFSC STATUS,Z
GOTO BALRA
Az első MOVFW utasítás valósítja meg az első pontot, vagyis az akkumulátorba olvassuk az IRANY regisztert. A következő öt sorral oldjuk meg a második pontot, vagyis a kapcsolóknak megfelelően 0-ra, 1-re változtatjuk az akkumulátort, vagy békén hagyjuk. Bárhogyan is változtattuk (vagy nem változtattuk), azt visszaírjuk az IRANY-ba. A maradék sorok képviselik a harmadik pontot, vagyis az elugrást. Minthogy IRANY regiszternek csak az első bite változik, bőven elegendő lenne egy BTFSC utasítás a tesztelésére. Azonban most tanulásképpen az előző számban tárgyalt egzakt tesztelést használunk, vagyis a BALRA címkére csak akkor ugrunk, ha IRANY értéke csak és csakis 1! JOBBRA címkéhez nem kell külön elágazás, mert kihasználjuk, hogy a feltétel hamis kiértékelése esetén a következő sorra ugrik a program, így elegendő, ha oda tesszük a jobbra forgató kódot. Ennek megfelelően kezdjünk is neki a jobbra forgatásnak!
JOBBRA: RRF PORTB,F
CALL DELAY
GOTO VISSZA
Az első utasítással elvégezzük az oroszlánrészét a forgatásnak. PORTB tartalmát eggyel jobbra forgatjuk. (A vesszővel elválasztott „F” annyit jelent, hogy a végeredményt tegye vissza a regiszterbe. Lehet még „W”, ekkor az akkumulátorba kapjuk az elforgatott értéket.) Ezután késleletünk egyet és vissza is térhetünk a gombteszteléshez. A balra forgatás is ugyanígy néz ki, melyet tegyünk a jobbra forgató rutin után:
BALRA: RLF PORTB,F
CALL DELAY
GOTO VISSZA
A programot egészítsük ki még a DELAY rutinnal és kész is a tökéletes futófényünk, amit akkor és úgy fordítgatunk, amikor és ahogyan csak akarunk! Vagy mégsem? Valami itt nem O.K.! A PIC áramkört amikor bekapcsoljuk, hol normálisan működik, hol pedig egyszerre két LED világít. Valami árulás van itt! Az árulás forrása pedig nem más, mint a C (Carry) jelzőbit, mely a Z-hez hasonlóan, szintén a STATUS regiszter részét képezi. Ennek a bitnek a szerepe egy amolyan ideiglenes tároló az utasítások számára. Pl. túlcsorduláskor (vagyis ha túl sokat adunk össze egyszerre) a „kilógó” helyiértéket tárolja. Forgatásnál pedig ebből és ebbe tárolnak a forgatóutasítások, mégpedig az alábbi folyamat szerint:
1. A PIC elforgatja a megadott regisztert,
2. A „hiányos” részre berakja a C értékét,
3. A „lelógó” részt pedig beteszi C-be.
Ez a rész összpontosít a maradék dolgokra, amit még tudnunk érdemes, mielőtt egy szinttel feljebb lépünk, a megszakításokhoz. Először az előző szám „házi feladatára” lássunk valami megoldást. A feladatra számos megoldás kínálkozik. Legegyszerűbb, ám a leghosszabb is, ha két rutin írunk, melyből az egyik balra, a másik jobbra futtatja a fényt, egy rövid kódrész pedig a kettő között kapcsol, a gombok állásának megfelelően. Ezen belül is több lehetőség kínálkozik, például egymás után állítgathatjuk a LED-eket konstans értékekkel, vagy valami logika szerint is "tolhatjuk" a fényt. Először az első megoldásnak essünk neki - ami leginkább az első programunkra fog emlékeztetni - majd próbáljuk ki kissé fejlettebb technikával is!
VISSZA: BTFSS PORTA,0
GOTO BALRA
BTFSS PORTA,1
GOTO JOBBRA
GOTO VISSZA
JOBBRA: MOVLW B'10000000'
MOVWF PORTB
CALL DELAY
MOVLW B'01000000'
MOVWF PORTB
CALL DELAY
MOVLW B'00100000'
MOVWF PORTB
CALL DELAY
MOVLW B'00010000'
MOVWF PORTB
CALL DELAY
MOVLW B'00001000'
MOVWF PORTB
CALL DELAY
MOVLW B'00000100'
MOVWF PORTB
CALL DELAY
MOVLW B'00000010'
MOVWF PORTB
CALL DELAY
MOVLW B'00000001'
MOVWF PORTB
CALL DELAY
GOTO VISSZA
BALRA: MOVLW B'00000001'
MOVWF PORTB
CALL DELAY
MOVLW B'00000010'
MOVWF PORTB
CALL DELAY
MOVLW B'00000100'
MOVWF PORTB
CALL DELAY
MOVLW B'00001000'
MOVWF PORTB
CALL DELAY
MOVLW B'00010000'
MOVWF PORTB
CALL DELAY
MOVLW B'00100000'
MOVWF PORTB
CALL DELAY
MOVLW B'01000000'
MOVWF PORTB
CALL DELAY
MOVLW B'10000000'
MOVWF PORTB
CALL DELAY
GOTO VISSZA
A CALL utasítások által hivatkozott DELAY rutin a már eddig megismert késleltető rutin egy az egyben. Ezt most helytakarékosság miatt nem közöltem le újra, de ha el akarjuk készíteni a programot, akkor szükségünk lesz rá. A lefordításra kész .asm fájl – amelyben már a DELAY is benne van – letölthető innen.
Ezzel a megoldással van azért némi probléma. Az egyik legnagyobb gond az, hogy csak egyszer fut végig, utána újra meg kell nyomni valamelyik gombot. (Vagy folyamatosan nyomva kell tartani.) Ezen persze könnyen lehet segíteni, mindössze a tesztelő részt kell átírnunk, hogy a gombok le nem nyomott állapotában ismételje meg az utolsó futást. A másik probléma pedig, hogy a kód nagyon hosszú. Ha profi PIC programot készítünk, bizony hamar tapasztalni fogjuk, hogy nagyon kevés memóriával gazdálkodhatunk, így ahol csak lehet, rövidítsünk a kódon! Futófényünkhöz egy igen remek megoldás van a rövidítésre, a forgatóutasítások használata. A forgatóutasítások annyit tesznek, hogy a megadott regiszter tartalmát eggyel jobbra vagy balra csúsztatják. Például ha egy regiszter értéke binárisan 00110010, akkor abból jobbra forgatás után 00011001 lesz. Ezen felül a jobboldalt „lecsúszott” bit (jelent esetben egy „nulla” bit), átkerül a másik oldalra. Pont ez kell nekünk! Gyakorlatilag egy utasítás helyettesíti minden szenvedésünket. (Pontosabban kettő, mert a másik irányba való forgatáshoz másik utasítás kell.) Sajnos még van egy dolog, ami megkeseríti az életünket. De ezzel egyelőre ne foglalkozzunk! Balra az RLF (Rotate Left File), jobbra az RRF (Rotate Right File) utasítás forgat.
A következő lesz a terv: a program elején a kapcsolótesztelő ciklust módosítjuk, hogy ne várjon a gomblenyomással, hanem menjen tovább a legutolsó billentyűlenyomás szerint a program, ha nincs lenyomott gomb. Ezután magukat a JOBBRA és BALRA címkéjű rutinokat kicseréljük a pársoros RLF és RRF utasításos megoldásra.
Először is töröljünk ki mindent a VISSZA címkéig! Bevezetésképpen állítsunk be egy értéket PORTB-nek, hiszen a későbbiekben csak forgatni fogjuk a már meglévő tartalmát. Ezen felül vezessünk be egy IRANY regisztert, ami a kapcsoló kiértékelésekor fogja mutatni, hogy merre menjünk tovább, ha nem volt gomblenyomás. Gépeljük be az alábbi sorokat közvetlenül az utolsó sor (vagyis a BCF utasítás) után:
MOVLW B'10000000'
MOVWF PORTB
CLRF IRANY
Mint látható, még nem helyeztünk el VISSZA címkét. A VISSZA címke csak ez után fog jönni, hiszen a PORTB-t nem akarjuk minden egyes ciklusban 10000000-ra állítani, ahogyan az IRANY regisztert sem törölni. Azonban az új regiszter felvételét jelezni kell a CBLOCK direktívánál is. Ezután a CBLOCK rész így kell, hogy kinézzen:
CBLOCK 0x0C
T1
T2
IRANY
ENDC
Nézzük, miként tudjuk megoldani a gombok problémáját! Szögezzük le, hogy ha IRANY értéke 0, akkor balra mutat, ha 1, akkor pedig jobbra! Ezután a kapcsolótesztelés működjön az alábbiak szerint:
1. Olvassuk be IRANY értékét.
2. A gombok állásának megfelelően írjunk bele 0-t, 1-et, vagy ha nincs gomb lenyomva, akkor írjuk vissza a régi értékét.
3. IRANY értékének megfelelően ugorjunk a JOBBRA vagy BALRA címkére.
Mindezt valahogy így, amit hozzá is tehetünk programunkhoz:
VISSZA: MOVFW IRANY
BTFSS PORTA,0
MOVLW 00
BTFSS PORTA,1
MOVLW 01
MOVWF IRANY
XORLW 01
BTFSC STATUS,Z
GOTO BALRA
Az első MOVFW utasítás valósítja meg az első pontot, vagyis az akkumulátorba olvassuk az IRANY regisztert. A következő öt sorral oldjuk meg a második pontot, vagyis a kapcsolóknak megfelelően 0-ra, 1-re változtatjuk az akkumulátort, vagy békén hagyjuk. Bárhogyan is változtattuk (vagy nem változtattuk), azt visszaírjuk az IRANY-ba. A maradék sorok képviselik a harmadik pontot, vagyis az elugrást. Minthogy IRANY regiszternek csak az első bite változik, bőven elegendő lenne egy BTFSC utasítás a tesztelésére. Azonban most tanulásképpen az előző számban tárgyalt egzakt tesztelést használunk, vagyis a BALRA címkére csak akkor ugrunk, ha IRANY értéke csak és csakis 1! JOBBRA címkéhez nem kell külön elágazás, mert kihasználjuk, hogy a feltétel hamis kiértékelése esetén a következő sorra ugrik a program, így elegendő, ha oda tesszük a jobbra forgató kódot. Ennek megfelelően kezdjünk is neki a jobbra forgatásnak!
JOBBRA: RRF PORTB,F
CALL DELAY
GOTO VISSZA
Az első utasítással elvégezzük az oroszlánrészét a forgatásnak. PORTB tartalmát eggyel jobbra forgatjuk. (A vesszővel elválasztott „F” annyit jelent, hogy a végeredményt tegye vissza a regiszterbe. Lehet még „W”, ekkor az akkumulátorba kapjuk az elforgatott értéket.) Ezután késleletünk egyet és vissza is térhetünk a gombteszteléshez. A balra forgatás is ugyanígy néz ki, melyet tegyünk a jobbra forgató rutin után:
BALRA: RLF PORTB,F
CALL DELAY
GOTO VISSZA
A programot egészítsük ki még a DELAY rutinnal és kész is a tökéletes futófényünk, amit akkor és úgy fordítgatunk, amikor és ahogyan csak akarunk! Vagy mégsem? Valami itt nem O.K.! A PIC áramkört amikor bekapcsoljuk, hol normálisan működik, hol pedig egyszerre két LED világít. Valami árulás van itt! Az árulás forrása pedig nem más, mint a C (Carry) jelzőbit, mely a Z-hez hasonlóan, szintén a STATUS regiszter részét képezi. Ennek a bitnek a szerepe egy amolyan ideiglenes tároló az utasítások számára. Pl. túlcsorduláskor (vagyis ha túl sokat adunk össze egyszerre) a „kilógó” helyiértéket tárolja. Forgatásnál pedig ebből és ebbe tárolnak a forgatóutasítások, mégpedig az alábbi folyamat szerint:
1. A PIC elforgatja a megadott regisztert,
2. A „hiányos” részre berakja a C értékét,
3. A „lelógó” részt pedig beteszi C-be.
Tehát programunk helyes működése mindössze attól függ, hogy induláskor a C jelzőbit „beleszemetelt”-e a forgatásba, vagy sem. Hogy biztosra vegyük, hogy nem szemetel bele, a program elején törölnünk kell a C jelzőbitet! (Elméletileg a forgatóutasítások előtt kéne, de ez további problémákat vet fel, és mi egyébként sem használunk más olyan utasítást, ami a C jelzőbitet használná.) Ehhez szúrjunk be egy CLRC utasítást a VISSZA címke elé.
Programunk ezzel valóban tökéletessé válik. A kész forráskódot letölthetjük innen.
Programunk ezzel valóban tökéletessé válik. A kész forráskódot letölthetjük innen.
PIC - 6. szám: Megszakítások 1.
Tartalom
Bevezető a megszakításokba
A PIC megszakításai általában
A Timer megszakítás
Bevezető a megszakításokbaKezdjük az alapvető kérdéssel: mi is az a megszakítás? Nevéből adódóan az az esemény, amikor a programunkat megszakítjuk. A megszakítás során a programunk nem áll le végleg, mindössze a futása felfüggesztődik, és egy különálló rutin kapja meg a vezérlést. Ha a rutin lefutott, akkor a program folytatódhat. Egyelőre semmi különlegeset nem látunk, hiszen ugyanezt a hatást váltja ki a CALL utasítás is. Azonban a megszakítás akármikor bekövetkezhet, a program bármely soránál. Ennek megfelelően a megszakítási rutinnak (vagyis annak a rutinnak, ami a megszakításkor meghívódik) észrevétlennek kell lennie, nehogy megbolygassa a program működését. Ez annyit jelent, hogy meghíváskor el kell mentenie minden kritikus regiszternek az értékét (akkumulátor, STATUS, stb.), visszatérés előtt pedig ezeket vissza kell állítania. Na de miért is jó nekünk egy olyan rutin, ami akármikor meghívódhat? Nos, az „akármikor” egész pontosan bizonyos eseményeket jelöl. Úgy, mint:
- Megváltozott a PORTB tartalma
- Lefutott az időzítő
- Az EEPROM írás befejeződött.
Most már az „akármikort” definiáltuk, csak még mindig nem tudjuk, hogy mire is jó ez az egész. Vegyük példának a PC-k működését, és azon belül is az egereket, melyek szintén megszakításokkal dolgoznak. Amikor megmozdítjuk az egeret, az egér jelzi a számítógép felé, hogy megszakítást kér. A processzor ilyenkor felfüggeszti az éppen futó programot és meghívja az egér megszakításához tartozó rutint. Ez nem más, mint az egér meghajtóprogramja (drivere). Ez a rutin ekkor kiolvassa az egérből az elmozdulás adatait, letárolja a számítógép memóriájába további felhasználás céljára (pl. kurzor mozgatása), majd vissza is adja a vezérlést a megszakított programnak. Bár meg lehetne oldani a problémát a megszakítás teljes kikerülésével is, vagyis hogy bizonyos időközönként megnézzük az egeret, hogy van-e valami mondanivalója. Ez azonban sokkal nagyobb terhelést jelentene a processzor számára, mintha nem is kellene foglalkoznia az egérrel, csak ha az szól. A PIC esetében is hasonlóan fontos szerepe van a megszakításnak. Minthogy a PORTB változása kiválthat megszakítást, az előző számban készített programunkat megírhatjuk akár úgy is, hogy a gombokra oda sem figyelünk, csak a léptetésre. Majd a gombok lenyomására magától meghívódik a gombkiértékelő rutin. Sajnos azonban a PORTA-ra nincs megszakítási lehetőség, ezért a gombokat majd fizikailag át kell helyeznünk a PORTB-re.
A PIC megszakításai általábanEddig programjainkat csak úgy „beleömlesztettük” a memóriába, a 0-dik memóriacímtől kezdve. (Ezt jelezte az ORG 0 sor) PIC-eknél azonban követelmény, hogy a megszakítási rutin mindig a 4-es memóriacímtől kezdődjön, mert bármi is okozta a megszakítást, ez a cím hívódik meg. Ez annyit jelent, hogy az ORG direktívával jeleznünk kell majd, hogy a program ne a 0-dik címtől induljon, valamint a megszakítási rutin a 4. memóriacímtől kezdődjön. Továbbá fontos dolog még, hogy a megszakításokat be kell kapcsolni, különben az egyes események nem fogják meghívni a megszakítási rutint.
Tartalom
Bevezető a megszakításokba
A PIC megszakításai általában
A Timer megszakítás
Bevezető a megszakításokbaKezdjük az alapvető kérdéssel: mi is az a megszakítás? Nevéből adódóan az az esemény, amikor a programunkat megszakítjuk. A megszakítás során a programunk nem áll le végleg, mindössze a futása felfüggesztődik, és egy különálló rutin kapja meg a vezérlést. Ha a rutin lefutott, akkor a program folytatódhat. Egyelőre semmi különlegeset nem látunk, hiszen ugyanezt a hatást váltja ki a CALL utasítás is. Azonban a megszakítás akármikor bekövetkezhet, a program bármely soránál. Ennek megfelelően a megszakítási rutinnak (vagyis annak a rutinnak, ami a megszakításkor meghívódik) észrevétlennek kell lennie, nehogy megbolygassa a program működését. Ez annyit jelent, hogy meghíváskor el kell mentenie minden kritikus regiszternek az értékét (akkumulátor, STATUS, stb.), visszatérés előtt pedig ezeket vissza kell állítania. Na de miért is jó nekünk egy olyan rutin, ami akármikor meghívódhat? Nos, az „akármikor” egész pontosan bizonyos eseményeket jelöl. Úgy, mint:
- Megváltozott a PORTB tartalma
- Lefutott az időzítő
- Az EEPROM írás befejeződött.
Most már az „akármikort” definiáltuk, csak még mindig nem tudjuk, hogy mire is jó ez az egész. Vegyük példának a PC-k működését, és azon belül is az egereket, melyek szintén megszakításokkal dolgoznak. Amikor megmozdítjuk az egeret, az egér jelzi a számítógép felé, hogy megszakítást kér. A processzor ilyenkor felfüggeszti az éppen futó programot és meghívja az egér megszakításához tartozó rutint. Ez nem más, mint az egér meghajtóprogramja (drivere). Ez a rutin ekkor kiolvassa az egérből az elmozdulás adatait, letárolja a számítógép memóriájába további felhasználás céljára (pl. kurzor mozgatása), majd vissza is adja a vezérlést a megszakított programnak. Bár meg lehetne oldani a problémát a megszakítás teljes kikerülésével is, vagyis hogy bizonyos időközönként megnézzük az egeret, hogy van-e valami mondanivalója. Ez azonban sokkal nagyobb terhelést jelentene a processzor számára, mintha nem is kellene foglalkoznia az egérrel, csak ha az szól. A PIC esetében is hasonlóan fontos szerepe van a megszakításnak. Minthogy a PORTB változása kiválthat megszakítást, az előző számban készített programunkat megírhatjuk akár úgy is, hogy a gombokra oda sem figyelünk, csak a léptetésre. Majd a gombok lenyomására magától meghívódik a gombkiértékelő rutin. Sajnos azonban a PORTA-ra nincs megszakítási lehetőség, ezért a gombokat majd fizikailag át kell helyeznünk a PORTB-re.
A PIC megszakításai általábanEddig programjainkat csak úgy „beleömlesztettük” a memóriába, a 0-dik memóriacímtől kezdve. (Ezt jelezte az ORG 0 sor) PIC-eknél azonban követelmény, hogy a megszakítási rutin mindig a 4-es memóriacímtől kezdődjön, mert bármi is okozta a megszakítást, ez a cím hívódik meg. Ez annyit jelent, hogy az ORG direktívával jeleznünk kell majd, hogy a program ne a 0-dik címtől induljon, valamint a megszakítási rutin a 4. memóriacímtől kezdődjön. Továbbá fontos dolog még, hogy a megszakításokat be kell kapcsolni, különben az egyes események nem fogják meghívni a megszakítási rutint.
egszakításoknál az egyik legfontosabb regiszter, amit használni fogunk, az INTCON. Részletes leírást az INTCON-ról a dokumentáció 17-ik oldalán találunk. Hasonlóan a STATUS-hoz, itt is mindegyik bit valamilyen speciális szereppel rendelkezik, és saját nevük van. Egyik ilyen bit a GIE (General Interrupt Enable), mely a megszakítások „főkapcsolója”. Ha ez nulla, akkor semmiféle megszakítás nem működhet a PIC-ben. A továbbiakban két megszakítást veszünk nagyító alá, a TIMER0-t és az RB-t. Mindkét megszakítás az INTCON regiszterben a T0IF, ill. az RBIF 1-re állításával jelzi, ha megszakítást generált. Ezzel tudjuk kitalálni, hogy tulajdonképpen melyik megszakítási esemény (RB vagy TIMER0) miatt került a vezérlés a 4. címre. Na de vegyük konkrétan magukat a megszakítási eseményeket, amiket a PIC16F84 ismer. Ezzel valamivel tisztábban láthatjuk majd a dolgokat.
A Timer megszakításA timer (időzítő) egy beépített áramköre a PIC-nek. Feladata, hogy egy TMR0 nevű regisztert bizonyos időközönként növeljen eggyel. Mindezt persze a programunkkal párhuzamosan. Ha a TMR0 regiszter túllépte a maximálisan elérhető 255-öt, (vagyis túlcsordul), akkor keletkezik egy TIMER megszakítás. A PIC16F84 mindössze egy TIMER-rel rendelkezik (amit TIMER0-nak hívnak), fejlettebb PIC-eknek több is van, melyek egymással párhuzamosan dolgozhatnak.
Hogy mire jó a TIMER megszakítás, azt talán már ki is találhattuk: ezzel gyakorlatilag írhatunk olyan rutinokat, amelyek megadott időközönként mindenképpen meghívódnak, a program aktuális állásától függetlenül. Ez akkor tud hasznos lenni, ha pl. a programunk – egyéb funkciók mellett – a pontos időt is méri. Ilyenkor a főprogram csinálhat bármit, a századmásodperceket tároló regisztert léptető rutin mindenképpen meghívódik század-másodpercenként.
Mi most azonban nem órát írunk, (próbapanelünk alkalmatlan rá) hanem a bevitelkezeléskor tárgyalt programot írjuk meg újra, majd hozzáadunk egy időzítőt. Írjuk meg a következő programot:
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_OFF
ORG 0
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'00000000'
MOVWF TRISB
BCF STATUS,RP0 ;BANK0
CLRF PORTB
VISSZA BTFSS PORTA,0 ; Bekapcs teszt
BSF PORTB,0
BTFSS PORTA,1
BSF PORTB,1
BTFSC PORTA,0 ; Kikapcs teszt
BCF PORTB,0
BTFSC PORTA,1
BCF PORTB,1
GOTO VISSZA
END
Ez a program, mint láthatjuk, nem ugyanaz, mint amit a bevitelkezelésnél vettünk, (bár ugyanazt csinálja) ezért egy kissé átbeszéljük, hogy hogy is működik. A VISSZA címkéig a már megszokott port ki- és bemenet beállítás található, azzal a plusz utasítással, hogy PORTB-t teljesen lenullázzuk. (CLRF PORTB) A VISSZA címkétől indul aztán a tényleges program. A „Bekapcs teszt” és a „Kikapcs teszt” négy-négy utasítása ellenőrzi a gombok lenyomott ill. felengedett állapotát. A lenyomás tesztnél BTFSS PORTA,x utasításokkal megnézzük az x-edik gomb állapotát, a BSF PORTB,x-szel pedig PORTB egy adott bitjét (azaz az x-edik LED-et) 1-re állítjuk. A felengedés tesztnél pedig pont fordítva, vagyis BCF PORTB,x utasításokkal kioltjuk azt a LED-et, amelyiknek a gombja fel van engedve. A működés pontosabb megértése végett érdemes ezt a programot az MPLAB debug üzemmódjában végignézni, egy stimulus fájllal. (Úgy, ahogyan a második számban vettük)
Programunkat égessük is be a PIC-be és nézzük meg, hogy mit csinál! Semmi extra nem lesz benne, a gombokkal ki- és bekapcsolhatjuk az első két LED-et. Most a programot viszont kiegészítjük egy TIMER megszakítással. Például csinálja programunk mostantól azt, hogy a gombok figyelése és a LED-ek gyújtogatása mellett az 5. LED-et folyamatosan villogtatja. Jó pár sort szúrunk majd a programba, kezdjünk is neki! Amint fent is említettük, a megszakításoknak mindig a 4. címtől kell kezdődniük. A PIC a főprogram futtatását azonban mindig a 0. címtől kezdi. Minthogy a 0-3-ig terjedő címtartomány édeskevés ahhoz, hogy bármi értelmes programot odategyünk, (hisz ez mindössze 4 sort jelent) a programot egy GOTO utasítással kell kezdeni, ami átugorja a megszakítás területét, így a megszakítás után lesz bőven „helyünk” a főprogramnak. Ennek megfelelően szúrjunk be egy GOTO START utasítást az ORG 0 direktíva után:
IST P=16F84
#INCLUDE "P16F84.INC"
__CONFLIG _XT_OSC&_CP_OFF&_WDT_OFF
ORG 0
GOTO START
START BSF STATUS,RP0 ;BANK1
Most már elkezdhetjük írni a 4. címtől a megszakítási rutint. Szúrjuk be a következő rutint a GOTO START utasítás és maga a főprogram START címkéje közé!
ORG 0
GOTO START
ORG 4
BCF INTCON,T0IF
MOVLW b'00010000'
XORWF PORTB,F
RETFIE
START BSF STATUS,RP0 ;BANK1
Lássuk sorban, hogy mit is csináltunk! Először is az ORG 4 direktívával megjelöltük, hogy a rutinnak a 4. címre kell kerülnie. A TIMER0 megszakítás esetén nem csak simán a 4. címre kerül a vezérlés, hanem az INTCON T0IF jelzőbitje 1-re vált. A PIC ezzel jelzi, hogy a TIMER0 miatt keletkezett megszakítás és nem pl. a PORTB miatt. Először is egy BCF INTCON,T0IF-fel töröljük a T0IF értékét, mert sajnos ezt a PIC magától nem teszi meg, viszont ha 1-en hagyjuk, akkor megdöglik a program. A következő két utasítással (MOVLW és XORWF) egy kizáró vagy műveletet végzünk PORTB tartalma és b’00010000’ között. Ezzel azt érjük el, hogy az ötödik LED az éppen aktuális állapotának az ellenkezőjét veszi fel. (Magyarul kigyullad, ha még nem égett, ill. kialszik, ha már égett) Emlékeztetőül: Ha egy bit és 0 között kizáró vagy műveletet végzünk (szakbarbárosabban: egy bitet 0-val „XOR-olunk”), akkor azzal a bittel nem történik semmi. Azonban ha 1-gyel XOR-oljuk, akkor az adott bit invertálódik. Vagyis:
Első bit Második bit Eredmény
0 0 0
0 1 1
1 0 1
1 1 0
Ezért van, hogy a b’00010000’ érték csak az ötödik LED-et fogja invertálni (PORTB 5. bitjét). Miután invertáltuk a bitet, vissza is térhetünk a megszakításból. Ellentétben a hagyományos, CALL utasítással hívott rutinokkal, a megszakítási rutinoknak mindig a RETFIE utasítással kell visszatérniük, és nem RETURN-nel!
Programunkban már szinte minden benne van, mindössze el kell helyeznünk a megszakítások bekapcsolását végző utasításokat a főprogram elején. (Ahol a többi dolgokat is beállítjuk.)
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'00000000'
MOVWF TRISB
MOVLW B'10000111'
MOVWF OPTION_REG
BCF STATUS,RP0 ;BANK0
CLRF PORTB
BSF INTCON,GIE
BSF INTCON,T0IE
VISSZA BTFSS PORTA,0 ; Bekapcs teszt
A Timer megszakításA timer (időzítő) egy beépített áramköre a PIC-nek. Feladata, hogy egy TMR0 nevű regisztert bizonyos időközönként növeljen eggyel. Mindezt persze a programunkkal párhuzamosan. Ha a TMR0 regiszter túllépte a maximálisan elérhető 255-öt, (vagyis túlcsordul), akkor keletkezik egy TIMER megszakítás. A PIC16F84 mindössze egy TIMER-rel rendelkezik (amit TIMER0-nak hívnak), fejlettebb PIC-eknek több is van, melyek egymással párhuzamosan dolgozhatnak.
Hogy mire jó a TIMER megszakítás, azt talán már ki is találhattuk: ezzel gyakorlatilag írhatunk olyan rutinokat, amelyek megadott időközönként mindenképpen meghívódnak, a program aktuális állásától függetlenül. Ez akkor tud hasznos lenni, ha pl. a programunk – egyéb funkciók mellett – a pontos időt is méri. Ilyenkor a főprogram csinálhat bármit, a századmásodperceket tároló regisztert léptető rutin mindenképpen meghívódik század-másodpercenként.
Mi most azonban nem órát írunk, (próbapanelünk alkalmatlan rá) hanem a bevitelkezeléskor tárgyalt programot írjuk meg újra, majd hozzáadunk egy időzítőt. Írjuk meg a következő programot:
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_OFF
ORG 0
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'00000000'
MOVWF TRISB
BCF STATUS,RP0 ;BANK0
CLRF PORTB
VISSZA BTFSS PORTA,0 ; Bekapcs teszt
BSF PORTB,0
BTFSS PORTA,1
BSF PORTB,1
BTFSC PORTA,0 ; Kikapcs teszt
BCF PORTB,0
BTFSC PORTA,1
BCF PORTB,1
GOTO VISSZA
END
Ez a program, mint láthatjuk, nem ugyanaz, mint amit a bevitelkezelésnél vettünk, (bár ugyanazt csinálja) ezért egy kissé átbeszéljük, hogy hogy is működik. A VISSZA címkéig a már megszokott port ki- és bemenet beállítás található, azzal a plusz utasítással, hogy PORTB-t teljesen lenullázzuk. (CLRF PORTB) A VISSZA címkétől indul aztán a tényleges program. A „Bekapcs teszt” és a „Kikapcs teszt” négy-négy utasítása ellenőrzi a gombok lenyomott ill. felengedett állapotát. A lenyomás tesztnél BTFSS PORTA,x utasításokkal megnézzük az x-edik gomb állapotát, a BSF PORTB,x-szel pedig PORTB egy adott bitjét (azaz az x-edik LED-et) 1-re állítjuk. A felengedés tesztnél pedig pont fordítva, vagyis BCF PORTB,x utasításokkal kioltjuk azt a LED-et, amelyiknek a gombja fel van engedve. A működés pontosabb megértése végett érdemes ezt a programot az MPLAB debug üzemmódjában végignézni, egy stimulus fájllal. (Úgy, ahogyan a második számban vettük)
Programunkat égessük is be a PIC-be és nézzük meg, hogy mit csinál! Semmi extra nem lesz benne, a gombokkal ki- és bekapcsolhatjuk az első két LED-et. Most a programot viszont kiegészítjük egy TIMER megszakítással. Például csinálja programunk mostantól azt, hogy a gombok figyelése és a LED-ek gyújtogatása mellett az 5. LED-et folyamatosan villogtatja. Jó pár sort szúrunk majd a programba, kezdjünk is neki! Amint fent is említettük, a megszakításoknak mindig a 4. címtől kell kezdődniük. A PIC a főprogram futtatását azonban mindig a 0. címtől kezdi. Minthogy a 0-3-ig terjedő címtartomány édeskevés ahhoz, hogy bármi értelmes programot odategyünk, (hisz ez mindössze 4 sort jelent) a programot egy GOTO utasítással kell kezdeni, ami átugorja a megszakítás területét, így a megszakítás után lesz bőven „helyünk” a főprogramnak. Ennek megfelelően szúrjunk be egy GOTO START utasítást az ORG 0 direktíva után:
IST P=16F84
#INCLUDE "P16F84.INC"
__CONFLIG _XT_OSC&_CP_OFF&_WDT_OFF
ORG 0
GOTO START
START BSF STATUS,RP0 ;BANK1
Most már elkezdhetjük írni a 4. címtől a megszakítási rutint. Szúrjuk be a következő rutint a GOTO START utasítás és maga a főprogram START címkéje közé!
ORG 0
GOTO START
ORG 4
BCF INTCON,T0IF
MOVLW b'00010000'
XORWF PORTB,F
RETFIE
START BSF STATUS,RP0 ;BANK1
Lássuk sorban, hogy mit is csináltunk! Először is az ORG 4 direktívával megjelöltük, hogy a rutinnak a 4. címre kell kerülnie. A TIMER0 megszakítás esetén nem csak simán a 4. címre kerül a vezérlés, hanem az INTCON T0IF jelzőbitje 1-re vált. A PIC ezzel jelzi, hogy a TIMER0 miatt keletkezett megszakítás és nem pl. a PORTB miatt. Először is egy BCF INTCON,T0IF-fel töröljük a T0IF értékét, mert sajnos ezt a PIC magától nem teszi meg, viszont ha 1-en hagyjuk, akkor megdöglik a program. A következő két utasítással (MOVLW és XORWF) egy kizáró vagy műveletet végzünk PORTB tartalma és b’00010000’ között. Ezzel azt érjük el, hogy az ötödik LED az éppen aktuális állapotának az ellenkezőjét veszi fel. (Magyarul kigyullad, ha még nem égett, ill. kialszik, ha már égett) Emlékeztetőül: Ha egy bit és 0 között kizáró vagy műveletet végzünk (szakbarbárosabban: egy bitet 0-val „XOR-olunk”), akkor azzal a bittel nem történik semmi. Azonban ha 1-gyel XOR-oljuk, akkor az adott bit invertálódik. Vagyis:
Első bit Második bit Eredmény
0 0 0
0 1 1
1 0 1
1 1 0
Ezért van, hogy a b’00010000’ érték csak az ötödik LED-et fogja invertálni (PORTB 5. bitjét). Miután invertáltuk a bitet, vissza is térhetünk a megszakításból. Ellentétben a hagyományos, CALL utasítással hívott rutinokkal, a megszakítási rutinoknak mindig a RETFIE utasítással kell visszatérniük, és nem RETURN-nel!
Programunkban már szinte minden benne van, mindössze el kell helyeznünk a megszakítások bekapcsolását végző utasításokat a főprogram elején. (Ahol a többi dolgokat is beállítjuk.)
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'00000000'
MOVWF TRISB
MOVLW B'10000111'
MOVWF OPTION_REG
BCF STATUS,RP0 ;BANK0
CLRF PORTB
BSF INTCON,GIE
BSF INTCON,T0IE
VISSZA BTFSS PORTA,0 ; Bekapcs teszt
Na ezt a négy új sort bizony elég szépen belekevertük a programba! Az OPTION_REG, – melyről eddig még nem volt szó – a BANK1-ben helyezkedik el, így még a BANK0-ra való váltás előtt kell értéket adni neki. (A regiszterek egy részét csak a BANK0-ban, másik részét a BANK1-ben, harmadik részét pedig mindkettőben elérhetjük. Hogy éppen melyik BANK tartományt érhetjük el, azt a STATUS regiszter RP0 bitjén adhatjuk meg. Az egyes regiszterek hollétéről a dokumentum 13. oldalán találunk egy táblázatot.) Az OPTION_REG regiszter amolyan kiegészítő beállításokat tartalmaz a megszakításokhoz. Az RBPU bitet (lásd képen) majd az RB megszakításnál vesszük, (addig is elég annyit tudni, hogy 1-re kell mindig tenni) minket most elsősorban a PS2-PS0 bitek érdekelnek. Ezek az ún. „előosztó” bitek, amik megmondják, hogy hány órajel-ciklusonként növekedjen egyet a TMR0 regiszter. (Emlékeztetőül: a TMR0 regiszter magától növekszik, és ha túllép a 255-ön, akkor keletkezik TIMER megszakítás) Minél gyakrabban növekszik a TMR0 értéke, annál gyorsabban következik be a megszakítás. A három előosztó bittel egy 0-7-ig terjedő bináris számot tudunk kirakni, ahol ’000’-tól lesz a leggyorsabb a számlálás (minden második ciklusnál növekszik a TMR0), ’111’-től pedig a leglassabb (256 ciklus után növekszik egyet.)
A teljesség kedvéért az alábbi léptetési értékeket adhatjuk meg az időzítőnek az OPTION_REG-en keresztül:
OPTION_REG első három bitje
Késleltetés mértéke (Hány órajel-ciklus után növekedjen egyet a TMR0)
000/2
001/4
010/8
011/16
100/32
101/64
110/128
111/256
Fordítsuk le a programot és égessük be a PIC-be, hogy lássuk az eredményt. Aztán kielemezzük részletesebben a látottakat. A kész programot letölthetjük innen is. Amint áramot adunk a PIC-nek, láthatjuk, hogy az ötödik LED veszettül villódzik. Ha pedig nyomogatjuk a gombokat, akkor azt is láthatjuk, hogy a programunk eredeti funkcióját is megtartotta, vagyis kigyújtja az első két LED-et a gombok állásának megfelelően. Bizony, ez már valódi multitaszk program, vagyis egyszerre több funkció lát el párhuzamosan. (Villogtat, és a gombokat is kezeli.)
Most térjünk vissza az MPLAB-hoz és nézzük meg, hogy mi is történik ilyenkor a programban! A Watch ablakba vegyük fel a TMR0 regisztert! (Ha nem lenne nyitva a Watch, akkor a Window/Watch window/New watch window menüponttal vehetünk elő egyet. Regisztert a Watch ablak kijelölésével és az INS billentyű megnyomásával adhatunk hozzá.) Továbbá, ha nem lenne a Watch ablakban, akkor tegyük még hozzá a PORTA és PORTB regisztereket is, esetleg még a W-t. Minthogy programunk éppen a leglassabb időzítésre van beállítva, (256 ciklusonként növeli a TMR0-t) az OPTION_REG-et beállító MOVLW utasítás értékét állítsuk B’10000111’-ről B’10000000’-ra! (Vagyis a leggyorsabbra, különben megöregszünk, mire egyet lép a TMR0 a szimulációban.) Fordítsuk újra a programot, és kezdhetjük a vizsgálatot! Nyomjunk F6-ot a program emulált indításához. Mint láthatjuk, a program a GOTO START sorral indul, vagyis a 0. címtől. Következőnek helytelenül a megszakítási rutinra futna rá, ezért kell elugrani. Lépjünk tovább F8-cal a VISSZA címkéhez! Mint láthatjuk, lépkedés közben a TMR0 nem növekedett, mert a megszakítás még nincs bekapcsolva. Azonban a VISSZA-tól kezdve növekedni fog, mert a BSF utasítások engedélyezik az általános és a TIMER megszakítást az INTCON regiszterben. Most ha nyomogatjuk az F8-at, akkor azt láthatjuk, hogy a program csak a billentyűtesztelő részben fut, ki se lép belőle. Azonban a TMR0 majd minden lépésnél növekszik egyet. Futtassuk a programot addig, amíg TMR0 el nem éri a 255-öt! (Vagy a H’FF’-et! Lenyomva is tarthatjuk az F8-at, így igen gyorsan lépkedhetünk, csak aztán a végső érték közelében lassítsunk!) Amint TMR0 túllépi a 255-öt, láthatjuk, hogy bármely sornál is állt a program, a vezérlés a 4. címen levő megszakítási rutinra kerül. Futtassuk végig a megszakítási rutint! Láthatjuk, ahogyan a XORWF utasítás átbillenti PORTB 5. bitjét, majd a RETFIE utasítás hatására visszakerül a vezérlés arra a pontra, ahol a gombtesztelő megszakadt. Közben a számlálás is elölről kezdődik. Tehát nem kellett bonyolult feltételeket és speciális DELAY rutinokat írnunk arra, hogy a gombok tesztelése mellett az 5. LED villogjon, a két feladat párhuzamosan tudott futni, bele sem nyúltunk a gombtesztelő programrészbe!
Most lássunk egy példát arra, hogy hogyan lehet a megszakítási rutin és a főprogram között összhangot teremteni! Írjuk meg az előző számban levő gombokra váltó futófényt TIMER megszakítással! A főprogram mindössze a gombokra figyeljen, és ennek megfelelően állítsa be az IRANY regiszter értékét 1-re vagy 0-ra! A megszakítás feladata legyen, hogy IRANY értékének megfelelően a fényt jobbra vagy balra lépteti! Kezdjük az elején, az IRANY regiszter deklarációjával és a megszakítási rutinnal!
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG_XT_OSC&_CP_OFF&_WDT_OFF
CBLOCK 0X0C
IRANY
ENDC
ORG 0
GOTO START
ORG 4
BCF INTCON,T0IF
BTFSC IRANY,0
GOTO BALRA
JOBBRA RRF PORTB,F
RETFIE
BALRA RLF PORTB,F
RETFIE
Nagyon magyaráznivaló nincs ebben. A követelménynek megfelelően töröljük az INTCON T0IF bitjét, majd az IRANY regiszter első bitjének megfelelően (minthogy ténylegesen csak az első bit fog változni az IRANY regiszterben) az RRF vagy RLF forgatóutasításra ugrunk, aztán visszatérünk a rutinból. Folytassuk a rutint a főprogram inicializációs részével!
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'00000000'
MOVWF TRISB
MOVLW B'10000111'
MOVWF OPTION_REG
BCF STATUS,RP0 ;BANK0
CLRF IRANY
CLRC
MOVLW 01
MOVWF PORTB
BSF INTCON,GIE
BSF INTCON,T0IE
A szokásos TRISA és TRISB regiszterek után beállítjuk az OPTION_REG-et, hogy a leglassabb legyen a számlálás. Töröljük az IRANY értékét (mégis csak így a leggyorsabb kezdőértéket adni neki), valamint a C jelzőbitet is. (Lásd előző számban, hogy miért) PORTB-n beállítjuk, hogy az egyik LED világítson, a későbbiekben majd ezt kell csak jobbra-balra tologatni. Miután mindezzel megvagyunk, bekapcsolhatjuk a megszakítások főkapcsolóját jelképező GIE bitet és a TIMER0-t jelképező T0IE-t. Most jöhet maga a főprogram, mely mindössze csak az IRANY értékét változtatja, a két gomb állásának megfelelően.
VISSZA: MOVFW IRANY
BTFSS PORTA,0
MOVLW 00
BTFSS PORTA,1
MOVLW 01
MOVWF IRANY
GOTO VISSZA
END
Ez a rutin gyakorlatilag ugyanaz, mint az előző számban lévő program tesztelő rutinja, annyi a különbség, hogy IRANY beállítása után nem ugrik a jobbra, ill. balra forgató rutinra, hanem elölről kezdi a tesztelést. A kiértékelés és forgatás már a megszakítás dolga, itt nem kell vele foglalkoznunk! Készen is vagyunk, égessük be a PIC-be, majd futtassuk a programot! (Illetve letölthetjük először innen!)
Programunk pont úgy működik, akár az előző számban levő változat: ha az egyik gombot nyomjuk, akkor balra fut a fény, ha a másik gombot, akkor meg jobbra. Csak a fene vigye el, miért ilyen gyors? Különösen, hogy a Timer0-t a leglassabbra állítottuk az OPTION_REG regiszterben! A Timer0 állításával nem tudunk ezen segíteni, mert valóban ez a leglassabb fokozata az időzítőnek. Azonban egy kis trükkel megoldhatjuk a problémát! Késleltetést nem tanácsos használni a megszakításon belül, mert kiszámíthatatlan következményekkel járhat. Illetve nagyon is kiszámíthatókkal: ugyanis a TIMER0 következő megszakításakor még mindig nem léptünk ki a megszakítási rutinból. Ugyan a rutin lefutásának idejére le vannak tiltva a megszakítások, de nem tudjuk pontosan időzíteni, hogy a rutin kilépése után mikor lesz a következő TIMER0 megszakítás. (No meg a megszakítások lebénítása egyébként sem szerencsés, az RB és a TIMER megszakítások kombinálásánál majd látni fogjuk.) A megoldás sokkal egyszerűbb: mindössze úgy kell megírnunk a megszakítási rutint, hogy az elejére teszünk egy számlálót. A számláló egy adott regiszter tartalmát csökkenti eggyel, és ha a számláló értéke 0, akkor engedi lefutni a rutint, egyébként meg azonnal visszatér. Tehát ha pl. 9 az említett regiszter értéke, akkor minden 9-dik megszakításnál fut csak le a megszakítási rutin, a többinél pedig visszatér. Egészítsük ki rutinunkat ezzel a számlálós megoldással!
Elsőnek vegyük fel a számláláshoz szükséges új regisztert (IDOZIT), valamint egy WSAVE nevű regisztert!
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_OFF
CBLOCK 0X0C
IDOZIT
WSAVE
IRANY
ENDC
ORG 0
GOTO START
A WSAVE regiszterre mindjárt láthatjuk, hogy miért van szükség. Nézzük, hogy hogyan kell módosítani a megszakítási rutint!
ORG 4
BCF INTCON,T0IF
DECFSZ IDOZIT ;COUNTER
RETFIE
MOVWF WSAVE
MOVLW 9
MOVWF IDOZIT ;COUNTER END
MOVFW WSAVE
BTFSC IRANY,0
GOTO BALRA
JOBBRA RRF PORTB,F
RETFIE
BALRA RLF PORTB,F
RETFIE
Először ugye törölnünk kell T0IF-et. Utána egy DECFSZ utasítással csökkentjük a számlálónkat. Ha a számláló még nem nulla, akkor a RETFIE utasítással ki is lépünk a rutinból. Ha pedig nulla, akkor átugorja a RETFIE-t. Ezután az akkumulátort betöltjük a WSAVE regiszterbe. Mint fent említettük, a megszakítás bármikor meghívódhat, akár egy értékadás közepén is. Már pedig ha pl. egy MOVLW 01 – MOVWF PORTB páros közepén szakad meg a futás és a megszakítási rutin elállítja a W-t, akkor biztosra vehetjük, hogy nem 1 fog kerülni PORTB-be, amikor visszatér a főprogramra a futás. Az eddigi megszakításos példákban a nem használtuk egyszerre a főprogramban és a megszakításban is az akkumulátort, most viszont jelen van mindkettőben, tehát el kell mentenünk W értékét, hogy ne zavarja meg a főprogram futását. (Azért érdemes kipróbálnunk a programot úgy is, hogy nem mentjük el a W-t!) Az akkumulátor elmentése után már nyugodtan használhatjuk azt, így az IDOZIT regisztert újra feltölthetjük 9-cel a következő megszakítás számára. Minthogy többször nem fog kelleni az akkumulátor, vissza is tölthetjük az elmentett értéket a MOVFW WSAVE utasítással. Innentől a megszakítási rutin a régi kerékvágás szerint folytatódik. Az inicializációs részhez még adjuk hozzá az IDOZIT regiszter kezdeti feltöltését!
....
MOVLW 01
MOVWF PORTB
MOVLW 9
MOVWF IDOZIT
BSF INTCON,GIE
BSF INTCON,T0IE
A kész program letölthető innen. A programot PIC-be égetve láthatjuk, hogy a sebesség most már megfelelő. Mi több, a gombok sokkal érzékenyebben reagálnak, mint a nem megszakításos megoldásban. Ez azért van, mert a nem megszakításos változatban amíg késleltettünk, a PIC nem csinált semmit, még a gombokat sem nézte. A gombok állását csak akkor figyelte, ha éppen átváltott egyik LED-ről a másikra. Itt viszont kihasználjuk a várakozást, és közben is vizsgáljuk a gombokat.
A következő számban folytatjuk a megszakításokat, mégpedig a PORTB által kiváltott RB megszakítással.
A teljesség kedvéért az alábbi léptetési értékeket adhatjuk meg az időzítőnek az OPTION_REG-en keresztül:
OPTION_REG első három bitje
Késleltetés mértéke (Hány órajel-ciklus után növekedjen egyet a TMR0)
000/2
001/4
010/8
011/16
100/32
101/64
110/128
111/256
Fordítsuk le a programot és égessük be a PIC-be, hogy lássuk az eredményt. Aztán kielemezzük részletesebben a látottakat. A kész programot letölthetjük innen is. Amint áramot adunk a PIC-nek, láthatjuk, hogy az ötödik LED veszettül villódzik. Ha pedig nyomogatjuk a gombokat, akkor azt is láthatjuk, hogy a programunk eredeti funkcióját is megtartotta, vagyis kigyújtja az első két LED-et a gombok állásának megfelelően. Bizony, ez már valódi multitaszk program, vagyis egyszerre több funkció lát el párhuzamosan. (Villogtat, és a gombokat is kezeli.)
Most térjünk vissza az MPLAB-hoz és nézzük meg, hogy mi is történik ilyenkor a programban! A Watch ablakba vegyük fel a TMR0 regisztert! (Ha nem lenne nyitva a Watch, akkor a Window/Watch window/New watch window menüponttal vehetünk elő egyet. Regisztert a Watch ablak kijelölésével és az INS billentyű megnyomásával adhatunk hozzá.) Továbbá, ha nem lenne a Watch ablakban, akkor tegyük még hozzá a PORTA és PORTB regisztereket is, esetleg még a W-t. Minthogy programunk éppen a leglassabb időzítésre van beállítva, (256 ciklusonként növeli a TMR0-t) az OPTION_REG-et beállító MOVLW utasítás értékét állítsuk B’10000111’-ről B’10000000’-ra! (Vagyis a leggyorsabbra, különben megöregszünk, mire egyet lép a TMR0 a szimulációban.) Fordítsuk újra a programot, és kezdhetjük a vizsgálatot! Nyomjunk F6-ot a program emulált indításához. Mint láthatjuk, a program a GOTO START sorral indul, vagyis a 0. címtől. Következőnek helytelenül a megszakítási rutinra futna rá, ezért kell elugrani. Lépjünk tovább F8-cal a VISSZA címkéhez! Mint láthatjuk, lépkedés közben a TMR0 nem növekedett, mert a megszakítás még nincs bekapcsolva. Azonban a VISSZA-tól kezdve növekedni fog, mert a BSF utasítások engedélyezik az általános és a TIMER megszakítást az INTCON regiszterben. Most ha nyomogatjuk az F8-at, akkor azt láthatjuk, hogy a program csak a billentyűtesztelő részben fut, ki se lép belőle. Azonban a TMR0 majd minden lépésnél növekszik egyet. Futtassuk a programot addig, amíg TMR0 el nem éri a 255-öt! (Vagy a H’FF’-et! Lenyomva is tarthatjuk az F8-at, így igen gyorsan lépkedhetünk, csak aztán a végső érték közelében lassítsunk!) Amint TMR0 túllépi a 255-öt, láthatjuk, hogy bármely sornál is állt a program, a vezérlés a 4. címen levő megszakítási rutinra kerül. Futtassuk végig a megszakítási rutint! Láthatjuk, ahogyan a XORWF utasítás átbillenti PORTB 5. bitjét, majd a RETFIE utasítás hatására visszakerül a vezérlés arra a pontra, ahol a gombtesztelő megszakadt. Közben a számlálás is elölről kezdődik. Tehát nem kellett bonyolult feltételeket és speciális DELAY rutinokat írnunk arra, hogy a gombok tesztelése mellett az 5. LED villogjon, a két feladat párhuzamosan tudott futni, bele sem nyúltunk a gombtesztelő programrészbe!
Most lássunk egy példát arra, hogy hogyan lehet a megszakítási rutin és a főprogram között összhangot teremteni! Írjuk meg az előző számban levő gombokra váltó futófényt TIMER megszakítással! A főprogram mindössze a gombokra figyeljen, és ennek megfelelően állítsa be az IRANY regiszter értékét 1-re vagy 0-ra! A megszakítás feladata legyen, hogy IRANY értékének megfelelően a fényt jobbra vagy balra lépteti! Kezdjük az elején, az IRANY regiszter deklarációjával és a megszakítási rutinnal!
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG_XT_OSC&_CP_OFF&_WDT_OFF
CBLOCK 0X0C
IRANY
ENDC
ORG 0
GOTO START
ORG 4
BCF INTCON,T0IF
BTFSC IRANY,0
GOTO BALRA
JOBBRA RRF PORTB,F
RETFIE
BALRA RLF PORTB,F
RETFIE
Nagyon magyaráznivaló nincs ebben. A követelménynek megfelelően töröljük az INTCON T0IF bitjét, majd az IRANY regiszter első bitjének megfelelően (minthogy ténylegesen csak az első bit fog változni az IRANY regiszterben) az RRF vagy RLF forgatóutasításra ugrunk, aztán visszatérünk a rutinból. Folytassuk a rutint a főprogram inicializációs részével!
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'00000000'
MOVWF TRISB
MOVLW B'10000111'
MOVWF OPTION_REG
BCF STATUS,RP0 ;BANK0
CLRF IRANY
CLRC
MOVLW 01
MOVWF PORTB
BSF INTCON,GIE
BSF INTCON,T0IE
A szokásos TRISA és TRISB regiszterek után beállítjuk az OPTION_REG-et, hogy a leglassabb legyen a számlálás. Töröljük az IRANY értékét (mégis csak így a leggyorsabb kezdőértéket adni neki), valamint a C jelzőbitet is. (Lásd előző számban, hogy miért) PORTB-n beállítjuk, hogy az egyik LED világítson, a későbbiekben majd ezt kell csak jobbra-balra tologatni. Miután mindezzel megvagyunk, bekapcsolhatjuk a megszakítások főkapcsolóját jelképező GIE bitet és a TIMER0-t jelképező T0IE-t. Most jöhet maga a főprogram, mely mindössze csak az IRANY értékét változtatja, a két gomb állásának megfelelően.
VISSZA: MOVFW IRANY
BTFSS PORTA,0
MOVLW 00
BTFSS PORTA,1
MOVLW 01
MOVWF IRANY
GOTO VISSZA
END
Ez a rutin gyakorlatilag ugyanaz, mint az előző számban lévő program tesztelő rutinja, annyi a különbség, hogy IRANY beállítása után nem ugrik a jobbra, ill. balra forgató rutinra, hanem elölről kezdi a tesztelést. A kiértékelés és forgatás már a megszakítás dolga, itt nem kell vele foglalkoznunk! Készen is vagyunk, égessük be a PIC-be, majd futtassuk a programot! (Illetve letölthetjük először innen!)
Programunk pont úgy működik, akár az előző számban levő változat: ha az egyik gombot nyomjuk, akkor balra fut a fény, ha a másik gombot, akkor meg jobbra. Csak a fene vigye el, miért ilyen gyors? Különösen, hogy a Timer0-t a leglassabbra állítottuk az OPTION_REG regiszterben! A Timer0 állításával nem tudunk ezen segíteni, mert valóban ez a leglassabb fokozata az időzítőnek. Azonban egy kis trükkel megoldhatjuk a problémát! Késleltetést nem tanácsos használni a megszakításon belül, mert kiszámíthatatlan következményekkel járhat. Illetve nagyon is kiszámíthatókkal: ugyanis a TIMER0 következő megszakításakor még mindig nem léptünk ki a megszakítási rutinból. Ugyan a rutin lefutásának idejére le vannak tiltva a megszakítások, de nem tudjuk pontosan időzíteni, hogy a rutin kilépése után mikor lesz a következő TIMER0 megszakítás. (No meg a megszakítások lebénítása egyébként sem szerencsés, az RB és a TIMER megszakítások kombinálásánál majd látni fogjuk.) A megoldás sokkal egyszerűbb: mindössze úgy kell megírnunk a megszakítási rutint, hogy az elejére teszünk egy számlálót. A számláló egy adott regiszter tartalmát csökkenti eggyel, és ha a számláló értéke 0, akkor engedi lefutni a rutint, egyébként meg azonnal visszatér. Tehát ha pl. 9 az említett regiszter értéke, akkor minden 9-dik megszakításnál fut csak le a megszakítási rutin, a többinél pedig visszatér. Egészítsük ki rutinunkat ezzel a számlálós megoldással!
Elsőnek vegyük fel a számláláshoz szükséges új regisztert (IDOZIT), valamint egy WSAVE nevű regisztert!
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_OFF
CBLOCK 0X0C
IDOZIT
WSAVE
IRANY
ENDC
ORG 0
GOTO START
A WSAVE regiszterre mindjárt láthatjuk, hogy miért van szükség. Nézzük, hogy hogyan kell módosítani a megszakítási rutint!
ORG 4
BCF INTCON,T0IF
DECFSZ IDOZIT ;COUNTER
RETFIE
MOVWF WSAVE
MOVLW 9
MOVWF IDOZIT ;COUNTER END
MOVFW WSAVE
BTFSC IRANY,0
GOTO BALRA
JOBBRA RRF PORTB,F
RETFIE
BALRA RLF PORTB,F
RETFIE
Először ugye törölnünk kell T0IF-et. Utána egy DECFSZ utasítással csökkentjük a számlálónkat. Ha a számláló még nem nulla, akkor a RETFIE utasítással ki is lépünk a rutinból. Ha pedig nulla, akkor átugorja a RETFIE-t. Ezután az akkumulátort betöltjük a WSAVE regiszterbe. Mint fent említettük, a megszakítás bármikor meghívódhat, akár egy értékadás közepén is. Már pedig ha pl. egy MOVLW 01 – MOVWF PORTB páros közepén szakad meg a futás és a megszakítási rutin elállítja a W-t, akkor biztosra vehetjük, hogy nem 1 fog kerülni PORTB-be, amikor visszatér a főprogramra a futás. Az eddigi megszakításos példákban a nem használtuk egyszerre a főprogramban és a megszakításban is az akkumulátort, most viszont jelen van mindkettőben, tehát el kell mentenünk W értékét, hogy ne zavarja meg a főprogram futását. (Azért érdemes kipróbálnunk a programot úgy is, hogy nem mentjük el a W-t!) Az akkumulátor elmentése után már nyugodtan használhatjuk azt, így az IDOZIT regisztert újra feltölthetjük 9-cel a következő megszakítás számára. Minthogy többször nem fog kelleni az akkumulátor, vissza is tölthetjük az elmentett értéket a MOVFW WSAVE utasítással. Innentől a megszakítási rutin a régi kerékvágás szerint folytatódik. Az inicializációs részhez még adjuk hozzá az IDOZIT regiszter kezdeti feltöltését!
....
MOVLW 01
MOVWF PORTB
MOVLW 9
MOVWF IDOZIT
BSF INTCON,GIE
BSF INTCON,T0IE
A kész program letölthető innen. A programot PIC-be égetve láthatjuk, hogy a sebesség most már megfelelő. Mi több, a gombok sokkal érzékenyebben reagálnak, mint a nem megszakításos megoldásban. Ez azért van, mert a nem megszakításos változatban amíg késleltettünk, a PIC nem csinált semmit, még a gombokat sem nézte. A gombok állását csak akkor figyelte, ha éppen átváltott egyik LED-ről a másikra. Itt viszont kihasználjuk a várakozást, és közben is vizsgáljuk a gombokat.
A következő számban folytatjuk a megszakításokat, mégpedig a PORTB által kiváltott RB megszakítással.
PIC - 7. szám: Megszakítások 2.
Tartalom
Az RB megszakítás
Több megszakítás használata egyszerre
A Watchdog Timer
Az RB megszakításAz RB megszakítás sok mindenben különbözik a Timer megszakítástól. RB megszakítás akkor keletkezik, ha a PORTB felső 4 bitjében (vagyis RB4-RB7) változás keletkezik, mégpedig kívülről jövő változás. Magyarul, ha rákötünk egy gombot a PORTB-re, és megnyomjuk a gombot, akkor keletkezik ez a megszakítás. Ez persze azt is jelenti, hogy PORTB-nek bemenetként kell funkcionálnia, vagyis a próbapanelt át kell szerelnünk.
PORTB 6-dik és 7-dik bitjéről, azaz a PIC 12-es és 13-as lábáról le kell választani a diódákat és az ellenállásokat, majd kössük át helyükre G1 és G2 gombokat! (A gombok jelenleg a 17-es és 18-as lábra vannak csatlakoztatva.) A rajzon pirossal vannak jelölve a változások. Ha az átalakítással megvagyunk, nézzük meg, hogy hogyan működik a gyakorlatban az RB megszakítás! Készítsük el a futófény programot, de ezúttal megfordítjuk a megszakítás szerepét: most a megszakítás állítja be az irányt és a főprogram „lépteti” a fényt. (A Timer-nél ugyebár a megszakítástól lépett egyet jobbra vagy balra a fény, a főprogram pedig megmondta, hogy jobbra vagy balra kell mennie.)
A deklaráció nagyon újat nem rejteget számunkra. A jó öreg DELAY rutinunk T1 és T2 regisztere, valamint az előző számban is alkalmazott WSAVE és IRANY lesz benne, valamint egy PB nevű regiszter is:
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_OFF
CBLOCK 0X0C
WSAVE
IRANY
T1
T2
PB
ENDC
Most pedig jöhet a megszakítási rutin. Mint mondtuk, a megszakításnak itt a gomblenyomás lesz a szerepe.
ORG 0
GOTO START
ORG 4
BCF INTCON,RBIF
MOVWF WSAVE
MOVFW IRANY
BTFSS PORTB,6
MOVLW 00
BTFSS PORTB,7
MOVLW 01
MOVWF IRANY
MOVFW WSAVE
RETFIE
Bár ennyi példa után már illik ezt a megoldást átlátnunk, azért elemezzük ki egy kicsit! Az első két sorban csak kikerüljük a megszakítást (mert 4-től indul minden megszakítási rutin). Töröljük az RB megszakítást jelző RBIF jelzőbitet egy BCF utasítással. Figyeljünk oda, hogy ezúttal az RBIF-et töröljük, nem a T0IF-et! A Timeres példához hasonlóan elmentjük az akkumulátort WSAVE regiszterbe, majd jön a lényege a megszakításnak. A következő hat sorra (MOVFW IRANY-tól MOVWF IRANY-ig) egy kicsit máshogy nézünk rá, hogy könnyebben megértsük. Az elején ugye betöltjük IRANY jelenlegi értékét az akkumulátorba, négy soron keresztül csinálunk valamit (az a valami lehet „semmi” is!) az akkumulátorral, majd visszaírjuk az IRANY-ba. Na most ugye mi is az a négy sor? Ha az első gomb volt lenyomva (PORTB,6), akkor akkumulátort nullázzuk, ha a másodikat nyomtuk meg (PORTB,7), akkor az akkumulátorba 1-et teszünk. Ha egyiket sem nyomtuk meg, akkor békén hagyjuk az akkumulátort, így IRANY értéke változatlan marad. Most felmerülhet a kérdés, hogy ugyan hogyan lehetséges az, hogy egy gomb se legyen lenyomva ennél a tesztelésnél, amikor a megszakítás eleve gombnyomásra hívódik meg? A válasz az, hogy a gombok felengedésekor is meghívódik a megszakítás! Ne feledjük, hogy az RB megszakítás a PORTB felső négy bitjének változására hívódik meg, amibe beletartozik a 0-ból 1-be menet és az 1-ből 0-ba menet is. A rutin végén az elmentett akkumulátort visszatöltjük és kilépünk.
Jöhet a főprogram a kezdeti értékek beállításával:
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'11000000'
MOVWF TRISB
MOVLW B'10000111'
MOVWF OPTION_REG
BCF STATUS,RP0 ;BANK0
CLRF IRANY
MOVLW 01
MOVWF PORTB
BSF INTCON,GIE
BSF INTCON,RBIE
TRISA értéke marad a szokásos. TRISB-t azonban ezúttal máshogy állítjuk be, mert a PORTB felső két bitje bemenetként funkcionál.
Tartalom
Az RB megszakítás
Több megszakítás használata egyszerre
A Watchdog Timer
Az RB megszakításAz RB megszakítás sok mindenben különbözik a Timer megszakítástól. RB megszakítás akkor keletkezik, ha a PORTB felső 4 bitjében (vagyis RB4-RB7) változás keletkezik, mégpedig kívülről jövő változás. Magyarul, ha rákötünk egy gombot a PORTB-re, és megnyomjuk a gombot, akkor keletkezik ez a megszakítás. Ez persze azt is jelenti, hogy PORTB-nek bemenetként kell funkcionálnia, vagyis a próbapanelt át kell szerelnünk.
PORTB 6-dik és 7-dik bitjéről, azaz a PIC 12-es és 13-as lábáról le kell választani a diódákat és az ellenállásokat, majd kössük át helyükre G1 és G2 gombokat! (A gombok jelenleg a 17-es és 18-as lábra vannak csatlakoztatva.) A rajzon pirossal vannak jelölve a változások. Ha az átalakítással megvagyunk, nézzük meg, hogy hogyan működik a gyakorlatban az RB megszakítás! Készítsük el a futófény programot, de ezúttal megfordítjuk a megszakítás szerepét: most a megszakítás állítja be az irányt és a főprogram „lépteti” a fényt. (A Timer-nél ugyebár a megszakítástól lépett egyet jobbra vagy balra a fény, a főprogram pedig megmondta, hogy jobbra vagy balra kell mennie.)
A deklaráció nagyon újat nem rejteget számunkra. A jó öreg DELAY rutinunk T1 és T2 regisztere, valamint az előző számban is alkalmazott WSAVE és IRANY lesz benne, valamint egy PB nevű regiszter is:
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_OFF
CBLOCK 0X0C
WSAVE
IRANY
T1
T2
PB
ENDC
Most pedig jöhet a megszakítási rutin. Mint mondtuk, a megszakításnak itt a gomblenyomás lesz a szerepe.
ORG 0
GOTO START
ORG 4
BCF INTCON,RBIF
MOVWF WSAVE
MOVFW IRANY
BTFSS PORTB,6
MOVLW 00
BTFSS PORTB,7
MOVLW 01
MOVWF IRANY
MOVFW WSAVE
RETFIE
Bár ennyi példa után már illik ezt a megoldást átlátnunk, azért elemezzük ki egy kicsit! Az első két sorban csak kikerüljük a megszakítást (mert 4-től indul minden megszakítási rutin). Töröljük az RB megszakítást jelző RBIF jelzőbitet egy BCF utasítással. Figyeljünk oda, hogy ezúttal az RBIF-et töröljük, nem a T0IF-et! A Timeres példához hasonlóan elmentjük az akkumulátort WSAVE regiszterbe, majd jön a lényege a megszakításnak. A következő hat sorra (MOVFW IRANY-tól MOVWF IRANY-ig) egy kicsit máshogy nézünk rá, hogy könnyebben megértsük. Az elején ugye betöltjük IRANY jelenlegi értékét az akkumulátorba, négy soron keresztül csinálunk valamit (az a valami lehet „semmi” is!) az akkumulátorral, majd visszaírjuk az IRANY-ba. Na most ugye mi is az a négy sor? Ha az első gomb volt lenyomva (PORTB,6), akkor akkumulátort nullázzuk, ha a másodikat nyomtuk meg (PORTB,7), akkor az akkumulátorba 1-et teszünk. Ha egyiket sem nyomtuk meg, akkor békén hagyjuk az akkumulátort, így IRANY értéke változatlan marad. Most felmerülhet a kérdés, hogy ugyan hogyan lehetséges az, hogy egy gomb se legyen lenyomva ennél a tesztelésnél, amikor a megszakítás eleve gombnyomásra hívódik meg? A válasz az, hogy a gombok felengedésekor is meghívódik a megszakítás! Ne feledjük, hogy az RB megszakítás a PORTB felső négy bitjének változására hívódik meg, amibe beletartozik a 0-ból 1-be menet és az 1-ből 0-ba menet is. A rutin végén az elmentett akkumulátort visszatöltjük és kilépünk.
Jöhet a főprogram a kezdeti értékek beállításával:
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'11000000'
MOVWF TRISB
MOVLW B'10000111'
MOVWF OPTION_REG
BCF STATUS,RP0 ;BANK0
CLRF IRANY
MOVLW 01
MOVWF PORTB
BSF INTCON,GIE
BSF INTCON,RBIE
TRISA értéke marad a szokásos. TRISB-t azonban ezúttal máshogy állítjuk be, mert a PORTB felső két bitje bemenetként funkcionál.
Ennek megfelelően az alsó hat bitet outputra (0) kell állítani, a felső kettőt pedig inputra (1). Az OPTION_REG esetében az előosztó bitek (azaz az alsó három bit) nem jelent számunkra semmit, mert nem használunk Timer megszakítást. Az utolsó bit azonban már annál fontosabb. Ez az ún. felhúzó ellenállást kapcsolja ki vagy be. 1 esetén – amire éppen állítottuk is – kikapcsolja. A felhúzó ellenállás lényege, hogy ha egy láb bementként funkcionál, de fizikailag nincs rákötve semmi, akkor az általa visszaadott érték kiszámíthatatlan.
A felhúzó ellenállás lényege, hogy ha egy ellenálláson keresztül a +5V-ra kötjük ezt a lábat, akkor ilyen esetben megkapja a +5V-ot a láb, vagyis a kiszámíthatatlanság helyett biztosan egyet ad vissza. Rendszerint ezt érdemes mindig bekapcsolva tartani, mi azonban most azért kapcsoljuk ki, mert ezt az ellenállást már az áramkörünkben megvalósítottuk. (Lásd ábra.)
Megadjuk a kezdőértékeket, majd bekapcsoljuk a megszakításokat. Kezdjük el a főciklust, mely IRANY függvényében jobbra, ill. balra futtatja a fényt. Először csak a jobbra forgatást nézzük!
JOBBRA: BTFSS PORTB,0
GOTO FORGJ
MOVLW B'00100000'
MOVWF PORTB
CALL DELAY
GOTO VISSZA
FORGJ: MOVLW B'00111111'
ANDWF PORTB,W
MOVWF PB
CLRC
RRF PB,W
MOVWF PORTB
CALL DELAY
GOTO VISSZA
Egy kicsit bonyolult, ahhoz képest, hogy csak forgatunk. Az első két sor az IRANY regiszter alapján való elugrás, nincs mit tárgyalni rajta. Következőnek azonban azt vizsgáljuk, hogy PORTB első bitje 0-e? Hogy erre miért van szükség? Mert két LED-et kiiktattunk, ezáltal nem használhatjuk az egész PORTB-t kimenetre. Tehát az RRF forgatóutasítás nem teljesen jó nekünk, hiszen a 0. után a 7. bitbe forgatná a futófényt, ahová mi per pillanat a gombot kötöttük. Ezért kell egy kitétellel kezdenünk a jobbra mozgatást, hogy ha éppen a 0. bitről akarna jobbra fordulni egyet a fény, akkor a fényt át kell tennünk az 5. bitre. Egyébként pedig mehet a FORGJ címkétől a mozgatás rendesen. Illetve mintha az előző példában még nem lett volna ennyire megbonyolítva az RRF használata! Miért is kell most ennyire az egekig bonyolítani? Nos a válasz egyszerű: ha jobbra forgatjuk PORTB tartalmát, akkor a forgatóutasítás figyelembe fogja venni és arrébb mozgatni az egyébként bevitelre használt 6. és 7. bitek értékét is. Minthogy ez igencsak eltorzítaná a futófényt (további LED-ek is elkezdenének világítani), ki kell maszkolnunk a 6. és 7. biteket forgatás előtt. Ezt a PORTB-ben nem tehetjük meg (nem bírálhatunk felül beviteli bitet), ezért PORTB maszkolását ideiglenesen az általunk deklarált PB regiszterbe másoljuk, PB-t forgatjuk el PORTB helyett és az immár jól forgatott értéket kiírjuk PORTB-re.
Hasonlóképpen néz ki a balra forgató rutin is, de szerencsére itt nem áll fenn a jobbra forgatás problémája, mert itt elegendő csak a C flag-et törölnünk:
BALRA: BTFSS PORTB,5
GOTO FORGB
MOVLW 01
MOVWF PORTB
CALL DELAY
GOTO VISSZA
FORGB: CLRC
RLF PORTB,F
CALL DELAY
GOTO VISSZA
Itt a kitétel az 5. bitre szól, vagyis onnan már ne akarjon továbbmenni a fény balra. A program végére még egy DELAY rutint kell beékelnünk, és már futhat is. A kész kód letölthető innen.
Megadjuk a kezdőértékeket, majd bekapcsoljuk a megszakításokat. Kezdjük el a főciklust, mely IRANY függvényében jobbra, ill. balra futtatja a fényt. Először csak a jobbra forgatást nézzük!
JOBBRA: BTFSS PORTB,0
GOTO FORGJ
MOVLW B'00100000'
MOVWF PORTB
CALL DELAY
GOTO VISSZA
FORGJ: MOVLW B'00111111'
ANDWF PORTB,W
MOVWF PB
CLRC
RRF PB,W
MOVWF PORTB
CALL DELAY
GOTO VISSZA
Egy kicsit bonyolult, ahhoz képest, hogy csak forgatunk. Az első két sor az IRANY regiszter alapján való elugrás, nincs mit tárgyalni rajta. Következőnek azonban azt vizsgáljuk, hogy PORTB első bitje 0-e? Hogy erre miért van szükség? Mert két LED-et kiiktattunk, ezáltal nem használhatjuk az egész PORTB-t kimenetre. Tehát az RRF forgatóutasítás nem teljesen jó nekünk, hiszen a 0. után a 7. bitbe forgatná a futófényt, ahová mi per pillanat a gombot kötöttük. Ezért kell egy kitétellel kezdenünk a jobbra mozgatást, hogy ha éppen a 0. bitről akarna jobbra fordulni egyet a fény, akkor a fényt át kell tennünk az 5. bitre. Egyébként pedig mehet a FORGJ címkétől a mozgatás rendesen. Illetve mintha az előző példában még nem lett volna ennyire megbonyolítva az RRF használata! Miért is kell most ennyire az egekig bonyolítani? Nos a válasz egyszerű: ha jobbra forgatjuk PORTB tartalmát, akkor a forgatóutasítás figyelembe fogja venni és arrébb mozgatni az egyébként bevitelre használt 6. és 7. bitek értékét is. Minthogy ez igencsak eltorzítaná a futófényt (további LED-ek is elkezdenének világítani), ki kell maszkolnunk a 6. és 7. biteket forgatás előtt. Ezt a PORTB-ben nem tehetjük meg (nem bírálhatunk felül beviteli bitet), ezért PORTB maszkolását ideiglenesen az általunk deklarált PB regiszterbe másoljuk, PB-t forgatjuk el PORTB helyett és az immár jól forgatott értéket kiírjuk PORTB-re.
Hasonlóképpen néz ki a balra forgató rutin is, de szerencsére itt nem áll fenn a jobbra forgatás problémája, mert itt elegendő csak a C flag-et törölnünk:
BALRA: BTFSS PORTB,5
GOTO FORGB
MOVLW 01
MOVWF PORTB
CALL DELAY
GOTO VISSZA
FORGB: CLRC
RLF PORTB,F
CALL DELAY
GOTO VISSZA
Itt a kitétel az 5. bitre szól, vagyis onnan már ne akarjon továbbmenni a fény balra. A program végére még egy DELAY rutint kell beékelnünk, és már futhat is. A kész kód letölthető innen.
Több megszakítás használata egyszerreDurvítsuk el a dolgot, használjuk egyszerre az RB-t és a Timer-t! Gyakorlatilag csak össze kell ötvöznünk a Timer-es és RB-s példát.
A deklarációban immár minden benne lesz, kivéve a DELAY rutinhoz szükséges T1 és T2 regisztert:
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_OFF
CBLOCK 0X0C
IDOZIT
WSAVE
IRANY
PB
ENDC
T1-re és T2-re (no meg az egész DELAY-re) azért nincs szükség, mert mint azt a Timer-es példában is láthattuk, az időzítést a Timer megszakítás fogja végezni. Nézzük a program Timer megszakításti rutinját!
ORG 0
GOTO START
ORG 4
BTFSS INTCON,T0IF
GOTO RBINT
TMRINT: BCF INTCON,T0IF
DECFSZ IDOZIT
RETFIE
MOVWF WSAVE
MOVLW 9
MOVWF IDOZIT
BTFSS IRANY,0
GOTO BALRA
JOBBRA: BTFSS PORTB,0
GOTO FORGJ
MOVLW B'00100000'
MOVWF PORTB
GOTO TMRKI
FORGJ: MOVLW B'00111111'
ANDWF PORTB,W
MOVWF PB
CLRC
RRF PB,W
MOVWF PORTB
GOTO TMRKI
BALRA: BTFSS PORTB,5
GOTO FORGB
MOVLW 01
MOVWF PORTB
GOTO TMRKI
FORGB: CLRC
RLF PORTB,F
GOTO TMRKI
TMRKI: MOVFW WSAVE
RETFIE
Eléggé ismerős kód. Majdnem ugyanaz, mint az RB megszakításos példa léptetőrutinja, csak egy kicsit át lett alakítva, hogy megszakításban tudjon futni. Az eleje a programnak azonban érdekes. A megszakítás első sora rögtön az, hogy megvizsgáljuk T0IF jelzőbit értékét. Ne feledjük, hogy minden megszakítás a 4. címen kezdődik, tehát azzal kell kezdenünk, hogy kitaláljuk, milyen megszakítás történt egyáltalán. Amennyiben T0IF nulla – tehát nem Timer megszakítás történt – elugrunk az RB-t lekezelő megszakítási rutinra. A Timer megszakítási rutin keretében elkészített „fényléptető” rutinban a CALL DELAY sorokra immár nincs szükségünk, hisz a Timer csak bizonyos időközönként hívódik meg, így gyakorlatilag hardveresen elintéztük a késleltetés problémáját. A GOTO VISSZA sorok helyett pedig egy megszakításból kiléptető kódot hívunk meg (TMRKI), ahol W értékét visszatöltjük, majd a megszakításból kilépünk. Most már van egy Timer megszakítási rutinunk, ami IRANY regiszter értékétől függően jobbra, vagy balra lépteti a futófényt. Nézzük az RB megszakítási rutint!
RBINT: BCF INTCON,RBIF
MOVWF WSAVE
MOVFW IRANY
BTFSS PORTB,6
MOVLW 00
BTFSS PORTB,7
MOVLW 01
MOVWF IRANY
MOVFW WSAVE
RETFIE
Ez egy az egyben az előző példa RB megszakítási rutinja, nem kellett átírni rajta semmit sem. A megszakítási rutinok után kezdődhet a főprogram a kezdeti értékadásokkal.
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'11000000'
MOVWF TRISB
MOVLW B'10000111'
MOVWF OPTION_REG
BCF STATUS,RP0 ;BANK0
CLRF IRANY
MOVLW 01
MOVWF PORTB
MOVLW 09
MOVWF IDOZIT
BSF INTCON,GIE
BSF INTCON,T0IE
BSF INTCON,RBIE
TRISA és TRISB értékét az átalakított kapcsoláshoz beállítjuk (vagyis ugyanaz, mint az előző példa). OPTION_REG esetében most már szerepet kap mind az RBPU (felhúzó ellenállás), mind az előosztó bitek. Vagyis az ellenállás ki, az előosztás maximum. IRANY, PORTB, IDOZIT regiszterek értékadása után bekapcsoljuk a Timer és RB megszakításokat.
Most jöhet a főprogram. Na most ugye felmerül az a kérdés, hogy ugyan mit fog tartalmazni a főprogram, ha már az RB megszakítás kezeli a gombokat, a Timer pedig lépteti a futófényt. A válasz: semmit! Azért hogy ne fusson vakvágányra a program, egy végtelen ciklussal zárjuk le:
VISSZA: GOTO VISSZA
END
Vagyis a program mindig ebben a végtelen ciklusban fog futni, ebből csak a Timer és az RB fogja egyszer-egyszer kizökkenteni. A kész program letölthető innen. Érdemes megfigyelnünk, hogy PIC-ünk úgy kezeli le a futófényt, hogy közben nem használtuk fel a programtörzset. Vagyis egy teljesen különálló programot is beleírhatnánk még a PIC-be, mely párhuzamosan futna a futófény programunkkal!
A deklarációban immár minden benne lesz, kivéve a DELAY rutinhoz szükséges T1 és T2 regisztert:
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_OFF
CBLOCK 0X0C
IDOZIT
WSAVE
IRANY
PB
ENDC
T1-re és T2-re (no meg az egész DELAY-re) azért nincs szükség, mert mint azt a Timer-es példában is láthattuk, az időzítést a Timer megszakítás fogja végezni. Nézzük a program Timer megszakításti rutinját!
ORG 0
GOTO START
ORG 4
BTFSS INTCON,T0IF
GOTO RBINT
TMRINT: BCF INTCON,T0IF
DECFSZ IDOZIT
RETFIE
MOVWF WSAVE
MOVLW 9
MOVWF IDOZIT
BTFSS IRANY,0
GOTO BALRA
JOBBRA: BTFSS PORTB,0
GOTO FORGJ
MOVLW B'00100000'
MOVWF PORTB
GOTO TMRKI
FORGJ: MOVLW B'00111111'
ANDWF PORTB,W
MOVWF PB
CLRC
RRF PB,W
MOVWF PORTB
GOTO TMRKI
BALRA: BTFSS PORTB,5
GOTO FORGB
MOVLW 01
MOVWF PORTB
GOTO TMRKI
FORGB: CLRC
RLF PORTB,F
GOTO TMRKI
TMRKI: MOVFW WSAVE
RETFIE
Eléggé ismerős kód. Majdnem ugyanaz, mint az RB megszakításos példa léptetőrutinja, csak egy kicsit át lett alakítva, hogy megszakításban tudjon futni. Az eleje a programnak azonban érdekes. A megszakítás első sora rögtön az, hogy megvizsgáljuk T0IF jelzőbit értékét. Ne feledjük, hogy minden megszakítás a 4. címen kezdődik, tehát azzal kell kezdenünk, hogy kitaláljuk, milyen megszakítás történt egyáltalán. Amennyiben T0IF nulla – tehát nem Timer megszakítás történt – elugrunk az RB-t lekezelő megszakítási rutinra. A Timer megszakítási rutin keretében elkészített „fényléptető” rutinban a CALL DELAY sorokra immár nincs szükségünk, hisz a Timer csak bizonyos időközönként hívódik meg, így gyakorlatilag hardveresen elintéztük a késleltetés problémáját. A GOTO VISSZA sorok helyett pedig egy megszakításból kiléptető kódot hívunk meg (TMRKI), ahol W értékét visszatöltjük, majd a megszakításból kilépünk. Most már van egy Timer megszakítási rutinunk, ami IRANY regiszter értékétől függően jobbra, vagy balra lépteti a futófényt. Nézzük az RB megszakítási rutint!
RBINT: BCF INTCON,RBIF
MOVWF WSAVE
MOVFW IRANY
BTFSS PORTB,6
MOVLW 00
BTFSS PORTB,7
MOVLW 01
MOVWF IRANY
MOVFW WSAVE
RETFIE
Ez egy az egyben az előző példa RB megszakítási rutinja, nem kellett átírni rajta semmit sem. A megszakítási rutinok után kezdődhet a főprogram a kezdeti értékadásokkal.
START BSF STATUS,RP0 ;BANK1
MOVLW B'00011111'
MOVWF TRISA
MOVLW B'11000000'
MOVWF TRISB
MOVLW B'10000111'
MOVWF OPTION_REG
BCF STATUS,RP0 ;BANK0
CLRF IRANY
MOVLW 01
MOVWF PORTB
MOVLW 09
MOVWF IDOZIT
BSF INTCON,GIE
BSF INTCON,T0IE
BSF INTCON,RBIE
TRISA és TRISB értékét az átalakított kapcsoláshoz beállítjuk (vagyis ugyanaz, mint az előző példa). OPTION_REG esetében most már szerepet kap mind az RBPU (felhúzó ellenállás), mind az előosztó bitek. Vagyis az ellenállás ki, az előosztás maximum. IRANY, PORTB, IDOZIT regiszterek értékadása után bekapcsoljuk a Timer és RB megszakításokat.
Most jöhet a főprogram. Na most ugye felmerül az a kérdés, hogy ugyan mit fog tartalmazni a főprogram, ha már az RB megszakítás kezeli a gombokat, a Timer pedig lépteti a futófényt. A válasz: semmit! Azért hogy ne fusson vakvágányra a program, egy végtelen ciklussal zárjuk le:
VISSZA: GOTO VISSZA
END
Vagyis a program mindig ebben a végtelen ciklusban fog futni, ebből csak a Timer és az RB fogja egyszer-egyszer kizökkenteni. A kész program letölthető innen. Érdemes megfigyelnünk, hogy PIC-ünk úgy kezeli le a futófényt, hogy közben nem használtuk fel a programtörzset. Vagyis egy teljesen különálló programot is beleírhatnánk még a PIC-be, mely párhuzamosan futna a futófény programunkkal!
A Watchdog TimerBeszélnünk kell még a PIC egy speciális áramköréről, mely a sima Timerhez hasonlít valahol, de teljesen önálló életet él. Ez a Watchdog Timer, melynek semmi más feladata sincs, csak az, hogy indítsa újra a PIC-et, ha lefagyna a benne levő program. A Watchdog Timer (továbbiakban WDT) elég egyszerűen dolgozik: folyamatosan számlál, és ha a számlálója túlcsordul, akkor küld egy reset impulzust (ellentétben a hagyományos Timer-rel, ami ilyenkor a 4. címre ugrik és beállítja a T0IF-et). A PIC-be égetett programnak pedig oda kell figyelnie arra, hogy a WDT számlálója ne csordulhasson túl. Bár a WDT egy teljesen különálló áramkör (a PIC-en belül persze), egyetlen vezérlést azért tudunk küldeni neki: nullázza le a számlálóját, ezzel biztosítva, hogy nem csordul túl. Természetesen a programnak bizonyos időközönként mindig újra és újra ki kell nulláznia a megállíthatatlanul számolni akaró WDT-t. Ugyebár ha lefagy a program, akkor nem tudja lenullázni a számlálót, így a WDT kireseteli. A WDT-t, hasonlóan a sima Timerhez, elláthatjuk előosztással, így sokkal később csordul túl a számlálója. (Ez lehet akár 2 másodperc is.) Az előosztást ugyanúgy az OPTION_REG-ben végezhetjük el, azonban sajnos egyszerre csak a Timer, vagy csak a WDT használhatja az előosztót. A WDT-t program közben ki-/bekapcsolni nem tudjuk, még a beégetés előtt kell eldöntenünk, hogy a WDT legyen-e aktív a PIC-ben, vagy sem. Eddigi példáink során a WDT végig ki volt kapcsolva. Bekapcsolásához a program fejlécében kell átírnunk a direktívákat:
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_ON
Megjegyzés: a „_CONFIG…” sor mindössze csak az égetőszoftvernek jelez, hogy az égetés végén kapcsolja ki vagy be a WDT-t, védje le a kódot (_CP_xxx), illetve állítsa be a rezgőkör típusát (_XT_OSC). Bármilyen égetőszoftvert használunk, lehetőségünk van rá, hogy a tényleges beégetés előtt ezeket a beállítást felülbíráljuk.
Vegyük elő az RB megszakítás illusztrálásához készült példaprogramot és javítsuk át WDT-sre! (Azért érdemes ezzel játszadozni, mert ez már az átalakított áramkörhöz készült, és nem használ Timer megszakítást, tehát lehet használni az előosztót.) A már fent is említett _WDT_ON-nal kapcsoljuk be a WDT-t, valamint az OPTION_REG-be B'10000111' helyett B'10001111'-t írjunk! (Ezzel az előosztás a WDT-re lesz érvényes.) Az átalakított programot fordítsuk le és égessük bele a PIC-be! (Figyeljük meg, hogy az égetőprogram jelzi a WDT használatát!) A program elsőre ugyanúgy fog működni, mint eddig. Azonban kb. 2 másodpercenként újraindul, mert a WDT túlcsordul. Most rakjunk bele a programba a WDT-t kinullázó utasítást! A látványosság kedvéért most 2 másodperces tűrést használunk, de valójában ez lehet akár 20 ms is, ha az előosztót úgy állítjuk be. Ezért a WDT törlését végző utasítást érdemes olyan helyre rakni (akár több helyre is), ami nagyon gyakran lefut a programban. A mi programunkban leggyakrabban a DELAY ciklusa fut, ezért az egyik NOP utasítást cseréljük fel a törlésért felelős CLRWDT-vel!
DELAY: MOVLW d'150'
MOVWF T1
DEL: MOVLW d'255'
MOVWF T2
DEL1: CLRWDT
NOP
NOP
NOP
NOP
NOP
NOP
NOP
DECFSZ T2,f
GOTO DEL1
DECFSZ T1,f
GOTO DEL
RETURN
END
Immár a program nem indul újra 2 másodpercenként. Sőt, még akkor sem, ha a WDT előosztását a legkisebbre vesszük, mert olyan gyakran fut rá a program a CLRWDT-re! Ezzel elértük, hogy fagyás esetén a program azonnal újrainduljon. A gyakorlatban nem a programozási hibák okozzák a fagyásokat, sokkal inkább külső körülmények: áramingadozás, extrém hőmérséklet, stb.
A következő számban megismerkedünk a PIC belső háttértárolójával, az EEPROM memóriával.
LIST P=16F84
#INCLUDE "P16F84.INC"
__CONFIG _XT_OSC&_CP_OFF&_WDT_ON
Megjegyzés: a „_CONFIG…” sor mindössze csak az égetőszoftvernek jelez, hogy az égetés végén kapcsolja ki vagy be a WDT-t, védje le a kódot (_CP_xxx), illetve állítsa be a rezgőkör típusát (_XT_OSC). Bármilyen égetőszoftvert használunk, lehetőségünk van rá, hogy a tényleges beégetés előtt ezeket a beállítást felülbíráljuk.
Vegyük elő az RB megszakítás illusztrálásához készült példaprogramot és javítsuk át WDT-sre! (Azért érdemes ezzel játszadozni, mert ez már az átalakított áramkörhöz készült, és nem használ Timer megszakítást, tehát lehet használni az előosztót.) A már fent is említett _WDT_ON-nal kapcsoljuk be a WDT-t, valamint az OPTION_REG-be B'10000111' helyett B'10001111'-t írjunk! (Ezzel az előosztás a WDT-re lesz érvényes.) Az átalakított programot fordítsuk le és égessük bele a PIC-be! (Figyeljük meg, hogy az égetőprogram jelzi a WDT használatát!) A program elsőre ugyanúgy fog működni, mint eddig. Azonban kb. 2 másodpercenként újraindul, mert a WDT túlcsordul. Most rakjunk bele a programba a WDT-t kinullázó utasítást! A látványosság kedvéért most 2 másodperces tűrést használunk, de valójában ez lehet akár 20 ms is, ha az előosztót úgy állítjuk be. Ezért a WDT törlését végző utasítást érdemes olyan helyre rakni (akár több helyre is), ami nagyon gyakran lefut a programban. A mi programunkban leggyakrabban a DELAY ciklusa fut, ezért az egyik NOP utasítást cseréljük fel a törlésért felelős CLRWDT-vel!
DELAY: MOVLW d'150'
MOVWF T1
DEL: MOVLW d'255'
MOVWF T2
DEL1: CLRWDT
NOP
NOP
NOP
NOP
NOP
NOP
NOP
DECFSZ T2,f
GOTO DEL1
DECFSZ T1,f
GOTO DEL
RETURN
END
Immár a program nem indul újra 2 másodpercenként. Sőt, még akkor sem, ha a WDT előosztását a legkisebbre vesszük, mert olyan gyakran fut rá a program a CLRWDT-re! Ezzel elértük, hogy fagyás esetén a program azonnal újrainduljon. A gyakorlatban nem a programozási hibák okozzák a fagyásokat, sokkal inkább külső körülmények: áramingadozás, extrém hőmérséklet, stb.
A következő számban megismerkedünk a PIC belső háttértárolójával, az EEPROM memóriával.