tutorial avr (03.06.2012)

22
Tutorial Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

Upload: andrei-nicolae

Post on 20-Jan-2016

137 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Tutorial AVR (03.06.2012)

Tutorial Introducere în programarea și utilizarea

microcontrollerelor Atmel AVR

Page 2: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

2

Cuprins

Partea 1 - “Hello world!” ................................................................................................................. 3

Introducere ................................................................................................................................... 3

De ce ai nevoie? ........................................................................................................................... 4 Programarea uC-ului .................................................................................................................... 5 Cum scriu cod pentru uC? ............................................................................................................ 8 Cum transfer programul în uC? .................................................................................................... 9 Leduri „clipitoare” ...................................................................................................................... 10

Tema 1 ........................................................................................................................................ 11

Partea 2 – Push the button .............................................................................................................. 12

De ce ai nevoie? ......................................................................................................................... 12 Cum folosesc butoanele în cod? ................................................................................................. 13 Exemple ...................................................................................................................................... 13 Tema 2 ........................................................................................................................................ 14

Partea 3 – The clock is ticking ....................................................................................................... 15 Exemple ...................................................................................................................................... 16

Partea 4 – Fade to black ................................................................................................................. 18 Cum se folosește? ....................................................................................................................... 18 Exemple ...................................................................................................................................... 18

Page 3: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

3

Partea 1 - “Hello world!”

Introducere

În această primă parte a tutorialului vei face cunoștință cu lumea microcontrolerelor

folosind un Atmel ATMega8 și niște leduri. Cu toate ca ceea ce ține de microcontrollere este

explicat la nivel de absolut începator sunt necesare cunoștințe cel puțin medii de programare în C.

În mare parte totul va fi explicat în limba română dar sunt necesare cunoștințe de limba engleză

pentru a înțelege anumite lucruri.

Ce este ATMega8? Este un microcontroller cu 8KB memorie Flash (e suficientă pentru

un program destul de complex), 1KB memorie RAM(acolo sunt allocate variabilele folosite in

program), 512B EEPROM (aici poți stoca date; spațiul e cam mic) și mulți pini pentru intrări /

ieșiri. Cam așa arată:

La acest microcontroller vei conecta câteva leduri cu care ne vom juca mai târziu.

În cazul în care ai uitat cum arata un led și cum se conectează arunca o privire pe imaginea de

mai jos:

Page 4: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

4

De ce ai nevoie?

Ca să te poți apuca de programarea propriu-zisă ai nevoie întâi de un circuit care să poată

îndeplini funcțiile implementate în program (program la care mă voi referi în continuare ca “soft”

sau “firmware” - FW). Pentru realizarea circuitului ai nevoie de următoarele:

Letcon, fludor, sacâz (colofoniu) – pentru lipirea componentelor (dacă

folosești breadboard nu sunt necesare). În cazul în care nu ai mai lipit până

acum componente uită-te la filmulețul din link-ul urmator:

http://www.youtube.com/watch?v=AOdnGUMi7lQ

Cablaj de test sau breadboard

8 leduri

8 rezistențe (100ohm sunt ok)

7805 – regulator de tensiune pentru a putea alimenta circuitul de la cam

orice alimentator găsit prin casă cu tensiune DC mai mare de 7V.

ATMega8 – microcontrollerul folosit în acest tutorial

Fire de legatură

USBasp – programatorul folosit în acest tutorial. Poți folosi orice

programator cu programele adecvate.

Pe lângă resursele hardware mai ai nevoie și de niște programe cu care să scrii / compilezi

codul și cu care să transferi softul rezultat în microcontroller (cuvântul e destul de lung așa că în

continuare voi folosi notația uC). Programele necesare sunt urmatoarele:

AVR Studio 4.18 – mediul de dezvoltare a codului. Conține și un simulator

foarte util. (http://en.stkshop.com/download--download_id-65.html)

AVR Studio SP3 – se instalează împreună cu AVR Studio 4.18. Îl găsești

aici:

http://www.atmel.com/dyn/resources/prod_documents/AVRStudio4.18

SP3.exe

WinAVR – compilatorul de C care se folosește împreună cu AVR Studio 4

(http://sourceforge.net/projects/winavr/files/WinAVR/20100110/WinA

VR-20100110-install.exe/download)

Khazama Programmer – programul cu care se transferă softul în uC

(http://khazama.com/project/programmer/)

Circuitul pe care îl vei folosi în această primă parte a tutorialului este cel din imaginea de

mai jos. Va trebui ca folosind uneltele descrise mai sus să conectezi componentele între ele ca în

schema. Recomandat este ca toate ledurile sa fie montate într-un singur rând pentru a înțelege

mai bine exemplele de cod prezentate.

Lângă leduri se vor monta (vertical - pentru a face economie de spațiu pe plăcuță;

orizontal în cazul în care spațiul nu e o problemă) rezistențele. Conexiunile până la uC se vor face

folosind fire de legătură. Poziția conectorului pentru programare nu este critică dar încearcă să

aranjezi componentele în așa fel încat să ocupi cât mai puțin spațiu. Pe măsura ce vei parcurge

tutorialul plăcuța se va umple cu componente!

Page 5: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

5

Programarea uC-ului

Pentru a înțelege ceea ce urmează este necesar să citești din datasheet-ul uC-ului

(http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf) următoarele:

Pag. 2 – Pin configurations

Pag. 5 – Port B

Pag. 51 – I/O Ports până la Table 20 (Pag. 53)

Pag. 55 până la Digital Input Enable and Sleep Modes

AVR Studio 4

Dupa ce ai citit ce este indicat mai sus te poți apuca de programarea propriu-zisă. AVR

Studio 4 este programul în care vei dezvolta, compila și simula codul. Pentru crearea unui proiect

nou deschide AVR Studio 4.

În fereastra care îți apare selectează New Project apoi AVR GCC.

Page 6: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

6

Dă un nume proiectului și selectează unde vrei sa fie salvat apoi apasă Next.

Page 7: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

7

Selectează AVR Simulator iar în dreapta ATMega8 apoi apasă Finish.

În fereastra care îți apare vei scrie codul.

Page 8: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

8

Cum scriu cod pentru uC?

Poate părea complicat la început însă este în mare același C pe care poate că l-ai facut la

informatică în liceu.

Ca și în liceu codul începe cu headerele necesare. Acum vei folosi doar #include

<avr/io.h> pentru accesarea pinilor. Urmează veșnicul void main() în care apare codul propriu-

zis.

Până să ajungi să scrii cod trebuie să știi câteva noțiuni mai puțin folosite în programarea

PC-ului:

Familia de uC AVR lucreaza pe 8biți. Asta înseamnă ca toți registri au

lungime de 8 biti putând stoca o valoare între 2550 xFFx 0000

111111110000000000 bb .

1 logic = VCC (în cazul de față 5V); 0 logic = GND (0V)

(1<<X) – înseamnă că reprezentarea lui 1 în baza doi (0b00000001) o

deplasez în stânga cu X poziții ( 71 X ). Pentru X=2 rezultatul este

0b00000100. Toată operația are ca rezultat setarea bitului X ca 1.

~(1<<X) – înseamnă că la toată operația descrisă cu un pas mai sus se

aplică complementul lui 1. Altfel spus: ce este 1 devine 0 si ce e 0 devine

1. Exemplu: ~(1<<3)=~(0b00001000)=0b1111011. Toată operația are ca

efect setarea bitului X ca 0 iar a celorlalți ca 1.

A|=B; - înseamnă A=A|B, adică A ia valoarea rezultată din aplicarea

operatiei logice SAU între A și B. La fel e pentru A&=B;

Înainte de a începe să scrii codul trebuie să te asiguri că proiectul are setările corecte.

Apasă butonul pentru a deschide fereastra de configurare. Pentru tutorialul acesta AVR

Studio este configurat ca în imaginea de mai jos:

Page 9: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

9

Acum poți începe să scrii primul tău cod pentru uC. Exemplul urmator demonstrează cum poți

aprinde două din cele opt leduri conectate pe PORTB, respectiv ledul 0 și 7. Poți alege orice led

și oricâte leduri.

#include <avr/io.h> //header necesar pt a putea accesa pinii uC-ului void main() { DDRB|=(1<<DDB0)|(1<<DDB7); //setează pinul 0 și 7 ca ieșire PORTB|=(1<<PB0)|(1<<PB7); //setează pinul 0 și 7 ca 1 logic while(1); //bucla infinită }

Cum transfer programul în uC?

După ce ai terminat de scris codul va trebui sa-l compilezi apăsând tasta F7. Dacă totul a

decurs bine ar trebui să vezi ceva asemănător cu imaginea de mai jos:

Deschide programul Khazama Programmer și selectează Load FLASH File (marcat cu

roșu).

În fereastra care îți apare mergi la locația unde ai salvat proiectul, folderul default. Acolo găsești

fișierul .hex rezultat în urma compilării codului. Selectează fișierul, apoi Open.

Asigură-te că programatorul e conectat atât la calculator cât și la montaj iar montajul este

alimentat. Selectează Command ->Read Chip Signature. Dacă rezultatul este 0x1E9307 poți trece

la programarea propriu-zisă a uC-ului apasând butonul Write FLASH to Chip marcat cu albastru

Page 10: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

10

în imaginea de mai sus. Presupunând că scrierea s-a făcut cu success acum ar trebui ca cele două

leduri despre care am vorbit mai sus să fie aprinse.

Leduri „clipitoare”

Hai acum să încercăm să facem ledurile să „clipească”. Deoarece uC-ul funcționeză la

frecvență foarte mare (1MHz în cazul celui folosit în acest tutorial) și execută o instrucțiune/ciclu

ledurile își vor schimba starea mult prea repede ca ochiul uman să sesizeze ceva. Avem nevoie de

o modalitate de a mări timpul cât ledul se află într-o stare. Pentru asta avem la dispozitie funcțiile

_delay_us(double __us) (întârzie execuția urmatoarei intrucțiuni cu numărul cerut de micro-

secunde) și _delay_ms(double __ms) (întârzie execuția următoarei instrucțiuni cu un anumit

număr de milisecunde) incluzând headerul util/delay.h.

Noi ne vom folosi de funcția _delay_ms() pentru a obține întârzieri vizibile iar

instrucțiunile necesare vor fi plasate în bucla infinită while(1) codul urmând a fi executat repetitiv

atât timp cât montajul este alimentat.

#include <avr/io.h> //header necesar pt a putea accesa pinii uC-ului #include <util/delay.h> //header necesar pentru delay void main() { DDRB|=(1<<DDB0)|(1<<DDB7); //setează pinul 0 și 7 ca ieșire while(1)

{PORTB|=(1<<PB0); //aprind ledul 0 _delay_ms(500); //aștept 500ms PORTB&=~(1<<PB0); //sting ledul 0 PORTB|=(1<<PB7); //aprind ledul 7 _delay_ms(500); //aștept 500ms PORTB&=~(1<<PB7); //sting ledul 7 }

}

O modalitate de a scrie codul mai elegant ar fi sa definim operațiile cu biții din PORTB și

delay-ul cu câte un nume. Exemplu:

#include <avr/io.h> //header necesar pt a putea accesa pinii uC-ului #include <util/delay.h> //header necesar pentru delay #define LED0_ON() PORTB|=(1<<PB0) #define LED0_OFF() PORTB&=~(1<<PB0) #define LED7_ON() PORTB|=(1<<PB7) #define LED7_OFF() PORTB&=~(1<<PB7) #define DELAY() _delay_ms(500) void main() { DDRB|=(1<<DDB0)|(1<<DDB7); //setează pinul 0 și 7 ca ieșire while(1)

Page 11: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

11

{LED0_ON(); //aprind ledul 0 DELAY(); //aștept 500ms LED0_OFF(); //sting ledul 0 LED7_ON(); //aprind ledul 7 DELAY(); //aștept 500ms LED7_OFF(); //sting ledul 7 }

}

Cele două coduri sunt echivalente. Diferența este doar de estetică. Poți testa codul

compilându-l și apoi scriindu-l în uC.

Tema 1

1. Folosind circuitul construit, implementează un numarator binar pe 8 biți.

2. Folosind circuitul construit, implementează un program care să deplaseze un led

aprins la stânga și la dreapta alternativ, lungimea deplasării fiind de 7 biți.

3. Implementează un program care să deplaseze ledul stins la stânga și la dreapta

alternativ, lungimea deplasarii fiind de 7 biți.

4. Combină programele create anterior (2 și 3) astfel încât pornind cu deplasarea ledului

aprins, după 3 deplasări complete se va trece la deplasarea ledului stins. După 3 astfel

de deplasări programul trece din nou la deplasarea ledului aprins, ciclul repetându-se

la nesfarșit.

5. Scrie un program care să conțină cel puțin 3 animații diferite și care se vor schimba

dupa un număr ales de cicluri.

6. Folosind 3 leduri consecutive pentru reprezentarea a 3 variabile binare a,b,c și unul

pentru f, implementează funcția cbacabf .

O variantă de rezolvare a temei se găsește în folderul Tema 1.

Page 12: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

12

Partea 2 – Push the button

În prima parte ai făcut cunoștință cu microcontrollerele și ai învățat cum se folosește un

port ca output. În cele ce urmează vei învăța cum poți folosi butoane pentru a schimba

funcționarea montajului.

De ce ai nevoie?

Pentru a putea folosi butoanele întâi să achiziționezi (dacă nu ai deja) și să adaugi la

circuitul existent urmatoarele componente:

2 push butoane

2 rezistente de 5K6

2 condensatori 100nF

Circuitul arată cum se vede mai jos. Este același circuit pe care l-ai folosit până acum cu

butoanele conectate la pinii PD2 și PD3.

Page 13: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

13

Cum folosesc butoanele în cod?

Înainte de a te apuca de scris codul trebuie să știi că aproape fiecare pin, pe lângă funcția

sa principală de input / output mai are și alte funcții secundare notate în paranteză în dreptul

pinului din schemă. Butoanele le-ai conectat pe pinii PD2 și PD3. În dreptul lor în schemă ai să

vezi scris INT0 respectiv INT1 care se referă la ÎNTreruperi externe.

Ce este o întrerupere? O întrerupere poate fi descrisă ca un eveniment care forțează

uC-ul să execute codul scris pentru acea întrerupere în locul codului principal. La terminarea

codului din întrerupere se revine unde a rămas în codul principal. Pentru a înțelege mai bine ce

este o întrerupere, cum se folosesc în general dar și cum vei folosi butoanele îți recomand sa

citești din datasheet-ul uC-ului următoarele:

Pag. 56 – Alternate Port Functions

Pag. 63 – Alternate Functions of Port D

Pag. 63-68 – External Interrupts

Pentru a te putea folosi efectiv de întreruperi va trebui să incluzi în cod headerul

avr/interrupt.h . Întreruperile sunt procesate de o funcție numită ISR (Interrupt Service Routine)

ce primește ca parametru numele vectorului de întrerupere (ex: ISR(INT0_vect)). Pentru butoane

vei folosi ISR(INT0_vect) și ISR(INT1_vect). În interiorul fiecărei funcții vei scrie ce anume vrei

să se întâmple când are loc întreruperea respectivă.

Butoanele au rezistențe de pull-up (pe pinul respectiv al butonului ai 5V) care sunt

conectate și la pinii uC-ului la un capat și masă în cealaltă parte. Când apeși unul din butoane,

tensiunea pe pinul uC-ului va deveni 0V, deci ai o tranziție HIGH-LOW (falling edge). Atât timp

cât butonul este apăsat ai LOW LEVEL. Când dai drumul butonului vei avea din nou 5V, deci o

tranziție LOW-HIGH (rising edge). În funcție de cum ai configurat întreruperea (vezi tabelul 31 și

tabelul 32 de la pagina 66-67 a datasheetului) ea se va declanșa la unul din evenimentele descrise

mai sus. În exemplele de mai jos întreruperea va fi configurată să declanșeze la falling edge.

Pe lângă configurarea corectă a întreruperilor, pentru ca acestea să declanșeze trebuie

activate global folosind funcția sei(). Pentru a dezactiva global întreruperile se folosește funcția

cli().

Exemple

1. Unul dintre butoane incrementează valoarea lui PORTB cu o unitate la fiecare apasare iar

celalalt decrementează valoarea lui PORTB în același fel.

#include <avr/io.h> #include <avr/interrupt.h>

ISR(INT0_vect) {PORTB++;}

ISR(INT1_vect) {PORTB--;}

void main()

Page 14: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

14

{DDRB=0xFF; //PortB ca iesire MCUCR|=(1<<ISC01)|(1<<ISC11); //declanseaza INT0 si INT1 pe tranzitie H-L

GIMSK|=(1<<INT0)|(1<<INT1); //activeaza intreruperile pe INT0 si INT1 sei(); while(1); }

2. Butonul conectat la INT0 intrementează valoarea lui PORTB atât timp cât e apăsat iar celălalt

împarte la doi valoarea la fiecare ridicare a butonului. #include <avr/io.h>

#include <avr/interrupt.h>

ISR(INT0_vect) {PORTB++;}

ISR(INT1_vect) {PORTB>>=1;}

void main() {DDRB=0xFF; //PortB ca iesire MCUCR|=(1<<ISC10)|(1<<ISC11); //declanseaza INT0 pe low level si INT1 pe

tranzitie //L-H GIMSK|=(1<<INT0)|(1<<INT1); //activeaza intreruperile pe INT0 si INT1

sei();

while(1);

}

Tema 2 – un exemplu de rezolvare se găsește în folderul Tema2

1. Folosind circuitul construit, implementează un numărător binar pe 8 biți cu incrementare

la apăsarea unuia dintre butoane și decrementare pe celalalt buton astfel încât valoarea

maxima să fie 255 chiar dacă se mai apasă butonul de incrementare după ce s-a ajuns la

această valoare. Analog pentru valoarea minimă 0.

2. Modifică codul problemei 5 de la Tema 1 în asa fel încât unul dintre butoane să schimbe

viteza animației iar celalalt să schimbe animația.

3. Scrie un program care la fiecare apăsare a butonului notat în schemă S1 să selecteze pe

rând unul din leduri. Apăsarea butonului S2 schimbă starea ledului selectat.

4. Scrie un program care să aprindă ledul 0 apoi la fiecare apăsare a butonului S1 va deplasa

ledul / ledurile aprinse cu o pozitie spre dreapta. Butonul S2 va decrementa valoarea

portului cu o unitate. INT0 va declanșa pe falling edge iar INT1 pe rising.

5. Scrie un program care sa implementeze un astabil, monostabil si bistabil folosind 2 leduri.

Butonul S1 va selecta modul de functionare iar S2 va actiona ca trigger când este cazul.

Page 15: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

15

Partea 3 – The clock is ticking

Dacă până acum ai făcut programe în care pentru a întârzia execuția cu un anumit timp ai

folosit o metodă destul de ineficientă (funcția _delay_ms()) pentru delay-uri mari, acum vei

învăța cum poți folosi timerele de care dispune uC-ul pentru a executa la intervale de timp

regulate o anumită parte de cod, în același timp lăsând restul programului să funcționeze normal.

Pentru a înțelege cum funționează timerele și ce fac exemplele de mai jos, aruncă o

privire (atentă) în datasheet la:

Pag. 3 – Overview (doar primul paragraf)

Pag. 69 – 8-bit Timer/Counter0, Overview, Registers, Definitions

Pag. 70 – Timer/Counter Clock Sources, Operation

Pag. 71 – Timer/Counter Timig Diagrams

Pag. 72-73 – 8-bit Timer/Counter Register Description

Pag. 74 - Timer/Counter0 and Timer/Counter1 Prescalers, Internal Clock

Source

Deoarece, așa cum ai citit în datasheet, timerele funționează independent de restul

programului, pentru a ști când timerul a ajuns la valoarea maximă, vei folosi o întrerupere într-un

mod asemănător cu cele pe care le-ai folosit pentru butoane. Întreruperea va fi

ISR(TIMER0_OVF_vect) care va declanșa de fiecare dată când timerul ajunge la valoarea

maximă. Acum probabil ai să te întrebi la ce te ajută toate chestiile astea? Hai să luăm un

exemplu concret în care uCf - frecventa la care lucrează uC-ul (în cazul de față 1MHz), Tf -

frecvența la care funcționează timerul.

HzfuC 1000000

Alegem un prescaler de 1/1024 =>

Hzff uCT 56.9761024/

Știind că timerul este pe 8 biți rezultă că ISR(TIMER0_OVF_vect) va declanșa de

976/256=3.81 ori pe secundă. Ce faci dacă vrei ca o anumită parte de cod să se execute mai rar de

3.81 ori într-o secundă? Răspunsul este destul de simplu: implementezi un timer software, care

asemenea celui hardware va avea un prescaler stabilit de tine.

Notă: În cele ce urmează voi folosi notații și expresii de genul „declanșează la 3.81Hz” =

declanșează de 3.81 ori pe sec.

În cazul de față dacă vrei sa obții execuția unui cod la aproximativ 1Hz poți face ca în

exemplul de mai jos:

ISR(TIMER0_OVF_vect) // declanseaza la 3.81Hz {if(++t==3) //la fiecare a treia întrerupere se va executa codul {t=0; //resetează timerul software ...

codul tau //do the monkey business ... }

}

Exemplul de cod de mai sus dă o frecvență de execuție a codului de aproximativ 1.27Hz.

ATENȚIE! Frecvența de 1.27Hz obținută mai sus este frecvența la care se va intra în if, respectiv

în branch. Frecvența de execuție a instrucțiunilor din branch este HzfuC 1000000 .

Page 16: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

16

Exemple

Acum că ai văzut cam cum stă treaba cu calculele, hai să vedem și ceva exemple practice

de cod. Exemplele ce urmează se bazează pe exemplele anterioare pentru a înțelege mai bine cum

se pot combina noțiunile învățate până acum.

1. “Leduri clipitoare” – versiunea 2 #include <avr/io.h>

#include <avr/interrupt.h>

ISR(TIMER0_OVF_vect) {static uint8_t t=0; if(++t==3)

{t=0; if(PORTB) PORTB=0; else PORTB=0xFF; }

}

void main() {DDRB=0xFF;

TIMSK|=(1<<TOIE0); //Enable Timer0 overflow interrupt TCCR0|=(1<<CS02)|(1<<CS00); //Timer0 prescaler =f/1024

sei(); while(1);

}

2. “Moving LEDs” - aplicație folosește Timer0 pentru a obține delay-ul necesar aprinderii și

stingerii ledurilor. INT0 și INT1 sunt folosite pentru detecția apăsării butoanelor care modifică

viteza și direcția de deplasare.

#include <avr/io.h>

#include <avr/interrupt.h>

uint8_t speed=1,t=0,led=0,i=0, dir=0; ISR(TIMER0_OVF_vect) {if(++t==speed)

{t=0; if(dir) {if(!led) {if(i<=7) {i++; PORTB=(PORTB<<1)+1; } else {led=1; i=0;} }

Page 17: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

17

else {if(i<=7) {i++; PORTB>>=1;} else {led=0; i=0;} } } else {if(!led) {if(i<=7) {i++; PORTB=(PORTB>>1)+0x80; } else {led=1; i=0;} } else {if(i<=7) {i++; PORTB<<=1;} else {led=0; i=0;} } } }

}

ISR(INT0_vect) {dir=!dir; PORTB=0; t=0; i=0; led=0; } ISR(INT1_vect) {if(++speed==4) speed=1; t=0; }

void main() {DDRB=0xFF;

MCUCR|=(1<<ISC01)|(1<<ISC11); //trigger on falling edge GIMSK|=(1<<INT0)|(1<<INT1); //enable INT0 and INT1 trigger interrupt TIMSK|=(1<<TOIE0); //Enable Timer0 overflow interrupt TCCR0|=(1<<CS02)|(1<<CS00); //Timer0 prescaler =f/1024 sei(); while(1);

}

Page 18: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

18

Partea 4 – Fade to black

În această parte ne vom ocupa de PWM – Pulse Width Modulation. Spus mai „pe

românește” variezi timpul cât aplici 1 logic pe pinii care generează PWM. Astfel poti controla

intensitatea luminii ledurilor instalate pe placă, viteza motoarelor de curent continuu, etc, sau poți

încerca să îți creezi propria instalație pentru bradul de Crăciun.

Cum se folosește?

În datasheet este scris foarte detaliat (poate chiar prea detaliat) cum funcționează

generatoarele de PWM și care sunt modurile de lucru. La bază totul este legat de timere (Timer1

și Timer2) care au și alte funcții pe lângă cea de bază. Ca să înțelegi o idee mai bine îți recomand

să arunci o privire în datasheet peste următoarele:

Pag 59-60 – Alternate Port Functions – PortB: OC1A, OC1B,OC2

Pag 76-83 – Timer1

Pag 85-88 – Output Compare Units

Pag 88 – Modes of operation: Normal Mode, Fast PWM Mode

Pag 97-103 – Register Description (doar ce tine de PWM, fara Input

Capture)

Asemănător cu Timer1 se folosește și Timer2 cu câteva deosebiri: este pe 8 biți și are doar

un singur generator de PWM. Pentru a-ți fi mai ușor să folosești și Timer2 pentru PWM în

exemplele ce urmează vei folosi Timer1 configurat în așa fel încât să fie compatibil cu Timer2.

Ca și până acum configurarea corectă a hardware-ului este esențială în buna funcționare a

montajului. Câțiva pași care trebuiesc urmați în folosirea PWM-ului ar fi:

Setarea pinilor folosiți pentru PWM ca output.

Setarea modului de lucru al generatorului de PWM folosind tabelele din

datasheet

Inițializarea registrului OCR folosit cu o valoare dorită

Activarea Timerului corespunzător generatorului de PWM care se dorește

a fi folosit prin selectarea unei surse de ceas

Acești patru pași reprezintă de fapt patru linii de cod care pot fi scrise oriunde în cod atât

timp cât se respectă ordinea. De preferat este ca cele patru linii de cod sa fie scrise grupat pentru

a evita problemele.

Exemple

1. În acest exemplu un singur generator de PWM va fi folosit doar pentru a demonstra

implementarea în cod a celor patru pași descriși mai sus.

#include <avr/io.h> #include <util/delay.h> void main() {DDRB|=(1<<DDB1); //seteaza pinii de PWM ca output TCCR1A|=(1<<COM1A1)|(1<<WGM12)|(1<<WGM10);

Page 19: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

19

// configureaza pwm: Fast PWM 8bit, Non-inverting

OCR1A=0; // initializeaza OCR1A cu 0 TCCR1B|=(1<<CS10); //seteaza sursa de ceas: clk/1 (no prescalling) while(1) //creeaza un effect de fade destul de “nice” {while(++OCR1A<255) _delay_ms(5); while(--OCR1A>0) _delay_ms(5); } }

2. Acest al doilea exemplu este puțin mai complex folosind atât Timer1 cât și Timer2 pentru

a genera trei semnale PWM. În acest fel înlocuind cele 3 leduri de pe PCB conectate la

generatoarele de PWM cu un singur led RGB s-ar putea obtine orice culoare existentă.

Scopul exemplului este însă altul: evidențierea modului de lucru asemanator al timerelor 1

și 2.

#include <avr/io.h> #include <util/delay.h> void main() { DDRB|=(1<<DDB1)|(1<<DDB2)|(1<<DDB3);//seteaza pinii de PWM ca output // configureaza Timer1: Fast PWM 8bit, Non-inverting TCCR1A|=(1<<COM1A1)|(1<<COM1B1)|(1<<WGM12)|(1<<WGM10); OCR1A=255; // initializeaza OCR1A cu 255,OCR1B cu 0 OCR1B=0; // configureaza Timer2: Fast PWM 8bit, Non-inverting TCCR2|=(1<<COM21)|(1<<WGM21)|(1<<WGM20); OCR2=0; // initializeaza OCR2 cu 0 //seteaza sursa de ceas Timer1: clk/1 (no prescalling) TCCR1B|=(1<<CS10); TCCR2|=(1<<CS20); //seteaza sursa de ceas Timer2: clk/1 (no prescalling) while(1) { while(--OCR1A>0) {OCR1B++; _delay_ms(5); } while(--OCR1B>0) {OCR2++; _delay_ms(5); } while(--OCR2>0) {OCR1A++; _delay_ms(5); } }}

Page 20: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

20

Partea 5 – Convertorul Analogic – Digital (ADC)

Până acum am văzut cum se pot folosi diverse module ale uC-ului pentru a afișa anumite

valori sau evenimente pe cele 8 leduri. Dacă în partea 2 (Push the button) am văzut cum putem

detecta evenimente exterioare de scurtă durată (apăsarea unui buton) ce se prezintă sub forma

unor semnale digitale (1 – butonul nu este apăsat, 0 – buton apăsat), în această parte a tutorialului

vom vedea cum putem citi semnale continue în timp (tensiunea pe o rezistență în acest caz).

Pentru a putea realiza acest lucru trebuie modificat montajul realizat anterior prin conectarea lui

AGND la GND, AVCC la VCC și AREF printr-un condensator la GND ca în schema prezentată

mai jos.

Page 21: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

21

Cum se folosește?

În datasheet funcționarea modulului de conversie analogic-digital este descrisă pe 13

pagini însă pentru a putea urma acest pas al tutorialului nu este necesară parcurgerea tuturor

paginilor. Totuși pentru o mai bună înțelegerea felului cum merg lucrurile îți recomand să citești

următoarele pagini:

Pag. 196-198 – Analog-to-Digital Converter - Features

Pag. 198 – Starting a Conversion, Prescaling and Conversion Timing

Pag. 200 – Changing Channel or Reference Selection

Pag. 201 – Voltage Reference

Pag. 205-208 - ADC Conversion Result, ADC Multiplexer Selection, ADC

Control and Status Register A, ADC Data Register

Acum voi explica pe rând ce înseamnă fiecare bloc despre care ai citit în paginile

enumerate mai sus:

Analog to Digital Converter (Convertor Analogic – Numeric): acest

dispozitiv are rolul de a converti valoarea unei tensiuni într-un număr.

Modul în care se face acest lucru diferă între tipurile de ADC. Pentru

aceasta are nevoie de o tensiune de referință, fixă, stabilă. În funcție de

tensiunea de referință și de rezoluția convertorului (numărul de biți pe care

lucrează), pentru aceeași valoare a tensiunii aplicate la intrare se pot obține

rezultate diferite. Așa cum ai citit la pag. 205 – ADC Conversion Result,

rezultatul (numărul) se obține după formula:

în cazul în

care se folosesc toți cei 10 biți ai convertorului.

Prescaler (pag. 198 – Prescalling and Conversion Timing) – ca orice

circuit digital, și ADC-ul are nevoie de un semnal de tact pentru a

funcționa. De frecvența acestui semnal depin timpul de conversie și

rezoluția maximă. Pentru a folosi ADC-ul la rezoluția maximă, Atmel

recomandă frecvențe între 50KHz și 200KHz. Timpul de conversie pentru

prima conversie este de 25 de perioade de tact (clock cycles) iar pentru

următoarele conversii este de 13 perioade.

ADC Multiplexer (pag. 200 – Changing Channel or Reference Selection):

Este posibil ca unui singur convertor să i se atribuie mai multe canale de

intrare, ca și în cazul uC-ului ATMega8 care are un singur ADC cu 5

canale. Acestea se conectează la convertor câte unul la un moment dat prin

intermediul unui multiplexor. Multiplexorul se comporta ca un comutator

cu mai multe pozitii. Poziția este dată de biții MUX3:0 din registrul

ADMUX. Astfel pentru canalul 0 (poziția 0) biții vor avea valorile 0000 (0

în zecimal) iar pentru canalul 5 (poziția 5) vor avea valorile 0101 (5 în

zecimal). Pentru celelalte combinații existente vezi Tabelul 75, pag. 206.

ADC Voltage Reference (Tensiunea de referință) – Așa cum am explicat

mai sus, de tensiunea de referință depinde în mod direct rezultatul

conversiei pentru aceeași tensiune aplicată la intrare. Convertorul existent

în ATMega8 oferă posibilitatea de a alege între 3 referințe: AREF

Page 22: Tutorial AVR (03.06.2012)

Tutorial – Introducere în programarea și utilizarea microcontrollerelor Atmel AVR

22

(tensiunea aplicată pe pinul AREF), AVCC (tensiunea de alimentare a

convertorului, 5V, aplicată pe pinul AVCC) sau referința internă de 2.56V.

Este recomandat ca tensiunea de referință să se aleagă cât mai apropiată de

valoarea maximă pe care o poate atinge semnalul de intrare însă sa nu fie

mai mică.

Exemple

1. Acest exemplu demonstrează cum se setează ADC-ul pentru a citi în mod continuu valoarea

tensiunii aplicată pe canalul 0 cu afișare pe cele 8 leduri.

#include <avr/io.h> void adc_init(void); uint8_t adc_read(uint8_t channel); int main(void) { DDRB=0xFF; //Portb devinde iesire adc_init(); //Initializeaza ADC while(1) //Bucla infinita { PORTB = adc_read(0); //Transmite pe PortB valoarea citita pe canalul 0 } } void adc_init() { ADMUX|=(1<<REFS0) //Referinta AVCC, | (1<<ADLAR);//Deplaseaza rezultatul la stanga (cei mai semnificativi 8 biti in ADHC) ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) //Prescaler: F_CPU/128 | (1<<ADEN); //Activeaza ADCul } uint8_t adc_read(uint8_t channel) { ADMUX &= 0xE0; //Sterge vechiul canal ADMUX |= channel; //Seteaza noul canal ADCSRA|=(1<<ADSC); //Porneste conversia while(!(ADCSRA&(1<<ADIF))); //Asteapta sfarsitul conversiei return ADCH; //Returneaza rezultatul (pe 8 biti) }