Utripanje LED diode, assembler, c

Pri izvajanju programov od mikrokontrolerja velikokrat zahtevamo določene časovne zakasnitve. Najenostavneje jih napravimo z zanko, ki ji določimo, kolikokrat naj se ponovi. Vsaka ponovitev zahteva določen čas, zato je skupni čas zakasnitve približno enak številu ponovitev zanke, pomnoženo s trajanjem ene ponovitve. Navadno se poleg zanke izvede še kakšna instrukcija, ki vpliva na skupni čas zakasnitve. Trajanje zakasnitve najlaže izmerimo v oknu Stopwatch v okolju MPLAB. Če želimo določiti natančen čas zakasnitve, po potrebi dodamo v program eno ali več instrukcij nop, ki ne naredijo nič, vsaka pa porabi en urin cikel. Lahko pa na spletu poiščemo enega od programov, ki nam za zahtevane čase izpiše ustrezne podprograme. Podprograme za zakasnitev pišemo vedno na koncu glavnega programa pred direktivo end. Za določanje ponovitev v zanki potrebujemo spremenljivke, ki jih definiramo z direktivo equ zbirnika MPLAB. Naslovimo jih v področje GPR-registrov podatkovnega pomnilnika RAM. Spremenljivkam bomo dali ime Temp z zaporedno številko.

Izdelali bomo program, ki bo vklopil utripanje treh LED diod, priključenih na pine RB0, RB2 in RB4, če je sklenjena tipka, ki je priključena na pin RA0. Program bomo namerno izdelali tako, da bo prikazana uporaba klicanja podprogramov v globino. Podprogramov za različne časovne zakasnitve je več. Osnovni je namenjen časovni zakasnitvi 1 ms. Podprogram za časovno zakasnitev 100 ms stokrat kliče podprogram za časovno zakasnitev 1 ms, podprogram za časovno zakasnitev 500 ms petkrat kliče podprogram za časovno zakasnitev 100 ms, podprogram za časovno zakasnitev 1 s pa dvakrat kliče podprogram za časovno zakasnitev 500 ms. Podprogrami se torej izvajajo v globino, saj kličemo neki podprogram iz drugega podprograma. Paziti moramo le, da ne presežemo meje osmih podprogramov v globino. Klicanje podprogramov ob sklenitvi tipke, priključene na pin RA0, prikazuje slika 1.

Slika 1: Klicanje podprogramov v globino 4

Slika 2: Priklop elementov na mikrokontroler

Program za utripanje LED diod:

;Utripanje LED diod. S tipko T priključeno na RA0 sprožimo utripanje LED diod priključenih na RB0, RB12 in RB4.

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

;Avtor: Milan Ivič, sept 2017

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

    list p=16f628a             ;Tip mikrokontrolerja

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

    

    __CONFIG 0x2129

    

    Temp1 equ 0x20 ;Spremenljivka za zakasnitev 500 µs

    Temp2 equ 0x21 ;Spremenljivka za zakasnitev 1 ms

    Temp3 equ 0x22 ;Spremenljivka za zakasnitev 100 ms

    Temp4 equ 0x23 ;Spremenljivka za zakasnitev 500 ms

    Temp5 equ 0x24 ;Spremenljivka za zakasnitev 1 s

        org 0x000

        goto Glavni_program       ;Nadaljuj na naslovu Glavni_program

        org 0x004

Glavni_program

    bsf STATUS,5                  ;Banka 1

    movlw b'00000001'

    movwf TRISA                   ;Pin RA0 je vhodni pin

    movlw b'11101010'

    movwf TRISB                   ;Pini RB0, RB2, RB4 so izhodni pini

    bcf STATUS,5                  ;Banka 0

    movlw .7                      ;Decimalno 7 je binarno 111

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

    clrf PORTB                    ;Inicializacija PORTB

;******** Ugotavljanje stanja tipke **************

PreveriTipko

    btfss PORTA,0                 ;Ali je tipka sklenjena?

    goto PreveriTipko

    movlw b'00010101'             ;Prestavi podatek v work register

    xorwf PORTB,f          ;Izvedi xor operacijo s podatki work registra in podatek shrani nazaj v register PORTB

    call Cas_1s               ;Klic podprograma

    goto PreveriTipko             ;Ponovno preveri stanje tipke

;---------------- Podprogram za zakasnitev 1 s ---------------------

Cas_1s

    movlw .2

    movwf Temp5          ;Temp5 = 2 => 2-krat pokliče podprogram Cas_500ms

zanka5

    call Cas_500ms       ;Pokliči podprogram za zakasnitev 500 ms

    decfsz Temp5,f       ;Ali se je podprogram za zakasnitev 500 ms že 2-krat izvedel?

    goto zanka5          ;Ne, Temp5 še ni enak 0

    return               ;Temp5 = 0, vrni se iz podprograma. Zakasnitev = 2 x 501 ms = 1,002 s

;---------------- Podprogram za zakasnitev 500 ms ---------------------

Cas_500ms

    movlw .5

    movwf Temp4          ;Temp4 = 5 => 5-krat pokliče podprogram Cas_100ms

zanka4

    call Cas_100ms       ;Pokliči podprogram za zakasnitev 100 ms

    decfsz Temp4,f       ;Ali se je podprogram za zakasnitev 100 ms že 5-krat izvedel?

    goto zanka4          ;Ne, Temp4 še ni enak 0

    return               ;Temp4 = 0, vrni se iz podprograma. Zakasnitev = 5 x 100,2 ms = 501 ms

;---------------- Podprogram za zakasnitev 100 ms ---------------------

Cas_100ms

    movlw .100

    movwf Temp3        ;Temp3 = 100 => 100-krat pokliče podprogram Cas_1ms

zanka3

    call Cas_1ms       ;Pokliči podprogram za zakasnitev 1 ms

    decfsz Temp3,f     ;Ali se je podprogram za zakasnitev 1 ms že 100-krat izvedel?

    goto zanka3        ;Ne, Temp3 še ni enak 0

    return             ;Temp3 = 0, vrni se iz podprograma. Zakasnitev = 100 x 1,002 ms = 100,2 ms

;---------------- Podprogram za zakasnitev 1 ms ---------------------

Cas_1ms

    movlw .1

    movwf Temp2       ;Temp2 = 2

zanka1

    movlw .199

    movwf Temp1       ;Temp1 = 199

zanka2

    nop               ;Vsaka instrukcija nop porabi 1 µs v vsaki ponovitvi

    nop               ;Fosc = 4 MHz, 4 MHz/4 = 1 MHz, T = 1/1 MHz = 1 µs v eni ponovitvi

    decfsz Temp1,f    ;Temp1 = Temp1 – 1. Podatek shrani nazaj v register Temp1

    goto zanka2       ;Temp1 še ni enak 0

    decfsz Temp2,f    ;Temp2 = Temp2 – 1. Podatek shrani nazaj v register Temp2. Temp2 = 0?

    goto zanka1       ;Temp2 še ni enak 0

    return            ;Temp2 = 0, vrni se iz podprograma. Zakasnitev = 1002 x 1 µs = 1, 002 ms

 end

Opis programa:

Z direktivo equ lahko ustvarimo konstanto z izbranim imenom, ki bo imela neko vrednost. Kjerkoli v programu se bo pojavilo ime te konstante, ga bo prevajalnik zamenjal z vrednostjo, ki smo jo konstanti priredili. Ime konstante mora biti na začetku vrstice. V programu smo prvih pet konstant (od Temp1 do Temp5) uporabili kot spremenljivke, saj smo jim dali za vrednost naslov lokacije podatkovnega pomnilnika RAM, kamor se bodo shranjevale njihove vrednosti.

Po pritisku na tipko, priključena je na pin RA0, program najprej vklopi vse tri LED diode. Če je tipka še vedno vklopljena, se po vrnitvi iz podprogramov vse tri LED diode izklopijo. V ta namen smo uporabili novo instrukcijo xorwf. Ta instrukcija izvrši operacijo xor nad enakoležečimi biti med delovnim registrom in registrom, zapisanim v prvem parametru. Drugi parameter določi, kam se bo rezultat operacije shranil. V našem primeru se bo shranil nazaj v isti register. Operacija xor se bo torej vsakič izvršila med registrom PORTB in delovnim registrom, ki smo mu določili vrednost s tako imenovano masko.

Slika 3: Uporaba operacije xor za preklapljanje LED diod

Programsko odpravljanje motenj na kontaktih tipke, ki nastanejo zaradi odbijanja kontaktov, v tem programu ni izvedeno, saj ne vpliva na delovanje programa in izdelanega elektronskega vezja.

Program za utripanje LED diod v jeziku c:

 /*

 Krmiljenje utripanja LED diod s tipko

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

 Avtor: Milan Ivič, sept 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              //Razglasitev konstante z direktivo #define => Kristalni oscilator 4 MHz

 __CONFIG (0x2129);                          //Konfiguracijski biti

 void main()

 {

    TRISB = 0xEA;

    PORTB = 0x00;

    TRISA = 0x01;

    CMCON = 0x07;

    while(1)

    {

      if(RA0 == 1)                     //Ali je tipka na RA0 sklenjena? Če je sklenjena, se izvede blok if stavka

      {

        PORTB = 0x15;              //Vklopi LED diode

        __delay_ms(1000);        //Počakaj 1 s

        PORTB = 0x00;              //Izklopi LED diode

        __delay_ms(1000);         //Počakaj 1 s

      }

      else                              //Če tipka ni sklenjena, postavi vse bite registra PORTB na 0

      {

        PORTB = 0x00;

      }

    }

 }

Opis programa:

Funkcija void main() je vstopna točka v program. Program se začne z zavitim oklepajem ({) za funkcijo void main() in konča s pripadajočim zavitim zaklepajem (}). Kar pišemo vmes, je naš program.

Najprej smo v funkciji void main() določili vhodne in izhodne pine mikrokontrolerja PIC16f628a:

TRISB = 0xEA;   

TRISB je 8-bitni register. Pini RB0, RB2 in RB4 morajo biti določeni kot izhodni pini, saj so nanje priključene LED diode, zato moramo bit 0, bit 2 in bit 4 postaviti na vrednost 0. V TRISB lahko zapišemo vrednosti na tri različne načine, lahko uporabimo šestnajstiški, dvojiški ali desetiški način zapisa. EA(16) = 11101010(2) = 234(10). Namesto TRISB = 0xEA bi lahko napisali TRISA = 0b11101010 ali TRISB = 234.

TRISA = 0x01;

TRISA je 8-bitni register. Pin RA0 moramo določiti kot vhodni pin, saj je nanj priključena tipka.

CMCON = 0x07;

CMCON je 8-bitni register. Prve tri bite tega registra moramo postaviti na 1 če hočemo izklopiti komparatorje in pin RA0 uporabiti kot vhodni pin.

Po začetnih nastavitvah se program izvaja v zanki while.

Zanka while (dokler je)

Zanke imenujemo tudi iterativni stavki, ker izvajajo ponavljanja, iteracije, na blokih. Zanka while dela, dokler je pogoj resničen. Sintaksa je:

while (je_res_pogoj) res_ blok

Izvajanje bloka res_blok se ponavlja, dokler je izraz je_res_pogoj resničen (različen od nič).

Slika 4: Delovanje zanke while

 V bloku zanke while preverjamo stanje tipke, priključene na pin RA0. LED diode se naj vklapljajo in izklapljajo (utripajo), če je tipka, priključena na pin RA0 sklenjena. Če ni sklenjena, se LED diode izklopijo (vse bite registra PORTB postavimo na 0).

Z if-else stavkom preverjamo stanje tipke. Če nas zanima ali je pogoj resničen (tipka je sklenjena) in ali je neresničen (tipka ni sklenjena) uporabljamo poleg if tudi else. V odvisnosti od izpolnitve postavljenega pogoja, se izvede eden od dveh, med seboj neodvisnih blokov.

Slika 5: Delovanje stavka if-else

Če je tipka sklenjena, LED diode vklopijo in se po preteku 1 s izklopijo. Vklapljajo in izklapljajo se tako dolgo, dokler je tipka sklenjena. Časovne zakasnitve smo dosegli z __delay_ms(1000);