⚡ Qu'est-ce qu'une interruption ?

Une interruption est un mécanisme matériel qui permet au microcontrôleur de suspendre temporairement l'exécution du programme principal pour traiter un événement urgent (appui sur un bouton, fin de conversion ADC, débordement d'un timer, etc.).

Sans interruption, le processeur devrait constamment vérifier (« polling ») si un événement s'est produit, ce qui gaspille des cycles. Avec les interruptions, le PIC peut vaquer à ses occupations et réagir instantanément quand un événement se produit.

Le PIC16F683 utilise un vecteur d'interruption unique à l'adresse 0x004. Toutes les interruptions passent par la même routine (ISR — Interrupt Service Routine), et c'est au programmeur de vérifier les flags pour déterminer la source.

📋 Sources d'interruption du PIC16F683

SourceFlagEnableDescription
INT (RA2)INTFINTEInterruption externe sur broche RA2
Timer0T0IFT0IEDébordement du Timer0
Timer1TMR1IFTMR1IEDébordement du Timer1
PORTB ChangeRAIFRAIEChangement d'état sur PORTA
ADCADIFADIEFin de conversion analogique
ComparateurC1IF/C2IFC1IE/C2IEChangement de sortie comparateur
EEPROMEEIFEEIEFin d'écriture EEPROM

⚙️ Activation d'une interruption

Pour activer une interruption, trois conditions doivent être remplies :

  1. GIE = 1 — Global Interrupt Enable : active le système d'interruptions global
  2. PEIE = 1 — Peripheral Interrupt Enable : nécessaire pour les interruptions périphériques (Timer1, ADC, etc.)
  3. xIE = 1 — Activer l'interruption spécifique (T0IE, INTE, TMR1IE, etc.)
Activation des interruptions
// Activer l'interruption Timer0
INTCONbits.T0IE = 1;   // Enable Timer0 interrupt
INTCONbits.T0IF = 0;   // Clear flag
INTCONbits.GIE = 1;    // Global interrupt ON

// Activer l'interruption Timer1 (périphérique)
PIE1bits.TMR1IE = 1;   // Enable Timer1 interrupt
PIR1bits.TMR1IF = 0;   // Clear flag
INTCONbits.PEIE = 1;   // Peripheral interrupt ON
INTCONbits.GIE = 1;    // Global interrupt ON

📝 Exemple complet : LED clignotante par interruption Timer0

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

volatile unsigned char compteur = 0;

// Routine d'interruption (ISR)
void __interrupt() isr(void) {
    if(INTCONbits.T0IF) {
        INTCONbits.T0IF = 0;  // TOUJOURS reset le flag !
        TMR0 = 6;             // Recharger pour ~1ms
        compteur++;

        if(compteur >= 250) {  // ~250ms écoulées
            compteur = 0;
            PORTA ^= 0b00000001;  // Toggle LED
        }
    }
}

void main(void) {
    OSCCON = 0b01100000;
    ANSEL = 0;
    TRISA = 0b11111110;
    PORTA = 0;

    // Config Timer0
    OPTION_REG = 0b00000010;  // Prescaler 1:8
    TMR0 = 6;

    // Activer interruptions
    INTCONbits.T0IE = 1;
    INTCONbits.T0IF = 0;
    INTCONbits.GIE = 1;

    // Boucle principale : le processeur est libre !
    while(1) {
        // Peut faire d'autres tâches ici
        NOP();
    }
}

⚠️ Règles essentielles pour les ISR

  • Toujours remettre le flag d'interruption à 0 (sinon l'ISR se redéclenche en boucle)
  • Garder l'ISR la plus courte possible
  • Déclarer les variables partagées avec volatile
  • Ne pas utiliser __delay_ms() dans une ISR
  • Éviter les calculs complexes dans l'ISR

🔘 Exemple : Bouton avec interruption externe

Interruption sur appui bouton (RA2/INT)
void __interrupt() isr(void) {
    if(INTCONbits.INTF) {
        INTCONbits.INTF = 0;
        PORTA ^= 0b00000010;  // Toggle LED sur RA1
        __delay_ms(50);       // Anti-rebond simple
    }
}

void main(void) {
    ANSEL = 0;
    TRISA = 0b11111100;  // RA0-RA1 sortie, RA2 entrée (INT)
    PORTA = 0;

    OPTION_REGbits.INTEDG = 0;  // Front descendant
    INTCONbits.INTE = 1;        // Enable INT
    INTCONbits.INTF = 0;
    INTCONbits.GIE = 1;

    while(1) { NOP(); }
}