Differences

This shows you the differences between two versions of the page.

Link to this comparison view

cn2:laboratoare:07:continut [2023/11/21 19:26]
mihnea.dinica [Timere și ATtiny20] - prescaler
— (current)
Line 1: Line 1:
-==== Timer/​Counter ==== 
  
-În esență, un timer este un simplu numărător implementat în hardware: 
-<code verilog> 
-reg [7:0] counter; 
- 
-always @(posedge clk) 
-    if (reset) 
-        counter <= 0; 
-    else 
-        counter <= counter + 1; 
-</​code>​ 
-Datorită faptului că un timer e implementat în hardware, incrementarea lui este complet transparentă pentru programator,​ permițându-i acestuia să execute alte task-uri. 
- 
-Un bun exemplu de utilizare a unui timer este păstrarea evidenței timpului într-o aplicație. Putem configura un "​stopwatch"​ care să declanșeze un eveniment ce va trebui tratat de software. Astfel, putem deduce că este foarte util ca un timer să poată genera o //​întrerupere//,​ adică o funcție care se execută atunci când un anumit eveniment hardware are loc (exemplu: counter-ul a ajuns la o valoare prag stabilita sau la punctul de overflow). 
- 
-În laboratorul de astăzi vom aprofunda conceptul de timere (fără întreruperi). 
- 
-În principiu, orice timer ar trebui să aibă următoarele caracteristici:​ 
-  * O **sursă de ceas** în baza căreia numără (în general, la fiecare tranziție pozitiva a acesteia) 
-    * Opțional, această sursă de ceas poate fi divizată în prealabil (aplicat un prescaler) pentru a număra la intervale mai rare de timp 
-  * Un registru **contor** în care este stocată valoarea curentă a numărătorii 
-    * În funcție de lățimea (numărul de biți) acestui registru, timer-ul poate număra până la o valoare mai mică sau mai mare 
-    * În general valoarea acestui registru este incrementată,​ adica timerele numără, în general, crescător ​ 
-    * Stabilim două valori de referință: ​ 
-      * **BOTTOM** (cea mai mică valoare de la/până la care poate număra timer-ul) 
-      * **MAX** (cea mai mare valoare până la/de la care poate număra timer-ul) 
-      * BOTTOM și MAX sunt două valori intrinseci implementării hardware a timer-ului (nu pot fi schimbate din software) 
-    * Opțional timer-ul poate implementa și o valoare configurabilă **TOP**, care poate lua orice valoare între BOTTOM și MAX 
-      * Atunci când contorul întâlnește această valoare, se pot întâmpla mai multe lucruri, depinzând de configurare:​ 
-        * Contorul se resetează la BOTTOM 
-        * Timer-ul începe sa numere descrescător 
-        * Generează un eveniment și se oprește din numărat (se mai numește și //one-shot timer//) 
- 
-<​note>​ 
-Denumirile BOTTOM, MAX și TOP sunt în concordanță cu terminologia Microchip, în [[http://​ww1.microchip.com/​downloads/​en/​DeviceDoc/​Atmel-8235-8-bit-AVR-Microcontroller-ATtiny20_Datasheet.pdf|datasheet-ul ATtiny20]], pagina 60. 
-</​note>​ 
- 
-==== PWM ==== 
- 
-[[http://​en.wikipedia.org/​wiki/​Pulse-width_modulation|Pulse-width modulation]] este o tehnică de generare a unui semnal analogic pornind de la unul digital (adică de a înlocui un [[http://​en.wikipedia.org/​wiki/​Digital-to-analog_converter|DAC]]). Fiindcă logica digitală nu poate genera la ieșire decât tensiuni de Vcc sau de GND (ideal vorbind), PWM își propune să sintetizeze tensiuni cuprinse între aceste valori prin comutarea foarte rapidă între cele două. ​ 
- 
-Spre exemplu, presupunând că avem o tensiune Vcc de 5V și GND de 0V, dacă am comuta //foarte// repede între 0V și 5V ieșirea, menținând procentele egale de timp între cele două nivele logice (adică la fel de mult pe 1 ca și pe 0), atunci am putea spune că, **în medie**, tensiunea noastră de ieșire este de 2.5V. Asemănător,​ dacă am păstra aceeași frecvență de comutație, însă am tine ieșirea 75% din timp pe 1 și 25% din timp pe 0, atunci am putea spune că avem o ieșire cu o tensiune medie de 0.75 * 5V + 0.25 * 0V = 3.75V. 
- 
-Putem spune, așadar, că PWM se traduce prin **modulare în factor de umplere** (factor de umplere înseamnă raportul dintre perioada cât stă semnalul pe 1 și perioada totală). Prin urmare tensiunea rezultată dintr-un semnal PWM este direct proporționala cu factorul de umplere. Astfel putem, plecând de la semnale digitale, să generăm semnale analogice. In cazul unui LED, spre exemplu, actionandu-l prin PWM putem sa-l aprindem/​stingem gradual, dand impresia ca isi modifica, de fapt, intensitatea. 
- 
-{{ :​cn2:​laboratoare:​07:​pwm_modulation.gif?​400 |}} 
-{{ :​cn2:​laboratoare:​07:​pwm_led.gif?​200 |}} 
- 
-=== Ce legătura are PWM cu timerele? === 
- 
-Un timer poate continua să numere după ce generează un eveniment. Grafic, este această imagine (în desen, valoarea la care se generează evenimentul este denumită "​Compare"​):​ 
- 
-{{ :​cn2:​laboratoare:​08:​avrtimer2.png?​500 |PWM folosind un timer}} 
- 
-Asociat numărătorului se afla un output: 
-<code verilog> 
-    assign out = (counter >= compare); 
-</​code>​ 
- 
-Putem observa că factorul de umplere al acestui semnal este invers proporțional cu valoarea lui ''​Compare''​. Ca atare, putem genera un semnal PWM. Cand valoarea counter-ului este mai mica decat valoarea de prag '​Compare',​ consideram ca semnalul PWM este pe LOW; cand este mai mare decat valoarea de prag, semnalul este pe HIGH; crescand aceasta valoare de prag, semnalul va sta mai putin in starea HIGH, ceea ce inseamna ca duty cycle va scadea. 
-==== Timere și ATtiny20 ==== 
- 
-Informațiile particulare despre timerele mictrocontrollerului ATtiny20 le putem găsi în [[http://​ww1.microchip.com/​downloads/​en/​DeviceDoc/​Atmel-8235-8-bit-AVR-Microcontroller-ATtiny20_Datasheet.pdf|datasheet]],​ capitolele 11 și 12. Noi vom implementa doar ''​Timer/​Counter0''​. 
- 
-Timerele ATtiny20 au 3 registre foarte importante: 
- 
-=== TCNT0 (Timer/​Counter Register) === 
- 
-Este registrul cu rolul de **contor**. 
- 
-<note important>​ 
-Incrementarea acestui registru se face automat de către hardware! 
-</​note>​ 
- 
-=== OCR0A/OCR0B (Output Compare Register A/B) === 
- 
-Aceste două registre conțin fiecare câte o valoare de **comparație** între BOTTOM și MAX. Atunci când ''​TCNT0 == OCR0A''​ sau ''​TCNT0 == OCR0B''​ se pot întâmpla mai multe lucruri, în funcție de configurare:​ 
-  * Nimic 
-  * Timer-ul se resetează (TOP este configurat să ia valoarea OCR0A/​OCR0B) 
-  * Timer-ul generează un eveniment și continuă să numere 
-  * Timer-ul generează un eveniment și se resetează (TOP este configurat să ia valoarea OCR0A/​OCR0B) 
- 
-Evenimentul generat atunci când ''​TCNT0 == OCR0A''​ sau ''​TCNT0 == OCR0B''​ poate fi: 
-  * O întrerupere 
-  * Schimbarea stării pinului OC0A/OC0B (pentru PWM mai ales) 
- 
-Registrele OCR0A/OCR0B pot fi folosite și pentru a genera un semnal PWM dacă timer-ul este configurat să genereze un eveniment de comparație și să continue să numere, și acel eveniment este schimbarea stării pinului OC0A/OC0B. În acest caz putem varia factorul de umplere modificând valoarea registrului OCR0A/OCR0B (în imaginea din capitolul precedent, valoarea "​Compare"​),​ iar semnalul PWM va fi dispoibil pe pinul OC0A/OC0B (în imaginea din capitolul precedent, valoarea "​Output"​). 
- 
-Fiindcă sunt două la număr înseamnă că putem genera două semnale PWM folosind un singur timer. 
- 
-=== TCCR0A/​TCCR0B (Timer/​Counter Control Register) === 
- 
-Acestea sunt registrele de **control** pentru modul de funcționare al timer-ului. În realitate, cele două alcătuiesc un singur registru de control, TCCR0, pe 16 biți. Registrul respectiv conține multe informații de configurare codificate În biții săi. O lista completă a acestora o puteți găsi în datasheet, capitolul 11.9.1 și 11.9.2 paginile 69-73, însă un rezumat scurt vom face aici. 
- 
-{{ :​cn2:​laboratoare:​08:​tccr0a.png?​ |TCCR0A}} 
- 
-Remarcăm faptul că registrul TCCR0A conține 3 sub-valori: 
-  * Biții 7:6 conțin valoarea **COM0A**, pe 2 biți, ce controlează funcția pinului de I/O OC0A 
-  * Biții 5:4 conțin valoarea **COM0B**, pe 2 biți, ce controlează funcția pinului de I/O OC0B 
-  * Biții 1:0 conțin (o parte din) valoarea **WGM0** (Waveform Generation Mode), un număr pe 3 biți (al cărui ultim bit se găsește În registrul TCCR0B) care setează modul de funcționare al timer-ului: normal, fast PWM, phase-correct PWM, CTC, etc. Vom vedea imediat ce face fiecare mod de funcționare 
- 
-{{ :​cn2:​laboratoare:​08:​tccr0b.png?​ |TCCR0B}} 
- 
-Remarcăm faptul că registrul TCCR0B conține 2 sub-valori: 
-  * Bitul 3 conține valoarea WGM02 din **WGM0**, nu are semnificatie de sine stătătoare 
-  * Biții 2:0 conțin valoarea **CS0** (Clock Source), pe 3 biți, care dictează valoarea prescalerului (dacă este cazul), respectiv dacă timer-ul este pornit sau oprit 
- 
-**Prescalerul** are rolul de a diviza frecventa ceasului. Astfel, in loc sa incrementam contorul unui timer la fiecare oscilatie, putem alege sa o facem la fiecare 2/​4/​8/​16/​32/​64/​etc oscilatii. 
- 
-<​note>​ 
-Literele A și B din denumirile registrelor TCCR0A/​TCCR0B nu au nici o legătură cu canalele A și B de la registrele OCR0A/​OCR0B. 
-</​note>​ 
- 
-=== Moduri de funcționare === 
- 
-După cum spuneam, timer-ul poate avea comportament diferit în funcție de configurare. Modurile de funcționare controlează direcția de numărare (crescătoare,​ descrescătoare sau ambele), valoarea TOP și parțial comportamentul pinilor OC0A/OC0B. 
- 
-Modul de funcționare poate fi setat modificând biții WGM0 din registrele TCCR0A și TCCR0B. Ele pot fi: 
-  * **Normal** (''​WGM0 == 0''​) 
-    * Timer-ul numără de la 0 (deci de la ''​BOTTOM == 0''​) crescător până la 255 (deci până la ''​TOP == MAX == 255''​),​ apoi se resetează înapoi la 0 
-    * Starea pinilor OC0A/OC0B poate fi schimbată atunci când se atinge pragul de comparație (OCR0A/​OCR0B) 
- 
-  * **CTC** (Clear Timer on Compare Match - ''​WGM0 == 2''​) 
-    * Timer-ul numără de la 0 (deci de la ''​BOTTOM == 0''​) crescător până la OCR0A (deci până la ''​TOP == OCR0A''​),​ apoi se resetează înapoi la 0 
-    * Starea pinilor OC0A/OC0B poate fi schimbată atunci când se atinge pragul de comparație (OCR0A/​OCR0B) 
- 
-{{ :​cn2:​laboratoare:​08:​ctc.png?​695x339 |CTC}} 
- 
-  * **Fast PWM** (''​WGM0 == 3 sau WGM0 == 7''​) 
-    * Timer-ul numără de la 0 (deci de la ''​BOTTOM == 0''​) crescător până la 255, sau până la OCR0A (deci până la ''​TOP == MAX == 255'',​ dacă ''​WGM0 == 3'',​ sau ''​TOP == OCR0A'',​ dacă ''​WGM0 == 7''​),​ apoi se resetează înapoi la 0 
-    * Starea pinilor OC0A/OC0B poate fi schimbată atunci când se atinge pragul de comparație (OCR0A/​OCR0B) și atunci când se atinge valoarea BOTTOM 
- 
-{{ :​cn2:​laboratoare:​08:​pwm.png?​695x339 |Fast PWM}} 
- 
-=== Exemplu de configurare === 
- 
-Timer-ul 0 al unui microcontroller ATtiny20 se poate configura în modul normal astfel: 
- 
-<code asm> 
-  ldi     R16, 0b00000000 ​   ; COM0A = 0 (normal port operation, OC0A disconnected) 
-                             ; COM0B = 0 (normal port operation, OC0B disconnected) 
-                             ; WGM0[1:0] = 0 (normal mode operation) 
-  out     ​TCCR0A,​ R16 
-        ​ 
-  ; Pornim timer-ul scriind o valoare diferită de 0 pe biții CS0 
-  ldi     R16, 0b00000101 ​   ; WGM0[2] = 0 (normal mode operation) 
-                             ; CS0 = 5 (clkT = clkIO/1024 from prescaler) 
-  out     ​TCCR0B,​ R16 
-</​code>​ 
- 
-Din punctul în care am scris ceva nenul pe biții 2:0 din TCCR0B (biții CS0), timer-ul a pornit. Este important de realizat că timer-ul nu poate fi cu adevărat pornit sau oprit ci, pur și simplu, dacă CS0 este 0, atunci modulul nu primește semnal de ceas, deci contorul nu va fi incrementat. Așadar, din momentul ultimei instrucțiuni din exemplul de cod, timer-ul începe să își incrementeze contorul cu câte o unitate la fiecare 1024 de cicli de ceas. 
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