Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Tutorial | Uvod u programiranje PIC mikrokontrolera u jeziku C
#1

Kao sto sam naslov kaze, ova tema ce biti posvecena svima koji zele da se oprobaju u pisanju programa za Microchip PIC mikrokontrolere.


Tema ce biti pisana deo po deo tj koliko budem mogao vremena da odvojim a prateci neke druge prirucnike tako da zaokruzim i predjem bitna poglavlja oko ove problematike.


Zamolio bih clanove foruma da u ovom kontekstu u ovoj temi ne postavljaju pitanje ili daju komentare kako bi odrzali temu bas u duhu tutorijala a da eventualna pitanja vezano za ovo postave u novoj temi.

Pa da krenemo lagano … Wink

Početkom devedesetih godina nama već prošloga veka, na polju elektronike i mikroelektronike se pojavio vrlo zanimljiv proizovod sa nazivom "PIC".


Proizvođač Microchip je napravio pravi bum na tržištu kada je predstavio prve serije programibilnih integrisanih kola (IC) koja su po prvi put do tad imali sve u sebi od potrebnih logičkih jedinica i funkcija neophodnih za rad tj. izvšavanja programiranih zadataka.



Jedan ovakav IC u sebi poseduje pravi mali računar sa potpuno istim logičkim delovima kao i naš PC sa koga čitamo ovaj tekst, CPU jedinica, memorija, ulazno i izlazni portovi, generatori takta (clock) i ostale napredne jedinice.?

Tokom godina, proizvođač je u svoju paletu proizvoda uključio nekoliko stotina različitih modela za upotrebu u širokoj paleti električnih uredjaja koje su podeljenje u nekoliko serija, počevši od mikrokontrolera iz serije PIC10, PIC12 preko serija sa pobošanim periferijama iz serije PIC16, zatim modernih 16-bitnih serija PIC18 sve do serija sa veoma snažnim CPU jedinicama obogaćeni različitim periferijama iobiljem memorije iz serije dsPIC i PIC32. ?

PIC integrisana kola mogu se naći u najrazličitijim kućistima, od malih 8-pin plastičnih kučista (TH) do kućišta sa velikim brojem nožica u SMD tehnici.



Dakle pred nama je jedan IC koji ima sve nama bitne delove za izvršavanje nekog korisničkog programa koji je preko ulazno-izlaznih nožica spojen sa ostatkom elektronike.

Svaki model PIC mikrokontrolera je spečifican na svoj način i detaljna specifikacija se može naći online ali je bitno naglasiti da su svi modeli bazirani na istoj modifikovanoj Harvard arhitekturii sto?praktično znači da je programiranje IC-ova i pisanje programa isto (ili vrlo slično) za sve serije PIC mikrokontrolera. Konkretan izbor PIC-a zavisi od zahteva aplikacije koja se projektuje ali za ovaj tutorial će biti (naknadno) usvojen neki lako dostupan model iz PIC18F ili dsPIC serije.

Za potrebe ovog tutorijala neophodno je pripremiti nekoliko stvari kako bi mogli da osposobimo razvojno okruženje gde je moguće pisati i testirati programe kao i napisan program prebaciti u PIC mikrokontroler pomoću programatorskog uređaja (PICkit2, PICkit3 i dugi).?

Programski razvojni alat za ovaj tutorijal će biti baziran na besplatnoj verziji MPLAB X IDE uz korišćenje takođe besplatnog Microchip XC prevodioca (compiler). Ovi programski paketi su raspoloživi za Windows, Linux i Mac OS X operativne sisteme. Tačnu proceduru za instalaciju možete potražiti online.

Napisan program može takodje da se simulira na samom PC računaru bez programiranja PIC-a što je zgodno u početku jer nisu neophodne razvojne pločice, PIC i programatori ali u nekom trenutku će svakako zatrebati kada se želi taj program pokrenuti u nekom praktičnom električnom kolu.

Razvojni proces jednog uređaja sa mikrokontrolerom se može ukratko svesti na sledeće faze:


1) Razrada idejnog plana, algoritmi rada, skice

2) Dizajniranje električne šeme

3) Izbor adekvatnog/optimalnog modela mikrokontrolerskog čipa za datu aplikaciju

4) Dizajniranje štampane pločice

5) Pisanje programa

6) Testiranje programa u simulatoru (opciono)

7) Prebacivanje gotovog programa u mikrokontroler (programiranje / flešovanje)

8) Detaljno testiranje u električnom kolu

9) Popravka i oklanjanje programskih grešaka tj "debagiranje" (ponavljanje faza 5 do 9)


Svaka faza zahteva pažnju, detaljno razmatranje zahteva i mogućih problema kao i određeno znanje i veštine za postizanje željenog cilja.


Pošto je tema oko programiranja mikrokontrolera (PIC ili bilo kog drugog) vrlo široka i obimna, jako je teško napisati sve bitne stvari vrlo sažeto.

Glavni problem potiče iz predpostavke da je za pisanje programa potrebno neko predznanje koje je pre svega vezano za razumevanje Bulove algebre tj logike (digitalna logika) kao i poznavanje raspoloživih mogućnosti nekog mikrokontrolerskog čipa.


PIC mikrokontroleri poseduju veoma veliki spektar raspoloživih mogućnosti i konkretan izbor za neku traženu aplikaciju može da bude vrlo mukotrpan posao posebno ako se rade velike serije gde je bitno svesti troškove materijala na minimum.

Ovde se dodatno stvari malo komplikuju jer je u nekim slučajevima moguće na nivou programa nadomestiti/realizovati neke kompleksne funkcije umesto da se izabere "jači" (obično i skuplji) mikrokontroler koje takve komplekse funkcije može da radi na nivou unutrašnjih specializovanih pod-sklopova tj modula.


Kao čest primer ovakvih stvari je realizacija npr RS232 ili I2C seriskog protokola za komunikaciju koji moze bez specializovanih HW modula da se realizuje čisto adekvatnim napisanim programom. Međutim, to nije ni malo jednostavan zadatak jer obično zahteva apsolutno poznavanje problematike tog protokola i zato se sve češće umesto prostih i jeftinih modela PIC-a biraju modeli koji takve funkcije imaju realizovane na nivou HW-a a programeru se daje "na izvolte" mali skup potrebnih parametara i opcija sa kojim na jednostavan način mogu iz svog programa da upravljaju tim modulima. Dodatno, HW funkcije su često daleko brže i pouzdanije u radu nego ekvivalent čisto programskom rešenju. Naravno, neke jako složene funkcije praktično i nisu moguće da se realizuju samo preko programa nego je neophodan specializovani modul u mikrokontroleru zaduže za te stvari kao npr USB komunikacioni port ili I2S port za povezivanje sa Audio A/D ili D/A čipovima.



Attached Files Thumbnail(s)

Reply
#2
Sledeća skica-šema prikazuje uprošćenu strukturu tipičnog PIC mikrokontrolera. Slika je ilustrativnog karaktera i ne odnosi se ni na jedan konkretan model.
Tačan raspored nožica varira od modela do modela ali za potrebe ovog tutorijala možemo i ovako predstaviti.

Sam mikrokontroler poseduje interno jos dosta različitih pod-sklopova i o njima ću pisati nešto kasnije.
U ovom trenutku je bitno samo da imamo osnovnu sliku onoga sta se nalazi unutar PIC-a.

Kao što se može vidite sa predhodne skice, osnovna šema povezivanja jednog PIC mikrokontrolera je krajnje jednostavna.
Dovoljno je dovesti adekvatno stabilisano napajanje na Vdd i Vss nožize, MCLR nožicu (reset, biće o tome kasnije više detalja) preko jednog otpornika vežemo na logičku jedinici (dakle na Vdd) i kontroler je spreman za rad.

Pošto se praktično sve vrti oko njegovih periferija i nožica, treba znati da su sve (slobodne) nožice programski upravljive.
To znači da je korisniku ostavljena mogućnost da svaku pojedinačnu nožicu moze da konfiguriše za određenu potrebu.
Dakle možemo da postavimo iz programa npr da nožica PA0 bude digitalni izlaz, da PA1 bude digitalni ulaz, da neka sledeća bude analogni ulaz i tako redom na osnovu potreba aplikacije i raspoloživih mogućnosti samog PIC-a. 

Svi PIC-evi imaju minimum mogućnost da nožice mogu da budu digitalnog tipa, bilo ulaz ili izlaz, dok ostale funkcije zavise od konkretnog modela.
Dodatno zgodna opcija oko tih nožica je ta da možemo da menjano njihovo ponašanje tokom rada programa, npr da u jednom (kratkom) trenutku budu ulazi a kasnije da budu izlazi sto se često koristi recimo kada "oskudevamo" sa raspoloživim nožicama pa možemo tako preko jedne nožice da realizujemo dve ili više električnih funkcija (npr multiplexing za povezivanje matrice LED dioda ili tastera).

Slobodne I/O nožice su grupisane po portovima (PORTA, PORTB, PORTC itd) i to do 8 nožica po jednom portu. Ovo je zgodna osobina zato što možemo (ako tako želimio) da odjednom postavimo stanje na svih 8 nožica i da nam to bude logički ekvivalentno nekom binarnom broju.

Recimo da smo "konfigurisali" da PORTC bude izlaz i ako bi npr u programu rekli "upiši 255 na PORTC", to će dovesti do toga da sve nožice od PC0 do PC7 imaju na svom izlazu logičku jedinicu (+5V).
Broj 255 (ili u hexadecimalno 0xFF) u binarnoj osnovi ima vrednost 11111111 što se perfektno slaže sa fizičkim rasporedom nožicama.
Ako bi npr upisali broj 0 u PORTC, to bi postavilo sve logicke nule (0V) na nožicama PC0 do PC7.
Isto tako ako bi upisali broj 170 (ili u hexadecimalno 0xAA) koji ima binarnu vrednost 10101010, na nožicama će mo dobiti da svaka druga bude sa logičkom jedinicim dok će ostale biti sa logičkom nulom.

Konfiguracija nožica se vrši preko specifičnih "registra" koji nose nazive TRISA i LATA za PORTA, TRISB i LATB za PORTB i tako redom. 
Ovo je nešto složenija problematika i to će te tokom vremena da "skontate" ali za ovaj tutorijal možemo da iskoristimo jako koristan plugin za Mplab X, Code Configurator (tnx @micro) koji preko grafičkog interfejsa može umesto nas da postavi potrebne vrednosti za te registre.
Dakle ovde se sve svodi na klikatanje po programu, još uvek nismo krenuli u praktično pisanje korisničkog programa.

Sledeća bitna stvar je vezana za oscilator. Dakle u samom PIC-u postoji pod-sklop koji služi za davanje "takta" (CLOCK) glavnoj CPU jedinici. Procesorska jedinica će tačno u ritmu CLOCK-a izvršavati zadate (programirane) komande jednu za drugom i to brzinom kojom je taj oscilator podešen da radi.
Slično kao i kod konfiguracije I/O nožica, interni oscilatorski modul takođe poseduje odredjene registre sa kojim se njegovo ponašanje može tačno definisati (najčešće vezano za generisanu učestanost - brzinu).
Spomenuti plugin Code Configurator nam takođe može pomoći oko jednostavnog "konfigurisanja" internog oscilatora i to samo preko kliktanja raspoloživih opcija.

Tokom godina ovaj oscilatorski modul je dobio jako puno korisnih opcija kao i mogućnost da generiše učestanosti preko nekoliko desetina MHz.
Jos jedna "moderna" opcija je ta da taj oscilator može da radi samo sa internim R/C komponentama tako da više nije neophodan spoljni kristal (XTAL, quarz).
Ovo je inače jako zgodna opcija iz više razloga, dobijamo uštedu u materijalu, dobijamo dodatne dve slobodne nožice koju su pre (na drugim starijim PIC modelima) bile rezervisane za priključivanje kristala i dobijamo na pouzdanosti jer se smanjuje mogućnost da nešto krene naopako sa spoljnim priključenim kristalom a ovde možda i najbitnijoj funkciji jednog mikrokontrolera.
Ako bi se iz bilo kog razloga nešto desilo sa spoljnim kristalom (loš kontakt, oštećen kristal i sl.) cela CPU jedinica bi ostala bez takta i izvršavanje programa bi bilo onemogućeno, prosto rečeno CPU bi se "zaglavio" u nekom statičnom stanju.
Važno je napomenuti da je proizvođač i pored internog R/C oscilatorskog modula ostavio mogućnost da se ipak priključi spoljni kristal. Oscilatori bazirani na kristalu imaju daleko stabilniju generisanu učestanost naspram promene temperature ili napona napajanja u odnosu na R/C oscilatore i ta opcija je neophodna kod nekih veoma vremenski preciznih/kritičnih aplikacija kao što je npr USB komunikacija.
Opet slično kao i sa I/O portovima, ponašanje rada internog oscilatora se moze menjati u toku rada programa preko konfiguracionih registra i ta opcija se često koristi kod aplikacija koje se napajaju iz baterije gde se spuštanjem brzine CLOCK-a smanjuje potrošnja struje.
Pošto u ovom trenutku imamo "delimično razjašnjene" dve bitne stvari, konfiguraciju portova i internog oscilatora, vreme je da se prebacimo u domen pisanja korisničkog programa Smile

Oko pisanja programa za početak ću napraviti analogiju tj poređenje sa nekim malo lakše svarljivim stvarima.

Pošto se na ovom forumu uglavnom bavimo elektronikom/elektrikom i ona je koliko-toliko razumljiva većini članova, uzeću za primer recimo klasični mehanički programator za veš mašine ili jos prostije npr dečija muzička kutija na navijanje Smile

Kod mehaničkog programatora za veš mašine imamo praktično jedan valjak na kome su postavljnje na odredjenim mestima "grbe" koje mehanički aktiviraju električne kontakte. Kako se valjak okreće, grbe koje su tačno raspoređene na određenim mestima (analogija programu) prespajaju mehaničke kontakte koji dalje uključuju ili isključuju određene pod-sklopove (pumpu za vodu, glavni motor mašine itd).
Cela ta postavljena logika na valjku se ponavlja u krug tačno onako kako je predviđeno (programirano) i to brzinom kojom je zadata pomoćnim motorčićem koji pokreće valjak (analogija CPU CLOCK-u).

Za svaki korak ili poziciju na tom valjku možemo napraviti analogiju sa jednom (ili više) mikroprocesorskih naredbi odnosno instrukcija koja može da naredi perifernoj jedinici npr "uključi izlaz 1,2 i 8 a isključi 5 i 6", pa u sledećem koraku npr "ugasi sve izlaze", pa u sledećem koraku "upali sve izlaze" i tako redom. Kada stigne do kraja programa (za veš masine to baš i nije slučaj Smile ), procesorskoj jedinici se uglavnom zadaje naredba "ajd kreni sad sve to ponovo ispočetka" Smile

U programerskom slengu proces ponavjanja jedne ili više instrukcija se naziva "programska petlja" ili na enegleskom jeziku "loop".
Postoje petlje koje se bezuslovno stalno ponavljaju i to su "beskonačne petlje" i one se uglavnom koriste kao "nosioci" kompletnog programa.
Takođe postoje "kontrolisane petje" koje se koriste u situacijama kada želimo da ponovimo neku (jednu ili više) programsku instrukciju određeni broj puta.

Dakle jedan program je skup pojedinačnih instrukcija (koraka) koje se jedna za drugom izvršavaju.

Slikovit opis jednog programa za veš mašine (bez kontrole tipa da li su zatvorena vrata ili da li ima vode i slično) bi mogao da izgleda ovako:

1) Početak programa (početak beskonačne petlje)
2) Pusti vodu u mašinu (upali elektro-ventil koji je vezan na nožicu PA0)
3) Zaustavi dovod vode u mašinu (ugasi elektro-ventil koji je vezan na nožicu PA0)
4) Pokreni glavni motor u smeru kazaljke na satu (upali relej vezan na PA1)
5) Stopiraj glavni motor (ugasi relej vezan na PA1)
6) Pokreni glavni motor u suprotnom smeru kazaljke na satu (upali relej vezan na PA2)
7) Stopiraj glavni motor (ugasi relej vezan na PA2)
8) Ispusti vodu iz mašine (upali relej za pumpu na PA3)
9) Stopiraj ispuštanje vode (ugasi relej za pumpu na PA3)
10) Ponovi sve od početka (skoči na korak 1, kraj beskonačne petlje)

U ovom primeru se sa jednog na drugi korak u realnim uslovima prelazi relativno sporo, sekunde i minuti su u pitanju.
Kod mikroprocesora i mikrokontrolera je prelazak na sledeći korak (tj izvršavanje jedne instrukcije) izuzetno brzo i meri se u nano sekundama. Osrednji PIC može da izvrši i preko 5 miliona takvih instrukcija u sekundi (5 MIPS)!
Ovde je bitno napomenuti da neke funkcije ne možemo baš iz jednog koraka da izvšimo nego je potrebno dosta više sitnih pojedinačnih koraka (instrukcija) pa se zato i ovih 5 MIPS brzine može u nekim situacijama smatrati sporim Smile Na primer, upis jednog karaktera na LCD displej moze zahtevati i stotinu pojedinačnih instrukcija!


Posle male pauze, nastavljam ovog puta sa jednim konkretnim primerom.

Pogledajte ovaj program (ne brinite, objasniću natenane Smile )

U prilogu je i kompletan projekat za MplabX kao i HEX fajl koji moze da se direktno usnimi u PIC.

/* 
* File: main.c
* Author: @mikikg
*
* DIY-Primer1
*
* Device: PIC16F886
*
* Compiler: XC8 (free version)
*
* Created: March 10, 2014, 12:03 AM
*/

//Glavni include fajl koji sadrzi kompletnu definiciju registra
#include <xc.h>


/**************************************************************
Osnovna konfiguracija za PIC16F886 (oscilator, watchdog itd)
**************************************************************/

// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RE3/MCLR pin function select bit (RE3/MCLR pin function is MCLR)
#pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown Out Reset Selection bits (BOR enabled)
#pragma config IESO = ON // Internal External Switchover bit (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled)
#pragma config LVP = OFF // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming)

// CONFIG2
#pragma config BOR4V = BOR40V // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V)
#pragma config WRT = OFF // Flash Program Memory Self Write Enable bits (Write protection off)


/**************************************************************
Funkcija za korisnicku konfiguracija I/O pinova
**************************************************************/
void PIC_Konfiguracija(void){

//Opsta konfiguracija
ANSEL = 0x00; //iskljuci sve A/D

//Konfiguracija za Port A
//ne koristimo ...

//Konfiguracija za Port B
TRISB = 0b00000001; //postavi B1-B7 kao izlaze, B0 kao ulaz
WPUB = 0b00000001; //ukljuci Pull-up interni otpornik za B0

//Konfiguracija za Port C
TRISC = 0b00000000; //postavi C0-C7 kao izlaze

}


/**************************************************************
Glavni program
**************************************************************/
void main(void) {

//Izbor clock-a
/*
111 = 8 MHz
110 = 4 MHz (default)
101 = 2 MHz
100 = 1 MHz
011 = 500 kHz
010 = 250 kHz
001 = 125 kHz
000 = 31 kHz (LFINTOSC)
^-- IRCF0
^--- IRCF1
^---- IRCF2
*/

OSCCONbits.IRCF0=0;
OSCCONbits.IRCF1=0;
OSCCONbits.IRCF2=0;

//Poziv funkcije za konfiguraciju pinova
PIC_Konfiguracija();

//Definicija promenljivih
int brojac;

//Beskonacna petlja
while(1){

//petlja za povecavanje promenljive brojac u opsegu 0 do 255
for (brojac=0; brojac<=255; brojac++) {

//upisi vrednost brojaca na Port C
PORTC = brojac;

}

}

}

//Kraj programa

.zip   DIY-primer1.X.zip (Size: 57,33 KB / Downloads: 5)

U predhodnom postu je prikazan jedan prost primer "oktalnog brojača" koji sve što radi je da broji u krug od 0 do 255 i taj broj "postavi" na izlazne nožice Porta C.
Gledano iz ugla elektronike, kada je brojač sa vrednošću 0, sve nožice PC0 do PC7 će imati 0V na izlazu, zatim kada je brojač sa vrednošću 1, nožica PC1 će biti sa 5V a sve ostale na tom portu sa 0V i tako redom se menjaju stanja i kada stigne do 255, sve nožice na Port C ce imati 5V.
Dakle ovde se dohvatamo binarne reprezentacije "decimalnih" brojeva.
Za lakše razumevanje ove reprezentacije kao i prebacivanja iz jedne u drugu, u vašem OS imate standardno digitron koji moze da se prebaci u "programerski mod" pa malo eksperimentišete jer je jako bitno da to shvatite zbog daljeg razumevanja cele ove problematike. Evo i slikovit prikaz te reprezentaije:

decimalno | binarno  | hexadecimalno
----------|----------|---------------
0         | 00000000 | 00 ili 0x00
1         | 00000001 | 01 ili 0x01
2         | 00000010 | 02 ili 0x02
3         | 00000011 | 03 ili 0x03
...
254       | 11111110 | FE ili 0xFE
255       | 11111111 | FF ili 0xFF

Još malo iz ugla elektronike, pošto se program samo vrti u krug i stalno povećava vrednost promenljive "brojac", praktično dobijamo jedan mali generator frekvencije i to takav da će na nožici PC0 da imamo "neku" frekvenciju (videćemo kasnije od čega zavisi), zatim na PC1 dumplo nižu, na PC2 četiri puta nižu i tako redom do PC7 gde će se dobiti 256 puta niža frekvencija signala.


Tehnički smo dobili skoro istu funkciju kao npr klasičan CMOS IC CD4022 "oktalni brojač" (pogledajte DS za još više detalja).

Ako bi hteli ovo i praktično da "vidimo", može se recimo priključiti na svaki od ovih izlaznih pinova PC0 do PC7 po jedna LED dioda preko recimo otpornika od 4.7K (8 LED i 8 otpornika) gde je drugi kraj diode vezan za masu (Vss). Dakle LED spojena na PC0 će najbrže da "svićka" i tako redom sve upola sporije do LED prikljućene na PC7.

Ovde je opisano "šta" smo sa prikazanim programom napravili, sledeći post će objasniti "kako".

BTW: Ako ima neki "dobrovoljac" da nacrta šemu ovog sklopa (PIC16F886 + 8 LED + 8 otpornika + LM7805) bilo bi sjajno jer meni bi to oduzelo puno vremena a bolje to vreme da iskoristim u "pisanije" Smile


U prikazanom primeru vidimo fajl sa nazivom "main.c". To je glavni ili početni fajl našeg programa i u suštini najviše vremena će mo tu provesti tokom pisanja programa. Ovaj fajl je obavezan i radi lakšeg snalaženja treba koristi baš to ime fajla.

Ostale fajlove će uglavnom sam Mplab X IDE da napravi za nas kada se kreira nov projekat i nisu u ovom trenutku toliko bitni.

Takođe u fajlu vidimo dosta teksta koji nije sastavni deo programa i to su "komentari". Komentari su veoma korisna stvar jer sa tim pojašnjavamo kako sebi tako i drugima šta određeno parče programa ili naredba konkretno radi. Ovo treba usvojiti kao praksu i komentarisati što je moguće više. Možda to liči kao "gubljenje vremena" ali videćete već, sad možda znate šta određeno parče programa radi ali posle dan-dva-tri se to zaboravi i podsetnik u obliku komentara je veoma veoma koristan!

Komentari u jeziku C (i jos nekim jezicima) se obično obeležavaju na dva načina, prvi je sa dve kose crte // za komentare koji se nalaze u jednoj liniji a drugi način je sa /* za početak i */ za kraj komentara koji može biti napisan u više linija. 
Naravno na ovaj način možemo tokom pisanja programa da "izbacimo" iz programa neke delove ili komande a da ih ne obrišemo iz fajla što je svakako zgodno kod raznih pokušaja i eksperimenata.

Sad krećemo u malo detaljniji opis prikazanog programa.

Ispod početnog komentara gde stoje neke uopštene informacije o fajlu, autoru itd, vidimo jednu vrlo bitnu liniju:

#include <xc.h>

Ovo je naredba/direktiva koju koristi naš prevodilac (compiler, XC8) i služi da povuče kompletnu definiciju za neke funkcije, adrese registara i gomilu još nekih definicija koje se kasnije mogu koristiti u programu.


Ovakve vrste fajlova se nazivaju "header" fajlovi i imaju ekstenziju .h.
On takodje učitava još dosta nekih pod-fajlova i to je sve automatizovano.

Caka sa ovim header fajlovima je ta što nam pružaju mogućnost da koristimo unificirana imena za nazive PIC registra a ne numeričke adrese koje se obično razlikuju od modela do modela PIC-a. Npr kada baratamo sa PORTC, prevodilac ce tačno znati koja je njegova fizička adresa za izabrani mikrokontroler.
Ovaj fajl ne utiče na zauzeće FLASH memorije i više se moze smatrati kao nekakv rečnik ili adresar.
Tehnički gledano header fajl nije neophodan ali zbog svih dobrih osobina koje pruža skoro se uvek koriste.


BTW: Zahvaljući @enaB imamo i šemu za povezivanje PIC16F886 sa LED diodama prema Primeru broj 1.


Ova šema će ujedno služiti i kao mala referenca za neke druge projekte jer imamo prikazano kako se PIC16F886 priključuje na napajanje i ICSP konektor za programiranje.

Zamenom LED dioda sa nekim drugim periferijama (npr MOSFET, taster, opto-couplet itd) i korišćenjem ostalih slobodnih nožica može se realizovati i neki drugi uređaj prema našoj želji i mogućnostima ovog mikrokontrolera.



Prateći dalje program iz posta broj #10, dolazimo do još jednog bitnog dela svakog programa, to je definicija konfiguracionih registra.

Pošto su PIC-evi napravljeni univerzalno za gomilu različitih aplikacija, neophodno je uraditi konfiguraciju u ovim registrima koja odgovara našoj HW postavci.

Podešavanje ovih opcija se vrši pomoću #pragma config direktiva. To su opet naredbe samom prevodiocu (više se odnose čak na sam SW za programiranje) koje mu daju instrukcije kako da postavi pojedine "osigurače" i one praktično nisu sastavni deo korisničkog programa u smislu da se ne mogu tokom rada menjati. Dakle šta u tim direktivama za konfiguraciju napišemo to će se tokom procesa programiranja (flešovanja) samog PIC-a odraditi.

Ovde je pre svega bitno definisati tip oscilatora koji će se koristiti, #pragma config FOSC.
PIC16F886 nudi 8 različitih opcija/modova za oscilator i oni su sledeći:


1. EC – External clock with I/O on OSC2/CLKOUT. 
2. LP – 32 kHz Low-Power Crystal mode.
3. XT – Medium Gain Crystal or Ceramic Resonator Oscillator mode.
4. HS – High Gain Crystal or Ceramic Resonator mode.
5. EXTRC_CLKOUT – External Resistor-Capacitor (RC) with FOSC/4 output on OSC2/CLKOUT.
6. EXTRC_NOCLKOUT – External Resistor-Capacitor (RC) with I/O on OSC2/CLKOUT.
7. INTRC_CLKOUT – Internal oscillator with FOSC/4 output on OSC2 and I/O on OSC1/CLKIN.
8. INTRC_NOCLKOUT – Internal oscillator with I/O on OSC1/CLKIN and OSC2/CLKOUT.


U većini slučajeva nama će biti interesantno samo dva tipa, prvi je INTRC_NOCLKOUT kada koristimo samo interni oscilator za maksimalni clock do 8MHz (a te dve nožice služe kao stadardne I/O, bonus) ili HS kada koristimo "klasične" spolja priključene kristale koji nam pružaju mogućnost clock-a do 20MHz za ovaj PIC model.

Sledeća konfiguraciona linija #pragma config WDTE postavlja "osigurač" za uključivanje ili isključivanje tkz "psa čuvara" (watchdog).
Watchdog je jedna specifična vrsta resetujućih vremenaca koji služe da resetuju ceo procesor ako se program iz nekog razloga "zaglavi".

Ovaj vremenac radi tako sto mi iz našeg programa moramo da ga resetujemo na neki regularan period pre nego što dođe do definisanog timeout, npr svakih 100us ili češće (sve je to podesivo) i to znači da naš program radi regularno tj da se nije zaglavio. Ako bi se program zaglavio u nekom delu onda neće ni biti ovaj vremenac resetovan i nakon definisanog njegovog perioda (timeout, recimo da je to 1s) on će izvršiti resetovanje celog procesora i ceo program će krenuti ispočetka. Ovo nam ne garantuje da program neće ponovo da se zaglavi ali je u nekim situacijama jako bitno da kada se resetuje program da se postave neka "sigurna" stanja za recimo izlazne pinove koji neće dovesti do nekakve havarije dalje u priključenom HW-u, npr nikako ne sme da budu uključena oba MOSFET kod half-bridge SMPS inače nastaje vatromet! Zato je bitno da se rad programa interno kontroliše preko ovog potpuno nezavisnog Watchdog modula i po potrebi resetuje CPU u slučaju nekakve neregularne situacije sa programom.

BTW: Postoji takođe mogućnost da vam se program zaglavi baš u blizini funkcija koje resetuju Watchdog i tu nema pomoći tako da se za neke veoma kritične aplikacije mora strogo vodi računa sta program (pokušava da) radi i izbeći sve eventualne situacije koje bi mogle da dovedu do takvog stanja, npr deljenje sa nulom ili čuveni "stack owerflow" i slično.

Slikovit primer ovom Watchdog vremencu je kao npr stepenišni automat za svetlo u zgradama. Recimo da ste krenuli sa zadnjeg sprata naniže i da na svakom spratu pritisnete taster za svetlo (resetuje vremenac). Ako to radite periodično na svakom spratu sve će biti ok i imaćete svetlo. Ako ste dangubili na nekom spratu i zaboravili da pritisnete taster za svetlo (zaglavio se program) taj vremenac ce odbrojati svoje definisano vreme i ugasiti svetlo (CPU reset) Smile

Watchdog je malo "advanced" opcija i ako niste sigurno kako se tačno koristi najbolje da je za početak isključite kao u primeru.
Takođe, kod nekoh PIC modela je ovaj tajmer po default uključen, kod nekih isključen i da nebi razmišljali koje je default stanje ako vam ne treba uvek postavite u konfiguraciji da je on isključen.

Kroz ovih par primera sam objasnio princip postavljanja osigurača u konfiguracionim registrima i preobimno bi bilo da ojašnjavam i sve ostale (ima kratak komentar šta/koja radi), mora malo i dokumentacija da se čita Smile

Ono što je zgodno sa Mplab X je to da ima deo gde možemo kroz opcije na ekranu da jednostavno postavimo sve te osigurače i ona se nalazi u meniju Window > PIC Memory Views > Configuration Bits.
Kada izklikćemo željenu konfiguraciju, pritisne se dugme kod tog ekrana "Generate Source Code to Ouput" i uradimo onda copy/paste tog generisanog code-a u naš program.

Ako ovo uradite kako treba (dobra osnova vam je prikazan primer) može odmah da se krene sa pisanjem korisničkog programa.



Za trenutak ćemo preskočiti parče programa u okviru funkcije PIC_Konfiguracija i preći na glavni deo programa.

Glavni korisnički program kod XC prevodioca mora da se nalazi u okviru funkcije main () i tu počinje izvršavanje programa nakon reseta procesora.
 
Funkcija main nema nikakve izlazne parametre, naznačeno sa void ispred imena funkcije kao ni ulazne parametre što je naznačeno sa void u zagradi iza imena.


Main funkcija ima ovakvu sintaksu:

vodi main(void) {


    //moj program ...
    //...


}

Velika otvorena zagrada { označava početak funkcije a zatvorena } kraj funckije. Sve što se nalazi izmedju ovih zagrada se smatra telom funkcije tj konekretno za main() korisničkim programom.


Kada se procesor resetuje kreće izvršavanje jedne po jedne naredbe koja se nalazi u okviru main() funkcije.

Prateći program iz primera nailazimo na sledeće tri linije:

    OSCCONbits.IRCF0=0;
    OSCCONbits.IRCF1=0;
    OSCCONbits.IRCF2=0;

Ove tri instrukcije postavljaju vrednosti bitova u OSCCON registrima koje se tiču brzine oscilatora. Iako smo u predhodnom postu spomenuli konfiguraciju oscilatora, tu je bilo navedeno kakvog je tipa taj oscilator ali ovde imamo mogućnost da dodatno i izaberemo kojom će tačnom brzinom da nam daje clock za procesor.


Ako bi totalno izbacili ove tri linije iz našeg programa prodrazumevala bi se default brzina od 4MHz za ovaj model PIC-a.

Zarad ovog tutorijala i obzirom da sam hteo da praktično prikažem kako LED svićkaju, izabrana je najmanja brzina od 31kHz, praktično je procesor debelo down-clock-ovan Smile
Da sam izabrao neke veće brzine, LED bi prebrzo svićkale i nebi moglo okom da se primeti, samo osciloskopom.
Izbor brzine clock-a se vrši postavljanjem odgovarajuće kombinacije za IRCF0, IRCF1 i IRCF2 (nula ili jedan) na osnovu male tablice koju sam postavio u komentaru.

OSCCONbits je ime registra a IRCFx je ime bita u tom registru. Ovo je sve definisano u spomenutom xc.h header fajlu.
Sličnu sintaksu prate skoro svi ostali registri i bitovi u PIC mikrokontroleru.

Dalje prateći program dolazimo do još jedne funckije, PIC_Konfiguracija().
Po samom nazivu funkcije odmah može da se vidi da je u pitanju naša (custom) funckija koju smo uveli da bi nam olakšala neke stvari.
Naravno nije moranje da tako napišemo ali opet zarad tutorijala sam tako napisao da bi shvatili kako rade korisničke funkcije.

Kada program koji se izvršavao u okviru main() naiđe na korisničku funkciju, on "uskače" u nju i kreće dalje da izvršava instrukcije koje su definisane u toj funkciji. Ovo ulaženje i izlaženje iz korisničke funkcije troši malkice procesorskog vremena i ako vam je baš "frka" za brzinu onda te funkcije mogu da se izbace i da se to sve isto ispiše u okviru main().
Taj "trošak" se svodi u na nekoliko procesorskih taktova (zaboravio sam koliko tačno) ali tako možemo drastično da smanjimo zauzeće FLASH memorije jer neke stvari koje bi trebali recimo 10 puta da uradimo (i 10x ispišemo u main), možemo samo jednom da napišemo ali da pozovemo tu funkciju više puta.
Dakle te funkcije mogu da se smatraju malim pod-programimam ili nezavisnim celinama koji odrade neku stvar. Vrlo često se takve funkcije pišu i u izdvojemin fajlovima što je korisno jer dobijamo "biblioteku" koje rade neke standardne operacije, npr baratanje LCD displejom. Na ovaj način možemo da dobijemo "reusable code", dakle jednom samo napišemo neku funkciju i nju kasnije koristimo za više programa/projekata, samo "dodamo" taj fajl u naš Mplab X projekat.

U prikazanom primeru naša funkcija PIC_Konfiguracija nema ulazne i izlazne parametre (naznačeno sa dva void) ali će te često imati potrebu da funkcijama prosledite neke parametre koja ona "smućka" i vrati nazad neki rezultat. Npr, možemo da napišemo funkciju za račun kvadratnog korena nekog broja, u tom slučaju imamo jedan ulazni parametar (broj za koji se računa) i jedan izlazni parametar (izračunatu vrednost). Isto tako moze da se barata i sa više ulazno/izlaznih parametara.

Pošto je programski jezik C malo "tvrd" po pitanju tipova podataka, kada je potrebno da funkcija prima i vraća neke podatke MORA da se navede i kakvog su tipa ti podaci. To je nešto složenija problematika i neophodno je da to malo detaljnije izučite. Sve oko toga može se naći u korisničkom uputstvu za XC prevodilac (nažalost 500+ stranica):
http://ww1.microchip.com/downloads/en/DeviceDoc/52053B.pdf
Za ovaj primer ću uzeti samo jedan tip podataka, to je "integer" ili skraćeno "int" i njega ću spomenuti nesto kasnije.

Da se vratimo na naš program, stigli smo do funkcije PIC_Konfiguracija i "ušli" u nju.
NAPOMENA: Iako se ova korisnička funkcija tehnički izvršava posle main(), ona MORA u listingu programa da bude napisana ispred main() jer program mora da zna unapred da ona postoji kako bi mogao da je pozove! Dakle, funkcija main() treba da se fizički nalazi na dnu fajla dok se sve ostale korisnicke funkcije definišu pre nje.

U funkcji PIC_Konfiguracija vidimo nekoliko instrukcija koje se tiču konfiguracije ulazno/izlaznih pinova. Više puta je spomenuto da su kod PIC-a njegove nožice više-namenske. Koju konkretnu namenu će imati određuje se upisom odgovorajuće vrednosti u kontrolnim registrima.
Svaki port ima nekoliko kontrolnih registra i za ovaj primer ću spomenuti samo 3, ANSEL, TRISx i WPUx. Ovo "x" reprezentuje ime za odredjeni port, dakle portu A pripadaju registri TRISA i WPUA, portu B pripadaju TRISB i WPUB, portu C pripadaju TRISC i WPUC.

ANSEL je kod ovog modela PIC-a jedinstven samo za port A i zato nije raspoloziv za ostale portove. Taj registar određuje da li će se nožica na tom portu koristiti kao analogni ulaz za A/D konverter. U primeru 1 se nismo "igrali" sa A/D konverterima i zato ćemo tu opciju potpuno isključiti tako što u taj registar upišemo nulu (ili hexadecimlano 0x00).
PS: Pogledajte predhodni post gde sam spominjao brojeve i njihovu reprezentaciju u decimalnom, binarnom i hexadecimalnom formatu.

TRISx registri služe za određivanje smera tj da li će nožica služiti kao ulaz ili kao izlaz. Ovo je 8bitni registar i svaki bit reprezentuje jednu nožicu na portu. Kada se za određeni bit postavi 0 to znači da će ta nožica biti izlaz, 1 znači da će biti ulaz.

WPUx registri služe za uključivanje ili isključivanje internog pull-up otpornika. Slično kao sa TRIS registrima, 0 znači da se isključi interni otpronik, 1 da se uključi. Interni otpornici su praktična stvar jer nam štede spoljne fizičke otpornike kada se ta nožica koristi kao ulaz. Vrednost tog internog otpornika se kreće negde oko nekoliko stotina kiloohm (pogledajte tačnu specifikaciju) i to treba imati u vidu jer možda da vrednost ne bude odgovarajajuća za sve primene. Za npr priključivanje nekakvog tasterčića/prekidača koji se nalazi na samoj pločici je ta vrednost otpora dovoljna.

Sa prikazanom konfiguracijom u primeru 1 smo završili neku našu osnovnu konfiguraciju svih portova i tu se završava naša PIC_Konfiguracija funkcija, program se "vraća" u main() i nastavlja dalje sa izvšavanjem ostalih instrukcija.



Stigli smo i do "promenljivih".

Linija u programu int brojac označava jednu promenljivu pod nazivom "brojac" koja je tipa "int" tj skraćeno od "integer" ili u prevodu, promenljiva koja čuva celobrojne vrednosti.

Промјенљива (програмирање)
Из Википедије, слободне енциклопедије

У програмирању, варијабла или промјенљива (како се чешће назива) означава именовану меморијску локацију која је подобна за складиштење одређеног податка. За разлику од истоименог појма у математици, промјенљива у програмирању није апстрактан појам већ у сваком тренутку извршења програма садржи тачно одређену, конкретну вриједност која се у сваком тренутку може замијенити другом.

Тип промјенљиве

У зависности од програмског језика, промјенљива може имати свој тип, односно уз њу се може везивати и информација о типу податка каквог може чувати. Виши програмски језици, нарочито скриптни, попут PHP-а, Јаваскрипта, језика љуски итд. не подржавају типове промјенљивих, док већина других, попут C-а, C++-а, Јаве, Паскала и других имају јасно изражене и ограничене типове промјенљивих.

Тип промјенљиве одређује какав тип податка дата промјенљива може садржати. Већина језика који подржавају типове података разликују основне (цјелобројне, реалне, кратке, дуге, бројчане, словне итд.) и структуралне (структуре, класе итд.). Одређени језици садрже и додатне, специјалне, типове промјенљивих - показиваче и/или референце (нпр. C познаје само показиваче, C++ и показиваче и референце, PHP само референце, док одређени језици попут Јаваскрипта и Јаве гледају на све променљиве као на референце).


 
Kod PIC mikrokontrolera zavisno od izabrane arhitekture (8,16 ili 32bit) promenljive imaju različite "dužine" za iste tipove.
 
Sledeće dve tablice prikazuju definiciju različitih tipova za 8bitnu platformu tj XC8 prevodilac koji koristimo u ovom primeru.
Crveno markiran deo je definicija za tip "int" koji smo upotrebili u programu.

Linija u programu int brojac praktično opet nije instrukcija za mikroprocesor nego se više smatra kao uputsvo samom prevodiocu kako nadalje u programu da tu promenljivu obrađuje ("handluje"). U tom smislu na ovu liniju nije moguće postaviti "break point" kod korišćenja debugera jer tu program praktično i ne prolazi.

Dakle iskazom int brojac smo unapred rezervisali SRAM memorisku lokaciju u mikroprocesoru (data memory) gde možemo da smestimo neke naše podatke tokom rada programa. Pošto je u pitanju promenljiva dužine 16bita, ona će na 8bitnoj PIC platformi fizički zauzeti dve memoriske lokacije. U suštini nas ne treba da interesuje kako to iza dublje u samom procesoru radi jer oko toga brine sam programski jezik i prevodilac ali moramo to imati u vidu zbog ukupnog zauzeća memorije u procesoru. Kada pišemo neke složenije programe i imamo dosta više promenljivih, moramo se držazti limita koji je specificiran za određeni model PIC-a i konkretno za PIC16F886A imamo 368 slobodnih memoriskih lokacija što je dovoljno i za relativno složene programe.
U pojedinim slučajevima sam prevodilac može za nas da "uštedi" malo memorije tako što će analizom našeg progrma tokom faze prevođenja da ustanovi kako/kad/gde smo sve koristili promenljive i da eventualno to malo u letu izmeni zarad manjeg SRAM zauzeća a bez vidljive funkcionalne izmene samog programa!

Prateći dalje program iz primera dolazimo do "programskih petlji" (eng. loops).
http://en.wikipedia.org/wiki/Loop_(computing)#Loops

Petlje u su naredbe/izkazi pomoću kojih možemo da ponovimo neke druge naredbe ili instrukcije određeni broj puta i petlje spadaju u grupu naredbi za kontrolu toka rada programa.

Osnovna dva tipa su petlje koje se izvršsavaju na osnovu ispunjavanja nekog definisanog uslova kao sto je while i petlje koje se izvršavaju tačno definisan broj puta poput for.

Petlje generalno imaju definiciju uslova rada i telo petlje gde smeštamo neke druge instrukcije.

Za while petlje sintaksa je sledeća:

while (neki_uslov) { //pocetak petlje

//telo petlje ...

//nasa instrukcija 1 
//nasa instrukcija 2
//... 

} //kraj petlje

Sa while petljom, napisane instrukcije u njenom telu će se izvršavati onoliko puta sve dok se ne ispuni uslov koji to ponavljanje ne prekine. Taj uslov (neki_uslov) se evaluira i ako rezultat te operacije bude tačan (true) petlja će nastaviti izvršavanje instrukcija navedenih u telu.

Ako neko/nešto iz tela petlje (ili bilo šta drugo, npr iz interapt rutine) tokom rada dovede da se u sledećem prolazu neki_uslov evaluira i dobije netačan (false) rezultat, petlja se prekida i ne ulazi se više u telo nego program nastavlja dalje izvšavanje instrukcija koje se nalaze iza definisanog kraja petlje.



U našem primeru imamo napisano while(1) i to znači da će neki_uslov uvek da daje tačan rezultat (1 se smatra/evaluira kao true) i na taj način smo dobili beskonačno ponavljanje tj beskonačnu petlju.
Petlje sa beskonačnim ponavljanjem se najčešće koriste kao nosioci celog korisničkog programa jer naravno cilj nam je da se napisan program ponavlja u nedogled sve dok je kontroler uključen.

Sledeći tip programskim petlji su petlje sa kontrolom brojanja prolaza, tj "čuvene" for petlje.
Ove petlje su vrlo slične while ali imaju mali dodatak da za neki_uslov možemo da navedemo mali iskaz koji nam daje mogućnost da navedemo ime varijable kojoj može da se za svaki prolaz uveća ili smanji njena vrednost i to u definisanom opsegu veličina.

Za for petlje sintaksa je sledeća:

for (promenljiva={pocetna vrednost}; promenljiva{ispunjava uslov}; promenljiva{iskaz za povecavanje/smanjivanje}) {

//telo petlje ...

//nasa instrukcija 1 
//nasa instrukcija 2
//... 

} //kraj petlje

Pogledajmo još jednom praktičan primer:

for (brojac=0; brojac<=255; brojac++) {
//telo petlje ...
}

Ovako napisana petlja može se protumačiti na sledeći način: ponavljaj telo petlje i to tako što za prvi/početni prolaz postavi promenljivu brojac na 0, zatim u sledećem prolazu povećaj za 1 promenljivu brojac i ponvaljaj izvršavanje tela petlje sve dok se ispunjava uslov da je brojac manji ili jednak od 255.



Na ovaj način smo dobili da se telo petlje ponavlja (obratite pažnju) tačno 256 puta.

Iskaz brojac++ je jedna zanimljiva stvar u programskim jezcima poput C/C++ i naziva se "increment" tj povećavanje promenljivih za 1. To je praktično skraćeno napisan iskaz kao npr brojac=brojac+1.
Analogno tome postoje takodje i "decrement" koji radi obrnutu stvar i piše se kao brojac-- a ekvivalnet je iskaza brojac=brojac-1.

Kombinacijom ovih increment ili decrement zajedno sa postavljanjem početka i kraja "opsega", sa for petljom možemo vrlo lako i prosto odraditi neke stvari predefinisan broj puta.

E sad ono što je zanimljivo sa for petljom, osim što smo promenljivu brojac iskoritili za definiciju broja prolaza, ništa nas ne sprečava da istu tu promenljivu dalje iskoristimo u programu na neke druge operacije kao što smo je urađeno sa iskazom PORTC = brojac (taj iskas sam objasnio u predhodnim postovima).
Ovako smo ubili dve muve odjednom, definisali broj prolaza i iskoristili promenljivu dalje u programu.

Prateći ovu logiku mogli smo da definisemo pelju i kao npr:

for (brojac=1255; brojac>=1000; brojac--) {
//telo petlje ...
}

Na ovaj način smo opet dobili da se telo petlje ponovi 256 puta ali se ovom prilikom broji unazad tj vrednost promenljive brojac će biti 1255 za prvi prolaz, pa 1254, 1253 i tako redom dok ne stigne do 1000.



Pošto sad znamo kako rade while i for petlje uviđamo još jednu zanimljivu stvar a to je da je njihova primena skoro ista i iste efekte možemo da dobijemo upotrebom jedne ili druge a to se vidi i kroz ovaj ekvivaletni code koji radi potpuno istu stvar kao i for petlja iz primera našeg programa:

//postavi pocetnu vrednost
brojac = 0;

while (brojac<=255) {
//telo petlje ...
//...

//povecaj za jedan
brojac++;
}

Izbor izmedju while i for je više stvar navike i preglednosti programa, na isto se svodi Wink
 
Tips & tricks
 
- Korišćenje increment/decrement nije limitirano samo za promenu za 1, moguće je i više kao npr:

brojac += 10; //uvecaj za 10
brojac *= 2; //uvecaj duplo
brojac -= promenljiva2 // umanji brojac za trenutnu vrednost promenljiva2
...
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)