INT sur GP2, Timer0/1/2, IOC (Interrupt-On-Change), ADC… : réagir instantanément aux événements
Une interruption permet au microcontrôleur de suspendre le programme principal pour exécuter une routine courte (ISR), dès qu’un événement se produit : débordement d’un timer, appui bouton, fin de conversion ADC, changement d’état d’une broche, etc.
Le PIC12F683 possède un vecteur d’interruption unique à l’adresse 0x0004. Toutes les sources arrivent dans la même ISR : c’est à toi de tester les flags pour savoir qui a déclenché l’interruption.
💡 Astuce : si tu ne veux pas câbler MCLR, mets MCLRE = OFF (GP3 devient une entrée).
Sur PIC12F683, on a des interruptions “cœur” (dans INTCON) et des interruptions “périphériques” (via PIE1/PIR1 + PEIE).
| Source | Flag | Enable | Où ? | Description |
|---|---|---|---|---|
| INT externe | INTF | INTE | INTCON | Interruption externe sur GP2/INT |
| Timer0 | T0IF | T0IE | INTCON | Débordement de TMR0 |
| IOC (changement GPIO) | GPIF | GPIE | INTCON (+ IOC) | Changement sur GP0..GP5 selon masque IOC |
| Timer1 | TMR1IF | TMR1IE | PIR1/PIE1 | Débordement Timer1 (périphérique) |
| Timer2 | TMR2IF | TMR2IE | PIR1/PIE1 | Débordement Timer2 (périphérique) |
| ADC | ADIF | ADIE | PIR1/PIE1 | Fin de conversion ADC |
| EEPROM | EEIF | EEIE | PIR1/PIE1 | Fin d’écriture EEPROM |
| Comparateur | CMIF | CMIE | PIR1/PIE1 | Événement comparateur |
| CCP1 | CCP1IF | CCP1IE | PIR1/PIE1 | Capture/Compare/PWM (selon mode) |
Note : Les noms de flags/enables ci-dessus correspondent aux registres INTCON/PIE1/PIR1 du PIC12F683.
Pour que ça déclenche, il faut :
// Timer0 (INTCON) INTCONbits.T0IF = 0; // Clear flag INTCONbits.T0IE = 1; // Enable Timer0 interrupt INTCONbits.GIE = 1; // Global interrupt ON // ADC (périphérique : PIE1/PIR1 + PEIE) PIR1bits.ADIF = 0; // Clear flag PIE1bits.ADIE = 1; // Enable ADC interrupt INTCONbits.PEIE = 1; // Peripheral interrupts ON INTCONbits.GIE = 1; // Global interrupt ON // IOC (Interrupt-On-Change sur GPIO) IOC = 0b00000100; // Exemple : surveiller GP2 (bit2) (adapte selon besoin) INTCONbits.GPIF = 0; // Clear flag INTCONbits.GPIE = 1; // Enable IOC interrupt INTCONbits.GIE = 1;
Après un changement GPIO, le flag GPIF peut rester actif tant que l’état “mismatch” n’est pas résolu. En pratique : lis GPIO puis remets GPIF=0 dans l’ISR.
Exemple “propre” : l’ISR fait le minimum (recharge + compteur + toggle), et la boucle principale reste libre.
/* * Projet : LED clignotante par interruption Timer0 * MCU : PIC12F683 (DIP-8) * LED : GP0 (pin 7) -> R 330Ω -> LED -> GND */ #pragma config FOSC = INTRCIO // Oscillateur interne + I/O sur GP4/GP5 #pragma config WDTE = OFF #pragma config PWRTE = ON #pragma config MCLRE = ON // GP3/MCLR actif (mets OFF si tu veux GP3 en entrée) #pragma config BOREN = ON #pragma config CP = OFF #pragma config CPD = OFF #include <xc.h> #include <stdint.h> #define _XTAL_FREQ 4000000 volatile uint8_t ms = 0; // ISR unique (vecteur 0x0004) void __interrupt() isr(void) { if(INTCONbits.T0IF) { INTCONbits.T0IF = 0; // TOUJOURS effacer le flag TMR0 = 6; // Ajuste pour ~1ms selon prescaler ms++; if(ms >= 250) { // ~250 ms ms = 0; GPIO ^= 0b00000001; // Toggle GP0 } } } void main(void) { // Oscillateur interne ~4MHz (IRCF=110) OSCCON = 0b01100000; // Tout en numérique ANSEL = 0; // ADC OFF (canaux en numérique) CMCON0 = 0x07; // Comparateur OFF (recommandé pour I/O) // GP0 en sortie, le reste en entrée (bits GP5..GP0) TRISIO = 0b00111110; GPIO = 0; // Timer0 : prescaler + source interne OPTION_REG = 0b00000010; // PSA=0, PS=010 (1:8) - exemple, adapte si besoin TMR0 = 6; // Interrupt Timer0 INTCONbits.T0IF = 0; INTCONbits.T0IE = 1; INTCONbits.GIE = 1; while(1) { // CPU libre : tu peux faire autre chose ici NOP(); } }
Ici, l’ISR se contente de poser un drapeau. Le debounce se fait dans la boucle principale.
/* * Bouton : GP2/INT (pin 5) vers GND (avec pull-up) * LED : GP0 (pin 7) */ #pragma config FOSC = INTRCIO #pragma config WDTE = OFF #pragma config PWRTE = ON #pragma config MCLRE = ON #include <xc.h> #define _XTAL_FREQ 4000000 volatile unsigned char btn_event = 0; void __interrupt() isr(void) { if(INTCONbits.INTF) { INTCONbits.INTF = 0; btn_event = 1; // On traite dans main } } void main(void) { OSCCON = 0b01100000; ANSEL = 0; CMCON0 = 0x07; // GP0 sortie (LED), GP2 entrée (bouton) TRISIO = 0b00111110; // GP0=0 sortie, le reste entrée GPIO = 0; // Pull-up interne sur GPIO (si tu veux) + activer WPU sur GP2 si besoin // OPTION_REGbits.nGPPU = 0; // Pull-ups globaux ON (selon config) // WPUbits.WPU2 = 1; // Pull-up sur GP2 (si registre dispo dans ton header) // INT externe sur GP2 OPTION_REGbits.INTEDG = 0; // 0 = front descendant (bouton vers GND) INTCONbits.INTF = 0; INTCONbits.INTE = 1; INTCONbits.GIE = 1; while(1) { if(btn_event) { btn_event = 0; // Anti-rebond (dans main, OK) __delay_ms(30); if( (GPIO & 0b00000100) == 0 ) { // GP2 toujours à 0 ? GPIO ^= 0b00000001; // Toggle LED (GP0) // Attendre relâchement si tu veux : // while((GPIO & 0b00000100) == 0) { } } } NOP(); } }