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.

Timere-le Timer 1

Timer-ul 1 funcţionează exact ca timerul 0 singura diferenţă fiind că regiştrii sunt pe 16 biţi.Timerul 1 are mai multe facilităţi, el putând fi folosit ca şi comparator la ieşire şi la intrare, generator de frecvenţă,generator de semnale PWM şi numărător de evenimente externe.
Momentan ne vom concentra asupra modului standard de folosire ca numărător.Numărătorul liber central este reprezentat de registrul TCNT1:
Acest registru este împărţit în doi regiştrii de 8 biţi,TCNT1H şi TCNT1L.La scrierea acestui registru trebuie mai întâi să scriem registrul TCNT1H apoi TCNT1L.Citirea se face exact invers citind mai întâi registrul TCNT1L apoi registrul TCNT1H.Aceste reguli sunt utile pentru păstrarea cât mai mare a preciziei NLC-ului deoarece el se incrementează şi în momentul operaţiei de scriere sau citire.Folosind un compilator în C citirea şi scrierea se face folosind o singură variabilă însă procesorul va face aceste operaţii în doi paşi.Acest lucru este periculos atunci când o rutină de tratare a unei întreruperi a fost lansată în timpul citirii sau scrierii acestui registru.Pentru a înlătura această problemă înainte de a efectua o operaţie cu regiştrii de 16 biţi se blochează sistemul de întreruperi.De exemplu în C codul necesar pentru o citire sigură arată cam aşa:

unsigned char
sreg;
unsigned int i;
//salveaza starea registrului SREG
sreg = SREG;
#asm
cli; opreste sistemul de intreruperi
#endasm
// copiaza TCNT1 in variabila i
i = TCNT1;
//pornim sistemul de intreruperi
SREG = sreg;

Un alt registru important pentru setarea caracteristicilor numărătorului este registrul de control TCCR1B:
Biţii 7 şi 6 sunt folosiţi pentru captura la intrare , bitul 5 este un bit rezervat iar biţii 4 şi 3 sunt folosiţi pentru comparatoare.Biţii 2:0 sunt cei care ne interesează mai mult deoarece prin ei setăm selecţia sau prescalarea ceasului:

CS12 CS11 CS10 Sursă/Prescalare
000-timerul este oprit;
001-ceasul intern fară prescalare;
010-ceasul intern prescalat cu 8;
011-ceasul intern prescalat cu 64;
100-ceasul intern prescalat cu 256;
101-ceasul intern prescalat cu 1024;
110-sursă externă a tactului la pinul T1,incrementare pe frontul descrescător;
111-sursă externă a tactului la pinul T1,incrementare pe frontul crescător;
Dacă dorim să folosim şi sistemul de întreruperi va trebui să folosim registrul TIMSK:
Pentru ca timerul să genereze o întrerupere la supraîncărcarea registrului TCNT1 trebuie să se permită folosirea sistemului de întreruperi şi trebuie ca bitul 2 din registru, bitul TOIE, să fie 1.Cererea de întrerupere este marcată în registrul TIFR prin bitul TOV1:
Dacă se generează o cerere de întrerupere bitul TOV1 devine 0 el fiind automat setat cu valoarea 1.După ce este executată rutina de tratare a întreruperii bitul se setează automat 1.

În exemplu următor vom avea un LED conectat la microcontroler care îşi va schimba starea la intervalul de 5 secunde.LED-ul va fi conectat la pinul 0 al portului B.Programul îl puteţi downloada de la secţiunea Download- Timer1- Aplicaţia1.Schema electrică este una simplă:

Calculul frecvenţei de numărare a NLC-ului se face ca la timer 1 însă se va lua în calcul că numărătorul este pe 16 biţi:

În CodeVision AVR va trebui să aveţi următoarea setare a timerului 1 pe lângă setarea pinului 0 al portului B:

Partea în care se întâmplă toată acţiunea este reprezentată de rutina de tratare a întreruperii generate de supraîncărcarea NLC-ului:

interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
// setarea valorilor NLC-ului
#asm
cli
#endasm

TCNT1H=0x4C;
TCNT1L=0x4B;
#asm
sei
#endasm
//schimbarea starii portului
PORTB.0 = ~PORTB.0;
}
În această rutină nu facem altceva decât să initializăm TCNT-ul şi să schimbăm valoarea portului B.
Cam atât despre timerul 1 momentan.

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:

Twitter Delicious Facebook Digg Stumbleupon Favorites More