Corso programmazione microcontrollori PIC® in C (aggiornamento MPLAB X) – Led lampeggiante su interrupt Timer

Benvenuti alla terza puntata dell’aggiornamento del vecchio Corso di programmazione microcontrollori PIC® in C. In questo post illustro gli aggiornamenti delle vecchie lezioni 4 e lezione 5 che prevedevano, rispettivamente, cos’è un interrupt e come utilizzare un interrupt su Timer0 per scandire il tempo ed eseguire operazioni asincrone con un esempio di led lampeggiante tramite interrupt.

Il Timer0 è un timer generico presente su tutti i PIC. Sul PIC16F887 che sto prendendo ad esempio al posto del PIC16F877A (e che, come detto, uscirà fuori produzione perchè sostituito dal PIC16F18877) il Timer0 è ancora del tipo “vecchio”, ma su tutti i nuovi PICmicro ha anche la possibilità di funzionare a 16bit anzichè solo ad 8bit. Tenete conto che i Timer0 nuovi possono essere resi compatibili con i vecchi abilitando la modalità ad 8 bit nel proprio registro di configurazione:

T0CON0bits.T016bit=0; // Timer0 ad 8 bit

In aggiunta questi nuovi PIC® non hanno più il registro OPTION_REG, che in realtà era una vera “insalata” dato che conteneva bit di configurazione di varie periferiche, ma registri di configurazione singoli per ogni periferica (come il T0CON0 appunto).

Tuttavia l’esempio di questo articolo (led lampeggiante su interrupt del Timer0) prevede l’utilizzo del PIC16F887 che a livello di codice e registri rimane molto simile al vecchio PIC16F877A. Non appena avrò sottomano il nuovo PIC16F18877 farò altre prove e aggiungerò anche quello in modo da dare una visione completa.

L’esempio è simile a quello vecchio: catturiamo un interrupt sul Timer0 ovvero facciamo in modo che il Timer0 vada in overflow dopo un tot di tempo (calcolato), al verificarsi dell’overflow viene generato un interrupt (ovvero la funzione main viene messa in pausa nel punto in cui si trova per poter servire la funzione di interrupt), nell’interrupt incrementiamo un counter, quando questo counter raggiunge un certo valore, invertiamo lo stato del led, si ripristina quindi la condizione di interrupt (reset dei flag) e la funzione termina, al termine della funzione di interrupt la CPU del PICmicro riprende la funzione main da dove l’aveva interrotta: questa è un’operazione asincrona e si capisce che, come ripetuto tantissime volte, la funzione di interrupt deve essere la più snella possibile.

In questo esempio ho messo sempre la word di configurazione a parte, in un file header, dopodichè nel modulo principale (main.c, quello in cui è contenuta la funzione main) ho dichiarato all’inizio tre prototipi funzione:

// prototipi funzione
// prototipi funzione
void settings(void); // impostazioni registri
void beep(void); // fa emettere un beep al cicalino
void __interrupt isr(void); // Interrupt Service Routine

Cos’è un prototipo funzione? E’ la funzione dichiarata senza tutta la parte “esecutiva” (la parte tra le parentesi graffe). A cosa serve? Serve a dire al compilatore: “Prima o poi incontrerai una funzione fatta in questo modo”. Questo, in realtà, non è sempre necessario, lo è soltanto nei casi in cui una funzione venga richiamata in un punto che precede (a livello di scrittura del codice) la sua definizione. Nel mio esempio la funzione settings, ad esempio, viene richiamata nel main ma io la definisco DOPO il main. In questo caso se non scrivessi il prototipo di funzione, mi verrebbe generato un errore. Avrei potuto dichiarare prima la funzione Settings e dopo la Main: in questo modo non ci sarebbe stato bisogno di dichiarare il prototipo. Così come per l’interrupt non è necessario dichiarare il prototipo dato che l’interrupt è una funzione che non viene mai richiamata direttamente (è un vettore in cui la CPU salta al verificarsi dell’evento)… MA, a parte che a me nel codice piace mettere sempre la funzione main in cima, penso sia anche una buona prassi perchè aiuta a mettere un po’ di ordine nelle cose e comunque stiamo facendo degli esercizi.

Ancora una volta ricordo che il void messo prima del nome della funzione serve per indicare che la funzione non restituisce alcun valore, il void messo tra le parentesi vuol dire che la funzione non accetta nessun parametro. Per quanto concerne l’interrupt ho già spiegato nella lezione di inizio – Paragrafo 9 – come va scritto. Nella funzione settings imposto gli IO e le opzioni del Timer0:

// configurazione registro OPTION_REG
OPTION_REGbits.T0CS=0; // sorgente clock TMR0 da Fosc/4
OPTION_REGbits.PSA=0; // prescaler assegnato a TMR0
OPTION_REGbits.PS=0b100; // prescaler 1:32
// nota: PS raggruppa tutti e 3 i bit PS2,PS1 e PS0.
// volendo è possibile definire anche i 3 bits separatamente

Vedete che nel caso dei bit del prescaler la parola “PS” raggruppa tutti e 3 i bit PSn, ecco perchè ho scritto 0b100 (100 in binario) anzichè solo zero o solo uno. Con un Clock di 20MHz e la prima impostazione (T0CS=0) faccio in modo che la sorgente di clock del Timer0 sia 5MHz, con il prescaler 1:32 divido il clock per 32, quindi alimenterò il Timer0 con una frequenza di 156250Hz ovvero una velocità di incremento del timer (periodo) di 1 unità ogni 1/156250=6.4uS.

Facendo partire il timer dal valore 100, esso andrà in overflow dopo 256-100=156 incrementi, ovvero dopo 156*6.4uS=998.4uS (circa 1mS). Quindi si verificherà un interrupt ogni millisecondo circa. Chiaramente l’interrupt deve esserre prima abilitato:

INTCONbits.T0IE=1; // interrupt su overflow TMR0 abilitato

Nella funzione di interrupt incremento un counter (TimerLed), quando questo counter arriva al valore 250, mi inverte lo stato del led, per cui avrò che il led sarà acceso per 250mS e spento per altri 250mS, ovvero lampeggerà ad una frequenza di 1/(250+250)mS = 2Hz.

Per ulteriori informazioni (modalità di calcolo per l’interrupt sul Timer, cosa sono gli interrupt ecc) fate riferimento alle due vecchie lezioni precedenti (4 e 5) che ho linkato in cima all’articolo. Il circuito rimane lo stesso della lezione precedente con l’aggiunta di un cicalino passivo su RC0 e sempre con il PIC16F887 al posto del 16F877A (Se avete la Freedom II ricordate che il cicalino va abilitato con l’apposito Jumper JP6):

In aggiunta nel nuovo sorgente faccio uso dei tipi standard del C per dichiarare variabili definite numeri interi (stdint.h), per ulteriori informazioni fate riferimento al primo aggiornamento.

Ricordo una cosa importante: se vedete che, dopo aver compilato e caricato il programma, il led lampeggia ma il cicalino non suona… verificate di aver selezionato questo nuovo progetto come principale, perchè probabilmente state caricando quello della lezione precedente! (è un errore comune).

Vi lascio con il download del codice sorgente di questa lezione:

Se questo articolo ti è piaciuto, condividilo su un social:
Se l'articolo ti è piaciuto o ti è stato utile, potresti dedicare un minuto a leggere questa pagina, dove ho elencato alcune cose che potrebbero farmi contento? Grazie :)