In questa pagina verrà mostrato come gestire le interruzioni del PIC18 generate da una sorgente esterna, quale un semplice interruttore. Qui una breve descrizione del concetto di interruzione.
Anche molte periferiche interne possono generare interruzioni: verranno esaminati nelle pagine dedicate alle singole periferiche.
PIC18 possiede due livelli di interruzioni: low_priority e high_priority, entrambe mascherabili (nota 1).
Le differenze:
Questa modalità a due livelli è disattiva al momento del reset, ma meglio abituarsi ad usarla sempre, attivandola in modo esplicito fin dall'inizio del programma settando l'apposito bit nel registro RCON:
RCONbits.IPEN = 1; // Enable priority levels on interrupts
Di seguito non sono riportati esempi che utilizzano la modalità legacy senza priorità, tipica dei PIC16.
Non è presente alcuna interruzione non mascherabile.
Preliminarmente occorre leggere sui fogli tecnici quali sono le sorgenti esterne di interruzioni presenti nel modello di PIC18 che abbiamo a disposizione. Due le situazioni comuni:
Il primo esempio proposto utilizza un'interruzione generata dalla pressione di un pulsante per spegnere un LED:
In questo esempio il LED non può essere nuovamente acceso se non resettando il PIC18 e ricominciando da capo l'esecuzione del programma.
Per comprendere il funzionamento dell'esempio (ed anche per individuare eventuali errori) è consigliabile inserire un breakpoint all'interno della funzione di gestione dell'interruzione (ISR) descritta più avanti: è infatti complesso utilizzare in questo contesto l'esecuzione passo-passo del codice.
Il microcontrollore di riferimento in questo esempio è il PIC18F14K50, ma dovrebbe funzionare su qualunque PIC18 che possiede i pin utilizzati. Il codice completo è a fondo pagina.
I riferimenti da consultare (indicati tra parentesi quadra nel codice) sono le tabelle presenti sui fogli tecnici del PIC18F14K50:
Esaminiamo ora la struttura della funzione main() il cui compito è quello di configurare PORTC, abilitare le interruzioni ed accendere il LED:
Il pin RC0, collegato al LED, viene configurato come uscita digitale e viene acceso il
LED:
TRISCbits.RC0 = 0; // Set RC0 pin as output [1]
PORTCbits.RC0 = 1; // Turn on RC0 pin [5]
Il pin RC1, collegato al pulsante, viene configurato come ingresso digitale:
TRISCbits.RC1 = 1; // Set RC1 pin as input [1]
ANSELbits.ANS5 = 0; // Digital input buffer of RC1 is enabled [6]
Come già scritto, deve essere inizialmente impostata la modalità di
funzionamento con doppia priorità:
RCONbits.IPEN = 1; // Enable priority levels on
interrupts [2]
Abilito RC1 (INT1) per generare un'interruzione ad alta priorità sul
fronte di discesa del segnale esterno, cioè alla pressione del pulsante:
INTCON3bits.INT1IP = 1; // INT1 External
Interrupt Priority set to High [3]
INTCON2bits.INTEDG1 = 0; // INT1 on falling edge [4]
INTCON3bits.INT1E = 1; // Enables the INT1 external
interrupt [3]
Infine azzero il flag che segnala la generazione di un'interruzione da parte
di INT1, che al momento dell'attivazione potrebbe già essere alto; se fosse
lasciato potrebbe immediatamente essere generato un'interruzione inesistente,
senza attendere la reale pressione del pulsante. Questo comportamento è
comune a molte periferiche:
INTCON3bits.INT1F = 0; // Clear INT1 flag [3]
Terminate tutte le operazioni di configurazione delle interruzioni per le singole periferiche, possiamo abilitare le interruzioni in modo globale:
INTCONbits.GIEH = 1; // Global interrupt enable
In alternativa è possibile anche utilizzare l'apposita macro ei().
Se viene usato interruzioni a bassa priorità, occorre ricordarsi di
abilitarle, con un ulteriore assegnamento, simile al precedente:
INTCONbits.GIEL = 1; // Enables all low priority interrupts
Dopo la configurazione delle periferiche, il processore può eseguire la
parte "principale" del codice. In questo breve esempio si tratta
semplicemente di un loop infinito:
while (1); // Do nothing, forever
Il termine ISR identifica la funzione che viene eseguita quando una periferica precedentemente abilitata genera un'interruzione. Nel dettaglio si tratta di codice che deve obbligatoriamente iniziare all'indirizzo di memoria 0x08 (oppure 0x18), ma scrivendo codice C possiamo ignorare questi aspetti, gestiti automaticamente dal compilatore che provvede anche al salvataggio dei registri nello stack.
Questa funzione appare come una funzione C di tipo void, senza parametri. Sono
presenti due parole chiave: interrupt
e __high_priority, dall'ovvio significato. Il nome
della funzione è arbitrario:
void interrupt __high_priority my_isr_h(void)
Questa funzione viene eseguita solo nel momento in cui una periferica genera una interruzione.
La struttura di una ISR è fissa:
Dato che tutte le periferiche condividono una sola ISR, in genere è necessario verificare più flag, uno per ciascuna periferica per cui è abilitata l'interruzione. Nel codice è presente anche lo scheletro del controllo di una seconda sorgente di interruzioni, commentato.
Nel codice è infine presente anche lo scheletro di una ISR a bassa priorità, qui non utilizzata.
Possono esistere solo due funzioni di tipo interrupt, una __high_priority ed una __low_priority.
Questo ulteriore esempio è relativo al PIC18F2431. In sintesi:
La descrizione dettagliata del codice è oggetto del secondo esercizio. Aspetti da tenere in primo piano:
La latenza, in questo contesto, misura il tempo necessario al PIC18 per "rispondere" ad una richiesta di interruzione. Dipende da diversi fattori:
A titolo di esempio si riporta il grafico associato al primo esempio presentato:
Il clock utilizzato è 1 MHz, il tempo complessivo circa 130 µs, pari all'esecuzione di 32 istruzioni macchina, tante rispetto a quanto si può ottenere in assembly. Ovviamente se si aumenta la frequenza di clock, tale tempo diminuisce proporzionalmente.
Modificando il codice per utilizzare interruzioni a bassa priorità (esercizio 1), il grafico corrispondente è il seguente, coerente col fatto che la latenza aumenta (di poco) passando da 130 a 170 µs.
Data di creazione di questa pagina: settembre 2014
Ultima modifica: 9 novembre 2019
Una pagina simile: Le interruzioni nel PIC18 (in assembly)
PIC18 in C - Versione 0.991 - luglio 2019
Copyright 2014-2019, Vincenzo Villa (https://www.vincenzov.net)
PIC18 in C di Vincenzo Villa è distribuito con Licenza Creative Commons Attribuzione 4.0 Internazionale