Gestione Degli INTERRUPT Il PIC 16F876 possiede vari tipi di Interrupt. Si parla di 14 differenti sorgenti di interrupt, ma molto facilmente nella programmazione se ne terranno in considerazione ben pochi: due o, più frequentemente, uno. Questo vuol dire, da una parte, che durante la programmazione ci si dovrà preoccupare di selezionare i tipi di interrupt di nostro interesse e dall' altra che si dovrà programmare le routine ad hoc che ci permettano di gestire gli interrupt. Per fare questo si devono conoscere tre aspetti: il primo è che per ogni interrupt che si verifica la chiamata a sottoprogramma che il PIC esegue è unica per tutti gli interrupt e avviene all' indirizzo 4H della memoria Flash,il secondo è che ci sono ben 5 registri dedicati alla gestione degli interrupt e il terzo è che sta al programmatore verificare il tipo di interrupt che si è verificato andando a testare i registri dedicati. Programmare una ISR interrupt Service Routine All' inizio del programma si deve quindi comunicare al compilatore la giusta sequenza di istruzioni differenziando la routine del programma principale dalla routine della gestione degli interrupt. La posizione di origine del programma si trova all indirizzo 0H, mentre quella di interrupt si trova all indirizzo 04H : per comunicare la giusta posizione al compilatore si devono usare le direttive ORG come di seguito mostrato: ;******************************************************************************************* ; Struttura di un programma.asm che utilizza gli interrupt ( interrupt0.asm ) ;******************************************************************************************** #include <std.inc> Org 0H ; RESET VECTOR Main_program ; all'inizio quando il pic viene alimentato, o subito dopo il ; reset il micro resetta il PC ed esegue ; l'istruzione main program ; cosi facendo il micro salta la ISR (interrupt ServiceRoutine) ;che deve eseguire solo quando viene generato un interrupt Org 4H ; INTERUPT VECTOR ; quando viene generato un interrupt il micro ; salva all'interno dello STACK POINTER l'indirizzo di ritorno ; dalla ISR e carica nel PC il valore 4h ; IN QUESTO SPAZIO cioè TRA org 4h e Retfie dobbiamo ; inserire la ISR ovvero il blocco di istruzioni che gestiscono ;l'evento che ha generato l'interrupt Retfie Main_program ; Questa istruzione si comporta come la Return di una ; subroutine cioè ritorna al programma principale nella ; posizione in cui è stato generato l'interrupt ; la Retfie Riabilita automaticamente il GIE Global Interrupt ; Enable che è il bit che serve per abilitare o disabilitare tutti ; gli interrupts del pic ; in questa parte del sorgente cioè tra Main_program e ; END verrà scritto il programma principale ; all' inizio dovremo CONFIGURARE I PORT e abilitare gli ; interrupt che servono end Autore Gabriele Viscardi 1
Dall esempio si nota che subito dopo aver dato origine al programma si deve fare un salto incondizionato (Goto Main_Program) in modo tale che il compilatore riservi il posto alla routine di interrupt nella memoria FLASH. Per riservare il posto alla routine di interrupt (ISR) basta dichiarare la sua posizione con la direttiva ORG 04H e quindi scrivere il suo codice. Per poter programmare bene una ISR è bene conoscere in che modo le interruzioni vengono gestite. Quando si verifica un' interrupt il PIC esegue le seguenti operazioni: 1) Disabilita il bit GIE del registro INTCON in modo tale che non vengano gestiti altri interrupt contemporaneamente, 2) Mette a 1 il Flag relativo all' interrupt che si è verificato (serve per identificare l evento che ha generato l interrupt) 3) Salva all interno dello STACK il contenuto del PC ( questo è l indirizzo di ritorno dalla ISR), e successivamente carica il PC il valore 4h Interrupt Vector ( cioè va ad eseguire la ISR ) Quindi, sapendo questo, durante la programmazione di questa routine si deve: 1) Fare attenzione alla posizione dove si colloca la sequenza iniziale della ISR che deve essere la 0x04 in modo che possa essere chiamata. (Questo viene fatto dalla direttiva ORG 04H) 2) Salvare i dati 10 del registro di stato STATUS e il registro accumulatore W (e altri registri che si pensa possano essere modificati durante l' interruzione). Questa è una delle classiche operazioni fatte nella gestione degli interrupt, essendo queste chiamate in momenti sconosciuti dell esecuzione del programma. 3) Identificare l' interrupt testando i bit di interesse dei registri: INTCON, PIR1,PIR2 4) Chiamare la subroutine di gestione appropriata 5) Cancellare il bit che identifica l' interrupt 6) Ripristinare gli eventuali registri precedentemente salvati 7) Uscire dalla subroutine attraverso l' istruzione retfie che oltre a ricaricare dallo stack il PC ( cioè l indirizzo di ritorno dalla ISR ) riporta a 1 il bit GIE per poter rilevare altri eventuali interrupt Autore Gabriele Viscardi 2
;************************************************************************************************************************ ; Struttura di un programma.asm che utilizza gli interrupt con il salvataggio dei registri principali ;************************************************************************************************************************* #include <STD.INC> EQU 20H ; copia del registro w w_temp EQU 21H ; copia del registro STATUS org org 0H Main_program 04H Banksel w_temp ; salva il contenuto del reg W STATUS,w ; salva il contenuto del reg STATUS ;inizio ISR END_ISR ; fine ISR Banksel,w STATUS ; carica nel registro STATUS la copia salvata in w_temp,w ; carica nel registro w la copia salvata in w temp Retfie ; ritorna al programma principale Main_program END Se all interno della ISR non vengono modificati i registri w status ecc.. non è necessario salvarne una copia prima della, mentre è di grande importanza salvarli se questi vengono modificati. È molto probabile che una routine di questo tipo possa dare dei problemi durante il salvataggio del registro STATUS. Infatti nell esempio precedente si può vedere che la direttiva Banksel prende il posto di una istruzione tipo BSF STATUS,RP0 che presuppone una variazione del registro STATUS prima ancora che si sia fatta una sua copia. Da notare che però anche se tecnicamente non corretta, una routine di questo tipo da problemi solo se viene chiamata, da un interrupt, in un punto del programma dove il banco di memoria selezionato risulta essere diverso da quello in cui si trova STATUS_TEMP, mentre non provoca nessun problema se ad esempio viene richiamata all' interno di un loop infinito in cui si era già preselezionato il banco di memoria di STATUS_TEMP. Nello stesso tempo, però una routine di questo tipo permette una gestione più veloce dell' interrupt rispetto ad una routine che invece non presenta questo tipo di errore. Morale: state attenti quando usate una routine di questo tipo e valutate se il risparmio di tempo è essenziale o se è meglio evitare possibili problemi durante la chiamata di interrupt. Una possibile gestione alternativa dell' interrupt che tiene conto del banco di memoria può essere: Autore Gabriele Viscardi 3
Struttura della macro SALVA_REGISTRI Qual è il banco di memoria RAM selezionato prima dell interrupt?? Test RP1 RP1 = 0 Sono nel banco 0 o 1 RP1 = 1 Sono nel banco 2 o 3 Test RP0 Test RP0 RP0 = 0 Sono nel Banco 0 RP0 = 1 Sono nel Banco 1 RP0 = 0 Sono nel Banco 2 RP0 = 1 Sono nel Banco 3 Salvo il registro W in W_temp e il registro FSR in FSR_temp Salvo il registro W in W_temp e il registro FSR in FSR_temp Salvo il registro W in W_temp e il registro FSR in FSR_temp Salvo il registro W in W_temp e il registro FSR in FSR_temp Modifico RP0 e RP1 del registro STATUS_TEMP con RP1 = 0 e RP0 = 0 Modifico RP0 e RP1 del registro STATUS_TEMP con RP1 = 0 e RP0 = 1 Modifico RP0 e RP1 del registro STATUS_TEMP con RP1 = 1 e RP0 = 0 Modifico RP0 e RP1 del registro STATUS_TEMP con RP1 = 1 e RP0 = 1 Goto Goto Goto Goto Autore Gabriele Viscardi 4
Contenuto.asm della macro SALVA_REGISTRI btfsc STATUS,RP1 banco_2o3 banco_0o1 btfsc banco_0 bcf bcf banco_1 bsf bcf banco_2o3 btfsc banco_2 bcf bsf banco_3 bsf bsf STATUS,RP0 banco_1 w_temp FSR,0 fsr_temp STATUS,0,RP0,RP1 w_temp FSR,0 fsr_temp STATUS,0,RP0,RP1 STATUS,RP0 banco_3 w_temp FSR,0 fsr_temp STATUS,0,RP0,RP1 w_temp FSR,0 fsr_temp STATUS,0,RP0,RP1 Autore Gabriele Viscardi 5
Prima di chiudere la ISR con l istruzione RETFIE è necessario ripristinare il contenuto dei registri precedentemente salvati Contenuto.asm della macro RIPRISTINA_REGISTRI swapf swapf,0 STATUS fsr_temp,0 FSR w_temp,1 w_temp,0 In generale una routine di INTERRUPT è cosi strutturata : #include std.inc 0RG 0RG 0H MAIN_PROGRAM 04H SALVA_REGISTRI ; macro che serve per salvare il contenuto dei registri prima della ISR ; label che serve per identificare l inizio della ISR ( non si può modificare ) ; in questo spazio inserisco tutte le istruzioni che servono per la gestione degli eventi che hanno ; generato l interrupt RIPRISTINA_REGISTRI ; macro che serve per ripristinare il contenuto dei registri precedentemente salvati Retfie ; chiude la ISR e ritorna all esecuzione del main Program MAIN_PROGRAM ; In questa parte devo : ; - configurare i registri per la gestione degli interrupt ; - configurazione dei port ; - istruzioni del programma ; ecc. END Autore Gabriele Viscardi 6
Una volta conclusa la programmazione della ISR si deve continuare la scrittura del programma principale. Per fare questo si pone l etichetta usata dal salto incondizionato MAIN_PROGRAM e si programma il cosiddetto Main del Programma. Nella programmazione del corpo del programma, oltre alle funzioni che il PIC dovrà eseguire per il compito che gli è stato assegnato, si deve: 1) Abilitare il tipo di interrupt che si vuole gestire andando a settare a 1 i bit appropriati nei registri: INTCON, PIE1, PIE2 2) Abilitare la segnalazione generale degli interrupt attraverso il settaggio a 1 del bit GIE che si trova nel registro INTCON 3) Tener conto che la chiamata ad un interrupt porta ad un salvataggio nello stack dell' indirizzo contenuto nel PC e che quindi si potrebbe verificare un overflow dello stack!! Registri di Interrupt I registri utilizzati per la gestione degli interrupt sono: INTCON, PIE1, PIE2, PIR1,PIR2 Autore Gabriele Viscardi 7
0000 Ogni bit di questi registri ha una sua funzione particolare ed esclusiva nella gestione degli interrupt. Si può però fare una distinzione dei bit in 3 classi : Il bit GIE e PEIE di INTCON I bit, esclusi GIE e PEIE, il cui nome finisce con E, cioè i bit TOIE, INTE, RBIE di INTCON e tutti i bit di PIE1 e PIE2. I bit il cui nome finisce con F, cioè i bit TOIF, INTF, RBIF di INCON e tutti i bit di PIR1 e PIR2. Nella prima classe si è posto il bit GIE che è il bit di abilitazione di tutti gli interrupt che possono essere segnalati. Come si può osservare dalla figura seguente, la segnalazione degli interrupt è fatta attraverso una rete logica multilivello e la comunicazione alla CPU dell interrupt è vincolata dal settaggio a uno di GIE. Allo stesso modo il bit PEIE possiede una grande importanza in quanto blocca la comunicazione di un interrupt da tutta una serie di sorgenti. Questo tipo di interruzioni vengono chiamate Interruzioni di periferica (Peripheral Interrupt). Senza addentrarci nella gestione di ogni singola sorgente di interruzione, possiamo dire che il registro INTCON permette la gestione degli INTERRUPT provenienti dal TIMER0, dal pin RB0/INT della porta B, e dagli ingressi RB4:RB7 sempre del PORTB mentre tutte gli altri interrupt sono generati dalle periferiche e quindi sono trattati in modo a se stante attraverso i registri PIEx e PIRx. Autore Gabriele Viscardi 8
La seconda classe si compone dei bit, detti ENABLE BIT, che permettono l abilitazione o la disabilitazione della segnalazione del singolo interrupt. Nell ultima classe si hanno i cosiddetti FLAG BIT che segnalano l avvenuto interrupt. Molto importante è sapere che anche quando una sorgente di interrupt non viene abilitata per mezzo del suo ENABLE BIT, se si verifica un interrupt questo viene segnalato sul FLAG BIT, e la comunicazione alla CPU dell interruzione è ostacolata dall AND logico con l ENABLE BIT. Si vuole far presente che all uscita della routine di getione dell interrupt è compito del programma il ritorno a zero del FLAG BIT che ha generato l interrupt, pena la nuova richiamata della stessa interruzione. Autore Gabriele Viscardi 9