PICmicro Cult – Gli interrupt

Gli interrupt

L'interrupt è una particolare caratteristica dei micro che consente di intercettare un evento esterno, interrompere momentaneamente il programma in corso, eseguire una porzione di programma specializzata per la gestione dell'evento verificatosi e riprendere l'esecuzione del programma principale.

Volendo fare un paragone con il mondo reale possiamo dire che l'interrupt rappresenta per il PIC quello che per noi rappresenta ad esempio la suoneria del telefono.
Per poter ricevere telefonate non dobbiamo preoccuparci di alzare continuamente la cornetta per vedere se c'è qualcuno che vuol parlare con noi, ma, grazie alla suoneria, possiamo continuare tranquillamente a fare le nostre faccende in quanto saremo avvisati da' questa ogni volta che qualcuno ci sta chiamando.
Appena sentiamo lo squillo, possiamo decidere di interrompere momentaneamente le nostre faccende, rispondere al telefono e, una volta terminata la conversazione, riprendere dal punto in cui avevamo interrotto(interrupt).
Riportando i termini di questo paragone al PIC abbiamo che:

  • le nostre faccende corrispondono al programma in esecuzione;
  • la chiamata da parte di qualcuno corrisponde all'evento da gestire;
  • lo squillo del telefono corrisponde alla richiesta di interrupt;
  • la nostra risposta al telefono corrisponde alla subroutine di gestione dell'interrupt.

è evidente quanto sia più efficiente gestire un evento con un interrupt anziché controllare ciclicamente il verificarsi dell'evento con il programma principale. Gran parte degli aspetti legati alla gestione dell'interrupt vengono inoltre trattati direttamente dall'hardware interno del PIC per cui il tempo di risposta all'evento è praticamente immediato.

Tipi di evento e bit di abilitazione

Il PIC16F84A è in grado di gestire in interrupt quattro eventi diversi, vediamo quali sono:

  1. Il cambiamento di stato sulla linea RB0 (External interrupt RB0/INT pin).
  2. La fine del conteggio del registro TMR0 (TMR0 overflow interrupt).
  3. Il cambiamento di stato su una delle linee da RB4 ad RB7 (PORTB change interrupts).
  4. La fine della scrittura su una locazione EEPROM (EEPROM write complete interrupt).

L'interrupt su ognuno di questi eventi può essere abilitato o disabilitato indipendentemente dagli altri agendo sui seguenti bit del registro INTCON:

  • INTE (bit 4) se questo bit viene messo a 1 viene abilitato l'interrupt sul cambiamento di stato sulla linea RB0
  • T0IE (bit 5) se questo bit viene messo a 1 viene abilitato l'interrupt sulla fine del conteggio del registro TMR0
  • RBIE (bit 3) se questo bit viene messo a 1 viene abilitato l'interrupt sul cambiamento di stato su una delle linee da RB4 ad RB7
  • EEIE (bit 6) se questo bit viene messo a 1 viene abilitato l'interrupt sulla fine della scrittura su una locazione EEPROM

Esiste inoltre un bit di abilitazione generale degli interrupt che deve essere settato anch'esso ad uno ovvero il bit GIE (Global Interrupt Enable bit) posto sul bit 7 del registro INTCON.

Interrupt vector ed Interrupt handler

Qualunque sia l'evento abilitato, al suo manifestarsi il PIC interrompe l'esecuzione del programma in corso, memorizza automaticamente nello STACK il valore corrente del PROGRAM COUNTER e salta all'istruzione presente nella locazione di memoria 0004H denominata Interrupt vector (vettore di interrupt).
È da questo punto che dobbiamo inserire la nostra subroutine di gestione dell'interrupt denominata Interrupt Handler (gestore di interrupt).
Potendo abilitare più interrupt, tra i primi compiti dell'interrupt handler è la verifica di quale, tra gli eventi abilitati, ha generato l'interrupt e l'esecuzione della parte di programma relativo.
Questo controllo può essere effettuato utilizzando gli Interrupt flag.

Interrupt flag

Dato che qualunque interrupt genera una chiamata alla locazione 04H, nel registro INTCON sono presenti dei flag che indicano quale è l'evento che ha generato l'interrupt, vediamoli:

  • INTF (bit 1) Se vale 1 l'interrupt è stato generato dal cambiamento di stato sulla linea RB0.
  • T0IF (bit 2) Se vale 1 l'interrupt è stato generato al termine del conteggio del timer TMR0.
  • RBIF (bit 0) Se vale 1 l'interrupt è stato generato dal cambiamento di stato di una delle linee da RB4 a RB7.

Come si vede per l'interrupt sul fine scrittura in EEPROM non è previsto alcun flag di segnalazione per cui l'interrupt handler dovrà considerare che l'interrupt è stato generato da questo evento quando tutti e tre i flag sopra citati valgono 0.
Importante: Una volta rilevato quale flag è attivo, l'interrupt handler deve azzerarlo altrimenti non verrà più generato l'interrupt corrispondente.

Ritorno da un interrupt handler

Quando viene generato un interrupt il PIC disabilita automaticamente il bit GIE (Global Interrupt Enable) del registro INTCON in modo da disabilitare tutti gli interrupt mentre è già in esecuzione un interrupt handler. Per poter ritornare al programma principale e reinizializzare a 1 questo bit occorre utilizzare l'istruzione:

RETFIE

Esempio pratico di gestione di un interrupt

Vediamo ora un esempio pratico di gestione degli interrupt. Prendiamo come base di partenza il source LED.ASM usato nella lezione 1 per realizzare il lampeggiatore a led. Come ricorderete questo programma fa semplicemente lampeggiare il LED1 a ciclo continuo utilizzando un ritardo software introdotto dalla subroutine Delay.
Vediamo ora come è possibile fargli rilevare la pressione di un tasto ed accendere il LED 2 contemporaneamente all'esecuzione del programma principale.

Il source d'esempio che andremo ad analizzare è disponibile nel file INTRB.ASM
Proviamo a compilarlo ed a eseguirlo utilizzando lo stesso schema elettrico realizzato nella lezione 3 (file example3.pdf in formato Acrobat Reader 12Kb).

Una volta scaricato il programma INTRB.ASM nella scheda PicTech noteremo che il LED 1 lampeggia esattamente come avveniva con il programma LED.ASM. Proviamo ora a premere uno qualsiasi dei tasti da SW1 a SW4 e vedremo che il LED 2 si accende immediatamente e rimane acceso per un tempo pari a 3 lampeggi del LED 1.
In pratica mentre il loop principale, derivato dal vecchio LED.ASM, continua a far lampeggiare il LED 1 utilizzando un ritardo software introdotto dalla subroutine Delay, il PIC è in grado di accorgersi della pressione di un tasto e di segnalarlo immediatamente sul LED 2 senza influenzare in maniera evidente la frequenza di lampeggio di LED1.
Prima di analizzare il source INTRB.ASM vediamo la differenza di comportamento con un altro source che effettua le stesse operazioni ma senza ricorrere agli interrupt.
A questo proposito compiliamo ed inseriamo nel PICmicro il programma NOINTRB.ASM. Noteremo che l'accensione del LED 2, in corrispondenza alla pressione di un tasto, è leggermente ritardata in quanto la lettura dello stato delle linee RB4-7 non viene effettuata dall'hardware di gestione dell'interrupt ma direttamente dal programma principale ad ogni ciclo di loop. Il leggero ritardo è quindi dovuto alla presenza della subroutine Delay all'interno del loop principale.

Analizziamo ora il source INTRB.ASM

Partiamo dalla direttiva ORG 00H che, come sappiamo serve a posizionare il nostro programma a partire dalla locazione di reset, ovvero dalla locazione con indirizzo 0.
Notiamo subito che la prima istruzione che incontra il PIC è un salto incondizionato alla label Start:

ORG 0x00
goto Start

seguito da un'altra direttiva:

ORG 0x04

e quindi dal codice della subroutine di gestione dell'interrupt:

bsf PORTB,LED2

movlw 3
movwf nTick

bcf INTCON,RBIF

retfie

Come abbiamo detto nella lezione precedente, l'interrupt handler deve necessariamente essere allocato a partire dall'indirizzo 0x04, quindi per evitare che venga eseguito al reset dobbiamo necessariamente saltarlo con una istruzione di salto incondizionato.
Il codice dell'interrupt handler, in questo caso, e' molto semplice e si limita ad accendere il LED 2, quindi inserire nel registro utente nTick il numero di lampeggi raggiunto il quale il LED 2 deve spegnersi e quindi azzerare il flag RBIF per consentire alla circuiteria di generazione dell'interrupt di continuare a funzionare.
L'istruzione RETFIE consente al PIC di tornare ad eseguire il programma interrotto dall'interrupt.

Ma perché viene generato un interrupt quando premiamo un tasto qualsiasi?

Tra le prime istruzioni che esegue il nostro PIC al reset troviamo le seguenti:

movlw 10001000B
movwf INTCON

dove in pratica viene messo ad uno il bit GIE (bit 7) che abilita in generale la circuiteria di generazione degli interrupt e quindi il bit RBIE (bit 3) che abilita, in particolare, l'interrupt su cambiamento di stato delle linee RB4-7.
In pratica, avendo collegato i pulsanti PU1, PU2, PU3 e PU4 proprio sulle linee di I/O RB4, RB5, RB6 ed RB7, con la pressione di uno di questi otteniamo un cambiamento di stato e quindi un interrupt.
Nel loop principale, oltre alle operazioni di accensione e spegnimento del LED 1, viene decrementato il contatore nTick fino al raggiungimento dello zero. In corrispondenza di questo viene spento il LED 2.

Esempio pratico di gestione di più interrupt

Vediamo ora come gestire più interrupt contemporaneamente.
Utilizzando sempre come base il source precedente INTRB.ASM proviamo a gestire anche l'interrupt sulla fine conteggio del registro TMR0. Ovvero facciamo lampeggiare il LED 3 in corrispondenza di ogni fine conteggio di TMR0.
Il source da utilizzare è DBLINT.ASM.
Compiliamo e scarichiamo il programma DBLINT.ASM nella scheda PicTech e vediamo che oltre a LED 1 che lampeggia con la solita frequenza, c'è il LED 3 che lampeggia contemporaneamente con una frequenza più elevata.
Premendo un tasto qualsiasi, inoltre, otteniamo la solita accensione per tre cicli del LED 2. L'effetto finale che otteniamo è l'esecuzione di tre compiti ad una velocità tale da sembrare in esecuzione parallela.

Analizziamo ora il source DBLINT.ASM

Le modifiche maggiori riguardano l'interrupt handler all'inizio del quale viene effettuato un controllo su quale evento abbia generato l'interrupt. Con le istruzioni:

btfsc	INTCON,T0IF
goto	IntT0IF
btfsc	INTCON,RBIF
goto	IntRBIF

viene controllato il flag T0IF e RBIF per vedere rispettivamente se l'evento che ha scatenato l'interrupt proviene dal registro TMR0 o dalle porta RB4-RB7. Quindi vengono lanciate in esecuzione le relative subroutine di gestione a partire dalle label IntT0IF e IntRBIF.
Prima di ridare il controllo al programma principale vengono azzerati i flag T0IF e RBIF assicurarsi che i successivi eventi possano scaturire nuovamente gli interrupt.

Scarica subito una copia gratis
Tags:

Una risposta

  1. Avatar photo slovati 25 Maggio 2009

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend