INT sur RB0, Timer0/1/2, changement sur PORTB, ADC, USART… : 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 sur un bouton, fin de conversion ADC, réception série, etc.
Le PIC16F877A possède lui aussi 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 quelle source a déclenché l’interruption.
💡 Astuce : pour éviter les conflits, on met souvent la LED sur PORTD et le bouton d’interruption sur RB0.
Sur le PIC16F877A, on retrouve des interruptions “cœur” dans INTCON, puis des interruptions périphériques via PIE1 / PIR1 et PIE2 / PIR2.
| Source | Flag | Enable | Où ? | Description |
|---|---|---|---|---|
| INT externe | INTF | INTE | INTCON | Interruption externe sur RB0/INT |
| Timer0 | T0IF | T0IE | INTCON | Débordement de TMR0 |
| Changement PORTB | RBIF | RBIE | INTCON | Changement sur RB4 à RB7 |
| Timer1 | TMR1IF | TMR1IE | PIR1 / PIE1 | Débordement Timer1 |
| Timer2 | TMR2IF | TMR2IE | PIR1 / PIE1 | Débordement Timer2 |
| ADC | ADIF | ADIE | PIR1 / PIE1 | Fin de conversion ADC |
| EEPROM | EEIF | EEIE | PIR2 / PIE2 | Fin d’écriture EEPROM |
| USART RX | RCIF | RCIE | PIR1 / PIE1 | Réception série |
| USART TX | TXIF | TXIE | PIR1 / PIE1 | Transmission série |
| CCP1 | CCP1IF | CCP1IE | PIR1 / PIE1 | Capture / Compare / PWM |
| CCP2 | CCP2IF | CCP2IE | PIR2 / PIE2 | Deuxième module CCP |
| SSP (I2C/SPI) | SSPIF | SSPIE | PIR1 / PIE1 | Événement I2C / SPI |
| PSP | PSPIF | PSPIE | PIR1 / PIE1 | Parallel Slave Port |
Pour qu’une interruption fonctionne, 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) PIR1bits.ADIF = 0; // Clear flag PIE1bits.ADIE = 1; // Enable ADC interrupt INTCONbits.PEIE = 1; // Peripheral interrupts ON INTCONbits.GIE = 1; // Global interrupt ON // Interruption externe sur RB0/INT INTCONbits.INTF = 0; INTCONbits.INTE = 1; OPTION_REGbits.INTEDG = 0; // 0 = front descendant INTCONbits.GIE = 1; // Changement sur PORTB (RB4 à RB7) INTCONbits.RBIF = 0; INTCONbits.RBIE = 1; INTCONbits.GIE = 1;
Pour remettre correctement RBIF à zéro, il faut en pratique lire PORTB d’abord, puis effacer le flag. Sinon l’interruption peut revenir immédiatement.
Exemple propre : l’ISR fait le minimum (reload + compteur + bascule de la LED) et la boucle principale reste libre.
/* * Projet : LED clignotante par interruption Timer0 * MCU : PIC16F877A (DIP-40) * LED : RD0 (pin 19) -> R 330Ω -> LED -> GND */ #pragma config FOSC = XT #pragma config WDTE = OFF #pragma config PWRTE = ON #pragma config BOREN = ON #pragma config LVP = OFF #pragma config CPD = OFF #pragma config WRT = OFF #pragma config CP = OFF #include <xc.h> #include <stdint.h> #define _XTAL_FREQ 4000000 volatile uint8_t ticks = 0; void __interrupt() isr(void) { if(INTCONbits.T0IF) { INTCONbits.T0IF = 0; TMR0 = 6; // Ajuste selon ton prescaler et ta fréquence ticks++; if(ticks >= 250) { ticks = 0; PORTDbits.RD0 = !PORTDbits.RD0; } } } void main(void) { // Met les broches analogiques en numérique ADCON1 = 0x06; // RD0 en sortie TRISD = 0b11111110; PORTD = 0x00; // Timer0 : horloge interne + prescaler 1:8 OPTION_REG = 0b00000010; TMR0 = 6; // Interruptions Timer0 INTCONbits.T0IF = 0; INTCONbits.T0IE = 1; INTCONbits.GIE = 1; while(1) { // Le CPU reste libre pour autre chose NOP(); } }
Ici, l’ISR se contente de poser un drapeau. L’anti-rebond est géré dans la boucle principale.
/* * Bouton : RB0/INT (pin 33) vers GND avec pull-up externe * LED : RD0 (pin 19) */ #pragma config FOSC = XT #pragma config WDTE = OFF #pragma config PWRTE = ON #pragma config BOREN = ON #pragma config LVP = OFF #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; } } void main(void) { ADCON1 = 0x06; // RB0 en entrée (bouton), RD0 en sortie (LED) TRISBbits.TRISB0 = 1; TRISDbits.TRISD0 = 0; PORTDbits.RD0 = 0; // Interruption externe sur front descendant OPTION_REGbits.INTEDG = 0; INTCONbits.INTF = 0; INTCONbits.INTE = 1; INTCONbits.GIE = 1; while(1) { if(btn_event) { btn_event = 0; // Anti-rebond dans main __delay_ms(30); if(PORTBbits.RB0 == 0) { PORTDbits.RD0 = !PORTDbits.RD0; } } } }
Très utile pour détecter l’appui sur plusieurs boutons. Sur le PIC16F877A, cette interruption concerne principalement RB4 à RB7.
volatile unsigned char portb_event = 0; void __interrupt() isr(void) { if(INTCONbits.RBIF) { unsigned char dummy = PORTB; // Lecture obligatoire INTCONbits.RBIF = 0; portb_event = 1; } }