INTERRUPT con e senza priorità
Riprendiamo la schematizzazione con gli interruttori prima
accennata.
Ad esempio, per una struttura di interrupt senza priorità, restando
valida la differenziazione tra interrupt periferici e non periferici già
presente nei mid-range:
-
Una sorgente di interrupt non periferico agisce sul bit
di flag IF, portandolo a livello 1.
Se, contemporaneamente è stato
abilitato quell' interrupt portando da programma a 1 il bit IE, la chiamata
di interrupt arriva allo switch costituito dal bit GIE.
Se questo bit è
stato posto a 1, la richiesta di interrupt viene processata.
-
Analogamente per un interrupt periferico, il flag di abilitazione IE a
livello 1 consente il passaggio della richiesta evidenziata dal flag IF.
Solamente che, per arrivare al processo di richiesta di interrupt, la
chiamata deve passare non solo da GIE, ma anche attraverso lo switch PEIE,
che, per abilitarla, deve essere stato pure lui posto a 1.
E' evidente che tutti gli interrupt periferici possono
essere esclusi o inclusi agendo sul solo bit di "interruttore
generale" PEIE, mentre GIE abilità tutto il complesso delle
richieste di interrupt.
Se consideriamo invece la struttura di interrupt con
priorità:
osserviamo che la funzione dei bit IF e IE è
identica, ma tra la sorgente e la destinazione è interposto un deviatore
costituito dal bit IP: se è stato programmato a livello 1, la richiesta si
inserisce sul ramo ad alta priorità; se è stato programmato a livello 0 la
richiesta passa al ramo a bassa priorità.
Gli "interruttori generali" dei due rami sono analoghi a quanto
visto per la singola priorità; solamente cambiano i nomi dei due bit,
diventando GIEL per lo switch della bassa priorità e GIEH per
quello complessivo.
La presenza o meno dei "deviatori" IP è fissata
dal bit IPEN: questa condizione è fissata nella fase di _CONFIG,
dato che costituisce un elemento fondamentale dell' organizzazione del
controller.
Per default al POR, il bit IPEN è disabilitato e il processore parte in
modalità "legacy" con i mid-range, ovvero con interrupt senza
priorità.
NOTA:
Va anche notato che i bit IF di flag dell' evento vengono
azionati in ogni caso dall' evento stesso, sia che la catena dell'
interrupt sia abilitata oppure no.
In altre parole, se, ad esempio, il TIMER0 ha terminato il conteggio,
il relativo flag TMR0IF va a livello 1.
Solo se il bit TMR0IE è stato abilitato dal programma, e lo è anche lo switch generale GIE, l' evento di
fine conteggio genererà una richiesta di interrupt.
Se TMR0IE e/o GIE
sono disabilitati, non viene richiesto l' interrupt, ma ugualmente
TMR0IF va a 1.
Questo determina due conseguenze:
- un evento su una periferica può essere analizzato sia
con una gestione di interrupt, sia con una gestione in polling,
semplicemente testando lo stato del relativo bit di flag.
- prima di abilitare una sorgente di interrupt è opportuno
CANCELLARE il flag IF relativo in quanto esso potrebbe essere
già a livello 1 per un evento precedente e quindi attivare
immediatamente una richiesta di interrupt inaspettata
|
Se nel caso di interrupt senza priorità è necessario un
solo vettore comune di accesso alle routines di gestione dell' evento, nel
caso di doppia priorità, Microchip ha inserito due vettori distinti.
Si vengono così ad identificare tre indirizzi particolari:
Indirizzo |
Funzione |
0x0000 |
Vettore del RESET |
0x0008 |
Vettore Interrupt senza priorità o a priorità alta |
0x0018 |
Vettore Interrupt a bassa priorità |
Il meccanismo è semplice: le sorgenti a cui è attribuita la priorità
maggiore inviano il program counter all' indirizzo 0008H , mentre quelle a
bassa priorità usano il vettore a 0018H.
O, in altre parole:
Durante la gestione di un interrupt a priorità singola, gli
switch "generali" PEIE/GIE vengono aperti per evitare una
interruzione dell' interruzione, che, avendo un solo vettore di accesso,
creerebbe un loop senza uscita.
Durante la gestione di un interrupt con priorità, il
funzionamento degli switch è più complesso, dato che il meccanismo di
gestione dei due livelli di interrupt è organizzato in modo tale che un
evento a priorità alta può interrompere una chiamata a livello basso in quel
momento attiva, ma non il contrario. E un evento ad alta priorità non
può interrompere un altro evento ad alata priorità in corso in quel
momento.
Ovvero, essendo in corso la gestione di un interrupt a bassa
priorità, che fa capo al vettore 0018h, l' apparire di un interrupt ad alta
priorità interrompe il processo in corso e passa la gestione al vettore
0008h, per rientrare poi al punto in cui era avvenuta la sospensione.
Questo permette una maggiore flessibilità di uso e una maggiore efficacia
nella gestione degli eventi di interruzione, sopratutto quando ne esistano
diversi.
Un automatismo fa si che all' uscita della gestione dell'
interrupt, l' istruzione RETFIE chiuda nuovamente gli
"interruttori generali" che erano stati aperti durante la gestione
dell' interrupt, permettendo così successive interruzioni, senza
alcun necessità di intervento del programmatore.
ATTENZIONE:
Questo determina la conseguenza che:
- il flag IF, una volta identificato l' evento, va
SEMPRE cancellato da programma (salvo poche eccezioni che si
cancellano da sole a seguito, in genere, della lettura di un
registro).
Se non venisse cancellato, il flag IF, restando a 1, al
momento della riabilitazione degli switch generali di interrupt da
parte dell' istruzione RETFIE, scatenerebbe una immediata
richiesta di interrupt, bloccando il processore in un loop
difficile da debuggare.
|
Se non viene usata la priorità, il vettore a 0018h può
essere ignorato.
Da notare che solo 10 bytes separano i due vettori, per cui,
nel caso di priorità sarà giocoforza inserire istruzioni di salto alle
routine di gestione dell' alta priorità.
Per contro, se non viene usata la priorità, si potranno scrivere queste in
modo continuo, ignorando come detto l' esistenza del vettore a 0018h (che,
non essendo abilitata la priorità, non sarà considerato dal
microcontroller).
Ad esempio, per interrupt con priorità:
;=============================================================================
; Vettore del RESET
ORG 0x00
nop
; per debug
goto main
; va al programma principale
;=============================================================================
; Vettore Interrupt Alta Priorità
ORG 0x08
goto
INThp ; vai alla gestione dell' evento
;=============================================================================
; Vettore Interrupt Bassa Priorità
ORG 0x18
; salvataggio ambiente
movff STATUS, status_lp
movff WREG, wreg_lp
movff FSR0L, fsr0l_lp
movff FSR0H, fsroh_lp
; gestione interrupt
btfss INTCON3,INT0IE ;
INT0 abilitato ?
bra
next1
; no - prossimo test
btfss INTCON3,INT0IF ; no
- INT0IF ?
bra
next1
; no - prossimo test
rcall
INT0Manage ; si - gestione
ebvento
next1 ......
; altri test
; restore ambiente
movff fsr0_lp, FSR0L
movff fsr0_lp+1, FSR0H
movff status_lp, STATUS
movff wreg_lp, WREG
retfie
INT0Manage
; gestione di INT0
......
bcf INTCON3,INT0IF
; cancella flag
return
; gestione interrupt alta priorità
INThp
.......
; gestione
retfie
FAST
; rientro con Fast Register Stack
|
e per interrupt senza priorità:
;=============================================================================
; Vettore del RESET
ORG 0x00
nop
; per debug
goto main
; va al programma principale
;=============================================================================
; Vettore Interrupt senza priorità
ORG 0x08
btfss INTCON3,INT0IE
; INT0 abilitato ?
bra
next1
; no - prossimo test
btfss INTCON3,INT0IF ; no
- INT0IF ?
bra
next1
; no - prossimo test
rcall
INT0Manage ; si - gestione
ebvento
next1 ......
; altri test
.......
retfie
FAST
; rientro con Fast Register Stack
INT0Manage
; gestione di INT0
......
bcf INTCON3,INT0IF
; cancella flag
return
|
L' appartenenza all' alta o bassa priorità è
programmabile: la maggior parte delle sorgenti (ma non tutte) dispone di un bit il cui nome
termina per IP (Interrupt Priority), all' interno di un registro specifico
che consente all' utente di attribuirgli uno dei due livelli.
Dunque, una gestione interrupt comprende varie fasi; per un
interrupt senza priorità:
1 |
posizionare al vettore 0008h le routines di gestione,
chiuse da un RETFIE |
2 |
abilitare l' interrupt della periferica desiderata |
3 |
abilitare l' interrupt generale e, se si tratta di
interrupt periferico, abilitare anche questo genere di interrupt |
4 |
all' evento, discriminare quale periferica ha generato
attraverso il flag
l' interrupt e gestirlo |
5 |
prima di terminare la gestione, cancellare il flag della
periferica |
Nel caso di interrupt con priorità:
1 |
posizionare al vettore 0008h le routines di gestione
dell' interrupt ad alta priorità |
2 |
posizionare al vettore 0018h le routines di gestione
della bassa priorità |
3 |
attribuire una priorità all' interrupt della periferica |
4 |
abilitare l' interrupt della periferica |
5 |
abilitare gli switches generali dell' interrupt |
6 |
all' evento, discriminare quale periferica ha generato attraverso
il flag
l' interrupt e gestirlo |
7 |
prima di terminare la gestione, cancellare il flag della
periferica |
AVVERTENZA:
Una volta che una interruzione a priorità alta è in servizio,
tutte le altre fonti di interruzione sono disabilitate fino al
completamento della gestione.
Per contro, una interruzione con priorità bassa può essere
interrotta a sua volta da una interruzione ad alta priorità.
Questo va considerato anche in relazione alla necessità di
salvare i registri del contesto, dato che il FAST REGISTER STACK ha
un solo livello di salvataggio.
Nelle pagine relative
a questa funzione si trovano maggiori dettagli.
|
La doppia priorità è a sua volta selezionabile , in via
generale, attraverso un bit IPEN (Interrupt Priority Enable) per cui è
possibile scegliere se utilizzarla o meno; per default al POR la scelta
è il modo senza priorità, il che rende compatibile la gestione con i
processori mid-range.
In tal senso è opportuno ricordare che il vettore è
passato da 004H dei mid-range a 008H e nel sorgente sarà necessaria
questa nuova definizione.
Volendo attivare la priorità, occorrerà portare IPEN = 1 (e, ovviamente,
aver predisposto la gestione dei due vettori di interrupt).
In modalità senza priorità esiste ancora la
differenziazione tra:
Sono interrupt non periferici quelli di:
-
TIMER0
-
PORTB Change
-
INT0, INT1, INT2
Sono "periferici" tutti gli altri, ad esempio
-
TIMER1, 2, 3
-
USART, MSSP
-
AD, Comparatori
-
HLVD
-
CCP. ECCP
-
ecc.
Nel caso di interrupt senza priorità, come abbiamo visto, la coppia GIE/PEIE
ha funzioni analoghe a quelle prima descritte, comuni ai mid-range:
La messa in servizio di un interrupt nell' ambiente senza priorità richiederà di :
1 |
disabilitare la priorità azzerando IPEN |
2 |
abilitare la sorgente con il bit IE |
4 |
abilitare l' interrupt generale con GIE e, se si tratta
di un interrupt periferico, anche con PEIE |
Se, invece, si utilizzano le priorità, la distinzione
periferico - non periferico non è più valida e le sorgenti si distinguono
tra di loro per il livello di priorità assegnato.
In questo caso i due bit di "interruttore
generale" sono sempre gli stessi, ma prendono il nome di GIEH
(per la priorità alta) e GIEL (per quella bassa). Il meccanismo però
resta analogo:
Quindi:
Modalità |
bit GIE |
bit PEIE |
senza priorità |
abilitazione globale - GIE |
abilitazione interrupt periferici - PEIE |
con priorità |
abilitazione globale - GIEH |
abilitazione priorità bassa - GIEL |
La messa in servizio di un interrupt nell' ambiente a
priorità richiederà di:
1 |
abilitare la priorità settando IPEN |
2 |
attribuire alla sorgente una priorità con il bit IP |
3 |
abilitare la sorgente con il bit IE |
4 |
abilitare l' interrupt generale con GIEH e, se la
priorità è bassa, anche con GIEL |
Se questo non viene fatto, per default si ha un solo
livello di priorità ed un funzionamento analogo (compatibile) con i
mid-range.