⏱️ Timers et compteurs AVR

Configurez Timer0, Timer1 et Timer2 de l'ATmega328P — prescaler, mode CTC, PWM matériel et interruptions

Vue d'ensemble des timers

L'ATmega328P dispose de 3 timers matériels qui fonctionnent indépendamment du processeur. Contrairement à delay() qui bloque le CPU, les timers comptent en arrière-plan et peuvent déclencher des actions (interruptions, génération PWM) sans interrompre le programme principal.

TimerRésolutionValeur maxSorties PWMUtilisation Arduino
Timer08 bits255OC0A (PD6/D6), OC0B (PD5/D5)millis(), micros(), delay()
Timer116 bits65535OC1A (PB1/D9), OC1B (PB2/D10)Servo library
Timer28 bits255OC2A (PB3/D11), OC2B (PD3/D3)tone()

⚠️ Conflit avec Arduino : Timer0 est utilisé par millis() et delay(). Si vous modifiez sa configuration, ces fonctions ne fonctionneront plus correctement. Timer1 est utilisé par la bibliothèque Servo. En programmation bare metal, vous avez un contrôle total sans ces contraintes.

⚙️ Le prescaler — Diviser la fréquence d'horloge

Le prescaler divise la fréquence de l'horloge système (16 MHz sur Arduino Uno) avant de l'envoyer au timer. Cela permet de ralentir le comptage pour atteindre des durées plus longues.

PrescalerFréq. timerPériode par tickOverflow Timer0 (8 bits)Overflow Timer1 (16 bits)
116 MHz62.5 ns16 µs4.096 ms
82 MHz0.5 µs128 µs32.77 ms
64250 kHz4 µs1.024 ms262.1 ms
25662.5 kHz16 µs4.096 ms1.049 s
102415.625 kHz64 µs16.384 ms4.194 s

Le prescaler est configuré via les bits CS02:CS01:CS00 du registre TCCR0B (pour Timer0) :

// Timer0 avec prescaler 64
TCCR0B = (1 << CS01) | (1 << CS00);  // CS02:01:00 = 011 → /64

// Timer1 avec prescaler 256
TCCR1B = (1 << CS12);                   // CS12:11:10 = 100 → /256

🎯 Mode CTC (Clear Timer on Compare Match)

Le mode CTC est le plus utilisé pour créer des temporisations précises. Le timer compte de 0 jusqu'à une valeur de comparaison (OCRnA), puis se remet à zéro automatiquement et recommence. Quand le match se produit, un drapeau est levé et une interruption peut être déclenchée.

Formule de calcul

OCRnA = (F_CPU / (prescaler × F_désirée)) − 1
Où F_CPU = 16 000 000 Hz et F_désirée = fréquence de l'interruption

Exemple : Interruption toutes les 1 ms avec Timer1

// F_désirée = 1000 Hz (1 ms)
// Prescaler = 64
// OCR1A = (16000000 / (64 × 1000)) − 1 = 249

#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint16_t compteur_ms = 0;

ISR(TIMER1_COMPA_vect) {
    compteur_ms++;
    if (compteur_ms >= 500) {   // Toutes les 500 ms
        PORTB ^= (1 << PB5);     // Toggle LED
        compteur_ms = 0;
    }
}

int main(void) {
    DDRB |= (1 << PB5);           // LED en sortie

    // Timer1 en mode CTC
    TCCR1B |= (1 << WGM12);        // Mode CTC
    TCCR1B |= (1 << CS11) | (1 << CS10);  // Prescaler 64
    OCR1A = 249;                    // Compare match à 249
    TIMSK1 |= (1 << OCIE1A);        // Activer interruption Compare Match A
    sei();                           // Activer interruptions globales

    while (1) {
        // Le CPU est libre ! Il peut faire autre chose ici.
    }
}

La LED clignote à 1 Hz (500 ms ON, 500 ms OFF) grâce à l'interruption timer, sans aucun delay(). Le processeur est libre de faire d'autres tâches dans la boucle principale.

🎨 Génération PWM matériel

Les timers AVR peuvent générer un signal PWM directement sur les broches de sortie, sans intervention du processeur. C'est beaucoup plus efficace et précis que le PWM logiciel.

Exemple : PWM sur OC1A (D9) avec Timer1

#include <avr/io.h>

int main(void) {
    DDRB |= (1 << PB1);     // OC1A (D9) en sortie

    // Timer1 : Fast PWM, 8 bits, prescaler 64
    TCCR1A = (1 << COM1A1)   // Clear OC1A on match, set at BOTTOM
           | (1 << WGM10);   // Fast PWM 8 bits (WGM = 0101)
    TCCR1B = (1 << WGM12)
           | (1 << CS11) | (1 << CS10);  // Prescaler 64

    OCR1A = 128;             // Duty cycle ≈ 50% (128/255)

    while (1) {
        // PWM tourne tout seul, CPU libre !
    }
}

Modifiez OCR1A pour changer le duty cycle : 0 = 0%, 128 = 50%, 255 = 100%. C'est exactement ce que fait analogWrite() en Arduino, mais ici vous contrôlez chaque paramètre.

📌 Registres des timers — Aide-mémoire

RegistreRôle
TCCRnA / TCCRnBConfiguration du mode (Normal, CTC, PWM) et du prescaler
TCNTnValeur actuelle du compteur (lecture/écriture)
OCRnA / OCRnBValeur de comparaison (seuil de match)
TIMSKnMasque d'interruptions (active/désactive les interruptions timer)
TIFRnDrapeaux d'interruptions (indiquent si un événement s'est produit)