Se afișează postările cu eticheta Aplicaţii. Afișați toate postările
Se afișează postările cu eticheta Aplicaţii. Afișați toate postările

duminică, 28 august 2011

USART

Prezentare generală
USART(Universal synchronous/asynchronous receiver/transmitter) 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 aplicaţiile noastre vom 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ă avem 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:
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.
Eroarea la transmisie se calculează după formula:
În tabelul de mai jos putem urmări ce influenţă are frecvenţa asupra erorii la transmisie:
Următorul exemplu are ca scop schimbul de date între microcontrolerul ATMega8 şi PC.Citirea şi trimiterea de date de la PC la microcontroler o puteţi face cu un program ca HiperTerminal.Datele pe care le va primi microcontrolerul le vom afişa pe un LCD.Transmisia de date se va face asincron ,lungimea cuvântului de date va fi de 8 biţi , vom folosi un singur bit de stop iar viteza de transmisie va fi de 4800 baud.
Programul îl puteţi downloada de la secţiunea Download -USART -Aplicaţie 1.

Înainte de a începe transmisia toate aceste setări vor fi făcute în regiştrii USART-ului după cum urmează:

UCSRA=0x00;
UCSRB=0x98;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x0C;

Schema electrică a aplicaţiei este următoarea:
Pentru a simula PC-ul am folosit o unealtă oferită de Proteus numită Virtual Terminal.Aceasta împreună cu oşciloscopul se găsesc în partea dreaptă a ferestrei la butonul Virtual Instruments Mode.
La secţiunea de generare a codului trebuie să setaţi LCD-ul pe portul D, pinul 0 al portului D ca pin de intrare, pinul 1 al portului D ca ieşire şi USART-ul după cum urmează:
Pentru recepţie am folosit întreruperea deoarece dorim să afişăm caracterul pe LCD imediat ce el a fost primit.
În partea de ciclare infinită a programului vom trimite date PC-ului:

while(1)
{
//trimitem toate caracterele ASCII catre computer
caracter ++;
printf("%c",13);
printf("%c",caracter);
delay_ms(150);
};

Observăm că trimiterea de date se face foarte simplu cu ajutorul funcţiei printf().Afişarea caracterelor pe LCD se face în rutina de tratare a întreruperii generate de recepţia datelor după cum urmează:

nr_caractere++;
lcd_putchar(data);
if(nr_caractere > 15)
{
lcd_clear();
nr_caractere = 0;
}

Afişarea se face pe o singură linie.Dacă s-a ajuns să se scrie 16 caractere pe LCD , adică să se completeze prima linie, LCD-ul este curăţat.Dacă simularea funcţionează cum trebuie ar trebui să vă afişeze cam aşa:
Pentru a trimite date microcontrolerului trebuie să daţi click pe Virtual Terminal şi să tastaţi caractere.
Fizic , pentru a se putea realiza conexiunea este nevoie de un integrat MAX232 care face legătura între microcontroler şi cablu serial astfel:
Momentan cam atât despre USART.

Convertorul Analog-Digital(AD)

Convertorul Analog Digital,după cum îi spune şi numele, are rolul de a converti valorile semnalelor analogice în valori numerice.Semnalele analogice care intră în convertor pot proveni de la diverse dispozitive însă în cele mai multe cazuri acestea provin de la diverşi senzori(senzori de lumina, temperatura,etc.).

Pentru a putea folosi un convertor mai întâi trebuie setat pragul minim şi maxim de tensiune la care el funcţionează.Convertorul microcontrolerului ATMega8 permite mai multe moduri de alegere a referinţei de tensiune cum ar fi o referinţa externă prin pinul AVCC,internă egală cu cu tensiunea de alimentare sau internă egală cu 2,56V.În momentul folosirii ,convertorul trebuie legat şi la masă, acest lucru făcându-se prin pinul AREF.

Acest convertor stochează valoarea tensiunii numeric într-un registru de 10 biţi.Convertorul acestui chip are 6 canale de intrare care se găsesc pe portul C şi sunt notate cu ADC(ADC0-ADC5).La un moment dat se poate citi doar un singur canal.Rezultatatul citirii va fi depus în regiştrii de 8 biţi ADCH şi ADCL.Timpul de citire poate fi şi el prescalat iar numărul de tacte necesare pentru citire este 13.
Selecţia referinţei şi a canalului se face prin registrul ADMUX care are următoarea formă:
Biţii 7 şi 8 (REFS1 şi REFS0) folosesc la selecţia referinţei astfel:
REFS1 REFS0
00- Referinţă externă la pinul AREF;
01- Referinţă internă VCC cu rezistenţă şi condensator la pinul AREF(R=1K
şi C=14pF );
10- Referinţă internă ca la combinaţia 0 1 însă reversată;
11- Referinţă internă de 2.56V cu rezistenţă şi condensator la pinul AREF(R=1K
şi C=14pF );

Biţii 2-0 (MUX3-MUX0) au rolul de a selecta canalul de unde va fi citit semnalul:
MUX3 MUX2 MUX1 MUX0
0000– ADC0 ,pinul PC0
0001– ADC1 ,pinul PC1
0010– ADC2 ,pinul PC2
0011– ADC3 ,pinul PC3
0100– ADC4 ,pinul PC4
1110– 1.3V
1111– GND

Pinul 5,ADLAR are rolul de a selecta modul de scriere a valorii citite în regiştrii ADCL şi ADCH.Aceşti regiştrii sunt de 8 biţi însă rezutatul este pe 10 biţi, astfel se folosesc ambii regiştrii pentru memorare în felul urmăror:
Dacă bitul ADLAR = 0 avem primul mod de reţinere în care primii doi cei mai semnificativi biţi sunt reţinuţi în ADCH iar restul de 8 biţi sunt reţinuţi în ADCL.Dacă ADLAR = 1 atunci primii 8 cei mai semnificativi biţi sunt reţinuţi în ADCH iar restul de 2 biţi sunt reţinuţi în ADCL.Modul doi de reţinere se poate folosi atunci când dorim ca rezultatul să fie returnat pe 8 biţi, caz în care putem citi doar registrul ADCH.

Controlul convertorului se face prin registrul ADCSRA care are următoarea formă:
Cel mai seminiciativ bit,bitul ADEN are rolul de a permite folosirea convertorului astfel dacă ADEN = 1 convertorul poate fi folosit.
Bitul 6,ADSC, este folosit pentru a porni convertorul.Dacă dorim să citim valoarea unei tensiuni la un moment dat se setează bitul ADSC=1.Acest bit rămâne setat până când se realizează conversia, după conversie valoarea lui va deveni 0.

Prin bitul 5,ADRF, se setează modul de citire al tensiunii,astfel dacă ADRF=1 convertorul citeşte repetat valoarea tensiunii iar dacă ADRF = 0 citirea se face doar când ADSC = 1.
Pentru generarea unui semnal de întrerupere în momentul terminării conversiei trebuie ca bitul ADIF să fie 1.Bitul ADIE are valoarea 1 atâta timp cât nu a fost trimis nici un semnal de întrerupere.Dacă s-a trimis o cerere de întrerupere el devine 0 până când se execută rutina de tratare.La terminarea execuţiei rutinei ADIE ia valoarea 1.

Ultimii 3 biţi,ADPS2-ADPS0 au rolul de a prescala timpul de citire a tensiunii astfel:
ADPS2 ADPS1 ADPS0 Prescalarea frecvenţei interne
000 2
001 2
010 4
011 8
100 16
101 32
110 64
111 128

Algoritmul de setare şi citire a convertorului este următorul:
În partea de setare a programului se setează registri ADMUX,setând referinţa,modul de citire şi canalul şi apoi se setează ADCSRA ,registru în care trebuie să avem mare grijă ca cel mai semnificativ bit să fie setat.
În partea de ciclare infinită trebuie să vedem cum am configurat citirea, periodică sau nu.Dacă citirea se face periodic atunci nu ne rămâne decât să citim valoarea convertorului.Dacă nu, citirea se face conform paşilor:
-pornim citirea:ADCSRA =ADCSRA | 0x40;
-aşteptăm să se termine citirea: while(ADCSRA | 0x40 == 1);
-citim valoarea convertorului;

Valoarea returnată va fi un număr cuprins între 0 şi 1024 dacă citirea se face pe 10 biţi sau între 0 şi 256 dacă citirea se face pe 8 biţi.Precizia citirii se calculează în felul următor:

Unde P este precizia şi Vref este tensiunea de referinţă care se poate împărţi la 1024 sau 256 în funcţie de numărul de biţi ai registrului de stocare.
Calcularea tensiunii efective se face după următoarea formulă:
Unde Tens reprezintă tensiunea efetivă,Vcit reprezintă valoarea returnată de convertor iar Vref tensiune de referinţă.

În exemplul următor vom citi valoarea unei tensiuni şi o vom afişa pe un LCD.Schema electrică este următoarea:

Progamul îl puteţi downloada de la secţiunea Download -Convertorul A/D -Aplicaţie 1.
Înainte de scrierea programului , la partea de setarea a generării codului vom avea următoarea setare:
Important aici este să nu uitaţi partea de setarea a LCD-ului pe portul D.
După generare, setările dinaintea părţii de ciclare vor fi următoarele:

ADMUX=0x00;

Adică referinţa va fi externă la pinu AREF,se citesc 10 bţi din partea stângă a registrului ADCH şi se selectează canalul 1.

ADCSRA=0x89;

Adică se permite folosirea convertorului,se activează permisia trimiterii cererii de întreruperea la terminarea citirii şi se prescalează ceasul cu 2.
Codul din bucla infinită este următorul:

ADCSRA |= 0x40; //pornim conversia
while(ADCSRA&0x40); //astempam ca citirea sa se finalizeze
adc_data = (adc_data*5*100)/1023; //calculam valoarea tensiunii
//transformam valoarea obtinuta in sir
valoare[0] = adc_data/100+48;
valoare[1] = '.';
valoare[2] = (adc_data/10)%10+48;
valoare[3] = adc_data%10+48;
valoare[4] = '\0';
sprintf(linie1,"V=%s",valoare); //copiem valoarea in sirul linie1
//afisam rezultatul pe LCD
lcd_clear();
lcd_gotoxy(0,0);
lcd_puts(linie1);
delay_ms(200); //asteptam 2 milisecunde

Aici microcontrolerul începe cu citirea tensiunii apoi se calculează valoarea efectivă a tensiunii.După aceste operaţii se transformă valoarea din long în şir de caractere după care se afişează pe ecran rezultatul.

Aplicaţia fizică o puteţi urmări aici:

Generatorul de semnale PWM

Generalităţi
Semnalele PWM sunt folosite în general pentru a controla viteza unui motor de curent continuu.Aceste motoare permit controlul vitezei prin modificarea tensiunii de alimentare însă viteza se modifică neliniar.Pentru a controla această viteză liniar s-a recurs la metoda de control prin semnale PWM.Semnalele PWM au următoarea formă:
În schema de mai sus observăm că semnalul este de perioadă T.Această perioadă este compusă din timpul în care semnalul este 1 logic(Tplin care se mai numeşte şi factor de umplere) şi timpul în care semnalul este 0 logic(Tgol). De obicei intervalul T este dat de 256 de tacte de ceas astfel TPlin sau TGol putând modifica viteza motorului cu pasul de aproximativ 0.4% la fiecare modificare cu o unitate a registrului de control al factorului de umplere.

Semnale PWM generate de microcontrolerul ATMega8
Microcontrolerul ATMega8 foloseşte pentru generarea semnalelor PWM Timer-ul 1 şi 2 însă momentan ne vom concentra asupra timer-ului 1.

PWM generat de Timer-ul 1
Regiştrii care ne interesează pentru setarea generatorului sunt următorii:
-Reginstrul TCNT1 care se incrementează la apariţia fiecărui impuls de tact, prescalat sau nu,intern sau extern.

-Rregiştrii OCR1A şi OCR1B care contrlează factorul de umplere în felul următor:
Observăm că timpul în care semnalul de ieşire este 1 este dat de registrul OCR1x.Dacă folosim de exemplu numai TCNT1L numărătorul va număra de la 0 la 255 şi dacă dorim ca factorul de umplere să fie 50%, atunci valoarea lui OCR1x va fi 128.În schemă mai observăm că numărarea se face în două sesuri,crescător şi descrescător.Setarea modului PWM se face prin biţii WGM13:0 după cum vom vedea mai jos.

-Registrul TCCR1A:
Biţii 4,5,6 şi 7 au următoarea semnificaţie:

COM1A1/COM1B1 COM1A0/COM1B0
0 0 - Comparatoarele sunt oprite
0 1 - Comparatoarele sunt active însă
depind de biţii WGM13:0
10 - TCNT1<=OCR1x-1 logic la ieşire,TCNT1>OCR1x -0
logic la ieşire;
11 - TCNT1<=OCR1x-0 logic la ieşire,TCNT1>OCR1x -1
logic la ieşire;

Biţii FOC1A, FOC1B sunt folosiţi pentru modul rapid aşa că îi vom seta 0.

-Registrul TCCR1B:
Biţii ICNC1 şi ICES1 sunt folosiţi pentru captura la intrare iar bitul 5 este un bit rezervat.Prin biţii 2:0 setăm prescalarea, lucru pe care l-am mai discutat la prezentarea timerului 1.
Biţii WGM13 şi WGM12 din TCCR1B împreună cu biţii WGM11 şi WGM10 din TCCR1A sunt folosiţi pentru alegerea modului de funcţionare a timerului:
Totuşi cele mai folosite moduri PWM sunt primele 3 care permit numărarea pe 8,9 sau 10 biţi.

Un lucru important pe care trebuie să îl cunoaşteţi când folosiţi generatoarele PWM sunt pinii de ieşire PWM notaţi cu OC1A şi OC1B în cazul microcontrolerului ATMega8.Atunci când sunt folosiţi pentru acest scop ei trebuiesc setaţi ca pini de ieşire ai portului.
În următorul exemplu vom dori ca la pinul OC1A să avem 50% din perioada T, 1 logic, iar la pinul OC1B dorim să avem doar 25% din interval 1 logic.Pentru uşurinţă vom folosi modul pe 8 biţi iar perioada T se va lua suficient de mare astfel încât să se poată observa comutarea.Codul programului îl puteţi descărca de la secţiunea Download -PWM -Aplicaţie 1.
Schema electrică este următoarea:
Observăm că la pinii OC1A şi OC1B avem legate 2 LED-uri iar în paralel cu ele un oşciloscop prin care vom vizualiza semnalele.Acest osciloscop se găseşte în Poteus la secţiunea de unelte(Virtual Instruments Mode) şi are numele OSCILLOSCOPE.Pentru ca efectul să poată fi urmărit vizual vom prescala ceasul cu 1024.Vom alege ca semnalul să fie 1 logic când TCNT1<=OCR1x şi 0 altfel iar modul de operare să fie primul, modul PWM pe 8 biţi.Valoarea lui OCR1A este 50% din valoarea perioadei, adică 128 iar valoarea lui OCR1B este 25% adică 64. Configurarea regiştrilor va fi următoarea:

TCCR1A=0b10100001;
TCCR1B=0b00000101;
OCR1A =125;
OCR1B =64;

Configurarea în Code Vision AVR a timerului este următoarea:
Foarte important este să nu uitaţi să setaţi pinii 1 şi 2 ai portului B ca pini de ieşire.După generare şi compilare programul funcţionează exact cum am dorit,perioada semnalului putând fi urmărită pe oşciloscopul simulării:

PWM generat de Timer-ul 2
Timer-ul 2 este asemănător cu timer-ul 1 diferenţa majoră fiind că el este doar pe 8 biţi şi că avem doar un singur canal de comparare la ieşire(OC2).Configurarea şi funcţionarea generatorului PWM este asemănătoare cu cea din Timer-ul 1 regiştrii având formă şi nume diferit.Regiştrii pe care îi vom folosi sunt următorii:

-Registrul TCNT2 ,registrul care numără fiecare impuls de ceas şi care joacă rolul de numărător liber central al timerului.

-Registrul TCCR2 care are rolul de a controla modul de funcţoinare a timerului 2:
Bitul 7 nu îl vom folosi pentru setarea generatorului aşa că îl vom seta 0.
Bitul 6 şi 3 (WGM20 şi WGM21) sunt folosiţi la setarea modului de funcţionare a timerului:

WGM21 WGM20
0 0 – Funcţionare normală;
0 1 – Funcţionare ca PWM,numărarea se face în două sensuri;
1 0 – Funcţionare ca CTC;
1 1 – Funcţionare ca PWM rapid,TCNT2-ul numără într-un singur sens;

Biţii 5 şi 4 setează modul de funcţionare al comparatorului:
COM21 COM20 –Modul PWM
00-Comparatorul dezactivat;
0 1-Combinaţie rezervată;
10-TCNT2<=OCR2-1 logic la ieşire,TCNT2>OCR2 -0 logic la ieşire;
11-TCNT2<=OCR2-0 logic la ieşire,TCNT2>OCR2 -1 logic la ieşire;

Biţii 2,1 şi 0 sunt folosiţi la prescalare timerului:
CS22 CS21 CS20
000-Timer oprit;
001-Frecvenţa ceasului intern;
010- Frecvenţa ceasului intern prescalată cu 8;
011- Frecvenţa ceasului intern prescalată cu 32;
100- Frecvenţa ceasului intern prescalată cu 64;
101- Frecvenţa ceasului intern prescalată cu 128;
110- Frecvenţa ceasului intern prescalată cu 256;
111- Frecvenţa ceasului intern prescalată cu 1024;
-Registrul OCR2, este registrul prin care setăm factorul de umplere al semnalului generat.

Pinul prin care preluăm semnalul PWM este plasat pe portul B la pinul 3:
Următorul exemplu are ca scop modificara factorului de umplere a semnalului în funcţie de anumite semnale de intrare.Pentru reglare vom folosi 3 butoane legate la primi 3 pini ai portului C în felul acesta având 8 trepte de reglare a factorului de umplere.Valoarea maximă pe care o poate avea POTRC-ul este 7 iar dacă împărţim 256 la 7 obţinem aproximativ 36 deci valoarea lui OCR2 va fi PORTC*36.Vom folosi al doilea mod, modul PWM de numărare în ambele sensuri ,vom prescala ceasul 1024 şi vom alege următoarea regulă : TCNT2<=OCR2- 1 logic la ieşire,TCNT2>OCR2 -0 logic la ieşire.Regiştrii vor avea următoarele valori:

TCCR2 = 0b01100111;
OCR2 = 0;

Codul programului şi simularea o puteţi downloada de la secţiunea Download -PWM -Aplicaţie 2.
Schema electrică este următoarea:
Când configuraţi proiectul în Code Vision AVR trebuie să aveţi grijă ca primii 3 pini ai PORTC-ului să fie configuraţi ca pini de intrare iar pinul 3 al PORTB-ului să fie configurat ca pin de ieşire.Pe lângă acestea trebuie să configuraţi şi timerul 2 în felul următor:
Dacă rulaţi simularea veţi putea modifica factorul de umplere în funcţie de combinaţia celor 3 butoane.

După informaţiile prezentate mai sus observăm că microcontrolerul ATMega 8 poate genera 3 canale PWM în felul acesta putând controla vitezele în mod diferit a 3 motoare de curent continuu. Cam atât momentan despre generatorul PWM.

Sistemul de întreruperi

Întreruperile într-un microcontroler sunt văzute ca nişte evenimente speciale generate de elementele periferice.Aceste evenimente pot fi reprezentate de apăsarea unui buton, atingerea unei valor a numărătorului,terminarea de citit a unei valori a convertorului analog digital,etc..Toate acestea sunt gestionate de un sistem intern numit sistem de întreruperi , sistem care are rolul de a oferi procesorului informaţii referitoare la natura întreruperii şi de a opri funcţionarea ciclică tratând întreruperea într-o rutină specială. Această rutină are o adresă specială de început.În cazul chip-ului ATMega8 adresele rutinelor de tratare corespunzătoare întreruperilor sunt următoarele:
Prioritatea de tratare a unei întreruperi în cazul în care avem mai multe cereri de întrerupere este marcată de prima coloana, astfel cea mai mare prioritate o are întreruperea generată de semnalul RESET,apoi întreruperea externă INT0 , întreruperea externă INT1 şi tot aşa.Trebuie să precizez că aceste adrese sunt mascate în CodeVisionAVR dar totuşi e bine să ştiţi cum funcţionează modul intern de tratare al întreruperilor.
Momentan voi trata doar întreruperile externe,INT0 şi INT1, deoarece sunt cele mai simple de configurat.
Pentru a putea folosi orce întrerupere trebuie mai întâi să informăm microcontrolerul că întreruperile sunt activate.Acest lucru se face prin comana sei, comandă care în C se poate scrie astfel:
#asm("sei")
Această comandă nu face altceva decât să seteze valoarea 1 în cel mai semnificativ bit,bitul I, din regist SREG,bit care permite folosirea sistemului de întreruperi.
Pentru dezactivarea întreruperilor se foloseşte comanda cli,în C fiind #asm("cli").
Activarea întreruperilor externe se face prin registrul GICR(General Interrupt Control Register) care are următoarea formă:
Pentru a activa întreruperea externă INT0 trebuie să setăm valoarea 1 în locaţia 6 din registru iar pentru activarea întreruperii externe INT1 trebuie setată valoarea 1 în bitul 7.Primii doi biţi ai registrului, IVCE şi IVSEL se folosesc atunci când în memoria microcontrolerului avem o secţiune boot însă în acest tutorial nu o să folosim aşa ceva.Restul biţilor 2-5 nu sunt folosiţi,ei fiind rezervaţi.
După ce am setat ce întreruperi externe sunt folosite, va trebui să setăm în ce moment să se activeze întreruperea.Acest lucru se poate seta prin registrul MCUCR:
Din acest registru doar primii 4 biţi(0-3) sunt atribuiţi întreruperilor externe,primii doi biţi(ISC00 şi ISC01) fiind atribuiţi întruperii INT0 iar biţii 3 şi 4(ISC10 şi ISC11) sunt atribuiţi întruperii INT1.Următoarea configuraţie este valabilă pentru ambele întreruperi încât biţii ISC11/ISC01 şi ISC00/ISC10 pot lua următoarele valori:
ISC11/ISC01 ISC00/ISC10
00- Nivelul de jos al semnalului va genera o cerere de întrerupere.
01- Orce schimbare a formei semnalului va genera o cerere de
întrerupere.
10- Frontul descrescător al semnalului va genera o cerere de
întrerupere.
11- Frontul crescător al semnalului va genera o cerere de întrerupere.

Dacă întreruperea a fost generată ea va fi semnalată în registrul GIFR:
Dacă pe parcursul rulării programului bitul 6 ia valoarea 0 atunci înseamnă că avem o cerere de întrerupere generată de întreruperea externă 0 iar dacă bitul 7 ia valoarea 0 înseamnă că avem o cerere de întrerupere generată de întreruperea externă 1,aceşti biţi având valoarea 1 atâta timp cât nici o cerere nu a fost lansată.Acest registru se verifică pentru a vedrea sursa întreruperii.Ddupă execuţia rutinei de tratare, bitul corespunzător întreruperii îi va fi automat atribuită valoarea 1,lucru necesar pentru a se evita ciclarea la infinit a rutinei.

În exemplul următor voi incrementa 2 variabile prin apăsarea a 2 butoane legate la pinii corespunzători cererii de întrerupere INT0 şi INT1.Valorile variabilelor care contorizează de câte ori au fost apăsate butoanele vor fi afişate pe un LCD.La unul dintre pinii microrontrolerului va fi legat un LED care îşi va schimba starea din aprins în stins şi invers la intervalul de o secundă.Codul programului îl puteţi descărca de la secţiunea Download-Întreruperi-Aplicaţie 1.

Schema electrică a aplicaţiei este următoarea:
Dacă ne uităm la schema chip-ului observăm că pinii portului D,PD2 şi PD3 sunt folosiţi şi ca pini pentru întreruperi acest lucru înpingându-ne să legăm LCD-ul la portul B.LED-ul îl vom lega la pinul 0 al portului D.Înainte de generarea programului în CodeVision AVR, vom seta LCD-ul pe portul B ,pinul PD0 ca pin de ieşire iar pentru setarea întreruperilor externe vom merge la External IRQ.Vom selecta ambele întreruperi iar în dreptul lor vom alege Rising Edge,adică se va lansa o cerere de întrerupere la trecerea semnalului de intrare de la valoarea 0 la valoarea 1:
Pentru această configurare programul va seta regiştrii de mai sus cu următoarele valori:
GICR =0b11000000 – se activează ambele întreruperi INT0 şi INT1;
MCUCR = 0b00001111 - se vor semnala cereri de întrerupere la trecerea semnalului din starea 0 în starea 1;

După ce aţi generat programul veţi observa că două noi funcţii au fost generate:
interrupt [EXT_INT0] void ext_int0_isr(void) şi interrupt [EXT_INT1] void ext_int1_isr(void) funcţii prin care vom trata apariţia întreruperilor.Codul din interiorul funcţiilor care tratează apăsarea unui buton este următorul:

interrupt
[EXT_INT0] void ext_int0_isr(void)
{
int0++; //la apasare se incrementeaza variabila int0
itoa(int0,convert); //convertim valoarea lui int0 in sir de caractere
strcpy(linie1,end); //atribuim sirului linie1 valoarea '\0'
strcat(linie1,in1); //adaugam la sirul linie1 textul"INT0="
strcat(linie1,convert);//adaugam la sirul linie1 valoarea variabilei int0
lcd_gotoxy(0,0); //mutarea cursorului in LCD la linia 0 coloana 0
lcd_puts(linie1); //afisarea datelor pe LCD
}
interrupt [EXT_INT1] void ext_int1_isr(void)
{
int1++; //la apasare se incrementeaza variabila int1
itoa(int1,convert); //convertim valoarea lui int1 in sir de caractere
strcpy(linie2,end); //atribuim sirului linie1 valoarea '\0'
strcat(linie2,in2); //adaugam la sirul linie1 textul"INT1="
strcat(linie2,convert);//adaugam la sirul linie1 valoarea variabilei int1
lcd_gotoxy(0,1); //mutarea cursorului in LCD la linia 1 coloana 0
lcd_puts(linie2); //afisarea datelor pe LCD
}

In aceste funcţii pe lângă incrementare am convertit variabilele corespunzătoare întreruperilor, din short în sir de caractere, am concatenat şirurile de caractere care conţin sursele întreruperilor şi valorile variabilelor transformate în şir de caractere şi apoi le-am afişat pe LCD.Variabilele cu care am lucrat în aceste funcţii sunt declarate în partea de declarare a variabilelor globale:

unsigned short int0=0,int1=0;
char in1[6] = "INT0=";
char in2[6] = "INT1=";
char convert[4],end[1]="";
char linie1[16],linie2[16];

Este necesară declararea lungimii şirurilor deoarece microcontrolerul nu poate aloca dinamic spaţii de memorie în memoria program de tip flash.
În bucla infinită while avem doar codul care va aprinde şi va stinge LED-ul după o secundă:

while (1)
{
PORTD = 0x00;
delay_ms(1000);
PORTD = 0x01;
delay_ms(1000);
};

Pentru a folosi funcţiile de concatenare şi copiere (strcat şi strcpy) a fost necesară includerea bibliotecii string.h.Pentru conversia din short în şir de caractere,funcţia itoa, am inclus biblioteca stdlib.h.

Poate că vă întrebaţi care e scopul folosirii întreruperilor pentru această aplicaţie când puteam să folosim doar citirea normală la nivel de port.Totuşi dacă veţi încerca să dezvoltaţi aplicaţia fără întreruperi veţi observa că sunt situaţii în care apăsaţi butoanele şi variabilele nu se incrementează.Acest lucru se întâmplă deoarece există situaţia în care programul aşteaptă să treacă o secundă iar dacă se apasă butonul şi se eliberează în acest timp,apăsarea nu este contorizată.Folosind întreruperile se elimină acest neajuns.
Funcţionarea fizică a aplicaţiei o puteţi vedea mai jos:

Timer-ele - Timer 0

Timer-ele sunt sisteme integrate în microcontroler care au ca scop măsurarea unor intervale fixe de timp şi de a genera întreruperi la expirarea intervalului măsurat.Tot cu ajutorul timer-elor se pot genera trenuri de impulsuri de o anumită frecvenţă sau compararea a 2 intervale de timp.Timer-ele funcţionează independent de unitatea centrală a micrcontrolerului astfel eliminându-se momentele de timp în care microcontrolerul este blocat într-o buclă de aşteptare.
Timer-ele conţin un numărător liber central(NLC) care are rolul de a număra fiecare impuls de tact.Acest NLC este de fapt un registru de 8 sau 16 biţi care la fiecare impuls este incrementat cu o unitate.Numărătorului i se poate da o valoare de start cuprinsă între 00h/0000h şi FFh/FFFFh.Dacă NLC-ul are valoarea FFh/FFFFh şi primeşte un impuls el se resetează,noua valoare fiind 00h/0000h.Tot în momentul resetării numărătorul trimite un semnal către sistemul de întreruperi.
Microcontrolerul ATMega8 oferă 3 timere:Timer 0,Timer 1 şi Timer 2 însă momentan ne vom concentra asupra Timer-ului 0.

Timer/Counter 0
Timer-ul 0 este un timer de uz general pe 8 biţi care are următoarea schemă bloc:
În cazul de faţă NLC-ul este reprezentat de registrul TCNT0 la care avem acces de citire şi scriere.Observăm din schemă că o dată ce s-a atins valoarea 0xFF se trimite un semnal către logica de control care poate genera o cerere de întrerupere.Sursa semnalului care incrementază NLC-ul poate fi externă(T0) sau internă(semnalul de tact al microcontrolerului) care poate fi prescalată.La fiecare tact clkT0 numărătorul TCNT0 este incrementat:
Registrul TCCR0 are rolul de a selecta şi prescala susra semnalului de tact, el având forma:
Din acest registru vom folosi numai ultimii 3 biţi (CS02, CS01, CS00) pentru setare.Valorile pe care le poate lua şi seminificaţiile lor sunt următoarele:
000– nu se selectează nici o sursă de tact;
001– foloseşte ceasul intern fără prescalare;
010– foloseşte ceasul intern cu prescalarea 8 (clkT0/8);
011– foloseşte ceasul intern cu prescalarea 64 (clkT0/64);
100– foloseşte ceasul intern cu prescalarea 256 (clkT0/256);
101– foloseşte ceasul intern cu prescalarea 1024 (clkT0/1024);
110– foloseşte o sursă externă de tact la pinul T0.Se ia în considerare frontul descrescător;
111– foloseşte o sursă externă de tact la pinul T0.Se ia în considerare frontul crescător;
Pentru a se putea genera o cerere de întrerupere a timer-ului 0 trebuie ca cel mai semnificativ bit al registrului SREG(bitul I) să fie 1 şi cel mai nesemnificativ bit al registrului TIMSK, bitul TOIE0 să fie tot 1.
Cererea de întrerupere este semnalizată în registrul TIFR în cel mai nesemnificativ bit,TOV0.Dacă registrul TCNT0 a fost umplut,TOV0 devine 0 şi atunci sistemul va genera o întrerupere executându-se codul de la adresa 0x009.
În majoritatea aplicaţiilor cu timere se foloseşte prescalarea sursei de tact.Calcului prescalării se face în felul următor:
Mai întâi calculăm frecvenţa la care dorim să se genereze o întrerupere.Frecvenţa va fi raportul dintre o secundă şi intervalul la care dorim să primim semnalul de la timer.
În funcţie de această frecvenţă vom alege frecvenţa externă sau vom prescala ceasul intern.Prescalarea ceasului intern se face în aşa fel încât frecvenţa rezultantă să fie egală sau mai mică cu frecvenţa dorită.
Dacă prescalând timerul nu obţinem o frecvenţă egală cu cea dorită şi obţinem o frecvenţă mai mică, atunci se calculează valoarea de start a NLC-ului.Această valoare va fi raportul dintre frecvenţa internă şi prescalarea aleasă la pasul anterior înmulţită cu frecvenţa dorită:
Pentru a înţelege mai bine cum se calculează frecvenţa vom calcula prescalarea şi n-ul pentru timpul de 0,2 secunde.Ceasul folosit va fi cel intern.Datele de pornire sunt: tdorit = 0.2s,fint =1MHz.

Observăm ca această frecvenţă este mai mică decât frecvenţa pe care dorim să o scoată timerul(5Hz), în consecinţă vom calcula valoarea de pormire a lui TCNT0.
Din păcate în numărător nu se pot reţine date în virgulă mobilă aşa că lui TCNT0 i se va atribui valoarea 195.Acest lucru va duce însă la o mică eroare de măsurare a timpului care totuşi este insesizabilă la o funcţionare pe termen scurt.
Pentru acest exempu configurarea regiştrilor va fi următoarea:

TCNT0=195;
TCCR0=0x05;
TIMSK=0x01;

Vom considera că la generarea unei întreruperi se va schimba starea unui pin la care avem legat un LED.Pinul de care vom lega LED-ul este PB0.
Programul îl puteţi downloada de la secţiunea Download-Timer0-Aplicaţie 1.

Schema electrică este una simplistă:
Când creaţi proiectul în CodeVision AVR,la secţiunea de setare a caracteristicilor, veţi seta pinul PB0 ca pin de ieşire iar pentru setarea parametrilor timer-ului veţi merge la Timers-Timer 0 şi veţi alege la Clock Source: System Clock, la Clock Value:0.977Hz,selectaţi Owerflow Interrupt şi la Time value treceţi C3h(195 în decimal).Observăm că frecvenţa aleasă nu este cea calculată de noi însă aceată alegere va seta TCCR0=0x05, adică prescalarea cu 1024.
Observăm că la generare avem următoarele setări:

TCCR0=0x05;
TCNT0=0xC3;
TIMSK=0x01;


În concsecinţă setările sunt bine făcute.Ce a mai apărut nou în program este funcţia de tratarea a întreruperilor generate de timer,funcţie în care vom iniţializa de fiecare dată TCNT0 şi în care vom schimba valoarea PORTB-ului:

interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitializam valoarea timerului
TCNT0=0xC3;
// Schimbam starea PORTB-ului
PORTB =~PORTB;
}

Bun, probabil aţi observat că acest program se putea face şi fără timere de aceea următorul exemplu va fi unul mai complex şi anume un ceas digital reglabil.Aplicaţia va avea un LCD pe care vom putea citi ceasul şi 2 butoane prin care se va regla minutul şi ora,butoane care vor fi legate la pinii INT0 şi INT1.Aplicaţia o puteţi downloada de la secţiunea Download- Timer 0- Aplicaţie 2.
Schema electrică este următoarea:
Următorul lucru pe care trebuie să îl facem este să calculăm frecvenţa timerului astfel încât să avem o întrerupre la o secundă.Pentru aceasta frecvenţa dorită este 1 MHz.Dacă folosim ceasul intern şi prescalăm cu maximul adică cu 1024 observăm că noua frecvenţă obţinută este de 3.81Hz, frecvenţă care este mai mare decât frecvenţa pe care dorim să o folosim.Pentru a elimina acest neajuns vom folosi o variabilă suplimentară în program care se incrementează la fiecare 0,1 secunde,adică la 10Hz.Când se ajunge la valoarea 10, variabila care reţine secundele se incrementează iar valoarea variabilei suplimentare va deveni 0.Momentan vom calcula valorile regiştrilor pentru frecvenţa dorită de 10Hz.În consecinţă vom folosi iar prescalarea cu 1024 însă vom calcula n-ul pentru 10Hz:
Pentru a mai diminua din erori vom rotunji n-ul la 98 şi nu 97.Configurarea pentru generarea programului în CodeVision AVR se face la fel ca mai înainte însă la Timer Value treceţi 62(98 în decimal).De asemenea nu uitaţi să configuraţi LCD-ul pe portul B şi să activaţi întreruperile externe.

Programul va avea 3 funcţi de tratare a cererilor de întrerupere,două pentru tratarea butoanelor care modifică ora şi minutul şi una pentru tratarea întreruperii generate de timerul 0.Funcţia care tratează întreruperea externă INT0 are rolul de a incrementa minutul iar funcţia care tratează întreruperea externă INT1 are rolul de a incrementa ora.Dacă minutul a ajuns la valoarea 60 sau ora la valoarea 24 , ele trebuiesc resetate.Funcţia care tratează timerul 0 are rolul de a incrementa o variabilă suplimentară până la valoarea 10 , adică echivalentul unei secunde,la această valoare ea resetându-se şi incrementând variabila secundă(sec) cu o unitate.În aceste funcţii sunt verificate variabilele de ceas dacă au ajuns la valoarea maximă şi dacă da ele sunt resetate şi totodată sunt incrementate variabilele care le urmează.

În cele trei funcţii se are grijă ca după fiecare modificare a variabilelor să se scrie noua stare a ceasului pe LCD.Acest lucru se face prin modificarea sirului de caractere linie1.
Din păcate ceasul nostru nu este unul exact, existând erori de măsurare a timpului, erori care pot fi înlăturate folosind un oscilator extern special pentru ceas.
Aplicaţia realizată fizic o puteţi vedea aici:

Twitter Delicious Facebook Digg Stumbleupon Favorites More