Gestire le interruzioni nel PIC18

Attenzione! Questa pagina contiene materiale obsoleto - Vai al tutorial relativo all'ambiente MPLAB X e XC8 Attenzione!

In questo breve tutorial verrà mostrato come gestire le interrupt con priorità del PIC18 utilizzando come una sorgente esterna oppure un contatore hardware interno al PIC. Qui una breve descrizione del concetto di interrupt..

E' nelle prime fasi di stesura il tutorial relativo all'ambiente MPLAB X e XC8.

Emulatore ICD3 con header per PIC18 su schedine demo Microchip

L'hardware utilizzato è costituito da:

Il circuito è già presente sulla scheda del Low Pin Count USB Development Kit aggiungendo la resistenza sul connettore J11 tra i pin 10 e 13, come mostrato in figura. E' comunque facilmente realizzabile anche su breadboard. Nelle immagini è mostrato anche un emulatore ICD3 con relativo header, ma non è necessario utilizzarlo, anche se oggettivamente è comodo.

La resistenza tra RC0 e Vcc

Per chi fosse curioso di capire perché non utilizzo l'interruttore presente sulla demo-board, rimando alla vecchia versione: Gestire le interruzioni nel PIC18.

Il PIC 18 utilizza due livelli di interruzione, definiti ad alta e bassa priorità. Per una descrizione dettagliata occorre fare riferimento ai manuali tecnici ma, in breve:

Il codice: interrupt esterna ad alta priorità

Questo programma  permette di accendere i tre LED quando viene collegata una resistenza di piccolo valore tra il pin RC0 e Vcc, indipendentemente dall'esecuzione del programma principale che semplicemente, dopo aver inizializzato i vari registri, non fa nulla. Qui potete scaricare il codice.

Quando la resistenza viene inserita si accende uno dei LED (operazione esclusivamente hardware, non vi è codice associato) e viene attivata una interruzione ad alta priorità (High Priority Interrupt), vengono salvati alcuni registi e quindi eseguito il codice presente all'indirizzo 0x08 (High Priority Interrupt Vector). In genere si tratta di una singola istruzione di salto, scritta in assembler perché lo spazio a disposizione è davvero poco. Nessuna preoccupazione: non serve imparare anche questo linguaggio, questa riga di codice è sempre uguale!

Il programma è suddiviso in due blocchi.

#pragma code high_vector = 0x08 // Setup interrupt vector

void interrupt (void)
{ _asm GOTO High_Int _endasm } // Jump to ISR function (in-line assembler)

Il primo blocco, evidenziato dalla direttiva #pragma code low_vector = 0x08 viene posto obbligatoriamente all'inizio del vettore di interruzione, di indirizzo fisso 0x08. In pratica si tratta di una singola istruzione assembler che, eseguita quando viene generato un interrupt, invoca la funzione C High_Int() che contiene il codice vero e proprio di gestione dell'interruzione.

Tutto il resto del programma viene allocato automaticamente dal compilatore, come specificato dalla successiva direttiva #pragma code.

Sono presenti due funzioni. Il main() come prima cosa imposta le porte di ingresso e uscita e quindi configura i molti registri che gestiscono le interruzioni, purtroppo senza supporto dalle librerie Microchip, riservate a PORTB:

TRISC = 0x01;            // Set RC1-RC2-RC3 as Output, RC0 as input
PORTC = 0x00;            // xxxx 000x - Turn off LEDs on port C
ANSEL = 0;               // Set as Digital I/O
RCONbits.IPEN = 1;       // Enable priority levels on interrupts
INTCON2bits.INTEDG0 = 1; // Interrupt on rising edge
INTCONbits.INT0IE = 1;   // Enables INT0 interrupts
INTCONbits.INT0IF = 0;   // Clear interrupt status
INTCONbits.GIEH = 1;     // Enables all interrupts (required even for LOW priority)

La descrizione dettagliata è presente sui fogli tecnici del PIC18 ma, in breve, occorre:

In seguito il programma non farà nulla.

La seconda funzione è invocata nel momento in cui la resistenza è collegata ed accende, in modo sostanzialmente casuale, i tre leb

void High_Int (void)
{if (INTCONbits.INT0IF == 1)
  {PORTC+=2; // Increments LEDs status
   INTCONbits.INT0IF = 0; // Clear interrupt status
 
}
}

Il codice prima controlla se l'interrupt è stato effettivamente generato da INT0 (tutte le periferiche condividono gli stessi interrupt), incrementa i LED accesi, e infine azzera il flag che ha segnalato l'interruzione.

Il fatto che sia una funzione per la gestione delle interruzioni è evidenziato dalla direttiva #pragma interruptlow.

Perché l'accensione dei LED sembra praticamente casuale mentre il codice li accende secondo un conteggio binario? Il collegamento di una resistenza o la chiusura di un interruttore è sempre soggetta a "rimbalzi": ogni volta che si chiude meccanicamente un circuito si ottengono anche decine di impulsi, ognuno dei quali incrementa, in questo caso, l'uscita visualizzata. Inserendo un breakpoint nella routine di interrupt il funzionamento è facilmente controllabile (nel mondo reale occorre inserire una qualche funzione di debounce, anti-rimbalzo in italiano)

Il codice: interrupt a bassa priorità dal timer

Il secondo esempio mostra come utilizzate Timer0 come sorgente dell'interrupt. In questo caso il segnale di interruzione viene generato internamente al PIC da un modulo hardware indipendente dal processore.

La struttura è molto simile al precedenti:

Scarica il codice sorgente: interruzione esterna ad alta priorità su RC0

Scarica il codice sorgente: interruttore a bassa priorità su RCA3 (leggere Gestire le interruzioni nel PIC18)

Scarica il codice sorgente: interruttore ad alta priorità su RCA3 (leggere Gestire le interruzioni nel PIC18)

Scarica il codice sorgente: Timer0 a bassa priorità

Licenza "Creative Commons" - Attribuzione-Condividi allo stesso modo 3.0 Unported


Pagina principaleAccessibilitàNote legaliPosta elettronicaXHTML 1.0 StrictCSS 3

Vai in cima