Documente Academic
Documente Profesional
Documente Cultură
CE TREBUIE SĂ PROGRAMĂM
1.1. Introducere
Sistem controlat
Sistem cu
Intrări Ieşiri
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
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
Breackpoint
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:
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
Save Workspace
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.
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.
Project / Build
compilează fişierele sursă;
construieşte proiectul.
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
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.
• 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.
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
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
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ă.
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
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.
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
Tens. Discretă In
µC Out
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
PA5 Intrare
PA6
Digitală
PA7
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
R3
Btn1
+Vcc PA2
I~0
I~0
PA0
R1
LED
Gnd
+5V +5V
R4
+Vcc
K1 LED4
PA1
K2
R2
LED2
Gnd
Î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
+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
• 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.
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
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
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.
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.
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.
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