CURSUL 06

Module de Tip Timer

Durată: 121 min de predare Nivel: licență, anul II - recomandat după Cursul 05 Disciplină: Microcontrollere și Microprocesoare Laborator asociat: Laboratorul 04 PDF: descarcă suportul EN English version

Până acum, timpul a existat doar indirect, ca durata pe care o consumă instrucțiunile executate. Aproape orice aplicație reală are însă nevoie de timp ca de o mărime pe care să o poată măsura și impune: un motor cere o putere medie reglabilă, un senzor cu ultrasunete cere lățimea unui impuls cu precizie de microsecunde, un protocol de comunicație cere eșantionarea liniei exact la mijlocul fiecărui bit. Pentru toate acestea, microcontrolerul are un bloc hardware dedicat, la bază un simplu numărător care avansează singur, în paralel cu programul - modulul de tip timer.

1Obiectul și structura cursului6 min

Un timer numără impulsuri primite pe intrarea sa de tact, fără ca procesorul să execute vreo instrucțiune pentru asta. Odată pornit, avansează indiferent dacă unitatea centrală calculează, așteaptă sau e într-un regim de consum redus - una din puținele periferice care oferă o măsură a timpului independentă de ceea ce face software-ul. Pe o placă Arduino Uno, ceasul e de 16 MHz, deci un impuls la fiecare 62,5 ns: numărul acumulat în registru se traduce direct în durată.

Recapitulare din Cursul 05
  • Rutina de tratare trebuie scurtă, fără întârzieri, comunică prin variabile volatile (Cursul 05)
  • O variabilă mai mare de un octet cere secțiune critică pentru citire atomică (Cursul 05)
Ambele reguli se aplică identic rutinelor de timer, care sunt cel mai frecvent tip de întrerupere folosit într-o aplicație reală.
  • Să explicați diferența dintre regimul timer și regimul counter al aceluiași modul hardware
  • Să distingeți modurile normal, CTC, PWM rapid și PWM cu fază corectă
  • Să calculați, din frecvența ceasului, factorul de prescalare și valoarea de comparare, perioada sau frecvența unei întreruperi sau a unui semnal PWM
  • Să explicați legătura dintre factorul de umplere și valoarea medie a unui semnal PWM
  • Să explicați de ce unitatea de captură dă o precizie pe care o rutină de întrerupere obișnuită nu o poate atinge

2Timer sau counter, și de ce delay() e o soluție proastă11 min

Timer sau counter

Vorbim despre timer când semnalul de tact are o distribuție uniformă în timp (un ceas cu perioadă cunoscută) - numărul acumulat e proporțional cu timpul scurs. Vorbim despre counter (numărător de evenimente) când semnalul vine din exterior, cu distribuție aleatoare - impulsuri de la un encoder, o barieră optică. Hardware-ul e identic: pe ATmega328P, biții de selecție a sursei de tact comută același modul între cele două regimuri - de aici numele oficial, Timer/Counter.

De ce delay() este cea mai proastă soluție

delay(1000) execută o buclă care citește repetat contorul de milisecunde, ieșind când acesta a crescut cu o mie - milioane de instrucțiuni cheltuite pentru a nu face nimic. Mai grav: cât timp programul așteaptă, nu poate răspunde la nimic altceva - un buton apăsat și eliberat în acest interval nu va fi observat, un caracter serial poate fi pierdut.

Temporizare blocantă și echivalentul ei neblocant
/* blocanta: procesorul e imobilizat 500 ms */
void loop() {
    digitalWrite(LED, HIGH); delay(500);
    digitalWrite(LED, LOW);  delay(500);
}

/* neblocanta: bucla se parcurge de mii de ori pe secunda */
unsigned long ultimaComutare = 0;
const unsigned long PERIOADA = 500;
void loop() {
    unsigned long acum = millis();
    if (acum - ultimaComutare >= PERIOADA) {
        ultimaComutare = acum;      /* se retine momentul, nu se aduna */
        stareLed = !stareLed;
        digitalWrite(LED, stareLed);
    }
    /* aici pot fi tratate butoane, senzori, comunicatie seriala */
}
Nici varianta neblocantă nu ar exista fără un timer millis() returnează un contor incrementat de rutina de întrerupere a unui timer hardware. Diferența e că programul consultă timpul în loc să îl aștepte. Etapa următoare, tratată în acest curs, e să lăsăm timerul să anunțe singur programul, prin întrerupere, sau chiar să genereze semnalul dorit direct la pin, fără ca procesorul să fie implicat deloc.

3Clasificarea modulelor de tip timer11 min

O clasificare orientată spre funcție distinge cinci structuri, cu hardware adesea comun.

Timerul simplu numără ceasul intern și se resetează la depășire (overflow), semnalând prin indicator și/sau întrerupere. Domeniul de numărare e [0, 2ⁿ-1] - un numărător de 8 biți la 16 MHz se umple în 16 µs, unul de 16 biți în 4,096 ms, insuficient pentru a cronometra o secundă fără prescalare.

Numărătorul de evenimente e același modul, cu tactul comutat pe un pin extern - nu mai măsoară timp, acumulează evenimente. Combinația celor două regimuri (un timer fixează o fereastră de o secundă, un counter acumulează impulsuri în ea) dă direct frecvența unui semnal, în hertzi.

Generatorul de PWM produce, integral în hardware, un semnal cu frecvență constantă și factor de umplere reglabil - discutat pe larg mai jos.

Watchdog timer-ul numără continuu, independent de program; programul are obligația să-l reinițializeze periodic. Dacă se blochează într-o buclă infinită sau într-o întrerupere din care nu mai iese, reinițializarea nu mai are loc, iar temporizatorul generează un reset care repornește sistemul într-o stare cunoscută.

RTC-ul (Real-Time Clock) păstrează ora și data pe termen lung, la un cuarț de 32.768 Hz (=2¹⁵, astfel încât o divizare succesivă cu doi, prin 15 bistabile, dă exact un impuls pe secundă, fără eroare de rotunjire). ATmega328P nu are un RTC complet, dar poate apropia funcția cu Timer2 în regim asincron.

ModulRezoluțieFactori de prescalareFuncții de bibliotecă dependente
Timer08 biți1, 8, 64, 256, 1024millis(), micros(), delay(), analogWrite() pe pinii 5, 6
Timer116 biți1, 8, 64, 256, 1024biblioteca Servo, analogWrite() pe pinii 9, 10
Timer28 biți1, 8, 32, 64, 128, 256, 1024tone(), analogWrite() pe pinii 3, 11
Reconfigurarea unui timer nu produce nicio eroare vizibilă

Nucleul Arduino configurează Timer0 cu prescalare 64 și îi activează întreruperea de depășire, care alimentează millis(), micros() și delay(). Dacă programul schimbă prescalarea lui Timer0 pentru un PWM de altă frecvență pe pinul 5, obține într-adevăr acea frecvență, dar accelerează sau încetinește simultan contorul de milisecunde - toate temporizările bazate pe millis() devin false, fără niciun mesaj de eroare. Regula: pe o placă Arduino, Timer0 se lasă în pace; experimentele de configurare manuală se fac pe Timer1 sau Timer2. Doar Timer1 are unitate de captură și registre de comparare pe 16 biți - singurul potrivit pentru măsurători fine de timp.

4Modurile de lucru: normal, CTC, PWM rapid, PWM fază corectă11 min

Comportamentul numărătorului între două reveniri la zero definește modul de lucru, ales prin biții WGM din TCCRnA/TCCRnB. Diferența privește valoarea maximă (TOP), sensul numărării și momentul comutării ieșirii.

Modul normal
Numărătorul crește de la 0 la 2ⁿ-1, revine la zero și marchează depășirea. Perioada e fixă, determinată doar de tact și rezoluție - schimbarea ei cere preîncărcarea numărătorului în fiecare rutină, operație care introduce o eroare dependentă de latența întreruperii.
Modul CTC (Clear Timer on Compare Match)
Numărătorul crește până la egalitatea cu OCRnA, moment în care e șters automat de hardware. TOP e preluat de registrul de comparare - perioada devine programabilă cu pas de un impuls de tact, iar ștergerea hardware nu depinde de momentul întreruperii, deci nu acumulează eroare de la o perioadă la alta. Modul potrivit pentru întreruperi periodice exacte.

PWM rapid: numărătorul parcurge continuu 0 → TOP; ieșirea e activă la începutul ciclului, inactivă la egalitatea cu registrul de comparare. Numărătorul parcurge domeniul o singură dată pe perioadă, deci frecvența e cea mai mare posibilă pentru o rezoluție dată.

PWM cu fază corectă: numărătorul urcă la TOP, apoi coboară la zero; ieșirea comută la fiecare trecere prin valoarea de comparare, o dată pe fiecare rampă. Impulsul rezultat e centrat pe mijlocul perioadei, indiferent de factorul de umplere - de aici numele: faza nu se deplasează la modificarea lățimii impulsului. Prețul: perioada devine aproximativ dublă, frecvența se înjumătățește față de PWM rapid.

Când contează fixarea fazei PWM rapid e potrivit pentru marea majoritate: iluminare, ventilatoare, motoare simple. PWM cu fază corectă se preferă unde deplasarea centrului impulsului ar produce perturbații - motoare fără perii, punți în H cu comandă complementară, aplicații audio.
Potriviți modul de lucru cu aplicația potrivită

5Modalități de accesare a datelor din timer9 min

Există trei căi prin care software-ul intră în contact cu modulul de timp.

Citirea directă a registrului de contor

uint8_t valoare8 = TCNT0;    /* numaratorul de 8 biti al Timer0 */
uint16_t valoare16 = TCNT1;  /* numaratorul de 16 biti al Timer1 */

Nu perturbă numărătorul - o simplă fotografie. Utilă pentru cronometrarea unei secvențe de cod (citire înainte și după) sau cronometrări scurte, fără depășire.

Citirea unui registru de 16 biți pe o mașină de 8 biți

TCNT1 se citește prin două instrucțiuni succesive (octet inferior, apoi superior). Hardware-ul rezolvă parțial problema printr-un registru tampon: la citirea octetului inferior, octetul superior e copiat automat într-un tampon, citit imediat după - dar acest mecanism funcționează doar dacă cele două citiri nu sunt separate de o întrerupere care accesează la rândul ei un alt registru de 16 biți al aceluiași timer, pentru că tamponul e unic. Accesul din programul principal la un registru de 16 biți al lui Timer1, dacă o întrerupere accesează același timer, trebuie protejat prin dezactivarea temporară a întreruperilor.

Interogarea indicatorilor de stare

while (!(TIFR1 & (1<<TOV1))) { ; }   /* bucla goala: se pierd cicluri */
TIFR1 = (1 << TOV1);                  /* stergerea se face scriind 1 */

Precizie maximă (reacție în câteva cicluri, fără latența unei întreruperi), dar cât timp bucla se învârte, procesorul nu poate face nimic altceva - exact defectul lui delay(), mai vizibil. Utilă în secvențe scurte și critice care nu pot accepta latența unei întreruperi.

Folosirea întreruperilor - modalitatea preferată

Trei condiții, toate obligatorii: activarea individuală a sursei (TIMSK0/1/2), activarea globală (sei()), și o rutină scrisă pentru vectorul corect - un vector fără rutină duce la resetarea microcontrolerului. Trei categorii de întreruperi de timer: Overflow (la depășire), Compare Match (la egalitatea cu registrul de comparare, echidistante în modul CTC) și Input Capture (la un front pe pinul de captură, cu memorare automată a numărătorului - secțiunea finală a cursului).

6Circuitul de prescalare și formulele de bază10 min

Un numărător de 8 biți alimentat direct cu ceasul de 16 MHz se depășește la fiecare 256×62,5 ns = 16 µs, adică de 62 500 de ori pe secundă - practic inutilizabil pentru temporizări, iar la fiecare depășire procesorul consumă cicluri pentru context. Soluția: circuitul de prescalare.

Prescalerul e un lanț de bistabile care împart succesiv frecvența la doi; un multiplexor alege etajul. Valorile disponibile sunt de forma 2^P: pentru Timer0 și Timer1, 1, 8, 64, 256, 1024; pentru Timer2, suplimentar 32, 128.

Formulele de bază
Durata unui impuls al numărătorului: T_tick = N/f_clk.
Mod CTC, perioada evenimentelor: T = N·(OCRnA+1)/f_clk, deci f = f_clk / (N·(OCRnA+1)).
Valoarea de comparare necesară unei perioade impuse: OCRnA = f_clk/(N·f) - 1.
Compromisul prescalerului

Un factor mare prelungește durata până la depășire (temporizări lungi cu un numărător mic), dar înrăutățește rezoluția: dacă un impuls durează 64 de perioade de ceas, momentele nu se pot distinge decât cu o precizie de 64 de perioade (4 µs la 16 MHz). Regula practică: cel mai mic factor de prescalare care mai acoperă intervalul dorit.

Exercițiu rezolvat - întrerupere exactă la fiecare milisecundă

ATmega328P, 16 MHz. Configurați un timer pentru o întrerupere la fiecare milisecundă.

Vezi rezolvarea

Impulsuri de ceas într-o milisecundă: f_clk/f = 16×10⁶/1000 = 16000.

Fără prescalare (N=1): OCRnA = 16000-1 = 15999 - nu încape în 8 biți, dar încape confortabil în cei 16 biți ai lui Timer1 (rezoluție maximă, 62,5 ns).

Cu un timer de 8 biți, OCRnA ≤ 255: se caută cel mai mic N cu 16000/N - 1 ≤ 255, adică N ≥ 62,5. Din listă, primul care satisface: N = 64. OCRnA = 16×10⁶/(64×1000) - 1 = 250 - 1 = 249 - întreg, încape în 8 biți, perioadă exactă, fără eroare. Verificare: 250 × 4 µs = 1000 µs. ✓

Calculator de prescaler: ce valoare scriu in OCRnA?
Numaratorul, prescalerul si valoarea de comparare

7Aplicație: o bază de timp de o milisecundă6 min

volatile uint32_t milisecunde = 0;

void configurareBazaDeTimp(void) {
    cli();
    TCCR1A = 0x00;
    TCCR1B = (1 << WGM12);              /* mod CTC, TOP dat de OCR1A */
    OCR1A = 249;                         /* 16 MHz / (64*250) = 1000 Hz */
    TCCR1B |= (1<<CS11)|(1<<CS10);      /* prescaler 64: porneste numaratorul */
    TIMSK1 = (1 << OCIE1A);
    TCNT1 = 0;
    sei();
}
ISR(TIMER1_COMPA_vect) { milisecunde++; }
De ce bitul de selecție a tactului se scrie ultimul Scrierea lui pornește efectiv numărătorul - dacă modulul ar porni înainte de configurarea completă, prima perioadă ar avea durată greșită. Rezultatul, comparat cu întreruperea la depășire fără prescalare: frecvența a scăzut de la 62 500 la 1000 pe secundă - sarcina asupra procesorului s-a redus de peste șaizeci de ori, iar perioada e exact cea dorită.

8Caracteristicile semnalelor periodice8 min

Un semnal x(t) e periodic, de perioadă T, dacă x(t) = x(t+T) pentru orice t. Frecvența e numărul de repetări pe secundă: f = 1/T.

Pentru un semnal dreptunghiular, factorul de umplere (duty cycle) e raportul dintre durata stării active T_on și perioadă: FU = (T_on/T)×100 [%]. 0% = permanent inactiv, 100% = permanent activ, 50% = simetric.

De ce factorul de umplere e mărimea care contează Dacă frecvența e suficient de mare față de inerția sarcinii, aceasta nu mai percepe comutările individuale, ci doar valoarea medie: V_med = Vcc × FU/100. La un motor CC, inerția mecanică și inductanța înfășurării netezesc curentul - turația se reglează continuu prin factorul de umplere. La un LED, ochiul integrează lumina pe ~20 ms - peste ~100 Hz, comutările devin invizibile (sub acest prag apare pâlpâirea; de aceea frecvențele PWM implicite Arduino, ~490 și ~980 Hz, sunt peste prag).
Randamentul, nu doar comoditatea, e motivul real al PWM

Aceeași reducere de putere s-ar putea obține cu un rezistor serie sau un tranzistor în regim liniar, dar diferența de putere s-ar transforma în căldură. Comutatorul PWM e fie complet deschis (curent nul), fie complet închis (cădere de tensiune aproape nulă) - puterea disipată pe el e mică în ambele stări. De aceea PWM reglează puteri de zeci sau sute de wați cu un tranzistor care abia se încălzește. Important: modularea în durată nu schimbă amplitudinea, doar valoarea medie - sarcina trebuie să aibă inerție suficientă pentru a o media; aplicat unei sarcini rapide, fără filtrare, un semnal PWM rămâne o succesiune de impulsuri complete.

9PWM rapid și PWM cu fază corectă9 min

Factorul de umplere si tensiunea medie - miscati cursorul

În PWM rapid, numărătorul crește continuu 0→TOP; pinul devine activ la revenirea la zero, inactiv la egalitatea cu registrul de comparare.

Formulele PWM rapid
Frecvență: f_PWM = f_clk / (N·(TOP+1)).
Factor de umplere: FU = (OCRnx+1)/(TOP+1) × 100%.
Rezoluție (trepte): R = log₂(TOP+1) biți.
Cu TOP=255, N=64, f_clk=16 MHz: f_PWM ≈ 976,6 Hz - exact frecvența analogWrite() pe pinii 5 și 6 ai Unoului, cu 8 biți de rezoluție (256 de trepte).

Produsul dintre frecvența PWM și numărul de trepte e constant, egal cu f_clk/N - nu se poate obține simultan frecvență mare și rezoluție mare fără a mări ceasul sau a reduce prescalarea.

În PWM cu fază corectă, numărătorul parcurge domeniul de două ori pe perioadă (urcă, coboară): f_PWM = f_clk / (2·N·TOP). Pe modulele AVR de 8 biți, numărul de impulsuri pe perioadă e 510 (=2×255) - cu N=64, rezultă ≈490,2 Hz, frecvența analogWrite() pe pinii 3, 9, 10, 11: aproximativ jumătate din PWM rapid, la aceeași rezoluție - prețul plătit pentru fixarea fazei.

Cele trei căi de a schimba frecvența PWM Factorul de prescalare (comod, dar valori discrete, puteri ale lui doi). Valoarea TOP, unde e registru (reglaj aproape continuu al frecvenței, dar reduce rezoluția factorului de umplere - numărul de trepte e chiar TOP). Frecvența ceasului de sistem (afectează tot ce depinde de timp - vitezele de comunicație, funcțiile de bibliotecă - folosită doar cu justificare solidă, de regulă pentru reducerea consumului).

10Implementarea PWM și legătura cu analogWrite()9 min

PWM rapid pe Timer2, cu factor de umplere reglabil
void configurarePWM2(void) {
    DDRB |= (1 << PB3);       /* OC2A = pinul 11 pe Uno */
    /* WGM21:WGM20=11 -> Fast PWM, TOP=0xFF; COM2A1:COM2A0=10 -> neinversat */
    TCCR2A = (1<<WGM21)|(1<<WGM20)|(1<<COM2A1);
    TCCR2B = (1 << CS22);     /* prescaler 64: 16MHz/(64*256) = 976,6 Hz */
    OCR2A = 25;                /* 26/256 ~ 10% umplere */
}
void setFactorUmplere(uint8_t procent) {
    OCR2A = (uint16_t)procent * 255 / 100;
}

Biții COM2A1:COM2A0 aleg ieșire neinversată (activ de la începutul perioadei până la egalitate), inversată (pereche complementară, sarcină comandată pe nivel jos), sau deconectată (pinul rămâne GPIO obișnuit, dar modulul continuă să numere și poate genera întreruperi).

Ce face analogWrite()

Identifică timerul asociat pinului cerut, activează blocul de comparare (biții COM) și scrie argumentul în registrul de comparare - numele e nefericit ales: ieșirea nu e analogică, ci digitală, comutând între 0 V și 5 V, doar valoarea medie variază continuu. Argumentul are 8 biți pentru că registrele de comparare ale timerelor de 8 biți au 8 biți - o limitare a hardware-ului, nu a funcției. Frecvența nu poate fi schimbată prin analogWrite() (nu atinge biții de prescalare). Doar șase pini ai Unoului o suportă, pentru că doar șase ieșiri de comparare există în hardware.

Registrele-tampon evită deformarea impulsului la o scriere „nepotrivită"

Dacă valoarea de comparare ar fi modificată exact când numărătorul tocmai a depășit-o, impulsul curent ar fi deformat, ar putea lipsi sau s-ar putea prelungi peste o perioadă întreagă. Modulele AVR folosesc registre-tampon: valoarea scrisă e reținută și transferată în registrul activ doar la începutul unei noi perioade - mecanismul elimină cele mai multe erori de sincronizare, dar nu toate; în modurile cu TOP variabil, o modificare la moment nepotrivit poate încă produce un ciclu de durată greșită.

11Capturarea evenimentelor externe13 min

Problema inversă generării: determinarea momentului exact la care s-a produs un eveniment extern, rezolvată de unitatea de captură (input capture).

Pinul ICP1 (pinul digital 8 pe Uno) e supravegheat permanent de hardware. La un front de sensul configurat, valoarea instantanee a numărătorului e copiată automat în ICR1, se activează indicatorul ICF1 și, dacă validată, se generează o cerere de întrerupere.

De ce captura e mai exactă decât citirea numărătorului într-o rutină obișnuită Memorarea se face în momentul frontului, nu în momentul în care programul ajunge să reacționeze. Într-o întrerupere externă obișnuită, rutina ar citi ea însăși numărătorul - valoarea ar fi afectată de latența întreruperii (terminarea instrucțiunii curente, salvarea contextului, o eventuală așteptare după altă rutină), care variază cu zeci de cicluri și nu poate fi compensată prin calcul. La captura hardware, întreruperea poate întârzia oricât - atât timp cât rutina citește ICR1 înainte de următorul front, valoarea rămâne exactă la nivelul unui impuls de tact.

Regula pentru alegerea prescalerului: eroarea de datare crește direct cu factorul de prescalare - pentru capturi se alege cel mai mic prescaler posibil care nu depășește domeniul de măsurare (Δt_max = (2^r - 1)·N/f_clk).

Aplicație - senzorul cu ultrasunete HC-SR04

Senzorul emite un tren de ultrasunete și activează o linie de ieșire exact atât timp cât durează călătoria dus-întors a undei. Distanța: d = v_sunet·Δt/2, cu v_sunet ≈ 343 m/s.

ISR(TIMER1_CAPT_vect) {
    if (TCCR1B & (1<<ICES1)) {      /* front crescator */
        captura1 = ICR1;
        TCCR1B &= ~(1<<ICES1);      /* urmatorul: descrescator */
    } else {                          /* front descrescator */
        latime = ICR1 - captura1;
        TCCR1B |= (1<<ICES1);
        gata = 1;
    }
    TIFR1 = (1 << ICF1);
}

Soluția uzuală de început, pulseIn(), blochează procesorul zeci de milisecunde și e afectată de orice întrerupere survenită în interval; cu unitatea de captură, măsurătoarea se face în hardware, programul pierde doar câteva microsecunde ale celor două rutine.

Vezi calculul de rezoluție și domeniu (prescaler 8)

T_tick = 8/16×10⁶ = 0,5 µs. Un impuls corespunde Δd = 343×0,5×10⁻⁶/2 ≈ 0,086 mm - mult sub precizia proprie a senzorului, deci rezoluția temporală nu e factorul limitativ.

Domeniul maxim: Δt_max = 65535×0,5 µs ≈ 32,8 ms, adică d_max ≈ 5,6 m - peste domeniul util al senzorului (câțiva metri). Prescalerul 8 e potrivit: rezoluție inutil de bună, domeniu suficient. Cu prescaler 1, domeniul ar scădea la 4,1 ms (~70 cm) - insuficient.

Scăderea pe 16 biți fără semn și un indicator care se poate seta singur

ICR1 - captura1 pe aritmetică fără semn rămâne corectă chiar dacă numărătorul a trecut prin zero între cele două capturi. ICF1 se șterge explicit după comutarea sensului frontului, pentru că operația de comutare poate ea însăși activa indicatorul, producând o întrerupere falsă imediat după ieșirea din rutină.

Măsurarea distanței cu HC-SR04 prin captură: puneți pașii în ordine

12Erori frecvente4 min

  • „Pe o placă Arduino pot reconfigura orice timer, inclusiv Timer0, fără efecte secundare." Fals - Timer0 alimentează millis(), micros() și delay(). Reconfigurarea prescalerului lui schimbă toate temporizările bazate pe millis(), fără niciun mesaj de eroare vizibil. Experimentele de configurare manuală a timerelor se fac pe Timer1 sau Timer2, nu pe Timer0.
  • „analogWrite() dă o ieșire analogică reală." Fals - ieșirea comută digital între 0 V și 5 V; doar valoarea ei medie, percepută de o sarcină cu inerție, variază continuu odată cu factorul de umplere. Tratați PWM ca pe un semnal digital cu factor de umplere variabil, nu ca pe o tensiune analogică reală.
  • „Pentru a măsura lățimea unui impuls, citesc numărătorul chiar în rutina întreruperii externe declanșate de front." Fals - valoarea citită e afectată de latența întreruperii (terminarea instrucțiunii curente, salvarea contextului), care variază cu zeci de cicluri și nu poate fi compensată. Folosiți unitatea de captură (Timer1 + ICP1), care memorează numărătorul în hardware, exact la momentul frontului.

13Rezumat și glosar5 min

Un timer e, la bază, un numărător hardware alimentat printr-un prescaler dintr-o sursă de tact stabilă. Trei mecanisme derivă din el: comparând numărătorul cu un prag și ștergându-l automat (mod CTC), se obțin întreruperi periodice exacte, care înlocuiesc delay(); comandând un pin în funcție de aceeași comparație, se obține un semnal PWM cu factor de umplere reglabil, cu randament mult mai bun decât reducerea tensiunii cu un rezistor serie; memorând numărătorul la un front extern (input capture), se obține datarea exactă a evenimentelor, cu o precizie pe care software-ul nu o poate atinge. Perioada sau frecvența obținută rezultă întotdeauna din trei numere - frecvența ceasului, factorul de prescalare, valoarea de comparare - și alegerea lor e un compromis între rezoluție și domeniu. Modulele de timp sunt resurse partajate cu bibliotecile Arduino: orice reconfigurare trebuie făcută știind ce funcții încetează, în acel moment, să mai spună adevărul.

Prescaler
divizor de frecvență între tactul sistemului și intrarea numărătorului.
TOP
valoarea maximă la care ajunge numărătorul înainte de a reveni la zero.
Mod CTC
Clear Timer on Compare Match - numărătorul se șterge automat la egalitatea cu registrul de comparare.
Factor de umplere
raportul dintre durata stării active și perioada totală a unui semnal dreptunghiular.
Input capture
memorarea automată, în hardware, a numărătorului la apariția unui front extern.

14Întrebări de verificare7 min

  1. Explicați deosebirea dintre regimul de timer și cel de counter al aceluiași modul hardware. Ce se modifică în configurare la trecerea de la unul la celălalt?
  2. De ce e delay() o risipă de resurse? Descrieți concret ce se pierde în timpul așteptării.
  3. Un numărător de 8 biți e alimentat direct cu ceasul de 16 MHz. După cât timp se produce depășirea, și de câte ori pe secundă ar fi apelată rutina de întrerupere?
  4. De ce factorii de prescalare disponibili sunt puteri ale lui doi, nu valori arbitrare? Ce se pierde la un factor de prescalare mai mare decât e necesar?
  5. Se cere o întrerupere la fiecare 10 ms, cu Timer1 la 16 MHz, în mod CTC. Calculați OCR1A pentru un factor de prescalare de 64 și verificați dacă rezultatul e realizabil.
  6. Comparați modul normal cu modul CTC din punctul de vedere al preciziei intervalelor generate.
  7. Un semnal PWM are perioada 2 ms și starea activă 0,4 ms. Calculați frecvența, factorul de umplere, și tensiunea medie la o amplitudine de 5 V.

15Direcții de aprofundare2 min

Cursul următor folosește exact același mecanism de comparare al unui timer, aplicat problemei inverse celei de generare - transformarea unei tensiuni analogice continue într-un număr: convertorul analog-numeric.

Formulele de prescalare, generarea de PWM și captura evenimentelor din acest curs devin montaj real în Laboratorul 04.