Sari la conținut
ELFORUM - Forumul electronistilor

Invatat programare de la zero


Postări Recomandate

Nu stiam despre acest gen de functie si acest gen de sintaxa.

Dar nu este surprinzator.

O functie o definesti si stii exact ce vrei sa faca. Daca vrei sa oferi o multitudine de valori, variabile ca parametri pentru functie, atunci folosesti un buffer, o arie care poate fi multidimensionala.

Poti sa dai ca parametru unei functii un pointer catre o zona de memorie. Si pe urma tot ce ai de facut este sa incrementezi acel pointer pentru a accesa oricate variabile. Pentru tipuri diferite de variabile se pot da ca parametri pointeri corespunzatori. Etc.

Genul de functie la care s-a facut referire merita mentionat doar ca o curiozitate dar nu mi se pare ca merita sa iti bati capul cu ea. Tot ce poti face cu acest tip de functii poti sa faci si cu tehnici de programare ortodoxe. 

 

Stiu ca am tot mentionat pointerii dar deocadata nu vreau sa incurc pe cei care sunt la inceput cu acest gen de informatii. Daca incep sa vorbesc despre pointeri, pointeri la pointeri etc o sa fie un oftat colectiv (pentru cei care urmaresc topicul).

Pana la urma nu am amintit nimic nici despre arii si stringuri de caractere ... iar acestea vor trebui discutate chiar si numai pentru tiparirea unor texte pe un LCD.

Editat de mars01
Link spre comentariu

Eu n-am folosit niciodata functii de-astea (in sensul ca n-am scris nici o astfel de functie), da' eu nu-s decat programator de duminica (sau de seara).

Stiu de existenta lor pentru ca erau mentionate in cartea de C pe care am citit-o cu ani in urma.

Editat de Liviu M
Link spre comentariu

1328 pagini. :O Spor!

 

Eu am K&R, ca pe asta mi-a recomandat-o cineva. Mi se pare o carte tare buna (informatie condensata, fara prea multe povesti), dar nu-s convins ca e cea mai buna pentru inceput (inainte de asta mai citisem cate ceva, nu eram chiar fresh).

Link spre comentariu
Vizitator red_dog4ever

Am inceput sa ma plictisesc de arduino, asa ca vreau sa ma apuc de programarea pic-urilor.

Cu ce procesor imi recomandati sa incep? Sunt multe pareri pro si contra pe google.

 

As prefera sa nu coste foarte mult uP .

Link spre comentariu
  • 4 săptămâni mai târziu...

Nu am mai scris aici in ultima vreme din lipsa de timp si partial ... lipsa de chef. :)

 

Astazi vreau sa scriu putin despre manipularea porturilor la nivel binar. O sa vorbesc despre formatul binar, mascare (masking), shiftare biti la stanga sau la dreapta (left shifting, right shifting) si operatiile logice la nivel de biti.

 

Extrem de plictisitor dar extrem de util cand se lucreaza cu registrii, porturile la nivel binar.

 

1. Formatul binar.

Dupa cum stim, procesoarele (da, si microcontrolerele au inclus un procesor, asa numitul ALU: arithmetric logical unit) nu stiu de limbaje de nivel inalt. Ele nu stiu nici de limbaje de nivel jos, cum ar fi limbajul de asamblare. Ele stiu sa lucreze cu nivele de tensiune. Avem tensiune electrica sau nu avem tensiune electrica undeva anume.

 

Cat anume este aceasta tensiune depinde de procesor / controler. La cele de care discutam noi (PIC pe 8 biti) printre tensiunile posibile sunt cele de 5V sau 3.3V adica Vdd, tensiunea de alimentare a controlerului (de fapt sunt controlere care incep sa functioneze de la tensiuni mult mai mici dar pentru simplificare am mentionat ce este mai sus).

 

Cand tensiunea este prezenta undeva (mai exact este mai mare de cca 2V, vorbim de nivele TTL) se cheama ca avem 1 logic. Cand tensiunea lipseste (mai exact este mai mica de 0.6V) se cheama ca avem un 0 logic. Exista si nivelele CMOS si low-CMOS.

 

Controlerele inteleg aceste nivele binare (1 si 0) in termeni de: adrese de memorie, date care se stocheaza la acele adrese de memorie si op code-uri (adica OPeration Code). Nici eu nu stiu prea multe cum functioneaza lucrurile aici dar la modul general procesorul "inhata" date de la o adresa din memoria RAM, le trece prin registri si apoi aplica acele op-code asupra acestor date. Aceste op-code pot fi sumari, substractari, multiplicari, divizari, shiftari, rotiri etc.

 

Ma voi referi mai departe doar la controlerele pe 8 biti, cum am facut si pana acum.

 

La acest tip de controlere, putem spune ca informatia este stocata in "unitati" de 8 biti. La PIC-uri arhitectura specifica poate organiza informatia/adrese pe 12/14 bit dar aceasta este o chestie de bucatarie interna.

 

O asemenea unitate de 8 biti este numita byte (sau octet).

Modul in care bitii sunt interpretati in biti este "little endian".

 

Ce inseamna aceasta?

Inseamna ca avem o conventie care spune ca bitul cel mai din stanga este bitul cu valoarea/eticheta cea mai mare iar bitul cel mai din dreapta este bitul cu valoarea/eticheta cea mai mica.

 

Adica asa:  bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0

Am pus simbolul  ' | ' pentru a servi ca separator vizual.

 

Fiecare dintre cei 8 biti, de la bit7 pana la bit0, poate lua valoarea 0 sau 1.

Pozitiei lui bit7 ii corespunde o valoare de 128 (adica 2 la puterea a 7-a), pozitiei lui bit6 ii corespunde valoarea de 64 (adica 2 la puterea a 6-a) si tot asa pana la pozitia lui bit0 careia ii corespunde valoarea 1 (adica 2 la puterea zero).

 

Cu alte cuvinte avem o corespondenta asa:

 

128 |  64  |  32  |  16  |  8     |   4    |    2  |    1 

bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0

 

Valoarea zecimala scrisa intr-un byte x de forma bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 este:

x = 128 * bit7 + 64 * bit6 + 32 * bit5 + 16 * bit4 + 8 * bit3 + 4 * bit2 + 2 * bit1 + 1 * bit0

unde bit7 ... bit0 pot lua valori numai de 1 sau 0.

 

Bun, la ce ne ajuta ce am scris mai sus?

Ce valoare zecimala avem scrisa in byte-ul: 11101010 ?

Pai  11101010 = 128 * 1 + 64 * 1 + 32 * 1 + 16 * 0 + 8 * 1 + 4 * 0 + 2 * 1 + 1 * 0 = 128 + 64 + 32 + 0 + 8 + 0 + 2 + 0 = 234

 

 

 

2. Despre shiftare (deplasa biti la stanga sau la dreapta).

 

Avem forma urmatoare:

128 |  64  |  32  |  16  |  8     |   4    |    2  |    1 

bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0

 

Dupa cum putem vedea fiecare pozitie este inconjurata de pozitii cu valoare  dubla sau la jumatate. Evident exceptie bitul7 si bitul 0 care se invecineaza doar cu cate o pozitie (bit6, respectiv bit1).

Cu alte cuvinte, daca am deplasa pozitia la un 1 de la dreapta la stanga, practic valoarea finala in byte se dubleaza ( *2). Iar daca am deplasa pozitia unui 1 de stanga la dreapta, valoarea finala a unui byte se injumatateste (/2). Aceasta este shiftarea.

 

Ex:

                            128 |  64  |  32  |  16  |  8     |   4    |    2  |    1 

Val initiala             0       1       0        0       0        1         0       0

In zecinal ce este scris mai sus are valoare:  128*0 + 64*1 + 32*0 + 16*0 + 8*0 + 4*1+ 2*0 + 1*0 = 68

 

Daca shiftam val initiala cu o pozitie la dreapta ne asteptam ca valoarea finala sa se injumatatesca:

 

                            128 |  64  |  32  |  16  |  8     |   4    |    2  |    1 

Val initiala             0       0       1        0       0        0         1       0

In zecinal ce este scris mai sus are valoare:  128*0 + 64*0 + 32*1 + 16*0 + 8*0 + 4*0+ 2*1 + 1*0 = 34

Si chiar asa si este.

 

 

Daca shiftam val initiala cu 3 pozitii la stanga ne asteptam ca valoarea finala sa se dubleze de 3 ori (2 * 2 * 2):

 

                            128 |  64  |  32  |  16  |  8     |   4    |    2  |    1 

Val initiala             0       0       1        0       0        0         0       0

In zecinal ce este scris mai sus are valoare:  128*0 + 64*0 + 32*1 + 16*0 + 8*0 + 4*0+ 2*0 + 1*0 = 32

Si chiar asa si este. 1-ul din pozitia bit 6 se deplaseaza la stanga pana dispare, iar 1-ul de pe pozitia bit2 ajunge (asa s-a nimerit) in pozitia lui bit6.

Doar ca ... valoarea rezultata nu este chiar cat ar trebui sa fie.

De ce?

 

68 *  2 * 2 * 2 = 544  Dar pe 8 biti putem scrie maxim 255! Deci valoarea rezultata este (datorita fenomenului de roll-over):  544 - 256 = 288 Inca un rollover (valoarea este mai mare ca 255) deci: 288 -256 = 32.

Exact valoarea pe care o vedem: 

 

Intrebarea este ...  am shiftat noi la stanga de 3 ori dar ce a intrat in pozitia bit0, bit1 si bit2? Ca este clar ca acolo intra ceva necunoscut pentru ca nu este "nimic" dincolo de pozitia bit0 (spre dreapta ma refer).

In principiu se considera ca la shiftare intra 0-uri pe pozitiile descoperite de la margine (ori ca este marginea din stanga a byte-ului ori marginea din dreapta). Dar am vazut ca multi programatori se asigura ca acele pozitii sunt zero folosind mascarea (bit masking) pe care o sa o descriu mai jos.

 

In limbajul C, shiftarea se realizeaza cu urmatorii operatori: 

' << ' se foloseste pentru shiftarea la stanga

' >> ' se foloseste pentru shiftare la dreapta

 

EXEMPLU

 

Un exemplu de programel care shifteaza la stanga un bit 1 pe portul C, si cand ajunge la bit 7 o ia de la capat. Daca punem LED-uri pe portul C (deci vor fi 8 LED-uri) practic vom vedea un LED aprins care se plimba de la dreapta la stanga si tot asa. 

 

Controler folosit: PIC seria 18F: PIC18F14K22

Se foloseste oscilatorul intern, cu PLL setat ON (adica se multiplica cu 4 frecventa). Frecventa de lucru este astfel 64MHz. Cum la PIC instructiunea dureaza 4 cicli de clock rezulta ca avem cel mult 16 MIPS (16 milioane instructiuni pe secunda).

Pinul de reset MCLR este dezactivat (deci este tinut intern in 1 logic si deci nu trebuie sa ne mai ocupam noi in circuit).

 

Setarile sunt asa:

Posted Image

 

Posted Image

 

Datahseet-ul este aici: http://ww1.microchip.com/downloads/en/DeviceDoc/41365D.pdf

 

iar aici avem registrii de configurat ca sa ne putem face treaba cu portul C, nestingheriti (est evorba de pagina 93 din datasheet):

Posted Image

 

Programul este acesta:

//functie de configurare a perifericelorvoid init_sys() {  // dezactivare convertoare ADC  ANSEL = 0;  ANSELH = 0;    SLRC_bit = 0;   // configurez slew-rate-ul pentru iesiri la viteza maxima  DAC1OE_bit = 0; // dezactivare DAC    // dezactivare comparatoare  C1ON_bit = 0;  C2ON_bit = 0;  TRISC = 0b00000000; // portul C este format tot din iesiri  LATC = 0b00000000;  // se porneste cu toti pinii port C in stare LOW  }void main() {  int i;             // variabila contor din ciclul for de mai jos  init_sys();        // apel functia de configurare a registrilor perifericelor  LATC = 0b00000001; // aprindem primul LED care apoi va fi 'plimbat'    // bucla infinita  while (1) {    for (i = 0; i < 7; i = i +1) {      LATC = LATC << 1; // shiftare a valorii initiale a LATC      Delay_ms(100);    // un delay cat sa stea LED-ul in starea curenta.    }    LATC = 0b00000001;  // aprindem primul LED pentru urmatorul ciclu care apoi va fi 'plimbat'    Delay_ms(100);      // un delay special pentru starea precedenta a LATC  }}

Efectul este asa:

 

Posted Image

 

 

Dupa cum vedeti acest controler are un numar de registri noi.

 

Nu am de gand sa ii tratez pe toti dar vreau sa mentionez doar unul dintre ei.

Mai exact este vorba de registrul LATx, unde x este litera portului (A,B,C etc functie de tipul de controler seria 18F folosit).

 

Acest registru LATx este un registru care este caracteristic seriei de controlere 18F si a unei parti din seria 16F.

El controleaza pinii setati ca iesire si le seteaza in 1 sau 0 dupa caz.

 

La seria 16F, controlul pinilor setati ca iesire dar si cei setati ca intrari era efectuat cu un singur registru PORTx (unde x este litera portului).

La seria 18F (cat si la seria advanced 16F), se poate proceda la fel ca la seria standard 16F, folosindu-ne numai de registrul PORTx.

Sau se poate folosi PORTx numai pentru citirea stare intrari si LATx pentru controlul starii iesirilor, asa cum este recomandat.

 

Registrul LATx a aparut ca solutie a unei probleme numite READ - MODIFY - WRITE. Nu intru in detalii, daca vreti cititi despre aceasta, problema este bine documentata pe forumurile Microchip, vreau doar sa va recomand sa folositi registrul LATx de cate ori aveti de ales pentru ca este mult mai eficient si va scapa de dureri de cap de care nici nu stiti ca exista.

 

Va urma.

 

LE: in GIF-ul de mai sus am pus pinul MCLR in VCC (din motive pe care numai proteus le stie, desi am setat MCLR disabled in configurare, nu tine cont si a fost necesar acest HIGH pe pinul MCLR facut in circuit ca sa nu stea in reset permanent).

Editat de mars01
Link spre comentariu

mars01, daca tot ziceai ca nu ai avut chef, dau un motiv sa explici si operatorul  "|".

codul contine mai putine linii asa:

void main() {  int i;       init_sys();    LATC = 0b00000001;  while (1) {      LATC = (LATC << 1) | (LATC >> 7); // 'rotire' a valorii initiale a LATC      Delay_ms(100);   }}

felicitari pentru rabdare si stil de expunere.

Link spre comentariu

3. Despre operatiile la nivel de biti

 

Operatiile uzuale la nivel de biti sunt (doar o enumerare):

 

AND - functioneaza pe tabela de adevar gasita la portile AND (si);                                                                     operator C: ' '

OR - functioneaza pe tabela de adevar gasita la portile OR (sau);                                                                       operator C: ' | '

XOR - functioneaza pe tabela de adevar gasita la portile XOR (sau exclusiv);                                                    operator C: ' ^ '

NOT - functioneaza pe tabela de adevar gasita la portile Inverter (complement fata de 1, inversor);                   operator C: ' ~ '

(si cele deja explicate: right shift, left shift).

 

Ce inseamna operatii la nivel de biti?

 

Daca avem doi bytes de forma:

 

byte1 = bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 

byte2 = bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 

 

cand aplicam o operatie binara (adica AND sau OR sau XOR sau NOT) intre bytes1 si bytes2, acea operatie se face intre bitii corespondenti aflati pe acea pozitie.

Cu alte cuvinte, o operatie binara aplicata pe cei doi bytes se face aplicand operatia:

 

intre bit7 al byte1 si bit7 al byte2,

intre bit6 al byte1 si bit6 al byte2

 

si tot asa pana la: 

 

intre bit0 al byte1 si bit0 al byte2.

 

Nu intru in detalii, daca vreti sa vedeti tabelele de adevar le puteti verifica de ex aici.

 

IMPORTANT

Ce trebuie sa stiti este ca:

AND intre doi biti este ca si cum ii inmultiti

OR intre doi biti este ca si cum ii adunati (doar ca 1 + 1 este tot 1)

XOR intre doi biti este ca si cum ii adunati (doar ca 1 + 1 este 0)

NOTA: mai sus ma refer la biti (aflati pe aceasi pozitie) si nu la bytes. OK?

 

 

Operatia NOT se aplica unui singur byte si nu intre doi bytes. Practic operatia NOT schimba starea, bit cu bit, pentru tot byte-ul (din 0 in 1 si din 1 in 0)

 

Exemple:

 

             byte1 =  0 0 1 0 0 0 1 1

             byte2 =  0 1 1 1 0 0 1 0

byte1 & byte2 =   0 0 1 0 0 0 1 0      (puteti vedea ca se inmultesc efectiv fiecare doi biti)

byte1  |  byte2 =   0 1 1 1 0 0 1 1      (se poate vedea ca se aduna efectiv bitul de sus cu bitul de jos si daca ambii biti sunt 1 se pune tot 1)

byte1  ^ byte2 =   0 1 0 1 0 0 0 1      (se poate vedea ca se aduna efectiv bitul de sus cu bitul de jos si daca ambii biti sunt 1 se pune de data aceasta 0)

          ~byte 1 =  1 1 0 1 1 1 0 0       (se vede ca fiecare bit din byte1 este inversat)

          ~byte 2 =  1 0 0 0 1 1 0 1       (se vede ca fiecare bit din byte2 este inversat)

 

Operatia XOR este folosita pentru a face toggle (adica schimbare de stare pentru anumiti biti). Tot ce trebuie facut este ca pe un anume byte, sa ii faceti XOR cu o valoare care are 1 pe bitul unde vrem sa facem toggle acelui bit al byte.

 

Spre exemplu avem registrul PORTC (care dupa cum spuneam grupeaza un numar de 8 biti care controleaza fiecare starea unui pin care in prealabil a fost setat ca iesire in registrul TRISC; sau daca vorbim de controlere seria 18F sau 16F adavanced, acelasi efect se obtine din registrul LATC dar protejat la problema read-modify-write).

 

Bun, vorbim de registrul PORTC. Vrem sa schimbam starea (facem toggle, adica inversam starea unui bit) DOAR si numai DOAR pentru bitul 3.

Facem asa:

PORTC = PORTC ^ 0b00001000;

In acest fel nu ne intereseaza ce valoare are bitul 3 din PORTC. De fiecare data cand se executa linia de mai sus, starea bitului 3 din PORTC se schimba in opus (daca era 0 devine 1, daca era 1 devine 0)

 

Cred ca puteti vedea ca se poate face foarte usor un "licurici" asa, nu?

void main() { // configuram aici registrii pentru dezactivare ADC, comparatoare etc / eu nu fac aceasta // din motive de ... lene; daca vreti un program functional va trebui sa o faceti dvs.  // Cu datasheet-ul in fata. TRISC = 0b00000000; // portul C este tot format din iesiri PORTC = 0b00000000; // portul C este initializat cu 0; fiecare pin asociat unui bit este LOW delay_ms(500);      // un delay de 500ms ca sa putem vedea si starea initiala, nu? // bucla infinita while (1) {   PORTC = PORTC ^ 0b00001000; // se schimba starea pinului corespondent bit3, pe restul ii lasam in pace ...   // daca vroiam sa schimb starea la 3 biti, adica licurici pe trei biti (sa zicem bit7, bit3 si bit0) faceam linia de mai sus asa:   // PORTC = PORTC ^ 0b10001001;   delay_ms(500);              // un delay de 500ms ca sa putem vedea efectiv starile cum se schimba la fiecare 500ms  }}

Cam asta este efectul (doar ca folosind un PIC18F am inlocuit registrul PORTC cu LATC in program)

Posted Image

 

 

 

 

Sau pentru un PIC18F14K22, un mic joc de lumini facut aiurea cu doar cateva modificari. Doar ca sa vedeti utilitatea imediata a "jonglarii cu biti". Setarile CONFIG sunt cele postate anterior pentru PIC18F14K22.

//functie de configurare a perifericelorvoid init_sys() {  // dezactivare convertoare ADC  ANSEL = 0;  ANSELH = 0;    SLRC_bit = 0;   // configurez slew-rate-ul pentru iesiri la viteza maxima  DAC1OE_bit = 0; // dezactivare DAC    // dezactivare comparatoare  C1ON_bit = 0;  C2ON_bit = 0;  TRISC = 0b00000000; // portul C este format tot din iesiri  LATC = 0b00000000;  // se porneste cu toti pinii port C in stare LOW  }void main() { init_sys();  TRISC = 0b00000000; // portul C este tot format din iesiri LATC = 0b00000000; // portul C este initializat cu 0; fiecare pin asociat unui bit este LOW delay_ms(500);      // un delay de 500ms ca sa putem vedea si starea initiala, nu? // bucla infinita while (1) {   LATC = LATC ^ 0b10101010;   delay_ms(500);              // un delay de 500ms ca sa putem vedea efectiv starile cum se schimba la fiecare 500ms   LATC = LATC ^ 0b01010101;   delay_ms(500);              // un delay de 500ms ca sa putem vedea efectiv starile cum se schimba la fiecare 500ms }}

Posted Image

Editat de mars01
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