Capitole utile din Datasheet ATmega324
PWM (Pulse Width Modulation) este o tehnică folosită pentru a varia în mod controlat tensiunea dată unui dispozitiv electronic. Această metodă schimbă foarte rapid tensiunea oferită dispozitivului respectiv din ON în OFF și invers (treceri rapide din HIGH în LOW, de exemplu 5V - 0V). Raportul dintre perioada de timp corespunzătoare valorii ON și perioada totală dintr-un ciclu ON-OFF se numește factor de umplere (duty cycle) și reprezintă, în medie, tensiunea pe care o va primi dispozitivul electronic. Astfel, se pot controla circuite analogice din domeniul digital. Practic, asta înseamnă că un LED acționat astfel se va putea aprinde / stinge gradual, iar în cazul unui motor acesta se va învârti mai repede sau mai încet.
În cazul unui motor, căruia i se aplică un semnal PWM cu factor de umplere de 0%, viteza de rotație a acestuia va fi egală cu 0 rpm. Un factor de umplere de 100% va duce la o turație maximă a acestuia.
Factorul de umplere se exprimă în procente și reprezintă cât la sută din perioada unui semnal acesta va fi pe nivelul ON. În Figura 1 se pot observa semnale PWM cu factori de umplere diferiți. Astfel, se poate deduce foarte ușor formula pentru a obține valoarea factorului de umplere (D):
D = t_on / (t_on + t_off) * 100 = pulse_width / period * 100
Astfel, tensiunea medie care ajunge la dispozitiv este dată de relația: D * Vcc.
<imgcaption image8 center |Semnal PWM cu diferiți factori de umplere ></imgcaption>
Modularea folosește variația factorului de umplere a unei forme de undă dreptunghiulară pentru a genera la ieșire o tensiune analogică. Considerând o formă de undă dreptunghiulară f(t) cu o valoare minimă ymin și o valoare maximă ymax și factorul de umplere D (ca în figura <imgref image8>) valoarea medie a formei de undă e dată de relația:
cum f(t) este o formă de undă dreptunghiulară valoarea sa maximă se atinge pentru 0<t<D*T.
Multe circuite digitale pot genera semnale PWM. Majoritatea microcontroller-elor oferă această facilitate, pe care o implementează folosind un numărător care este incrementat periodic (conectat direct sau indirect la o unitate de ceas) și care este resetat la sfârșitul fiecărei perioade a PWM-ului. Când valoarea numărătorului este mai mare decât valoarea de referință, ieșirea PWM (output-ul) trece din starea HIGH în starea LOW (sau invers).
În cadrul laboratorului trecut am văzut că microcontroller-ul Atmega324 are 3 timere: Timer0 pe 8 biți, Timer1 pe 16 biți și Timer2 pe 8 biți.
Fiecare dintre aceste timere putea fi configurat în diferite moduri. În cazul timer-ului 1 configurarea se realiza prin intermediul registrelor TCCR1A și TCCR1B cu ajutorul biților WGM13 - WGM10
. Printre aceste moduri se numărau:
Atmega324 dispune de 6 canale de PWM distribuite astfel:
Conform tabelului 16-5. din datasheet, Timer-ul 1 mai poate fi configurat să funcționeze și în modul PWM. Din punct de vedere al microcontroller-ului Atmega324 există 3 tipuri de PWM:
Numărarea se face doar pe frontul crescător al semnalului de ceas. În modul Fast PWM, modificarea factorului de umplere se realizează instant, în schimb semnalul nu este centrat (este defazat, practic apare un “glitch” la modificarea semnificativă a factorului de umplere). Se utilizează pentru majoritatea aplicațiilor, mai puțin cele în care este nevoie de un control precis (de exemplu motoare BLDC, audio). Există mai multe moduri de Fast PWM oferite de către microcontroller. De exemplu, pentru Timer-ul 1 avem:
<imgcaption image11 center |Fast PWM Timing></imgcaption>
Pentru variantele de Fast PWM oferite de către celelalte timere (0 și 2), studiați datasheet-ul.
Numărarea se face pe ambele fronturi ale ceasului. Pentru aceleași configurații ca Fast PWM, Phase Correct PWM este de două ori mai lent (dar are o rezoluție mai bună). În comparație cu modul Fast PWM, semnalul este centrat (modificare simetrică a factorului de umplere), efectul fiind practic vizibil la modificarea factorului de umplere.
Există și pentru acest mod mai multe variante de configurare, asemănătoare celor de la Fast PWM.
<imgcaption image12 center |Phase Correct PWM Timing></imgcaption>
Comparație între Fast PWM și Phase Correct PWM:
<imgcaption image9 center |Fast PWM (stanga) vs. Phase Correct PWM (dreapta)></imgcaption>
Principala diferență între acest mod și cel de Phase Correct este momentul în care este actualizat registrul OCRnx
. Practic, spre deosebire de modul Phase Correct PWM se păstrează durata semnalului ON pe toată perioada T_on + T_off și rezultă un semnal “curat”, fără distorsiuni (de exemplu frecvențe nedorite care pot avea ca efect pierderi de energie în motoare DC).
<imgcaption image12 center |Phase And Frequency Correct PWM Timing></imgcaption>
Lucrul cu PWM-ul presupune inițializarea unui timer și configurarea output-ului pe pini. Fiecare timer are doi pini pe care genera ca output un astfel de semnal (cele două canale): Timer0 are OC0A
și 0C0B
, Timer1 are OC1A
și OC1B
etc.
<imgcaption image123 center |ATmega324 PWM Output Pins></imgcaption>
De exemplu, presupunem că avem Timer1 configurat pe modul de funcționare Fast PWM (modul de funcționare nu trebuie neapărat să conțină cuvântul 'PWM' ca să poată fi folosit pentru generarea unui semnal). Fast PWM este caracterizat de o frecvență fixă și un prag stabilit de programator, ce poate fi modificat în timpul execuției.
Vom configura tipul de output cu biții numiți COM..
din registrele TCCR..
ale timer-ului corespunzător (în exemplul nostru timer-ul 1). Descoperim în capitolul 16 (16-bit Timer/Counter1 and Timer/Counter3 with PWM) - secțiunea Register Description - TCCR1A, TCCR1B și TCCR1C
Vedem că:
COM..
special pentru modul Fast PWM
COM
pentru fiecare mod de funcționare a PWM-ului!
1 0
pentru biții COM1A1 COM1A0
va lăsa semnalul de pe pinul OC1A
pe 1 în timpul numărătorii (până la atingerea pragului) și va pune semnalul pe 0 de la atingerea pragului până la capătul unui ciclu (numărare completă până la TOP). Modificările făcute asupra semnalului în funcție de prag și counter în acest mod se pot observa în desenul din Figura 4.OCR1A = x * TOP / 100
(pentru Fast PWM, TOP poate fi 0xFF, 0x1FF sau 0x3FF, în funcție de configurația aleasă)<imgcaption image11 center |Fast PWM Timing></imgcaption>
<imgcaption image12 center |Phase Correct PWM Timing></imgcaption>
//PD5 output - OC1A este PD5 DDRD |= (1 << PD5); //Conform tabelului 16-5 din datasheet, pentru modul Fast PWM 8-bit, biții WGMn0 si WGMn2 au valoarea 1 TCCR1A |= (1 << WGM10); //TCCR1A conține doar biții WGM10 si WGM11, WGM12 și WGM13 se găsesc in TCCR1B TCCR1B |= (1 << WGM12); //Conform tabelului 16-3 din datasheet, pentru modul non-inverting COM1A1 = 1 și COM1A0 = 0 TCCR1A |= (1 << COM1A1); //Conform tabelului 16-6 din datasheet, pentru Prescaler de 1024 scriem 1 pe CS12 si CS10 TCCR1B |= (1 << CS12) | (1 << CS10); //Pragul la care se schimbă semnalul (Fig. 4); fill-rate 0.5: jumătate din timp 1, jumătate din timp 0 //Deoarece în acest mod TOP este 0xFF, OCR1A va fi 50 * 255 / 100 OCR1A = 255 / 2;
Obiectivul exercițiilor este să controlăm culoarea unui LED RGB cu ajutorul PWM. Un LED RGB este compus din 3 diode care emit culori diferite: una roșie, una verde și una culoare albastră. Cu acest LED se poate obține orice culoare printr-o combinație de intensități pe fiecare diodă în parte. Cele trei LED-uri sunt conectate la:
OC1A
este asociat timer-ului 1 (pinul PD5
). Acest timer controlează LED-ul roșu
.OC2A
este asociat timer-ului 2 (pinul PD7
). Acest timer controlează LED-ul verde
.OC0A
este asociat timer-ului 0 (pinul PB3
). Acest timer controlează LED-ul albastru
.LED-urile sunt conectate in modul “active-low” (LED-ul este aprins atunci cand pinul aferent este LOW, si stins atunci cand pinul este HIGH).
<imgcaption ledrgb center|> </imgcaption>
TOP
la ICR1
.ICR1
pentru ca frecvența la care funcționează timer-ul sa fie exact 1Hz.canalul A
să rămână 25%, iar pentru canalul B
să rămână 50%.COM
trebuie setate conform tabelului 16-3 din datasheet (Capitolul 16.11.1). Hint: Deoarece avem LED-uri intr-o configuratie active-low, trebuie ales modul 'inverting'.PB2
pentru a cicla prin valorile posibile ale acestuia.PB2
.PD6
pentru a controla valoarea factorului de umplere.update_color
pentru a da o nouă culoare LED-ului RGB. Această funcție este apelată periodic de bucla principală.update_color
va trebui să funcționeze după următorul automat de stări:roșu
și verde
au duty cycle de 100%, albastru
de 0%. În această stare, roșu
scade și albastru
crește. Se trece în starea 2 când roșu
are duty cycle 0%, albastru
are duty cycle 100%.roșu
crește și verde
scade. Se trece în starea 3 când roșu
are duty cycle 100% și verde
are duty cycle 0%.verde
crește și albastru
scade. Se trece în starea 1 când verde
are duty cycle 100% și albastru
are duty cycle 0%.PD4
, așa cum am văzut în primul laborator. PD4
este și ieșirea B a timer-ului 1, deci am putea folosi un factor de umplere de 50% pentru a produce sunet pe speaker. Scopul acestui exercițiu este să redați melodia pusă în scheletul de laborator.surprise_notes
sunt diferitele note folosite, cu valorile în Hertzidurations
este un vector cu duratele asociate fiecărei note din surprise_notes
CTC
cu TOP la OCR1A
și setați prescaler-ul la valoarea 1024
.update_song
astfel încât la intervalele date de durations
schimbați frecvența Timer-ului 1 (care e determinată de OCR1A) pentru a reda frecvențele din surprise_notes
; funcția este apelată în bucla principală la fiecare 25ms.TCNT
la fiecare update al TOP
-ului. Altfel, în unele situații, va trebui să așteptați un overflow pentru ca TCNT
să se reseteze.PD5
. Faceți modificările necesare astfel încât LED-ul și melodia să funcționeze în același timp.