Sunteți pe pagina 1din 32

1.

CE TREBUIE SĂ PROGRAMĂM

1.1. Introducere

Înainte de a ne apuca să scriem programe pentru aplicaţii cu microcontrolere, trebuie să


avem o imagine completă a produsului în care va ajunge să fi inclus programul scris şi apoi
o imagine completă a paşilor pe care trebuie să-i străbatem pentru ca proiectul să ajungă din
faza de idee în aceea de aplicaţie funcţională.

1.1.1. Ce este produsul final?


De regulă produsul final în care va fi inclus programul este un circuit cu microcontroler
care este încorporat într-un sistem gazdă (mecanic, electric, hidraulic, etc.), aşa cum este
sugerat în figura 1.1. Din acest motiv sistemele cu microcontroler mai sunt cunoscute sub
denumirea generică de sisteme încorporate (embedded systems). Un exemplu intuitiv de
astfel de sistem încorporat este sistemul de frânare ABS de la autovehicule. Acesta
controlează procesul de frânare în aşa fel încât roţile să nu se blocheze (să nu patineze)
niciodată, indiferent de cât de puternic se apasă pe pedala de frână şi de starea suprafeţei
drumului. În astfel de ipostaze, circuitul (sistemul) cu microcontroler trebuie să:
• preia prin intrările sale informaţii de la sistemul gazdă. Informaţiile sunt furnizate
de senzori și traductoare care furnizează semnale electrice despre: viteză, poziţie,
forţă, presiune, temperatură, starea unor contacte (închis / deschis), etc.
• controleze prin ieşirile sale diferite elemente ale sistemului gazdă, precum:
motoare de curent continuu, motoare pas cu pas, electrovalve, bobinele unor relee,
etc, conform cu programul înscris în memoria sa.

Sistem controlat

Sistem cu
Intrări Ieşiri
microcontroler

Figura 1.1. Microcontrolerul privit în contextul sistemului controlat


2 Ioan P. MIHU

1.1.2. Etapele realizării unei aplicaţii cu microcontroler

Foarte sintetic etapele realizării unei aplicaţii cu microcontroler, sunt cele din figura 1.2.

- Caiet sarcini
Idee
- Echipă dezvoltare Specificaţii
/
- Planificare dezvoltare tehnice
Comandă
- Costuri

Alegere
microcontroler

Specificaţii software Proiectare scheme


electrice şi electronice

Alegere furnizori
Proiectare
structură program Proiectare cablaj Feedback
PCB - Punere în funcţiune
Scriere program în C Realizare cablaj - Cerinţe noi
(PCB) - Scădere costuri
Montare piese
Compilare

Circuit electronic
Fişier .hex Programator cu
microcontroler

Software Hardware

Embedded system
Sistem funcţional funcţional
cu TESTE şi vândut !
microcontroler

Figura 1.2. Etapele realizării unei aplicaţii cu microcontroler

Ideea realizării unei aplicaţii cu microcontroler se naşte fie din pasiune (în cazul
studenţilor) fie ca cerinţă a unui beneficiar. În ambele cazuri, trebuie formulate foarte clar
obiectivele care urmează a fi realizate. În cazul în care produsul se realizează ca urmare a
cererii unui beneficiar, obiectivele se prezintă sub forma unui caiet de sarcini care va face
parte integrantă din contractul de realizare al produsului.
Din acest moment coordonatorul proiectului va trebui să elaboreze o strategie în care
trebuie să aibă în vedere: componenţa echipei de realizare a proiectului, termene pentru
fiecare etapă, colaborări cu terţi atunci când este cazul, evaluare soluţii tehnice, alegerea
furnizorilor pentru piesele electronice, etc. O altă misiune importantă este aceea de a
extrage din caietul de sarcini transmis de beneficiar, specificaţiile tehnice necesare
proiectării inginereşti.
ANSI-C pentru microcontrolere 3

Dezvoltarea produsului începe cu componenta de hardware. Este firesc să fie aşa, fiindcă
programul trebuie să fie conceput pentru a rula pe acest circuit, deci abia după ce se cunosc
detaliile hardware importante ale plăcii electronice, se poate trece la proiectarea
programului.
Prima etapă a realizării circuitului electronic este proiectarea schemei electrice şi
electronice. Placa va trebui să conţină pe lângă microcontroler şi alte circuite precum:
circuite alimentare, circuite de adaptare a tensiunilor de intrare la posibilităţile
microcontrolerului, circuite driver pentru amplificarea / adaptarea semnalelor de ieşire ale
microcontrolerului la necesităţile elementelor comandate (motoare pas cu pas, motoare de
curent continuu, etc). După proiectarea schemei electrice se trece la proiectare circuitului
imprimat, iar apoi la popularea sa cu componente electronice.
Tehnologia de realizarea a circuitului se alege în funcţie de volumul de producție ce
urmează a fi realizat. Astfel dacă trebuie realizate produse unicat sau serii foarte mici,
atunci circuitul se va realiza în tehnologii cât mai simple, inclusiv cele manuale. Dacă în
schimb trebuie realizate produse de serie mare şi foarte mare se vor alege tehnologii
automatizate, linii de fabricaţie. Dacă resursele companiei sunt limitate, sau experienţa în
realizarea unor produse complexe este redusă, atunci se poate apela la companii specializate
pentru realizarea circuitului electronic.
Echipa de software trebuie să intre în scenă după ce este conturat proiectul hardware,
deoarece abia acum se pot face specificaţiile necesare pentru programatori: care pini ai
microcontrolerului sunt intrări, care sunt ieşiri, parametri mărimilor de intrare, parametri
mărimilor de ieşire, etc. După ce se proiectează o structură a programului, se scrie apoi
programul în limbaj C şi apoi se compilează. Finalitatea muncii de programare este fişierul
ce conţine codul executabil. Acesta trebuie încărcat în memoria microcontrolerului, lucru ce
se realizează cu ajutorul unui circuit de programare, numit „programator”.
Principial există două mari opţiuni de programare: alegerea de a programa în limbaj de
asamblare sau în limbaj C. Un proiect care îţi ia o săptămână să îl programezi în C, îţi va
lua cu siguranţă o lună dacă vrei să-l programezi în asamblare. Orice program care în
limbaj de asamblare depăşeşte 2k devine un coşmar. Apar atât de multe variabile cărora
trebuie să le urmăreşti evoluţia, atât de multe efecte colaterale ale apelului fiecărei
subrutine, încât este imposibil să finalizezi proiecte mari în limbaj de asamblare.
În contextul realizării de proiecte ample, decizia de a programa în C sau de a programa în
limbaj de asamblare nu este greu de luat: pentru eficientizare trebuie programat în C.
Compilatoarele de C au însă două dezavantaje: costă și în plus generează cod executabil
mult mai mare decât compilatoarele pentru limbaj de asamblare, de aproximativ 3 ori mai
mare pentru aplicaţii simple şi aproximativ dublu pentru aplicaţiile mari. De aceea
programarea în limbaj de asamblare mai are un cuvânt de spus. De exemplu, o soluţie de
optimizare a programelor scrise în C, este folosirea unor porţiuni scrise în asamblare.
Am ajuns în alt punct în care trebuie luată o decizie şi anume ce compilator vom folosi.
Criteriul esenţial în alegerea compilatorului rămâne costul. Din nefericire, datorită
arhitecturii diferite a fiecărei familii de microcontrolere PIC, există puţine compilatoare
gratuite sau cu preţ foarte scăzut, aşa cum au spre exemplu microcontrolerele ATMEL
(GNU C Compiler dezvoltat pe sourceforge.net). Principalele compilatoare aflate în
discuţie sunt: Microchip, Hi_Tech și CCS. Iată în sinteză câteva elemente care ne pot ajuta
la luarea deciziei:
4 Ioan P. MIHU

a) Compilatoare pe 8 biţi, pentru familiile PIC12/16. Compania Microchip oferă


gratuit compilatorul pentru limbajul de asamblare, dar nu oferă compilatoare
pentru C. Se folosesc compilatoarele unor firme partenere:
• Compilatoare free sau student version. De regulă acestea limitează codul
rezultat la 2K și nu permit decât folosirea primelor 2 bank-uri de memorie
RAM. Au avantajul mare că permit dezvoltarea de aplicaţii educative. Dintre
acestea menţionez: Hi-Tech-Lite (cu foarte puţine restrângeri faţă de
versiunea profesională), Micro C, BoostC, WIZ C.
• Compilatoare profesionale, nu au constrângeri privind mărimea codului, ba
dimpotrivă au facilităţi de optimizare a codului. Sunt cele recomandate
pentru realizarea aplicaţiilor comerciale mari. Dintre acestea menţionez: Hi-
Tech, CSS, IAR, BoostC, Micro C, WIZ C.
b) Compilator pe 8 biţi, pentru familia PIC18.
• Microchip oferă compilatorul C18, care are o versiune free (student edition).
• Hi-Tech oferă compilatorul pentru PIC18, şi în versiunea Lite.
c) Compilatoare pe 16 biţi, pentru familiile PIC24/dsPIC30/ dsPIC33.
• Microchip oferă compilatorul C30, care are o versiune free (student edition).
• Hi-Tech oferă compilatorul pentru familia PIC18.
d) Compilatoare pe 32 biţi, pentru familiile PIC32.
• Microchip oferă compilatorul C32, care are o versiune free (student edition).
• Hi-Tech oferă compilatorul pentru PIC32 şi în versiunea Lite.
Compilatoarele Microchip şi Hi-Tech sunt complet compatibile ANSI C, pe când
compilatoarele CCS având incluse funcţii ce simplifică programarea anumitor părţi
hardware, nu sunt complet compatibile. Aceasta face ca funcţii scrise pentru CCS, să nu fie
portabile în proiecte ce folosesc compilatoarele C18, C30 sau Hi-Tech.
Toate compilatoarele menţionate mai sus pot fi instalate gratis, pentru o perioadă limitată,
perioadă în care compilatorul poate fi testat. Exceptând situaţiile în care se fac alte
precizări, toate detaliile pe care le vom prezenta pe parcursul acestei lucrări, se referă la
compilatorul Hi-Tech Lite.
Una dintre cele mai importante etape ale realizării produsului o constituie cea de testare.
Testarea trebuie să aibă în vedere atât partea de hardware cât mai ales cea de software.
Există domenii de activitate în care volumul de muncă şi de fonduri alocate testării îl
depăşeşte cu mult pe cel dedicat proiectării, ca de exemplu în astronomie, aviaţie, etc.
Specificaţiile de testare trebuie să acopere toate situaţiile în care produsul ar putea ajunge.
Echipa de testare trebuie să dovedească extrem de multă imaginaţie, pentru a acoperi şi cele
mai greu de imaginat situaţii în care poate ajunge un produs. Aplicaţiile în care sunt
implicaţi un număr foarte mare de parametri de intrare și de ieşire, necesită eforturi
deosebit de mari pentru a putea acoperi toate combinaţiile posibile. În aceste situaţii, în
multe cazuri se realizează echipamente de testare automată.
Nu trebuie să minimizăm interesul pentru costul produsului. În condiţiile economiei de
piaţă, una dintre cele mai apăsătoare cerinţe pe care trebuie să o aibă în faţă cel ce
realizează produsul este cea legată de costul final al acestuia. Oricât de bun ar fi un produs,
dacă nu se vinde, el nu serveşte la nimic din punct de vedere economic. Pe băncile şcolii,
studentul nu trebuie să gândească prioritar la acest lucru, dar trebuie să fie conştient de el.
ANSI-C pentru microcontrolere 5

1.2. Realizarea aplicaţiei în mediul MPLAB

Fiecare firmă producătoare de microcontrolere pune la dispoziţie sau recomandă folosirea


unor medii integrate de dezvoltare a aplicaţiilor (Integrated Development Environment) şi a
unor compilatoare specifice. Astfel firma Microchip pune la dispoziţie mediul MPLAB
IDE. Acest mediu este un set de instrumente dedicate programării microcontrolerelor de tip
PIC și DSPIC folosite în realizarea de aplicaţii de tip embedded systems. MPLAB este
gratis şi poate fi uşor utilizat de studenţi. Există de asemenea și o bibliotecă impresionantă
de exemple care oferă numeroase soluţii pentru o mare diversitate de aplicaţii. Interfaţa
MPLAB este cea din figura 1.3.

Breackpoint

Figura 1.3. Mediul de programare MPLAB

Instalarea MPLAB se face simplu după ce aducem fişierul de instalare de pe pagina de


Internet a firmei Microchip. MPLAB oferă numeroase facilităţi pentru programarea
6 Ioan P. MIHU

microcontrolerelor. Vom prezenta succint elementele și paşii ce trebuie făcuţi pentru


realizarea unui proiect prin care să reuşim programarea microcontrolerului.
1.2.1. Crearea proiectului.

Conceptul de proiect este folosit în multe medii de programare de nivel înalt şi este foarte
util fiindcă înglobează într-un singur fişier .mpc adresele tuturor resurselor (fişierelor),
compilatorul ales, setările compilatorului şi generării fişierelor proiectului (output). Pentru
a crea un nou proiect cea mai simplă cale este folosirea meniului Project / Project Wizard,
unde sunt indicaţi toţi paşii necesari. Un nou proiect se poate crea şi astfel:

Figura 1.4. Structura de directoare a proiectului

a) Creaţi mai întâi un director în care veţi plasa noul proiect. Acest director va conţine
grupate eficient toate fişierele care vor fi implicate în proiect. Trebuie să realizăm
structura de fişiere din figura 1.4 dacă dorim ca proiectul să fie portabil, adică
directorul proiectului să poată fi mutat în oricare altă parte, sau pe alt calculator.
Popularea directoarelor cu fişiere este următoarea:
• În directorul source se introduc fişierele sursă (.c) şi fişierele header (.h)
aferente fişierelor .c. Dacă se doreşte crearea unui director pentru fişierele
header aferente fişierelor sursă .c, acesta trebuie să fie în directorul source!
• În directorul output ajung toate fişiere generate de compilare şi de
construcţia proiectului Project / Build:
• fişierele .cof, .hex rezultatele finale ale compilării. Fişierul .hex conţine
codul executabil, cel care va ajunge în memoria flash a
microcontrolerului.
• fişierul .lst care conţine translatarea codului sursă .c în limbaj de
asamblare.
• fişierul .map care conţine modul în care va fi plasat codul executabil
• În directorul include se pot introduce:
• fişiere care conţin definiții de configurare a biţilor şi porturilor
microcontrolerului (exemplu: pic.h)
• fişiere cu funcţii standard, numite și fişiere antet. (exemplu: stdio.h)
ANSI-C pentru microcontrolere 7

Ambele tipuri de fişiere se găsesc în zone standard de includere ale


compilatorului C (stdio.h), sau ale mediului MPLAB (pic.h). Le putem copia
în directorul include, pentru a face programul nostru portabil, independent de
locul în care se găsesc aceste programe pe alte calculatoare. Fişierele header
(.h) aferente fişierelor sursă (c.) nu se introduc aici, ci în directorul source.
• În directorul lib se introduc fişiere librării (.lib) dacă este cazul.
• În directorul lkr se introduc fişiere obiect (.obj) rezultate în urma compilării
altor resurse, dacă este cazul.
• În directorul temp se pun fişiere auxiliare (documentaţii, date catalog, ciorne,
etc.). Acestea nu fac parte din proiect dar dacă sunt plasate aici sunt rapid
accesibile din fereastra proiect.mcw.

b) După construirea structurii directoarelor, trebuie „înştiinţat” mediul MPLAB unde


sunt aceste directoare (trebuie setată calea de căutare a fişierelor). Acest lucru se
realizează din fereastra Directories din meniul Project / Buit options / Project. În
figura 1.5, este arătat un exemplu de introducere a căii relative la directorul
proiectului. Se poate introduce şi calea absolută, însă doar folosind calea relativă
proiectul va deveni portabil. Tot din această fereastră se alege și calea pentru
directorul output, selectând-l din Shw directories for:

Figura 1.5. Setarea locaţiilor directoarelor proiectului


8 Ioan P. MIHU

Save Workspace

Figura 1.6. Crearea unui nou proiect

c) Folosiţi apoi, fie meniul Project / New, fie iconiţa din bară, ca în figura 1.6.
Numele proiectului poate fi acelaşi cu al directorului care îl găzduiește. Este
recomandat să daţi nume cât mai sugestive dar care să nu fie prea lungi.
d) Acţionând iconiţa Save Workspace se va genera un fişier .mcw care „ţine minte”
toate setările: microcontrolerul selectat, biţii de configurare, programatorul
selectat, ferestrele active, programul debug ales, breackpoint, etc. În acest fel
putem relua lucrul exact acolo unde a fost întrerupt.
e) După crearea proiectului apare fereastra Project.mcw, care ne propune includerea
de fişiere în proiect (figura 1.7). Această fereastră este accesibilă din View /
project. Primul fişier pe care îl vom include este fişierul sursă .c care conţine
funcţia main(). În prealabil fişierul se creează cu editorul MPLAB din meniul File
/ New și se salvează în directorul proiectului.
f) Includerea fişierului .c în proiect se face cu click dreapta pe Source File din
fereastra Projec.mcwt. Din acest moment, fişierul face parte din proiect.

Figura 1.7. Project Window - includerea de fişiere în proiect

g) Vor fi incluse apoi în proiect și alte fişiere: header (.h), object (.obj), library (.lib)
dacă este cazul. În secţiunea Other File se pot adăuga fişierele de documentaţie
ANSI-C pentru microcontrolere 9

(.doc, .pdf, etc). Ele nu sunt incluse în proiect dar sunt utile pe durata dezvoltării,
fiind extrem de uşor accesibile prin simplul click pe numele acestuia.

1.2.2. Meniuri principale MPLAB

Aceste meniuri reprezintă facilităţi care servesc la dezvoltarea proiectului. Acestea se


selectează din meniurile afişate în bara superioară. Iată câteva dintre ele:
a) Configure. Permite selectarea microcontrolerului care se găseşte pe placa
electronică, şi pentru care scriu programul în fereastra Select Device, figura 1.8.
Tot aici sunt indicate cu buline verzi şi principalele instrumente existente pentru
microcontrolerul ales. Alte detalii despre configurarea microcontrolerului
(Configuration bits) vor fi prezentate în capitolul 13.

Figura 1.8. Selectarea microcontrolerului

b) Project. Cel mai important element al mediului de dezvoltare rămâne compilatorul


de limbaj C. Despre alegerea compilatorului, am vorbit deja. După ce se
instalează, compilatorul devine un plug-in al mediului MPLAB, adică un
accesoriu care-i extinde funcţionalitatea. Pot fi instalate mai multe compilatoare.
Pentru un proiect, va trebui selectat doar un singur compilator.
• Alegerea compilatorului dintre compilatoarele instalate se face în fereastra de
dialog Project / Select Language Toolsuite, ca în figura 1.9. Lista
10 Ioan P. MIHU

compilatoarelor instalate este accesibilă în Active Toolsuite. După alegere


compilatorul devine integrat în MPLAB.

Figura 1.9. Selectarea compilatorului


• Opţiunile de compilare sunt accesibile din meniul Project / Build Option /
Project. Pentru început este bine să păstraţi opţiunile implicite (defalut).
• Compilarea programului şi generarea fişierelor proiectului se face din meniul
Project / Build . Căi mai rapide de compilare sunt iconiţa Build (figura 1.10)
din bara principală sau tasta F10.

Project / Build
compilează fişierele sursă;
construieşte proiectul.

Figura 1.10. Compilarea programului.

• Rezultatul compilării este prezentat sintetic în două moduri:


• În fereastra View / Output, sunt prezentate în cifre volumul ocupat în
memoria RAM, volumul ocupat în memoria flash de fişierul executabil.
• În fereastra View / Memory Usage Gauge este prezentat în mod grafic
volumul de memorie folosit de date şi de program.
Meniul Project oferă şi alte facilităţi dintre care o parte vor fi prezentate în
următoarele capitole ale acestei lucrări. Pentru detalii şi pentru exploatarea
resurselor oferite de MPLAB, consultaţi informaţiile oferite în Help, sau în paginile
de Internet aferente.
c) View. Permite alegerea ferestrelor pe care le dorim vizibile. Ferestrele permit
interfaţa propriu-zisă cu programul. Cele mai importante ferestre sunt View /
Project și View / Output care sunt vizibile implicit. O altă fereastra importantă
este fereastra Editor, cea în care se editează fişierele sursă. Se deschide cu dublu
click în fereastra Project.mcw, pe numele fişierului dorit. Celelalte ferestre sunt
utile în procesul de depanare şi de testare a programului. În ideea de a nu încărca
ANSI-C pentru microcontrolere 11

ecranul cu prea multă informaţie, este bine să închidem ferestrele pe care nu le


utilizăm.

1.2.3. Controlul și depanarea programului

a) Debuger. Permite selectarea modului de depanare a programului. Este o fereastră


foarte mult utilizată de programatori, fiindcă dezvoltarea unui proiect
(programarea) presupune un volum mare de lucru pentru depanare, testare şi
punere la punct a programului. Din submeniul Debuger / Select Tool, se poate
alege soluţia dorită. Recomandate sunt:
• MPLAB SIM (figura 1.11). Acesta este simulatorul de bază al mediului
MPLAB. El permite testarea şi depanarea programului fără ca acesta să fie
introdus în memoria microcontrolerului. Practic se simulează funcţionarea
microcontrolerului pe PC-ul pe care avem instalat MPLAB. Dacă programul
foloseşte şi porturi de intrare atunci este posibilă generarea unor semnale
virtuale de intrare. Acestea se creează în fereastra Debuger / Stimulus;
• ICD2, ICD3, PICkit2, sunt circuite de programare şi depanare în circuit.
Aceste se conectează între PC și microcontrolerul montat (lipit) pe placa
electronică. Acest mod de depanare se efectuează în condiţii reale, când
aplicaţia funcţionează în sistemul pentru care a fost proiectată.

Figura 1.11. Selectarea simulatorului MPLAB SIM

După selectarea MPLAB SIM, în bara de meniuri apar iconiţele din figura
1.12 cu care putem controla fluxul programului în regim de simulare.
Simularea are ca scop vizualizarea evoluţiei variabilelor programului, în
ferestrele Watch, Disassembly Listing, File Registers, sau Program
Memory.
12 Ioan P. MIHU

Run Animate Step Reset Breackpoints


Rulează până la Rulează lent, Rulează linie cu linie Opreşte Vizualizarea tuturor
primul breackpoint linie cu linie la click stânga pe icon rularea punctelor de oprire

Figura 1.12. Controlul fluxului programului, în regim de simulare

b) Breackpoints. Permite vizualizarea și managementul punctelor de oprire în


program (breackpoint). Introducerea punctelor de oprire se face mai simplu cu
dublu click în editorul fişierului sursă .c, în faţa liniei la care dorim să oprim
rularea.
c) Watch. Este fereastra în care sunt afişate variabilele programului. Este extrem de
utilă în procesul de depanare (debug) a programului. Variabilele pe care dorim să
le afişăm se selectează din lista de variabile a ferestrei. Putem inspecta valorile
variabilelor în diferite formate (binar, decimal, hexa). Pentru a profita de această
facilitate, trebuie:
• să fie selectat Debuger / Select Tool / MPLAB SIM;
• să fie folosite puncte breackpoint în codul sursă. Simularea programului se
va „opri” la linia din program la care a fost ataşat breackpoint. Din acest
moment, putem folosi butoanele destinate rulării pas cu pas aflate în meniul
din bara superioară sau tastele F7 și F8.

d) Disassembly Listing, File Registers, Program Memory, sunt alte ferestre care
permit urmărirea (”spionarea”) evoluţiei variabilelor pe parcursul paşilor din
program.
e) Output window. Aici sunt afişate informaţii importante în urma procesului de
compilare. Dacă nu avem greşeli în program și compilarea este reuşită, atunci aici
vor fi afişate informaţii sintetice legate de resursele de memorie utilizate de
program. Dacă compilarea este nereuşită, atunci aici sunt afişate informaţii legate
de locul şi tipul greşelii din program.

1.2.4. Programarea microcontrolerului

a) După compilare, programul executabil rezultat (fişierul .hex) trebuie transferat în


memoria microcontrolerului aflat pe placa aplicației proiectate. Pentru aceasta
trebuie selectat „aparatul” cu care vom face transferul. Acesta se numeşte
programator. Selecţia programatorului se face din fereastra Programmer. După
selecţie în meniul principal apar iconiţele din figura 1.13.
b) Cele mai utilizate programatoare sunt: ICD2, ICD3, PICkit 1, PICkit 2, PICkit 3,
PICSTART Plus.
c) Înainte de selectarea programatorului, trebuie:
• Să conectăm fizic programatorul între PC și placa cu microcontroler.
ANSI-C pentru microcontrolere 13

• Să precizăm dacă placa are propria sursă de alimentare sau este alimentată
doar de programator, alimentat la rândul său la +5V prin USB de la calculator.
• Să fie corectă conectarea la placa cu microcontroler. Selectați 'Altă tensiune'
în meniul de dialog, atunci când microcontrolerul este alimentat la 3,3V (alta
decât 5V), deoarece tensiunea de la placă ajunge la programator şi invers.
d) După programare, pentru ca aplicația să ruleze există două soluții: fie se apasă
butonul 'Relese from reset', fie se deconectează placa de la programator.
e) Programatoarele menționate permit și depanarea în circuit a programului, după ce
acesta a fost încărcat în microcontroler. Pentru aceasta, se urmează procedura
descrisă la 1.2.3, doar că se va selecta programatorul folosit.

Write flash Reset and


Transferă .hex în Conect
flash memory to ICD2

Read flash Verify program Erase Verify Release from Hold in Reset
Încarcă .hex din Programarea flash erase flash reset
flash memory este OK ? memory memory. Rulează ! Oprește rularea

Figura 1.13. Programare în circuit.


14 Ioan P. MIHU

2. UN PIC DESPRE STRUCTURA MICROCONTROLERULUI

2.1. Structura unui microcontroler

Obiectivul declarat al acestei cărți este acela de a prezenta limbajul de programare C în


contextul folosirii lui pentru realizarea de sisteme încorporate (sisteme cu microcontrolere).
Fiindcă acest capitol are un conţinut hardware este firesc ca studentul, care de regulă
doreşte să devină programator, să-şi pună astfel de întrebări:
• Chiar trebuie să cunoaştem elemente de hardware, precum memorie, porturi,
regiștri, etc, ca să putem programa un microcontroler ?
• Nu putem programa în C un microcontroler fără să ştim şi cum este acesta făcut?
Răspunsul este unul singur: nu putem programa un microcontroler fără a-i cunoaşte
elementele esenţiale ale structurii ! Este foarte importantă cunoaşterea microcontrolerului,
și în special a felului în care este organizată memoria sa, pentru a înţelege corect modul de
lucru cu variabile, cu pointeri.
Microcontrolerele din familia PIC au o arhitectură Harward, având zona de date separată de
zona de program, fiecare din aceste zone fiind deservite de câte o magistrală. Pentru a
prezenta elementele structurii, ne vom referi în continuare la microcontrolerul PIC16F877,
a cărui structură este prezentată în figura 2.1. Primul lucru pe care îl remarcăm este
prezenţa a 3 zone de memorie, toate aflate în interiorul chip-ului:
• memoria RAM (RAM file register) organizată în cuvinte de câte 8 biţi;
• memoria EEPROM (Data EEPROM) organizată în cuvinte de câte 8 biţi;
• memoria PROGRAM (FLASH Program Memory) organizată în cuvinte de 14 biţi
Mai trebuie să mai reţinem şi prezenţa mai multor periferice printre care:
• 5 porturi de intrare ieşire (I/O), conectate fizic la pinii capsulei microcontrolerului;
• convertor analog - numeric (A/D);
• 3 circuite de temporizare (Timer);
• port de comunicaţie serială sincronă.
Toate perifericele au câte o adresă fizică și pot fi accesate (citite sau scrise) de către
unitatea centrală, pe durata rulării programului înscris în FLASH Program Memory.
Legătura dintre periferice, memoria RAM și unitatea centrală ALU, se face prin magistrala
de date, care la PIC16F877 are 8 biţi, deci procesează date pe 8 biţi. În funcţie de mărimea
magistralei de date, microcontrolerele pot prelucra date pe mai mulţi biţi astfel:
• Microcontrolere pe 8 biţi – familiile PIC12, PIC16, PIC18;
• Microcontrolere pe 16 biţi – familiile PIC24, dsPIC30, dsPIC33;
ANSI-C pentru microcontrolere 15

• Microcontrolere pe 32 biţi – familiile PIC32.


Pentru a nu avea un număr mare de pini, acelaşi pin poate fi programat la un moment dat:
• să fie: intrare (digitală sau analogică), ieşire digitală sau în stare de înaltă impedanţă;
• să îndeplinească o anume funcţie: I/E, clock.

Figura 2.1. Structura generală a microcontrolerului PIC16F877

2.2. Memoria RAM

Memoria RAM (RAM file register) este cea în care se află variabilele programului pe
care-l vom scrie. Organizarea acestei zone de memorie (harta memoriei) este prezentată în
figura 2.2. Constatăm existenţa a două categorii:
16 Ioan P. MIHU

• Zona Regiştrilor Speciali (Special Function Registers). Aceştia sunt folosiţi de


către unitatea centrală şi de către periferice, pentru a controla activitatea hardware
(electrică) a microcontrolerului.
• Zona variabilelor alocate prin program. Variabilele de orice tip pe care le vom
declara pe parcursul scrierii programului, vor fi plasate în zonele încercuite
(General purpose registers) din memoria RAM a microcontrolerului. Nu există
alte zone în care se pot afla variabile.

Figura 2.2. Organizarea memoriei RAM, pentru PIC16F877

Din analiza informaţiilor prezentate în figura 2.2, rezultă câteva concluzii legate de numărul
și dimensiunea variabilelor pe care le vom declara:
• Din totalul de 512 (1FFh) octeţi existenţi în RAM, spaţiul total pe care-l putem
aloca variabilelor alocate prin program este de 96+80+96+96 = 368 octeţi, restul
fiind destinaţi regiştrilor speciali. Altfel spus am putea crea un program în care să
avem cel mult 368 variabile de tip char.
ANSI-C pentru microcontrolere 17

• În fiecare din bank-urile 2 şi 3, există câte 16 octeţi care pot fi accesaţi numai prin
pointeri.
• Dacă avem variabile de tip tablou, dimensiunea maximă a acestora nu poate depăşi
dimensiunea permisă a bank-ului în care o declarăm. Spre exemplu am putea crea
un program în care să avem patru variabile tablou, ale căror dimensiuni maxime
vor putea fi:
unsigned char x[96];
unsigned bank1 char y[80];
unsigned bank2 char z[96];
unsigned bank3 char w[96];
• Atunci când creăm un program, trebuie să gestionăm cu mare atenţie dimensiunea
şi locul în care plasăm variabilele.
• Pe parcursul scrierii programului, variabilele se apelează numai prin numele lor
fără a mai preciza și bank-ul şi adresa la care se află. Acest lucru este un mare
avantaj faţă de programele scrise în limbaj de asamblare unde trebuie să fim foarte
atenţi ca de fiecare dată când utilizăm o variabilă să precizăm în ce bank se găseşte.
• Nu scăpăm însă de problema accesului la variabile din bank-uri diferite atunci când
acestea sunt apelate prin pointeri. Pentru a apela prin adresă o variabilă din bank0
sau bank1 avem nevoie de un singur octet, fiindcă aşa cum se observă din figura
2.2, aceste două bank-uri ocupă zona de la 00h, până la FFh adică 256 locaţii. În
schimb, pentru a apela prin adresă variabile din bank2 sau bank3, un singur octet
nu ajunge. Probabil că soluţia pe care o sugeraţi imediat este aceea ca orice
variabilă de tip pointer să aibă rezervaţi doi octeţi. Nu s-a făcut aşa, probabil din
motive ce ţin de istoria dezvoltării microcontrolerelor, la primele fiind suficient un
octet, deoarece nu aveau decât un singur bank de RAM.
• Pentru rezolvarea problemei există două soluţii.
• Folosirea pointerilor de tip 'const'. Aceştia au alocaţi doi octeţi și pot
accesa orice bank. Dezavantajul este că nu pot fi folosiţi decât pentru a
citi variabile aflate la adresele pe care le pointează.
• Folosirea pointerilor de tip 'far'. Aceştia pot accesa adrese din orice bank,
şi pot fi folosiţi atât pentru a citi cât şi pentru a scrie la adresa respectivă.
• Dacă la compilarea programului apar mesaje de eroare sau de atenţionare trebuie
citită cu atenţie documentaţia de programare a microcontrolerului cu care se
lucrează.

2.3. Memoria EEPROM

Memoria EEPROM este cea în care se află toate constantele programului pe care-l vom
scrie. Constantele de orice tip pe care le vom declara pe parcursul scrierii programului, se
vor afla numai în EEPROM, neexistând alte zone în care acestea se pot afla. De aici câteva
concluzii legate de numărul și dimensiunea constantelor pe care le vom declara.
• Memoria EEPROM a unui PIC 16F877 are dimensiunea de 256 octeţi. În
concluzie nu putem avea mai mult de 256 constante a câte un octet.
• Memoria EEPROM este nevolatilă. Valorile înscrise în ea nu se şterg la
întreruperea tensiunii, la resetarea sau la reprogramarea microcontrolerului.
18 Ioan P. MIHU

Aşa cum am mai precizat, gestiunea variabilelor alocate prin program şi care se găsesc în
memoria RAM, este foarte uşoară: atât pentru citire cât şi pentru scriere, variabilele se
apelează numai prin numele lor fără a mai preciza și adresa la care se află.
Nu acelaşi lucru se întâmplă cu datele pe care le vom scrie şi citi din memoria EEPROM a
microcontrolerului. Pentru citire trebuie folosiţi următorii regiştri:
EEADR Registrul în care trebuie specificată adresa de la care vom citi data
EEPGD Registrul care specifică adresa de început a zonei EEPROM
EEDATA Registrul în care se citeşte valoarea
RD Bit care la setare va iniţia ciclul de citire
Iată un exemplu de funcţie care returnează un octet de la o adresă EEPROM specificată
unsigned char ReadByteFromEEPROM(const unsigned char address)
{
unsigned char read_value; // Variabilă pentru a reţine valoarea citita
EEADR = address; // Citeşte de la adresa aceasta
EEPGD = 0; // Pointează la începutul EEPROM memory
RD = 1; // Iniţiază un ciclu de citire
read_value = EEDATA; // Preia octetul din registrul de date
return read_value; // Returnează octetul read byte
}
Scrierea unui octet este și mai laborioasă. Iată regiştri şi biţii folosiţi la scriere:
EEADR Registrul în care trebuie specificată adresa la care vom scrie data;
EEPGD Registrul care specifică adresa de început a zonei EEPROM;
EEDATA Registrul în care se depune octetul ce se va înscrie apoi în EEPROM;
WREN Bit pentru validarea scrierii în EEPROM;
WR Bit care la setare va iniţia ciclul de scriere în EEPROM;
EECON2 Registru de control al scrierii în EEPROM
EEIF Bit care semnalizează terminarea unui ciclu de scriere.
Pe durata scrierii:
• trebuie blocate alte întreruperi pentru a nu fi perturbat procesul de scriere;
• trebuie aşteptat până la terminarea completă a procesului de scriere.
Iată un exemplu de funcţie care scrie un octet la o adresă EEPROM specificată
void WriteByteToEEPROM(unsigned char data, const unsigned char address)
{
EEADR = address; // Adresa la care se scrie
EEDATA = data; // Data care se scrie
WREN = 1; // Validează scrierea în EEPROM
GIE = 0; // Invalidează întreruperile pe durata scrierii
EECON2 = 0x55; // Configurează EECON2 pentru scriere
EECON2 = 0xAA; // Configurează EECON2 pentru scriere
WR = 1; // Inițiază ciclul de scriere
while(!EEIF); // Aşteaptă până se termina scrierea
WREN = 0; // Ștergere soft a bitului WREN (validare scriere)
EEIF = 0; // Ștergere soft a bitului EEIF (terminare scriere)
}
În concluzie:
ANSI-C pentru microcontrolere 19

• Zona de memorie EEPROM a unui microcontroler fiind nevolatilă, poate fi folosită


pentru memorarea unor date importante. Pentru a putea utiliza ulterior aceste date,
trebuie să reţinem adresa la care le-am scris.
• Tot într-o zonă de memorie EEPROM sunt înscrise şi valori care nu pot fi
modificate, precum: data fabricaţiei, etc.
• Memoria de tip EEPROM se poate extinde în afara chip-ului microcontrolerului,
cu aşa numite "chip-uri de memorie". Acestea au de regulă memorie nevolatilă de
tip EEPROM şi se folosesc atunci când trebuie salvate un volum mare de date
esenţiale.

2.4. Memoria PROGRAM

Memoria PROGRAM este o memorie de tip ROM (Read Only Memory), în care se înscrie
codul executabil, cel rezultat după compilare. Acest cod este de regulă un fişier cu extensia
'.hex'. Acesta se înscrie în microcontroler o singură dată la început, cu ajutorul unui circuit
de programare, numit programator. După ce acest cod executabil este înscris în memoria
program, se zice că "microcontrolerul este programat". Termenul folosit pentru soft-ul
aflat în memoria program a unui microcontroler este de firmware. Nici un bit din conţinutul
aceste memorii nu se modifică pe durata rulării aplicaţiei.
După ce microcontrolerul este programat şi este montat în circuitul electronic dedicat
aplicaţiei, atunci când este rulată aplicaţia, din memoria program sunt citite una după alta
cuvintele ce reprezintă pentru microcontroler instrucţiuni de efectuat. Memoria program nu
este organizată "pe octeţi", ci pe cuvinte a căror lungime depinde de tipul
microcontrolerului. În funcţie de acest criteriu, microcontrolerele având magistrala de date
pe 8 biţi, se clasifică în:
• Baseline processor - au instrucţiunile pe 12 biţi. Sunt microcontrolerele din
familia PIC12.
• Midrange processor - au instrucţiunile pe 14 biţi. Sunt microcontrolerele din
familia PIC16. (Exemplu PIC16F877)
• High-end processor - au instrucţiunile pe 16 biţi. Sunt microcontrolerele din
familia PIC18.
Evident, cu cât este mai lung cuvântul (instrucţiunea) din memoria program, el poate
gestiona activităţi mai complexe la nivelul unităţii centrale a microcontrolerului, și implicit
la nivel hard. Consecinţa imediată este aceea că microcontrolerele având instrucţiuni lungi,
au performanţe superioare. Ca dovadă, microcontrolerele din seria PIC12 au un set de 36 de
instrucţiuni, pe când cele din seria PIC18 au un set de 80 de instrucţiuni.
Din punct de vedere al caracterului ROM al memoriei program, există două categorii de
microcontrolere PIC:
• cu memorie de tip PROM. Acestea pot fi programate o singură dată, după care
urmează să fie montate pe placa pe care vor rula aplicaţia pentru care au fost
programate. Ele nu se mai pot reprograma, în ipoteza că la un moment dat
aplicaţia ar trebui modificată. Fiind microcontrolere ieftine, acestea sunt
recomandate pentru realizarea de produse de serie mare. În această categorie sunt
incluse microcontrolerele care conţin litera 'C' în denumire, ca de exemplu:
PIC16C84, etc.
20 Ioan P. MIHU

• cu memorie de tip EEPROM. Acestea pot fi reprogramate de mai multe ori, fiind
indicate pentru experimente sau dezvoltări de aplicaţii. În această categorie sunt
incluse microcontrolerele care conţin litera 'F' în denumire, ca de exemplu:
PIC16F84, PIC16F877, etc. La acestea se foloseşte termenul de memorie FLASH
pentru memoria program.
Alte observaţii utile legate de memoria program
• Dimensiunea memoriei FLASH la PIC 16F877 este de 8k (1FFF). Deci nu avem
voie să scriem un fişier .c după a cărui compilare fişierul .hex rezultat, să fie mai
mare de 8k. Este o limitare care nu trebuie să ne sperie, fiindcă chiar și aplicaţiilor
"serioase", le sunt suficienţi cei 8k de memorie FLASH. La alte tipuri de
microcontrolere dimensiunea memoriei FLASH este mult mai mare, ca de
exemplu PIC 18Fxx, care are 256K de memorie program, care permite crearea de
aplicaţii "foarte serioase". Oricum dimensiunea limitată a memoriei program,
obligă programatorul să aibă în vedere dimensiunea codului .hex rezultat. O altă
măsură pentru generarea de fişiere .hex de lungime minimă este compilarea
fişierelor .c, cu opţiunea: 'Global optimisation level=9'.
• Aşa cum se observă în figura 2.3, memoria program este organizată pe mai multe
pagini, 4 în cazul lui PIC 16F877. Acest lucru este transparent pe durata scrierii
programului.

Figura 2.3. Organizarea memoriei program (FLASH) la PIC 16F877


ANSI-C pentru microcontrolere 21

Merită să mai revenim puţin asupra modului în care se programează un microcontroler.


Înscrierea fizică (electrică) a fişierului executabil '.hex' în memoria program se face cu
ajutorul unui circuit de programare numit programator. Acesta este un montaj electronic
conectat pe USB sau pe interfaţa serială a calculatorului în care se află fişierului '.hex'.
Acestea pot fi:
• Programatoare care au un soclu, în care se introduce microcontrolerul căruia
urmează să-i fie înscris programul în memoria program. După înscriere,
microcontrolerul se scoate din soclu iar apoi se montează pe placa pentru care a
fost proiectată aplicaţia înscrisă în el. Dezavantajul unui astfel de procedeu este
acela că dacă se lucrează la dezvoltarea unui program cu un grad de complexitate
mai mare, care trebuie modificat de multe ori, aceasta obligă la montarea și
demontarea de multe ori a microcontrolerului în programator, operaţii prin care se
pot distruge pinii acestuia.
• Programatoare 'în circuit'. Acestea sunt recomandate dezvoltării aplicaţiilor care
necesită multe modificări și încercări pe durata realizării lor. În acest caz, se
realizează placa (circuitul) pe care microcontrolerul este montat definitiv prin
lipire. Programatorul se conectează prin intermediul unui conector montat pe placă
la doi pini (SDA și SCL) ai microcontrolerului, dedicaţi pentru comunicaţii seriale
I2C și care pot fi folosiţi pentru programare. Programarea 'în circuit' se poate face
la rândul ei pe una din următoarele două căi:
− Înscrierea serială directă conectând programatorul la pinii SDA (Serial
Clock)şi SCL (Serial Data) ai microcontrolerului.
− Înscrierea serială folosind pinii Tx și Rx protocolului serial RS232. Pentru
aceasta este însă nevoie de un program ajutător, numit bootloader. Acesta se
încarcă iniţial în microcontroler, folosind un programator clasic. După aceea
microcontrolerul se montează definitiv prin lipire pe placă iar înscrierea în
continuare a programului se face fără programator, folosind interfaţa serială a
calculatorului, şi un program de transmisie serială, precum utilitarul
Hiperterminal. Deşi se găseşte în memoria program, bootloader-ul nu
influenţează deloc aplicaţia, fiindcă el este încărcat la sfârşitul memoriei
FLASH, iar dimensiunea sa este mică, de aproximativ 250 cuvinte.

2.5. Programarea porturilor unui microcontroler

Din întreaga structură a microcontrolerului, am prezentat până acum câteva elemente legate
de memorie, fiindcă sunt indisolubil legate de programarea propriu-zisă.
22 Ioan P. MIHU

Sistem controlat Placă electronică

Tens. Analogică In Out


Senzor

Tens. Discretă In
µC Out

Figura 2.4. Microcontrolerul privit în contextul sistemului controlat

Consider că este nu doar util, ci obligatoriu ca înainte de a-l programa să facem cunoştinţă
și cu porturile microcontrolerului. Porturile unui microcontroler reprezintă acele terminale
(pini) cu care acesta intră în legătură, direct sau prin intermediul unor circuite electronice
(circuite de adaptare, circuite drivere, etc), cu sistemul pe care-l controlează.

PA0

PA1

PA2

PA3 Ieşire Digitală


Fiecare pin al unui port
Portul A PA4 poate fi programat: Analogică

PA5 Intrare

PA6
Digitală
PA7

Figura 2.5. Cum poate fi programat orice pin al portului A

Aşa cum este prezentat în figura 2.5, orice pin al unui port poate fi programat / setat să se
comporte fie ca intrare fie ca ieşire. Pinii portului vor fi setaţi intrări/ieşiri în funcţie de
necesităţile conectării microcontrolerului la sistemul controlat. Pentru a şti dacă un pin
trebuie setat intrare sau ieşire, este bine să cunoaştem comportamentul electric al pinului
faţă de circuitul extern la care este conectat, atunci când acesta este setat intrare sau ieşire.
ANSI-C pentru microcontrolere 23

+5V +5V +5V

R3
Btn1
+Vcc PA2

I~0

I~0
PA0
R1

RIN →∞ RIN →∞ Btn2

LED
Gnd

Figura 2.6. Comportamentul electric al unor pini setaţi să fie intrări


Aşa cum rezultă din figura 2.6, atunci când un pin este setat să fie intrare, microcontrolerul
se comportă ca având o rezistenţă de intrare foarte mare. Din acest motiv, curentul absorbit
de microcontroler de la circuitul la care este conectat este foarte mic. Ideal ar fi ca acest
curent absorbit să fie nul. Aceasta ar însemna că dacă privim microcontrolerul ca pe un
instrument care citeşte / măsoară tensiuni din circuitul la care este conectat, prezenţa sa nu
afectează (nu „deranjează”) funcţionarea electrică a circuitului măsurat.

+5V +5V

R4

+Vcc

K1 LED4
PA1

K2

R2

LED2
Gnd

Figura 2.7. Comportamentul electric al unui pin setat să fie ieşire

În figura 2.7, este analizat cazul în care un pin este setat ca ieşire. În acest caz vom denumi
circuitul la care este conectat ca fiind sarcină. Se observă că microcontrolerul are la ieşire
un circuit de tip push-pull care-i permite fie să injecteze curent în sarcină, fie să absoarbă
24 Ioan P. MIHU

curent, după cum este închis comutatorul K1 sau K2 și după cum sarcina este conectată la
masă sau la potenţialul Vcc.

+5V +5 +5
V V
R3 Btn1

+Vcc PA2

PA1

PA0 R2 R1

Btn2 LED
Gnd

Figura 2.8. Exemplu de conexiuni electrice ale microcontrolerului


În figura 2.8 este prezentat un exemplu de conectare a unui microcontroler la un sistem de
circuite electrice simple. Natura acestor circuite, ne obligă să alegem pentru fiecare pin,
statutul de pin de intrare sau pin de ieşire. Un criteriu simplu de a decide este acela al
transferului / consumului de energie: dacă un pin controlează / dictează consumul de
energie al circuitului la care este conectat, atunci acel pin va fi unul de ieşire. Dacă
consumul energetic al circuitului conectat nu este influenţat de pinul la care este conectat
atunci pinul va fi unul de intrare. Detaliind situaţia pinilor din figura 2.8, constatăm că:
• pinul PA0 trebuie setat ca intrare digitală deoarece el trebuie să „citească” una din
cele două stări ale butonului Btn2 (apăsat /eliberat),
• pinul PA1 trebuie setat ca și ieşire deoarece la el se găseşte conectată o diodă LED,
care va trebui să poată fi aprinsă / stinsă.
• pinul PA2 trebuie setat ca și intrare digitală fiindcă el trebuie să „citească” una din
cele două stări ale butonului Btn1 (apăsat /eliberat),.
ANSI-C pentru microcontrolere 25

+5V +5V

R3 Btn1
PA2

PA1

PA0 R2 R1

Btn2 LED

x x x x x 0 Y 1 x x x x x 1 0 1
PORTA TRISA

Figura 2.9. Posibilităţi de programare a pinilor portului A.

Din figura 2.9 se vede că:


• statutul de intrare / ieşire al unui pin este setat de bitul corespunzător lui din
registrului TRISA. Pentru exemplul dat trebuie să avem o linie în program ca cea
de mai jos:
TRISA = xxxxx101b; // cu '0' pinul devine ieşire, cu '1' devine intrare
// deci: PA0 şi PA2 au devenit intrări;
// PA1 a devenit ieşire;
Convenţia „1 = intrare / 0 = ieşire” este uşor de memorat dacă asociem pe '0' cu
litera 'o' din cuvântul output și pe '1' cu litera 'i' din cuvântul input.
În exemplul dat, primii 5 biţi din PORTA nu sunt folosiţi. În acest caz primii 5 biţi
din TRISA nu contează ce valoare iau și de aceea sunt notaţi cu 'x'.
• O dată stabilit caracterul de intrare / ieşire al unui pin acesta va putea fi folosit
conform funcţiei pe care o are, prin intermediul registrului PORTA.
• În ipoteza că am setat pinul PA1 ca fiind ieşire, atunci:
- dacă dorim să „aprindem” LED-ul, trebuie ca bitul corespunzător pinului
PA1 să fie setat pe '1'. Exemplu:
RA1 = 1; // Echivalent cu PORTA=xxxx xx1xb; LED-ul se va aprinde
- dacă dorim să „stingem” LED-ul, bitul va trebui setat pe '0'. Exemplu:
RA1 = 0; // Echivalent cu PORTA=xxxx xx0xb; LED-ul se va stinge
• Nu este obligatoriu ca scriind '0' la un pin de ieşire LED-ul să fie stins, iar dacă
scriem '1' LED-ul să fie aprins. Ceea ce trebuie reţinut este că dacă scriem '1' atunci
pinul respectiv va ajunge conectat la potenţialul +Vcc, iar dacă scriem '0' atunci
pinul respectiv va ajunge conectat la potenţial nul (la masă) aşa cum rezultă din
figura 2.7. Ţinând cont de aceste precizări vom stabili apoi care este starea LED-
ului, în funcţie de configuraţia circuitului electric în care se află.
26 Ioan P. MIHU

• Dacă pinul este intrare digitală atunci în bitul corespunzător din registrul PORTA
se va găsi '0' sau '1' în funcţie de tensiunea ce ajunge la pin, prin circuitul electric
exterior microcontrolerului. Dacă avem de citit stări ale unor butoane, atunci
valoarea citită depinde de configuraţia circuitului în care este inclus butonul.
Pentru circuitul din figura 2.9, vom avea:
TRISA = xxxxx1x1b; // PA0 și PA2 au devenit intrări
Bit Xbtn; // declarăm o variabilă Xbtn de un bit.
Xbtn = RA0; // Citim primul bit al portului PORTA. Variabila Xbtn devine:
// '0' dacă butonul Btn2 este apăsat, sau
// '1' dacă butonul Btn2 este eliberat.
Xbtn = RA2; // Citim al treilea bit al portului PORTA. Variabila Xbtn devine:
// '1' dacă butonul Btn1 este apăsat, sau
// '0' dacă butonul Btn1 este eliberat.
• Citirea valorii dintr-un port în linia ce urmează imediat după setarea acestuia, poate
fi afectată de impedanţa conectată ca sarcină la pinul aferent portului.
Char Var1; // declarăm o variabilă Var1 de un octet.
TRISA = xxxxx1x1b; // Setăm pinii RA0 şi RA2 ca intrări
Var1 = PORTA; // poate citi portul A cu erori
Char Var1; // declarăm o variabilă Var1 de un octet.
TRISA = xxxxx1x1b; // Setăm pinii RA0 şi RA2 ca intrări
nop; // comanda 'nop' întârzie derularea programului,
// cu un ciclu, ceea ce face ca statutul pinilor
// declaraţi intrări să se stabilizeze
Var1 = PORTA; // se va citi corect
• Aşa cum am menţionat anterior, un pin declarat ca intrare poate fi setat fie intrare
digitală, fie intrare analogică. Un exemplu detaliat al folosirii intrărilor analogice
este prezentat în capitolul 12.

2.6. Alegerea microcontrolerului

2.6.1. Criterii legate de arhitectura microcontrolerului


Problema poate fi formulată astfel: ce tip de microcontroler se potriveşte cel mai bine
aplicaţiei mele? Cele mai obiective criterii sunt legate de cerinţele tehnice ale aplicaţiei.
Iată câteva dintre acestea: numărul de pini, tipul capsulei, tensiunea de alimentare, volumul
de memorie flash, volumul de memorie RAM, porturi specifice: I2C, UART, ADC, etc,
ANSI-C pentru microcontrolere 27

Figura 2.10. Instrument soft pentru alegerea µC adecvat

Căutarea microcontrolerului optim nu este un lucru tocmai uşor, mai ales dacă suntem la
început de drum în utilizarea acestora. Compania Microchip vine în ajutor cu un instrument
software extrem de util pentru alegerea microcontrolerului optim. Acesta este accesibil pe
pagina de Internet a companiei [4xa]. O parte din interfaţa acestuia este prezentată în
figura 2.10. Se observă că putem selecta aria de căutare, cu ajutorul unor „potenţiometre
grafice” (slider). Putem îngusta mereu aria de căutare până la momentul în care ajungem la
microcontrolerul dorit din punct de vedere tehnic.
Iată în continuare în sinteză principalele caracteristici ale familiilor de µC Microchip.
28 Ioan P. MIHU

Figura 2.11. Imagine de ansamblu a familiilor Microchip [yy]

Magistrala Lungime Familie / Performanțele


Date Instrucţiune Tip reprezentativ familiei
PIC12 / 0,064k RAM, 1 kB Flash; 20 MHz; 6 I/O pin; 2 Timers;
12 bit
PIC12F675 7 Interrupt sources;10 bit ADC/4 ch; 32 Base Instructions
PIC16 0,368k RAM, 8 kB Flash; 20 MHz;
14 bit / 14 Interrupt sources; 3 Timers; 2 PWM channels;
8 bit
PIC16F877 1 USART; 10 bit ADC/8 ch; 35 Base Instructions,
PIC18 1,6k RAM, 32 kB Flash; 40 MHz; 2 PWM channels;
16 bit / 18 Interrupt sources; 4 Timers;1 USART; 10 bit ADC/8 ch;
PIC18F452 75 Base Instructions, 8x8 hardware multiplier;
PIC24 16k RAM, 256 kB Flash; 40 MIPS; 35 I/O pin; 2 UART;
/ 2 SPI; 2 I2C; 10 bit ADC/13 ch; 5 PWM channels;
PIC24FJ128- 43 Interrupt sources; 5 Timers; 76 Base Instructions, 17x17
GA006 hardware multiplier;
16k RAM, 256 kB Flash3 30 MIPS; 35 I/O pin;
dsPIC30
2 UART; 2 SPI; 2 I2C; 10 bit ADC/13 ch; CAN bus;
16 bit 24 bit /
45 Interrupt sources; 5 Timers; 8 PWM channels; 76 Base
dsPIC30F4012
Instructions, 17x17 single cycle fractional/ integer multiplier;
dsPIC33 30k RAM, 256 kB Flash; 40 MIPS; 85 I/O pin;
/ 2 UART; 2 SPI; 2 I2C; 10 bit 2ADC/32 ch; CAN bus;
dsPIC33FJ256- 117 Interrupt sources; 5 Timers; 8 PWM channels; 83 Base
GP506 Instructions, 16x16 single cycle fractional/ integer multiplier;
128k RAM; 512 kB Flash; 80 MHz / 1,65MIPS/MHz
PIC32
O instrucţiune / ciclu; Cache memory
/
32 bit 128 bit 16ch DMA; Ethernet; 2CAN buss; Full speed USB;
PIC32MX360-
Multitasking embedded control;
F512L
Free MPLAB XC Compiler

Tabelul 2.1. Sinteză a performanţelor familiilor PIC


ANSI-C pentru microcontrolere 29

2.6.2. Criterii legate de viteza de calcul


Aplicaţiile cele mai incitante ale microcontrolerelor sunt cele numite „în timp real”. Este
vorba de acele aplicaţii, la care reacţia / răspunsul sistemului trebuie să fie suficient de
rapid, pentru a fi util cerinţelor aplicaţiei. Spre exemplu dacă sistemul cu microcontroler
echipează sistemul de direcţie al unei rachete de croazieră, care într-o secundă parcurge 400
m, este vital ca din momentul în care microcontrolerul primeşte informaţiile despre poziţia
și direcţia actuală a unui obstacol, el să ia și să transmită decizia de a schimba direcţia, în
cel mai scurt timp posibil, în timp real. Decizia se ia în urma parcurgerii unui algoritm de
calcul, deci în urma unor calcule. Este vital ca aceste calcule să fie efectuate cât mai rapid.
La fel dacă avem un sistem de recunoaştere a vorbirii, la care eşantioanele se citesc cu o
frecvenţă de eşantionare de 8kHz, atunci în intervalul dintre două eşantioane (Te= 1/8000
s= 0,125ms) trebuie efectuate toate calculele cerute de algoritm.
De regulă, algoritmii sunt liniari, calculele implicate fiind adunări şi înmulţiri. Cunoscând
algoritmul, ştim câte înmulţiri şi câte adunări trebuie efectuate. Este firesc deci să avem o
imagine de ansamblu a timpului de calcul alocat unei adunări şi unei înmulţiri, de către
fiecare familie de microcontrolere. Acest lucru este prezentat sintetic în tabelul 2.1.

Timpul de calcul
Familie (cicli instrucţiune)
Magistrala Lungime
/ Adunare Înmulţire
Date Instrucţiune
Nr. Instrucţiuni 8 bit + 16 bit + float + 8 bit x 16 bit x float x
8 bit 16 bit float 8 bit 16 bit float
PIC12 3 130 2 4 526
12 bit 1
32 Instructions soft soft soft soft soft
PIC16 3 130 2 4 526
8 bit 14 bit 1
35 Instructions soft Soft soft soft soft
PIC18 3 115 4 451
16 bit 1 1
75 Instructions soft soft soft soft
PIC24 88 90
1 1 1 1
76 Instructions soft soft
dsPIC30 84 90
16 bit 24 bit 1 1 1 1
76 Instructions soft soft
dsPIC33 84 90
1 1 1 1
83 Instructions soft soft
PIC32
64 49
32 bit 128 bit 124 Instructions 1 1 1 1
soft soft
Multitasking

Tabelul 2.2. Referitor la viteza de calcul

Vom analiza pentru început timpii de calcul la adunare. Toate familiile de microcontrolere
au în setul de instrucţiuni, pe cea de adunare: ADDWF = adunarea a două variabile, sau
ADDWFC = adunarea unei variabile cu o constantă. Aceasta înseamnă că dacă numerele
sunt pe câte un octet, iar magistrala de date este de 8 bit, adunarea se execută hardware de
către unitatea centrală într-un ciclu de instrucţiune. În schimb dacă avem de adunat
numere pe doi octeţi, iar magistrala de date este de 8 biţi, atunci adunarea lor va necesita
evident mai mulţi cicli instrucţiune. Dacă magistrala de date este de 16 biți atunci adunarea
a două numere de 16 biţi se va face tot într-o singură instrucţiune.
30 Ioan P. MIHU

Să continuăm analiza datelor din tabelul 2.2 cu operaţia de adunare. Familia PIC18 este
prima care are înglobat un multiplicator hardware de 8x8 bit. Aceasta înseamnă că
efectuează înmulţirea într-un singur ciclu instrucţiune (există instrucţiunea MUL, în setul
de instrucţiuni). Familiile PIC24 şi dsPIC30, PIC32 au multiplicatoare hardware 17x17,
care le permit înmulţirea a două numere de 16 biţi într-un singur ciclu instrucţiune.

Figura 2.12. Secvenţă de program folosită pentru tabelul 2.2

Datele din tabelul 2.2. au fost colectate rulând porţiunea de cod din figura 12.12 și folosind
apoi simulatorul MPLAB SIM. După setarea acestui simulator în meniul Debug, pentru
analiză vom folosi fereastra Disassembly Listing (figura 2.14), sau mai detaliat fereastra
Program Memory (figura 2.13), din care putem afla numărul de instrucţiuni parcurse
folosind butonul Step Into pentru păşirea înainte în program. În figura 2.14 se observă că
la PIC24, înmulţirea numerelor pe 2 octeţi se face într-un singur ciclu.

Figura 2.13. Program Memory Window

Figura 2.14. Fereastra Diassembly Listing


ANSI-C pentru microcontrolere 31

Din cele două ferestre menţionate putem afla numărul de instrucţiuni al unor rutine. Nu este
corect să judecăm viteza de calcul după numărul de instrucţiuni pe care la are o rutină de
multiplicare, deoarece ea cuprinde şi bucle care se repetă, durata de execuţie a rutinei fiind
dependentă de numerele cu care se operează. Cel mai bun mod de a judeca lucrurile este
analiza timpului de calcul a codului executabil rezultat, cu ajutorul simulatorului MPLAB
SIM în fereastra Simulator Trace, ca în figura 2.15. Acesta are avantajul că aici sunt
contorizați cu exactitate numărul de cicli instrucţiune.

Figura 2.15. Simulator Trace Window

Aşa după cum se observă din tabelul 2.2, familiile PIC12 şi PIC 16, nu au o instrucţiune
de înmulţire, deci nu pot efectua înmulţirea decât prin rutine software, adică prin artificii în
care înmulţirea este făcută prin adunări repetate. De exemplu un microcontroler PIC16
efectuează înmulţirea a două numere reprezentate pe câte 2 octeţi în 4 cicli instrucţiune.
Acolo unde în tabelul 2.2 există menţiunea 'soft', înseamnă că operaţia respectivă nu este
efectuată într-o singură instrucţiune, ci este executată de o rutină.
O problemă și mai mare o reprezintă adunarea şi înmulţirea numerelor în virgulă
flotantă. Deşi acestea oferă precizii mari, timpul de efectuare a unei adunări sau a unei
înmulţiri este foarte mare. Toate operaţiile cu variabilele de tip float se efectuează cu rutine
soft foarte lungi, din două motive:
• La data realizării acestui volum, nici o familie de microcontrolere Microchip
nu are înglobată FPU (Floating Point Unit), deci operaţiile cu numere în
virgulă flotantă nu pot fi realizate hardware, ci doar prin emulare software.
• Numerele în format floating point după standardul IEEE754 trebuie „mai întâi
despachetate” pentru a fi aduse la o reprezentare în format de numere întregi,
singurele pe care ALU le ştie efectua, iar apoi după terminarea calculelor, ele
„trebuie împachetate” din nou în respectivul format.
Numărul de cicluri instrucţiune menționat în tabel poate varia fiindcă este dependent de
valorile operanzilor (mai ales când acestea sunt valori particulare precum 0, 1, NaN, etc.),
precum și de tipul compilatorului. O altă măsură a performanţelor compilatorului de a
opera cu numere în virgulă flotantă este numărul de cicli instrucţiune în care se calculează
valoarea unor funcţii matematice precum: sin(x), tg(x), ln(x), etc, chiar dacă acestea nu sunt
folosite în algoritmii în timp real.
În concluzie, revenind la imaginea de ansamblu oferită de tabelul 2.2, observăm că aici sunt
menționate operaţii efectuate pe diverse tipuri de date: 8 biți, 16 biți sau float, care pot fi
efectuate într-un singur ciclu instrucţiune sau în foarte multe. Devine clar, că trebuie
32 Ioan P. MIHU

accentuată responsabilitatea programatorului de a alege tipul potrivit de variabile, pentru


ca viteza de calcul să fie cât mai mare posibil. Astfel dacă doreşte să lucreze cu variabile pe
8 biţi, atunci va alege un microcontroler cu o magistrală de 8 biţi, și în program va declara
variabilele de tip char sau unsign char. Dacă este obligat să lucreze cu precizii mai mari şi
trebuie să folosească variabile pe 16 biţi, atunci este recomandat să aleagă un
microcontroler cu o magistrală de date de 16 biţi (PIC24) și în program să-şi declare
variabilele de tip int. Dacă doreşte precizii foarte mari, este firesc să se gândească la
folosirea variabilelor de tip float. Aşa cum se vede din tabel, folosirea lor trebuie făcută cu
mari rezerve atunci când apar constrângeri legate de timpul de calcul. Criteriile care stau la
baza alegerii tipului de variabilă nu sunt doar acestea, de aceea le vom relua în capitolul 4.7
Necesitatea de a efectua calcule rapide este de fapt „motorul” care a împins producătorii de
microcontrolere să realizeze familii de circuite cu arhitecturi noi, cu performanţe tot mai
ridicate.

2.6.3. Criterii economice


Alături de criteriile tehnice, există și altele, cel puţin la fel de importante: costul
microcontrolerului, costul compilatorului, experienţa în utilizare, etc. Aceste criterii sunt
importante atât pentru studentul care realizează proiecte didactice sau ca hobby din propriul
buget, cât și pentru proiectele industriale, realizate de companii specializate în producerea
de sisteme încorporate cu microcontrolere.
În concluzie, alegerea microcontrolerului potrivit, este un pas important şi nu foarte uşor
de făcut atunci când trebuie realizate aplicaţii competitive, de mare complexitate şi cu
performanțe ridicate.

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