Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

10
voti

Lampeggio led tramite Timer Interrupt - Parte 1

Introduzione

Durante le vacanza natalizie ho proseguito lo studio dei PICMicro e della loro programmazione seguendo l'ottimo corso di settorezero.com scritto da Giovanni Bernardo ed ho comprato un PIC16F876.

Ho studiato cosa vuol dire interrupt: è un evento di varia natura (ce ne sono diversi) che quando si scatena, se abilitato, fa interrompere al PIC l'esecuzione del programma principale e lo fa passare all'esecuzione di un'altra routine, spesso chiamata Interrupt Service Routine (abbreviata ISR).

Ho scoperto che ce n'è uno sul timer (nel dettaglio il Timer0) interno al PIC.

Ho letto che il timer è un contatore a 8 bit, (2 elevato 8 = 256) cioè conta da 0 fino a 255 e poi torna a 0 e riparte; quando questo accade si può far generare un interrupt.
Il timer conta con un tempo scandito dalla velocità di clock, normalmente conta una unità ogni 4 cicli di clock.

Esiste però un Prescaler che può essere impostato e permette di moltiplicare (per 2 o 4 o 8 o 16 o 32 o 64 o 128 o 256) il numero di cicli di clock necessari all'incremento di una unità del Timer0.

Riepilogando si può dire che il Prescaler si può impostare per cambiare, entro certi limiti, il tempo con cui il Timer0 genera l'interrupt detto Timer0 overflow.

Se sappiamo manipolare gli "oggetti" presentati sopra tramite la programmazione, abbiamo a disposizione un nuovo metodo per gestire il tempo con un PIC, in particolare siamo in grado di assicurarci l'esecuzione di una serie di istruzioni (quelle che andremo a mettere nella ISR) ogni tot di tempo, ad esempio ogni 0,5 secondi.

Viene naturale pensare allora di sfruttare questa possibilità per realizzare il tipico "led lampeggiante" sfruttando l'interrupt del timer: ogni mezzo secondo tramite l'ISR si inverte lo stato dell'uscita digitale di un pin, passando da alta a bassa e viceversa si realizza così il lampeggio.

LET'S GO! Partiamo!

Dopo una prima infarinatura sul mondo dei PICMicro e una serie di scopiazzamenti di codice trovato in rete ed adattamento del codice al poprio PIC, secondo me arriva il momento di fare da soli. Almeno provarci.

La mia mente torna indietro ai tempi delle superiori, quando ero arrivato a coprire con un foglio il risultato degli esercizi di matematica apposta per evitare di sbirciarlo durante la trascrizione, così non ero influenzato dalla conoscenza del risultato durante lo svolgimento, ad esempio, di una espressione.
Ricordo anche diversi ragazzi e ragazze a cui ho dato ripetizioni di matematica, che spesso vedevo lanciare occhiate sul mio foglio mentre risolvevo l'esercizio (per poterli eventualmente assistere nella soluzione o spiegargli i passaggi poco chiari).
Dicevo loro che a me non interessava che mi copiassero, però ricordavo anche che durante la verifica io non sarei stato lì a fianco: era quindi meglio arrangiarsi da soli se volevano migliorare i loro risultati.
Sarò stato antipatico ma era la verità.

Tornando a noi, poco importa se riusciremo nella stesura del codice C al primo colpo, l'importante è mettersi in questa condizione di "isolamento" e provarci perchè se ci blocchiamo significa che stiamo facendo emergere le nostre lacune di preparazione.
Passo 1: rendersene conto.
Passo 2: correre ai ripari, cioè ritornare sulla teoria, ritornare a sbirciare del codice di esempio già scritto per rafforzare le nostre conoscenze.

Levatevi di torno gli articoli sui PIC con gli esempi, avviate MPLAB X IDE, create un nuovo progetto e create infine un nuovo file sorgente in C.

Buttate giù i passaggi elementari che secondo voi servono per raggiungere lo scopo del lampeggio: non addentratevi subito nei dettagli, in questa fase rimanete ad un livello superficiale seguendo un approccio di tipo top-down.

In questo momento il mio programma è già alla versione finale, testata e funzionante, ma sono andato a ripescare una delle primissime versioni per metterla qui sotto sfruttando la Local History di MPLAB X IDE.
Dopo avere letto l'articolo magari andate a scoprire di cosa si tratta, anche se il nome spiega già la funzionalità.
Anche conoscere l'ambiente di sviluppo ed imparare a "muoversi" in esso è importante.

#include <xc.h>

// CONFIG
#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config CP = OFF         // FLASH Program Memory Code Protection bits (Code protection off)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low Voltage In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection (Code Protection off)
#pragma config WRT = ON         // FLASH Program Memory Write Enable (Unprotected program memory may be written to by EECON control)

void main (void)
{
    //IMPOSTAZIONE PORTE INPUT/OUTPUT

    //IMPOSTARE PRESCALER
    //INIZIALIZZARE IL TIMER
    //ROUTINE DI INTERRUPT
        //CONTROLLO SORGENTE INTERRUPT
        //RESET TIMER OVERFLOW
        //CONTROLLO E INCREMENTO VARIABILE
}

Qui sopra trovate le mie prime idee, senza censure =P.
Non è facile trovarsi davanti a un foglio bianco e farsi venire in mente tutto quello che serve per scrivere il programma!

Avevo chiaro che serviva includere il file xc.h che contiene i nomi mnemonici che permettono di scrivere il programma senza ricordarsi le locazioni dei registri a memoria, e fa anche altre diavolerie che per ora non mi interessano.
Poi avevo chiaro che il PIC andava configurato, ed ho quindi generato il sorgente che imposta i bit di configurazione tramite MPLAB X IDE, l'ho copiato ed incollato.
A quel punto secondo me era ora di far partire il programma principale, impostare le porte come ingressi o uscite ed impostare il prescaler.

Avevo poi letto che era possibile inizializzare il timer, forse suona meglio dire precaricare il timer. Il verbo caricare richiama proprio l'idea del caricamento della molla di un orologio.
Mettiamoci in testa che se il PIC ha un timer, che è un contatore, il valore del conteggio dovrà pur essere parcheggiato da qualche parte, e con tutta probabilità ci possiamo andare a mettere le mani e variare questo valore.
A quale scopo? Beh nel caso del precaricamento è come far partire un corridore che di solito fa i 100 metri, anzichè dall'inizio, da 50 metri: ovvio che arriverà più in fretta del solito al traguardo.
Ecco quindi svelato un altro metodo per variare a nostro piacimento, sempre entro certi limiti ovviamente, l'intervallo di tempo con cui avviene lo straripamento del Timer0 (il Timer0 overflow) e di conseguenza il Timer Interrupt... il quale richiama l'attenzione del PIC e lo fa subito saltare ad eseguire la ISR.

Ed ecco arrivato il momento della routine di interrupt; ho letto che ci sono più sorgenti di interrupt, e quindi è bene andare a controllare cosa ha scatenato l'interrupt anche se, come in questo caso, ho abilitato soltanto una sorgente di interrupt: il timer. Ah già, giusto, devo abilitare ed impostare gli interrupt... dopo lo aggiungo al programma.

Per permettere di controllare la sorgente dell'interrupt, il PIC implementa una serie di interrupt flag: immaginiamocele come una serie di spie che si accendono per segnalare il relativo interrupt. Controllando le spie, capisco cosa è successo.
L'overflow del Timer0 fa accendere la spia cosiddetta Timer0 Interrupt Flag (nome mnemonico T0IF), ed è nostro compito, dopo essere andati a vedere "se si è accesa", reimpostarla spegnendola così da essere pronta a segnalare il prossimo T0IF.

Infine devo controllare che valore ha raggiunto la mia variabile, e se non ha raggiunto ancora il valore desiderato, la devo incrementare.
Mi spiego meglio: la ISR viene eseguita dal PIC ad ogni Timer0 overflow, ossia dopo un certo tempo (certo, era quello che volevamo).
Questo tempo però è breve, molto più breve di 0,5 secondi ossia 500 millisecondi. E' bene farsi un'idea del limite minimo e massimo, ma per non soffermarci ora e proseguire diciamo che dura 1 millisecondo; più avanti spiegherò meglio questi limiti di tempo.
Con questa precisazione è chiaro che devo contare 500 esecuzioni della ISR per arrivare a 500 millisecondi.
O meglio, devo contare 500 accensioni della spia Timer0 Interrupt Flag (ricordiamoci di ragionare nel modo più generico possibile: anche se non è questo il caso, esistono svariate sorgenti di interrupt e non solo il "nostro" Timer0 Interrupt: da qui in poi non lo ripeterò più).
Per fare questo conteggio mi appoggio su una variabile di tipo adatto al mio conteggio.
Ah già la variabile, la devo dichiarare ed inizializzare... altra cosa da aggiungere al codice!

Vai alla seconda parte

4

Commenti e note

Inserisci un commento

di ,

Grazie Mir, mi state ripagando della fatica che ho fatto a scrivere le parti dell'articolo (ad es. il riassunto dell'evoluzione del codice nella parte 3 è stato estenuante), cercando di condensare al meglio la mia esperienza, spiegando tutti i dettagli che in altri tutorial spesso vengono tralasciati, e cercando di "trasportare" il lettore nell'analisi del problema e nel raggiungimento dell'obiettivo prefissato. Non so se ci sono riuscito ma provo molto piacere nel leggere i vostri commenti entusiasti!

Rispondi

di ,

Non sono un Picman .. :) ma riconosco che hai fatto un gran bel lavoro ildani,Complimenti.

Rispondi

di ,

Ti ringrazio Paolino, se tu che sei molto più avanti nella conoscenza dei PIC mi dai questo segnale positivo significa che sono sulla buona strada... la vedo lunghissima però, sto procedendo proprio piano cmq l'importante è non stancarsi e lasciar perdere.

Rispondi

di ,

Ottimo esercizio, ottimo approccio! Bravo.

Rispondi

Inserisci un commento

Per inserire commenti è necessario iscriversi ad ElectroYou. Se sei già iscritto, effettua il login.