Let's GO PIC!!!
cap. 6
Gli interrupt
Sesto capitolo del tutorial Let's GO PIC !!! in cui si introdurrà una tipologia di programmazione più avanzata e nello stesso tempo più comune e funzionale. Quanto presentato nelle puntate precedenti tiene impegnato il processore distogliendone l'attenzione dai segnali provenienti dal bordo macchina verso i PORT di input, e un nuovo task verrà eseguito solo dopo che sia terminato quello in corso. In pratica questa modalità di programmazione non è solo più lenta ma realmente pericolosa dato che un segnale rapido di emergenza non solo sarà soddisfatto in un secondo momento ma potrebbe addirittura venire ignorato se la sua permanenza all'input fosse più breve del tempo necessario ad terminare il task in corso.
Il
contesto
Prima di addentrarci nella
programmazione forniamo ai "nuovi del settore" alcuni concetti di
base che coinvolgono non solo il software ma anche l'architettura interna del
processore o analogamente del microcontrollore. Anche se questa sesta
puntata vuole essere estremamente succinta, facciamo comunque qualche breve
divagazione introduttiva come si addice al classico stile "ad.noctis".
In primis, facciamo una domanda: Quanti di voi riconosco quest'uomo?
Nessuno ??? Male!.. molto Male!! perchè quest'uomo è uno che ha ben contribuito a fare grade il nostro paese. E' nato in una frazione della citta di Vicenza e si chiama Federico Faggin. Grazie a lui possiamo usare i computer odierni dato che è l'inventore del microprocessore. Hooo ma che sguardi stupiti, pensavate che il processore lo avessero inventato gli americani, o i giapponesi o che ne so i russi? No no, è un prodotto nostrano, esattamente come la polenta, la pizza, o più tecnologicamente il telefono ecc ecc.
Quando faccio questo indovinello ai miei giovani allievi molti rispondo: Bill Gate, ovvio lo sanno tutti. Bhè magari sarà stato un ottimo commerciale e forse programmatore ma non mi sembra che l'hardware fosse il suo campo.
Comunque, facciamo un saltino a questa pagina, e vediamo chi sono le persone che con il loro genio e il loro lavoro hanno reso comoda e semplice la nostra vita:
http://e-ducation.net/inventors.htm
Se siete tornati dal salto al link, avete lo spirito giusto per continuare. Avete notato che visi semplici hanno queste persone che hanno cambiato il mondo? Bene, potrebbero essere ogniuno di voi, questo vi da la carica è?.
Vediamo cos'è il "contesto".
Si dice contesto, lo stato attuale di una elaborazione di programma, rappresentata dal contenuto di ogni registro interno del microprocessore / microcontrollore.
E' indispensabile conoscere l'architettura interna, o almeno le parti fondamentali del microprocessore, e a tal fine rimane sempre valida l'architettura di base ideata da Faggin.
Descriviamo brevemente gli oggetti visibili nello schema a blocchi:
Bus: Con il termine bus si identificano le linee fisiche su cui sono trasferiti i bit di informazione.
I bus contenuti in un generico microprocessore (ovvero un oggetto da studio) sono:
La A.L.U.
Aritmetic Logic Unit, solitamente disegnato con forma trapeziodale o simile, contiene la rete logica sequenziale/combinatoria utilizzata dal microprocessore per l'elaborazione dei dati. La ALU è un elemento privo di memoria, percui deve essere assistita in ingresso da due (o più) registri temporanei (nel caso dei P.L.C. Siemens, ad esempio, si chiamano ACCU1 e ACCU2). L'accumulatore, per i PIC denominato registro di lavoro, o working register, ( registro W) sarà considerato la destinazione canonica di tutti i movimenti di dati da e per la memoria, ed anche il luogo nel quale si depositano i risultati delle operazioni. I dati che vanno alla A.L.U. transitano e sono memorizzati nei registri accumulatori (nel caso dei PIC nel W). Per quanto riguarda l'hardware della ALU deve essere in grado di eseguire almeno le seguenti operazioni minime: Addizione, Incremento, AND, Ex-Or, Shift destro e sinistro, roll, Clear dell'accumulatore. Sempre sotto controllo da parte della ALU è il registro di STATO, il quale contiene i FLAG, (bit che indicano lo stato di funzionamento).
Registri specifici.
Questi dispositivi memorizzano in modo temporaneo i dati da elaborare o le impostazioni di alcune sezioni hardware del microprocessore/microcontrollore. Uno di questi registri è ad esempio TRISx dove x indica il PORT di I/O a cui si fa riferimento. Anche lo status register fa parte di questi registri e mantiene dei bit di significato speciale (flag). Le informazioni riposte in questi registri vi permangono (Latch) fino al momento di una sovrascrittura.
Registro di lavoro W.
Costituisce il registro principale di un PIC e sarà la parte più in uso durante il normale funzionamento di qualunque programma. Ogni operazione che interessa il W register ne cancella il contenuto precedente a causa di una sovrascrittura. Il working register può operare direttamente con la memoria o i dispositivi di I/O, esistono infatti, in assembly, operazioni di trasferimento del tipo:
Registri di uso generale A,B,C,D.
Vengono utilizzati come se fossero delle celle di memoria R.A.M., e quindi sono indirizzabili (accessibili) anche un byte alla volta. Questi registri sono molto utili per trattenere dati parziali di un'addizione (o operazioni in genere) contestualmente al contenuto del W register.
Status Register (SR).
in questo registro sono contenuti i flags i quali hanno lo scopo di memorizzare i risultati di alcuni test eseguiti dalla ALU. I flags (bandierine di segnalazione) normalmente presenti in un microprocessore sono:
C (carry), segnala se durante un'operazione algebrica si è verificato un riporto. Normalmente una segnalazione di carry corrisponde ad un errore.
Z (zero), segnala quando il risultato di una operazione è nullo.
N (negative), segnala se il primo bit di una rappresentazione flating point è 1, quindi se il numero rappresentato è negativo. Inoltre, quando si lavora in complemento a due N risulta alto.
P (parity), questo flag è in grado di segnalare se durante un trasferimento dati (sia interno che esterno) si è verificato un errore di trasmissione.
Program counter (PC).
IL PC contiene l'indirizzo dell'istruzione che si sta eseguendo e predispone il microprocessore all'esecuzione della prossima tramite il processo dell'auto incremento. Considerando la memoria come una matrice, ad ogni sua riga corrisponde un valore esadecimale "indirizzo". Per estrarre un'istruzione o dato dalla memoria è necessario "puntare" a tale cella caricando il suo indirizzo nel PC. La lunghezza di tale registro determina la massa di dati indirizzabili in memoria cioè la "locazione" di memoria più lontana dall'origine (estensione). Durante l'esecuzione di un programma si possono verificare due casi:
Stack pointer (SP).
Questo registro che consente la tecnica di programmazione a blocchi, cioè consente la chiamata a sottoprogrammi. Lo stack è una struttura di tipo L.I.F.O. (last in first out) situata in una particolare area di memoria dipendente dal tipo di microprocessore. Lo stack di tipo LIFO è organizzato in modo che l'ultimo dato ad essere inserito sia necessariamente il primo ad essere letto "estratto". un'immagine mentale della struttura la possiamo ottenere pensando a una pila di piatti che dopo essere stati lavati sono in attesa di venire asciugati. Questa modalità (stack) è quella che riguarda direttamente il salvataggio dei contesti durante l'attivazione di una routine di servizio che risolve un interrupt.
Architettura interna dei PIC
La famiglia dei PIC è estremamente
vasta ed a ogni esemplare corrisponde una simile ma diversa architettura
interna. Nella sostanza il procedimendo di commutazione del contesto non varia
ma la differenza sarà la quantità di registri e lo spazio nella stak occupato
dal singolo contesto.
L'immagine riassume le cose principali da coinvolgere nella programmazione basata sull'attivazione e test dei segnali di interrupt.
Sul PORTB, colorati di rosso e cerchiati in rosa le principali fonti che possono provenire dall'esterno, ovvero la presenza al PORTB pin RB0 oppure il cambio di stato sempre nel PORTB ma nei pin da RB4 a RB7, mentre la fonte interna più sfruttata è senzaltro il segnale di interrupt generato dallo scadere (overflow) del timer hardware TMR0, che dovrà essere interpretato e gestito come un contatore di ciclo, dato che non è possibile inserire funzioni all'interno della routine di servizio.
In rosa ho indicato il working regester, ovvero la destinazione di tutti i caricamenti dei dati che devono essere processati, il program counter è evidenziato in alto in giallo,e l'area dello stack pointer evidenziata in azzurro. Tutte informazioni fotografate e salvate perché fondamentali per il corretto ritorno dopo l'esecuzione della routine di servizio ISR (iterrupt service routine).
Visto dall'esterno, il PIC 16F876A mette a disposizine gli interrupt dall'esterno sui pin evidenziati in rosso:
La memoria del PIC 16F876 come quella del modello superiore 877 è mappata, rispetto al vettore di interrupt in questa maniera:
Questa informazione potrà essere marginale se programmiamo in C ma se programmiamo in assembly è fondamentale, difatti dovremmo inizializzare la memoria all'indirizzo 4h con una direttiva "org", dove ha origine il vettore di interrupt. Subito sopra possimao vedere gli otti livelli di stack che corrispondono anche alla quantità di contesti commutabili senza un preventivo rientro dalla routine di servizio dell'eccezione (evento che scatena l'interrupt) che ha lanciato l'interrupt in corso.
Fonti di interrupt
Il segnale di interruzione del task in corso può arrivare sia dell'esterno del micro che dall'interno. Può arrivare ad esempio dalla periferica UART o dal modulo di capture e compare (fonti interne) o dal PORTB, su variazione di stato o su presenza di RB0 (fonti esterne).
Nello specifico caso del processore di riferimento del corso "Let's GO PIC !!!" è il 16F877A o il fratello minore 16F876A di cui si invita a tenere sotto occhio il databook.
scarica il data book -> download 16F87x
Se state seguendo il corso "Let's GO PIC!!!" muniti della Micro-GT versatile IDE potete svolgere le prove con una moltitudine di modelli di processori, ma se usate la Micro-GT mini potrete sfruttare i processori a 28 pin, che comunque non sono pochi e molto potenti. Possiamo installare il PIC16F876A (come noto), oppure i 18F2550 o 18F2580 per i quali vi rimando al sito della MicroChip per il download del PDF.
Nella documentazione troverete quante e quali sono le fonti di interrup dei vari processori, ma per quanto riguarda il microcontrollore di riferimenti della Micro-GT mini, 16F876A, troverete 14 differenti fonti, alcune generate internamente e altre che possono provenire dall'esterno ma che devono giungere in posizioni preassegnate dell'I/O, ad esempio il RB0.
Nelle prime fasi sperimentali useremo solo le fonti più semplici, come ad esempio il segnale al pin RB0 o il cambio di stato in alcuni pin del PORT B.
La prima cosa da comprendere è quindi la necessità di specificare da quale di queste fonti disponibili si vuole accettare il seganle di interrupt, tramite il settaggio software negli apposii registri.
La seconda cosa è che l'attivazione di interrupt comporta l'attivazione di una routine di servizio, che in Ansi C, o nel compilatore Hitech C16 che abitualmente utilizziamo, viene implementata* (significa svolta, realizzata, scritta) come una funzione, ovvero una void che obbilgatoriamente dovrà chiamarsi "interrupt" seguita dal nome custom prescelto dal programmatore.
esempio:
void interrupt led_blink( ){ //inizio routine di servizio
//corpo della routine di servizio.
//assolutamente vietato inserire qui dentro altre funzioni.
//inserire solo comandi direttamente eseguibili.
}//fine routine di servizio
Eventuali temporizzazioni dell'evento svolto dalla routine di interrupt vanno eseguite in due maniere:
Nel primo caso possiamo usare le "funzioni" delay, dato che si trovano fuori dalla funzione di interrupt, nel secondo caso non si può fare.
Dobbiamo tenere presente che questo microcontrollore ha cinque registri da configurare, ma che non saremo costretti a configurare ogni bit, alcuni li potremmo lasciare nel suo default.
La terza cosa che ci dovremmo preocupare di verificare è da dove si è sviluppato l'interrupt, ovvero identificarne la fonte e quindi leggere e impostare i bit dei predetti registri.
Gli interrupt, all'interno del PIC 16F876 sono manipolati dalla logica sottorappresentata che riceve come input dei vari operatori logici, i Flag provenienti dalle 13 fonti di interrupt (la quattordicesima nell'876 non è implementata come nell'877, ovvero la PSPIF) e dai vari registri di configurazione.
E' molto evidente che l'ultimo AND lascia passare qulsiasi fonte di interrupt solo se è alto il flag GIE (general interruput enable) che risulta essere il settimo bito del registro INTCON.
Della documentazione da me rielaborata, di fonte internet, il registro INTCON risulta essere questo:
Registro INTCON
Bit7 |
Bit6 |
Bit5 |
Bit4 |
Bit3 |
Bit2 |
Bit1 |
Bit0 |
GIE |
PEIE |
T0IE |
INTE |
RBIE |
T0IF |
INTF |
RBIF |
Il registro INTCON è un area di lettura e scrittura (R/W) in cui è possibile abilitare dei bit abbinati alle varie fonti di interrupt.
INTCONT, anche se non specificato nel databook dovrebbe signigicare INTurrpt CONtrol.
Ogni bit di flag viene impostato alto quando si verifica la richiesta di interrupt dall'eseterno o da una periferica interna (di quelle in grado di farlo) ma la richiesta di interruzione viene ignorata se non è abilitato il bit GIE, che come già detto è il settimo bit.
In definitiva questo registro è il controllo di ingresso della rete logica vista sopra, difatti i bit da 0 a 6 sono usati per la configurazione e abilitazione gestendo anche le 4 fonti esterne
Ribadiamo che non ci sarà alcun effetto senza la presenza di GIE in settima posizione del registro ovvero il bit 6.
.
|
R = bit leggibile W= bit scrivibile U = bit libero, letto come ‘0’ reset
|
bit 7: GIE: bit Global Interrupt Enable bit 6 PEIE: Perifeferal interrupt enable bit 1 = abilita tutti gli
interrupt dalle periferiche non mascherate
bit 5 T0IE: TMR0 abilita interrupt che segnala Overflow del Timer0 1 = abilita
interrupt TMR0
bit 4: INTE: RB0/INT abilita Interrupt
sul pin del PIC bit 3: RBIE: RB bit di abilitazione interrupt sul cambio di stato sul PORTB 1 = abilita su
cambio di stato del PORT RB
bit 2: T0IF: interrupt su tempo scaduto
di TMR0 (overflow interrupt flag bit)
bit 1: INTF: RB0/INT Interrupt Flag bit 0 = basso significa che non c'è richiesta di interrupt RB0/INT
BIT 0: RBIF: RB alza il falg se c'è cambio di stato su uno dei bit a PORTB 1 = Quando almeno un bit
tra RB7 e RB4 cambia stato (deve essere resettato via software) |
Notiamo che alcuni flag sono indicati con "if" e altri con "E", con ovvio significato di IF->testa la condizione ovvero se si è verificato o meno l'interrup in questione, mentre "E" enable, abilita il suddetto interrupt a lanciare la routine di servizio, ovviamente se presente anche GIE.
Tra le fonti di interrupt figurano anche quelle collegate ai convertitori analogici digitali, d'ora in poi abbreviati A/D.
Quando il corventitore A/D ha eseguito la conversione, il risultato è caricato nei registri ADRESH e ADRESL.
Il bit GO/DONE (ADCON0 bit2)viene resettao e il flag di interrupt ADIF viene posto a 1. Gli altri bit coinvolti con funzioni di impostazione dell'interrupt per la conversione AD sono ADIE, che si pone a 1 all'inizio della conversione, PEIE va posto a 1 e ovvimente il GIE.
I flag relativi alle interruzioni provenienti dalle periferiche sono contenuti nei registri di funzione speciale PIR1 e PIR2 i cui corrispondenti bit di enable (abilitazione) sono contenuti nei registri di funzione speciale PIE1 e PIE2.
Durante la "risposta" a un interrupt , ovvero durante l'esecuzione dell'ISR, il flag GIE viene azzerato con l'effetto di impedire in questo momento il lancio del servizio di un altro interrupt.
Rispondento all'interrupt avviene come prima cosa il salvataggio dell'indirizzo di ritorno dall'ISR con un pusch (o caricamento in testa) nell'area di stack, e il program counter viene caricato con l'indirizzo 4h in cui è allocato il vettore di interrupt.
Una volta all'interno dell'ISR, viene testato quale degli interrupt flags è alto ovvero quale fonte ha causato l'eccezzione.
Tale falg, una volta identificato va resettato come ultima istruzione all'interno della routine di servizio, al fine di non rientrarci per ricorsione e non per la vera presenza di una eccezzione che chiede di essere servita con priorità.
Il nostro programma di test.
scarica il programma di test degli interrupt e modificalo a seconda delle tue esigenze -> download interrupt test.
#include <pic.h> //Libreria
standard dei PIC che permette di definire i registri TRIS, PORT e altre
funzioni base
#include <htc.h> //Libreria specifica di hitech che
permette le inline delay e di definire la funzione interrupt
#define _XTAL_FREQ
4000000 //
frequenza quarzo, molto importante se si usano porzioni di esso nei prescaler
#define led
RB5 //definizione
di etichette mnemoniche, per comodità di lettura, ai pin del PIC
#define led1 RB6
#define led2 RB7
void configura(); //predichiarazione
della funzione di settaggio dei registri da modificare
internamente a seconda dei casi
void main(){ //inizio programma principale
led1=1;
led2=0; //condizione iniziale
dell'oscillazione astabile dei due led
configura(); //chiama la
funzione di settaggio registri, il corpo si trova sotto il main, funziona
perchè predidichiarata
while(1){ //ciclo infinito, non ha senso ripetere
i settaggi a ogni ripetizione del programma
led1=!led1; //inverte
lo stato del led1
__delay_ms(49);__delay_ms(49); __delay_ms(49);__delay_ms(49);
led2=!led2; //inverte lo stato del led2
__delay_ms(49);__delay_ms(49); __delay_ms(49);__delay_ms(49);
} // fine
del while
} // fine del main
void interrupt TIS(){ //funzione
interrupt che contiene la routine di servizio
if(INTF==0x01){ //test del flag dell'interrupt
INTF=0x00; //se siamo entrati qui allora il
flag ha già fatto la sua funzione e lo devo resettare per prossimo uso
led=!led;
//inversione dello stato del
led che si attiva su interrup esterno presente su RB0
}
}
void configura(){ //funzione
che imposta tutti i registri prima di cominciare l'esecuzione del programma
TRISA=0x00; PORTA=0x00;
TRISB=0b00000001;
//PORTB=0x00;
TRISC=0x00;
PORTC=0x00;
TMR0=0x00; //abilita e
resetta il Timer hardware TMR0
INTCON=0b10000000;
// INTurrpt CONtrol register
PIE1=0x00;
PIE2=0x00;
PIR1=0x00;
PIR2=0x00;
OPTION_REG=0b10000111;
GIE=0x01; //impostiamo
a 1 il flag del general interrupt enable
//T0IE=0x01;
INTE=0X01; //interrupt
enable abilitato
RBIE=0x00;
//abilita interrupt sul
cambio di stato del PORTB
//T0CS=0x00;
//PSA=0x00;
//PS2=0x01;
//PS1=0x01;
//PS0=0x01;
}
Fase di sperimentazione e collaudo
http://www.youtube.com/watch?feature=player_profilepage
Cenni a un prossimo esempio.
In un prossimo episodio di "Let's GO PIC!!!" vedremo come creare un segnale PWM utilizzando il TMR0 e il segnale di interrupt genarato dal suo overflow.
Accenno che il TMR0 è un dispositivo hardware che si incrementa automaticamente e che funge più da contatore che da reale timer.
Sarà appunto contando le sue attivazioni che ne otteniamo un tempo, generalmente come multiplo di una frazione del clock che giunge ad overflow in 255 iterazioni.
All'overflow viene generato l'interrupt che sfrutteremo o per l'incremento di un secondo contatore o solo per il suo reset.
L'incremento del TMR0 genera una rampa che potremmo intercettare con i comparatori integrati.
Spostando la soglia di comparazione andiamo ad agire sul duty cycle del segnale PWM di cui staimo tenedo costante la frequenza ad esempio a 22khz.
La soglia di comparazione la potremmo ottenere da un segnale analogico proveniente da un potenziometro conesso al pin 2 ovvero AN0.
Ecco cosa otteremo:
Per la soluzione di questo esercizio basato sull'interrupt su overflow del TMR0 vi rimando a una specifica puntata del corso.
Sperando di essere riuscito a trasmettere con chiarezza questa introduzione all'uso degli interrupt auguro a tutti buon divertimento
I circuiti stampati utilizzati nel corso “Let’s GO PIC !!!” sono disponibili, Micro-GT IDE, Micro-GT mini, mini shield, ecc ecc.
Chi fosse interessato mi contatti alla mail ad.noctis@gmail.com
This opera "Let's GO PIC!!! cap 6" is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Italy License