Sari la conținut
ELFORUM - Forumul electronistilor

Invatat programare de la zero


riciu

Postări Recomandate

Cum spuneam, ce scriu eu nu este o expunere academica si nici nu am pretentia ca "tin cursuri". Fac programare la nivel de hobby.

Garantat daca ar fi sa primesc review-uri de la profesionisti, mi-as lua "papara". Sunt si vor fi greseli, cunoasterea mea este limitata. Dar ... incerc.

 

Imi este dificil sa gasesc termeni care sa fie cat de cat familiari si celor care nu au prea multe cunostiinte in domeniu dar care isi doresc sa invete. Asa a aparut si "perifericul".

Cand am pomenit de periferic, am avut grija sa mentionez in paranteze in cateva locuri ca ma refer la modulele din controler. Cum ar fi porturile I/O, timerele, convertoarele ADC, etc. practic tot ce nu este ALU, oscilator, memoria FLASH de program si memoria RAM de date.

Editat de mars01
Link spre comentariu

 

Posted Image

 

Asadar, daca facem bitul 7 din registrul CVRCON zero, adica bitul CVREN il incarcam cu valoarea zero, vom dezactiva modulul (perifericul) care genereaza tensiunea de referinta ajustabila pentru comparator.

Fiindca registrul se ocupa numai de acest periferic, putem fara grija sa il initializam pe tot cu 0 logic (adica ii facem "clear" la tot).

CVRCON = 0b00000000;

Atentie: pentru dezactivare conteaza numai primul 0 de dupa '0b' adica bitul 7. Restul pot fi ce vreti voi. In acest caz este asa dar in alte cazuri nu, pentru ca spuneam ca sunt unii registri care trebuie tratati cu grija si modificat doar bitul care vrem sa il modificam. Nu este cazul aici asa ca mergem mai departe.

as vrea sa remarc ceva

la fiecare registru avem o valoare la reset(alimentare)

deasupra la CVREN este scris R/W-0, adica bitul respectiv se poate scrie sau citi si are valoarea 0 la reset, deci Comparatorul la reset este dezactivat

U-0 adica bit nefolosit si are valoare 0

o sa mai gasim R/W-x adica bitul respectiv se poate scrie sau citi dar are o valoare necunoscuta la alimentare sau reset daca utilizatorul nu a modificat valoarea

Link spre comentariu

Comparatorul la reset este dezactivat

 

Foarte adevarat dar "l-am dezactivat din nou", in scop didactic. Am si mentionat pe undeva acest lucru :)

Multumesc pentru completari.

La mai multe !!!

Editat de mars01
Link spre comentariu

Rezumat.

 

Am ales pinul 2 al controlerului PIC16F877A pentru a controla un LED si pentru a realiza astfel un licurici.

Pentru realizarea proiectului am ales sa ne folosim de mediul de dezvoltare (IDE + compilator) mikroC for PIC produs de mikroElectronica.

Am efectuat setarea registrului CONFIGURATION WORDS direct din interfata grafica a mikroC for PIC odata cu crearea proiectului.

 

Ce am facut pana acum a fost sa ne asiguram ca pinul 2 este setat ca pin digital (digital I/O) si mai mult, este setat ca IESIRE.

 

Pentru a seta pinul 2 (care are eticheta RA0/AN0 si deci face parte din portul A) ca pin digital am facut urmatoarele:

- am dezactivat comparatorul (un modul) folosindu-ne de registrul asociat CMCON

- am dezactivat convertorul ADC (un modul) cu ajutorul registrului asociat ADCON0

- am dezactivat modulul de generare tensiune de referinta variabila (16 trepte) din registrul asociat CVRCON

 

Pentru a seta pinul 2 (care in acest moment este un pin digital I/O - digital INPUT/OUTPUT) ca si IESIRE (in mod default este setat ca INTRARE) am folosit registrul asociat TRISA.

 

Am invatat ca regula mnemotehnica faptul ca vom face 1 logic bitul de control din TRISx al unui pin fizic atunci cand vrem sa il facem INTRARE si il vom face 0 logic cand vrem ca pinul sa fie IESIRE. (unde x este denumirea portului in care se gaseste grupat pinul pe care dorim sa il configuram). 

 

Cand spun pin, ma refer la un pin fizic al controlerului.

 

Fiecare pin (in afara de cei destinati oscilator sau alimentare) face parte dintr-o forma de organizare numita porturi. Acestor porturi li se da ca denumire o litera, incepand cu A. 

Fiecare port poate avea pana la maxim 8 pini inclusi, dar functie de pinout-ul controlerului pot fi mai putini pini inclusi intr-un port.

Fiecare port are un numar de asazisi REGISTRI care il controleaza, il configureaza. La modul minimal, fiecare port are cel putin un registru denumit PORTx si un registru numit TRISx (unde x este litera portului). Ex: PORTA, PORTB, TRISA, TRISB etc.

 

Registrul PORTx nu l-am tratat inca dar vom face acest lucru imediat.

 

Un microcontroler este format dintr-un soi de procesor (unitatea ALU - arithmetric logic unit) si un numar mai mare sau mai mic de module (le-am zis pana acum periferice).

Pe langa acestea, fiecare controler mai contine un oscilator (cu mai multe prescalere),  memorie program FLASH si memorie date RAM.

 

Modulele uzuale pot fi (dar nu se restrange numai la lista urmatoare): port I/O, convertor ADC, referinta variabila de tensiune, convertor DAC, timere (vom discuta despre ele mai tarziu), port paralel, comparator, module PWM/CCP/ECCP, module control LCD, touch sense etc

 

Fiecare modul (componenta interna a microcontrolerului) are asociat un numar de REGISTRI de control, configurare si, uneori, stocare date.

 

Toti acesti registri asociati modulelor ajuta in a configura modulele (care sunt facute sa fie cat mai flexibile si adaptabile nevoilor utilizatorilor).

Adesea modulele produc ele insele informatie sau preiau informatie din lumea exterioara (pentru aceasta li se asociaza unii dintre pinii fizici ai controlerului) si pentru aceasta, evident, au registri speciali unde se stocheaza aceasta informatie sau altfel spus, aceste date.

 

Un REGISTRU, cred ca am inteles pana acum, este un loc special din memorie unde stocam informatii esentiale cu privire la configurarea unui modul sau pentru a stoca informatii.

Registrii au intotdeuna un nume si sunt intotdeauna descrisi in detaliu, fiecare bit in parte, in datasheet (foaia tehnica).

 

Registrii la controlerele uzuale (cele pe care le folosim noi sunt cele pe 8bit) sunt formati din 8 biti organizati de la bitul cu numarul cel mai mare in stanga catre bitul  cu numarul cel mai mic in dreapta. Aceasta inseamna ca modul de organizare este "little endian - ala mic la urma :)" si deoarece registrii au 8 bit atunci bitul din stanga este bitul 7 iar bitul cel mai din dreapta este bitul 0.

Nu este obligatoriu ca toti bitii sa fie folositi. Unii biti sunt marcati de producator in datasheet ca "nefolositi". Acei biti pot fi ignorati si ii puteti face 1 sau 0 dupa cum va vine pentru ca nu sunt luati in seama de controler.

 

Fiecare bit poate lua valoare 0 sau 1. Functie de ce valoare are, va genera un efect in acel modul pe care il controleaza atunci cand facem noi modificarea. Sau va reprezenta o informatie pe care o putem folosi atunci cand acel bit va fi "scris" de o informatie venita ori din interiorul controlerului ori din lumea exterioara, printr-un pin fizic.

 

FIECARE BIT din REGISTRU are si el un NUME. Astfel ca poate fi usor identificat. Uneori compilatoarele permit accesarea sa directa cu un nume gen ADEN_bit. 

Fiecare bit din registru poate fi scris individual scriind pe pozitia sa un 1 sau 0 dupa caz. Dar pentru aceasta trebuie scris tot registrul, asta inseamna ca atunci cand vreti sa scrieti bitul3 cu sa zicem un 1, atunci trebuie sa ii adaugati si pe "fratiorii" sai (la dreapta sa si la stanga sa) astfel ca trebuie scris simultan toti cei 8 biti.

Exista o tehnica, care implica manipularea bitilor (operatii pe biti), care permite modificarea unui bit fara a ii deranja pe restul de 7biti (un registru are 8 biti, nu?)

Ambele modalitati sunt folosite.

 

Uneori, atunci cand registrii sunt folositi pentru a stoca informatie venita de la un modul (exemplu un convertor ADC pe 10 biti - adica informatia pe care o ofera are 10biti), nu este suficient un singur registru de 8 biti pentru a stoca acea informatie. In acel caz se vor asocia doi registri (de 8 bit) pentru a stoca informatia (daca informatia are mai putin de 16biti, evident, pentru ca daca are mai mult de 16bit atunci este nevoie de 3 registri samd. Dar mai mult de 2 registri nu o sa gasiti la controlerele pe 8bit).

 

In cazul in care sunt doi registri asociati pentru a stoca datele (care evident au mai mult de 8bit) atunci acei registri vor avea acelasi nume cu un sufix H pentru primul si un sufix L pentru ultimul.

H vine de la HIGH si L vine de la LOW. Practic registrul cu H il plasati in stanga si va contine bitii cu numar de ordine mai mare si registrul cu L se plaseaza in dreapta si va contine bitii cu numar de ordine mai mic.

 

Un exemplu pentru registrii unui ADC pe 10bit (right justified - aliniati in dreapta):

 

- registrul cu H la urma ar arata asa: uuuuuu bit9 bit8

unde u insemana nefolosit (unused)

- registrul cu L la urma ar arata asa: bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0

 

Dupa ce datele ajung in acesti doi registri (care au o denumire specifica controlerului folosit si pe care ii vom studia cand vom ajunge la convertorul ADC), le vom citi continutul in program stocand ce gasim in registri, in doua variabile, una pentru fiecare registru si vom reconstitui informatia intr-o variabila finala, ajutandu-ne de operatiile pe biti (care le vom studia mai tarziu).

Editat de mars01
Link spre comentariu

5. Registrul PORTA

 

Am ajuns in punctul in care avem un pin 2 configurat ca IESIRE digitala. Deci putem scrie pe acel pin o valoare de 0 logic (am sa ii spun de acum incolo LOW) sau de 1 logic (am sa ii spun de acum incolo HIGH).

 

Bun, dar cum facem aceasta? Folosim un pix si scriem High sau Low pe pin? :)

 

Nu. Ne folosim de registrul PORTA (sau pentru alt port - si l-am scris cu litere mici deci ma refer la gruparea de pini - schimbam A de la final cu litera portului care ne intereseaza).

 

Posted Image

 

Scriem valoarea care o vrem pe pozitia bitului care corespunde pinului caruia ii schimbam starea.

Pinul 2 al controlerului corespunde lui RA0.

Asa ca daca scriem in PORTA pe pozitia lui RA0 (este de fapt bitul0 al registrului PORTA) un LOW, pinul va fi fizic ~0V.

PORTA = 0b00000000;

Daca scriem in PORTA pe pozitia lui RA0 un HIGH, pinul va fi fizic ~5V (sau 3.3V functie de tensiunea de alimentare a controlerului).

PORTA = 0b00000001;

NOTA: Pozitiile in registrii PORTx si TRISx sunt corespondente. Cu alte cuvinte bitul 3 din TRISA cuprinde informatie cu privire daca pinul atasat bitului 3 este INTRARE (daca este 1) sau IESIRE (daca este 0) iar bitul 3 din PORTA contine informatie cu privire la starea pinului asociat acelui bit, HIGH (daca bitul este 1) sau LOW (daca bitul este 0).

 

Si cam asta este.

 

Trecem la program.

Editat de mars01
Link spre comentariu

Programul "licurici" este urmatorul:

void main() {     CMCON = 0b00000111;  //dezactivam comparator     ADCON0 = 0;          //dezactivam ADC     CVRCON = 0;          //dezactivam referinta variabila de tensiune          TRISA = 0b00000000;  //facem IESIRE din pinul2 adica RA0     PORTA = 0b00000000;  //pornim cu LED-ul atasat pe pinul2 stins. Adica RA0 = LOW;          /*urmeaza bucla infinita     Cat timp 1 este adevarat (si 1 este adevarat tot timpul) executa ce este intre acolade     */     while(1) {        PORTA = 0b00000000; //facem RA0 = 0 deci pinul2 este LOW        delay_ms(500);      //controlerul sta la aceasta linie si nu face nimic timp de 500 milisecunde        PORTA = 0b00000001; //facem RA0 = 1 deci pinul2 este HIGH        delay_ms(500);      //controlerul sta la aceasta linie si nu face nimic timp de 500 milisecunde     }}

Asa arata in interfata mikroC:

 

Posted Image

 

Si asa arata circuitul de simulare realizat in Proteus ISIS 7.10

 

Posted Image

 

Prin apasarea butonului "Build" in interfata grafica a mikroC se compileaza programul si in caz ca nu sunt erori va fi generat fisierul .HEX care ulterior este scris cu un programator gen PicKit in microcontrolerul nostru. Vezi prima poza.

 

Atasez simularea in Proteus ISIS 7.10 cat si proiectul mikroC.

 

********************************************************************************************************************************

 

LE: 

 

Pentru a vedea cum se lucreaza cu registrii si faptul ca se poate scrie simultan starea mai multor pini in registrul PORTx asociat, am modificat programul astfel incat:

- cand LED-ul de pe pinul 2 (adica RA0) este ON atunci LED-urile de pe pinii 33 (RB0) si 35 (RB2) sunt OFF.

- cand LED-ul de pe pinul 2 (RA0) este OFF, LED-urile pe care le-am conectat pe pinii 33(RB0) si 35(RB2) sunt ON.

 

Posted Image

void main() {     CMCON = 0b00000111;  //dezactivam comparator     ADCON0 = 0;          //dezactivam ADC     CVRCON = 0;          //dezactivam referinta variabila de tensiune          TRISA = 0b00000000;  //facem IESIRE din pinul2 adica RA0     PORTA = 0b00000000;  //pornim cu LED-ul atasat pe pinul2 stins. Adica RA0 = LOW;     /*     Pentru a face mai vizibile pozitiile de interes,     am facut in portul B toti pinii INTRARI mai putin pinii asociati     bitilor 0 si 2 din portul B, adica RB0 si RB2 */     TRISB = 0b11111010;     //pornim cu ei (si LED-urile conectate) in stare LOW     PORTB = 0b00000000;          /*urmeaza bucla infinita     Cat timp 1 este adevarat (si 1 este adevarat tot timpul) executa ce este intre acolade     */     while(1) {        PORTA = 0b00000000; //facem RA0 = 0 deci pinul2 este LOW        PORTB = 0b00000101; //facem RB0 = 1 si RB2 = 1 deci pinii asociati sunt HIGH        delay_ms(500);      //controlerul sta la aceasta linie si nu face nimic timp de 500 milisecunde                PORTA = 0b00000001; //facem RA0 = 1 deci pinul2 este HIGH        PORTB = 0b00000000; //facem RB0 = 0 si RB2 = 0, deci pinii asociati sunt LOW        delay_ms(500);      //controlerul sta la aceasta linie si nu face nimic timp de 500 milisecunde     }}

O sa discutam ulterior despre sintaxa dar deocamdata cautati pe internet  si cititi despre:

- decisions: if - else

- loops: while(), do while(), for(;;)

- functions

licurici.zip

licurici_proiect_mikroC.zip

Editat de mars01
Link spre comentariu

Ca sa raspund trebuie sa explic bucla while(expresie).

Vroiam sa iau o pauza ca sa va las timp sa va obisnuiti cu conceptele deja prezentate. Eventual sa mai aduag exemple cu usoare variatii pentru a face mai clar programul de mai sus.

 

Dar hai sa explic ce este cu while (expresie).

 

Cum spuneam, in C (ca si in alte limbaje) programul se executa linie cu linie (cu exceptia intreruperilor dar aici este un subiect mai avansat, pentru mai tarziu, si a instructiunii GOTO dar la care utilizarea este descurajata). Deocamdata simplificam si consideram ca programul (adica tot ce este intre acoladele de dupa void main() ) se executa linie cu linie.

NOTA: Faza cu acoladele este mai greu de explicat. E atat de simpla ca e greu de simplificat. O sa vedem din exemple sau poate vine cineva cu o explicatie buna. In orice caz orice acolada deschisa trebuie inchisa. La fel si cu orice paranteza.

 

Cand programul intalneste o bucla de tip while( expresie), se va evalua expresia dintre paranteze.

Daca expresia este adevarata atunci se va executa ce este intre acoladele sale (instructiunea while (expresie) are asociat un set de acolade) si imediat dupa ce se inchide acolada se revine din nou la inceput si se reevalueaza expresia.

Si tot asa pana cand ...

daca expresia este falsa atunci se sare peste liniile de program cuprinse intre acoladele sale si se merge mai departe.

 

Cu alte cuvinte cand dai de instructiunea:

while(1) { ...}  

unde ... sunt linii de program

 

atunci se va executa ce este intre acolade pana 1 devine egal cu zero adica pana expresia dintre paranteze devine falsa.

 

E clar ca 1 nu o sa fie niciodata egal cu zero, asadar ce este dupa while (1) intre acolade se executa la infinit.

Editat de mars01
Link spre comentariu
while(1) {...{

se poate scrie si 

while (1 == 1) {...}

Remarcati dublu egal in expresie.

Intotdeuana cand compari doua cantitati (variabile etc) intr-o expresie, in C nu se scrie:

daca a=3 atunci ...

 

Se scrie:

daca a==3 atunci ...

 

Multumesc Liviu pentru explicatie.

Editat de mars01
Link spre comentariu

Felicitari mars01.while ... se poate scrie si while(1+1==2) :)Eu zic sa te opresti putin. Sa astepti comentarii si sa vezi daca s-a inteles pina aici.Poate si sa pui ceva intebari. De exemplu: De ce nu ai folosit RA4 ?

Link spre comentariu

Pin RA4 is multiplexed with the Timer0 module clock input to become the RA4/T0CKI pin. The RA4/T0CKI pin is a Schmitt Trigger input and an open-drain output. All other PORTA pins have TTL input levels and full CMOS output drivers.

 

Cum se poate citi in datasheet la pagina 41 sectiunea 4-1, RA4 poate fi configurat doar ca iesire open-drain. Ceea ce presupune ca mai trebuie pus un rezistor de la pin la VDD. Dar stiai asta "you tricky little ..."  :)

 

LE: O chestie mai spun. 16F877A are nevoie ca in mod clar sa i se puna pinul de reset (MCLR, adica pinul 1) in nivel HIGH (legat cu un resistor de pull up la bara de VDD) pentru ca sa functioneze. Altfel sta in reset.

Alte controlere au un resistor de pull up intern care se poate activa cu o anume setare intr-un ... REGISTRU ...

 

Bun. Ne mai auzim deseara (vorba vine). Sa mai imi fac si jobul :)

Editat de mars01
Link spre comentariu

pentru a invata programare, in sensul de a intelege, trebui sa cunosti "limbajul", termenii cu care se lucreaza, si nu in ultimul rand arhitectura masinii ce trebuie programate.

 

legat de termenul de "registru", nu am vazut sa fie facut referire clara ca pot fi registri de intrare si iesire, registri doar de intrare, registri doar de iesire (care se pot asemana cu intrerupatoarele si schimba o actiune). Registri pot fi mapati pe memorie (pe ROM - read only memory, RAM) sau mapeaza direct stari de control sau semnale externe.

 

nu am vazut intrebari, de la cei care invata, comentarii la pagini intregi de text. Ar fi util ca @mars01 sa primeasca un raspuns de la cei interesati, daca li se pare mai usor de inteles, sau daca termenii sau expresiile le sunt necunoscute, sa se poata "calibra", sau sa puna accent mai mult sau mai putin pe anumite aspecte.

 

o intrebare de la mine: ce e aia periferic?

  eu unul nu am pus intrebari pt ca astept sa termine ( si cred ca a terminat) cu aprinsul led-ului si dupa ce voi citii tot si voi vedea ce am inteles si ce nu, voi pune si intrebari. Cred ca a iesit un topic foarte frumos si legat, fara interventii in afara topicului. Se vede clar diferenta intre modul de a explica al lui mars01 si al documentatiilor scrise probabil de profesionisti pt care orice termen tehnic din domeniu e o banalitate. Dupa prima lectura inca sint in ceata, insa cu bucurie recunosc ca nu asa cum eram la inceput. Si sint sigur ca dupa ce voi mai citi topicul de doua ori, bineinteles cu calm si cu pixul in mana, voi putea face programul singur. Mi-am dat seama ca e totusi un domeniu greu, cel putin pana il inveti. Din cate am intles, nu toate microcontrolerele sint la fel, si inainte de a face unproiect trebuie sa studiezi bine microcontrolerul respectiv. Deci eu , voi incerca sa ma axez in toate proiectele pe unul singur pana il invat bine. Imi aduc aminte ca cineva spunea odata ca cel mai bine se invata cu pasi mici si cu exemple practice. Recunosc ca exemplul folosit in acest tutorial m-a ajutat mult sa inteleg teoria. Cand m-am apucat sa invat C-ul ( pt PC, dar eu nu stiam asta) din tutoriale, dupa fiecare tutorial imi dadeam singur exercitii. Dupa cateva instructiuni invatate, deja stiam sa-mi depistez greselile si era o senzatie nemaipomenita. Insa vedeam eu ca parca nu era limbajul pt microcontrolere. In concluzie, eu cred ca cel mai bine se invata putin cate putin si avansat pe baza proiectelor practice pentru ca doar din teorie se face un talmes balmes in creier si la sfarsit constati ca nu ai retinut si invatat nimic. de exemplu, faza cu intrerupatoarele (biti) o vazusem cu o zi inainte intr-o documentatie, din care nu intelesesem nimic. Era capitolul INTRERUPERI. De la mars am prins imediat asemanrea intre intrarupator si bit.

 

 

core, tu ai spus la un moment dat ..."sau mapeaza direct stari de control sau semnale externe."  Nu am intles ce ai vrut sa spui aici. A mapa ce inseamna?

Link spre comentariu

deci... while(1) este o "conventie"

Nu tocmai. Mumai "1 == adevarat" e o conventie.while(1) este un mod de a genera o bucla infinita. La fel de bine pot fi create si folosind for (la care avem grija sa nu indeplinim conditia de iesire) sau goto.Exemplu cu for: 
 for(;;) {}
cu goto:
eticheta_start:... //programgoto eticheta_start 
Nu sunt sigur ca sintaxa pentru goto e corecta (nu l-am folosit niciodata). Editat de Liviu M
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