Sunteți pe pagina 1din 88

Mihaela HNATIUC Cătălin Constantin POMAZAN

Microcontrolerul PIC16F84
Aplicații practice

Editura
N A U T ICA
Mihaela HNATIUC Cătălin Constantin POMAZAN

Microcontrolerul PIC16F84
Aplicații practice

Editura
N A U T ICA
Copyright © 2015, Editura NAUTICA
Toate drepturile asupra acestei ediţii sunt rezervate Editurii

Coperta: Cătălin Constantin POMAZAN

Editura NAUTICA, 2015


Editură recunoscută de CNCSIS
Str. Mircea cel Bătrân nr.104
900663 Constanţa, România
tel.: +40-241-66.47.40
fax: +40-241-61.72.60
e-mail: info@imc.ro
www.edituranautica.org.ro

Descrierea CIP a Bibliotecii Naţionale a României

MIHAELA HNATIUC
CĂTĂLIN CONSTANTIN POMAZAN
MICROCONTROLERUL PIC 16F84. APLICAŢII PRACTICE

Constanţa : Nautica, 2015


ISBN: 978-606-681-047-0
Cuprins

Laborator 1.......................................................................................................................................1
Microcontrolerul PIC16F84, platforma experimentală Z11/EV
și mediul de dezvoltare MPLAB X

Laborator 2.....................................................................................................................................17
Instrucțiuni pentru transferul de date

Laborator 3.....................................................................................................................................26
Instrucțiuni aritmetice și logice
1. Lumină dinamică cu sens prestabilit............................................................................29
2. Lumină dinamică cu sens modificabil..........................................................................32
3. Aprinderea selectivă a LED-urilor folosind butoanele PS1 și PS2................................34
4. Utilizarea butoanelor PS1 și PS2 pe post de comutator..............................................36
5. Utilizarea unui singur buton pe post de comutator....................................................37

Laborator 4.....................................................................................................................................39
Instrucțiuni de salt condiționat și necondiționat
1. Comanda releelor cu ajutorul butoanelor PS1 și PS2..................................................44
2. Citirea tastaturii matriciale și utilizarea afișorului cu șapte segmente........................46
3. Afișarea tastei apăsate pentru o durată prestabilită...................................................49

Laborator 5.....................................................................................................................................51
Întreruperi
1. Controlul intensității luminoase folosind tehnica PWM..............................................55
2. Utilizarea TMR0 și a sistemului de întreruperi............................................................57
3. Comanda unui buzzer..................................................................................................58
4. Alarmă electronică.......................................................................................................60

Laborator 6.....................................................................................................................................62
Interfațarea cu afișajul alfanumeric
1. Afișarea unui text pe două rânduri..............................................................................68
2. Cronometru digital cu afișajul LCD 2x16 caractere......................................................70

Laborator 7.....................................................................................................................................73
Memoria EEPROM. Citirea și scrierea de date
1. Tastatură cu memorie permanentă.............................................................................75

Bibliografie.....................................................................................................................................79
CUVÂNT ÎNAINTE

Această lucrare se adresează studenților de la secțiile Electrotehnică și Telecomunicații și


reprezintă o introducere practică în programarea automatelor programabile ce conțin
microcontrolere. Materialul cuprinde șapte lucrări de laborator ce includ o parte teoretică și una
aplicativă pentru asimilarea conceptelor de bază și stilului de lucru specific microcontrolerului
PIC16F84 cu ajutorul platformei experimentale Z11/EV produsă de Elettronica Veneta.

Autorii
Microcontrolerul PIC16F84 – Aplicații practice

LABORATOR 1
Microcontrolerul PIC16F84,
platforma experimentală Z11/EV
și mediul de dezvoltare MPLAB X

Scopul lucrării
Lucrarea de laborator prezintă succint microcontrolerul PIC16F84, platforma experimentală
Z11/EV produsă de Elettronica Veneta care va fi utlizată În aplicațiile de laborator precum și
mediul de dezvoltare integrat MPLAB X cu care vor fi scrise secvențele de cod pentru
microcontroler.

Breviar teoretic
Un microcontroler este un dispozitiv electronic ce include într-un singur integrat toate
dispozitivele specifice unui sistem cu microprocesor cum ar fi:
• unitate centrală de calcul – ce interpretează şi execută instrucţiunile programului
• memorie PROM (Programabile Read-Only Memory) – ce memorează permanent
instrucţiunile programului rulat
• memorie RAM (Random Access Memory) – ce păstrează temporar variabilele de lucru
ale programului
• linii de intrare / ieşire – pentru comanda unor dispozitive externe sau citirea
informaţiilor de la senzori, butoane, etc.
PIC16F84 este un microcontroler cu arhitectură RISC (Reduced Instruction Set Computing)
construit pe o arhitectură Harvard cu instrucțiuni pe 14 biți și magistrală de date de 8 biți.

1
Microcontrolerul PIC16F84 – Aplicații practice

Fig. 1: Diagrama pinilor pentru PIC16F84 [1]

Diagrama pinilor pentru acest microcontroler conform [1] este reprezentată în fig.1, unde:

OSC1/CLKIN intrare oscilator cuarț/intrare ceas extern

OSC2/CLKOUT ieșire oscilator cuarț; conectat la rezonatorul cu cristal de cuarț în mod


oscilator; în mod oscilator RC este ieșirea de tact (1/4 din frecvența OSC1)

MCLR master clear (reset)/intrare pentru tensiunea de programare

PORTA este un port bidirecțional de intrare/ieşire (I/O) de 5 biţi

RA0 pin de intrare/ieşire programabil

RA1 pin de intrare/ieşire programabil

RA2 pin de intrare/ieşire programabil

RA3 pin de intrare/ieşire programabil

RA4/T0CKI pin de intrare/ieşire programabil; poate fi selectat ca intrare de tact pentru


numărătorul TMR0

PORTB este un port bidirecțional de intrare/ieşire (I/O) de 8 biţi

RB0/INT pin de intrare/ieşire programabil; poate fi programat ca intrare pentru


întreruperi externe

RB1 pin de intrare/ieşire programabil

RB2 pin de intrare/ieşire programabil

RB3 pin de intrare/ieşire programabil

RB4 pin de intrare/ieşire programabil; generează o întrerupere la schimbarea


valorii pinului

2
Microcontrolerul PIC16F84 – Aplicații practice

RB5 pin de intrare/ieşire programabil; generează o întrerupere la schimbarea


valorii pinului

RB6 pin de intrare/ieşire programabil; generează o întrerupere la schimbarea


valorii pinului / tact pentru programare serială

RB7 pin de intrare/ieşire programabil; generează o întrerupere la schimbarea


valorii pinului / date pentru programarea serială

VSS masa de referință

VDD plus tensiune de alimentare


Cele 35 de comenzi din setul de instrucțiuni al microcontrolerului PIC16F64 sunt reprezentate
pe 14 biți și sunt executate într-un singur ciclu mașină cu excepția instrucțiunilor de salt JUMP și
BRANCH.

Fig. 2: Schema bloc a microcontrolerului PIC16F84 [1]

Un ciclu mașină este executat în patru impulsuri ale semnalului de ceas. Aceasta înseamnă că
pentru o frecvență a semnalului de ceas de 20 MHz se obține o durată de 200 ns pe ciclu
instrucțiune.
Memoria PROM (Programmable Read-Only Memory) pentru stocarea programului are

3
Microcontrolerul PIC16F84 – Aplicații practice

capacitatea de 1024 cuvinte de 14 biți (1k cuvânt). Conținutul acesteia este programabil printr-o
procedură specială și nu se șterge la întreruperea alimentării microcontrolerului.
Memoria RAM (Random Access Memory) de date este împărțită în două zone distincte. Prima
dintre ele conține octeții ce configurează modul de funcționare al microcontrolerului. Aceștia
poartă denumirea de regiștri cu funcții speciale (Special Function Registers). A doua zonă
conține octeții ce constituie memoria de uz general. Aceștia poartă numele de regiștri de uz
general (General Purpose Registers).
Viteza de scriere / citire a memoriei RAM este ridicată, dar conținutul acesteia se pierde la
întreruperea alimentării.

Fig. 3: Organizarea memoriei RAM pentru PIC16F84 [1]

Pentru a putea accesa un spațiu de adresare mai mare, memoria RAM este organizată în două

4
Microcontrolerul PIC16F84 – Aplicații practice

bancuri. Fiecare banc permite adresarea unui număr de 128 de locații. Primele 12 locații din
fiecare banc sunt rezervate pentru regiștrii cu funcții speciale SFR. Restul locațiilor reprezintă
regiștrii de uz general GPR. Adresele primelor 68 de locații GPR din al doilea banc sunt mapate
în primul banc (conținutul lor este comun pentru ambele bancuri).Pentru stocarea permanentă
a unor informații, microcontrolerul dispunde și de o memorie EEPROM (Electrically Erasable
Programmable Read-Only Memory) cu o capacitate de 64 de octeți. Această memorie poate fi
accesată numai prin adresare indirectă. Viteza de scriere în această memorie este mult mai
lentă decât a memoriei RAM, dar conținutul se păstrează și după întreruperea alimentării.
Pentru interfațarea cu exteriorul PIC16F84 dispune de 13 pini de intrare/ieșire organizați în
două porturi: PORTA (5 biți) și PORTB (8 biți). Semnalele pe pinii de intrare/ieșire sunt reflectate
de conținutul regiștrilor PORTA și PORTB. La un moment dat fiecare dintre acești pini poate fi
doar intrare sau doar ieșire. Configurarea ca intrari sau ieșiri se face individual, utilizând regiștrii
cu funcții speciale TRISA și TRISB. Pentru fiecare bit din PORTA și PORTB îi corespunde un bit în
TRISA și TRISB ce stabilește modul de funcționare ca intrare sau ieșire pentru pinul asociat.
PIC16F84 dispune de un modul temporizator/numărător de 8 biți denumit TMR0 a cărui
conținut ce poate fi scris și citit. Acesta poate fi configurat să folosească drept intrare un semnal
de tact generat intern sau extern și dispune de un circuit divizor (prescaler) de 8 biți
programabil. Pentru semnalul de tact extern se poate configura utilizarea frontului crescător sau
descrescător și depășirea capacității de stocare pentru TMR0 poate genera o întrerupere.
Pe lângă TMR0 microcontrolerul dispune de încă un circuit temporizator independent WDT
(Watchdog Timer) programabil ce funcționează independent pe baza unui semnal de ceas
generat intern. Acesta este de regulă utilizat pentru resetarea microcontrolerului dacă acesta se
blochează din cauza diverselor condiții de funcționare.
Microcontrolerul PIC16F84 permite utilizarea a patru tipuri de întreruperi:
• apariția unui semnal pe pinul RB0/INT
• depășirea generată de TMR0
• schimbarea semnalelor la pinii RB4 - RB7
• terminarea inscripționării în memoria EEPROM
Detalii suplimentare referitoare la structura internă și funcționarea microcontrolerului PIC16F84
vor fi prezentate în cadrul lucrărilor de laborator următoare în funcție de specificul acestora.
Instrucțiunile recunoscute de microcontrolerul PIC16F84 pot fi grupate în funcție de
funcționalitatea lor în:
• instrucțiuni de transfer de date în memoria internă de date, memoria program sau stivă
• instrucțiuni de transfer de date în memoria externă
• instrucțiuni aritmetice

5
Microcontrolerul PIC16F84 – Aplicații practice

• instrucțiuni logice pe bit și octet


• instrucțiuni de lucru cu subrutine
• instrucțiuni de salt condiționat și necondiționat
Instrucțiunile sunt memorate în cuvinte de 14 biți si sunt executate în unul sau două cicluri
mașină. În funcție de structura lor acestea pot fi clasificate în:
• instrucțiuni cu regiștri la nivel de octet
• instrucțiuni cu regiștri la nivel de bit
• instrucțiuni literale (cu valori exprimate explicit) și de control
Locațiile de memorie (octeții) din memoria de date poartă și denumirea de regiștri (regiștrii cu
funcții speciale și regiștrii de uz general).
Instrucțiunile cu regiștri ce lucrează la nivel de octet au următoarea structură:

OPCODE = codul instrucțiunii (6 biți)


d = bit ce specifică registrul destinație
0 = registrul de lucru W (registrul acumulator)
1 = destinația este un alt registru decât W
adr. registru = adresa pe 7 biți a registrului de uz general
Aceste instrucțiuni se referă în general la operații pe care trebuie să le execute UAL (Unitatea
Aritmetică și Logică). Atunci când instrucțiunea implică doi regiștri, unul dintre ei este registrul
de lucru W (registrul acumulator), iar celălalt este o locație de memorie (registru) specificată în
cadrul instrucțiunii. Bitul d specifică dacă rezultatul prelucrării va fi memorat în registrul de lucru
W sau în registrul cu adresa specificată în adr. registru.
Instrucțiunile cu regiștri ce lucrează la nivel de octet au următoarea structură:

OPCODE = codul instrucțiunii (4 biți)


bit = poziția bitului asupra căruia se acționează (3 biți)

6
Microcontrolerul PIC16F84 – Aplicații practice

adr. registru = adresa pe 7 biți a registrului ce conține bitul vizat


Instrucțiunile de control și cele literale (cu valori exprimate explicit) au două forme
asemănătoare:

OPCODE = codul instrucțiunii (6 sau 3 biți)


valoare = valoarea operandului (8 sau 11 biți)
Programarea microcontrolerului se poate face în limbaj de asamblare. Acesta reflectă fidel setul
de instrucțiuni al microcontrolerului dar prezintă dezavantajul că pentru fiecare microcontroler
trebuie asimilat setul specific de instrucțiuni. Acest impediment poate fi evitat dacă se utilizează
pentru programare în locul limbajului de asamblare un limbaj de nivel înalt cum este C. Codul
sursă scris în C este mai simplu de înțeles și gestionat dar reprezentarea binară a acestuia este
mai mare decât reprezentarea binară a codului sursă scris în limbaj de asamblare pentru același
algoritm. Din acest motiv, pentru aplicațiile ce trebuie să răspundă în timp real, când viteza de
reacție este critică, chiar dacă scrierea codului sursă este mai greoaie, se preferă limbajul de
asamblare.
În lucrările de laborator ce urmează, codul sursă va fi prezentat atât în limbaj de asamblare cât și
în C pentru a putea compara diferențele între cele două variante.
Pentru varianta scrierii codului sursă în limbaj de asamblare vor fi folosite următoarele notații
convenționale:
Pentru notarea modului de adresare în tabelele cu instrucțiuni:
1 – adresare directă
2 – adresare indirectă
3 – adresare la registru
4 – adresare imediată
Pentru descrierea formală a instrucțiunii:
s – registru sursă
d – registru destinație
() – referire la conținutul unui registru

7
Microcontrolerul PIC16F84 – Aplicații practice

(()) – referire la conținutul unei locații de memorie a cărei adresă se află memorată într-
un registru
data8 – valoare reprezentată pe 8 biți
data16 – valoare reprezentată pe 16 biți
← – asignare
↑ – concatenare

Materiale didactice utilizate


Toate lucrările de laborator prezentate în această lucrare folosesc platforma experimentală
Z11/EV pentru microcontrolerul PIC16F84 produsă de Elettronica Veneta.
Schema electronică completă a acestei platforme se regăsește în Anexa 1. Schemele de
conectare incluse în lucrările de laborator reprezintă porțiuni simplificate ale acestei platforme
experimentale.

Fig. 4: Platforma experimentală Z11/EV

Platforma experimentală include ca periferice un afișor LCD 2 linii x 16 caractere, un afișor cu


șapre segmente, o tastatură cu 4 x 4 taste, 13 LED-uri, două butoane, două relee, un buzzer și o
interfață serială RS-232. Placa mai conține registre tampon și jumperi pentru configurarea în
patru variante diferite a perifericelor la microcontroler:
• J1 conectează tastatura matricială și afișorul cu șapte segmente
• J2 conectează afișorul LCD
• J3 conectează LED-urile, butoanele PS1, PS2, releele RL1 și RL2 și buzzerul
• J4 conectează portul serial

8
Microcontrolerul PIC16F84 – Aplicații practice

În partea din dreapta jos, platforma experimentală mai include și un programator serial pentru
microcontrolerul PIC16F84. Pentru salvarea programului în memoria PROM microcontrolerul
trebuie plasat în soclul programatorului. După încheierea programării microcontrolerului, acesta
trebuie mutat în soclul central al plăcii experimentale pentru efectuarea lucrărilor de laborator.
Pentru alimentarea cu energie electrică este folosită sursa PSLC produsă tot de Elettronica
Veneta ce permite obținerea tensiunilor de ±5V și ±12V necesare plăcii Z11/EV.

Desfășurarea lucrării
Pentru scrierea programelor este utilizat mediul integrat de dezvoltare MPLab X produs de
Microchip (lucrarea de față face referire la versiunea 2.26). Codul sursă pentru lucrările de
laborator este prezentat în limbaj de asamblare și/sau în C. Mediul integrat de dezvoltare
(Integrated Development Environment) oferă suport nativ pentru limbajul de asamblare
(MPASM versiunea 5.59), pentru limbajul C fiind necesară instalarea suplimentară a
compilatorului MPLAB XC8 (lucrarea de față face referire la versiunea 1.33 a acestuia).

Fig. 5: Fereastra principală a mediului de dezvoltare integrată MPLab X

Pe lângă instrumentele necesare scrierii și compilării codului sursă acest IDE conține module
pentru depanarea codului, editarea biților de configurare ai microcontrolerelor, simularea
funcționării și monitorizarea memoriei acestora, accesarea cu ușurință a foilor de catalog
referitoare la microcontrolerele produse de Microchip și a altor materiale de informare. Pentru
accesarea acestora este necesară o conexiune la internet.

9
Microcontrolerul PIC16F84 – Aplicații practice

MPLab X este un mediu complex de dezvoltare și din acest motiv vor fi prezentate succint în
continuare doar etapele necesare scrierii codului sursă și compilării acestuia pentru
microcontrolerul PIC16F84.
În fereastra principală a IDE-ului (fig.5) vom observa în partea superioară meniul general și o
bandă cu butoane pentru comenzile frecvente. În partea centrală stânga vor fi afșate informații
referitoare managementul de proiect, gradul de ocupare al memoriei microcontrolerului cu care
se lucrează și alte informații utile legate de acestea. Regiunea centrală cea mai mare conține
inițial numeroase comenzi pentru crearea sau deschiderea unui nou proiect, accesarea unor
tutoriale referitoare la MPLab X și a altor resurse on-line. Tot în această zonă se va face și
editarea codului sursă. În partea de jos a ferestrei IDE-ului se află linia de stare ce va afișa în
funcție de context diverse informații referitoare la starea curentă a mediului de dezvoltare.
Orice aplicație scrisă pentru un microcontroler trebuie inclusă într-un proiect.
Crearea proiectului poate fi inițializată de pe pagina de start folosind comanda Create New
Project, cu ajutorul butonului specific din bara de unelte sau din meniul principal folosind File ->
New Project... (fig.6).

Fig. 6: Inițializarea creării unui proiect

Aceasta inițializează asistentul pentru crearea proiectului (fig.7).

Fig. 7: Stabilirea categoriei și tipului de proiect

10
Microcontrolerul PIC16F84 – Aplicații practice

Vom alege pe prima pagină a asistentului Microchip Embedded și Standalone Project și


continuăm cu butonul Next.

Fig. 8: Selectarea microcontrolerului

Pagina a doua a asistentului (fig.8) permite selectarea microcontrolerului căruia îi este dedicat
proiectul. La Device vom selecta PIC16F84 și apoi continuăm cu butonul Next.

Fig. 9: A treia pagină a asistentului pentru proiecte

MPLab X permite conectarea cu mai multe tipuri de programatoare și unelte hardware produse
de Microchip. A treia pagină a asistentului pentru proiecte (fig.9) permite selectarea unui
echipament hardware din această categorie. Deoarece placa experimentală Z11/EV este
echipată cu propriul programator ce nu este compatibil cu MPLab X, la Hardware Tools vom
selecta Simulator și vom continua cu butonul Next.

11
Microcontrolerul PIC16F84 – Aplicații practice

Fig. 10: Selectarea compilatorului

Pe pagina a patra a asistentului (fig.10) trebuie selectat compilatorul cu care vom lucra. Pentru
proiectele scrise în limbaj de asamblare vom selecta mpasm, iar pentru proiectele scrise în
limbajul C vom selecta XC8 și vom continua cu butonul Next.

Fig. 11: Stabilirea numelui și locației proiectului

În final (fig.11), trebuie specificat numele proiectului și directorul în care acesta va fi salvat.
Foarte important pe această pagină este să bifăm și opțiunea Set as main project. Aceasta
determină considerarea proiectului nou adăugat ca proiect principal. Finalizarea creării
proiectului se face apăsând pe butonul Finish.
Ca urmare a acestor operațiuni, în fereastra Projects va apare structura de directoare a
proiectului creat. Adăugarea unui fișier sursă în cadrul proiectului se poate realiza în mai multe

12
Microcontrolerul PIC16F84 – Aplicații practice

feluri. O variantă este utilizarea comenzii File -> New File... din meniul aplicației. Aceasta
conduce la deschiderea unei ferestre de asistență în care trebuie selectat tipul fișierului adăugat
(fig.12). Pentru adăugarea unui fișier sursă în asamblare vom alege categoria Assembler și tipul
de fișier AssemblyFile.asm. Pentru adăugarea unui fișier sursă C vom selecta categoria C și C
Source File la tipul fișierului. în continuare se apasă butonul Next.

Fig. 12: Selectarea tipului de fișier sursă

Pe a doua pagină a asistentului trebuie specificat numele și locația fișierului sursă (fig. 13).
Finalizarea adăugării se face cu butonul Finish.

Fig. 13: Specificarea numelui și locației fișierului sursă

Fișierul sursă adăugat va apare în cadrul proiectului și va fi deschis automat pentru editare.
După scrierea codului sursă, compilarea acestuia se poate face fie cu comanda Run -> Clean and

13
Microcontrolerul PIC16F84 – Aplicații practice

Build Main Project din meniul principal, fie cu ajutorul butonului asociat din zona cu comenzi
rapide de sub meniul principal. Ca urmare a acestor operațiuni, în directorul în care a fost salvat
proiectul vor fi create mai multe subdirectoare suplimentare în care vor fi salvate diversele
fișiere generate la compilare (fig.14).

Fig. 14: Structura logică și structura de directoare a unui proiect în MPLAB X

Dacă operațiunea de compilare a decurs fără erori, în subdirectorul dist\default\production al


proiectului se va regăsi un fișier având același nume cu proiectul ce are extensia hex. Acest fișier
trebuie programat în memoria internă a microcontrolerului.

Fig. 15: Meniul pentru utilizarea simulatorului

Pe lângă facilitățile pentru scrierea codului sursă, MPLAB X prezintă numeroase funcții utilitare

14
Microcontrolerul PIC16F84 – Aplicații practice

pentru urmărirea funcționării aplicației și depanarea codului sursă. Una dintre acestea este
simulatorul care permite analiza semnalelor la pinii microcontrolerului. Componentele acestuia
se pot activa din din meniul Window -> Simulator al mediului integrat de dezvoltare (fig.15).
Simulatorul permite modelarea unor semnale discrete pentru microcontrolerele din familia PIC
și dsPIC produse de Microchip și dispune de un generator de semnal și de un analizor logic.
Pentru programarea fișierului hex generat prin compilarea codului sursă în memoria
microcontrolerului, programatorul de pe placa experimentală Z11/EV trebuie conectat cu
ajutorul unui cablu de comunicații seriale la portul serial al calculatorului pe care a fost compilat
proiectul. În continuare va fi lansat utilitarul IC-Prog (fig.16) pentru încărcarea în memoria
PROM a microcontrolerului a fișierului hex.
După deschiderea IC-Prog se încarcă fișierul hex cu comanda File -> Open File... din meniul
principal. Se verifică să fie selectat microcontrolerul PIC16F84 în combo box-ul din dreapta sus a
ferestrei aplicației și în secțiunea Configuration se selectează XT pentru Oscillator și se șterg
toate bifele de la Fuses.
Se verifică poziționarea corectă a microcontrolerului în soclul programatorului (pinul 1 trebuie
să fie poziționat în stânga sus). În continuare se folosește comanda Command -> Program All din
meniul principal al utilitarului IC-Prog sau butonul asociat din bara de comenzi rapide. În finalul
operațiunii de programare va fi afișat un mesaj de confirmare a finalizării corecte sau un mesaj
de eroare.

Fig. 16: Utilitarul IC-Prog

15
Microcontrolerul PIC16F84 – Aplicații practice

Trebuie specificat faptul că utilitarul IC-Prog nu poate fi lăsat deschis dacă se dorește
modificarea codului sursă a proiectului inițial deoarece fișierul hex este blocat de către acesta,
iar compilatorul nu poate rescrie fișierul în aceste condiții.

16
Microcontrolerul PIC16F84 – Aplicații practice

LABORATOR 2
Instrucțiuni pentru transferul de date

Scopul lucrării
Lucrarea de laborator prezintă instrucțiunile de transfer de date specifice microcontrolerului
PIC16F84 și exemple practice de utilizare a acestora. Studenții vor dobândi cunoștințe de
utilizare a porturilor microcontrolerului cu ajutorul secvențelor de cod scrise atât în limbaj de
asamblare și în C.

Breviar teoretic
Instrucțiunile de transfer de date folosesc drept sursă și destinație registrul acumulator și ceilalți
regiștrii (regiștrii cu funcții speciale și cei de uz general). Microcontrolerul PIC16F84 nu are
instrucțiuni de transfer cu stiva. Tabelul 1 prezintă instrucțiunile de transfer de date la nivel de
octet și descrierea lor formală. În acest tabel cu w este simbolizat registrul acumulator, cu f este
simbolizat un registru de uz general, K este o valoare numerică imediată, iar d reprezintă
direcția transferului de date atunci când este vorba de instrucțiunea MOVF.
Mnemonic Descriere formală Timp de Indicatori
execuție (cicli) afectați
MOVF f,d d=0: (w) ← (f) 1 Z
d=1: (f) ← (w)
MOVWF f (f) ← (w) 1 Z
MOVLW K (w) ← K 1 -
CLRF f (f) ← 0 1 Z
CLRW (w) ← 0 1 Z
SWAPF f,d f(7:4) ← f(3:0) 1 -
f(3:0) ← f(7:4)
Tabelul 1: Instrucțiuni pentru transferul de date ([1])

17
Microcontrolerul PIC16F84 – Aplicații practice

Registrul acumulator (Working Register) este utilizat în majoritatea instrucțiunilor pentru


transferul de data la nivel de octet.
În tabelul 2 sunt prezentate instrucțiunile ce acționează la nivel de bit pentru PIC16F84. Cu f
este simbolizat registrul asupra căruia se acționează, iar cu b este notat bitul afectat.
Mnemonic Descriere formală Timp de Indicatori
execuție (cicli) afectați
BCF f,b (f.b) ← 0 1 -
BSF f,b (f.b) ← 1 1 -
Tabelul 2: Instrucțiuni pentru operațiuni la nivel de bit ([1])

Pe lângă registrul acumulator, registrul de stare STATUS este deasemenea foarte important în
funcționarea microcontrolerului. Biții acestuia semnalizează starea în care se află unitatea
aritmetică și logică a microcontrolerului după executarea fiecărei instrucțiuni, biții pentru
selecția bancului de menorie utilizat și starea semnalului Reset (fig. 17).
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
IRP RP1 RP0 TO PD Z DC C
Fig. 17: Structura registrului STATUS

Semnificația biților este următoarea:


C (Carry) - bit de transfer care este afectat la operaţii aritmetice adunare, scădere şi
transfer. Poate fi scris sau citit și este afectat automat de unele instrucțiuni (ADDF,
ADDLW, RRF, RLF).
DC (Digit carry) - bit de transfer de la bitul 4 spre 5 în cadrul registrului acumulator. Este
afectat la aceleaşi operaţii ca şi bitul C. Poate fi scris sau citit.
Z (Zero) - bit care indică atunci când are valoarea 1 logic faptul că a fost obținut
rezultatul zero pentru operaţia aritmetică efectuată. Poate fi scris sau citit.
PD (Power-down) - bit ce indică starea alimentării microcontrolerului. După fiecare reset
obişnuit şi după executarea instrucţiunii CLRWDT valoarea sa este 1. Instrucţiunea SLEEP
îl resetează când microcontrolerul intră în regimul consum redus. Setarea lui repetată
este posibilă prin reset sau prin pornirea sau oprirea sursei. Starea sa poate fi modificată
de asemenea de un semnal la pinul RB0/INT, de o schimbare la portul RB, de terminarea
scrierii în EEPROM-ul de date intern, şi de watchdog de asemenea. Acest bit poate fi
doar citit.
TO (Time-out) - bit ce indică depăşirea timpului configurat pentru watchdog. Watchdog
Timer este un registru temporizator ce funcționează independent de restul
microcontrolerului și permite scoaterea acestuia din starea de blocare dacă a trecut o
anumită perioadă de timp. În mod normal bitul TO este setat pe 1 logic la pornirea

18
Microcontrolerul PIC16F84 – Aplicații practice

microcontrolerului sau la execuția instrucțiunilor CLRWDT şi SLEEP. La depășirea valorii


maxime a temporizatorului watchdog, bitul TO este resetat pentru a semnaliza faptul că
microcontrolerul s-a blocat. Acest eveniment poate fi configurat să reseteze
microcontrolerul. Bitul poate fi doar citit.
RP1:RP0 (Register Bank Select) - biţi pentru selecţia bancurilor de registre din memoria
RAM. Aceşti doi biţi sunt utilizați ca biții cei mai semnificativi ai adresei la adresarea
directă. Pentru că instrucţiunile ce adresează memoria direct au doar şapte biţi, ele au
nevoie doar de încă un bit pentru a adresa cei 256 octeţi ai memoriei interne pentru
PIC16F84. De aceea producătorul copului recomandă menținerea bitului RP1 pe 0. Biții
pot fi citiți şi scriși.
IRP (Register Bank Select) - bit de selecţie a adreselor folosit la adresarea indirectă a
memoriei RAM. PIC16F84 nu utilizează acest bit și de aceea producătorul cipului
recomandă menținerea acestuia pe 0. Poate fi scris şi citit.
Comunicarea microcontrolerului cu mediul exterior se realizează prin intermediul porturilor. Cea
mai simplă aplicație pentru exemplificarea utilizării acestora este programarea
microcontrolerului pentru aprinderea și stingerea LED-urilor conectate la portul B.
PIC16F84 are 13 pini ce pot fi utilizați ca intrări sau ieșiri digitale. Aceștia sunt grupați în două
porturi – Port A (5 pini) și Port B (8 pini). Pentru citirea sau scrierea la aceste porturi sunt
rezervați doi regiștri cu funcții speciale: PORTA și respectiv PORTB.
Pentru a putea specifica modul de utilizare al porturilor fie ca intrări fie ca ieșiri este necesară
folosirea unor registre suplimentare, câte unul pentru fiecare port în parte TRISA pentru PORTA
și TRISB pentru PORTB. Pentru fiecare pin al unuia dintre porturi există în registrul TRIS asociat
un bit ce specifică dacă pinul este utilizat ca intrare sau ieșire digitală. De exemplu dacă registrul
TRISB conține FFh pinii PORTB sunt configurați ca intrări iar dacă conținutul registrului este
TRISB este 00h atunci pinii PORTB sunt ieșiri. Pentru TRISA și PORTA sunt luați în considerare
numai primii 5 biți mai puțin semnificativi.
Limbajul de asamblare folosește mnemonice pentru a simboliza instrucțiunile mașină cunoscute
de microprocesor și valorile numerice atribuite variabilelor și constantelor. În acest context, un
mnemonic reprezintă o etichetă ușor de memorat asociată unei instrucțiuni mașină sau unei
locații de memorie în care este memorată o variabilă, sau a unei valori numerice constante
explicite.
Un program scris în limbaj de asamblare este compus dintr-o suită de declarații. Sintaxa unui
astfel de program trebuie să respecte câteva reguli simple:
• fiecare declarație este fie o instrucțiune fie o directivă de asamblare
• pe o linie poate fi scrisă o singură declarație
• o instrucțiune mașină (op-code) este reprezentată printr-un mnemonic

19
Microcontrolerul PIC16F84 – Aplicații practice

• fiecare mnemonic simbolizează o singură instrucțiune mașină


• fiecare instrucțiune conține o instrucțiune mașină și eventual unul sau doi operanzi
• operanzii sunt chiar datele ce trebuie prelucrate
Directivele de asamblare sunt instrucțiuni specifice asamblorului cu care se face conversia din
cod sursă în cod binar executabil.
Fiecare linie a codului sursă poate conține etichete, mnemonice și directive pentru asamblor,
operanzi, comentarii. Ordinea și amplasarea acestora în cadrul liniei este importantă. Lungimea
maximă a unei linii de cod este de 256 de caractere. Practic, o linie de cod este împărțită în
patru zone. Prima zonă poate conține doar etichete sau dacă nu, este lăsată necompletată. A
doua zonă poate conține mnemonicele instrucțiunilor microcontrolerului sau directivele pentru
asamblor. Zona a treia poate conține operanzii instrucțiunilor separați prin virgulă. A patra zonă
poate conține comentarii ce sunt obligatoriu precedate de punct și virgulă (;). Separarea între
zone se face utilizând cel puțin un spațiu.
Etichetele sunt utilizate pentru a marca amplasarea unei linii de cod sau pentru a înlocui o
valoare numerică. Întotdeauna acestea trebuie amplasate începând cu prima coloană a unei linii
de cod și se termină cu două puncte (:) sau spațiu. Etichetele nu pot începe cu cifre și pot avea
maxim 32 de caractere. În mod implicit, este important la utilizarea lor să se țină seama de
scrierea cu majuscule și minuscule. Cu alte cuvinte ele trebuie scrise de fiecare dată la fel pentru
a putea fi recunoscute corect.
Mnemonicile și directivele pentru asamblor nu țin seama de scrierea cu majuscule sau
minuscule. Directivele pentru asamblor trebuie totuși precedate de semnul diez (#). Dacă nu
este specificată o etichetă, mnemonicile și directivele pentru asamblor trebuie amplasate
începând cu coloana a doua sau o coloană mai mare. Dacă linia de cod conține și o etichetă,
eticheta trebuie terminată cu semnul două puncte (:).
Operanzii sunt întotdeauna amplasați după mnemonica instrucțiunii și sunt separați de aceasta
prin unul sau mai multe spații. Dacă instrucțiunea conține mai mulți operanzi, separarea lor se
face utilizând semnul virgulă (,).
Comentariile sunt întotdeauna precedate de sumnul punct și virgulă (;). Tot ceea ce urmează pe
linie de la acest semn către dreapta este considerat comentariu.
Directivele sunt comenzi pentru asamblor ce nu sunt de obicei transformate în instrucțiuni
mașină. Acestea sunt utilizate pentru a controla modul de funcționare al asamblorului.
Directivele nu țin seama de majuscule sau minuscule. Există directive cu denumiri și format
diferit ce au aceeași funcționalitate. Utilizarea lor este acceptată pentru compatibilitatea cu
standardele vechi de scriere a codului. Iată câteva directive uzuale recunoscute de asamblorul
MPASM ce este integrat în MPLAB X:
constant – directiva este utilizată pentru a crea constante ce pot fi utilizate în cadrul

20
Microcontrolerul PIC16F84 – Aplicații practice

codului sursă; constantele nu pot fi modificate ca valoare în cadrul codului

constant aprins = 1 ; eticheta aprins primeste valoarea numerica 1

#define – directiva este utilizată pentru definirea unui text de substituire pentru eticheta
specificată; la compilarea codului sursă la fiecare apariție a etichetei asamblorul va
înlocui denumirea acesteia cu textul asociat acesteia

#define prag 0x25 ; eticheta prag va fi inlocuita cu valoarea 0x25

end – directiva specifică terminarea codului sursă


equ – directiva permite definirea unei constante, de obicei pentru etichetarea unei
adrese din memoria RAM atunci când codul sursă este scris într-un singur fișier

LED equ 0x03 ; eticheta LED reprezinta valoarea 0x03

if ... else ... endif – directivele permit definirea unor zone de cod ce vor fi incluse în
funcție de valoarea logică a expresiei scrise după if; sunt acceptate și alternativele #if ...
#else ... #endif; directiva else poate lipsi

versiune equ 6

if versiune == 5

movlw 0x01
movwf PORTB

else

movlw 0x02
movwf PORTA

endif

ifdef ... else ... endif – directivele sunt asemănătoare cu if ... else ... endif cu diferența că
se ține seama doar de faptul că a fost specificat cu directiva #define un anumit simbol;
sunt acceptate și alternativele #ifdef ... #else ... #endif

#define debug

ifdef debug

movlw 0x01
movwf PORTB

21
Microcontrolerul PIC16F84 – Aplicații practice

else

movlw 0x02
movwf PORTA

endif

ifndef ... else ... endif – directivele sunt asemănătoare cu ifdef ... else ... endif cu
diferența că este inclusă secvența ce urmează după ifndef doar dacă simbolul nu a fost
definit; sunt acceptate și alternativele #ifndef ... #else ... #endif
include – directiva permite includerea unui fișier sursă

include p16f84.inc ; include fisierul cu definitiile pentru PIC16F84

org – directiva permite specificarea adresei la care va fi amplasat în memorie codul


asociat secvenței de instrucțiuni ce urmează acestei directive

org 0x0000 ; codul ce urmeaza incepe la adresa 0000

processor – directiva permite specificarea microcontrolerului căruia îi este dedicat codul


sursă; această directivă nu este obligatorie dacă proiectele sunt create cu MPLAB X
deoarece tipul microcontrolerului este definit în proprietățile proiectului

processor pic16f84

set – directiva permite atribuirea la o etichetă a unei expresii valide

lungime set 0x05


latime set 0x02
arie set lungime * latime

#undefine – directiva anulează declararea făcută cu #define

#undefine debug

variable – directiva este utilizată pentru a crea variabile ce pot fi utilizate în cadrul
codului sursă; variabilele pot fi modificate în cadrul codului; variabilele pot primi sau nu
valori inițiale

variable aprins = 1
variable arie

22
Microcontrolerul PIC16F84 – Aplicații practice

while ... endw - directiva permite repetarea secvenței de cod intercalate între while și
endw atâta timp cât expresia scrisă după while este adevărată; repetarea nu se face la
runtime (în timpul execuției programului), ci codul sursă este copiat de mai multe ori
inainte de compilare

variable i = 0

while i<3
movlw i
i += 1
endw

Pentru a introduce secvențe de cod scrise în limbaj de asamblare în cadrul programelor scrise în
limbajul C se pot utiliza directivele preprocesor #asm ... #endasm. Variabilele declarate în C pot
fi utilizate în cadrul secvenței scrise în limbaj de asamblare.

int a = 1;
#asm
movlw _a
addlw 0x10
movwf PORTB
#endasm

Desfășurarea lucrării
Pornind de la schema electronică din fig.18 ce reprezintă o porțiune simplificată din schema
plăcii experimentale Z11/EV, va fi realizat programul pentru aprinderea a două LED-uri atât în
limbaj de asamblare cât și în C. Se observă din schema electronică faptul că LED-urile sunt
conectate la portul B al microcontrolerului, și că pentru aprinderea acestora pinii trebuie să fie
în starea 1 logic. Vor fi utilizate doar LED-urile conectate la pinii RB0 și RB1.
Pentru funcționarea corectă a plăcii experimentale Z11/EV jumperul de configurare va fi pus pe
poziția J3.
Pentru ambele variante de scriere a codului sursă va fi utilizat mediul integrat de dezvoltare
MPLAB. După compilare, fișierul binar va fi încărcat în memoria program a microcontrolerului
folosind utilitarul IC-Prog conform procedurii detaliate în cadrul laboratorului 1.

23
Microcontrolerul PIC16F84 – Aplicații practice

Fig. 18: Schema electronică de conectare a LED-urilor la microcontroler


Programul scris în asamblor pentru aprinderea LED-urilor conectate la pinii RB0 și RB1 ai
microcontrolerului este:

include p16f84.inc ; include fisierul cu definitiile pentru PIC16F84


LED equ 0x03 ; stabileste LED-urile ce vor fi aprinse (0x03 = 00000011)

org 0x0000 ; adresa unde incepe programul 0000h


bsf STATUS,RP0 ; selecteaza bancul 1
movlw 0x00 ; incarca constata 0 în acumulator
movwf TRISB ; seteaza pinii PORTB ca iesiri
bcf STATUS,RP0 ; selecteaza bancul 0
loop
movlw LED ; se incarca valoarea LED în acumulator
movwf PORTB ; transfera valoarea din acumulator in registrul PORTB
goto loop

end ; marcarea terminarii codului sursa

Programul scris în C pentru aprinderea LED-urilor conectate la pinii RB0 și RB1 ai


microcontrolerului este:

#include<pic.h>

void main(){
TRISB=0x00; // seteaza pinii PORTB ca iesiri
while(1){ // bucla infinita
PORTB=0x03; // seteaza in 1 logic pinul 0 si 1 al portului B
}
}

24
Microcontrolerul PIC16F84 – Aplicații practice

Întrebări și aplicații
1. Scrieți în limbaj de asamblare și în C o aplicație pentru aplinderea alternativă a LED-urilor
(primul aprins, următorul stins, al treilea aprins, șamd). Programul va fi executat în buclă
infinită. Ce se observă?
2. La ce adresă se găsește PORTB și TRISB?
3. Care este rolul registrului TRISB? Dar al registrului PORTB?

25
Microcontrolerul PIC16F84 – Aplicații practice

LABORATOR 3
Instrucțiuni aritmetice și logice

Scopul lucrării
Lucrarea de laborator prezintă instrucțiuni aritmetico-logice și exemple de subrutine realizate cu
acestea. La sfârșitul laboratorului studenții vor avea noțiuni de programare a operațiilor
aritmetice și logice pentru microcontrolerul PIC16F84 atât în C cât și în limbaj de asamblare.

Breviar teoretic
Instrucțiunile aritmetice specifice limbajului de asamblare pentru microcontrolerul PIC16F84 se
regăsesc în tabelul 3. Rezultatul acestor instrucțiuni este salvat în registrul acumulator sau într-
un registru de uz general specificat în cadrul instrucțiunilor.
Mnemonic Descriere formală Timp de Indicatori
execuție (cicli) afectați
ANDWF f,d d=1: (f) ← (w) AND (f)
1 Z
d=0: (w) ← (w) AND (f)
ANDLW k (w) ← k AND (w) 1 Z
ADDWF f,d d=1: (f) ← (w) + (f)
1 C, DC, Z
d=0: (w) ← (w) + (f)
ADDLW k (w) ← k + (w) 1 C, DC, Z
SUBWF f,d d=1: (f) ← (w) - (f)
1 C, DC, Z
d=0: (w) ← (w) - (f)
SUBLW k (w) ← k - (w) 1 C, DC, Z
IORWF f,d d=1: (f) ← (w) OR (f)
1 Z
d=0: (w) ← (w) OR (f)
IORLW k (w) ← k OR (w) 1 Z

26
Microcontrolerul PIC16F84 – Aplicații practice

Mnemonic Descriere formală Timp de Indicatori


execuție (cicli) afectați
XORWF f,d d=1: (f) ← (w) XOR (f)
1 Z
d=0: (w) ← (w) XOR (f)
XORLW k (w) ← (w) XOR k 1 Z
INCF f,d d=1: (f) ← (f) + 1
1 Z
d=0: (w) ← (f) + 1
DECF f,d d=1: (f) ← (f) - 1
1 Z
d=0: (w) ← (f) - 1
RLF f,d
1 C
d=1: (f) ← RL(f)
d=0: (w) ← RL(f)
RRF f,d
1 C
d=1: (f) ← RR(f)
d=0: (w) ← RR(f)
COMF f,d d=1: (f) ← (f)
1 Z
d=0: (w) ← (f)
Tabelul 3: Instrucțiuni atitmetice și logice în limbaj de asamblare ([1])

În limbajul C aceste instrucțiuni sunt realizate cu ajutorul operatorilor aritmetici și logici


(tabelurile 4-8).
Operator Descriere Exemplu
+ adunare a=b+7
- scădere a=b-3
* înmulțire a=b*2
/ împărțire a=b/4
% restul împărțirii întregi a=b%2
Tabelul 4: Operatori aritmetici

Operator Exemplu Expresie echivalentă


+= a += 7 a=a+7
-= a -= 3 a=a-3
*= a *= 2 a=a*2
/= a /= 4 a=a/4
%= a %= 2 a=a%2
Tabelul 5: Operatori de atribuire aritmetică

27
Microcontrolerul PIC16F84 – Aplicații practice

Opera Descriere Exemplu Expresie echivalentă


tor
++ incrementare ++a
a=a+1
a++
-- decrementare --a
a=a-1
a--
Tabelul 6: Operatori de incrementare și decrementare

Operator Descriere Exemplu


> mai mare b>a
>= mai mare sau egal a >= 5
< mai mic a<b
<= mai mic sau egal a <= b
== egal a == 6
!= diferit a != b
Tabelul 7: Operatori relaționali

Operator Descriere Exemplu


~ complement a = ~b
<< deplasare spre stânga a = b << 2
>> deplasare spre dreapta a = b >> 2
& AND c=a&b
| OR c=a|b
^ XOR c=a^b
Tabelul 8: Operatori pe bit

Materiale didactice utilizate


Pentru realizarea lucrării de laborator sunt necesare:
• platforma experimentală Z11/EV cu microcontrolerul PIC16F84
• calculator personal cu sistem de operare Windows
• mediul integrat de dezvoltare MPLAB X
• utilitarul IC-Prog
• cablu serial de date

28
Microcontrolerul PIC16F84 – Aplicații practice

• sursa de alimentare PSLC/EV (±5V și ±12V) pentru Z11/EV și cablurile aferente

Desfășurarea lucrării
Un exemplu simplu de cod scris în limbaj de asamblare pentru ilustrarea unei operațiuni simple
de adunare a două valori numerice 35 (0x23) și 68 (0x44) este următorul:

movlw 0x44 ; incarca valoarea 68 în acumulator


movf 0x80, 1 ; salveaza aceasta valoare la adresa 0x80
movlw 0x22 ; incarca valoarea 35 în acumulator
addwf 0x80, 1 ; aduna la acumulator valoarea de la adresa 0x80
; rezultatul adunarii este salvat la adresa 0x80

În limbajul C această secvență poate fi scrisă cu o singură instrucțiune:


int a = 35 + 68;
Operațiunile aritmetice și logice sunt prezente în majoritatea aplicațiilor prezentate în
continuare.
În cadrul acestei lucrări de laborator vor fi implementați diverși algoritmi pentru aprinderea și
stingerea automată sau la comandă a LED-urilor de pe placa experimentală Z11/EV. Pentru
aceasta jumperul de configurare al plăcii Z11/EV va fi pus pe poziția J3.

1. Lumină dinamică cu sens prestabilit


Schema electronică simplificată cu ajutorul căreia este implementată o lumină dinamică cu sens
prestabilit folosind cele 8 LED-uri ale plăcii experimentale Z11/EV este reprezentată în fig.19.

Fig. 19: Schema electronică de conectare a LED-urilor la microcontroler

29
Microcontrolerul PIC16F84 – Aplicații practice

Algoritmul pentru realizarea luminii dinamice cu sens prestabilit este redat în fig.20 în două
variante, în funcție de sensul dorit pentru deplasarea luminii.
Pentru realizarea întârzierilor există mai multe metode practice. În scop didactic, în cadrul
acestei aplicații temporizările sunt obținute software prin folosirea unor bucle cu contor.
Valoarea maximă pe care o poate avea variabila contor determină durata temporizării. În
aplicațiile ulterioare vor fi prezentate și alte modalități practice pentru obținerea unor
temporizări mai precise în cadrul algoritmilor.

Fig. 20: Algoritmul pentru lumină dinamică cu sens fix (deplasare spre stânga și deplasare spre dreapta)

Programul scris în limbaj de asamblare pentru deplasarea luminii dinamice de la dreapta spre
stânga (de la bitul 0 către bitul 7 al portului B) conform algoritmului reprezentat în prima
schemă logică din fig.20 este următorul:

include p16f84.inc ; include fisierul cu definitiile pentru PIC16F84

COUNT_1 equ 0x0C ; variabila folosita pentru temporizare


COUNT_2 equ 0x0D ; variabila folosita pentru temporizare
IESIRE equ 0x0E ; variabila ce va memora pozitia LED-ului aprins

30
Microcontrolerul PIC16F84 – Aplicații practice

; Initializare registri
init: org 0x0000
movlw 0x00
movwf TRISB
movlw 0xFF
movwf COUNT_1
movlw 0xFF
movwf COUNT_2
movlw 0x01
movwf IESIRE

; Program principal
main: movf IESIRE,0
movwf PORTB
call delay
bcf STATUS,C ; sterge bitul CARRY din registrul STATUS
rlf IESIRE ; deplaseaza spre stanga bitul in variabila IESIRE
btfss STATUS,C ; test bit de stare CARRY
goto main
movlw 0x01
movwf IESIRE
goto main

; Rutina de temporizare
delay: decfsz COUNT_1,1
goto delay
movlw 0xFF
movwf COUNT_1
decfsz COUNT_2,1
goto delay
movlw 0xFF
movwf COUNT_1
movlw 0xFF
movwf COUNT_2
return

end

Programul în C pentru deplasarea luminii dinamice de la dreapta spre stânga conform


algoritmului reprezentat în prima schemă logică din fig.20 este următorul:

#include <pic.h>
// procedura de temporizare
void delay(){
for(int i=0;i<5000; i++);
}

void main() {
TRISA = 0xFF; // portul A este programat ca intrari

31
Microcontrolerul PIC16F84 – Aplicații practice

TRISB = 0x00; // portul B este programat ca iesiri


PORTB = 0x01; // LED-ul RB0 aprins, restul stinse

// variabila in care se memoreaza deplasarea bitului este initializata cu 1


char a=1;

while(1){
delay();
a=a<<1; // deplasare spre stanga

// reinitializarea variabilei a daca bitul a "iesit" din octet


if(a==0) a=1;
PORTB=a;
}
}

Cele două variante ale programului (în limbaj de asamblare și în C) pot fi modificate cu ușurință
pentru a realiza deplasarea spre dreapta a luminii dinamice (de la bitul 7 către bitul 0 al portului
B).
Se remarcă diferența lungimii codului sursă între varianta scrisă în limbaj de asamblare și cea
scrisă în C.

2. Lumină dinamică cu sens modificabil


Pentru a permite modificarea la cerere a sensului de deplasare a luminii dinamice pot fi utilizate
butoanele PS1 și PS2 de pe placa experimentală Z11/EV. Schema electronică simplificată pentru
această configurație este reprezentată în fig.21.

Fig. 21: Lumină dinamică cu sens modificabil

Se poate remarca din această schemă că apăsarea butoanelor PS1 și PS2 pune la masă pinii RA4
și respectiv RA3 ai portului A. Cu alte cuvinte, pentru detectarea apăsării acestor butoane este
necesară testarea valorii 0 logic la pinii RA4 și RA3 ai portului A ce trebuie configurați ca intrări

32
Microcontrolerul PIC16F84 – Aplicații practice

digitale.
Deplasarea luminii se face doar atunci când unul dintre butoane este apăsat. Dacă nici unul
dintre butoane nu este apăsat, sau dacă ambele butoane sunt apăsate, un singur LED va fi
aprins.
Schema logică a algoritmului pentru realizarea acestei aplicații este prezentată în fig.22.

Fig. 22: Algoritmul pentru lumină dinamică cu sens modificabil

Programul scris în C pentru implementarea acestui algoritm este:

#include <pic.h>

// procedura de intarziere
void delay(){
for(int i=0;i<5000; i++);

33
Microcontrolerul PIC16F84 – Aplicații practice

void main() {
TRISA=0xFF; // configureaza PortA ca intrari
TRISB=0x00; // configureaza PortB ca iesiri
char a=0b00000001; // variabila a memoreaza octetul ce va fi scris in PortB

while(1){
PORTB=a; // scrie a in PortB
delay(); // apeleaza functia de intarziere

// daca este apasat PS1 deplaseaza bitii spre stanga


if(RA4==0){
a=a<<1;
if(a==0) a=0b00000001; //reinitializare LSB daca bitul a "disparut"
}

// daca este apasat PS2 deplaseaza bitii spre dreapta


if(RA3==0) {
a=a>>1;
if(a==0) a=0b10000000; // reinitializare MSB daca bitul a "disparut"
}
}
}

3. Aprinderea selectivă a LED-urilor folosind butoanele PS1 și PS2


Folosind aceeași schemă electronică din fig.21 poate fi realizată o aplicație cu funcționalitate
diferită. Algoritmul de funcționare este prezentat în fig 23.
Aplicația permite aprinderea a patru LED-uri (cele asociate biților mai puțin semnificativi ai
portului B: RB0 – RB3) la apăsarea butonului PS1 și a celorlalte patru LED-uri la apăsarea
butonului PS2 (cele asociate biților mai semnificativi ai portului B: RB4 – RB7). LED-urile vor fi
aprinse numai atâta timp cât butonul asignat acestora este apăsat. Se observă din schema logică
faptul că apăsarea unuia dintre butoane nu afectează funcționalitatea celuilalt.
Codul poate fi cu ușurință modificat pentru aprinderea unor alte combinații de LED-uri la
apăsarea fiecărui buton. De exemplu, la apăsarea butonului PS1 pot fi aprinse doar LED-urile
impare, iar la apăsarea butonului PS2 pot fi aprinse doar LED-urile pare.
Ca și la lucrarea anterioară a fost preferată scrierea codului sursă în limbajul C pentru că această
formă este mai scurtă și mai simplu de citit decât varianta în limbaj de asamblare.

34
Microcontrolerul PIC16F84 – Aplicații practice

Fig. 23: Algoritmul pentru aprinderea selectivă a LED-urilor

Codul sursă scris în C este următorul:

#include <pic.h>

void main(){
TRISA=0xFF; // configureaza PortA ca intrari
TRISB=0x00; // configureaza PortB ca iesiri
while(1){
if(RA4==0)
PORTB=PORTB | 0xF0; // aprinde LED-urile MSB
else
PORTB=PORTB & 0x0F; // stinge LED-urile MSB

if(RA3==0)
PORTB=PORTB | 0x0F; // aprinde LED-urile LSB
else
PORTB=PORTB & 0xF0; // stinge LED-urile LSB
}
}

Practic aplicația testează permanent stările butoanelor PS1 și PS2 și în funcție de acestea
aprinde sau stinge LED-urile asociate.

35
Microcontrolerul PIC16F84 – Aplicații practice

4. Utilizarea butoanelor PS1 și PS2 pe post de comutator


Folosind schema electronică simplificată din fig.22 poate fi realizată o aplicație ce simulează
funcționarea unui comutator pentru aprinderea și stingerea LED-urilor conectate la portul B al
microcontrolerului.
În această situație, la apăsarea butonului PS1 LED-urile vor fi aprinse și vor rămâne aprinse chiar
și după elibeararea butonului. Pentru stingerea lor se folosește în aceeași manieră butonul PS2.
Algoritmul aplicației este prezentat în fig.24.

Fig. 24: Algoritmul pentru utilizarea butoanelor PS1 și PS2 pe post de comutator

Codul sursă scris în C pentru implementarea acestui algoritm este următorul:

#include <pic.h>

void main(){
TRISA=0xFF; // configureaza PortA ca intrari
TRISB=0x00; // configureaza PortB ca iesiri
PORTB=0;
while(1){
if(RA4==0)

36
Microcontrolerul PIC16F84 – Aplicații practice

PORTB= 0xFF; // aprinde LED-urile


if(RA3==0)
PORTB = 0x0; // stinge LED-urile
}
}

5. Utilizarea unui singur buton pe post de comutator


Aplicația prezentată anterior deși foarte simplă ca algoritm, necesită utilizarea ambelor butoane
pentru îndeplinirea funcției de comutator, iar dacă acestea sunt apăsate simultan, LED-urile vor
fi aprinse și stinse succesiv.

Fig. 25: Algoritmul pentru utilizarea unui singur buton pe post de comutator

Pentru a înlătura aceste dezavantaje, algoritmul reprezentat în fig.25 permite utilizarea unui
singur buton pentru îndeplinirea funcției de comutator. La prima apăsare a butonului PS1 LED-
urile vor fi aprinse până la o nouă apăsare a acestui buton. Algoritmul ia în considerare și faptul

37
Microcontrolerul PIC16F84 – Aplicații practice

că butonul poate fi apăsat mai mult timp.


Codul sursă scris în C pentru implementarea acestui algoritm este următorul:

#include <pic.h>

void main(){
TRISA=0xFF; // configureaza PortA ca intrari
TRISB=0x00; // configureaza PortB ca iesiri
PORTB=0;
int activ=0; // memoreaza starea anterioara a butonului
while(1){
if(activ==0){
if(RA4==0){
activ=1;
if(RB0==0)
PORTB=0xFF;
else
PORTB=0;
}
} else
if(RA4==1)
activ=0;
}
}

Întrebări și aplicații
1. Scrieți diferite secvențe de instrucțiuni pentru a exemplifica utilizarea instrucțiunilor
aritmetice și logice în limbaj de asamblare. Ce indicatori de stare sunt modificați în cazul
fiecărei secvențe în parte?
2. Modificați algoritmul aplicației 5 pentru a utiliza butonul PS1 pentru aprinderea și
stingerea a patru LED-uri și PS2 pentru aprinderea și stingerea celorlalte patru LED-uri.
3. Rescrieți aplicațiile 2, 3, 4 și 5 în limbaj de asamblare.

38
Microcontrolerul PIC16F84 – Aplicații practice

LABORATOR 4
Instrucțiuni de salt condiționat și necondiționat

Scopul lucrării
Lucrarea de laborator prezintă instrucțiuni de salt condiționat și necondiționat și exemple de
subrutine realizate cu acestea. La sfârșitul laboratorului studenții vor avea noțiuni de
programare a operațiilor de salt condiționat și necondiționat pentru microcontrolerul PIC16F84
atât în C cât și în limbaj de asamblare.

Breviar teoretic
Instrucțiunile de salt permit modificarea succesiunii de execuție a instrucțiunilor unui program.
Salturile pot fi efectuate în funcție de evaluarea logică a unor expresii și în acest caz este vorba
de instrucțiuni de salt condiționat sau pot fi realizate la întâlnirea unor instrucțiuni specifice
numite instrucțiuni de salt necondiționat.
În limbaj de asamblare instrucțiunile de salt condiționat recunoscute de PIC16F84 se regăsesc în
tabelul 9. Primele două instrucțiuni de salt condiționat testează valoarea unui bit dintr-un
registru și în funcție de valoarea acestuia execută sau nu saltul peste următoarea instrucțiune.
Ultimele două instrucțiuni din acest tabel decrementează sau incrementează valoarea unui
registru și dacă valoarea devine 0 programul va sări peste următoarea instrucțiune.
Instrucțiunile de salt necondiționat în limbaj de asamblare recunoscute de PIC16F84 se regăsesc
în tabelul 10.
Pentru a încheia prezentarea setului de instrucțiuni în limbaj de asamblare recunoscute de
PIC16F84 în tabelul 11 sunt specificate instrucțiunile ce nu pot fi încadrate în categoriile
prezentate anterior.

39
Microcontrolerul PIC16F84 – Aplicații practice

Mnemonic Descriere formală Timp de Indicatori


execuție (cicli) afectați
BTFSC f,b dacă (f:b)==0 sare peste
1(2) -
următoarea instrucțiune
BTFSS f,b dacă (f:b)==1 sare peste
1(2) -
următoarea instrucțiune
DECFSZ f,d d=1: (f) ← (f) – 1 și salt
peste următoarea
instrucțiune dacă (f)==0
1(2) -
d=0: (w) ← (f) – 1 și salt
peste următoarea
instrucțiune dacă (w)==0
INCFSZ f,d d=1: (f) ← (f) + 1 și salt
peste următoarea
instrucțiune dacă (f)==0
1(2) -
d=0: (w) ← (f) + 1 și salt
peste următoarea
instrucțiune dacă (w)==0
Tabelul 9: Instrucțiuni de salt condiționat ([1])

Mnemonic Descriere formală Timp de Indicatori


execuție (cicli) afectați
GOTO k (PC) ← k 2 -
CALL k stiva ← (PC), (PC) ← k 2 -
RETURN (PC) ← stiva 2 -
RETLW k (w) ← k, (PC) ← stiva 2 -
RETFIE (PC) ← stiva, GIE ← 1 2 -
Tabelul 10: Instrucțiuni de salt necondiționat ([1])

Mnemonic Descriere formală Timp de Indicatori


execuție (cicli) afectați
NOP - 1 -
CLRWDT (WDI) ← 0, (IQ) ← 1,
1 TO, PD
(PD) ← 1
SLEEP (WDI) ← 0, (IQ) ← 1,
1 TO, PD
(PD) ← 0
Tabelul 11: Instrucțiuni diverse ([1])

40
Microcontrolerul PIC16F84 – Aplicații practice

În limbajul C instrucțiunile de salt condiționat și necondiționat pot fi grupate în:


• instrucțiuni decizionale (if, switch)
• structuri repetitive (for, while, do...while)
• instrucțiuni de salt necondiționat (break, continue, goto, apelul de funcții, return)
Instrucțiunea de salt necondiționat goto încurajează complicarea structurii unui program și
îngreunează înțelegerea funcționării acestuia și de accea este recomandată evitarea sa. Din
acest motiv ea nu va fi detaliată în cele ce urmează.

Instrucțiunea if
Instrucțiunea if permite implementarea unei structuri decizionale simple ce permite ramificarea
în două direcții diferite a execuției unui program în funcție de evaluarea unei expresii logice.
Structura generală a acestei instrucțiuni este:

if(condiție) {
secvență 1
} else {
secvență 2
};

Dacă la evaluarea logică a condiției aceasta este adevărată, atunci se execută secvența de
instrucțiuni notată cu secvență 1. Dacă rezultatul evaluării este fals, se execută secvența de
instrucțiuni notată cu secvență 2. Dacă o secvență conține o singură instrucțiune, acoladele ce o
încadrează pot lipsi. Deasemenea, un caz particular al acestei structuri presupune existența doar
a primei secvențe. Aceasta va fi executată doar dacă evaluarea condiției conduce la o valoare
adevărată:

if(condiție) {
secvență 1
};

Instrucțiunea switch
Instrucțiunea switch permite implementarea unei structuri decizionale multiple. Structura sa
generică este:

switch(expresie) {
case constantă 1:
secvență 1
break;
case constantă 2:

41
Microcontrolerul PIC16F84 – Aplicații practice

secvență 2
break;

case constantă n:
secvență n
break;
default:
secvență implicită
};

Structura acestei instrucțiuni are câteva particularități. După evaluarea expresiei dintre
paranteze rezultatul este comparat pe rând cu valorile constantelor specificate în clauzele case.
Dacă de exemplu expresia are valoarea constantei 1, se va executa secvența 1 de instrucțiuni.
Dacă nu, se compară valoarea sa cu constanta 2, și așa mai departe până este întâlnită o
egalitate. Dacă valoarea expresiei diferă de toate valorile constante declarate prin clauze case,
atunci se execută secvența implicită de instrucțiuni.
Existența instrucțiunii break este foarte importantă deoarece la întâlnirea sa execuția va fi
transferată instrucțiunii ce urmează structurii switch. Dacă instrucțiunea break lipsește, din
momentul în care s-a găsit o egalitate între valoarea expresiei și una dintre constantele
specificate, se vor executa toate secvențele de cod până la terminarea structurii switch (inclusiv
secvența implicită) sau până la întâlnirea primei instrucțiuni break.

Instrucțiunea while
Instrucțiunea while permite implementarea unei structuri repetitive (bucle) cu test inițial.
Structura sa generică este următoarea:

while(condiție) {
secvență
};

Secvența de instrucțiuni ce formează corpul buclei va fi repetată atâta timp cât condiția este
evaluată ca fiind adevărată din punct de vedere logic. Dacă la testarea inițială condiția este
falsă, secvența nu va fi executată nici măcar o singură dată.
Este imperativ ca instrucțiunile din corpul buclei să influențeze condiția testată pentru ca la un
moment dat aceasta să devină falsă și să se părăsească astfel structura repetitivă.
Trebuie menționat aici faptul că în limbajul C orice expresie evaluată numeric prin valoarea zero
este considerată falsă, în timp ce orice valoare numerică diferită de zero (pozitivă sau negativă)
este evaluată ca fiind adevărată.

42
Microcontrolerul PIC16F84 – Aplicații practice

Instrucțiunea do...while
Instrucțiunea do...while permite implementarea unei structuri repetitive (bucle) cu test final.
Structura sa generică este următoarea:

do {
secvență
} while(condiție);

Secvența de instrucțiuni ce formează corpul buclei va fi repetată atâta timp cât condiția este
evaluată ca fiind adevărată din punct de vedere logic. În această situație corpul buclei va fi
executat cel puțin o dată.
Este imperativ ca instrucțiunile din corpul buclei să influențeze condiția testată pentru ca la un
moment dat aceasta să devină falsă și să se părăsească astfel structura repetitivă.

Instrucțiunea for
Instrucțiunea for permite implementarea unei structuri repetitive (bucle) cu contor. Structura sa
generică este următoarea:

for(inițializare;condiție;modificare) {
secvență
};

Bucla for utilizează o variabilă cu ajutorul căreia se numără de câte ori se va repeta secvența ce
formează corpul buclei. Inițializarea acesteia se face de regulă printr-o instrucțiune de atribuire.
Secvența va fi repetată atâta timp cât condiția specificată este adevărată. Modificarea variabilei
contor se face prin specificarea unei expresii ce incrementează sau decrementează valoarea
acesteia. În cadrul parantezelor instrucțiunii, de la dreapta către stânga pot lipsi una, două sau
toate cele trei expresii, în schimb separatorii dintre acestea sunt obligatorii.

Instrucțiunile break și continue


Instrucțiunea break scrisă în interiorul unei structuri repetitive (while, do...while, for) sau a unei
structuri decizionale multiple (switch) determină părăsirea imediată a acestei structuri și
continuarea execuției de la instrucțiunea imediat următoare structurii.
Instrucțiunea continue scrisă în interiorul unei structuri repetitive cu test inițial sau final (while,
do...while) ignoră restul instrucțiunilor din cadrul buclei și trece controlul la evaluarea condiției
de repetabilitate. În situația structurii repetitive cu contor (for) se ignoră restul instrucțiunilor,
se face incrementarea sau decrementarea contorului și se reevaluează condiția de
repetabilitate.

43
Microcontrolerul PIC16F84 – Aplicații practice

Materiale didactice utilizate


Pentru realizarea lucrării de laborator sunt necesare:
• platforma experimentală Z11/EV cu microcontrolerul PIC16F84
• calculator personal cu sistem de operare Windows
• mediul integrat de dezvoltare MPLAB X
• utilitarul IC-Prog
• cablu serial de date
• sursa de alimentare PSLC/EV (±5V și ±12V) pentru Z11/EV și cablurile aferente

Desfășurarea lucrării

1. Comanda releelor cu ajutorul butoanelor PS1 și PS2


Pentru a comanda activarea închiderii și deschiderii periodice a releelor RL1 și RL2 folosind
butoanele PS1 și respectiv PS2 se utilizează schema electronică simplificată din fig.26.

Fig. 26: Schema electronică pentru comanda releelor RL1 și RL2

La apăsarea butonului PS1 releul RL1 se va închide și se va deschide periodic atâta timp cât

44
Microcontrolerul PIC16F84 – Aplicații practice

butonul este apăsat. În mod similar este implementată funcționarea pentru butonul PS2 și
releul RL2.
Pentru funcționarea acestei aplicații, jumperul de configurare al plăcii experimentale Z11/EV
trebuie pus pe poziția J3.
Programul scris în limbaj de asamblare pentru implementarea aplicației este următorul:

include p16f84.inc ; include fisierul cu definitiile pentru PIC16F84

COUNT_1 equ 0x0C ; variabila folosita pentru temporizare


COUNT_2 equ 0x0D ; variabila folosita pentru temporizare
RLSTATUS equ 0x0E ; variabila ce memoreaza starea releelor
P1 equ .04 ; bitul asociat butonului PS1
P2 equ .03 ; bitul asociat butonului PS2
RL1 equ .00 ; bitul asociat releului RL1
RL2 equ .02 ; bitul asociat releului RL2

; Initializare
init: org 0x0000
bsf STATUS,RP0
movlw 0x00
movwf TRISB
movlw B'11111000'
movwf TRISA
bcf STATUS,RP0
movlw 0xFF
movwf COUNT_1
movlw 0xFF
movwf COUNT_2
movlw 0x00
movwf RLSTATUS ; RLSTATUS = 0

; Program principal
movf RLSTATUS,0
movwf PORTA
call DELAY
loop: btfss PORTA,P2
call RELE1
btfss PORTA,P1
call RELE2
movf RLSTATUS,0
movwf PORTA
call DELAY
goto loop

RELE1: movf RLSTATUS,0 ; RLSTATUS in W


xorlw B'00000100'
movwf RLSTATUS

45
Microcontrolerul PIC16F84 – Aplicații practice

return

RELE2: movf RLSTATUS,0 ; RLSATUS in W


xorlw B'00000001'
movwf RLSTATUS
return

; intarziere
DELAY: decfsz COUNT_1,1
goto DELAY
movlw 0xFF
movwf COUNT_1
decfsz COUNT_2,1
goto DELAY
movlw 0xFF
movwf COUNT_1
movlw 0xFF
movwf COUNT_2
return

end

2. Citirea tastaturii matriciale și utilizarea afișorului cu șapte segmente


Pornind de la schema simplificată din fig.27 va fi realizată o aplicație cu microcontrolerul
PIC16F84 care să citească tastatura matricială și să afișeze cifra apăsată la afișorul cu 7
segmente.

Fig. 27: Schema electronică de conectare a tastaturii matriciale și a afișorului cu 7 segmente

46
Microcontrolerul PIC16F84 – Aplicații practice

Pentru funcționarea corectă a plăcii experimentale Z11/EV jumperul de configurare va fi pus pe


poziția J1.
Se observă din schema electronică faptul că liniile și coloanele tastaturii matriciale sunt
conectate pe portul B. Pinii portului B sunt ieșiri pentru coloane (RB0-RB3), fie intrări pentru linii
(RB4-RB7).
Pentru citirea tastaturii matriciale se trec inițial toate coloanele tastaturii pe 1 logic (5V). Se
trece pe rând fiecare coloană pe 0 (0V) și se testează pe rând ce linie este la potențial 0 (0 logic).
Astfel se poate identifica ce tastă a fost apăsată și valoarea asociată ei este transmisă la afișor.
Afișorul cu 7 segmente este conectat de microcontroler prin intermediul unui circuit
decodificator (4511) ce se comportă și ca memorie tampon între microcontroler și afișor. LED-ul
care indică punctul de la afișor este pus la masă, aprinderea celor 7 LED-uri care indică cifre sunt
selectate în funcție de cuvântul de selecție transmis la pinii A, B, C, D ai decodorului. Pinii de
selecție ai decodorului sunt conectați la PORTA pinii RA0, RA1, RA2, RA3 ai microcontrolerului.
Codul sursă scris în C pentru implementarea aplicației este următorul:

#include <pic.h>
void main() {
char tasta; // memoreaza tasta apasata
TRISA=0b00000000; // initializare semnale PortA
TRISB=0b11110000; // initializare semnale PortB
RB0=1; RB1=1; RB2=1; RB3=1; // toate coloanele matricii pe 1
while(1){
RB0=0; // testare taste de pe prima coloana
if(RB4==0) tasta=1;
if(RB5==0) tasta=4;
if(RB6==0) tasta=7;
if(RB7==0) tasta=14;
RB0=1;
RB1=0; // testare taste de pe a doua coloana
if(RB4==0) tasta=2;
if(RB5==0) tasta=5;
if(RB6==0) tasta=8;
if(RB7==0) tasta=0;
RB1=1;
RB2=0; // testare taste de pe a treia coloana
if(RB4==0) tasta=3;
if(RB5==0) tasta=6;
if(RB6==0) tasta=9;
if(RB7==0) tasta=15;
RB2=1;
RB3=0; // testare taste de pe a patra coloana
if(RB4==0) tasta=10;
if(RB5==0) tasta=11;
if(RB6==0) tasta=12;

47
Microcontrolerul PIC16F84 – Aplicații practice

if(RB7==0) tasta=13;
RB3=1;

PORTA = tasta; // afiseaza tasta apasata


}
}

Varianta programului scrisă în limbaj de asamblare:

include p16f84.inc ; include fisierul cu definitiile pentru PIC16F84

org 0x0000 ; adresa unde incepe programul 0000h

; Initializare
bsf STATUS,RP0 ; selecteaza bancul 1
movlw B'11110000'
movwf TRISB ; seteaza PORTB pinii LSB ca iesiri si cei MSB ca intrari
movlw B'00000000'
movwf TRISA ; seteaza pinii PORTA ca iesiri
bcf STATUS,RP0 ; selecteaza bancul 0
bsf PORTB,0
bsf PORTB,1
bsf PORTB,2
bsf PORTB,3

; Programul principal
main
bcf PORTB,0
btfss PORTB,4
movlw D'1'
btfss PORTB,5
movlw D'4'
btfss PORTB,6
movlw D'7'
btfss PORTB,7
movlw D'14'
bsf PORTB,0

bcf PORTB,1
btfss PORTB,4
movlw D'2'
btfss PORTB,5
movlw D'5'
btfss PORTB,6
movlw D'8'
btfss PORTB,7
movlw D'0'
bsf PORTB,1

48
Microcontrolerul PIC16F84 – Aplicații practice

bcf PORTB,2
btfss PORTB,4
movlw D'3'
btfss PORTB,5
movlw D'6'
btfss PORTB,6
movlw D'9'
btfss PORTB,7
movlw D'15'
bsf PORTB,2

bcf PORTB,3
btfss PORTB,4
movlw D'10'
btfss PORTB,5
movlw D'11'
btfss PORTB,6
movlw D'12'
btfss PORTB,7
movlw D'13'
bsf PORTB,3

movwf PORTA ; transfera valoarea din acumulator catre afisor


goto main

end ; marcarea terminarii codului sursa

3. Afișarea tastei apăsate pentru o durată prestabilită


În cadrul aplicației anterioare afișarea tastei apăsate se face doar atât cât tasta este apăsată.
Pentru a permite afișarea cu remanență (sub 1 secundă) a valorii pe ecran, codul sursă în C
poate fi modificat astfel:

#include <pic.h>

void delay(){
for(int i=0;i<5000;i++)
for(int j=0;j<2;j++);
};

void main() {
char tasta; // memoreaza tasta apasata
TRISA=0x00; // initializare semnale PortA
TRISB=0xF0; // initializare semnale PortB
PORTB=0x0F; // toate coloanele matricii pe 1
while(1){
RB0=0; // testare taste de pe prima coloana
if(RB4==0) tasta=1;

49
Microcontrolerul PIC16F84 – Aplicații practice

if(RB5==0) tasta=4;
if(RB6==0) tasta=7;
if(RB7==0) tasta=14;
RB0=1;
RB1=0; // testare taste de pe a doua coloana
if(RB4==0) tasta=2;
if(RB5==0) tasta=5;
if(RB6==0) tasta=8;
if(RB7==0) tasta=0;
RB1=1;
RB2=0; // testare taste de pe a treia coloana
if(RB4==0) tasta=3;
if(RB5==0) tasta=6;
if(RB6==0) tasta=9;
if(RB7==0) tasta=15;
RB2=1;
RB3=0; // testare taste de pe a patra coloana
if(RB4==0) tasta=10;
if(RB5==0) tasta=11;
if(RB6==0) tasta=12;
if(RB7==0) tasta=13;
RB3=1;

PORTA = tasta; // afiseaza tasta apasata

if((tasta>=0)&&(tasta<=9)){
delay();
tasta=0xFF;
PORTA=tasta;
};
};
}

50
Microcontrolerul PIC16F84 – Aplicații practice

LABORATOR 5
Întreruperi

Scopul lucrării
Lucrarea de laborator prezintă sistemul de întreruperi al microcontrolerului PIC16F84 și exemple
practice de utilizare a acestuia. Studenții vor dobândi cunoștințe de programare a întreruperilor
cu ajutorul secvențelor de cod scrise atât în limbaj de asamblare și în C.

Breviar teoretic
Prin intermediul întreruperilor microcontrolerul poate detecta apariția unor evenimente
externe sau interne și poate executa secvențe de cod pentru tratarea acestor evenimente
(fig.28).

Fig. 28: Modul de funcționare al microcontrolerului la apariția unei întreruperi [2]

La apariția unei întreruperi execuția normală a programului rulat este întreruptă, adresa

51
Microcontrolerul PIC16F84 – Aplicații practice

următoarei instrucțiuni (conținutul registrului PC) este salvată în vârful stivei, în PC se încarcă
adresa vectorului de tratare a întreruperii (0004h), bitul de validare globală a întreruperilor GIE
este resetat și se continuă execuția de la adresa din PC (Program Counter). La terminarea rutinei
de tratare a întreruperii (pentru microcontrolerul PIC16F84 la execuția instrucțiunii RETFIE), este
setat la loc bitul GIE pentru activarea întreruperilor nemascate, în registrul PC se încarcă adresa
din vârful stivei și programul principal continuă de unde a rămas în momentul apariției
întreruperii.
PIC16F84 dispune de patru surse de întrerupere:
• apariția unei depășiri de capacitate pentru registrul circuitului temporizator TMR0
• apariția unui semnal extern pe pinul RB0/INT
• schimbarea nivelelor logice pe pinii RB7:RB4
• încheierea unui operațiunii de scriere în memoria internă EEPROM
Microcontrolerul PIC16F84 pune la dispoziţie o singură adresă pentru vectorul de întrerupere
(0004h).
Logica de tratare a întreruperilor este reprezentată în fig.29.

Fig. 29: Logica de tratare a întreruperilor [1]

Pentru fiecare sursă de întrerupere sunt utilizate două semnale:


• IF (Interrupt Flag) semnalizează apariția întreruperii
• IE (Interrupt Enable) permite mascarea întreruperii
În plus, există și un semnal GIE (General Interrupt Enable) ce permite activarea sau dezactivarea
globală a sistemului de semnalizare a întreruperilor.

Registrul INTCON
Registrul cu funcții speciale dedicat sistemului de întreruperi este registrul INTCON. Cu ajutorul

52
Microcontrolerul PIC16F84 – Aplicații practice

acestuia programatorul poate activa sau dezactiva global sistemul de întreruperi sau fiecare tip
de întrerupere în parte. Biții ce semnalizează apariția unui anumit tip de întrerupere sunt setați
hardware prin mecanismul de generare a întreruperilor. Programatorul trebuie să reseteze
acești indicatori în rutina de tratare a întreruperii pentru a putea fi detectată apariția
următoarei întreruperi. Structura registrului INTCON este prezentată în fig.30.
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
GIE EEIE T0IE INTE RBIE T0IF INTF RBIF
Fig. 30: Structura registrului INTCON

Semnificația biților acestui registru este următoarea:


GIE (General Interrupt Enable) - activează (1) sau dezactivează (0) funcționarea
sistemului de întreruperi.
EEIE (EE Write Complete Interrupt Enable) - activează (1) sau dezactivează (0) generarea
unei întreruperi la terminarea scrierii în memoria EEPROM de date internă.
T0IE (Timer0 Interrupt Enable) - activează (1) sau dezactivează (0) generarea unei
întreruperi de către modului Timer0.
INTE (RB0/INT Interrupt Enable) - activează (1) sau dezactivează (0) generarea unei
întreruperi de către semnalul de pe pinul RB0/INT.
RBIE (RB Port Change Interrupt Enable) - activează (1) sau dezactivează (0) generarea
unei întreruperi la schimbarea semnalelor pe pinii RB4-RB7 ai portului B.
T0IF (Timer0 Overflow Interrupt Flag) - semnalizează (1) apariția unei depășiri la Timer0.
Bitul trebuie resetat în rutina de tratare a întreruperii.
INTF (RB0/INT Interrupt Flag) - semnalizează (1) apariția unei întreruperi la pinul
RB0/INT.
RBIF (RB Port Change Interrupt Flag) - semnalizează (1) schimbarea cel puțin a unui
semnal pe pinii RB4-RB7 ai portului B. Bitul trebuie resetat în rutina de tratare a
întreruperii.

Registrul OPTION_REG
Un alt registru cu funcții speciale important ce permite configurarea modulului Timer0 este
registrul OPTION_REG a cărui structură este reprezentată în fig.31.
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
RBPU INTEDG T0CS T0SE PSA PS2 PS1 PS0
Fig. 31: Structura registrului OPTION_REG

Semnificația biților acestui registru este următoarea:

53
Microcontrolerul PIC16F84 – Aplicații practice

RBPU (PORTB Pull-up Enable) - activează (0) sau dezactivează (1) rezistențele interne de
pull-up pentru portul B.
INTEDG (Interrupt Edge Select) - specifică dacă întreruperea este generată de frontul
crescător (1) sau descrescător (0) al semnalului de pe pinul RB0/INT.
T0CS (TMR0 Clock Source Select) - specifică daca TMR0 este incrementat de ceasul
intern (0) sau de schimbarea semnalului pe pinul RA4/T0CKI (1).
T0SE ( TMR0 Source Edge Select) - specifică dacă incrementarea TMR0 se face pe frontul
crescător (0) sau descrescător (1) al semnalului de pe pinul RA4/T0CKI.
PSA (Prescaler Assignment) - specifică dacă divizorul (prescaler) este asociat TMR0 (0)
sau WatchDog Timer (1).
PS2 - PS0 (Prescaler Rate Select) - specifică valoarea divizorului conform următorului
tabel:
Valoare PS2-PS0 Divizor Divizor
TMR0 WDT
000 1:2 1:1
001 1:4 1:2
010 1:8 1:4
011 1:16 1:8
100 1:32 1:16
101 1:64 1:32
110 1:128 1:64
111 1:256 1:128

Materiale didactice utilizate


Pentru realizarea lucrării de laborator sunt necesare:
• platforma experimentală Z11/EV cu microcontrolerul PIC16F84
• calculator personal cu sistem de operare Windows
• mediul integrat de dezvoltare MPLAB X
• utilitarul IC-Prog
• cablu serial de date
• sursa de alimentare PSLC/EV (±5V și ±12V) pentru Z11/EV și cablurile aferente

54
Microcontrolerul PIC16F84 – Aplicații practice

Desfășurarea lucrării

1. Controlul intensității luminoase folosind tehnica PWM


Folosind schema din fig.34 va fi realizată în continuare o aplicație ce permite varierea intensității
luminoase a LED-urilor în mod automat, de la minim la maxim și invers.

Fig. 32: Schema electronică de conectare a LED-urilor la microcontroler

Pentru realizarea acestei lucrări se utilizează tehnica de modulare în durată a impulsurilor sau
PWM (Pulse Width Modulation) cu ajutorul căreia poate fi controlată tensiunea aplicată unui
dispozitiv electric utilizând un semnal digital. Semnalul digital este schimbat alternativ din
starea OFF în ON și invers cu o frecvență ridicată.
Mărimile caracteristice pentru definirea semnalului PWM sunt perioada semnalului (duty cycle)
și factorul de umplere (fill factor). Perioada semnalului reprezintă cel mai mic interval de timp
după care semnalul se repetă. Factorul de umplere este raportul între durata cât semnalul este
în starea ON față de perioada semnalului. Factorul de umplere este exprimat de regulă
procentual.

Fig. 33: Exemple de semnale PWM cu factori de umplere diferiți

Pentru calculul tensiunii medii la ieșirea generatorului PWM se poate folosi formula 1:

55
Microcontrolerul PIC16F84 – Aplicații practice

t ON
U= ⋅U cc (1)
t ON +t OFF
unde:
U tensiunea la ieșire (0 - Ucc)
tON durata cât semnalul este ON
tOFF durata cât semnalul este OFF
Ucc tensiunea de alimentare
Microcontrolerul PIC16F84 nu conține module PWM și de aceea această tehnică trebuie
simulată software.
Pentru realizarea temporizărilor se folosește funcția __delay_us existentă în bibliotecile
compilatorului de C. Aceasta primește ca parametru un întreg reprezentând numărul de
microsecunde cât trebuie întârziată execuția programului. Deoarece viteza de lucru a
microcontrolerului depinde de frecvența de tact a oscilatorului cu cuarț, este necesară definirea
variabilei _XTAL_FREQ pentru calibrarea întârzierii.
Programul în C pentru microcontroler este:

#include<pic.h>
#include<htc.h>

#define _XTAL_FREQ 4000000 // frecventa oscilatorului pentru __delay_us

// functia de temporizare
void delay(unsigned int valoare){
for(int i=0;i<valoare;i++)
__delay_us(10);
};

void main(){
int maxOn = 10; // numarul de trepte de variere al factorului de umplere
int pas = 1; // pasul de variere al factorului de umplere
int ledOn = 0; // factorul de umplere curent
int cnt = 0; // variabila pentru periodicitatea de modificare a
// factorului de umplere
TRISA=0xFF; // initializare porturi
TRISB=0x00;
while(1){
PORTB=0xFF; // aprinde LED-urile
delay(ledOn); // temporizare ON
PORTB=0x00; // stinge LED-urile
delay(maxOn-ledOn); // temporizare OFF
cnt++;
if(cnt==500){ // dupa 500 de cicluri

56
Microcontrolerul PIC16F84 – Aplicații practice

cnt=0; // reseteaza contorul


ledOn+=pas; // modifica factorul de umplere
};
// modifica sensul de variere al factorului de umplere daca s-a
// ajuns la factorul de umplere maxim (100%) sau minim (0%)
if(pas==1) {
if(ledOn==maxOn) pas=-1; // sens descrescator
} else {
if(ledOn==0) pas=1; // sens crescator
}
}
}

2. Utilizarea TMR0 și a sistemului de întreruperi


Pornind de la schema electronică simplificată din fig.34 va fi realizată în continuare o aplicație ce
aprinde și stinge LED-ul conectat la pinul RB0 al microcontrolerului. Perioada de funcționare va
fi configurată la 2 secunde (1 secundă aprins, o secundă stins).
Pentru realizarea acestei aplicații jumperul de configurare al plăcii Z11/EV va fi pus pe poziția J3.

Fig. 34: Schema electronică a luminii pulsatorii

Microcontrolerul PIC16F84 are un modul cronometru/numărător (TMR0) ce poate fi utilizat


pentru măsurarea foarte precisă a timpului sau numărarea unor evenimente. Funcționarea
acestuia este programabilă utilizând biții din registrul cu funcție specială OPTION_REG.
Valoarea cronometrului/numărătorului asociat lui TMR0 este memorată în registrul cu funcții
speciale TMR0 care este un registru pe 8 biți ce poate fi scris sau citit. Utilizarea TMR0 ca și
cronometru se face resetând bitul T0CS din OPTION_REG. TMR0 va fi incrementat automat la
fiecare impuls de ceas (daca nu se folosește divizorul).

57
Microcontrolerul PIC16F84 – Aplicații practice

Utilizarea TMR0 ca și numărător se face setând bitul T0CS din OPTION_REG. TMR0 va fi
incrementat fie pe frontul ascendent (T0SE=0) fie pe cel descendent (T0SE=1) al semnalului de
pe pinul RA4/T0CKI.
Atunci când TMR0 trece de la FFh la 00h (owerflow = depășire de capacitate de memorare) este
generată intern o întrerupere. Această depășire este semnalizată prin setarea (1) bitului T0IF din
registrul cu funcții speciale INTCON. Întreruperea poate fi dezactivată prin resetarea (0) bitului
T0IE din INTCON. Bitul T0IF trebuie resetat în soft de rutina de tratare a întreruperii pentru
Timer0. Întreruperea generată de modulul TMR0 nu poate trezi procesorul din starea de
consum redus (sleep) deoarece în această perioadă funcționarea TMR0 este întreruptă.
Codul sursă scris în C pentru realizarea luminii pulsatorii este:

#include <pic.h>

unsigned int cnt = 0;


// declarea rutinei de intrerupere prin plasarea in memoria program la adresa 04h
void interrupt isr() @ 0x04;

void initTimer(){
OPTION_REG &= 0xC0; // divizor 1:2
T0IE = 1; // TMR0 Overflow Interrupt Enable
GIE = 1; // Global Interrupts Enable
}

void main() {
TRISB = 0;
RB0 = 0;
initTimer();
while(1){
if (cnt>=245){ // 1 sec
cnt=0;
RB0 = ~RB0;
}
}
}

void interrupt isr() {


if(T0IF) {
cnt++;
T0IF = 0; // sterge semnalizarea intreruperii
}
}

3. Comanda unui buzzer


În continuare va fi realizată o aplicație pentru emiterea de sunete la difuzorul montat pe placa

58
Microcontrolerul PIC16F84 – Aplicații practice

experimentală Z11/EV. Pentru realizarea acestei aplicații jumperul de configurare al plăcii


Z11/EV va fi pus pe poziția J3.
Schema electronică simplificată este reprezentată în fig.35.

Fig. 35: Schema electronică de conectare la un difuzor

Programul scris în limbaj de asamblare este următorul:

include p16f84.inc ; include fisierul cu definitiile pentru PIC16F84

#define SUNET 0xB9

; subrutina de tratare a intreruperilor


int: org 0x0004
movf PORTA,0 ; preia valoarea din PORTA in W
xorlw 0x02 ; inverseaza valoarea bitului 1 din W
movwf PORTA ; scrie W in PORTA
movlw SUNET ; reinitializeaza TMR0 cu valoarea SUNET
movwf TMR0
bcf INTCON,T0IF ; sterge indicatorul intreruperii de la TMR0
bsf INTCON,GIE ; reactiveaza sistemul de intreruperi
retfie

; initializare
init: org 0x0050
bsf STATUS,RP0 ; activeaza bancul 1 de memorie
bcf OPTION_REG,T0CS ; foloseste pentru TMR0 ceasul intern
bcf OPTION_REG,PSA ; asociaza divizorul la TMR0
bsf OPTION_REG,PS0 ; seteaza divizorul cu 011
bsf OPTION_REG,PS1 ; adica 1:16
bcf OPTION_REG,PS2 ;
movlw 0x00 ; configureaza PORTB ca iesiri
movwf TRISB

59
Microcontrolerul PIC16F84 – Aplicații practice

movlw 0x18 ; configureaza PORTA cu 11000


movwf TRISA
bcf STATUS,RP0 ; activeaza bancul 0 de memorie
bsf INTCON,T0IE ; activeaza intreruperile de la TMR0
movlw SUNET
movwf TMR0 ; preseteaza TMR0 cu valoarea SUNET
bsf INTCON,GIE ; activeaza sistemul de intreruperi

; program principal
main: nop
nop
goto main

end

4. Alarmă electronică
Pe baza schemei electronice din fig.35 se poate realiza o aplicație cu funcționalitate de alarmă
electronică. Codul scris în limbaj de asamblare este următorul:

include p16f84.inc ; include fisierul cu definitiile pentru PIC16F84

COUNT_1 equ 0x0C


COUNT_2 equ 0x0D
SUNET equ 0x0E
#define SUNET1 0xB9
#define SUNET2 0xE0

; subrutina de tratare a intreruperilor


int: org 0x0004
movf PORTA,0 ; preia valoarea din PORTA in W
xorlw 0x02 ; inverseaza valoarea bitului 1 din W
movwf PORTA ; scrie W in PORTA
movlw SUNET ; reinitializeaza TMR0 cu valoarea SUNET
movwf TMR0
bcf INTCON,T0IF ; sterge indicatorul intreruperii de la TMR0
bsf INTCON,GIE ; reactiveaza sistemul de intreruperi
retfie

; initializare
init: org 0x0050
bsf STATUS,RP0 ; activeaza bancul 1 de memorie
bcf OPTION_REG,T0CS ; foloseste pentru TMR0 ceasul intern
bcf OPTION_REG,PSA ; asociaza divizorul la TMR0
bsf OPTION_REG,PS0 ; seteaza divizorul cu 011
bsf OPTION_REG,PS1 ; adica 1:16
bcf OPTION_REG,PS2 ;
movlw 0x00 ; configureaza PORTB ca iesiri

60
Microcontrolerul PIC16F84 – Aplicații practice

movwf TRISB
movlw 0x18 ; configureaza PORTA cu 11000
movwf TRISA
bcf STATUS,RP0 ; activeaza bancul 0 de memorie
bsf INTCON,T0IE ; activeaza intreruperile de la TMR0
movlw SUNET1
movwf SUNET
movwf TMR0 ; preseteaza TMR0 cu valoarea SUNET1
movlw 0xFF
movwf COUNT_1
movlw 0xFF
movwf COUNT_2
bsf INTCON,GIE ; activeaza sistemul de intreruperi

; program principal
main: movlw SUNET1
movwf SUNET
call DELAY
call DELAY
movlw SUNET2
movwf SUNET
call DELAY
call DELAY
goto main

; rutina temporizare
DELAY
decfsz COUNT_1,1
goto DELAY
movlw 0xFF
movwf COUNT_1
decfsz COUNT_2,1
goto DELAY
movlw 0xFF
movwf COUNT_1
movlw 0xFF
movwf COUNT_2
return

end

61
Microcontrolerul PIC16F84 – Aplicații practice

LABORATOR 6
Interfațarea cu afișajul alfanumeric

Scopul lucrării
Lucrarea de laborator prezintă afișorul cu cristale lichide cu 2x16 caractere și modul de
conectare și comunicare a acestuia cu microcontrolerul. Studenții la sfârșitul laboratorului vor
avea noțiuni de programare a LCD-ului cu microcontrolerul PIC16F84.

Breviar teoretic
Afişajul 162B conţine un controler CMOS tip KS0070B pentru afişoare LCD [3]. Acesta este
capabil să afişeze 1 sau 2 linii cu caractere 5x7 puncte sau 1 linie cu caractere de 5x10 puncte.
Poate fi interfaţat cu microcontrolerul pe 4 sau 8 biţi. Setul de caractere cuprinde 192 de
simboluri 5x7 puncte şi 32 simboluri 5x10 puncte. Permite programarea unor simboluri definite
de utilizator prin folosirea generatorului de caractere.
Semnificația pinilor acestui integrat este următoarea:

DB0...DB3 - pini bidirecționali de date (nu se folosesc atunci când conectarea se face pe
4 pini)
DB4...DB7 - pini bidirecționali de date (DB7 se poate utiliza ca semnal BUSY)
RS (Register Select) - selectează registrul de instrucțiuni (0) sau registrul de date (1)
R/W (Read/Write) – stabilește sensul de transmitere al datelor (0 = scriere, 1 = citire)
E (Enable) - tranzitia HIGH → LOW a acestui semnal pornește scrierea la sau citirea de la
afișaj
VSS - masa
VDD - plusul tensiunii de alimentare
VEE - reglaj contrast afișaj

62
Microcontrolerul PIC16F84 – Aplicații practice

LED+ - plusul tensiunii de alimentare a iluminării afișajului


LED- - masa iluminării afișajului
Memoria RAM pentru afişare are 80 octeţi.
Funcţionarea cu magistrala de date de 4 sau 8 biţi se configurează la iniţializarea afişajului.
Pentru operaţiunile de scriere sau citire se folosesc doi regiştri speciali:
• Data Register - registrul de date
• Instruction Register - registrul de instrucţiuni
Registrul de date (DR) este folosit temporar pentru stocarea caracterului ce urmează a fi scris
sau citit în/din memoria de date (Display Data RAM) sau memoria generatorului de caractere
(Character Generator RAM). Locaţia din memoria RAM ce urmează a fi folosită este selectată cu
ajutorul instrucţiunii de setare a adresei. Fiecare operaţiune internă pentru scrierea sau citirea
din memoria RAM se face automat. La citirea unui caracter din DDRAM sau CGRAM, caracterul
de la următoarea adresă este transferat automat în DR. La scrierea unui caracter în DR,
informaţia din DR este transferată automat în DDRAM sau CGRAM.
Registrul de instrucţiuni (IR) este utilizat doar pentru memorarea instrucţiunilor primite de la
microcontroler. Acest registru nu moate fi folosit pentru citire.
Selectarea DR sau IR se foloseşte semnalul RS indiferent dacă se lucrează în mod pe 4 sau 8 biţi.
Tranziția semnalului de pe pinul E (Enable) din 1 în 0 declanșează scrierea la sau citirea de la
afișaj.
CGRAM poate fi utilizată pentru memorarea de caractere definite de utilizator.
Atunci când afișajul execută comenzi interne, singura instrucțiune acceptată este cea care
testează indicatorul Busy Flag. Când BF=1 controlerul procesează o comandă internă. În acest
timp nu sunt acceptate comenzi. Înainte de trimiterea unei comenzi la afişor trebuie să se
verifice ca BF=0.
Interfața afișajului funcționează atât cu 8 biți de date cât și cu 4 biți de date. În acest al doile caz,
un octet trebuie trimis sau citit ca două cuvinte de 4 biți transmise secvențial, urmărind şi BF.
Adresarea regiștrilor DR și IR se face prin intermediul semnalelor RS și R/W astfel:
RS R/W Operațiune
0 0 scrie comanda în IR
0 1 citește Busy Flag (DB7) și Address Counter (DB0 - DB6)
1 0 scrie data în DR (în DDRAM sau CGRAM)
1 1 citește data din DR (din DDRAM sau CGRAM)
Address Counter (AC) memorează adrese din DDRAM sau CGRAM. El este incrementat sau

63
Microcontrolerul PIC16F84 – Aplicații practice

decrementat automat după fiecare instrucţiune de scriere sau citire de date.


Memoria DDRAM poate memora maxim 80 octeţi. Dacă se utilizează afişarea pe o singură linie
adresele locaţiilor de memorie sunt în intervalul 00h – 4Fh. Pentru afişarea pe două linii
adresele se află în intervalele 00h-27h şi 40h-67h.
Harta adreselor RAM pentru afișajul LCD este următoarea:
Caracter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Prima
00h 01h 02h 03h 04h 05h 06h 07h 08h 09h 0Ah 0Bh 0Ch 0Dh 0Eh 0Fh
linie
A doua
40h 41h 42h 43h 44h 45h 46h 47h 48h 49h 4Ah 4Bh 4Ch 4Dh 4Eh 4Fh
linie
Generatorul de caractere are o memorie ROM în care sunt definite 192 caractere 5x7 puncte şi
32 caractere 5x10 puncte şi o memorie RAM în care utilizatorul poate defini 8 caractere 5x8
puncte.
Afișajul acceptă patru categorii de instrucțiuni:
• instrucțiuni de configurare
• instrucțiuni de setare a adresei din DDRAM sau CGRAM
• instrucțiuni de transfer cu memoira DDRAM sau CGRAM
• instrucțiuni pentru executarea de funcții diverse
În cele ce urmează sunt descrise pe scurt aceste instrucţiuni. În situaţia în care în loc de 0 sau 1
este trecut x.
Clear Display – şterge ecranul scriind codul 20h (caracterul spaţiu) în toată memoria DDRAM,
setează AC pe 0, deplasează cursorul în stânga şi setează modul de modificare a AC pe
incrementare (la fiecare citire sau scriere de date AC este incrementat automat).
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 0 0 0 0 0 1
Return Home – setează AC pe 00h şi mută cursorul pe poziţia iniţială. Memoria DDRAM nu este
modificată.
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 0 0 0 0 1 x
Entry Mode Set - setează direcția de deplasare a cursorului și a ecranului la scrierea sau citirea
datelor.
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 0 0 0 1 I/D SH

64
Microcontrolerul PIC16F84 – Aplicații practice

I/D = Increment/Decrement – modul de modificare al AC la citirea sau scrierea de date:


I/D = 1: incrementare
I/D = 0: decrementare
SH = Shift of Entire Display – dacă SH = 1 şi este vorba de o operaţiune de scriere in DDRAM, se
deplasează întregul conţinut al ecranului în funcţie de I/D spre dreapta sau stânga.
Display ON / OFF – controlează modul de afişare.
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 0 0 1 D C B
D = Display On/Off Bit – dacă D = 1 ecranul este aprins, dacă D = 0 ecranul este stins dar datele
de pe ecran rămân în DDRAM
C = Cursor On/Off Bit – dacă C = 1 cursorul este vizibil, dacă C=0 cursorul este ascuns fără a
modifica registrul I/D
B = Blink On/Off Bit – dacă B = 1 cursorul clipeşte, dacă B = 0 cursorul nu clipeşte
Cursor or Display Shift - controlează deplasarea cursorului sau a textului textului fără citirea sau
scrierea datelor.
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 0 1 S/C R/L x x
Modalităţi de deplasare în funcţie de biţii S/C şi R/L:
S/C R/L Operaţiune
0 0 deplasează cursorul spre stânga şi decrementează AC
0 1 deplasează cursorul spre dreapta şi incrementează AC
1 0 deplasează spre stânga tot conţinutul ecranului şi cursorul
1 1 deplasează spre dreapta tot conţinutul ecranului şi cursorul
Function Set – configurare funcţionare
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 DL N F x x
DL = interface Data Length control bit– lăţimea magistralei de date: DL = 0: 4 biţi (datele vor fi
transferate câte 4 biţi în două operaţiuni), DL = 1: 8 biţi
N = line Number control bit – numărul de linii utilizate: N = 0: o linie, N = 1: două linii
F = display Font control bit – stabileşte fontul utilizat: F = 0: font 5x7 puncte, F = 1: font 5x10
puncte

65
Microcontrolerul PIC16F84 – Aplicații practice

Set CGRAM Address – setează adresa CGRAM conform AC


RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 1 AC5 AC4 AC3 AC2 AC1 AC0
Această instrucţiune face accesibilă memoria CGRAM pentru microcontroler.
Set DDRAM Address - setează adresa DDRAM conform AC
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 1 AC6 AC5 AC4 AC3 AC2 AC1 AC0
Această instrucţiune face accesibilă memoria DDRAM pentru microcontroler. Pentru afişarea pe
o singură linie adresa este în intervalul 00h – 4Fh. Pentru afişarea pe două linii adresa este în
intervalul 00h – 27h pentru prima linie şi 40h – 67h pentru a doua linie.
Read Busy Flag and Address – citeşte starea BF şi adresa din AC
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 1 BF AC6 AC5 AC4 AC3 AC2 AC1 AC0
Dacă BF =1 afişajul nu primeşte comenzi deoarece este ocupat cu prelucrări interne.
Microcontrolerul trebuie să aştepte BF = 0 înainte să trimită date sau comenzi către afişaj.
Write Data to DDRAM or CGRAM – scrie date în DDRAM sau CGRAM
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
1 0 .D7 .D6 .D5 .D4 .D3 .D2 .D1 .D0
Selecţia DDRAM sau CGRAM este realizată folosind anterior instrucţiunea Set DDRAM Address
sau Set CGRAM Address. După scrierea valorii, AC este incrementat sau decrementat în funcţie
de indicatorul I/D.
Read Data from DDRAM or CGRAM – citeşte date din DDRAM sau CGRAM
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
1 1 .D7 .D6 .D5 .D4 .D3 .D2 .D1 .D0
Selecţia DDRAM sau CGRAM este realizată folosind anterior instrucţiunea Set DDRAM Address
sau Set CGRAM Address. După citirea valorii, AC este incrementat sau decrementat în funcţie
de indicatorul I/D. Este obligatorie setarea adresei înaintea acestei operaţiuni.

La alimentarea afișajului acesta trebuie inițializat. Secvența de inițializare pentru interfațarea pe


8 biți este următoarea:
Power ON
|
Asteaptă minim 15ms după ce VDD ajunge la 4,5V
|
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0

66
Microcontrolerul PIC16F84 – Aplicații practice

0 0 0 0 1 1 x x x x
|
Așteaptă minim 4,1ms
|
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 1 x x x x
|
Așteaptă minim 0,1ms
|
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 1 x x x x
|
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1 I/D SH
|
Sfârșit inițializare

Materiale didactice utilizate


Pentru realizarea lucrării de laborator sunt necesare:
• platforma experimentală Z11/EV cu microcontrolerul PIC16F84
• calculator personal cu sistem de operare Windows
• mediul integrat de dezvoltare MPLAB X
• utilitarul IC-Prog
• cablu serial de date
• sursa de alimentare PSLC/EV (±5V și ±12V) pentru Z11/EV și cablurile aferente

Desfășurarea lucrării
Placa experimentală Z11/EV dispune de un afișor LCD 2x16 caractere conectat la microcontroler
pe 8 biți. Schema electronică simplificată este reprezentată în fig.36. Pentru utilizarea acesteia
este necesar ca jumperul de configurare al plăcii experimentale să fie conectat pe poziția J2.

67
Microcontrolerul PIC16F84 – Aplicații practice

Fig. 36: Interfațarea pe 8 biți a unui afișaj LCD 2x16 caractere

1. Afișarea unui text pe două rânduri


Pentru afișarea unui text static la afișajul LCD, programul în C pentru microcontroler este
următorul:

#include<pic.h>
// definire semnale pentru LCD
#define lcd_RS RA2 // Registry Select
#define lcd_RW RA1 // Read/Write
#define lcd_E RA0 // Enable
#define lcd_DATA PORTB // date

// subrutina de intarziere in ms
void delay(unsigned int a){
unsigned int i, j;
for(i=0;i<a;i++)
for(j=0;j<14;j++);
}

// declaratii functii pentru LCD


void InitLCD(void);
void SendCmd(char);
void SendData(char);
void WriteString(const char *);
void ClearLCD();

void main(){

68
Microcontrolerul PIC16F84 – Aplicații practice

TRISA=0b00000000; // initializare semnale PortA


TRISB=0b00000000; // initializare semnale PortB
InitLCD();
WriteString("PIC16F84");
SendCmd(0x45); // pozitionare linia 2 coloana 6
WriteString("LCD demo");
while(1);
}

// definitii functii pentru LCD


// trimite un caracter pentru afisare
void SendData(char t){
lcd_RS = 1; // date
lcd_RW = 0; // scriere
lcd_DATA = t; // caracter
lcd_E = 1;
delay(1);
lcd_E = 0; // trecerea 0->1 initiaza scrierea
delay(1);
}

// trimite o comanda la LCD


void SendCmd(char z){
lcd_RS = 0; // comanda
lcd_RW = 0; // scriere
lcd_DATA = z; // cod comanda
lcd_E = 1;
delay(1);
lcd_E = 0; // trecerea 0->1 initiaza scrierea
delay(1);
}

// initializare LCD
void InitLCD(void){
delay(15);
SendCmd(0x30);
delay(5);
SendCmd(0x30);
delay(1);
SendCmd(0x30);
delay(1);
SendCmd(0x38);
SendCmd(0x0c);
SendCmd(0x01);
SendCmd(0x06);
}

// scrie un sir de caractere


void WriteString(const char *s){

69
Microcontrolerul PIC16F84 – Aplicații practice

while(*s)
SendData(*s++);
}

// sterge afisajul
void ClearLCD(void){
SendCmd(0x01);
delay(10);
}

2. Cronometru digital cu afișajul LCD 2x16 caractere


Folosind aceeași schemă electronică din fig.36 va fi realizat în continuare un cronometru digital
ce afișează ora, minutul și secunda.
Pentru realizarea temporizărilor se poate folosi funcția __delay_ms existentă în bibliotecile
compilatorului de C. Aceasta primește ca parametru un întreg reprezentând numărul de
milisecunde cât trebuie întârziată execuția programului. Deoarece viteza de lucru a
microcontrolerului depinde de frecvența de tact a oscilatorului cu cuarț, este necesară definirea
unei variabile pentru calibrarea întârzierii. Aceasta se face cu instrucțiunea de precompilare:

#define _XTAL_FREQ 4000000

Valoarea numerică specificată în această instrucțiune reprezintă frecvența în Hz a oscilatorului


de cuarț.
Cu aceste considerente, programul în C pentru microcontroler este următorul:

#include <xc.h>
#include<stdio.h>

// definire semnale pentru LCD


#define lcd_RS RA2 // Registry Select
#define lcd_RW RA1 // Read/Write
#define lcd_E RA0 // Enable
#define lcd_DATA PORTB // date

#define _XTAL_FREQ 4000000 // frecventa oscilatorului pentru __delay_ms

// declaratii pentru temporizator


unsigned int cnt=0;
void interrupt isr() @ 0x04;
void initTimer();

// declaratii functii pentru LCD


void InitLCD(void);
void SendCmd(char);

70
Microcontrolerul PIC16F84 – Aplicații practice

void SendData(char);
void WriteString(const char *);
void ClearLCD();

void main(){
int sec=0, min=0, ora=0;
TRISA=0; // initializare semnale PortA
TRISB=0; // initializare semnale PortB
InitLCD();
ClearLCD();
initTimer();
while(1){
if (cnt>=1000){
cnt=0;
sec++;
if(sec>=60){
min++;
sec=0;
};
if(min>=60){
min=0;
ora++;
};
if(ora>=24) ora=0;
char buffer[10];
sprintf(buffer, "%02i:%02i:%02i", ora, min, sec);
ClearLCD();
WriteString(buffer);
};
};
};

// definitii functii pentru temporizator


void interrupt isr() {
if(T0IF) {
cnt++;
T0IF = 0;
TMR0 = 6;
};
};

void initTimer (){


OPTION_REG &= 0XC1; // prescaler 1:4
T0IE = 1;
GIE = 1;
TMR0 = 6;
};

// definitii functii pentru LCD


// trimite un caracter pentru afisare

71
Microcontrolerul PIC16F84 – Aplicații practice

void SendData(char t){


lcd_RS = 1; // date
lcd_RW = 0; // scriere
lcd_DATA = t; // caracter
lcd_E = 1;
__delay_ms(1);
lcd_E = 0; // trecerea 0->1 initiaza scrierea
__delay_ms(1);
};

// trimite o comanda la LCD


void SendCmd(char t){
lcd_RS = 0; // comanda
lcd_RW = 0; // scriere
lcd_DATA = t; // cod comanda
lcd_E = 1;
__delay_ms(1);
lcd_E = 0; // trecerea 0->1 initiaza scrierea
__delay_ms(1);
};

// initializare LCD
void InitLCD(void){
__delay_ms(16);
SendCmd(0x30);
__delay_ms(5);
SendCmd(0x30);
__delay_ms(1);
SendCmd(0x30);
__delay_ms(1);
SendCmd(0x38); //function set: 8 bit interface, 2 lines, 5x8 character
SendCmd(0x08); // display off
SendCmd(0x01); //clear display
SendCmd(0x06); // entry mode set: direction increment, do not shift display
// activare LCD
SendCmd(0x0C); // display on, cursor off, cursor blinking off
};

// scrie un sir de caractere


void WriteString(const char *s){
while(*s)
SendData(*s++);
};

// sterge afisajul
void ClearLCD(void){
SendCmd(0x01);
};

72
Microcontrolerul PIC16F84 – Aplicații practice

LABORATOR 7
Memoria EEPROM. Citirea și scrierea de date

Scopul lucrării
Lucrarea de laborator prezintă memoria EEPROM și modul de citire și scriere a datelor. Studenții
la sfârșitul laboratorului vor avea noțiuni de programare a memoriei EEPROM din
microcontrolerul PIC16F84.

Breviar teoretic
Memoria EEPROM este formată dintr-o matrice de celule de memorie care, la rândul lor, sunt
formate din perechi de tranzistori ce au între ei un strat subţire de oxid izolator. Aceasta poate fi
ştearsă şi reprogramată (rescrisă) în mod repetat prin aplicarea unei tensiuni mai mari decât cea
generată de circuitul extern sau intern.
Microcontrolerul PIC16F84 conține intern 64 octeţi de date EEPROM în intervalul de adrese 00h
to 3Fh. Memoria de date EEPROM poate fi scrisă şi citită în timpul funcționării
microcontrolerului, dar nu este direct mapată în spaţiul regiștrilor de uz general, accesarea fiind
posibilă prin adresare indirectă cu ajutorul regiștrilor cu funcții speciale. Această memorie
rezistă la 10.000.000 de cicluri de scriere/ştergere. Informațiile inscripționate în memoria
EEPROM internă sunt păstrate pentru minim 40 de ani.
Regiștrii cu funcții speciale utilizați pentru programarea memoriei EEPROM sunt:
• EECON1 controlează procesul de scriere / citire
• EECON2 (registru neimplementat fizic)
• EEDATA stochează temporar octetul scris în sau citit din memoria EEPROM
• EEADR înregistrează adresa locaţiei EEPROM accesate. Pentru că pot fi accesate numai
64 locații de memorie, cei doi biți mai semnificativi vor fi întotdeauna 0 pentru a asigura
accesarea corectă a zonei de memorie.

73
Microcontrolerul PIC16F84 – Aplicații practice

Structura registrului EECON1 este reprezentată în fig.37.


bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
- - - EEIF WRERR WREN WR RD
Fig. 37: Structura registrului EECON1

Semnificația biților acestui registru este următoarea:


EEIF (EEPROM Write Operation Interrupt Flag) – semnalizează terminarea scrierii (1) sau
sau faptul că operațiunea nu a început sau este în curs de finalizare (0). După ce scrierea
s-a terminat cu succes, acest bit trebuie resetat din program pentru a putea detecta
astfel încheierea unei operații de scriere.
WRERR (EEPROM Error Flag) – semnalizează (1) apariția unei erori la scrierea în
EEPROM. Valoarea implicită (0) este modificată automat doar dacă scrierea este
întreruptă de un semnal MCLR sau de un reset provenit de la circuitul watchdog.
WREN (EEPROM Write Enable) – permite (1) sau previne (0) scrierea în memoria
EEPROM de date.
WR (Write Control) – inițiază (1) scrierea în memoria EEPROM de date. Bitul este resetat
automat de hardware la terminarea ciclului de scriere. Este permisă din software doar
setarea acestui bit.
RD (Read Control) - inițiază (1) citirea din memoria EEPROM de date. Bitul este resetat
automat de hardware la terminarea ciclului de citire. Este permisă din software doar
setarea acestui bit.
Compilatorul XC8 permite utilizarea unor funcții pentru scrierea și citirea din memoria EEPROM
de date. Prototipul funcției pentru scrierea în memoria EEPROM este următorul:

void eeprom_write(unsigned char address, unsigned char value);

La utilizarea acestei funcții trebuie ținut seama de faptul că funcția face doar inițializarea scrierii
și nu așteaptă finalizarea acestei operațiuni. De aceea, orice acces la memoria EEPROM trebuie
să testeze dacă nu cumva există o operațiune de scriere în curs prin testarea bitului WR din
registrul EECON1 astfel:

while(WR) continue;

Prototipul funcției pentru citirea din memoria EEPROM de date este următorul:

unsigned char eeprom_read(unsigned char address);

Ca și în cazul operațiunii de scriere, înaintea citirii din EEPROM trebuie testat dacă nu există o

74
Microcontrolerul PIC16F84 – Aplicații practice

operațiune de scriere în desfășurare.

Materiale didactice utilizate


Pentru realizarea lucrării de laborator sunt necesare:
• platforma experimentală Z11/EV cu microcontrolerul PIC16F84
• calculator personal cu sistem de operare Windows
• mediul integrat de dezvoltare MPLAB X
• utilitarul IC-Prog
• cablu serial de date
• sursa de alimentare PSLC/EV (±5V și ±12V) pentru Z11/EV și cablurile aferente

Desfășurarea lucrării

1. Tastatură cu memorie permanentă


Pentru funcționarea corectă a plăcii experimentale Z11/EV jumperul de configurare va fi pus pe
poziția J1.

Fig. 38: Schema electronică de conectare a tastaturii matriciale și a afișorului cu 7 segmente

75
Microcontrolerul PIC16F84 – Aplicații practice

Pornind de la schema simplificată din fig.38 va fi realizată o aplicație cu microcontrolerul


PIC16F84 care citește și afișează cifrele tastaturii matriciale la afișorul cu 7 segmente. Aplicația
memorează în regiștrii de uz general (memoria RAM) primele zece cifre apăsate și la apăsarea
tastei A afișează fiecare cifră memorată pentru 0,5 secunde. La apăsarea tastei B memoria este
ștearsă și pot fi stocate alte zece taste. Pentru memorarea permanentă a cifrelor, aplicația
folosește tasta C pentru a reține cifrele în memoria EEPROM de date. La apăsarea tastei D,
cifrele memorate în EEPROM sunt readuse în memoria RAM și apoi afișate la afișorul cu 7
segmente.
Codul sursă scris în C pentru această aplicație este următorul:

#include <pic.h>

#define _XTAL_FREQ 4000000 // frecventa oscilatorului pentru __delay_ms

unsigned char buffer_taste[10]; // memoreaza codul primelor 10 taste apasate


unsigned char pz_buffer = 0; // prima pozitie libera in buffer_taste

void afiseaza_buffer()
{
for(unsigned char i=0;i<10;i++){
if(buffer_taste[i]==0xFF) break;
PORTA = buffer_taste[i];
__delay_ms(500);
PORTA=0xFF; // sterge afisajul
__delay_ms(250);
};
PORTA=0xFF; // sterge afisajul
};

void sterge_buffer()
{
for(pz_buffer=0;pz_buffer<10;pz_buffer++)
buffer_taste[pz_buffer]=0xFF;
pz_buffer=0;
};

void scrie_EEPROM()
{
for(unsigned char i=0;i<10;i++){
while(WR) continue; // asteapta terminarea ciclului de scriere anterior
eeprom_write(i, buffer_taste[i]);
};
};

void citeste_EEPROM()
{

76
Microcontrolerul PIC16F84 – Aplicații practice

for(unsigned char i=0;i<10;i++){


while(WR) continue; // asteapta terminarea ciclului de scriere anterior
buffer_taste[i]=eeprom_read(i);
};
pz_buffer=0;
};

void main() {
unsigned char tasta; // memoreaza codul ultimei taste apasate
unsigned char tasta_apasata=0; // semnalizeaza daca a fost deja apasata o tasta
TRISA=0b00000000; // initializare semnale PortA
TRISB=0b11110000; // initializare semnale PortB
RB0=1; RB1=1; RB2=1; RB3=1; // toate coloanele matricii pe 1
PORTA=0xFF; // sterge afisajul
sterge_buffer();
while(1){
tasta=20; // nici o tasta valida apasata
RB0=0; // testare taste de pe prima coloana
if(RB4==0) tasta=1;
if(RB5==0) tasta=4;
if(RB6==0) tasta=7;
if(RB7==0) tasta=19; // tasta invalida
RB0=1;
RB1=0; // testare taste de pe a doua coloana
if(RB4==0) tasta=2;
if(RB5==0) tasta=5;
if(RB6==0) tasta=8;
if(RB7==0) tasta=0;
RB1=1;
RB2=0; // testare taste de pe a treia coloana
if(RB4==0) tasta=3;
if(RB5==0) tasta=6;
if(RB6==0) tasta=9;
if(RB7==0) tasta=19; // tasta invalida
RB2=1;
RB3=0; // testare taste de pe a patra coloana
if(RB4==0) tasta=10; // tasta A (afisare buffer_taste)
if(RB5==0) tasta=11; // tasta B (stergere buffer_taste)
if(RB6==0) tasta=12; // tasta C (scrie buffer_taste in EEPROM)
if(RB7==0) tasta=13; // tasta D (citeste buffer_taste din EEPROM)
RB3=1;
// daca tasta nu a fosta eliberata nu se mai face tratarea acesteia
if((tasta!=20) && (tasta_apasata==1)) continue;
switch(tasta){
case 20: // semnalizeaza elibeararea tastei
tasta_apasata=0;
break;
case 10: // afisare buffer_taste
tasta_apasata=1;
afiseaza_buffer();

77
Microcontrolerul PIC16F84 – Aplicații practice

break;
case 11: // stergere buffer_taste
tasta_apasata=1;
sterge_buffer();
break;
case 12: // scrie buffer_taste in EEPROM
tasta_apasata=1;
scrie_EEPROM();
break;
case 13: // citeste buffer_taste din EEPROM si afiseaza
tasta_apasata=1;
citeste_EEPROM();
afiseaza_buffer();
break;
default: // tratare cifra valida
tasta_apasata=1;
if(pz_buffer<10) buffer_taste[pz_buffer++]=tasta;
PORTA = tasta;
if((tasta>=0)&&(tasta<=9)){
__delay_ms(500);
PORTA=0xFF; // sterge afisajul
};
};
};
}

78
Microcontrolerul PIC16F84 – Aplicații practice

BIBLIOGRAFIE
1: Microchip, PIC16F8X 18-pin Flash/EEPROM 8-Bit Microcontrollers, 2002
2: Milan Verle, PIC Microcontrollers, 2008, http://www.mikroe.com/products/view/11/book-
pic-microcontrollers/
3: Displaytech, 162B LCD Module
4: M. Hnatiuc, Microcontrolere CISC si RISC. Arhitecturi si principii de programare, Editura
Nautica, Constanta, 2013, ISBN: 978-606-681-014-2
5: Cătălin J. Iov, Mircea B. Slănină, Proiectarea electronică cu PADS, Editura Nautica, Constanţa,
2014, ISBN: 978-606-681-043-2
6: Cătălin J. Iov, Mircea B. Slănină, High speed issues in electronics: Mentor Graphics HyperLynx,
Proceedings of ISEEE 2008, Galaţi, September 12-13, pag. 159 – 164, 2008, ISSN:1842-8046

79
Tiparul executat în
TIPOGRAFIA
UNIVERSITĂȚII MARITIME
Constanța

S-ar putea să vă placă și