Mivel mikrovezérlőről van szó, ezért kell egy program ami fut benne. Előre megjegyzem, hogy erre a PIC-re lehetett volna sokkal jobb programot is írni, de ez is nagyon jól működik. Nem spóroltam a hellyel, mivel van bőven a PIC-ben belőle.
A program Assembly nyelven íródott. A program 12F508-ra írt verzióját Skory készítette. A program átírása közben belekerült egy újabb funkció, a véletlenszerűen változó dobáshossz, továbbá a véletlenszámot nem a Timer modul segítségével generálja a program. A PIC programozást mindenképpen érdemes ezen a nyelven elkezdeni, mivel így lehet megérteni a legjobban a PIC-ek felépítését.
A program a következőket tudja:
- A dobott értéket a dobókocka pöttyeinek megfelelően rajzolja ki
- Körülbelül másfél perc tétlenség után automatikusan kikapcsol
- Két gombnyomással is (dupla kattintás
) kikapcsolható
- A dobókocka pergési (gurulási) ideje véletlenszerű
- A pergés közben változó hangmagasságú hangot hallat
- Áram alá helyezéskor felvillantja az összes LED-et és 6-ot csippan a hangszóró
A program bemutatása előtt nem árt néhány alapfogalmat tisztázni:
Bit: egyetlen logikai értéket takar. Értéke csak 0 vagy 1 lehet.
Byte: ez egy szám típus. Egy bájt 256 értéket hete fel. Összesen 8 bitből áll. Értéke 0-255-ig terjedhet. 256=(28)
Regiszter: egy bájtot tárolni képes része a memóriának
A PIC ún. RISC processzor, tehát a fejlesztésénél arra törekedtek, hogy minél kevesebb parancsot kelljen használni a programozásnál. Ez nagyban megkönnyíti az Assembly nyelv tanulását, viszont a processzor alapból csak egyszerű feladatokat tud elvégezni. Ez azt takarja, hogy a processzornak nincs olyan utasítása, amellyel például osztani lehetne, ezt szoftveresen kell megoldani.
A következőkben kitárgyaljuk a program működését:
A programnak csak a gondolatmenet megértéséhez szükséges részeit mutatom be, a felépítés szempontjából lényegtelen részeket kihagyom.
Konfigurációs rész:
list P=PIC12F629
include "P12F629.INC"
__CONFIG _CP_OFF & _BODEN_OFF & _MCLRE_ON & _PWRTE_ON & _INTRC_OSC_NOCLKOUT
Itt adhatjuk meg a fordítónak, többek között, hogy milyen típusú PIC-el szeretnénk dolgozni:
A többi jelölés:
_CP_OFF : Kód védelem kikapcsolva. (ha ezt bekapcsoljuk és beégetjük a PIC-be a programot nem tudjuk majd kiolvasni belőle)
_BODEN_OFF: Brown Out Reset kikapcsolva. (ha be van kapcsolva és a tápfeszültség a megadott mérték alá csökken, akkor reseteli a processzort, alacsony feszültségű táplálásnál nem ajánlott)
_MCLRE_ON: az MCLR láb resetel
_WDT_OFF : Watch Dog Timer kikapcsolva (ez egy beépített időzítő a PIC-be, ha bekapcsoljuk és valami értelmetlen jel miatt lefagy a processzor akkor egy kis idő múlva újraindítja)
_INTRC_OSC_NOCLKOUT : A PIC belső oszcillátorát használjuk. Init=belső
#define egyes b'00010000' ; középső 1-es pont - GP4-es láb
#define kettes b'00000001' ; egyik kettes átló - GP0-ás láb
#define kettesb b'00000100' ; másik kettes átló - GP2-es láb
#define kettesc b'00000010' ; középső kettes vonal - GP1-es láb
#define harmas (egyes | kettes)
#define harmasb (egyes | kettesb)
#define negyes (kettes | kettesb)
#define otos (egyes | kettes | kettesb)
#define hatos (kettes | kettesb | kettesc)
#define hetes (kettes | kettesb | kettesc | egyes)
#define hangsz b'00100000' ; hangszóró a GP5-re kötve
Itt nevezzük el a program számára a különböző LED csoportokat. Látható, hogy itt vannak megadva az egyes csoportok kombinációi is. Például: ötös, hatos.
ck equ 20h
delay1 equ 21h
delay2 equ 22h
delay3 equ 23h
szam_0 equ 24h
kikapcsolo equ 25h
on_count equ 26h
periodusido equ 27h
timer equ 28h
random equ 29h
random2 equ 30h
szam_guritas equ 31h
guritas_random equ 32h
Itt defináljuk a változókat egy-egy memóriaterületre. A szabad memóriaterületek elhelyezkedése megtekinthető a PIC adatlapjában a 2.2.2-es oldalon, vagy ITT. Látható, hogy a szabad memóriaterület 20h-nál kezdődik és 60h-nál ér véget. Mi a programban 20h-tól kezdjük a számozást.
PROG CODE 0
goto start
start:
bsf STATUS, RP0
call 3ffh
movwf OSCCAL
movlw b'11001000'
movwf TRISIO
movlw b'00000000'
movwf OPTION_REG
movlw b'00001000'
movwf IOC
bcf STATUS, RP0 ;
clrf GPIO
movlw b'00000111'
movwf CMCON
movlw b'00001000' ;
movwf INTCON ;
movlw b'11001000'
movwf OPTION_REG
Ebben a részben beállítgatjuk, hogy melyik láb legyen ki illetve bemenet illetve még pár dolgot.
Erre most nem térnék ki bővebben.
btfsc STATUS,3 ; 0 a harmadik bit?
goto poweronreset ; ha bekapcsolás volt , akkor a reset rutinhoz ugrik
btfsc kikapcsolo,0 ; 0 a kikapcsoló alsó bit?
goto kikapcs ; ha bit 1-es, akkor kikapcsol
call delay100ms ; késleltetés a nyomógomb pergésének kiküszöbölésére
bsf kikapcsolo,0 ; ha a következő pár sorban jel érkezik a nyomógombtól, akkor kikapcsolás
Ez rész ellenőrzi, hogy az áramkör hogyan indult el. Tehát áram alá lett helyezve vagy resetelve lett az MCLR lábon.
RLF random,W ;
RLF random,W ;
btfsc random,4 ;
xorlw 1 ;
btfsc random,5 ; Véletlen számot generálunk a következő véletlen szám generátor kezdőértékéhez
xorlw 1 ;
btfsc random,3 ;
xorlw 1 ;
movwf random ;
RLF random,W ;
RLF random,W ;
btfsc random,4 ;
xorlw 1 ;
btfsc random,5 ; Véletlen számot generálunk most már ténylegesen a játék számára
xorlw 1 ;
btfsc random,3 ;
xorlw 1 ;
movwf random ;
call villogtat_1
; LED-ek villogtatása, csak a játék kredvéért
+ késleltetés + hang
clrf kikapcsolo ; innentől a gombnyomás nem vált ki kikapcsolást
call villogtat_1
; LED-ek villogtatása, csak a játék kredvéért
+ késleltetés + hang
movfw random ; előkészítés a véletlen szám osztásához
call osztas ; osztási maradék a szam_0 változóban
movfw szam_0 ; w-ban (0...11)
call szamok ; konvertálás (egyes-hatos)
movwf GPIO ; a tárolt érték kijelzése, vagyis a LED-ek állapotának beállítása
Számunkra most ez a rész a legfontosabb. Először is véletlen számot generálunk a dobott érték számára. Hogy ne ismétlődjenek a számsorok feltűnően, ezért a véletlen szám generátor kezdőértékének is generálunk egy véletlen számot. Másodszor pedig meghívja a LED-ek villogtatásához szükséges szubrutint, majd ha az első villogás lefutott törli a kikapcsolo regisztert, ezzel megakadályozva, hogy a következő gombnyomásra kikapcsoljon. Tehát, ha a villogás első felében még egyszer megnyomjuk a gombot, a dobókocka kikapcsol. Majd, ha töröltük, még egyszer meghívjuk a villogtató szubrutint.
Ha ez mind lefutott betesszük a munkaregiszterbe a random (véletlen szám) regiszter értékét. A következő sorban pedig meghívjuk az osztás szubrutint. Erre azért van szükség, mert a véletlen szám értéke 0-255-ig terjedhet és elég problémás lenne kijelezni ilyen nagy számokat 7 LED-el.
Az osztás rutin 12-vel fog osztani. A végén nem a hányadost fogjuk kiíratni, hanem a maradékot. 12-való osztásnál ez 0-11-ig terjedhet.
Ha leosztottuk a számot, akkor meghívjuk a "szamok" nevű szubrutint, ami az osztott értéket átkonvertálja a LED-ek szerinti formára. Végül a konvertált értéket kiírjuk a kimenetre (vagyis a GPIO portra)
movlw d'6' ; várakozás
movwf ck ; ennyiszer fut le a ciklus
call delay20s ; késleltetés
decfsz ck,1 ; ck csökkentlése 0-ig
goto $-2 ; visszaugrás 2 sort, ha még nem nulla
goto kikapcs ; elég hosszú idő telt el, kikapcsolás
Itt valósítjuk meg az időzített kikapcsolást, ha lefutott az időzítő ciklus, odaugrik a kikapcs programrészhez és SLEEP üzemmódba kerül a processzor. Ilyenkor a PIC adatlapja szerint 1nA (nano!) a fogyasztása. Ez nagyon kicsi! Ezért nem került a kapcsolásba áramtalanító kapcsoló. 2 darab AAA elemről kis fogyasztású LED-ekkel így is évekig elműködhet.
Most következzen a szubrutinok bemutatása:
villogtat_1:
RLF guritas_random,W ;
RLF guritas_random,W ;
btfsc guritas_random,4 ;
xorlw 1 ;
btfsc guritas_random,5 ; Véletlen számot generálunk a következő véletlen szám generátor kezdőértékéhez
xorlw 1 ;
btfsc guritas_random,3 ;
xorlw 1 ;
movwf guritas_random ;
RLF guritas_random,W ;
RLF guritas_random,W ;
btfsc guritas_random,4 ;
xorlw 1 ;
btfsc guritas_random,5 ; Véletlenszámot generálunk a gurítás hosszának megállapítására
xorlw 1 ;
btfsc guritas_random,3 ;
xorlw 1 ;
movwf guritas_random ;
movfw guritas_random ; guritas_random érték a work-be
call osztas_guritas ; osztjuk a véletlenszámot
movlw d'6'
btfsc szam_guritas, 1 ; a ha a szam_guritas első bitjének értéke 0, akkor marad a 6 a workben
movfw szam_guritas ; ha nem nulla, akkor a workbe mozgatjuk
btfsc szam_guritas, 2 ; a ha a szam_guritas második bitjének értéke 0, akkor marad a 6 a workben
movfw szam_guritas ; ha nem nulla, akkor a workbe mozgatjuk
btfsc szam_guritas, 3 ; a ha a szam_guritas harmadik bitjének értéke 0, akkor marad a 6 a workben
; és így tovább a 8. bit-ig....
jmp1:
; véletlenszám generálás (ez csak a villogás miatt kell)
call sorsol ; szám: 0...11 a szam_0 változóban
movfw szam_0 ; a dobott érték kijelzése
call szamok ; konvertálás a LED-ek portra kötése szerinti fotmára
movwf GPIO ; LED-ek bekapcsolása
;call delay100ms ; várakozás - ez volt korábban
call beep_1 ;most hangot is adunk ki
decfsz ck,1 ;ismétlés amíg a ciklusváltozó nullára csökken
goto jmp1 ; vissza a ciklus elejére
retlw 0 ; visszatérés a függvényből
Ez a szubrutin felelős a LED-ek villogtatásáért és a közben hallható zajért ami a hangszóróból jön. Mint látható a kód elején lefuttatjuk ugyan azt a véletlenszám generátort, amit a program elején. Ezzel a gurítás hosszát állapítjuk meg. Ha lefutott a véletlenszám generálás, meghívjuk az ide tartozó osztás rutint és osztjuk 4-gyel. Így maximum 3-szor fogja lefuttatni az egész ciklust. De van egy kis gond! Mi van, ha nincs maradék az osztásnál? Akkor a szam_guritas regiszter értéke nulla lesz és a program nagyon hosszú ciklusba kezd! Ezt kiküszöbölhetjuk egy nagyon leleményes módszerrel. Mielőtt bármit csinálnánk a munkaregiszterbe helyezzük mondjuk a 6-ot. (movlw d'6'). Ha ez kész elkezdjük a szam_guritas regisztert elkezdjük vizsgálni bitről-bitre. Ha az éppen vizsgálat alatt lévő bit értéke 0, akkor kihagyja a program a következő sort, ami a munkaregiszterben törölné a 6-ot és az osztott véletlenszámot írná a helyébe. De, ha valamelyik bit értéke 1, akkor nem ugorja át a következő sort és a munkaregiszterben a 6-ot felülírja az osztott véletlenszámmal. Ezzel kiküszöbölhető, hogy a szuibrutin számláló ciklusába 0 kerüljön, mivel ha a regiszter minden bitjének az értéke 0, akkor soha nem fogja átírni a munkaregiszter értékét, így marad benne a 6, tehát 6-ot gurul a kocka. Ha viszont a szam_guritas regiszter tartalmaz akár egy darab 1-est is, akkor az kerül bele a munkaregiszterbe, így a véletlenszámnak megfelelő hosszú időt "gurul" a kocka.
beep_1:
; hang előállítási rutin
movlw d'200' ; 200 ciklusos lesz
movwf delay1
bp1:
movlw d'80' ; ettől függ a periódusidő
addwf szam_0,w ; és persze a számtól is, hogy többféle hangmagasság legyen
movwf periodusido ; eltesszük a változóba az aktuális értéket
swapf delay1,w ; kiszámolunk egy másik lehetséges frekvenciát is
andlw d'15' ; ez nem a számtól, hanem külső ciklustól függ
iorlw d'80'
;btfsc on:count,0
btfsc timer,6 ; ez dönti el, hogy a két kiszámolt frekvencia közül melyik szóljon éppen
decfsz periodusido,1 ; periódusidő várakozó ciklusa
goto $-1
movlw hangsz ; a hanszóró bitje a GPIO porton
xorwf GPIO,1 ; hangszóró bit invertálása
decfsz delay1,1 ; 0-ig fogunk számolni
goto bp1 ; vissza a ciklus elejére
bcf GPIO,hangszb
; a végén a hangszóró kikapcsp (biztos, ami biztos alapon
)
retlw 0 ; visszatérés a függvényből
Ez a szubrutin a hang előállításért felelős. A "gurítás" közben ez adja a változó magasságú hangot. A hang magassága attól függ, hogy a PIC-be épített időzítőből milyen értéket olvas ki. A hangadást pedig nagyon egyszerűen csinálj, gyorsan kapcsolgatja ki-be azt a lábat, amire a hangszóró van kötve.
poweronreset:
; ide ugrik, ha a program resettel kezdett (pl.: elemcsre)
clrf on_count ;bekapcsolásszámláló nullázás
movlw hetes
; összes LED bekapcsolás (hetest dobtam!
)
movwf GPIO ; bekapcsoljuk a LED-eket
movlw d'6' ; várakozás
movwf ck ; enyiszer fut le a ciklus
call beep_1
call delay100ms ; 100ms késleltetés
decfsz ck,1 ; ck csökkentlése 0-ig
goto $-3
Ha azzal indul el a program, hogy áramot adunk az áramkörre, nem a gombot nyomjuk meg, akkor felvillantja az összes LED-et és 6-ot csippan a hangszóró.
kikapcs:
call delay100ms ; késleltestés a gombpergés és hasonlók miatt
call beep_1 ; hanszóró csippantás + még késleltetés
incf on_count,1 ;bekapcsolás számolás (valójában kikapcsolást számolunk, de mostmár mindegy)
clrf kikapcsolo ; kikapcsoló nullázása, hiszen mindjárt kikapcsolunk
clrf GPIO ; kimenetetk nullázása, LED-ek és a hangszóró kikapcsolása
sleep ; alvó üzemmód, kikapcsolás a következő reset-ig
Ha letelik az automatikus kikapcsolás ideje, vagy kétszer egymás után gyorsan megnyomjuk a nyomógombot, akkor ez a ciklus hívódik meg. Ez semmi mást nem csinál, csak késleltet egy kicsit, csippant egyet a hangszórón, majd nullázza az összes kimenetet (kikapcsol mindent) és SLEEP módba teszi a processzort.
sorsol:
; véletlenszám generálás
movfw TMR0 ; a beépített TIMER értékét olvassuk véletlenszámnak
A változó magasságú hang generálásához olvassa a belső időzítő értékét.
osztas:
movwf szam_0
movlw d'12' ;12-vel fogunk osztani
bcf STATUS,
C ; C-biz nullázása, megint csak biztos, ami biztos alapon 
subwf szam_0,w ; levonjuk az osztót
BTFSC STATUS,C ; addig amíg sz osztandó el nem fogy
goto osztas ; vissza a ciklus elejére, ha az osztandó még nem 0
; az osztási maradék a szam_0 változóba került
retlw 0
Ezzel a szubrutinnal végezzük el a program elején az osztást. Tulajdonképpen a véletlenszámból, addig vonogatja ki az osztót, amíg a végeredmény nulla nem lesz. Az osztési maradék a szam_0 változóban marad, azt fogjuk kiírni.
osztas_guritas:
movwf szam_guritas
movlw d'4' ; 4-gyel fogunk osztani
bcf STATUS,
C ; C-biz nullázása, megint csak biztos, ami biztos alapon 
subwf szam_guritas,w ; levonjuk az osztót
BTFSC STATUS,C ; addig amíg sz osztandó el nem fogy
goto osztas_guritas ; vissza a ciklus elejére, ha az osztandó még nem 0
; az osztási maradék a szam_guritas változóba került
retlw 0
Ez az osztás rutin pedig a girítás hosszának megállapításához van. Ez ugyan azt csinálja, mint az előző, csak 4-gyel oszt és a maradék a szam_guritas regiszterbe kerül.
szamok:
; itt valósítjuk a meg a szám- LED-ek állapota konverziót
addwf PCL,f
retlw egyes
retlw kettes
retlw harmas
retlw negyes
retlw otos
retlw hatos
retlw egyes
retlw kettesb
retlw harmasb
retlw negyes
retlw otos
retlw hatos
Itt konvertáljuk az osztott véletlenszámot a LED-ek kimenetre kötésének szerintire.
delay20s:
; ez az egyik késleltető ciklus, semmi mást nem csinál, csak futnak a ciklusok és közbentelik az idő
clrf delay3
goto d2
delay100ms:
; 100ms késleltetés
movlw 1
movwf delay3
d2:
movlw d'70'
movwf delay2
d1:
clrf delay1
decfsz delay1,f
goto $-1
decfsz delay2,f
goto d1
decfsz delay3,f
goto d2
retlw 0
Időzítő ciklusok. Semmi mást nem csinál, csak futnak a ciklusok és telik vele az idő.
Vége a programnak!
A programot az MLAB nevű fejlesztőkörnyezettel fordítottam le, ami letölthető a Microchip honlapjáról: ITT
A lefordított .HEX fájlt letöltheted: ITT (jobb klikk: mentés másként) Ezt kell beégetni a PIC-be.
A cikk még nem ért véget, lapozz!