Modulele timer sunt numărătoare hardware care funcționează independent de procesor. Ele permit generarea de intervale precise, măsurarea duratelor și producerea semnalelor PWM - fără a bloca programul cu delay().
1Obiectivele lucrării
- Înțelegerea rolului prescalerului și calculul frecvenței de numărare
- Configurarea modului CTC pentru generarea unei baze de timp precise
- Utilizarea întreruperii de comparare pentru execuție periodică
- Generarea semnalelor PWM și controlul factorului de umplere
- Înlocuirea funcției
delay()cu temporizare neblocantă
2Materiale necesare
- 1 Arduino Uno
- 1 Breadboard
- 2 LED-uri + rezistoare 220 Ω
- 1 Potențiometru 10 kΩ
- 1 Buzzer pasiv (opțional)
- 6 Fire jumper
3Noțiuni teoretice
Principiul de funcționare
Un timer este un numărător care crește cu 1 la fiecare impuls de ceas primit. Când atinge valoarea maximă (255 pentru 8 biți, 65 535 pentru 16 biți), se resetează și setează un fanion de depășire. Fiind implementat în hardware, numără în paralel cu execuția programului.
Timerele disponibile pe ATmega328P
| Timer | Rezoluție | Pini PWM | Folosit intern de |
|---|---|---|---|
| Timer0 | 8 biți | D5, D6 | millis(), delay() - evitați modificarea |
| Timer1 | 16 biți | D9, D10 | biblioteca Servo |
| Timer2 | 8 biți | D3, D11 | tone() |
Prescalerul
Ceasul de 16 MHz este prea rapid pentru intervale lungi: un timer pe 16 biți s-ar umple în doar 4,1 ms. Prescalerul împarte frecvența înainte de a ajunge la numărător:
| Prescaler N | Frecvență numărare | Durată 1 increment | Timp până la overflow (16 biți) |
|---|---|---|---|
| 1 | 16 MHz | 62,5 ns | 4,10 ms |
| 8 | 2 MHz | 0,5 µs | 32,8 ms |
| 64 | 250 kHz | 4 µs | 262 ms |
| 256 | 62,5 kHz | 16 µs | 1,05 s |
| 1024 | 15,625 kHz | 64 µs | 4,19 s |
Moduri de funcționare
4Calculator interactiv de timer
Modificați parametrii și urmăriți cum se schimbă perioada și frecvența. Graficul arată numărătorul crescând până la valoarea de comparare și semnalul rezultat la pin.
5Generarea semnalului PWM
PWM (Pulse Width Modulation) produce o tensiune medie variabilă prin comutarea rapidă între 0 și 5 V. Raportul dintre timpul „pornit" și perioada totală se numește factor de umplere. Un LED comandat astfel pare că își schimbă intensitatea, iar un motor își schimbă turația.
TCCR0A → TCCR1A) și domeniul numărătorului.
Timer1 este preferat pentru că nu interferează cu millis().PWM la nivel de registru - ce face analogWrite()
Funcția analogWrite() ascunde configurarea a trei regiștri. Scrisă explicit, aceeași
operație arată astfel - și devine complet controlabilă: puteți alege frecvența, rezoluția și polaritatea,
lucruri imposibile prin bibliotecă.
void pwmInit() {
DDRB |= (1 << PB1); // pinul 9 (OC1A) ca iesire
// COM1A1 = 1, COM1A0 = 0 -> iesire necinversata: HIGH la start, LOW la compare
TCCR1A |= (1 << COM1A1);
TCCR1A &= ~(1 << COM1A0);
// WGM13:WGM10 = 0101 -> Fast PWM pe 8 biti (TOP = 255)
TCCR1A |= (1 << WGM10);
TCCR1A &= ~(1 << WGM11);
TCCR1B |= (1 << WGM12);
TCCR1B &= ~(1 << WGM13);
// prescaler 64 -> f_PWM = 16 MHz / (64 * 256) = 976 Hz
TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10)); // curata campul
TCCR1B |= (1 << CS11) | (1 << CS10); // scrie 011
}
void pwmSet(uint8_t duty) {
OCR1A = duty; // 0 = mereu stins, 255 = aproape mereu aprins
}
void setup() {
pwmInit();
pwmSet(128); // 50% - echivalent cu analogWrite(9, 128)
}
void loop() { }
|= - ar rămâne activi și biții anteriori. Se șterge întâi tot câmpul cu
&= ~(...), apoi se scrie valoarea dorită. Este idiomul standard pentru orice câmp de mai
mulți biți, explicat pe larg în Laboratorul 1B.analogWrite() impune 490 Hz pe
pinii 9 și 10. Pentru comanda unui motor, această frecvență cade în banda audibilă și produce un fluierat
supărător. Prin configurare directă puteți urca la peste 20 kHz, deasupra pragului de auz - o cerință
frecventă în practică, imposibil de îndeplinit cu biblioteca.
Portarea configurării unui timer
| Etapă | AVR / ATmega328P | STM32 | ESP32 |
|---|---|---|---|
| Activarea ceasului | implicită | RCC->APB1ENR |= TIM2EN | gestionată de SDK |
| Prescaler | TCCR1B, biții CS12:CS10 | TIMx->PSC (valoare directă) | divizor în configurare LEDC |
| Valoare de comparare | OCR1A | TIMx->CCR1 | ledc_set_duty() |
| Perioadă / TOP | ICR1 sau fix | TIMx->ARR | rezoluție în biți |
| Activare întrerupere | TIMSK1 |= (1<<OCIE1A) | TIMx->DIER |= CC1IE | callback înregistrat |
6Cod sursă
Bază de timp de 1 secundă în mod CTC
volatile bool secundaTrecuta = false;
ISR(TIMER1_COMPA_vect) { // se execută exact la fiecare secundă
secundaTrecuta = true;
PORTB ^= (1 << PB5); // comută LED-ul de pe pinul 13
}
void setup() {
DDRB |= (1 << PB5);
cli(); // oprește întreruperile pe durata configurării
TCCR1A = 0; // curăță registrele
TCCR1B = 0;
TCNT1 = 0; // resetează numărătorul
OCR1A = 62499; // (16 000 000 / 256) - 1 = 1 secundă
TCCR1B |= (1 << WGM12); // mod CTC
TCCR1B |= (1 << CS12); // prescaler 256
TIMSK1 |= (1 << OCIE1A); // activează întreruperea de comparare
sei(); // reactivează întreruperile
DDRB |= (1 << PB4); // pinul 12: martor pentru bucla principală
}
void loop() {
if (secundaTrecuta) {
secundaTrecuta = false;
PORTB ^= (1 << PB4); // dovada că bucla a primit evenimentul
}
}Temporizare neblocantă - alternativa la delay()
delay() oprește complet programul. Cu millis() se pot rula mai multe activități
cu perioade diferite, în paralel:
const uint8_t LED_1 = 8, LED_2 = 9;
unsigned long ultimul1 = 0, ultimul2 = 0;
const unsigned long PERIOADA_1 = 250; // ms
const unsigned long PERIOADA_2 = 1000; // ms
void setup() {
pinMode(LED_1, OUTPUT);
pinMode(LED_2, OUTPUT);
}
void loop() {
unsigned long acum = millis();
if (acum - ultimul1 >= PERIOADA_1) {
ultimul1 = acum;
digitalWrite(LED_1, !digitalRead(LED_1));
}
if (acum - ultimul2 >= PERIOADA_2) {
ultimul2 = acum;
digitalWrite(LED_2, !digitalRead(LED_2));
}
// bucla rămâne liberă pentru alte sarcini
}Control PWM al intensității
Factorul de umplere este comandat aici direct din program, printr-o rampă. La lucrarea a cincea îl veți înlocui cu valoarea citită de la un potențiometru, prin convertorul analog-numeric.
const uint8_t PIN_LED = 9; // pin PWM (Timer1)
void setup() {
pinMode(PIN_LED, OUTPUT);
}
void loop() {
// creste treptat: LED-ul se aprinde lin
for (uint16_t duty = 0; duty <= 255; duty += 5) {
analogWrite(PIN_LED, duty);
delay(25);
}
// scade treptat: LED-ul se stinge lin
for (uint16_t duty = 255; duty > 0; duty -= 5) {
analogWrite(PIN_LED, duty);
delay(25);
}
}duty/255 × 5 V.7Montaj interactiv
Un timer este un numărător care merge singur. Montajele de mai jos vă arată amândouă utilizările lui: generarea unui semnal PWM (unde vedeți unda și factorul de umplere) și măsurarea unui interval exact în modul CTC. Schimbați prescalerul din regiștri și priviți cum se modifică frecvența.
CS11 în CS12 (prescaler 256) și priviți frecvența afișată sub undă: scade de 32 de ori, iar LED-ul începe să pâlpâie vizibil.8Sarcini de lucru
- Configurați Timer1 în mod CTC pentru a genera o întrerupere la fiecare secundă și verificați precizia cu un cronometru pe 60 de secunde.
- Calculați și verificați valorile OCR1A pentru perioade de 0,5 s, 2 s și 5 s. Ce se întâmplă la 5 s cu prescaler 256?
- Realizați un program care aprinde două LED-uri cu perioade diferite (250 ms și 1 s), fără a folosi
delay(). - Comandați intensitatea unui LED prin PWM, cu factorul de umplere dat de un potențiometru.
- Măsurați frecvența semnalului PWM pe pinul 9 și comparați cu valoarea teoretică de 490 Hz.
- Generați un efect de „respirație”: intensitatea LED-ului crește și scade lin, continuu.
9Aplicație de aprofundare
millis()).
Adăugați un buton pe INT0 pentru start / stop și unul pe INT1 pentru resetare.