Interruzioni

Un pulsante connesso al ad un pin del PIC18

In fase di sviluppo Stesura preliminare In fase di sviluppo

In questa pagina verrà mostrato come gestire in assembly le interruzioni del PIC18 generate da una sorgente esterna, quale un semplice interruttore. Qui una breve descrizione del concetto di interruzione.

Al solito, è vivamente consigliata la lettura in parallelo della spiegazione semplificata qui riportata e della la spiegazione "ufficiale" presente nei fogli tecnici.

Il PIC18 prevede due distinte modalità di uso delle interruzioni:

Di seguito verranno esaminate entrambe le modalità anche se la prima ha valenza soprattutto per chi già ha utilizzato i PIC16 (oppure semplicemente vuole un approccio più semplice).

L'hardware di riferimento è il PICdemo oppure il PICdemo R2 anche se il circuito è realizzabile senza alcun problema direttamente su breadboard. In particolare vengono utilizzati:

Gli esempi utilizzano il PIC18F26K20, ma sono facilmente estensibili a qualunque PIC18.

Compatibility mode

Questa modalità è attiva all'accensione e quindi per attivarla non occorre fare nulla; è associata al valore zero del flag Interrupt Priority ENable bit  (IPEN=0).

Il codice di esempio, scaricabile a fondo pagina, fa scorrere di una posizione l'unico LED acceso quando viene premuto un pulsante, in corrispondenza del fronte di salita; per fare ciò vengono utilizzate le interruzioni generate da un segnale esterno applicato direttamente al pin RB0/INT0.

Per la sua gestione vengono utilizzati tre flag del registro INTCON (REGISTER 9-1: INTERRUPT CONTROL REGISTER):

Il codice (scaricabile a fondo pagina) è suddiviso in tre parti:

Inizializzazione

Occorre evidentemente come prima cosa configurare PORTC e PORTB rispettivamente come uscita (per collegare gli otto LED) e come ingresso con pull-up interno (per collegare un interruttore). Nulla di nuovo:

 clrf TRISC ; PORTC come uscita, tutti gli 8 pin
 movlw B'00000001' ; LED0 acceso; LED1-7 spenti
 movwf LATC

 movlw 0xFF ; PORTB come ingresso, tutti gli 8 pin
 movwf TRISB
 bsf WPUB, INT0 ; Abilita il pull-up interno su INT0
 bcf INTCON2, RBPU ; Pull-up attivo su tutti i pin abilitati

Occorre quindi procedere alla abilitazione delle interruzioni, impostando il flag relativo a INT0 (INT0 Interrupt Enable) e quello globale (Global Interrupt Enable), relativa all'intero processore:

 bsf INTCON, INT0IE ; Abilito INT0 a generare interruzioni
 bsf INTCON, GIE ; Abilito la CPU a ricevere interruzioni

Superloop

Dopo le configurazioni iniziali, il processore inizia un ciclo infinito dove non vene fatto assolutamente nulla di significativo. In particolare il codice NON legge il valore dell'interruttore e NON imposta l'accensione dei LED.

repeat
  nop ; non fa nulla (No OPeration), per sempre
  nop
  ;...
  bra repeat

ISR

La Interrupt Service Routine è il codice che viene eseguito quando l'hardware rileva la generazione di un interrupt. Alcune operazioni vengono fatte automaticamente quando l'interrupt è generata e quindi non corrispondono ad alcuna istruzione:

In genere il codice della ISR inizia con la ricerca del dispositivo hardware ha effettivamente generato l'interruzione. Nell'esempio solo INT0 è stata abilitata e quindi una interruzione è sicuramente generata da essa. In una applicazione reale invece sono molte le sorgenti di interrupt e quindi l'unica ISR deve esaminate tutti i vari flag alla ricerca di quello attivo (nota 5).

Inoltre, obbligatoriamente, la ISR deve azzerare in modo esplicito il flag dalla periferica corrispondente all'interruzione generata, lo stesso descritto nel paragrafo precedente. Saltando tale passaggio infatti, subito dopo la conclusione della ISR, verrebbe generata una nuova interruzione, evidentemente non esistente, in una ripetizione infinita. Nell'esempio:

  bcf INTCON, INT0IF ; Azzera il flag che segnala INT0

La ISR termina infine sempre con l'istruzione retfie, a volte seguita da FAST. L'istruzione ha i seguenti effetti:

Di seguito l'intera struttura di una ISR tipica dove vengono esaminate a titolo di esempio tre possibili sorgenti di interrupt (INT0, Timer0, cambiamento di RB), osservando i corrispondenti flag ed eseguendo di conseguenza parti specifiche di codice (non tutte implementate):

ISR CODE 0x0008
 btfsc INTCON, INT0IF ; Interruzione causata da INT0 ?
 bra GestioneINT0 ; Esegue il codice corrispondente

 btfsc INTCON, TMR0IF ; Interruzione da Timer0 ?
 bra GestioneTimer0 ;

 btfsc INTCON, RBIF ; Interruzione dal cambiamento di un bit di RB ?
 bra GestioneRB0 ;

 retfie FAST ; Nessuna interrupt riconosciuta... Indizio di un errore?

GestioneINT0
 rlncf LATC ; "Ruota" i LED
 bcf INTCON, INT0IF ; Azzera il flag che segnala INT0
 retfie FAST ; Torna ad eseguire il codice principale

GestioneTimer0 ; Non implementato
 bcf INTCON, TMR0IF
 retfie FAST

GestioneRB0 ; Non implementato
 bcf INTCON, RBIF
 retfie FAST

Prima di proseguire è utile riflettere sui quesiti 2 e 3.

Altri esempi di interruzione, generati da specifiche periferiche:

Latenza

La latenza, in questo contesto, misura il tempo necessario al PIC18 per "rispondere" ad una richiesta di interruzione. Dipende da diversi fattori:

Il grafico seguente fa riferimento al codice in Compatibility Mode, eseguito alla frequenza di 1 MHz. Esso mostra il tempo che intercorre tra l'innesco dell'interruzione (fronte di salita di INT0, in blu nel grafico) e l'accensione del LED, in rosso). Tale tempo è misurato in circa 32 µs con una variabilità casuale dell'ordine di qualche microsecondo.

Tale tempo è compatibile con quanto indicato nei fogli tecnici, ottenuto dalla somma di

Complessivamente tra i 7 e gli 8 cicli macchina della durata di 4 µs ciascuno, quindi tra 28 e 32 microsecondi totali.

PIC18: latenza dell'interrup

Come confronto, il codice generato dal compilatore C introduce una latenza di circa 150 µs, dovuta soprattutto al salvataggio di oltre 10 variabili interne e ad altrettante nop (nota 4)

Il codice

Esercizi e quesiti

  1. Per quale motivo il vettore di reset è costituito da un semplice salto e non dal codice principale vero e proprio, come in altri esempi?
  2. Per quale motivo durante l'esecuzione di una interruzione, viene automaticamente inibita la generazione di altre interruzioni?
  3. Il fatto che i registri shadow sono ad un solo livello è un problema?

Note

  1. Tutte le interruzioni qui descritte sono mascherabili
  2. É possibile anche generare una interruzione sul fronte di discesa agendo sul bit 6 del registro INTCON2
  3. Anche se i registri shadow sono indicati come stack, in realtà sono costituiti da un solo registro e quindi il loro uso è limitato ad un solo livello di interruzione
  4. La presenza di queste nop appare incomprensibile. Sembra avere come unico scopo il rallentamento del codice quando il compilatore C è in free mode
  5. Ogni periferica in grado di generare una interruzione dispone di (almeno) un diverso flag che, quando settato, indica la causa della interruzione

 

Data di creazione di questa pagina: maggio 2016
Ultima modifica: 18 aprile 2018


Licenza Creative Commons Attribuzione 4.0 Internazionale


Pagina principaleAccessibilitàNote legaliPosta elettronicaXHTML 1.0 StrictCSS 3

Vai in cima