Lezione 19
🎓 PIC16 — From Zero to Hero · Lezione 19 · Modulo 3
Interrupt su RB0/INT e RB4–RB7 🔔
Due sorgenti di interrupt su PORTB: il pin dedicato RB0/INT con selezione del fronte
e il cambio di stato su RB4–RB7. Gestione di sorgenti multiple nella stessa ISR.
🎯 Cosa impari in questa lezione
- Differenza tra RB0/INT (fronte singolo) e RB4–RB7 IOC (cambio di stato)
- Configurare INTEDG in OPTION_REG per scegliere fronte salita o discesa
- Leggere PORTB nella ISR per cancellare il latch IOC e azzerare RBIF
- Rilevare quale pin è cambiato con XOR tra valore attuale e PORTB_LAST
- Gestire più sorgenti di interrupt in un’unica ISR con catena BTFSS
- Latenza di interrupt: 3–4 cicli macchina dalla richiesta al primo opcode ISR
- Il pattern priorità software: ordine dei controlli IF nella ISR
📋 Scheda lezione
Lezione: 19 / 42
Modulo: 3 — Timer
Device: PIC16F84A
Prerequisiti: Lez. 18
Difficoltà: ★★★★☆
Bit nuovi: INTF, INTE, RBIF, RBIE, INTEDG
Tecnica chiave: IOC, XOR per rilevare cambio pin
1 · Le due sorgenti di interrupt su PORTB
Il PIC16F84A offre due meccanismi distinti per reagire a eventi su PORTB senza polling. Il pin RB0/INT è dedicato: reagisce a un singolo fronte (salita o discesa) selezionabile con il bit INTEDG in OPTION_REG, setta INTF e chiama la ISR se INTE=1. I pin RB4–RB7 usano invece il meccanismo IOC (Interrupt-On-Change): qualsiasi transizione su qualunque di questi pin setta RBIF, indipendentemente dalla direzione.
2 · RB0/INT — Interrupt esterno con selezione del fronte
Configurazione RB0/INT: OPTION_REG bit6 = INTEDG (1=salita, 0=discesa). INTCON bit4 = INTE (abilita interrupt). INTCON bit1 = INTF (flag, da azzerare nella ISR). RB0 deve essere configurato come input in TRISB (bit0=1). Se si usano pull-up interni PORTB, impostare RBPU=0 in OPTION_REG — ma attenzione: RBPU=0 abilita pull-up su tutti i pin TRISB=1, incluso RB0.
Codice — Contatore di pulsazioni su RB0
; ============================================================ ; Lezione 19 — Interrupt esterno su RB0/INT ; Conta i fronti di salita su RB0, mostra conteggio su RB1-RB4 ; PIC16F84A @ 4 MHz ; ============================================================ LIST P=16F84A #INCLUDE <p16f84a.inc> __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF CBLOCK 0x0C W_SAVE STATUS_SAVE BTN_COUNT ; contatore pulsazioni (0-15) ENDC ORG 0x0000 GOTO INIT ORG 0x0004 ISR MOVWF W_SAVE SWAPF STATUS,W MOVWF STATUS_SAVE ; Controlla sorgente: INTF (bit1 INTCON) BTFSS INTCON,INTF GOTO ISR_EXIT BCF INTCON,INTF ; OBBLIGATORIO: pulisci flag INCF BTN_COUNT,F MOVLW D'16' SUBWF BTN_COUNT,W ; W = BTN_COUNT - 16 BTFSC STATUS,Z CLRF BTN_COUNT ; reset a 0 dopo 15 ; Mostra conteggio su RB1-RB4 (nibble basso × 2) MOVF BTN_COUNT,W ANDLW 0x0F ; sicurezza: solo 4 bit MOVWF PORTB ; RB0-RB3 come display ISR_EXIT SWAPF STATUS_SAVE,W MOVWF STATUS SWAPF W_SAVE,F SWAPF W_SAVE,W RETFIE ORG 0x0020 INIT BSF STATUS,RP0 ; Bank 1 MOVLW b'10000000' ; RBPU=1 (pull-up off), INTEDG=0 (fronte discesa) MOVWF OPTION_REG MOVLW b'00000001' ; RB0 input, RB1-RB7 output MOVWF TRISB BCF STATUS,RP0 ; Bank 0 CLRF PORTB CLRF BTN_COUNT ; Abilita INT su RB0 MOVLW b'10010000' ; GIE=1, INTE=1 MOVWF INTCON MAIN NOP GOTO MAIN END
3 · RB4–RB7 IOC — Interrupt su cambio di stato
Il meccanismo IOC confronta il valore attuale di RB4–RB7 con un latch interno aggiornato all’ultima lettura di PORTB. Se differiscono, setta RBIF. La cancellazione del flag richiede obbligatoriamente una lettura di PORTB nella ISR — senza questa lettura, RBIF non si azzera e la ISR entra in loop. Per rilevare quale pin è cambiato si usa XOR tra il valore letto e PORTB_LAST (il valore salvato alla lettura precedente).
⚠ Sequenza obbligatoria per cancellare RBIF:
1. MOVF PORTB,W — legge PORTB e aggiorna il latch di confronto interno
2. BCF INTCON,RBIF — azzera il flag
Se si inverte l’ordine (BCF prima della lettura) RBIF si riazzera ma il latch resta misallineato — alla prossima ISR il cambio risulta già registrato anche se il pin non è cambiato.
Codice — Rilevamento cambio stato RB4–RB7
; ============================================================ ; Lezione 19 — Interrupt su cambio stato RB4-RB7 (RBIF) ; Legge quale pin è cambiato e aggiorna un registro di stato ; PIC16F84A @ 4 MHz ; ============================================================ LIST P=16F84A #INCLUDE <p16f84a.inc> __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF CBLOCK 0x0C W_SAVE STATUS_SAVE PORTB_LAST ; valore precedente di PORTB (per rilevare quale pin) CHANGED_PINS ; maschera dei pin che hanno cambiato stato FLAGS ENDC #DEFINE RB_CHANGED FLAGS,0 ORG 0x0000 GOTO INIT ORG 0x0004 ISR MOVWF W_SAVE SWAPF STATUS,W MOVWF STATUS_SAVE BTFSS INTCON,RBIF GOTO ISR_EXIT ; Leggi PORTB per latching (obbligatorio per cancellare RBIF) MOVF PORTB,W ; lettura azzera mismatch latch BCF INTCON,RBIF ; Calcola quali pin sono cambiati XORWF PORTB_LAST,W ; W = bit cambiati (XOR con valore precedente) ANDLW 0xF0 ; solo RB4-RB7 MOVWF CHANGED_PINS ; Aggiorna PORTB_LAST MOVF PORTB,W MOVWF PORTB_LAST BSF RB_CHANGED ; segnala al main ISR_EXIT SWAPF STATUS_SAVE,W MOVWF STATUS SWAPF W_SAVE,F SWAPF W_SAVE,W RETFIE ORG 0x0030 INIT BSF STATUS,RP0 MOVLW b'01000000' ; RBPU=0 (pull-up abilitati!), INTEDG=1 MOVWF OPTION_REG MOVLW 0xFF ; tutto input MOVWF TRISB BCF STATUS,RP0 CLRF FLAGS ; Leggi PORTB iniziale per inizializzare PORTB_LAST MOVF PORTB,W MOVWF PORTB_LAST ; Abilita RBIE MOVLW b'10001000' ; GIE=1, RBIE=1 MOVWF INTCON MAIN BTFSS RB_CHANGED GOTO MAIN BCF RB_CHANGED ; CHANGED_PINS contiene la maschera dei pin cambiati ; Es: 0b00110000 = RB4 e RB5 sono cambiati ; Azione: rispecchia RB4-RB7 su RA0-RA3 (se fossero output) ; (logica applicativa omessa — dipende dal progetto) GOTO MAIN END
4 · ISR con sorgenti multiple
Il PIC16 ha un unico vettore di interrupt a 0x0004. Quando la ISR viene invocata non si sa quale sorgente ha scattato — bisogna controllare ogni flag IF nell’ordine di priorità desiderato. La priorità è software: la sorgente controllata per prima viene servita per prima anche se entrambe hanno scattato contemporaneamente.
; ============================================================ ; Lezione 19 — Gestione sorgenti multiple di interrupt ; Timer0 overflow + RB0/INT contemporaneamente ; Pattern: identificazione sorgente nella ISR ; PIC16F84A @ 4 MHz ; ============================================================ LIST P=16F84A #INCLUDE <p16f84a.inc> __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF CBLOCK 0x0C W_SAVE STATUS_SAVE OVF_COUNT FLAGS ENDC #DEFINE TICK_FLAG FLAGS,0 ; da TMR0 #DEFINE BTN_FLAG FLAGS,1 ; da RB0/INT ORG 0x0000 GOTO INIT ; ============================================================ ; ISR unica per tutte le sorgenti ; Controlla ogni flag IF in ordine di priorità ; ============================================================ ORG 0x0004 ISR MOVWF W_SAVE SWAPF STATUS,W MOVWF STATUS_SAVE ; --- Priorità 1: Timer0 --- BTFSS INTCON,T0IF GOTO CHECK_INT BCF INTCON,T0IF DECFSZ OVF_COUNT,F GOTO CHECK_INT MOVLW D'15' MOVWF OVF_COUNT BSF TICK_FLAG ; --- Priorità 2: RB0/INT --- CHECK_INT BTFSS INTCON,INTF GOTO ISR_EXIT BCF INTCON,INTF BSF BTN_FLAG ISR_EXIT SWAPF STATUS_SAVE,W MOVWF STATUS SWAPF W_SAVE,F SWAPF W_SAVE,W RETFIE ORG 0x0040 INIT BSF STATUS,RP0 ; T0CS=0, PSA=0, PS=111 (÷256), INTEDG=1 (fronte salita RB0), RBPU=1 MOVLW b'11000111' MOVWF OPTION_REG CLRF TRISA MOVLW 0x01 ; RB0 input MOVWF TRISB BCF STATUS,RP0 CLRF PORTA CLRF FLAGS MOVLW D'15' MOVWF OVF_COUNT ; Abilita TMR0 + INT + GIE MOVLW b'10110000' ; GIE=1, T0IE=1, INTE=1 MOVWF INTCON MAIN ; Gestisci tick temporale BTFSC TICK_FLAG CALL ON_TICK ; Gestisci pulsante BTFSC BTN_FLAG CALL ON_BUTTON GOTO MAIN ON_TICK BCF TICK_FLAG MOVLW 0x01 XORWF PORTA,F ; toggle RA0 ogni ~1s RETURN ON_BUTTON BCF BTN_FLAG MOVLW 0x02 XORWF PORTA,F ; toggle RA1 a ogni pressione RETURN END
5 · Confronto RB0/INT vs RB4–RB7 IOC
RB0/INT
Un solo pin. Fronte selezionabile (↑ o ↓). Ideale per pulsante singolo, segnale di sincronismo esterno, encoder. Non rileva entrambi i fronti nello stesso ciclo. Non richiede lettura di PORTB per cancellare.
RB4–RB7 IOC
Quattro pin monitorati contemporaneamente. Reagisce a qualsiasi transizione (↑ e ↓). Ideale per tastiera, DIP switch, bus parallelo. Richiede lettura PORTB + PORTB_LAST per identificare quale pin. Pull-up interni utili per pulsanti senza resistenze esterne.
🧪 Prova tu
- Carica il programma RB0/INT. Nel simulatore, imposta RB0 a 0, poi porta a 1 (fronte salita). Osserva INTF settarsi a 1 e la ISR entrare. Verifica BTN_COUNT incrementare su PORTB.
- Cambia INTEDG da 1 a 0 (fronte discesa). Ripeti il test — ora l’interrupt deve scattare quando RB0 torna a 0, non quando va a 1.
- Carica il programma IOC. Cambia il valore di RB5 nel pannello I/O. Verifica che RBIF si setti, la ISR entri, e CHANGED_PINS contenga 0b00100000 (solo bit5 alto).
- Sfida: nel programma a sorgenti multiple, aggiungi anche RBIE. Nella ISR gestisci tre sorgenti: T0IF, INTF, RBIF. Verifica che ognuna aggiorni un LED diverso su RA0, RA1, RA2.
❓ Quiz rapido
1. Perché nella ISR per RB4–RB7 bisogna leggere PORTB prima di fare BCF INTCON,RBIF?
Mostra risposta
RBIF è generato da un comparatore interno che confronta i pin RB4–RB7 con un latch aggiornato all’ultima lettura di PORTB. Finché il latch non corrisponde ai pin, RBIF rimane alto. Leggere PORTB aggiorna il latch — solo dopo il comparatore non vede differenze e RBIF può essere cancellato con BCF. Se si fa BCF prima della lettura, RBIF si azzera momentaneamente ma il latch è ancora disallineato: al ciclo successivo RBIF si risetta immediatamente anche senza nuove transizioni → loop infinito nella ISR.
2. Come si rileva quale pin tra RB4–RB7 ha cambiato stato?
Mostra risposta
Si mantiene in RAM una variabile PORTB_LAST con il valore letto alla ISR precedente. All’ingresso della ISR corrente si legge PORTB in W, si XOR con PORTB_LAST: i bit che risultano 1 nell’XOR sono quelli che hanno cambiato valore. Si applica ANDLW 0xF0 per isolare solo RB4–RB7. Infine si salva il nuovo valore in PORTB_LAST. Questa tecnica funziona anche se cambiano più pin contemporaneamente.
3. Se T0IF e INTF scattano nello stesso istante, quale viene servita per prima nella ISR delle sorgenti multiple?
Mostra risposta
T0IF, perché viene controllata per prima con BTFSS INTCON,T0IF. La priorità non è hardware ma dipende dall’ordine dei controlli BTFSS nella ISR. Il programmatore decide la priorità mettendo il controllo della sorgente più critica per primo. INTF viene servita dopo, nello stesso ingresso ISR se il codice non usa GOTO per saltare i controlli successivi.
4. RB0 può essere usato sia come INT che come pin IOC (RB0 fa parte di RB0–RB3 che non hanno IOC). Vero o falso?
Mostra risposta
Falso. Sul PIC16F84A solo RB4–RB7 hanno il meccanismo IOC. RB0–RB3 non generano RBIF. RB0 ha però la funzione speciale INT (interrupt esterno dedicato tramite INTF/INTE). I due meccanismi sono distinti e non sovrapposti: RB0 = solo INT, RB4–RB7 = solo IOC.
Prossima lezione →
Lezione 20 — Progetto Modulo 3: Orologio digitale
Timer0 + interrupt come base del tempo. Display multiplexato HH:MM su quattro cifre 7 segmenti. Pulsanti su RB0/INT per set ora e set minuti. Il progetto riassuntivo del Modulo 3.
© Prof. D. Bertolino · PIC16 From Zero to Hero · WebPicSimulator · Lezione 19