Programare în C pentru începători
"În primul rând, învață informatica și toată teoria. În al doilea rand, dezvolta-ți un stil de programare. La sfârșit, uită totul și doar programează." - George Carrette
.
- Mai jos sunt subiectele pe care le voi aborda eu .Voi posta noțiuni de bază pentru cei care doresc să invețe.
- 1. Etapele de compilare ale unui program în C.
- 2. Identificatori,separatori ,cuvinte cheie,constante .
- 3. Tipuri de date,operatori și expresii (operatorii de atribuire, operatorii aritmetici,operatorii relationali,operatorii binari, operatorii booleeni ,operatorul sizeof). O sa vă prezint și ordinea evaluarii operanzilor.
- 4. Variabile (Clase de memorie,Specificatorii de clasă de memorie,Specificatorii de tip,Declaratori, Modificatorii de acces).
- 5. Instrucțiuni în C (Instrucțiunile de selectie și Instrucțiunile de ciclare)
- 6. Funcții ( la acest capitol voi discuta și despre POINTERI,deoarece funcțiile și pointerii se leagă ).
- 7. Structuri și uniuni.
- 8. Tablouri Unidimensionale/ Bidimensionale.
- 9. Noțiuni despre microcontrolere ( o să vă povestesc despre proiectele mele cu Arduino / Atmega,o sa vă povestesc puțin despre arhitectura acestora, PWM, CAN, TIMERE,ÎNTRERUPERI ).
- 10. Noțiuni fundamentale de circuite electrice.
- 11. Electronică analogică/digitală.
- 12. AUTOMOTIVE
Despre mine
Va salut. Mă numesc Sorin,sunt absolvent de automatică,și mă mândresc de instituția unde am învățat. Sunt pasionat de programare,mai exact de limbajul C ,și îmi doresc să-i ajut și pe cei care doresc să învețe acest limbaj. Mai multe informații puteți citi la sectiunea Despre mine .
IMPORTANT !
Greșelile frecvente în lucrul cu operatorii la nivel de bit si operatorii logici
Confundarea operatorilor "!" și "~".
"!" transformă ceva "fals" (=0) în "adevărat" și vice-versa.
"~" transformă toate zerourile în 1 și vice-versa.
Exemplu: !1100 = 0; ~1100 = 0011
Confundarea operatorilor "&" și "&&".
"&" efectuează operația de intersecție a doi biți.
"&&" returnează starea de adevăr a expresiei, evaluată cu tabelul de adevăr AND, în care 0 reprezintă "fals" și orice altceva reprezintă "adevărat".
Exemple: 1101 & 1001 = 1001;
1101 && 1001 = 1
1100 && 0001 = 1
Confundarea operatorilor "|" și "||".
"|" efectuează operația de uniune a doi biți.
"||" returnează starea de adevăr a expresiei, evaluată cu tabelul de adevăr OR, în care 0 reprezintă "fals" și orice altceva reprezintă "adevărat".
Exemple: 1101 | 1001 = 1101;
1101 || 1001 = 1
1. Etapele de compilare ale unui program în C

1. Etapele de compilare ale unui program C :
a) Codificarea algoritmului înr-un limbaj de programare ;
- trebuie de știut că această codificare se realizează într-un program sursă.
- se salvează fișierul sursă ( de exemplu cu numele "Salutare.c")
- un fișier C se salvează cu extensia c.
b) Compilarea și link-editarea - BUILD ;
- în acest pas trebuie să reținem : prin compilarea și link-editarea programul sursă este tradus integral în limbajul mașinii,iar rezultatul este un fișier executabil.
c) Execuția programului -RUN;
- această etapă se poate realiză utilizând comenzile IDE-ului RUN
De reținut !
Directivele de preprocesor sunt preluate de către compilator înainte de compilarea acestuia. Toate directivele (să rețineți că încep cu #include,etc.) ,sunt procesate mai întâi,iar simbolurile ce apar în cadrul programului sunt înlocuite cu valoarea lor. De abia după aceea programul este compilat.
Exemplu
Directiva #include cere includerea în compilator a unor fișiere sursă C care sunt de obicei fișiere antet(header), ce reunesc declarații de funcții standard .
Fișierele de tip header nu sunt biblioteci de funcții, și nu conțin definiții de funcții.
Fișierele antet(header) conțin declarații de funcții.
Declarațiile funcțiilor standard de I/E(intrări-ieșiri),sunt reunite în fișierul "stdio.h" care trebuie inclus în compilare #include. Parantezele <> sunt delimitatorii ai șirului de caractere., ce reprezintă numele fișierului,și arată că acest nume trebuie căutat într-un anumit director.
#define (directivă) este folosită pentru definirea constantelor simbolice.
O sa vă dau un exemplu ,ca să înțelegeți :
#define Asd 50
Când se întalnește #define se ia primul șir de caractere de după "#define" și se înlocuiește cu al doilea șir de caractere. După cum observi, "Asd" este primul șir de caractere apoi urmează un spatiu apoi "50". Peste tot în cod unde găsește "Asd" compilatorul înlocuiește cu "50".
În cazul nostru: #define Multiply(a, b) a*b
Peste tot unde găsește "Multiply(a, b)" se înlocuiește cu "a*b".
Compilatorul este deștept și știe că: a = (op1+1) b = (op2+2)
În exemplul nostru: op1 = 12; op2 = 5; x = 0;
Apoi linia aceasta: x = Multiply(op1+1, op2+2);
Se transformă în aceasta: x = (op1+1)*(op2+2); - pasul 1 (aici se face practic înlocuirea aceea) x = (12+1)*(5+2);
x = (12+1)*(5+2); - pasul 2
x = (13)*(7); - pasul 3
x = (12+1)*(5+2); - pasul 4
x = 91 - pasul 5
Ca să vă dau alt exemplu puteți considera: #define DefineTEST(a, b) a-b+100
x = DefineTEST(op1+1, op2+2);
se transforma în asta:
x = (op1+1)-(op2+2)+100; - pasul 1
x = (12+1)-(5+2)+100; - pasul 2
x = (13)-(7)+100; - pasul 3
2.Identificatori,separatori,cuvinte cheie, constante
.

Identificatori
De reținut !
Un identificator este o succesiune de litere şi cifre dintre care primul caracter este în mod obligatoriu o literă. Se admit şi litere mari şi litere mici dar ele se consideră caractere distincte. Liniuţa de subliniere _ este considerată ca fiind literă.
Separatori
Separatorul cel mai frecvent este aşa numitul spaţiu alb (blanc) care conţine unul sau mai multe spaţii, tab-uri, new-line-uri sau comentarii. Mai jos vă atașez o lista a separatorilor admişi în limbajul C :
( ) Parantezele mici încadrează lista de argumente ale unei funcţii sau delimitează anumite părţi în cadrul expresiilor aritmetice etc ;
{ } Acoladele încadrează instrucţiunile compuse, care constituie corpul unor instrucţiuni sau corpul funcţiilor;
[ ] Parantezele mari încadrează dimensiunile de masiv sau indicii elementelor de masiv ;
" " Ghilimelele încadrează un şir de caractere ;
' ' Apostrofurile încadrează un singur caracter sau o secvenţă de evitare ;
; Punct şi virgula termină o instrucţiune ;
/* Slash asterisc început de comentariu ;
*/ Asterisc slash sfîrşit de comentariu ;
Un comentariu este un şir de caractere care începe cu caracterele /* şi se termină cu caracterele */.
Cuvinte cheie
De reținut !
Cuvintele cheie sunt identificatori rezervaţi limbajului C. Ei au o semnificaţie bine determinată şi nu pot fi utilizaţi decât aşa cum cere sintaxa limbajului. Cuvintele-cheie se scriu obligatoriu cu litere mici, de exemplu ( if , while ,for , int ,double,break ,etc).
Constante
În limbajul C există următoarele tipuri de constante: întreg (zecimal, octal, hexazecimal), întreg lung explicit, flotant, caracter, simbolic.
Constante întregi
O constantă întreagă constă dintr-o succesiune de cifre.
O constantă octală este o constantă întreagă care începe cu 0 (cifra zero), şi este formată cu cifre de la 0 la 7.
O constantă hexazecimală este o constantă întreagă precedată de 0x sau 0X (cifra 0 şi litera x).
Cifrele hexazecimale includ literele de la A la F şi de la a la f cu valori de la 10 la 15.
Important !
Trebuie să rețineți că o constantă întreagă este generată pe un cuvînt (doi sau patru octeţi, dacă sistemul de calcul este pe 16 sau 32 de biţi).
Trebuie să rețineți că o constantă zecimală a cărei valoare depăşeşte pe cel mai mare întreg cu semn reprezentabil pe un cuvînt scurt (16 biţi) se consideră de tip long şi este generată pe 4 octeţi.
Trebuie să rețineți că o constantă octală sau hexazecimală care depăşeşte pe cel mai mare întreg fără semn reprezentabil pe un cuvînt scurt se consideră de asemenea de tip long.
Trebuie să rețineți că o constantă întreagă devine negativă dacă i se aplică operatorul unar de negativare '-'.
Constante de tip explicit
Trebuie să rețineți că o constantă întreagă zecimală, octală sau hexazecimală, urmată imediat de litera l sau L este o constantă lungă.
Aceasta va fi generată în calculator pe 4 octeţi.
Exemplu: 123L.
O constantă întreagă zecimală urmată imediat de litera u sau U este o constantă de tip întreg fără semn.
Litera u sau U poate fi precedată de litera l sau L.
Exemplu: 123lu.
Constante flotante
Trebuie să rețineți că o constantă flotantă constă dintr-o parte întreagă, un punct zecimal, o parte fracţionară, litera e sau E şi opţional, un exponent care este un întreg cu semn. Partea întreagă şi partea fracţionară sunt constituite din câte o succesiune de cifre. Într-o constantă flotantă, atât partea întreagă cât şi partea fracţionară pot lipsi dar nu ambele,de asemenea poate lipsi punctul zecimal sau litera e şi exponentul, dar nu deodată (şi punctul şi litera e şi exponentul).
Exemplu: 123.456e-7 sau 0.12e-3 .
Constante caracter
Trebuie să rețineți că o constantă caracter constă dintr-un singur caracter scris între apostrofuri, de exemplu 'x'. Valoarea unei constante caracter este valoarea numerică a caracterului, în setul de caractere al calculatorului. De exemplu în setul de caractere ASCII caracterul zero sau '0' are valoarea 48 în zecimal, total diferită de valoarea numerică zero.
3. Tipuri de date,operatori și expresii

De reținut !
Tipurile de date reprezintă tipul de informație care poate fi stocat într-o variabilă.
În C ,există tipuri de date fundamentale ( char,int,float,etc.) , și tipuri de date derivate ( tablouri,funcții,pointerii,etc)
Tipurile char și unsigned char memorează valori întregi. La afișarea unei date de acest tip nu se va afișa numărul pe care îl memorează ci caracterul care are are codul ASCII egal cu acel număr.
Tipurile întregi permit memorarea de valori întregi. Tipul de bază este int. O dată de tip int poate memora valori întregi cuprinse între -231 și 231-1.
Tipurile reale - în virgulă mobilă memorează valori reale, reprezentate prin mantisă și exponent. În acest mod se pot reprezenta valori foarte mari, dar precizia reprezentării poate fi slabă (numărul de cifre semnificative memorate poate fi mult mai mic decât numărul de cifre din număr.)
Tipurile reale sunt: float, double, long double.
Tipul pointer- o dată de tip pointer memorează o adresă de memorie ( atât trebuie să rețineți momentan ,o sa revin asupra acestui tip la capitolul Funcții și Pointeri)
Tipul bool - anumite operații care se fac cu datele au ca rezultat valori de adevăr: adevărat sau false.
3.1 Operatori

Operatorii au ca efect realizarea unor operații care returnează un rezultat.
Operatorul de atribuire (=)
Prin atribuire se memorează o valoare într-o variabilă.
Câteva exemple : variabilă = expresie;
• int a; /declararea unei variabile
• a=5 ; / atribuirea unei valori constante unei variabile
• int b = 5; / declarația unei valori cu inițializare
• b=a+10 ; / atribuirea valorii unei expresii unei variabile
• a=b=100; / atribuire multiplă
• a=a+b; / expresia conține ca operand variabila către care se face atribuirea ,întâi este evaluată expresia a+b cu valoarea pe care o avea înainte de operație ,și apoi se face operația de atribuire.
Operatorul unar +
Un exemplu
• int a ;
• a=+5; / expresia +5 întoarce valoarea 5
Operatorul unar -
Un exemplu
• int a, b=2;
• a= -b; / expresia -b întoarce valoarea lui b cu semn schimbat (-2)
• variabila b nu este afectată de operație, pastrându-și valoarea
• a= -2; b=2;
Operatorul % întoarce restul împărțirii a 2 întregi;
Un exemplu
• int a=15; • int b;
• b=a%2; / b preia valoarea restului împărțirii lui 2 ,deci preia valoarea 1
• b=a%1.5 / eroare ,unul dintre operanzi nu este întreg
Operatorii de incrementare (++) și decrementare (--) pot fi prefixați sau postfixați ;
• ++ modificarea valorii operandului se face înainte de a-i folosi valoarea;
• -- se folosește valoarea inițială a operandului ,și apoi se efectuează modificarea ;
Exemplu
• int i=5, j;
• j=++i; / operatorul este prefixat deci se efectuează operația de incrementare a lui i , și apoi se face atribuirea către j → i=6; j=6;
• j=i-- ; / operatorul este postfixat deci întâi se efectuează operația de atribuire către j ,și apoi se face incrementarea a lui i →i=5; j=6;
3.2 Operatorii Logici
Atenție ! == nu este echivalent cu =
== verifică dacă operanzii dați sunt egali sau nu;
• Exemplu : 5 ==5 → rezulta Adevărat , deoarece 5 este egal cu 5;
• 4 == 5 → rezultă Fals , deoarece 4 nu este egal cu 5
Operatorul AND ( ȘI) logic - && → condiția este ca ambele să fie adevarate
Un exemplu ca să înțelegeți :
void main()
{
int examen1 = 8;
int examen2 = 6;
if (examen1 > 7 && examen2 >7)
{
printf("Esti ales sa participi la olimpiada!");
}
else {
printf ("Ai note mici !");
}
Acum sa explicam codul scris de mai sus :
Va returna FALS ,deoarce una dintre expresii este falsă
examen1 > 7 ( va rezulta adevarat deoarece 8 este mai mare decât 7)
examen2 > 7 ( va rezulta fals ,deoarece 6 nu este mai mare decat 7)
Operatorul OR (SAU) logic || condiția este ca cel puțin una din cele două să fie adevărate ,ca să returneze ADEVĂRAT
Să luăm exemplul anterior :
if ( examen1==8 || examen2==8) {
printf( "Ai nota 8 !");
}
else {
printf ( "Nu ai nici o nota 8 !");
}
Să explicăm codul scris de mai sus
Va returna ADEVĂRAT ,deoarece una din cele două expresii este adevărată
examen1 == 8 ( 8== 8 este adevărată )
examen2 ==8 ( 6 == 8 este falsă)
Dat fiind că una din cele două expresii este adevărată ,atunci vă returna ADEVĂRAT
Operatorul ternar ? : a = =10 ?: b :c
conditie (a = =10 ) instructiune 1 (b) instructiune 2(c)
sintaxa este următoarea: conditie ? : instructiune1 : instructiune 2;
Luam exemplul anterior :
• 8==8 ? printf("Adevarat !") : printf("Fals !") ;
• conditie: se intreaba 8 este cu 8 ,daca este Adevarat ,se va afisa prima instructiune, daca va fi Fals se va executa a doua instructiune dupa :
Operatorul de negare ! are rolul de a inversa un adevăr.
if ( ! examen1==8)
{
printf(" da,examen1 este egal cu 8! ");
}
else {
printf( " nu,este egal cu 8! ");
}
! examen1 ==8 → daca este ADEVARAT va schimba in FALS , daca este FALS va schimba in ADEVARAT
! examen1==8 fiind operatorul in fata va schimba in FALS ,și va returna FALS
Operatorul sizeof - permite aflarea lungimii în octeți a unei date sau expresii în funcție de tipul declarat.
Sintaxa
• sizeof (expresie)
• sizeof (tip)
Momentan este bine să rețineți ca operatorul sizeof - permite aflarea lungimii în octeți a unei date sau expresii în funcție de tipul declarat. Asupra acestui operator voi reveni pe viitor .
Aplicații ! Să recapitulăm ce am învățat.
1) ! ( 4 >5) && (11 ==11) || ( 9 ! = 9)
• Mai întâi rezolvăm parantezele simple cu operatorii relaționali ! ( F && A || F ) acum trebuie sa rezolvăm noua paranteză, urmând ordinea NOT,AND,OR.
F&& A va returna F , si expresia devine !F=A
2) ( 3==5) || ! ( 4 > 2)
F || ! A → F || F → F
Ordinea evaluarii operanzilor ( este foarte importantă) este prezentată mai jos , am încercat să explic ordinea acestora pe niveluri ( este bine sa rețineți această ordine)
3.3 Operații pe biți
1. SI logic - operatorul &. Aceasta operatie compara bitii doi cate 2, iar rezultatul este dat conform urmatoarei tabele de adevar:
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
2. SAU logic - operatorul | . Aceasta operatie compara bitii doi cate doi, iar rezultatul este dat conform urmatoarei tabele de adevar:
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
3. SAU exclusiv - operatoul ^ - XOR. Aceasta operatie logica se aplica atat asupra unui singur bit, cat si a unui numar format dinj mai multi biti. Practic, daca avem pe aceeasi pozitie doi biti cu aceeasi valoare, operatorul XOR va returna 0, altfel va returna 1, conform tabelei de mai jos:
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
4. Negare logica - operatorul ~ .Aceasta operatie se aplica asupra unui singur bit, iar rezultatul este dat conform exemplelor de mai jos:
~0 = 1 ~1 = 0
5. Șhiftare logica la stanga - operatorul << .Aceasta operatie logica se aplica atat a unui singur bit, cat si a unui numar format din mai multi biti. Operatorul muta toti bitii mai in stanga cu numarul de pozitii mentionar in dreapta operatorului si completeaza numarul initial in dreapta valori de 0 pe pozitiile bitilor mutati. bitii cei mai din straga sunt stersi exemplu:
Fie numarul 0x10101011
0x10101011 << 3 = 0x01011000
ATENTIE!
Shiftarea la stanga a unui numar binar inseamna de fapt inmultirea cu 2 la puterea numarului de pozitii a numarului in reprezentare zecimala
exemplu:
fie numarul 15, reprezentat pe 8 biti -> 0x00001111
0x00001111 << 2 = 0x00111100 -> 2^5 + 2^4 + 2^3 + 2^2 = 32 + 16 + 8 + 4 = 60 = 15 * 2^2
La shiftare la stanga, fiecare pozitie inmulteste cu 2 valoarea numarului
6. Șhiftare logica la drepta - operatorul >> Aceasta operatie logica se aplica atat asupra unui singur bit, cat si a unui numar format din mai multi biti. Operatorul muta toti bitii mai in dreapta cu numarul de pozitii mentionat in dreapta operatorului si completeaza numarul initial in dreapta cu valori de 0 pe pozitiile bitilor mutati. bitii cei mai din dreapta sunt stersi
exemplu:
fie numarul 0x01011001
0x01011001 >> 3 = 0x00001011
ATENTIE! Shiftarea la dreapta a unui numar binar inseamna de fapt impartirea cu 2 la puterea numarului de pozitii a numarului in reprezentare zecimala
exemplu:
fie numarul 64, reprezentat pe 8 biti -> 0x01000000
0x01000000 >> 2 = 0x00010000 -> 2^4 = 16 = 64 / 2^2
La shiftarea la dreapta, fiecare pozitie imparte la 2 valoarea numarului
.
3.4 Precedența operatorilor în limbajul C
ATENȚIE !
Este foarte important ,să tineți cont de această ordine a operatorilor ,i-am împărțit pe mai multe niveluri de prioritate .
Țineți minte : Cel mai prioritar este nivelul 1, dupa care este nivelul 2, si asa mai departe pana la nivelul 15 .
Nivelul 1 Operatori: ++(doar cand este la finalul variabilei), --(doar cand este la finalul variabilei), (), [ ], ., ->
Nivelul 2 Operatori: ++(doar cand este in fata variabilei), --(doar cand este in fata variabilei), + si - (doar cand sunt folositi pentru operatii pe biti, adica sunt aplicati pe numere binare) !, & (adresa), (cast tip) (de exemplu (int *)), * (pointer), sizeof
Nivelul 3 * (inmultire), / (impartire), % (rest)
Nivelul 4 + (adunare), - (scadere) pentru numere intregi
Nivelul 5 <<, >> - shiftare stanga si dreapta pe biti
Nivelul 6 Operatorii de comparare: <, <=, >, >=
Nivelul 7 Operatorii de comparare: ==, !=
Nivelul 8 & - SI pe biti
Nivelul 9 ^ - XOR (sau exclusiv) pe biti
Nivelul 10 | - SAU pe biti
Nivelul 11 && - operatorul SI logic
Nivelul 12 || - SAU logic
Nivelul 13 ?: - operator conditional
Nivelul 14 Operatorii de asignare: =, +=, -=, 8=, /=. %=, <<=, >>=, &=, ^=, |=
Nivelul 15 Operatorul , - folosit la decalratii de enumerari si la apelul parametrilor functiilor
Recapitulare operatori
Am terminat de prezentat primele trei subiecte. În această
prezentare,voi face o scurta recapitulare din ce am prezentat. Pentru
cei începători ,vă sfătuiesc să vă instalați Code Bloks-ul sau orice
IDE,este necesar deoarece fiecare cod trebuie rulat .La fiecare subiect
pe care îl voi preda ,voi realiza și niste aplicații practice pe care le
voi explica.
Linkul necesar pentru a descărca Code Blocks-ul : https://www.codeblocks.org/
Exemplu de un program in C,cu operații pe biți :
int main(void) {
unsigned int a = 10, b = 9;
printf("a = 0x%04x, b = 0x%04x\n", a, b);
// Afisam SI logic intre a si b //
10 in baza 2 = 0b1010
9 in baza 2 = 0b1001
8 in baza 2 = 0b1000
printf("a & b = 0x%04x\n", a & b);
// Afisam SAU logic intre a si b /
10 in baza 2 = 0b1010
9 in baza 2 = 0b1001
b in baza 2 = 0b1011
printf("a | b = 0x%04x\n", a | b);
// Afisam XOR logic intre a si b //
10 in baza 2 = 0b1010
9 in baza 2 = 0b1001
3 in baza 2 = 0b0011
printf("a ^ b = 0x%04x\n", a ^ b);
// Afisam negare logica pentru a si b //
10 in baza 2 = 0b1010
~10 in baza 2 = 0b0101
9 in baza 2 = 0b1001
~9 in baza 2 = 0b0110
printf("~a = 0x%04x, ~b = 0x%04x\n", ~a, ~b);
// Afisam shiftare stanga pentru a si b //
10 in baza 2 = 0b00001010 /
10 << 2 in baza 2 = 0b00101000
9 in baza 2 = 0b00001001
9 << 2 in baza 2 = 0b00100100
printf("a << 2 = 0x%04x, b << 2 = 0x%04x\n", a << 2, b << 2);
// Afisam shiftare dreapta pentru a si b //
10 in baza 2 = 0b00001010
10 >> 2 in baza 2 = 0b00000010
9 in baza 2 = 0b00001001 /
9 >> 2 in baza 2 = 0b00000010
printf("a >> 2 = 0x%04x, b >> 2 = 0x%04x\n", a >> 2, b >> 2);
Exemplu Sa presupunem ca int
se reprezinta pe 2 octeti.
Care va fi rezultatul programului?
#include
int main()
{
printf("%x\n", -1>>1);
return 0;
}
Explicatie
0'b 1000 0001 = -1 (pt. integer = 2 bytes, adica 16 biti)
-1 >> 1 = 0'b 1000 0000 0000 0001 >> 1 = 0'b 0100 0000 0000 0000 = 0x4000 Deci, programul afiseaza 0x4000.
Exemplu Care va fi rezultatul programului?
#include
int main() {
int a=0, b=1, c=3;
*((a) ? &b : &a) = a ? b : c;
printf("%d, %d, %d\n", a, b, c);
return 0;
}
Explicatie
Linia *((a) ? &b : &a) = a ? b : c; nu va avea nicun efect, deoarece a este 0 si se evaluaza ca true. Din acest motiv, la adresa lui b se va memora tot valoarea lui b, deci instructiunea nu va avea nicun efect.
Deci, programul afiseaza 0, 1, 3
Exemplu Care va fi rezultatul programului?
#include
int main()
{
int x, y, z;
x=y=z=1;
z = ++x || ++y && ++z;
printf("x=%d, y=%d, z=%d\n", x, y, z);
return 0;
}
Explicatie
Programul afiseaza x=2, y=2, z=2, deoarece z = 2 || 2 && 2 = 2 || 0 = 2.
.
4. Variabile
Recapitulare !
După cum am văzut atunci când am prezentat reprezentarea numerelor în calculator și lucrul pe biți, numerele sunt reprezentate în calculator în binar. Ele sunt o înșiruire de 8, 16, 32 sau 64 de biți și dimensiunea maximă a unui număr depinde de arhitectura procesorului.
În acest capitol vom discuta despre VARIABILE.
Ce este o variabila ?
Trebuie să știți că o variabilă trebuie să existe undeva în memorie pentru a putea fi folosită, ca orice altă informație din calculator, variabilele ocupă o locație la o adresă în memorie. Pe scurt o variabilă este un obiect de
programare caruia i se atribuie un nume.
Variabilele se deosebesc după
denumire și pot primi diferite valori. Numele variabilelor sunt
identificatori. Numele de variabile se scriu de obicei cu litere mici
(fara a fi obligatoriu).
IMPORTANT ! Variabilele în limbajul C sunt caracterizate prin două atribute: clasa de memorie si tip.
1. Clase de memorie
Trebuie să știți că limbajul C prezinta patru clase de memorie: automatică, externă, statică, registru.
Variabile automatice- acestea sunt variabile locale fiecarui bloc sau functii . Ele se declară prin specificatorul de clasă de memorie auto sau implicit prin context. O variabilă care apare în corpul unei functii sau al unui bloc pentru care nu s-a făcut nici o declaratie de clasa de memorie se consideră implicit de clasa auto. O variabilă auto este actualizată la fiecare intrare în bloc și se distruge în momentul când controlul a părasit blocul.
Variabile externe - acestea sunt variabile
cu caracter global. Ele se definesc în afara oricărei funcții și
pot fi apelate prin nume din oricare funcție care intră în
alcatuirea programului.
La întâlnirea unei definiții de variabilă externă compilatorul alocă și memorie pentru această variabilă. Aceste variabile există și își pastrează valorile de-a lungul execuției întregului program.
Variabile statice - acestea se declară
prin specificatorul de clasă de memorie static. Aceste variabile sunt la
rândul lor de doua feluri: interne și externe. Variabilele statice interne sunt
locale unei funcții și se definesc în interiorul unei funcții,
dar spre deosebire de variabilele auto, ele își păstrează valorile tot
timpul execuției programului.
Variabilele statice externe se
definesc în afara oricărei funcții și orice funcție are
acces la ele. Aceste variabile sunt însă globale numai pentru
fișierul sursă în care ele au fost definite. Nu sunt recunoscute în
alte fișiere.
Variabile registru- sunt variabile care se
declara prin specificatorul de clasa de memorie register. Ca si variabilele auto ele sunt locale unui bloc
sau funcții și valorile lor se pierd la ieșirea din blocul sau
funcția respectivă. Variabilele declarate register indică compilatorului
că variabilele respective vor fi folosite foarte des.
O să vă mai vorbesc și despre VARIABILE VOLATILE
În primul rând , trebuie să știți că variabilele volatile sunt variabile asupra căruia compilatorul nu poate face nici un fel de optimizare. Este o variabilă în care își poate modifica valoarea în timpul rulării codului din cauze ce nu tin de execuția efectivă a codului.
EXEMPLU !
#include<stdio.h>
void main()
{
int a=7; // am declarat o variabila globala;
void f1 (void)
{
printf ("a=%d", a);
}
void f2 (void)
{
int a=9; // am declarat o variabila locala
printf ("a=%d", a);
}
void main()
{
int b=21, c=6; // am declarat variabile locale functiei main;
printf ("a=%d,b=%d,c=%d", a,b,c);
}
}
Recapitulare
#include <stdio.h>// variabilele globale sunt cele care sunt vazute in cadrul intregului fisier
// ca durata de viata, ele sunt active pe tot parcursul rularii programului
// de obicei, ele se declara sub zona unde sunt incluse fisierele de tip header
// este obligatoriu ca variabilele globale sa se afle in afara oricarei functii
// numar1 are vizibilitate in cadrul oricarei functii din fisier. poate fi accesat de oricare dintre functiile prezenta in fisier
void afisare()
{
printf("Numar1 are valoarea: %d", numar1);
printf("Numar2 are valoarea: %d", numar2);
printf("Numar3 are valoarea: %d", numar3);
}
int main()
{
// variabilele locale sunt cele care sunt declarare in interiorul unei functii
// sau a unei structuri (de exemplu: for, if, while etc.)
// durata lor de viata este pana in momentul in care este inchisa functia sau
// structura in care ele sunt declarate, adica pana in momentul in care se ajunge
// la acolada de inchidere
// variabila numar2 are durata de viata doar in cadrul functiei main
printf("Numar1 are valoarea: %d\n", numar1);
for (int numar3 = 0; numar3 < 2; numar3 ++)
// numar3 a fost declarat ca varibila locala in cadrul instructiunii for
// el are durata de viata doar in cadrul instrctiunii for, urmand ca la iesirea din for sa fie distrus
//acesta linie de cod va genera eroare de compilare deoarece numar3 a fost distrus la iesirea din structura for si aici nu mai are vizibilitate
afisare();
return 0;
static int sum(int x, int y, int z)
{
return (x + y + z);
}
IMPORTANT !
Cand se pune static in fata unei functii inseamna ca acea functie poate fi folosita DOAR in fisierele .h/.c in care a fost declarata, cu precizarea ca fisierele .h/.c trebuie sa aiba acelasi nume
Variabile externe
extern int sum(int x, int y, int z)
{
return (x + y + z);
}
IMPORTANT !
Cand se pune extern in fata unei functii inseamna ca poate fi folosita si in afara fisierului .h/.c in care a fost declarata.
Recapitulare Variabile,Tipuri de date operatori si expresii
1 . Care va fi rezultatul programului ?
void main()
{
int a,b,c,d;
a=b=c=d=1;
a=++b>1 || ++c>1 && ++d>1;
printf("%d%d%d%d",a,b,c,d);
}
Explicatie : În primul rând este necesar să ținem cont de precedența operatorilor ( lista cu precedența operatorilor poate fi găsită la subiectul 3.4).
a=++b>1 || ++c>1 && ++d>1;
Deoarece ++ pus în stânga se execută după ce se face comparatia între b,c,d si 1. Toate cele 3 comparații vor da fals, adică 0. Deci și a va avea valoarea 0, iar b, c, b vor avea valoarea 2, ca urmare a incrementărilor.
Associativitatea || ,operatia este de la stânga la dreapta, așa că (++b>1) va fi mai întai executată, apoi b crește incremental la 2 și partea stângă a operației || va deveni adevarată, așa că partea dreaptă nu va fi executată și c,d ramane neschimbata.
++b incrementul de la b la 2, deci b este acum > 1, astfel încât întreaga expresie devine adevărată, deci 1 este atribuită la a, c și d rămân neschimbate, deoarece nu a fost nevoie să se evalueze restul expresiei.
Va afisa : 1 2 1 1
2 . Care va fi rezultatul programului ?
int main ()
{
unsigned int x = 1;
printf("Signed Result %d \n", -x);
printf("Unsigned Result %ud \n", -x);
return 0;
}
Explicatie :
0000 0000 0000 0000 0000 0000 0000 0001 = 1 pe 32 biti
1000 0000 0000 0000 0000 0000 0000 0001 = -1 pe 32 biti
reprezentare unsigned = (65535/2) + 1 = 32767 + 1 = 32768.
Programul afișează reprezentarea fără semn a lui - 1, care este 32768.
Signed Result -1
Unsigned Result 32768
3 . Care va fi rezultatul programului ?
#include<stdio.h>
int main()
{
printf("%x", -1>>1);
return 0;
}
Explicatie :
1000 0000 0000 0000 0000 0000 0000 0001 = -1 pe 32 biti
-1>>1 --> 0100 0000 0000 0000 0000 0000 0000 0000
Programul afiseaza 0x40000000
4 . Care va fi rezultatul programului ?
#include<stdio.h>
int main()
{
unsigned int m = 32;
printf("%x", ~m);
return 0;
}
Explicatie :
0000 0000 0010 0000
1111 1111 1101 1111 = FFDF
5 . Care va fi rezultatul programului ?
#include<stdio.h>
int main()
{
printf("%d, %d, %d", sizeof(3.0f), sizeof('3'), sizeof(3.0));
return 0;
}
Explicatie :
sizeof(3.0f) = sizeof(float) = 4. sizeof('3') = sizeof(char) = 1. sizeof(3.0) = sizeof(double) = 4.
Deci, programul afiseaza
4, 1, 4
5. Instrucțiuni în C

În acest capitol vom discuta despre instrucțiunile in limbajul C. Pentru început vreau sa definim termenul de instructiune.
O instructiune o putem define ca fiind o portiune de program ce poate fi executată,adica specifica o actiune : iteratie,selectie,etc.
5.1 Instructiunea de selectie
Cele doua tipuri de instructiuni de selectie din C sunt if si switch.
5.1.1 Instructiunea IF
Forma generala a instructiunii if este :
// instructiuni
//...
}
if...else este cea mai simpla instructiune conditionala,forma generala este urmatoarea
if (conditie) {
// instructiuni
// ...
} else {
// alte instructiuni
// ...
}
Instructiunea evalueaza expresia conditie si execută instrucţiunile dintre acolade doar daca rezultatul este nenul. În varianta cu else, pentru rezultat nul este executat blocul de instrucţiuni aflat după else. Exemplu :
if (m == s) {
printf("Numerele sunt egale");
} else if (m > s) {
printf("M este mai mare");
}
Explicatie :
Prima data se evaluează expresia conditie si anume m==s, daca este adevarata se executa instructiunea dintre acoloade si anume printf("Numerele sunt egale"), iar in caz contrar se executa ce urmeaza dupa else si anume printf("M este mai mare").
5.1.2 Instructiunea SWITCH
Instructiunea switch testeaza succesiv valoarea unei expresii fata de o lista de constant de tip caracter sau intreg. Cand apare egalitate, se executa instructiunea sau instructiunile associate acelei constante.
Forma generala a instructiunii switch este :
case constanta1:
// instructiuni1
case constanta2:
// instructiuni2
...
default:
// instructiuni
}
Valoarea expresie este evaluata la un tip intreg, apoi aceasta valoare este comparata cu fiecare constanta, este rulat blocul de instrucţiuni al valorii găsite. În caz ca numărul nu este egal cu nici una dintre constante, este executat blocul aflat după default.
Important !!!!
După executarea ultimei instrucţiuni dintr-un bloc case, execuţia nu continua după blocul switch si la inceputul următorului bloc case constanta sau default. Pentru a ieşi din blocul switch, se foloseşte instrucţiunea break.
5.2 Instructiuni de ciclare
Aceste instructiuni de ciclare permit ca o secventa de instructiuni sa se execute repetat,pana cand se indeplineste o anumite conditie.
5.2 Instructiunea FOR ( cu numar cunoscuti de pasi)
Forma generala acestei instructiuni este urmatoarea :
for ( initializare; conditie; increment) instructiune ;
expresia initializare - este folosita in mod special pentru a da o valoare initiala variabilei care controleaza ciclul ;
expresia conditie - testeaza variabila de control a ciclului cu o valoare finala ori de cat ciclul se repeat sau o expresie care controleaza ciclul ;
expresia increment - se executa la sfarsitul fiecarui ciclu,adica dupa executarea instructiunii sau secventei de instructiuni.
Exemplu
În exemplul de mai jos, bucla for este utilizata pentru a afisa numerele de la 1 la 100.
#include<stdio.h>
void main ()
{
int m;
for(m=1; m<=100; m++)
printf("%d", m);
}
Explicatie :
Variabila m este utilizata pe post de "contor" al instructiunii for, numarand la a cata iteratie s-a ajuns. Executia instructiunii for se incheie atunci cand numarul de iteratii devine egal cu 100.
Initializarea lui m cu 1 se realizeaza o singura data si anume la inceput;
m<=100 este conditia de continuare a executiei;
m++ se efectueaza dupa fiecare executie a ciclului (postincrementare).
În cazul instructiunii for, oricare dintre cele trei expresii poate lipsi. Lipsa expresiei conditionale este echivalenta cu o bucla infinita, cum ar fi:
for ( ; ; ) { // instrucţiunile de aici sunt intr-o bucla infinita //}Important !!!!
Instructiunea for nu prezinta limite in ceea ce priveste expresiile (initializare,conditie,increment) care apar in interiorul sau. De exemplu ,nu trebuie neaparat sa se foloseasca expresia initializare pentru a initialize variabila de control a ciclului. In locul expresiei conditie se pot folosi si alte mijloace pentru a opri un ciclu. Nu este neaparat nevoie sa se incrementeze sau sa se decrementeze o variabila.
RETINETI !!!!!
O bucla este o structura de control care provoaca executarea unei instructiuni sau a unei secvente in mod repetat,pana cand sunt indeplinite una sau mai multe conditii.
Iterarea - de fiecare data cand se executa corpul buclei spunem ca facem cate o trecere prin bucla,aceasta trecere se numeste iteratie.
5.3 Instrucțiunea WHILE ( cu numar necunoscut de pași)
Forma generală a instrucțiunii while este următoarea :
while ( expresie) instrucțiune ;
unde : expresie - poate fi orice expresie valida in C. Valoarea acestei expresii se considera adevarata daca este diferita de zero si falsa daca este zero ;
instructiune - reprezinta corpul buclei si poate fi o instructiune oarecare sau un bloc de instructiuni.
Important !
Ciclul buclei se reia atata timp cat expresia este adevarata. Cande expresia devine falsa controlul programului se da urmatoarei instructiuni din program. Valoarea expresiei este evaluata de la inceputul ciclului astfel daca valoarea expresiei este falsa de la inceput,ciclul nu se va executa niciodata
Exemplu
Sa luam ca exemplu o aplicatie pentru un aparat care saluta oamenii. Aparatul este montat in fata unui stadion de fotbal si are rolul sa salute oamenii care intra pe stadion sa urmareasca meciul,iar in functie de cate bilete a vandut.
int bilete ;
int persone=0;
printf("Introduceti nr de bilete vandute:");
scanf("%d",&bilete);
while( personae ! = bilete) {
printf("Salut esti a %d a persoana care a venit la meci:")
}
Acum sa explicam fiecare linie in parte :
- while( persone ! = bilete) - conditia este cat timp numarul de personae nu este egal cu bilete sa se execute ce se afla intre acolade si anume printf("Salut esti a %d a persoana care a venit la meci ;
- specificatorul de format %d va fi incrementat la fiecare pas cu cate o unitate ;
- while( persone ! = bilete) sa presupunem ca am introdus de la tastatura 5 bilete vandute ,iar personae 0 ;
- se verifica apoi daca 0 este diferit de 5 ,este adevarat
si se executa instructiunea
printf("Salut esti a %d a persoana care a venit la meci:"),persoana este 0, apoi se incrementeaza si persoana ajunge la 1; - se verifica apoi daca 1este diferit de 5 ,este adevarat si
se executa instructiunea
printf("Salut esti a %d a persoana care a venit la meci:"),persoana este 1, apoi se incrementeaza si persoana ajunge la 2 ; - si continuam asa pana ajungem la 5 ,si expresia devine falsa ,si se va iesi in afara buclei.
5.4 Instructiunea DO-WHILE
Am vorbit despre instructiunile for si while si am invatat ca cele doua instructiuni fac parte din categoria instructiunilor cu test initial,insa instructiunea do while este singura care face parte din categoria instructiunilor cu test final.
Forma generala acesti instructiuni este :
do { // instructiune} while (expresie);Ciclul do-while repeta instructiunea sau secventa de instructiuni din corpul ciclului atata timp cat expresia este adevarata. Deci iesirea din bucla se face cand expresia devine falsa.
5.5 Instructiuni de salt
Este important sa stiti ca instructiunile de salt din C sunt : break , got, continue si return .
Dintre acestea instructiunile break si continue pot fi folosite in instructiunile de ciclare, instructiunea break se foloseste si in instructiunea switch ,pe cand instructiunile goto si return pot sa apara oriunde in program
5.5.1 Instructiunea break
Break pe langa utilizarea descrisa la instrucţiunea switch, poate fi folosita pentru a ieşi forţat dintr-o instrucţiune de repetiţie.
Exemplu
for( ; ; ) {
i++;
if (i > 10) {
break; // ieşire forţată din bucla //
}
printf( "i=%d\n", i );
}
5.5.2 Instructiunea continue
Continue forţeaza terminarea iteratiei curente a buclei si trecerea la iteraţia următoare. În cazul instrucţiunii for, acest lucru presupune executarea instrucţiunii de incrementare,apoi se evalueaza condiţia de continuare a buclei.
În exemplul de mai jos putem observa implementarea unei bucle infinite cu ajutorul instrucţiunii continue:
if (i == 0) {
continue;
}
i++;
}
5.5.3 Instructiunea return
Return este instrucţiunea de terminare a funcţiei curente. Aceasta poate fi apelată in forma return.
5.5.4 Instructiunea goto
Goto este o instrucţiune de salt a execuţiei. Instrucţiunea primeşte ca parametru o etichetă, următoarea instrucţiune executată după goto este cea de la eticheta dată.
6. FUNCȚII ÎN C

Salutare ! Astazi vom discuta despre functii in C, ma voi axa mai mult pe partea aplicativa ,este important sa intelegeti cum functioneaza deoarece functiile au un rol important in programare.
Să trecem la treabă :
Ce trebuie sa intelegeti in primul rand cand vine vorba despre o functie in limbajul C :
In primul rand funcţiile împart taskuri complexe în bucăţi mici mai uşor de înţeles şi de programat.
De asemenea, funcţiile sunt utile pentru a ascunde detalii de funcţionare ale anumitor părţi ale programului, ajutând la modul de lucru al acestuia. Utilizând funcţii, care reprezintă unitatea fundamentală de execuţie a programelor C, se obţine o divizare logică a programelor mari şi complexe.
Utilizarea unei functii presupune doua elemente distincte :
- definirea functiei ;
- apelul functiei ;
Definirea unei functii reprezinta precizarea tipului returnat de functia respectiva, a argumentelor functiei si a tipurilor acestora si scriera corpului functiei (instructiunile care vor fi executate cand va fi apelata functia)
Forma generala a unei functii este urmatoarea :
tip_returnat nume_functie ( tip_date1 arg1, tip_date2 arg2, .......,tip_dateN argN);
{
corpul_ functiei;
}
Un mic exemplu ca sa intelegeti,si anume vom creea o functie care returneaza maximul dintre doua valori (ambele de tip int):
int Max(int a, int b)
{
if (a >= b)
return a;
else
return b;
}
Asa dupa cum stiti si v-am explicat si la capitolul legat de instructiuni , instructiunea return opreste executia functiei in acel punct, asa ca functia Max as putea sa o scriu si asa:
{
if (a >= b)
return a;
return b;
}
In programare, functiile pot sa nu returneze valori, ci sa fie doar subprograme al caror scop e sa execute o serie de instructiuni, urmarindu-se nu calcularea unei valori, ci obtinerea efectului executarii acelei secvente de instructiuni.
Apelarea unei functii consta in folosirea propriu-zisa a functiei,intr-o alta functie.
Apelarea se poate face atat in functia principala (main) cat si intr-o alta functie.
Inainte de a fi folosita, o functie poate fi declarata.
Declararea (precizarea prototipului) unei functii este necesara doar daca functia respectiva este declarata ( in program) dupa ce a fost declarata functia main () sau daca se folosesteo functie intr-o alta functie , inainte ca prima sa fie declarata.
Important !!!
Daca functia nu returneaza nici o valoare, atunci se considera ca returneaza VOID ( tipul void)
Exemplu
Un mic exemplu folosind functiile in C , pe care il voi comenta linie cu linie ca sa intelegeti ( sfatul meu este sa rulati toate aceste exemple sa va invatati cu codul)
Vreau sa calculez suma a doua numere . Ce trebuie sa fac ?
In primul rand voi declara o functie care implementeaza calculul sumei a doua numere
void calculeaza_suma ( int nr1,int nr2)
{
printf( '' Suma celor doua numere :%d",nr1+nr2) ;
// in mesajul de mai sus se afiseaza direct suma numerelor pe consola//
o alta varianta este :
int suma;
suma=nr1+nr2
printf(" Suma celor doua numere :%d",suma);
}
void main()
{
int nr1; // am declarat primul numar //
int nr2; // am declarat al doilea numar//
printf("Introduceti primul nr :%d") ;
scanf("%d",&nr1); // se citeste primul numar in variabila nr1//
// apoi dam un alt mesaj pentru a citi al doilea numar//
printf("Introduceti al doilea nr :%d") ;
scanf("%d",&nr2); // se citeste al doilea numar in variabila nr1//
// apoi apelam fuctia care calculeaza suma numerelor si afiseaza suma pe consola
calculeaza_suma(nr1,nr2);
Un alt aspect important despre care o sa vorbim este despre transmiterea parametrilor.
De retinut este faptul ca apelul unei functii se face specificand parametrii care se transmit acesteia. În limbajul C, există doua moduri de transmitere a parametrilor. Poate ca unii nu aveti incă cunoștințele necesare pentru a înțelege ambele moduri, insa la acest capitol o sa va prezint doar unul, urmand ca la pointeri sa va prezint si celalalt mod.
Transmiterea parametrilor prin valoare
Important !
Funcţia va lucra cu o copie a variabilei pe care a primit-o şi orice modificare din cadrul funcţiei va opera asupra aceste copii. La sfarşitul execuţiei funcţiei, copia va fi distrusă şi astfel se va pierde orice modificare efectuată.
Pentru a nu pierde modificările făcute se foloseşte instrucţiunea return, care poate întoarce, la terminarea funcţiei, noua valoare a variabilei. Problema apare în cazul în care funcţia modifică mai multe variabile şi se doreşte ca rezultatul lor să fie disponibil şi la terminarea execuţiei funcţiei.
Exemplu de transmitere a parametrilor prin valoare:
Acum este timpul sa punem in practica ce am invatat la functii ,o sa va prezint cateva exemple explicate de catre mine ,si sfatul meu este sa le rulati si voi.
Aplicatia 1
extern int sum(int x, int y, int z)
{
return (x + y + z);
}
Explicatie
Cand se pune extern in fata unei functii inseamna ca poate fi folosita si in afara fisierului .h/.c in care a fost declarata .
Aplicatia 2
static int sum(int x, int y, int z)
{
return (x + y + z);
}
Explicatie
Cand se pune static in fata unei functii inseamna ca acea functie poate fi folosita DOAR in fisierele .h/.c in care a fost declarata, cu precizarea ca fisierele .h/.c trebuie sa aiba acelasi nume.
Aplicatia 3
#include<stdio.h>
int main ()
{
int j = 1;
while(j <= 300)
{
printf("%c %d\n", j, j);
j++;
}
return 0;
}
Explicatie
Programul va afisa toate caracterele cu codurile de la 1 la 300 si numerele de la 1 la 300.
Valoarea lui j va incapea mereu intr-un short int de 5 bytes, asa ca la afisare in functia printf nu vor exista probleme atunci cand se face conversia la char cu %c.
POINTERI
In continuare o sa vorbim despre pointeri (capitolul meu preferat) si la ce sunt buni pointerii.
Un pointer este o variabilă care reţine o adresă de memorie.
În C, un pointer poate reprezenta:
- adresa unor date de un anumit tip;
- adresa unei funcții ;
- adresa unei adrese de memorie;
- adresa unei zone cu conţinut necunoscut (pointer către void)
De retinut !
Dimensiunea unui pointer depinde de arhitectura și sistemul de operare pe care a fost compilat programul. Dimensiunea unui pointer se determină cu sizeof(void *) .
Operatorul de referențiere
& - apare în fața variabilei asupra căreia acționează
Este aplicat unei variabile, avand ORICE tip de date, și obține ADRESA de (din) memorie a variabilei respective.
Operatorul de dereferențiere
* - apare în fața variabilei asupra căreia acționează
Este aplicat unei variabile de tip pointer și obține valoarea stocată la adresa respectivă (indicata de pointer).
De retinut !!!!!
Declararea unui pointer nu înseamnă alocarea unei zone de memorie în care pot fi stocate date. Un pointer este tot un tip de date, a cărui valoare este un număr ce reprezintă o adresă de memorie. Pentru ca dereferențierea să aiba loc cu succes, pointer-ul trebuie să indice o adresă de memorie validă, la care programul are acces.
Exemple folosind pointeri
Transferul se va face intre adrese ale locatiilor de memorie, cu retinere simultana si a valorilor retinute la acestea.
#include<stdio.h>
void main()
{
int a=1; // a este o locatie de memorie pentru elemente de tip int pe doi octeti, pe care am initializat-o cu valoarea 1 //
int* b; //b este o variabila pointer catre tipul int, adica capabil a retine adrese ale altor variabile de tipul specificat //
b=&a; // in adresa b retinem adresa locatiei de memorie (adica a variabilei) a, deci b devine adresa locatiei de memorie a //
Transferul se va face doar intre valori retinute la anumite locatii de memorie.
#include<stdio.h>
void main()
{
int a=1; //a este o locatie de memorie pentru elemente de tip int pe doi octeti,pe care am initializat-o cu valoarea 1 //
int *b; // b este o variabila pointer catre tipul int, adica capabila a retine adrese ale
altor variabile de tipul specificat //
b=&x; // in adresa b retinem adresa locatiei de memorie (adica a variabilei) x,deci b devine adresa locatiei de memorie x //
*b = a; // transferul se face intre valori,valorii retinute la adresa b ii atribuim valoarea retinuta in a //
Un pointer către void nu poate fi folosit direct în operaţii cu pointeri, ci trebuie convertit mai întâi la un pointer către un tip de date.
Exemple folosind interschimbarea de valori
Aplicatia 1
// functia f1 nu va face nicio modificare asupra valorilor lui a si b deoarece a si b nu sunt utilizati ca parametrii sub forma de pointeri//
void f1 (int a, int b)
{
int c;
c=a; a=b; b=c;
}
// funcia f2 va interschimba valorile la care pointeaza a si b, deoarece//
// a si b sunt pointeri (adrese) catre zona de memorie unde sunt stocate//
// valorile numerelor. Astfel, modificarile din functie NU SE PIERD! //
void f2 (int *a, int *b)
{
int c;
c=*a; *a=*b;*b=c;
}
int main()
{
int a=4, b=5, c=6;
// Nu modifica a si b//
f1(a, b);
// Interschimba valorile lui b si c. Vom avea b = 6, c = 5//
f2(&b, &c);
// Afiseaza -5, deoarece a = 4, b = 6, c = 5 //
printf ("%d", c-a-b);
return 0;
}
Aplicatia 2
int swap2(int a, int b)
{
int temp;
temp=a;
b=a;
a=temp;
return 0;
}
Explicatie
Functia swap2 nu va avea niciun efect asupra valorilor lui a si b, deoarece a si b sunt transmise catre functie prin valoarea si nu ca pointer. Din aceasta cauza, modificarile efectuate asupra lui a si b in interiorul functiei se pierd in momentul in care se iese din functie, iar a si b revin la valorile pe care le aveau inainte sa fie apelata functia.
Pe scurt, apelul functiei swap2 NU ARE NICIUN EFECT.
Aplicatia 3
#include<stdio.h>
int*f();
int main()
{
int *p=f();
printf("%d",*p);
}
int *f();
{
int* j=(int*)malloc(sizeof(int));
*j=10;
return j;
}
Explicatie
Programul afiseaza 10, deoarece in functia f este alocata memorie dinamica, prin intermediul unui pointer, pentru variabila j. Asadar, valoarea retinuta in variabila j se transmite prin apelarea functiei catre pointerul p, care este afisat.
Funcții și Pointeri în C
7. Structuri și uniuni.
8. Tablouri Unidimensionale
8.1 Matrici în C
Prezentarea subiectelor pe care le voi aborda în ceea ce privește partea de microcontrollere.
programarewithsorin
Va salut. Sper,să vă fie de folos noțiunile de programare în C pe care le-am postat aici. Momentan este suficient ca un începător să parcurgă tot ce am postat. Sfatul meu este sa rulați toate exemplele pe care le-am explicat. La început aveți și niste link-uri cu site-uri pentru teste online,unde vă puteți verifica cunoștiințele legate de C. În următoarea perioadă o să încep să vă prezint noțiuni legate despre MICROCONTROLLERE,proiecte tehnice cu microcontrollere realizate de către mine , apoi o să prezint niște noțiuni de bază despre electronică, urmând să intru și în partea de automotive ( Adaptive Cruise Control, ECU, ABS, Autosar)
Mai jos o să postez subiectele pe care le voi aborda eu în ceea ce privește partea de microcontrolere :
- 1. Prezentarea generală a microcontrollere-lor;
- 2. În proiectele tehnice pe care le-am realizat eu am folosit ATmega324 si ATmega8, despre aceste microcontrollere o să si vorbesc;
- 3. Programarea microcontroller-ului;
- 4. Porturi;
- 5. Datasheet-ul;
- 6. Interfața serială;
- 7. Memorii ;
- 8. Întreruperi,Timere ;
- 9. PWM,CAN,ADC;
Dacă aveți intrebări mă puteți contacta la :
- adresa de e-mail : sorin.automatica@gmail.com ;
- facebook : https://www.facebook.com/sorellmms ;
- Linkedin : https://www.linkedin.com/in/sorin-m-7a625192/;
9. Noțiuni despre microcontrollere

1· PREZENTAREA GENERALĂ A MICROCONTROLLERE-LOR DIN FAMILIA AVR.
Microcontrollerele din familia AVR prezinta o organizare de tipul RISC executand o instuctiune / ciclu masina. Prezenta unor blocuri interne ca : oscilator intern , timere ,unitate UART , SPI , rezistoare pull-up , PWM ( pulse widith modulation) , ADC ,comparatoare, determina utilizarea acestor microcontrollere intr-o gama foarte larga de aplicatii.
Instructiunile acestei familii de microcontrollere au fost proiectate pentru a reduce dimensiunea unui program scris in limbaj C sau in limbaj de asamblare.
Posibilitatea programarii memoriei FLASH si a memoriei EEPROM, determina ca aceste microcontrollere sa aiba o larga utilizare datorata costului mic de dezvoltare a unei aplicatii (timpul de proiectare scurt).
O alta calitate remarcabila a acestor microcontrollere este consumul foarte mic de energie. Domeniul tensiunilor de alimentare este cuprins intre 1.8 si 5V.Prezinta 6 moduri diferite de stand-by ceea ce ne asigura ca aceste microcontrollere nu vor consuma energie decat atunci cand este nevoie.
Controlul software al frecventei garanteaza o viteza maxima de executie atunci cand este nevoie , iar in restul timpului microcontrolerul poate trece in stand-by unde consumul de energie este minim.
Utilizarea acestor microcontrollere poate reduce semnificativ timpul de dezvoltare a unei aplicatii datorita prezentei pe acestea a unui bloc de depanare in timp real , circuitul aflandu-se chiar pe placa ce reprezinta aplicatia. Se pot face in timp real operatii de "watch" asupra unor registri , operatii de rulare pas cu pas , operatii de oprire in breakpoint.
Sunt produse microcontrollere de uz general precum si microcontrollere cu functii specializate.
Printre cele mai utilizate microcontrollere de uz general :
ATMEGA 8 , ATMEGA 16 , ATMEGA 128, ATMEGA 162,
ATMEGA 324, etc.
Ce este un microcontroller?
Un microcontroller(µC) este o componenta electronica care integrează un microprocesor şi dispozitive periferice, punandu-se accent pe un cost de productie mic si un consum energetic redus, altfel spus pe optimizarea aplicatei. Principala diferenţa dintre un microcontroller (µC) si un microprocesor (µP) o constituie faptul ca un µC integrează atât unitatea de procesare cat si memorie de program, memorie de date, interfeţe de intrare-ieşire și periferice.


Figura 1.. Tipuri de microcontrollere
MPORTANT !!!!!!!!
Cele mai intalnite structuri din circuitul integrat al unui µC sunt următoarele:
- Unitatea centrală de procesare cu o arhitectură care poate fi pe 8, 16, 32 sau 64 de biţi ;
- Memorie de date volatila (RAM) și/sau non-volatila (Flash sau EEPROM) ;
- Memorie de program non-volatila (Flash sau EEPROM) ;
- Porturi digitale de intrare-ieşire de uz general ;
- Interfeţe seriale de comunicație (USART, SPI, I2C, PCM, USB, SDIO etc.) ;
- Timere (cu rol intern, sau utilizate în generarea semnalelor periodice -ex: PWM-, sau ca watchdog) ;
- Convertoare analog-digitale și digital-analogice, front-end-uri analogice și alte circuite dedicate semnalelor analogice
- Sursă de tensiune integrată ;
- Interfață pentru programare şi debugging ;

Figura 2. Schema bloc a unui microcontroller
Deoarece un microcontroller (µC)este un caz particular de calculator, (calculator specializat pentru operaţii I/O, realizat pe un singur chip), acesta este compus din cele cinci elemente de bază: unitate de intrare, unitate de memorie, unitate aritmetică şi logică, unitate de control şi unitate
de ieşire.
Unitatea de control împreună cu unitatea aritmetică şi logică compun împreună unitatea centrală de prelucrare pe care o vom referi în continuare prescurtat cu UCP.
Unităţile de intrare şi ieşire vor fi tratate împreună şi vor fi referite prescurtat ca sistem I/O.
Blocurile componente ale µC sunt legate între ele printr-o magistrală internă (bus).
Magistrala vehiculează semnale de adresă, de date şi semnale de control. Mărimea acestor magistrale constituie una dintre caracteristicile cele mai importante ale unui µC. Prin magistrala de adrese unitatea centrală de prelucrare (UCP) selectează o locaţie de memorie sau un dispozitiv I/O, iar pe magistrala de date se face schimbul de informaţie între UCP şi memorie sau dispozitivele I/O.
Între UCP şi memorie sunt transferate atât date cât şi instrucţiuni. Acestea se pot
transfera pe o singură magistrală de date sau pe magistrale de date diferite.
Arhitectura von Neumann prevede existenţa unui bus unic folosit pentru circulaţia datelor şi a instrucţiunilor. Când un controller cu o astfel de arhitectură adresează memoria, bus-ul de date este folosit pentru a transfera întai codul instrucţiunii, apoi pentru a transfera date.
Arhitectura Harvard prevede un bus separat pentru date şi instrucţiuni. Când codul instrucţiunii se află pe bus-ul de instrucţiuni, pe bus-ul de date se află datele instrucţiunii anterioare. Structura MC este mai complexă, dar performanţele de viteză sunt mai bune.
Unitatea centrală de prelucrare este compusă din unitatea aritmetică şi logică (UAL) şi din unitatea de control.
Unitatea aritmetică şi logică este secţiunea responsabilă cu efectuarea operaţiilor aritmetice şi logice asupra operanzilor ce îi sunt furnizaţi.
Unitatea ce control este responsabilă cu decodificarea codului operaţiei conţinut de codul unei instrucţiuni. Pe baza decodificării unitatea de control elaborează semnale pentru comanda celorlalte blocuri funcţionale pentru a finaliza executarea unei instrucţiuni. Modul de implementare al acestui bloc este de asemenea transparent utilizatorului.
Unitatea centrală de prelucrare conţine un set de registre interne, similare unor locaţii de memorie, folosite pentru memorarea unor date des apelate sau pentru programarea unor anumite funcţii.
2. Arhitectura microcontroller-ului ATmega8
În continuare voi discuta despre microcontroller-ul ATmega8 ,pe care l-am uitilizat cel mai des la realizarea proiectelor mele tehnice.
Caracteristici principale ale microcontroller-ului Atmega8 :
- Arhitectură RISC avansată;
- Memorie program nevolatilă şi memorie de date ;
- Periferice :
• 1 Timer/Counter de 16 biţi cu moduri separate de prescalare, comparaţie şi captură;
• Real Time Counter cu oscilator separat;
• 3 canale PWM;
• ADC;
• Interfaţă serială pe 2 fire de tip Byte-oriented;
• USART;
• Interfaţă serială SPI de tip Master/Slave;
• Watchdog Timer programabil cu oscilator încorporat;
• Comparator analogic.


Figura 3. Microcontroller ATmega8
3. Porturi
Vă salut ! În acest capitol o sa vorbesc despre porturi.
Trebuie să știți că porturile unui microcontroler sunt cele mai folosite dispozitive de schimb de date cu exteriorul.Avantajul cel mare este că ele sunt paralele, putându-se astfel realiza schimbul a mai multor biţi de date simultan.Pentru un programator porturile nu sunt altceva decât niste regiştrii prin care se pot scrie şi citi date.Lungimea unui port este influenţată de numărul de biţi pe care procesorul îi poate executa la un moment dat.În cazul nostru, microcontrolerul ATMega8 (despre acest microcontroler o să vorbesc ) este pe 8 biţi deci şi dimensiunea regiştrilor va fi tot de 8 biţi.De obicei unui port sunt asociaţi doi regiştrii, unul care setează direcţia pinilor iar altul care realizează schimbul de date între unitatea centrală a microcontrolerului şi exterior.
Microcontrolerului ATMega8 are asociat unui port 3 regiştrii,DDR care setează direcţia pinilor,PORT care setează datele de ieşire ale portului la nivel de pin şi PIN care memorează intrările microcontrolerului la nivel de pin.Numărul porturilor este tot 3 şi sunt numite B,C şi D.Dacă priviți pe schema generală avem următoarea configuraţie:

Pinii PB0-PB7 corespund portului B,PC0-P6 portului C iar PD0-PD7 portului D.Observăm că portul C are doar 7 intrări.Ei bine ultimul bit din regiştrii asociaţi portului nu este flosit.Pentru a vă face o imagine mai bună asupra legăturii dintre pinii microcontrolerului şi regiştri asociaţi portului urmăriţi figura de mai jos :
În imaginea de mai sus puteți observa că pinului 0 îi corespunde locaţiei 0 a portului, pinului 1 locaţiei 1 şi tot aşa.În cazul de faţă pinii sunt notaţi cu P, pentru portul B avem PB , pentru portul C avem PC iar pentru portul D avem PD.
Pentru a înțelege modul de configurare şi de utilizare a porturilor din ATMega8 voi crea un exempu simplu în care vom avea setaţi primii 4 pini ai portului B ca pini de intrare iar primii 4 pini ai portului C ca pini de ieşire.La pinii de intrare vor fi legate patru butoane iar la pinii de ieşire ai portului C vom lega patru Led-uri.La apăsarea butoanelor vom dori ca led-urile corespunzătoare butoanelor apăsate să se aprindă. Schema electrică în Proteus a aplicaţiei este următoarea:

Programul din memoria microcontrolerului nu va face alceva decât să preia informaţia de la portul B şi să o transmită la portul C. Pentru scrierea programului în CodeVision AVR mergeți la secţiunea de setare și anume la Ports>Port C şi setaţi primii 4 biţi de direcţie ca în figura de mai jos apoi daţi din meniul File, Generate ,Save and exit.

După cum am mai spus, direcţia
pinilor se setează cu ajutorul registrului DDR(Data
Direction Register).Dacă veţi urmări în secţiunea de setare a programului
la începutul funcţiei main veţi observa că
DDRB =0x00,adică toţi pinii sunt setaţi ca intrare, iar DDRC = 0x0F, adică doar primii 4 pini
sunt setaţi ca ieşiri, restul fiind setaţi ca intrări.Reprezentarea valorii 0x0F este o reprezentare în hexazecimal
şi este echivalentă cu 0b00001111 în binar şi cu 15 în zecimal. În bucla while,bucla
de ciclare infinită, vom avea o singură linie care va transfera datele de la
intrarea portului B la ieşirea portului C adică vom transfera conţinutul
registrului PINB în registrul PORTC:
PORTC =
PINB;
Programul este gata putând fi deja compilat.Pentru încărcarea programului în schema din Proteus daţi dublu click pe micorocontroler.Ca rezultat veţi deschide o fereastră cu titlul Edit Component.Mergeţi la câmpul Program File şi adăugaţi fişierul program.Acest fişier se găseşte în locţia în care aţi salvat anterior proiectul din Code Vision AVR, în directorul Exe.Dacă compilarea codului a decurs normal ar trebui să aveţi ca rezultat un fişier cu extensia *.hex
Următoarea aplicaţie este puțin
mai complexă, ea având rolul simulării unei instalaţii luminoase la care vom
avea posibilitatea alegerii unui program de funcţionare prin apăsarea unor
butoane.De data asta vom folosi tot portul B ca port de intrare la care vom
lega la primii 4 pini, 4 butoane cu care vom alege modul de funcţionare iar ca
port de ieşire vom folosi portul D la pinii căruia vom lega 8 Led-uri.Dacă se
apasă primul buton, adică bitul 0 al registrului PINB este 1 logic, vom avea
modul 1 de funcţionare,dacă se apasă butonul 2, adică bitul 1 al registrului
PINB este 1 logic,vom avea modul 2 de funcţionare şi tot aşa până la butonul
4.Dacă nu este apăsat nici un buton se vor activa pe rând toate cele 4
moduri.Dacă se apasă două sau mai multe butoane nu se va întâmpla nimic.
Schema elecrtrică a aplicaţiei în Proteus va fi următoarea:

De data aceasta toţi pinii
portului D vor fii setați ca fiind pini de ieşire.
Pentru o lizibilitate mai buna a codului am ales să am câte o funcţie pentru
fiecare mod de funcţionare,funcţii care sunt plasate înaintea funcţiei
main.Pentru modul 1 codul este următorulul:
void mod1(void)
{
PORTD
= 0b00000001;
while(PORTD)
{
delay_ms(100);
PORTD
= PORTD<<1;
}
}
La început iniţializăm PORTD-ul cu valoarea 0b00000001
deoarece dorim ca primul led să fie aprins.Pentru ca al doilea Led să se
aprindă şi să se stingă primul trebuie să mutăm 1-ul de pe prima poziţie pe
poziţia a doua.Acest lucru se face prin codul PORTD = PORTD<<1 care reprezintă şiftare bitului 1.După
execuţia primului ciclu while noua valoare a PODRTD-ului va fi
0b00000010.Pentru a vă face o idee cum funcţionează funcţia urmăriţi codul de
mai jos:
Iniţializare :PORTD = 0b00000001
Ciclu 1:PORTD = 0b00000010
Ciclu 2:PORTD = 0b00000100
Ciclu 3:PORTD = 0b00001000
Ciclu 4:PORTD = 0b00010000
Ciclu 5:PORTD = 0b00100000
Ciclu 6:PORTD = 0b01000000
Ciclu 7:PORTD = 0b10000000
Ciclu 8:PORTD = 0b00000000
Deoarece la ciclul 8 valoarea PORTD va
fi 0 se va ieşi din ciclu while şi totodată şi din funcţia mod1.Observăm că aici apare o funcţie pe care nu am avut ocazia să o folosesc mai
înainte şi anume delay_ms.Aceasta
are rolul de a întârzia programul cu n milisecunde, în cazul nostru 100 de
milisecunde.Pentru a folosi această funcţie trebuie să includeţi librăria
delay.h.Probabil că vă întrebaţi de ce este nevoie de aşa ceva.Ei bine dacă nu
am avea un timp între schimbările portului am vedea toate Led-urile aprinse
datorită vitezei mari de comutare.Pentru modul 2 de funcţionare codul este asemănător însă operaţia de şiftare se
face de la stânga la dreapta.void mod2(void)
{
PORTD
= 0b10000000;
while(PORTD)
{
delay_ms(100);
PORTD
= PORTD>>1;
}
}În modul 3 vom aprinde mai întâi led-urile din mijloc (pinii 3 şi 4), apoi vom
stinge led-urile din mijloc aprinzând led-urile următoare( pinii 2 şi 5) şi tot
aşa până la aprinderea led-urilor 1 şi 8(pinii 0 şi 7).void mod3(void)
{
PORTD
= 0b00011000;
delay_ms(100);
PORTD
= 0b00100100;
delay_ms(100);
PORTD
= 0b01000010;
delay_ms(100);
PORTD
= 0b10000001;
delay_ms(100);
}Modul 4 se va comporta exact invers modului 3,aprinzând ledurile din exterior
spre interior.void mod4(void)
{
PORTD
= 0b10000001;
delay_ms(100);
PORTD
= 0b01000010;
delay_ms(100);
PORTD
= 0b00100100;
delay_ms(100);
PORTD
= 0b00011000;
delay_ms(100);
}În bucla de ciclare infinită vom face o selecţie a acestor 4 moduri în funcţie
de butoanele pe care le-am apăsat.while(1)
{
switch(PINB)
{
//se apasa primul buton si se seteaza modul 1 de functionare
case 0b00000001:
mod1();
break;
//se apasa al doilea buton si se seteaza modul 2 de
functionare
case 0b00000010:
mod2();
break;
//se apasa al treilea buton si se seteaza modul 3 de functionare
case 0b00000100:
mod3();
break;
//se apasa al patrulea buton si se seteaza modul 4 de
functionare
case 0b00001000:
mod4();
break;
//daca nu s-a apasat nici un buton se vor parcurge toate cele 4
moduri
case 0b00000000:
mod1();
mod2();
mod3();
mod4();
//daca s-a apasat mai multe butoane nici un mod va fi activ
default:
PORTD
= 0x00;
}
};Pentru a vedea ce butoane au fost apăsate trebuie să verificăm pinii
portului B.Această verificare se face prin swhitch care verifică valorile
registrului PINB.Dacă s-a apăsat butonul 1 vom avea valoarea 0b00000001 şi se
execută funcţia mod1,dacă s-a apăsat butonul 2 vom avea valoarea 0b00000010 şi
se execută funcţia mod2, şi tot aşa până la butonul 4.Dacă nu s-a apăsat nici
un buton ,adică PINB = 0b00000000 atunci se vor executa pe rând cele 4
funcţii,iar dacă avem altă combinaţie a registrului PINB valoarea lui PORTD va
fi 0.
4. Prezentare generală USART
Despre USART (Universal synchronous/asynchronous receiver/transmitter) trebuie să știți că este un standard de comunicare serială între diverse dispozitive cum ar fi comunicarea între calculator (prin portul serial COM) şi alte dispozitive.Acesta se poate folosi pentru comunicare în conjuncţe cu standardele RS-232,RS-422 sau RS-485 însă în exemplele prezentate voi folosi doar standardul RS-232 pentru comunicarea cu PC-ul.Comunicarea serială presupune folosirea unei singure legături dacă ea este unidirecţională, adică există un trasmiţător şi un receptor sau invers.Dacă este bidirecţională este nevoie de două legături,pe o legătură realizându-se transmisia iar pe alta recepţia.Dacă se foloseşte modul sincron pe lângă legăturile de transmisie a datelor se mai foloseşte o legătură prin care se stabileşte acelaş semnal de tact între dispozitive.Schema de principiu este următoarea:

Important ! Trebuie să rețineți că dispozitivul care transmite date se numeşte master iar cel care primeşte se numeşte slave.De obicei masterul stabileşte formatul cuvântului şi frecvenţa ceasului iar datele furnizate se vor obţine la pinul TX.Slave-ul va trebui configurat astfel încât să primească cuvinte care au acelaş format cu cele generate de master şi să funcţioneze la aceeaşi frecvenţă.Pinul prin care un dispozitiv primeşte date este pinul RX.Viteza de transmisie a datelor se măsoară in BAUD , unitate care reprezintă numărul de biţi transmişi într-o secundă.Această viteză trebuie să fie egală între dispozitivele care comunică serial.La transmisia asincronă pot apărea diverse erori deoarece frecvenţele de tact pot fi puţin diferite chiar dacă ele sunt setate aparent egal.Acest neajuns este înlăturat la transmisia sincronă deoarece semnalul de tact generat de master este transmis slave-ului prin legătura XCK.Momentan ne vom concentra pe transmisia asincronă deoarece ea este cea mai folosită la comunicarea între dispozitivele periferice şi PC,având doar două legături.
Transmisia asincronă
După cum am văzut mai sus pentru ca această transmisie să fie bidirecţională trebuie să folosim două conexiuni, una prin care trimitem şi una prin care primim datele. Cuvântul care este trimis este format din două părţi , o parte care reprezintă datele iar altă parte care reprezintă codul de verificare:

Orce cuvânt începe cu un bit de start care trebuie să fie 0.Următorii biţi sunt biţii de date care pot avea o lungime între 5 şi 8 biţi iar la sfârşit avem un bit de stop care întodeauna trebuie să fie 1.Microcontrolerul ATMega8 poate trimite următorul format de cuvânt:

Primul bit St reprezintă bitul de start.Lungimea cuvântului de date poate fi cuprinsă între 5 şi 9 biţi.Pentru o mai bună securitate în transmisie se poate seta la sfârşitul cuvântului de date un bit de paritate care verifică paritatea biţilor de date.Sfârşitul cuvântului poate fi reprezentat de unul sau doi biţi care trebuie să aibă valoarea 1.Transmiţătorul sau receptorul sunt libere doar atunci când s-au transmis biţii de stop şi sunt în stare Idle, stare care este reprezentată de 1 logic pe linile de conexiune.La configurarea a două dispozitive care vor comunica serial asicron trebuie să avem grijă ca viteza de transfer să fie egală la transmisie şi recepţie şi să avem aceeaşi lungime a cuvântului de date.De asemenea trebuie totuşi să stabilim dacă folosim verificarea parităţii şi dacă folosim unul sau doi biţi de stop.
Principala sursă de eroare poate apărea atunci când configurarea între dispozitive nu este făcută corect, aceasta fiind o problemă de incompatibilitate în comunicare.Alte erori pot fi generate pe linile de transmisie, ele putând fi detectate prin bitul de paritate sau prin biţii de stop.La transmiterea sau recepţia a mai multor cuvinte consecutive trebuie verificat dacă USART-ul a realizat operaţia completă de trimitere sau citire, altfel poate apărea o altă eroare de transmisie generată de data asta de partea soft.Schimbul de date se face prin doi regiştrii, unul în care se înscriu datele şi unul de şiftare prin care se transmite cuvântul.În ATMega8 datele se scriu sau se citesc din registrul UDR.De exemplu pentru transmiterea unui caracter char pe 8 biţi trebuie să scriem caracterul în UDR.USART-ul va trece automat conţinutul lui UDR în registrul serial apoi îl va transmite pe linia de legătură celuilalt dispozitiv care va prelua datele tot în registrul serial al cărui conţinut va fi transmis registrului UDR.Registrul serial,dacă dispozitivul este configurat ca transmiţător, va pune pe lângă date şi bitul de start , bitul de paritate şi bitul/biţii de final.Dacă dispozitivul este configurat ca receptor, registrul serial va verifica dacă transmisia este corectă şi dacă da datele cuvântului vor fi copiate în UDR.
Regiştrii folosiţi în ATMega8 pentru comunicarea serială sunt următorii:
-Registrul UDR care are rolul de a stoca datele necesare pentru transmisie sau recepţie:

Dacă se realizează transmisia, datele sunt stocate în TXB iar dacă se realizează recepţia datele se găsesc în RXB.
-Registrul UCSRA:

Bitul 7 -RXC- indică starea registrului de recepţiei.Dacă încă se mai primesc date valoarea lui este 1 iar dacă recepţia a fost efectuată complet valoarea lui este 0.
Bitul 6 -TXC- indică starea registrului de transmisiei.Dacă încă se mai transmit date valoarea lui este 1 iar dacă transmisia a fost efectuată complet valoarea lui este 0.
Bitul 5 - UDRE - indică dacă UDR poate fi citit sau scris.Dacă este 1 UDR poate fi scris, dacă este 0 nu se pot efectua operaţii cu acest registru.
Aceşti 3 biţi pot genera întreruperi care pot fi tratate în rutine speciale .
Bitul 4 - FE - Detectează dacă există erori la transmiterea cuvântului verificând bitul/biţii de final.Dacă acest/aceşti biţi au o valoare diferită de 1 atunci bitul FF ia valoarea 1.Dacă totul este în regulă atunci valoarea lui FE va fi 0.
Bitul 3 -DOR - Detectează dacă există erori de suprascrierea USAR-tului.De exemplu la recepţie poate apărea o astfel de eroare dacă UDR şi registrul serial este plin şi alt caracter este pe cale de a fi primit.FE este 1 dacă s-a semnalat o astfel de eroare şi 0 dacă nu avem o eroare de acest fel.
Bitul 2 - PE - are rolul de a detecta eroarea de paritate.Dacă s-a detectat o astfel de eroare ia valoarea 1 iar dacă nu valoarea lui va fi 0.
Bitul 1 - U2X - Acest bit este 1 dacă dorim să dublăm viteza de transmisie în modul asicron şi 0 dacă se foloseşte viteza normală.Acest bit trebuie să aibă întotdeauna valoarea 0 dacă este folosit modul sincron.
Bitul 0 - U2X- Se setează 1 dacă se foloseşte comunicarea multiprocesor şi 0 dacă se foloseşte comunicarea uniprocesor.In exemplele ce vor urma îl vom seta ca 0.
-Registrul UCSRB

Bitul 7-RXCIE - 1 setează generarea unei întreruperi la terminarea recepţiei,0 întreruperea nu este flosită.
Bitul 6-RXCIE - 1 setează generarea unei întreruperi la terminarea transmisiei,0 întreruperea nu este flosită.
Bitul 5-RXCIE - 1 setează generarea unei întreruperi atunci când registrul UDRIE poate fi folosit,0 întreruperea nu este folosită.
Bitul 4- RXEN - 1 setează USART-ul ca receptor.
Bitul 3- TXEN - 1 setează USART-ul ca Transmiţător.
Bitul 2- UCSZ2 - se foloseşte împreună cu o parte din biţii registrului UCSRC după cum vom vedea mai jos.
Bitul 1 - RXB8 - este al 9-lea bit din schimbul de date pe 9 biţi şi se foloseşte la recepţie.
Bitul 0 - TXB8 - este al 9-lea bit din schimbul de date pe 9 biţi şi se foloseşte la transmisie.
-Registrul UCSRC:

Bitul 7 -URSEL- se foloseşte pentru selecţia regiştrilor astfel:1 dacă se lucrează cu registrul UCSRC şi 0 dacă se lucrează cu registrul UBBRH.
Bitul 6 -UMSEL-Prin acest bit se setează modul de funcţionare al USART-ului:1 dacă funcţionează în modul sincron şi 0 dacă funcţionează în modul sincron.
Bitul 5 şi 4 -UPM1 şi UPM0 - Se folosesc pentru selecţia parităţii:
UPM1 UPM0
00 - Nu se foloseşte bitul de paritate.
01 - Combinaţie rezervată.
10 - Se foloseşte bitul de paritete ca bit par.
11 - Se foloseşte bitul de paritete ca bit impar.
Bitul 3 -USBS- se foloseşte pentru a seta numărul biţilor care reprezintă sfârşitul cuvântului: 1- se setează doi biţi de stop,0-se setează un singur bit de stop.
Biţii 2 şi 1- UCSZ1 şi UCSZ0- se folosesc împreună cu UCSZ2 din UCSRB pentru a seta lungimea cunvântului de date:
UCSZ2 UCSZ1 UCSZ0 Lungimea Cuvântului
000 - 5 biţi;
001 - 6 biţi;
010 - 7 biţi;
011 - 8 biţi;
100 - Combinaţie rezervată;
101 - Combinaţie rezervată;
110 - Combinaţie rezervată;
111 - 9 biţi;
Bitul 0 - UCPOL-Se foloseşte pentru modul sincron şi setează ce front al semnalului de pe linia XCK va fi folosit în transmisie.În modul asincron acesta se setează 0.Valorile pe care le poate lua sunt următoarele:0 - se foloseşte frontul crescător, 1 se foloseşte frontul descrescător.
-Registrul UBRR - Acest registru se foloseşte pentru a seta viteza de transmisie a datelor.

Bitul 15 - URSEL - trebuie să fie 0 atunci când se efectuează operaţii cu acest registru.
Biţii 12,13,12 sunt nişte biţi rezervaţi nefolosiţi în acest microcontroler.
Biţii 11:0 sunt folosiţi pentru a seta viteza de transmisie a datelor.Totuşi setarea se face doar cu biţii cei mai semnificativi, 11:8.
Viteza de transmisie a datelor se calculează după formula:

Unde BAUD reprezintă viteza de transmisie măsurată în Baud,f_int reprezită frecvenţa internă a procesorului iar UBBR este valoarea setată în registru.