miercuri, 18 decembrie 2013

LEDURI, BUTOANE SI XC8 (Partea II)


     In cea de-a doua parte a prezentarii vom pune picul la treaba. Sa incepem prin a prezenta schema de ansamblu a proiectului:



     Dupa cum se vede nu este nimic special. PORTB este utilizat pentru a aprinde si stinge ledurile care reprezinta numarul scris in binar al butonului  care a fost apsat. Mecanismul, dupa cum am spus si in prima parte, este unul destul de simplu:
  •  schimba foarte repede starea bitilor RD4,RD5,RD6,RD7 (definiti ca iesiri) 
  • scaneaza foarte repede bitii RD0,RD1,RD2,RD3 (definiti ca intrari) si vezi daca isi schimba starea
  • daca starea se schimba actioneaza in consecinta.


     Acum haideti sa vedem codul si sa comentam aspectele mai importante.



    Nu voi insista asupra configurarii picului, comentariile din cod vorbesc de la sine. Cea mai importanta variabila din intreg codul este variabila globala "shifter". Dupa cum se vede a fost scrisa in binar pentru a putea intelege mai bine rolul acesteia. Practic aceasta variabila este un fel de imagine a PORTD. Voi explica mai pe indelete rolul ei ceva mai tarziu. 

Mai departe in main () setam PORTB ca fiind output (TRISB=0) iar PORTD jumatate ca input si jumatate ca output (TRISD=0b00001111).  Apoi se seteaza registrul ADCON1 pentru a folosii toti pinii ca intrari sau iesiri digitale. 

     In momentul in care se schimba starea logica a bitilor 4-7 din PORTD este necesar ca fiecare pin dintre cei mentionati sa "stea" o perioada de timp "HIGH", perioada in care programul verifica daca a fost sau  nu apasat un buton. 
    Acest lucru se realizeaza cu ajutorul unui timer, mai precis Timer1. Timer-ul in cauza incrementeza valoarea lui TMR1L cu +1 dupa fiecare instructiune efectuata. Cand TMR1L da pe-afara (adica depaseste valoarea de 255 sau 0xFF) se adauga +1 la TMR1H. Cand si cel din urma depaseste 255 atunci automat este setat bitul TMR1IF din registrul PIR1. 

    Din cele afirmate mai sus rezulta ca se numara practic un numar de 65535 instructiuni. Dar cat dureaza o instructiune? Dupa cum se vede in cod am folosit un cristal de quartz de 8MHz. Asta inseamna ca o perioada tine 1/8000000 s sau 0.125 us. Cum picul are nevoie de de 4 astfel de perioade pentru a executa o instructiune rezulta ca timpul necesar este de 0.125x4=0.5us. Revenind la TIMER1 vedem ca timpul necesar pentru ca TMR1IF din registrul PIR1 sa devina "1" este : 0.5 x 65535 = 32.7675 ms. Evident ca se poate seta registrul T1CON in asa fel incat sa se incrementeze TMR1L la mai mult de  o instructiune. Acest lucru se face  setand bitii T1CKPS0  si T1CKPS1 confrom datasheet-ului. Pentru scopul de fata un prescaler de 1:1 este suficient.

     Mai departe se intra in bucla infinita "while(1)". Primul lucru care se executa aici si care de fapt este inima intregului program este apelarea functiei "scan ()" .
      Primul lucru care se executa in "scan ()" este atribuirea lui PORTD a variabilei shifter. In acest moment PORTD are bitul 4 setat ca "HIGH" (presupunand ca shifter este 0b00010000 ) . Din acest moment incep sa se petreaca urmatoarele evenimente:
  1. Se porneste TIMER1
  2. Pentru un interval de timp de ~33ms (atat timp cat TMR1IF=0) se verifica urmatoarele:
  • daca a fost apasat un buton valoarea PORTD se schimba, acest lucru este verificat facand operatia "XOR" sau "SAU EXCLUSIV" intre noua valoare a PORTD si valoarea originala shifter.
  • daca a fost detectata o schimbare (variabila button nu este egala cu 0) atunci, deoarece este posibil ca butonul sa fi fost eliberat, se reconstruieste valoarea lui PORTD din momentul apasarii butonului facand operatia logica "SAU" intre shifter si button.
                      iata un exemplu : sa zicem ca era randul lui RD4 sa fie "HIGH" si a fost apasat butonul "1" 

                      shifter=0b00010000 ; PORTD=0b00010001;
                      button=shifter XOR PORTD
                      button=0b00000001
                      intre timp butonul 1 a fost eliberat astfel ca PORTD este egal din nou cu shifter
                      a=shifter OR button 
                      a=0b00010001, (17)  adica valoarea lui PORTD din momentul apasarii butonului

  • in functie de aceasta valoare se aprind ledurile din PORTB.    

     Inapoi in main () si in while(1). Dupa ce se iese din functia scan() trebuie verificat daca nu cumva shifter are valoarea 128 (adica  RD7 a fost "HIGH"). Daca da atunci se revine la valoarea de baza a lui shifter iar daca nu se aplica functia shift left asupra aceleiasi variabile. Ultimul lucru care trebuie facut este sa resetam valorile TMR1H , TMR1L si flagul TMR1IF accesand functia reset_timer().

     Cam asta ar fi. In cel mai scurt timp voi incerca sa postez si un filmulet cu dovada ca functioneaza. 
     In alta ordine de idei incepand cu posturile viitoare vom  atasa fiecarui buton cate o functie care va face diverse giumbuslucuri cu leduri si alte jucarii.








Niciun comentariu:

Trimiteți un comentariu