Gli eventi che possono causare un interrupt nel PIC sono numerosi:
Tutti gli interrupt nel PIC sono mascherabili, cioè possono essere disabilitati agendo sui bit di alcuni registri di controllo (INTCON,PIE1, PIE2, PIR1). Qui ci occuperemo nel dettaglio del registro INTCON (che gestisce gli interrupt esterni e quelli generati dal Timer0).
Per quanto riguarda i registri PIE1 e PIE2, i quali permettono di abilitare gli interrupt provenienti dalle altre periferiche (comparatore, ADC, EUSART) e dai Timer1 e Timer2, e il corrispondente registro dei flag PIR1 (Peripheral Interrupt Request Register), rimandiamo il lettore al datasheet del PIC.
Il registro INTCON, come si è detto, gestisce gli interrupt esterni e quelli dovuti al Timer0. Lo schema dei pin del registro è il seguente:
Ii bit del registro INTCON sono di due tipi diversi:
I bit di abilitazione permettono di mascherare (disabilitare) un certo tipo di interrupt: ciò significa che se quell'interrupt è disabilitato, le eventuali richieste di interruzione provenienti da quella sorgente non saranno servite.
I bit di flag invece segnalano il fatto che si è verificato un certo tipo di interruzione. Servono per la routine di servizio degli interrupt che li usa per sapere quale particolare tipo di interrupt deve essere servito. E' importante che la routine di servizio azzeri il flag corrispondente dopo aver servito l'interrupt stesso, altrimenti successive richieste di quell'interrupt non verranno servite.
Il significato dei singoli pin è il seguente:
La figura seguente mostra la gestione logica delle diverse sorgenti di interrupt in base ai bit di abilitazione INTCON (e dei registri PIE1 e PIE2 di cui abbiamo accennato prima):
Per esempio si osserva che se GIE è zero, vengono disabilitati tutti gli interrupt; se RABIE è zero, vengono disabilitate tutte le sorgenti di interrupt collegate alla porta OR numero 1; se PEIE è zero vengono disabilitati gli interrupt connessi all'OR numero 2 eccetera.
I bit di abilitazione di INTCON possono essere abilitati o disabilitati da programma, come mostra l'esempio seguente:
Per quanto riguarda i bit di flag di INTCON, essi possono essere testati all'interno della routine di servizio dell'interrupt allo scopo di identificare la sorgente dell'interrupt stesso e di eseguite quindi una diversa porzione di codice a seconda di quale interrupt è stato prodotto. Essi devono essere azzerati dal programmatore all'interno della routine di servizio dell'interrupt (ISR) per evitare che la richiesta di interrupt rimanga attiva. Vedremo qualche esempio fra breve.
Il PIC prevede una locazione fissa per la subroutine di servizio degli interrupt: essa deve trovarsi sempre all'indirizzo 0004 nella memoria di programma. Ciò significa che ogni volta che il PIC riceverà un interrupt, verrà sempre eseguita una CALL all'indirizzo 0004.
La routine di servizio viene terminata con un'istruzione RETFIE (return from interrupt). Questa istruzione fa riprendere l'esecuzione dal punto in cui il programma è stato interrotto.
ATTENZIONE: l'istruzione RETFIE riabilita automaticamente gli interrupt (gli interrupt vengono disabilitati automaticamente quando viene ricevuta una richiesta di interrupt, per evitare che interrupt multipli interrompano l'esecuzione di una routine di servizio dell'interrupt).
Poiché all'avvio (o dopo un reset) il PIC inizia a eseguire la prima istruzione in memoria all'indirizzo zero, per fare spazio al programma principale e alla routine di servizio degli interrupt, è necessario risistemare la disposizione del codice assembly come mostrato nell'esempio seguente:
Si osservi la GOTO main all'indirizzo 0x000, necessaria perché altrimenti il main avrebbe a disposizione solo 4 locazioni di memoria (da 0x000 a 0x003)... decisamente insufficienti in quasi tutti i casi pratici. La ORG 0x004 serve per posizionare la ISR all'indirizzo prefissato. Il programma principale (etichetta main) viene invece caricato in memoria dopo la ISR.
La figura seguente, che mostra graficamente la disposizione della routine di servizio e del programma principale nella memoria del PIC, dovrebbe chiarire il concetto:
In generale la ISR nel PIC deve:
salvare
registri interni del PIC (a questo proposito è
consigliato salvare i registri W, STATUS e
PCLATH);
azzerare i bit di flag
(per evitare che la richiesta di interrupt
rimanga attiva);
eseguire il
servizio all'interrupt (in caso di più sorgenti
di interrupt, la routine deve preliminarmente
identificare da quale sorgente proviene
l'interrupt ricevuto);
ripristinare i registri interni precedentemente salvati.
Consideriamo un esempio pratico di una routine di servizio che gestisce le richieste di interrupt dal pin RA2/INT e da TIMER0:
Si osservi che la routine di servizio dell'interrupt è unica e comune per tutti gli interrupt. Le subroutine RA2INT e TIMER0INT (nomi scelti a piacere) sono normali sottoprogrammi chiamati dalla ISR a seconda del tipo di interrupt identificato.
Osserviamo inoltre che all'inizio delle subtoutine RA2INT e TIMER0INT vengono resettati i bit di flag degli interrupt corrispondenti. Questo è necessario poiché questi flag vengono settati al momento della richiesta dell'interrupt, ma non vengono azzerati automaticamente. Pertanto se non azzerati via software, al termine dell'esecuzione della routine di interrupt (quando, con l'esecuzione di RETFIE gli interrupt vengono nuovamente abilitati) verrebbe erroneamente riconosciuto nuovamente un interrupt dalla stessa sorgente.
Si noti che il ripristino del registro W viene effettuato in un modo particolare:
La prima SWAPF scambia fra loro i 4 bit meno significativi e i 4 bit più significativi del registro w_temp (e salva il risultato in w_temp). La seconda SWAPF ripete lo scambio (ripristinando in questo modo l'ordine dei bit iniziale), ma salva il risultato in W. Alla fine di queste istruzioni W contiene il valore che era stato salvato in w_temp.
Perché allora non viene eseguita semplicemente una MOVF w_temp,W? La ragione è che quest'ultima istruzione modifica i bit dello Status Register, a differenza della SWAP che li lascia invarianti.
In pratica la struttura generale della routine di servizio dell'interrupt può essere semplicemente copiata dal file template del PIC (naturalmente le operazioni di servizio di interrupt vere e proprie, devono essere scritte dal programmatore!).
Sito realizzato in base al
template offerto da
http://www.graphixmania.it