Questa pagina contiene materiale obsoleto
L'interruzione (interrupt) è il modo utilizzato da un microcontrollore per gestire eventi hardware che avvengono indipendentemente dall'esecuzione del programma principale. Qui una breve descrizione del concetto di interrupt..
Scrivere la funzione che risponde all'interruzione non è difficile. La cosa più difficile è forse convincere una periferica a generare l'interrupt.
Questo programma permette di accendere o spegnere alcuni LED quando viene premuto un interruttore, indipendentemente dall'esecuzione del programma principale. Potete scaricare il codice a fondo pagina. L'hardware utilizzato è quello presente nel PICkit2: quattro LED collegati a PORTC e un interruttore SW1 collegato a RA3.
La prima operazione che deve fare il main() è la configurazione delle periferiche che possono generare interruzioni. Le sorgenti di interrupt in un PIC possono essere molteplici e quindi diversi sono i bit da configurare. La figura seguente mostra come alcuni di questi bit sono connessi affinché un'interruzione sia effettivamente generata. Occorre notare che il PIC16F690 possiede un solo vettore di interrupt e quindi una qualunque interruzione, indipendentemente dalla periferica che l'ha generata, genera un solo Master Interrupt e quindi viene eseguita sempre la stessa funzione C.
IOCA3 = 1; // Enable RA3 pin interrupt; see INTERRUPT-ON-CHANGE PORTA
REGISTER
RABIE = 1; // Enable interrupt on PORTA and PORTB; see INTERRUPT CONTROL
REGISTER
ei(); // Enable all interrupt (GIE = 1)
Con le tre righe precedenti, nell'ordine:
for(;;); // Do nothing...
Il resto del main() non fa assolutamente nulla, per sempre. Evidentemente non è un uso intelligente della potenza di calcolo del PIC...
void interrupt isr(void) // Interrupt Service Routine
{if (RABIF) // Interrupt from PORTA/B ?
{if (!RA3) // If RA3 = 0 (key pressed)
PORTC++; // PORTC increment
RABIF = 0; // Clear PORTA/B interrupt flag
}
if (xxx) {...} //
Check other interrupt sources...
}
Il codice sopra mostrato è quello relativo alla Interrupt Service Routine, riconoscibile per la parola chiave interrupt.
Questa funzione è unica per tutte le sorgenti di interruzione e quindi la prima operazione deve sempre essere quella di individuare se PORTA, in particolare il pin RA3, ha generato l'interruzione.
Una volta individuata la sorgente dell'interruzione, il codice incrementa di uno il numero mostrato dai quattro LED. E' importante, prima del termine della funzione, azzerare il flag corrispondente all'interrupt effettivamente generata altrimenti verrebbe subito generata una nuova interruzione.
Una nota: nel codice allegato non è presente nessuna procedura antirimbalzo dell'interruttore: a volte la pressione di SW1 causa l'esecuzione della funzione più volte e quindi più incrementi dell'uscita.
Nell'esempio seguente l'interruzione al programma principale arriva dal TIMER1, un contatore a 16 bit (0-65535) interno al PIC stesso: ad intervalli costanti di circa 262 ms viene generato un segnale di interrupt che attiva l'esecuzione della funzione isr(). La configurazione del TIMER1 è la parte più delicata del codice ma non è necessario comprenderne subito tutti i dettagli.
Il codice è scaricabile a fondo pagina, pronto per il copia & incolla. Nei commenti ignorerò quanto già decritto helloREALworld.c.
volatile unsigned char counter; // 0-255 counter (updated in ISR)
Questa variabile è definita come volatile perché può essere modificata indipendentemente dall'esecuzione del programma principale: improvvisamente può essere modificata dalla funzione di gestione dell'interrupt. In pratica viene detto al compilatore di disattivare alcune ottimizzazioni del codice.
PIE1 = 0b00000001; // Enable TIMER1 interrupt;
see PERIPHERAL INTERRUPT ENABLE REGISTER 1
INTCON = 0b01000000; // Enables all peripheral interrupt; see INTERRUPT
CONTROL REGISTER
T1CON = 0b00110101; // See TIMER 1 CONTROL REGISTER
// 0 Ignored
// 0 Timer always count
// 1 Prescaler set to 8
// 1 Prescaler set to 8
// 0 Ignored
// x Ignored
// 0 Internal clock
// 1 Timer 1 enabled
Il TIMER1 così configurato ha in ingresso un segnale pari ad un quarto del clock del processore, diviso per 8 dal prescaler. Quando ha contato 65.536 segnali, genera un interrupt. In formule:
ei(); // Enable all interrupt
Le interrupt devo essere attivate solo dopo la configurazione delle periferiche.
for(;;) {PORTC = counter;} // Output counter to LEDs
Questo ciclo infinito non fa altro che prendere la variabile counter e mandarla sulla porta C a cui sono connessi quattro LED: come si vede non è mai modificata da nessuna assegnazione nel main() ma solo dalla funzione seguente:
void interrupt isr(void) // Interrupt routine
{if(
// Check if TIMER1 is the interrupt source
(TMR1IE)&&
// Is TIMER1 interrupt enabled? AND
(TMR1IF))
// Is TIMER1 overflowed?
{counter++;
// YES -> update counter
TMR1IF=0;
// Clear Interrupt Flag of Timer1
}
}
Anche in questo caso prima di terminare la funzione deve essere azzera il flag del TIMER1 che ha generato l'interruzione.
Scarica il codice sorgente (interruttore)
Scarica il codice sorgente (timer)
Esempi simili a questi ma per un diverso processore potete trovarli anche alla pagina Gestire le interruzioni nel PIC18.
PIC16F690 in C - OBSOLETO - Versione 1 - Ottobre 2014
Copyright 2009-2014, Vincenzo Villa (https://www.vincenzov.net)
Quest'opera stata rilasciata con licenza Creative Commons | Attribuzione-Condividi allo stesso modo 3.0 Unported.