0% found this document useful (0 votes)
173 views217 pages

Part1 r31

invatarea programari in limbaj asamblar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
173 views217 pages

Part1 r31

invatarea programari in limbaj asamblar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 217

PROGRAMAREA PIC® - urilor

Autor : BIGONOFF

Partea I

INTRODUCERE IN MICROCONTROLERE
CU PIC16F84

Editia 31
II
CUPRINS :

1. INTRODUCERE............................................................................................................................................... 1

2. SISTEMELE DE NUMERATIE ..................................................................................................................... 3


2.1 SISTEMUL ZECIMAL ....................................................................................................................................... 3
2.2 SISTEMUL BINAR ........................................................................................................................................... 3
2.3 SISTEMUL HEXAZECIMAL .............................................................................................................................. 5
2.4 OPERATIILE ................................................................................................................................................... 6
2.5 NUMERELE CU SEMN ..................................................................................................................................... 6
2.6 OPERATIILE BOOLEENE ................................................................................................................................. 7
2.6.1 Complementul (« NOT ») ..................................................................................................................... 8
2.6.2 Functia « SI » (« AND »)...................................................................................................................... 8
2.6.3 Functia « SAU » (« OR ») .................................................................................................................... 9
2.6.4 Functia « SAU EXCLUSIV » (« Exclusif OR » sau « XOR ») ............................................................. 9
2.7 CITEVA CUVINTE DESPRE UNITATI ............................................................................................................... 10
3. ALCATUIREA SI FUNCTIONAREA PIC®-URILOR.............................................................................. 13
3.1 CE ESTE UN PIC® ? ..................................................................................................................................... 13
3.2 DIFERITELE FAMILII DE PIC®-URI ............................................................................................................... 14
3.3 IDENTIFICAREA UNUI PIC®......................................................................................................................... 14
3.4 ORGANIZAREA UNUI 16F84......................................................................................................................... 15
3.4.1 Memoria de program.......................................................................................................................... 15
3.4.2 Memoria EEPROM............................................................................................................................. 16
3.4.3 Memoria RAM .................................................................................................................................... 16
4. ORGANIZAREA INSTRUCTIUNILOR ..................................................................................................... 17
4.1 GENERALITATI ............................................................................................................................................ 17
4.2 TIPURILE DE INSTRUCTIUNI ......................................................................................................................... 17
4.2.1 Instructiunile « orientate pe octet ».................................................................................................... 17
4.2.2 Instructiunile « orientate pe bit » ....................................................................................................... 17
4.2.3 Instructiunile generale........................................................................................................................ 18
4.2.4 Salturile si apelurile de subrutina ...................................................................................................... 18
4.3 PANORAMICUL INSTRUCTIUNILOR ............................................................................................................... 18
4.4 INDICATORII DE STARE ................................................................................................................................ 20
4.4.1 Indicatorul de stare « Z » ................................................................................................................... 20
4.4.2 Indicatorul de stare « C »................................................................................................................... 21
5. INTRODUCERE IN MPLAB® ..................................................................................................................... 23
5.1 PREGATIRI IN VEDEREA UTILIZARII MPLAB® ............................................................................................ 23
5.2 CREAREA PRIMULUI NOSTRU PROIECT ......................................................................................................... 23
6. ORGANIZAREA UNUI FISIER « .ASM » .................................................................................................. 30
6.1 COMENTARIILE............................................................................................................................................ 30
6.2 DIRECTIVELE ............................................................................................................................................... 30
6.3 FISIERELE « INCLUDE »................................................................................................................................ 30
6.4 DIRECTIVA _CONFIG................................................................................................................................. 31
6.5 ASIGNARILE ................................................................................................................................................ 32
6.6 DEFINITIILE ................................................................................................................................................. 32
6.7 MACRO-URILE ............................................................................................................................................. 33
6.8 ZONA VARIABILELOR .................................................................................................................................. 33
6.9 ETICHETELE ................................................................................................................................................ 34
6.10 DIRECTIVA « ORG » ................................................................................................................................. 34
6.11 DIRECTIVA « END » SI SFIRSITUL PROGRAMULUI...................................................................................... 34
7. REALIZAREA UNUI PROGRAM............................................................................................................... 36
7.1 CREAREA PRIMULUI NOSTRU PROGRAM ...................................................................................................... 36
7.2 ASAMBLAREA UNUI PROGRAM .................................................................................................................... 37
8. SIMULAREA UNUI PROGRAM ................................................................................................................. 40
8.1 LANSAREA SIMULATORULUI SI STABILIREA PARAMETRILOR ....................................................................... 40
8.2 EXPLICAREA REGISTRELOR FUNDAMENTALE .............................................................................................. 42

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

FOLOSIREA PREZENTULUI DOCUMENT............................................................................................... 206

VII
1. Introducere

Iata-ne asadar, impreuna, porniti in marea aventura a programarii PIC®-urilor. Am sa


incerc sa ramin cit mai concret posibil, insa totodata este necesara o anumita "cantitate" de
teorie pentru a atinge scopul urmarit.

Am sa incep deci acest mic curs, printr-o recapitulare a sistemelor de numeratie. Nu ma


indoiesc ca deja cunoasteti aceste notiuni, insa sint sigur ca veti intelege ca este imposibil sa
faceti o programare serioasa a unui microcontroler fara sa stiti ce este acela un bit, sau cum se
fac conversii din zecimal in hexazecimal si invers.

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 !

Eu personal am folosit MPLAB® versiunea 6.30 incepind de la editia 13 a cursului. In


editiile anterioare foloseam versiunea 5.20. Versiunea 6.60 este disponibila in sectiunea
"arhiva" de la Microchip®.

1
2
2. Sistemele de numeratie

2.1 Sistemul zecimal

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.

De ce zecimal ? Pentru ca foloseste o numeratie cu 10 cifre. Spunem ca este un sistem in


BAZA 10. Ca o istorioara, se foloseste un sistem in baza 10 pentru ca stramosii nostri au
inceput sa numere pe cele 10 degete ale lor, fara sa fie nevoie sa caute altceva.

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.

Observati aici un lucru foarte important : numararea rangurilor se face intotdeauna de la


dreapta la stinga, incepind cu 0. Pentru numarul nostru, 502, valoarea va fi deci :

502 = 2 x 100 + 0 x 101 + 5 x 102

Pentru inmultire puteam folosi si simbolul (*).

Si amintiti-va ca 100 = (10/10) = 1, ca 101 = 10, si ca 102 = 10 x 10 = 100 etc.

2.2 Sistemul binar

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).

Pentru a ne descurca in continuare, vom adopta urmatoarele conventii : toate numerele


zecimale vor fi scrise ca atare, sau sub forma D’xxx’ ; toate numerele binare vor avea forma
B’xxxxxxxx’ unde ‘x’ inseamna 0 sau 1, dupa cum veti vedea.

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

Cum, evident 0 de inmultit cu ceva = 0 si 1 inmultit cu o cifra da chiar cifra respectiva,


putem reduce calculul precedent la :

B ‘10010101’ = 1 + 4 + 16 + 128 = 149

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.

Se poate proceda de exemplu in felul urmator (una din metode) :

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.

Sa reluam numarul nostru 149 :

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.

2.3 Sistemul hexazecimal

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 :

Tabel de conversie pentru quarteti (semi-octeti)

Binar Hexazecimal Zecimal


B’0000’ 0x0 0
B’0001’ 0x1 1
B’0010’ 0x2 2
B’0011’ 0x3 3
B’0100’ 0x4 4
B’0101’ 0x5 5
B’0110’ 0x6 6
B’0111’ 0x7 7
B’1000’ 0x8 8
B’1001’ 0x9 9
B’1010’ 0xA 10
B’1011’ 0xB 11
B’1100’ 0xC 12
B’1101’ 0xD 13
B’1110’ 0xE 14
B’1111’ 0xF 15

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 :

0x95 = 9 x 161 + 5 x 160 = 149, ceea ce este corect.

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‘
--------------
?

- Adunam cifrele din dreapta, si obtinem 1 + 0 = 1


- Scriem 1
- Adunam 1 + 1, si obtinem 10 (in binar nu exista 2 !). Scriem 0 si retinem 1
- Adunam 0 + 1 + un 1 retinut si obtinem 10. Scriem 0 si retinem 1
- Adunam 1 + 1 + un 1 retinut si obtinem 11. Scriem 1 si retinem 1
- Ramine un 1 pe care l-am retinut, adica 1.

Rezultatul este deci B’11001’, adica 25.

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.

2.5 Numerele cu semn

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.

Pentru a obtine un numar negativ, va trebui sa parcurgem doua etape :

- Se inverseaza toti bitii din numar.


- Se adauga 1

Se obtine ceea ce se numeste COMPLEMENTUL FATA DE 2 al numarului.

6
Exemplu : fie numarul 5 : B’00000101’. Cum scriem –5 ?

- inversam toti bitii (complementul fata de 1) B’11111010’


- se adauga 1 (complementul fata de 2) –5 = B’11111011’

Pentru a face conversia inversa, se procedeaza la fel.

- Se inverseaza toti bitii B’00000100’


- Se adauga 1 B‘00000101’

Regasim numarul nostru initial, 5 ceea ce este logic, deoarece - (-5) = 5.

In cazul numerelor cu semn, avem deci doua noi limitari :

- Cea mai mare valoare este B’01111111’, adica +127


- Cea mai mica valoare devine B’10000000’, adica –128.

Remarcati ca operatiile continua sa functioneze. Sa luam –3 + 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.

Sa stiti ca numerele semnifica numai ceea ce realizatorul programului a decis ca reprezinta:


daca el lucreaza cu numere cu semn sau nu, sau daca acest octet reprezinta cu totul altceva (o
temperatura, un caracter, etc.). Singurul lucru important este ca sa se respecte conventiile
stabilite la crearea acestui octet. Este deci la latitudinea voastra de a decide de ce aveti nevoie
pentru astfel de tipuri de date.

2.6 Operatiile booleene

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 :

NOT B’10001111’ ne da ‘B01110000 ‘.

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.

2.6.2 Functia « SI » (« AND »)

Se mai numeste si inmultirea bit cu bit, sau « AND » si adesea se noteaza cu « & ».

Ea consta in a "suprapune" doua cuvinte binare si a inmulti fiecare bit cu bitul


corespunzator de acelasi rang. Deci, pentru a efectua o operatie « SI », avem nevoie
intotdeauna de 2 octeti.

Diferitele posibilitati sint prezentate in continuare (tabelul se citeste pe orizontala). Prima


linie : 0 AND 0 = 0. Acest tip de tabel se numeste « tabela de adevar »

Bit 1 Bit 2 AND


0 0 0
0 1 0
1 0 0
1 1 1

Observam deci ca singura posibilitate de a obtine un « 1 », este ca bitul 1 SI bitul 2 sa fie


« 1 ». Acest lucru corespunde unei inmultiri, 1 x 1 = 1, 0 x 1 = 0, 1 x 0 = 0.

Exemplu :

B’11001100’ SI B’11110000’ da B’11000000’

La ce serveste aceasta instructiune ? Ei bine, ea se foloseste pentru MASCAREA bitilor


care nu ne intereseaza.

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 »)

Se mai numeste OR ; notata adesea cu « | » ea permite, dupa cum indica si numele, sa


pozitionam un bit in "1" daca bitul 1 SAU bitul 2 este "1".

Urmatoarea tabela de adevar ilustreaza functionarea acestei functii :

Bit 1 Bit 2 OR
0 0 0
0 1 1
1 0 1
1 1 1

Un mic exemplu :

B’10001000’ SAU B’11000000’ va da B’11001000’

La ce serveste aceasta instructiune ? Ea serveste la fortarea in "1" a oricarui bit dintr-un


cuvint binar, indiferent de continutul sau precedent.

In exemplul precedent, observati ca primii 2 biti au fost fortati in "1", indiferent de


valoarea lor precedenta.

2.6.4 Functia « SAU EXCLUSIV » (« Exclusif OR » sau « XOR »)

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.

Iata deci tabela de adevar :

Bit 1 Bit 2 XOR


0 0 0
0 1 1
1 0 1
1 1 0

Exemplu :

B’10001000‘ XOR B’11000000’ va da B’01001000‘

La ce serveste aceasta instructiune ? Ei bine, ea foloseste la inversarea unuia sau mai


multor biti, fara a-i modifica pe ceilalti. In exemplul precedent, observati ca prin punerea la 1
a 2 biti din al doilea octet, bitii corespunzatori din primul octet au fost inversati.

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.

Simbolurile folosite in mod curent sint :

• pentru OCTET : « B » (pentru byte) sau « O » (pentru octet), sau chiar « o »


• pentru BIT : «b»

Englezii sint obligati sa foloseasca « B » pentru octet pentru a nu se confunda cu « b »,


simbolul pentru bit. In Franta nu exista aceasta confuzie, deoarece numai termenul "bit" a
scapat de traductomania franceza. Deci, pentru octet vom avea notatia « B » in toata lumea, cu
exceptia Frantei unde vom avea « O » sau « o ».

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.

Producatorii de dispozitive de stocare (hard-discuri), nu respecta insa aceste reguli. Ei au


ramas conservatori si considera multiplii ca puteri ale lui 10, in ciuda faptului ca toti
informaticienii gindesc in puteri ale lui 2. Astfel, cind cumparati un HDD de 100Mo (100
megaocteti), el va avea fix 100.000.000 octeti. Sistemele de operare din PC-uri (ex: Windows)
raporteaza insa « Megaoctetii informatici », adica 100.000.000 / 220 = 95.4 Megaocteti ! Pe
scurt, 100 Megaocteti ai fabricantului de hard-discuri reprezinta 95.4 Megaocteti pentru
informatician.
Este clar ca aceasta situatie nu putea continua. De aceea, pe la inceputul anilor 2000, s-a

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 :

Kibi (Ki) 210 1024 1,024k


Mebi (Mi) 220 1048576 1,048576 M
Gibi (Gi) 230 1073741824 1,073741824 G
Tebi (Ti) 240 1099511627776 1,099511627776 T

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.

3.1 Ce este un PIC® ?

Un PIC® nu este nimic altceva decit un microcontroler, adica o unitate de prelucrare a


informatiei de tip microprocesor, la care s-au adaugat periferice interne ce permit realizarea
de montaje fara sa fie nevoie sa adaugam componente externe.

Denumirea "PIC®" este un copyright al Microchip®, deci ceilalti fabricanti nu mai au


dreptul sa foloseasca aceasta denumire pentru microcontrolerele lor.

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.

Probabil ca ati ghicit ca pe piata se gasesc 2 familii diferite de microprocesoare, RISC si


CISC (Complex Instructions Set Computer). Circuitele CISC, dispun de o viteza mai redusa
de procesare, dar instructiunile sint mai complexe, mai puternice si deci mai numeroase. Deci,
se pune problema unei alegeri de strategie.

Toate PIC®-urile Mid-Range (de clasa medie) au un set de 35 instructions, fiecare


instructiune fiind stocata intr-un singur cuvint de program, iar executarea fiecarei instructiuni
(in afara de salturi) dureaza 1 singur ciclu. Se ajunge deci la o viteza de lucru foarte mare, iar
invatarea instructiunilor se face foarte rapid. Executia pe un singur ciclu este tipica pentru
componentele RISC.

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.

3.3 Identificarea unui PIC®

Pentru a identifica un PIC®, va veti folosi de indicativul sau.


Primele 2 cifre indica categoria PIC®-ului, 16 indicind un PIC® Mid-Range.

Urmeaza uneori litera L : aceasta indica faptul ca PIC®-ul poate functiona si cu o tensiune
mai redusa (Low Power).

In continuare, urmeaza :

C arata ca memoria de program este un EPROM sau, mai rar, un EEPROM


CR pentru a indica o memorie de tip ROM
F pentru a indica o memorie de tip FLASH.

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.

O componenta care nu poate fi reprogramata se numeste O.T.P. (One Time Programming)


adica o componenta cu programare unica.

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.

3.4 Organizarea unui 16F84

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.

3.4.1 Memoria de program

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.

3.4.3 Memoria RAM

Memoria RAM (Random Access Memory) este aceea pe care o folosim tot timpul. Toate
datele stocate insa in ea, se pierd la oprirea alimentarii.

Memoria RAM este organizata in 2 bancuri la 16F84. RAM-ul este sub-imparit in


continuare in doua zone. In fiecare din aceste bancuri gasim « locatii de memorie speciale »
denumite REGISTRE SPECIALE, si locatii de memorie « libere » pe care le puteti folosi
dupa placul dvs.

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.

Observati in partea superioara numele tuturor registrelor speciale folosite de PIC®. O sa


vorbim despre fiecare dintre ei, n-aveti grija !

Fiecare registru determina o functionare speciala a PIC®-ului, sau activeaza o functie


speciala. Observati faptul ca unele registre sint identice in cele 2 bancuri (FSR de exemplu).
Asta inseamna ca ele pot fi accesate fie din bancul 0 fie din bancul 1, fara nici o diferenta.

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..

4.2 Tipurile de instructiuni

Veti constata ca exista 4 tipuri de instructiuni :

4.2.1 Instructiunile « orientate pe octet »

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.

4.2.2 Instructiunile « orientate pe bit »

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 :

• 7 biti pentru a indica operandul (adresa locatiei din RAM).

4.2.3 Instructiunile generale

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 :

• 6 biti pentru codul instructiunii

• O valoare IMEDIATA codificata pe 8 biti (deci intre 0 si 255).

4.2.4 Salturile si apelurile de subrutina

Sint instructiuni care provoaca o rupere in secventa normala de derulare a programului. Ele
sint codificate in felul urmator :

• Instructiunile sint codificate pe 3 biti

• Destinatia (adresa ei) este codificata pe 11 biti

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

10 bits (210 = 1024 = 1 Ki).

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.

4.3 Panoramicul instructiunilor

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.

Prima coloana va indica MNEMONICUL si OPERANZII pentru fiecare operatie.


Mnemonicele sint cuvinte rezervate (deci pe care nu le puteti folosi decit pentru acest scop)
intelese si interpretate de catre programul de asamblare.

Remarcati aici confuzia de limbaj obisnuita pentru termenul ASAMBLOR, care se


foloseste odata pentru a indica programul care permite asamblarea codului (programul de
asamblare), iar altadata pentru a denumi limbajul folosit de editor (limbajul de asamblare).

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.

Pentru asamblorul MPLAB®, pe care-l vom utiliza in lectiile urmatoare, va trebui sa


folosim sintaxa care urmeaza. Vom avea, in ordine :

- 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).

Ø Prima coloana este rezervata pentru etichete (repere).

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.

Linia_mea ; aceasta este o eticheta


movf STATUS,W ; incarca registrul de stare in registrul de lucru

Ø A 2-a coloana a tabelului este o scurta descriere a instructiunii.

Ø A 3-a coloana ne indica numarul de ciclii necesari pentru executia instructiunii.

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.

Ø A 5-a coloana este primordiala, caci ea cuprinde INDICATORII DE STARE (sau


STATUS FLAGS) afectati dupa executarea instructiunii. Vom trata acesti indicatori in
detaliu, caci ei constituie cheile programarii..

Ø Ultima coloana trimite la notele din subsolul paginii.

- Nota 1 este foarte importanta, ea referindu-se la metoda de « citire / modificare / scriere »


proprie porturilor de intrare / iesire (I/O). Vom reveni la acestea in momentul cind vom vorbi
despre PORTURI.
- Nota 2 ne spune ca o modificare a unui timer reseteaza prescaler-ul. Vom reveni cind
vom aborda timerul TMR0.
- Nota 3 spune ca daca veti folosi instructiunea pentru modificarea contorului de program
(contorul care contine adresa URMATOAREI instructiuni de executat), va fi necesar un ciclu
suplimentar. Acest lucru este logic, caci aceasta echivaleaza cu un salt. Vom vedea ca aceasta
tehnica este practica pentru a cauta valori intr-un tabel construit in memoria de program.

4.4 Indicatorii de stare

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.

4.4.1 Indicatorul de stare « Z »

Indicatorul Zero functioneaza in felul urmator :

Daca rezultatul unei operatii CARE AFECTEAZA ACEST FLAG este 0, flag-ul Zero
trece in 1.

Deci, « sa nu amestecam borcanele »… Spunem ca daca « Z = 1 » este echivalent cu a


spune ca « rezultatul = 0 ». Tabelul 9-2, coloana 5 va arata instructiunile care modifica flag-
ul Z.

De exemplu, daca faceti o adunare cu ADDWF si rezultatul obtinut este 0, bitul Z va


deveni 1. Daca rezultatul va fi < > 0 (diferit de zero), bitul Z va fi 0. In cazul al doilea bitul a
fost modificat.

Din contra, daca stocati o valoare cu instructiunea MOVWF, bitul Z nu va fi modificat,


chiar daca valoarea stocata este 0. Aceste precizari sint valabile si pentru celelalte flag-uri, asa
ca nu voi mai reveni.

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 :

Daca adunati B’11111110’ (254)


+ B’00000011’ (3)
___________________
Veti obtine B’100000001’, (257) deci 9 biti.

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

De remarcat ca daca ati fi avut de adunat B’11111110’ si B’00000010’, ati fi obtinut


B’00000000’. In acest caz insa, veti avea C = 1 SI Z = 1, ceea ce inseamna rezultat nul, dar
cu transport (deci rezultatul va fi = 256).

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.

5.1 Pregatiri in vederea utilizarii MPLAB®

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.

Eu personal, nu agreez ideea de a-mi lasa datele impreuna cu programele in partitia


principala. Daca si dvs. ginditi la fel, creati in alta parte un director in care va veti pastra
datele si denumiti-l, de exemplu, DataPIC.

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 :

C:\Program Files\MPLAB IDE.

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.

5.2 Crearea primului nostru proiect

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.

MPLAB® este un program construit in jurul notiunii de "proiect". Un proiect permite sa se


memoreze toate variabilele mediului de lucru necesar in realizarea unui… proiect. El va va
permite refacerea completa a mediul dvs. de lucru atunci cind il redeschideti.

MPLAB® 6 dispune de un "Wizard" pentru crearea de proiecte, care va permite sa creati


automat noi proiecte. Noi nu vom folosi aceasta facilitate in scopul de a va obisnui cu
principalele optiuni ale proiectului si locul in care le gasiti. Daca totusi doriti sa folositi

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.

In aceasta fereastra, vedeti numele proiectului dvs. (essai1.mcp), si fisierele asociate


acestui proiect. Pentru moment, inca nu aveti niciunul, ceea ce este normal.

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®.

In ferestrele inferioare, veti vedea numele executabilelor utilizate de MPASM®, care


deocamdata nu va intereseaza. Apasati <OK> si inchideti fereastra.

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.

Pentru a nu uita, va sfatuiesc totusi ca INTOTDEAUNA sa puneti un prefix in fata


numerelor, ceea ce va evita multa bataie de cap. Folositi de exemplu, sintaxele urmatoare :

0x10, H’10’, sau 10h : notatii hexazecimale


B’00010000’ : notatii binare
D’16 ‘ : notatii zecimale
.16 : alta notatie zecimala (nu uitati punctul « . »)

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 !).

Pentru a deschide un fisier care nu este inclus in arborescenta, folositi meniul


File => Open…, sau icoana de deschidere din bara de instrumente.

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.

Obisnuiti-va ca intotdeauna sa adaugati comentarii programelor voastre. Fiti siguri ca dupa


6 luni, nu va veti mai aminti ce ati vrut sa faceti, comentariile fiind atunci de un mare folos
daca veti dori sa va modificati programul.

Vom completa deci cadrul indicind diferitele referinte.

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.

Spre deosebire de acestea, INSTRUCTIUNILE vor fi traduse in OPCODE si incarcate in


PIC®. Este neaparat necesar sa facem aceasta distinctie.

6.3 Fisierele « include »

Linia 35 indica asamblorului ca ASIGNARILE se gasesc in fisierul P16F84.inc. Ce contine


acest fisier ? Ei bine, acesta contine valoarea tuturor CONSTANTELOR pe care le vom
folosi.
Pentru a vedea ce contine acest fisier, mergeti in meniu la File => Open, selectati « All
source files » in cadrul inferior si deschideti P16F84.inc din directorul

C:\Program Files\MPLAB IDE\MCHIP_Tools pentru MPLAB® 6 si


C:\Program Files\Microchip\MPASM Suite pentru MPLAB® 7.

30
Odata depasita zona de comentarii, veti vedea linii de forma :

fsr EQU H'04'

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.

6.4 Directiva _CONFIG

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.

Am pus in fisier toate valorile posibile ale acestor parametri, cu explicatiile


corespunzatoare. Este suficient sa inlocuiti una din aceste valori cu cea dorita.
De exemplu, ca sa activam Code Protect (protectie la citire), vom inlocui simplu linia :

__CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _HS_OSC

cu linia :

__CONFIG _CP_ON & _WDT_ON & _PWRTE_ON & _HS_OSC

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 ».

La ce folosesc ? Acestea servesc la INTRETINEREA programului vostru. De fapt este


mult mai simplu sa retineti in programul vostru valoarea « MASCA » decit sa lucrati cu
valoarea 0x5B.

Asignarile se comporta ca o simpla substitutie. In momentul asamblarii, de fiecare data


cind asamblorul va gasi o asignare, o va inlocui automat cu valoarea sa.

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.

Va sfatuiesc cu toata convingerea sa folositi ASIGNARI si alte metode pe care le vom


vedea mai departe. Sintaxa e simpla si foloseste prescurtarea EQU (egal cu...).

Exemplu de asignare :

mavaleur EQU 0x05

6.6 Definitiile

Sa mergem putin mai jos… Vom descoperi in liniile de la 72 la 74 exemple de DEFINITII.


Sa stiti ca « definitiile » functioneaza ca si ASIGNARILE, numai ca noi rezervam asignarile
pentru valori, in timp ce definitiile inlocuiesc un text mai complex.

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 :

#DEFINE monbit PORTA , 1

Folosirea acestui macro se efectueaza foarte simplu folosind numele sau in program.

De exemplu :

bsf monbit ; pune monbit pe 1

va fi tradus de MPLAB® ca :

bsf PORTA , 1 ; pune monbit pe 1

32
6.7 Macro-urile

Mai jos, in liniile de la 82 la 85 gasim un MACRO :

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).

Un macro inlocuieste deci o bucata de cod pe care o folosim frecvent. Macro-ul


functioneaza numai ca o simpla prelucrare de text.

In exemplul nostru, de fiecare data cind va fi intilnit macro-ul LIREIN, el va fi inlocuit in


momentul asamblarii prin cele 2 linii :

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®.

6.8 Zona variabilelor

Toata zona definita de utilizator incepe cu DIRECTIVA CBLOCK, urmata de adresa de


inceput a zonei.

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

CBLOCK 0x00C ; inceput zona variabile

In continuare, puteti folosi 68 de locatii de memorie, care se vor conforma sintaxei


urmatoare : numele variabilei urmat de semnul « : » si urmat de numarul de locatii folosite.

De exemplu :

w_temp : 1 ; Zona de 1 byte


montableau : 8 ; Zona de 8 bytes

33
In continuare, va trebui sa precizati sfirsitul zonei curente cu ajutorul directivei :

ENDC ; Sirsit de zona

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.

6.10 Directiva « ORG »

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 :

org 0x000 ; Adresa de pornire dupa reset


goto init ; Adresa 0: initializare

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).

6.11 Directiva « END » si sfirsitul programului

Aceasta directiva precizeaza locul unde trebuie sa se incheie asamblarea programului


vostru. Ea este obligatorie in toate programele, caci in caz contrar va apare o eroare care va va
indica faptul ca s-a ajuns la sfirsitul fisierului (End Of File) fara sa fie intilnita o directiva
END.

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

Si daca ne imaginam ca « instructiunea x » se gaseste la adresa 0x50 (de exemplu), in


memoria flash a PIC®-ului veti gasi :

Adresa 0x50 : instructiunea x asamblata


Adresa 0x51 : instructiunea y asamblata
Adresa 0x52 : 0x3FFF (memorie flash goala = 14 biti in 1)
Adresa 0x53 : 0x3FFF (idem)
...

Altfel spus, PIC®-ul vostru, odata executata instructiunea y, nu se va opri ci va executa


instructiunea al carei cod hexazecimal este 0x3FFF. Si aceasta instructiune exista, este vorba
de « addlw 0xFF ». Deci, PIC®-ul vostru va executa “prosteste” instructiuni « addlw 0xFF »
pina la sfirsitul memoriei program. Odata ajuns la sfirsitul memoriei, el nu se opri nicidecum
ci va incrementa contorul de program care va depasi capacitatea de numarare si va reveni la
0x00. Programul vostru va incepe sa se execute inca odata (cu registrele eventual modificate
datorita primei treceri, si neinitializate, caci nu este vorba de un reset).

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

sau cu punere in Stand-by :

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

7.1 Crearea primului nostru 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.

goto start ; Adresa 0: pornire

Coboriti mai departe la linia 225, unde veti gasi eticheta noastra start, deci unde va sari
programul nostru. Stergeti apoi linia :

clrwdt ; sterge watch dog

Linistiti-va, vom lamuri totul in continuare....

Mai ramine deci o instructiune pe care deja am vazut-o, instructiunea « goto ».

goto start ; bucleaza

La pagina 62 din datasheet, invatam ca instructiunea « goto » este urmata de o valoare


IMEDIATA (adica o valoare sub forma unui numar), codata pe 11 biti (amintiti-va ca
programul poate avea 1 Ki, deci cu 11 biti putem sari oriunde in memoria de program).

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 !) :

clrf mavariable ; sterge mavariable

CLRF este o instructiune tratata detaliat in capitolul dedicat instructiunilor. Ea ne spune ca


locatia de memorie precizata dupa instructiune (sau o variabila) va fi stearsa. Bitul Z va fi
pozitionat conform rezultatului operatiei, ca de obicei.

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.

Pentru a incheia, inlocuiti

goto start

prin

goto boucle

Programul se va termina intotdeauna cu DIRECTIVA « END ». Programul vostru ar trebui


sa arate cam asa :

start
clrf mavariable ; sterge mavariable
boucle
incf mavariable , f ; incrementeaza mavariable
goto boucle ; bucleaza
END ; directiva de sfirsit de program

7.2 Asamblarea unui 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.

Deleting intermediary files... done.


Executing: "C:\Program Files\MPLAB IDE\MCHIP_Tools\mpasmwin.exe" /q /p16F84
"Essai1.asm" /l"Essai1.lst" /e"Essai1.err" /rDEC
Message[302] D:\DATAPIC\ESSAI1.ASM 199 : Register in operand not in bank 0. Ensure
that bank bits are correct.
Message[302] D:\DATAPIC\ESSAI1.ASM 215 : Register in operand not in bank 0. Ensure
that bank bits are correct.
Error[113] D:\DATAPIC\ESSAI1.ASM 227 : Symbol not previously defined (mavariable)
Error[113] D:\DATAPIC\ESSAI1.ASM 229 : Symbol not previously defined (mavariable)
Halting build on first failure as requested.

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.

Problema iese in evidenta la liniile « error ». Aici se spune ca simbolul « mavariable » nu a


fost definit de catre utilizator. Aceasta eroare se regaseste in cele 2 linii unde am folosit
aceasta variabila. De fapt, am uitat sa o DECLARAM. Pentru a remedia acest neajuns,
inchidem fereastra cu mesajele de eroare si ne intoarcem la editor.

Sa facem loc sub linia 97, in zona variabilelor, si sa adaugam :

mavariable : 1 ; declar variabila

Va trebui deci sa aveti ceva de genul :

CBLOCK 0x00C ; inceput zona variabile


w_temp :1 ; zona de 1 byte
status_temp : 1 ; zona de 1 byte
mavariable : 1 ; imi declar variabila mea
ENDC ; sfirsit zona

Sa reluam asamblarea, apasind <F10>. De data aceasta, totul decurge bine, indicatorul
ramine verde, iar dupa mesajele de atentionare, gasim mesajul :

BUILD SUCCEEDED: (constructie incheiata cu succes).

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

- Sa creati si sa modificati un proiect


- Sa va asamblati programul
- Sa-l rulati pe simulator pentru depanare.

8.1 Lansarea simulatorului si stabilirea parametrilor

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.

Pentru a vizualiza o variabila, dispuneti de mai multe metode :

• 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 »).

Pentru a suprima o variabila, este suficient sa o selectati si sa apasati <DEL>.

Pe ecran va trebui sa aveti ceva asemanator cu imaginea de mai jos :

Notati ca la pornire, MPLAB® considera ca valoarea variabilei voastre este « 0 », ceea ce


nu este neaparat adevarat in cazul unui PIC® real, deoarece in RAM se pot gasi valori
aleatoare dupa punerea sub tensiune.

Faceti click-dreapta si alegeti « properties » pentru a preciza stilul de afisare a valorilor,


adica 8 biti si format hexazecimal, desi asa ar trebui sa fie implicit.

41
Apasati apoi <OK> pentru a inchide fereastra.

8.2 Explicarea registrelor fundamentale

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.

8.2.1 Registrele « PCL » si « PCLATH »

Un procesor este o componenta care executa SECVENTIAL o serie de INSTRUCTIUNI


organizate intr-un ansamblu denumit PROGRAM.

Exista deci in procesor un SECVENTIATOR, adica un numarator care va indica


URMATOAREA instructiune ce va fi executata. Acest secventiator, in tehnica procesoarelor
este denumit « numarator de ordine », « indicator de program » etc. In cazul PIC®-urilor, el
se numeste PC, de la Program Counter. PC-ul nu este accesibil direct utilizatorului.

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).

Mai exista un alt registru de 5 biti care participa la functionarea secventiatorului. El se


numeste PCLATH (PC LATch counter High). El este deasemenea accesibil atit pentru citire,
cit si pentru scriere de catre utilizator.

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.

- In cazul modificarii directe a registrului PCL de catre utilizator, la fel ca pentru un


registru obisnuit, PCL este incarcat in PC si se completeaza cu cei 5 biti din registrul
PCLATH. Cum 16F84 nu adreseaza decit 1Ki cuvint de memorie program (210),
bitii b2, b3 si b4 ai PCLATH nu vor fi folositi.

Observati ca limita registrului PC este de 13 biti, ceea ce inseamna ca PIC®-urile din


familia mid-range vor putea adresa o capacitate de memorie de program de maxim 8Ki
cuvinte (adica 213).

Este foarte important sa ne amintim ca registrul PC indica intotdeauna adresa instructiunii


urmatoare, deci a instructiunii care n-a fost inca executata. Este neaparat necesar sa intelegem
bine acest lucru pentru a putea analiza programele pe care le depanam.

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.

8.2.3 Registrul « STATUS »

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.

b2 : Z Zero Acest bit e pus in 1 daca rezultatul ultimei operatii da 0.


Amintiti-va totusi ca acesti biti (flag-uri) nu se modifica decit
pentru instructiunile care ii precizeaza (Status bit affected).

b3 : PD Power Down Indica ce eveniment a generat ultima oprire a PIC®-ului


(instructiunea SLEEP sau depasirea timer-ului WatchDog).
Vom vedea acest lucru mai departe. Veti observa ca bitul PD
are o mica bara deasupra, care semnifica faptul ca starea
activa este 0. Deci, cind avem 0 => bit validat. Daca nu puteti
scrie aceasta bara superioara, scrieti inversiunile in
underline.

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

b7 : IRP Indirect RP Permite sa se decida ce banc se adreseaza in cazul unei


adresari indirecte (pe care o vom studia mai departe).

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).

Amintiti-va ca resetul provoaca pornirea de la adresa 0x00. Deci, sa verificam :

- Deasupra liniei selectate, gasiti directiva « ORG 0x00 » care precizeaza ca linia
urmatoare este la adresa 0x00 : primul semn bun.

- In continuare, sa examinam PCL si PCLATH, care amindoua sint 0 : deci,


urmatoarea instructiune executata este cea situata la adresa 0x00. Din nou, bine.

Sa examinam linia in chestiune. Ea ne spune ca urmatoarea instructiune, dupa executia sa


va fi cea situata la adresa « start ».

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 :

PCL 34 52 00110100, adica 0x34, sau 52 zecimal, sau B’00110100’

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.

O simulare, ramine totusi, o simulare...

Linia la care am ajuns este :

incf mavariable , f ; incrementeaza mavariable

Registrul PCL va indica : PCL 35 53 00110101

adica adresa urmatoare : fara salt, fara ruptura de secventa, deci functionare secventiala a
programului.

Sa aruncam o privire in fereastra « Watch », unde vedem variabila « mavariable ».


Valoarea sa este 0x00 (sau H’00’), si adresa ei este 0x0E. De ce ?
Sa examinam putin zona de declarare a variabilelor :

CBLOCK 0x00C ; inceputul zonei variabilelor


w_temp :1 ; zona de 1 byte
status_temp : 1 ; zona de 1 byte
mavariable : 1 ; declar variabila mea

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.

Apasati acum <F7> pina cind linia urmatoare va fi executata :

incf mavariable , f ; incrementeaza mavariable

Va fi selectata linia urmatoare :

goto boucle ; bucleaza

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).

Sa examinam acum registrul STATUS : ce constatam ?

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.

- Rularea pas-cu-pas cu tasta <F8> va permite sa faceti acelasi lucru ca si cu <F7>, cu


diferenta ca o subrutina (pe care o vom vedea mai departe), este executata ca un
singur pas, ca si cum ar fi o singura instructiune. Cu tasta <F7> se va intra in
subrutina pentru a executa instructiunile una cite una.

Si inca o metoda... Mergeti la linia

goto boucle ; bucleaza

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>.

In fereastra « Trace », veti obtine raportul tuturor instructiunilor executate de programul


vostru, de la lansare si pina la oprire. Acest mod de lucru se dovedeste foarte util atunci cind
apar rezultate inexplicabile in urma rularii programului.

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.

Bineinteles ca dvs. puteti experimenta aceste instructiuni in MPLAB® si in simulatorul


acestuia, inserindu-le dupa eticheta « start » din programul vostru.

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.

9.1 Instructiunea « GOTO » (du-te la)

Aceasta instructiune realizeaza ceea ce se numeste un salt neconditionat, sau o ruptura de


secventa sincrona neconditionata.

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.

Iata deci, in rezumat, cum functioneaza goto :

- Adresa de salt pe 11 biti este incarcata in PC


- Cei 2 biti lipsa sint incarcati din PCLATH (b3 si b4), (nu si pentru 16F84).
- Se obtine o adresa de 13 biti (10 biti pentru 16F84)
- Continuarea programului se face de la noua adresa din PC

Sa ne amintim ca pentru 16F84, adresa de salt = adresa reala. Nu va trebui deci sa va


preocupati de nimic. Pentru celelalte, in caz de depasire, MPLAB® vi le va semnala.

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.

9.2 Instructiunea « INCF » (INCrement File)

Aceasta instructiune provoaca incremenetarea continutului unei locatii (denumita si File).

Sintaxa

incf f , d

Pentru toate instructiunile, « f » reprezinta « File », adica locatia de memorie la care se


refera operatia respectiva, « d », reprezentind destinatia.

Daca nu specifica altfel, « d », va fi intotdeauna, la alegere :


• f (litera f) : in acest caz rezultatul va fi depus in locatia de memorie.
• W (litera W) : in acest caz rezultatul va fi depus in registrul de lucru W, iar continutul
locatiei de memorie nu este modificat. Acest registru de lucru se mai numeste si
« acumulator »

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.

Bitii afectati din registrul STATUS

Singurul bit afectat de aceasta operatie este bitul Z.


Singurul mod de a obtine 0 incrementind un octet, este de a trece de la 0xFF la 0x00.
Transportul nu este necesar, deci nu va fi luat in consideratie. Deci, daca dupa o incrementare
obtineti Z = 1, inseamna ca aveti depasire. Z va fi deci 1, daca inainte de executie (f) era
0xFF.

Exemplu :

incf mavariable , f ; continutul locatiei mavariable este


; marit cu 1. Rezultatul este depus in
; locatia mavariable
incf mavariable , w ; continutul locatiei mavariable este
; incarcat in W si marit cu 1. W contine
; deci continutul lui mavariable + 1.
; Variabila mavariable nu este modificata.

9.3 Instructiunea « DECF » (DECRement File)

Decrementeaza continutul locatiei specificate. Functionarea sa este identica cu cea a


instructiunii precedente.

Sintaxa

decf f , d ; (f) – 1 -> (d)

51
Bitii afectati din registrul STATUS

Singurul bit afectat de aceasta operatie este bitul Z.


Daca inainte de executia instructiunii (f) era 1, Z va deveni 1 dupa executie (1 – 1 = 0).

Exemplu :

decf mavariable , f ; decrementeaza mavariable, rezultatul


; e pus in mavariable
decf mavariable , w ; decrementeaza mavariable, si pune
; rezultatul in W

9.4 Instructiunea « MOVLW » (MOVe Literal to W)

Aceasta instructiune incarca valoarea specificata (valoare literala, sau valoare imediata), in
registrul de lucru W.

Sintaxa

movlw k ; k->W : k reprezinta o valoare intre 0x00 si 0xFF.

Bitii afectati din registrul STATUS

Niciunul (chiar daca incarcati valoarea "0").

Exemplu :

movlw 0x25 ; incarca 0x25 in registrul W

9.5 Instructiunea « MOVF » (MOVe File)

Incarca continutul locatiei specificate in destinatie.

Sintaxa
movf f , d ; (f) -> (d)

Bitii afectati din registrul STATUS

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.

movf mavariable , w ; incarca continutul lui mavariable in W.

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.

Daca executam instructiunea urmatoare :

movlw mavariable

asamblorul o va traduce inlocuind "mavariable" prin VALOAREA sa. Atentie, valoarea nu


inseamna CONTINUTUL. Asamblorul va realiza deci urmatoarea inlocuire :

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).

Din contra, daca executam instructiunea urmatoare :

movf mavariable , w

asamblorul o va traduce deasemenea, insa va inlocui "mavariable" prin valoarea sa. Vom avea
deci :

movf 0x0E , w

Ceea ce inseamna : incarca CONTINUTUL locatiei 0x0E in W. Vorbim aici de o


ADRESARE DIRECTA. Precizez ca este primordial sa nu facem confuzii, in caz contrar, nu
veti mai intelege nimic despre realizarea programelor.
Dupa aceasta instructiune, W va contine deci 0x50. Evolutia este de tipul (f) => (d).

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.

9.6 Instructiunea « MOVWF » (MOVe W to File)

Permite salvarea continutului registrului de lucru W intr-o locatie de memorie.

Sintaxa

movwf f ; (W) -> (f)

Bitii afectati din registrul STATUS

Niciunul.

53
Exemplu :

movlw 0x50 ; incarca 0x50 in registrul W


movwf mavariable ; mavariable contine acum 0x50.

9.7 Instructiunea « ADDLW » (ADD Literal and W)

Aceasta operatie permite adaugarea unei valori literale (adresare imediata) la continutul
registrului de lucru W.

Sintaxa

addlw k ; (W) + k -> (W)

Bitii afectati din registrul STATUS

Z Daca rezultatul operatiei devine 0, Z va fi pus in 1


C Daca rezultatul operatiei este superior lui 0xFF (255), C va fi pus in 1
DC Daca rezultatul operatiei produce un transport intre bitii 3 si 4, DC va fi pus in 1

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 :

movlw 253 ; incarca 253 (in zecimal) in W


addlw 4 ; adauga 4. W contine 1, Z devine 0,
; C devine 1 (depasire).
addlw 255 ; adauga 255. W devine 0, C devine 1, Z devine 1

9.8 Instructiunea « ADDWF » (ADD W and F)

A nu se confunda cu instructiunea precedenta. Inca odata, aici avem o ADRESARE


DIRECTA. CONTINUTUL registrului W este adaugat la CONTINUTUL registrului F.

Sintaxa

addwf f , d ; (w) + (f) -> (d)

Bitii afectati din registrul STATUS

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)

Atentie, aici este o capcana. Instructiunea ar fi trebuit sa se numeasca SUBWL. De fapt, se


scade W din valoarea literala, si nu invers.

Sintaxa

sublw k ; k – (W) -> (W)

Bitii afectati din registrul STATUS

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.

Formula de scadere va fi deci : k precedat de un al 9-lea bit – continutul lui W =


rezultat pe 8 biti in W cu al 9-lea bit in C.

Exemplul 1 :

movlw 0x01 ; incarca 0x01 in W


sublw 0x02 ; scade W din 2
; rezultat : 2 – (W) = 2-1 = 1
; Z = 0, C = 1, deci rezultat pozitiv

Sa efectuam aceasta operatie manual ::

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 :

movlw 0x02 ; incarca 0x02 in W


sublw 0x02 ; scade 2 – (w) = 2 – 2 = 0
; Z = 1 , C = 1 : rezultat nul

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

Se procedeaza intotdeauna in acelasi fel.

Exemplul 3 :

movlw 0x03 ; incarca 0x03 in W


sublw 0x02 ; scade 2 – (W) = 2 – 3 = -1
; Z = 0, C = 0, rezultat negativ

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

Procedind la fel, vom obtine B’11111111’, cu bitul C = 0. Si atunci, veti spune,


B’11111111’, este FF, nu –1. Ei bine, amintiti-va ca VA TREBUI sa cititi bitul C pentru a
interpreta corect rezultatul scaderii.

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

subwf f , d ; (f) – (W) -> (d)

Bitii afectati din registrul STATUS

C , DC , Z

Exemplu :

movlw 0x20 ; se incarca 0x20 in w


movwf mavariable ; pune w in (mavariable) (0x20)
movlw 0x1F ; incarca 0x1F in w
subwf mavariable , w ; (mavariable) - (w) -> (w)
; adica 0x20 – 0x1F = 0x01
; rezultat in w, C = 1, Z = 0
movwf autrevariable ; salveaza 0x01 intr-o alta variabila

9.11 Instructiunea « ANDLW » (AND Literal with W)

Aceasta instructiune realizeaza o operatie « SI » BIT CU BIT intre continutul registrului W


si valoarea literala care urmeaza.

Sintaxa

andlw k ; unde k = octet : (w) AND k -> (w)

Bitii afectati din registrul STATUS

Exemplu :

movlw B’11001101’ ; incarca w


andlw B’11110000’ ; efectueaza un "and" (&)

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

Amintiti-va ca se efectueaza un SI pentru fiecare pereche de biti de acelasi rang. Singurii


biti care ramin 1 sint cei pentru care ambii operanzi sint 1. Deci, facind un SI cu valoarea
B’11110000’ MASCAM bitii de la 0 la 3 si nu lasam accesibili decit bitii de la 4 la 7.
Atit timp cit nu jonglati in hexazecimal, va sfatuiesc ca intotdeauna sa traduceti numerele
in binar pentru toate instructiunile ce manipuleaza biti.

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

andwf f , d ; (f) AND (w) -> (d)

Bitii afectati din registrul STATUS

Exemplu :

movlw 0xC8 ; incarca 0XC8 in w


movwf mavariable ; salveaza in mavariable
movlw 0xF0 ; incarca masca
andwf mavariable , f ; (mavariable) = 0xC0 (s-a eliminat
; quartet-ul mai putin semnificativ)

9.13 Instructiunea « IORLW » (Inclusive OR Literal with W)

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

iorlw k ; (w) OR k -> (w)

Bitii afectati din registrul STATUS

Exemplu :

movlw 0xC3 ; incarca 0xC3 in W


iorlw 0x0F ; FORTEAZA bitii de la 0 la 3 in 1
; rezultat ; (w) = 0xCF

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)

Efectueaza un SAU intre registrul W si o locatie specificata. Este deci o instructiune cu


ADRESARE DIRECTA. N-am sa mai dau exemplu, deoarece cred ca ati inteles.

Sintaxa

iorwf f , d ; (w) OR (f) -> (d)

Bitii afectati din registrul STATUS

9.15 Instructiunea « XORLW » (eXclusive OR Literal with W)

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

xorlw k ; (w) xor k -> (w)

Bitii afectati din registrul STATUS

Exemplu :

movlw B’11000101’ ; incarca W


xorlw B’00001111’ ; XOR cu valoarea
; rezultat : B ‘11001010’. Cei 4 biti mai
; putin semnificativi au fost inversati

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)

Este exact aceeasi operatie ca si XORLW, dar cu ADRESARE DIRECTA.

Sintaxa

xorwf f , d ; (w) xor (f) -> (d)

Bitii afectati din registrul STATUS

9.17 Instructiunea « BSF » (Bit Set F)

Este o instructiune care permite intr-un mod foarte simplu de a FORTA direct un bit
dintr-o locatie de memorie, in 1.

Sintaxa

bsf f , b ; bitul b din locatia de memorie (f) este pus in 1


; evident, b este cuprins intre 0 si 7

Bitii afectati din registrul STATUS

Niciunul

Exemplu :

bsf STATUS , C ; pune bitul C in 1 in registrul STATUS


bsf mavariable , 2 ; pune bitul 2 al (mavariable) in 1

9.18 Instructiunea « BCF » (Bit Clear F)

Este o instructiune care permite intr-un mod foarte simplu de a FORTA direct un bit
dintr-o locatie de memorie, in 0.

Sintaxa

bcf f , b ; bitul b din locatia de memorie (f) este pus in 0


; evident, b este cuprins intre 0 si 7

Bitii afectati din registrul STATUS

Niciunul.

Exemplu :

bcf STATUS , C ; pune bitul C din registrul STATUS pe 0


bcf mavariable , 2 ; pune bitul b2 al (mavariable) pe 0

60
9.19 Instructiunea « RLF » ( Rotate Left through Carry)

Rotatie catre stinga prin Carry.


Operatiile de decalare sint operatii foarte des folosite. PIC®-urile au particularitatea ca nu
poseda decit operatii de ROTATIE. Veti vedea ca folosind aceste operatii, se pot realiza
foarte usor decalaje.
Operatia de ROTIRE CATRE STINGA se efectueaza astfel : Se memoreaza bitul C
(Carry), dupa care fiecare bit al octetului este deplasat catre stinga. Vechiul b7 iese din octet
prin stinga si devine noul Carry. Vechiul b0 este inlocuit cu vechiul Carry. Este vorba deci, de
o rotatie pe 9 biti.

Sintaxa

rlf f , d ; (f) rotatie catre stinga prin carry -> (d)

Bitii afectati din registrul STATUS

Exemplul 1 :

Un mic exemplu poate fi mai util decit un discurs lung.

bsf STATUS , C ; pune carry in 1


movlw B’00010111’ ; incarca valoarea in w
movwf mavariable ; initializeaza mavariable
rlf mavariable , f ; rotatie spre stinga

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 :

bcf STATUS , C ; pune carry pe 0


movlw b’00010111’ ; incarca valoarea in w
movwf mavariable ; initializeaza mavariable
rlf mavariable , f ; rotatie spre stinga

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)

Rotatie catre dreapta prin Carry.

Operatia de ROTIRE CATRE DREAPTA se efectueaza astfel : Se memoreaza bitul din


Carry, dupa care fiecare bit al octetului este deplasat catre dreapta. Vechiul b0 iese din octet
prin dreapta si devine noul Carry. Vechiul Carry devine noul bit b7. Este vorba deci,
deasemenea, de o rotatie pe 9 biti.

Sintaxa

rrf f , d ; (f) rotatie catre dreapta prin carry -> (d)

Bitii afectati din registrul STATUS

Exemplul 1 :

bsf STATUS , C ; pune carry pe 1


movlw B’00010111’ ; incarca valoarea in w
movwf mavariable ; initializeaza mavariable
rrf mavariable , f ; rotatie spre dreapta

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 :

bcf STATUS , C ; pune le carry pe 0


movlw b’00010111’ ; incarca valoarea in w
movwf mavariable ; initializeaza mavariable
rrf mavariable , f ; rotatie spre dreapta

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.

9.21 Instructiunea « BTFSC » (Bit Test F, Skip if Clear)

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

btfsc f , b ; se testeaza bitul b din locatia (f).


; daca bitul e 0, se sare PESTE instructiunea
; urmatoare, daca nu, se executa
; instructiunea urmatoare
Instructiune executata daca e fals ; daca bitul e 0, nu va fi
; executata (skip)
Continuare program ; programul continua aici

Bitii afectati din registrul STATUS

Niciunul.

Exemplul 1 :

Iata un exemplu in care trebuie sa se execute o singura instructiune suplimentara daca bitul
testat este 1.

btfsc STATUS , C ; testeaza daca bitul C din STATUS e 0


bsf mavariable , 2 ; nu (C = 1), atunci bitul 2 din mavariable
; e pus in 1
xxxx ; continuare program daca C = 0

Exemplul 2 :

Ce facem daca tratarea necesita mai multe instructiuni ? Ei bine, se combina salturile
conditionate cu cele neconditionate (de exemplu goto).

movlw 0x12 ; incarca 12 in registrul de lucru


subwf mavariable , f ; se scade 0x12 din mavariable
btfsc STATUS , C ; se testeaza daca rezultatul este negativ
;(C=0)
goto positif ; nu, atunci se sare la tratamentul pentru
; pozitiv
xxxx ; se continua aici daca rezultatul este
; negativ

Aceste proceduri sint identice pentru toate salturile conditionate, deci nu le voi mai detalia
cu alte explicatii.

9.22 Instructiunea « BTFSS » (Bit Test F, Skip if Set)

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

btfss f, b ; se testeaza bitul b din memoria (f).


; daca bitul e 1, se sare PESTE instructiunea
; urmatoare, daca nu, se executa
; instructiunea urmatoare
xxxx ; daca bitul este 1, nu va fi executata (skip)
xxxx ; programul continua aici

Bitii afectati din registrul STATUS

Niciunul.

Exemplu :

btfss STATUS , C ; testeaza daca bitul C din STATUS este 1


bsf mavariable , 2 ; nu, (C = 0), atunci bitul 2 din mavariable
; e pus in 1
xxxx ; continuare program daca C = 1

9.23 Instructiunea « DECFSZ » (DECrement F, Skip if Z)

Vom continua studiul salturilor conditionate cu o instructiune foarte utilizata pentru


creerea de bucle. Aceasta instructiune decrementeaza o locatie de memorie si sare daca
rezultatul decrementarii da o valoare nula.

Sintaxa

decfsz f , d ; (f) –1 -> (d). Salt daca (d) = 0

Bitii afectati din registrul STATUS

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

Cum a fost gindit acest program ? Foarte simplu, iata :

- se initializeaza contorul de bucla


- se pune o eticheta de inceput de bucla
- se scriu instructiunile care trebuie executate de mai multe ori
- instructiunea decfsz permite determinarea sfirsitului buclei
- instructiunea goto permite localizarea inceputului buclei

64
ATENTIE !

• Daca ati pus

Decfsz contor , w ; decrementeaza contorul si testeaza-i


; valoarea

bucla va rula la infinit, caci variabila contor nu va fi niciodata modificata.

• 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 :

movf contor , f ; se pozitioneaza Z


btfsc STATUS , Z ; salt daca Z = 0, deci daca contor >0
goto suite ; contor = 0, nu se intra in bucla
boucle ; eticheta inceput de bucla
addwf mavariable , f ; aduna 5 la mavariable
decfsz contor , f ; decrementeaza contorul si testeaza-i
; valoarea
goto boucle ; daca contorul nu e 0, din nou bucla
suite ; se sare direct aici daca contor = 0
movf mavariable , w ; incarca valoarea obtinuta in w

9.24 Instructiunea « INCFSZ » (INCrement F, Skip if Zero)

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

incfsz f , d ; (f) + 1 -> (d) : salt daca (d) = 0

Bitii afectati din registrul STATUS

Niciunul.

9.25 Instructiunea « SWAPF » (SWAP nibbles in F)

Putem traduce aceasta instructiune prin « inverseaza cuartetii in F ». Aceasta operatie


inverseaza cuartetul (semi-octetul) inferior cu cel superior.

Sintaxa

swapf f , d ; inverseaza bitii b0/b3 din (f) cu b4/b7 -> (d)

Bitii afectati din registrul STATUS

Niciunul : aceasta particularitate ne va fi foarte utila atunci cind vom vorbi despre
65
intreruperi.

Exemplu :

movlw 0xC5 ; incarca 0xC5 in W


movwf mavariable ; pune continutul lui W in mavariable
swapf mavariable , f ; (mavariable) = 0x5C

9.26 Instructiunea « CALL » (CALL subroutine)

Aceasta instructiune realizeaza un salt neconditionat catre un sub-program. Ce este un sub-


program ? Ei bine, acesta este o bucata de program care poate fi apelata din mai multe locuri
de catre programul asa-zis « principal ».

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

call eticheta ; apel de subrutina la adresa "eticheta".

Mecanism :

La lansarea instructiunii, adresa instructiunii URMATOARE este salvata in virful unei


stive (ca o stiva de farfurii). Atunci cind subrutina este terminata, adresa memorata este
extrasa din stiva si incarcata in PC. In acest fel, programul continua din locul in care
ramasese.

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.)

Bitii afectati din registrul STATUS

Niciunul.

9.27 Instructiunea « RETURN » (RETURN from subroutine)

Intoarcere din subrutina. Este intotdeauna pereche cu o instructiune CALL. Aceasta


instructiune indica sfirsitul portiunii de program considerata ca subrutina (SR). Retineti ca
pentru orice instructiune « CALL » intilnita, programul va trebui sa contina o instructiune
« RETURN ».

66
Sintaxa

return ; sfirsit de subrutina. PC-ul este incarcat din stiva


; programul va continua de la adresa care urmeaza
; dupa instructiunea CALL

Bitii afectati din registrul STATUS

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 :

movlw 0xCA ; valoare contor


movwf contor ; initializare contor de bucla
boucle
decfsz contor , f ; decrementare contor, salt daca e 0
goto boucle ; bucla daca nu e 0
xxx ; continuare program

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 :

xxx ; instructiune oarecare


xxx ; instructiune oarecare
xxx ; instructiune oarecare
xxx ; instructiune oarecare
tempo ; aici, e nevoie de temporizare
xxx ; instructiune oarecare
xxx ; instructiune oarecare
xxx ; instructiune oarecare
tempo ; aici deasemenea
xxx ; instructiune oarecare
xxx ; instructiune oarecare
tempo ; si inca odata aici
xxx ; instructiune oarecare

Primul lucru care ne vine in minte, este sa copiem temporizarea in locurile necesare. Se
obtine deci un program ca acesta :

xxx ; instructiune oarecare


xxx ; instructiune oarecare
movlw 0xCA ; valoare contor
movwf contor ; initializare contor de bucla
boucle
decfsz contor , f ; decrementare contor, salt daca e 0
goto boucle ; bucla daca nu e 0
xxx ; instructiune oarecare
xxx ; instructiune oarecare
xxx ; instructiune oarecare
movlw 0xCA ; valoare contor
movwf contor ; initializare contor de bucla

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.

tempo ; eticheta de inceput a subrutinei


movlw 0xCA ; valoare contor
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

In a doua etapa, vom modifica programul principal pentru ca de fiecare data cind avem
nevoie de o temporizare, sa apeleze subrutina. Vom obtine :

xxx ; instructiune oarecare


xxx ; instructiune oarecare
xxx ; instructiune oarecare
call tempo ; apelare subrutina
xxx ; instructiune oarecare, programul continua aici
xxx ; instructiune oarecare
xxx ; instructiune oarecare
call tempo ; apelare subrutina
xxx ; instructiune oarecare, programul continua aici
xxx ; instructiune oarecare
call tempo ; apelare subrutina
xxx ; instructiune oarecare, programul continua aici

In acest caz, subrutina "tempo" nu apare decit o singura data in memoria program.

Se pot face si imbunatatiri : sa presupunem ca dorim o temporizare cu durata variabila. Se


modifica subrutina suprimind valoarea de initializare, pe care o punem in programul principal.
Aceasta se numeste sub-program cu transfer de parametru(i).

Ca exemplu, programul nostru devine :

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

Iar programul principal va arata cam asa :

xxx ; instructiune oarecare


xxx ; instructiune oarecare
xxx ; instructiune oarecare
movlw 0x25 ; incarca w cu 0x25
call tempo ; apelare subrutina tempo, cu durata 0x25
xxx ; instructiune oarecare, programul continua aici
xxx ; instructiune oarecare
xxx ; instructiune oarecare
movlw 0x50 ; incarca w cu 0x50
call tempo ; apelare subrutina tempo, cu durata 0x50
xxx ; instructiune oarecare, programul continua aici
xxx ; instructiune oarecare
movlw 0x10 ; incarca w cu 0x10
call tempo ; apelare subrutina tempo, cu durata 0x10
xxx ; instructiune oarecare, programul continua aici

Iata ca acum stiti ce este un sub-program. Copilaresc, nu-i asa ?

9.28 Instructiunea « RETLW » (RETurn with Literal in W)

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

retlw k ; (w) = k, dupa care return

Bitii afectati din registrul STATUS

Niciunul.

Exemplu :

test ; eticheta de inceput a subrutinei


btfss mavariable , 0 ; testeaza bitul 0 al mavariable
retlw 0 ; daca e 0, sfirsit de subrutina cu (w)=0
retlw 1 ; dac nu, se iese cu (w) = 1

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

retfie ; intoarcere din intrerupere

Bitii afectati din registrul STATUS

Niciunul.

9.30 Instructiunea « CLRF » (CLeaR F)

Aceasta instructiune sterge continutul unei locatii de memorie specificate.

Sintaxa

clrf f ; (f) = 0

Bitii afectati din registrul STATUS

Z, care devine intotdeauna 1 dupa aceasta operatie.

Exemplu :

clrf mavariable ; (mavariable) = 0

9.31 Instructiunea « CLRW » (CLeaR W)

Aceasta instructiune sterge registrul de lucru W.

Sintaxa

clrw ; (w) = 0

Este o instructiune care nu este cu adevarat indispensabila, caci se poate folosi


instructiunea « movlw 0 ». Diferenta este ca in acest din urma caz, bitul Z nu e afectat.

Bitii afectati din registrul STATUS

Z, care devine intotdeauna 1 dupa aceasta operatie.

9.32 Instructiunea « CLRWDT » (CLeaR WatchDog)

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

clrwdt ; reseteaza timerul watchdog

Bitii afectati din registrul STATUS

Niciunul.

9.33 Instructiunea « COMF » (COMplement F)

Efectueaza complementarea unei locatii de memorie specificate, adica inverseaza toti bitii
octetului din aceasta locatie.

Sintaxa

comf f , d ; NOT (f) -> (d)

Bitii afectati din registrul STATUS

Exemplu :

movlw B’11001010’ ; incarca valoarea in W


movwf mavariable ; initializeaza mavariable
comf mavariable , w ; incarca inversul lui mavariable in W
; (W) = B’00110101’

Sugestie : folosind aceasta instructiune, se poate deasemenea testa si daca continutul


locatiei devine 0xFF. De fapt, in acest caz, W va deveni 0, iar Z va trece deci in 1.

9.34 Instructiunea « SLEEP » (Trecerea in mod stand-by)

Pune PIC®-ul in mod Standby. El nu va reveni decit in anumite conditii pe care le vom
vedea mai tirziu.

Sintaxa

sleep ; oprire PIC

71
Bitii afectati din registrul STATUS

T0, PD

9.35 Instructiunea « NOP » (No Operation)

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

nop ; nimic mai simplu, nu face nimic

Si cu asta incheiem analiza celor 35 de instructiuni folosite in mod obisnuit in PIC®-urile


mid-range.
S-ar putea sa vi se para dificil, dar dupa putina practica, veti invata repede pe dinafara toate
aceste instructiuni. Puteti sa va consolati cu ideea ca ati scapat usor, pentru ca unele
procesoare CISC au mai multe sute de instructiuni.

9.36 Instructiuni depasite

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.

Este vorba de instructiunea OPTION, care pune continutul registrului « W » in registrul


OPTION_REG, si instructiunea TRIS, care pune continutul lui « W » in registrul TRISA sau
TRISB, urmind ca sa folosim TRIS PORTA sau TRIS PORTB.

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.

Nu v-am prezentat aceste instructiuni decit pentru a va permite sa intelegeti un eventual


program scris de cineva care le-a folosit.

72
10. Modurile de adresare

Toate instructiunile folosesc un mod propriu de a ajunge la informatiile pe care le


manipuleaza. Aceste metode se numesc « moduri de adresare ».

Am sa va dau cite un mic exemplu concret privind fiecare mod de adresare.


Sa presupunem ca doriti sa puneti niste bani in buzunar….

10.1 Adresarea literala sau imediata

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 :

movlw 0x55 ; incarca valoarea 0x55 in W

10.2 Adresarea directa

Intr-o ADRESARE DIRECTA, puteti spune : am sa pun continutul sertarului nr. 10 in


buzunar. Aici, locul continind valoarea utila este dat DIRECT in fraza. Dar mai intii va trebui
sa deschidem sertarul pentru a afla ce vom pune efectiv in buzunar. Nu vom pune deci in
buzunar numarul sertarului, ci continutul lui. Pentru a face o analogie cu sintaxele precedente,
pot spune ca pun (sertarul 10) in buzunar.

Exemplu :

movf 0x10 , W ; incarca continutul locatiei 0x10 in W

10.3 Adresarea indirecta

Intr-o ADRESARE INDIRECTA, puteti spune :


Functionarul de la ghiseul nr. 3, imi va spune numarul sertarului, care contine suma pe care
o voi pune in buzunar.

Aici, dvs. obtineti numarul sertarului INDIRECT, prin intermediul functionarului de la


ghiseu.

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

Cei mai grabiti cauta deja in tabelul 4-2 registrul 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.

In exemplul precedent, functionarul de la ghiseu este registrul FSR. Adresarea indirecta


este putin diferita la PIC®-uri, caci adresa de destinatie se va gasi intotdeauna in aceeasi
locatie. S-ar putea spune ca in banca noastra nu exista decit un singur functionar (FSR). Oare
cum se va descurca ?

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 :

movlw 0x50 ; incarcam o valoare oarecare


movwf mavariable ; si o punem in variabila « mavariable »
movlw mavariable ; se incarca ADRESA lui mavariable,
; de exemplu, in lectiile precedente
; era 0x0E. (W) = 0x0E
movwf FSR ; se pune adresa de destinatie in FSR.
; vom spune ca FSR INDICA catre mavariable
movf INDF , w ; incarca CONTINUTUL lui INDF in W.

CONTINUTUL LUI INDF ESTE TRADUS DE PIC® CA FIIND CONTINUTUL


LOCATIEI DE MEMORIE INDICATA DE CATRE FSR (W) = 0x50

74
10.4 Citeva exemple

Va trebui sa ma repet, insa modurile de adresare trebuie bine intelese. Inmultind


exemplele, eu sper ca toata lumea va putea intelege. Pentru cei obisnuiti cu diverse
procesoare, va rog sa-mi scuzati repetarile. Registrele sint initializate cu valorile precedente.

movlw mavariable

Este adresarea IMEDIATA, sau LITERALA ; deci se incarca VALOAREA lui


mavariable, cea ce corespunde in realitate cu ADRESA sa. Deci 0x0E este incarcat in W.
Aceasta adresare se recunoaste dupa « l » din instructiunea movlw. Atentie, valoarea lui
mavariable nu este continutul sau.

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...

Va trebui deci sa recurgeti la diverse trucuri pentru a compensa aceste lacune.

E logic ca usurinta de invatare si de punere in practica se plateste printr-o flexibilitate mai


redusa in crearea aplicatiilor.

75
Note:…

76
11. Realizarea unui program incorporat

Sub denumirea de « software incorporat » se intelege, in general, un program destinat sa


ruleze local pe o placa electronica ce dispune de un microcontroler. Vom incepe deci cu
partea mai distractiva. Vom crea mici programele pentru a le rula pe o placuta echipata cu un
PIC®.

11.1 Materiale necesare

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) :

- 1 PIC® 16F84 sau 16F84A in capsula PDIP, de orice frecventa.


- 1 soclu cu 18 pini, cu distanta intre rinduri de 0.3’’
- 1 cuart de 4 MHz
- 2 condensatoare de 27 pF
- 1 LED rosu
- 1 rezistenta de 330 ohmi.
- 1 buton cu contact « normal deschis »
- fir de conexiune
- 1 alimentator stabilizat

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 :

- 1 trafo de retea cu tensiune de iesire de 9…15V, necritica


- 1 condensator intre 10 si 100µF/35V
- 1 condensator de 0.1 µF
- 1 dioda 1N4007, sau de alt tip care sa suporte un curent de 1A (necritic)
- 1 stabilizator integrat de tip 7805

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.

11.2 Realizarea placutei de test

- Montati PIC®-ul intr-un soclu


- Montati componentele pe placuta de test si faceti conexiunile conform
schemei de mai jos.
- Verificati totul inainte de a conecta alimentarea
- Daca n-aveti un alimentator stabilizat, folositi o baterie de 4.5V sau realizati
schema de mai jos.

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.

Faceti o copie a fisierului "m16f84.asm", si redenumiti aceasta copie LED_cli.asm. Lansati


MPLAB® si raspundeti cu « nu » atunci cind programul va intreaba daca doriti sa modificati
Essai1.

Schema de conexiuni a PIC®-ului (nu uitati sa conectati MCLR la Vdd)

Shema unui mic alimentator stabilizat

11.3 Realizarea proiectului

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. *
; *
;******************************************************************

11.5 Alegerea configuratiei

Mai jos in acest fisier veti gasi :

__CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _HS_OSC

; '__CONFIG' precizeaza parametrii scrisi in procesor in momentul


; programarii. Definitiile sint date in fisierul "include".
; Iata valorile si definitiile lor :

;_CP_ON Code protection ON : imposibil de citit


;_CP_OFF Code protection OFF
;_PWRTE_ON Timer reset on power-on activ
;_PWRTE_OFF Timer reset dezactivat
;_WDT_ON Watch-dog activ
;_WDT_OFF Watch-dog dezactivat
;_LP_OSC Oscilator cu cuart cu consum redus
;_XT_OSC Oscilator cu cuart de viteza medie, sau extern
;_HS_OSC Oscilator cu cuart de viteza mare
;_RC_OSC Oscilator cu retea RC

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 continuare, lasati PWRTE pe ON pentru a preciza ca doriti sa folositi un RESET


« securizat », deci cu un timp prelungit inainte de pornire. Aceasta va permite o pornire mai
lenta dupa conectarea alimentarii. Va voi explica acest lucru ceva mai tirziu.

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.

FOLOSIREA PARAMETRULUI « RC » PENTRU UN CEAS EXTERN POATE DUCE LA


DISTRUGEREA PIC®-ULUI.

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 :

__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

11.6 Registrul OPTION

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.

Cum noi nu folosim intreruperile in aceasta lectie, putem lasa b6 = 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.

Cum noi am pus b5 = 0, b4 nu va fi folosit. Il vom lasa deci in starea b4 = 0.

b3 : PSA

In PIC® exista un predivizor. Ce este acesta ? Acest numarator indica numarul de


impulsuri care vor trebui primite pentru a produce o incrementare a destinatiei. Vom reveni in
detaliu asupra functionarii sale cind vom vorbi de timer-ului tmr0.

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).

b2, b1, b0 : PS2, PS1, PS0

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.

Daca nu doriti sa folositi deloc predivizorul, singura metoda este de a pune b3 = 1


(predivizor pe watchdog) si bitii PS2 – PS0 pe 0. In acest caz, nu avem predivizor pe tmr0, iar

81
pentru watchdog avem predivizare 1:1, adica nici un fel de divizare. Noi vom pune deci b2 =
b1 = b0 = 0.

11.7 Editarea programului

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.

Eliminati asignatia referitoare la INTERMASK caci noi nu vom folosi intreruperile in


acest prim program. In zona asignarilor va trebui deci sa ramina :

;******************************************************************
; ASIGNARI *
;******************************************************************

OPTIONVAL EQU H'0008 ; Valoare registru optiuni


; Rezistente pull-up ON
; Fara prescaler

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

In prima coloana avem numele MACRO-ului (avem 2 MACRO-uri, LEDON si LEDOFF).


Directiva macro semnifica "inceput macro" iar endm "sfirsit macro". Retineti ca MACRO-
urile pot avea, evident, mai multe linii de cod.

Sa luam exemplul nostru : cind vom folosi linia urmatoare in programul nostru (atentie, nu
puneti MACRO-ul in prima coloana) :

LEDON

in momentul compilarii, asamblorul va inlocui LEDON prin :

bsf LED

Va inlocui deasemena LED prin PORTA , 2. In realitate se va obtine :

bsf PORTA , 2

Am obtinut deci o usurare a scrierii si intretinerii. Pastrati in minte ideea ca MACRO-urile


sint doar simple substitutii in tratarea textului. Daca MACRO-ul vostru contine 50 de linii de
cod, cele 50 de linii vor fi copiate in PIC® pentru fiecare apel al acestui MACRO.

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 :

11.8 Registrul PORTA

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 :

bsf PORTA , 1 ; trimite 1 logic la RA1

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 a testa o intrare, se poate proceda in modul urmator :

btfss PORTA , 3 ; testeaza RA3 si salt daca devine +5V

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.

Incepind de la pagina 75, capitolul 11, aveti caracteristicile maxime de functionare a


PIC®-ului. Puteti retine ca va trebui sa va limitati la maxim 20mA pe un pin de iesire, cu un
maxim total de 80mA pentru PORTA si 150mA pentru PORTB.

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.

11.8.1 Functionarea detaliata a PORTURILOR

PORTURILE functioneaza intotdeauna dupa principiul :


CITIRE => MODIFICARE => SCRIERE. De exemplu, daca veti scrie
bsf PORTA , 1

PIC®-ul va proceda in felul urmator :

1. Este citit PORTA in totalitate (chiar si bitii configurati ca iesire)


2. Bitul 1 este pus in 1
3. PORTA este rescris in totalitate (pentru pinii de iesire).

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 :

bsf PORTA , 1 ; pune RA1 in 1

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 se gaseste la aceeasi adresa ca si PORTA, insa in bancul 1. Adresa sa


completa pe 8 biti este deci 0x85.

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 :

bsf STATUS , RP0 ; se trece in bancul 1

bcf TRISA , 2 ; bit 2 din TRISA pe 0 = iesire pentru RA2


bcf STATUS , RP0 ; se revine in bancul 0
bsf PORTA , 2 ; se trimite +5V pe RA2 : LED-ul se aprinde
.
.
.
bcf PORTA , 2 ; 0V pe RA2, LED-ul se stinge

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…

11.10 Registrele PORTB et TRISB

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.

11.11 Exemplu de aplicare

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)).

bsf STATUS , RP0 ; se trece in bancul 1


bcf OPTION_REG , RBPU ; rezistente pull-up activate
bcf TRISA , 2 ; bit 2 al TRISA pe 0 = iesire pe RA2
; TRISB nu trebuie configurat caci el
; devine implicit intrare la resetul PIC
bcf STATUS , RP0 ; se revine in bancul 0
boucle ; eticheta inceput bucla principala
btfss PORTB , 2 ; testeaza RB2, salt daca = 1
bsf PORTA , 2 ; RB2 = 0, deci LED-ul se aprinde
btfsc PORTB , 2 ; testeaza RB2, salt daca = 0
bcf PORTA , 2 ; RB2 = 1, deci LED-ul se stinge
goto boucle ; reia bucla

11.12 Rutina de initializare

Sa examinam instructiunile de dupa eticheta « init ».

Primele 2 linii,

clrf PORTA ; iesiri port A pe 0


clrf PORTB ; iesiri port B pe 0

pun iesirile porturilor (A si B) pe 0. Ca urmare, atunci cind configurati porturile ca iesiri,


ele vor avea implicit valoarea 0V. Daca electronica dvs., pentru protectie, necesita alte valori,
va trebui sa le schimbati explicit.

Mai departe, gasim linia :

clrf EEADR ; permite reducerea consumului

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.

Linia urmatoare ne permite sa trecem in bancul 1. Pina la noi ordine, instructiunile


urmatoare folosesc registre situate in bancul 1.

bsf STATUS,RP0 ; se trece in bancul 1

Mai departe, gasim :

movlw OPTIONVAL ; se schimba masca


movwf OPTION_REG ; initializare registru optiuni

Aduceti-va aminte ca OPTIONVAL este o constanta. Valoarea sa a fost definita anterior,


prin grija noastra, ca fiind 0x08. Aceasta valoare va fi trimisa aici in registrul OPTION
(OPTION REG). Amintiti-va ca OPTION_REG este numele declarat in MPLAB® pentru
registrul OPTION.

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

Iata deci un exemplu concret de folosire a adresarii indirecte.

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

Se face apoi incrementarea lui FSR, care acum indica 0x0D.

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.

In continuare, stergeti liniile urmatoare :

bcf TRISA , 0 ; bitul PORTA.0 ca iesire (exemplu)


bcf STATUS , RP0 ; selectati bancul 0
movlw INTERMASK ; masca de intreruperi
movwf INTCON ; incarcati controlul intreruperii

caci ele nu ne intereseaza deocamdata.

Sa vedem acum ce trebuie sa mai adaugam.

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

Unii vor spune « se stinge LED-ul ! ». Ei bine, nicidecum. Aceasta instructiune va fi


inlocuita de catre asamblor prin :

bcf PORTA , 2

Adica, altfel spus :

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
; ----------------------

bcf LED ; LED ca iesire (banc 1)

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 :

bcf STATUS , RP0 ; se revine in bancul 0

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.

Iata la ce trebuia sa serveasca rutina de initializare, pe care ar fi trebuit sa o intelegeti in


totalitate (n-aveti nevoie de o aspirina ?).

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

Coboriti acum in programul principal si stergeti linia :

clrwdt ; sterge watchdog

90
11.13 Rezultatele compilarii

Lansati compilarea cu tasta <F10>.

In fereastra cu rezultatele compilarii, va trebui sa gasiti o linie de forma :

Message[302] D:\DOCUME~1\LESSONS\DATAPIC\LED_CLI.ASM 101 : Register in


operand not in bank 0. Ensure that bank bits are correct.

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 :

movwf OPTION_REG ; initializare registru optiuni

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.

11.14 Programul principal

Dorim sa facem LED-ul nostru sa clipeasca cu o frecventa de 1Hz (1 clipire / secunda). Va


trebui deci sa creem un program de forma :

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

Alegeti metoda pe care o preferati. Important este sa intelegeti ambele metode.

11.15 Subrutina de temporizare

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.

Fiecare ciclu de instructiune dureaza deci 1 milionime de secunda, sau o microsecunda


(µs). Iata deci unitatea de lucru adecvata PIC®-ului.

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.

Sa o facem. Sa incepem deci prin a ne declara variabila (cmpt1) in zona de RAM. Sa


adaugam deci aceasta declaratie. Vom obtine :

;******************************************************************
; 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

Sa realizam acum bucla.

tempo
clrf cmpt1 ; sterge contor1
bucla1
decfsz cmpt1 ; decrementeaza contor1
goto bucla1 ; daca nu e 0, bucleaza
return ; intoarcere din subrutina

Sa lansam compilarea (F10) :

Vom obtine in fereastra de rezultate o linie suplimentara de forma :

Message[305] D:\DOCUME~1\LESSONS\DATAPIC\LED_CLI.ASM 134 : Using default


destination of 1 (file).

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 :

decfsz cmpt1 ; décrémenter compteur1

De fapt, instructiunea decfsz este de forma « decfsz f , d ». Am uitat deci sa indicam


destinatia operatiei. MPLAB® va atentioneaza ca a folosit pentru voi ‘,f '
Fiti deci atenti, caci daca ati fi vrut sa obtineti rezultatul in W, programul era fals.
Modificati-va deci comanda adaugind ‘,f ".

decfsz cmpt1 , f ; decrementeaza contor1

Acum, sa calculam durata acestei temporizari :

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

Durata totala va fi deci :

Ø 2 cicluri pentru apelarea subrutinei (call tempo)


Ø 1 ciclu pentru resetarea variabilei
Ø 257 cicluri pentru cele 256 de decrementari

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 :

cmpt1 : 1 ; contor de bucle 1


cmpt2 : 1 ; contor de bucle 2

Sa scriem deci cele doua bucle imbricate :

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.

Ce temporizare se obtine ? Sa calculam aproximativ :

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

cmpt1 : 1 ; contor de bucle 1


cmpt2 : 1 ; contor de bucle 2
cmpt3 : 1 ; contor de bucle 3

ENDC ; sfirsit zona

Iata forma finala a codului :

;******************************************************************
; 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.

Puneti programul in PIC® cu ajutorul programatorului.

Puneti PIC®-ul pe placa de test, nealimentata. Conectati alimentarea si priviti :

LED-UL VOSTRU CLIPESTE CU FRECVENTA DE 1HZ.

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.

12.1 Ce este o intrerupere ?

Sa ne imaginam o conversatie normala :

Ø Fiecare interlocutor ia cuvintul atunci cind ii vine rindul sa vorbeasca.


Ø Intervine atunci un eveniment exterior a carui tratare este urgenta. De exemplu, de la
etajul III al cladirii, cade un pian la picioarele celui cu care vorbiti.
Ø Va imaginati ca interlocutorul vostru nu va astepta sfirsitul frazei dvs. pentru a va
semnala pericolul. Deci el va va INTRERUPE cursul normal al conversatiei, in scopul
de a putea TRATA IMEDIAT EVENIMENTUL exterior.

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 :

Ø Programul se deruleaza normal


Ø Survine un eveniment specific
Ø Programul principal este intrerupt (apare deci o INTRERUPERE), si va trata
evenimentul, inainte de a relua programul principal din locul unde a fost intrerupt.

Intreruperea este deci o RUPTURA DE SECVENTA ASINCRONA, adica nesincronizata


cu derularea normala a programului.

Remarcati opozitia fata de rupturile de secventa sincrone, provocate de programul insusi


(goto, call, btfss…)

12.2 Mecanismul general al unei intreruperi

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 :

Ø Programul ruleaza normal


Ø Apare evenimentul
Ø Programul incheie instructiunea in curs de executie
Ø Programul sare la adresa de tratare a intreruperii
Ø Programul trateaza intreruperea
Ø Programul sare la instructiunea care urmeaza ultimei instructiuni executate din
programul principal

Va trebui sa retineti ca nu orice eveniment poate declansa o intrerupere. Pentru aceasta,


trebuie indeplinite 2 conditii principale :

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.

Organigrama generala de executie a unei intreruperi.

Ce putem sa spunem in legatura cu aceasta organigrama ? Ei bine, putem deja sa spunem


ca programul principal "nu stie" cind va fi intrerupt, deci este crucial sa-i refacem registrele
exact la fel cum erau inainte de aparitia intreruperii.
Sa presupunem ca instructiunea XXX pozitionase un flag (de exemplu, bitul indicatorului
Z). Daca din nefericire, rutina de intrerupere a modificat acest bit, programul nu va mai putea
sa ruleze corect.
Vedem deasemenea ca instructiunea XXX isi incheie executia inainte ca programul sa se
ramifice catre rutina de intrerupere. O instructiune odata inceputa, nu va fi niciodata
intrerupta.

12.3 Mecanismul de intreruperi al PIC®-urilor

Bineinteles, PIC®-urile respecta in general principiile de mai sus, insa au particularitatile


lor. Sa vedem acum care este mecanismul de intreruperi al PIC®-urilor :

Ø 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 de reactie la tratarea unei intreruperi se calculeaza astfel :

Ø Ciclul curent al instructiunii este terminat


Ø Flagul de intrerupere este citit la inceputul ciclului urmator
Ø Acest ciclu este incheiat, dupa care procesorul se opreste un ciclu pentru a incarca
adresa 0x04 in PC
Ø Procesorul se conecteaza la adresa 0x04 unde ii va trebui un ciclu suplimentar pentru a
incarca instructiunea de executat

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 :

Ø O intrerupere nu poate fi intrerupta de o alta intrerupere. Deci intreruperile sint


invalidate automat atunci cind se face un salt la adresa 0x04 prin stergerea bitului GIE
(pe care-l vom descrie in continuare).
Ø Intreruperile sint re-validate automat odata cu intoarcerea din intrerupere. Instructiunea
RETFIE actioneaza exact la fel ca si RETURN, dar ea repozitioneaza in acelasi timp
bitul GIE.

12.4 Sursele de intreruperi ale lui 16F84

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.

12.5 Dispozitivele puse in practica

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.

Sa ne imaginam un hotel. Baiatul de la room-service reprezinta programul nostru.


Hotelul are 4 camere, si in fiecare camera exista un buton. Fiecare buton care apeleaza
room-service-ul este legat la un bec, aflat la receptia hotelului.
Fiecare lampa, are posibilitatea sa actioneze o sonerie daca intrerupatorul general este
inchis, si intrerupatorul propriu este inchis. Iata deci schema obtinuta (atentie, este o schema
simbolica, nu electrica, liniile reprezentind circulatia informatiei, nu curentul electric) :

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.

Notati deci urmatoarele aspecte :

Ø 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.

12.6 Registrul INTCON (INTerrupt CONtrol)

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 :

b7 : GIE Bitul Global Interrupt Enable. El permite validarea sau


invalidarea tuturor intreruperilor odata. Acest bit corespunde
deci intrerupatorului nostru de validare generala

b6 : EEIE Bitul Eeprom write complete Interrupt Enable. Acest bit


permite validarea intreruperii de sfirsit de scriere in EEPROM
(vom studia mai tirziu mecanismul de scriere in EEPROM).

b5 : T0IE Bitul Tmr0 Interrupt Enable. Valideaza intreruperea generata


de depasirea timerului tmr0.

b4 : INTE Bitul INTerrupt pin Enable. Valideaza intreruperea in cazul


modificarii nivelului de pe pinul RB0.
ATENTIE : Amintiti-va ca bitul 6 al registrului OPTION,
care determina care este sensul tranzitiei care provoaca
intreruperea. Se poate deci alege tranzitia 0=>1, sau 1=>0, dar
nu ambele simultan.

b3 : RBIE Bitul RB port change Interrupt Enable. Valideaza


intreruperile la modificarile de nivel pe intrarile RB4 - RB7.

b2 : T0IF Bitul Tmr0 Interrupt Flag. Este un FLAG, deci el


semnalizeaza. In acest caz, depasirea timerului tmr0.

b1 : INTF Bitul INTerrupt pin Flag. Semnalizeaza o tranzitie pe pinul


RB0 in sensul determinat de INTEDG din registrul OPTION
(b6).

b0 : RBIF Bitul Port Interrupt Flag. Semnalizeaza ca una din intrarile


RB4 – RB7 a fost modificata.

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.

Dupa aceste explicatii detaliate, ar trebui sa fi inteles functionarea intreruperilor in 16F84.


Sa analizam acum citeva puncte ale rutinei de intrerupere insasi. Pentru a ramine intr-un cadru
concret, vom combina putin teoria cu practica.

12.7 Salvarea si restaurarea starii

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 :

Sa presupunem ca programul principal a fost intrerupt intre cele 2 instructiuni urmatoare :

movf mavariable , w ; incarca mavariable si seteaza Z


btfss STATUS , Z ; testeza bitul Z si salt daca devine 1

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.

12.7.1 Despre salvarea registrelor

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.

Daca si in rutina de intrerupere si in programul principal folosim adresarea indirecta, va


trebui sa restauram si registrul FSR.

12.7.2 Metoda de salvare

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.

Salvarea se rezuma deci la :

movwf w_temp ; salveaza W intr0o locatie de salvare


movf STATUS,w ; transfera STATUS in W
movwf status_temp ; salveaza STATUS

Retineti ca datasheet-urile si alte documente de referinta, ca de exemplu manualul de


referinta pentru mid-range, indica metoda urmatoare :

movwf w_temp ; salveaza W intr0o locatie de salvare


swapf STATUS,w ; transfera STATUS in W
movwf status_temp ; salveaza STATUS

Constatati ca in loc de un movf, Microchip recomanda folosirea unui swapf, care nu


modifica nici un bit din STATUS in cursul manevrei. Evident, octetul incarcat este
« basculat » si deci va trebui sa folosim o « basculare » si la restaurare.
Dupa ce am intrebat despre aceasta problema, n-am putut obtine de la Microchip nici o

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.

12.7.3 Metoda de restaurare

Mi-ati putea spune : despre ce sa vorbim aici, e suficient sa procedam invers ca la salvare
si gata :

movf status_temp,w ; incarca STATUS-ul salvat


movwf STATUS ; restaureaza STATUS
movf w_temp,w ; restaureaza W

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 :

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

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

• Sau metoda sustinuta la inceput si folosita in curs :

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.

Iata deci structura de baza a unei rutine de intrerupere :

;******************************************************************
; 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

; comutare catre diferite intreruperi


; ------------------------------------
; aici, se testeaza (eventual) de care intrerupere este vorba

; tratare intreruperi
; --------------------

; aici, se poate trata intreruperea, dupa care i se sterge flag-ul

; 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).

Cum eu sint un partizan al programarii structurate, rutina de comutare se ramifica de fapt


in sub-programe separate. Nimic nu ne impiedica de fapt sa folosim sub-programe intr-o
rutina de intrerupere. Atentie totusi la limitele stivei de lucru disponibile !

12.7.4 Operatii asupra registrului STATUS

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.

Pentru a fi mai explicit, iata citeva exemple :

movwf STATUS ; nicio problema, movwf nu modifica niciun flag


movf STATUS,w ; nicio problema, STATUS nu este destinatia
andwf STATUS ; bitul Z va fi modificat in functie de rezultat
addwf STATUS ; idem pentru C, DC, si Z.
clrf STATUS ; modifica Z, si deci nu se obtine rezultatul dorit

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).

12.7.5 Particularitati ale instructiunii « RETFIE »

La acest nivel al expunerii noastre, o buna intrebare ar fi urmatoarea : de ce exista o


instructiune RETFIE, atunci cind am putea folosi RETURN ?
Dupa cum stiti, o intrerupere nu poate fi ea insasi intrerupta de catre o alta. Intr-un astfel de
caz, registrele W si STATUS ar fi « distruse » de o a doua operatie (sau chiar de catre alte
procesoare). Deci, atunci cind programul este ramifucat de catre o intrerupere, bitul GIE este
pus automat pe 0.

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.

Ma puteti intreba : dar daca as proceda asa ?

bsf INTCON , GIE ; repune GIE in 1


return ; si revine din rutina de intrerupere

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.

12.8 Utilizarea unei rutine de intrerupere

Faceti o copie a fisierului m16f84.asm. Redenumiti-l « myinter.asm ». Creati un nou


proiect MPLAB® cu numele « myinter » urmind procedura cunoscuta.

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).

Aceasta va arata totusi ca electronica si programarea microcontrolerelor sint foarte strins


legate. Cind construiti un circuit, va trebui sa va ginditi deja la modul in care veti realiza
programul. Invers, daca dispuneti de un circuit existent, va sint impuse deja constringeri la
nivelul programarii. De exemplu, este imposibil sa tratam butonul nostru prin intreruperi daca
el ar fi legat la intrarea RB2.

Rezistenta este construita intern in PIC®, fiind vorba de o rezistenta pull-up catre +5V,
care poate fi activata prin soft.

Ca de obicei, completati antetul si eliminati liniile inutile in VARIABILE, MACRO si


DEFINITII. Atentie, nu eliminati variabilele w_temp si status_temp.

#include <p16F84.inc> ; Definiri constante

Iata antetul pe care-l puteti completa :

;******************************************************************
; 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.

Modificati configuratia, pentru a suprima functionarea watch-dog-ului :

__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

Sa calculam valoarea pe care o vom pune in registrul OPTION.

Ø b7 pe 0, caci avem nevoie de rezistenta pull-up catre +5V, pentru buton


Ø b6 pe 0, caci dorim o intrerupere cind apasam butonul, deci cind nivelul trece din 1 in
0 (frontul descendent)
Ø b5 / b0 pe 0, caci nu au importanta in aceasta aplicatie

Deci, sa facem asignarea pentru B’00000000’ :

;******************************************************************
; ASIGNARI *
;******************************************************************

OPTIONVAL EQU H'0000' ; Valoare registru optiuni


; Rezistenta pull-up ON
; Intrerupere pe front descendent RB0

Pentru registrul INTCON, va trebui sa avem :

Ø b7 pe 1 : pentru a valida intreruperile


Ø b6 pe 0 : fara intreruperi EE
Ø b5 pe 0 : fara intreruperi tmr0
Ø b4 pe 1 : intreruperea RB0 activa
Ø b3 pe 0 : fara intreruperi RB
Ø b2 / b0 pe 0 : sterge flag-urile

Acestea ne dau : B’10010000’, adica 0x90.

Sa asignam o constanta pentru aceasta valoare :

INTERMASK EQU H'90' ; Masca de intrerupere


; Intreruperi pe RB0

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

Am ajuns la zona variabilelor. Va trebui sa pastram w_temp si status_temp, caci aceste


variabile sint folosite de rutina de intreruperi pentru a salva registrele W si STATUS.

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

w_temp : 1 ; salvare W in intrerupere


status_temp : 1 ; salvare STATUS in intrerupere
cmpt1 : 1 ; contor de bucle 1 in tempo
cmpt2 : 1 ; contor de bucle 2 in tempo
cmpt3 : 1 ; contor de bucle 3 in tempo

ENDC ; sfirsitul zonei

12.9 Analiza rutinei de intrerupere

Am vazut deja prima parte, care consta in salvarea registrelor utilizate.

;******************************************************************
; 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

In continuare, va trebui sa determinam care este originea intreruperii in curs. In cazul de


fata, este vorba obligatoriu de intreruperea RB0/INT, caci noi nu am activat-o decit pe
aceasta. Am putea deci sa omitem aici acest test, insa scopul este de a va explica metodele pe
care le veti folosi. Vom pastra deci in totalitate explicatiile pentru ca voi sa puteti folosi
oricare dintre aceste combinatii. Sa examinam deci aceasta parte :

; comutare catre diferite intreruperi


; se inverseaza ordinea pentru a modifica prioritatile
; -----------------------------------------------------

btfsc INTCON,T0IE ; testeaza daca intreruperea generata


; de timer este validata
btfss INTCON,T0IF ; da, testeaza daca intreruperea
; generata de timer este in curs
goto intsw1 ; nu, testul urmator
call inttimer ; da, trateaza intreruperea de timer
goto restorereg ; si termina intreruperea
; ELIMINATI ACEASTA ULTIMA LINIE PENTRU
; A TRATA MAI MULTE INTRERUPERI DEODATA

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.

Ultima linie permite sa se sara la sfirsitul rutinei de intrerupere, adica la restaurarea


registrelor si iesire. Deci, daca am fi avut 2 surse de intrerupere simultane, doar una ar fi fost
tratata. Daca, din contra, se suprima aceasta linie, intreruperea urmatoare va fi tratata imediat
ce se incheie intreruperea in curs. Am studiat aceasta osatura pentru a va lasa toate variantele
posibile, si direct utilizabile.

Observatii :

Ø Daca nu se foloseste niciodata intreruperea tmr0, se poate suprima aceasta portiune de


cod.
Ø Daca intreruperea tmr0 este valida pe tot parcursul programului, se poate renunta la
testarea lui T0IE (caci el va fi permanent in 1).
Ø In cazul a 2 intreruperi simultane generate de 2 evenimente distincte, prima intrerupere
tratata va fi cea testata prima. Ordinea testelor va modifica deci prioritatea
intreruperilor.

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

; aici, locul pentru intreruperea EEPROM

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

12.10 Adaptarea rutinei de 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
; --------------------

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

Am putea deasemenea sa inlaturam linia call intrb0, si sa punem in locul ei direct


procedura de tratare. Vom pastra totusi acest apel de subrutina. In cazul in care veti dori sa
optimizati o aplicatie la extrem, veti putea sa o ginditi separat. In caz contrar, va sfatuiesc sa
acordati prioritate lizibilitatii programului (insist din nou asupra acestui lucru).
Sa vedem programul mai departe. Vom gasi 3 rutine de tratare a intreruperilor apelate la
origine de catre rutina noastra de intreruperi. Cum noi am eliminat 2 din aceste apeluri, nu
mai trebuie sa pastram subrutinele corespunzatoare.
Singura de care vom avea nevoie va fi deci :

;******************************************************************
; 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

Goto restorereg ; si sfirsit de intrerupere

corespondenta ar putea fi precedata de testul lui W returnat de subrutina. Acesta este un


exemplu ceva mai dificil de tratat aici, dar e bine sa-l aveti in vedere atunci cind veti jongla cu
PIC®-urile.

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)

BANK0 ; se trece in bancul 0


movlw INTERMASK ; masca de intrerupere
movwf INTCON ; incarca controlul intreruperii
goto start ; salt in programul principal

Nu ne mai ramine decit sa suprimam linia :

clrwdt ; sterge watch dog

din programul principal, din moment ce am dezactivat watchdog-ul.

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.

N-am sa revin aici asupra mesajelor de atentionare.

12.12 Constructia programului principal

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.

Din moment ce dorim sa folosim intreruperile, inversarea aprinderii LED-ului se va face in


rutina de intrerupere a butonului. La prima vedere, am putea gindi ca programul principal nu
mai are nimic de facut.

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).

Programul nostru principal ar putea fi deci de forma :

start

goto start ; bucleaza

Insa, pentru a usura vizualizarea a ceea ce se intimpla sub emulator, va recomand sa


adaugati citeva NOP-uri inutile. Iata deci programul nostru principal :

;******************************************************************
; 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.

Schema logica 1 (versiune teoretic functionala)

Observati ca aceasta rutina de intrerupere este cit se poate de simpla, si corespunde pe


deplin la ceea ce ne-am propus. Vom trece deci la rularea pe simulator.

12.14 Rularea pe simulator a unei rutine de intrerupere

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.

Sa lansam compilarea cu <F10>. In continuare, apasati <F6> pentru a reseta programul,

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…

Sa examinam registrul PORTB in fereastra de afisare a registrelor speciale. Veti vedea ca


toti bitii sint in 0. Aceasta inseamna ca MPLAB® nu stie ce circuite electronice ati conectat
dvs. pe acesti pini. Cade deci in sarcina dvs. de a-i indica ce nivel ar trebui sa vada pe pinii
respectivi. Pentru cei care nu au inteles functionarea rezistentei pull-up, iata inca odata
schema echivalenta :

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.

Vom simula acum apasarea butonului.

Ø Apasati primul buton pentru a aplica 0 pe RB0


Ø Reveniti in editor si apasati o singura data <F7>. Instructiunea care urmeaza
evenimentului este instructiunea situata la adresa 0x04, caci trecerea din 1 in 0 pe pinul
RB0 provoaca o intrerupere.
Ø Avansati incet in rutina de intrerupere prin apasari repetate pe <F7>. Examinati efectul
diferitelor instructiuni executate.

Odata executata linia

xorwf PORTA , f ; inverseaza RA2

veti constata ca LED-ul s-a aprins (RA2 a trecut in 1 in registrul PORTA). Avansati incet pina
la linia :

retfie ; intoarcere din intrerupere

si nu mai apasati <F7>. In acest loc, gasim intoarcerea din rutina de intrerupere in programul
principal. Apasati inca odata <F7>.

Ce se intimpla ? In loc sa revenim in programul principal, reincepem o noua intrerupere !

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 ».

12.15 Prima corectie : resetarea flag-ului

Va trebui deci sa adaugam linia urmatoare in subrutina intrb0 :

bcf INTCON , INTF ; stergere flag INT/RB0

Vom obtine deci :

movlw B'00000100' ; bitul care se va inversa


BANK0 ; caci nu se stie in ce banc sintem

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.

Verificati daca rutina de intrerupere se termina, predind controlul catre programul


principal.

Acum LED-ul este aprins (RA2 = 1).

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.

Am obtinut deci pe simulator functionarea urmatoare :

Ø O apasare pe buton aprinde LED-ul


Ø O alta apasare stinge LED-ul
Ø S.A.M.D.

Am obtinut deci rezultatul dorit.

Puneti PIC®-ul in programator si scrieti-l cu fisierul « Myinter.hex ». Puneti PIC®-ul pe


placuta de test (modificata) si apasati butonul de mai multe ori. LED-ul nu functioneaza deloc
asa cum am prevazut ! El reactioneaza, dar complet aleatoriu. Ce se intimpla ?

12.16 Sa ne raportam la scara de timp a PIC®-ului

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.

PIC®-ul este deci mult mai rapid decit butonul nostru.

Sa nu uitati niciodata ca PIC®-urile nu lucreaza la scara de timp umana. Va trebui sa tineti


cont de asta.

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).

Rutina de intrerupere ia sfirsit. Ne reintoarcem in programul principal care continua sa


testeze flag-ul « tempo ».. Acesta a fost pus in 1 de catre rutina de intrerupere. Atunci,
programul principal apeleaza o rutina de temporizare (pe care am vazut-o deja in lectiile
anterioare).

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

Mai intii, ne trebuie o rutina de temporizare. Deschideti fisierul « Led_cli.asm si copiati


rutina de temporizare.

;******************************************************************
; 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 *
;****************************************************************

CBLOCK 0x00C ; inceput zona variabile


w_temp : 1 ; salvare W in intrerupere
status_temp : 1 ; salvare STATUS in intrerupere
cmpt1 : 1 ; contor de bucle 1 in tempo
cmpt2 : 1 ; contor de bucle 2 in tempo
flags : 1 ; un octet pentru 8 flag-uri
; rezervam b0 pentru flag-ul tempo
; b1 : liber
; b2 : liber
; b3 : liber
; b4 : liber
; b5 : liber
; b6 : liber
; b7 : liber
ENDC ; sfirsit zona

#DEFINE tempoF flags,0 ; definire flag tempo

Sa modificam programul nostru principal urmarind schema logica. Vom obtine :

;******************************************************************
; 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

Nu ne mai ramine decit sa modificam rutina noastra de intrerupere conform schemei


logice. Vom obtine :

;******************************************************************
; 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.

bcf INTCON , INTF ; sterge flag-ul INT

Bineinteles, va puteti spune ca e inutil sa stergem flag-ul in rutina de intrerupere. Este


logic, insa e mai bine sa va obisnuiti cu niste deprinderi sanatoase. Vom lasa deci asa aceasta
instructiune.

Recompilati-va programul si incarcati-l in PIC®. Cuplati alimentarea.

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.

Ca bonus, iata schema unui intrerupator basculant (tele-intrerupator) functional, care


foloseste programul vostru :

Observatii asupra programului nostru :

Noi am folosit o temporizare de o valoare oarecare. Aceasta temporizare inhiba toate


evenimentele generate de buton (acesta era si scopul) pe o durata de ± 250ms. Deci, in mare,
ati putea apasa butonul, de maximum 4 ori pe secunda.
Dar, care este durata reala a vibratiilor ? Ei bine, ea depinde de butonul folosit, de
124
tehnologie si de marime. Pentru a cunoaste exact durata vibratiilor, reduceti progresiv durata
temporizarii. Imediat ce intrerupatorul vostru nu mai lucreaza corect, inseamna ca ati atins
limita. Calculind durata temporizarii, veti obtine durata aproximativa a vibratiilor. Ordinul de
marime este cel mai adesea de citeva milisecunde.

12.19 Observatii importante

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 consecinta, NU VETI PUTEA SA FOLOSITI calculul de timp calculind numarul de


instructiuni dintr-o portiune de cod, in care poate exista riscul sa apara o intrerupere.

De exemplu, subrutina noastra de temporizare din « Led-cli », ne va da o durata care risca


sa se prelungeasca. In acest program, totusi, intreruperile nu sint active in momentul executiei
acestei temporizari. Urmariti schema logica.

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.

Odata ce folositi intreruperile, programul vostru va trebui sa infrunte evenimente


asincrone in derularea sa. Deci, dvs. nu veti putea NICIODATA sa testati TOATE
eventualitatile posibile.

CU ALTE CUVINTE, NU VETI PUTEA FI NICIODATA SIGURI CA NUMAI


PRIN SIMULARE, PROGRAMUL VOSTRU ESTE COMPLET TESTAT.

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

Odata cu incheierea acestui capitol, am invatat mecanismele intreruperilor si punerea lor in


practica. Vom mai relua unele din aceste notiuni in lectia despre timer.

Sper ca am demistificat acest concept, prea adesea imaginat ca o “metoda a profesionistilor”.


In realitate, odata in plus, nu e nici un fel de magie. Nu e decit o exploatare, uneori mai complexa, a
unor procese simple si usor de invatat.

Retineti numai ca, parasiti lumea sincrona pentru a intra in lumea asincrona, cu mult mai dificil de
testat complet intr-un mod eficace.

Folosirea intreruperilor impune stapinirea perfecta a mecanismelor folosite, si va obliga sa luati in


calcul toate posibilitatile ce pot surveni la supravegherea unui eveniment exterior. Si acest lucru este
cu atit mai adevarat pentru programele cu mai multe surese de intreruperi.

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.

13.1 Diferitele moduri de functionare

Am vazut ca timer-ul 0 este de fapt un numarator. Dar ce sa numarati cu el ? Ei bine, aveti


doua posibilitati :

Ø In prima faza, puteti numara impulsurile primite pe pinul RA4/T0CKI. Spunem in


acest caz ca sintem in mod numarator.
Ø Puteti deasemenea sa numarati impulsurile de ceas din PIC®-ul insasi. In acest caz,
ceasul avind durata fixa, noi numaram in realitate, timp. Deci, noi vom fi in mod
timer.

Alegerea unuia sau altuia dintre aceste doua moduri se face prin bitul 5 din registrul
OPTION : T0CS (Tmr0 Clock Source select bit).

T0CS = 1 : Functionare in mod numarator


T0CS = 0 : Functionare in mod timer

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).

T0SE = 0 : numara daca intrarea RA4/T0CKI trece din 0 in 1


T0SE = 1 : numara daca intrarea RA4/T0CKI trece din 1 in 0

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.

13.3 Metode de utilizare a timer-ului 0

Cum folosim timer-ul 0, si care sint posibilitatile oferite la acest nivel, vor fi subiectele
despre care vom vorbi aici.

13.3.1 Modul de citire simpla

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 :

Clrf tmr0 ; inceput numarare dupa 2 cicluri


; (vedeti observatia din chenarul de mai jos)
xxx ; aici un numar oarecare de instructiuni
movf tmr0 , w ; incarca valoarea de numarat
movwf mavariable ; salveaza pentru prelucrari ulterioare

13.3.2 Modul de scanare a flag-ului

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 :

clrf tmr0 ; inceput numarare


bcf INTCON , T0IF ; stergere flag
loop
btfss INTCON , T0IF ; testeaza daca numaratorul a depasit
goto loop ; nu, asteapta depasirea
xxx ; continuare : 256 evenimente numarate

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 :

movlw 256-100 ; incarca (256 – 100)


movwf tmr0 ; initializare tmr0
bcf INTCON,T0IF ; stergere flag
loop
btfss INTCON,T0IF ; testeaza daca numaratorul a depasit
goto loop ; nu, asteapta depasirea
xxx ; da, continua : 100 evenimente numarate

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

13.3.3 Modul intrerupere

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.

13.3.4 Metodele combinate

Sa presupunem ca ati dori, de exemplu, sa masurati timpul dintre 2 impulsuri aplicate pe


pinul RB0. Deasemenea, sa presupunem ca in acest timp ar avea loc mai multe depasiri ale lui
tmr0. O metoda simpla de masurare a timpului, ar fi urmatoarea :

1. La primul impuls pe RB0, pornim tmr0 in mod intrerupere.


2. La fiecare intrerupere generata de tmr0, se incrementeaza o variabila
3. La a doua intrerupere pe RB0, se citeste tmr0 si se opresc intreruperile
4. Timpul total va fi deci = (256 x variabila) + tmr0

Am folosit deci intreruperile pentru multiplii de 256, si citirea directa a lui tmr0 pentru
« unitati ».

13.4 Predivizorul

Sa presupunem ca folosim un cuart de 4 MHz. Avem deci in acest caz (4.000.000/4) =


1.000.000 cicluri pe secunda. Fiecare ciclu de ceas dureaza deci 1/1.000.000 dintr-o secunda,
adica 1 µs.

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 :

Timpi Timpi tipici Watchdog


PSA PS2 PS1 PS0 /tmr0 /WD
tmr0 (minim)
0 0 0 0 2 1 512 µs 18 ms (7ms)
0 0 0 1 4 1 1024 µs 18 ms (7ms)
0 0 1 0 8 1 2048 µs 18 ms (7ms)
0 0 1 1 16 1 4096 µs 18 ms (7ms)
0 1 0 0 32 1 8192 µs 18 ms (7ms)
0 1 0 1 64 1 16384 µs 18 ms (7ms)
0 1 1 0 128 1 32768 µs 18 ms (7ms)
0 1 1 1 256 1 65536 µs 18 ms (7ms)
1 0 0 0 1 1 256 µs 18 ms (7ms)
1 0 0 1 1 2 256 µs 36 ms (14 ms)
1 0 1 0 1 4 256 µs 72 ms (28 ms)
1 0 1 1 1 8 256 µs 144 ms (56 ms)
1 1 0 0 1 16 256 µs 288 ms (112 ms)
1 1 0 1 1 32 256 µs 576 ms (224 ms)
1 1 1 0 1 64 256 µs 1,152 Sec (448 ms)
1 1 1 1 1 128 256 µs 2,304 Sec (996 ms)

- PSA la PS0 sint bitii de configurare ai predivizorului


- /tmr0 indica valoarea predivizorului rezultanta pentru timer0
- /WD indica valoarea predivizorului rezultanta pentru Watchdog
- Timpi tmr0 indica timpii max intre 2 intreruperi tmr0 pentru un cuart de 4 MHz
- Timpi Watchdog indica timpii tipici disponibili intre 2 reset-uri watchdog (independent
de cuartul folosit). Valoarea dintre paranteze indica timpul minim, care va putea fi luat in
consideratie pentru toate circumstantele.

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 un Copy/Paste a noului vostru fisier m16f84.asm si redenumiti aceasta copie


« Led_tmr.asm ». Relansati MPLAB® si creati un nou proiect pe care-l veti numi «
Led_tmr.pjt ». Adaugati-i noul vostru fisier « Led_tmr.asm ».

Creati-va antetul (continui sa insist) :

;******************************************************************
; 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 *
;******************************************************************

Definiti in continuare valorile _CONFIG, dezactivind watchdog-ul.

Sa calculam in continuare numarul necesar de depasiri ale tmr0. Avem nevoie de o


temporizare de 500 ms, adica 500.000 µs.

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.

Ne-am hotarit sa folosim o predivizare de 256 cu 7 treceri prin rutina de intrerupere.


Timpul obtinut va fi deci in realitate de (256 x 256 x 7) = 458.752 µs, in loc de cele
500.000µs teoretice ale noastre.

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 :

OPTIONVAL EQU H'0087' ; Valore registru optiuni


; Rezistente pull-up OFF
; Prescaler timer cu 256

In continuare va trebui sa determinam valoarea pe care o vom pune in registrul INTCON


pentru a obtine intreruperile pe timer-ul 0. Aceasta va fi B’10100000’, adica 0xA0.

INTERMASK EQU H'A0' ; Intreruperi pe tmr0

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.

cmpt : 1 ; numarator de treceri

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

13.5.3 Rutina de intrerupere

Sa realizam acum rutina noastra de intreruperi.

Mai intii, se decrementeaza numaratorul de treceri, daca nu e nul, nu se executa nimic de


aceasta data.

decfsz cmpt,f ; decrementeaza numaratorul de treceri


return ; daca nu e 0, nu face nimic

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

Nu ne mai ramine decit sa stergem linia

clrwdt ; sterge watchdog

132
din programul principal, deoarece watchdog-ul este dezactivat.

Compilati programul. Il vom rula acum pe simulator. Nu uitati sa lansati simulatorul si


deschideti fereastra registrelor speciale. Avansati pas cu pas in program pina cind acesta
ajunge in programul principal.

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.

13.6 Modificarea registrelor in simulator

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.

Acum, la fiecare apasare pe <F7> se va incrementa tmr0 (fara predivizor).

In continuare, deschideti o fereastra pentru vizualizarea variabilelor, cu View => Watch.


Afisati variabila « cmpt » asa cum v-am explicat in capitolele precedente.

Continuati sa apasati <F7> si constatati ca depasirea lui tmr0 provoaca o intrerupere, si ca


aceasta intrerupere provoaca decrementarea lui cmpt. Pentru a nu astepta prea mult, folositi-
va de fereastra « modify » pentru a pune cmpt in 1.

Continuati simularea. Veti constata ca urmatoarea intrerupere va provoca modificarea starii


lui RA2.

13.7 Incercarea pe placuta de test

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 :

/1 : ne va da 500.000/256 = 1953,125 treceri. Nu e practic.


/2 : ne va da 500.000/512 = 976,5625 treceri. Nu e practic.
/4 : ne va da 500.000/1024 = 488,28125 treceri . Nu e practic.
/8 : ne va da 500.000/2048 = 244,140625 treceri. In acest caz, va fi necesar un singur
numarator, caci numarul de treceri este mai mic decit 256.
Care va fi precizia obtinuta ? Daca vom initializa cmpt cu 244 si vom pune predivizorul sa
divida cu 8, durata obtinuta va fi :

256 x 8 x 244 = 499.712 µs, deci 499.712 x 2 = 999.424µs per aprindere.

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 :

TIMEBASE EQU D’244’ ; baza de timp = 244 zecimal

Daca aveti vreo problema, fisierul functional al acestui exercitiu este disponibil sub
denumirea « Led_tmr.asm ».

Ø AVANTAJE OBTINUTE : O precizie mai mare


Ø DEZAVANTAJE : Mai multe intreruperi generate, deci mai mult timp pierdut pentru
programul principal. In cazul nostru acest lucru n-are importanta deoarece programul
nu face nimic altceva, dar nu intotdeauna sintem in aceasta situatie.

13.9 O a doua imbunatatire a preciziei

Se poate imbunatati in continuare precizia programului vostru. Pentru aceasta, puteti sa nu


folositi predivizorul, dar sa folositi mai multe numaratoare pentru 1953,125 de treceri. La a
1953-a trecere, puteti chiar sa generati o a II-a temporizare adaugind o valoare initiala in
tmr0. De exemplu :

Ø Se detecteaza 1953 de treceri cu ajutorul mai multor numaratoare


Ø La a 1953-a trecere, cind avem 1953 x 256 = 499.968 µs, ne mai lipsesc deci 500.000 -
499.968 = 32 µs
Ø Va trebui deci sa adaugam 256 - 32 = 224 la timer-ul tmr0, dar tinind cont de cele 2
incrementari pierdute sistematic la fiecare modificare a lui TMR0, vom adauga 256-30

movlw 226 ; depasire in 30 + 2 cicluri


addwf tmr0

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 ?

1.000.000/(256 x 256 x 7 x 2) = 1,089913504 µs

Asta inseamna deci o frecventa a instructiunilor de 1/1,089913504 µs = 0,917504 MHz.


Cum frecventa ciclurilor interne este egala cu frecventa cuartului impartita la 4, vom avea
deci nevoie de un cuart de 0,917504 x 4 = 3,670016 MHz (rezultatul este in MHz pentru ca
am impartit la µs ; sau, impartind printr-o milionime, e echivalent cu a inmulti cu un milion).

Singura problema ar fi daca exista disponibile in comert cuarturi de aceasta frecventa. In


caz contrar, va trebui sa reluati calculele cu alte valori pentru predivizor si numarator.

Deci daca gasiti un cuart de frecventa corespunzatoare, veti obtine un ceas cu precizia
cuartului. Mai aveti de adaugat un afisaj si gata ceasul !

13.11 Metoda de lux : ceasul dublu

Metoda precedenta prezinta dezavantajul de a incetini PIC®-ul. Ce facem daca dorim in


acelasi timp viteza maxima, dar si precizia maxima ? Stati linistiti, nu-i nici o problema.
Veti comanda PIC-ul cu cuartul vostru si veti crea un alt oscilator extern avind un cuart
special pentru temporizare. Veti aplica semnalul obtinut pe pinul RA4/T0CKI si va veti
configura timer-ul 0 in mod numarator. Prin urmare, PIC®-ul vostru va rula la viteza sa
maxima, iar intreruperile tmr0 sint generate de catre o alta baza de timp, adecvata masurarii
evenimentelor pe care le doriti.

13.12 Exemplu de folosire a 2 intreruperi

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.

14.1 Marimea si localizarea memoriei EEPROM

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 ?

La fel, adresa 0x2006 contine identificarea componentei. Prin aceasta, un programator


evoluat poate face distinctia intre un 16F84 si un 16F84A, caci identificatorul constructiv
difera.
16F84 dispune de 64 de locatii EEPROM la dispozitia dvs. Vom vedea imediat cum sa le
folosim.

14.2 Pregatirea programului

Incepeti prin a face un Copy/Paste a fisierului « Led_tmr1.asm » si redenumiti aceasta


copie « eep_test.asm ». Creati un nou proiect in MPLAB® cu acelasi nume si adaugati-i acest
fisier.

Editati antetul programului :

;******************************************************************
; 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 *
;******************************************************************

Mai departe, sa adaugam o variabila in zona variabilelor. Ea va contine valoarea de


reincarcare a contorului.

reload : 1 ; valoare de reincarcare a contorului

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 :

Ø Se initializeaza o locatie EEPROM denumita « eereload » cu valoarea 0x07, in


momentul programarii
Ø La pornire, se citeste EEPROM-ul si se pune continutul sau in « reload ».
Ø Continutul lui « reload » este folosit pentru reincarcarea contorului cmpt atunci cind
acesta ajunge la 0.

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

14.3 Initializarea zonei EEPROM

Observam pe schema logica ca noi citim EEPROM-ul cu scopul de a pune continutul


stocat aici, in variabila noastra. Dar noi stim bine ca am initializat acest EEPROM in
momentul programarii PIC®-ului.
Credeti probabil ca nu serveste la nimic sa initializam EEPROM-ul la fiecare pornire a
PIC®-ului, caci altfel de ce sa mai folosim o zona de memorie care rezista la reset sau la
intreruperea tensiunii ?
Noi vom initializa deci aceasta zona direct, in momentul programarii. Acest lucru se face
cu ajutorul directivei « DE » de la Data Eeprom, situata in zona de date EEPROM, adica la
0x2100.
Sa cream deci o zona EEPROM, imediat dupa cea a variabilelor.

;******************************************************************
; 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.

Dar, raspundeti-mi, de ce valoarea respectiva este la adresa 0x00 si nu la adresa 0x2100 ?


De fapt, va trebui sa distingem 2 adrese. Adresa fizica a acestei locatii de memorie este
intr-adevar 0x2100. Insa aceasta adresa este accesibila numai in mod programare.
Programul poate accesa aceste locatii numai printr-o procedura speciala si cu o adresa
asa-zisa relativa. Aceasta adresa incepe deci de la 0x00.

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 :

#DEFINE eereload 0x00 ; adresa din EEPROM a eereload

sau

eereload EQU 0x00

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.

14.4 Registrul EEDATA

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.

14.5 Registrul EEADR

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.

14.6 Registrul EECON1

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 :

bitii 7/6/5 biti neutilizati

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.

bit 3 : WRERR WRite ERRor. Este un bit de eroare. El trece in 1 daca o


operatie de scriere in EEPROM a fost intrerupta, de exemplu,
de un reset.

bit 2 : WREN WRite ENable. Autorizare incepere ciclu de scriere.

bit 1 : WR WRite. Incepere ciclu de scriere. El este readus la 0 automat,


odata ce scrierea este incheiata

bit 0 : RD ReaD. Incepere ciclu de citire. Ramine in 1 timp de 1 ciclu,


dupa care este readus automat pe 0.

Observatie :

In cazul in care un ciclu de scriere va fi intrerupt ca urmare a depasirii watchdog-ului sau a


unui reset, puteti citi bitul WRERR care va va semnala acest lucru.
Registrele EEDATA si EEADR ramin neschimbate, si dvs. puteti relua ciclul de scriere.
Acest lucru nu este valabil, evident, in cazul unei intreruperi de tensiune. In acest caz, va voi
explica metoda mea personala de verificare, la sfirsitul acestui capitol.

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.

14.8 Citirea din memoria EEPROM

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.

READEE macro adeeprom ; macro cu 1 parametru (argument)


movlw adeeprom ; incarca adresa eeprom (argument primit)
movwf EEADR ; adresa de citire in registrul EEADR
bsf STATUS , RP0 ; se trece in bancul 1
bsf EECON1 , RD ; lanseaza citirea EEPROM
bcf STATUS , RP0 ; se revine in bancul 0
movf EEDATA , w ; incarca valoarea citita in W
endm ; sfirsit macro

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 :

READEE eereload ; citirea adresei eereload pentru eeprom

va citi EEPROM-ul la adresa "eereload", adica la adresa 0x00.


Acest macro, ca si toate celelalte modificari principale, vor fi adaugate fisierului vostru
m16f84.asm. Eu vi l-am dat gata modificat sub denumirea « m16f84_n2.asm ».

Incepind din lectia viitoare, acesta va inlocui fisierul vostru actual « m16F84.asm ».

Sa revenim la schema noastra logica. Va trebui deci sa adaugam citirea EEPROM-ului la


initializari, si sa plasam aceasta valoare citita in "reload" SI in "cmpt". Iata rutina modificata :

; 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

Compilati-va programul si puneti-l in PIC®. LED-ul va trebui acum sa clipeasca cu o

143
frecventa de 1Hz. Daca nu merge, verificati si comparati cu fisierul « eep_test1.asm » furnizat
impreuna cu aceasta lectie.

14.9 Scrierea in memoria EEPROM

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 :

Ø Microchip recomanda ca aceasta procedura specifica sa nu fie intrerupta de catre o


intrerupere, deci vom bloca intreruperile pe durata acestei faze.
Ø La sfirsitul procedurii de scriere, datele nu sint inca inregistrate in EEPROM. Aceasta
se va intimpla cam 10 ms mai tirziu (adica timpul echivalent a 10.000 de instructiuni).
Nu veti putea deci scrie o noua valoare in EEPROM, si nici nu veti putea citi aceasta
valoare inainte de a verifica incheierea scrierii precedente.
Ø Sfirsitul scrierii poate fi constatat prin generarea unei intreruperi (daca bitul EEIE este
in 1), prin citirea flag-ului EEIF (daca a fost pus pe 0 inainte de inceperea scrierii), sau
prin verificarea bitului WR care este in 1 pe toata durata ciclului de scriere.

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.

WRITEE macro addwrite ; datele se gasesc in W


LOCAL loop ; eticheta locala
movwf EEDATA ; pune octetul in registrul de date
movlw addwrite ; incarca adresa de scriere si
movwf EEADR ; pune-o in registrul de adresa
loop
bcf INTCON , GIE ; blocheaza intreruperile
btfsc INTCON , GIE ; testeaza daca GIE este in 0
goto loop ; daca nu, reia
bsf STATUS , RP0 ; se trece in bancul 1
bcf EECON1 , EEIF ; sterge flag de sfirsit scriere
bsf EECON1 , WREN ; autorizeaza acces scriere
movlw 0x55 ; incarca 0x55
movwf EECON2 ; trimite comanda
movlw 0xAA ; incarca 0xAA
movwf EECON2 ; trimite comanda
bsf EECON1 , WR ; lanseaza ciclul de scriere
bcf EECON1 , WREN ; blocheaza scrierea urmatoare
bsf INTCON , GIE ; deblocheaza intreruperile
bcf STATUS , RP0 ; revenire in bancul 0
endm

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.

14.10 Utilizarea practica a memoriei EEPROM

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 :

cmpt2 : 1 ; contor de treceri 2

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.

Un cuvint despre bug-ul blocarii intreruperilor in 16C84

In 16C84, blocarea intreruperilor nu era luata in consideratie decit dupa instructiunea


urmatoare. Deci, intre « bcf INTCON, GIE » si instructiunea urmatoare, putea avea loc o
intrerupere.
Cum « retfie » repunea automat bitul GIE in 1, intreruperea era repusa in functiune fara

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.

Iata noua schema logica :

Si rutina de intrerupere timer :

;*******************************************************************
; 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

; reincarca contor de treceri


; ---------------------------
movf reload , w ; incarca valoarea continuta in reload
movwf cmpt ; in contorul de treceri

; incrementeaza contor de treceri 2


; ---------------------------------
incf cmpt2 , f ; incrementeaza contor de treceri 2

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.

Sa vedem deci programul nostru principal :

;******************************************************************
; 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

; Testeaza daca scrierea precedenta in EEPROM s-a incheiat


; --------------------------------------------------------
; (facultativ aici, caci timpul scurs
; este suficient de mare)
BANK1 ; se trece in bancul 1
wait
btfsc EECON1 , WR ; testeaza daca avem scriere in curs
goto wait ; da, asteapta
BANK0 ; se revine in bancul 0

; scrie continutul lui reload in EEPROM


; -------------------------------------
movf reload , w ; incarca reload
WRITEE eereload ; scrie la adresa 0x00
goto start ; bucleaza
END ; directiva sfirsit de program

Compilati programul si incarcati-l in PIC®. Puneti circuitul pe placuta de test. Remarcati


ca LED-ul clipeste cu o frecventa aproximativa de 1 Hz. La a 17-a aprindere, frecventa de
clipire se reduce putin, si tot asa dupa fiecare 16 treceri.
Apreciati frecventa de clipire curenta, dupa care intrerupeti alimentarea PIC®-ului.
Asteptati citeva secunde, dupa care recuplati alimentarea. LED-ul clipeste cu frecventa
precedenta, caci parametrul de temporizare a fost salvat in EEPROM.
Fisierul care trebuia obtinut la sfirsitul acestei lectii este disponibil sub denumirea
« eep_test.asm».
147
14.11 Securizarea accesului in memoria EEPROM

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

Watchdog-ul, sau ciinele de paza este un mecanism de protectie al programului vostru.


El supravegheaza daca acesta ruleaza totdeauna in spatiul si in intervalul de timp care i-a
fost alocat.

15.1 Principiul de functionare

Activarea sau dezactivarea watchdog-ului se decide in momentul programarii PIC®-ului,


cu ajutorul directivei _CONFIG. Daca se precizeaza ca « _WDT_OFF », watchdog-ul va fi
dezactivat. Daca, din contra, se precizeaza ca « _WDT_ON », watchdog-ul va fi activ.

NU ESTE DECI POSIBIL SA ACTIVAM SAU SA DEZACTIVAM WATCHDOG-UL


IN TIMPUL EXECUTIEI PROGRAMULUI.

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.

15.2 Predivizorul si watchdog-ul

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.

15.3 Rolul watchdog-ului

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 :

1. Watchdog-ul este un mecanism intern sau extern destinat a asigura o anumita


securitate unei aplicatii oarecare. El se activeaza atunci cind nu este solicitat la
intervale regulate. El se asigura ca softul incorporat pare sa functioneze asa cum a
fost prevazut.

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.

4. Watchdog-ul poate fi si extern, caz in care, cu pretul unei complexitati sporite a


circuitelor, poate prezenta avantajul de a putea actiona chiar si in cazul defectarii
electronice a PIC®-ului si a circuitelor sale de comanda, mecanismul nefiind legat
de functionarea acestuia. De exemplu, se poate astfel decupla alimentarea generala
a unui sistem, se pot opri motoare, etc. chiar daca circuitele principale de comanda
sunt defecte.

5. Watchdog-ul integrat este o masura de securitate foarte eficace fata de simplitatea


sa si relativ fiabila pentru a iesi din situatii neprevazute care aduc programul PIC®-
ului intr-o stare nedeterminata. El ar trebui în mod automat sa fie folosit pentru
orice aplicatie finalizata.

6. Watchdog-ul poate fi un remediu pentru situatii anormale provocate de un bug


nedetectat in faza de depanare, de o anomalie de rulare a programului provocata de
un fenomen extern (perturbatii, probleme de alimentare, etc.), de starea neprevazuta
a unui periferic (blocare in cursul transmisiei), sau orice alta situatie anormala.

7. Watchdog-ul nu este o infailibila si absoluta care sa permita iesirea din toate


situatiile delicate. Pentru aplicatiile cu risc critic sau mortal (ascensoare, masini
unelte, etc.), vor trebui intotdeauna prevazute mecanisme redundante de securitate
(hardware, fara inteligenta inglobata). Portul centurii de siguranta va creste gradul
de securitate, dar nu va va garanta niciodata ca veti iesi teferi dintr-un accident.

8. Watchdog-ul nu este conceput pentru a masca erorile de conceptie ale softului, un


soft corect conceput trebuind sa ruleze fara probleme fara activarea watchdog-ului,
care nu va fi validat decit dupa faza de depanare.

9. Nu trebuie sa introducem instructiuni « clrwdt » in rutinele de intrerupere, pentru a


ascunde o anomalie la nivelul programului principal.

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.

11. PIC®-urile 16F (spre deosebire de alte modele de PIC®) nu dispun de o


instructiune «reset». O modalitate simpla de a provoca un reset software este de a
folosi o bucla infinita ce nu va contine instructiuni « clrwdt », ceea ce va provoca
un reset al PIC®-ului prin depasirea watchdog-ului.

15.4 Folosirea corecta a watchdog-ului

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.

15.5 Ceea ce nu trebuie facut

Amintiti-va ca o intrerupere intrerupe programul si il directioneaza catre adresa 0x04.


Odata intreruperea teminata, programul revine in locatia in care se gasea, chiar daca acesta
este in afara zonei normale a programului.
Deci, daca veti pune o instructiune « clrwdt » intr-o rutina de intrerupere, aceasta
instructiune risca sa se execute chiar daca programul vostru este blocat. Acest lucru este
tocmai opusul a ceea ce se doreste. In consecinta :

NU TREBUIE NICIODATA SA FOLOSIM O INSTRUCTIUNE CLRWDT INTR-O


RUTINA DE INTRERUPERE.

15.6 Masurarea timpului real al watchdog-ului

Valoarea de 7 ms este valoarea minima, iar valoarea de 18 ms este valoarea constatata in


general. Dar care este valoarea exacta a acestui interval pentru PIC®-ul propriu ? Sintem
tentati sa raspundem la aceasta intrebare.

Faceti un Copy/Paste a fisierului vostru « Led_cli.asm ». Redenumiti acest fisier


«wdt.asm» si creati un nou proiect.

Intr-o prima faza, nu va atingeti de _CONFIG. Modificati valoarea registrului OPTION :

OPTIONVAL EQU B'10001111' ; Valoare registru de optiuni


; Rezistente pull-up OFF
; prescaler Wdt = 128

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 :

clrwdt ; sterge watchdog

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

Compilati programul, incarcati-l in PIC®, si cuplati alimentarea.

Ce se intimpla ? Dupa aproximativ ¼ seconde, LED-ul se aprinde, si asta-i totul. Nu se mai


intimpla nimic altceva. Este exact ce am prevazut.

Modificati acum linia _CONFIG pentru a activa watchdog-ul :

__CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _HS_OSC

Recompilati programul, incarcati-l in PIC® si alimentati montajul. Ce se intimpla ? Ei


bine, LED-ul clipeste. Veti constata probabil ca timpul ce separa doua stingeri (sau doua
aprinderi) este de aproximativ 2 secunde.

Explicatie

Watchdog-ul nostru nu a fost readus la 0 de la pornirea PIC®-ului. Deci, odata ce


intervalul
« durata de baza x predivizor » este atins, se provoaca un reset care antreneaza o stingere a
LED-ului si un restart, dupa care ciclul se reia la infinit.

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.

15.7 Simularea unei blocari de program

Faceti din nou o copie a fisierului vostru « Led_cli.asm » si redenumiti-l « secur.asm ».


Creati un nou proiect. Schimbati linia de configurare :

OPTIONVAL EQU H'000F' ; Valoare registru optiuni


; Rezistente pull-up ON
; Prescaler wdt pe 128

si linia DEFINE (vedeti ca am modificat placuta de test)

#DEFINE BUTON PORTB , 0 ; bouton-poussoir

Modificati in continuare programul principal :

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.

Compilati programul si incarcati-l in PIC®. Alimentati montajul : LED-ul clipeste. Apasati


un moment pe buton si LED-ul nu va mai clipi. Programul ruleaza intr-o bucla infinita care
simuleaza o blocare, care ar fi putut aparea, de exemplu, ca urmare a unui semnal parazit si
care a "aruncat" programul in afara zonei sale de functionare normala.

15.7.1 Corectie prin folosirea watchdog-ului

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.

Iata mai jos programul principal modificat :

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

Am programat watchdog-ul cu un predivizor de 128, ceea ce ne impune sa trimitem o


comanda « clrwdt » la fiecare 7 x 128 ms, adica la fiecare 896 ms.

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.

15.8 Alegerea valorii predivizorului

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.

15.9 Timp tipic, minim, si maxim

Am vazut ca au aparut mai multe notiuni legate de temporizarea watchdog-ului. Este


important sa facem distinctie intre diferitele valori. Deci, sa le recapitulam :
Ø Timpul tipic (18 ms) este timpul in care, IN GENERAL watchdog-ul produce un reset
in cazul unei blocari. Este deci, « timpul de reactie » normal (sau tipic) al watchdog-
ului.

Ø 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.

Va sfatuiesc deci sa va realizati programele in felul urmator :

Ø 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.

In acest fel, veti fi siguri ca watchdog-ul nu va servi la recuperarea unor erori de


programare din partea voastra (repornind PIC®-ul dupa un blocaj datorat unei bucle infinite,
de exemplu).

16. Modul SLEEP

In continuare, in aceasta lectie, vom studia un mod foarte particular de functionare al


PIC®-urilor, care le permite sa intre intr-o stare stand-by, in scopul reducerii consumului.

16.1 Principiul de functionare

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 :

Ø Watchdog-ul este adus la 0, la fel cum ar fi facut si o instructiune « clrwdt ».

Ø Bitul T0 din registrul STATUS e pus in 1

Ø Bitul PD din registrul STATUS e pus in 0

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.

16.2 Iesirea din modul « sleep »

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 :

Ø Aplicarea unui nivel 0 pe pinul MCLR. Aceasta va provoca resetarea circuitului.


PIC®-ul va efectua un reset clasic, pornind de la adresa 0x00. Utilizatorul va putea
testa bitii T0 si PD dupa pornire, pentru a identifica evenimentul produs (reset,
watchdog sau punere sub tensiune).

Ø 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 ».

Ø Aparitia unei intreruperi RB0/INT, RB sau EEPROM.

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.

16.3 Revenirea cu bitul GIE dezactivat

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 :

Ø Este executata instructiunea care urmeaza dupa instructiunea « sleep ».

Ø Programul continua de la adresa 0x04, ca pentru o intrerupere obisnuita

Notati faptul ca daca nu doriti sa executati instructiunea care urmeaza dupa instructiunea
« sleep », este suficient sa puneti in acest loc o instructiune « nop ».

16.5 Imposibilitatea punerii in mod « sleep »

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.

16.6 Folosirea modului « sleep »

Vom realiza un mic exercitiu privitor la punerea in stand-by a PIC®-ului nostru.

Faceti din nou o copie a fisierului vostru « m16F84.asm » si redenumiti-l « sleep.asm ».


Creati un nou proiect.
Alocati predivizorul watchdog-ului, cu o valoare de 32. Aceasta ne da o durata tipica de
depasire de 18 ms x 32 = 576 ms.

OPTIONVAL EQU H '008D' ; Valoare registru optiuni


; Prescaler wdt = 32

Definiti LED-ul pe pinul RA2 :

#DEFINE LED PORTA, ; LED ca iesire

Puneti in continuare LED-ul ca iesire in rutina de initializare (atentie, in bancul 1) :

bcf LED ; LED ca iesire

Sa scriem in continuare programul principal :


157
;******************************************************************
; PROGRAM PRINCIPAL *
;******************************************************************
start
bsf LED ; aprinde LED
sleep ; punere in stand-by
bcf LED ; stinge LED
sleep ; punere in stand-by
goto start ; bucleaza

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.

Atentie deci, fiti constienti de faptul ca REVENIREA PIC®-ului din stand-by


necesita un anumit timp.

16.7 Caz tipic de utilizare

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 ».

16.7.1 Pentru un consum minim

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.

Bineinteles ca nu trebuie sa uitam de rezistentele de pull-up de pe portul B, daca le aveti


activate. Atunci, toate intrarile conectate la masa vor consuma curent.
Toate intrarile nefolosite vor trebui sa fie legate fie la masa, fie la Vdd, caci daca le veti
lasa flotante, fiecare tranzitie de nivel accidentala va consuma un mic curent. Retineti ca

158
obtineti acelasi rezultat configurind pinii nefolositi ca intrari.

Un eventual ceas extern va trebui oprit.


Pentru alte PIC®-uri mai sunt si alte masuri ce trebuie luate, ca de exemplu oprirea
convertoarelor analogice/numerice. Veti intelege ca va trebui sa priviti problema in ansamblul
ei si nu doar sa scrieti instructiunea « sleep ».

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.

17. Ce mai gasim in datasheet

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 »

17.1 Structura interna

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.

17.2 Secventa de decodare

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.

Remarcati ca in timp ce o instructiune este executata, urmatoarea este deja incarcata


(« fetch »). Aceasta explica de ce in cazul unui salt, instructiunea urmatoare incarcata nefiind
cea care trebuie executata, este necesar un ciclu suplimentar pentru a incarca instructiunea
corecta.

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.

17.4 Registrele speciale

Tabelul 4-1 va da o vedere globala a tuturor registrelor speciale. Prima coloana va da


adresa registrului, a doua, numele simbolic, dupa care urmeaza numele fiecarui bit.
Penultima coloana va da valoarea fiecarui bit dupa o punere sub tensiune, in timp ce ultima
coloana va da valoarea bitilor pentru celelalte tipuri de RESET (watchdog si MCLR).
Bitii notati cu 0 sau 1 sint cei al caror nivel este cel indicat. Bitii notati cu « u », sint bitii
care nu sint afectati de RESET (unchanged). Bitii notati cu « x », sint cei a caror stare nu
poate fi cunoscuta in acest moment.

17.5 Electronica porturilor

Figurile de la 5-1 la 5-4 permit electronistilor sa inteleaga particularitatile porturilor I/O


din punct de vedere al caracteristicilor lor electrice. Veti vedea, dupa cum v-am spus deja, ca
pinul RA4, de exemplu, este o iesire cu « colector in gol », sau mai bine spus cu « drena in
gol », configuratie care nu permite impunerea unui nivel 1 la iesire.

17.6 Registrul de configurare

Registrul de configurare se gaseste la adresa 0x2007, in afara spatiului normal de adresare


al PIC®-ului. El nu este accesibil decit in momentul programarii.

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 :

__CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _HS_OSC

va corepunde deci pentru un 16F84 la:

ORG 0x2007
DA B’111111111111110’

NU VA SFATUIESC SA FOLOSITI O ASTFEL DE PRACTICA, caci vom vedea mai jos


de ce.

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.

Tabelul 8-2, face acelasi lucru pentru oscilatoarele cu cuart.


Notati faptul ca bitii amintiti sint integrati in _HS_OSC si alte configuratii de oscilator, si
de aceea nu-i veti intilni niciodata direct.

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.

EVITATI CIRCUITELE DE TIP LS (74LS04), care va pot da mari batai de cap.

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.

Fie F frecventa ceasului aplicat si T durata unui ciclu-instructiune :

T=4/F

De exemplu, pentru un cuart de 4 MHz : T = 4 / 4.000.000 = 1 µs

Reciproca formulei, F = 4 / T ne ajuta sa calculam frecventa care corespunde unui ciclu-


instructiune dat. Daca dorim de exemplu un ciclu-instructiune de 1,5 µs, ne va trebui un ceas
de :
F = 4 / (1,5 x 10-6) = 2,667 x 106 = 2,667 MHz

161
17.7.1 Precizia oscilatorului

Nu uitati ca in calculele voastre sa tineti cont intotdeauna de eroarea existenta. Toleranta


ceasului vostru depinde direct de metoda folosita. Un ceas cu cuart va da intotdeauna o
precizie mai buna decit o simpla retea RC.
De exemplu, sa presupunem ca doriti sa construiti un ceas pe baza unui PIC®. Vom evalua
ordinul de marime al erorii obtinute in functie de tipul de oscilator folosit.

Vom presupune ca programul vostru este realizat corect, si ca o eroare de masurare a


timpului din aceasta cauza, este exclusa.

Sa incepem cu RETEAUA RC.

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.

Sa luam acum un CUART. Acesta ne va da o eroare tipica, conform tabelului 12-2, de 50


PPM (Part Par Million), adica 0.000005%. Sa calculam deci abaterea ceasului nostru :
86400 x 50 x 10-6 = 4,32 s
Iata deci un rezultat mult mai acceptabil.

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.

Eveniment Ramificare (PC) STATUS


Punere sub tensiune 0x00 0001 1xxx
MCLR la masa in timpul functionarii normale 0x00 000u uuuu
MCLR la masa in timpul modului sleep 0x00 0001 uuuu
Reset prin depasire timer watchdog 0x00 0000 1uuu
Iesire din mod sleep prin depasire watchdog Continuare program uuu0 0uuu
Iesire din mod sleep prin intrerupere cu GIE = 0 PC + 1 uuu1 0uuu
Iesire din mod sleep prin intrerupere cu GIE = 1 PC + 1 apoi 0x04 uuu1 0uuu

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.

17.9 Punerea sub tensiune

Imediat ce puneti PIC®-ul sub tensiune, un circuit intern analizeaza tensiunea de


alimentare. Cind tensiunea urca de la 1.2 V la 1.7 V, se genereaza automat un RESET. Acesta
este resetul de punere sub tensiune (Power On Reset). Notati faptul ca acest circuit nu
provoaca un reset daca tensiunea scade. Ramine in sarcina dvs. sa va ocupati de acest caz
particular daca aveti probleme. Alte PIC®-uri, au un mecanism special de tratare a acestei
probleme, ca de exemplu, 16F876. Il vom aborda in partea a doua a cursului.

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.

In continuare, dupa scurgerea timpului precedent, avem o temporizare suplimentara ce


consta in numararea a 1024 de oscilatii ale ceasului principal (Oscillator Start-up Timer).
Aceasta temporizare permite asigurarea unei functionari stabile a ceasului. Acest OST nu se
163
foloseste in modul OSCILATOR RC, dar este valid pentru punerile sub tensiune si pentru
"treziri" (nu se foloseste pentru celelalte forme de reset, pentru care se presupune ca ceasul
este deja stabil).

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.

17.10 Caracteristici electrice

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.

17.11 Portabilitatea programelor

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 :

Ø Va trebui neaparat sa folositi directivele prevazute (_CONFIG) ci nu accesul direct de


genul (ORG 0x2007). Aceasta deoarece aceste locatii si semnificatia lor pot fi
modificate de la un model de PIC® la altul.
Ø Va trebui sa folositi fisierele « .inc » de la Microchip corespunzatoare PIC®-ului pe
care va rula programul vostru. De exemplu, « P16f84.inc ».
Ø Va trebui sa cititi noile datasheet-uri pentru a analiza diferentele dintre componentele
initiale si cele noi. La nevoie, modificati programul.
Ø Puteti deasemenea sa va recompilati programul, dupa ce ati inlocuit linia « include »
prin cea continind noul fisier « include ».

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.

17.12 Actualizarea componentelor

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.

In continuare veti vedea ca RAM-ul accesibil utilizatorului s-a marit de la 36 la 68 de


octeti.
Apoi, veti constata ca Microchip a adaugat un filtru pe pinul MCLR cu scopul de a evita ca
parazitii sa nu provoace un reset neprevazut. Acesta mareste totodata lungimea impulsului
necesar pentru a genera un reset dorit.

Urmeaza un avertisment asupra citorva caracteristici electrice ale PIC®-ului. Aceste


avertismente fac trimitere la diferite tabele de caracteristici electrice.
Imediat dupa acestea, urmeaza inca o corectie la nivelul functionarii lui PORTA, atunci
cind PIC®-ul era folosit la frecvente sub 500 KHz.

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

V-am vorbit deja si despre acest lucru in lectiile anterioare.

17.13 Concluzii

Iata-va acum in posesia tuturor informatiilor necesare pentru a va dezvolta propriile


programe cu 16F84.
Atentie, va trebui totusi sa exersati mult inainte de a deveni un as al programarii. Veti
spune destul de des : « nu-i posibil, trebuia sa mearga, circuitul meu 16F84 este defect ». In
99% din cazuri, este vorba de un bug al programului vostru, pe care-l veti gasi poate dupa mai
multe zile de cautari. Nu va descurajati, recititi documentatiile, chiar daca aveti impresia ca le
stiti pe dinafara.

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.

Va voi da dictionarele de rime, ca voi sa puteti sa va scrieti poemele...

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 :

movf mem1 , w ; incarca mem1


subwf mem2 , w ; efectueaza scaderea mem2 – mem1

Mai departe, pentru a afla rezultatul, va fi suficient sa testati bitii C si Z din registrul
STATUS :

- Daca Z = 1 , cele 2 locatii de memorie contin aceeasi valoare

- 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 ».

movf mem1 , w ; incarca mem1


xorwf mem2 , w ; daca sint egale, toti bitii vor fi 0,
; iar Z va fi 1

18.2 Scaderea unei valori din W

Sa presupunem ca aveti o valoare in W si doriti sa scadeti 5 din aceasta valoare. Primul


reflex este urmatorul :

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 :

Ø Se inmulteste 1100 cu 1. Se obtine deci 1100 (D’12’)


Ø Se inmulteste 1100 cu 0 si se deplaseaza rezultatul spre stinga. Se obtine 0000.
Ø Se inmulteste 1100 cu 1 si se deplaseaza rezultatul spre stinga. Se obtine 1100
completat cu 00, adica 110000, deci D ‘48’
Ø Se inmulteste 1100 cu 1 si se deplaseaza rezultatul spre stinga. Se obtine 1100
completat cu 000, adica 1100000, deci D’96’.
Ø Se aduna totul si se obtine 10011100, adica D’156’.

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 :

Ø Se inmulteste 1100 cu 1. Se pune rezultatul in rezultatul final.


Ø Se inmulteste 1100 cu 0. Se deplaseaza o data. Se aduna la rezultatul final.
Ø Se inmulteste 1100 cu 1. Se deplaseaza de 2 ori. Se aduna la rezultatul final.
Ø Se inmulteste 1100 cu 1. Se deplaseaza de 3 ori. Se aduna la rezultatul final.

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 :

clrf resulH ; sterge rezultatul cu pondere mare


clrf resulL ; idem pentru pondere mica
movlw 0x08 ; pentru 8 biti
movwf cmpt ; initializeaza contorul de bucle
movf multi , w ; incarca inmultitorul
movwf multemp ; salveaza in multemp
movf multan , w ; deinmultitul in w
loop

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 ».

18.4 Inmultirea cu o constanta

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 :

Ø sa inmultim cu 2 (deci sa deplasam operandul spre stinga)


Ø sa inmultim inca odata cu 2 (adica inmultim cu 4, deci deplasam inca odata)
Ø sa adunam operandul original (operand + operand x 4 = operand x 5)
Ø sa inmultim rezultatul cu 2 (deci deplasare spre stinga)

Iata deci o inmultire cu 10 foarte rapida. Am efectuat : x 2, x 2, +1, x 2, adica 3 deplasari


si o adunare simpla.
In general, putem face diverse aranjamente pentru a obtine rezultatele inmultirilor cu
constante prin aceasta metoda. Astfel cistigati timp pretios in programul dvs.
Iata un programel care inmulteste un numar de 4 biti continut in mem1, cu 10. Rezultatul
se obtine in resul. Putei sa adaptati foarte usor acest program pentru numere pe 8 biti.

movf mem1 , w ; incarca operandul pe 4 biti


movwf resul ; salveaza in rezultat
bcf STATUS , C ; sterge Carry
rlf resul , f ; inmulteste cu 2
rlf resul , f ; inmulteste cu 4
addwf resul , f ; aduna mem1, deci inmulteste cu 5
rlf resul , f ; inmulteste cu 10

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.

movlw mem ; indica prima zona


movwf FSR ; se initializeaza indicatorul catre
; zona sursa
movlw 15 ; 15 variabile de transferat
movwf cmpt ; salveaza in numaratorul de bucle
loop
movf INDF , w ; incarca sursa
movwf tampon ; trebuie salvata intr-o locatie tampon
movlw mem2-mem1 ; ecartul de adresa intre cele 2 locatii
addwf FSR , f ; indica catre destinatie
movf tampon , w ; reincarca valoare sursa
movwf INDF ; salveaza in destinatie
movlw (mem1-mem2)+1 ; ecart intre adresa de destinatie si
; adresa sursei urmatoare +1
addwf FSR , f ; adauga la indicator
decfsz cmpt , f ; decrementeaza contorul de bucle
goto loop ; nu e ultima locatie, urmatoarea

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 :

CBLOCK 0x10 ; s-a ales o zona de memorie in RAM


mem1 : 15 ; 15 locatii pentru sursa
ENDC

CBLOCK 0x30 ; s-a ales o alta zona, dar la intimplare


mem2 : 15 ; 15 locatii pentru destinatie
ENDC

Sau, daca nu se doreste a se preciza organizarea zonelor folosite ::

mem1 EQU 0x10


mem2 EQU 0x30

Acum, sursa se intinde de la B’00010000’ la B’00011111‘, iar destinatia, de la


B’00110000’ la B’00111111’
Observam ca numai bitul 5 din FSR face distinctia intre zonele sursa si destinatie. Pentru
modificari de biti, nu ne trebuie acumulatorul, deci nu va trebui sa salvam si sa reincarcam
valoarea de transferat.
Putem deci sa modificam programul in modul urmator :

movlw mem ; indica prima zona


movwf FSR ; se initializeaza indicatorul catre
; zona sursa
movlw 15 ; 15 variabile de transferat

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

La PIC®-urile care utilizeaza mai multe bancuri, ca de exemplu 16F876, puteti


deasemenea sa folositi aceeasi adresa dar in doua bancuri diferite, si sa treceti dintr-unul in
altul modificind bitul IRP din registrul STATUS.

18.6 Tabelele in memoria program

Sa presupunem ca avem nevoie de un tabel de dimensiuni mari. De exemplu, un tabel de


200 de elemente. Unde punem acest tebel ? In RAM nu avem destul loc, in EEPROM nici
atit. Nu ne mai ramine decit memoria de program.
Problema este ca 16F84, fata de 16F876, de exemplu, nu dispune de nici o metoda de citire
a datelor din memoria de program. Singura metoda de acces este folosirea instructiunilor.
Sa presupunem ca avem un caz simplu : vrem sa cream un tabel care sa contina patratele
numerelor de la 0 la 15. Am putea folosi un mic sub-program de forma urmatoare :

Ø Se testeaza daca numarul ridicat la putere = 0. Daca da, se intoarce 0


Ø Se testeaza daca numarul ridicat la putere = 1. Daca da, se intoarce 1
Ø Se testeaza daca numarul ridicat la putere = 2. Daca da, se intoarce 4
Ø S.a.m.d…

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:

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

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.

Sa presupunem deci, ca numarul de ridicat la patrat se gaseste in registrul W. Daca ne


gasim pe prima linie a tabelului, adunind W la PCL, vom sari direct la linia corecta. Sa facem
completarile in subrutina noastra. Va fi suficient deci sa adaugam inaintea tabelului linia :

patrat
addwf PCL , f ; aduna W la PCL

Deci, daca se incarca 4 in W si se face un apel « call patrat », PCL-ul va fi incrementat cu


4 si programul va sari la linia « retlw 16 ».
Reamintiti-va ca de fapt PC-ul indica intotdeauna instructiunea urmatoare, deci in
momentul executiei instructiunii « addwf PCL , f », acesta trebuia sa indice initial linia
« retlw 0 ».
Ajunsi aici, va trebui sa va reamintiti ca adresa pentru o operatie asupra registrului PCL
este completata prin continutul registrului PCLATH. Va trebui deci sa initializati corect acest
registru cu numarul « paginii de 8 biti » al adresei tabelului vostru. De exemplu, daca tabelul
vostru se gaseste la adresa 0x200, va trebui sa puneti 0x02 in PCLATH.

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 ».

Este important sa intelegeti ca toate instructiunile « retlw » trebuie sa se gaseasca in


aceeasi pagina. In cazul precedent (tabelul mic), retineti ca exista deja o instructiune care se
gaseste in aceasta pagina, ceea ce face ca numarul de locatii disponibile sa fie mai mic cu o
unitate.
Am putea modifica tabelul in asa fel incit, primul element al tabelului sa indice un offset
« 0 » din pagina de 8 biti.

org 0x2FF ; adresa tabel (prima linie « retlw » = 0x300)


patrat
addwf PCL , f ; aduna W la PCL
retlw 8 ; primul element din tabel : adresa 0x300
retlw 4 ; al doilea element din tabel : adresa 0x301

Intotdeauna in cazul tabelelor de 256 de elemente, observam ca va trebui neaparat sa


incepem tabelul cu un offset 0 (PCL = 0). Este inutil deci sa facem o adunare cu « 0 », este
suficient sa punem direct W in PCL, oricare ar fi adresa din acesta, atita timp cit PCLATH a
fost corect configurat. Vom obtine deci :

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

18.7 Variabilele locale

Unul din reprosurile care se intilnesc adesea in legatura cu PIC®-urile, il constituie


numarul restrins de variabile disponibile. N-ar trebui totusi sa credem ca, sub pretextul ca
programam in limbaj de asamblare, nu dispunem de variabile locale.
In realitate, folosirea acestui tip de variabile este foarte simpla si reduce considerabil
numarul de variabile necesare intr-un program. Va voi explica aici metoda mea personala de
folosire a acestui tip de variabile.

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.

Ø Prima subrutina se numeste « tempo », si foloseste 2 numaratoare pentru a realiza o


contorizare a timpului.

Ø A doua, este subrutina « fonction ». Ea primeste un parametru in W, trebuie sa salveze


rezultatul intr-o variabila si foloseste alte 2 variabile pentru calculele sale
176
intermediare.

Ø Conditia pentru folosirea de variabile locale identice este ca nici una din subrutine sa
nu o apeleze pe cealalta.

18.7.1 Determinarea variabilelor locale

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.

18.7.2 Constructia fara 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

Observam ca vom avea nevoie de 5 variabile. Sa folosim acum variabile locale.

18.7.3 Constructia cu variabile locale

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 :

#DEFINE cmpt1 Local1 ; contor1 = variabila locala 1


#DEFINE cmpt2 Local2 ; contor2 = variabila locala 2
#DEFINE interm1 Local1 ; rezultat intermed1 = variabila locala 1
#DEFINE interm2 Local2 ; rezultat intermed2 = variabila locala 2

177
Si iata cum, fara sa avem nevoie de limbajul C, putem foarte simplu sa folosim variabile
locale.

18.8 Impartirea printr-o constanta

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

Iata-va in posesia citorva sugestii si trucuri de programare. Scopul a fost de a va arata


metodele de rationament, cu scopul de a va permite sa va dezvoltati propriile trucuri si
subtilitati.

19. Utilizarea de subrutine intr-un fisier separat

19.1 Intrebari si punct de plecare

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

Sa creem deci un fisier « mesroutines.inc » in care « ne vom prosti » scriind sub-programe


sub forma de cod sursa. De exemplu, sa ne imaginam ca dorim sa utilizam doua subrutine :
«aduna16biti» si «scade16biti». Vom scrie pur si simplu in codul sursa continutul celor doua
subrutine ale noastre :

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.

Aceasta, de exemplu, va duce la o eroare :

#include <mesroutines.inc> ; codul din « mesroutines.inc » este


; introdus la 0x00
ORG 0x00 ; ceea ce urmeaza trebuie sa fie tot la 0x00
Instructiunea 1 ; instructiunea la 0x00 = locatie deja
; ocupata
Instructiunea 2 ; instructiunea la 0x01 = locatie deja
; ocupata
...

De ce ? Pur si simplu pentru ca continutul fisierului « mesroutines.inc » va fi copiat la


adresa 0x00 (daca nu se precizeaza altfel, se face automat), si deci la adresa 0x00 vom avea
instructiunea 1 din « aduna16biti » cit si instructiunea 1 a programului principal.
Evident ca MPASM® nu poate sa puna doua instructiuni la aceeasi adresa si deci va aparea
179
o eroare de suprascriere pentru instructiunile de la adresa 0x00 si urmatoarele.

Va trebui deci sa punem « include » la locul potrivit, ca in exemplul urmator :

ORG 0x00 ; ce urmeaza se gaseste la 0x00


goto main ; instructiune la 0x00 : corect

ORG 0x04 ; vector de intrerupere


Tratareintrerup ; aici codul incepe la 0x04 : corect

main ; pornire corecta a programului dupa salt


Instructiunea 1
call aduna16biti ; apel de subrutina
...
#include <mesroutines.inc> ; codul mesroutines.inc se gaseste aici:
; corect

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

si “variabila1” va trebui sa fie declarata in programul « asm » sub amenintarea unei


erori care ne va spune ca « variabila 1 nu exista ».
Pe scurt, avantajul acestei metode este ca incluziunea este simplu de efectuat, neexistind
nicio complicatie deosebita.
Dar metoda are dezavantaje majore si anume :
• Atunci cimd creati un fisier de subrutine, tot continutul acestui fisier este « copiat »
in fisierul vostru « asm » si este deci asamblat. Drept urmare, voi puneti in
memoria program tot codeul continut in fisierul vostru « inc », chiar daca n-aveti
nevoie de toate rutinele care sunt incluse in el. Caz tipic : voi va faceti o biblioteca
matematica si intr-un program oarecare voua nu va trebuie decit o simpla adunare.

• Sunteti constrinsi in fisierul vostru « asm » sa declarati toate variabilele citate in


fisierul vostru « inc », chiar si daca nu folositi rutina care le utilizeaza.

• 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.

19.3 Incapsularea in macro-uri simple

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 :

m_aduna16biti macro ; incapsuleaza intr-un macro


instructiunea1
instructiunea 2
...
return
endm ; sfirsit de macro

m_scade16biti macro ; incapsuleaza intr-un macro


instructiunea 1
instructiunea 2
...
return
endm ; sfirsit de macro

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 :

#include <mesroutines.inc> ; se adauga declararea de macro-uri, nu cod


ORG 0x00 ; codul urmator incepe la 0x00

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 :

#include <mesroutines.inc> ; adaug declararea de macro


ORG 0x00 ; cod incepind de la 0x00

goto main ; salt la programul principal


ORG 0x04 ; vector de intrerupere

Tratareintrerup ; tratare intrerupere

main ; program principal


Instructiunea 1 ; instructiune oarecare
call aduna16biti ; apel de sub-program la eticheta data
...
...
aduna16biti ; eticheta = nume de sub-program
m_aduna16biti ; care contine codul macro-ului

Avantaje :

• Includeti fisierul vostru de macro-uri inca de la inceputul programului, neavind


importanta daca veti folosi sau nu macro-uri.

• Nu declarati decit variabilele efectiv folosite.

• Singurul cod efectiv necesar este prezent in fisierul asamblat, fara nicio pierdere de
memorie program.

Inconveniente :

• Trebuie tot timpul sa cunoasteti numele variabilelor folosite de macro-ul vostru,


plecind de la fisierul vostru sursa « asm ».

• Dac veti include macro-uri ce folosesc aceleasi nume de variabile, riscati aceleasi bug-
uri ca si la prima metoda.

Dar mai exista un mijloc de a trece aceste ultime obstacole :

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 :

m_aduna16biti macro var1,var2,result ; macro cu parametri


movf var1,w ; ne referim la parametri ca nume de variabile
addwf var2,w
...
...
movwf result
...
return
endm ; fin de macro

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 :

#include <mesroutines.inc> ; adaug declararea de macro


ORG 0x00 ; cod incepind de la 0x00
goto main ; salt la programul principal

ORG 0x04 ; vector de intrerupere


Tratareintrerup ; tratare intrerupere

main ; programul principal


Instructiunea 1 ; instructiune oarecare
call aduna16biti ; apel de sub-program la eticheta data
...
...
aduna16biti ; eticheta = nume de sub-program
_aduna16biti operand1,operand2,rezultat ; macro cu variabilele de
; folosit

Avantaje :

• Toate avantajele metodei precedente

• Nu mai e nevoie sa cunoastem numele variabilelor caci vom folosi propriile noastre
variabile

Inconveniente :

Nu mai exista niciun inconvenient, am impacat si capra si varza...

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.

20. Norma ISO7816

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.

20.1 Particularitati utile ale normei ISO7816

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.

19.1.1 Comenzile ISO7816

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 :

CLASA INSTRUCTIUNE PARAMETRUL1 PARAMETRUL2 LUNGIME

Sau, sub forma prescurtata :

CLASS INS P1 P2 LEN


Semnificatia fiecarui octet din comanda receptionata este :

Ø 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.

Ø P1 si P2 : sint 2 parametrii pe care cardul ii primeste impreuna cu comanda.


Semnificatia lor depinde de comanda trimisa. Ei pot sa nu fie folositi, dar totusi
trebuie sa fie prezenti.

Ø LEN : poate reprezenta 2 lucruri :


1) reprezinta numarul de octeti care urmeaza comenzii, in acest caz cardul va astepta sa
receptioneze LEN octeti suplimentari inainte de a trata comanda in totalitatea sa
2) reprezinta lungimea sirului de raspuns pe care masterul o va astepta sa o receptioneze
dinspre card. Rolul LEN va fi stabilit de catre instructiune.

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).

20.1.2 Protocolul schimbului de informatii

Iata in continuare cum se desfasoara un schimb de informatii intre master si cardul


ISO7816.

• La punerea in functiune, masterul genereaza un RESET pe pinul MCLR, cardul


raspunzind cu un anumit numar de octeti. Acest raspuns se numeste ATR, de la Answer
To Reset (raspuns la reset).
• Masterul trimite comanda CLASS INS P1 P2 LEN
• Cardul retrimite instructiunea care confirma receptia INS (ecou).
• Masterul trimite eventual datele suplimentare
• Cardul trimite un eventual raspuns
• Cardul trimite starea si revine in modul de asteptare de comenzi.

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.

20.2 Legaturile seriale asincrone

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.

Pentru a receptiona corect bitii transmisi, va trebui sa stabilim un protocol de comunicatie.


Acesta trebuie sa contina urmatoarele informatii :

• Viteza de transmisie in bauds


• Formatul, adica numarul de biti de start, de stop, de date si tipul de paritate.

Vom explica acum aceste concepte si vom arata care sint valorile folosite in norma
ISO7816.

20.2.1 Bitul de start

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

20.2.2 Bitii de date

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.

20.2.3 Bitul de paritate

Bitul de paritate este o verificare a bunei functionari a transferului. La emisie, se numara


toti bitii de date care sint in 1. La sfirsitul numararii, se adauga un bit 1 sau 0, in asa fel incit
sa se obtina un numar total de biti par sau impar.
Vom spune ca se foloseste o paritate para daca numarul de biti in 1 din bitii de date, plus
bitul de paritate este un numar par.
In acelasi mod, o paritate impara va da un numar impar de biti in 1.
Retineti faptul ca bitul de paritate nu este indispensabil intr-o legatura serie asincrona.
Vom avea deci 3 posibilitati de alegere : fara paritate, paritate para , sau paritate impara.
In cazul normei ISO7816, va trebui sa folosim o paritate para.

20.2.4 Bitul de stop

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.

20.2.5 Viteza si debitul

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 :

1s / 9600 = 104,17 µs.

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 :

1 bit de start + 8 biti de date + 1 bit de paritate + 2 biti de stop = 12 biti.

Timpul total pentru receptia unui octet va fi deci de 1250 µs.


Debitul maxim, exprimat in octeti pe secunda va fi deci egal cu :

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.

20.3 Achizitia bitilor

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.

Bitul 0 va fi deci citit dupa 93 + 46 impulsuri de tact de la inceperea bitului de start. In


continuare, vom putea citi toti bitii la intervale de timp echivalente cu 93 de instructiuni.

Odata citit bitul de paritate, avem doua posibilitati :

Ø 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.

19.4 Caracteristicile cardurilor « standard »

Pe cardurile disponibile in comert, de tip « card pentru incuietoare codata », conexiunile


folosie sint, in general, urmatoarele :

Nume pin Pin Semnal Rolul semnalului Tip


MCLR 4 RST Reset card Intrare
VSS 5 GND Masa Alimentare
Semnal de date pentru EEPROM
RB4 10 SDA Bidirectional
extern (24C16)
RB5 11 SCL Ceas pentru EEPROM extern Iesire
RB7 13 DATA Date mod serial, half-duplex Bidirectional
VDD 14 +5V Alimentare +5V Alimentare
CLKIN 16 CLK Intrare ceas extern Oscilator

20.5 Crearea si initializarea proiectului

Urmind metoda obisnuita, faceti Copy/Paste la fisierul « m16f84.asm » si redenumiti copia


« iso7816.asm ». Creati un proiect in MPLAB®. Dupa aceasta, creati antetul programului :

;******************************************************************
; 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 :

LIST p = 16F84 ; definire procesor


#include <p16F84.inc> ; definire constante
radix dec ; se lucreaza in zecimal (implicit)

__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

; protectie cod OFF


; timer reset la power-on activ
; watchdog inactiv
; oscilator cu cuart de mare viteza

Sa calculam acum valoarea registrului OPTION.


Va trebui sa folosim, din motive de compatibilitate electronica, rezistenta pull-up catre
+5V pe pinul RB7. Vom avea deci bitul b7 din registrul OPTION = 0.

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 :

OPTIONVAL EQU 0x08 ; valoare registru optiuni


; rezistente pull-up ON
; prescaler pe 1

20.6 Baza de timp

Observam deja ca avem nevoie de 2 temporizari : una de 93 de instructiuni si una de 93 +


46 de instructiuni, corespunzind la 1 si la 1.5 biti. Sa construim deci o subrutina.

;******************************************************************
; 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

Cum functioneaza aceasta rutina ? Remarcati ca aveti 2 puncte de intrare, temp_1bd si


temp_1b. Vom incepe prin examinarea acestui din urma punct.

Vom incepe prin incarcarea duratei corespunzatoare a 91 de instructiuni inainte de


depasirea timerului, adica 0 - 91, sau mai simplu, -91. Nu trebuie totusi sa uitam ca un anumit
numar de instructiuni au fost deja executate de la ultima rulare a rutinei timer, cind am
receptionat sau transmis bitul precedent (si ca pierdem 2 incrementari ale tmr0 atunci cind il
modificam).

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.

20.7 Receptia unui octet

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

Iata deci sub-programul nostru :

;******************************************************************
; Receptia unui octet provenind de la master *
;******************************************************************
;------------------------------------------------------------------
; Caracter citit in W. Nu se verifica paritatea
;------------------------------------------------------------------
Receptie
; asteapta bitul de start
; -----------------------

btfsc SERIAL ; testeaza daca avem bit de start


goto Receive ; nu, asteapta

; ne pozitionam in mijlocul primului bit util


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

call temp_1bd ; asteapta 1 bit si jumatate

; receptie caracter
; -----------------

movlw 0x8 ; vom avea de citit 8 biti


movwf cmptbts ; incarca in numaratorul de biti

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

; in acest moment sintem in centrul bitului de paritate ramine deci


; sa asteptam 1.5 biti pentru a ne situa in al doilea bit de stop
; --------------------------------------------------------------

call temp_1bd ; asteapta 1,5 biti


movf caract , w ; incarca caracterul citit
return ; si revenire

Acest sub-program nu contine dificultati deosebite. Remarcati ca in loc de a pune fiecare


bit receptionat direct in pozitia corecta, ii introducem in bitul b7 servindu-ne de o instructiune
« rrf ». Bitul precedent este "impins" in bitul b6, si asa mai departe. La sfirsit, primul bit se va
gasi deci in b0, ultimul raminind in b7, ceea ce de fapt am dorit sa obtinem.

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.

20.8 Emisia unui caracter

Sa nu uitam ca cardul noastra nu emite decit ca raspuns la o interogare din partea


« masterului ». Deci, sub-programul nostru de emisie va fi apelat dupa ce sub-programul
nostru de receptie a receptionat un octet.

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.

Un bun compromis, cit si o usurare a scrierii programului, ne permite sa alegem un timp de


asteptare de 1.5 biti inainte de a incepe sa emitem. Am ales aceasta valoare caci aceasta
temporizare permite initializarea timerului. Aceasta valoare nu este totusi critica. Trebuie doar
sa raspundem dupa ce « masterul » a trecut in mod receptie, insa inainte ca acesta sa considere
ca cardul n-a raspuns.

Programul nostru va trebui deci sa efectueze urmatorele operatii :

• Sa astepte timpul stabilit, inainte de emisie


• Sa treaca in mod emisie si sa trimita bitul de start
• Sa trimita cei 8 biti, incepind cu bitul b0
• Pentru fiecare bit « 1 » trimis, inverseaza paritatea, pentru a obtine o paritate para
• Sa trimita bitul de paritate
• Sa trimita cei 2 biti de stop
• Sa revina in mod receptie

Iata deci sub-programul nostru :

;******************************************************************
; 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

; transmite bitul de start

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 cei 8 biti de date


; ----------------------------
Send_loop
rrf caract , f ; decaleaza caracterul, b0 in carry
rrf caract , w ; carry in b7 din W
xorwf parite , f ; seteaza paritatea
xorwf PORTB , w ; pastreaza 1 daca se schimba SERIAL
andlw 0x80 ; nu se modifica decit RB7
xorwf PORTB , f ; daca da, inverseaza RB7
call temp_1b ; asteapta intre 2 biti
decfsz cmptbts , f ; decrementeaza contorul de biti
goto Send_loop ; daca nu e ultimul, urmatorul

; 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

; transmite cei 2 biti de stop


; ----------------------------
BANK1 ; se trece in bancul 1
bsf SERIAL ; repune SERIAL ca intrare (nivel 1)
BANK0 ; se trece in bancul 0
call temp_1b ; asteapta temporizarea intre 2 biti
call temp_1b ; asteapta temporizarea intre 2 biti
return ; si revenire

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.

O remarca asupra trasmiterii propriu-zise a bitului, adica aceste 3 instructiuni :

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 ».

Daca efectuam acum un « xorwf » intre PORTB si « W », cu rezultatul in PORTB, vom


inversa RB7 numai daca b7 din « W » este 1, sau cu alte cuvinte, vom inversa RB7 numai
daca valoarea sa actuala este diferita de valoarea pe care trebuie sa o transmitem.

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

20.10 Transmiterea ATR-ului

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

ATR DE 0x07 ; raspunsul ATR


DE 0xAB ; B7 01 BB AB 07
DE 0xBB
DE 0x01
DE 0xB7

20.11 Transmiterea starii

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.

Am inventat o stare fictiva pentru folosirea in acest exercitiu. Starea « 80 00 » va indica


faptul ca comanda a fost executata corect. Voi folosi deasemenea si starea « 60 40 » pentru a
indica faptul ca comanda nu exista.
Vom crea deci 2 sub-programe : unul care transmite starea standard, si un altul care
transmite o stare oarecare.

;******************************************************************
; 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

20.12 Receptia clasei

Acum, cardul nostru trece in mod receptie si asteapta prima comanda.


Programul nostru, din motive de simplitate, nu gestioneaza decit o singura clasa. Noi ne
multumim deci cu citirea octetului, fara a-l verifica si fara a-l trata. De fapt, este vorba doar de
197
un exercitiu didactic.

;******************************************************************
; 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

20.13 Receptia lui INS, P1, P2, si LEN

Sa examinam acum rutina noastra de receptie a instructiunii, a parametrilor P1 si P2, si a


lungimii sirului.

;******************************************************************
; 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.

20.14 Controlul instructiunii receptionate

Odata receptionata comanda, va trebui sa o tratam. In exemplul nostru didactic, am


implementat o singura comanda. Singura instructiune valida este 0x25.
Aceasta instructiune calculeaza suma dintre P1 si P2, si transmite rezultatul. Cum LEN
contine lungimea sirului de raspuns, daca LEN este mai mare decit 1, rapunsul va fi completat
cu 0xFF. Toate celelalte instructiuni vor fi considerate incorecte.

Iata testul nostru :

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

; trateaza instructiunea incorecta


; --------------------------------
movlw 0x40 ; incarca octet 2 status si trimite
movwf status2 ; pune in variabila
movlw 0x60 ; incarca octet 1 status
goto Statxx ; trimite status

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 ».

20.15 Tratarea unei instructiuni

Am ajuns acum la tratarea propriu-zisa a instructiunii. Vom trata aceasta instructiune in


felul urmator :

• Ca pentru orice instructiune, se retrimite instructiunea receptionata (ecou)


• Cardul trimite suma dintre P1 si P2
• Cadrul de transmis este completat cu 0xFF pentru a obtine o lungime totala a sirului de
date identica cu cea specificata in Ser_Len.
• Se transmite starea standard « 80 00 »

;******************************************************************
; 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

; testeaza lungimea raspunsului


; -----------------------------
decf Ser_Len , f ; caci rezultatul e deja trimis
btfsc STATUS , Z ; testeaza daca e complet

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

; trimite starea standard


; -----------------------------
goto Statstd ; trimite starea standard

20.16 Variabilele

Ne mai ramine sa declaram variabilele folosite. Am hotarit sa folosesc variabile locale


acolo unde este posibil:

;******************************************************************
; DECLARAREA VARIABILELOR *
;******************************************************************

CBLOCK 0x00C ; inceput zona variable


Ser_Ins : 1 ; instructiune ISO7816
Ser_P1 : 1 ; parametru 1 ISO7816
Ser_P2 : 1 ; parametru 2 ISO7816
Ser_Len : 1 ; lungime date ISO7816
local1 : 1 ; variabila locala 1
local2 : 1 ; variabila locala 2
local3 : 1 ; variabila locala 3
local4 : 1 ; variabila locala 4
temp_sauvw : 1 ; salvare W pentru temp
ENDC ; sfirsit zona

; rutina ATR
; ----------
#DEFINE cmpt1 local1 ; contor de octeti pentru ATR

; subrutine send si receive


; -------------------------
#DEFINE caract local2 ; caracter de trimis
#DEFINE parite local3 ; bit de paritate
#DEFINE cmptbts local4 ; contor de biti

; 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.

Am primit deasemenea o multime de scrisori de la cititori care s-au crezut "avizati" sa


inceapa lectura cursului cu acest capitol. Nu trebuie sa punem carul inaintea boilor, si nu
intimplator acest capitol este pus la sfirsitul cursului, caci am considerat ca este neaparat
necesara intelegera mai intii a notiunilor de dinainte.

Anexa 1 : Intrebari frecvente (F.A.Q.)

Am sa incerc in continuare sa raspund la niste intrebari puse frecvent de catre cititori.

A1.1 Consider ca 8 sub-programe este putin

Atentie, nu trebuie sa confundati numarul de sub-programe cu numarul de imbricatii de


sub-programe. Numarul de sub-programe este nelimitat (sau in limita memoriei disponibile).
Avem o imbricatie, atunci cind un sub-program apeleaza un alt sub-program. Pentru a
numara nivelele de imbricare, urmariti desfasurarea programului vostru in ordinea de
executie. Scrieti (+1) pentru fiecare instructiune « call » intilnita si (-1) pentru fiecare
instructiune « return » sau « retlw ».
Daca programul vostru este corect, trebuie sa fie indeplinite cele 3 conditii care urmeaza :

• Trebuie ca niciodata numararea sa nu devina negativa


• Nu trebuie niciodata sa depasim 8 in cursul numararii
• La sfirsitul executiei programului, trebuie sa se ajunga din nou la 0.

A1.2 N-am folosit decit 8 imbricatii, si totusi programul meu se


blocheaza

Nu trebuie sa uitati faptul ca si intreruperile folosesc stiva. Daca folositi intreruperile, nu


mai aveti dreptul decit la 7 nivele de imbricare (fara subrutinele folosite eventual de rutina de
intrerupere).

A1.3 Programul meu pare ca nu mai iese niciodata din intreruperi

Ati uitat sa stergeti flag-ul care a provocat intreruperea.

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.

A1.5 Primesc un mesaj de eroare EOF inaintea instructiunii END

Verificati daca fisierul vostru contine directiva END.


Daca nu sinteti in cazul acesta, deschideti fisierul vostru « .asm » intr-un editor de text din
Windows si verificati daca este corect, cu asezarea corecta a liniilor. Daca nici aici nu gasiti
probleme, inseamna ca fisierul a fost scris cu un editor care nu genera corect CR-ul.
In acest caz, incercati sa faceti un Copy/Paste a acestui fisier, intr-un fisier gol, creat cu un
alt editor.

A1.6 Cum dezasamblez un fisier « .hex » ?

Doresc sa va descriu 2 metode pentru dezasamblarea unui fisier in format hexazecimal.


Prima metoda foloseste MPLAB®-ul :

1. Mergeti in meniul « File => Import »


2. Selectati fisierul « .hex » pe care doriti sa-l dezasamblati
3. Selectati « View => Program memory »
4. Alegeti tab-ul care corespunde dorintei dvs.

A doua metoda foloseste IC-Prog, celebrul utilitar de programare a PIC®-urilor, disponibil


pretutindeni :

1. Incarcati fisierul « .hex » din meniul « File => Open file »


2. Selectati « View => Assembler ». Asta-i tot.

A1.7 Folosirea minusculelor si majusculelor

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 :

• Selectati « Project => Build options => Project »


• Alegeti tab-ul « MPASM assembler »
• Bifati casuta « Disable case sensitivity »

A1.8 Alegerea unui programator

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.

A1.9 Am o eroare de « stack »

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 » ».

Ce inseamna aceste mesaje ?

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)

A1.10 Care sint diferentele intre 16F84 si 16F84A ?

Iata o intrebare care revine cu regularitate printre scrisorile mele. Va dau aici principalele
deosebiri dintre aceste componente :

• Frecventa maxima disponibila creste de la 10 la 20 MHz.


• Tensiunea maxima pentru Vdd este scazuta de la 6V la 5.5V.
• Resetul necesita o durata a impulsului de 2µs pe MCLR, in loc de 1µs
• Timpul de crestere al tensiunii de alimentare este luat in calcul pentru pornire
• Curentul tipic de programare a flash-ului scade de la 7.3 la 3 mA
• Curent tipic consumat dublu pentru un 16F84A la 10 MHz fata de un 16F84 la 20 MHz
• Mai multe modificari ale caracteristicilor electrice (tensiuni nivele logice, curenti, etc.)
• Divizare prin 2.5 a timpilor de scriere si stergere a EEPROM-ului
• Divizare prin 2.5 a timpilor de scriere si stergere in memoria flash.

Nimic in ceea ce priveste programarea, sint diferente numai la nivel electric.

A1.11 Am o eroare 173 in cursul asamblarii

In momentul lansarii asamblarii, obtineti o eroare 173 in genul :

Error[173] Source file path exceeds 62 characters

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

Realizarea acestor cursuri mi-a luat o multime de timp si de investitii (documentatii,


materiale, abonamente, etc.).
Deasemenea, pentru a putea continua, si daca apreciati ceea ce fac, va rog, si asta va sta in
putinta, sa contribuiti si voi un pic, fiecare dupa posibilitatile si dorintele sale.

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

Lucrarea prezenta este destinata usurarii intelegerii programarii PIC®-urilor in general, si a


lui 16F84 in special. Adoua parte, consacrata restului gamei via 16F876 este disponibila pe
site-ul meu.
Comunicati autorului (politicos) toate erorile constatate, in scopul indreptarii lor in folosul
tuturor.
Webmasterilor care mi-au cerut, le-am acordat permisiunea de a pune pe site-urile lor
prima parte a cursului, in vederea download-ului. Multumesc tuturor celor care au jucat
cinstit.
Totusi, unii nu au facut-o, si nu si-au actualizat in mod regulat site-ul, in functie de noile
versiuni. Acest lucru duce la primirea regulata de mail-uri de la persoane care imi semnaleaza
erori corectate de mult timp, sau utilizatori care constata cu ciuda ca au tiparit o versiune
veche a cursului. Tinind cont de numarul de pagini, aceasta nu face placere la toata lumea.

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.

Prezentul document poate fi folosit de catre toti, si copiat integral, cu conditia de a nu


modifica nimic. El poate fi totodata folosit ca suport de curs in intregime sau in parte, cu
conditia de a preciza sursa originala si legatura catre site-ul meu.

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.

Sa stiti ca eu raspund intotdeauna corespondentei primite, insa retineti urmatoarele :

• 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.

• Daca aveti aplicatii personale, nu ezitati sa le impartasiti tuturor. Pentru aceasta, mi le


puteti trimite mie prin email.

• Cu aceasta versiune, am incercat sa raspund cererilor legitime ale persoanelor care


lucreaza pe diferite platforme (Mac, Linux, Windows, etc.). Daca totusi, versiunea
furnizata nu functioneaza pe masina voastra, spuneti-mi. Retineti ca acest curs foloseste
pentru exercitii MPLAB®, deci va trebui eventual sa adaptati aceste exercitii in functie de
softul pe care probabil il folositi.

Multumesc webmasterului de la www.abcelectronique.com, pentru gazduirea gratuita.


Multumesc lui Byte, pentru propunerea sa de gazduire gratuita.
Multumesc lui Grosvince, pentru propunerea sa de gazduire gratuita.
Multumesc lui Thierry, pentru ca mi-a oferit numele de domeniu.
Multumesc lui Bruno, pentru reluarea platilor din 2007.

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 2 la 15/06/2001 : Corectia citorva erori

• Actualizare editia 3 la 24/07/2001 : Corectie detaliata cu Fribottes

• Actualizare editia 4 la 26/10/2001 : Citeva mici erori gasite

• Actualizare editia 5 la 27/02/2002 : Inca citeva erori corectate

• Actualizare editia 6 la 20/04/2002 : Citeva corectrii, imbunatatiri ale punctelor care


suscita intrebari frecvente, trecerea in format PDF si comprimare "rar" pentru a permite
folosirea pe toate masinile, masculinizarea termenului « PIC® »

• Actualizare editia 7 la 22/09/2002 : Citeva corecturi sintactice si ortografice minore,


citeva adaugiri.

• Actualizare editia 8 la 25/10/2002 : Mici corecturi la capitolul 2.4. Enuntul citat in


exemplu nu corespunde celui dezvoltat efectiv. Corectia link-ului catre site-ul Microchip
la pagina 27. Modificari ale definitiilor CISC si RISC din pagina 17.

• 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 11 la 19/04/2003 : Modificare registre modificate penru instructiunea


"sleep" la pagina 74, adaugarea programatorului in lista de materiale necesare, modificari
de definitii la pagina 123, corecturi minore la pagina 70.

• Actualizare editia 12 la 16/06/2003 : Corecturi minore la paginile 108, 198

• Actualizare editia 13 la 19/09/2003 : Modificari majore pentru trecerea de la MPLAB®


5.50 la MPLAB® IDE 6.30. Schimbare numerotare, numeroase modificari in capitole
intregi. Necesita retiparire completa, fisiere exemplu reconstruite.

• Actualizare editia 14 la 11/12/2003 : Modificari in schema de la pagina 134, corecturi in


paginile 201, 190, 191 (modificare radix ca urmare a trecerii de la MPLAB 5 la
MPLAB 6).

• Actualizare editia 15 la 13/02/2004 : Corectia unei erori repetitive in exemple si curs, la


nivelul initializarii EEADR (schimbare de banc o linie mai tirziu), paginile 96, 99, 123,
142 si fisiere exemplu si machete.

• Actualizare editia 16 la 09/03/2004 : Corecturi la paginile 58, 59

• 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 18 la 02/08/2004 : Adaugarea timpilor de trezire la pagina 171,


observatie la pagina 113

• 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 20 le 03/04/06 : Adaugat un mic capitol despre consumul minim in


mod sleep.

• Actualizare editia 21 le 13/10/06 : Corecturi minore (sublinieri, paranteze, orthografie…)


paginile 19, 25, 39, 40, 41, 47, 51, 52, 53, 54, 57, 61, 67, 68, 69, 71, 72, 76, 80, 81, 86,
98, 118, 120, 128, 152, 178, trecerea numerelor H’00xx‘ pe 8 biti. Adaugarea unei
observatii la pagina 44. Revizia 21b, repune la loc schema optocuplorului, care disparuse.

• Actualizare editia 22 le 24/01/07 : Modificari importante in tot ceea ce trateaza tmr0 ca


urmare a faptului ca s-a uitat luarea in considerare a celor 2 cicluri pierdute datorita
tuturor modificarilor lui tmr0. Modificari si adaptari in paginile 29, 44, 52, 95 inclusiv in
fisierele asm implicate. Diverse corecturi minore (tabulatii, spatii, punctuatie,
orthografie...), mica modificare in pagina 54 privind simulatorul (explicatia ramasese la
versiunea 5). Modificare pagina 21 la nivelul explicarii mentiunii « -xx » pe care am uitat
sa o adaptez diverselor experiente efectuate pe aceasta tema.

• Actualizare editia 23 le 04/11/07 : Adaugarea unei liste de consideratii asupra watchdog-


ului, in capitolul 15.3. Corecturi ortografice a sensului frazei la paginile 16, 23, 34, 37,
44, 61, 64, 79, 83, 86, 89, 91, 96, 108, 111, 112, 113, 114, 120, 124, 128, 138.
Modificarea unei cai la pagina 38. Precizari asupra termenului « setat » in paginile 27, 43,
50, 54, 60, 110. Corecturi asupra numarului de biti in pagina 12. Decalarea unor numere
de pagina. La cererea amicala a Microchip®, am adaugat simbolul ® pentru toti termenii
aprtinind Microchip®.

• Actualizare editia 24 le 08/02/08 : Modificari minore in pagina 47. Inlocuirea sistemului


de numerotatie prin numeratie. Refacere completa a capitolelor de la 12.7.2 la 12.7.4
(adaugari) implicind renumerotarea tuturor paginilor urmatoare.

• Actualizare editia 25 le 07/09/08 : Corectura importanta la pagina 140.

• Actualizare editia 26 le 08/02/09 : Corecturi minore la paginile 50 si 63. Adaugarea


resetului explicit a flagului de intrerupere de la intreruperea EEPROM.

• Actualizare editia 27 le 15/02/09 : Precizari privind timpul de latenta al intreruperilor de


la pagina 109.

• Actualizare editia 28 le 24/02/09 : Adaugarea unui paragraf despre unitati, adoptarea


definitiva a Kibi in loc de o simpla observatie.

• 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).

• Actualizare editia 30 le 13/10/10 : Ca urmare a abandonarii versiunii « 2 » a PicKit® si


ICD®, modificare sfaturi in anexa A1.8

• Actualizare editia 31 le 28/11/10 : observatii modificate in pagina 148. Adaugarea unui


capitol privind utilizarea de rutine intr-un fisier separat.

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

Site : http://www.abcelectronique.com/bigonoff (multumesc gazdei)

Domeniu : www.bigonoff.org (multumesc lui Thierry)


Email : [email protected] (Atentie, BIGOCOURS nu BIGONOFF)

Tradus : DUMITRESCU DANUT, Bacau – Romania

IANUARIE 2011

210

You might also like