Prekinitve in časovnik (Timer) v mikrokontrolerju

Prekinitve so pomembna lastnost mikrokontrolerja. Ob prekinitvi mikrokontroler preneha izvajati glavni program in skoči v prekinitveno rutino, imenovano interrupt. Ko se ta zaključi, mikrokontroler nadaljuje izvajanje glavnega programa tam, kjer ga je pred prekinitvijo končal.

Prekinitvena rutina se vedno nahaja v programskem pomnilniku na naslovu 0x004. Pisanje na naslov 0x004 dosežemo z direktivo org. Prekinitvena rutina se mora končati z instrukcijo retfie (return from interrupt). Da se prekinitvena rutina ali prekinitveni program ne začne izvajati tudi takrat, ko v mikrokontrolerju ni prekinitve, se mora na naslovu 0x000 nahajati instrukcija goto, ki pošlje mikrokontroler v glavni program mimo prekinitvene rutine.

Kako pa lahko sprožimo prekinitev?

Mikrokontroler PIC16f628a pozna 10 različnih načinov proženja prekinitev. V tem poglavju bomo spoznali nekatere načine, druge pa bomo po potrebi obravnavali pri posameznih programih.

Slika 1: Prikaz delovanja prekinitev

Na delovanje prekinitev vpliva register INTCON, ki se nahaja v vseh štirih bankah podatkovnega pomnilnika RAM.

Tabela 1: Register INTCON (RAM naslov: 0Bh, 8Bh, 10Bh, 18Bh)

Odzivanje mikrokontrolerja na prekinitve omogočimo tako, da sedmi bit (GIE) tega registra postavimo na vrednost 1.

Omogočimo ali onemogočimo lahko vsak vir prekinitve posebej, tako da postavimo ustrezni bit registra INTCON na vrednost 1 oziroma na vrednost 0. Če želimo vsak vir prekinitve v prekinitveni rutini obravnavati posebej, moramo vedeti, kaj je vir posamezne prekinitve oziroma zakaj je v določenem trenutku nastala prekinitev. To odčitamo iz vrednosti zastavic T0IF, INTF in RBIF. Zastavica T0IF (tretji bit v registru INTCON) se postavi na 1, če je nastala prekinitev na časovniku TMR0, zastavica INTF (drugi bit v registru INTCON) se postavi na 1, če je prekinitev nastala na pinu RB0, zastavica RBIF (prvi bit v registru INTCON) pa se postavi na 1, če se je prekinitev pojavila zaradi spremembe signala na enem izmed pinov od RB4 do RB7.

Zastavica EEIF za ugotavljanje prekinitve po koncu vpisa v pomnilnik EEPROM se nahaja v SFR-registru PIR1 (osmi bit tega registra), zastavica za ugotavljanje prekinitev ob spremembi komparatorjevega izhoda pa predstavlja šesti bit (CMIF) registra PIR1. V registru PIR1 so tudi zastavice za nekatere druge vire prekinitev.

Ob prekinitvi moramo torej v prekinitveni rutini najprej pogledati, kje je izvor prekinitve. Ko to ugotovimo, skočimo na tisti del prekinitvene rutine, ki je določen temu izvoru. Preden pa se iz prekinitvene rutine vrnemo v glavni program z instrukcijo retfie, moramo zastavico, ki nam je sporočila vir prekinitve, postaviti na vrednost 0.

Postopek v prekinitveni rutini:

1. Izklopimo bit GIE, da se med izvajanjem prekinitvene rutine ne pojavi nova prekinitev.

2. Vrednosti registrov Work in STATUS shranimo posebej v pomnilnik RAM, ker se lahko v prekinitveni rutini spremenita.

3. Nastavimo ustrezno banko, da imamo dostop do registrov z zastavicami.

4. Ugotovimo vir prekinitve s testiranjem bitov (zastavic).

5. Napišemo ustrezen del prekinitvene rutine za posamezni vir prekinitve.

6. Ustrezni bit (zastavico), ki nam je sporočil vir prekinitve, postavimo na vrednost 0.

7. Povrnemo vrednost registrov Work in STATUS.

8. Izvedemo instrukcijo retfie, ki nas vrne v glavni program. Instrukcija retfie avtomatsko vklopi in postavi na vrednost 1 bit GIE in s tem ponovno omogoči odzivanje mikrokontrolerja na prekinitve.

Primer proženja prekinitve mikrokontrolerja s spremembo signala na pinu RB0 (assembler). Po vsakem pritisku na tipko T (sprememba signala iz 0 na 1), priključeno na pin RB0, se naj sproži prekinitev. V prekinitveni rutini se naj preklopi LED dioda, ki je priključena na pin RA1. Če se po prvem prehodu prekinitvene rutine vklopi, se naj v naslednjem izklopi itd.

 ;-------------------------------------------------------------------------------------

 ;Prekinitve na RB0.

 ;Okolje MPLAB IDE v8.92, prevajalnik MPASM Assembler V5.51, oscilator 4 MHz.

 ;Avtor: Milan Ivič, okt 2017

 ;-------------------------------------------------------------------------------------

     list p=16f628a  ;Tip mikrokontrolerja.

    #include <p16f628a.inc>  ;Vključi v program datoteko p16f628a.inc,    

    __CONFIG 0x2129

 

    Delovni equ 0x20                   ;V to spremenljivko bomo začasno shranili vrednost registra Work.

    Status equ 0x30                     ;V to spremenljivko bomo začasno shranili vrednost registra STATUS.

   

    org 0x000                           ;Reset vektor.

    goto Glavni  ;Nadaljuj izvajanje programa na naslovu Glavni.

    org 0x004                           ;Prekinitveni vektor.

;******************************** Prekinitvena rutina ******************************************

    bcf INTCON,GIE                    ;Onemogočimo odzivanje mikrokontrolerja na prekinitve.

    movwf Delovni                       ;Shranimo vrednost registra Work v Delovni.

    swapf STATUS,w                    ;Zamenjamo polbajta v registru STATUS, rezultat shranimo v Work.

    movwf Status                        ;Shranimo vrednost registra STATUS (ki je trenutno v Work) v Status.

    bcf STATUS,5                        ;Prehod v banko 0.

    movlw b'0000010'                   ;Maska za preklapljanje svetleče diode na pinu RA1.

    xorwf PORTA,f                       ;Operacija XOR med masko in PORTA.

    bcf INTCON,INTF                   ;Zastavico INTF postavimo na 0.

    swapf Status,w

    movwf STATUS                       ;Vrnemo vrednost registra STATUS.

    swapf Delovni,f

    swapf Delovni,w                     ;Vrnemo vrednost registra Work.

    retfie                              ;Izhod iz prekinitvene rutine.

Glavni

    bcf STATUS,5                       ;Banka 0

    movlw .7

    movwf CMCON                      ;Omogočimo vhodno-izhodne pine na PORTA.

    bsf STATUS,5                       ;Banka 1

    bcf TRISA,1                         ;Pin RA1 je izhodni pin.

    bsf TRISB,0                         ;RB0 je vhod.

    bsf OPTION_REG,6               ;Bit 6 (INTEDG) postavimo na 1, ker se naj sproži prekinitev pri spremembi signala na RB0 iz 0 na 1.

    bcf STATUS,5                        ;Banka 0.

    bcf PORTA,1                         ;Inicializacija pina RA1.

    movlw b'10010000'

    movwf INTCON                       ;Omogočene so prekinitve na RB0.

    bcf PORTB,0                         ;Inicializacija pina RB0.

Zanka

    nop

    nop

    goto Zanka                          ;Program je v zanki, pri spremembi na RB0 se bo sprožila prekinitev.

  end

Prikaz proženja prekinitev mikrokontrolerja PIC16f628a z vsakokratno spremembo signala (iz 0 na 1) na pinu RB0:

Preden zapustimo prekinitveno rutino, moramo zbrisati zastavico, saj tega mikrokontroler ne naredi sam. V nasprotnem primeru bo mikrokontroler neprenehoma klical prekinitveno rutino.

Nova instrukcija v programu je swapf, ki zamenja polbajte v registru, zapisanem v prvem parametru, in jih shrani na mesto, ki ga določimo z drugim parametrom. Če je drugi parameter f, to pomeni, da se bo rezultat shranil v isti register.

Slika 2: Prikaz delovanja instrukcije swapf

Polbajt (nibble) je računalniški pojem za skupino 4 bitov. En bajt ima torej dva polbajta, zgornjega in spodnjega. V programu smo instrukcijo swapf uporabili za shranitev registrov STATUS in Work, ker ne spreminja nobenih zastavic. Register STATUS vsebuje tri zastavice, ki se postavijo glede na rezultat zadnje operacije. Da se zastavice ne spremenijo, register STATUS shranjujemo z instrukcijo swapf, saj ta nikoli ne spreminja nobene zastavice. Tudi delovni register Work shranjujemo zato, da se njegova vrednost v prekinitveni rutini ne spremeni. Če se namreč prekinitev zgodi ravno takrat, ko želimo v glavnem programu uporabiti vrednost delovnega registra in v prekinitveni rutini njegovo vrednost spremenimo, se bo po vrnitvi v glavni program v njem nahajala napačna vrednost.

Ob pravilni nastavitvi mikrokontrolerja PIC16f628a, bo pin RB0 sprožil prekinitev ob vsaki ustrezni spremembi signala. Prekinitev lahko sproži ob vsakem prehodu signala z 0 na 1 ali nasprotno. Kakšen prehod signala bo sprožil prekinitev, določimo z bitom INTEDG v registru OPTION. Če bomo prožili prekinitve s pinom RB0, ga moramo določiti kot vhodni pin mikrokontrolerja.

Prekinitve lahko prožimo tudi s spremembo signala na kateremkoli pinu od RB4 do RB7. Tudi te pine moramo v tem primeru določiti kot vhode. Za odzivanje mikrokontrolerja na prekinitve pri spremembi signala na pinu RB0 moramo v registru INCTON postaviti na 1 bita GIE in INTE, za odzivanje na pinih od RB4 do RB7 pa bita GIE in RBIE.

Prekinitve v programu proži sprememba signala na pinu RB0. Prekinitev na pinu RB0 se bo sprožila pri prehodu signala iz nizkega nivoja v visoki. Registru OPTION smo namreč postavili bit 6 (INTEDG) na vrednost 1. V glavnem programu smo določili vrednost registra INTCON tako, da se lahko mikrokontroler odziva na zahtevane prekinitve. Zatem smo program spravili v zanko, saj ne uporabljamo nobenih dodatnih vhodnih pinov. V zanki smo zanalašč vstavili dve instrukciji nop da pri simulaciji preglednejše opazujemo potek izvajanja programa. Kljub temu da je program v zanki, delujejo prekinitve v ozadju. Ob prekinitvi program skoči v prekinitveno rutino, kjer s pomočjo maske in operacije xor preklaplja LED diodo, priključeno na pin RA1.

Poglejmo še program po zgornjih zahtevah v programskem jeziku c:

 /*

  Prekinitve na RB0.

  Okolje MPLAB IDE v8.92, HI_TECH compiler for PIC10/12/16 MCUs V9.82, oscilator 4 MHz.

  Avtor: Milan Ivič, okt 2017.

 */

    #include <htc.h>                    //Predprocesorska direktiva za Hi-Tech compiler.

    #include <pic.h>                    //Predprocesorska direktiva, ki vključuje potrebne datoteke za PIC16f628a.

    #define _XTAL_FREQ 4000000          //Kristalni oscilator 4 MHz.

    __CONFIG (0x2129);                  //Konfiguracijski biti.

 void main()

 {

    GIE = 1;                            //Omogočimo odzivanje mikrokontrolerja na prekinitve.

    INTE = 1;                           //Omogočeno proženje prekinitev s spremembo signala na RB0.

    TRISB&=0B00000001;       //RB0 je vhod (oblika binarnega zapisa v register).

    CMCON = 7;                       //RA1 je digitalni pin.

    TRISA1 = 0;                        //RA1 je izhodni pin.

    INTEDG = 1;                       //Prožimo prekinitev s spremembo siglala na RB0 iz 0 na 1 (register OPTION).

    while(1);                           //Neskončna zanka ki ničesar ne naredi, porablja le strojne cilke CPU.

 }

 void interrupt ISR()                 //Prekinitvena rutina (Interrupt Service Routine).

 {

    GIE = 0;                            //Onemogočimo prekinitve v prekinitveni rutini.

    if(INTF == 1)                       //Ali je prišlo do prekinitve na RB0?

    {

        if(RA1 == 0)                    //Preklapljanje LED diode na RA1.

            RA1 = 1;

        else

            RA1 = 0;

        INTF = 0;                       //Preden zapustimo prekinitveno rutino zastavico postavimo na 0.

    }

 }

Časovnik (timer) v mikrokontrolerju

Mikrokontroler PIC16f628a vsebuje več časovnikov: TMR0, TMR1, TMR2 in časovnik stražnega mehanizma (watchdog timer). Njihove vloge bomo spoznali pri posameznih programih v nadaljevanju.

Najprej spoznajmo časovnik 0, ki ga predstavlja SFR-register z imenom TMR0. Najdemo ga v banki 0 in banki 2 podatkovnega pomnilnika RAM na naslovih 01h in 101h, njegova vrednost pa se iz ene banke preliva v drugo. Je 8-bitni register, katerega vrednost se poveča za 1 ob vsakem izpolnjenem pogoju proženja. Ko doseže maksimalno vrednost 255 (FFh), se vrne na 0 (00h) in se povečuje naprej. Pravimo, da je časovnik prekoračil svojo vrednost ali s tujko, da je nastal overflow na časovniku. Ob prekoračitvi svoje vrednosti časovnik TMR0 sproži prekinitev, če smo jo seveda omogočili. Časovnikova vrednost se poveča vsakič, ko mine en strojni cikel (en strojni cikel traja četrtino frekvence oscilatorja), ali pa vsakič, ko se na pinu RA4 spremeni signal. V prvem primeru časovnik deluje kot števec časa, saj se povečuje v točno določenih časovnih intervalih, v drugem primeru pa deluje kot števec impulzov, ki se pojavijo na pinu RA4. Mikrokontroler PIC16f628a vsebuje tudi preddelilnik, ki deli prožilne impulze, namenjene časovniku.

Slika 3: Shema delovanja časovnika TMR0

S preddelilnikom lahko dosežemo, da se časovnik TMR0 poveča za 1 vsakih 64 strojnih ciklov ali po vsakih 64 impulzih na pinu RA4, če ga uporabljamo kot števec impulzov. Preddelilnik je torej priročen, kadar želimo s časovnikom meriti daljše časovne intervale. Brez preddelilnika bi na časovniku nastala prekoračitev (overflow) pri 4 MHz oscilatorju v času 255 μs. S prvimi tremi biti registra OPTION lahko nastavimo osem različnih stopenj preddelitve. Ali bo časovnik deloval kot števec časa ali kot števec impulzov, določimo z vrednostjo bita T0CS, to je šesti bit registra OPTION. Če ga uporabljamo kot merilnik impulzov, lahko s petim bitom istega registra (bit T0SE) izbiramo, ali se bo povečeval pri prehodu iz nizkega nivoja v visokega ali pri prehodu iz visokega nivoja signala na pinu RA4 v nizkega.

Tabela 2: Register OPTION

V register TMR0 lahko poljubno vpisujemo podatke in tako določimo točen čas, kdaj bo nastala prekinitev, če ga prožimo s taktom strojne ure. Za odzivanje mikrokontrolerja na časovnikove prekinitve moramo vklopiti bit GIE in bit T0IE v registru INTCON. Če vpišemo neko vrednost v register TMR0, se začne njegova vrednost povečevati čez čas dveh strojnih ciklov. Za zakasnitev 200 μs (pri oscilatorju 4 MHz) bi morali v register TMR0 vpisati vrednost 57, saj bo za to zakasnitev potrebnih poleg začetnih dveh korakov še 198 korakov, da bo presegel vrednost 255.

Tabela 3: Preddelilnik

Slika 4 prikazuje delovanje časovnika TMR0. Na njegovo delovanje vplivajo nastavitve registrov OPTION, INCTON in TRISA. Vpliv prvih dveh smo že opisali, v registru TRISA pa moramo določiti pin RA4 kot vhod, če hočemo časovnik TMR0 uporabiti kot števec impulzov. Na prikazanem primeru vidimo, da je preddelilnik dodeljen časovniku TMR0 (bit PSA ima vrednost 0), ki deluje kot števec časa (bit T0CS ima vrednost 0). Sinhronizacija traja dva strojna cikla, če v register vpišemo neko vrednost. Ko časovnikov register TMR0 prekorači vrednost 255 (overflow), za kar je potrebnih 256 strojnih ciklov, se sproži prekinitev. Indikator prekinitve, zastavica T0IF, se postavi na vrednost 1. Na sliki je prikazan tudi časovnik stražnega mehanizma, ki poskrbi, da napaka v programu ne ustavi izvajanja mikrokontrolerja.

Slika 4: Primer delovanja časovnika TMR0

Časovnik stražnega mehanizma (WDT, watchdog timer) ima svoj notranji RC-oscilator, ki skrbi za neprestano povečevanje njegove vrednosti. Zunanji kristalni oscilator ne vpliva na njegovo delovanje. Ko doseže maksimalno vrednost (time-out), povzroči ponastavitev (reset) mikrokontrolerja in program se začne izvajati od začetka. Če je mikrokontroler v stanju mirovanja (sleep mode) ali je v programu napaka, stražni mehanizem poskrbi, da mikrokontroler ne ustavi izvajanja programa. Brez uporabe preddelilnika doseže maksimalno vrednost v času 18 ms, z uporabo največjega preddelilnega faktorja pa znaša ta čas več kot 2,3 s.

Slika 5: Prikaz delovanja časovnik stražnega mehanizma (watchdog timer)

Če ga z nastavitvijo konfiguracijskih bitov nismo izklopili, moramo v programu njegov register redno počistiti, njegovo vrednost spraviti na 0, preden doseže časovno omejitev (time-out). To dosežemo z instrukcijo clrwdt, ki ne potrebuje nobenega parametra. Časovnika stražnega mehanizma v naših programih ne uporabljamo, zato ga izklopimo pri določitvi konfiguracijskih bitov.

Izdelajmo program, ki uporablja prekinitev na časovniku TMR0. Prekinitve se naj prožijo s sklenitvijo tipke T1, priključene na pin RA0. Na pinih RB0, RB2, RB4 in RB7 so priključene svetleče diode. Če je tipka T1 sklenjena, svetleče diode utripajo s frekvenco 0,5 Hz (1 s svetijo, 1 s ne svetijo). PORTB inicializiramo s tipko T2, priključeno na pin RA1.

 ;---------------------------------------------------------------------------------------------------------------

 ;Prekinitve na časovniku TMR0.

 ;Okolje MPLAB IDE v8.92, prevajalnik MPASM Assembler V5.51, oscilator 4 MHz.

 ;Avtor: Milan Ivič, okt 2017

 ;---------------------------------------------------------------------------------------------------------------

    list p=16f628a                      ;Tip mikrokontrolerja.

    #include <p16f628a.inc>             ;Vključi v program datoteko p16f628a.inc

    __CONFIG 0x2129

    Delovni equ 0x20                    ;V to spremenljivko bomo začasno shranili vrednost registra Work.

    Status equ 0x30                     ;V to spremenljivko bomo začasno shranili vrednost registra STATUS.

    Stevec equ 0x22                     ;Spremenljivka za štetje prekinitev, ki jih povzroči prekoračitev vrednosti TMR0 (overflow).

        org 0x000

        goto Glavni                     ;Nadaljuj na naslovu Glavni.

        org 0x004

;******************************** Prekinitvena rutina ******************************************

    bcf INTCON,GIE                      ;Onemogočimo odzivanje mikrokontrolerja na prekinitve.

    movwf Delovni                       ;Shranimo vrednost registra Work v Delovni.

    swapf STATUS,w                      ;Zamenjamo polbajta v registru STATUS, rezultat shranimo v Work.

    movwf Status                        ;Shranimo vrednost registra STATUS (ki je trenutno v Work) v Status.

    bcf STATUS,5                        ;Prehod v banko 0.

    decfsz Stevec,f                     ;Zmanjsaj stevec za 1, preskoči naslednjo instrukcijo če je Stevec = 0.

    goto Koncaj

    movlw b'10010101'                   ;Maska

    xorwf PORTB,f                       ;Preklapljanje LED diode na RB0, RB2, RB4 in RB7.

    movlw .122

    movwf Stevec                        ;Stevec je 122.

Koncaj

    bcf INTCON,T0IF                   ;Izbrisi TOIF - indikator prekinitve na TMR0.

    swapf Status,w

    movwf STATUS                        ;Vrnemo vrednost registra STATUS.

    swapf Delovni,f

    swapf Delovni,w                     ;Vrnemo vrednost registra Work.

    retfie                              ;Izhod iz prekinitvene rutine.

 ;************************************ Glavni program ***************************************

Glavni               

    bcf STATUS,5                        ;Banka 0.

    movlw .7

    movwf CMCON                         ;Omogočimo vhodno-izhodne pine na PORTA.

    bsf STATUS,5                        ;Banka 1.

    bsf TRISA,0                         ;Pin RA0 je vhodni pin.

    bsf TRISA,1                         ;Pin RA1 je vhodni pin.

    movlw b'01101010'

    movwf TRISB                         ;RB0, RB2, RB4 in RB7 so izhodi (LED diode).

    movlw b'10000100'

    movwf OPTION_REG               ;Preddelilnik 1:32 (PS0=0, PS1=0, PS2=1), izključimo notranje pull-up upore na PORTB (bit 7 mora biti na 1)

    bcf STATUS,5              ;Banka 0.

    clrf PORTB

    movlw b'10100000'              ;Omogoči prekinitve na TMR0.

    movwf INTCON

Preveri_RA0

    btfsc PORTA,0                  ;Ali je tipka RA0 sklenjena?

    goto Preveri_RA0               ;Da - omogoči povečevanje TMR0.

    clrf TMR0                      ;Ne - onemogoči povečevanje TMR0, da no bo dosegel vrednost 255.

    goto Preveri_RA1

Preveri_RA1

    btfss PORTA,1

    goto Preveri_RA0

    clrf PORTB                     ;Tipka na RA1 je sklenjena, izklopi vse LED diode.

    goto Preveri_RA0

 end

Odzivanje mikrokontrolerja na prekinitve ob prekoračenju vrednosti (overflow) časovnikovega registra TMR0 smo omogočili z določitvijo vrednosti registra INTCON v glavnem programu, vklopili smo njegov osmi (GIE) in šesti bit (T0IE). Časovniku smo v registru OPTION določili preddelitev 1:32, zato se njegova vrednost poveča za 1 po vsakem 32-tem strojnem ciklu. Dokler tipka T1 ni sklenjena, časovnik ne doseže vrednosti 255, saj se njegov register po instrukciji clrf in parametru TMR0 med programom za branje tipk programsko postavi na 0. Ob sklenitvi te tipke pa se ta instrukcija ne izvede, zato časovnik nemoteno povečuje svojo vrednost. Ko bo register TMR0 prekoračil vrednost 255 (b'11111111' oz. 0xFF), se bo sprožila prekinitev. Program se bo nadaljeval v prekinitveni rutini na naslovu 0x004.

Merjenje časa poteka s pomočjo časovnika (timerja) TMR0. Ker s časovnikom kljub preddelilniku ne moremo doseči zakasnitev 1 sekunde, si pomagamo z dodatno spremenljivko Stevec. Naslovili smo jo na naslov 0x22, kjer je prostor za GPR registre v RAM podatkovnem pomnilniku. Prekinitev je nastavljena tako, da se sproži vsake 8,192 ms. TMR0 se povečuje od 0 do 255, preddelilnik pa je nastavljen na 1:32. Pri 4 MHz kristalu je to 256 x 1 ms x 32 = 8,192 ms. Spremenljivka Stevec šteje število prekinitev. Ko se jih zgodi 122 preteče čas 1 sekunde (8,192 ms x 122 = 0,999424 s).

Preklapljanje LED diod na PORTB smo dosegli z instrukcijo xor med masko, ki smo jo vpisali v delovni register in PORTB.

Da LED diode ne bi svetile, ko sprostimo tipko T1, PORTB inicializiramo s tipko T2. Namesto tipke T1 lahko uporabimo ustrezni senzor, na primer senzor gibanja. Ko zazna gibanje, se kot indikator vklopi utripanje LED diod. Paziti moramo le na to, da je na vhodnem pinu ustrezni napetostni nivo pri stanju logične 0 in pri stanju logične 1.

Pred izhodom iz prekinitvene rutine ne smemo pozabiti izbrisati indikatorja prekinitve, zastavice T0IF.

Program po zgornjih zahtevah v programskem jeziku c:

 /*

  Prekinitve na TMR0.

  Okolje MPLAB IDE v8.92, HI_TECH compiler for PIC10/12/16 MCUs V9.82, oscilator 4 MHz.

  Avtor: Milan Ivič, okt 2017.

 */

    #include <htc.h>                    //Predprocesorska direktiva za Hi-Tech compiler.

    #include <pic.h>                    //Predprocesorska direktiva ki vključuje potrebne datoteke za PIC16f628a.

    #define _XTAL_FREQ 4000000          //Kristalni oscilator 4 MHz.

    __CONFIG (0x2129);                  //Konfiguracijski biti.

    

    int stevec = 0;                     //Deklariranje spremenljivke z imemon stevec tipa int (cela števila).

 void main()

 {

    GIE = 1;                 //Omogočimo odzivanje mikrokontrolerja na prekinitve.

    OPTION_REG = 0x87; //Preddelitev 1:256 in izključimo notranje pull-up upore na PORTB (bit 7 mora biti na 1).

    TRISA&=0B00000011;         //RA0 in RA1 sta vhoda (oblika binarnega zapisa v register).

    TRISB&=0B01101010;         //RB0, RB2, RB4 in RB7 so izhodi.

    CMCON = 7;         //Izklop komparatorjev na PORTA.

    TMR0 = 0;                           //Inicializacija registra TMR0.

    

    while(1)

    {

        if(RA0 == 1)

            TMR0IE = 1;         //Omogočamo prekinitve na časovniku TMR0, če je tipka na RA0 sklenjena.

        else

            TMR0IE = 0;                 //Če je tipka na RA0 ni sklenjena, so prekinitve na časovniku TMR0 onemogočene.

        if(RA1 == 1)

        {

            TMR0IE = 0;             //Če je tipka na RA1 sklenjena, so prekinitve na časovniku TMR0 onemogočene, LED diode pa izklopljene.

            PORTB = 0;

        }

    }

 }

 void interrupt ISR()

 {

     GIE = 0;                 //Onemogočimo prekinitve v prekinitveni rutini.

     if(TMR0IE && TMR0IF) //Ali je prišlo do prekinitve na časovniku TMR0? Lahko tudi if(TMR0IF == 1)

     {

        stevec++;         //Spremenljivka stevec se poveča za 1 po vsaki prekoračitvi (overflow) TMR0.

        if(stevec == 15)

        {

            if(PORTB == 0)              //Preklaljanje LED diod.

                PORTB = 0x95;         //95 šestnajstiško je 10010101 dvojiško.

            else

                PORTB = 0x00;

                stevec = 0;

        }

        TMR0IF = 0;         //Zastavico prekinitev postavimo na 0.    

     } 

 }

Komentar oz. opis delovanja je napisan že v programu. Kako izračunamo želeno število ponovitev prekinitvene rutine, preden LED diode preklopijo:

Frekvenca delovanja CPU je 1/4 priključene frekvence XT oscilatorja, ki je v našem primeru 4 MHz:

f = 1/4 4 MHz = 1 MHz

Časovna perioda znaša T = 1/f = 1/4 MHz = 1 µs.

Čas v katerem časovnik TMR0 prekorači svojo vrednost brez preddelitve znaša 1 µs 256 = 256 µs.

Čas v katerem časovnik TMR0 prekorači svojo vrednost s preddelilnikom 1:256 znaša 256 µs 256 = 65,536 ms = 0,065536 s.

Da se LED diode preklopijo po 1 sekundi torej potrebujemo 1 s / 0,065536 s = 15,26 prekoračitev vrednosti časovnika TMR0. Zaokrožimo na 15 prekinitev in dobimo čas, po katerem LED diode preklopijo t = 15 0,065536 s = 983,042 ms.

Še kratke opombe:

Prekinitve na komparatorju

Pini mikrokontrolerja PIC16f628a so na PORTA povezani s funkcijo komparatorja (primerjalnika) in napetostne reference. Zato delovanje pinov na PORTA določimo z ustrezno izbiro bitov v registru CMCON (Comparator control register) in registrom VRCON (Voltage reference control register). Na razpolago imamo različne načine priključitve komparatorjevih vhodov.

Kadar za PORTA izberemo funkcijo komparatorja, register TRISA ravno tako nadzoruje smer pinov na PORTA, zato moramo paziti, da pravilno določimo vhodno/izhodne pine.

Pri vaji bomo uporabili način priključitve komparatorjevih vhodov, kot prikazuje slika 6.

Slika 6: Uporabljeni način priključitve komparatorjevih vhodov, PIC16f628a

Uporabili bomo analogni vhod AN1 (pin 18, RA1/AN1), kamor bomo priključili izhodni priključek senzorja gibanja. Na nainvertirujočem (+) vhodu komparatorja bomo zagotovili referenčno napetost vrednosti Vref = 1,72 V. Če senzor gibanja zazna gibanje, se napetost na njegovem izhodu spremeni iz 0 V na 3,3 V. V primeru ko senzor gibanja zazna gibanje, bo torej na njegovem izhodnem priključku in hkrati na pinu 16 (RA1/AN1) mikrokontrolerja PIC16f628a napetost višja od vrednosti referenčne napetosti (3,3 V > 1,72 V).

Ko napetost na neinvertirujočem (-) vhodu komparatorja preseže vrednost napetosti na invertirujočem (+) vhodu, se spremeni izhod komparatorja, bit C2OUT. Hkrati pa se sproži prekinitev v mikrokontrolerju, če smo jo omogočili.

Mikrokontroler bomo nastavili tako, da se bo vsakič, ko bo senzor gibanja zaznal gibanje, sprožila prekinitev. V prekinitveni rutini bomo dvakrat za kratek čas vklopili LED diodo kot indikator, da je senzor gibanja zaznal gibanje v prostoru.

Slika 7: Priklop elementov na mikrokontroler

Uporabili bomo senzor gibanja HC-SR501, pasivni infra rdeči senzor. Osnovne lastnosti:

Slika 7: Senzor gibanja HC-SR501 (vir: http://www.datasheet-pdf.download/hc-sr501-pir-motion-sensor/)


Za nastavitev mikrokontrolerja PIC16f628a bomo ustrezno določili bite v naslednjih registrih:

Zastavica ki javi vzrok prekinitve na komparatorju se nahaja v registru PIR1.

INTCON register:

Bit GIE (bit 7) postavimo na 1 => Omogočimo odzivanje mikrokontrolerja na prekinitve.

Bit PEIE (bit 6) postavimo na 1 => Omogočimo odzivanje mikrokontrolerja na prekinitve, ki jih določimo v registru PIE 1.

CMCON register:

Bit CM0 (bit 0) postavimo na 0.

Bit CM1 (bit 1) postavimo na 0.

Bit CM2 (bit 2) postavimo na 1.

Uporabili bomo drugi komparator (slika 6). Na neinvertirujoči vhod komparatorja (+) bomo priključili referenčno napetost Vref, na invertirujoči vhod komparatorja (-), pin RA1 pa bomo priključili izhodni priključek senzorja gibanja.

Bit CIS (bit 3) postavimo na 0, ker ne bomo uporabljali pina RA0 in RA3.

Bit C2INV (bit 5) postavimo na 1, ker želimo imeti invertirani izhod drugega komparatorja .

Bit C2OUT (bit 7), vrednost bita lahko samo beremo, ne moremo pa zapisati njegove vrednosti.

Če je vrednost bita C2INV enaka 1 (kot je v našem primeru), se bo bit 7 (C2OUT) postavil na 1 takrat, ko bo napetost na RA1/AN1 večja od vrednosti referenčne napetosti Vref. Če bo torej senzor gibanja zaznal gibanje v prostoru, bo napetost na RA1/AN1 3,3 V. Ker je 3,3 V večja vrednost od referenčnih 1,72 V, bo preklopil bit C2OUT. Sprožila se bo prekinitev, saj smo jo v programu omogočili.

VRCON register:

Z biti 0 - 3 (VR0 - VR3) nastavljamo vrednost referenčne napetosti Vref. V našem primeru imamo vrednosti teh bitov 0011, kar pri uporabi večjega razpona referenčne napetosti pomeni da bo Vref = 1,72 V.

Bit VRR (bit 5) postavimo na 0, ker uporabljamo lestvico večjih razponov referenčne napetosti (glej).

Bit VRON (bit 6) postavimo na 0, saj ne želimo imeti referenčne napetosti na pinu RA2.

Bit VREN (bit 7) je Vref enable bit in ga postavimo na vrednost 1, saj želimo uporabiti referenčno napetost.

PIE1 register:

Tabela 4: Register PIE1

Bit CMIE (bit 6) postavimo na 1, saj želimo omogočiti prekinitve na komparatorju.

Register PIR1:

Tabela 5: Register PIR1

Bit CMIF (bit 6) predstavlja zastavico (Comparator Interrupt Flag bit), ki se bo postavila na 1, ko se bo sprožila prekinitev na komparatorju (ko bo preklopil bit C2OUT v registru CMCON). To se zgodi, ko senzor gibanja zazna gibanje v prostoru.

Program po naših zahtevah v programskem jeziku c:

 /*

  Prekinitve na komparatorju. Dvakratni kratki vklop LED diode (RB3), če senzor gibanja zazna gibanje v prostoru.

  Okolje MPLAB IDE v8.92, HI_TECH compiler for PIC10/12/16 MCUs V9.82, oscilator 4 MHz.

  Avtor: Milan Ivič, okt 2017.

 */

    #include <htc.h>                    //Predprocesorska direktiva za Hi-Tech compiler.

    #include <pic.h>                    //Predprocesorska direktiva ki vključuje potrebne datoteke za PIC16f628a.

    #define _XTAL_FREQ 4000000          //Kristalni oscilator 4 MHz.

    __CONFIG (0x2129);                  //Konfiguracijski biti.

    

 void main()

 {

    INTCON = 0B11000000;                //GIE = 1, PEIE = 1, Omogočamo odzivanje na prekinitve, periferne prekinitve (PIE1 register).

    PIE1 = 0B01000000;                  //CMIE = 1, Omogočamo prekinitve na komparatorju.

    CMCON = 0B00100100;

    VRCON = 0B10000011;                 //Vref = 1,72 V.

    

    TRISA = 0B00000010;                 //RA1 je vhod.

    TRISB = 0B11110111;                 //RB3 je izhod.    

    PORTB = 0B00000000;                 //Inicializacija registra PORTB.

    

    while(1)

    {

             

    }

 }

                

 void interrupt ISR()                     //Prekinitvena rutina.

 {

    GIE = 0;                              //Onemogočimo prekinitve v prekinitveni rutini.

    if(C2OUT == 1)                        //Ali je prišlo do prekinitve na komparatorju?

    {

    //Dvakratni kratki utrip LED diode, priključene na pin RB3:    

        RB3 = 1;

        __delay_ms(300);

        RB3 = 0;

        __delay_ms(200);

        RB3 = 1;

        __delay_ms(300);

        RB3 = 0;

    }

    CMIF = 0;                 //Zastavico komparatorjeve prekinitve postavimo na 0.

 }

Program ki vsebuje več vrst prekinitev

Program naj deluje po naslednjih zahtevah:

Slika 8: Priklop elementov na mikrokontroler

Program po naših zahtevah v programskem jeziku c:

 /*

  Uporaba več vrst proženja prekinitev na mikrokontrolerju PIC16f628a.

  1 Proženje prekinitev s spremembo signala na RB0 => Vklop LED diode na RB3.

  2 TMR0, s tipko na RB1 prožimo prekinitve na časovniku TMR0 => Utripanje LED diode na RB4.

  3 Komparator, če senzor zazna gibanje => Dvakratni kratki utrip LED diode, priključene na pin RB5.

  S tipko na RB2 izklopimo vse LED diode.

  Okolje MPLAB IDE v8.92, HI_TECH compiler for PIC10/12/16 MCUs V9.82, oscilator 4 MHz.

  Avtor: Milan Ivič, nov. 2017.

 */

    #include <htc.h>                    //Predprocesorska direktiva za Hi-Tech compiler.

    #include <pic.h>                    //Predprocesorska direktiva ki vključuje potrebne datoteke za PIC16f628a.

    #define _XTAL_FREQ 4000000          //Kristalni oscilator 4 MHz.

    __CONFIG (0x2129);                  //Konfiguracijski biti.

    int stevec = 0;                     //Deklariranje spremenljivke z imemon stevec tipa int.

    

 void main()

 {

    INTCON = 0B11010000;                //GIE = 1, PEIE = 1, INTE = 1, Omogočamo odzivanje na prekinitve na komparatorju in na RB0.

    OPTION_REG = 0B11000111;

    TRISA = 0B00000010;                 //RA1 => senzor gibanja.

    TRISB = 0B00000111;                 //RB0 (prekinitev), RB1 (omog. prekinitev na TMR0), RB2 izklop LED diod.

    PIE1 = 0B01000000;                  //CMIE = 1, Omogočamo prekinitve na komparatorju.    

    VRCON = 0B10000011;                 //Vref = 1,72 V.

    CMCON = 0B00100100;       

    PORTB = 0x00;                       //Inicializacija registra PORTB.

    TMR0 = 0;                           //Inicializacija registra TMR0.

    

    while(1)

    {

        if(RB1 == 1)

            T0IE = 1;                   //Če je tipka na RB1 sklenjena, omogočamo prekinitve na časovniku TMR0.

        else

            T0IE = 0;                   //Prekinitve na časovniku TMR0 so onemogočene.

        if(RB2 == 1)

            PORTB = 0x00;

    }

 }

 void interrupt ISR()                   //Prekinitvena rutina (Interrupt Service Routine).

 {

    GIE = 0;                            //Onemogočimo prekinitve v prekinitveni rutini.

    if(INTF == 1)                       //Ali je prišlo do prekinitve na RB0?

    {

        RB3 = 1;

        INTF = 0;                       //Zastavico postavimo na 0.

    }

    

    if(TMR0IE && TMR0IF)           //Ali je prišlo do prekinitve na časovniku TMR0?

    {

        stevec ++;

        if(stevec == 8)

        {

            if(RB4 == 0)

                RB4 = 1;

            else

                RB4 = 0;

            stevec = 0;

        }

        TMR0IF = 0;                     //Zastavico postavimo na 0. (ali T0IF = 0).

    }

    if(C2OUT == 0)                      //Ali je prišlo do prekinitve na komparatorju?

    {

    //Dvakratni kratki utrip LED diode, priključene na pin RB5:

        RB5 = 1;

        __delay_ms(120);

        RB5 = 0;

        __delay_ms(60);

        RB5 = 1;

        __delay_ms(120);

        RB5 = 0;

        __delay_ms(60);

        RB5 = 1;

        __delay_ms(120);

        RB5 = 0;

    }

    CMIF = 0;                           //Zastavico komparatorjeve prekinitve postavimo na 0.

 }