Laboratorul 3: Timere, Pulse Width Modulation (PWM)

Capitole utile din Datasheet ATmega324P

  • 1. Pin Configurations
    • secțiunea 1.1 - pag. 15
  • 16. 8-bit Timer/Counter0 with PWM
    • secțiunea 16.9 - Register Description - pag. 140
  • 17. 16-bit Timer/Counter1 with PWM
    • secțiunea 17.14 - Register Description - pag. 173
  • 19. 8-bit Timer/Counter2 with PWM and asynchronous operation
    • secțiunea 19.11 - Register Description - pag. 202

1. Pulse Width Modulation

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, spre 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.

Practic, se pot controla circuite analogice din domeniul digital. Spre exemplu, unui LED i se va putea controla intensitatea luminoasă, la o serie de LED-uri RGB se va putea controla precis chiar și culoarea, unui speaker – frecvența sunetului redat la un moment de timp, unui motor – viteza de rotație etc.

1.1. Principiul de funcționare

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 de mai jos, 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[\%] = \frac{t\_on}{t\_on + t\_off} \cdot 100 = \frac{pulse\_width}{period} \cdot 100$

Astfel, tensiunea medie care ajunge la dispozitiv este dată de relația: D * Vcc.

Figura 8. Semnal PWM cu diferiți factori de umplere

Modularea folosește variația factorului de umplere a unui semnal dreptunghiular pentru a genera la ieșire o tensiune analogică. Considerând o formă de undă dreptunghiulară f(t) cu o valoare minimă ymin=0 și o valoare maximă ymax și factorul de umplere D (ca în figură) valoarea medie a formei de undă e dată de relația:

$\bar{y} = D \cdot ymax$

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).

Detalii

Detalii

Semnalul PWM poate fi generat și software prin mai multe metode: bit-banging, timer cu ISR. Practic orice metodă prin care se poate genera software secvența:

  • Set Pin High
  • Wait for T_on
  • Set Pin Low
  • Wait For T_off

Totuși, în multe situații se dorește o frecvență de ordinul kHz sau zeci de kHz a semnalului PWM (ex. controlul motoarelor DC sau Brushless DC), iar o astfel de metodă nu este cea mai eficientă, fiind necesară intervenția procesorului pentru tratarea întreruperilor frecvente.

2. Lucrul cu PWM pe microcontrollere AVR

În cadrul laboratorului anterior, 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 prin intermediul registrelor TCCRnA și TCCRnB, cu ajutorul biților WGMnx. Printre aceste moduri se numărau:

  • Normal mode
  • CTC mode (Clear Timer on Compare match) cu TOP la OCRnA
  • Fast PWM – pe care îl vom folosi noi azi!
  • Phase Correct PWM etc.

La modurile de lucru cu PWM, timer-ul va folosi unul dintre comparatoarele atașate (cu valorile din registrele OCRnA / OCRnB) pentru a stabili valoarea unui pin de output (fiecare timer are câte 2 canale de output denumite: OCnA și OCnB).

Pentru a vedea exact ce pini are asociați ca output unui timer, vedeți capitolul Pin Configurations din Datasheet-ul ATmega324P:

 PWM Output Pins

Ne vom ocupa în principal de modul Fast PWM care este utilizat pentru majoritatea aplicațiilor, mai puțin cele în care este nevoie de un control precis (de exemplu motoare BLDC, audio).

Fast PWM

La modul Fast PWM, numărarea se face doar pe frontul crescător al semnalului de ceas. 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).

Există mai multe moduri de Fast PWM oferite de către microcontroller. De exemplu, pentru Timer-ul 1 avem:

  • Fast PWM pe 8 biți, cu valoarea de TOP = 0x00FF.
  • Fast PWM pe 9 biți, cu valoarea de TOP = 0x01FF.
  • Fast PWM pe 10 biți, cu valoarea de TOP = 0x03FF.
  • Fast PWM cu valoarea de TOP în ICR
  • Fast PWM cu valoarea de TOP în OCRnA

În acest laborator vom folosi doar modul Fast PWM pe 8 biți. Pentru variantele de Fast PWM oferite de către celelalte timere (0 și 2) și pentru celelalte moduri PWM, consultați datasheet-ul.

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.

  • Modul 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).
  • Pentru a obține un factor de umplere x%, aici vom seta OCR1A = x * TOP / 100 (pentru Fast PWM, TOP poate fi 0xFF, 0x1FF sau 0x3FF, în funcție de configurația aleasă)

Exemplu de inițializare a Timer1 in modul Fast PWM 8-bit non-inverting, cu Prescaler la valoarea 1024 (frecvența PWM-ului va fi de 12MHz / 1024 / 255 ~= 45 Hz):

/* OC1A is PD5, must be set to OUTPUT */
DDRB |= (1 << PD5);
/* We must choose Fast PWM 8-bit, WGMn[3:0] = 0b0101 */
/* Note: WGM11 and WGM10 are on TCCR1A, while WGM12 and WGM13 are on TCCR1B! */
TCCR1A = (1 << WGM10);
TCCR1B = (1 << WGM12);
/* set COM1A to non-inverting output: COM1A[1:0] = 0b10 */
TCCR1A |= (1 << COM1A1);
/* Use 1024 prescaler: CS1[2:0] = 0b101 */
TCCR1B |= (1 << CS12) | (1 << CS10);
/* set duty cycle to 50%; note that we're using 8-bit mode, so TOP = 255 */
OCR1A = 127;

Phase Correct PWM

Deși nu îl vom folosi la laborator, este util de știut diferențele între modul de PWM rapid și acesta care poate fi mai precis (necesar în unele aplicații, precum controlarea motoarelor).

Principala diferență între acest mod și cel de Fast PWM este faptul că timer-ul va porni de la BOTTOM (i.e., poziția zero), va urca până la TOP (valoarea maximă) apoi va număra, descrescător, înapoi la BOTTOM. Comparatorul va da semnalul de output după aceiași regulă ca la Fast PWM, în funcție de rezultatul comparării cu registrul OCRnx. Însă cea mai importantă diferență este faptul că valoarea lui OCRnx rămâne salvată pe toată durata unui ciclu complet, astfel se păstrează durata semnalului ON pe toată perioada T_on + T_off și rezultă un semnal “curat”, fără decalaje ale frecvenței de PWM (de exemplu, unele frecvențe nedorite ar putea avea pierderi de energie în motoare DC).

 Phase Correct PWM Timing

Comparație între Fast PWM și Phase Correct PWM:

Fast PWM (stanga) vs. Phase Correct PWM (dreapta)

3. Exerciții

Obiectivul exercițiilor este să controlăm culoarea unui LED RGB cu ajutorul PWM. 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:

  • Red: pinul PD5 are funcția OC1A (asociat Timer-ului 1);
  • Green: pinul PD7 are funcția OC2A (asociat Timer-ului 2);
  • Blue: pinul PB3 are funcția OC0A (asociat Timer-ului 0);

Reminder: LED-urile sunt conectate in modul anod comun / “active-low” (LED-ul este aprins atunci cand pinul aferent este LOW, si stins atunci cand pinul este HIGH).

Task 0:

  1. Descărcați arhiva cu scheletul de cod și rulați exemplul. Ce observați că se întamplă pe plăcuță?
    • Conectați-vă și verificați consola de monitorizare a serialei; vă mai amintiți de la laboratorul trecut cum se făcea temporizarea?
    • Ce puteți spune despre comportamentul LED-ului? Ce timer este folosit și în ce mod este el configurat?
    • Unde se modifică intensitatea? Ce input primește acea formulă și unde se folosește output-ul?

Task 1:

  1. Dorim să facem și culoarea blue să se aprindă similar;
    • Vedeți mai sus ce timer va trebui să configurați! Folosiți tot modul Fast PWM (însă, obligatoriu: citiți și folosiți descrierea registrelor de la Secțiunea 12.9 din Datasheet-ul lui ATmega324P, acestea diferă față de cele de la Timer 1!);
    • Faceți ca această nouă culoare să pulseze mai rapid (și în paralel) decât cea roșie! Ce observați?

Task 2:

  1. Pentru a nu o supăra pe Greta Thunberg, ne trebuie și culoarea green! Însă, cum probabil vă imaginați, este mai complicat, din cauză că GPIO-ul LED-ului PD7 are outputul asociat timerului 2, însă acesta este deja folosit pentru a număra milisecundele de la momentul pornirii MCU-ului (system ticks)…
    • No worries! Vom învăța să facem PWM manual, prin întreruperi, pe ideea: timer-ul nostru numără de la 0 la valoarea 188 apoi face Clear (executând întreruperea COMPA – care ne incrementează systicks');
    • Ne amintim că fiecare timer are 2 comparatoare, deci îl vom folosi pe al doilea (COMPB) pentru a genera o întrerupere (TIMER2_COMPB) la o valoare prag între 0 – 188 (deci valoarea medie pentru 50% va fi 94);
    • LED-ul verde va trebui aprins (deci GPIO-ul pus pe 0) din întreruperea comparatorului A (i.e., atunci când se resetează timerul) iar, la întreruperea comparatorului B, va trebui oprit (i.e., GPIO setat pe 1), astfel factorul de umplere fiind dat de valoarea registrului OCR2B ;)
    • Atenție: Valoarea comparatorului A va trebui să rămână tot OCR2A = 188 (dorim, în continuare, să avem numărătoarea de milisecunde)!

Task 3:

  1. Dorim să ciclăm prin toate culorile reprezentabile folosind cele 3 LED-uri RGB. Pentru a facilita acest lucru, le vom calcula în domeniul alternativ de reprezentare HSV (Hue, Saturation, Value), apoi le vom converti în RGB pentru a le putea outputa:
    • aveți formula implementată în funcția convert_HSV_to_RGB()!
    • parametrul Hue va da nuanța culorii, Saturation și Value pot fi hardcodate la 1 pentru intensitate maximă;
    • folosiți contorul de system ticks pentru a anima schimbarea culorii, similar cum era acesta folosit pentru a pulsa intensitatea la primele exerciții (desigur, dezactivați codul ce face acest lucru).

Task 4 (bonus):

  1. Speaker-ul de pe placă este conectat pe pinul 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 (vedeți modulul sound.c).
    • surprise_notes sunt diferitele note folosite, cu valorile frecvențelor în Hertzi;
    • durations este un vector cu duratele asociate fiecărei note din surprise_notes;
    • update_notes va trebui să redea notele muzicale în ordinea dorită, reconfigurând timer-ul mereu
    • Cel mai simplu va fi să reconfigurați Timer-ul 1 să fie în mod CTC, deci TOP va fi OCR1A ce va da frecveța semnalului; pentru a obține un semnal de 50%, OCR1B va trebui să fie la jumătate din valoarea primului registru;
    • Funcția update_notes va trebui apelată în bucla principală la fiecare 25ms (folosiți systicks pentru a știi când a trecut acest interval de timp de la ultimul update).
    • Atenție: resetați 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.
  2. Bonus 2: animarea culorilor RGB de la exercițiul anterior și redarea sunetelor trebuie să funcționeze în același timp!

4. Linkuri utile

5. Responsabili laborator

pm/lab/lab3-2023-2024.txt · Last modified: 2024/03/24 22:18 by teodor.dicu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0