Sari la conținut
ELFORUM - Forumul electronistilor

Invatat programare de la zero


riciu

Postări Recomandate

Ok, am putin timp ca sa scriu asa ca ...

 

Licuriciul cu functii. Si extins ... Si cu alt controller (12F675) ca sa facem lucrurile mai interesante.

 

Programul licurici aprinde un LED si il stinge in mod regulat, la un anumit interval de timp. Intervalul de timp va fi variabil si controlat de un potentiometru conectat la un pin (pinul 6 pot observa din datasheet ca are functie analogica, ADC, deci il voi folosi pe acesta).

 

Vom face urmatoarele:

 

- Folosim un controler 12F 675. E un controler mic, ieftin si are suficienti pini pentru ce vrem sa facem. Plus ca avem posibilitatea de a vedea cum se lucreaza cu un controller din seria 12F - are anumite particularitati.

- cream o functie care va aprinde si stinge LED-ul pe pinul GP0 (adica pinul fizic 7). Functia va avea ca parametru (informatie de "intrare") perioada licuriciului.

- cream o functie care "citeste" analogic tensiunea pe pinul 6 si intoarce (adica functia are un rezultat) durata perioadei de alternare care va fi folosita ca parametru de intrare de functia care aprinde si stinge LED-ul.

 

Prin urmare vom folosi atat capabilitatile de pini digitali ale 12F675 cat si capabilitatile de pini analogici (pentru a citi tensiunea data de un potentiometru pe pinul 6).

 

PASUL 1:

Folosim compilatorul mikroC for PIC. Acesta ne ofera de-a gata functia de citire a ADC-ului :)

 

Citim datasheet-ul aici.

 

Obtinem urmatoarele informatii:

- pag1: controlerul are oscilator intern. Asadar scapam de o grija, folosim osc-ul intern.

- pag1: La Peripheral Features citim, controlerul are urmatoarele module: port I/O (il folosim), comparator analog (il dezactivam), modul ADC (il folosim), module Timer (doua bucati, Timer0 si Timer1) - ne le folosim (desi am putea :) )

- pag2: controlerul are un singur port I/O in care aduna toti pinii controlerului mai putin pinii de alimentare. Pinii fizici sunt etichetati cu GP0 ... GP5.

- pag 19: Numele portului I/O este GPIO, este unul singur si are numai  6 biti (spuneam ca standard, porturile au cate 8 biti dar in acest caz pentru ca pinii sunt putini se folosesc doar 6biti adica exact cati pini sunt), de la GPIO0 la GPIO5. Avem doi registri pentru controlul pinilor dpdv digital: GPIO si TRISIO.

- pag 44: Ne intereseaza registrul ANSEL astfel incat sa setam acel bit din registru corespunzator pinului fizic 6 (adica il facem 1) pentru a face pinul fizic 6 un pin analogic. Ca sa il facem nu numai analogic dar si intrare, folosim la momentul potrivit registrul TRISIO. De unde am stiut de registrul ANSEL? Cam toate controlerele mai moderne de la Microchip au un registru numit ANSEL (sau daca avem mai multi pini analogici decat incap intr-un registru, mai exista un registru suplimentar numit ANSELH de la "ANSEL high part") asa ca trebuie cautat in datasheet la sectiunea ADC.

 

Cam astea ar fi informatiile necesare. Daca mai lipseste ceva, editez pe parcurs.

 

PASUL 2:

 

Cream un proiect in mikroC.

Posted Image

 

Next.

Next.

 

Bifam sa includem in proiect toate librariile oferite gratis de mikroC - ne face viata mai usoara.

Posted Image

 

Selectam Internal Oscillator fara ClockOut si cu pin digital pe celalalt pin.

Posted Image

 

MCLR disabled - nu avem nevoie de un pin de reset. Daca l-am lasa pe Enable ar trebui sa adaugam un rezistor de pull-up catre VDD. Si nu vrem :)

Posted Image

 

Dezactivam BrownOut iar protectiile la copiere raman pe Disabled.

Posted Image

 

PASUL 3:

In acest moment avem totul pregatit ca sa ne apucam sa facem codul.

Posted Image

Editat de mars01
Link spre comentariu

PASUL 4:

Configuram controller-ul.

Pentru aceasta, doar ca sa ne aflam in treaba, cream o functie void init_sys(). Observati ca nu are nici-un parametru si nici nu intoarce ceva (nu intorce nimic pentru ca are void in fata si nu are parametri pentru ca nu are nimic intre paranteze).

Practic folosim aceasta functie degeaba, doar ca sa "impachetam" codul mai ordonat si sa avem totul cat mai logic in functia principala void main().

In aceasta functie vom seta toti acei registri ai controlerului, acolo unde este necesar, astfel incat sa putem face programul sa functioneze.

 

a) Dezactivam comparatorul: la pagina 35 avem registrul de control al compratorului CMCON. Vedem ca bitii CM2:CM0 controleaza modul de lucru al comparatorului. De unde stiu ca il dezactivez de aici? Pentru ca am citit sectiunea aceasta. Citesti si afli ce te intereseaza ...

 

Pe pagina 37 aflam ca:

Posted Image

 

Asadar ... trebuie ca bitii CM2 = 1 si CM1 = 1 si CM0 = 1 ca sa dezactivam comparatorul. Ok?

 

b) Bitul 0 din registrul GPIO corespunde pinului 7 pe care o sa punem LED-ul licuriciului. Deci o sa fie pin digital si iesire. Il initializam cu zero (LED-ul sta default OFF).

Asadar:

bitul 0 din TRISIO este 0 (ne amintim ca zero, 0,  seamana cu 'O" din "OUTPUT")

bitul 0 din GPIO este 0 (practic LED-ul la pornire este OFF)

 

Bitul 1 din registrul GPIO corespunde pinului 6 pe care o sa avem potentiometrul de reglaj al timpului, duratei de "clipocire". Deci facem acest pin analogic si intrare. Bitul 1 din ANSEL face din pinul 6 un pin analogic cand este setat (adica cand este 1 logic).

Asadar:

bitul 1 din TRISIO este 1 (ne amintim ca unu, 1, seamana cu "I" din "INPUT").

bitul 1 din ANSEL este 1.

 

Posted Image

 

 

c) pentru ca oprim ce nu ne intereseaza sa folosim, oprim si timerele:

Pentru aceasta dezactivam global intreruperile prin stergerea (se pune valoarea 0) bitului 7 din registrul INTCON adica a bitului GIE (global interrupt enable). Chiar daca nu am discutat despre intreruperi ii dam inainte si le dezactivam, ca oricum nu stim acum ce este cu ele, nu?

bitul 7 adica GIE, din registrul INTCON este 0

 

Fiecarui timer i se poate dezactiva individula intreruperea asociata.

TIMER 0: se pune 0 in bitul 5 din registrul INTCON, adica bitul numit T0IE (timer0 interrupt enable)

TIMER1: se pune 0 in bitul 0 din registurl T1CON, adica bitul TMR1ON (timer1 ON)

 

Toata chestia aceasta de la punctul c) nu este necesara. Pentru ca intreruperile respective nu sunt asociate nici-unei functii deci nu se intrerupe nimic. Dar ... este un exercitiu bun de setare a registrilor.

 

De unde stiu toto registrii astia? Citind datasheet-ul. Totul este acolo. Prima data s-ar putea sa para ciudat, strain, nu intelegi nimic. Dar dupa cateva parcurgeri, chiar si pe sarite, o sa devina totul clar.

 

 

ATENTIE:

In compilatorul mikroC, fiecare bit poate fi controlat individual cu un cuvant cheie de forma:

nume_bit

Numele bit-ului il gasiti in datasheet cand se face descrierea registrilor care ii contin.

E suficient sa incepeti sa scrieti numele si apoi apasati combinatia de taste: CTRL + SPACE si apare o lista cu sugestii.

 

Ex:

Posted Image

 

 

Si acum scriem codul functiei void init_sys()

void init_sys() {  // dezactivam comparator  CM2_bit = 1;  CM1_bit = 1;  CM0_bit = 1;    // dezactivam intreruperi  GIE_bit = 0;    // ne ocupam de pinul 7 unde punem LED-ul si care corespunde GP0  // GP0 este bitul 0 din GPIO  TRISIO0_bit = 0; // pinul este iesire  GP0_bit = 0; // pinul GP0 adica pinul 7 fizic porneste in starea OFF    // ne ocupam de pinul 6 unde punem potentiometrul si care corespunde lui ANS1  // ANS1 este bitul 1 din ANSEL  TRISIO1_bit = 1; // pinul este intrare  ANS1_bit = 1;    // pinul este pin analogic}

De unde am scos pe GP0 si ANS1? De unde am stiut de aceste cuvinte cheie? Din nou, repet, am citit in datasheet denumirile bit-ilor ...

Editat de mars01
Link spre comentariu

PASUL 5:

 

Cream functia care functie de parametrii sai va face LED-ul sa clipoceasca cu o anumita perioada, sau nu. Numele acesteia este:

void blink (int durata, int stare);

Functia nu intorce nimic (nu este nevoie pentru ca efectul ei este sa stinga si sa aprinda un LED). De aici si cuvantul cheie void de la inceputul liniei precendente.

Functia are parametri: durata clipocelii, parametrul durata si daca functia este activa sau nu, parametrul stare.

 

 

Convertorul ADC va citi pe pinul 6 o tensiune de la 0 la 5V (data de un potentiometru) si o va transforma intr-un numar de la 0 la 1023. De ce de la 0 la 1023? Pentru ca acest convertor ADC care il gasim ca modul in controlerul PIC 12F675 este un controler pe 10bit.

Iar 2 la puterea 10, din care scadem 1, ne da numarul maxim care ni-l poate oferi un numar pe 10bit si este 1024 - 1 = 1023.

 

De ce scadem 1? Pentru ca dispozitivele digitale numara de la 0 si nu de la 1. Ori pentru 1024 de valori care incep cu cifra 0, numarul maxim este 1023.

 

O chestie mai subtila.

Functia delay_ms() oferita de compilatorul mikroC este o functie care ... accepta ca parametru o valoare in milisecunde. Cand programul da de un apel al acestei functii, va sta pe loc (va bate pasul pe loc) pentru aceasta perioada si pe urma trece mai departe.

O chestie insa care nu este vizibila la prima vedere este ca acest parametru al functiei, ca un ex: delay_ms(53) (ce se gaseste intre paranteze este de fapt aceasta durata de intarziere) nu este o variabila standard....

 

Stim ca in C, toti parametrii unei functii sunt de fapt variabile. Asadar cand scriem delay_ms (10) noi ii dam un parametru cu valoarea 10 functiei delay_ms. Acest parametru este o variabila constanta. Adica o asa numita constanta.

Cum stim acest lucru?

 

Scriem in fereastra mikroC cuvantul cheie delay si apoi apasati combinatia de taste CTRL + SPACE.

Veti acea acces la informatii cu privire la parametrii functiei si ...

 

Posted Image

 

Vedem clar ca parametrul functiei delay_ms() este o constanta: const unsigned long Time_In_ms

 

Cu ce ne influenteaza?

 

Pai noi vrem sa variem durata de clipoceala functie de o valoare de la 0 la 1023 data de convertorul ADC. Practic unul din parametrii functiei de blink

void blink(int durata, int stare);

si ma refer la parametrul durata, pe care intentionez sa il livrez ca parametru unei functii delay_ms() (deci avem o functie apelata intr-o alta functie, functii imbricate la care facea referire colegul @core cand a amintit ca aceste controlere au o limita de o adancime de 8 asemenea imbricari, una in alta).

 

Ori daca acest parametru al functiei delay_ms() este o constanta eu nu pot sa ii dau ca parametru o variabila standard (care sa ia valoare de la 0 la 1023).

 

De ce nu pot sa ii pun o variabila standard ca parametru functiei delay_ms() care am vazut ca are ca parametru o varaibila constanta? Poate va amintiti cand spuneam ca o constanta este ... o valoare constanta. Nu poti sa pui o variabila standard in loc de o variabila constanta (adica prescurtat constanta). De ce? De aia :) Cititi si voi pe net.

 

Din aceasta cauza va trebui sa utilizez un artificiu.

 

Mai exact am sa aleg un delay minim, o rezolutie minima. Sa zicem ca durata maxima de clipoceala este de 1023 * 100ms = 102300ms = 102.3 secunde = 1minut si 42.3 secunde.

Am ales ca valoarea sa se divida cu 1023 pentru ca stiu ce vreau sa fac. Inainte de a face efectiv.

 

Am sa fac in interiorul functiei un ciclu FOR care va repeta continutul ciclului de atatea ori cat este valoarea parametrului durata al functiei blink.

Iar in interiorul ciclului se va executa la fiecare trecere prin ciclul for, functia delay_ms(100) adica o intarziere de 100ms.

 

Deci vom avea o rezolutie de 100ms. Vom putea modifica valoarea clipocelii in trepte de 100ms.

Si cum functia delay_ms(100) are ca parametru o valoare constanta, toata lumea fericita.

 

Al 2-lea parametru al functiei blink este INT stare. Daca acest parametru este 1 (de fapt orice alta valoare dar care sa nu fie 0), functia va face clipoceala. Daca parametrul este 0, chiar daca functia este apelata si mai are si un parametru durata, functia nu va face nimic. Este ca un intrerupator. Nu util in acest caz dar daca punem de ex un switch pe un pin si valoarea acestuia o dam parametrului stare, va imaginati ca clipoceala va depinde de switch.

 

Si implementarea.

void blink( int durata, int stare) {  int i; // variabila iterator din ciclul FOR.    /* daca stare este 0 iesi din functie fara sa faci nimic.  La intalnirea cuvantului cheie RETURN se iese din functie */  if (stare == 0) {    return;  }  else {    GP0_bit = 1; // aprindem LED-ul    // urmeaza delay-ul cat timp LED-ul este ON    for (i = 0; i <= durata; i = i + 1){      delay_ms(100);    }        GP0_bit = 0; // stingem LED-ul    // urmeaza delay-ul cat timp LED-ul este OFF    for (i = 0; i <= durata; i = i + 1){      delay_ms(100);    }  }}

Va urma.

Editat de mars01
Link spre comentariu

@Liviu Tx, multumesc. Fac ce pot, cand pot.

 

O scurta referire la codul functiei blink de deasupra.

 

Daca @Liviu M era pe aici :) ar fi remarcat imediat ca exista o greseala micuta in cod.

Mai exact in ciclul for, am scris:

for (i = 0; i <= durata; i = i + 1){      delay_ms(100);}

dar cum numaram de la 0 nu este corect sa fac i <= durata. Aceasta introduce un delay suplimentar de 100ms fata de ce este intentionat. Corect este i < durata.

 

Asa ca functia corecta ar fi asa:

void blink( int durata, int stare) {  int i; // variabila iterator din ciclul FOR.    /* daca stare este 0 iesi din functie fara sa faci nimic.  La intalnirea cuvantului cheie RETURN se iese din functie */  if (stare == 0) {    return;  }  else {    GP0_bit = 1; // aprindem LED-ul    // urmeaza delay-ul cat timp LED-ul este ON    for (i = 0; i < durata; i = i + 1){      delay_ms(100);    }        GP0_bit = 0; // stingem LED-ul    // urmeaza delay-ul cat timp LED-ul este OFF    for (i = 0; i < durata; i = i + 1){      delay_ms(100);    }  }}
Editat de mars01
Link spre comentariu

O scurta paranteza..

Pentru aplicatii nepretentioase putem folosi functia

Vdelay_ms()

care face acelasi lucru cu delay_ms() doar ca nu este la fel de precisa dar parametrul este o variabila. Codul de mai sus arata asha cu vdelay :

 

void blink( int durata, int stare) {int i; // variabila iterator din ciclul FOR./* daca stare este 0 iesi din functie fara sa faci nimic.La intalnirea cuvantului cheie RETURN se iese din functie */if (stare == 0) {return;}else {GP0_bit = 1; // aprindem LED-ul// urmeaza delay-ul cat timp LED-ul este ONvdelay_ms(100*durata);GP0_bit = 0; // stingem LED-ul// urmeaza delay-ul cat timp LED-ul este OFFvdelay_ms(100*durata);}}
Link spre comentariu

@djvas nu stiam de functia Vdelay() :) Bine punctat.

@Liviu M, cred ca te-ai plictisit de programe din astea de duzina :) Mai lipseste o initializare ADC_Init() prin functia init_sys() (nu am facut inca functia de citire a ADC-ului) dar le pun cap la cap maine. Sper.

 

Urmatorul program o sa fie ceva simplu, in care apasam un buton si se schimba starea din OFF in ON a unui LED si tot asa, dar doar pe tranzitia de la HIGH la LOW a butonului.

Editat de mars01
Link spre comentariu

Continuare.

 

Mai avem de facut functia care citeste date analogice cu ajutorul convertorului ADC, de pe pinul 6 al controller-ului.

Pe pinul 6 avem canalul 1 al ADC, desemnat AN1 si care a fost "activat" prin setarea (valoarea 1) bitului ANS1 din registrul ANSEL.

 

In colectiile de functii (libraries) furnizate pentru ADC de catre mikroC se gasesesc urmatoarele functii:

void ADC_Init();

care va initializa convertorul ADC (se face toata bucataria interna cu initializarile celorlalri registri care tine de ADC - pentru detalii ... cititi sectiunea ADC din datasheet (foaia tehnica).

 

Cat si functia la care ne intereseaza rezultatul:

unsigned ADC_Read(unsigned short channel);

In sintaxa mikroC atunci cand intalniti doar cuvantul cheie "unsigned" sau doar cuvantul cheie "signed" fara referire la tipurile (types, cum ar fi char, int, long) inseamna ca se refera la unsigned int respectiv la, signed int.

 

In functia de mai sus, unsigned ADC_Read(unsigned short channel), parametrul 'channel' se refera la urmatorul lucru.

 

In controlerele PIC pe 8 bit (adica toate din seriile 10F, 12F, 16F, 18F) daca nu si la altele, se gaseste un singur convertor ADC (posibil sa fie si exceptii).

Adica avem un singur modul ADC, dar dupa cum putem observa (vezi poza de mai jos), sunt mai multi pini care au functia de pin analogic ADC (desemnati cu ANx). Aceasta deoarece, in fapt, acesti pini sunt multiplexati si acceseaza doar acest singur modul ADC. Acesti pini multiplexati sunt denumiti aici, canale (channels).

 

Posted Image

 

Noi ne-am ales pinul 6 (adica AN1, adica channel 1). Puteam sa alegem si un altul, nu exista (in acest caz) un motiv special pentru care l-am ales pe acesta. Erau mai multi si am ales unul. Asta este :)

 

Asadar apelul functiei ADC_Read() va arata ceva de genul:

unsigned int rezultat;rezultat = ADC_Read(1);

De ce tipul variabilei unde pun ce iese din functia ADC_Read(1) este tip unsigned int?

Pentru ca functia imi intoarce un rezultat de tip unsigned int.

 

De ce parametrul functiei ADC_Read(1) este ... '1' ?

Pentru ca pinul 'citit' este pinul asociat canalului ADC cu numarul 1. Din datasheet am vazut ca acesta este expus la mediul exterior (dupa efectuarea de configurari in registrii TRISIO si ANSEL) prin pinul fizic 6.

 

In paranteza precedenta am spus ca am modificat registrii TRISIO si ANSEL ca sa expunem canalul 1 al ADC prin pinul 6. 

Aceasta am facut-o in functia init_sys() pe care am scris-o mai devreme.

Adica aici:

  // ne ocupam de pinul 6 unde punem potentiometrul si care corespunde lui ANS1  // ANS1 este bitul 1 din ANSEL  TRISIO1_bit = 1; // pinul este intrare - aici modificam un bit din registrul TRISIO  ANS1_bit = 1;    // pinul este pin analogic - aici modificam un bit din registrul ANSEL

Ce nu am facut este faptul de a scrie in functia init_sys() inca o linie necesara si anume:

ADC_init();

Aceasta, cum am scris, se ocupa de bucataria interna de a pregati convertorul ADC pentru o conversie. O conversie a unei tensiuni (nici-odata mai mare de VDD, dar posibil mai mica cand se foloseste, in anumite cazuri, tensiunea de referinta - nu e cazul insa aici) intr-un numar cu valoare de la 0 la 1023 [am scris anterior ca acest convertor ADC din PIC12F675 este pe 10bit: numerele pozitive care se scriu pe 10 bit sunt de la 0 la ((2^10) - 1)]

 

De unde stiu despre aceste functii?

Din help-ul mikroC.

 

Cel mai usor se ajunge la sectiunea dorita din help, prin efectuarea de 'dublu click' pe numele functiei din Library Manager.

Ca mai jos.

Posted Image

 

Sau se poate intra in help (tasta F1 din pagina principala a mikroC) si intrari pe calea:

mikroC for C libraries -> Hardware Libraries -> ADC Library

 

Posted Image

 

 

Cum spuneam trebuie sa modificam functia init_sys() si sa adugam ADC_init():

void init_sys() {  // dezactivam comparator  CM2_bit = 1;  CM1_bit = 1;  CM0_bit = 1;    // dezactivam intreruperi  GIE_bit = 0;    // ne ocupam de pinul 7 unde punem LED-ul si care corespunde GP0  // GP0 este bitul 0 din GPIO  TRISIO0_bit = 0; // pinul este iesire  GP0_bit = 0; // pinul GP0 adica pinul 7 fizic porneste in starea OFF    // ne ocupam de pinul 6 unde punem potentiometrul si care corespunde lui ANS1  // ANS1 este bitul 1 din ANSEL  TRISIO1_bit = 1; // pinul este intrare  ANS1_bit = 1;    // pinul este pin analogic    ADC_Init();      // initializare ADC}

Si acum scriem o functie care va returna rezultatul ADC.

Total inutil sa scriem o functie care sa includa functia ADC_read(), dar pentru a va arata cum se face o functie care intoarce o valoare si cum se foloseste aceasta, am sa fac totusi aceasta.

Tineti minte insa, functia ADC_read() se poate folosi ca atare.

 

Asadar functia de citire a ADC-ului:

unsigned int citeste_ADC() {  /* Combinam in urmatoare linie atat declararea variabilei locale rezultat cu tipul unsigned int,     cat si ii dam valoare cu ce iese din functia ADC_Read(1)  */  unsigned int rezultat = ADC_Read(1);  return rezultat;  // returnam valoarea variabilei locale rezultat.}

Luati aminte: cu doar functia aceasta mica de deasupra, cuplata cu ADC_init() din functia de initializare cat si configurarile registrilor TRISIO SI ANSEL pentru pinul dorit analogic si care are aceasta posibilitate, citim o intrare ADC. Adica citim o tensiune

La fel de usor, cu cateva modificari, puteam face un ... voltmetru. Nu? :) Cat de usor se face un voltmetru !!! Sau ampermetru !!! Sau de ce nu, power metru !!!

 

O sa ajungem si la acesta, eventual aprindem un LED daca tensiunea este mai mare decat 12V, aprindem doua daca tensiunea este mai mare de 14.4V si aprindem un LED rosu sa zicem, daca tensiunea este sub 10V. Aplicatie ... auto?

 

Incet incet, programul nostru se contureaza.

Mai ramane sa combinam tot ce am scris intr-un singur program si ... cum zice americanu': "Bob's your uncle" :)

 

 

LE: toata vorba lunga de mai sus a fost doar pentru ca trebuia sa explic ce fac. Desi procesul pare alambicat, in realitate este ceva care dureaza 10 ... 15 minute. Ma refer la realizarea unui asemenea programel. Mai mult stai si cauti in datasheet registrii care trebuie modificati decat stai sa scrii efectiv cod. Din aceasta cauza multi tipi pe Internet fac tot felul de programele "pe banda".

Daca vreti, o  sa puteti si voi sa faceti acelasi lucru. In scurt timp. :)

Nu o sa mai stati sa asteptati sa va faca altcineva proiectul de voltmetru sau statie de lipit sau termostat sau automat masina de spalat sau porti automate sau mai stiu eu ce proiect care se cauta pe forum ca painea calda :)

Editat de mars01
Link spre comentariu

Mai exista o problema in functia init_sys() ...

 

Asa cum se poate vedea mai jos, unii dintre pinii controller-ului sunt setati in mod default la pornire ca ... pini analogici.

Ori noi nu am facut decat sa confirmam ca pinul 6 (adica AN1) este analogic dar nu am facut nimic ca pinul 7 sa fie pin digital ...

 

Asadar lipseste o 'initializare' a registrului ANSEL cu 0 (inainte evident de a face ANS1_bit  = 1, lucru care schimba doar bitul 1 din registrul ANSEL).

Sau, asa cum am facut in programul finalizat de mai jos, am modificat punctual unde m-a interesat si anume am adaugat o linie de cod:

ANS0_bit = 0; // pinul fizic 6 este pin digital

Posted Image

 

In figura de mai sus, deasupra la fiecare bit scrie ceva. De ex, la bitii ANS3, ANS2, ANS1, ANS0 deauspra scrie R/W-1.

A mai facut un coleg o mentiune pe topic despre aceasta.

Tradus inseamna ca acei pini sunt READ/ Write (adica se pot citi dar si scrie) iar valoarea default la pornirea controllerului (sau dupa reset) este de 1 logic. Cu alte cuvinte pinii care corespund acestor biti (ANS3, ANS2, ANS1, ANS0) sunt la pornire pini analogici (pinii corespondenti sunt in ordine: pinii fizici 7,6,5,3).

 

Functia init_sys() corecta ar fi asa:

void init_sys() {  // dezactivam comparator  CM2_bit = 1;  CM1_bit = 1;  CM0_bit = 1;    // dezactivam intreruperi  GIE_bit = 0;  // ne ocupam de pinul 7 unde punem LED-ul si care corespunde GP0  // GP0 este bitul 0 din GPIO  TRISIO0_bit = 0; // pinul este iesire  ANS0_bit = 0;   // pinul fizic 6 este pin digital  GP0_bit = 0; // pinul GP0 adica pinul 7 fizic porneste in starea OFF  // ne ocupam de pinul 6 unde punem potentiometrul si care corespunde lui ANS1  // ANS1 este bitul 1 din ANSEL  TRISIO1_bit = 1; // pinul este intrare  ANS1_bit = 1;    // pinul este pin analogic    ADC_Init();      // initializare ADC}

Urmatoarea modificare.

 

Daca lasam valoarea de 100ms in functia delay_ms() valoarea rezultata va fi un pic cam mare daca facem o simulare. Pana la urma este un licurici pe care sa-l vezi ca isi schimba starea intr-un timp rezonabil si nu sa stai cu minutele dupa aceasta.

 

Prin urmare, in functia blink(durata, stare) schimbam valoarea delay_ms(100) in delay_ms(10).

Cu alte cuvinte valoarea maxima pentru durata unei stari (ON sau OFF) a LED-ului este de 1023 (valoarea max oferita de ADC) multiplicata cu 10ms.

 

Avem astfel o rezolutie de 10ms si o durata maxima a unei stari de 10230ms = 10.23 sec.

 

Functia modificata este:

void blink( unsigned int durata, int stare) {  int i; // variabila iterator din ciclul FOR.  /* daca stare este 0 iesi din functie fara sa faci nimic.  La intalnirea cuvantului cheie RETURN se iese din functie */  if (stare == 0) {    return;  }  else {    GP0_bit = 1; // aprindem LED-ul    // urmeaza delay-ul cat timp LED-ul este ON    for (i = 0; i <= durata; i = i + 1){      delay_ms(10);    }        GP0_bit = 0; // stingem LED-ul    // urmeaza delay-ul cat timp LED-ul este OFF    for (i = 0; i <= durata; i = i + 1){      delay_ms(10);    }  }}

Intr-un final programul complet si functional este urmatorul:

void init_sys() {  // dezactivam comparator  CM2_bit = 1;  CM1_bit = 1;  CM0_bit = 1;    // dezactivam intreruperi  GIE_bit = 0;  // ne ocupam de pinul 7 unde punem LED-ul si care corespunde GP0  // GP0 este bitul 0 din GPIO  TRISIO0_bit = 0; // pinul este iesire  ANS0_bit = 0;   // pinul este digital  GP0_bit = 0; // pinul GP0 adica pinul 7 fizic porneste in starea OFF  // ne ocupam de pinul 6 unde punem potentiometrul si care corespunde lui ANS1  // ANS1 este bitul 1 din ANSEL  TRISIO1_bit = 1; // pinul este intrare  ANS1_bit = 1;    // pinul este pin analogic    ADC_Init();      // initializare ADC}void blink( unsigned int durata, int stare) {  int i; // variabila iterator din ciclul FOR.  /* daca stare este 0 iesi din functie fara sa faci nimic.  La intalnirea cuvantului cheie RETURN se iese din functie */  if (stare == 0) {    return;  }  else {    GP0_bit = 1; // aprindem LED-ul    // urmeaza delay-ul cat timp LED-ul este ON    for (i = 0; i <= durata; i = i + 1){      delay_ms(10);    }        GP0_bit = 0; // stingem LED-ul    // urmeaza delay-ul cat timp LED-ul este OFF    for (i = 0; i <= durata; i = i + 1){      delay_ms(10);    }  }}unsigned int citeste_ADC() {  /* Combinam in urmatoare linie atat declararea variabilei locale rezultat cu tipul unsigned int,     cat si ii dam valoare cu ce iese din functia ADC_Read(1)  */  unsigned int rezultat = ADC_Read(1);  return rezultat;  // returnam valoarea variabilei locale rezultat.}void main() {  unsigned int durata_blink;  // variabila durata_blink va stoca rezutlatul functiei blink(durata,stare)  init_sys();                 // apelam si executam astfel functia init_sys() o singura data    /* Urmatoarea bucla este o bucla infinita care se va executa la nesfarsit      (pana la reset de fapt  )  */  while (1) {    durata_blink = citeste_ADC(); // citim valoarea din ADC si o stocam in variabila durata_blink        /* apelam (executam) functia blink cu parametrii durata_blink        si stare = 1, adica functia lucreaza.    */    blink(durata_blink, 1);  }}

Si ca sa va dau ce sa faceti, am modificat programul dar functionalitatea ramane aceasi. Este doar ca sa va arat ca lucrurile se pot face si altfel.

Ambele versiuni fac exact acelasi lucru.

void init_sys() {  // dezactivam comparator  CMCON = 7;    // dezactivam intreruperi  INTCON = 0;    ANSEL = 0;  // toti pinii care au functie analogica sunt digitali    // ne ocupam de pinul 7 unde punem LED-ul si care corespunde GP0  // GP0 este bitul 0 din GPIO  TRISIO0_bit = 0; // pinul este iesire  GP0_bit = 0; // pinul GP0 adica pinul 7 fizic porneste in starea OFF  // ne ocupam de pinul 6 unde punem potentiometrul si care corespunde lui ANS1  // ANS1 este bitul 1 din ANSEL  TRISIO1_bit = 1; // pinul este intrare  ANS1_bit = 1;    // pinul este pin analogic    ADC_Init();      // initializare ADC}void blink( unsigned int durata, int stare) {  int i; // variabila iterator din ciclul FOR.  /* daca stare este diferita de 0 fa ce ai de facut.     Daca stare este 0 atunci sari peste blocul dintre acoladele lui 'if' si     dute la sfarsitul functiei unde desi nu se vede, avem un 'return' implicit*/  if (stare != 0) {    GP0_bit = 1; // aprindem LED-ul    // urmeaza delay-ul cat timp LED-ul este ON    for (i = 0; i <= durata; i = i + 1){      delay_ms(10);    }    GP0_bit = 0; // stingem LED-ul    // urmeaza delay-ul cat timp LED-ul este OFF    for (i = 0; i <= durata; i = i + 1){      delay_ms(10);    }  }}unsigned int citeste_ADC() {  /* Combinam in urmatoare linie atat declararea variabilei locale rezultat cu tipul unsigned int,     cat si ii dam valoare cu ce iese din functia ADC_Read(1)  */  unsigned int rezultat = ADC_Read(1);  return rezultat;  // returnam valoarea variabilei locale rezultat.}void main() {  init_sys();                 // apelam si executam astfel functia init_sys() o singura data    /* Urmatoarea bucla este o bucla infinita care se va executa la nesfarsit      (pana la reset de fapt  )  */  while (1) {    // apelam (executam) functia blink cu parametrii rezultatul citeste_ADC()    // observati cum dau ca parametru direct functia citeste_ADC()     blink(citeste_ADC(), 1);  }}

Sau o alta varianta similara, programul modificat conform sugestiei colegului @djvas, folosind functia vdelay_ms(). Plus alte cateva aspecte, inlocuind cu lucruri echivalente.

Din nou, programul face acelasi lucru ca si celelalte doua versiuni de mai sus.

void init_sys() {  // dezactivam comparator  CMCON = 0b00000111;    // dezactivam intreruperi  GIE_bit = 0;    ANSEL = 0x00;  // toti pinii care au functie analogica sunt digitali    // ne ocupam de pinul 7 unde punem LED-ul si care corespunde GP0  // GP0 este bitul 0 din GPIO  TRISIO0_bit = 0; // pinul este iesire  GP0_bit = 0; // pinul GP0 adica pinul 7 fizic porneste in starea OFF  // ne ocupam de pinul 6 unde punem potentiometrul si care corespunde lui ANS1  // ANS1 este bitul 1 din ANSEL  TRISIO1_bit = 1; // pinul este intrare  ANS1_bit = 1;    // pinul este pin analogic    ADC_Init();      // initializare ADC}void blink( unsigned int durata, int stare) {  /* daca stare este diferita de 0 fa ce ai de facut.     Daca stare este 0 atunci sari peste blocul dintre acoladele lui 'if' si     dute la sfarsitul functiei unde desi nu se vede, avem un 'return' implicit*/  if (stare != 0) {        GP0_bit = 1; // aprindem LED-ul    // urmeaza delay-ul cat timp LED-ul este ON    vdelay_ms(10 * durata);    GP0_bit = 0; // stingem LED-ul    // urmeaza delay-ul cat timp LED-ul este OFF    vdelay_ms(10 * durata);  }}unsigned int citeste_ADC() {  return ADC_Read(1);  // returnam valoarea intoarsa de functia ADC_Read(1).}void main() {  init_sys();                 // apelam si executam astfel functia init_sys() o singura data    /* Urmatoarea bucla este o bucla infinita care se va executa la nesfarsit      (pana la reset de fapt  )  */  while (1) {    // apelam (executam) functia blink cu parametrii rezultatul citeste_ADC()    // observati cum dau ca parametru direct functia citeste_ADC()     blink(citeste_ADC(), 1);  }}

Si inca o varianta echivalenta, doar ca sa vedeti cum se mai poate face. Am renuntat complet la functia citeste_ADC() :) dar functionalitatea ramane.

void init_sys() {  // dezactivam comparator  CMCON = 0x07;    // dezactivam intreruperi  GIE_bit = 0;    ANSEL = 0x00;  // toti pinii care au functie analogica sunt digitali    // ne ocupam de pinul 7 unde punem LED-ul si care corespunde GP0  // GP0 este bitul 0 din GPIO  TRISIO0_bit = 0; // pinul este iesire  GP0_bit = 0; // pinul GP0 adica pinul 7 fizic porneste in starea OFF  // ne ocupam de pinul 6 unde punem potentiometrul si care corespunde lui ANS1  // ANS1 este bitul 1 din ANSEL  TRISIO1_bit = 1; // pinul este intrare  ANS1_bit = 1;    // pinul este pin analogic    ADC_Init();      // initializare ADC}void blink( unsigned int durata, int stare) {  /* daca stare este diferita de 0 fa ce ai de facut.     Daca stare este 0 atunci sari peste blocul dintre acoladele lui 'if' si     dute la sfarsitul functiei unde desi nu se vede, avem un 'return' implicit*/  if (stare != 0) {        GP0_bit = 1; // aprindem LED-ul    // urmeaza delay-ul cat timp LED-ul este ON    vdelay_ms(10 * durata);    GP0_bit = 0; // stingem LED-ul    // urmeaza delay-ul cat timp LED-ul este OFF    vdelay_ms(10 * durata);  }}void main() {  init_sys();                 // apelam si executam astfel functia init_sys() o singura data    /* Urmatoarea bucla este o bucla infinita care se va executa la nesfarsit      (pana la reset de fapt  )  */  while (1) {    // apelam (executam) functia blink cu parametrii rezultatul functiei ADC_read(1)    // observati cum dau ca parametru direct functia ADC_read(1)()    // se poate vedea astfel ca functia ADC_read() se putea folosi direct dar cum spus    // am preferat sa fac o functie suplimentara (in ex anterioare) doar pentru exemplificare a unei functii care intoarce ceva    blink(ADC_read(1), 1);  }}

Ultima varianta scrisa fara comentarii ca sa va dati seama cat de putin cod este scris:

void init_sys() {  CMCON = 0x07;  GIE_bit = 0;   ANSEL = 0x00;     TRISIO0_bit = 0;   GP0_bit = 0;   TRISIO1_bit = 1;   ANS1_bit = 1;        ADC_Init();      }void blink( unsigned int durata, int stare) {  if (stare != 0) {     GP0_bit = 1;     vdelay_ms(10 * durata);    GP0_bit = 0;     vdelay_ms(10 * durata);  }}void main() {  init_sys();                   while (1) {    blink(ADC_read(1), 1);  }}

Daca sunt intrebari acum este momentul sa le puneti, inainte de a trece la un alt program. Intrebarile pot fi si generale adica gen: "nu inteleg partea cu ADC-ul" etc si nu doar punctuale legate de liniile de cod.

Editat de mars01
Link spre comentariu

Ca sa fac un rezumat rapid cu privire la functii:

 

- functiile sunt grupari .... functionale, a unor linii de cod, intre doua acolade. Ele au un nume gasit inainte de aceste acolade (una de deschidere si una de inchidere).

- o functie are un numar de parametri si un singur rezultat

- in corpul functiei, variabila care contine rezultatul este prefatata cu cuvantul cheie 'RETURN'. Odata intalnit cuvantul cheie 'RETURN' se iese din functie. Indiferent daca dupa cuvantul cheie este o variabila (adica se intoarce acea variabila ca si rezultat al functiei) sau nu este urmat de altceva (adica functia nu intoarce un rezultat)

- daca se ajunge la sfarsitul liniilor de cod care formeaza corpul functiei, si se intalneste acolada de inchidere a functiei, chiar daca nu s-a intalnit cuvantul cheie 'RETURN' se iese din functie si se continua executia codului de dupa functie.

- functiile au o declaratie si o definire. In declaratie, la fel ca si la variabile, se declara tipurile parametrilor si al rezultatului intors de functie. In definitie se scrie efectiv codul functiei.

- variabilele declarate in interiorul unei functii (intre acoladele functiei) sunt numite variabile locale si nu pot fi utilizate in restul programului. Variabilele locale exista doar pe timpul executiei functiei, in urma functiei ramane doar rezultatul intors de functie (cu ajutorul cuvantului cheie 'RETURN'). Sau modificari facute in timpul executiei functiei asupra variabilelor globale, registri (sau variabile tip pointer). Aceasta poate crea probleme de memorie insuficienta RAM daca programatorul nu a calculat 'worst case scenario' in privinta ocuparii memoriei RAM si este aproape de limita superioara a memoriei RAM disponibile.

- o functie poate lua ca parametru o alta functie (sau mai multe) si in unele cazuri poate lua ca parametru pe ea insasi (functii recursive)

- microcontrolerele au o limitare hardware (in functie de familie) la cat de multe functii pot fi apelate una in alta in alta in alta si tot asa. Controlerele 8 biti generatie mai veche au limita la 8 asemenea functii iar controlerelepe 8 biti de generatii noi ajung si la 31 de nivele de imbricare (o functie apelata intr-o functie apelata intr-o functie si tot asa de 31 de ori).

 

- functiile ajuta la partitionarea codului in blocuri usor de inteles fara a fi nevoie de a sti efectiv cum se face acea operatie. Ex, functia 'print'. O folosim dar nu ne intereseaza sa o cream de fiecare data cand avem nevoie de aceasta functie. Ne este suficient sa stim ce face. O scriem odata si gata. Daca e cazul, in unele situatii (asa cum este situatia functiei print) functiile elementare fiind furnizate de producatorul compilatorului.

- functiile pot fi grupate in colectii de functii, asa numitele "libraries". Aceste "libraries" sunt incluse (adica sunt "aduse" in programul nostru) cu ajutorul directivei #include <nume_librarie>

- functiile admit ca parametri (dar si returneaza) tot felul de variabile: cele cu tipuri de baza (char, int, long, float) cat si tip string, enum, struct sau variabile de tip pointer. Ce sunt acestea de la urma ... alta data.

Editat de mars01
Link spre comentariu

 

- functiile au o declaratie si o definire. In declaoratie, la fel ca si la variabile, se declara tipurile parametrilor si al rezultatului intors de functie. In definitie se scrie efectiv codul functiei.

 

sunt functii definite mai ciudat.

 

exemplu (tema pentru ... acasa):  int functie(int,...);

Editat de Vezi Muti
Link spre comentariu

Creează un cont sau autentifică-te pentru a adăuga comentariu

Trebuie să fi un membru pentru a putea lăsa un comentariu.

Creează un cont

Înregistrează-te pentru un nou cont în comunitatea nostră. Este simplu!

Înregistrează un nou cont

Autentificare

Ai deja un cont? Autentifică-te aici.

Autentifică-te acum
×
×
  • Creează nouă...

Informații Importante

Am plasat cookie-uri pe dispozitivul tău pentru a îmbunătății navigarea pe acest site. Poți modifica setările cookie, altfel considerăm că ești de acord să continui.Termeni de Utilizare si Ghidări