⏱️ Qu'est-ce qu'un Timer ?

Un Timer (ou compteur) est un périphérique matériel intégré au microcontrôleur qui compte les impulsions d'horloge de manière autonome, sans intervention du processeur. Cela permet de créer des délais précis, de mesurer des durées, ou de générer des événements périodiques.

Le PIC16F683 dispose de deux timers : le Timer0 (8 bits, compte de 0 à 255) et le Timer1 (16 bits, compte de 0 à 65535). Chaque timer peut fonctionner en mode timer (horloge interne) ou en mode compteur (signal externe).

L'avantage principal des timers par rapport à __delay_ms() est qu'ils fonctionnent en arrière-plan. Le processeur peut exécuter d'autres tâches pendant que le timer compte, ce qui est essentiel pour les applications temps réel.

📊 Timer0 — Compteur 8 bits

Le Timer0 est le timer le plus simple du PIC16F683. Il utilise un registre 8 bits (TMR0) qui s'incrémente à chaque cycle d'horloge (ou chaque front du signal externe).

Registres de configuration

RegistreBitFonction
OPTION_REGT0CS (bit 5)Source : 0 = horloge interne, 1 = signal externe (T0CKI)
OPTION_REGPSA (bit 3)Prescaler : 0 = assigné au Timer0, 1 = assigné au WDT
OPTION_REGPS2:PS0 (bits 2-0)Valeur du prescaler : 000=1:2, 001=1:4, ... 111=1:256
INTCONT0IF (bit 2)Flag d'overflow : passe à 1 quand TMR0 déborde (255→0)
INTCONT0IE (bit 5)Active l'interruption Timer0

Exemple : Clignotement avec Timer0

C (XC8)
#include <xc.h>
#define _XTAL_FREQ 4000000

void main(void) {
    ANSEL = 0b00000000;
    TRISA = 0b11111110;  // RA0 en sortie
    PORTA = 0;

    // Config Timer0 : horloge interne, prescaler 1:256
    OPTION_REG = 0b00000111;
    // T0CS=0 (interne), PSA=0 (prescaler→Timer0)
    // PS2:PS0=111 (1:256)

    TMR0 = 0;  // Initialiser le compteur

    while(1) {
        // Timer0 déborde toutes les ~65ms
        // (256 × 256 / 1MHz = 65.536ms)
        if(INTCONbits.T0IF) {
            INTCONbits.T0IF = 0;  // Reset flag
            PORTA ^= 0b00000001;  // Toggle LED
        }
    }
}

📊 Timer1 — Compteur 16 bits

Le Timer1 offre une plage de comptage beaucoup plus grande (0 à 65535) grâce à ses deux registres 8 bits : TMR1H (octet haut) et TMR1L (octet bas). Il peut aussi utiliser un cristal externe 32.768 kHz pour un comptage très précis.

Registre T1CON

BitNomFonction
7T1GINVInversion du gate
6TMR1GEGate enable
5-4T1CKPSPrescaler : 00=1:1, 01=1:2, 10=1:4, 11=1:8
3T1OSCENOscillateur Timer1 activé
2T1SYNCSynchronisation externe
1TMR1CSSource : 0 = Fosc/4, 1 = externe
0TMR1ON1 = Timer1 activé

Exemple : Délai de 500ms avec Timer1

C (XC8)
// Timer1 : Fosc/4 = 1MHz, prescaler 1:8
// Fréquence comptage = 125 kHz → période = 8µs
// Pour 500ms : 500000/8 = 62500 comptages
// Valeur initiale = 65536 - 62500 = 3036 = 0x0BDC

T1CON = 0b00110001;  // Prescaler 1:8, ON
TMR1H = 0x0B;
TMR1L = 0xDC;

while(!PIR1bits.TMR1IF);  // Attendre overflow
PIR1bits.TMR1IF = 0;       // Reset flag

💡 Timer0 vs Timer1

Timer0 (8 bits) : Simple, idéal pour des délais courts et des tâches de base. Déborde fréquemment (max ~65ms avec prescaler 256).

Timer1 (16 bits) : Plus polyvalent, permet des délais plus longs (jusqu'à ~524ms avec prescaler 8). Peut utiliser un cristal externe pour une précision maximale.

🧮 Calcul des délais Timer

Formule générale pour calculer le temps de débordement :

// Temps = (Valeur_max - Valeur_initiale) × Prescaler × (4 / Fosc)
//
// Timer0 (8 bits, Fosc=4MHz, prescaler=256) :
// Temps = 256 × 256 × (4/4000000) = 65.536 ms
//
// Timer1 (16 bits, Fosc=4MHz, prescaler=8) :
// Temps max = 65536 × 8 × (4/4000000) = 524.288 ms