ELEMANIA
PIC16F690 - Interrupt nel PIC
Gli eventi di interrupt nel PIC

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.

Registro INTCON: bit di abilitazione e bit di flag

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:

  1. bit di abilitazione/disabilitazione interrupt
    abilitano (1) o disabilitano (0) gli interrupt;
  2. bit di flag
    rilevano la presenza (1) o assenza (0) di una certa condizione di interrupt.

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:

            BSF INTCON, INTE ; abilita interupt su RA2/INT
            BSF INTCON, RABIE ; abilita interrupt cambio stato PORTA/PORTB
            BSF INTCON, TOIE ; abilita timer interrupt

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.

Subroutine di servizio dell'interrupt (ISR) e RETFIE

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:

#include <P16F690.inc> ; processor specific variable definitions

    __CONFIG _CP_OFF & _CPD_OFF & _BOR_OFF & _PWRTE_ON & _WDT_OFF &         _INTRC_OSC_NOCLKOUT & _MCLRE_ON & _FCMEN_OFF & _IESO_OFF

    ORG 0x000 ; locazione iniziale

    GOTO main ; va all'inizio del programma


;******************* INTERRUPT SERVICE ROUTINE *****************
     ORG 0x004 ; interrupt vector location

     RETFIE ; return from interrupt
;*******************************************************


main
            ; il codice del programma principale va qui

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:

 

La routine di servizio dell'interrupt nel dettaglio

In generale la ISR nel PIC deve: 

  1. salvare registri interni del PIC (a questo proposito è consigliato salvare i registri W, STATUS e PCLATH);

  2. azzerare i bit di flag (per evitare che la richiesta di interrupt rimanga attiva);

  3. eseguire il servizio all'interrupt (in caso di più sorgenti di interrupt, la routine deve preliminarmente identificare da quale sorgente proviene l'interrupt ricevuto);

  4. 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:

#include <P16F690.inc>

    __CONFIG _CP_OFF & _CPD_OFF & _BOR_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT & _MCLRE_ON & _FCMEN_OFF & _IESO_OFF


;***** Definizione di registri GPR per il salvataggio del contesto
    w_temp EQU 0x7D
    status_temp EQU 0x7E
    pclath_temp EQU 0x7F


;**********************************************************************
    ORG 0x000 ; inizio memoria di programma
    goto main ; salta all'inizio del programma principale

;******************* INTERRUPT SERVICE ROUTINE *****************
    ORG 0x004 ; inizio routine di interrupt

    movwf w_temp ; salva W                    ; salvataggio registri del PIC
    movf STATUS,w ; copia STATUS in W
    movwf status_temp ; salva STATUS
    movf PCLATH,w ; copia PCLATH in W
    movwf pclath_temp ; salva PCLATH

    btfsc INTCON, INTF ; controlla se ci sono interrupt da RA2/INT
    call RA2INT

    btfsc INTCON, T0IF ;; controlla se ci sono interrupt da TIMER0
    call TIMER0INT

    movf pclath_temp,w                         ; ripristino registri del PIC
    movwf PCLATH
    movf status_temp,w
    movwf STATUS
    swapf w_temp,f
    swapf w_temp,w
   
    retfie ; return from interrupt
;*******************************************************

RA2INT
    bcf INTCON, INTF ; azzera il flag di interrupt da RA2/INT
                            ; istruzioni della subroutine per l'interrupt da RA2/INT
    return

TIMER0INT
    bcf INTCON, T0IF ; azzera il flag di interrupt da TIMER0
                            ; istruzioni della subroutine per l'interrupt da TIMER0
    return

main

                            ; codice programma principale

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:

    swapf w_temp,f
    swapf w_temp,w

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!).

 

precedente - successiva

Sito realizzato in base al template offerto da

http://www.graphixmania.it