Configurez Timer0, Timer1 et Timer2 de l'ATmega328P — prescaler, mode CTC, PWM matériel et interruptions
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.
| Timer | Résolution | Valeur max | Sorties PWM | Utilisation Arduino |
|---|---|---|---|---|
| Timer0 | 8 bits | 255 | OC0A (PD6/D6), OC0B (PD5/D5) | millis(), micros(), delay() |
| Timer1 | 16 bits | 65535 | OC1A (PB1/D9), OC1B (PB2/D10) | Servo library |
| Timer2 | 8 bits | 255 | OC2A (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 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.
| Prescaler | Fréq. timer | Période par tick | Overflow Timer0 (8 bits) | Overflow Timer1 (16 bits) |
|---|---|---|---|---|
| 1 | 16 MHz | 62.5 ns | 16 µs | 4.096 ms |
| 8 | 2 MHz | 0.5 µs | 128 µs | 32.77 ms |
| 64 | 250 kHz | 4 µs | 1.024 ms | 262.1 ms |
| 256 | 62.5 kHz | 16 µs | 4.096 ms | 1.049 s |
| 1024 | 15.625 kHz | 64 µs | 16.384 ms | 4.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
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.
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
// 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.
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.
#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.
| Registre | Rôle |
|---|---|
TCCRnA / TCCRnB | Configuration du mode (Normal, CTC, PWM) et du prescaler |
TCNTn | Valeur actuelle du compteur (lecture/écriture) |
OCRnA / OCRnB | Valeur de comparaison (seuil de match) |
TIMSKn | Masque d'interruptions (active/désactive les interruptions timer) |
TIFRn | Drapeaux d'interruptions (indiquent si un événement s'est produit) |