Sei sulla pagina 1di 20

La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

Cerca GO

Social

Home Drivers PICMicro Risorse Servizi Software Info

1k
Questo sito e tutto il suo contenuto sono distribuiti sotto la licenza Creative Commons Attribuzione -
Non Commerciale - Non opere derivate 2.5 Italia e con le condizioni d'uso definite nel disclaimer: siete
pregati di leggere entrambi questi documenti prima di usufruire dei contenuti di questo sito. Per alcuni
contenuti è necessaria una registrazione gratuita: non è necessario pagare e non è necessario
Online Now
accumulare punteggi per accedere agli articoli e scaricare i sorgenti: basta solo essere onesti. Se
volete che questo sito continui a rimanere attivo, a contribuire ogni giorno alla diffusione della cultura libera, non copiate il
materiale per ripubblicarlo in altri luoghi. Se volete partecipare su settorezero e rendere le vostre idee, i vostri progetti,
10 Visitatori connessi
fruibili da tutti senza limitazioni potete farlo tranquillamente.

Meta
Registrati
Accedi
Feed RSS articoli
La gestione degli interrupt sui pic12 – pic16 – pic18 – Feed RSS commenti

pic24 – dspic Elenco Categorie


Elenco Categorie
Autore: Giovanni Bernardo | Data pubblicazione: 11 dicembre 2010 Seleziona una categoria
Categorie: dsPIC / PIC24 PICmicro 10/12/16 PICmicro 18

La gestione degli interrupt è una cosa forse complicata ma che consente


sicuramente di realizzare applicazioni molto complesse ed efficienti e spesso
impensabili da eseguire tramite polling. Ho pensato quindi di fare cosa gradita
eseguendo una comparazione tra le modalità di funzionamento e di gestione
degli interrupt sulle varie fasce di pic. Questo, pertanto, vuole essere sia un
articolo di approfondimento sugli interrupt che una terza raccolta di appunti
dedicati all’esplorazione e alla migrazione verso i pic a 16 bit.

Riassumo brevemente cos’è un interrupt (o “interruzione” in italiano): è una


condizione particolare, interna o esterna al pic, che consente di mettere in
pausa il nostro programma principale e fare in modo che la CPU si occupi
piuttosto di eseguire una porzione di codice associata alla particolare
condizione che ha causato l’interruzione. Quando questo codice è stato
eseguito (ovvero: l’interrupt è stato servito), la CPU può tornare ad eseguire il
programma principale. Sfruttando opportunamente gli interrupt è possibile
realizzare, verosimilmente, programmi multitasking, ovvero che eseguono più
operazioni contemporaneamente.

Le differenze nella gestione degli interrupt variano, ovviamente, a seconda


dell’architettura del pic e di conseguenza anche il codice da scrivere in C è
differente. Per questa trattazione farò riferimento ai seguenti linguaggi:

PICC compiler (Hitec-C) per i pic12 e pic16 (fornito di serie con MPLAB)
MPLAB C18 per i pic18 (download)
MPLAB C30 per i pic24 e dsPic (download)

Se usate compilatori diversi da questi elencati, alcune cose, a livello di


scrittura del codice, potrebbero essere diverse.
Siti Amici
Ricordo qui alcuni concetti fondamentali riguardanti gli interrupt e da tenere
bene a mente quando andremo ad affrontare la loro gestione. Le routine di
gestione degli interrupt devono sempre avere i seguenti requisiti per tutte le
tipologie di MCU:

1. Non accettano parametri in ingresso e non restituiscono nulla in uscita

1 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

(utilizzano sempre il tipo VOID)


2. Non possono essere richiamate dal programma principale
3. Devono essere più corte possibile. Qualora si richieda di eseguire
istruzioni laboriose è sempre bene nell’ISR settare un flag che verrà poi
controllato nel main per eseguire le operazioni associate all’evento di
interruzione.
4. Non devono richiamare altre funzioni

I punti 1 e 2 sono obbligatori, pena la mancata compilazione del programma, i


punti 3 e 4 sono invece fortemente consigliati e ritenuti “moralmente”
obbligatori.

Quando si verifica un interrupt, lo stato dei registri principali (che variano da


pic a pic) viene generalmente salvato per poi essere ripristinato al termine
della ISR (Interrupt Service Routine: ovvero la serie di istruzioni da eseguire nel
momento in cui si verifica un’interruzione) in maniera tale da poter riprendere
il programma nell’esatto punto e nell’esatto stato in cui era stato interrotto.

Al verificarsi di un interrupt, quindi, non viene eseguita subito la prima


istruzione dell’ISR ma trascorre un certo lasso di tempo durante il quale
vengono appunto eseguite queste operazioni di salvataggio di stato. Il tempo
che intercorre tra il verificarsi dell’interrupt e la prima istruzione dell’ISR
eseguita è detto tempo di latenza. I pic di fascia medio/alta hanno delle
funzionalità hardware che permettono di diminuire i tempi di latenza
velocizzando le operazioni di salvataggio di stato.

Ma come fa il pic a capire quali istruzioni deve eseguire al verificarsi di un


interrupt e a quale punto tornare dopo che ne ha terminato l’esecuzione?

Il pic tiene conto delle istruzioni da eseguire nel nostro programma mediante
un registro particolare chiamato PC (Program Counter – diviso in due parti). In
tale registro la CPU carica la locazione di memoria nella quale è contenuta la
successiva istruzione da eseguire: man mano che il programma va avanti, il PC
viene incrementato.

Nel momento in cui si verifica un interrupt, la CPU deve mettere in pausa il


programma principale e dedicarsi quindi ad eseguire le istruzioni definite nella
nostra ISR: questo viene realizzato in automatico caricando nel PC il cosiddetto
interrupt vector ovvero quella particolare locazione di memoria nella quale
partono le istruzioni dell’ISR. In pratica la CPU viene dirottata in un’altra
posizione per poter eseguire un codice diverso e poi ritornare al punto in cui
si era fermata in precedenza.

E’ facile capire, quindi, che il PC può anche essere alterato via


software ma le applicazioni che fanno uso di questa tecnica
richiedono conoscenze molto approfondite.

La CPU per poi poter ritornare al punto in cui il programma è stato interrotto
utilizza una serie di registri chiamati stack. In tali registri vengono
memorizzate le locazioni di memoria a cui tornare dopo un’interruzione o
dopo che è stato eseguito un salto qualsiasi (istruzioni GOTO o chiamate a
funzioni esterne).

I registri dello stack sono sequenziali dall’alto verso il basso: ogni volta che si
esegue un salto, l’ultima locazione di memoria a cui ritornare viene
memorizzata verso il basso e a tal scopo c’è anche un registro aggiuntivo
chiamato Stack Pointer (STKPTR) che permette di capire in che punto dello
stack ritornare (o in quale punto dello stack memorizzare il prossimo ritorno).
Si hanno quindi più livelli (più registri) di stack a seconda dell’architettura. Sui
pic12 e pic16 di fascia bassa, ad esempio, ci sono 8 livelli di stack il che
significa che possiamo annidare massimo 8 “salti”. Quando si annidano
funzioni (una funzione che ne richiama un’altra, la quale ne richiama un’altra
ancora e così via) si fa appunto utilizzo di salti e quindi dello stack. Nel
Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

2 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

causa comportamenti imprevisti. Il compilatore in genere segnala questa


possibilità con un warning durante la compilazione (possible stack overflow).

Alcuni pic utilizzano un set particolare di registri, chiamati registri shadow (o


fast stack), che servono a memorizzare in maniera rapida lo stato di alcuni
registri principali per poterli riportare alle condizioni precedenti l’interrupt una
volta che questo è terminato. Non tutti i pic hanno questi registri e quelli che
ce li hanno ne hanno uno per ogni registro da memorizzare durante un
interrupt. La presenza di questa funzionalità rende le ISR più veloci (nel senso
che hanno una latenza più bassa).

Le modalità con le quali i pic salvano il proprio stato al verificarsi di un


interrupt sono sempre riportate sul datasheet alla voce Context saving during
interrupts o Automatic contest saving (generalmente nel capitolo Special
features of CPU o incluse nel capitolo riguardante gli interrupts.)

Dopo fatte queste premesse, analizzerò qui come i vari pic (e di conseguenza i
loro compilatori) gestiscono gli interrupt.

Dispositivi Base-Line
Questa categoria non ha la funzionalità degli interrupt e ho incluso questo
paragrafo giusto per spirito di completezza. A questa categoria appartengono
i PIC10, che sono pic in formato SMD a 6 pin (o anche in formato DIP a 8 pin
dei quali 2 però non sono connessi). Quindi se intendete utilizzare un pic10
per la vostra applicazione, sappiate che non avete a disposizione nessun tipo
di interrupt.

Gestione degli interrupt sui PIC12 e PIC16


I pic12 e pic16 hanno un unico interrupt vector posizionato alla locazione
0x04. I dispositivi Midrange (PIC12 e PIC16Fxxx) hanno 8 livelli di stack, i
dispositivi Midrange Enhanced (pic16F1xxx) hanno 16 livelli di stack.

Essendoci dopo la locazione 0x04 tutto il resto della memoria programma, le


routine di interrupt possono essere anche molto lunghe purchè ovviamente il
loro tempo di esecuzione non superi il lasso di tempo tra un interrupt e il
successivo. Ricordo difatti che le routine di interrupt devono sempre essere il
più corte possibile e, anche se abbiamo a disposizione tanto spazio in cui
scrivere le nostre routine e una frequenza di clock molto elevata (che ci
consente, cioè, di eseguire le istruzioni molto rapidamente), bisogna abituarsi
a scrivere solo l’essenziale e a scriverlo in maniera efficiente.

I pic della serie MidRange al verificarsi di un interrupt, salvano in automatico il


contenuto del Program Counter nello Stack e quello dei registri W e STATUS in
registri temporanei (W_TEMP e STATUS_TEMP). I PIC dichiarati obsoleti e fuori
produzione (come il 16F84 e il 16F877) € , ma anche altri di fasci bassa,
salvano soltanto il Program Counter e il salvataggio di W e STATUS e/o altri
registri importanti deve eventualmente essere realizzato via software.

Per questi pic, che registrano da sè solo il PC, l’Hitech-C


esegue un controllo dell’ISR e delle funzioni eventualmente
richiamate da questa per capire se è il caso o meno di
eseguire il salvataggio di altri registri importanti. In caso
affermativo aggiunge in automatico parti di codice per
consentire il ripristino dei registri alla fine dell’Interrupt.

I pic della serie Midrange Enhanced (es.: il PIC16F1934 o il PIC16F1827 per


citarne alcuni), oltre al program counter, salvano i registri W, STATUS, BSR,
FSR, PCLATH nei registri SHADOW.

Il PICC (hitec-C) gestisce le routine di interrupt mediante l’attributo interrupt


da anteporre al nome della funzione che fungerà da gestore interrupt:

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

3 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

Sui pic12/16 ogni interrupt ha un bit di abilitazione (suffisso -IE) che permette
all’interrupt di essere catturato dall’ISR e un flag di avvenuto interrupt
(suffisso -IF) che ci permette di capire se un interrupt si è verificato o meno.
Dato che su questi pic qualsiasi interrupt causa il salto all’unico vettore di
interruzione (e quindi il richiamo della nostra ISR), nell’ISR dovremo capire
quale interrupt è scattato per poterlo servire e questo viene fatto testando,
con una serie di IF, i flag di avvenuto interrupt che ci interessano.

Gli interrupt sui PIC12 e PIC16 hanno una sorta di interruttore generale: il bit
GIE (Global Interrupt Enable) che consente di mascherare tutte le sorgenti di
interrupt, ovvero di non far richiamare l’ISR anche se i bit di abilitazione sono
settati. Vi è inoltre un altro bit di abilitazione per gli interrupt di periferica: PIE
(Peripheral Interrupt Enable) che serve per mascherare tutti gli interrupt i cui
flag di abilitazione si trovano nei registri PIEx. GIE maschera comunque anche
i bit gestiti da PIE.

Gestione degli interrupt sui PIC18


Sui pic18 abbiamo due interrupt vector: quello ad alta priorità (posizionato
alla locazione di memoria programma 0x08) e quello a bassa priorità
(locazione 0x18). Ogni interrupt può quindi essere impostato per essere
gestito in alta o in bassa priorità. A cosa serve la priorità?

Se il processore sta servendo un interrupt a bassa priorità e durante queste


operazioni si verifica un interrupt ad alta priorità, l’ISR che gestisce quello a
bassa priorità viene messa in pausa per consentire alla CPU di servire quello
ad alta priorità. Quando la CPU avrà finito di servire l’ISR ad alta priorità,
riprenderà l’esecuzione dell’ISR a bassa priorità e alla fine di quest’ultima
riprenderà il programma principale.

L’utilizzo di tale caratteristica viene effettuato settando il bit IPEN del registro
RCON. Quando IPEN=1 ci sono due bit che abilitano gli interrupt ad alta
priorità (GIEH, bit 7 del registro INTCON) e bassa priorità (GIEL, bit 6 del
registro INTCON). Il processore, al verificarsi di un interrupt, andrà a cercarsi
le istruzioni da eseguire nelle locazioni 0x08 o 0x18 a seconda della priorità
che è stata assegnata all’interrupt che ha causato l’interruzione del flusso del
programma principale.

Quando si verifica un interrupt ad alta priorità, oltre a salvare il PC nello stack,


il contenuto dei registri WREG, BSR e STATUS viene salvato nei registri shadow
(Fast Register Stack), cosa, quest’ultima, che non avviene per gli interrupt a
bassa priorità.

I pic18 possono gestire gli interrupt anche in maniera compatibile con i pic16:
se poniamo IPEN=0 (condizione di default) non avremo distinzione di priorità.
In questo caso abbiamo che il bit 7 del registro INTCON viene indicato come
GIE ed abilita la gestione globale degli interrupt, mentre il bit 6 del registro
INTCON prende il nome di PIE e serve per abilitare gli interrupt di periferica. In
modalità compatibile, tutti gli interrupt vengono dirottati alla locazione 0x08 e
non essendoci distinzioni di priorità verranno serviti in sequenza. In altre
parole in modalità compatibile tutti gli interrupt sono sempre gestiti come se
fossero ad alta priorità e quindi fanno anche uso del Fast Register Stack. Per
memorizzare i salti i pic18 hanno ben 31 livelli di stack.

Gli interrupt sui PIC18, per poter essere gestiti, hanno anch’essi un bit di
abilitazione (identificato dal suffisso -IE) che consente loro di essere rilevati e
un flag che indica l’avvenuto interrupt (suffisso -IF) il quale viene settato
anche se il relativo bit di abilitazione è posto a zero. In aggiunta qui abbiamo,
per ogni interrupt, anche un bit di priorità (suffisso -IP) che permette di
impostare per ogni interrupt una priorità bassa (bit posto a zero) o alta (bit a
1).

Il numero di locazioni tra l’interrupt ad alta priorità e quello a bassa priorità


Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

4 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

memoria si inserisce un salto (GOTO) ad un’altra routine che effettivamente


gestirà l’interrupt. Per poter gestire quindi gli interrupt sui PIC18, il codice da
scrivere è piuttosto laborioso (anche più di quello necessario rispetto ai pic a
16 bit come vedremo tra poco) e forse anche un po’ brutto perchè il C18 fa
utilizzo delle direttive #pragma che non sono conformi allo standard ANSI C
ma di sicuro facilitano di molto le cose:

// prototipi di funzione
void interrupt_priorita_bassa(void);
void interrupt_priorita_alta(void);

// specifico che la successiva porzione di codice deve essere messa


// nella locazione 0x08, ovvero quella destinata all'interrupt
// ad alta priorità. I nomi che assegno sia al vettore che
// alla funzione li posso dare a piacere e comunque di questi
// nomi non ne avrò più bisogno nel resto del codice
#pragma code vettore_alta_priorita = 0x08
void ISRH(void)
{
// imposto, in assembler, un salto alla mia routine di
// gestione dell'interrupt a priorità alta
_asm GOTO interrupt_priorita_alta _endasm
}

// specifico che la successiva porzione di codice deve essere messa


// nella locazione 0x18, ovvero quella destinata all'interrupt
// a bassa priorità.
#pragma code vettore_bassa_priorita = 0x18
void ISRL(void)
{
// imposto, in assembler, un salto alla mia routine di
// gestione dell'interrupt a priorità bassa
_asm GOTO interrupt_priorita_bassa _endasm
}

// specifico che il codice seguente andrà in altre locazioni


// di memoria scelte dal compilatore
#pragma code

// specifico che la funzione seguente è associata ad un interrupt


#pragma interrupt interrupt_priorita_alta
void interrupt_priorita_alta(void)
{
// codice per gestire interrupt a priorità alta
}

// specifico che la funzione seguente è associata ad un interrupt


// a bassa priorità
#pragma interruptlow interrupt_priorita_bassa
void interrupt_priorita_bassa(void)
{
// codice per gestire interrupt a priorità bassa
}

void main(void)
{
// main
}

Nel caso in cui si lavora in modalità compatibile basta eliminare le porzioni di


codice che gesticono gli interrupt a bassa priorità. Nelle routine di interrupt,
infine, come sui pic12/16 dovremo mettere una serie di IF per verificare quale
particolare interrupt ha causato il salto alla ISR. Si controlleranno quindi i vari
flag di interrupt -IF. Appena finito di servire l’interrupt, dovremo ricordarci
come sempre di azzerarne il flag.

Gestione degli interrupt sui pic a 16bit (PIC24 e dsPIC)


I pic a 16 bit (PIC24 e dsPIC) hanno una moltitudine di interrupt vectors a
partire dalla locazione 0x000004. A differenza dei pic ad 8 bit, qui ogni
sorgente di interrupt, sia esterna che interna, ha il proprio esclusivo interrupt
vector. Chi è abituato con i pic ad 8 bit si trova davanti ad una cosa nuova. Per
questi pic abbiamo una Interrupt Vector Table (IVT) nella quale sono
contenuti tutti i vettori di interrupt. La tabella è possibile trovarla nel
datasheet del dispositivo o nel family reference manual:

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

5 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

estratto dal dsPic33 Family Reference Manual - Parte 1, sezione 6

Chiariremo dopo il significato dei valori delle colonne di questa tabella.

Apparentemente riuscire a gestire oltre un centinaio di sorgenti di interrupt su


questi pic (ben 126!) potrebbe apparire una cosa molto complicata. In realtà la
gestione degli interrupt sui pic a 16bit è ancora più semplice rispetto a quella
dei cugini ad 8 bit; il C30 ci viene in aiuto con una funzione di interrupt per
ogni richiesta di interruzione: non avremo più, quindi, un’unica ISR (o due
come nel caso dei pic18) all’interno della quale dovremo andare a discernere
l’interrupt che si è verificato bensì tante funzioni di interrupt separate.
Vedremo tra poco come tutto questo si traduce in codice.

Lo stack, inoltre, sui pic a 16 bit è gestito in maniera completamente diversa e


non ha un limite fisso. Abbiamo visto che sui pic18 lo stack è gestito via
hardware mediante una pila di 32 registri e uno stack pointer. Sui dsPic e
pic24 lo stack è gestito via software nella memoria RAM a partire dalla
locazione 0x0800 (appena al di sotto dell’area dedicata agli SFR) e il suo limite
viene imposto tramite il registro SPLIM. La funzione di stack pointer è assolta
dal working register W15.

Le funzioni di interrupt e la parola chiave __attribute__

La scrittura delle funzioni di interrupt sul C30 utilizza una parola chiave
facente parte del set avanzato di istruzioni del C Ansi: __attribute__ seguita da
uno o più attributi separati da virgole inclusi in parentesi tonde doppie.
Questa parola chiave serve per specificare degli attributi particolari associati
alle funzioni (o alle variabili) che ne fanno utilizzo. Per farla breve, la funzione
di interrupt associata al Timer1 su un pic a 16 bit, utilizzando MPLAB C30,
andrà scritta come:

void __attribute__((interrupt)) _T1Interrupt(void)


{
// funzioni da eseguire sull'interrupt del Timer1
}

Con le parole chiave __attribute__((interrupt)) stiamo specificando che questa


non è una normale funzione, bensì è una funzione da richiamare in
automatico al verificarsi del particolare interrupt che abbiamo specificato
dopo: _T1Interrupt.

L‘identificatore _T1Interrupt è associato all’interrupt vector dell’overflow sul


Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

6 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

estensione .GLD anzichè .LKR) del pic in questione.

Ad esempio, per il dsPIC33FJ128GP802 il file di linker si trova in:

C:\programmi\Microchip\MPLAB C30\support\dsPIC33F\gld\

ed ha più o meno lo stesso nome del dsPIC in questione: p33FJ128GP802.gld.


Andando ad aprire tale file con un normale editor di testo (io consiglio sempre
Notepad++), vediamo che da riga 236 partono le definizioni della interrupt
vector table.

A parte i primi 8 interrupt che sono particolari e di cui parlerò dopo, possiamo
individuare dei nomi abbastanza semplici da capire, l’unica differenza è che in
questo file i vettori di interrupt sono indicati con due underscore prima del
nome, mentre noi nel codice ne andremo a mettere uno solo. Dal momento
che consultare il file di linker è abbastanza noioso, l’elenco dei nomi
mnemonici associati ai vettori di interrupt è anche contenuto nella guida
utente dell’ MPLAB C30 (capitolo 8): vedete che sono riportati i nomi
mnemonici per le varie famiglie di pic a 16bit.

La guida utente dell’ MPLAB C30, come tutte le guide dei


compilatori, si trova nella cartella docs del percorso di
installazione.

Ogni interrupt è identificato anche da un numero, indicato in tabella come


IRQ# (Interrupt Request number): difatti gli interrupt li possiamo chiamare,
oltre che con il loro nome mnemonico, anche come _Interrupt72 ad esempio. Il
numero di IRQ, inoltre, stabilisce anche un certo livello di priorità naturale
come vedremo dopo.

Nota per gli “anziani”: ricordate quando sui vecchi 386/486


ecc per far funzionare la scheda audio nei giochi dovevamo
per forza settare manualmente gli IRQ? Se due dispositivi
condividevano lo stesso numero di IRQ ecco che qualcosa non
funzionava…

La tabella interrupt alternativa

Al disotto dello spazio di memoria occupato dalla IVT, fisicamente, c’è la AIVT
(Alternate Interrupt Vector Table). La tabella alternativa per gli interrupt viene
utilizzata per applicazioni particolari o in fase di test/debug. Settando il bit
ALTIVT nel registro INTCON2 tale tabella viene abilitata con la conseguenza
che gli interrupt non saranno più serviti dalle funzioni che utilizzano i vettori
della IVT ma da quelle che utilizzano i vettori della AIVT (ogni interrupt ha
difatti un vettore sia nella IVT che nella AIVT).

Il vettore della AIVT viene identificato con il prefisso Alt- dopo l’underscore.
Volendo possiamo quindi definire, nello stesso programma, due ISR per lo
stesso interrupt:

void __attribute__((interrupt)) _T1Interrupt(void)

void __attribute__((interrupt)) _AltT1Interrupt(void)

ovviamente di default al verificarsi dell’interrupt sul timer1 verrà eseguita


unicamente la prima funzione, se invece mettiamo a 1 il bit ALTIVT verrà
eseguita unicamente la seconda.

Macro per le ISR

Ricordarsi ogni volta di scrivere __attribute__((interrupt)) è abbastanza


Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

7 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

scrivere le funzioni di interrupt come:

void _ISR _T1Interrupt(void)

vediamo difatti che nel file header di un qualsiasi pic a 16 bit è appunto
definita la macro:

#define _ISR __attribute__((interrupt))

che ci permette di scrivere la stessa cosa ma in maniera più corta!

Le ISR “rapide”

Quando si verifica un interrupt, sappiamo che la CPU deve salvarsi lo stato di


tutti i registri vitali (in questo caso i 4 registri di lavoro e il registro di stato SR)
in maniera tale da poter ripristinare la situazione operativa alle condizioni
precedenti una volta che l’interrupt è terminato.

I pic a 16bit, come i pic18, hanno un set di registri shadow da utilizzare per il
salvataggio rapido dello stato. Utilizzando tali registri l’interrupt viene servito
nella maniera più rapida possibile. Per sfruttare questa caratteristica si
aggiunge un altro attributo all’isr che serve appunto a specificare la volontà di
utilizzare questi registri shadow:

void __attribute__((interrupt,shadow)) _T1Interrupt(void)

oppure utilizzando la macro _ISRFAST:

void _ISRFAST _T1Interrupt(void)

Il set di registri shadow è uno solo per cui una, ed una sola, routine di
interrupt può utilizzare questa caratteristica. In altre parole, se nel nostro
programma intendiamo gestire più di un interrupt, una sola ISR potrà fare uso
della _ISRFAST e tutte le altre dovranno usare la normale _ISR.

I livelli di priorità e l’annidamento

Abbiamo visto che i pic18 hanno due livelli di priorità. Sui pic a 16 bit ne
abbiamo ben 7 impostabili per i “nostri” interrupt. Le cose quindi si stanno
complicando! Anche qui se si verificano contemporaneamente due interrupt a
diversi livelli di priorità, la CPU servirà prima quello a priorità più alta. Nel caso
in cui si verifichino contemporaneamente due interrupt allo stesso livello di
priorità, verrà servito per primo quello con l’IRQ più basso, ecco perchè prima
parlavo di “priorità naturale”.

Sui pic a 16 bit anche la CPU ha un suo livello di priorità che ha la funzione di
mascherare gli interrupt indesiderati: gli interrupt che hanno un livello di
priorità minore o uguale al livello di priorità impostato per il processore (IPL),
vengono ignorati.

Ad esempio se IPL si trova a 4, verranno serviti soltanto gli interrupt con un


livello da 5 a salire. Al powerup IPL si trova a zero e il livello di priorità di tutti
gli interrupt è posto a 4, per cui di default tutti gli interrupt vengono serviti.

Il livello di priorità del processore è impostabile manualmente via software


agendo sui bit IPL0,IPL1,IPL2 del registro SR (CPU Status Register):

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

8 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

di ogni pic a 16bit):

SET_CPU_IPL(ipl) // imposta ad ipl il livello di priorità del processore. ipl da 0 a 7

SET_AND_SAVE_CPU_IPL(save_to, ipl) // salva il livello corrente nella locazione di memori

RESTORE_CPU_IPL(saved_to) // reimposta il livello di priorità a quello salvato nella loca

Il livello di priorità degli interrupt, invece, viene settato nei registri IPCx. Ad
esempio, per l’interrupt su Timer1, il livello di priorità viene impostato nel
registro IPC0 agendo sui bit <14:12>. Il livello di priorità degli interrupt può
variare da 1 a 7 (7 livelli), non può quindi assumere valore 0: impostare a zero
il livello di priorità di un interrupt equivale a disabilitarlo.

La descrizione dei registri IPCx purtroppo non si trova nel datasheet ma nel
Family Reference Manual della famiglia del pic a 16 bit in uso (per i dsPic33
fare riferimento al dsPIC33 Family Reference Manual – Part 5 – Section 47, per i
pic24F fare riferimento al PIC24F Family Reference Manual – Section 8).

Ricordo di nuovo che ponendo livello di priorità del


processore (IPL) a 0, tutti gli interrupt vengono serviti in
quanto i livello di priorità degli interrupt vale minimo 1.

I pic a 16 bit supportano l’annidamento degli interrupt: se il processore sta


servendo un interrupt ad un certo livello di priorità e durante queste
operazioni si verifica un interrupt ad un livello di priorità più alto, l’esecuzione
dell’interrupt a livello più basso viene messa in pausa per poter servire
l’interrupt a livello alto. Questo comportamento è quello di default e può
essere alterato settando il bit NSTDIS del registro INTCON1. Mettendo ad 1 il
bit NSTDIS (Nested Interrupt Disable), gli interrupt vengono serviti in
sequenza.

In altre parole, ponendo NSTDIS a 1, un interrupt, anche se ha un livello di


priorità più alto di quello correntemente servito, dovrà attendere che
l’interrupt precedente sia stato completato. Tuttavia nel caso in cui si
verifichino contemporaneamente due interrupt, verrà servito per prima quello
a priorità più alta e quindi il livello di priorità, in questo particolare caso, viene
sfruttato unicamente per risolvere eventuali conflitti.

Il processore realizza questa funzione ponendo in automatico a 7 il livello di


priorità del processore non appena un interrupt si verifica ed inizia ad essere
servito: in questo modo se si verificano altri interrupt durante questo tempo,
non saranno serviti. IPL viene resettato non appena l’interrupt corrente cessa
di essere servito consentendo alla CPU di catturare gli altri interrupt. Con
NSTDIS a 1, inoltre, IPL può essere soltanto letto ma non scritto.

Impostare manualmente IPL a 7 causa la disattivazione degli


interrupt in quanto il livello di priorità dei normali interrupt
non può essere superiore a 7 e un interrupt per essere servito
deve avere un livello di priorità superiore a quello del
processore.

Possiamo assumere che con NSTDIS=1 il comportamento è simile a quello dei


pic16.

Abilitare e rilevare gli interrupt sui pic a 16bit

Sui pic a 16bit, come sugli altri pic, ogni interrupt può essere
attivato/disattivato tramite il proprio bit di abilitazione (che ha suffisso -IE).
Però qui la situazione è più ordinata: i bit di abilitazione si trovano nei registri
IECx, quindi non c’è più distinzione tra interrupt “normali” e interrupt di
periferica.

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

9 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

che indica se l’interrupt si è verificato o meno. I flag di avvenuto interrupt


hanno il suffisso -IF, si trovano nei registri IFSx, e vengono settati comunque
anche se l’interrupt non è stato abilitato.

La descrizione dei registri IECx e IFSx purtroppo non si trova nel datasheet ma
nel Family Reference Manual della famiglia del pic a 16 bit in uso (per i
dsPic33 fare riferimento al dsPIC33 Family Reference Manual – Part 5 – Section
47, per i pic24F fare riferimento al PIC24F Family Reference Manual – Section
8).

I flag di interrupt anche qui vanno azzerati non appena l’interrupt è stato
servito, altrimenti non si esce mai dalle ISR. Nel caso del timer1, quindi, la
routine di interrupt sarà:

void _ISR _T1Interrupt(void)


{
// istruzioni
_T1IF=0; // azzero il flag di interrupt su Timer1
}

A differenza dei pic di fascia inferiore, qui non abbiamo il bit GIE che permette
il mascheramento degli interrupt (nè tantomeno un bit PIE per abilitare gli
interrupt di periferica): ogni interrupt ha il suo bit di abilitazione, punto e
basta. Agendo sui livelli di priorità, però, dal momento che nessun interrupt
può avere priorità 7, basta mettere a 7 il livello di priorità del processore per
mascherare gli interrupt.

Esiste inoltre un’istruzione assembler (DISI) che permette di disabilitare tutti


gli interrupt per un determinato numero di cicli:

asm volatile ("disi #0x3FFF");

in alternativa è possibile utilizzare una builtin:

__builtin__disi(0x3FFF);

Questa istruzione in pratica disabilita gli interrupt per i prossimi 0x3FFF


(16383) cicli macchina (valore massimo). L’istruzione DISI ferma
temporaneamente solo gli interrupt aventi livelli di priorità da 1 a 6, gli
interrupt con livelli superiori non vengono fermati.

Se i cicli macchina impostati non sono ancora passati ma vogliamo comunque


ripristinare gli interrupt, si può farlo azzerando il registro di conteggio dei cicli
macchina dopo i quali riattivare gli interrupt (DISICNT : Disable Interrupt
Control Register):

DISICNT=0;

Il registro DISICNT si trova normalmente a zero, se lo impostiamo su un valore


diverso da zero (operazione che in pratica viene eseguita dall’istruzione DISI),
gli interrupt vengono disabilitati temporaneamente e il contatore esegue un
conto alla rovescia man mano che i cicli macchina procedono; arrivato a zero
gli interrupt vengono riabilitati. L’eventuale entrata in funzione dell’istruzione
DISI è anche indicata dal bit DISI nel registro INTCON2.

Questa istruzione diventa necessaria nei punti in cui modifichiamo al volo i


livelli di priorità nel nostro programma, in quanto durante queste operazioni
gli interrupt devono essere temporaneamente disattivati oppure può tornare
utile in quei punti del programma in cui sono richieste operazioni critiche che
richiedono di essere eseguite in un certo tempo e quindi non devono essere
interrotte.

Le trappole

Quando abbiamo visto la IVT vi ho anticipato che i primi 8 interrupt sono


particolari. Questi interrupt vengono chiamati trappole (traps) e sono utilizzati
per catturare le condizioni di errore del processore o malfunzionamenti
(guasto dell’oscillatore, operazioni matematiche errate ecc).
Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

10 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

e compromettono il funzionamento della nostra applicazione, le trappole


hanno un livello di priorità al di sopra di tutti gli altri interrupt (hanno un
livello da 8 a 15), non vengono mai disattivati dall’istruzione DISI e non
vengono rallentati dal flag NSTDIS (passano sempre avanti). Le trappole
sfruttano comunque l’annidamento perchè è ovvio che una condizione di
allarme a priorità più alta ne scavalchi una a priorità più bassa. Le trappole
vengono anche dette interrupt non mascherabili.

Dal momento che le trappole hanno un livello di priorità da 8 a 15, oltre ai 3


bit IPL visti prima (che possono quindi contenere un numero fino a 7), c’è
anche un bit IPL3 nel registro CORCON, che serve appunto a rilevare questa
condizione di interrupt particolare (direi più condizione di allarme). Il bit IPL3,
difatti, a differenza degli IPL0:2, può solo essere letto o resettato ma non
posto a 1.

La trappola a priorità più alta è il guasto dell’oscillatore (sebbene sopra di lei


ci sia una trappola attualmente inutilizzata, probabilmente prevista per usi
futuri). La trappola del controller DMA (livello 10), la trappola di errore
matematico (livello 11) e la trappola di errore dello stack (livello 12) sono
anche dette soft traps, le altre sono dette hard traps. La differenza sta nel
fatto che le trappole “hard” forzano la CPU ad interrompere l’esecuzione del
codice appena dopo che l’istruzione che ha causato la trappola viene
completata, mentre le trappole soft si prendono un ciclo in più per confermare
la condizione di errore.

MPLAB C30 associa, di default, le trappole ad una routine di reset del


processore ma nulla vieta di dirottare la gestione delle trappole scrivendo
delle nostre routine come per i normali interrupt. Anche le trappole, quindi,
hanno il loro vettore alternativo nella AIVT.
Quando una trappola causa il reset del processore viene settato il bit TRAPR
(Trap Reset Flag) del registro RCON (Reset Control register).

Come vedete sui pic a 16bit la gestione degli interrupt è molto raffinata ed
orientata alla massima sicurezza.

Bibliografia
Manuale HI-TECH C Tools for the PIC10/12/16 MCU Family
Corso C18 step by step di Mauro Laurenti – www.laurtec.it€ (seconda
edizione, pag. 144)
MPlab C compiler for PIC24 MCUs and dsPIC DSCs user’s guide (cap. 8)
PIC18F to PIC24F Migration: An Overview
dsPIC33F Family reference manual (parte 5 – sezione 47 – interrupts part
V)
PIC24F Family reference manual (sezione 8 – interrupts)
Programming 16-bit microcontrollers in C – Learning to fly with the PIC24,
Lucio di Jasio (cap. 5)

Articoli che potrebbero interessarti


Corso programmazione PICMicro in C – Lezione 4 – Cosa sono gli
interrupt, concetti di base per sistemi operativi MultiTasking su PICMicro
Adattatore multizoccolo per PICkit 2
Come scegliere il pic più adatto al nostro progetto facendo anche
attenzione al package
Scheda di sviluppo per dspic / pic24 in formato DIP28
Rimappare le periferiche sui pic a 16bit (dsPic/PIC24). La funzione
Peripheral Pin Select (PPS)

Like 6 people like this. Be the first of your friends.

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

11 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

L'articolo ti è piaciuto o ti è stato utile per risolvere un problema?


SettoreZero è realizzato soltanto con contenuti originali: tutti gli
articoli sono curati con passione dagli autori e nulla viene copiato da
altri siti. Supporta e mantieni in vita SettoreZero con una donazione:
basta soltanto un caffè o una birra. Puoi supportare SettoreZero anche
con uno dei progetti elencati nella sezione servizi o partecipare anche
tu con un tuo articolo/progetto personale.

Se desiderate che SettoreZero continui a rimanere gratuito e fruibile da


tutti, non copiate il nostro materiale e segnalateci se qualcuno lo fa.

Puoi andare alla fine dell'articolo e lasciare un commento. I trackback e i ping non sono
attualmente consentiti.

COMMENTI (15) TRACKBACKS (1)

#1 da Giovanni il 13 dicembre 2010

Buone e utile l’articolo. Se posso ti faccio un’osservazione. Io utilizzo Hitech C e la


dimensione del codice di interrupt viene gestito automaticamente dal compilatore.
Non come sembta nel tuo articolo quando affermi che la lunghezza del codice ISR ad
alta priorità non può superare l’intervallo di indirizzi 0x0008 – 0x0018. Questo forse
vale per assembler e forse per PIC c 18.
Oppure ho capito male io quanto hai scritto?

#2 da Giovanni Bernardo il 13 dicembre 2010

Hai scritto la stessa cosa che ho detto io o forse non hai letto bene
l’articolo. Sui PIC12 e PIC16 (e quindi con il PICC) ho scritto che
problemi di spazio non ce ne sono perchè dopo il vettore di
interruzione viene tutta la memoria programma. Il problema sta sui
PIC18 (e di conseguenza con il C18) che hanno un range limitato per
l’interrupt ad alta priorità che parte da 0x08 però poi, dopo 0x10
locazioni c’è il vettore di bassa priorità… quindi o non ci
“allarghiamo” o usiamo un’istruzione goto per ridirezionare le
istruzioni… L’istruzione goto ce la metti tu, il compilatore poi
gestisce l’allocazione del tutto e quindi pure qua, a questo punto,
problemi di spazio non ce l’hai. Ma ti devi ricordare di usare il goto.
Questo com MPLAB C18. L’hitec per PIC18 non lo so come si
comporta, comunque sia ho specificato all’inizio che per i pic18
faccio riferimento a MPLAB C18.

#3 da Haru il 14 marzo 2011

Ciao, ho una piccola richiesta: potresti fare un esempio di interrupt per i pic16? Non
ho capito come rilevare “cosa” ha generato l’interrupt, e come farlo capire al
programma stesso.

Nel senso, se il programma prevede un timer con interrupt e interrupt abilitati su tutta
la portB, come fà il programma a capire se l’interrupt è causato da un timer o da una
porta? ci vogliono delle if o è meglio controllare lo stato dei registri in assembly?

#4 da Giovanni Bernardo il 14 marzo 2011

Si controlla con una serie di IF sui pic a 8 bit. I pic a 16 bit hanno una
funzione distinta per ogni interrupt. Nelle lezioni di programmazione
dei picmicro hai voglia di esempi di interrupt che ci sono, io uso
esclusivamente gli interrupt per ogni cosa.

#5 da Haru il 14 marzo 2011

non ci avevo fatto caso, grazie.

#6 da Stefano il 4 maggio 2011

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

12 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

saluti a tutti.
Correggimi se sbaglio, in questo capitolo spieghi che al momento dell’initerrupt il pic
salva in una memoria temporanea il contenuto una serie di registri tra i quali il PC. Ma
non tutti pic lo fanno, quelli più obsoleti registrano solamente il PC, come il 16F877A.
Leggendo nel datascheet anche il più recente 16F887 a quanto pare non salva
automaticamente gli altri registri.
Quindi sarà il compilatore stesso a provvedere a questi salvataggi con la funzione
“interrupt” che userermo poi nel nostro programma?

#7 da Giovanni Bernardo il 5 maggio 2011

Bastava dare un occhio al manuale dell’Hitech-C. Il manuale dice che


su questi pic che salvano solo PC, è il compilatore, in base al
contenuto delle istruzioni nella ISR, a decidere se aggiungere o meno
delle istruzioni che prevedono il salvataggio di altri registri chiave:

Enhanced Mid-Range PIC devices save the W, STATUS, BSR and FSRx
registers in
hardware (using special shadow registers) and hence these registers
do not need to
be saved by software. In fact, the compiler will never have to produce
code to save any
other registers when compiling for an Enhanced Mid-Range as no
additional registers
are ever used. This makes interrupt functions on Enhanced
Mid-Range PIC devices
very fast and efficient.

Other Mid-Range PIC processors only save the entire PC (excluding


the PCLATH register)
when an interrupt occurs. The the W, STATUS, FSR and PCLATH
registers and
the BTEMP1 pseudo register must be saved by code produced by
the compiler, if
required. The compiler fully determines which registers and objects
are used by an interrupt function,
or any of the functions that it calls (based on the call graph
generated by the compiler),
and saves these appropriately.

#8 da Romeo Sibau il 16 dicembre 2011

Ciao, complimenti per il sito e tutte le informazioni utili che ci sono.


Non sono un’abituè dei forum e tento generalmente di risolvere da me i problemi che
mi si presentano ma questa volta davvero non ne esco.
Trovando questo articolo molto ben fatto mi sono deciso a chiedere aiuto.
Spiego brevemente ciò che devo fare e con quale Pic, allora, la mia applicazione deve
catturare, utilizzando tassativamente gli interrupt due impulsi uno su RB0 e l’altro su
RB1, non è importante se sul fronte di salita o sul fronte di discesa.
Avendo già utilizzato in precedenti applicazioni gli interrupt da periferica ritenevo
erroneamente che la gestione degli stessi avesse la stessa semplicità, anche per quelli
esterni … mi sono dovuto ricredere.
Sto utilizzando un PIC 18F87J60 che mi sono ritrovato su una scheda di sviluppo, che
mi permette di simulare attraverso i soliti pulsanti la chiusura dei contatti che
dovrebbero scatenare l’interrupt.
Dopo vari tentativi e diverse configurazioni sono approdato al codice pubblicato su
questo sito e ne ho preso spunto per scrivere quanto segue :

#include
#include

// Variabili globali
char InterruptRB0;
char InterruptRB1;
char InterruptRB2;
char InterruptRB3;
// prototipi di funzione

void interrupt_priorita_bassa(void);
void interrupt_priorita_alta(void);

#pragma code vettore_alta_priorita = 0x08


void ISRH(void)
{
// imposto, in assembler, un salto alla mia routine di
// gestione dell’interrupt a priorità alta
_asm GOTO interrupt_priorita_alta _endasm
}

#pragma code vettore_bassa_priorita = 0x18


void ISRL(void)
{
_asm GOTO interrupt_priorita_bassa _endasm
}

#pragma code

#pragma interrupt interrupt_priorita_alta


void interrupt_priorita_alta(void)

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

13 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

if (INTCONbits.INT0IF==1) {
InterruptRB0=1;
INTCONbits.INT0IF=0;
}
if (INTCON3bits.INT1IF==1) {
InterruptRB1=1;
INTCON3bits.INT1IF=0;
}
}

#pragma interruptlow interrupt_priorita_bassa


void interrupt_priorita_bassa(void)
{
// codice per gestire interrupt a priorità bassa
// Bassa priorità RC6 : RC7

if (INTCON3bits.INT2IF==1) {
InterruptRB2=1;
INTCON3bits.INT2IF=0;
}
if (INTCON3bits.INT3IF==1) {
InterruptRB3=1;
INTCON3bits.INT3IF=0;
}
}

void setup (void){

TRISC = 0b00000000; // Tutti output


PORTC = 0b00000000; // Reset della porta C

TRISB = 0b11111111;

INTCONbits.INT0IE = 1; //Sempre ad alta priorità


INTCON2bits.INTEDG0=1; // Rileva interrupt sul fronte di salita

INTCON3bits.INT1IE= 1;
INTCON3bits.INT1IP= 1; //Abilito interrupt ad alta priorità
INTCON2bits.INTEDG1=1; // Rileva interrupt sul fronte di salita

INTCON3bits.INT2IE= 1;
INTCON3bits.INT2IP= 0; //Abilito interrupt a bassa priorità
INTCON2bits.INTEDG2=1; // Rileva interrupt sul fronte di salita

INTCON3bits.INT3IE= 1;
INTCON2bits.INT3IP= 0; //Abilito interrupt a bassa priorità
INTCON2bits.INTEDG3=1; // Rileva interrupt sul fronte di salita

InterruptRB0 = 0;
InterruptRB1 = 0;
InterruptRB2 = 0;
InterruptRB3 = 0;

RCONbits.IPEN=1;
INTCONbits.RBIE=0;
INTCONbits.GIEH=1;
INTCONbits.GIEL=1;
INTCONbits.GIE=1;
}

void main(void) {
setup();

while (1) {

if (InterruptRB0 == 1) {
PORTCbits.RC0=1;
Delay10TCYx(1);
PORTCbits.RC0=0;
InterruptRB0 = 0;

if (InterruptRB1 == 1) {
PORTCbits.RC1=1;
Delay10TCYx(1);
PORTCbits.RC1=0;
InterruptRB1 = 0;
}

if (InterruptRB2 == 1) {
PORTCbits.RC6=1;
Delay10TCYx(1);
PORTCbits.RC6=0;
InterruptRB2 = 0;
}

if (InterruptRB3 == 1) {
PORTCbits.RC7=1;
Delay10TCYx(1);
PORTCbits.RC7=0;
InterruptRB3 = 0;

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

14 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

La conclusione è che simulando con MPASM tutto funziona correttamente mentre se


carico il tutto sul PIC non succede assolutamente nulla.
Qualcuno è in grado di dirmi se sbaglio qualcosa nella sezione setup() ?
Ringrazio anticipatamente per eventuali suggerimenti.

#9 da Giovanni Bernardo il 16 dicembre 2011

Delay nell’interrupt?

#10 da Romeo Sibau il 16 dicembre 2011

Mancava una piccola precisazione forse: si tratta


solo codice di prova, il delay mi serve solo per
accendere e spegnere un led per dimostrare che
l’interrupt è avvenuto

#11 da Giovanni Bernardo il 16


dicembre 2011

ah aspetta ma i delay non li hai


messi nell’interrupt… ma perchè
l’hai strutturata in questo modo?
Metti le funzioni da eseguire
nell’interrupt e non con tutto
questo giro di flag

#12 da nmira il 16 gennaio 2012

ciao a tutti,avrei un quesito da sottoporvi ,sono in fase di progettazione di un


emulatore di un vecchio lettore di nastri dati utilizzando chipkit max32,sono a buon
punto avendo già realizzato attraverso Mpide 0023 una soluzione funzionante
effettuando il polling delle porte interessate.
Purtroppo mi sono accorto di perdere delle preziose informazioni che dovrebbero
essere lette dopo max 100-200 ns.
Quale potrebbe essere il suggerimento per avere il minor tempo di latenza possibile
modificando il codice utilizzando un interrupt?

#13 da Giovanni Bernardo il 19 gennaio 2012

Ciao,
la tua idea è davvero molto interessante. C’è il Commodore64 di
mezzo? Utilizzare gli interrupt può aiutarti a rendere il tutto più
efficiente ma non più veloce. Il MAX32 può girare a ben 80MHz con
un tempo per ciclo di 12.5nS … mi sembra strano che non ti basti
anche se in realtà non ho idea delle velocità che ci sono in gioco. Stai
loggando il progetto da qualche parte così che gli possa dare un
occhio?

#14 da nmira il 21 gennaio 2012

grazie per la risposta,la scheda che utilizzava il lettore che devo emulare utilizza uno
Z80 a 4.8Mhz e fa parte di un vecchio dispositivo tipo CNC che devo far ripartire per
riprendere i vecchi programmi memorizzati sui nastri (molto simili alle vecchie
cassette audio) ed eventualmente ripristinare il macchinario collegandolo ad un
Pc.Avevo iniziato il lavoro con Arduino e poi avendo notato che la velocità del polling
era troppo bassa ho cercato e mi sono imbattuto nel chipkit Max32.Ho dovuto
comunque cambiare la frequenza dello Z80 (2Mhz max) per poter avere un risultato
soddisfacente, purtroppo perdo dei collegamenti ad altri dispositivi collegati in RS232
e questo mi penalizza.Il progetto è molto semplice, ho due porte da 8bit,una di
comandi e l’altra di dati,a seconda di quando viene selezionato il dispositivo vengono
scatenate delle risposte che abilitano la scrittura o la lettura dei
ti posto il codice del programma,scusate gli orrori ma sono abituato ad altri sistemi di
programmazione che non hanno problemi di RAM,STACK ecc..
il ciclo di selezione avviene ad una frequenza di circa 180khz per la durata di
1microsec,all’interno del quale devo attendere il fronte di discesa del clock per il
sincronismo di lettura scrittura dati.

boolean tea = false;


boolean emulator=false;
boolean firstTime=false;

boolean client=false;
unsigned char c=0x0;

//———————————————————
// definizione degli output per comandi
//———————————————————

const int dma_ack = 44;


const int dma_req = 45;
const int G = 46;
const int DIR = 47;
const int MAX_BUFF_WRT =1024;
byte dato=0;
Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

15 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

byte dataRead[0xff];
byte indexItr=0;
boolean comunicazione=false;//false =seriale true =tcp
boolean ciclo=true;
byte bufSerialeWrite[MAX_BUFF_WRT];
byte bufSerialeWriteTot[MAX_BUFF_WRT];
byte bufSerialeRead[MAX_BUFF_WRT];
byte bufSerialeReadTot[MAX_BUFF_WRT];
byte bufRicezione[100];
static long contatore=0;
static boolean controllo=true;
byte i=0;
byte tipoOperazione=0;
byte type=2;
byte inByte=0x0;
byte indice=0x0;
byte maxElementi=8;
byte elemento=0;

void setup()
{
// disabilitazione ingressi analogici
AD1PCFG=0xffff;
// porta dati
TRISE=0xff;
//porta comandi
TRISB=0xff;
pinMode(G,OUTPUT);
pinMode(DIR,OUTPUT);
digitalWrite(G,HIGH);
digitalWrite(DIR,HIGH);
pinMode( dma_ack,OUTPUT);
pinMode(dma_req,INPUT);
memset(bufSerialeWrite,0x0,MAX_BUFF_WRT);
bufSerialeWrite[0]=0xdb;
bufSerialeWrite[51]=0xfb;
memset(bufSerialeRead,0x0,MAX_BUFF_WRT);
bufSerialeRead[0]=0xdb;
bufSerialeRead[51]=0xfb;
memset(bufSerialeWriteTot,0x0,MAX_BUFF_WRT);
memset(bufSerialeReadTot,0x0,MAX_BUFF_WRT);
memset(bufRicezione,0x0,sizeof(unsigned char)*100);
Serial.begin(115000);
Serial.flush();
digitalWrite( dma_ack,HIGH);
memset(dataRead,0x0,0xff);
memset(data,0x0,0xff);
}

void loop()
{

while (ciclo)
{
if(!firstTime)
{
Version(2);
firstTime=true;
digitalWrite(G, HIGH);

memset(bufRicezione,0x0,sizeof(unsigned char)*100);
char myString[] = “**** READY ****”;
memcpy(bufRicezione,myString,strlen(myString));
Serial.flush();
Serial.write(bufRicezione,100);
delay(100);
memset(bufRicezione,0x0,sizeof(unsigned char)*100);
}
dato=0;

int a=Serial.available();

if(a >99)
{
for(byte ricezione=0;ricezione<a;ricezione++)
{
bufRicezione[ricezione]=Serial.read();
}
Serial.flush();

c = bufRicezione[0];

if (c == 0x41 && !emulator)//A


{
digitalWrite(G, HIGH);
delay(200);
pinMode( dma_ack,INPUT);
TRISB=0xf7;
TRISE=0xff;
emulator=true;
Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

16 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

char myString[] = "**** EMULATOR ****";


memcpy(bufRicezione,myString,strlen(myString));
Serial.flush();
Serial.write(bufRicezione,100);
delay(100);
}

if(c == 0x71 )//q


{

digitalWrite(G,HIGH);
char myString[] = "**** END TAPE ****";
memcpy(bufRicezione,myString,strlen(myString));
Serial.flush();
Serial.write(bufRicezione,100);
delay(200);
Serial.end();
ciclo=false;
tea=false;
emulator=false;
}

if(!tea)
{

if (c == 0x43 && emulator)//C


{

controllo=true;
contatore=0;
Leggo(dato);
delay(1500);

// azzera i buffer globali


if ((c == 0x25) && emulator)
{
memset(bufSerialeWriteTot,0x0,MAX_BUFF_WRT);
memset(bufSerialeReadTot,0x0,MAX_BUFF_WRT);
memset(bufRicezione,0x0,sizeof(unsigned char)*100);
}

// ritorna il cmd inviato x verificare che l'operazione precedente sia conclusa


if ((c == 0x20) && emulator)
{
Serial.flush();
Serial.write(bufRicezione,100);
memset(bufRicezione,0x0,sizeof(unsigned char)*100);
}

}
Serial.println("**** !!!!!!!!!!!!!! ****");
delay(10);
Serial.end();

}
/////////////////////////////////////////////////////////////
void Version(byte flag)
{
byte i=0x0;
digitalWrite(G, HIGH);
for (i=0x0;i<=flag;i++)
{
//Clock();
digitalWrite(DIR, LOW);
delay(100);
digitalWrite(DIR, HIGH);
delay(400);
}
digitalWrite(DIR, LOW);
memset(bufRicezione,0x0,sizeof(unsigned char)*100);
char myString[] = "**** Ver. 1.0 ****";
memcpy(bufRicezione,myString,strlen(myString));
Serial.write(bufRicezione,100);
delay(500);

//////////////////////////////////////////////////////////////////////////
void Leggo(byte flag)
{
byte val=0x0;
byte slk=0x0;
byte ck=0x0;
boolean dt=false;
byte asa=0x0;

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

17 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

int indexBuf=0;
int ix=0x0;
byte date[1024];
byte sette=0x0;
boolean datoPreparato=false;
boolean dueCk=false;
int i=0;
memset(date,0x0,1024);
if(controllo)dt=false;
fISR=2;

do
{
do
{
val=PORTB;
slk=(val&0x20);
if(!controllo)contatore++; //controllo in caso di errore

}while((slk==0x20)&&(contatore<200000)); // fronte discesa bit selezione

if(contatore=200000)
{
ck=1;
dt=true;
}

if((!ck )&&(flago))
{
flago=false;
date[ix]=val;
ix=ix+1;
if(val==0x83)//cassette status register
{
TRISE=0x00;//dati in lettura
PORTE=0xff;
}

if(val==0x81)//Isr
{
TRISE=0x00;//dati in lettura

if(fISR==1)
{
PORTE=0x7f;
if(flag==7)dt=true;
}
}

if(val==0x95)//cmd
{
TRISE=0xff; //dati in scrittura
asa=PORTE;
if(asa==0x3e)
{
fISR=1;
}
date[ix]=asa;
ix=ix+1;
}

if(val==0x97)//cmd
{
TRISE=0xff;
bufSerialeWrite[indexBuf]=PORTE;
indexBuf=indexBuf+1;
if(indexBuf>94)dt=true;
}

do
{
val=PORTB;
ck=(val&0x40);
}while(ck!=0x40); // fronte salita clock

flago=true;
}
while(!dt);
TRISE=0x00;
PORTE=0x0;
TRISE=0xff;

Serial.write(date,100);

/////////////////////////////////////////////////////////////
void IniComandi(void)
{

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

18 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

TRISB=0x8;
digitalWrite(G, LOW);
delayMicroseconds(1);
}
/////////////////////////////////////////////////////////////
void IniDati(void)
{
PORTE=0xff;
delayMicroseconds(1);
}
/////////////////////////////////////////////////////////////
void WrtComandi(unsigned int val)
{
//unsigned int app=0x0;
//app=PORTB>>8;
//PORTB=val|(app << 8);
PORTB=val;
delayMicroseconds(1);
}
//////////////////////////////////////////////////////////
void WrtDati(byte val)
{
PORTE=val;
delayMicroseconds(1);
}
//////////////////////////////////////////////////////////
byte RdDati(void)
{
byte valore=0x0;
valore=PORTE;
//valore=LATSESET;
return valore;

}
/////////////////////////////////////////////////////////////

int Setto_dati(int mode)


{
if(mode==0)
{
TRISE=0xffff;
}
else
{
TRISE=0x00;
}
delayMicroseconds(1);
return 0;
}

#15 da slavin89 il 6 luglio 2012

selve a tutti ho una domanda ma per esporvela vi porto il mio esempio..


allora ho una funzione main che può richiamare a sua volta le funzioni prova1(); e
prova2(); poi ho la funzione interrupt..se io dal main richiamo prova1(); come posso
far lavorare prova1(); assieme all’interrupt?
perchè al richiamo della funzione io andrò a fare tutto ciò che ne è all’interno ma
essendo l’interrupt un’ulteriore funzione non la eseguirà se non nel main.
avevo pensato di copiare il listato all’interno di interrupt all’interno del ciclo in prova1
ma non funziona correttamente forse perchè devono obbligatoriamente lavorare su
due funzioni differenti quindi come potrei fare?
saluti

Devi essere collegato per lasciare un commento.

Commenti recenti Immagini dai post I tags più “gettonati” Archivio articoli
Giovanni Bernardo su ORbit16™ Arduino audio basic board c Archivio articoli
USB Servo Controller chipkit circuito clock code codice Seleziona mese
Massimo Peino su ORbit16™ USB commodore controllo corso data
Servo Controller display eeprom game GPS
fendo su Termometro Wi-Fi con i2c led memoria
lcd
indicazione umidità, microchip modulo mplab
temperatura percepita e
controllo relè mediante ESP8266
net open orbit16 pic
pic24fj64gb002 pic32
waldemar su Montare insieme
due banchi di memoria RAM con
picmicro
Programmazione protocollo
frequenze differenti. Le DDR: pwm rs232 scheda serial
frequenze di clock, tempi di
seriale source tutorial usb
latenza, dual channel vb VB.NET video
eddie84 su Termometro Wi-Fi
Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

19 di 20 25/05/17, 10:08
La gestione degli interrupt sui pic12 - pic16 - pic18 - pic24 - dspic | Set... http://www.settorezero.com/wordpress/la-gestione-degli-interrupt-sui-...

controllo relè mediante ESP8266

settorezero.com e il logo Zroid™ ©2007÷2015 Giovanni Bernardo - E' vietata la copia e la distribuzione anche parziale dei contenuti di questo sito web senza l'esplicito consenso dell'autore.
I contenuti di settorezero.com sono distribuiti sotto una licenza Creative Commons Attribuzione-Non Commerciale-Non Opere derivate 2.5 Italia a cui vanno aggiunte le condizioni d'uso definite nel
disclaimer.
settorezero.com e tutti i suoi contenuti sono tutelati dalla legge sul diritto d'autore per cui i trasgressori sono perseguibili a norma di legge. Settorezero fa uso dei cookie leggi l'informativa estesa.

Il tema di questo sito è basato sul tema Fusion per wordpress, realizzato originariamente da digitalnature e fa uso del plugin Wassup per il computo delle statistiche. Per contattare l'autore siete pregati di
utilizzare la sezione contatti.
Per essere aggiornato con tutte le novità di settorezero.com seguici anche anche su Facebook Twitter Tumblr Blogspot Youtube.

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu
ne sia felice.

20 di 20 25/05/17, 10:08