Part1 r31
Part1 r31
Autor : BIGONOFF
Partea I
INTRODUCERE IN MICROCONTROLERE
CU PIC16F84
Editia 31
II
CUPRINS :
1. INTRODUCERE............................................................................................................................................... 1
III
8.2.1 Registrele « PCL » si « PCLATH » .................................................................................................... 42
8.2.2 Registrul « W » ................................................................................................................................... 43
8.2.3 Registrul « STATUS »......................................................................................................................... 43
8.3 LANSAREA SIMULARII ................................................................................................................................. 45
9. SETUL DE INSTRUCTIUNI......................................................................................................................... 50
9.1 INSTRUCTIUNEA « GOTO » (DU-TE LA)....................................................................................................... 50
9.2 INSTRUCTIUNEA « INCF » (INCREMENT FILE)............................................................................................ 51
9.3 INSTRUCTIUNEA « DECF » (DECREMENT FILE)......................................................................................... 51
9.4 INSTRUCTIUNEA « MOVLW » (MOVE LITERAL TO W).............................................................................. 52
9.5 INSTRUCTIUNEA « MOVF » (MOVE FILE).................................................................................................. 52
9.6 INSTRUCTIUNEA « MOVWF » (MOVE W TO FILE)..................................................................................... 53
9.7 INSTRUCTIUNEA « ADDLW » (ADD LITERAL AND W) .............................................................................. 54
9.8 INSTRUCTIUNEA « ADDWF » (ADD W AND F) .......................................................................................... 54
9.9 INSTRUCTIUNEA « SUBLW » (SUBSTRACT W FROM LITERAL).................................................................. 55
9.10 INSTRUCTIUNEA « SUBWF » (SUBSTRACT W FROM F)............................................................................ 57
9.11 INSTRUCTIUNEA « ANDLW » (AND LITERAL WITH W) ........................................................................... 57
9.12 INSTRUCTIUNEA « ANDWF » (AND W WITH F)....................................................................................... 58
9.13 INSTRUCTIUNEA « IORLW » (INCLUSIVE OR LITERAL WITH W) .............................................................. 58
9.14 INSTRUCTIUNEA « IORWF » (INCLUSIVE OR W WITH FILE)..................................................................... 59
9.15 INSTRUCTIUNEA « XORLW » (EXCLUSIVE OR LITERAL WITH W) .......................................................... 59
9.16 INSTRUCTIUNEA « XORWF » (EXCLUSIVE OR W WITH F) ....................................................................... 60
9.17 INSTRUCTIUNEA « BSF » (BIT SET F)........................................................................................................ 60
9.18 INSTRUCTIUNEA « BCF » (BIT CLEAR F)................................................................................................... 60
9.19 INSTRUCTIUNEA « RLF » ( ROTATE LEFT THROUGH CARRY).................................................................... 61
9.20 INSTRUCTIUNEA « RRF » ( ROTATE RIGHT THROUGH CARRY).................................................................. 62
9.21 INSTRUCTIUNEA « BTFSC » (BIT TEST F, SKIP IF CLEAR) ........................................................................ 62
9.22 INSTRUCTIUNEA « BTFSS » (BIT TEST F, SKIP IF SET).............................................................................. 63
9.23 INSTRUCTIUNEA « DECFSZ » (DECREMENT F, SKIP IF Z)........................................................................ 64
9.24 INSTRUCTIUNEA « INCFSZ » (INCREMENT F, SKIP IF ZERO).................................................................... 65
9.25 INSTRUCTIUNEA « SWAPF » (SWAP NIBBLES IN F) ................................................................................. 65
9.26 INSTRUCTIUNEA « CALL » (CALL SUBROUTINE)..................................................................................... 66
9.27 INSTRUCTIUNEA « RETURN » (RETURN FROM SUBROUTINE) ................................................................ 66
9.28 INSTRUCTIUNEA « RETLW » (RETURN WITH LITERAL IN W) .................................................................. 69
9.29 INSTRUCTIUNEA « RETFIE » (RETURN FROM INTERRUPT)...................................................................... 70
9.30 INSTRUCTIUNEA « CLRF » (CLEAR F)...................................................................................................... 70
9.31 INSTRUCTIUNEA « CLRW » (CLEAR W) .................................................................................................. 70
9.32 INSTRUCTIUNEA « CLRWDT » (CLEAR WATCHDOG) ............................................................................. 70
9.33 INSTRUCTIUNEA « COMF » (COMPLEMENT F)......................................................................................... 71
9.34 INSTRUCTIUNEA « SLEEP » (TRECEREA IN MOD STAND-BY) .................................................................... 71
9.35 INSTRUCTIUNEA « NOP » (NO OPERATION) .............................................................................................. 72
9.36 INSTRUCTIUNI DEPASITE............................................................................................................................ 72
10. MODURILE DE ADRESARE..................................................................................................................... 73
10.1 ADRESAREA LITERALA SAU IMEDIATA ...................................................................................................... 73
10.2 ADRESAREA DIRECTA ................................................................................................................................ 73
10.3 ADRESAREA INDIRECTA ............................................................................................................................ 73
10.3.1 Registrele FSR si INDF .................................................................................................................... 74
10.4 CITEVA EXEMPLE ...................................................................................................................................... 75
11. REALIZAREA UNUI PROGRAM INCORPORAT................................................................................. 77
11.1 MATERIALE NECESARE.............................................................................................................................. 77
11.2 REALIZAREA PLACUTEI DE TEST ................................................................................................................ 77
11.3 REALIZAREA PROIECTULUI ........................................................................................................................ 78
11.4 EDITAREA FISIERULUI SURSA .................................................................................................................... 79
11.5 ALEGEREA CONFIGURATIEI ....................................................................................................................... 79
11.6 REGISTRUL OPTION................................................................................................................................. 80
11.7 EDITAREA PROGRAMULUI ......................................................................................................................... 82
11.8 REGISTRUL PORTA .................................................................................................................................. 84
11.8.1 Functionarea detaliata a PORTURILOR ......................................................................................... 85
11.9 REGISTRUL TRISA.................................................................................................................................... 86
11.10 REGISTRELE PORTB ET TRISB .............................................................................................................. 86
11.11 EXEMPLU DE APLICARE ........................................................................................................................... 87
IV
11.12 RUTINA DE INITIALIZARE......................................................................................................................... 87
11.13 REZULTATELE COMPILARII ...................................................................................................................... 91
11.14 PROGRAMUL PRINCIPAL .......................................................................................................................... 91
11.15 SUBRUTINA DE TEMPORIZARE ................................................................................................................. 92
12. INTRERUPERILE ....................................................................................................................................... 98
12.1 CE ESTE O INTRERUPERE ? ......................................................................................................................... 98
12.2 MECANISMUL GENERAL AL UNEI INTRERUPERI ......................................................................................... 98
12.3 MECANISMUL DE INTRERUPERI AL PIC®-URILOR ..................................................................................... 99
12.4 SURSELE DE INTRERUPERI ALE LUI 16F84 ............................................................................................... 100
12.5 DISPOZITIVELE PUSE IN PRACTICA ........................................................................................................... 101
12.6 REGISTRUL INTCON (INTERRUPT CONTROL) ...................................................................................... 102
12.7 SALVAREA SI RESTAURAREA STARII ........................................................................................................ 103
12.7.1 Despre salvarea registrelor............................................................................................................ 104
12.7.2 Metoda de salvare .......................................................................................................................... 104
12.7.3 METODA DE RESTAURARE .................................................................................................................... 105
12.7.4 Operatii asupra registrului STATUS .............................................................................................. 107
12.7.5 Particularitati ale instructiunii « RETFIE »................................................................................... 107
12.8 UTILIZAREA UNEI RUTINE DE INTRERUPERE ............................................................................................ 108
12.9 ANALIZA RUTINEI DE INTRERUPERE ........................................................................................................ 110
12.10 ADAPTAREA RUTINEI DE INTRERUPERE ................................................................................................. 112
12.11 INITIALIZAREA ...................................................................................................................................... 114
12.12 CONSTRUCTIA PROGRAMULUI PRINCIPAL .............................................................................................. 115
12.13 CONSTRUCTIA RUTINEI DE INTRERUPERE .............................................................................................. 116
12.14 RULAREA PE SIMULATOR A UNEI RUTINE DE INTRERUPERE ................................................................... 116
12.15 PRIMA CORECTIE : RESETAREA FLAG-ULUI ............................................................................................ 119
12.16 SA NE RAPORTAM LA SCARA DE TIMP A PIC®-ULUI .............................................................................. 120
12.17 PROBLEMA ELIMINARII VIBRATIILOR ..................................................................................................... 121
12.18 FINALIZAREA PROGRAMULUI ................................................................................................................ 122
12.19 OBSERVATII IMPORTANTE ..................................................................................................................... 125
12.20 CONCLUZII ............................................................................................................................................ 126
13. TIMER-UL 0 ............................................................................................................................................... 126
13.1 DIFERITELE MODURI DE FUNCTIONARE ................................................................................................... 126
13.2 REGISTRUL TMR0 .................................................................................................................................... 127
13.3 METODE DE UTILIZARE A TIMER-ULUI 0 .................................................................................................. 127
13.3.1 Modul de citire simpla.................................................................................................................... 127
13.3.2 Modul de scanare a flag-ului.......................................................................................................... 127
13.3.3 Modul intrerupere........................................................................................................................... 128
13.3.4 Metodele combinate........................................................................................................................ 128
13.4 PREDIVIZORUL ........................................................................................................................................ 128
13.5 APLICATIE PRACTICA A TIMER-ULUI 0 ..................................................................................................... 130
13.5.1 Pregatiri ......................................................................................................................................... 130
13.5.2 Initializarea .................................................................................................................................... 131
13.5.3 Rutina de intrerupere...................................................................................................................... 132
13.6 MODIFICAREA REGISTRELOR IN SIMULATOR ........................................................................................... 133
13.7 INCERCAREA PE PLACUTA DE TEST .......................................................................................................... 133
13.8 O PRIMA IMBUNATATIRE A PRECIZIEI ...................................................................................................... 134
13.9 O A DOUA IMBUNATATIRE A PRECIZIEI .................................................................................................... 134
13.10 METODA CORECTA : ADAPTAREA CEASULUI.......................................................................................... 135
13.11 METODA DE LUX : CEASUL DUBLU......................................................................................................... 135
13.12 EXEMPLU DE FOLOSIRE A 2 INTRERUPERI .............................................................................................. 135
13.13 CONCLUZIE ........................................................................................................................................... 137
14. ACCESELE IN MEMORIA « EEPROM ».............................................................................................. 139
14.1 MARIMEA SI LOCALIZAREA MEMORIEI EEPROM ................................................................................... 139
14.2 PREGATIREA PROGRAMULUI.................................................................................................................... 139
14.3 INITIALIZAREA ZONEI EEPROM ............................................................................................................. 141
14.4 REGISTRUL EEDATA ............................................................................................................................. 142
14.5 REGISTRUL EEADR................................................................................................................................ 142
14.6 REGISTRUL EECON1.............................................................................................................................. 142
14.7 REGISTRUL EECON2.............................................................................................................................. 143
14.8 CITIREA DIN MEMORIA EEPROM ........................................................................................................... 143
V
14.9 SCRIEREA IN MEMORIA EEPROM........................................................................................................... 144
14.10 UTILIZAREA PRACTICA A MEMORIEI EEPROM ..................................................................................... 145
14.11 SECURIZAREA ACCESULUI IN MEMORIA EEPROM................................................................................ 148
14.12 CONCLUZII ............................................................................................................................................ 148
15. WATCHDOG-UL ....................................................................................................................................... 148
15.1 PRINCIPIUL DE FUNCTIONARE .................................................................................................................. 148
15.2 PREDIVIZORUL SI WATCHDOG-UL ............................................................................................................ 149
15.3 ROLUL WATCHDOG-ULUI......................................................................................................................... 149
15.4 FOLOSIREA CORECTA A WATCHDOG-ULUI ............................................................................................... 150
15.5 CEEA CE NU TREBUIE FACUT ................................................................................................................... 151
15.6 MASURAREA TIMPULUI REAL AL WATCHDOG-ULUI ................................................................................. 151
15.7 SIMULAREA UNEI BLOCARI DE PROGRAM ................................................................................................ 153
15.7.1 Corectie prin folosirea watchdog-ului............................................................................................ 153
15.8 ALEGEREA VALORII PREDIVIZORULUI ..................................................................................................... 154
15.9 TIMP TIPIC, MINIM, SI MAXIM................................................................................................................... 154
15.10 CONCLUZIE ........................................................................................................................................... 155
16. MODUL SLEEP.......................................................................................................................................... 155
16.1 PRINCIPIUL DE FUNCTIONARE .................................................................................................................. 155
16.2 IESIREA DIN MODUL « SLEEP »................................................................................................................. 156
16.3 REVENIREA CU BITUL GIE DEZACTIVAT .................................................................................................. 156
16.4 REVENIREA CU BITUL GIE ACTIV ............................................................................................................ 157
16.5 IMPOSIBILITATEA PUNERII IN MOD « SLEEP » ........................................................................................... 157
16.6 FOLOSIREA MODULUI « SLEEP » .............................................................................................................. 157
16.7 CAZ TIPIC DE UTILIZARE .......................................................................................................................... 158
16.7.1 Pentru un consum minim ................................................................................................................ 158
16.8 CONCLUZIE ............................................................................................................................................. 159
17. CE MAI GASIM IN DATASHEET .......................................................................................................... 159
17.1 STRUCTURA INTERNA .............................................................................................................................. 159
17.2 SECVENTA DE DECODARE ........................................................................................................................ 159
17.3 ORGANIZAREA MEMORIEI........................................................................................................................ 160
17.4 REGISTRELE SPECIALE ............................................................................................................................. 160
17.5 ELECTRONICA PORTURILOR..................................................................................................................... 160
17.6 REGISTRUL DE CONFIGURARE ................................................................................................................. 160
17.7 DIFERITELE TIPURI DE OSCILATOARE ...................................................................................................... 161
17.7.1 Precizia oscilatorului ..................................................................................................................... 162
17.8 RESETUL ................................................................................................................................................. 162
17.9 PUNEREA SUB TENSIUNE.......................................................................................................................... 163
17.10 CARACTERISTICI ELECTRICE.................................................................................................................. 164
17.11 PORTABILITATEA PROGRAMELOR .......................................................................................................... 164
17.12 ACTUALIZAREA COMPONENTELOR ........................................................................................................ 165
17.13 CONCLUZII ............................................................................................................................................ 166
18. SUGESTII, SOLUTII SI SUBTILITATI DE PROGRAMARE............................................................. 168
18.1 COMPARATIILE ........................................................................................................................................ 168
18.2 SCADEREA UNEI VALORI DIN W............................................................................................................... 168
18.3 INMULTIRILE ........................................................................................................................................... 169
18.4 INMULTIREA CU O CONSTANTA................................................................................................................ 171
18.5 ADRESARE INDIRECTA CATRE 2 ZONE DIFERITE....................................................................................... 172
18.6 TABELELE IN MEMORIA PROGRAM........................................................................................................... 173
18.7 VARIABILELE LOCALE ............................................................................................................................. 176
18.7.1 Determinarea variabilelor locale ................................................................................................... 177
18.7.2 Constructia fara variabile locale.................................................................................................... 177
18.7.3 Constructia cu variabile locale ...................................................................................................... 177
18.8 IMPARTIREA PRINTR-O CONSTANTA......................................................................................................... 178
18.9 CONCLUZIE ............................................................................................................................................. 178
19. UTILIZAREA DE SUBRUTINE INTR-UN FISIER SEPARAT........................................................... 178
19.1 INTREBARI SI PUNCT DE PLECARE ............................................................................................................ 178
19.2 UTILIZAREA DIRECTA DE RUTINE IN FISIER .............................................................................................. 179
19.3 INCAPSULAREA IN MACRO-URI SIMPLE .................................................................................................... 181
VI
19.4 METODA FINALIZATA .............................................................................................................................. 183
19.5 CONCLUZIE ............................................................................................................................................. 184
20. NORMA ISO7816 ....................................................................................................................................... 184
20.1 PARTICULARITATI UTILE ALE NORMEI ISO7816...................................................................................... 184
19.1.1 Comenzile ISO7816 ........................................................................................................................ 184
20.1.2 Protocolul schimbului de informatii ............................................................................................... 185
20.2 LEGATURILE SERIALE ASINCRONE ........................................................................................................... 186
20.2.1 Bitul de start ................................................................................................................................... 186
20.2.2 Bitii de date..................................................................................................................................... 187
20.2.3 Bitul de paritate .............................................................................................................................. 187
20.2.4 Bitul de stop.................................................................................................................................... 187
20.2.5 Viteza si debitul .............................................................................................................................. 187
20.3 ACHIZITIA BITILOR .................................................................................................................................. 188
19.4 CARACTERISTICILE CARDURILOR « STANDARD » .................................................................................... 189
20.5 CREAREA SI INITIALIZAREA PROIECTULUI ............................................................................................... 189
20.6 BAZA DE TIMP ......................................................................................................................................... 190
20.7 RECEPTIA UNUI OCTET ............................................................................................................................ 191
20.8 EMISIA UNUI CARACTER .......................................................................................................................... 193
20.9 INITIALIZAREA ........................................................................................................................................ 195
20.10 TRANSMITEREA ATR-ULUI ................................................................................................................... 195
20.11 TRANSMITEREA STARII .......................................................................................................................... 197
20.12 RECEPTIA CLASEI .................................................................................................................................. 197
20.13 RECEPTIA LUI INS, P1, P2, SI LEN........................................................................................................ 198
20.14 CONTROLUL INSTRUCTIUNII RECEPTIONATE.......................................................................................... 198
20.15 TRATAREA UNEI INSTRUCTIUNI ............................................................................................................. 199
20.16 VARIABILELE ........................................................................................................................................ 200
20.17 CONCLUZII ............................................................................................................................................ 201
ANEXA 1 : INTREBARI FRECVENTE (F.A.Q.) ......................................................................................... 201
A1.1 CONSIDER CA 8 SUB-PROGRAME ESTE PUTIN .......................................................................................... 201
A1.2 N-AM FOLOSIT DECIT 8 IMBRICATII, SI TOTUSI PROGRAMUL MEU SE BLOCHEAZA ................................... 201
A1.3 PROGRAMUL MEU PARE CA NU MAI IESE NICIODATA DIN INTRERUPERI................................................... 201
A1.4 NU POT SA FOLOSESC SIMULATORUL, NU-MI APAR OPTIUNILE ................................................................ 202
A1.5 PRIMESC UN MESAJ DE EROARE EOF INAINTEA INSTRUCTIUNII END ..................................................... 202
A1.6 CUM DEZASAMBLEZ UN FISIER « .HEX » ?............................................................................................... 202
A1.7 FOLOSIREA MINUSCULELOR SI MAJUSCULELOR ...................................................................................... 202
A1.8 ALEGEREA UNUI PROGRAMATOR ............................................................................................................ 202
A1.9 AM O EROARE DE « STACK » ................................................................................................................... 203
A1.10 CARE SINT DIFERENTELE INTRE 16F84 SI 16F84A ? ............................................................................. 204
A1.11 AM O EROARE 173 IN CURSUL ASAMBLARII .......................................................................................... 204
CONTRIBUTII BENEVOLE .......................................................................................................................... 205
VII
1. Introducere
Linistiti-va, am sa fiu cit se poate de scurt, ca sa putem trece rapid la abordarea subiectelor
care ne intereseaza. Daca sinteti deja "profesionisti", puteti sari primul capitol si sa treceti mai
departe.
Nu ezitati niciodata sa-mi faceti observatii, sau sa-mi atrageti atentia asupra erorilor care e
posibil sa-mi fi scapat (www.abcelectronique.com/bigonoff sau www.bigonoff.org ). Puteti
copia sau cita, folositi cum vreti informatiile de aici, traduceti documentul in alta limba sau in
alt format. In acest caz, trebuie doar sa respectati dorinta autorului si anume, de a-mi trimite si
mie un exemplar al lucrarii dvs. si asta, numai din dorinta de putea profita cit mai multi
oameni de acest curs ([email protected]).
Va atrag atentia asupra faptului ca pentru eficacitatea acestui curs, va trebui sa rezolvati
toate exercitiile pe care vi le propun. Solutiile exercitiilor sint disponibile ca fisiere-exemplu
furnizate ca anexe ale cursului.
Tot ce va va trebui, este un 16F84, un cuart de 4 MHz, o placuta de test, un LED, un buton
si programul MPLAB®, pus gratuit la dispozitia dvs. de catre firma Microchip® la adresa
http://www.microchip.com.. De la aceeasi adresa puteti lua si datasheet-ul lui 16F84.
Am pierdut multe zile ca sa realizez aceste exercitii. Le-am verificat personal pe macheta,
unul cite unul. Va recomand sa incercati voi insiva aceste programele inainte de a-mi pune pe
e-mail intrebari la care puteti obtine raspunsul si singuri daca veti face acest mic efort.
Credeti-ma pe cuvint, ca doar la punerea in practica va dati seama ca nu ati inteles bine ceva
ce parea a fi evident. Eu raspund intotdeauna mesajelor primite, dar trebuie sa spun ca uneori
este deadreptul enervant sa primesti o intrebare de la cineva care afirma ca a asimilat in
totalitate acest curs, intr-o ora. Mi-ajunge !
1
2
2. Sistemele de numeratie
Sintem obisnuiti inca din copilarie sa folosim sistemul de numeratie zecimal, asa incit nu
ne mai dam seama cum functioneaza de fapt acest sistem, totul devenind un automatism.
Pozitia cifrelor are deasemenea o mare importanta. Cifrele mai putin semnificative se
gasesc in dreapta numarului, ponderea lor crescind pe masura ce ne deplasam spre stinga. Prin
urmare, in numarul 502, 5 are o pondere mai mare decit 2. In realitate, fiecare cifra, pe care o
putem denumi DIGIT, are o valoare care depinde de RANGUL sau. Cu cit trebuie sa
inmultim o cifra in functie de pozitia sa ? Este foarte simplu, se ridica BAZA folosita, la
puterea indicata de RANGUL sau.
Toate acestea par complicat de scris, dar este foarte simplu de inteles. Daca intelegeti acest
principiu, veti intelege automat orice sistem de numeratie.
Sa reluam, de exemplu, numarul nostru, 502. Ce reprezinta 2 ? Ei bine, este foarte simplu,
valoarea sa este egala cu 2 de inmultit cu baza (10) ridicata la puterea rangului cifrei, adica 0.
Dar un numar ridicat la puterea 0 este egal cu 1. 2 reprezinta deci 2 x 1.
Ati inteles pina aici ? Atunci, ceea ce urmeaza va va parea simplu. Pentru voi nu e nici o
problema sa numarati pe degete pina la 10, insa pentru calculatoare nu este asa de simplu. Ele
nu stiu sa faca distinctie decit intre 2 nivele (prezenta, sau absenta unei tensiuni). Deci
sistemul de numeratie zecimal este complet nepotrivit.
Vom intelege imediat ca singurul sistem potrivit este un sistem in baza 2, denumit
SISTEM BINAR. Acest sistem nu are decit 2 cifre, 0 si 1. Cum primele calculatoare
(si PIC®-urile) lucreaza cu numere de 8 cifre binare, aceste numere au fost denumite octeti
(sau bytes, in engleza). Cifra 0 sau 1 este denumita BIT (unitate binara, sau BInary uniT).
3
Sa analizam acum un numar binar, sa zicem octetul B’10010101’. Care este deci valoarea
sa in zecimal ? Ei bine, este foarte simplu, se aplica acelasi algoritm ca pentru zecimal.
Pornind de la dreapta spre stinga, vom gasi :
B’10010101’ = 1 x 20 + 0 x 21 + 1 x 22 + 0 x 23 + 1 x 24 + 0 x 25 + 0 x 26 + 1 x 27
Observati deci ca este foarte usor de convertit in zecimal un numar binar de oricite cifre.
Dar invers ? Ei bine, este la fel de simplu. Va trebui doar sa stiti tabela puterilor lui 2. Aceasta
se invata foarte usor dupa ce o folosim de citeva ori.
Care este cea mai mare putere a lui 2 cuprinsa in 149 ? Raspuns : 7 (27 = 128)
Spunem ca bitul 7 va fi 1. Odata stabilit acest lucru, ne ramine 149 - 128 = 21
Bitul 6 reprezinta 64, care este mai mare decit 21, deci b6 = 0
Bitul 5 reprezinta 32, care este mai mare decit 21, deci b5 = 0
Bitul 4 reprezinta 16, care se cuprinde in 21, deci b4 = 1, ramine 21 - 16 = 5
Bitul 3 reprezinta 8, care este mai mare decit 5, deci b3 = 0
Bitul 2 reprezinta 4, care se cuprinde in 5, deci b2 = 1, ramine 5 - 4 = 1
Bitul 1 reprezinta 2, care e mai mare decit 1, deci b1 = 0
Bitul 0 reprezinta 1, adica tocmai ceea ce a ramas, deci b0 = 1, rest 0.
Numarul binar obtinut este deci B’10010101’, care este tocmai octetul nostru initial.
Retineti ca, daca obtinem un numar cu mai putin de 8 cifre, vom completa cu zerouri puse la
stinga numarului. Adica, B’00011111’ = B‘11111’, la fel cum 0502 = 502.
Nu uitati ca intotdeauna sa completati octetii pentru a obtine 8 biti, caci acest lucru este
impus de marea majoritate a asambloarelor (vom vedea ce sint acestea pe parcursul cursului).
Retineti ca cea mai mare valoare ce poate fi reprezentata pe un octet va fi deci :
B’11111111’. Daca faceti conversia (sau folositi calculatorul din Windows in mod
"Stiintific"), veti obtine 255. Toate numerele mai mari decit 255 necesita deci mai mult de un
octet pentru a putea fi reprezentate.
Am sa va dau o alta metoda simpla pentru conversia din zecimal in binar, procedind invers,
adica de la dreapta la stinga. Metoda consta in a scrie restul impartirii prin 2 a numarului.
149 / 2 = 74 – Rest 1
74 / 2 = 37 – Rest 0
37 / 2 = 18 – Rest 1
18/ 2 = 9 – Rest 0
9/2 = 4 – Rest 1
4/2=2 – Rest 0
2/2=1 – Rest 0
1/2=0 – Rest 1
4
Reluind toate resturile si incepind cu ultimul (sau completind de la dreapta la stinga), vom
obtine B’10010101’, care este exact valoarea gasita anterior. In general aceasta a doua metoda
este mai simpla pentru cei care nu sint obisnuiti sa jongleze cu puterile lui 2, dar prima este
mai rapida de efectuat mental daca va obisnuiti cu ea.
Reprezentarea numerelor binare nu este usor de folosit, si scrierea unei succesiuni lungi de
1 si 0 poate reprezenta o mare sursa de erori. Se impunea deci gasirea unei solutii mai practice
pentru reprezentarea numerelor binare. S-a decis deci ca sa se "taie" in doua fiecare octet si sa
se reprezinte fiecare bucata (QUARTET, sau SEMIOCTET) printr-o cifra.
Cum un quartet poate lua orice valoare intre b’0000’ si b’1111’, se constata ca se obtin
valori cuprinse intre 0 si 15. Aceasta inseamna in total 16 combinatii. Cele 10 cifre ale
sistemului zecimal nu sint deci suficiente pentru a reprezenta aceste valori..
In loc de a se inventa 6 simboluri noi, s-a decis sa se foloseasca primele 6 litere ale
alfabetului, pe post de CIFRA. Acest sistem de numeratie in baza 16 a fost deci denumit
sistem hexazecimal.
Retineti ca acest sistem nu este decit o reprezentare mai eficace a numerelor binare, si face
conversia dintr-unul in altul aproape instantanee. In lectiile urmatoare, vom nota un numar
hexazecimal punindu-i in fata 0x. Sa vedem daca ati inteles :
Pentru a reprezenta un octet, ne vor trebui deci doua cifre hexazecimale. De exemplu,
numarul nostru B’10010101’ este reprezentat in hexazecimal prin 0x95. Daca faceti
conversia din hexazecimal in zecimal, folosind acelasi principiu ca mai sus, veti obtine :
Ca proba, care este cel mai mare numar care poate fi reprezentat cu 2 cifre hexazecimale
5
Raspuns : 0xFF, adica 15 x 16 + 15 = 255.
Daca ati inteles totul pina aici, veti putea converti orice numar, din orice baza, in oricare
alta baza. Veti putea intilni in unele carti referiri la sistemul octal, care este un sistem in baza
8 si care a fost larg utilizat in trecut. Astazi, acest sistem aproape ca a disparut.
2.4 Operatiile
Dupa ce ati invatat sa convertiti numere intre diverse formate, veti vedea ca este la fel de
usor sa faceti operatii cu aceste numere, indiferent de format.
Pentru aceasta, este suficient sa urmati aceleasi reguli ca si in sistemul zecimal.
Un mic exemplu : Cit face B’1011’ + B ‘1110’ ? Ei bine, se procedeaza exact in acelasi fel
ca pentru o operatie in zecimal :
B’1011‘
+ B’1110‘
--------------
?
Cele 2 numere initiale erau B’1011’, adica 11, si B’1110’, adica 14. Veti proceda la fel
pentru numerele hexazecimale stiind ca 0xF + 0x1 = 0x10, adica 15 + 1 = 16.
In unele aplicatii, este necesar sa putem folosi numere negative. Deoarece procesoarele nu
inteleg semnul « - », si deoarece trebuie sa ne limitam la numere de 8 biti, singura metoda
gasita, a fost de a introduce semnul in numar.
S-a ales deci (nu intimplator) bitul 7 pentru a reprezenta semnul. In numerele cu semn,
daca bitul 7 are valoarea "1", este vorba de un numar negativ. Prin urmare, B’10000000’ (-0)
va fi atunci egal cu B’00000000’ (0). In plus, din motive de usurare a calculului s-a decis sa
se foloseasca o notatie usor diferita.
6
Exemplu : fie numarul 5 : B’00000101’. Cum scriem –5 ?
B ’11111101’ (-3)
+ B ’00000101’ (5)
-----------------------------
= B’100000010’ (2)
Si atunci, imi veti spune, rezultatul nu e 2 ? Ba da, dar daca va uitati bine veti vedea 9 biti,
ori procesorul nu lucreaza decit cu 8. Al 9-lea bit este pus intr-un bit special pe care-l vom
studia mai tirziu. In registrul procesorului ramin deci numai cei 8 biti din dreapta, adica 2 care
este rezultatul corect pentru (–3 ) + 5.
Acum, daca m-ati urmarit cu atentie, mi-ati putea pune urmatoarea intrebare :
Cind vad B’11111101’, este vorba despre –3 sau 253 ? Ei bine, nu puteti sti daca nu tineti
seama de context.
Ce sint astea, ma veti intreba ? Ei bine, pentru simplitate, sa spunem ca sint operatii care se
efectueaza bit cu bit pentru un octet dat. In locul unei teorii stufoase despre algebra lui Boole
va voi arata concret operatiile ce trebuie neaparat cunoscute pentru programarea PIC®-urilor
si a altor microcontrolere.
7
2.6.1 Complementul (« NOT »)
Reprezinta ceea ce mai puteti gasi si sub denumirea de « inversare » sau « NOT » sau
complement fata de 1. El este adesea notat cu « ! ». Functionarea sa este foarte simpla si
consta in inversarea tuturor bitilor dintr-un octet (0 devine 1 si 1 devine 0).
Exemplu :
Vedeti aici ca pentru operatiile booleene, este mai usor de lucrat in binar. Convertiti
exemplul de mai sus in hexazecimal (vom spune de-acum « hexa ») sau in zecimal, si
incercati sa-l complementati direct. Adio neuroni !
La ce serveste o astfel de operatie ? De exemplu, pentru a citi o valoare ale carei nivele
active au fost inversate, pentru a obtine numere negative, sau alte operatii pe care le vom
vedea in continuare.
Se mai numeste si inmultirea bit cu bit, sau « AND » si adesea se noteaza cu « & ».
Exemplu :
Priviti exemplul de mai sus : al 2-lea octet contine 4 biti "1" si 4 biti "0". Priviti rezultatul
obtinut : primii 4 biti au fost conservati (1100), iar in locul celorlalti 4 gasim "0".
In concluzie, cu ajutorul acestei instructiuni se poate pune pe "0" unul sau mai multi biti
dintr-un cuvint binar indiferent de continutul sau precedent.
8
2.6.3 Functia « SAU » (« OR »)
Bit 1 Bit 2 OR
0 0 0
0 1 1
1 0 1
1 1 1
Un mic exemplu :
Iata si ultima functie de care ne vom ocupa in aceasta sectiune. Ea este adesea denumita
XOR (eXclusif OR). Ea se comporta ca si functia OR, cu o mica exceptie.
Pentru a obtine 1, trebuie ca bitul 1 sa fie 1 SAU bitul 2 sa fie 1 EXCLUZIND ambii biti
simultan. Daca ambii biti sint 1, atunci rezultatul va fi 0.
Exemplu :
Aici se incheie primul capitol dedicat PIC®-urilor. Stiu ca a fost un pic cam plictisitor, dar,
daca nu va veti insusi temeinic cele ce urmeaza, nu veti putea sa va realizati corect propriile
programe.
9
2.7 Citeva cuvinte despre unitati
Am vorbit pina aici de octeti si biti. In informatica, se folosesc curent termenii de Byte
(octet) si bit (binary unit) pentru a denumi aceste unitati de baza.
Sa vorbim acum despre multiplii unitatilor de baza (Kilo, Mega, etc.), si situatia se
inrautateste rapid.
De fapt, in baza 10, baza noastra “cea de toate zilele”, s-a decis sa se adopte multiplii de 3
ai puterilor lui 10. Avem astfel :
Kilo = 103
Mega = 106
Giga = 109
Tera = 1012
Atunci insa cind lucram in baza 2, lucrurile se complica si nu se mai potrivesc cu ceea ce
sintem obisnuiti. Prin urmare, 103 nu mai reprezinta decit un simplu numar printre altele (va
las dvs. placerea sa-l convertiti in binar). Va trebui deci ca pentru « multipli » sa gasim niste
valori particulare care sa fie adoptate ca regula. In prima faza, s-a procedat pur si simplu la
"recuperarea" termenilor folositi in baza 10 (Kilo, Mega, Giga, etc.), exact cum au fost
"recuperate" literele pentru a le "transforma" in cifre in hexazecimal. Acestor termeni insa, li
s-au alocat valori care reprezinta puteri multiplii de 10 ai bazei 2. Astfel, s-au definit :
Kilo = 210
Mega = 220
Giga = 230
Tera = 240
Prin urmare, in informatica, 1 Kilooctet reprezinta 210 octeti, adica 1024 de octeti, la fel
cum 1 Kilogram reprezinta 1000 grame. Situatia se "inrautateste" pentru Mega, deoarece 1
Megaoctet valoreaza 220 octeti, adica 1048576 octeti, la fel cum un Megagram valoreaza
1.000.000 grame.
10
decis sa se faca ordine in acest domeniu. Era clar prin urmare ca se impunea inventarea unor
noi termeni, ceea ce s-a si facut, primul dinre ei fiind « Kibi » (Kilobit binar) care reprezinta
un multiplu in baza 2 si anume 210. S-a ajuns deci la conventiile urmatoare :
Astfel stabilite noile conventii, nu mai existau ambiguitati asupra termenilor folositi.
Trebuiau deci "adoptati" in informatica termenii Ki, Mi, etc. in locul lui K, M, G.... Situatia
reala este insa departe de ceea ce s-a dorit, marea majoritate a lucrarilor de informatica si a
softurilor continuind sa foloseasca denumirea de Kilo pentru puterile lui 2. Aceasta situatie va
ramine voua in atentie, si probabil ca se va rezolva in anii urmatori.
Incercati totusi sa nu confundati Gibi cu o celebra marca de wisky, pentru ca nu este decit
o simpla coincidenta, si v-ar face rau la neuroni !
11
Note:…
12
3. Alcatuirea si functionarea PIC®-urilor
In sfirsit, ceva mai interesant… Vom incepe sa vorbim despre un PIC®, si in particular,
despre 16F84. Retineti ca tot ce vom invata despre 16F84 va putea fi folosit direct pentru
16F876, care nu sint nimic altceva decit un 16F84 imbunatatit. Fiecare PIC® dispune de
functionalitatile modelelor anterioare, la care se adauga noi functii. In momentul trecerii la
revizia 13 a cursului, modelele pe care le veti intilni vor fi probabil 16F84A, dar acest lucru
nu va pune nici un fel de problema pentru studiul cursului.
Mai intii, va trebui sa descarcati datasheet-ul lui 16F84, caci este un document pe care-l
vom folosi in restul lectiilor. Va sfatuiesc imperativ sa-l tipariti, caci veti avea nevoie de el tot
timpul atunci cind veti incepe sa va realizati propriile programe.
Aceste datasheet-uri sint cartile mele de capatii. Am considerat mai intelept sa lucram
practic si sa le comentam, in loc sa traduc “orbeste” respectivele documente.
PIC®-urile sint componente de tip RISC (Reduced Instructions Set Computer), sau
componente cu set redus de instructiuni. De ce ? Ei bine, sa stiti ca, cu cit este mai redus
numarul de instructiuni, cu atit mai usor si mai rapid pot fi ele decodificate si deci cu atit mai
rapid poate functiona circuitul.
Ceasul aplicat PIC®-ului este predivizat cu 4 in interiorul acestuia. Aceasta este baza de
timp care stabileste durata unui ciclu. Daca folosim de exemplu, un cuart de 4 MHz , obtinem
deci 1.000.000 cicluri/secunda, sau, cum PIC®-ul executa practic 1 instructiune pe ciclu,
(exceptind salturile) rezulta o viteza de procesare de 1 MIPS (1 Milion de Instructiuni Pe
Secunda).
Ginditi-va ca exista PIC®-uri la 20MHz ; rezulta deci o viteza de procesare mai mult decit
onorabila…
13
3.2 Diferitele familii de PIC®-uri
In momentul scrierii primei editii a acestui curs, PIC®-urile erau impartite in 3 mari
familii : familia Base-Line (linia de baza), care foloseste cuvinte de instructiuni (vom vedea
ce sint acestea) pe 12 biti pentru unele PIC®-uri (12C508), familia Mid-Range (clasa medie),
care foloseste cuvinte pe 14 biti (si din care fac parte 16F84 si 16F876), si familia High-End
(clasa de virf) care foloseste cuvinte de 16 biti. In continuare, au aparut si alte familii, ca
Enhanced family, si lucrurile nu se vor opri aici.
Noi ne vom limita in acest curs la familia Mid-Range, considerind ca daca ati inteles totul,
veti putea trece usor la alta familie, sau chiar la alt tip de microcontroler. Pentru a va ajuta sa
va diversificati cunostintele, mai sint disponibile si alte cursuri.
Retineti ca datasheet-ul lui 16F84 nu este decit o mica parte din documentatia completa.
Pentru a obtine documentatia completa, va trebui sa descarcati inca vreo 600 de pagini de la
Microchip®, si care reprezinta datasheet-urile pentru gama Mid-Range.
Totusi, documentatia de baza este suficienta pentru 99,9 % din aplicatii, si, in plus,
datasheet-urile Mid-Range sint disponibile sub forma unui fisier structurat pe capitole. Eu
personal, am aproape totul tiparit, dar pe voi va sfatuiesc sa le consultati doar in momentul
cind veti avea nevoie de ele.
Urmeaza uneori litera L : aceasta indica faptul ca PIC®-ul poate functiona si cu o tensiune
mai redusa (Low Power).
In continuare, urmeaza :
Retineti ca numai o memorie de tip FLASH sau EEPROM poate fi stearsa, deci nu sperati
sa puteti reprograma un PIC® de tip CR. Pentru versiunile C, uitati-va cu atentie in datasheet.
16C84 poate fi reprogramat, adica e vorba de o memorie EEPROM. 12C508, de exemplu, are
o memorie de program EPROM deci care poate fi stearsa numai prin expunere la lumina
ultravioleta. Deci, pentru stergere este necesara o fereastra transparenta pe chip, acestea fiind
versiuni speciale pentru dezvoltare si nu versiuni curente ale circuitului.
Mai departe veti constata ca ultimele cifre identifica precis PIC®-ul (84).
La urma veti vedea pe capsula un sufix de forma « -XX » unde XX reprezinta frecventa
maxima de ceas ce se poate aplica circuitului. De exemplu, –04 inseamna 4 MHz. De fapt, se
pare ca aceste indicatii sint pur comerciale si ca toate PIC®-urile de un anumit tip pot rula la
14
viteza maxima pentru acest model, viteza data la inceputul datasheet-ului. Aceasta inscriptie
pare deci complet inutila. Nu imi asum responsabilitatea pentru aceasta afirmatie, insa las la
latitudinea voastra sa judecati si sa testati acest aspect.
Deci, un 16F84-04 este un PIC® Mid-Range (16) a carui memorie de program este de tip
FLASH (F), deci reinscriptibila, de tip 84 si este capabil sa lucreze cu o frecventa de ceas
teoretica de 4 MHz, (probabil 10 MHz pentru un 16F84 si 20 MHz pentru un 16F84A).
O ultima indicatie pe care o veti gasi, este tipul capsulei. Noi vom folosi pentru
experimentele noastre capsula PDIP, care este o capsula DIL cu 18 pini, cu o distanta intre
rinduri de 0.3" (ingusta). Versiunea de 4 MHz va fi mai mult decit suficienta pentru scopurile
noastre.
Retineti tot aici ca PIC®-urile sint componente STATICE, ceea ce inseamna ca frecventa
de ceas poate fi redusa pina la oprirea completa, fara pierdere de date sau disfunctionalitati.
Spre deosebire de acestea, componentele DINAMICE (cum sint procesoarele din PC-ul
vostru), au nevoie ca frecventa de ceas sa ramina in limite foarte precise. Nu incercati deci sa
setati Pentium-ul vostru PIII/500 la 166 MHz, caci este o componenta dinamica si nu va
functiona…
Deci, daca doriti sa comandati un PIC® pentru a-l folosi in restul cursului, cereti un
PIC16F84-04 in capsula PDIP. N-are importanta daca este un PIC16F84 sau PIC16F84A.
Memoria unui 16F84 este impartita in 3 zone. La pagina 4 din datasheet, veti gasi tabela
1-1 care ofera o imagine a familiei 16F8x. Numerele paginilor poate fi diferit, in functie de
actualizarile Microchip®. In acest caz, va trebui sa cautati un pic, sau sa folositi datasheet-ul
pe care vi l-am dat la curs.
Pentru cei nerabdatori, figura 3-1 de la pagina 8 arata organizarea interna a unui 16F84.
Memoria de program este constituita din 1 Ki de cuvinte de 14 biti. In aceasta zona va veti
scrie dvs. programele. Asa se explica de ce fisierele de pe PC au 2 Kio (Kibiocteti).
Prin urmare, sint necesari 2 octeti pentru a codifica 14 biti. Deasemenea, asa se explica de
ce atunci cind cititi un PIC® gol, veti citi numai 0x3FFF. Asta face in binar
B’11111111111111’, adica 14 biti. Va voi explica mai departe de unde provin acesti
"faimosi" 14 biti.
Retineti aici ca o instructiune este codificata pe 1 cuvint. Deci, 1 Ki inseamna putin peste o
mie de instructiuni posibile (ceea ce nu e prea rau !). Cind dvs. veti scrie programe de 1 Ki,
fara nici o indoiala, ca nu veti mai avea probleme cu aplicatiile voastre.
15
3.4.2 Memoria EEPROM
Memoria EEPROM (Electrical Erasable Programmable Read Only Memory), consta din
64 octeti pe care-i puteti scrie prin intermediul programului vostru. Acesti octeti nu se sterg
dupa o intrerupere a alimentarii si sint folositi pentru pastrarea de parametrii semi-permanenti.
Folosirea lor implica o procedura speciala, pe care o vom vedea in continuare, caci nu este
vorba despre o memorie RAM, ci mai degraba un ROM de tip special. Este deci mai rapida la
citire decit la scriere. Daca ati programat des EPROM-uri (2416), cred ca ati observat deja
acest lucru.
Memoria RAM (Random Access Memory) este aceea pe care o folosim tot timpul. Toate
datele stocate insa in ea, se pierd la oprirea alimentarii.
In cazul lui 16F84, dispuneti de 68 de octeti liberi. Organizarea RAM-ului este prezentata
in tabelul 4-2 de la pagina 13 din datasheet. Observati separarea verticala in 2 bancuri, iar in
partea de jos vedeti doua bancuri de 68 de octeti de RAM.
Din nefericire, indicatia « mapped in bank 0 » va indica faptul ca accesarea unuia din cei
68 de octeti din bancul 0 sau din bancul 1, va avea ca efect accesarea aceleiasi locatii de
memorie.
Remarcati faptul ca bancul 0 foloseste adresele de la 0x00 la 0x7F, iar bancul 1 de la 0x80
la 0xFF. Zonele gri nu sint folosite (si sint deci neutilizabile). Locatia 0x00 este o locatie care
nu poate fi accesata.
Pentru marea majoritate a registrelor, fiecare bit are o functie speciala. Tabelul 4-1 de la
pagina 14 va arata numele bitilor folositi in aceste registre.
16
4. Organizarea instructiunilor
4.1 Generalitati
Haideti, curaj, totul devine din ce in ce mai concret. O sa facem un mic survol prin setul de
instructiuni al PIC®-urilor. Vom sari direct la pagina 55 din datasheet, la capitolul 9. Si
deoarece acest curs nu este o prezentare a unui manual tehnic, ci are ca scop intelegerea
notiunilor, va trebui sa parcurgem capitolele din datasheet pe sarite.
La pagina indicata, veti gasi un mic dreptunghi gri care ne atrage atentia asupra a doua
instructiuni vechi, care nu se mai folosesc. Deci, nici noi nu le vom folosi. In tabelul 9-1 se
arata cum sint codificate instructiunile in PIC®. Si iata, veti descoperi in sfirsit la ce servesc
cei 14 biti din memoria de program..
Sint instructiuni care manipuleaza datele sub forma de octeti. Ele sint codificate in modul
urmator :
• 6 biti pentru codul instructiunii : e logic, caci existind 35 de instructiuni, vor fi necesari 6
biti pentru a le putea codifica pe toate (26 = 64 > 35).
• 1 bit de destinatie (d) pentru a indica daca rezultatul obtinut va trebui sa fie retinut in
registrul de lucru al unitatii de calcul (W de la Work), sau salvat in operand (F de la File).
• Ramin 7 biti pentru a codifica operandul (File). Acesti 7 biti reprezinta adresa locatiei din
RAM de unde se va lua (sau unde se va pune) octetul de prelucrat (respectiv cel rezultat).
Hopa, prima problema : cei 7 biti nu permit accesul la toata memoria RAM, deci iata
explicatia impartirii memoriei RAM in doua bancuri.
Prin urmare, va trebui sa gasim o solutie pentru a completa bitul lipsa. Ati spus cumva « un
bit din unul din registre » ? BRAVO, vad ca ati inteles bine. In realitate, este vorba de bitul
RP0 din registrul STATUS.
Ah, ati remarcat ca exista si un RP1 ? Ei bine, da, 16F876 are 4 bancuri, si acest bit va fi
folosit pentru alte PIC®-uri pe care le vom studia in partea a II-a. Va trebui sa lasati RP1 pe 0
pentru 16F84, daca vreti sa « portati » fara probleme programele dvs. catre un PIC® mai
evoluat.
Sint instructiunile destinate manipularii directe a bitilor unui anumit registru. Ele sint
codificate in felul urmator :
17
• 4 biti pentru codul instructiunii (in spatiul ramas liber de la instructiunile precedente)
• 3 biti pentru a indica numarul bitului de manipulat (bitul 0 … 7), si din nou :
Sint instructiunile ce manipuleaza date care sint codificate direct in instructiune. Vom
vedea acest lucru mai in detaliu atunci cind vom vorbi despre modurile de adresare. Ele sint
codificate in modul urmator :
Sint instructiuni care provoaca o rupere in secventa normala de derulare a programului. Ele
sint codificate in felul urmator :
Putem deduce deja ca salturile nu dau acces decit la 2 Ki din memoria de program (211).
Acest lucru nu pune nici un fel de problema, 16F84 neavind decit 1 K de memorie. Pentru a
codifica deci un salt in interiorul memoriei de program, ne vor trebui deci
Nu uitati ca, reglementarile oficiale ar dori sa folosim termenul « kilobinary » sau « kibi »,
sau prescurtat « Ki » pentru a exprima 2 la puterea 10. Se pare totusi ca nu multa lume
foloseste acesti termeni in practica.
Va voi arata acum cum functioneaza tabelul din figura 9-2, pagina 56. Acest tabel va
permite ca, dintr-o singura privire sa aflati cum functioneaza fiecare instructiune.
18
Incercati sa folositi termenii corecti, situatia in domeniul informaticii nefiind totdeauna
simpla, nu adaugati confuzii inutile.
Deci, in acest loc veti gasi instructiunile propriu-zise pe care le veti putea folosi in
programele dvs.
- Eticheta (facultativa)
- Spatiu(i) sau TAB(-uri),
- Mnemonice (cu majuscule sau minuscule),
- TAB(-uri) sau Spatiu(i)
- Operand sau valoare
- Virgula eventuala de separatie
- Bit de destinatie W sau F sau eventual numarul bit-ului (de la 0 la 7) daca este necesar
- Spatiu(i) sau TAB(-uri)
- Punct si virgula (facultativ, daca nu exista comentarii)
- Comentariu. (facultativ)
Retineti ca mnemonicele nu se pot gasi in prima coloana, si ca tot ce urmeaza dupa punct si
virgula este ignorat de asamblor (este deci o zona de comentariu).
Dispuneti deasemenea de posibilitatea de a insera unul sau mai multe spatii sau TAB-uri de
fiecare parte a virgulei.
Iata aici, cu titlu de exemplu, doua linii valide : cuvintele in verde sint cuvinte rezervate,
cele in albastru sunt instructiunile, cele in galben sint libere, iar restul este comentariu.
Retineti ca toate instructiunile necesita numai un singur ciclu, in afara de salturi care
necesita doua cicluri, si operatiile de test cu salt, al caror rezultat al testului produce salt
(instructiunile notate 1(2)).
Ø A 4-a coloana contine ceea ce se numeste OPCODE, adica cuvintul binar de 14 biti pe
care MPLAB® il va genera pentru dvs. plecind de la mnemonic.
Nu trebuie deci sa va faceti griji, dar sa stiti ca puteti programa direct PIC®-ul si fara
asamblor, construind direct fisierul .HEX si introducind valorile gasite in aceasta coloana.
Va trebui insa atunci sa calculati totul, in special salturile. Asa am procedat eu la inceput,
cu un procesor 6502, caci nu dispuneam de un asamblor. Aceasta tehnica se poate folosi
deasemenea pentru construirea de programe auto-modificabile, din cauza restrictiilor de
memorie.
19
Linistiti-va, aceasta tehnica tine acum de evul-mediu al informaticii. Suplimentar, puteti
face corelatii intre valorile prezentate aici si valorile din tabelul 9-1, cu titlu pur educativ.
Daca nu doriti asta, uitati de aceasta coloana.
Acesti indicatori sint indispensabili pentru programare. Este deci absolut necesar sa
intelegem foarte bine cum functioneaza (cel putin a lui Z si C).
Cititi deci cu foarte mare atentie cele ce urmeaza. Toti indicatorii sint biti ai registrului
STATUS. Uitati-va la tabelul de la pagina 15. Vom discuta aici despre flag-urile Z si C.
Ceilalti vor fi tratati la studiul registrelor.
Daca rezultatul unei operatii CARE AFECTEAZA ACEST FLAG este 0, flag-ul Zero
trece in 1.
20
4.4.2 Indicatorul de stare « C »
Acesta este indicatorul pentru Carry (transport). Daca rezultatul unei operatii produce o
depasire, bitul C va fi pus in 1. De fapt, acest bit constituie al 9-lea bit al operatiei.
Un mic exemplu :
Cum registrele PIC®-ului nu au decit 8 biti, veti obtine B’00000001’ (1) si bitul C
pozitionat in 1 (de fapt este al 9-lea bit, caci 28 = 256 si nu este suficient). Prin urmare,
rezultatul final va fi :
256 + 1 = 257
Ceilalti biti ai registrului de stare vor fi tratati ceva mai departe, pe parcursul cursului,
atunci cind va fi necesar.
21
Note:…
22
5. Introducere in MPLAB®
Vom incepe acum marea aventura cu primul nostru programel. Va voi explica
instructiunile si registrele pe masura ce le vom folosi.
Incepind cu editia a 13-a a cursului, vom lucra cu MPLAB® 6.x, care succede versiunii 5.x
folosita in curs pina la versiunea 12.
Primul lucru care trebuie facut este de a cauta versiunea curenta de MPLAB® 6.60 pe site-
ul Microchip® : http://www.microchip.com , sectiunea "arhive". Puteti deasemenea sa folositi
si o versiune 7.xx care este destul de asemanatoare. In acest curs, capturile de ecran au fost
facute cu MPLAB® 6.3, insa diferentele ar trebui sa fie minore pentru versiunile mai recente.
Dezarhivati fisierele si instalati programul. Pe parcursul instalarii veti intilni mai multe
ferestre explicative privind diferite alte "scule" ale Microchip®, ca debugger-ul si simulatorul.
Presupunind ca nu aveti (inca) aceste scule, inchideti toate aceste ferestre pentru a incheia
instalarea.
Copiati acolo fisierul m16F84.asm furnizat impreuna cu cursul. Acesta este un fisier pe
care l-am creat cu scopul de a putea demara rapid un program nou. L-am denumit
« m16f84.asm », « m » provenind de la « macheta ».
Daca nu doriti sa creati un director nou, copiati fisierul in directorul de instalare implicit :
Pentru fiecare program nou pe care-l veti crea, efectuati un copy / paste al fisierului
m16F84.asm. Pentru primul nostru program, acest fisier va fi redenumit Essai1.asm.
Acum puteti lansa MPLAB® IDE din meniul de Start sau cu dublu-click pe icoana de pe
desktop (daca ati acceptat crearea acestei icoane la instalare). Dupa citeva momente, va aparea
un ecran gol, cu meniuri si bare de instrumente. Daca aveti vreo fereastra deschisa in
MPLAB® 6, inchideti-le pe toate, astfel incit sa stiti cum sa le deschideti si toata lumea sa
inceapa cu aceasi configuratie.
23
wizard-ul, il puteti selecta din meniu Project => Wizard.
Selectati din meniu Project => New…. Fereastra care se deschide va permite sa
introduceti numele proiectului si directorul de lucru pentru acest proiect. Pentru director,
dispuneti de un buton "Browse" care va ajuta sa selectati corect directorul dorit fara sa gresiti.
Introduceti « Essai1 » ca nume pentru noul dvs. proiect si alegeti directorul in care ati pus
fisierul-macheta si fisierul vostru essai1.asm. Am denumit fisierul asm la fel ca numele
proiectului, insa nu este neaparat obligatoriu.
Odata apasat butonul <OK>, va apare o noua fereastra in coltul superior stinga al
desktop-ului MPLAB® IDE.
Vom trece acum la precizarea parametrilor importanti ai proiectului nostru, dar mai intii
vom preciza tipul de PIC® pe care-l vom folosi. Selectati Configure => Select Device…; va
apare o fereastra din care veti alege PIC®-ul. Notati ca implicit, Microchip® va propune un
PIC® din familia 18F; deh, promovarea unui nou produs obliga… Alegeti 16F84.
24
Aceasta fereastra va indica deasemenea, prin intermediul unor LED-uri rosii si verzi, care
sint sculele suportate de PIC®-ul ales. Observati deci ca simulatorul integrat (MPLAB® SIM)
functioneaza cu acest PIC®, dar In-Circuit Debugger (MPLAB® ICD 2), nu, etc. Apasati
<OK> si fereastra se va inchide.
Va trebui acum sa precizam ce limbaj vom folosi, stiind ca noi lucram in limbaj de
asamblare, dar ca si alte compilatoare sint propuse de Microchip® si de alte firme.
Selectati Project => Select language toolsuite… In ferastra care se deschide, alegeti din
meniul derulant « Microchip MPASM® toolsuite ». MPASM® este de fapt asamblorul
implicit al Microchip®.
25
Va trebui acum sa indicam lui MPASM® care este fisierul (sau fisierele) nostru sursa.
Aceasta se realizeaza in fereastra "essai1.mcw" care a ramas deschisa in coltul din stinga-sus.
Pentru a adauga un fisier sursa, este foarte simplu : faceti click pe « source files », apoi
alegeti « Add ». Odata ales fisierul, numele acestuia va aparea in arborescenta. Notati ca
MPLAB® cauta implicit in directorul vostru de lucru, ales anterior.
Aici, este important sa intelegeti ca fisierul-sursa ales va fi cel care va fi asamblat sau
compilat. Altfel spus, daca in acest fisier se gaseste o instructiune « include » care include un
alt fisier, dvs. nu va trebui sa-l selectati explicit si pe acesta, caci va fi adaugat automat in
proiect in momentul asamblarii.
Contrar acestora, daca doriti sa asamblati simultan doua fisiere, si in primul fisier nu se
face nici o referire la cel de-al doilea, atunci va trebui sa adaugati si acest fisier in
arborescenta.
Toate proiectele noastre nu necesita decit un singur fisier. Pentru cazul in care proiectul
vostru necesita unul sau mai multe fisiere suplimentare, directiva « include » va fi adaugata
fisierului sursa si deci asamblorul va integra automat si celalalt (celelalte) fisiere.
Nu mai aveti nevoie de nimic altceva, celelalte elemente ale arborescentei nu sint necesare
pentru un proiect in limbaj de asamblare de genul aceluia de care ne vom ocupa noi.
Ne-a mai ramas un element important de precizat, si anume sistemul de numeratie folosit
implicit. Selectati : Project => Build Options => Project. Se va deschide o fereastra din care
se alege TAB-ul "MPASM Assembler".
26
Cum noi am hotarit deja sa folosim notatia hexazecimala sub forma 0x, iar cea binara sub
forma B’xxx’, toate numerele fara prefix vor fi considerate ca fiind in sistemul zecimal. Bifati
deci optiunea « Decimal ». Nu va atingeti de celelalte optiuni, pe care poate le veti folosi cind
veti deveni un "profesionist" si cind veti dori sa adaptati anumiti parametri. In momentul de
fata, nu va intereseaza acesti parametri.
Un numar scris fara prefix va fi interpretat conform parametrului descris anterior, si deci
risca sa puna probleme daca veti incerca sa integrati acest fisier intr-un alt proiect, sau, mai
simplu, daca veti da altora fisierul-sursa fara sa le precizati notatia implicita.
In momentul de fata, MPASM® este gata sa va asambleze programul. Dar, imi veti spune,
nu este nimic pe ecran !
Practic, MPASM® nu se ocupa de fisierele afisate pe ecran, singurul lucru care conteaza
este fisierul inscris in arborescenta si fisierele conectate cu acesta.
Puteti avea pe ecran cite fisiere doriti sau puteti sa nu aveti niciunul ; lui MPLAB® nu-i
pasa. Pe de alta parte, toate fisierele deschise de la ultima salvare a proiectului vostru vor fi
automat deschise la urmatoarea deschidere a proiectului, chiar daca nu servesc la nimic.
27
Aceasta proprietate este practica pentru a putea face copy / paste pornind de la alt fisier sau
pentru a vedea cum a facut altcineva.
Bineinteles, noi vom afisa fisierul nostru sursa, pentru a vedea ce contine. Metoda cea mai
simpla este de a da dublu-click pe numele sau in arborescenta din fereastra Essai1.mcw.
Faceti-o ! Iata deci, fisierul vostru de plecare deschis. Modificati dimensiunea ferestrei in asa
fel incit sa ocupe partea stinga a ecranului. Daca nu aveti loc, nu mai aveti nevoie de fereastra
proiectului, asa ca o puteti masca sau minimiza (nu trebuie sa o inchideti, deoarece inchideti
proiectul !).
Daca doriti ca ecranul vostru sa semene cu al meu, modificati fontul editorului. Pentru
aceasta, selectati Edit => Properties, apoi TAB-ul "Text". Dati apoi un click pe Select font
si alegeti "Courier New", stil "Regular", dimensiunea "9".
Pentru cei pe care-i intereseaza, eu lucrez pe un monitor de 22", cu rezolutia de 1280 x 1024,
asta ca sa va faceti o idee despre proportii. Verificati deasemenea ca dimensiunea pentru TAB
sa fie 4, iar la nevoie, modificati-o (se numeste « size » in MPLAB® > 7.5).
Fisierul vostru .asm este acum pe ecran. De notat, pentru cei ce ati lucrat cu MPLAB® 5 ca
functionarea TAB-ului este diferita in MPLAB® 5 si MPLAB® 6, ceea ce inseamna ca
aspectul paginii unui fisier scris in MPLAB® 5 nu va fi respectat in MPLAB® 6. Eu am
modificat toate fisierele in consecinta.
Pentru cei obisnuiti cu MPLAB® 5, veti constata ca in MPLAB® 6 este mai simplu de
lucrat, ceea ce va fi si mai vizibil atunci cind ne vom ocupa de simulator. Vom trece acum la
examinarea mai detaliata a acestui fisier.
28
Note:…
29
6. Organizarea unui fisier « .asm »
Mai intii, dati un click, nu conteaza unde, in interiorul fisierului. Va aflati in interiorul unui
simplu editor de text. In coltul din stinga-jos, veti vedea un numar de linie si de coloana.
Acestea indica pozitia curenta a cursorului. Ma voi folosi de aceasta pozitionare pentru a va
ghida in interiorul fisierului. Pentru moment, nu adaugati nici o linie, pentru a pastra
corespondenta corecta cu acest text. Daca nu vedeti numarul de linii, mergeti la meniul
Edit => Properties si bifati « line numbers » in TAB-ul « Editor ».
Daca nu reusiti sa faceti modificari in fisierul vostru, si tastatura dvs. pare inactiva (lucru
intilnit la versiunile MPLAB® 5.x), este pentru ca ati folosit un caracter extins in numele
fisierului vostru. Spre deosebire de MPLAB® 5, MPLAB® 6 lucreaza acum cu toate
caracterele extinse, atit pentru fisiere, cit si pentru directoare.
6.1 Comentariile
De la linia 1 la 31, vedeti un cadru mare. Daca va uitati atent, primul caracter din fiecare
linie este « ; ». Tot ceea ce urmeaza dupa acest caracter, este considerat zona de comentariu si
puteti scrie aici tot ce doriti.
6.2 Directivele
La linia 34 gasim o DIRECTIVA destinata lui MPASM® care ii indica ce tip de procesor
este folosit in acest program.
DIRECTIVELE nu fac parte din program, deci ele nu sint traduse in coduri de operatii
(OPCODE) ci servesc numai pentru a indica asamblorului cum sa lucreze. Acestea sint deci
COMENZI destinate asamblorului insusi.
30
Odata depasita zona de comentarii, veti vedea linii de forma :
Aceasta linie inseamna ca FSR este EGAL cu 0x04. Altfel spus, atunci cind veti folosi
FSR intr-o instructiune, MPASM® va interpreta FSR ca fiind 0x04, 0x04 fiind pur si simplu
adresa lui FSR in memoria PIC®-ului.
H’04’ este o alta metoda valida de a exprima un numar hexazecimal, la fel ca si 04h.
Daca luati tabelul 4-2 de la pagina 13, va veti lamuri. Acest fisier este deci destinat, in
principal, evitarii memorarii tuturor adreselor, un nume fiind mult mai simplu de retinut si
folosit. Puteti inchide acum fisierul P16F84.inc pentru a nu ocupa in mod inutil fereastra
programului.
Linia urmatoare incepe prin « __CONFIG ». Aceasta linie contine faimoasele "fuzibile"
care fixeaza functionarea PIC®-ului.
Valorile scrise aici vor fi integrate in fisierul « .hex » pentru a indica programatorului
(dispozitivului de programare) valorile ce vor fi scrise fizic la adresele specifice din PIC®.
Vom reveni.
Sa stiti deci ca daca un fisier « .hex » a fost creat de un programator atent, care a folosit
aceasta directiva, voi nu va mai trebui sa definiti acesti parametri in momentul programarii
fizice a circuitului. Softul programatorului va merge sa caute acesti parametri chiar in datele
din fisier.
Din nefericire, se gasesc multi care cred ca aceasta directiva, comentariile si alte facilitati
sint destinate numai incepatorilor, si nu le folosesc. Mare greseala ! Primesc cu regularitate
scrisori de la persoane care au considerat aceasta procedura inutila, si care mai apoi au intrat
in deruta totala.
cu linia :
Faceti-o ! Remarcati ca diferitele valori sint legate prin simbolul « & » (AND), explicat in
lectia despre sistemele de numeratie. El functioneaza deci prin punerea unor biti pe 0, daca va
mai amintiti. Deci, fiti atenti ca intotdeauna sa precizati TOTI parametrii, chiar si pe cei pe
care nu-i folositi.
Valorile corespunzatoare se gasesc din nou in fisierul « P16F84.INC ». Deci, nu este nici o
magie, totul se explica daca va ginditi putin.
31
6.5 Asignarile
La liniile 57 si 62, veti gasi ASIGNARI personale care functioneaza conform aceluiasi
principiu ca cele din fisierul « .inc ».
Un alt avantaj este acela ca daca inlocuiti valoarea unei asignari, modificarea se va face
automat in tot programul. Nu veti risca deci sa pierdeti valori pe drum.
Exemplu de asignare :
6.6 Definitiile
De exemplu, am putea folosi un PORT urmat de un numar de bit, sau la fel de bine o
instructiune cu parametrii sai.
O definitie este construita in felul urmator : directiva #DEFINE, urmata de numele pe care
dorim sa-l folosim, apoi sirul care va fi substituit. De exemplu :
Folosirea acestui macro se efectueaza foarte simplu folosind numele sau in program.
De exemplu :
va fi tradus de MPLAB® ca :
32
6.7 Macro-urile
LIREIN macro
comf PORTB , 0
andlw 1
endm
Un macro se compune dintr-un nume scris in prima coloana, urmat de directiva « macro ».
Portiunea de cod care constituie macro-ul incepe pe linia urmatoare. Sfirsitul unui macro este
definit prin directiva « endm » (end of macro).
comf PORTB , 0
andlw 1
Macro-ul simplifica deci numai scrierea, dar nu reduce dimensiunea fisierului .hex obtinut,
deoarece cele doua linii vor fi scrise in PIC®.
Notati faptul ca se pot folosi macro-uri mai complexe, cu transfer de parametrii, insa
pentru moment, nu vom intra in aceste functii speciale.
Deasemenea, retineti ca dispuneti de ajutor in meniul Help => MPASM Help. De fapt,
help-ul lui MPLAB® vizeaza doar folosirea softului. Ajutorul privind limbajul, se gaseste in
MPASM®, deoarece este vorba despre limbajul pe care-l foloseste MPLAB®.
Pentru a ne plasa variabilele noastre, care sint locatii de memorie carora li s-a dat un nume,
vom consulta din nou tabelul 4-2. Vedem ca zona RAM libera utilizabila incepe la adresa
0x0C. Zona noastra de variabile va contine deci directiva
De exemplu :
33
In continuare, va trebui sa precizati sfirsitul zonei curente cu ajutorul directivei :
6.9 Etichetele
In programe, in prima coloana veti gasi ceea ce noi numim ETICHETE. Acestea sint
denumiri alese de voi si constituie REPERE pentru program. Asamblorul le va inlocui cu
adresa din program unde se gasesc, scutindu-va pe voi de a face aceste calcule. Vom vedea
mai departe cum functioneaza acest principiu.
Directiva ORG, urmata de o adresa, precizeaza la ce adresa din PIC® vor fi puse
instructiunile care urmeaza. Este important sa retinem 2 lucruri :
• Dupa un RESET, sau o punere sub tensiune, PIC®-ul porneste intotdeauna de la adresa
0x00. Inceputul programului vostru va trebui deci sa se gaseasca la aceasta adresa.
• Adresa 0x04 este adresa folosita pentru intreruperi (vom vedea mai departe principiul de
functionare).
Nu va ramine deci prea mult spatiu pentru a va pune programul ! Vom incepe deci printr-
un salt catre locatia programului principal, unde vom avea mai mult loc. Sa ne uitam deci la
linia 103 si sa vedem cum functioneaza :
Prima linie este o DIRECTIVA care indica faptul ca linia urmatoare va fi plasata la adresa
0x00.
A doua linie este o INSTRUCTIUNE, (explicata la pagina 62 din datasheet), care indica
PIC®-ului ca programul trebuie sa "sara" la adresa « init » (« init » este o ETICHETA).
Deci dupa RESET, PIC®-ul executa instructiunea goto init care se gaseste la adresa 0x00,
urmata de instructiunea care se gaseste la adresa init (mai jos in program, imediat sub eticheta
init).
Toate instructiunile situate dupa directiva END vor fi pur si simplu ignorate.
34
Atentie, aceasta nu inseamna deloc ca PIC®-ul se va opri in locul unde se gaseste
directiva voastra « END », pentru simplul motiv ca nu este vorba despre o instructiune care sa
fie asamblata si inscrisa in PIC® ci de o directiva destinata asamblorului MPASM® insusi.
Tinind cont de cele de mai sus, daca scrieti un program, el va trebui sa se incheie ca mai
jos :
Instruction x
Instruction y
END
Deci daca doriti sa opriti programul dupa o singura executie, va trebui sa-l blocati intr-o
bucla. Il puteti bloca fara punere in stand-by :
boucle
goto boucle
boucle
sleep
goto boucle
Bucla este in acest caz o masura de securitate in cazul reactivarii PIC®-ului datorita unui
parazit sau al depasirii duratei watchdog-ului. Deci, nu uitati : END nu inseamna « oprire ».
35
7. Realizarea unui program
Inainte de a incepe primul nostru program, vom face citeva modificari in fisierul
« essai1.asm », in scopul de a pastra ceea ce ne intereseaza.
Mai intii, mergeti la linia 104 (numarul poate fi diferit la voi) si inlocuiti linia goto init prin
goto start. Nu faceti greseli de ortografie.
Coboriti mai departe la linia 225, unde veti gasi eticheta noastra start, deci unde va sari
programul nostru. Stergeti apoi linia :
Valoarea poate bineinteles sa fie inlocuita printr-o eticheta, MPASM® calculind in locul
vostru amplasarea sa. Datasheet-ul va arata deasemenea faptul ca este vorba de un salt
NECONDITIONAT, adica el se va efectua intotdeauna, fara conditii. Se reaminteste
deasemenea faptul ca un salt se efectueaza pe 2 cicluri.
Sa facem loc sub eticheta start si sa adaugam linia urmatoare (atentie, nu in prima
coloana !) :
Insa cum scopul instructiunii este de a pune variabila pe 0, bitul Z va fi intotdeauna 1 dupa
aceasta operatie. Retineti ca locatia de memorie se poate situa intre 0 si 127 (0x7F). Este
logic, conform tabelului 4-2, vedeti ca RAM-ul contine 127 de locatii pentru fiecare din cele
doua bancuri.
In continuare, puneti o eticheta (in coloana 1) pe care o veti numi boucle. Sub aceasta
eticheta adaugati instructiunea :
boucle
incf mavariable , f
36
Aceasta instructiune este tratata deasemenea in capitolul referitor la instructiuni. Veti
vedea ca aceasta instructiune incrementeaza (+1) continutul variabilei mavariable, si ca
rezultatul operatiei este pus in locatia d. Pentru toate instructiunile, d poate sa fie « f », in care
caz rezultatul este stocat in variabila respectiva, sau in « w », in care caz rezultatul este pus in
registrul de lucru, si variabila nu este modificata.
Observati deasemenea ca bitul de stare ‘Z’ este afectat de aceasta operatie. Va reamintesc
inca odata ca daca rezultatul operatiei da "0", Z va fi pus in "1". El va fi pus in "0" in toate
celelalte cazuri.
goto start
prin
goto boucle
start
clrf mavariable ; sterge mavariable
boucle
incf mavariable , f ; incrementeaza mavariable
goto boucle ; bucleaza
END ; directiva de sfirsit de program
Asamblarea unui proiect se poate face in doua moduri. Fie se asambleaza numai fisierul
selectat cu <F10>, fie se asambleaza toate fisierele din arborescenta apasind <CTRL> +
<F10>. Dat fiind faptul ca noi nu avem decit un singur fisier in arborescenta, vom folosi
<F10>.
Sa incercam acum sa asamblam acest program pentru a obtine un fisier .hex. Apasati tasta
« F10 », se deschid niste ferestre, si MPLAB® transfera comanda lui MPASM® care incearca
sa ne asambleze programul. Asamblarea se opreste, apare o bara rosie si o noua fereastra
(«output») care contine rezultatele de iesire ale comenzii. Daca intilniti o problema
neprevazuta (de exemplu, eroarea 173), aruncati un ochi la sfirsitul cursului, in anexe.
37
Ce s-a intimplat ? Ei bine, sa examinam raportul. Veti vedea mai intii mesaje de
atentionare (warning). Aceste sint mesaje destinate a va atrage atentia, dar care nu impiedica
efectuarea asamblarii. Nu uitati ca exista o portiune in program care nu foloseste la nimic in
exemplul nostru, dar care este totusi scrisa (deasupra etichetei start). Vom folosi aceasta parte
in capitolele urmatoare.
Sa reluam asamblarea, apasind <F10>. De data aceasta, totul decurge bine, indicatorul
ramine verde, iar dupa mesajele de atentionare, gasim mesajul :
Am construit primul nostru program si am invatat deja 3 din cele 35 de instructiuni pe care
le foloseste PIC®-ul. Cine a spus ca e complicat ?
Puteti lasa fereastra de iesire deschisa, puteti sa o inchideti sau sa o minimizati. Sa iesim
din program si sa confirmam cererile de salvare. In cursul editarii, salvati-va in mod regulat
fisierul sursa cu <CTRL> + <S>.
Mergeti acum in directorul de lucru, unde va trebui sa gasiti 7 fisiere generate de aplicatie
de forma « essai1.xxx ».
Remarcati existenta primului vostru fisier .hex. Fisierul, asa cum va trebui el sa arate la
sfirsitul acestui capitol, este disponibil in fisierele din exemple, ca toate celelalte care vor fi
create in continuare.
38
Note:…
39
8. Simularea unui program
In capitolul precedent am creat primul nostru programel pentru 16F84. Bineinteles ca acest
program nu foloseste la nimic, dar ne va fi de folos pentru experimentarea simulatorului din
MPLAB®. La sfirsitul acestui capitol, veti fi in masura
Incepeti deci prin lansarea lui MPLAB®. Acesta cunoaste deja numele ultimului dv.
proiect (essai1.mcp) si-l va deschide automat. Daca ati mai facut si alte incercari personale,
incarcati-le prin intermediul meniului « Project => Open ». Veti avea in fata fereastra din
ultima lectie.
Nu uitati ca daca aveti vreo problema, fisierul « essai1.asm » este disponibil in fisierele
exemplu furnizate impreuna cu acest curs. Deasemenea, reamintiti-va ca lansarea asamblarii
se face cu tasta <F10>.
Utilitatea unui simulator este de a vizualiza functionarea programului, deci va trebui sa-i
comunicam lui MPLAB® ceea ce dorim sa vedem. Porniti mai intii simulatorul din
MPLAB®.
Selectati meniul « Debugger => Select tool => MPLAB® Sim ». Asta-i totul. In bara de
unelte vor aparea unelte noi. Remarcati aparitia de noi elemente si in meniul « Debugger ».
Cei obisnuiti cu MPLAB® 5 vor constata inca odata ca procedura este mai simpla.
Daca n-ati facut-o deja, puneti fereastra cu fisierul sursa in stinga. Va trebui sa facem sa
apara pe ecran informatiile pe care dorim sa le urmarim. Selectati « View => Special
function registers ». Se va deschide o noua fereastra. Mariti-o si plasati-o in dreapta ferestrei
fisierului sursa (sau in alta parte daca nu aveti loc).
Vedeti in aceasta fereastra continutul tuturor registrelor pe care le-ati gasit in tabelul 4-2 de
la pagina 13. In capitolele urmatoare, le vom folosi progresiv, pe toate.
In programelul nostru, noi folosim o variabila; sa o facem deci sa apara. Mergeti in meniul
« View => Watch ». Va apare o noua fereastra. Dispuneti de 4 tab-uri in care puteti pune 4
serii de variabile, ceea ce este foarte practic pentru depanare.
• Dati dublu-click in caseta situata imediat sub « symbol name » si tastati numele variabilei.
Daca variabila nu exista, apare mentiunea « not found » in coloana « value », in caz
contrar, apare valoarea actuala a variabilei.
• Dati dublu-click in caseta situata imediat sub coloana « Add... » (adresa), si introduceti
manual adresa (in cazul de fata, 0x00E, deci veti introduce « E »).
40
• Selectati numele variabilei in fisierul sursa, dupa care faceti click pe selectia realizata si
trageti variabila catre fereastra « Watch », metinind apasat butonul mouse-ului. Ajunsi la
destinatie, eliberati butonul mouse-ului (« Drag and drop »).
41
Apasati apoi <OK> pentru a inchide fereastra.
Iata-va deci gata de a lansa o simulare. Dar la ce v-ar servi acest lucru, daca nu intelegeti
modificarile care se vor produce in registrele speciale ? Voi incepe deci prin a va explica
registrele de baza necesare la intelegerea procesului.
Principiul de baza este intotdeauna acelasi. In PIC®-uri, registrele neavind decit 8 biti,
adresa maxima pe care o pot stoca este 255. Vor fi necesare deci 2 registre pentru a accesa o
adresa. Din acest punct de vedere, PIC®-urile au o functionare putin diferita.
Vom gasi mai intii un registru care contine adresa inferioara a PC-ului, adica cei 8 biti mai
putin semnificativi. Acest registru este accesibil atit pentru citire, cit si pentru scriere. El se
numeste PCL (PC Low).
42
Registrul PC complet avind 13 biti, va trebui sa adaugam la PCL inca 5 biti suplimentari.
Exista doua cazuri posibile :
- In cazul unui salt, de exemplu, continutul registrului PC este incarcat direct cu cei
11 biti de destinatie continuti de instructiune in ea insasi. Cei 2 biti lipsa sint extrasi
din registrul PCLATH. Biti 3 si 4, care trebuie stabiliti de catre utilizator, sint pusi
direct in pozitiile 11 si 12 ai registrului PC pentru a completa adresa de destinatie.
Cum 16F84 nu gestioneaza decit 1Ki cuvint de memorie program, nu vom avea
nevoie de acest registru in cazul salturilor. Amintiti-va ca 16F84 nu gestioneaza
decit 10 din cei 13 biti ai registrului PC.
8.2.2 Registrul « W »
Acest registru este folosit de PIC®-uri pentru efectuarea tuturor tipurilor de calcule.
Amintiti-va ca destinatia unui rezultat (d) poate sa fie o locatie din RAM (f) sau registrul de
lucru (w). Acesta este deci, un registru fundamental.
Acesta este un registru al carui fiecare bit are o semnificatie speciala. El este folosit in
principal pentru toate operatiile care presupun testari. Este considerat deasemena un registru
fundamental. Descrierea sa se gaseste in tabelul de la pagina 15.
Iata care sint bitii ce intra in componenta acestui registru, incepind cu bitul 0 (b0), deci
bitul cel mai din dreapta (sau bitul cel mai putin semnificativ). Remarcati ca folosim termenul
LSB, uneori pentru byte-ul cel mai putin semnificativ, iar alteori pentru bitul cel mai putin
semnificativ. Este un alt abuz de limbaj, insa contextul permite foarte bine evitarea
confuziilor.
b7 b6 b5 b4 b3 b2 b1 b0
IRP RP 1 RP 0 TO PD Z DC C
43
b0 : C Carry (transport) Acest bit este de fapt al 9-lea bit al unei operatii. De exemplu,
daca la adunarea a 2 octeti se obtine o valoare >255 (0xFF),
acest bit va fi pus in 1.
b1 : DC Digit Carry Acest bit este folosit in principal, atunci cind se lucreaza cu
numere exprimate in BCD : el indica un transport de la bitul 3
catre bitul 4. Notatia BCD (Binary Coded Decimal) inseamna
ca dintr-un octet, fiecare quartet reprezinta un numar zecimal.
Nu vom folosi acest principiu aici.
b4: TO Time-Out Acest bit indica (daca este 0), ca punerea in functiune
urmeaza unei opriri provocata de depasirea temporizarii
WatchDog sau a unei puneri in StandBy. Distinctia este
facuta de catre bitul PD .
b5 : RP0
Register Bank Select 0 Indica in ce banc de RAM se lucreaza. 0 => bancul 0, iar 1 =>
bancul 1.
b6 : RP1
Register Bank Select 1 Permite selectia bancurilor de memorie 2 si 3. In 16F84 nu
exista aceste bancuri, deci acest bit va fi lasat pe 0 pentru a
garanta compatibilitatea ascendenta a programului
(portabilitatea).
In tabelul de mai jos, sint date adresele de memorie disponibile din memoria RAM, in
functie de valoarea bitilor b5 si b6.
Banc
b6 b5 Adrese RAM
selectat
0 0 0 00 h – 7F h
0 1 1 80 h – FF h
1 0 2 100 h – 17F h
1 1 3 180 h – 1FF h
44
8.3 Lansarea simularii
Si iata ca acum cunoastem 4 dintre registre, care sint suficiente pentru a incepe simularea
programului nostru.
Spre deosebire de MPLAB® 5, nu este necesar sa selectam explicit fereastra sursa pentru
comenzile simulatorului, ele fiind permanent activate, indiferent de fereastra selectata in
interiorul MPLAB®. Daca n-ati facut-o inca, apasati <F10> pentru asamblare, dupa care
apasati <F6>.
Linia
goto start ; Adresa 0: initializare
este acum indicata printr-o sageata verde. Sageata verde indica urmatoarea linie care va fi
executata de simulator. De fapt, ati realizat un RESET al programului dvs. In loc sa folositi
butoanele tastaturii (mai practic), puteti sa folositi noile unelte din bara de unelte, sau noile
item-uri din meniul « Debugger ». Corespondenta este urmatoarea :
<F6> : reset
<F7> : step into (avanseaza cu un pas in program)
<F8> : step over (idem, dar un apel de sub-program este executat ca un tot in loc de a
intra in interior).
- Deasupra liniei selectate, gasiti directiva « ORG 0x00 » care precizeaza ca linia
urmatoare este la adresa 0x00 : primul semn bun.
Apasati <F7>. Programul dvs. este acum pozitionat la linia care urmeaza etichetei « start ».
S-a efectuat deci un SALT, sau o ruptura de secventa.
start
clrf mavariable ; sterge mavariable
Amintiti-va ca in acest stadiu, instructiunea n-a fost inca executata. Registrul PCL va
deveni :
Deci MPASM® a calculat singur la ce locatie se gaseste eticheta « start ». Daca ati fi dorit
sa calculati adresa singuri ar fi trebuit sa numarati toate liniile precedente pentru a vedea unde
va aflati. In plus, la fiecare modificare de program, ar fi trebuit sa recalculati toate adresele.
Din fericire, MPASM® face toate aceste lucruri in locul vostru.
Apasati din nou <F7> pentru a executa aceasta instructiune : Sterge mavariable. Cum
"mavariable" era deja 0, nu se intimpla nimic la nivelul acesteia.
45
ATENTIE, REAMINTITI-VA ! La punerea sub tensiune, locatiile din memoria RAM ale
PIC®-ului contin valori aleatorii. MPLAB® nu poate sti care sint aceste valori. De aceea, el
pune implicit valoarea 0. Daca vreti ca o locatie din RAM-ul PIC®-ului sa contina efectiv
valoarea 0, SINTETI OBLIGATI SA O FACETI CHIAR VOI, in caz contrar, programul va
functiona corect pe simulator, dar nu si in circuitul real.
adica adresa urmatoare : fara salt, fara ruptura de secventa, deci functionare secventiala a
programului.
Zona incepe de la 0x0C. Variabila w_temp se gaseste deci la aceasta adresa si cuprinde un
octet. Status_temp va fi deci la 0x0D, iar mavariable la 0x0E. Este foarte simplu !
Sa apasam inca odata <F7>. Variabila « mavariable » va deveni « 0x01 », caci operatia de
incrementare a fost executata. Ati inteles deja, executia urmatoare va va retrimite la
instructiunea care urmeaza etichetei « boucle ». Distrati-va apasind de mai multe ori tasta
<F7> pentru a observa evolutia variabilei.
In acest stadiu, va veti intreba ce se va intimpla cind variabila voastra va atinge valoarea
0xFF ? Bineinteles, n-aveti decit sa apasati de inca 500 ori tasta <F7>. Va trebui deci sa
acceleram putin procesul.
Vom modifica valoarea lui « mavariable ». Pentru aceasta, nimic mai simplu, este suficient
sa dati dublu-click pe caseta « value » situata linga numele variabilei si sa scrieti o alta
valoare (in hexazecimal, pentru ca acesta este formatul pe care l-am ales).
Dati dublu-click si scrieti aici « FF », dupa care dati click in afara casetei pentru a valida
valoarea.
46
Sa examinam ce s-a intimplat :
Variabila « mavariable » a devenit 0. Este logic, caci 0xFF + 1 = 0x100, ori 0x100 necesita
9 biti (256). Se obtine deci 00 (si un transport de la bitul 8).
Bitii 2, 3 si 4 sint 1, ceilalti fiind 0. Bitii 3 si 4 sint activi pe 0. Ei fiind 1, sint deci inactivi.
Ramine bitul 2... Daca aruncam o privire pe tabelul 4-3, vedem ca este vorba de bitul Z
(care este 0). Este logic, caci rezultatul operatiei este 0.
In acest punct, va veti intreba de ce bitul C (Carry) nu a fost pus in 1. Bravo, caci puneti
intrebari corecte, din moment ce rezultatul ultimei incrementari este B’00000000’ si un
transport B’1’. Jos palaria pentru cei care s-au gindit sa verifice functionarea instructiunii
« incf » de la pagina 62 a datasheet-ului. De fapt, veti constata daca verificati ca singurul bit
afectat (modificat) de aceasta instructiune este bitul Z, nu si bitul C : « Status affected : Z ».
Ne vom indrepta atentia catre alte metode de executie a programului in simulator. Apasati
<F6> pentru a aduce PC la 0. In meniul « Debug => Run » veti gasi toate metodele posibile.
Incercati deci urmatoarele :
- Apasati <F9> pentru a lansa rapid programul, fara vizualizare. Apasati <F5> pentru
a-l opri acolo unde a ajuns.
- Meniul « Debugger => Animate » va permite o executie animata, mai lenta, dar
care va permite sa urmariti derularea programului.
din programul dvs. Puneti mouse-ul la inceputul liniei si faceti click-dreapta. Va apare un
meniu. Puteti pune puncte de oprire in programul dvs. Puteti deasemenea sa cereti
programului sa ruleze pina la un punct specificat (run to cursor). Fereastra « Trace » (sau din
meniu « View => Trace » ) va ofera informatii suplimentare despre rularea programului.
Meniul « Debugger » va permite si alte configuratii. Cititi Help-ul privind simulatorul pentru
informatii mai detaliate. Sub-meniul « Debugger => Stimulus Controler » va permite, de exemplu
sa stabiliti puncte de oprire conditionale.
Un dublu-click pe o linie pune deasemenea un punct de oprire pe care-l veti sesiza prin
simbolul « B » care va apare in stinga liniei. Un alt dublu-click, il va anula.
Puneti un punct de oprire in programul dvs. Apasind <F6> si apoi <F9>, programul va fi
executat pina la linia rosie, dupa care se va opri. Iata inca o metoda practica de a depana
programele lungi.
47
Cu toate acestea, exista o noua procedura, chiar si mai rapida : urmarirea programului.
Eliminati punctul de oprire, apasti <F6>, dupa care selectati « View => Simulator trace ».
Se va deschide o noua fereastra, cu mesajul « no items to display ». Apasati acum <F9> si
apoi imediat <F5>.
Mai exista o multime de optiuni privind depanarea unui program, pe care nu le pot explica
in totalitate in acest curs. Dvs. le veti descoperi una cite una in cursul experimentelor voastre.
Nu ezitati sa examinati cu atentie toate meniurile.
La ora actuala exista chiar mai putine optiuni decit in versiunea 5 a MPLAB®. Totusi, ma
gindesc ca acest lucru se datoreaza lipsei de maturitate a programului. Microchip® a adaugat
deja functii care existau in versiunea 5 si care nu existau in versiunea 6. Pentru nostalgicii lui
MPLAB® 5, vor mai trebui sa astepte putin pina cind toate lipsurile vor fi recuperate.
48
Note:…
49
9. Setul de instructiuni
Instructiunile sint detaliate incepind de la pagina 57 din datasheet. Poate parea inutila
reluarea lor aici, insa am optat pentru o explicare practica a acestor instructiuni. Am
considerat ca explicarea lor prin mici exemple concrete poate fi mai folositoare decit simpla
lor traducere din datasheet.
Voi trece deci la explicarea acestor instructiuni intr-o ordine care imi usureaza explicatiile,
incepind cu cele deja cunoscute, si avind ca scop final adunarea la un loc, in acelasi
document, a tuturor referintelor instructiunilor.
Amintiti-va ca instructiunea goto contine cei 11 biti ai locatiei de destinatie. Cei 2 biti
ramasi sint incarcati din registrul PCLATH. Amintiti-va modul in care este alcatuit registrul
PC in cazul salturilor. Nu se poate sari decit in interiorul unei aceleiasi pagini de 211, adica
2048 cuvinte.
Chestia asta n-are nici un fel de importanta pentru 16F84 care nu are decit un 1 Ki cuvint
de memorie program, dar va trebui luata in consideratie pentru PIC®-urile de capacitate mai
mare (16F876). Vom reveni asupra acestei probleme in partea a doua a cursului.
Sintaxa
goto eticheta
Exemplu :
Start
goto continuare ; programul sare la instructiunea care
; urmeaza dupa eticheta « continuare »
xxxxxxxx
continuare
xxxxxxxx ; instructiunea executata dupa salt: programul
; continua de aici
50
Remarcati ca puteti sari atit inainte cit si inapoi. Goto necesita 2 ciclii de ceas, ca dealtfel
toate salturile.
Sintaxa
incf f , d
Formula este deci (f) + 1 => (d). Parantezele inseamna « continutul lui… ». Sau, mai pe
romaneste : Continutul locatiei de memorie specificate este incrementat cu 1, iar rezultatul
este pus in locul precizat de « d », care poate fi locatia de memorie precizata de « f » sau
registrul de lucru W, « f » raminind in acest caz nemodificat.
Exemplu :
Sintaxa
51
Bitii afectati din registrul STATUS
Exemplu :
Aceasta instructiune incarca valoarea specificata (valoare literala, sau valoare imediata), in
registrul de lucru W.
Sintaxa
Exemplu :
Sintaxa
movf f , d ; (f) -> (d)
Inca odata, singurul bit afectat este Z (daca f devine 0, Z devine 1).
Exemplul 1 :
Pentru aceasta instructiune voi incerca sa fiu un pic mai explicit. Veti intelege de ce.
ATENTIE !
Este neaparat necesar aici sa facem distinctie intre movlw k si movf f,w.
52
In primul caz, este incarcata VALOAREA in W, iar in al doilea, CONTINUTUL locatiei
specificate. Sa ne amintim din lectia precedenta ca mavariable reprezenta adresa 0x0E din
RAM. Sa presupunem acum ca aceasta locatie (adica mavariable) contine 0x50.
movlw mavariable
movlw 0x0E
Dupa executia instructiunii, in registrul W vom avea 0x0E. Vom vorbi in acest caz de o
ADRESARE IMEDIATA. Evolutia instructiunii este de tipul f => (d) (atentie, f nu este intre
paranteze).
movf mavariable , w
asamblorul o va traduce deasemenea, insa va inlocui "mavariable" prin valoarea sa. Vom avea
deci :
movf 0x0E , w
Exemplul 2 :
movf mavariable , f
Ce face aceasta instructiune ? Daca ati urmarit totul, ea pune CONTINUTUL lui
"mavariable" in "mavariable". A spune ca asta nu serveste la nimic este tentant, dar este
prematur.
De fapt, chiar daca continutul lui "mavariable" ramine neschimbat, bitul Z poate fi pus in
1. Aceasta instructiune permite deci sa se verifice daca (mavariable) = 0.
Sintaxa
Niciunul.
53
Exemplu :
Aceasta operatie permite adaugarea unei valori literale (adresare imediata) la continutul
registrului de lucru W.
Sintaxa
Nu va preocupati prea tare de flag-ul DC, el nu se foloseste decit pentru operatiile asupra
quartetilor, de exemplu in cazul numerelor BCD (Binary Coded Decimal).
Exemplu :
Sintaxa
C, DC, si Z
Exemplu :
movlw 12 ; incarca 12 in W
movwf mavariable ; mavariable devine acum 12
movlw 25 ; incarca 25 in W
addwf mavariable , f ; rezultat : (W) + (mavariable), deci
; 25 + 12, rezultat = 37 salvat in
; mavariable (,f).
54
9.9 Instructiunea « SUBLW » (SUBstract W from Literal)
Sintaxa
C, DC, Z
Notati aici ca bitul C functioneaza invers fata de adunare. Acest lucru este comun pentru
cea mai mare parte a procesoarelor de pe piata. Celelalte folosesc uneori un bit special pentru
scadere, bit cunoscut cel mai adesea sub numele de « borrow » (imprumut).
Daca rezultatul este pozitiv, deci fara depasire, C = 1. Daca apare o depasire, C devine 0.
Acest lucru este logic, si se explica efectuind manual o scadere. Bitul C reprezinta al 9-lea
bit adaugat din oficiu valorii initiale. Daca se face o scadere manuala care da o valoare < 0, se
obtine deci o valoare finala pe 8 biti, transportul obtinut urmind sa se scada din bitul C. Daca
rezultatul este > 0, nu exista transport, rezultatul final fiind deci pe 9 biti.
Exemplul 1 :
C b7 b6 b5 b4 b3 b2 b1 b0 Dec
1 0 0 0 0 0 0 1 010 2
- 0 0 0 0 0 0 0 01 1 1
= 1 0 0 0 0 0 0 0 1 1
Cum procedam ? Ei bine, la fel ca pentru o scadere zecimala. Incepem cu bitii din dreapta :
0 – 1, nu se poate, deci imprumutam 10 (notat ca exponent cu verde), si evident vom scadea o
unitate suplimentara din bitul b1. Avem deci : B ‘10’ – B’1’, caci nu uitati ca ati imprumutat
10 in BINAR. Rezultat = 1. Scadem in continuare bitii b1 : vom avea 1 – 0 – 1 impumutat,
deci 1 – 0 – 1 = 0. Se continua de la dreapta la stinga pina la al 9-lea bit, care este Carry.
Rezultat final : B’00000001’ si carry = 1.
55
Exemplul 2 :
C b7 b6 b5 b4 b3 b2 b1 b0 Dec
1 0 0 0 0 0 0 1 0 2
- 0 0 0 0 0 0 0 1 0 2
= 1 0 0 0 0 0 0 0 0 0
Exemplul 3 :
C b7 b6 b5 b4 b3 b2 b1 b0 Dec
1 010 010 010 010 010 010 110 010 2
- 01 01 01 01 01 01 01 11 1 3
= 0 1 1 1 1 1 1 1 1 -1
Cum acesta este 0, dvs. sinteti AVERTIZAT ca rezultatul operatiei este negativ. Sau, cum
stim valoarea absoluta a unui numar negativ ? Facind complementul fata de 2. Amintiti-va :
- Complementul fata de 1 al lui B’11111111’ = B’00000000’ (se inverseaza toti bitii)
- Complementul fata de 2 = complementul fata de 1 + 1, deci B’00000001’.
-
Deci, complementul fata de 2 al lui 0xFF este –0x01.
Pentru verificare, daca adaugam 1 la –1, veti obtine B’11111111’ +
B’00000001’ = B’00000000’ = 0.
Dvs. va trebui sa stapiniti foarte bine scaderile. Unii, fara indoiala ca vor gindi ca am
explicat mult prea in detaliu, dar experienta m-a invatat ca scaderile reprezinta adesea o
capcana in realizarea multor programe.
Si inca un detaliu : pentru a scadea 1, puteti la fel de bine sa adunati -1. Rezultatul va fi
exact acelasi, raminindu-va voua sarcina de a interpreta biti Z si C. Va las sa incercati singuri
pentru a va convinge.
56
9.10 Instructiunea « SUBWF » (SUBstract W from F)
Raminem tot la scaderi, insa, de aceasta data, in locul unei adresari immediate, vom avea o
ADRESARE DIRECTA.
Sintaxa
C , DC , Z
Exemplu :
Sintaxa
Exemplu :
b7 b6 b5 b4 b3 b2 b1 b0
1 1 0 0 1 1 0 1
AND 1 1 1 1 0 0 0 0
= 1 1 0 0 0 0 0 0
57
9.12 Instructiunea « ANDWF » (AND W with F)
Presupun ca pina aici ati inteles. Din nou, aceeasi operatie, dar in ADRESARE DIRECTA.
Am sa accelerez un pic explicatiile.
Sintaxa
Exemplu :
Si acum, aceleasi instructiuni, dar pentru functia SAU inclusiv. Inclusiv inseamna contrarul
lui exclusiv, adica bitul rezultat va fi 1 daca unul dintre biti, SAU AMBII OPERANZI sint 1.
Sintaxa
Exemplu :
b7 b6 b5 b4 b3 b2 b1 b0
1 1 0 0 0 0 1 1
OR 0 0 0 0 1 1 1 1
= 1 1 0 0 1 1 1 1
Deci, cu un SAU inclusiv (OR), se poate FORTA oricare bit in 1 (spre deosebire de SI,
care poate forta oricare bit in 0).
58
9.14 Instructiunea « IORWF » (Inclusive OR W with File)
Sintaxa
Spre deosebire de SAU inclusiv, iata acum pe SAU EXCLUSIV. Tabela sa de adevar este
aceeasi ca la SAU inclusiv, cu exceptia faptului ca daca 2 biti sint in 1, rezultatul va fi 0.
Aceasta instructiune serveste deci la INVERSAREA oricarui bit dintr-un octet. Ca efect,
daca faceti « 1 XOR 0 », veti obtine 1, iar daca faceti « 0 XOR 0 », veti obtine 0.
Deci, daca aplicati XOR 0, valoarea initiala ramine neschimbata.
Daca din contra, aplicati « 0 XOR 1 », veti obtine 1, iar cu « 1 XOR 1 », veti obtine 0.
Aplicind XOR 1, veti inversa bitul, indiferent de starea sa initiala.
Acum puteti deci sa FORTATI un bit in 1 cu SAU, sa MASCATI un bit (sa-l puneti in 0)
cu SI, si sa-l INVERSATI cu XOR.
Sintaxa
Exemplu :
b7 b6 B5 B4 b3 b2 b1 b0
1 1 0 0 0 1 0 1
XOR 0 0 0 0 1 1 1 1
= 1 1 0 0 1 0 1 0
Remarcati ca toti bitii octetului initial au fost inversati in pozitia in care fiecare bit din al
doilea operand era 1.
59
9.16 Instructiunea « XORWF » (eXclusive OR W with F)
Sintaxa
Este o instructiune care permite intr-un mod foarte simplu de a FORTA direct un bit
dintr-o locatie de memorie, in 1.
Sintaxa
Niciunul
Exemplu :
Este o instructiune care permite intr-un mod foarte simplu de a FORTA direct un bit
dintr-o locatie de memorie, in 0.
Sintaxa
Niciunul.
Exemplu :
60
9.19 Instructiunea « RLF » ( Rotate Left through Carry)
Sintaxa
Exemplul 1 :
C b7 b6 b5 b4 b3 b2 b1 b0
F 1 0 0 0 1 0 1 1 1
RLF 0 0 0 1 0 1 1 1 1
Observati ca toti bitii au fost decalati catre stinga. « C » a fost reintrodus in b0. Rezultatul
este pe 9 biti.
Exemplul 2 :
Daca ati inteles, rezultatul va fi B’00101110’, cu Carry = 0. Daca Carry era initial 0, s-a
efectuat o simpla deplasare catre stinga. Ce se intimpla, in zecimal, cind se efectueaza o astfel
de operatie ?
Sa luam numarul 125 si decalindu-l in stinga in zecimal, obtinem 1250. Am inmultit de
fapt numarul cu baza sa (in zecimal = 10). Ei bine, acelasi lucru se intimpla si in binar.
Sa luam B’00010111 ‘, adica 23 in zecimal. Decalindu-l, obtinem B’00101110', adica 46.
Am efectuat deci o inmultire cu 2. Retineti acest lucru, caci va fi foarte folositor mai departe.
61
9.20 Instructiunea « RRF » ( Rotate Right through Carry)
Sintaxa
Exemplul 1 :
b7 b6 b5 b4 b3 b2 b1 b0 C
F 0 0 0 1 0 1 1 1 1
RRF 1 0 0 0 1 0 1 1 1
Observati ca toti bitii au fost decalati catre dreapta. « C » a fost reintrodus in b7. Rezultatul
este pe 9 biti.
Exemplul 2 :
Daca ati inteles, rezultatul va fi B’00001011’, cu Carry = 1. Daca initial Carry este 0, se
efectueaza o simpla deplasare spre dreapta.
Ce s-a intimplat ? Numarul nostru initial, adica 23 in zecimal a devenit 11. Carry
reprezinta bitul « -1 », deci, jumatate din bitul 0, deci ½. De fapt, si in zecimal, cifra « 1 »
dupa cifra unitatilor are valoarea 1/baza, adica 1/10. In binar va fi deci ½..
Daca privim din nou cei 9 biti, obtinem 11 ½.. Am efectuat deci O IMPARTIRE LA 2.
Retineti acest lucru, caci va va fi deasemena foarte util in continuare.
Tradus ad-literam ar insemna : testeaza bitul din locatia de memorie si fa un salt daca
acesta este 0. Este vorba aici de primul SALT CONDITIONAT, sau RUPTURA DE
SECVENTA SINCRONA CONDITIONATA.
62
Altfel spus, nu vom avea salt decit daca conditia este indeplinita.
Retineti ca in acest caz instructiunea va necesita 2 cicluri, in caz contrar nefolosind decit
un singur ciclu. In plus, mai trebuie sa retinem ca pentru toate aceste tipuri de salturi, NU SE
SARE DECIT LA INSTRUCTIUNEA URMATOARE, Cu alte cuvinte, sintaxa nu contine
adresa de salt, dupa cum vom vedea.
Sintaxa
Niciunul.
Exemplul 1 :
Iata un exemplu in care trebuie sa se execute o singura instructiune suplimentara daca bitul
testat este 1.
Exemplul 2 :
Ce facem daca tratarea necesita mai multe instructiuni ? Ei bine, se combina salturile
conditionate cu cele neconditionate (de exemplu goto).
Aceste proceduri sint identice pentru toate salturile conditionate, deci nu le voi mai detalia
cu alte explicatii.
Tradus ad-literam spune : testeaza bitul din locatia de memorie si sari daca are valoarea 1.
Toate remarcile facute pentru instructiunea « BTFSC » ramin valabile.
63
Sintaxa
Niciunul.
Exemplu :
Sintaxa
Niciunul.
Exemplul 1 :
movlw 3 ; incarca 3 in w
movwf contor ; initializeaza contor
movlw 0x5 ; incarca 5 in w
boucle ; eticheta
addwf mavariable , f ; aduna 5 la mavariable
decfsz compteur , f ; decrementeaza contorul si
; testeaza-i valoarea
goto boucle ; daca contorul nu e 0, bucleaza
movf mavariable , w ; incarca valoarea obtinuta in w
64
ATENTIE !
• Daca puneti 0 in contorul de bucla, bucla va fi parcursa de 256 ori. Daca nu doriti ca
bucla sa fie parcursa in acest caz, va trebui sa adaugati un test INAINTE de executarea
primei bucle, ca in exemplul urmator :
Exemplul 2 :
Nu vreau sa detaliez aceasta instructiune caci ea este perfect identica cu cea precedenta, in
afara de faptul ca variabila este incrementata in loc sa fie decrementata.
Sintaxa
Niciunul.
Sintaxa
Niciunul : aceasta particularitate ne va fi foarte utila atunci cind vom vorbi despre
65
intreruperi.
Exemplu :
Punctul de plecare este memorat automat in asa fel incit, dupa executarea sub-programului,
programul continua din locul unde ramasese anterior. Acest lucru pare cam dificil, dar este
foarte simplu. Urmariti exemplele din descrierea instructiunii RETURN.
Sintaxa
Mecanism :
Notati ca daca sub-programul (sau subrutina) se apeleaza pe ea insasi sau un alt sub-
program, adresa va fi salvata deasemenea in virful stivei.
Atentie, aceasta stiva are o marime limitata, de 8 locatii. Nu exista nici un test de stiva,
deci dvs. va trebui sa va gestionati sub-programele in asa fel incit sa nu depasiti 8 locatii, in
caz contrar, programul vostru se va bloca.
Retineti ca atunci cind iesiti dintr-un sub-program, locatia din stiva se elibereaza. Deci
limita nu consta in numarul de ori in care apelati o subrutina, ci in numarul de nivele de
imbricare (adica o subrutina care apeleaza o alta subrutina, care apeleaza o alta subrutina, etc.)
Niciunul.
66
Sintaxa
Niciunul.
Exemple :
Cum acesta este un concept foarte important, am sa detaliez un pic mai mult.
Sa presupunem ca un program are nevoie de o mica temporizare (cum fiecare instructiune
"consuma" timp, se poate aranja in asa fel incit sa se piarda timp intentionat, in scopul
intirzierii rularii). Sa scriem :
Sa ne imaginam acum ca aceasta mica temporizare este apelata in mod regulat de catre
programul principal. Sa scriem cam cum va arata programul principal :
Primul lucru care ne vine in minte, este sa copiem temporizarea in locurile necesare. Se
obtine deci un program ca acesta :
67
boucle2
decfsz contor , f ; decrementare contor, salt daca e 0
goto boucle2 ; bucla daca nu e 0
xxx ; instructiune oarecare
xxx ; instructiune oarecare
movlw 0xCA ; valoare contor
movwf contor ; initializare contor de bucla
boucle3
decfsz contor , f ; decrementare contor, salt daca e 0
goto boucle3 ; bucla daca nu e 0
xxx ; instructiune oarecare
Nu este prea elegant, caci daca dorim sa modificam valoarea temporizarii, va trebui sa o
modificam peste tot in program, fara sa uitam nici un loc. In plus, se ocupa o multime de
spatiu in memorie.
Se poate spune : « sa folosim un macro », care, daca va amintiti, efectueaza o substitutie in
momentul asamblarii. Este adevarat, caci atunci nu va trebui sa facem modificari decit intr-un
singur loc, dar, in PIC®-ul nostru, codul se va regasi de atitea ori de cite ori vom folosi
temporizarea. Se pierde o gramada de spatiu, mai ales daca portiunea de cod este mare si este
folosita de multe ori.
Ca sa rezolvam aceasta problema, vom folosi tehnica sub-programelor. In prima etapa, sa
modificam putin temporizarea, pentru a o transforma intr-o subrutina.
In a doua etapa, vom modifica programul principal pentru ca de fiecare data cind avem
nevoie de o temporizare, sa apeleze subrutina. Vom obtine :
In acest caz, subrutina "tempo" nu apare decit o singura data in memoria program.
68
tempo ; eticheta de inceput a subrutinei
movwf contor ; initializare contor de bucla
boucle
decfsz contor , f ; decrementare contor, salt daca e 0
goto boucle ; bucla daca nu e 0
return ; sfirsitul subrutinei
Intoarcere din subrutina cu valoare literala in registrul W. Este o instructiune foarte simpla.
Ea este echivalenta cu o instructiune RETURN, dar permite iesirea dintr-o subrutina cu o
valoare specificata in W.
Sintaxa
Niciunul.
Exemplu :
Programul care a apelat subrutina cunoaste deci rezultatul operatiei, citind registrul « W ».
69
9.29 Instructiunea « RETFIE » (RETurn From IntErrupt)
Aceasta instructiune indica o intoarcere din intrerupere (vom vedea ulterior ce sint
intreruperile). Aceasta instructiune lucreaza la fel ca instructiunea RETURN, cu diferenta ca
intreruperile sint repuse automat in functiune in momentul revenirii in programul principal.
Sintaxa
Niciunul.
Sintaxa
clrf f ; (f) = 0
Exemplu :
Sintaxa
clrw ; (w) = 0
Reseteaza ciinele de paza (watchdog) al programului vostru. Vom aborda ulterior folosirea
facilitatii watchdog. Retineti doar ca acesta este un mecanism foarte practic care permite un
RESET automat al PIC®-ului in cazul unei blocari a programului (provocata de paraziti, de
70
exemplu).
Mecanismul este usor de inteles : se cere programului dvs. sa trimita aceasta instructiune la
intervale regulate. Daca comanda n-a fost primita intr-un interval de timp dat, PIC®-ul va
reporni de la adresa 0x00. Este exact acelasi mecanism ca cel folosit de mecanicii de
locomotiva care trebuie sa apese un buton la intervale regulate. Daca butonul n-a fost apasat,
trenul se opreste. Se determina in acest mod daca mecanicul este tot timpul in starea de atentie
necesara.
Sintaxa
Niciunul.
Efectueaza complementarea unei locatii de memorie specificate, adica inverseaza toti bitii
octetului din aceasta locatie.
Sintaxa
Exemplu :
Pune PIC®-ul in mod Standby. El nu va reveni decit in anumite conditii pe care le vom
vedea mai tirziu.
Sintaxa
71
Bitii afectati din registrul STATUS
T0, PD
Cum probabil sinteti deja obositi, si eu deasemenea, va prezint instructiunea care nu face
nimic si care nu modifica nimic. Am putea crede ca ea nu serveste la nimic. De fapt ea este
folosita mai ales pentru a consuma timp, de exemplu pentru a astepta una sau doua
instructiuni, pina se face o achizitie, de exemplu. Se foloseste doar ocazional.
Sintaxa
Ramin 2 instructiuni care erau folosite in versiunile precedente de PIC®-uri. Ele sint inca
recunoscute de catre PIC16F84, dar folosirea lor nu mai este recomandata de catre
Microchip®. Ca urmare, compatibilitatea viitoare a acestora nu este garantata.
Aceste instructiuni nu mai sint necesare in momentul de fata, caci aceste registre sint acum
direct accesibile prin intermediul unor instructiuni clasice.
Va sfatuiesc deci imperios sa nu folositi aceste instructiuni, caci puteti avea probleme cu
viitoarele versiuni de PIC®-uri.
72
10. Modurile de adresare
Prin ADRESARE IMEDIATA sau ADRESARE LITERALA, puteti spune : eu pun 100 lei
in buzunar. Valoarea face parte IMEDIAT parte din fraza. Eu am specificat LITERALmente
valoarea la care ma refer. Nu mai este nevoie de nici o informatie suplimentara.
Exemplu :
Exemplu :
Va trebui deci sa-i cereti acestui functionar numarul sertarului pe care va trebui sa-l
deschideti pentru a va lua banii. Nu vom pune deci in buzunar, nici numarul ghiseului, nici
numarul sertarului pe care vi-l va spune functionarul. Exista deci 2 operatii prealabile inainte
de a obtine suma pe care o veti pune in buzunar.
Acest tip de adresare necesita 2 registre, dintre care unul este mai ciudat, caci nu exista cu
adevarat. Deci, sa le examinam...
73
10.3.1 Registrele FSR si INDF
INDF inseamna INDirect File. L-ati vazut ? Ei da, este faimosul registru de la adresa 0x00.
Acest registru nu exista cu adevarat, el nefiind altceva decit un procedeu special de acces la
FSR de catre PIC®, din considerente de constructie electronica interna.
Registrul FSR este la adresa 0x04 in ambele bancuri. Nu va fi deci necesar ca sa schimbati
bancul pentru a-l accesa, indiferent care este bancul pe care-l folositi in momentul respectiv.
Mai intii, va trebui sa scriem adresa vizata in registrul FSR. Apoi, vom ajunge la aceasta
adresa indicata de registrul INDF.
Se poate spune deci ca INDF este de fapt registrul FSR folosit pentru a ajunge la locatia de
memorie. Deci, cind vrem sa modificam locatia de memorie indicata, se modifica FSR, iar
cind vrem sa cunoastem adresa locatiei indicate, se apeleaza tot la FSR. Daca dorim sa
accesam CONTINUTUL locatiei indicate, o accesam via INDF. Vom lamuri toate acestea
printr-un mic exemplu, dar mai inainte,
ATENTIE !
Continutul registrului FSR indica o adresa pe 8 biti, ori, in unele PIC®-uri, zona RAM
contine 4 bancuri (16F876). Adresa completa este deci o adresa pe 9 biti.
Adresa completa este obtinuta, la adresarea DIRECTA, prin adaugarea bitilor 7 si 8 sub
forma de RP0 si RP1 (RP1 nu se foloseste pentru 16F84 caci nu avem decit doua bancuri) si
prin adaugarea bitului IRP in cazul adresarii INDIRECTE (nefolosit la 16F84). Aveti grija
deci sa lasati intotdeauna bitii IRP (din registrul STATUS) si RP1 pe 0, pentru a asigura
portabilitatea programului dvs.
Exemplu :
74
10.4 Citeva exemple
movlw mavariable
movf mavariable , w
De aceasta data, este o adresare DIRECTA, deci, se merge la adresa lui mavariable ca sa
vedem ce contine in interior. Se gaseste CONTINUTUL lui mavariable, deci (w) = 0x50 (in
exemplul nostru). Pentru asamblor, mavariable va fi inlocuita prin 0x0E, deci movf 0x0E,w
movf INDF , w
Acum, avem o adresare INDIRECTA. Acest mod de adresare se recunoste imediat prin
folosirea registrului INDF. PIC®-ul se va uita in registrul FSR si va citi adresa continuta, in
acest caz 0x0E. El va merge mai departe la locatia vizata, careia ii citeste CONTINUTUL.
Deci, in (W) vom avea continutul locatiei 0x0E, adica 0x50.
movf FSR , w
Aici este o capcana. Este de fapt o adresare DIRECTA. Se pune deci in (W)
CONTINUTUL registrului FSR, adica 0x0E (adresa indicata) va fi pus in (W).
Voi incheia cu un cuvintel pentru specialistii in microprocesoare care citesc acest curs doar
pentru a se mentine in forma.
Ati constatat deja ca nu pomenesc nimic de modurile de adresare de tip indexat (pre si
post, cu sau fara offset). Aceasta deoarece pur si simplu acest mod de adesare nu exista in
PIC®-urile de tip 16F...
75
Note:…
76
11. Realizarea unui program incorporat
Vom folosi o placuta de test cu gauri dispuse pe mai multe rinduri. Legaturile le vom face
cu fire de conexiune. Veti putea gasi astfel de placute in orice magazin de componente
electronice. Pentru a pune in practica lectiile urmatoare, vom avea nevoie de urmatoarele
materiale (recuperabile si ieftine) :
Daca nu dispuneti de un alimentator de +5V, puteti fie sa folositi o baterie plata de 4.5V,
fie sa realizati un mic alimentator a carui schema este data mai jos. In acest caz, va vor mai
trebui :
Toate acestea impreuna, nu va vor ruina. In plus, componentele vor putea fi recuperate
pentru aplicatiile voastre viitoare. Veti vedea ca PIC®-urile pot fi folosite in mii de aplicatii
cotidiene. Bineinteles, nu omiteti sa va construiti un programator pentru PIC®. Consultati
anexa A1.8 pentru mai multe detalii.
Odata terminat montajul, mai verificati odata, fara sa introduceti PIC®-ul in soclu, si
77
cuplati alimentarea. Verificati daca tensiunile la pinii de alimentare ai PIC®-ului sint corecte.
Acum totul e gata pentru inceperea experimentelor.
In momentul de fata stiti sa realizati un proiect in MPLAB®. Creati deci proiectul LED_cli
in directorul de lucru (Project => New project). Daca aveti pe ecran o fereastra « untitled »,
inchideti-o.
Nu uitati, bineinteles, sa deschideti fisierul « LED_cli.asm ». Acest fisier este deasemenea
disponibil in anexa.
78
11.4 Editarea fisierului sursa
Completati antetul fisierului dupa cum doriti. Eu va dau mai jos un exemplu. Obisnuiti-va
ca intotdeauna sa va documentati programele. Nu este un lux, este o cerinta imperativa pentru
o intretinere eficace delungul timpului.
;******************************************************************
; PROGRAM DE CLIPIRE A UNUI LED CONECTAT LA PORTUL A.2 AL *
; UNUI PIC16F84. PROGRAM DE TEST PRIVIND FUNCTIONAREA *
; PIC-URILOR *
;******************************************************************
; NUME: LED-CLI *
; Data: 09/02/2001 *
; Versiune: 1.0 *
; Circuit: Placa de test *
; Autor: Bigonoff *
;******************************************************************
; *
; Fisier necesar: P16F84.inc *
; *
;******************************************************************
; *
; Note: Acest mic program face sa clipeasca un LED pe portul *
; A.2 cu o frecventa de 1 Hz. *
; Acest program face parte din lectia 6 a cursului. *
; *
;******************************************************************
Am inclus comentariile in fisier pentru a putea face modificarile mai rapid, fara sa fie
nevoie sa apelam la datasheet. As vrea sa va dau citeva explicatii scurte.
Remarcati ca efectuam un AND (&) intre diferitele valori, deci nivelele active sint pe 0,
deoarece un « AND » nu permite de a forta decit « 0 ». Va trebui deci sa precizati TOATE
valorile, chiar daca nu le folositi.
79
Primul parametru precizeaza daca PIC®-ul vostru va fi sau nu protejat la citire. Lasati
acest parametru pe « CP_OFF » = neprotejat.
Al doilea parametru precizeaza daca watchdog-ul este activ sau nu. Pentru moment,
inlocuiti WDT_ON cu WDT_OFF pentru a-l dezactiva.
In final, urmeaza definirea oscilatorului pe care-l veti folosi. Tabelul 8-1 de la pagina 40 va
da valorile recomandate in functie de frecventele folosite pentru un PIC® de 10 MHz.
Retineti ca valoarea _HS_OSC este indicata pentru frecvente ridicate, cum e si cazul
nostru, deoarece folosim viteza maxima a PIC®-ului. Daca ati cumparat o versiune de 10 sau
20 MHz, nu va panicati, modul HS_OSC va functiona la fel de bine.
Este important sa nu folositi modul _RC_OSC atunci cind folositi un cuart. Acest
parametru este folosit pentru functionarea cu retele R/C, ca cea desenata in figura 8-7 de la
pagina 41.
Chiar daca in practica PIC®-urile sint componente destul de fiabile, evitati greselile la
acest nivel. Acum, cunoscind perfect parametrii _CONFIG, va trebui sa aveti linia urmatoare :
Daca priviti tabelul 4-2, veti constata ca acest registru se gaseste la adresa 0x81, deci in
bancul 1. In fisierele include ale MPLAB®, acest registru este gasit sub numele de
OPTION_REG.
Acesta este deci numele pe care va trebui sa-l folositi. Vom detalia acest registru in aceasta
sectiune. Acest registru este un registru de biti, adica fiecare bit are un rol bine definit.
Tabelul de la pagina 16 va prezinta continutul acestui registru :
b7 : RBPU
Cind acest bit este pus pe 0 (activ pe nivel 0 = underline), pe fiecare pin al PORTB este
conectata o rezistenta catre +5V (pull-up resistor). Vom vedea in aceasta lectie functionarea
portului B. Daca priviti schema noastra de test, veti vedea ca butonul conectat la RB2, pune
acest pin la masa atunci cind e apasat.
In schema noastra, cind butonul nu e apasat, pe RB2 avem un nivel ambiguu ; validind
aceasta optiune, prin rezistenta interna, pinul RB2 este pus la +5V si va avea deci nivelul
logic 1.
80
Retineti ca aceasta optiune valideaza rezistentele pe TOTI pinii portului B. Nu poate fi deci
selectata numai o anumita rezistenta. Deasemenea, aceasta functie nu exista decit pentru
portul B
Probabil ca ati constatat deja, ca nu toate porturile sint identice : va trebui sa ne gindim la
acest lucru in momentul conceperii unui circuit. In cazul nostru, vom activa aceste rezistente
punind b7 = 0.
b6 : INTEDG
Acest bit precizeaza, in cazul folosirii intreruperilor pe RB0, frontul care declanseaza
intreruperea. Daca b6 = 1, intreruperea pe RB0 se declanseaza pe frontul 0 => 1. Daca b6 = 0,
intreruperea se va declansa pe frontul 1 => 0.
b5 : TOCS
Acest bit determina functionarea timer-ului 0, pe care-l vom vedea in curind. Retineti ca
timer-ul 0 este incrementat fie de catre ceasul intern (sincronizat prin program), (pentru b5 =
0), fie el numara impulsurile primite prin pinul RA4, (pentru b5 = 1).
Cum acest din urma mod necesita un circuit de generare a impulsurilor externe, noi vom
folosi pentru timer-ul 0 ceasul intern, deci b5 = 0.
b4 : TOSE
Specifica, pentru cazul in care bitul 5 = 1, frontul care determina incrementarea timer-ului
0. Daca b4 = 1, se face incrementarea pe frontul 5V => 0V pe RA4, iar daca b4 = 0, invers.
b3 : PSA
Pentru moment e suficient sa stiti ca acest predivizor poate efectua una din doua functii :
efectueaza o pre-divizare fie la nivelul timer-ului watchdog (pentru b3 = 1), fie la nivelul
timer-ului tmr0 (pentru b3 = 0). In cazul nostru, puneti b3 = 1 (vom vedea mai departe de ce).
Acesti 3 biti determina valoarea predivizarii registrului de mai sus. Vom avea deci 8 valori
posibile, valori date in tabelul de la pagina 16.
Observati ca valorile sint diferite pentru watchdog fata de tmr0. Nu exista o "divizare prin
1" pentru tmr0.
81
pentru watchdog avem predivizare 1:1, adica nici un fel de divizare. Noi vom pune deci b2 =
b1 = b0 = 0.
Iata inca un registru descris. Noi deci vom folosi in program valoarea B’00001000’, adica
0x08. Eu de obicei nu folosesc valori fixe in programele mele, in scopul usurarii intretinerii
acestora. Eu pun aceste valori la inceputul programului folosind asignari sau definitii.
Asignarea este deja creata mai jos in program. Am creat o CONSTANTA pe care am
denumit-o OPTIONVAL si care va contine valoarea pe care o voi pune mai tirziu in registrul
OPTION_REG. Va reamintesc ca CONSTANTELE nu ocupa loc in PIC®, ele fiind inlocuite
de asamblor doar in momentul compilarii. Ele ajuta la usurarea citirii programului.
Cautati deci asignarile mai jos in program, si inlocuiti valoarea alocata pentru
OPTIONVAL prin cea pe care am stabilit-o si adaugati comentariile voastre.
;******************************************************************
; ASIGNARI *
;******************************************************************
Sa mai coborim putin, pina la zona definitiilor. Vom da un nume butonului si LED-ului.
Instructiunile bcf si bsf pe care le vom folosi pentru seta sau citi 1 sau 0 in registre au
sintaxa « bsf f , n », si cum registrul de acces se numeste PORTA (pentru portul A) si
PORTB (pentru portul B), vom folosi DEFINITII pentru a putea introduce pe « f » si « n » in
acelasi timp.
Din schema vedem ca LED-ul este conectat pe bitul 2 al portului A, iar butonul, pe bitul 2
al portului B. Vom sterge deci definitiile date ca exemplu si le vom inlocui cu ale noastre.
Vom avea deci :
;******************************************************************
; DEFINITII *
;******************************************************************
#DEFINE LED PORTA , 2 ; LED rosu
#DEFINE BOUTON PORTB , 2 ; Buton
Notati faptul ca LED si BOUTON sint nume pe care le-am ales liber, in conditiile in care
nu coincid cu vreun cuvint-cheie. Nici nu poate fi vorba sa folosim de exemplu STATUS sau
MOVLW, chiar daca aceste exemple sint cam trase de par. S-ar putea totusi ca vreodata, din
neatentie, sa folositi un cuvint rezervat. La ce servesc definitiile ?
Sa presupunem de exemplu, ca v-ati hotarit sa puneti LED-ul pe bitul 1 din PORTB (RB1).
Ei bine, nu va trebui sa scotociti prin tot programul, va trebui sa faceti modificari doar in zona
DEFINE.
82
Daca mai coborim putin, ajungem in zona MACRO-urilor. Nu prea avem treaba aici, dar,
ca exemplu, chiar le vom folosi.
Stergeti MACRO-ul dat ca exemplu si introduceti cele de mai jos :
;******************************************************************
; MACRO *
;******************************************************************
LEDON macro
bsf LED
endm
LEDOFF macro
bcf LED
endm
Sa luam exemplul nostru : cind vom folosi linia urmatoare in programul nostru (atentie, nu
puneti MACRO-ul in prima coloana) :
LEDON
bsf LED
bsf PORTA , 2
Am ajuns in zona variabilelor. Le vom adauga pe masura ce vor fi necesare. Stergeti deci
cele 2 variabile prezente caci ele sint folosite in rutinele de intreruperi, pe care nu le vom
folosi aici.
;******************************************************************
; DECLARARI DE VARIABILE *
;******************************************************************
CBLOCK 0x00C ; inceput zona variabile
ENDC ; sfirsitul zonei
La ORG 0x00 sa lasam apelul catre rutina de initializare. Toate programele au o etapa de
initializare a variabilelor si registrelor. Obisnuiti-va sa separati aceasta zona de restul
programului.
Cum noi nu vom folosi intreruperile, eliminati tot ce urmeaza, pina la rutina de initializare.
Veti obtine :
83
;******************************************************************
; PORNIRE DUPA RESET *
;******************************************************************
org 0x000 ; adresa de pornire dupa RESET
goto init ; adresa 0: initializare
;******************************************************************
; INITIALIZARI *
;******************************************************************
continuare program...
In acest stadiu, inainte de a continua, vom studia registrele pe care le vom folosi, si mai
intii de toate :
Acest registru este un pic mai deosebit, el dind acces direct catre lumea exterioara. Acesta
este de fapt registrul care reprezinta imaginea pinilor RA0 – RA4, adica 5 pini. Daca m-ati
urmarit pina aici, acesta este registrul care va servi la aprinderea LED-ului.
Acest registru este situat la adresa 05H, in bancul 0. Fiecare bit al acestui registru
reprezinta un pin. Vor fi folositi deci doar 5 biti. Pentru a scrie intr-un pin de iesire, vom pune
bitul corespunzator in 1 sau 0, conform nivelului dorit.
De exemplu :
va pune un nivel de +5V pe pinul RA1. Pentru ca acest lucru sa fie posibil, acest pin trebuie
sa fi fost configurat ca iesire (vezi TRISA).
Pentru electronisti, in figura 5-1 de la pagina 21, aveti schema interna a bitilor RA0 – RA3.
Observati ca iesirea poate fi pusa la 1 sau 0 prin intermediul a doua tranzistoare de iesire in
montaj push-pull. Cind acesti pini sint programati ca intrari, ei sint sensibili la nivele intre 0 si
+5V. Nivelul de tranzitie depinde de fiecare exemplar de PIC®, dar in general, este situat la
jumatatea tensiunii de alimentare. O intrare lasata « in aer » este vazuta ca avind nivel 0.
Evitati totusi aceste cazuri, din cauza problemelor care pot aparea datorita parazitilor.
In figura 5-2 este aratat pinul RA4 care, ca iesire, este de tipul open-drain, iar ca intrare,
este prevazut cu un trigger Schmitt.
PORTURILE dispun de cite o dioda de protectie catre masa si catre +5V. Printr-o simpla
rezistenta serie, prin acesti pini, puteti trimite in exterior semnale in gama de tensiune
Vss/Vdd.
Pinul RA4 face exceptie de la aceasta regula, caci el nu dispune decit de o dioda fata de
masa.
Retineti cu totii deci ca pinii RA0 – RA3 pot fi folositi ca intrari pentru nivele intre 0 si
+5V, sau ca iesiri ce scot 0V sau +5V. Spre deosebire de acestia, pinul RA4, ca iesire, nu
poate scoate +5V. El nu poate decit sa lase iesirea « in aer » pentru 1 logic sau sa o puna la
84
masa pentru 0 logic. Ca intrare, el are un circuit (trigger Schmitt), care elimina comutarile
aleatorii atunci cind nivelul de intrare nu este ferm (de exemplu, atunci cind variaza lent de la
0 la +5V).
Tineti cont de aceste particularitati atunci cind va proiectati montajul. De exemplu, daca
am vrea sa punem LED-ul intre iesirea RA4 si masa, el nu se va aprinde niciodata.
Daca veti folosi valori apropiate de aceste limite, analizati tabelele cu mai multa atentie,
pentru a afla exact limitele de putere si temperatura. Noi nu vom avea astfel de probleme in
exercitiile noastre. Consumul LED-ului nostru pe pinul RA2 e dat de formula urmatoare :
I = (5V – Tensiunea pe LED) / 330 ohms, adica (5V – 1,75V) / 330 ohms = 9,85mA.
Consecinte :
Sa presupunem de exemplu ca bitul RA4 trebuie sa fie iesire si trebuie pus in 1. Cum el
este de tip open-drain, sa presupunem ca un circuit extern il forteaza momentan in 0. Daca
efectuati operatia :
atunci cind PORTA va fi citit, RA4 va fi citit ca 0, chiar daca el trebuie sa fie pe 1. RA1 va fi
fortat in 1 si totul va fi rescris in PORTA. RA4 este deci acum in 0, fara ca voi sa-l fi
modificat explicit.
Este adevarat ca un astfel de caz nu este unul curent (de exemplu, folosirea lui RA4 pentru
comanda unei linii I2C bus), dar e bine de stiut, daca intr-o zi una din interfetele voastre va va
face probleme, si aveti impresia ca vreunul din pinii de iesire isi schimba nivelul logic fara ca
voi sa o doriti.
85
11.9 Registrul TRISA
Acest registru are o functionare foarte simpla si este legata de functionarea lui PORTA.
Fiecare bit pus in 1 configureaza pinul corespunzator ca INTRARE, Fiecare bit pus in 0,
configureaza pinul corespunzator ca IESIRE.
La resetarea PIC®-ului, toti pinii sint setati ca INTRARI, cu scopul de a nu trimite semnale
nedorite pe pini. Bitii din TRISA vor fi deci in 1 la fiecare reset.
Retineti deasemenea ca, nefiind decit 5 pini folositi in PORTA, numai 5 biti (b0 – b4) vor
fi folositi din TRISA.
Exemplu de utilizare :
Sa luam ca exemplu schema micului nostru circuit : ce trebuie sa facem pentru a aprinde
LED-ul ? (PS : nu puneti deocamdata aceste instructiuni in proiectul dvs.).
Mai intii, va trebui sa configuram TRISA si sa punem bitul 2 (RA2) ca iesire.
Cum registrul TRISA se gaseste in bancul 1, iar adresarea DIRECTA nu foloseste decit 7
biti, va trebui deci sa comutam bitul RP0 din registrul STATUS (vezi capitolele precedente).
In continuare, putem trimite nivelul "1" logic catre PORTA, corespunzator unui nivel de
+5V pe pinul RA2. Secventa corecta va fi deci :
Notati faptul ca deoarece RA2 va ramine ca iesire pe toata durata programului (depinde de
schema), vom pune valoarea lui TRISA in rutina de initializare. Cind vom vrea sa aprindem
sau sa stingem LED-ul in programul principal, nu va trebui sa folosim decit o instructiune.
Incepem sa intram in concret…
Aceste registre functioneaza exact la fel ca PORTA si TRISA, dar contin 8 pini RB. In
acest caz, sint folositi toti bitii.
Sa vedem acum care sint particularitatile portului B. Am vazut deja una, din moment ce
intrarile portului B pot fi conectate intern la +5V prin rezistente pull-up.
Activarea acestora se face prin bitul 7 al registrului OPTION. Schema interna, vizibila in
fig. 5-3 si 5-4 de la pag. 23 a datasheet-ului, va arata ca bitii b0 si b4 - b7 pot fi folositi ca
86
sursa de intreruperi, in plus bitul 0 putind fi folosit independent pentru a genera un alt tip de
intrerupere.
Vom vedea functionarea intreruperilor intr-un alt capitol. Retineti totusi ca schemele pe
care le veti concepe voi insiva in viitor vor trebui sa tina cont de particularitatile porturilor.
Nota :
Dupa un reset, va puteti intreba poate, care este starea unui registru sau altul ?
Veti gasi aceste explicatii in tabelul de la pagina 14. Veti vedea ca dupa un reset,
registrul OPTION_REG va avea toti bitii pusi pe 1 : va trebui deci sa specificati stergerea
bitului b7 pentru a activa rezistentele pull-up.
Pornind de la schema noastra, noi dorim sa aprindem LED-ul atunci cind apasam butonul,
si sa-l stingem atunci cind il eliberam. Iata un exemplu de program (atentie, aveti grija ca
nivelul logic pe RB2 devine 0 atunci cind butonul este apasat (conexiune la masa)).
Primele 2 linii,
Nu va faceti probleme pentru aceasta linie. Retineti numai ca o valoare diferita in acest
registru, va mari un pic consumul PIC®-ului (pentru versiunea 16C84). Aceasta linie ofera
87
deci un mic avantaj montajelor alimentate din baterii. Registrul EEADR il vom vedea atunci
cind vom vorbi de accesele la EEPROM-ul intern. Retineti ca acest "bug" a fost corectat in
16F84. Aceasta linie nu va fi deci necesara in cazul nostru.
Urmeaza o mica rutina, destinata stergerii RAM-ului. Amintiti-va, si asta este foarte
important, ca la punerea sub tensiune, RAM-ul contine valori aleatoare. Pentru a evita
surprizele neplacute, am introdus aceasta rutina care ruleaza la fiecare pornire si care ne
asigura ca RAM-ul nu contine decit 0.
; Sterge RAM
; ------------
movlw 0x0c ; initializare indicator locatii
movwf FSR ; indicator de adresare indirecta
init1
clrf INDF ; sterge RAM indicat de FSR
incf FSR , f ; incrementeaza indicatorul de locatii
btfss FSR , 6 ; test daca s-a atins sfirsit zona (>=0x40)
goto init1 ; daca nu, bucleaza
btfss FSR , 4 ; test daca s-a atins sfirsit zona (>=0x50)
goto init1 ; daca nu, bucleaza
xxxxx ; aici continua programul
In prima etapa, se initializeaza indicatorul catre zona ce trebuie manipulata (aici, zona
RAM a utilizatorului). Aceasta zona incepe la adresa 0x0C (vezi tabelul 4-2) si se termina la
adresa 0x4F inclusiv. Zona situata in bancul 1 nu exista pentru acest PIC® (imaginea bancului
0) si deci nu va fi nevoie sa fie initializata.
Urmeaza instructiunea clrf INDF, care inseamna stergerea locatiei de memorie a carei
adresa se gaseste in registrul FSR. Se sterge deci locatia situata la adresa 0x0C
Urmeaza apoi 2 teste. Pentru a iesi din aceasta rutina si a ajunge la linia care constituie
continuarea programului (notata cu xxxxx), va trebui deci sa evitam cele 2 linii goto.
Ajungeti la primul goto daca bitul 6 din FSR devine 1. Pentru a evita acest goto, FSR va
trebui sa aiba o valoare de minim B’01000000’, sau 0x40. In acest moment, adresele dintre
88
0x0C si 0x3F sint deci puse pe 0.
Ajungem la cel de-al doilea test. Urmatorul goto va fi deci sarit daca bitul 4 al FSR este
pe 1. Vom atinge deci linia "xxxxx", numai daca bitii 4 si 6 ai FSR vor fi in 1. Asta, ca adresa
inseamna B’01010000’, adica 0x50.
Se putea folosi si un contor de bucla, impreuna cu instructiunea decfsz. Era insa necesara
folosirea unei variabile, variabila care ar fi fost ea insasi stearsa de catre aceasta rutina, si ar fi
dus la blocarea programului. Rezulta deci, ca metoda folosita aici este cea mai simpla.
Mai intii, va trebui sa declaram RA2 ca iesire, pentru a putea comanda LED-ul.
Instructiunea va fi :
bcf TRISA , 2
Dar noi am declarat la DEFINITII ca LED sa fie egal cu PORTA, 2. Pentru a permite o
modificare usoara a LED pe un alt pin, ce se va intimpla daca vom scrie :
bcf LED
bcf PORTA , 2
bcf 0x05 , 2
Sau, RP0 va fi pus intotdeauna pe 1 in momentul executarii acestei linii. Se indica deci
intotdeauna bancul 1. Daca va uitati ce se afla la adresa 0x05 in bancul 1, adica la adresa
0x85, veti gasi TRISA. Astfel, operatia a pus RA2 ca iesire.
Modificind doar DEFINE LED la inceputul programului, veti putea modifica toate
referirile la RA2 in tot programul, chiar si pentru TRISA. Adaugati un mic comentariu in
antet si veti obtine :
; initializari specifice
; ----------------------
Reveniti acum in bancul 0 inainte de a termina initializarile. Este o practica buna, caci
89
multe erori sint provocate de erori de banc. Adaugati deci :
Incheiati cu linia :
goto start
care ramifica programul principal. Pentru moment, acest lucru ar putea parea inutil, caci
programul principal urmeaza direct dupa aceasta instructiune, dar vom adauga mai tirziu un
sub-program care va fi intercalat intre aceste doua linii.
Linistiti-va, acestea par complicate la inceput, insa, odata asimilate, sint foarte simple. In
plus, ati vazut practic tot ce trebuie sa stiti pentru a realiza un program fara rutine de
intreruperi.
;******************************************************************
; INITIALIZARI *
;******************************************************************
init
clrf PORTA ; iesiri port A pe 0
clrf PORTB ; iesiri port B pe 0
clrf EEADR ; permite reducerea consumului
bsf STATUS , RP0 ; selectare banc 1
movlw OPTIONVAL ; incarcare masca
movwf OPTION_REG ; initializare registru de optiuni
; Stergere RAM
; ------------
movlw 0x0c ; initializare indicator locatii
movwf FSR ; indicator de adresare indirecta
init1
clrf INDF ; sterge RAM
incf FSR , f ; incrementeaza indicatorul de locatii
btfss FSR , 6 ; test daca s-a atins sfirsit zona (>=0x40)
goto init1 ; daca nu, bucleaza
btfss FSR , 4 ; test daca s-a atins sfirsit zona (>=0x50)
goto init1 ; daca nu, bucleaza
; Initializari specifice
; ----------------------
bcf LED ; LED ca iesire (banc 1)
bcf STATUS , RP0 ; revenire in banc 0
goto start ; salt la programul principal
90
11.13 Rezultatele compilarii
Acesta este un mesaj de atentionare care va semnaleaza faptul ca la linia 101 (asta in cazul
meu, dar voi, avind alte spatii sau comentarii, puteti avea un alt numar de linie), ati accesat un
registru care nu se gaseste in bancul 0.
MPASM® va cere sa verificati daca bitul vostru RP0 este setat corect. Mergeti in editor la
linia indicata in mesaj. Veti gasi :
O privire aruncata asupra tabelului 4-2 va arata ca registrul OPTION este in bancul 1. Cum
insa bitul vostru RP0 a fost pus pe 1 in acest loc, nu sint probleme. Cind veti realiza programe
mari, veti avea tone de asemenea mesaje. Daca doriti sa evitati aceste mesaje de banc,
adaugati linia :
Errorlevel –302
direct sub linia "#include" din fisierul vostru sursa. Semnul « - » inseamna ca dvs. scoateti
acest mesaj din mesajele active, iar « 302 » este numarul mesajului care va fi ignorat.
start
- aprind LED-ul
- astept 1/2 secunde
- sting LED-ul
- astept 1/2 secunde
- reiau de la start
Observam ca folosim de 2 ori o temporizare de 1/2 secunde. Vom crea deci un sub-
program pe care il vom numi « tempo ». Programul nostru principal va avea deci aspectul
urmator :
start
bsf LED ; aprinde LED-ul
call tempo ; apeleaza temporizarea de 0.5s
bcf LED ; stinge LED-ul
call tempo ; apeleaza temporizarea de 0.5s
goto start ; bucleaza
91
Am fi putut deasemenea sa scriem (folosind macro-urile noastre) :
start
LEDON ; aprinde LED-ul
call tempo ; apeleaza temporizarea de 0.5s
LEDOFF ; stinge LED-ul
call tempo ; apeleaza temporizarea de 0.5s
goto start ; bucleaza
N-am tratat pina acum timerul tmr0 si nici intreruperile. Scopul aici este de a va face sa
intelegeti functionarea lui 16F84. Pentru a realiza o temporizare, este suficient in cazul nostru
sa pierdem timp in 16F84 intre fiecare inversare a starii LED-ului.
Va trebui deci sa pierdem oproximativ 0.5s. Secundele nu sint unitati adecvate pentru
PIC®-uri, care lucreaza cu o viteza cu mult mai mare. Vom folosi deci unitati de timp
compatibile cu PIC®-urile.
PIC®-ul nostru are tactul dat de frecventa cuartului, adica 4 MHz. PIC®-ul executa un
ciclu de instructiune la fiecare 4 ciclii ai ceasului principal. PIC®-ul va executa deci
(4.000.000/4) = 1 million de cicluri pe secunda.
Cea mai mare parte a instructiunilor (exceptind salturile) se executa intr-un singur ciclu,
ceea ce inseamna aproximativ 1 milion de instructiuni pe secunda. Veti intilni uneori
denumirea de MIPS. Aceasta inseamna Milioane de Instructiuni Pe Secunda. PIC®-ul nostru,
cu acest cuart va avea deci o putere de procesare de aproape 1 MIPS.
Deci, temporizarea noastra de 0.5s dureaza 500.000 microsecunde. Altfel spus, va trebui sa
« facem nimic » 500.000 de cicluri in rutina noastra de temporizare. Observati deci ca
PIC®-ul vostru, in aplicatia noastra, va pierde timpul nefacind nimic util.
Prima idee care ne vine in minte este sa realizam o bucla care va incrementa sau va
decrementa o variabila.
;******************************************************************
; DECLARARE VARIABILE *
;******************************************************************
CBLOCK 0x00C ; inceput zona variabile
cmpt1 : 1 ; contor de bucle 1
ENDC ; sfirsit zona
Sa cream acum scheletul subrutinei noastre, pe care o vom plasa intre rutina de initializare
si programul principal. Nu uitati sa adaugati totdeauna comentarii :
92
;******************************************************************
; SUBRUTINA DE TEMPORIZARE *
;******************************************************************
;------------------------------------------------------------------
; Aceasta subrutina introduce o intirziere de 500.000 µs.
; Ea nu necesita nici un parametru si nici nu returneaza vreo unul.
;------------------------------------------------------------------
tempo
; vom pune codul nostru aici
return ; intoarcere din subrutina
tempo
clrf cmpt1 ; sterge contor1
bucla1
decfsz cmpt1 ; decrementeaza contor1
goto bucla1 ; daca nu e 0, bucleaza
return ; intoarcere din subrutina
Numarul de linie poate fi diferit in functie de codul vostru sursa. Cum compilarea s-a
efectuat corect, este vorba inca odata de un mesaj de atentionare. Mergeti la linia indicata
(134 la mine). Sinteti la linia :
tempo
clrf cmpt1 ; 1 ciclu
bucla1
decfsz cmpt1 ; 1 ciclu daca nu se sare, 2 daca se sare
; deci, nu va sari de 225 ori si va sari 1 data
goto bucla1 ; 2 cicluri X 255 de treceri
return ; 2 cicluri
93
Ø 510 cicluri pentru cele 255 instructiuni goto
Ø 2 cicluri pentru return.
Adica, un total de 772 cicluri. Sintem departe de cele 500.000 cicluri necesare. Pentru
calculele urmatoare, vom neglija cele 2 cicluri pentru call si cele 2 cicluri pentru return
(nesemnificativ fata de cele 500.000 cicluri necesare).
Va trebui sa lungim subrutina, realizind o a doua bucla care va forta executarea primei
bucle de 256 ori. Incepem prin a declara o noua variabila, cmpt2 :
tempo
clrf cmpt2 ; sterge contor2
bucla2
clrf cmpt1 ; sterge contor1
bucla1
decfsz cmpt1 , f ; decrementeaza contor1
goto bucla1 ; daca nu e 0, bucleaza
decfsz cmpt2 , f ; daca e 0, decrementeaza contor2
goto bucla2 ; daca cmpt2 nu e 0, reia bucla1
return ; revenire din subrutina
Vedeti ca prima noastra bucla este tot acolo, dar in loc sa efectueze return odata terminata,
vom relua bucla atita timp cit cmpt2 nu este egal cu 0. Vom executa deci bucla 1 de 256 ori.
Durata buclei 1 : 257 cicluri + 510 cicluri + 1 ciclu (clrf cmpt1) = 768 cicluri.
Dar aceasta bucla va fi executata de 256 ori, deci 768 x 256 = 196608 cicluri, la care putem
adauga cele citeva cicluri de initializare, etc.
Noi avem nevoie insa de 500.000 cicluri. Va trebui sa folosim aceasta bucla de
(500.000/196608) = 2, 54 ori. Nu ne pricepem insa sa realizam o jumatate de bucla. Vom rula
bucla de 2 ori. Vom face in asa fel incit primele doua bucle sa dureze 500.000/2 = 250.000
cicluri.
Fiecare instructiune adaugata in bucla 1 este executata de 256 x 256 ori. Fiecare
instructiune adaugata in bucla 2 este executata de 256 ori. Fiecare ciclu exterior buclelor este
executat doar 1 data. Avem deci posibilitatea de a realiza temporizari foarte precise, ceea ce
nu este necesar aici. Totusi, vom incerca sa imbunatatim cit de cit precizia.
Daca adaugam 1 ciclu inutil in bucla 1, adaugam 256 x 256 = 65536 cicluri. Va mai trebui
sa adaugam inca 250.000 – 196608 = 53392 cicluri. Aceasta ne va da o eroare de 12.000
cicluri, adica 12 miimi de secunda (12ms). Precizia este de departe suficienta pentru aplicatia
noastra, dar va reamintesc ca puteti efectua si un calcul precis. Va sfatuiesc chiar sa-l faceti,
ca exercitiu. Va puteti verifica rezultatele cu un cronometru.
Pentru a pierde un ciclu, vom adauga o instructiune NOP, care nu face nimic.
94
Mai ramine deci de realizat ultima bucla. Sa cream o a treia variabila cmpt3 si o a treia
bucla (linistiti-va, exista si metode mai simple, dar acestea sint explicate numai cu scop
didactic : singurul lucru important este sa intelegeti mecanismul).
;******************************************************************
; DECLARARI DE VARIABILE *
;******************************************************************
CBLOCK 0x00C ; inceput zona variabile
;******************************************************************
; SUBRUTINA DE TEMPORIZARE *
;******************************************************************
;------------------------------------------------------------------
; Aceasta subrutina introduce o intirziere de 500.000 µs.
; Ea nu necesita nici un parametru si nici nu returneaza vreo unul.
;------------------------------------------------------------------
tempo
movlw 2 ; pentru 2 bucle
movwf cmpt3 ; initializeaza contor 3
bucla3
clrf cmpt2 ; sterge contor 2
bucla2
clrf cmpt1 ; sterge contor 1
bucla1
nop ; pierde 1 ciclu *256 *256 *2
decfsz cmpt1 , f ; decrementeaza contor 1
goto bucla1 ; daca nu e 0, bucleaza
decfsz cmpt2 , f ; daca e 0, decrementeaza contor 2
goto bucla2 ; daca cmpt2 nu e 0, reia bucla 1
decfsz cmpt3 , f ; daca e 0, decrementeaza contor 3
goto bucla3 ; si cmpt3 pas 0, reia bucla2
return ; revenire din subrutina
Lansati compilarea cu <F10>. Daca totul e bine, veti obtine in directorul de lucru fisierul
LED_CLI.HEX.
Felicitari, iata primul vostru program incorporat. Drumul e deschis pentru mii de alte
aplicatii.
95
SFAT IMPORTANT :
Daca in acest stadiu veti sari pina-n tavan strigind « MERGE ! » (cunosc asta), nu va
precipitati sa incercati s-o faceti pe prietena voastra sa va admire realizarea. Ma indoiesc ca ea
va va impartasi entuziasmul pentru « acest beculet care clipeste ».
Fisierul care trebuia obtinut la sfirsitul acestei lectii este disponibil in fisierele furnizate.
96
Note:…
97
12. Intreruperile
Iata un capitol care cu siguranta va stirni un pic de teama viitorilor programatori. Totusi,
mecanismul intreruperilor este destul de usor de inteles, si la fel de usor de pus in practica,
daca-l vom trata intr-o maniera clara si structurata.
Interlocutorii isi vor relua conversatia din punctul in care au ramas, imediat ce pericolul a
trecut (si daca au evitat pianul, bineinteles !).
Ei bine, pentru programatori este valabil exact acelasi principiu :
Putem spune, fara sa gresim prea mult, ca o rutina de intrerupere este un sub-program
special, declansat de aparitia unui eveniment specific. Aceste afirmatii par cam dificile, dar
veti vedea ca este foarte simplu.
Iata deci cum functioneaza :
98
Ø Evenimentul respectiv trebuie sa figureze in lista de evenimente susceptibile sa
declanseze o intrerupere pentru procesorul respectiv
Ø Utilizatorul trebuie sa fi autorizat intreruperea, adica trebuie sa fi semnalat faptul ca
evenimentul respectiv ar putea sa genereze o intrerupere.
Ø Mai intii, adresa de start a tuturor intreruperilor este fixa. Ea este 0x04.
Ø Toate intreruperile genereaza un salt al programului catre aceasta adresa. Toate sursele
de intrerupere ajungind la aceasta adresa, daca programatorul foloseste mai multe surse
de intrerupere, va trebui sa determine el insusi care este cea pe care va trebui sa o
99
trateze.
Ø Cind PIC®-ul sare la aceasta adresa, el nu salveaza nimic automat, in afara de
continutul PC pentru a cunoaste adresa de intoarcere din intrerupere. Cade deci in
sarcina programatorului sa faca salvarile.
Ø Continutul PC-ului este salvat in stiva interna (care are 8 nivele). Deci, daca folositi
intreruperile, nu aveti decit 7 nivele de imbricare pentru sub-programele voastre. Cu
atit mai putin daca folositi sub-programe pentru tratarea intreruperilor.
Timpul mort total va fi deci cuprins intre 3 si 4 cicluri. Daca vrem sa fim exacti,
logica interna face ca toate intreruperile survenite inainte de primul sfert al instructiunii
in curs vor fi luate in considerare ca si cum instructiunea nici nu ar fi inceput. Altfel
spus, timpul mort va fi cuprins intre 3 si 3.75 cicluri. Retineti deasemenea ca daca
intreruperea este sincrona cu programul (intrerupere interna, generata de timer), ea nu
va putea interveni in mijlocul unui ciclu si deci timpul mort va fi obligatoriu de 3 cicluri.
Remarcati ca :
Din acest punct de vedere 16F84 este foarte sarac, din moment ce el nu dispune decit de
4 surse de intreruperi posibile (fata de 13 ale lui 16F876, de exemplu). Evenimentele
susceptibile de a declansa o intrerupere sint urmatoarele :
Ø TMR0 : Depasirea timerului tmr0. Odata ce continutul timerului tmr0 trece din 0xFF
in 0x00, se poate genera o intrerupere. Vom folosi aceasta proprietate in capitolul
dedicat lui tmr0.
Ø EEPROM : Aceasta intrerupere poate fi generata atunci cind se termina o scriere intr-o
locatie din EEPROM-ul intern.
Ø RB0/INT : Se poate genera o intrerupere atunci cind pe pinul RB0 (numit si pinul
INTerrupt), configurat ca intrare, apare o modificare a nivelului logic. Vom studia
acest caz aici.
Ø PORTB : In acelasi fel, poate fi generata o intrerupere atunci cind apare o modificare
100
de nivel logic pe unul din pinii RB4 – RB7. Nu este posibil sa limitam intreruperea
doar la unul dintre acesti pini. Intreruperea poate fi validata ori pentru toti cei 4 pini,
ori pentru niciunul.
Cum interzicem sau autorizam intreruperile, cum detectam care este evenimentul
declansator, si cum le gestionam ? Vom incepe mai intii printr-o abordare simbolica, pentru a
va ajuta sa vizualizati procedura.
Cind veti intelege aceasta schema, inseamna ca ati inteles intreruperile. Veti vedea imediat
ca sint doua metode pentru a chema baiatul de la room-service : fie prin intermediul becului,
fie prin intermediul soneriei.
Ø Pentru prima metoda, baiatul trebuie sa vina sa vada becurile la intervale regulate,
pentru a vedea daca cineva l-a chemat. El trebuie deci sa vina si sa CERCETEZE cu
privirea tabloul de semnalizare. Este vorba despre metoda SCANARII.
Ø Prin intermediul soneriei, baiatul este INTRERUPT din treburile sale de catre aceasta.
El nu mai trebuie sa vina sa cerceteze degeaba tabloul de semnalizare, dar va fi
deranjat din munca sa de catre sonerie. Aceasta este metoda INTRERUPERILOR.
Ø Clientul din camera nu poate decide el care metoda va fi utilizata, ci baiatul va decide
daca va activa sau nu soneriile.
Ø Baiatul poate bloca toate soneriile odata, sau va decide care camera va putea actiona
soneria.
101
Ø Intrerupatoarele de validare nu afecteaza becurile.
Ø Daca baiatul este intrerupt din activitatea sa de catre sonerie, el trebuie sa mearga
neaparat la tablou pentru a vedea becurile, ca sa afle cine a sunat. Aceasta, in afara de
cazul cind nu a validat decit o sonerie, dintr-o singura camera.
Ø Ceea ce nu apare explicit in aceasta schema simplificata, dar care trebuie retinut, este
faptul ca un bec odata aprins, ramine aprins. Baiatul va trebui sa il stinga manual.
Sa aplicam deci toate acestea, pentru cazul lui 16F84. Ati priceput deja ca becurile si
intrerupatoarele reprezinta in 16F84 biti din niste registre particulare. Va voi prezenta acum
registrul principal de gestionare a intreruperilor din 16F84.
Acest registru se gaseste la adresa 0x0B, in ambele bancuri. El va fi deci accesibil in orice
moment. El este detaliat in figura 4-5 de la pagina 17. Este un registru de biti, fiecare bit avind
o functie particulara. Iata in detaliu acesti biti :
102
Remarca :
Amintiti-va ca flag-urile nu revin singure la 0. Acest lucru trebuie facut de catre programul
dvs, altfel exista riscul de a ramine blocati la infinit intr-o rutina de intrerupere.
Spunem ca aceste flag-uri sint FLAG-URI REMANENTE. Aveti grija ca resetarea bitului
RBIF trebuie sa fie precedata de o citire sau de o scriere in PORTB cu scopul de a pune capat
conditiei de generare a flag-ului de intrerupere.
Remarcati ca toti bitii al caror nume se termina cu E (Enable) sint de fapt comutatoare de
validare (sint intrerupatoarele din schema noastra simbolica). Bitii al caror nume se termina in
F sint flag-uri (indicatori). Ei reprezinta becurile in schema noastra. In schema noi avem 5
intrerupatoare si 4 becuri. Ori, noi nu avem decit 8 biti in INTCON0. Ce ne lipseste ?
Daca priviti cu atentie, ne lipseste bitul EEIF. Linistiti-va insa, acest bit exista si el, doar ca
este in registrul EECON1, pe care-l vom studia la lectia despre accesarea EEPROM-ului.
Acum vom modifica un pic schema noastra simbolica pentru a corespunde cu un PIC16F84
real.
Daca priviti din nou organigrama rutinei de intrerupere, veti constata ca va trebui sa salvati
si sa restaurati starea procesorului. In ce consta aceasta ?
Dupa cum v-am spus deja, programul principal intrerupt nu stie unde a ramas si deci va
trebui sa refaceti starea registrelor pentru ca acesta sa poata continua din starea in care se
gasea in momentul aparitiei intreruperii, dupa ce tratarea intreruperii s-a terminat.
103
Un mic exemplu :
Este foarte probabil ca rutina voastra de intrerupere sa utilizeze cel putin o instructiune
care sa modifice bitul Z. Va trebui deci sa restaurati registrul STATUS in starea in care era,
inainte de a iesi din rutina de intrerupere. In caz contrar, testul nu se va efectua pe valoarea lui
mavariable, ci pe o valoare a lui Z modificata de rutina de intrerupere. In plus, este aproape
sigur ca si registrul W va fi modificat, si deci va trebui sa-l salvam si pe acesta.
Pentru inceput, ce-i mai simplu : registrul PC este salvat automat, deci programul va reveni
singur la adresa corecta.
Registrul STATUS insa, va trebui restaurat de catre rutina de intrerupere, dupa cum vom
vedea.
Registrul W va fi deasemenea modificat de catre rutina de intrerupere. Daca mai trebuie
salvate si alte registre, depinde de structura programului vostru. Singurele registre care trebuie
salvate obligatoriu sint deci STATUS si W. Daca rutina voastra de intrerupere contine doar
instructiuni care nu modifica nici STATUS nici W (bsf, etc), teoretic ati putea sa nu mai faceti
salvarile, dar acesta este un caz particular extrem de rar.
Pentru a salva registrele W si STATUS vom folosi o metoda clasica. Aceasta consta in a
salva W inaintea lui STATUS, deoarece salvarea lui STATUS presupune mai intii incarcarea
acestui registru in registrul de lucru W, ceea ce duce deci la pierderea starii registrului W.
104
informatie despre motivul exact al acestei metode propuse (poate fi o ramasita de la un bug
mai vechi privind manipularea registrului STATUS ?). Se pare ca aceasta metoda a disparut
din recomandarile oficiale ale Microchip. Cu toate acestea, primele versiuni ale acestui curs
au folosit aceasta metoda recomandata, astfel ca este aceasta este abordarea pe care o veti gasi
in surse şi documente. As vrea doar sa stiti ca puteti folosi ce metoda va convine, caci in
principiu, nu se schimba nimic.
ATENTIE : Acestea nu sunt valabile decit pentru registrul STATUS. In ceea ce priveste
restaurarea lui W, vor fi aplicate remarcile din cele ce urmeaza.
Mi-ati putea spune : despre ce sa vorbim aici, e suficient sa procedam invers ca la salvare
si gata :
Totul pare simplu, si totusi aici se ascunde o mare capcana. De fapt, ultima instructiune
« movf » modifica flagurile din STATUS. Deci, restaurind registrul W voi distrugeti
restaurarea lui STATUS. Acest lucru este evident inacceptabil, deci se impune folosirea unei
alte metode.
Trucul consta in folosirea pentru restaurarea lui W a unei instructiuni care nu modifica
niciun bit din registrul STATUS. Daca aruncam o privire asupra instructiunilor disponibile
vom vedea ca instructiunea « swapf » aduce variabila in W FARA a modifica niciun bit in
STATUS.
Totusi, aceasta instructiune prezinta inconvenientul de a inversa cei 4 biti mai semnificativi
cu cei 4 mai putin semnificativi din octetul respectiv, ceea ce duce la o restaurare incorecta.
Pentru a anula aceasta inversare, va trebui deci sa folosim 2 instructiuni « swapf »
consecutive, care vor readuce octetul la starea initiala.
Rutina modificata va arata deci dupa cum urmeaza :
In acest mod, registrul W arata bine si este corect restaurat fara sa fi modificat registrul
STATUS restaurat mai inainte.
Iata deci pe scurt cele doua moduri de a proceda si care sunt perfect echivalente.
• Metoda « intuitiva » :
salvare
movwf w_temp ; salveaza W intr-o locatie de salvare
movf STATUS,w ; transfera STATUS in W
movwf status_temp ; salveaza STATUS
tratare_intrerupere
…
…
…
105
restaurare
movf status_temp,w ; incarca octetul STATUS salvat
movwf STATUS ; restaureaza STATUS
swapf w_temp,f ; inverseaza locatia de salvare a lui W
swapf w_temp,w ; inverseaza din nou si pune rezultatul in W
salvare
movwf w_temp ; salveaza W intr-o locatie de salvare
swapf STATUS,w ; transfera STATUS in W
movwf status_temp ; salveaza STATUS
tratare_intrerupere
…
…
…
restaurare
movf status_temp,w ; incarca octetul STATUS salvat
movwf STATUS ; restaureaza STATUS
swapf w_temp,f ; inverseaza locatia de salvare a lui W
swapf w_temp,w ; inverseaza din nou si pune rezultatul in W
De fapt, nu exista nicio diferenta practica intre cele doua metode, insa este neaparat
necesar sa va amintiti ca este obligatorie folosirea de 2 ori a instructiunii « swapf » pentru
restaurarea lui W.
;******************************************************************
; RUTINA DE INTRERUPERE *
;******************************************************************
; salvare registre
; -----------------
org 0x004 ; adresa intreruperi
movwf w_temp ; salvare registru W
swapf STATUS,w ; inverseaza STATUS cu rezultatul in W
movwf status_temp ; salveaza STATUS inversat
; tratare intreruperi
; --------------------
; restaurare registre
; --------------------
swapf status_temp,w ; inverseaza vechiul STATUS, rezultatul in W
movwf STATUS ; restaurare STATUS
swapf w_temp,f ; inversare L si H in vechiul W,
; fara a modifica Z
swapf w_temp,w ; re-inversare L si H in W
; W restaurat fara a modifica STATUS
retfie ; revenire din intrerupere
106
Cam asa ar trebui sa arate scheletul rutinei voastre de intrerupere : nu-i prea complicat, nu-i
asa ? Nu ne ramine decit sa o completam, caci fisierul m16F84.asm contine deja rutinele de
salvare si restaurare (pe care tocmai le-am explicat) si testele de tip de intrerupere (pe care le
vom detalia in continuare).
N-as vrea sa inchei aceasta parte fara a va atrage atentia asupra unui aspect important, si
anume instructiunile care au ca destinatie registrul STATUS.
Toate instructiunile al caror rezultat pot modifica unul sau mai multi biti din
registrul STATUS, si a caror tinta este insusi registrul STATUS, fac ca fiecare bit ce
poate fi modificat de instructiune sa se regaseasca ca informatie read-only.
Fiti deci foarte prudenti, si daca destinatia instructiunii voastre este registrul
STATUS, folositi de preferinta un movwf, un swapf, sau instructiuni pentru
manipularea bitilor (bcf, bsf).
Pentru ca o noua intrerupere sa poata fi deservita odata ce cea in curs este terminata, bitul
GIE trebuie repus in 1. Acest lucru este executat automat de catre RETFIE.
Ei bine, exact asta face RETFIE, dar cu o mica diferenta, si aceasta diferenta este de cea
mai mare importanta : RETFIE este o instructiune unica, deci care nu poate fi intrerupta de
catre o intrerupere (care poate sa apara intre cele 2 instructiuni de mai sus).
107
In cazul in care ar fi folosite cele doua instructiuni de mai sus, odata bitul GIE pus in 1,
daca unul din flag-urile de intrerupere este permanent in 1 (o alta intrerupere, sau aceeasi care
se repeta), programul se va intoarce la rutina de intrerupere inainte de a executa RETURN,
adica inainte de a restaura PC-ul.
Acesta va continua sa ocupe o locatie in stiva. Stiind ca stiva este limitata la 8 locatii,
exista mari sanse de blocare a programului prin depasirea capacitatii stivei.
Vom crea un program care va inversa starea unui LED la fiecare apasare a unui buton.
Modificati placuta voastra de test, adaugindu-i un buton pe intrarea RB0 (imi pare rau, dar
cind am desenat schema, ma gindeam sa explic intreruperile prin timer).
Rezistenta este construita intern in PIC®, fiind vorba de o rezistenta pull-up catre +5V,
care poate fi activata prin soft.
;******************************************************************
; Acest program este un program didactic destinat sa prezinte *
; functionarea intreruperilor *
; *
;******************************************************************
; *
; NUME: Intrerupere printr-un buton conectat la pinul RB0 *
; Data: 13/02/2001 *
108
; Versiune: 1.0 *
; Circuit: Placuta de test *
; Autor: Bigonoff *
; *
;******************************************************************
; *
; Fisier necesar: P16F84.inc *
; *
;******************************************************************
; Note: Acest program transforma un simplu buton intr-un *
; intrerupator basculant. O apasare aprinde LED-ul, urmatoarea *
; il stinge. *
;******************************************************************
Veti spune ca exagerez punind comentarii peste tot. Credeti-ma pe cuvint, comentariile
permit o intretinere usoara a programelor. Ele va vor scuti mult timp pretios pe care ati putea
sa-l pierdeti.
;******************************************************************
; ASIGNARI *
;******************************************************************
109
In continuare, in zona DEFINITII, vom defini LED-ul si butonul :
;******************************************************************
; DEFINITII *
;******************************************************************
#DEFINE Buton PORTB , 0 ; buton
#DEFINE LED PORTA , 2 ; LED
In zona MACRO, putem scrie 2 macro-uri pe care le vom utiliza frecvent. Acestea sint
instructiunile pentru comutare intre bancul 0 si bancul 1. Ele vor fi incluse in noul fisier
« m16f84.asm » denumit « m16f84_new.asm ». La sfirsitul acestei lectii, stergeti vechiul
fisier si redenumiti « m16F84_new » in « m16f84 ».
;*****************************************************************
; MACRO *
;*****************************************************************
BANK0 macro
bcf STATUS , RP0 ; se trece in bancul 0
endm
BANK1 macro
bsf STATUS , RP0 ; se trece in bancul 1
endm
Vom recupera deasemenea si mica noastra rutina de temporizare din fisierul «led_cli.asm».
Vom avea deci nevoie de variabilele folosite de aceasta rutina. Toate acestea la un loc, pentru
moment, arata asa :
;******************************************************************
; DECLARARI DE VARIABILE *
;******************************************************************
CBLOCK 0x00C ; inceput zona variabile
;******************************************************************
; RUTINA DE INTRERUPERE *
;******************************************************************
;salvare registre
;-----------------
org 0x004 ; adresa de intrerupere
movwf w_temp ; salvare registru W
110
swapf STATUS , w ; inversare status cu rezultatul in W
movwf status_temp ; salvare status inversat
Primele 2 instructiuni examineaza daca este vorba de o intrerupere generata de tmr0. Veti
spune : de ce 2 linii ? Ar fi suficient sa examinam numai T0IF.
Ei bine, nu e chiar asa de simplu. Sa ne imaginam ca tmr0, pe care noi nu-l folosim
deocamdata, are depasire. Bitul T0IF este deci pus in 1 si nu genereaza intrerupere, caci T0IE
este pe 0. Similar, daca am fi validat intreruperile generate de timer si am fi avut o intrerupere
datorita altei cauze, am fi avut T0IE pe 1 si T0IF pe 0.
Prin urmare noi vom trata intreruperea numai daca aceasta este validata si daca are si flag-
ul activat (de unde si dublul test). Examinati acest dublu test si observati functionarea
instructiunilor btfsc si btfss .
Daca T0IE este 0, se sare direct la testul urmator, prin linia goto. Daca T0IE este 1, se
verifica si T0IF. Daca acesta este 0, am ajuns la linia goto care trece la testul urmator. Daca
este 1, se apeleaza subrutina de tratare a intreruperii generate de timer0.
Observatii :
111
Mai departe, gasim aceeasi procedura pentru intreruperile de tip RB0 (pe care noi o vom
folosi) si intreruperile RB4/RB7. Remarcati locul prevazut pentru a adauga testul pentru
intreruperea EEPROM. Toate aceste modificari au fost incluse in fisierul
« m16f84_new.asm ».
intsw1
btfsc INTCON , INTE ; testeaza daca intr. RB0 e validata
btfss INTCON , INTF ; da, testeaza daca intr. RB0 este in
; curs
goto intsw2 ; nu salt la testul urmator
call intrb0 ; da, trateaza intr. RB0
bcf INTCON , INTF ; sterge flag intr. RB0
goto restorereg ; si incheie intreruperea
; SUPPRIMATI ACEASTA LINIE PENTRU
; A TRATA MAI MULTE INTRERUPERI DEODATA
intsw2
btfsc INTCON , RBIE ; testeaza daca intr. RB4/RB7 e validata
btfss INTCON , RBIF ; da, testeaza daca intr. RB4/RB7 este in
; curs
goto intsw3 ; nu, salt
call intrb4 ; da, trateaza intr. RB4/RB7
bcf INTCON , RBIF ; sterge flag intr. RB4/RB7
goto restorereg ; si incheie intreruperea
intsw3
In final gasim partea ce serveste la restaurarea registrelor salvate. Am vazut deja aceasta
procedura :
; restaurare registre
; --------------------
restorereg
swapf status_temp,w ; inverseaza vechiul STATUS rezultatul in W
movwf STATUS ; restaurare STATUS
swapf w_temp,f ; inversare L si H din vechiul W
; fara a modifica pe Z
swapf w_temp,w ; re-inversare L si H in W
; W restaurat fara a modifica STATUS
retfie ; intoarcere din intrerupere
Vom modifica acum aceasta rutina de intrerupere pentru a o adapta la cazul nostru
particular. N-avem decit o singura sursa de intreruperi validata, deci, daca intram in aceasta
intrerupere, vom fi fortati sa tratam INT/RB0. Vom suprima deci testele.
Ne mai ramine :
112
;******************************************************************
; RUTINA DE INTRERUPERE *
;******************************************************************
;salvare registre
;-----------------
org 0x004 ; adresa de intrerupere
movwf w_temp ; salvare registru W
swapf STATUS,w ; inversare STATUS cu rezultatul in W
movwf status_temp ; salvare STATUS inversat
call intrb0 ; tratare intrerupere RB0
; restaurare registre
; --------------------
;******************************************************************
; INTRERUPEREA RB0/INT *
;******************************************************************
intrb0
return ; sfirsit intrerupere RB0/INT
; poate fi inlocuit prin
; retlw pentru a intoarce cod de eroare
Ea nu contine decit intoarcerea din subrutina corespunzatoare apelului call intrb0. Atentie,
la acest nivel nu folositi RETFIE, caci nu iesiti din intrerupere, ci dintr-o subrutina apelata
de rutina de intrerupere.
Daca veti folosi RETFIE in acest moment, veti reactiva intreruperile inainte de a iesi, lucru
care va duce la blocarea programului.
Remarcati ca puteti folosi retlw pentru a retine o valoare predefinita, tratata de rutina
voastra de intrerupere. De exemplu, retlw0 daca doriti sa tratati si alte intreruperi si retlw1
daca se iese din rutina. In acest caz, linia
113
12.11 Initializarea
Mergind mai departe, gasim rutina de initializare pe care am explicat-o deja in lectia
trecuta. Aici vom inlocui pur si simplu schimbarile de banc prin macro-urile pe care le-am
scris, si vom configura RA2 ca iesire pentru LED.
Vom obtine :
;******************************************************************
; INITIALIZARI *
;******************************************************************
init
clrf PORTA ; iesiri portA pe 0
clrf PORTB ; iesiri portB pe 0
clrf EEADR ; permite reducerea consumului
BANK1 ; trecere in bancul 1
movlw OPTIONVAL ; incarcare masca optiuni
movwf OPTION_REG ; initializare registru optiuni
; Stergere RAM
; ----------------
movlw 0x0c ; initializare indicator locatie
movwf FSR ; indicator de adresare indirecta
init1
clrf INDF ; sterge RAM
incf FSR , f ; indica urmatoarea locatie
btfss FSR , 6 ; testeaza atingere sfirsit zona (>=0x40)
goto init1 ; nu, bucleaza
btfss FSR , 4 ; testeaza atingere sfirsit zona (>=0x50)
goto init1 ; nu, bucleaza
; configurare PORTURI
; -------------------
bcf LED ; RA2 ca iesire (TRISA)
Remarca :
O mare parte a erorilor din programe sint provocate de erori de selectie a bancurilor (mai
ales pentru PIC®-urile cu 4 bancuri). Eu va sfatuiesc sa adoptati urmatoarea conventie :
totdeauna sa intrati intr-o rutina cu bancul 0, si totdeauna sa va asigurati ca sinteti in bancul 0
inainte de a iesi. In caz de derogare de la aceasta regula, precizati acest lucru clar in antetul
subrutinei.
Porniti compilarea pentru a verifica daca nu s-au strecurat erori. Va trebui sa obtineti ceva
de genul (numarul de linie poate sa difere) :
114
Message[302] D:\DOCUME~1\LESSONS\DATAPIC\MYINTER.ASM 143 : Register in
operand not in bank 0. Ensure that bank bits are correct.
Build completed.
In continuare, vom trece la realizarea unui intrerupator basculant. Ce este acesta ? Ei bine,
vom realiza functia urmatoare : o apasare pe buton va aprinde LED-ul, iar o a doua apasare il
va stinge. Va voi indruma pas cu pas in realizarea programului, incercind sa va arat
problemele practice pe care le puteti intilni la o realizare de acest tip.
Va reamintesc ca acesta este un program didactic. Vom realiza acest intrerupator basculant
intr-un mod mai elegant in lectia despre timerul tmr0.
Precizare importanta :
In nici un caz nu puteti lasa un program « sa-si faca de cap » in afara zonei programului
vostru. Daca un program n-are nimic de facut, va trebui atunci sa-l buclati catre el insusi, caci
programul nu se opreste (in afara de cazul in care trece in mod sleep, dupa cum vom vedea
mai departe).
start
;******************************************************************
; PROGRAM PRINCIPAL *
;******************************************************************
start
nop ; instructiune inutila
nop ; instructiune inutila
nop ; instructiune inutila
nop ; instructiune inutila
nop ; instructiune inutila
goto start ; bucleaza
END ; directiva sfirsit de program
115
12.13 Constructia rutinei de intrerupere
Am programat PIC®-ul in asa fel incit la aparitia unui front descendent pe intrarea
INT/RB0 sa se produca o intrerupere. Este suficient deci ca sa efectuam inversarea starii
LED-ului in cadrul rutinei care trateaza aceasta intrerupere. Acest lucru se realizeaza foarte
simplu cu un « SAU exclusiv ». Putem deci scrie :
;******************************************************************
; INTRERUPERE RB0/INT *
;******************************************************************
;------------------------------------------------------------------
; inverseaza nivelul pe RA2 la fiecare trecere
;------------------------------------------------------------------
intrb0
movlw B'00000100' ; bitul care se va inversa
BANK0 ; caci nu se stie in ce banc sintem in
; momentul intreruperii (programul principal
; ar fi putut sa schimbe bancul). Nu este
; cazul aici, insa este o precautie
; inteleapta
xorwf PORTA , f ; inverseaza RA2
return ; sfirsit intrerupere RB0/INT
Iata, prima noastra incercare este gata. Vom rula acest program pe simulator. Mai
inainte insa, va voi prezenta schema logica a programului pe care l-am realizat. Atunci cind
programele voastre vor deveni mai complexe, va sfatuiesc sa apelati la o schema logica
inainte de a scrie vreo instructiune.
Nu uitati ca simulatorul trebuie sa fie activ, in caz contrar, activati-l (vedeti capitolul
despre debugger). Selectati « special functions registers » , daca nu ati facut-o deja.
116
apoi apasati <F7>. Apasati repetat <F7> urmarind evolutia programului. Nu uitati ca el va
efectua 68 de bucle pentru a sterge RAM-ul, deci aveti rabdare. Profitati de acest ragaz pentru
a observa comportamentul registrului FSR in adresarea indirecta. Puteti deasemenea sa
apasati <F9>, apoi apasati <F5> dupa citeva momente.
Odata ajuns in programul principal, programul va rula intr-o bucla infinita. Ca urmare,
pentru a intra in rutina de intrerupere, va fi necesar un eveniment exterior (apasare buton). As
dori sa va explic acum cum putem simula un eveniment exterior.
Mergeti in meniul Debugger => Stimulus. Daca din intimplare aveti mesaje de eroare
despre vreun fisier, ignorati-le. Selectati tab-ul « Pin stimulus »
Apasati pe « Add Row » pentru a crea un buton de actionare. In fereastra se creaza o noua
linie, cu un buton denumit « Fire ». Faceti click odata in coloana « Pin », imediat linga buton.
Casutele « Pin » si « Action » se vor completa automat. Largiti coloanele cu mouse-ul pentru
o vizualizare mai usoara.
Vom preciza acum actiunea butonului nostru. In casuta « Type » vom alege « Asynch »
pentru "asincron". Prin urmare, evenimentul poate interveni in orice moment al executiei
programului. Pentru moment, nu faceti click pe butonul « Fire ».
Din meniul derulant de la casuta « Pin », vom determina care pin va fi stimulat prin
actiunea butonului. Cum butonul nostru se gaseste pe pinul RB0, selectati acest pin.
Daca va uitati la casuta « Action », veti vedea ca aveti acces la modul de functionare al
butonului. Puteti alege intre "Pulse" (genereaza un impuls), "Low" (pune un nivel 0), "High"
(pune un nivel 1) sau "Toggle" (inverseaza nivelul la fiecare apasare). Pentru acest exemplu,
noi vom alege optiunea cea mai putin practica, dar cea mai explicita.
Alegeti deci « Low ». Acum, butonul de stimulare este configurat. Va trebui sa obtineti o
fereastra ca cea de mai jos :
117
Creati acum o noua linie, cu « Add Row », si creati un al doilea buton, dar cu parametrul
« High » in casuta "Action". Puteti sa adaugati si un comentariu in casuta « Comment ». Nu
va jenati…
118
Observam deci ca atunci cind butonul nu este apasat, pe pinul RB0 avem un nivel 1
datorita rezistentei pull-up pe care noi am activat-o.
Sa apasam al doilea buton o singura data si sa mergem in editor. Apasam <F7> pentru a
avansa un pas si sa validam modificarea de nivel. Examinati PORTB : RB0 acum in 1.
Butonul nostru nu a fost apasat. Apasati de citeva ori <F7> pentru a verifica ca nu s-a mai
intimplat nimic altceva.
veti constata ca LED-ul s-a aprins (RA2 a trecut in 1 in registrul PORTA). Avansati incet pina
la linia :
si nu mai apasati <F7>. In acest loc, gasim intoarcerea din rutina de intrerupere in programul
principal. Apasati inca odata <F7>.
Pentru a provoca o noua intrerupere, trebuie ca bitul "Enable" SI bitul "Flag" al unei surse
de intrerupere sa fie in 1. Ori, nu este decit un singur bit in 1, si acesta este INTE.
Sa examinam deci INTF (este bitul 1 din INTCON). Acest bit este intotdeauna 1, deci, o
noua intrerupere. Am comis deci o eroare clasica : am uitat sa stergem flag-ul la sfirsitul
tratarii intreruperii noastre. Remarcati ca aceasta stergere este integrata in portiunea « switch
» din rutina de intrerupere din fisierul m16f84.asm. Noi am sters aceasta linie din neatentie cu
ocazia suprimarii diferitelor teste (de fapt l-am sters intentionat « din neatentie » : am
considerat ca se retine mai usor urmarea unei erori). Altfel spus, ati suprimat linia « fara stire,
dar cu voia dvs ».
119
; (programul principal ar fi putut sa schimbe
; bancul). Nu este cazul aici, insa este o
; precautie inteleapta
xorwf PORTA , f ; inverseaza RA2
bcf INTCON , INTF ; stergere flag INT/RB0
return ; sfirsit intrerupere RB0/INT
Sa recompilam programul nostru cu <F10>, apoi <F6>, dupa care reluati intreaga
procedura pe care am vazut-o.
Apasati al doilea buton pentru a simula eliberarea butonului principal. Apasati de citeva ori
<F7> si apasati <RB0 (L)> pentru a simula o a doua apasare a butonului principal. Urmariti
rutina de intrerupere si constatati ca de data aceasta LED-ul se stinge.
Ei bine, raspunsul este foarte simplu. PIC®-urile sint componente foarte rapide. Ele nu
lucreaza la aceeasi scara de timp ca noi. Sa incercam sa ne transformam intr-un PIC®. Vom
vedea atunci un BUTON enorm.
Atunci cind butonul este apasat, vom vedea o bara enorma care scurtcircuiteaza 2 contacte.
Aceasta bara este elastica. Ce vede PIC®-ul ? El vede o bara flexibila enorma care cade de la
o inaltime considerabila pe 2 contacte metalice. Odata facut contactul, bara va vibra de mai
multe ori. La fiecare apasare a butonului, PIC®-ul vede deci o serie de inchideri si deschideri
succesive, in loc de una singura, conform scarii noastre de timp.
120
12.17 Problema eliminarii vibratiilor
Cum remediem aceasta problema ? Ei bine, foarte simplu, asteptind un timp mai
indelungat decit durata vibratiilor inainte de a autoriza o noua intrerupere pe RB0.
Sa folosim cunostintele noastre actuale pentru a rezolva aceasta problema. Este foarte util
sa desenam o schema logica a ceea ce vom face.
Explicatii :
Programul principal isi face in mod normal initializarile sale, dupa care testeaza daca a fost
introdusa o cerere de temporizare prin pozitionarea flag-ului « tempo ». Daca flagul nu este
activ, bucla ruleaza la infinit.
Daca este apasat butonul, apare o intrerupere, RA2 este inversat, rutina de intrerupere pune
flag-ul « tempo » pe 1 si interzice orice noua intrerupere pe RB0. Orice actiune asupra lui
RB0 va ramine deci fara efect. (deci vibratiile nu mai sint luate in consideratie).
Dupa scurgerea timpului necesar incetarii vibratiilor, flag-ul « tempo ». este anulat (pentru
a nu rula la infinit), si intreruperile sint din nou activate, pentru a putea trata o noua apasare de
buton.
Vedeti deci aici, ca uneori poate fi util sa blocam momentan intreruperile dupa care sa le
deblocam in anumite momente specificate.
121
12.18 Finalizarea programului
;******************************************************************
; SUBRUTINA DE TEMPORIZARE *
;******************************************************************
;------------------------------------------------------------------
; Aceasta subrutina introduce o intirziere de 500.000 µs.
; Ea nu necesita nici un parametru si nici nu returneaza vreounul.
;------------------------------------------------------------------
tempo
movlw 2 ; pentru 2 bucle
movwf cmpt3 ; initializeaza contor 3
bucla3
clrf cmpt2 ; sterge contor 2
bucla2
clrf cmpt1 ; sterge contor 1
bucla1
nop ; pierde 1 ciclu *256 *256 *2
decfsz cmpt1 , f ; decrementeaza contor 1
goto bucla1 ; daca nu e 0, bucleaza
decfsz cmpt2 , f ; daca e 0, decrementeaza contor 2
goto bucla2 ; daca cmpt2 nu e 0, reia bucla 1
decfsz cmpt3 , f ; daca e 0, decrementeaza contor 3
goto bucla3 ; daca cmpt3 nu e 0, reia bucla 2
return ; revenire din subrutina
Vom modifica putin aceasta subrutina. Putem renunta la bucla exterioara, caci 500ms este
mult prea mult fata de timpul de vibratie al butonului. Renuntam deasemena si la
instructiuneile NOP. Vom obtine :
;******************************************************************
; SUBRUTINA DE TEMPORIZARE *
;******************************************************************
;------------------------------------------------------------------
; Aceasta subrutina introduce o intirziere. Ea nu necesita
; nici un parametru si nici nu returneaza vreounul.
;------------------------------------------------------------------
tempo
clrf cmpt2 ; sterge contor 2
bucla2
clrf cmpt1 ; sterge contor 1
bucla1
decfsz cmpt1 , f ; decrementeaza contor 1
goto bucla1 ; daca nu e 0, bucleaza
decfsz cmpt2 , f ; daca e 0, decrementeaza contor 2
goto bucla2 ; daca cmpt2 nu e 0, reia bucla 1
return ; revenire din subrutina
Am spus ca nu ne mai trebuie variabila cmpt3. O putem deci sterge din zona de variabile.
Vom avea totusi nevoie de un flag, adica de un bit. Sa-l creem in aceasta zona.
122
;****************************************************************
; DECLARARE VARIABILE *
;****************************************************************
;******************************************************************
; PROGRAM PRINCIPAL *
;******************************************************************
start
btfss tempoF ; testeza daca flag-ul tempo e 1
goto start ; nu, asteapta pina devine 1
call tempo ; da, executa tempo
bcf tempoF ; sterge flag tempo
bsf INTCON , INTE ; reactiveaza intreruperile INT
goto start ; bucleaza
END ; directiva sfirsit de program
;******************************************************************
; INTRERUPEREA RB0/INT *
;******************************************************************
;------------------------------------------------------------------
; inverseaza nivelul pe RA2 la fiecare parcurgere
; interzice orice intrerupere noua si valideaza flag-ul tempo
;------------------------------------------------------------------
intrb0
movlw B'00000100' ; bitul care se va inversa
BANK 0 ; caci nu se stie in ce banc sintem
; (programul principal ar fi putut sa
; schimbe bancul). Nu este cazul aici,
; insa este o precautie inteleapta
xorwf PORTA,f ; inverseaza RA2
bcf INTCON,INTF ; sterge flag INT/RB0
bcf INTCON,INTE ; blocheaza alte intreruperi RB0
bsf tempoF ; flag tempo = 1
return ; sfirsit intrerupere RB0/INT
123
Sa compilam programul nostru si sa incarcam noul fisier .hex in PIC®. Sa cuplam
alimentarea. Tot nu functioneaza de fiecare data... De ce ? Sa ne gindim la ce se intimpla.
Odata rutina de intrerupere incheiata, intreruperile sint validate. Contactul vibreaza fara sa
determine un apel de intrerupere. DAR FLAG-UL SAU ESTE PUS IN 1 din cauza vibratiilor.
Amintiti-va ca bitii "Enable" nu actioneaza asupra flag-urilor. Deci, imediat ce programul
principal revalideaza intreruperile, este generata direct o intrerupere deoarece INTF era deja
pus in 1.
Va trebui deci sa va ginditi la toate atunci cind folositi intreruperile. Daca un program
functioneaza pe simulator dar nu si in circuitul real, incepeti prin a suspecta problemele de
temporizare.
Este deci suficient sa adaugam o instructiune care sa reseteze flag-ul INTF inainte de a
reactiva intreruperile.
De data aceasta, totul functioneaza perfect. Iata deci, un montaj foarte practic pe care-l
puteti folosi. Daca inlocuiti LED-ul printr-un mic releu, sau un triac cu optocuplor, ati realizat
deja un intrerupator basculant. Puneti un buton in fiecare loc din care doriti sa aprindeti becul,
si veti putea sa-l stingeti sau sa-l aprindeti dintr-o multime de locuri, fara sa folositi
intrerupatoare speciale cu o gramada de fire.
Amintiti-va ca atunci cind folositi intreruperile intr-un program, nu veti putea niciodata sa
stiti timpul care separa 2 instructiuni succesive.
Ca urmare, intre cele doua instructiuni pot fi tratate una sau mai multe rutine de
intrerupere.
In plus, pentru orice secventa a carei derulare in timp este critica si nu poate fi intrerupta,
dvs. va trebui sa inhibati intreruperile. Ramine in sarcina voastra sa le reactivati la momentul
potrivit.
Acest lucru explica de ce gestionarea proceselor critice in timp real necesita mai multe
calculatoare (pe navetele spatiale, de exemplu). Aceste calculatoare nefiind sincronizate, o
eroare de aceasta natura aparuta pe unul dintre ele, are putine sanse de a se produce simultan
si pe al doilea. Pentru a sti care este cel cu probleme, a fost deci necesar un cel de-al treilea
calculator.
Ginditi-va tot timpul la acest lucru daca veti fi pusi in situatia de a realiza un program de
care sa depinda siguranta unor oameni sau bunuri.
Daca veti intelege bine toate acestea, veti vedea ca eventualele bug-uri legate de erorile de
programare pot aparea in orice moment, intr-un mod care pare aleatoriu. Si va mai mirati ca
PC-ul vostru se blocheaza ?
125
12.20 Concluzii
Retineti numai ca, parasiti lumea sincrona pentru a intra in lumea asincrona, cu mult mai dificil de
testat complet intr-un mod eficace.
De aceea, va sfatuiesc foarte serios ca, atunci cind realizati programe complexe, sa incepeti prin a
scrie un algoritm general, fie in limbaj obisnuit, fie folosind o schema logica.
13. Timer-ul 0
In acest capitol vom vorbi despre temporizari si numarari. 16F84 nu are decit un singur
timer de 8 biti, spre deosebire de alte PIC®-uri din familie (ca de exemplu 16F876). Daca
examinam atent functinarea timer-ului 0, vom vedea de fapt ca este vorba de un numarator.
Alegerea unuia sau altuia dintre aceste doua moduri se face prin bitul 5 din registrul
OPTION : T0CS (Tmr0 Clock Source select bit).
In cazul in care ati hotarit sa lucrati in mod numarator, va trebui sa precizati pe ce front se
va face numararea. Acest lucru e precizat de bitul 4 din registrul OPTION : T0SE (Tmr0
Source Edge select bit).
126
13.2 Registrul tmr0
Acest registru, care se gaseste la adresa 0x01 in bancul 0, contine pur si simplu valoarea
curenta a timer-ului 0. Puteti citi sau scrie aceasta valoare. Daca, de exemplu, ati configurat
tmr0 ca numarator, citirea registrului tmr0 va va da numarul de impulsuri aplicate pe pinul
RA4/T0CKI.
Cum folosim timer-ul 0, si care sint posibilitatile oferite la acest nivel, vor fi subiectele
despre care vom vorbi aici.
Prima metoda care ne vine in minte este urmatoarea : citim registrul tmr0 pentru a vedea
ce contine. Valoarea citita reflecta numarul de evenimente survenite, tinind cont de faptul ca
tmr0 nu poate numara decit pina la 255. In caz de depasire, tmr0 reincepe numararea de la 0.
Va ramine voua sarcina de a exploata aceasta posibilitate.
Un mic exemplu :
La acest nivel va trebui sa stim, ca orice depasire a timer-ului 0 (trecere din 0xFF in 0x00)
antreneaza punerea in 1 a flag-ului T0IF din registrul INTCON. Veti putea deci sa folositi
acest flag pentru a vedea daca ati avut o depasire a timer-ului 0, sau, cu alte cuvinte, daca
timpul programat s-a scurs. Aceasta metoda are dezavantajul ca se pierde timp inutil.
Un mic exemplu :
Dar dvs. ati putea spune ca nu doriti sa asteptati obligatoriu 256 de incrementari ale lui
tmr0. Sa presupunem ca doriti sa asteptati 100 incrementari. Pentru aceasta este suficient sa
puneti in tmr0 o astfel de valoare incit dupa 100 de incrementari, tmr0 sa efectueze o
depasire.
127
Exemplu :
Acest lucru ar putea parea in regula, dar, de fapt, orice schimbare a lui TMR0
conduce la o oprire a numararii din partea acestuia, pe o durata echivalenta cu 2 cicluri
instrucţiune. Daca exemplul precedent nu foloseste predivizorul, va trebui deci sa tinem
cont de aceasta pierdere punind « 256-98 » si nu « 256-100 » in timer
Acesta este evident, modul principal de folosire al timer-ului 0. Prin urmare, atunci cind in
registrul INTCON, T0IE este pus in 1, de fiecare data cind flag-ul T0IF trece in 1, se
genereaza o intrerupere. Se va folosi o procedura la fel ca cea din lectia privind intreruperile.
Am folosit deci intreruperile pentru multiplii de 256, si citirea directa a lui tmr0 pentru
« unitati ».
13.4 Predivizorul
Hotarim sa folosim timer-ul 0 in mod timer si in mod intrerupere. Vom avea deci o
intrerupere la fiecare 256 µs, adica putin peste un sfert de miime de secunda.
Daca vrem sa realizam un LED clipitor cu o frecventa de 1 Hz, vom avea nevoie de o
temporizare de 500 ms, adica de 2000 ori mai mare decit putem obtine. Nu este deci practic.
Pentru a usura situatia, dispunem de un PREDIVIZOR.
Ce este acesta ? Nimic altceva decit un divizor de impulsuri situat INAINTEA intrarii de
numarare a lui tmr0. Putem deci alege sa avem un impuls pe tmr0 la fiecare 2 evenimente
128
aparute, sau la fiecare 64 de evenimente (de exemplu).
Priviti tabelul de la pagina 16 din datasheet. Veti vedea in partea de jos a tabelului bitii de
la PS0 la PS2 din registrul OPTION care determina valoarea predivizorului.
Aceste valori variaza, pentru tmr0, intre 2 si 256. Bitul PSA, determina daca predivizorul
deserveste timer-ul 0 sau watchdog-ul. Iata mai jos un tabel care exprima toate posibilitatile
acestor biti :
Observatii importante :
Ø Nu exista decit un singur predivizor, care poate fi alocat fie timer-ului, fie watchdog-
ului (pe care-l vom studia un pic mai tirziu). El nu poate fi alocat ambelor, in acelasi
timp.
Ø Nu exista un predivizor = 1 pentru tmr0. Daca nu doriti sa folositi predivizorul, va
trebui deci obligatoriu sa-l alocati watchdog-ului, cu valoarea 1 (linia verde din tabel).
Ø Valoarea continuta in predivizor nu este accesibila. De exemplu, daca decideti sa
folositi un predivizor de 64, si acesta are la un moment dat numarate deja 30 de
evenimente, nu aveti nici un mijloc de a sti acest lucru. Predivizorul limiteaza deci
precizia in cazul citirii directe.
Ø Scrierea in registrul tmr0 STERGE continutul predivizorului. Evenimentele numarate
deci pina in acel moment de catre predivizor, vor fi pierdute.
Ø Toate modificarile lui TMR0 (stergere, incrementare, operare...) vor produce o oprire a
timer-ului corespunzind la 2 incrementari viitoare, ceea ce duce la numararea cu 2
unitati mai putin decit era prevazut.
129
13.5 Aplicatie practica a timer-ului 0
Vom pune la treaba timer-ul 0 intr-o prima aplicatie practica. Vom relua primul nostru
exercitiu, acela de a face un LED sa clipeasca cu o frecventa aproximativa de 1 Hz.
13.5.1 Pregatiri
;******************************************************************
; Faceti sa clipeasca un LED cu o frecventa aproximativa de 1 Hz *
; *
;******************************************************************
; NUME: LED CLIPITOR CU TIMER0 *
; Data: 17/02/2001 *
; Versiune: 1.0 *
; Circuit: Placa de test *
; Autor: Bigonoff *
;******************************************************************
; Fisier necesar: P16F84.inc *
;******************************************************************
; Note: Folosirea didactica a tmr0 in mod intrerupere *
;******************************************************************
Fara divizor, timer-ul 0 genereaza o intrerupere la fiecare 256 µs. Va trebui deci sa folosim
predivizorul. Daca luam cea mai mare valoare disponibila, adica 256, vom avea o intrerupere
la fiecare (256 x 256) = 65536 µs.
Va trebui deci sa trecem de (500.000/65536) = 7,63 ori prin rutina de intrerupere. Cum noi
nu putem avea un numar fractionar de treceri, vom alege 7 sau 8, urmind sa acceptam o
eroare, intr-un sens sau altul.
Notati ca daca treceti de 7 ori, veti avea un timp mai scurt, pe care puteti sa-l prelungiti
oricind. In caz contrar, timpul va fi prea lung, si nu se mai pot face corectii.
Este evident ca acceptarea unei erori este in functie de aplicatie. Daca doriti sa faceti sa
clipeasca o ghirlanda pentru pomul de Craciun, eroarea de temporizare va fi nesemnificativa.
Daca din contra, doriti sa construiti un cronometru, o astfel de eroare ar fi inacceptabila. Vom
incepe deci prin a ignora eroarea.
130
Reluind tabelul de la pagina 16 privitor la continutul registrului OPTION, va trebui sa
initializam acest registru cu B’10000111’, adica 0x87. Aceasta inseamna, rezistentele pull-up
dezactivate (nu sint necesare), timer 0 pe sursa interna si predivizor pe tmr0 cu valoarea 256.
Vom obtine deci :
In continuare, definitiile :
;******************************************************************
; DEFINITII *
;******************************************************************
#DEFINE LED PORTA,2 ; LED
Nu ne vom atinge de rutina noastra principala de intrerupere, caci avem suficient spatiu
pentru a pastra testele.
Sa scriem deci rutina noastra de intreruperi pentru timer.Vedem mai intii ca va trebui sa
numaram trecerile prin rutina tmr0, deci vom avea nevoie de o variabila. Sa o declaram in
zona 0x0C.
13.5.2 Initializarea
Fiind mult mai usor de detectat o valoare egala cu 0 decit cu 7, noi vom decrementa deci
variabila noastra de la 7 la 0. Vom inversa starea LED-ului odata ce este atinsa valoarea 0. Va
trebui deci sa initializam variabila noastra cu 7 pentru prima trecere.
Vom face acest lucru in rutina de initializare, inainte de goto start. Sa profitam si sa
declaram aici si portul LED-ului ca iesire.
Vom obtine :
;******************************************************************
; INITIALIZARI *
;******************************************************************
init
clrf PORTA ; iesiri portA pe 0
clrf PORTB ; iesiri portB pe 0
clrf EEADR ; permite reducerea consumului
BANK1 ; trecere in bancul 1
movlw OPTIONVAL ; incarca masca
movwf OPTION_REG ; initializare registru optiuni
131
; stergere RAM
; ------------
movlw 0x0c ; initializare indicator locatii
movwf FSR ; indicator de adresare indirecta
init1
clrf INDF ; sterge RAM
incf FSR,f ; indica urmatoarea locatie
btfss FSR,6 ; testeaza atingere sfirsit zona (>=0x40)
goto init1 ; nu, bucleaza
btfss FSR,4 ; testeaza atingere sfirsit zona (>=0x50)
goto init1 ; nu, bucleaza
; initializare porturi
; --------------------
bcf LED ; pune LED-ul ca iesire
BANK0 ; trecere in bancul 0
movlw INTERMASK ; masca de intreruperi
movwf INTCON ; incarca control intreruperi
; initializare variabile
; ----------------------
movlw 7 ; incarca 7
movwf cmpt ; initializare contor de treceri
goto start ; salt in programul principal
Mai departe, daca rezultatul e nul, va trebui sa inversam starea LED-ului si sa reincarcam 7
in numaratorul de treceri. Iata rezultatul final :
;******************************************************************
; INTRERUPERI TIMER 0 *
;******************************************************************
inttimer
decfsz cmpt,f ; decrementeaza contorul de treceri
return ; nu e 0, nu se face nimic
BANK0 ; trecere in banc 0 pentru precautie
movlw b'00000100' ; alege bitul de inversat
xorwf PORTA,f ; inverseaza stare LED
movlw 7 ; pentru 7 noi treceri
movwf cmpt ; incarca contorul de treceri
return ; sfirsit intrerupere timer
132
din programul principal, deoarece watchdog-ul este dezactivat.
Observatii :
Ø Ultimul registru din fereastra registrelor speciale « T0pre » este un registru care nu
exista fizic in PIC®. El este folosit de MPLAB® pentru a calcula predivizarea, in
cursul simularii. Totusi acest registru nu mai apare in versiunile de MPLAB®
superioare lui 5.x, pentru PIC®-urile 16F cit si pentru cele 18F.
Ø De fiecare data cind « T0pre » atinge valoarea de predivizare, tmr0 este incrementat
cu 1. Aceasta inseamna ca atunci cind avem o depasire a acestui registru, vom avea o
intrerupere.
Ø In programul principal, « T0pre » este incrementat cu 2 unitati atunci cind apasam
<F7>. Este normal, caci acest program se comporta ca un salt (goto), si fiecare salt
necesita 2 cicluri.
Cum noi nu vom sta ore in sir ca sa simulam acest program, vom modifica registrele in
cursul simularii. Acest lucru se face foarte simplu in MPLAB6, nefiind necesar decit sa facem
dublu click pe valoarea pe care dorim sa o modificam, in fereastra «special function register».
In plus, puteti face click in coloana in care doriti, astfel incit puteti introduce valori in
zecimal, hexazecimal sau binar.
Vom folosi aceasta posibilitate. Mai intii, sa suprimam predivizorul. Pentru aceasta, vom
scrie B’10001000’, adica 0x88. Faceti dublu click in casuta « hex » din linia OPTION_REG.
Incarcati fisierul .hex obtinut in PIC®-ul vostru si conectati alimentarea placutei. Numarati
aprinderile LED-ului in timp de 1 minut. Va trebui sa gasiti ceva in jurul a 65 ÷ 66 de
impulsuri pe minut. Aceasta va arata precizia obtinuta.
In realitate, veti avea o aprindere la fiecare (256 x 256 x 7 x 2) = 917.504 µs. Intr-un
minut, va trebui sa avem 60.000.000/917.504 = 65,3 aprinderi. Teoria se confirma in practica.
Fisierul il aveti anexat cursului sub denumirea « led_tmr1.asm ».
133
13.8 O prima imbunatatire a preciziei
Vom incerca acum sa imbunatatim precizia programului nostru. Putem incepe prin
modificarea predivizorului. Sa incercam mai multe valori succesive :
Intr-un minut, vom avea deci 60.000.000/999.424 = 60,034 aprinderi. Iata deci o precizie
net superioara.
Puteti acum sa va modificati programul singuri, dupa aceste indicatii. Observati ca va
trebui sa modificati valoarea 07 in 244 in 2 locuri, ceea ce nu e prea practic. Adaugati deci o
asignare, ca de exemplu :
Daca aveti vreo problema, fisierul functional al acestui exercitiu este disponibil sub
denumirea « Led_tmr.asm ».
Bineinteles ca pentru a realiza temporizarea de 32 µs, care este foarte scurta, va trebui sa
optimizam la maximum rutinele de intrerupere. Vom suprima testele si sub-programele, etc.
Dar acestea, ramin in general la limita posibilului. Nu vom trata acest procedeu aici, caci nu
prezinta interes din moment ce intreruperile vor ajunge sa ocupe majoritatea timpului CPU.
134
13.10 Metoda corecta : adaptarea ceasului
Sa presupunem ca doriti sa realizati un cronometru. In acest caz, precizia este lucrul cel
mai important, viteza trecind pe locul doi. Vom face in asa fel incit demultiplicatoarele sa
cada pe valori intregi. Cum ? Foarte simplu, schimbind durata unei instructiuni, deci
schimbind cuartul PIC®-ului.
Exemplu :
Cum noi nu putem accelera un PIC® peste viteza sa maxima (noi folosim un PIC® de 4
MHz), noi putem numai sa-l incetinim. Plecam deci de la o baza de timp prea rapida.
De exemplu : sa reluam cazul nostru initial : predivizare cu 256, numarator de treceri cu
valoarea initiala 7. Durata obtinuta cu un cuart de 4 MHz : 256 x 256 x 7 per temporizare,
deci 256 x 256 x 7 x 2 per aprindere, adica 917.504 µs. Ori, noi dorim 1.000.000 µs. Va fi
deci suficient sa facem calculul invers. Cit trebuie sa dureze o instructiune ?
Deci daca gasiti un cuart de frecventa corespunzatoare, veti obtine un ceas cu precizia
cuartului. Mai aveti de adaugat un afisaj si gata ceasul !
In acest mic exemplu noi vom folosi 2 surse de intrerupere diferite, cu scopul de a va arata
un exemplu concret privind acest mod de utilizare. Vom recrea programul nostru pentru
intrerupatorul basculant, inlocuind temporizarea printr-o intrerupere pe tmr0.
135
Remarcati ca programul nostru principal nu face nimic. Veti putea deci sa folositi alte
posibilitati in aceasta diagrama fara a perturba functionarea intrerupatorului basculant.
Vom avea deci o intrerupere pentru RB0, si una pentru tmr0. Aveti deasupra schema
logica de care ne vom folosi.
Faceti o copie a fisierului vostru m16f84.asm si redenumiti-l « telerupt.asm ». Creati un
nou proiect « telerupt.pjt ». Editati fisierul la fel ca mai inainte : anulati watchdog-ul, setati
LED-ul ca iesire, activati initial intreruperile pe RB0/INT.
Creati o rutina de intrerupere pentru tmr0 de 260 ms, adica predivizare de 256 si 4 treceri.
Incercati sa realizati singuri acest program. Incarcati-l in PIC® si rulati-l. Vedeti ca schema
logica nu cuprinde si numaratorul de treceri pentru tmr0. Va las sa reflectati.
O apasare pe buton aprinde LED-ul, o alta, il stinge. Daca nu merge, cautati greseala sau
folositi simulatorul. Va dau si programul functional, pentru cazul in care v-ati impotmolit.
Observatie :
Este foarte important sa intelegeti bine ca tmr0 trebuie sters INAINTE de a sterge flag-ul
T0IF si de a reactiva intreruperile tmr0.
Daca procedati altfel, riscati ca tmr0 sa depaseasca intre momentul stergerii flag-ului si
momentul stergerii lui tmr0. In acest caz flag-ul va fi pus in 1 imediat dupa ce a fost sters.
Programul vostru ar putea deci sa dea rateuri intermitente.
Nu uitati ca orice modificare lui TMR0 duce la pierderea a 2 incrementari prevazute.
136
13.13 Concluzie
Stiti acum sa folositi timer-ul 0. Metodele pomenite aici sint o baza de plecare pentru
aplicatii mai serioase. Va sfatuiesc calduros sa realizati practic toate aceste exercitii. Chiar si
din greseli puteti invata cite ceva...
137
Note:…
138
14. Accesele in memoria « EEPROM »
Va voi vorbi in acest capitol despre procedurile de acces la EEPROM-ul intern al PIC®-
ului. Acesta nu trebuie confundat cu scrierea intr-un EEPROM extern de tip 2416 (de
exemplu). Pentru acest tip de EEPROM este suficient sa respectam indicatiile din datsheet-ul
componentei respective, despre care va voi vorbi dealtfel in partea a doua a cursului.
Adresa fizica a zonei EEPROM incepe, pentru PIC®-urile mid-range, de la adresa 0x2100.
Aceasta adresa este situata in afara spatiului normal de adresare al PIC®-urilor (amintiti-
va, 8 K-cuvinte, adica o adresa maxima de 0x1FFF), deci deja deducem ca avem nevoie de o
procedura speciala pentru a o accesa.
Retineti faptul ca, chiar daca aceste locatii nu sint direct accesibile prin program, din
contra, ele sint accesibile in momentul programarii. Veti putea deci sa scrieti EEPROM-ul in
momentul programarii PIC®-ului.
Acest lucru este valabil si pentru registrele speciale ale PIC®-ului. De exemplu, adresa
0x2007 contine parametrii pe care-i scrieti in _CONFIG. Veti putea deci inlocui aceasta
directiva printr-o scriere directa la adresa 0x2007. Nu va sfatuiesc sa procedati astfel din
motive de portabilitate si de evolutie rapida catre o alta familie de PIC®-uri. In plus, de ce sa
ne complicam, cind se poate mai simplu ?
;******************************************************************
; PROGRAM DE CLIPIRE A UNUI LED CU O FRECVENTA CE DEPINDE *
; DE O VALOARE STOCATA IN EEPROM *
;******************************************************************
; NUME: LED CLIPITOR CU TIMER0 SI FOLOSIRE EEPROM *
; Data: 18/02/2001 *
; Versiune: 1.0 *
; Circuit: Placa de test *
; Autor: Bigonoff *
;******************************************************************
; Fisier necesar: P16F84.inc *
;******************************************************************
139
; Note: Demonstratie de folosire a datelor din EEPROM *
; Baza de timp pentru clipire este continuta in EEPROM *
;******************************************************************
In programul nostru initial, de fiecare data cind numaratorul de treceri a timer-ului ajungea
loa 0, il reincarcam cu valoarea 0x07. Acum, il vom incarca cu valoarea continuta in variabila
« reload ». Procedura folosita va fi urmatoarea :
Avantajul acestei proceduri : daca se modifica valoarea bazei de timp in EEPROM, aceasta
modificare nu se va mai pierde in momentul deconectarii alimentarii.
Mai jos, va dau schema logica a ceea ce vom realiza intr-o prima etapa.
Ati putea remarca ca ar putea parea inutil sa citim EEPROM-ul si de a recopia continutul
sau in reload. Deci, de ce nu folosim valoarea din EEPROM direct in restul programului ?
Raspunsul este simplu. Procedura de citire din EEPROM este mai complexa decit o simpla
citire din RAM. Va trebui deci sa limitam la maximum accesarea EEPROM-ului.
Vom incepe prin modificarea rutinei noastre de intrerupere. Singura linie de modificat este
aceea care incarca din oficiu valoarea 7 in W. Acum, vom incarca continutul din reload. Vom
avea deci :
140
inttimer
decfsz cmpt , f ; decrementeaza contorul de treceri
return ; dac nu e 0, nu se face nimic
BANK0 ; trecere in banc 0 pentru precautie
movlw b'00000100' ; alege bitul de inversat
xorwf PORTA , f ; inverseaza stare LED
movf reload , w ; incarca valoarea continuta in reload
movwf cmpt ; in contorul de treceri
return ; sfirsit intrerupere timer
;******************************************************************
; DECLARARE ZONA EEPROM *
;******************************************************************
org 0x2100 ; adresa de inceput a zonei EEPROM
DE 0x07 ; valoarea de reincarcare a contorului
Lansati compilarea programului dvs. Veti dori fara indoiala sa verificati daca EEPROM-ul
vostru contine valoarea 0x07. Nimic mai simplu : lansati « EEPROM memory » in meniul
« Windows » si veti vedea aceasta valoare.
Deci, ca sa rezumam, pentru a accesa adresa 0x2100, vom folosi procedura de acces
EEPROM cu adresa 0x00. Adresa 0x2101 va corespunde adresei 0x01, s.a.m.d.
Bineinteles ca puteti da un nume acestor adrese, la fel ca pentru variabile. Vom folosi deci
numele eereload pentru a desemna valoarea de reincarcare continuta in EEPROM la adresa
0x00. Vom adauga un define in zona de iniatializare a EEPROM-ului :
sau
141
In schema noastra logica, va trebui sa citim EEPROM-ul. Sint folosite 4 registre pentru a
accesa EEPROM-ul. Le vom examina pe rind.
Acest registru va tranzita datele care vor fi scrise sau care sunt citite din EEPROM. Acest
registru se gaseste la adresa 0x08, in bancul 0.
In acest registru, situat la adresa 0x09, in bancul 0, vom preciza pe 8 biti adresa la care se
refera operatia de citire sau scriere in EEPROM. Am vazut deja ca pentru aceasta familie de
PIC®-uri, nu putem depasi 256 de locatii de EEPROM. Pentru 16F84, zona admisibila se
gaseste intre adresele 0x00 si 0x3F, adica 64 de locatii.
Acest registru, situat la adresa 0x88, in bancul 1, contine 5 biti care definesc, sau indica
functionarea ciclurilor de citire / scriere in EEPROM. Iata semnificatia lor :
bit 4 : EEIF EEprom write operation Interrupt Flag bit. Este flag-ul
care este in legatura cu intreruperea EEPROM. El trece in 1
odata ce scrierea in EEPROM este incheiata. Daca bitul EEIE
din registrul INTCON este pe 1, atunci va fi generata o
intrerupere.
Observatie :
142
14.7 Registrul EECON2
Acesta este un registru « fantoma », din moment ce el nu exista fizic. Este vorba de fapt de
o simpla adresa, 0x89, din bancul 1, care serveste la trimiterea de comenzi PIC®-ului,
comenzi care se refera la procedurile EEPROM. Dvs. nu-l veti putea utiliza decit folosindu-va
de instructiunile explicate mai jos.
Pentru a citi date din EEPROM, e suficient sa puneti adresa respectiva in registrul EEADR.
In continuare, puneti bitul RD in 1 (citire). Datele citite le puteti recupera din registrul
EEDATA. Nu trebuie, bineinteles, sa uitam diferitele schimbari de bancuri.
Cum aceasta procedura este scurta si mereu aceeasi, vom crea un macro care sa realizeze
aceasta operatie. Cum macro-ul trebuie sa contina adresa de unde se citeste, vom realiza un
macro cu transfer de parametru. Iata macro-ul ce trebuie adaugat. Pentru a apela acest macro,
va trebui sa va gasiti in bancul 0, si ea va returna valoarea citita in registrul W.
Remarcati faptul ca dvs. furnizati un argument catre macro. Veti desemna acest (sau acesti)
parametrii dupa directiva macro.
Am folosit aici argumentul adeeprom pentru a indica adresa din EEPROM. Fiecare folosire
a lui adeeprom in macro-ul nostru va fi inlocuita prin argumentul primit in momentul apelarii
macro-ului.
Pentru a folosi acest macro, va trebui deci sa-i furnizam un argument. De exemplu, linia :
Incepind din lectia viitoare, acesta va inlocui fisierul vostru actual « m16F84.asm ».
; initializare variabile
; ------------------------
READEE eereload ; citeste locatie eeprom 0x00
movwf reload ; puneti-o in reload
movwf cmpt ; si initializati contorul de treceri
goto start ; salt in programul principal
143
frecventa de 1Hz. Daca nu merge, verificati si comparati cu fisierul « eep_test1.asm » furnizat
impreuna cu aceasta lectie.
Si acum, imi veti spune pe buna dreptate, ca nu ne serveste la nimic sa citim din EEPROM
daca nu scriem nimic in el. Programul nostru nu are nimic in plus fata de ce aveam mai
inainte. Aveti dreptate. Haideti deci sa studiem metoda de scriere in EEPROM. Cum va puteti
imagina, aceasta metoda foloseste aceleasi registre.
Procedura care urmeaza consta mai intii in punerea datelor in registrul EEDATA si adresa
in EEADR. Urmeaza o secventa specifica (n-aveti nimic de inteles, caci este impusa de
contructor) care trebuie trimisa PIC®-ului.
Observatii :
Vom scrie un macro pentru scrierea in EEPROM. De aceasta data, va trebui sa-i furnizam
macro-ului 2 parametrii, respectiv octetul ce va fi scris (pe care presupunem ca l-am pus in
W), si adresa locatiei unde se va scrie.
144
Observatii :
Ø Am folosit aici 3 instructiuni pentru a pune GIE pe 0. Acest lucru a fost necesar
datorita unui bug din 16C84, dar care a fost corectat in 16F84. Testul de verificare nu
mai este necesar incepind de la aceasta versiune. Acest lucru mi-a dat insa ocazia de a
va arata etichetele locale. Lasati-le deci asa, ca exemplu.
Ø Directiva LOCAL precizeaza ca simbolul folosit in acest macro nu va exista decit in
interiorul acesteia. Fara aceasta directiva, daca veti folosi un singur apel de macro
WRITEE in programul dvs., nu va fi nici o problema. Daca insa veti folosi de 2 ori
acest macro, pentru fiecare apel, MPASM® va inlocui WRITEE prin toata lista de
instructiuni continute pina la "endm". Veti avea deci in programul dvs. real de 2 ori
eticheta « loop », in doua locuri diferite. Acest lucru va genera o eroare. Directiva
LOCAL informeaza MPASM® ca fiecare folosire a macro-ului lucreaza cu o eticheta
« loop » diferita. In acest fel, nu mai exista deci dubii in folosire.
Ø Toata portiunea subliniata cu galben contine secventa impusa de catre Microchip
pentru scrierea EEPROM-ului.
Ø Procedura de scriere in EEPROM este relativ lunga si ia o multime de timp. Ginditi-va
ca pentru a scrie toti cei 64 octeti, ne trebuie peste 6 zecimi de secunda.
Ø Numarul de cicluri de scriere in EEPROM este limitat. Durata de viata a EEPROM-
ului este de circa 10 milioane de cicluri. Daca programul vostru are vreo eroare si
scrie fara incetare date in EEPROM, PIC®-ul vostru se va defecta in mai putin de 28
de ore. Fiti deci vigilenti si verificati-va atent programul.
Ø Inainte de a scrie in EEPROM, va trebui sa verificati daca nu cumva o alta scriere este
in desfasurare. Folositi pentru aceasta bitul WR din registrul EECON1. Daca acest bit
este 0, nu avem nici o scriere in curs.
Acum, vom modifica din nou programul nostru, pentru a putea scrie in EEPROM. Vom
incrementa durata temporizarii la fiecare 16 clipiri ale LED-ului (deci la fiecare 32 de treceri
prin rutina de intrerupere), si vom salva aceasta noua valoare in EEPROM.
N-avem nevoie aici de intreruperile generate de EEPROM, insa dvs. va trebui sa intelegeti
suficient de bine principiul intreruperilor, pentru a-l putea folosi in caz de nevoie.
Vom proceda deci la modificarea rutinei noastre de intrerupere. Va trebui sa adaugam un al
II-lea contor (cmpt2) in zona RAM. Sa declaram deci aceasta variabila :
Da, stiu, existau mijloace de optimizare pentru a nu folosi decit un contor. Nu acesta este
insa scopul acestui exercitiu. Sa incercam sa raminem clari si sa ne concentram asupra
subiectului acestui capitol.
In realitate, modificarea este foarte simpla. In rutina de intrerupere timer, vom incrementa
contorul 2 care va fi folosit in programul principal.
145
stirea programatorului. Era deci necesar, dupa punerea in 0 a lui GIE, de a verifica daca
aceasta punere in 0 s-a realizat efectiv. In caz contrar, daca s-ar produce o intrerupere in acest
loc, operatia ar fi reluata.
Incepind cu 16F84, intreruperile sint blocate imediat ce instructiunea este executata, deci
inainte de executia instructiunii urmatoare. Deci bug-ul nu mai exista.
;*******************************************************************
; INTRERUPERE TIMER 0 *
;*******************************************************************
inttimer
; testeaza contorul de treceri
; ----------------------------
decfsz cmpt , f ; decrementeaza contor de treceri
return ; daca nu e 0, nu face nimic
; inverseaza LED
; --------------
BANK0 ; pentru siguranta
movlw b'00000100' ; selecteaza bitul de inversat
xorwf PORTA , f ; inverseaza stare LED
146
return ; sfirsit intrerupere timer
Vom scrie acum in EEPROM din programul principal. Sint doua motive pentru care nu
scriem din rutina de intrerupere timer :
Ø Este necesar, in masura in care este posibil, sa sa iesim cit mai repede dintr-o rutina de
intrerupere, aceasta gasindu-se de regula intr-un program, care are de prelucrat urgent
si alte informatii.
Ø Tot ceea ce poate fi pus usor in alta parte, trebuie pus acolo. Nu putem debloca
intreruperile (GIE) dintr-o rutina de intrerupere. Ori, rutina noastra de scriere repune
GIE = 1. Daca ati vrea sa folositi aceasta rutina intr-o rutina de intrerupere, va trebui sa
suprimati in prealabil reactivarea lui GIE.
;******************************************************************
; PROGRAM PRINCIPAL *
;******************************************************************
start
; testeaza daca avem 16 inversari ale LED
; ---------------------------------------
btfss cmpt2 , 5 ; testeaza daca avem 32 de treceri
goto start ; daca nu, asteapta
clrf cmpt2 ; da, sterge contorul 2
; incrementeaza reload
; ------------------------
incf reload , f ; incrementeaza reload
incf reload , f ; incrementeaza de 2 ori, caci este mai
; vizibil
Atunci cind scrieti in zona EEPROM a unui PIC®, nu puteti fi siguri niciodata ca o nu va
apare o intrerupere de tensiune care sa intrerupa procedura. In consecinta, va trebui sa va
asigurati ca datele din EEPROM sint valide inainte de a le folosi la urmatorul restart.
Iata aici o procedura care va va permite sa fiti siguri de acest lucru.
Ø Puneti un antet in primul (sau primii) octet(i) din EEPROM-ul vostru. De exemplu,
putem pune 0x55 in octetul 0.
Ø Atunci cind scriem noi valori in EEPROM, vom incepe prin stergerea antetului nostru
Ø Vom scrie apoi datele noastre in EEPROM.
Ø Pentru a incheia, vom rescrie antetul in EEPROM.
Ø La restart-ul PIC®-ului, daca antetul este prezent, aceasta inseamna ca si datele din
EEPROM sint valide. In caz contrar, inseamna ca ciclul de scriere a fost intrerupt. In
acest caz, ramine la latitudinea dvs. cum sa procedati, de exemplu, rescriind in
EEPROM valorile implicite.
Ø Pentru marirea sigurantei sistemului pentru datele critice, puteti pune mai multi octeti
in antet.
14.12 Concluzii
Iata inca o etapa parcursa in cunoasterea lui 16F84. Acum puteti folosi zona EEPROM a
PIC®-ului dvs. pentru a va stoca datele remanente.
Nu ezitati sa experimentati, si nu uitati sa va verificati programul pentru a evita scrierile
inutile sau prea frecvente in EEPROM.
Totusi, nu intrati in panica, daca programul vostru este scris corect, sinteti departe de limita
de 1.000.000 de cicluri de scriere garantate.
15. Watchdog-ul
Functionarea watchdog-ului este legata de un timer intern special, care nu este sincronizat
nici cu programul si nici cu vreun eveniment extern.
Durata specifica de depasire a acestui timer este de aproximativ 18 ms. Aceasta valoare
trebuie acceptata cu precautie, caci ea variaza in functie de diversi parametri, ca tensiunea de
alimentare sau temperatura.
148
In practica, va trebui sa folosim o valoare minima de 7 ms. De fapt, Microchip va
garanteaza ca nici un PIC® nu se va reseta inainte de aceste 7 ms. Vi se indica faptul ca
timpul mediu de reset al PIC®-urilor este de 18 ms, insa nu vi se garanteaza acest timp, este
numai un « timp constatat in general ».
De fiecare data cind instructiunea clrwdt este trimisa catre PIC®, timer-ul watchdog-ului
este readus la 0, ca si valoarea continuta in predivizorul sau. Daca in mod accidental aceasta
instructiune nu a fost primita in intervalul prevazut, PIC®-ul este restartat de la adresa 0x00 si
bitul TO din registrul STATUS este pus in 0.
La restart, citind acest bit, aveti posibilitatea sa aflati daca PIC®-ul a pornit dupa o punere
sub tensiune, sau daca acest restart a survenit ca urmare a unei blocari a programului.
Am vazut in lectiile precedente ca predivizorul putea fi alocat fie lui tmr0, fie watchdog-
ului, prin intermediul bitului PSA din registrul OPTION. Daca decidem sa alocam
predivizorul pentru watchdog (PSA = 1), tabelul de la pagina 16 a datasheet-ului ne va da
valorile predivizorului conform bitilor PS0/PS2.
In realitate, pentru watchdog este vorba de un "post-divizor", dar asta nu intereseaza decit
electronica interna a PIC®-ului. Pe dvs. nu trebuie sa va tracaseze aceasta diferenta.
Acest post-divizor multiplica timpul de depasire al watchdog-ului. De exemplu, cu un
divizor cu 2, veti obtine un timp minim de 7 ms x 2 = 14 ms. Sau, considerind valoarea
medie, obtinem o durata de 18 ms x 2 = 36 ms.
Deci, pentru un cuart de 4 MHz, veti fi obligati sa trimiteti instructiunea clrwdt la cel putin
fiecare 14.000 de cicluri-instructiune. In cea mai mare parte a cazurilor, resetul se va efectua
in realitate dupa 18 ms x 2 = 36 ms, adica 36.000 de cicluri-instructiune.
Watchdog-ul este destinat sa verifice daca programul vostru nu s-a « ratacit » in vreo zona
non-valida din program (datorita parazitilor de alimentare, de exemplu), sau daca nu s-a
blocat intr-o bucla infinita (greseala de program). Deasemenea, el serveste la « trezirea » unui
PIC® pus in mod « sleep », dupa cum vom vedea ceva mai tirziu.
Retineti ca noi vorbim despre modului watchdog integrat in PIC®. Totusi, toate
microcontrolerele (si deci si PIC®-urile) pot fi echipate cu un sistem watchdog extern (un
circuit ce va fi resetat la intervale regulate de un pin al microcontrolerului), care prezinta
anumite avantaje, ca de exemplu acela de a putea comanda direct electronica externa (de
exemplu intreruperea alimentarii generale).
Am sa amintesc aici citeva notiuni legate de watchdog, care poate fi intern (in PIC®) sau
eventual extern :
2. Watchdog-ul poate servi, dupa caz, la repornirea unei aplicatii, la oprirea ei, la
punerea ei intr-o stare securizata, la semnalarea unui defect, sau la orice alta actiune
dorita de realizatorul aplicatiei.
149
3. Watchdog-ul integrat in PIC® depinde de buna functionare a electronicii. Rolul sau
consta in a provoca un RESET al PIC®-ului. Sarcina programatorului consta in a
lua masurile necesare daca resetul se datoreaza watchdog-ului (oprire, repornire
normala sau limitata, semnalizare, etc.). Sunt prevazuti biti specifici pentru a testa
cauza unui reset aparut in cursul programului.
10. In PIC®-uri, watchdog-ul integrat poate permite revenirea din modul stand-by a
PIC®-ului la intervale regulate, mai mult sau mai putin precise.
Primul lucru pe care trebuie sa-l facem pentru a profita de aceasta protectie integrata este
de a stabilii parametrii in _CONFIG, pentru a activa watchdog-ul. Primul lucru de constatat
este ca :
150
Daca indicati « _WDT_ON » pentru un program care nu gestioneaza watchdog-ul, acesta
se va restarta permanent, si deci nu va functiona, caci nu va contine nici o instructiune
«clrwdt».
Aceasta este o eroare frecventa pentru cei care nu stapinesc bitii de configurare din
programatorul lor. Bitii de configurare indicati in fisier sint de fapt modificabili de catre
marea majoritate a soft-urilor de programare, care sint capabile sa forteze o valoare pentru
_CONFIG diferita de cea prevazuta de catre realizatorul programului PIC®-ului.
Se intimpla deasemenea foarte frecvent ca unii programatori sa uite sa includa directiva
«_CONFIG » in programul lor, ceea ce nu permite configurarea automata a flag-urilor de
configurare. Este o practica foarte paguboasa, caci utilizatorul final va trebui sa stie (sau sa
ghiceasca) care este starea flag-urilor pe care va trebui sa le configureze in momentul
programarii.
In continuare, va trebui sa plasati in programul vostru una sau mai multe instructiuni
« clrwdt » aranjindu-le in asa fel incit o instructiune « clrwdt » sa fie primita in intervalul
necesar cerut de PIC®-ul vostru. Va recomand sa nu tineti cont de timpul nominal de 18 ms,
ci mai degraba de intervalul cel mai defavorabil, adica 7 ms. Considerind acest timp ca fiind
timpul maxim autorizat, veti putea fi siguri ca programul vostru va merge in orice conditii.
In continuare, eliminati variabila cmpt3 si modificati rutina tempo pentru a elimina bucla
exterioara :
151
;******************************************************************
; SUBRUTINA DE TEMPORIZARE *
;******************************************************************
;------------------------------------------------------------------
; Aceasta subrutina introduce o intirziere de 250 ms.
; Ea nu necesita nici un parametru, si nici nu returneaza vreounul
;------------------------------------------------------------------
tempo
clrf cmpt2 ; sterge contor 2
boucle2
clrf cmpt1 ; sterge contor 1
boucle1
nop ; pierde 1 ciclu
decfsz cmpt1 , f ; decrementeaza contor 1
goto boucle1 ; daca nu e 0, bucleaza
decfsz cmpt2 , f ; daca e 0, decrementeaza contor 2
goto boucle2 ; daca cmpt2 nu e 0, reia bucla 1
return ; revenire din subrutina
Adaugam linia :
imediat dupa eticheta init, pentru a fi siguri ca aduceti watchdog-ul la 0 (in realitate, inutil).
Sa finalizam programul principal pentru a aprinde LED-ul dupa 250 ms :
;******************************************************************
; PROGRAM PRINCIPAL *
;******************************************************************
start
call tempo ; se asteapta 250ms
bsf LED ; aprinde LED-ul
loop
goto loop ; raminem aici
END ; directiva sfirsit de program
Explicatie
152
Pentru a afla baza de timp a watchdog-ului nostru personal, in circumstantele actuale de
tensiune si temperatura, va trebui deci sa impartim timpul dintre 2 stingeri (sau aprinderi) prin
pre-divizor, adica 128.
Personal, am cronometrat 2.2 sec. Deci, watchdog-ul meu lucreaza cu un timp de baza de:
2200 ms / 128 = 17,2 ms. Ma incadrez deci in media tipica de 18 ms. Remarcati ca este mai
putin de 18 ms. Deci, daca as fi folosit un clrwdt la fiecare 18 ms, programul meu s-ar fi
blocat pentru acest PIC®. Aceasta ne arata ca totusi e bine sa luam in calcul valoarea minima
de 7 ms.
start
bsf LED ; aprinde LED
call tempo ; apeleaza tempo de 0.5s
bcf LED ; stinge LED (LED OFF)
call tempo ; apeleaza tempo de 0.5s
btfsc BOUTON ; testeaza butonul
goto start ; nu e apasat, bucleaza
plante
goto plante ; daca programul a ajuns aici, se va bloca
; intr-o bucla infinita
END ; directiva sfirsit program
In acest fel, o apasare pe buton va trimite programul intr-o zona pe care am creat-o noi si
care simuleaza o blocare a programului sub forma unei bucle infinite.
Sa modificam acum programul nostru. Mai intii vom valida watchdog-ul, cum am vazut
anterior, prin modificarea _CONFIG. Apoi, vom face in asa fel incit sa resetam watchdog-ul
la intervale regulate.
153
start
bsf LED ; aprinde LED
clrwdt ; sterge watchdog
call tempo ; apeleaza tempo de 0.5s
bcf LED ; stinge LED (LED OFF)
clrwdt ; sterge watchdog
call tempo ; apeleaza tempo de 0.5s
btfsc BOUTON ; testeaza butonul
goto start ; nu e apasat, bucleaza
plante
goto plante ; daca programul a ajuns aici, se va bloca
; intr-o bucla infinita
END ; directiva sfirsit program
Cum apelul temporizarii noastre ia 500 ms, va trebui deci sa trimitem un « clrwdt » inainte
si dupa fiecare apel al temporizarii, pentru a nu depasi intervalul watchdog-ului.
Am putea deasemenea, ca in locul acestor doua instructiuni, sa folosim un singur «clrwdt»
in subrutina « tempo ».
Sa recompilam programul si sa-l incarcam in PIC®. Cuplind alimentarea, vom vedea ca LED-ul
clipeste permanent.
Apasati butonul citeva momente ; LED-ul se va opri un moment din clipire, dupa care va reincepe
sa clipeasca. Watchdog-ul a "recuperat" blocarea programului dvs.
In general, trebuie sa incercam sa calculam predivizorul in asa fel incit sa nu fie nevoie sa
punem instructiuni « clrwdt » in prea multe locuri.
Deasemenea, va trebui sa tinem cont de timpul de reactie obtinut atunci cind marim
predivizorul. Daca o recuperare de blocaj care se face in 2 secunde va convine, sau daca
programul vostru necesita o recuperare in 18 ms, sint factori care conditioneaza alegerea
valorii predivizorului. Totul necesita un compromis.
Din pacate, nu totdeauna aveti de ales. Daca predivizorul este deja alocat pentru tmr0, de
exemplu, nu va ramine alta alegere decit sa trimiteti un « clrwdt » la fiecare 7 ms.
In acest caz, va trebui sa puneti o instructiune « clrwdt » in mijlocul rutinei de temporizare,
caci aceasta dureaza mai mult de 7 ms.
Amintiti-va sa nu folositi aceasta instructiune intr-o rutina de intrerupere, caci acest lucru
vine in contradictie cu insasi principiul watchdog-ului (in afara unor cazuri foarte speciale). In
schimb, veti putea folosi « clrwdt » intr-o subrutina fara nici o problema.
Ø Timpul minim (7 ms), este intirzierea maxima de care dispuneti intre 2 instructiuni
154
«clrwdt» pentru a evita un reset nedorit al programului dvs.
Ø Timpul maxim (33 ms), este timpul de reactie al watchdog-ului in cazul cel mai
defavorabil, in functie de componenta, si de conditiile de utilizare. Microchip va garanteaza
aici ca resetul se va efectua in maxim 33 ms.
15.10 Concluzie
In momentul de fata sinteti in masura sa creati programe rezistente la blocajele clasice, insa
doar daca veti folosi judicios watchdog-ul. In general, este preferabil sa faceti efortul de a-l
folosi, caci munca depusa in plus, este neglijabila in comparatie cu siguranta in functionare
obtinuta.
ATENTIE : Watchdog-ul utilizat ca protectie, in cazul unei programari corecte si a unei
cartele bine concepute, n-ar trebui sa intre niciodata in functiune. El nu va reactiona decit in
cazuri extrem de rare (paraziti violenti, furtuni, etc.). El nu trebuie sa fie folosit pentru a
masca erori de conceptie ale programului. Acesta va trebui sa functioneze fara ajutorul
watchdog-ului.
Ø Scrieti-va programele si puneti instructiunile « clrwdt » asa cum ati stabilit, insa NU
ACTIVATI WATCHDOG-UL cu directiva _CONFIG.
Ø Puneti-va programul in PIC® si lasati-l sa ruleze suficient de mult pentru a fi siguri ca
nu aveti vreun bug.
Ø Odata programul verificat ca fiabilitate, veti reprogranma PIC®-ul, cu watchdog-ul
activat.
Modul « sleep » sau « power down » este un mod particular in care puteti pune un PIC®,
prin intermediul instructiunii « sleep ». In acest mod, PIC®-ul este pus in stand-by si
inceteaza executia programului. La primirea acestei instructiuni, va fi executata urmatoarea
secventa :
155
Ø Oscilatorul este oprit, si PIC®-ul nu mai executa nici o instructiune.
Odata adus in aceasta stare, PIC®-ul este oprit. Consumul circuitului este redus la minim.
Daca tmr0 era sincronizat dupa ceasul intern, el nu va mai fi incrementat.
Spre deosebire de tmr0, watchdog-ul are un circuit propriu de ceas. Acesta continua sa
numere, ca si cum nimic nu s-ar fi intimplat.
Pentru a profita la maxim de scaderea consumului (in montajele cu baterii, de exemplu),
Microchip recomanda a se acorda atentie proiectarii circuitelor conectate la pinii de
intrare/iesire, in asa fel incit sa nu avem nici o circulatie de curent prin acesti pini, indiferent
de nivelul logic in care se afla. Aceasta inseamna ca nivelele prezente pe acesti pini in
momentul intrarii in mod sleep, ramin in seama aplicatiei externe.
Intrarea in modul « sleep » n-ar fi de nici un folos daca nu s-ar putea si iesi din acest mod.
Circuitul 16F84 nu reactioneaza in acest mod decit la evenimentele urmatoare, care sint
singurele susceptibile de a aduce 16F84 in starea de functionare normala. Aceste evenimente
sint :
Ø Scurgerea timpului stabilit pentru watchdog. Notati faptul ca pentru a folosi acest
eveniment pentru a "trezi" PIC®-ul, trebuie ca watchdog-ul sa fi fost activat prin bitii
de configurare. In acest caz particular, depasirea watchdog-ului nu provoaca o resetare
a PIC®-ului, ci numai "il trezeste". Dupa "trezire", se executa instructiunea care ar fi
urmat in program dupa instructiunea « sleep ».
Notati ca in acest din urma caz, pentru ca o astfel de intrerupere sa poata reactiva
procesorul, e necesar ca aceasta sa fi fost activata anterior. Spre deosebire de acestea, bitul
GIE, nu e necesar sa fi fost activat anterior pentru a putea genera "trezirea" PIC®-ului.
De exemplu, puteti decide sa reactivati PIC®-ul la sfirsitul unui ciclu de scriere in
EEPROM. Pantru a realiza acest lucru, va trebui sa puneti bitul EEIE din INTCON in 1, dupa
care lansati ciclul de scriere, urmat de instructiunea « sleep ». Odata terminata scrierea, PIC®-
ul este reactivat si isi continua programul.
Daca PIC®-ul este "trezit" printr-o intrerupere, in timp ce bitul GIE din INTCON este pus
pe 0, programul va continua pur si simplu cu instructiunea care urmeaza instructiunii «sleep».
156
16.4 Revenirea cu bitul GIE activ
In cazul in care bitul GIE este activ, revenirea ca urmare a unei intreruperi se va produce
conform secventei urmatoare :
Notati faptul ca daca nu doriti sa executati instructiunea care urmeaza dupa instructiunea
« sleep », este suficient sa puneti in acest loc o instructiune « nop ».
Daca bitul GIE este in 0, iar bitii de activare plus fag-urile unei intreruperi sint ambii in 1
in momentul executiei instructiunii « sleep » (de exemplu INTE = INTF = 1), instructiunea
« sleep » va fi pur si simplu ignorata de catre procesor.
Acest lucru este logic, caci conditiile de revenire sint deja indeplinite, chiar inainte de
punerea in stand-by. Va fi deci sarcina dvs. de a pune eventual acesti biti in 0. Bitul PD va va
permite sa stiti daca instructiunea « sleep » a fost executata (PD = 0).
Daca bitul GIE este in 1, este inutil să spun, dar oricum am sa scriu, nu putem avea situatia
din cazul precedent, avind in vedere faptul ca atunci cind se va produce o intrerupere, aceasta
va duce la stergerea flag-urilor INTE sau INTF. Face exceptie bineinteles, cazul in care ati
pus instructiunea « sleep » in interiorul rutinei de intrerupere.
Retineti deasemenea ca in cazul in care instructiunea « sleep » nu este executata,
watchdog-ul nu mai este adus la 0. Daca ati vrea sa resetati watchdog-ul in acest moment si
nu sinteti siguri daca instructiunea « sleep » a fost executata, adaugati instructiunea « clrwdt »
inainte de aceasta instructiune.
Functionarea este foarte simpla. Dupa aprinderea sau stingerea LED-ului, PIC®-ul este pus
in stand-by. Odata scurs timpul watchdog-ului, PIC®-ul se "trezeste" si executa instructiunea
urmatoare. Iata deci un program de clipire ultra-scurt si cu un consum minim.
In plus, iata o alta modalitate de a masura temporizarea watchdog-ului PIC®-ului propriu.
Observatie :
Notati faptul ca "trezirea" PIC®-ului vostru nu este instantanee. De fapt, daca folositi un
cuart, PIC®-ul va astepta 1024 de cicluri de ceas inainte de a relua programul. Va trebui sa
tineti cont de acest lucru.
Acest « timp mort » este necesar pentru ca oscilatorul de precizie cu cuart sa atinga o
anumita stabilitate.
Acest mod de functionare este folosit in principal in aplicatiile in care consumul de energie
este critic (cazul bateriilor). In acest caz, vom pune PIC®-ul in mod « Power Down » sau
« sleep » cit de des va fi posibil.
O alta aplicatie tipica este un program in care PIC®-ul n-are nimic de facut in asteptarea
unui eveniment exterior particular. In acest caz, in loc sa folosim bucle infinite, putem sa
rezolvam eficient problema printr-o simpla instructiune « sleep ».
Modul SLEEP asigura punerea in starea de Stand-by a PIC®-ului. Principalul sau rol fiind
economisirea de energie, el presupune reducerea consumului global al componentei. Pentru
aceasta, nu este suficient de a pune numai PIC®-ul in mod SLEEP ci si a mediului din jurul
sau.
Mai intii, trebuie sa stim ca trecerea in mod SLEEP nu modifica starea pinilor configurati
ca iesiri. Daca in momentul trecerii in mod SLEEP, PIC®-ul vostru aprindea un LED, atunci
el va ramine aprins (si va consuma curent). Altfel spus, inainte de a trece in mod SLEEP, toti
pinii configurati ca iesiri vor fi pusi in 1 sau 0 in asa fel incit niciunul sa nu consume curent.
158
obtineti acelasi rezultat configurind pinii nefolositi ca intrari.
16.8 Concluzie
Stiti acum cum sa puneti un PIC® in stand-by cu scopul de a economisi la maxim energia.
Nu ma indoiesc de faptul ca dvs. veti gasi o gramada de aplicatii pentru acest mod foarte
practic si foarte usor de realizat.
Si iata ca am ajuns la capatul studiului circuitului nostru 16F84. In acest capitol, as vrea sa
parcurgem impreuna datasheet-ul lui 16F84 pentru a vedea toate partile despre care nu am
vorbit pina in prezent.
Ceea ce va propun eu aici este un soi de desis. Nu voi intra totusi in descrierile tehnice
detaliate, caci acestea n-ar interesa decit electronistii (acestia fiind perfect apti de a intelege o
figura ca cea notata 3-1). Pentru ceilalti, acestea nu au nici o importanta, ei vizind numai
utilizarea PIC®-urilor.
Aceasta lectie va va arata cum trebuie inteles un datasheet.
Cum datasheet-urile sint fara indoiala actualizate de catre Microchip, eu ma voi referi la
cea pe care am folosit-o pina acum in realizarea cursului si care se numeste « 16F84.pdf »
In figura 1-3, veti vedea cum este construit un 16F84. Dupa cum am spus mai sus, aceasta
nu prezinta decit un interes limitat pentru folosirea practica a procesorului.
Totusi, puteti remarca largimea bus-urilor interne care va amintesc limitarile modurilor de
adresare. Vedeti de exemplu ca PC (Program Counter) nu are decit 13 biti.
Inima lui 16F84, ca a oricarui procesor, este ALU. In aceasta Unitate Logico Aritmetica se
efectueaza toate calculele. Remarcati legatura dintre registrul W si ALU.
In figura 3-2, vedeti clar impartirea in 4 a unui ciclu de ceas. Fiecare din aceste 4 ceasuri
necesare la realizarea unei instructiuni este detaliat aici. Exemplul 3-1 va arata cum decurge
executia unui fragment de program.
159
17.3 Organizarea memoriei
In figura 4-1 vedeti organizarea memoriei unui PIC®. Notati faptul ca stiva si PC-ul sint
situate in afara spatiului de adresare, si deci nu sint accesibile prin program.
Dvs. accesati acest registru cu ajutorul directivei « _CONFIG »,, sau cu ajutorul directivei
« DA » precedata de directiva « ORG 0x2007 ». In acest moment, datele care urmeaza
directivei « DA » vor preciza cei 14 biti folositi de catre 16F84 (5 biti pentru 16C84).
Explicarea si pozitia celor 14 biti sint date in figura 8-1. Nu uitati ca aici este vorba de un
cuvint de 14 biti (la fel ca pentru instructiuni).
Ca exemplu, linia urmatoare :
ORG 0x2007
DA B’111111111111110’
160
17.7 Diferitele tipuri de oscilatoare
Figura 8-3 ne arata configuratia pe care am folosit-o pentru ceasul PIC®-ului nostru. Este
varianta cu cuart extern. Rezistenta RS este inutila pentru un cuart clasic. Daca veti folosi un
rezonator ceramic cu condensatoare integrate in locul unui cuart, puteti elimina
condensatoarele C1 si C2.
Tabelul 8-1 va arata diferitele valori pentru C1 si C2 in functie de frecventa, cit si modurile
corespunzatoare selectate prin bitii de configurare FOSC1 si FOSC0, daca folositi un
rezonator ceramic.
Figura 8-4 ne arata cum sa folosim un ceas extern in locul oscilatorului intern. Retineti ca
in acest caz, configurarea ceasului ca oscilator RC in _CONFIG, duce la distrugerea PIC®-
ului.
Figurile 8-5 si 8-6 va arata cum sa construiti un oscilator extern. In acest caz, va sfatuiesc
personal sa folositi circuitul 74HCU04 care functioneaza bine pentru frecvente mai mari sau
egale cu 4 MHz.
Eu folosesc in acest caz o schema derivata din montajul paralel recomandat. Montajul meu,
pe 4 MHz, foloseste o rezistenta R de 3.3 MΩ in locul celei de 47 KΩ, rezistenta reglabila am
inlocuit-o printr-una fixa de 2 KΩ, si am eliminat rezistenta reglabila din partea stinga a
schemei. Am inlocuit cele 2 condensatoare prin unele de 27 pF.
Ramine oscilatorul cu retea Rezistenta – Condensator. In acest mod, oscilatia este asigurata
prin doua componente pasive, fara cuart (din motive de economie).
Nu folositi acest mod daca temporizarile din programul vostru sint critice. Conectarea
componentelor se face conform schemei din figura 8-7, folosind o rezistenta de ordinul a
47 KΩ si un condensator de cca. 27 pF.
Nu uitati in nici unul din cazuri ca viteza de executie a instructiunilor este un sfert din
viteza ceasului. Folositi formula urmatoare pentru a calcula durata unui ciclu instructiune.
T=4/F
161
17.7.1 Precizia oscilatorului
Daca veti alege aceasta solutie pentru ceas, atunci cu siguranta ati facut cea mai proasta
alegere. Ca urmare, frecventa oscilatorului va depinde de temperatura, de precizia
componentelor, si de variatia lor in timp. Sa nu va mirati ca obtineti o eroare de 12 ore intr-o
zi. Solutia este inacceptabila.
Daca veti folosi un REZONATOR CERAMIC, tabelul 12-1 va arata preciziile obtinute in
functie de citeva marci si modele testate de Microchip. Aceste valori ne dau o precizie de
ordinul a 0.5%.
Stiind ca intr-o zi avem 24 de ore, si ca fiecare ora contine 3600 s, putem spune ca o zi are
24 x 3600s = 86400 s. O eroare de 0.5% ne va da o eroare estimata de 86400 x 5 x 10-3 =
432 secunde, adica o abatere de peste 6 minute pe zi.
Acest mic exemplu a avut menirea de a ne face sa "simtim" ordinul de marime al preciziei
ce poate fi obtinuta prin metodele curente.
Retineti ca in exemplul ceasului nostru, am fi putut obtine precizii si mai mari. Am fi putut
folosi de exemplu un circuit specializat extern, special conceput pentru astfel de aplicatii, sau
am fi putut numara sinusoidele retelei de curent alternativ.
De fapt, aceasta ultima metoda este foarte fiabila, caci societatile furnizoare de energie
electrica corecteaza permanent frecventa, avind obligatia de de a respecta strict numarul de
cicluri in 24 de ore (50 Hz = 50 cicluri / secunda)
17.8 Resetul
Figura 8-8 va prezinta schema circuitului de reset intern. Nu va faceti probleme cu privire
la aceasta schema. Retineti doar faptul ca, pentru o functionare normala a PIC®-ului, pinul
MCLR trebuie legat la +5V. Punerea la masa a acestui pin, provoaca un reset al circuitului.
Daca, de exemplu, doriti un buton de reset in montajul dvs., legati pinul MCLR la +5V
printr-o rezistenta de 10 KΩ. Puneti butonul de reset intre pinul MCLR si masa, dupa cum se
vede in schema de mai jos.
162
Tabelul 8-3 va arata toate evenimentele legate de reset, si efectul lor asupra PC (acolo unde
programul se ramifica) si registrul STATUS.
Tabelul 8-4 este foarte interesant, caci el va arata continutul fiecarui registru dupa un reset
si dupa o "trezire". Amintiti-va ca valorile « x » inseamna stare necunoscuta, « u » inseamna
valoare neschimbata in raport cu valoarea precedenta, iar « q » inseamna ca starea depinde de
cauza evenimentului.
Acest reset declanseaza un timer intern, independent de viteza PIC®-ului. Acest timer
mentine PIC®-ul oprit pentru un timp tipic de 72 ms, de la detectia conditiei de reset. Acest
timp se numeste « PoWeR-up Timer » sau PWRT. El poate fi activat sau dezactivat prin
intermediul bitului de configurare PWRTE. Eu va sfatuiesc sa-l lasati totdeauna activat, in
afara cazului in care timpul de pornire la punerea sub tensiune este critic pentru aplicatia
voastra.
Figura 8-10 va arata diagrama de timp tipica pentru o punere sub tensiune. O voi explica
rapid. Stiti probabil ca deplasarea pe orizontala de la stinga la dreapta rprezinta timp.
Linia 1 va arata cresterea tensiunii de alimentare (VDD). Odata ce aceasta tensiune ajunge
la 1.2 - 1.7 V, este initiat procesul de punere sub tensiune.
A doua linie, va arata ca initierea procesului nu depinde de nivelul liniei MCLR. In acest
exemplu, aceasta linie trece in starea SUS un pic mai tirziu. Daca ati legat-o la +5V, ea va
trece in 1 in acelasi timp cu linia VDD, fara nici o legatura intre ele.
In continuare, veti vedea ca resetul intern (POR) este validat imediat ce alimentarea a
trecut de o valoare prestabilita. Se scurge, eventual, timpul de 72 ms al PWRT-ului, daca
acesta este activat.
Dupa scurgerea acestui timp, OST-ul declanseaza numararea celor 1024 de cicluri de ceas
pentru modurile respective. Cu aceasta, procedura este incheiata.
Daca in acest moment MCLR este in 1, PIC®-ul porneste direct (tabelul 8-10). Daca
MCLR este tot la 0, PIC®-ul va porni instantaneu imediat ce MCLR devine 1 (tabelul 8-11).
Tabelul 8-13 va arata ce s-ar intimpla daca alimentarea creste prea lent, atunci cind MCLR
este conectat la alimentare. In acest caz, resetul intern se incheie inainte de stabilirea unei
tensiuni normale, ceea ce nu este indicat. In acest caz, folositi schema din figura 8-9 pentru a
intcetini cresterea tensiunii pe MCLR si pentru a prelungi timpul de reset.
Figurile 8-14 si 8-15 va sugereaza metodele de protectie in cazul scaderii tensiunii, insa
fara a ajunge la 0.
Incepind din capitolul 11, veti gasi toate specificatiile electrice ale circuitului. Acestea
intereseaza electronistii ce concep montaje particulare. Pentru uzul curent, explicatiile din
lectiile precedente sint suficiente. Pentru aplicatiile mai pretentioase la specificatiile electrice,
va trebui sa studiati aceste tabele mai in detaliu. Eu gasesc inutila detalierea acestor
caracteristici.
Iata un punct crucial. Ce va deveni programul vostru daca veti schimba modelul de PIC® ?
Iata ce va trebui sa faceti pentru a va asigura portabilitatea programelor :
164
Notati deci ca programul dvs. va fi cu atit mai usor de portat daca veti respecta sfaturile
date in acest curs, daca veti folosi la maximum declaratiile, definitiile si macro-urile, si daca
va veti comenta programele.
Notati deasemenea ca daca ati inteles cele de mai inainte, UN FISIER .HEX CONCEPUT
PENTRU UN PROCESOR, NU POATE NICIDATA SA FIE FOLOSIT PENTRU UN ALT
PROCESOR, in afara unor cazuri foarte rare.
Nu sperati deci sa puneti un astfel de fisier scris pentru 16F84 intr-un 16F876. In partea a
doua a cursului, va voi explica cum sa procedati pentru a realiza migrarea programelor
voastre.
In anexa E, veti gasi un tabel care ne arata diferentele dintre 16C84 si 16F84. Sa aruncam o
privire mai de aproape.
Prima linie arata ca PWRTE are nivel schimbat. Ca urmare, PWRTE in 1 activeaza timerul
de reset de 72 ms pentru 16C84. Pentru 16F84, va trebui sa punem acest bit in 0.
Daca ati folosit directiva _CONFIG, va fi suficient sa va recompilati programul, caci noul
fisier « p16F84.inc » integreaza automat modificarea. Daca din contra, ati folosit scrierea
directa in locatia 0x2007, in ciuda avertismentelor din aceste lectii, va trebui sa va modificati
programul.
Apoi Microchip a adaugat un « trigger Schmitt » la nivelul intrarii RB0 atunci cind aceasta
este folosita ca intrerupere. Pentru non-electronisti, va trebui sa stiti doar ca acest trigger
limiteaza riscul unor false intreruperi ca urmare a unui nivel incert pe acest pin.
Deasemenea veti vedea ca punerea la 0 a bitilor 6 si 7 din EEADR nu mai provoaca o
modificare a consumului de curent al PIC®-ului. Aceasta este o corectie a unui bug. In
16C84, lasarea acestor biti in 1 marea consumul circuitului. V-am vorbit deja despre acest
lucru.
De remarcat ca faimosul CP (Code Protect) care continea un singur bit in 16C84, are acum
9 biti. Acest lucru permite creerea de diferite tipuri de protectie a codului pe celelalte PIC®-
uri din familie (protectie date, protectie EEPROM, etc).
Odata in plus, daca ati fi folosit directivele _CONFIG, ar fi fost suficient sa va recompilati
programul dupa simpla inlocuire a tipului de componenta in directiva « include ». Pentru
ceilalti, va trebui sa recititi datasheet-ul in intregime pentru fiecare noua versiune a aceleiasi
componente si sa modificati programul in consecinta.
Constatati in incheiere corectia unui bug intern atunci cind puneati bitul GIE pe 0.
165
De acum inainte, nu se va mai putea genera o intrerupere in ciclul urmator. Bucla « loop »
care era necesara pentru a verifica daca intreruperile au fost dezactivate corect, nu mai e
necesara in 16F84. Aceasta bucla era de forma :
loop
bcf INTCON , GIE ; dezactiveaza intreruperile
btfsc INTCON , GIE ; testeaza daca o intrerupere nu a repus
; bitul GIE pe 1
goto loop ; daca da, reia bucla
17.13 Concluzii
Am facut adesea greseli intentionate pe care le-am corectat ulterior. Am facut acest lucru
pentru a va usura viata, aratindu-va ceea ce uneori erau greseli grosolane, si explicindu-va ce
trebuie sa faceti pentru a obtine un rezultat fiabil.
N-am sa va las inca in pace, caci vreau sa va explic citeva subtilitati practice de
programare pentru programele cele mai uzuale. Insa deja, dvs. sinteti capabili acum sa lucrati
singuri.
166
Note:…
167
18. Sugestii, solutii si subtilitati de programare
In acest capitol, vom examina citeva metode simple pentru a iesi din situatii dificile
clasice.
18.1 Comparatiile
Ce poate fi mai simplu decit sa efectuam o comparatie intre 2 numere. Este suficient sa
efectuam o scadere. Sa comparam, de exemplu, mem1 cu mem2 :
Mai departe, pentru a afla rezultatul, va fi suficient sa testati bitii C si Z din registrul
STATUS :
- Daca Z = 0 si C = 1, rezultatul este pozitiv, deci mem2 este mai mare decit mem1
- Daca Z = 0 si C = 0, rezultatul este negativ, deci mem2 este mai mic decit mem1
Daca doriti sa comparati numai identitatea dintre doua valori, fara a modifica C, puteti
deasemenea folosi instructiunea « xor ».
sublw 5
Este o eroare clasica, caci de fapt ati efectuat (5-W) in loc de (W-5). Va trebui deci sa
faceti complementul fata de 2 al acestei valori pentru a obtine rezultatul corect. Deci, mai bine
procedati asa :
addlw -5
Si iata, adunind -5, de fapt scadeti 5. In caz contrar, gestiunea bitului C va necesita un pic
mai multa "gimnastica". Va las sa reflectati...
168
18.3 Inmultirile
Cum se face o inmultire ? Ei bine, pur si simplu in acelasi fel in care o facem manual, in
mod obisnuit.
Vom crea o rutina care inmulteste 2 numere de 8 biti. Rezultatul va necesita deci 16 biti,
adica 2 octeti. Prin urmare, pentru a obtine numarul de digiti necesar pentru rezultatul unei
inmultiri, va trebui sa adunam numarul de digiti ai operanzilor.
Sa facem deci manual o inmultire. Vom inmulti 12 cu 13, adica vom inmulti 4 biti cu 4 biti
iar rezultatul va fi pe 8 biti.
1 1 0 0 12
X 13
12
0 0
0 0 48
0 0 0 96
1 0 0 1 1 1 0 0 156
De fapt, ce-am facut ? Am inmultit 12 cu fiecare « cifra » a lui 13, incepind din dreapta.
Am deplasat fiecare rezultat intermediar spre stinga cu cite o pozitie, inainte de adunarea
finala.
In binar nu avem decit 2 cifre : 0 si 1. Se inmulteste deci fie cu 0 (ceea ce inseamna ca nu
se face nimic) fie cu 1 (ceea ce se rezuma la a recopia cifra).
Iata deci ce vom obtine in binar :
Daca vom realiza acest program, ne vor trebui 4 variabile suplimentare pentru a stoca
rezultatele intermediare, sau 8 in cazul unei inmultiri de 8 biti cu 8 biti. Am putea deasemenea
sa ne gindim sa evitam aceste rezultate intermediare, adunind treptat rezultatele intermediare
la rezultatul final. Rezulta algoritmul urmator :
Ati putea fi tentati sa scrieti acest program. Acest mod de lucru este practic pentru
inmultirile de 4 biti cu 4 biti. Pentru inmultiri de 8 biti cu 8 biti, va trebui sa faceti o gramada
de adunari de numere de 16 biti cu numere cu 16 biti. In plus, va trebui sa deplasati
inmultitorul, ceea ce ar implica fie sa-l modificati (ceea ce nu intotdeauna este de dorit), fie
sa-l salvati, ceea ce necesita un octet suplimentar.
Daca ne gindim un pic, am putea spune ca decit sa deplasam inmultitorul catre stinga,
putem deplasa rezultatul catre dreapta. Este acelasi lucru, insa nu mai aveti de facut decit
adunari pe 8 biti, intotdeauna cu bitii cei mai semnificativi ai rezultatului.
Iata cum ar functiona acestea in exemplul nostru :
169
Ø Se inmulteste 1100 cu 1. Se stocheaza rezultatul in locatia destinata, in partea din
stinga. Deci, la rezultat vom avea : 11000000
Ø Se deplaseaza rezultatul spre dreapta. Rezultat = 01100000
Ø Se inmulteste 1100 cu 0. Se adauga la rezultat. Rezultat = 01100000
Ø Se deplaseaza rezultatul spre dreapta. Rezultat = 00110000
Ø Se inmulteste 1100 cu 1. Se adauga la rezultat, in stinga. Rezultat = 11000000 +
00110000 = 11110000
Ø Se deplaseaza rezultatul spre dreapta. Rezultat = 01111000
Ø Se inmulteste 1100 cu 1. Se adauga la rezultat, in stinga. Rezultat = 11000000 +
01111000 = 100111000 (9 biti). Al 8-lea bit (cel verde) este carry.
Ø Se deplaseaza rezultatul spre dreapta. Rezultat = 10011100, adica D’156’
Retineti ca adunam intotdeauna la semioctetul cu pondere mai mare (cel din stinga). In
cazul unei inmultiri de 8 biti cu 8 biti, se aduna deci la octetul cu pondere mai mare. Pentru
moment, vom avea depasire la adunarea pe 9 biti. Amintiti-va ca transportul se gaseste in
CARRY. Ori, la o deplasare, bitul CARRY face parte din rezultat, deci se recupereaza
automat cel de-al 9-lea bit, care va deveni al 8-lea dupa deplasarea spre dreapta.
Aceasta procedura este foarte simplu de transpus intr-un program. Ca dovada, iata pseudo-
codul pentru o inmultire de 8 biti cu 8 biti :
Sterge rezultatul
Pentru fiecare din cei 8 biti ai inmultitorului
Daca bitul din dreapta inmultitorului = 1
Aduna deinmultitul la ponderea mai mare a rezultatului
Deplaseaza cei 16 biti ai rezultatului spre dreapta
Deplaseaza inmultitorul spre dreapta
Bitul urmator
Daca vom pune deplasarea inmultitorului la inceput, inainte de testul de bit, vom recupera
bitul de testat in Carry. Programul devine :
Sterge rezultatul
Pentru fiecare din cei 8 biti ai inmultitorului
Deplaseaza inmultitorul spre dreapta
Daca Carry = 1
Aduna deinmultitul la ponderea mai mare a rezultatului
Deplaseaza cei 16 biti ai rezultatului spre dreapta
Bitul urmator
Recunoasteti ca este foarte simplu. Vom traduce acum acest pseudo-cod intr-un program.
Vom folosi variabila multi ca inmultitor, multan ca deinmultit, resulH ca rezultat cu
ponderea mai mare si resulL ca rezultat cu pondere mai mica. multemp este inmultitorul
temporar care va fi modificat. cmpt este contorul de bucle. Iata deci programul :
170
rrf multemp , f ; deplaseaza inmultitorul spre dreapta
btfsc STATUS , C ; testeaza daca bitul Carry = 1
addwf resulH , f ; da, aduna la rezultatul cu pondere mare
rrf resulH , f ; deplaseaza rezultatul cu pondere mare
rrf resulL , f ; deplaseaza rezultatul cu pondere mica
decfsz cmpt , f ; decrementeaza contorul de bucle
goto loop ; nu e gata, bitul urmator
Puteti crea un mic proiect pe care sa-l testati in simulator. Veti vedea ca functioneaza
perfect. Va puteti face o subrutina sau un macro pe care sa-l adaugati la fisierul vostru
« include ».
Cele 2 linii in gri realizeaza o deplasare a rezultatului pe 16 biti. Bitul pierdut in resulH
este recuperat in Carry si reintrodus ca bitul 7 in resulL. Truc de programare : daca adunarea
precedenta s-a efectuat (linia albastra), al 9-lea bit rezultat din adunare este in Carry, si se
regaseste deci ca bitul 7 in resulL. Daca insa adunarea nu s-a efectuat, bitul Carry este adus la
0 in momentul testului (linia galbena). Observati acum importanta testarii bitului cel mai putin
semnificativ al inmultitorului servindu-ne de Carry.
Odata acumulata experienta, veti ajunge si dvs. cu siguranta, la astfel de rezultate. Acest
mic program este disponibil sub denumirea « multi.asm ».
In cazul precedent, am facut o inmultire intre 2 variabile care puteau lua orice valoare. In
cazul in care folositi o constanta, adica cunoasteti valoarea inmultitorului in momentul
conceperii programului, va trebui, din motive de optimizare, sa incercati sa rationalizati
inmultirea cu 2.
De fapt, pentru a inmulti o valoare cu 2, este suficient sa deplasam acea valoare spre
stinga. Acest lucru este foarte usor de pus in practica. Nu uitati totodata sa aduceti bitul C la 0
inaintea deplasarii, pentru a nu introduce un bit nedorit in cuvintul deplasat.
Sa presupunem deci ca in programul dvs., ati dori sa faceti inmultiri cu 10 (in zecimal).
Vom incerca sa transpunem aceasta in cazul inmultirilor cu 2. Este foarte simplu.
Pentru a inmulti cu 10, va trebui :
171
18.5 Adresare indirecta catre 2 zone diferite
Iata inca un truc. Imaginati-va ca trebuie sa copiati 15 variabile dintr-o zona de memorie in
alta (sau sa comparati doua zone de memorie diferite, etc). Veti constata rapid ca va
confruntati cu o problema : nu dispuneti decit de un singur indicator FSR pentru a indica
locatia variabilele voastre.
Sa realizam deci acest program : mem1 este adresa de inceput a primei zone iar mem2 este
adresa de inceput a zonei a doua.
Acest program este valabil atunci cind nu puteti alege zonele de plecare si de destinatie.
Insa, ce ne impiedica sa ne punem variabilele in locurile in care ne convin ? Sa punem mem1
si mem2 in zona variabilelor :
172
movwf cmpt ; salveaza in numaratorul de bucle
loop
movf INDF , w ; incarca sursa
bsf FSR , 5 ; indica destinatia
movwf INDF ; salveaza in destinatie
bcf FSR , 5 ; indica sursa
incf FSR , f ; indica urmatoarea locatie
decfsz cmpt , f ; decrementeaza contorul de bucle
goto loop ; nu e ultima locatie, urmatoarea
Veti observa imediat ca vor trebui mai multe instructiuni pentru fiecare valoare a tabelului.
In cazul unui tabel cu 200 de elemente, nu vom avea destul loc in memoria progam pentru a le
scrie. Vom folosi deci o alta solutie.
Esenta acestei solutii consta in folosirea instructiunii retlw care permite intoarcerea unei
valori trecute ca argument. Sa scriem deci tabelul, sub forma unor instructiuni retlw. Vom
obtine:
173
Nu ne mai ramine decit sa gasim o solutie pentru a ne ramifica catre linia corecta din
tabelul/programul nostru. Daca ne amintim ca putem efectua diverse operatii asupra lui PCL,
am putea folosi aceasta proprietate.
patrat
addwf PCL , f ; aduna W la PCL
In plus, operatiile asupra registrului PCL nu vor modifica automat si registrul PCLATH,
deci tabelul vostru nu va trebui sa depaseasca 256 de elemente, si, mai mult, nu va trebui sa
treaca in « pagina de 8 biti » urmatoare (o pagina fiind plaja de adrese ce corespunde celor
256 de valori posibile ale lui PCL, fara ca PCLATH sa se modifice).
Cum evitam aceste neplaceri ? Foarte simplu, impunind tabelului o adresa de inceput in asa
fel incit tabelul sa se gaseasca in intregime in aceeasi « pagina » de 256 de locatii. Acest lucru
este valabil pentru un tabel de maxim 256 de elemente. Iata care ar fi locatiile disponibile ca
adrese de start, pentru un tabel de o asemenea marime :
Adresa B’00000 00000000’, adica adresa 0x00. Inutilizabila, caci e adresa de reset
Adresa B’00001 00000000’, adica adresa 0x100. Prima locatie utilizabila
Adresa B’00010 00000000’, adica adresa 0x200. A doua locatie utilizabila
Adresa B’00011 00000000’, adica adresa 0x300. Ultima locatie utilizabila.
Bineinteles ca daca dvs. aveti nevoie de un tabel mai mic, puteti sa-l incepeti si de la alta
adresa, insa aveti grija sa nu depasiti pagina de 8 biti.
Acum puteti deci sa adaugati linia « org 0x300 » inaintea etichetei de inceput a subrutinei.
Notati ca ceea ce va explic aici, este valabil numai pentru tabelele al caror sfirsit este in
aceeasi pagina in care au inceput. Ori, cum linia « addwf » este inclusa in pagina, va trebui sa
scadeti aceasta linie din cele 256 de locatii disponibile. Vom obtine deci :
;******************************************************************
; TABEL DE PATRATE *
;******************************************************************
;------------------------------------------------------------------
; Nu uitati ca directiva "END" trebuie sa se gaseasca dupa ultima
; linie a programului. Nu lasati deci aceasta directiva in
; programul principal.
;------------------------------------------------------------------
174
org 0x300 ; adresa inceput tabel
patrat
addwf PCL , f ; aduna W la PCL
retlw .0 ; patratul lui 0 = 0
retlw .1 ; patratul lui 1 = 1
retlw .4 ; patratul lui 2 = 4
retlw .9 ; patratul lui 3 = 9
retlw .16 ; patratul lui 4 = 16
retlw .25 ; patratul lui 5 = 25
retlw .36 ; patratul lui 6 = 36
retlw .49 ; patratul lui 7 = 49
retlw .64 ; patratul lui 8 = 64
retlw .81 ; patratul lui 9 = 81
retlw .100 ; patratul lui 10 = 100
retlw .121 ; patratul lui 11 = 121
retlw .144 ; patratul lui 12 = 144
retlw .169 ; patratul lui 13 = 169
retlw .196 ; patratul lui 14 = 196
retlw .225 ; patratul lui 15 = 225
END ; directiva sfirsit program
Nu ne mai ramine decit sa cream un mic program principal care sa faca apel la aceasta
subrutina.
;******************************************************************
; PORNIRE DUPA RESET *
;******************************************************************
org 0x000 ; Adresa de start dupa reset
;******************************************************************
; PROGRAM PRINCIPAL *
;******************************************************************
start
clrf numar ; sterge numar
loop
movf numar , w ; incarca numar
call patrat ; adu patratul numarului
incf numar , f ; incrementeaza numar
btfss numar , 4 ; testeaza daca numar > 15
goto loop ; nu, numarul urmator
goto start ; da, se reia de la 0
Compilati programul, dupa care, rulati-l pe simulator. Veti constata ca... nu merge. De ce ?
Pentru ca, in realitate, toate operatiile in care PC este destinatie, necesita interventia lui
PCLATH. Ori, noi am lasat PCLATH la 0. Deci, adresa de calcul nu este cea buna. Inainte de
salt, va trebui sa adaugam :
movlw 03
movwf PCLATH
cu scopul de a permite lui PCLATH sa indice pagina 0x300 atunci cind calculeaza
incrementarea lui PCL.
Mai avantajos ar fi sa inlocuiti linia "org 0x300" prin :
repere
ORG (repere+31) & 0x3E0 ; adresa de inceput tabel
175
Scopul aceste manevre este de a obtine automat prima adresa disponibila pentru care nu
vom avea o depasire a lui PCL. Pentru un tabel de 256 de elemente, folositi « ORG (repere +
255) & 0x300 ». Incercati cu creionul pe o bucatica de hirtie pentru a va convinge. De
exemplu, se calculam sfirsitul tabelului (repere + 255). ORG va indica atunci pagina corecta.
Se elimina apoi bitii corespunzatori (255 = 8 biti) marimii totale a memoriei program
(0x3FF). Va ramine deci & 0x300.
Fisierul obtinut este disponibil sub denumirea « tableau.asm ».
patrat
movwf PCL , f ; aduna W la PCL (adresa = urmatoarea din
; programul principal)
org 0x300 ; adresa efectiva a tabelului (256 de locatii
; disponibile)
retlw 8 ; primul element din tabel : adresa 0x300
retlw 4 ; al doilea element din tabel : adresa 0x301
Daca ne aducem aminte, acestea sint variabile care nu sint folosite decit in interiorul unei
subrutine si care nu ne mai sint de folos odata ce subrutina este incheiata. Sa plecam de la un
program imaginar, care foloseste doua subrutine.
Ø Conditia pentru folosirea de variabile locale identice este ca nici una din subrutine sa
nu o apeleze pe cealalta.
Daca examinam cele doua subrutine, vom constata ca cele 2 variabile din subrutina
« tempo » nu sint utile decit in interiorul acesteia. Putem deci sa folosim variabile locale.
Variabila "rezultat" din subrutina « fonction » va trebui sa fie pastrata dupa executia
acestei subrutine, deci nu este o variabila locala. Spunem ca este o variabila globala. In
schimb, cele 2 variabile folosite pentru calculele intermediare, nu ne mai sint de nici un folos,
deci acestea sunt variabile locale.
Cum va arata zona noastra de variabile fara folosirea de variabile locale ? Hai sa cream
aceasta zona :
CBLOCK 0X0C
Cmpt1 : 1 ; contor 1 pentru rutina tempo
Cmpt2 : 1 ; contor 2 pentru rutina tempo
Resultat : 1 ; rezultat pentru rutina fonction
Interm1 : 1 ; rezultat intermediar 1 pentru fonction
Interm2 : 1 ; rezultat intermediar 2 pentru fonction
ENDC
Mai intii, vom rezerva locatiile necesare pentru variabilele locale. Cel mai mare numar de
variabile locale folosite de o functie este 2. Vom avea deci 2 variabile locale. Sa le declaram.
Mai departe, ne mai ramine sa adaugam o variabila globala. Sa ne cream zona de date :
CBLOCK 0X0C
Local1 : 1 ; variabila locala 1
Local2 : 1 ; variabila locala 2
Resultat : 1 ; rezultat pentru fonction
ENDC
Nu ne vor mai trebui deci decit 3 variabile, in loc de cele 5 initiale. Pentru a le gasi mai
usor, putem sa atribuim aceleasi nume ca mai inainte pentru variabilele noastre locale,
adaugind doar directive DEFINE sau EQU :
177
Si iata cum, fara sa avem nevoie de limbajul C, putem foarte simplu sa folosim variabile
locale.
Iata aici o sugestie care mi-a fost trimisa de catre un internaut, Richard L.
Pentru a imparti un numar la o constanta, este suficient sa inmultim acest numar cu o alta
constanta, a carei valoare este egala cu 256 impartit la constanta initiala.
Se obtine un rezultat pe 16 biti, rezultatul impartirii gasindu-se in octetul mai semnificativ.
Exemplu :
Pentru a imparti un numar la 10, este suficient sa-l inmultim cu (256/10). In hexazecimal,
256/10 da 0x19 sau 0x1A (putem sa rotunjim).
Sa presupunem ca variabila noastra contine valoarea 120 in zecimal, adica 0x78.
Daca inmultim 0x78 cu 0x1A, obtinem 0x0C30. Octetul mai semnificativ este deci 0x0C,
adica 12 in zecimal.
Daca am fi ales 0x19, am fi obtinut ca raspuns 11.
18.9 Concluzie
Cum as putea sa-mi pun rutinele intr-un fisier separat ? De ce obtin erori de tipul
«overwriting» atunci cind incerc sa o fac ? De ce MASM imi da erori de legatura (link) cind
adaug fisiere suplimentare ?
Avind in vedere toate aceste intrebari, pe care le regasesc cu regularitate in mailul meu, ar
fi cazul sa lamuresc un pic diferitele moduri de a proceda pentru a pune cod in fisiere
suplimentare pe care apoi sa le unesc cu fisierul sursa principal.
Primul lucru pe care ar trebui sa-l intelegem, este ca nu trebuie sa adaugam referinte la
fisierul repectiv in ierarhia proiectului, caci acest lucru ar face MPASM® sa creada ca el
trebuie sa lege mai multe fisiere in momentul asamblarii, ceea ce nu este cazul in ceea ce
priveste fisierele sursa (asta se aplica pentru fisierele obiect, dar nu prezinta prea mare interes
pentru noi).
Vom plasa deci sub-programele noastre intr-un fisier separat. Prin conventie, va trebui sa-i
punem extensia « .inc ». De exemplu, sa-i spunem « mesroutines.inc ».
Exista doua metode principale de a include continut sub forma de cod sursa in programul
nostru principal. Vom incepe cu cea mai evidenta.
178
19.2 Utilizarea directa de rutine in fisier
aduna16biti
instructiunea 1
instructiunea 2
instructiunea 3
...
instructiunea n
return
scade16biti
instructiunea 1
instructiunea 2
instructiunea 3
...
instructiunea n
return
Si asa mai departe pentru fiecare subrutina pe care vrem sa o includem. Retineti ca acest
fisier nu va contine nici directive « ORG », nici declaratii de variabile de tip « cblock ».
Un fisier de inclus nu este un fisier autonom, si, invers, nu se poate include un fisier
autonom « asm » intr-un alt fisier « asm », caci aceasta va duce la imposibilitatea
asamblarii din cauza prezentei mai multor date contradictorii, avind aceleasi adrese.
Trebuie acum sa includem acest cod in fisierul nostru sursa principal (fisierul nostru
«asm»). Acest lucru se realizeaza introducind in sursa linia :
#include <mesroutines.inc>
MPASM® va inlocui linia voastra « include » prin tot continutul fisierului vostru « .inc ».
Este o simpla operatie de copy/paste efectuata automat de catre MPASM®, nu e nicio
filozofie acolo.
Atentie deci la capcana : va trebui sa introduceti linia « include » nu la inceputul fisierului
ci din contra, in locul unde codul continut in fisierul « .inc » va trebui sa fie copiat.
Ca in acest exemplu, rutinele voastre vor fi puse dupa programul vostru principal si nu
exista nici o imposibilitate pentru MPASM® sa scrie codul. MPASM® va face un simplu
copy/paste si va asambla codul ca mai jos :
ORG 0x00
goto main
ORG 0x04
Tratareintrerup
main
Instructiunea 1
call aduna16biti
...
...
aduna16biti
Instructiunea 1
Instructiunea 2
Instructiunea 3
...
Instructiunea n
return
scade16biti
Instructiunea 1
Instructiunea 2
Instructiunea 3
...
Instructiunea n
return
…
Ramine problema variabilelor. Daca veti declara un bloc « CBLOCK » in fisierul vostru
include, veti da din nou de o imposibilitate : doua blocuri relative la aceeasi adresa si cu
continut diferit. Variabilele voastre vor fi deci folosite in rutinele voastre dar declarate in zona
CBLOCK de programul vostru principal.
180
Fisierul include va contine deci linii de genul :
movf variabila1, w
• Sunteti constrinsi sa cunoasteti numele tuturor variabilelor din fisierul vostru «inc»
si sa folositi aceleasi nume in programul vostru principal.
• Daca includeti doua fisiere « inc » continind nume de etichete identice, veti obtine
o eroare de asamblare (imposibil de rezolvat de catre MPASM®).
• Daca includeti doua fisiere « inc » continind nume de variabile identice, riscati sa
produceti bug-uri in programul principal, aceleasi variabile fiind folosite de rutine
diferite fara ca voi sa va dati seama.
Exista deci o metoda mai subtila pentru a va crea fisierul vostru « inc », si anume aceea de
a incapsula toate subrutinele voastre in macro-uri, ca mai jos :
Pornind de aici, puneti la inceputul fisierului vostru « asm » ordinul de a include fisierul
vostru de macro-uri ce contine sub-programele, ca de exemplu :
181
Instructiunea 1 ; instructiunea la 0x00, nefolosita inca:
; corect
Instructiunea 2
De aceasta data, fara nicio eroare, din moment ce un macro nu este transformat in cod decit
atunci cind este apelat, nu atunci cind e declarat. Deci, pentru moment, codul vostru «asm» nu
contine nici un fel de cod cuprins in fisierul vostru « mesroutines.inc », si, aici e subtilitatea,
tot ce are de facut este sa-i spuna pur si simplu ce sa faca daca intilneste numele uneia dintre
aceste macro-uri in fisierul sursa.
Acum este suficient sa puneti o eticheta de apel urmata de apelarea macro-ului, pentru a
pune codul continut de macro in locul in care doriti voi sa fie. Sa ne imaginam ca n-aveti
nevoie decit de o simpla adunare, si nu si de scadere ; fisierul vostru sursa va lua forma
urmatoare :
Avantaje :
• Singurul cod efectiv necesar este prezent in fisierul asamblat, fara nicio pierdere de
memorie program.
Inconveniente :
• Dac veti include macro-uri ce folosesc aceleasi nume de variabile, riscati aceleasi bug-
uri ca si la prima metoda.
182
19.4 Metoda finalizata
Singurele inconveniente care ne-au mai ramas privesc aceste nenorocite de variabile.
Exista totusi un mijloc de a rezolva aceasta problema : folosirea de macro-uri cu parametri.
Prin urmare, este suficient sa ne modificam macro-urile incluzind numele variabilelor
ca parametrii, ca mai jos :
In fisierul nostru « asm », vom folosi atunci macro-ul precizind numele pe care am
decis sa-l dam variabilelor noastre in declaratia CBLOCK din programul nostru principal.
Daca ne imaginam ca am hotarit sa declaram «operand1 » si « operand2 » cele doua
variabile ale noastre pe care trebuie sa le adunam si « rezultat » ca rezultat, ne va fi suficient
sa scriem fisierul nostru sursa ca mai jos :
Avantaje :
• Nu mai e nevoie sa cunoastem numele variabilelor caci vom folosi propriile noastre
variabile
Inconveniente :
183
19.5 Concluzie
Cu aceste metode, acum dispuneti de posibilitatea de ava crea propriile biblioteci de rutine
in cod sursa, usor recuperabile, si fara sa folositi cod inutil in fisierul generat. Ar fi o prostie
sa nu profitati, mai ales daca nu va place nici voua sa tot faceti copy/paste de cod.
Scopul acestei anexe este de a va arata cum sa realizati o aplicatie practica ce depaseste
limitele aparente ale PIC®-ului. Pentru aceasta, am decis sa creez scheletul unui program de
gestiune al unui card ISO7816, caci este un subiect de actualitate (nu ma intrebati de ce, caci
am sa ma fac ca nu stiu).
In plus, este un excelent pretext pentru a arata ca este posibil sa folosim un 16F84, care
totusi nu are facilitati de comunicatie seriala, pentru a comunica in acest fel cu lumea
exterioara.
Aceasta anexa se va limita la norma in general, fara a intra intr-o aplicatie specifica a
acestei norme. Scopul nu este de fapt de a va construi o propria aplicatie (de exemplu o
incuietoare codata), ci mai degraba de va arata cum sa vi-o concepeti singuri.
Este deci inutil sa-mi trimiteti mail-uri pentru a afla cum e folosita aceasta norma in
practica, pentru cine stie ce aplicatie mai neortodoxa, caci nu va voi raspunde.
Sa vedem mai intii citeva particularitati ale normei ISO7816 care ne vor servi la crearea
aplicatiei noastre
Veti gasi aceste carduri peste tot in comertul cu componente electronice, sub denumirea de
« card pentru incuietoare codata ». Ele permit realizarea unei multitudini de aplicatii, iar
cititoarele sint disponibile peste tot. Pentru a comunica cu acest card, veti avea nevoie de o
interfata a carei schema o puteti gasi pe Internet sub denumirea « phœnix », si de un soft de
comunicatie, sau de softul furnizat impreuna cu interfata.
Ø Cardul nu dispune de un ceas propriu, el fiind furnizat de catre interfata sau de catre
master, si este de regula sub 4 MHz.
Ø Timpul ce separa fiecare bit este definit de norma ca fiind 1 bit emis la fiecare 372 de
impulsuri ale ceasului principal. Aceasta ne da un debit binar de ordinul a 9600 bauds
pentru un ceas de 3.57 MHz (3570000 / 372), sau 10752 bauds pentru cuartul nostru
de 4 MHz.
Ø Transmisia este de tip asincron, cu 1 bit de start, 8 biti de date, 1 bit de paritate para si
2 biti de stop.
Ø Comunicatia este de tip half-duplex, adica emisia si receptia se face alternativ pe
aceeasi linie fizica.
In acest exercitiu vom folosi comenzi « container » ale normei ISO7816. Trebuie sa stim
ca acest card va trebui sa raspunda la comenzi organizate conform protocolului standard al
acestei norme.
184
Cardul nu va lua niciodata initiativa unui schimb de informatii. El nu face decit sa
raspunda unor comenzi de forma :
Ø CLASS : precizeaza grupul de instructiuni din care face parte. Daca numarul de
instructiuni folosite nu e mare, nimic nu ne interzice sa nu folosim decit o singura
clasa. Este ceea ce vom face si noi in acest exercitiu simplificat.
Ø INS : este instructiunea propriu-zisa, sau comanda. Acesta este octetul care determina
operatia ce va fi efectuata de card.
Retineti ca exista si moduri extinse pentru norma ISO7816, care permit transmiterea si
receptionarea mai multor octeti intr-o singura operatie. Acest mod este definit ca mod T = 1.
In cazul nostru, vom presupune ca informatia nu circula decit intr-un singur sens pentru o
instructiune data (mod T = 0).
Deci, daca presupunem, de exemplu, instructiunea imaginara 0x14 din clasa 0xD5 care
necesita un raspuns din partea cardului (instructiune dinspre card spre master). Masterul
precizeaza ca P1 = 0x01 si P2 = 0x02. Vom avea deci (imaginindu-ne octetii de date) :
185
• Masterul trimite : D5 14 01 02 10
• Deducem ca masterul asteapta 16 octeti ca raspuns (0x10 = D'16')
• Cardul raspunde : 14 (confirmare receptie)
• Cardul trimite : 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F (de exemplu), adica 16
octeti de raspuns, in afara de confirmarea receptiei si stare.
• Cardul trimite : 90 00, stare folosita in general pentru a indica faptul ca operatia a fost
executata.
Acum, daca in aceleasi conditii, ne vom imagina instructiunea 0x15 insotita de octeti cu
destinatia card (instructiune master catre card), vom avea :
• Masterul trimite : D5 15 01 02 10
• Cardul raspunde : 15 (confirmare receptie)
• Masterul trimite : 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F (de exemplu)
• Cardul raspunde : 90 00
Observati ca numai sensul de circulatie al celor 16 octeti isi schimba directia. Deci,
intotdeauna masterul este acela care trimite secventa de start, iar cartela raspunde cu
confirmarea receptiei si starea.
Schimburile de informatii intre master si card se fac prin intermediul unei legaturi seriale
asincrone. Pentru a intelege cele ce urmeaza, va trebui sa intelegem ce inseamna acestea.
Modul serial este definit ca fiind modul in care toti bitii unui octet sint transmisi in serie,
adica unul dupa altul. El este opus modului paralel, in care toti bitii sint transmisi in acelasi
timp, fiecare pe un fir separat.
"Asincron" este opusul lui "sincron", adica este vorba despre o legatura care nu furnizeaza
un ceas care sa indice inceputul si sfirsitul fiecarui bit transmis.
Vom avea deci nevoie de un mecanism destinat care sa repereze pozitia fiecarui bit. Notati
aici faptul ca este vorba despre un mod asincron special, cardul folosind acelasi ceas ca si
masterul. Viteza nu va fi deci exprimata neaparat in bauds, ci mai degraba in numarul de
impulsuri de ceas. Vom avea un bit la fiecare 372 de impulsuri de ceas.
Vom explica acum aceste concepte si vom arata care sint valorile folosite in norma
ISO7816.
In repaos, linia de date se gaseste in starea 1. Emitatorul pune aceasta linie in starea 0 :
acesta este bitul de start. Aceasta modificare de nivel va permite detectarea inceputului
receptiei bitilor serie.
186
Valorile admisibile sint de 1 sau 2 biti de start. Norma ISO7816 necesita un singur bit de
start
Dupa receptia bitului de start, urmeaza bitii de date, incepind cu bitul 0. Normele uzuale
folosesc 7 sau 8 biti de date. Norma ISO7816 foloseste 8 biti de date, ceea ce inseamna ca
putem avea valori intre 0 si 0xFF pentru fiecare octet receptionat.
Dupa receptia bitilor precedenti, este neaparat necesar sa readucem linia de comunicatie in
starea 1 pentru a putea detecta bitul de start al octetului urmator. Acesta este rolul bitului de
stop. Valorile admisibile sint de 1 sau 2 biti de stop.
In norma ISO7816, vom folosi 2 biti de stop, adica bitul de stop va avea o durata
echivalenta cu durata a 2 biti.
Durata fiecarui bit este o constanta care depinde de viteza de transmisie. De exemplu,
pentru o viteza de 9600 bauds, adica 9600 de biti pe secunda, fiecare bit va dura :
Timpul necesar pentru receptia unui octet intreg este suma timpilor necesari pentru receptia
fiecaruia din bitii acestui octet. In cazul normei ISO7816, noi vom folosi :
Debit maxim = 1 / (numar de biti x durata unui bit), adica, pentru norma ISO7816 :
-6
1 / 1250 x 10 = 800 octeti pe secunda.
187
ACEASTA ESTE VALABILA PENTRU O VITEZA DE TRANSFER DE 9600 BAUDS.
Retineti ca aceasta viteza nu este impusa de norma, fiind deci posibil de a mari acest debit.
Vorbim aici de debitul maxim, caci acesta este cazul in care un octet incepe imediat dupa
sfirsitul celui precedent, ceea ce nu este neaparat obligatoriu pentru o legatura asincrona.
Vom explica mai intii ceea ce vedem in graficul de mai jos. Pe axa verticala avem nivelele
logice, iar pe axa orizontala, timpul. Fiecare patrat reprezinta durata unui bit.
Sa examinam acest grafic. Linia este in starea 1, pe durata nedeterminata. Intervine bitul de
start, care incepe la doua patratele de axa verticala. Observam ca cel mai bun moment pentru
a citi bitul 0 este de a-l sonda in mijlocul latimii sale, in scopul obtinerii celui mai mic risc de
eroare posibil.
Amintiti-va ca pentru norma ISO7816, latimea unui bit este de 372 de impulsuri de tact.
Cum PIC®-ul nostru imparte deja la 4 frecventa cesului, rezulta ca latimea unui bit echivalent
va fi de 372 / 4 = 93 impulsuri de ceas.
Ø Daca vom dori sa citim in continuare un alt octet, va trebui sa ne pozitionam undeva
intre cei 2 biti de stop, cu scopul de a fi gata sa detectem bitul de start urmator. Va
trebui deci sa asteptam intre 0.5 si 2.5 biti.
Ø Daca vom dori sa transmitem un octet dupa aceasta citire, nu o vom putea face inainte
de sfirsitul celui de-al 2-lea bit de stop, adica minim dupa 2.5 cicluri.
Din curiozitate, care este octetul pe care l-am receptionat in acest exemplu ? Este foarte
simplu :
B0 = 1
B1 = 0
B2 = 0
B3 = 1
B4 = 0
B5 = 1
188
B6 = 0
B7 = 1
Paritate = 0
Octetul receptionat este deci B’10101001, adica 0xA9. Sa veificam paritatea. Numarul de
biti in 1 receptionati = 4, deci paritate para, adica conforma cu norma ISO7816.
;******************************************************************
; Acest fisier este baza de plecare pentru gestiunea unui *
; card conform cu norma ISO7816 *
; *
;******************************************************************
; *
; NUME: ISO7816 *
; Data: 10/03/2001 *
; Versiune: 1.0 *
; Circuit: Card pentru incuietoare codata *
; Autor: Bigonoff *
; *
;******************************************************************
; *
; Fisier necesar : P16F84.inc *
; *
;******************************************************************
; *
; - MCLR Comanda de reset a cardului Intrare *
; - RB4 SDA - date pentru eeprom extern Bidirectional *
; - RB5 SCL - ceas pentru eeprom extern Iesire *
; - RB7 DATA - date mod serial Bidirectional *
; *
;****************************************************************
189
Sa definim configuratia : nu vom folosi watchdog-ul pentru aceasta aplicatie, si vom folosi
un ceas extern. Vom avea deci :
Vom lucra cu timerul tmr0. Cum ecartul dintre 2 biti este de 93 de instructiuni, vom lucra
fara predivizor, ceea ce ne impune sa alocam predivizorul pentru watchdog. Sa definim deci
valoarea pe care o vom pune in OPTION :
;******************************************************************
; TEMPORIZARE *
;******************************************************************
;------------------------------------------------------------------
; temp_1bd: initializeaza tmr0 pentru ca temporizarea sa fie
; egala cu echivalentul a 1 bit si jumatate, adica
; 46 + 93 de incrementari ale lui tmr0 - timpul necesar
; pentru a ajunge in rutina
; temp_1b : asteapta ca ecartul dintre temporizarea precedenta
; si cea actuala sa fie de 1 bit, adica de 93 de
; instructiuni
;------------------------------------------------------------------
temp_1bd
movlw -38 ; tine cont de 2 cicluri de oprire ale tmr0
movwf TMR0 ; initializeaza tmr0
call temp_suite ; si asteapta 1/2 bit
temp_1b
movlw -93 ; ecart intre 2 biti + 2 cicluri de oprire
addwf TMR0 , f ; adauga la valoarea actuala
temp_suite
bcf INTCON , T0IF ; sterge flag
190
temp_wait
btfss INTCON , T0IF ; asteapta depasire timer
goto temp_wait ; nu e gata, asteapta
return ; si iesi
Care este numarul acestor instructiuni ? Este pur si simplu numarul continut in tmr0, din
moment ce rularea precedenta a asteptat ajungerea sa la 0. Deci nu va trebui sa punem -91 in
tmr0, ci SA ADUNAM -91 la continutul actual al lui tmr0.
In acest mod, vom fi siguri ca depasirea lui tmr0 se va face exact dupa 93 de cicluri de la
depasirea precedenta, indiferent de numarul de instructiuni executate intre cele doua apeluri.
Aceasta este valabil, bineinteles, cu conditia ca tmr0 sa nu fi depasit mai inainte, insa aceasta
va presupune faptul ca nu avem timp sa o executam in intregime intre 2 biti, deci PIC®-ul
nostru nu e suficient de rapid, sau aplicatia noastra nu e suficient optimizata ca timp de
executie.
Imediat dupa aceasta, vom repune flag-ul T0IF pe 0, si vom astepta linistiti ca depasirea
timerului sa modifice din nou flag-ul.
Sa examinam acum punctul de intrare temp_1bd. Vom incepe prin a initializa tmr0 cu -38,
pentru a incepe temporizarea de 40 de instructiuni pornind din acest punct.
Cum la inceputul achizitiei de date avem nevoie de o jumatate de bit, nu a existat un bit
precedent, deci va trebui SA INCARCAM aceasta valoare si nu sa facem o ADUNARE. In
plus, intre detectia bitului de start si initializarea lui tmr0, s-au executat deja citeva
instructiuni. Grosier, am putea considera ca s-au executat 6 instructiuni, ceea ce ne da o
valoare pentru incarcare de -(46 - 6) = -40, si tinind cont de cele doua cicluri pierdute : -38.
Care este in realitate marja noastra de eroare ? Ei bine, aceasta va fi de jumatate din durata
unui bit, adica 46 de cicluri, pentru a nu cadea pe bitul adiacent. Nu sintem deci la limita unui
singur ciclu diferenta, insa trebuie sa raminem cit mai precisi posibil pentru cazul in care o
deriva a dispozitivului conectat va mari imprecizia. Va trebui sa ne gindim ca poate si cel ce a
scris programul master-ului a folosit o astfel de toleranta.
Mai departe, vom apela subrutina din ea insasi in asa fel incit sa atingem durata prevazuta,
apoi subrutina continua sa realizeze temporizarea de 93 de cicluri. Am asteptat deci in total 93
+ 40 de cicluri, adica durata a 1.5 biti. In plus, aceasta subrutina nu foloseste nici o variabila.
Acum, ca ati inteles cum este receptionat un octet in mod serie asincron, este timpul sa
scriem sub-programul de receptie al unui octet. In acest exercitiu, nu vom verifica daca bitul
de paritate receptionat este corect. Va las pe voi sa integrati singuri acest test, daca va va
interesa. In acest caz, va trebui sa cititi 9 biti si sa verificati daca numarul de biti « 1 »
receptionati este par. Exista mai multe metode posibile.
Sub-programul nostru va trebui deci sa efectueze urmatoarele operatii :
191
• Sa astepte aparitia bitului de start
• Sa astepte un timp de 93 + 46 de instructiuni
• Pentru fiecare din cei 8 biti, sa citeasca bitul si sa-l puna in caracterul receptionat in
pozitia corecta
• Sa se pozitioneze undeva intre bitii de stop
;******************************************************************
; Receptia unui octet provenind de la master *
;******************************************************************
;------------------------------------------------------------------
; Caracter citit in W. Nu se verifica paritatea
;------------------------------------------------------------------
Receptie
; asteapta bitul de start
; -----------------------
; receptie caracter
; -----------------
Recloop
bcf STATUS , C ; Carry = 0
btfsc SERIAL ; testeaza daca bitul = 0
bsf STATUS , C ; Carry = bit receptionat
rrf caract , f ; introduceti bitul prin stinga
call temp_1b ; asteapta mijlocul caracterului urmator
decfsz cmptbts , f ; decrementeaza numaratorul de biti
goto Recloop ; nu e ultimul, urmatorul
Referitor la ultima temporizare, ne gasim in acest moment in centrul celui de-al 9-lea bit,
192
adica a bitului de paritate. Cum noi nu-l luam in consideratie, nu trebuie sa-l citim. Va trebui
atunci sa ne pozitionam intr-un loc in care linia de comunicatie este repusa in 1, adica intr-
unul din bitii de stop, in scopul de a putea eventual sa incepem sa asteptam un nou caracter.
Va trebui deci sa asteptam intre 0.5 si 2.5 biti. Subrutina de 1.5 biti cade direct in acest
interval si este direct utilizabila.
Acest sub-program foloseste variabile pe care le vom declara mai departe.
Este important sa ne amintim ca lucram in mod half-duplex, adica aceeasi linie este
utilizata atit la emisie cit si la receptie. Cum fiecare din interlcutori vorbeste cind ii vine
rindul, va trebui sa ii lasam fiecaruia timpul de a trece in regim de citire dupa trimiterea
ultimului sau mesaj. Acesta se numeste « timp de revenire ». Trebuie deasemenea sa ne
amintim ca rutina noastra de receptie se termina oriunde in mijlocul bitilor de stop, si va
trebui sa lasam timp emitatorului sa termine de trimis bitii de stop in intregime.
;******************************************************************
; Trimite un octet catre cititorul de card *
;******************************************************************
;------------------------------------------------------------------
; trimite octetul continut in registrul W catre cititorul de card
;------------------------------------------------------------------
Send
movwf caract ; salveaza caracterul de transmis
call temp_1bd ; asteapta 1 bit si jumatate
BANK1 ; se trece in bancul 1
bcf SERIAL ; portul serial ca iesire
BANK0 ; revine in bancul 0
193
; ------------------------
bcf SERIAL ; trimite 0 : bitul de start
clrf parite ; sterge bitul de paritate
movlw 8 ; vom avea 8 biti de transmis
movwf cmptbts ; incarca in contorul de biti
call temp_1b ; asteapta temporizarea intre 2 biti
; transmite paritatea
; -------------------
movf parite , w ; incarca paritatea para calculata
xorwf PORTB , w ; daca SERIAL difera de bitul de transmis
andlw 0x80 ; nu se modifica decit RB7
xorwf PORTB , f ; atunci se inverseaza RB7
call temp_1b ; asteapta sfirsit bit paritate
Daca ati fost atenti, ati remarcat o usoara inversare in ceea ce priveste sfirsitul protocolului.
De fapt, in loc de a transmite un nivel 1 (bit de stop), apoi sa asteptam timp de 2 biti si apoi sa
comutam linia pe receptie, am trecut mai intii linia in mod receptie si apoi am asteptat o
durata de 2 biti.
Aceasta procedura duce la un rezultat identic, deoarece rezistenta pull-up catre +5V de pe
PORTB, pe care am validat-o, impune un nivel 1 pe RB7 atunci cind acest pin este pus in
mod receptie, un nivel 1 corespunzind unui bit de stop.
Rutina care serveste la transmiterea celor 8 biti foloseste instructiunea « xorwf » in loc de a
testa daca bitul de transmis este 1 sau 0. Rutina incepe prin plasarea bitului de transmis in
pozitia b7.
De ce b7 ? Foarte simplu, pentru ca si in PORTB tot bitul b7 este cel pe care va trebui sa-l
modificam. Noi folosim de fapt RB7. Procedura folosita permite ca in acelasi timp sa setam si
bitul de paritate.
Faceti aceasta operatie manual pe o bucata de hirtie pentru a va convinge. Incercati sa
scrieti o rutina folosind « btfss » si « btfsc » si comparati rezultatele.
194
xorwf PORTB , w ; pastreaza 1 daca se schimba SERIAL
andlw 0x80 ; nu se modifica decit RB7
xorwf PORTB , f ; daca da, inverseaza RB7
Se incepe prin citirea lui PORTB si se face un « xorwf » cu bitul de transmis continut in
« W ». Cum « W » nu contine decit acest bit, bitii RB0 - RB6 nu vor fi modificati de
operatiile urmatoare.
Daca bitul 7 continut in « W » este diferit de cel prezent in RB7, vom obtine b7 = 1 in
« W ». In cazul in care b7 din « W » este identic cu RB7, vom obtine 0. Sa nu uitam ca
instructiunea « xorlw » ne permite sa punem rezultatul tot in « W ».
Acestea pot parea un pic cam incilcite, insa daca veti incerca sa scrieti rutina altfel, veti
vedea ca facind mai multe incercari, veti ajunge la un rezultat identic, si asta numai din cauza
paritatii de care trebuie sa avem grija. Asta, in afara de cazul in care acceptati sa modificatii si
celelalte linii ale PORTB.
Retineti faptul ca puteti ignora verificarea bitului de paritate la receptie, daca asa doriti. La
emisie insa, sunteti obligati sa setati corect acest bit, caci exista o probabilitate foarte mare ca
masterul sa verifice acest bit de paritate.
20.9 Initializarea
Vom studia acum corpul programului nostru principal. Ca orice program care se respecta,
vom incepe prin initializari. Acestea sint foarte simple, ele limitindu-se doar la initializarea
registrului OPTION.
;******************************************************************
; INITIALIZARI *
;******************************************************************
org 0x000 ; adresa de start dupa reset
init
BANK1 ; se trece in bancul 1
movlw OPTIONVAL ; schimba masca
movwf OPTION_REG ; initializeaza registrul OPTION
BANK0 ; se trece in bancul 0
ATR inseamna Answer To Reset, adica raspunsul la un reset. Aceasta este o comanda
transmisa de catre card la punerea sub tensiune sau dupa un reset generat de catre master prin
pinul « MCLR ».
Mai intii, vom astepta un pic ca masterul sa fie gata sa receptioneze ATR-ul nostru. Ar
putea fi necesara o ajustare a acestui timp in functie de caracteristicile masterului, care are
probabil altceva de facut la restart, decit sa se ocupe de cardul vostru.
195
;******************************************************************
; PROGRAM PRINCIPAL *
;******************************************************************
start
; se incepe cu o mica asteptare
; -----------------------------
call temp_1bd ; asteapta 1 bit si jumatate
In continuare, putem transmite ATR-ul. Din motive de simplitate, am scris ATR-ul in zona
EEPROM a PIC®-ului. Am ales un ATR de 5 caractere, inventat de mine. Consultati
caracteristicile masterului pentru a cunoaste ATR-urile valide pentru aplicatia voastra.
;******************************************************************
; TRANSMITEREA ATR-ului *
;******************************************************************
;------------------------------------------------------------------
; Transmite un ATR fictiv : ATR-ul este in cei 5 octeti situati
; la adresele 0x04...0x00 din EEPROM-ul intern. ATR-ul este
; scris in ordine inversa.
;------------------------------------------------------------------
movlw 0x5 ; avem 5 octeti
movwf cmpt1 ; incarca in contorul de bucle = adresa
ATR_loop
decf cmpt1 , w ; adresa de citit = (contor de bucle - 1)
call Rd_eeprom ; citeste un octet din eeprom-ul intern
call Send ; transmite catre decodor
decfsz cmpt1 , f ; decrementeaza contor
goto ATR_loop ; nu e gata, urmatorul
Observatii :
Folosirea comenzii « decfsz » usureaza scrierea buclelor, insa, cum contorul de bucle este
in acelasi timp si offset-ul de pozitie in EEPROM, ATR-ul va fi scris invers in EEPROM,
adica de la ultimul la primul octet.
In momentul apelului subrutinei « Rd_eeprom », numaratorul de bucle va varia de la 5 la 1.
Insa, adresa pentru EEPROM va varia de la 4 la 0. Deci, instructiunea « decf » permite sa se
incarce in « W » valoarea contorului de bucle - 1.
Subrutina « Rd_eeprom » nu este nimic altceva decit macro-ul nostru de citire a zonei
EEPROM, usor transformata. Iat-o :
;******************************************************************
; Citirea unui octet din eeprom-ul inten *
;******************************************************************
;------------------------------------------------------------------
; Citeste un octet din eeprom-ul intern. Adresa este trecuta
; in W. Octetul citit este in W.
;------------------------------------------------------------------
Rd_eeprom
movwf EEADR ; adresa de citire in registrul EEADR
bsf STATUS , RP0 ; trece in bancul 1
bsf EECON1 , RD ; lanseaza citirea EEPROM
bcf STATUS , RP0 ; revine in bancul 0
movf EEDATA , w ; incarca valoarea citita in W
return ; revenire
196
Sa ne gindim si cum scriem ATR-ul in zona EEPROM :
;******************************************************************
; DECLARAREA ZONEI EEPROM *
;******************************************************************
org 0x2100 ; adresa de inceput a zonei eeprom
Norma ISO7816 cere ca la fiecare transmitere a unui raspuns din partea cardului, sa
urmeze 2 octeti de stare, care sa indice felul in care a fost interpretata comanda.
;******************************************************************
; TRANSMITEREA STARII STANDARD *
;******************************************************************
;-----------------------------------------------------------------
; Transmite starea standard, in acest caz luind 0x80 0X00
;-----------------------------------------------------------------
Statstd
movlw 0x80 ; ia primul octet satus
call Send ; transmite-l
clrw ; sterge w
call Send ; transmite 00
goto classe ; si trateaza clasa
;******************************************************************
; TRANSMITEREA UNEI STARI SPECIFICE *
;******************************************************************
;------------------------------------------------------------------
; Transmite mai intii octetul continut in W, apoi octetul
; continut in status2
;------------------------------------------------------------------
Statxx
call Send ; se transmite valoarea
movf status2 , w ; incarca octetul de transmis
call Send ; se transmite al doilea octet al starii
;******************************************************************
; CITIREA CLASEI *
;******************************************************************
;------------------------------------------------------------------
; In acest exemplu se considera ca nu avem decit o singura
; clasa valida. ; Se asteapta receptia clasei si nu se trateaza.
;------------------------------------------------------------------
classe
call Receive ; citeste octetul venit de la master
;******************************************************************
; RECEPTIA LUI INS,P1,P2,LEN *
;******************************************************************
;------------------------------------------------------------------
; INS va fi pus in variabila Ser_ins, P1 va fi pus in Ser_P1
; iar P2 in Ser_P2.
; Lungimea cimpului de date va fi pusa in Ser_len
;------------------------------------------------------------------
movlw Ser_Ins ; indica locatia pentru instructiune
movwf FSR ; initializeaza indicatorul indirect
read_loop
call Receive ; citeste un octet
movwf INDF ; salveaza-l in locatia prevazuta
incf FSR , f ; indica urmatoarea locatie
btfss FSR , 0x4 ; testeaza daca s-a atins adresa 0x10
goto read_loop ; nu, octetul urmator
Puteti constata folosirea adresarii indirecte pentru salvarea octetilor receptionati in 4 locatii
consecutive. Vom alege adresele de la 0x00 la 0x0F, ceea ce ne permite sa detectam usor
incheierea comenzii. Prin urmare, odata salvat al 4-lea octet, FSR va indica 0x10, si deci va fi
suficient sa-i testam bitul 4 pentru a testa sfirsitul buclei, fara a fi nevoie de un contor de bucle
suplimentar.
198
;******************************************************************
; COMUTA URMATOAREA INSTRUCTIUNE RECEPTIONATA *
;******************************************************************
;------------------------------------------------------------------
; Ne vom imagina ca vom reactiona la o instructiune 0x25
; Toate celelalte instructiuni vor fi considerate ca fiind
; incorecte.
;------------------------------------------------------------------
; testeaza instructiunea receptionata
; -----------------------------------
movf Ser_Ins , w ; incarca instructiunea primita
sublw 0x25 ; compara cu 0x25
btfsc STATUS , Z ; testeaza daca e identica
goto Ins25 ; da, trateaza instructiunea 25
Observam aici ca daca instructiunea primita este 0x25, vom sari la tratarea ei. In caz
contrar, vom trimite starea « 60 40 » care semnifica in cazul nostru, « instructiune incorecta ».
;******************************************************************
; TRATAREA INSTRUCTIUNII 25 *
;******************************************************************
Ins25
; trimite ecoul comenzii
; ----------------------
movf Ser_Ins , w ; incarca comanda primita
call Send ; retrimite-o in ecou
; retrimite P1 + P2
; -----------------
movf Ser_P1 , w ; incarca P1
addwf Ser_P2 , w ; + P2
call Send ; trimite rezultat
199
goto Statstd ; da, trimite starea standard
; compleeaza cu 0xFF
; -----------------------
Insloop
movlw 0xFF ; valore de trimis
call Send ; trimite 0xFF
decfsz Ser_Len , f ; decrementeaza contorul de bucle
goto Insloop ; nu e gata, urmatorul
20.16 Variabilele
;******************************************************************
; DECLARAREA VARIABILELOR *
;******************************************************************
; rutina ATR
; ----------
#DEFINE cmpt1 local1 ; contor de octeti pentru ATR
; pentru STATUS
; -------------
#DEFINE status2 local1 ; octetul 2 al starii
; pentru instructiunea 25
; -----------------------
#DEFINE cmpt2 local1 ; contor de octeti
200
20.17 Concluzii
Acum stiti sa comunicati printr-o legatura serie asincrona. In plus, aveti notiunile de baza
necesare pentru a realiza un card care sa satisfaca cerintele normei ISO7816. Fara indoiala ca
voi veti gasi numeroase aplicatii care sa puna in opera teoria vazuta aici.
Va reamintesc ca aici este vorba de un exercitiu imaginar. Este deci inutil de a-mi scrie
pentru a ma intreba cum sa transformam acest exemplu, pentru a-l folosi in practica pentru
vreo « cheie pentru incuietoare codata » oarecare. Va reamintesc ca nu va voi raspunde.
201
A1.4 Nu pot sa folosesc simulatorul, nu-mi apar optiunile
Ati uitat sa-i indicati lui STLAB ca folositi simulatorul. Mergeti in meniul « Debugger =>
Select tool » si selectati « MPLAB SIM ». Configurati-l mai departe dupa cum v-am explicat
in capitolul respectiv.
In mod implicit, MPLAB® face distinctie intre minuscule si majuscule. Daca acest lucru
va face probleme, puteti modifica setarile in proprietatile proiectului vostru. Pentru aceasta :
M-am decis sa-mi modific radical sfatul meu initial ca urmare a evolutiei strategiei
comerciale a MICROCHIP, si care, pentru mine, face mai putin tentanta constructia unui
programator « personal ». Pe de alta parte, programele cele mai cunoscute pentru
progamatoarele de PIC®-uri nu mai urmaresc cu adevarat aparitia noilor modele.
Daca v-ati decis sa va construiti voi insiva programatorul, cel putin nu va construiti un
programator de tip JDM (sursa de mari probleme), care se recunoaste prin faptul ca este
202
conectat pe portul serial fara sa dispuna de o alimentare proprie (isi trage alimentarea direct
din portul serial). Deasemenea evitati sa construiti un programator serial pe care sa-l legati la
calculatorul dvs. (care nu are port serial), printr-un adaptor USB/RS232, pentru simplul motiv
ca de regula nu functioneaza.
Sfatul meu este deci clar : cumparati un programator original PiKit3 sau ICD3. Nu
cumparati versiunile « 2 », caci ele nu vor fi utilizabile pentru noile PIC®-uri viitoare.
Avantajele sunt numeroase :
• Posibilitatea de a programa direct din MPLAB® (fara sa aveti nevoie de un alt soft).
• Programatorul va recunoaste toate PIC®-urile prevazute in versiunea voastra de
MPLAB®, pe care este suficient sa-l actualizati atunci cind apare un nou PIC®.
• Aceste programatoare functioneaza deasemenea ca debugger-e in circuit (vezi partea a
4-a a cursului), si deci veti putea sa va depanati “din zbor” toate programele destinate
PIC®-urilor care dispun de facilitatea ICD, adica marea majoritate a PIC®-urilor
recente.
• Veti avea siguranta ca programatorul vostru functioneaza si deci veti putea sa va
concentrati pe alte cauze in caz ca montajul vostru nu functioneaza.
• Garantia este foarte extinsa, ICD2-ul meu, de exemplu, este mereu considerat de
MICROCHIP ca fiind in garantie in ciuda anilor multi de functionare (grantie “pe
viata”?).
Sfatul meu este deci clar : nu va construiti propriul programator decit daca nu aveti decit
un singur PIC® de programat sau daca va este imposibil sa va procurati un programator
oficial.
Am primit mail-uri de la persoane care imi spuneau : « Atunci cind imi rulez programul
pas-cu-pas in MPLAB®, primesc la un moment dat un mesaj de genul : « Stack overflow »
sau « Stack underflow » ».
De fapt, un mesaj « stack overflow » poate insemna ca ati depasit cele 8 nivele de sub-
programe permise. Va reamintesc ca nu exista decit 8 locatii in stiva (stack), si deci, o noua
tentativa de a pune ceva in aceasta, urmata de o subrutina sau de o intrerupere, va provoca
blocarea stivei.
Totusi, in cea mai mare parte a cazurilor, este vorba de o eroare in structura programului
vostru. Mesajul « stack overflow » survine daca ati stocat deja in stiva 7 locatii, iar mesajul
« stack underflow » survine daca incercati sa extrageti din stiva mai mult decit ati introdus (de
exemplu, « return » fara « call »).
Pentru a rezolva aceste probleme, daca nu ati depasit cele 8 nivele (vezi A 1.2), verificati
punctele urmatoare :
• Fiecare apelare printr-un « call » trebuie sa revina in programul apelant printr-un «return»
sau un « retlw »
• Fiecare « return » sau « retlw » intilnit trebuie sa fie precedat de un « call » corespondent
• Iesirea dintr-o subrutina cu un « goto » trebuie sa duca la un punct unde se va gasi un
«return»
• O rutina nu trebuie sa se apeleze pe ea insasi (in afara de functiile recursive : acestea sint
203
rare, la o asemenea marime a stivei)
Iata o intrebare care revine cu regularitate printre scrisorile mele. Va dau aici principalele
deosebiri dintre aceste componente :
Aceasta inseamna ca pentru a accesa fisierul vostru sursa aveti o cale ce depaseste 62 de
caractere, aceasta datorita unor nume prea lungi sau a prea multor subdirectoare.
De exemplu :
C:\Mon_repertoire\sources\Sources_MPLAB\Cours\Cours_Part1\Exercices\test1.Asm
In acest caz va fi suficient sa va puneti directorul mai aproape de radacina, sau sa scurtati
numele folosite. Iata un exemplu corect :
C:\Cours_PIC_1\Exercices\test1.asm
Aceste probleme ar fi trebuit sa dispara totusi in versiunile mai recente ale MPLAB®.
204
Contributii benevole
Am deci nevoie de ajutorul vostru pentru a continua aceasta aventura. De fapt, eu nu mai
dispun cu adevarat de capacitatea de a-mi consacra tot timpul liber pentru a scrie cursuri si
programe fara a primi o mica "mina de ajutor".
Totodata, n-as vrea sa blochez accesul la fisiere, si sa impun o taxa pentru a le obtine. De
fapt, eu tin ca ele sa ramina disponibile pentru toata lumea.
Am decis deci sa instaurez un sistem de contributie benevola care sa permita celui ce
doreste, si in functie de propriile sale criterii, sa ma ajute financiar. Scopul nu este de a ma
imbogati eu, ci mai degraba de a ma ajuta "s-o scot la capat".
Nu este vorba deci de o plata, si nici de vreo obligatie. Este vorba doar de un ajutor fara
promisiuni de nici un fel, si fara constringeri. Eu voi continua sa raspund la mail-uri la toata
lumea, fara deosebire si fara intrebari despre acest subiect.
Deci, pentru cel ce doreste, o buna metoda ar fi ca el sa descarce documentul ales, sa-l
citeasca sau sa-l foloseasca, si apoi sa decida daca acesta merita osteneala de a ma ajuta in
functie de cit de util ii este.
Daca da, intrati pe site-ul meu : www.abcelectronique.com/bigonoff sau
www.bigonoff.org, si mergeti la pagina « cours-part1 ». Aici veti gasi, la pagina
« contributions », procedura de urmat. Ginditi-va ca aceste contributii imi sint foarte utile, si
imi vor permite sa-mi continui munca pentru voi.
Pentru a multumi celor care au contribuit, fie prin trimiterea unei scrisori, fie prin
infiintarea unui site de interes public, voi oferi softul BigoPIC v2.0
Nu uitati sa scrieti email-ul cu litere de tipar, pentru a putea sa va raspund.
Eu raspund intotdeauna scrisorilor primite. De aceea, daca nu primiti raspuns, nu ezitati
nici un moment sa ma contactati pentru a verifica daca nu exista vreo problema.
Multumesc anticipat tuturor celor care m-au ajutat, sau care ma vor ajuta sa continui
aceasta munca de sisif.
205
Folosirea prezentului document
De aceea, din aceste motive, si pentru usurarea intretinerii de catre mine, am hotarit ca
acest curs va fi disponibil numai pe site-ul meu : http://www.abcelectronique.com/bigonoff
sau www.bigonoff.org . Deasemenea, daca gasiti cursul meu in alta parte, va multumesc daca
ma veti atentiona.
Din nefericire, cu toate ca au fost avertizati, mai multi webmasteri au refuzat sa inlocuiasca
partea a 6-a a cursului disponibila pe site-ul lor printr-o legatura catre site-ul meu (probabil
din motive de rating). Din nefericire, aceasta se traduce prin internauti care tiparesc un curs
depasit (care trateaza de exemplu MPLAB5), numai de dragul unora de a-si vedea contoarele
de pagina invirtindu-se...
Bineinteles, ca am autorizat (si incurajat) webmasterii sa puna legaturi catre site-ul meu,
fara sa fie nevoie sa-mi ceara permisiunea. Deasemenea, voi face si eu acelasi lucru, daca mi
se va cere. Astfel, eu sper sa ating un maxim de utilizatori.
Toate drepturile asupra continutului acestui curs, si asupra programelor care-l insotesc,
ramin proprietatea autorului, si aceasta, chiar daca vreo notificare tinde sa demonstreze
contrariul (conditiile gazduitorului).
Autorul nu va putea fi facut raspunzator in nici un fel pentru consecintele directe sau
indirecte rezultate din lectura sau aplicarea cursului sau a programelor.
Toate utilizarile cu caracter comercial sint interzise fara consimtamintul scris al autorului.
Toate extrasele sau citatele cu titlu de exemplu trebuie sa fie insotite de referintele
documentului.
Autorul spera ca nu incalca nici un drept de autor realizind acest document si nu a folosit
decit programele puse gratuit la dispozitia publicului de catre firma MICROCHIP. Datasheet-
urile sint deasemenea disponibile gratuit pe site-ul firmei, la adresa :
http://www.microchip.com
Daca va place acest document si-l folositi, sau daca aveti critici, va multumesc daca-mi
trimiteti un mic email. Acestea imi vor permite sa stiu daca trebuie sa continui sau nu aceasta
206
aventura cu partile urmatoare.
• Eu nu fac programe pentru examenul de absolvire pentru studenti (chiar si daca platesc),
acestea fiind cereri care ajung in fiecare saptamina in corespondenta mea. Mai intii pentru
ca nu am timp, si mai apoi pentru ca nu consider asta un lucru bun. Si ca sa inchei, pentru
a ne amuza un pic, daca as da tarifele mele, acesti studenti ar risca un infarct.
• Din nefericire, nu am timp sa depanez programe complete. Nu-mi trimiteti deci programe
cu un mesaj de genul : "Nu merge, imi puteti spune de ce ?". Eu pierd mai mult de 2 ore
pe zi ca sa raspund la mesaje, si daca as vrea sa le depanez, as pierde toata ziua. Intelegeti
ca este imposibil, ginditi-va ca nu sinteti singurul care puneti intrebari. Mai degraba,
puneti o intrebare precisa despre partea care vi se pare incorecta.
Multumesc tuturor celor care mi-au scris pentru a-mi comunica sustinerea lor : credeti-ma
acest lucru te motiveaza.
Si o ultima remarca : este imposibil ca voi sa gasiti aici urma unui plagiat, ca dealtfel si in
cursurile precedente, dat fiind faptul ca eu nu am citit nicio lucrare pe acest subiect, altele
decit datsheet-urile de la MICROCHIP. Totul este deci izvorit din propriile mele experiente.
Daca gasiti vreo dovada de plagiat, sa stiti ca ceilalti au copiat, si acest lucru se aplica si la
cartile publicate conventional (aceasta remarca nu este nevinovata).
207
Editie terminata la 09/02/2001.
ATENTIE ! – Referintele de pagina sunt valabile pentru varianta originala, in limba franceza.
• Actualizare editia 9 la 01/11/2002 : Greseli de ortografie in paginile 82, 83, 85, 86, 87,
89, 90, 94.
• Actualizare editia 10 la 04/12/2002 : Suprimarea unei linii care crea confuzii la pagina
183, citeva corecturi minore la paginile 99, 100, 104, 108, 122, 124, 128, 129, 131,132,
134, modificaare contributii la pagina 209.
• Actualizare editia 17 la 09/06/2004 : Corecturi la paginile 34, 198, anexe, adaugarea unei
208
sugestii de programare, tabla de materii, modificarea adresei mele de email.
• Actualizare editia 19 le 27/03/06 : Corecturi la paginile 11, 12, 13, 15, 17, 19, 21, 24, 26,
27, 29, 30, 33, 37, 38, 40, 47, 50, 52, 54, 63, 72, 80, 83, 85, 93, 96, 114, 131, 133, 135,
171, 197, 198, 220, repunere in pagina a unor portiuni de cod ca urmare a unei modificari
accidentale de formatare, actualizare pagina 20, observatii la paginile 24 et 58. Adaugat o
anexa.
• Actualizare editia 29 le 05/10/10 : Corecturi in paginile 18, 19, 22, 41, 49, 56. Adaugarea
209
de explicatii in pagina 44 despre sfirsitul programelor (directiva END). Modificari totale
ale anexei A1.8 (alegerea programatorului).
Pe site-ul meu, veti gasi deasemena formulare pentru raportarea de informatii, care va
permit sa va exprimati asupra cursului si asupra fisierelor propuse pentru download.
Realizator : Bigonoff
IANUARIE 2011
210