miercuri, 30 iulie 2014

Scoala de vara Lectia 3 - Logica binara. Masca, Testare si setare biti individuali.

    Aplicatiile dezvoltate pentru microcontrollere se deosebesc de aplicatiile de nivel inalt prin faptul ca aici se lucreaza cu periferiile la nivel jos, adica este necesar de cele mai dese ori de accesat un registru la nivel de bit sa la nivel de bit sau cativa biti consecutivi din acelasi registru periferic. Limbajul C nu ofera un mecanism direct pentru asemenea acces, accesul minimal fiind la nivel de byte, data de tip char.
   Pemtru a accesa pentru modificare sau verificare a unui singur bit dintr-un byte, cea mai eleganta metoda este prin aplicarea unei masti.
    Vom numi masca o constanta in care bitii de interes sunt unitati, iar bitii ignorati zerouri. spre exemplu o masca unde bitul de interes este butul 4 va arata in felul urmator:
    #define MASK 0b00010000
iar o masca cu bitii de interes 2 si 5 va arata dupa cum urmeaza:
    #define MASK 0b00100100
    In programare, mai ales la cea de nivel jos si in special pentru lucrul la nivel de biti cu masca pe larg se utilizeaza reprezentarea in cod hexazecimal. mai mult, reprezentarea in HEX, am putea-o numi o reprezentare compresata a numerelor binare, un digit al careia reprezinta strict un quartet de biti, indiferent pe ce pozitie in numar se afla. in asa mod sirurile lungi de numere binare se put reduce de 4 ori. Practicand des lucrul cu constantele in HEX, programatorii practic o inteleg ca un nativ de nivel jos in programarea pentru microcontrollere.
    Deci, mastile se utilizeaza pentru acces la nivel de bit, Pentru modificare vom avea doua tipuri de operatii - setare si resetare.
    Pentru setare a bitilor de interes cu ajutorul mastii este necesar de a atribui variabilei dorite rezultatul operatiei OR intre variabila si masca. Ecemplu:  data |= MASK
    Pentru resetare  - este nevoie de a atribui rezultatul operatiei AND cu inversul mastii 
Exemplu :   data &= ~MASK

Surse de informare suplimentare:  
      Constante. Logica booleana. Masca.

Aplicatie practica:

In aplicatia din lectia precedenta accesul la accesul la registrii periferic se efectua ca un tot intreg.
In aplicatia din aceasta lectie vom exersa cu accesul la registrii periferici la nivel de bit.
Deci aplicatia data consta in citirea continua a starii unui buton, iar la detectarea apasarii butonului se va schimba starea LED-ului in opus.
Scema electrica de data asta va prezenta un buton conectat in mod similar ca si in lectia precedenta (2) la unul din pinii de pe portul B, sa zicem pinul PINB2, celalalt terminal al butonului conectat la ground. starea pasiva a butonului, deconectat, va fi "1" datorita rezistentei de pull-up conectata prin configuratia microcontrollerului.. Catodul LED-ul va fi conectat la unul din pinii portului A, sa zicem PINA5, Anodul printr-o rezistenta de limitare a curentului la Alimentare.
Pinii care nu sunt utilizati se recomanda de a fi configurati pe intrare pull-up activat.
Detectarea apasarii pinului se va face cu un test a lui PINA cu masca 1<<2 (0b00000100 sau 0x04)
vom defini masca pentru buton in felul urmator : 
#define BUTTON_MASK 0b00000100
Testul (PINA & BUTTON_MASK) va detecta daca valoarea pinului selectat este "1" adica deconectat, respectiv pentru testarea daca butonul este apasat va trebui sa testam daca pinul selectat este "0", vom testa valoarea inversa colectata la pin, deci testul va arata dupa cum urmeaza 
(~PINA & BUTTON_MASK).vezi explicatiile mai sus.
la detectarea apasarii butonului se va verifica starea curenta a LED-ului cu masca definita dupa cum urmeaza:
#define LED_MASK 0b00100000
verificarea se va face la sursa de semnal, adica in registrul PORTA, pentru ca s-ar putea ca nivelul logic pe pinul fizic sa nu corespunda cu cel asteptat din cauza posibilei atenuari a semnalului.
Daca testul (PORT & LED_MASK) va da rezultat TRUE ceia ce ar insemna ca pe pin este generata valoarea "1", adica LED-ul este stins, se va executa comanda de aprindere a LED-ului prin resetarea bitului in PORTA corespunzator. PORTA&=~LED_MASK, vezi explicatii referitor manipularea bitilor mai sus. Pentru testul picat, FALSE, se va executa comanda de stingere - PORTA|=LED_MASK.
S-ar parea ca problema este rezolvata, insa nu este chiar asa, Luand in consideratie viteza de procesare a microcontrollerului cu productivitatea de ~1 instructiune per tact al frecventei procesorului, si considerand frecventa controllerului sa zicem de 1Mhz, asta ar insemna ca intr-o secund el va reusi sa reamizeze pana la 1 milion de operatii. Secventa de cod pe care o realizam in aceasta aplicati este plasata in cadrul buclei infinite, si deci odata realizata o va lua de la inceput, adica la testarea apasarii butonului. Intervalul dintre doua testari consecutive fa vi de ordinul micro-secundelor la cate instructiuni sunt realizate in secventa de cod. Rezulta ca fizic cat de rapizi nu vom fi cu apasarea butonului, nu vom reusi sa deconectam pana la urmatoarea verificare, si deci, transferul dintro stare in alta a LED-ului se va executa de un numar mare si necontrolat de ori.
Pentru a evita acest lucru, vom realiza o bucla de asteptare a deconectarii butonului prin testarea acestei situatii. testul va arata in felul urmator (PINA & BUTTON_MASK).
S-ar parea ca am rezolvat problema, dar in realitate mai exista si problema zgomotului mecanic, care se manifesta prin vibratiile mecanice la apasarea butonului, care vor provoca detectii multiple de apasare deconectare a butonului.
Cel mai efectiv mod de evitare a acestui efect este de a introduce o retinere a executiei programului pe perioada atenuarii zgomotului mecanic.
Vom chema in acest scop functia de retinere cu un interval suficient, sa zicem de 50 milisecunde
_delay_ms(50)
De mentionat ca acesta functie functioneaza normal doar in cazul in care sunt introduse optimizarile de compilare.
Totodata nu este recomandata utilizarea acestor functii in programele profesioniste, principalul motiv este ca nu asigura acelasi comportament la diferite nivele de optimizare a compilatorului, insa la nivel de cod incepator este o functie destul de utila. Vom vedea in aplicatiile de nivel mai avansat cum se abordeaza asemenea probleme. ins la moment, se pare ca am rezolvat problema.

#inclunde <avr/io.h> // includem libraria de definire
// a adreselor periferiilor
#include <util/delay.h> // includem libraria pentru retineri

#define BUTTON_MASK 0b00000100 // definim maska pentru buton
#define LED_MASK 0b00100000 // definim maska pentru LED

int main(void){
// partea de initializarea a programului


// partea de procesare a programului
while(1){ // bucla infinita de mentinere 
// sub control a microcontrollerului

while(~PINA & BUTTON_MASK); // bucla de blocare pana 
// la apasarea butonului

_delay_ms(50); // evitarea zgomotului
// mecanic la apasare
 
if(PORTA & LED_MASK) // test LED stins
PORTA &=~LED_MASK // aprindem LED prin resetare pin
else // LED-ul este stins
PORTA |= LED_MASK // aprindem LED prin setare pon

while(PINA & BUTTON_MASK); // bucla de blocare pana 
// la deconectarea butonului

_delay_ms(50); // evitarea zgomotului
// mecanic la deconectare
 
} sfarsit bucla infinita

return 0; // nu se executa niciodata

}sfarsit functia main/program