Lezione 20 di 43

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.

Sorgenti di interrupt su PORTB — RB0/INT vs RB4-RB7/IOC PORTB RB7 RB6 RB5 RB4 RB3 RB2 RB1 RB0 RB0 / INT INTEDG=1→↑ INTEDG=0→↓ setta INTF RB4–RB7 / IOC qualsiasi cambio di stato setta RBIF INTCON — bit rilevanti GIE (bit7) Global Interrupt Enable T0IE (bit5) abilita TMR0 INTE (bit4) abilita RB0/INT RBIE (bit3) abilita RB4-RB7 IOC T0IF (bit2) flag TMR0 overflow INTF (bit1) flag RB0/INT RBIF (bit0) flag RB4-RB7 IOC

2 · RB0/INT — Interrupt esterno con selezione del fronte

INTEDG — selezione fronte attivo su RB0/INT INTEDG = 1 — fronte di salita ↑ INTF=1 INTF=1 interrupt ad ogni salita: pulsante con pull-down INTEDG = 0 — fronte di discesa ↓ INTF=1 INTF=1 interrupt al rilascio: pulsante con pull-up

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.

ISR con sorgenti multiple — albero di identificazione ISR @ 0x0004 BTFSS INTCON,T0IF no sì BCF T0IF BSF TICK_FLAG BTFSS INTCON,INTF no→EXIT sì BCF INTF BSF BTN_FLAG ripristina contesto + RETFIE Latenza interrupt: 3-4 cicli macchina dall’overflow al primo opcode della ISR = 3–4 µs @ 4 MHz
; ============================================================
; 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

  1. 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.
  2. 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.
  3. 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).
  4. 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