Indice |
Introduzione
Questa è la quarta ed ultima parte dell'articolo Lampeggio led tramite Timer Interrupt.
Non lampeggia
Come ho scritto nell'ultima frase della terza parte dell'articolo, mi trovavo di fronte ad un led semiacceso, eppure mi sembrava di avere scritto correttamente il codice!
In MPLAB X IDE apro le proprietà del progetto, e nella configurazione seleziono in Hardware Tools la voce Simulator anzichè PICkit3 per provare a simulare il funzionamento del programma.
Metto alcuni breakpoint nel codice per far fermare l'esecuzione del programma e dare un'occhiata alle variabili e ai registri prima di ripartire: il codice funziona bene.
Cosa c'è allora che non fa funzionare il circuito nella pratica?
La soluzione
Il giorno seguente, mentre sistemo un po' le finestre in Firefox dove tengo aperti i siti che sto consultando per imparare la programmazione dei PIC, la mia attenzione viene catturata da un Webinar (abbreviazione di Web Seminar, seminario on-line) della Microchip intitolato "Flash, Damned LED! Flash I say!" (lampeggia, dannato led! Lampeggia ho detto!).
Faccio partire il video ed il relatore Jeff O'Keefe spiega che affronterà i comuni errori commessi dai principianti per far lampeggiare un led.
Non ero molto fiducioso di incontrare la soluzione al mio problema, e invece...
Il video arriva alla quarta slide che richiama dei concetti che già conoscevo, ma che non avevo a quanto pare ben assimilato.
Anche in questo caso il problema e la relativa soluzione erano banali! Non mi ero ricordato che i pin spesso svolgono più di una funzione e sono condivisi da più periferiche, sono multiplexati.
In particolare il pin RA0 scelto da me per collegare il led, svolge sia la funzione di ingresso/uscita digitale RA0, sia la funzione di ingresso/uscita analogico AN0. Per configurare correttamente questo pin dovevo agire su un secondo registro, oltre che sul noto TRIS: si tratta del registro ADCON1, ossia uno dei due registri usati per configurare il convertitore analogico/digitale. Come suggerisce la slide del seminario, con l'istruzione ADCON1 = 0b00000110; si impostano tutti i pin disponibili per il convertitore A/D come ingressi/uscite digitali.
Aggiungo in fretta quest'istruzione al mio codice, reimposto il programmatore PicKit3 e carico il programma sul mio PIC16F87. Ed ecco il led che lampeggia! Poi provo a cambiare il valore del TempoLampeggio e noto che funziona bene, il led lampeggia più o meno brevemente a seconda del valore. Finalmente ho realizzato quello che volevo.
Codice finale
#include <xc.h> //DEFINE #define TempoLampeggio 500 #define Led PORTAbits.RA0 // 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) //VARIABILI unsigned int Tempo = 0; void main(void) { //IMPOSTAZIONE PORTE INPUT/OUTPUT //PORTA is a 6-bit wide, bi-directional port. TRISA = 0; TRISB = 0; TRISC = 0; //IMPOSTAZIONE PORTE: TUTTE DIGITALI ADCON1 = 0b00000110; //ACCENSIONE Led Led = 1; //IMPOSTARE PRESCALER // Generato da PicTimer 1.0.1.0 // © Bernardo Giovanni - www.settorezero.com // Codice valido per Hitec-C // Fosc :4MHz // Timer0 preload :6 // Prescaler :4 // Interrupt time :1,0000mS // OPTION // bit 0 -> PS0 Prescaler Rate Select bit 0 // bit 1 -> PS1 Prescaler Rate Select bit 1 // bit 2 -> PS2 Prescaler Rate Select bit 2 // bit 3 -> PSA Prescaler assegnato a Timer0 (1=Watchdog Timer) // bit 4 -> T0SE Timer0 Signal Edge: 0=low->high 1=high->low // bit 5 -> T0CS Timer0 Clock Select: internal clock (1=T0CKI transition) // bit 6 -> INTEDG INTerrupt Edge (1=raise 0=fall) // bit 7 -> RBPU PortB PullUp (0=off 1=on) OPTION_REG = 0b00000001; //IMPOSTARE INTERRUPT // INTCON // bit 0 -> RBIF PortB Interrupt Flag // bit 1 -> INTF RB0/INT Interrupt Flag // bit 2 -> T0IF Timer0 Interrupt Flag // bit 3 -> RBIE PortB Interrupt Enable (off) // bit 4 -> INTE INT Interrupt Enable (off) // bit 5 -> TMR0IE Timer0 Interrupt Enable (on) // bit 6 -> PEIE PEripheral Interrupt Enable (off) // bit 7 -> GIE Global Interrupt Enable (on) INTCON = 0b10100000; //PRECARICARE IL TIMER // Preload Timer0 // TMR0=8; // valore con correzione TMR0 = 8; // con correzione while (1) { } } //ROUTINE DI INTERRUPT void interrupt isr(void) { //CONTROLLO SORGENTE INTERRUPT TIMER0 OVERFLOW if (T0IF) { //REIMPOSTAZIONE DEL PRECARIMENTO TIMER0 TMR0 = 8; //INCREMENTO VARIABILE TEMPO E CONTROLLO VALORE //CON INVERSIONE STATO LED ED AZZERAMENTO TEMPO Tempo++; if (Tempo >= TempoLampeggio) { Led = !Led; Tempo = 0; } //AZZERAMENTO TIMER0 OVERFLOW T0IF = 0; } }
Conclusioni
In questo articolo ho cercato di massimizzare il coinvolgimento del lettore condividendo il ragionamento, frammentando al massimo i passaggi teorici sperando che il lettore possa comprendere, e fare suo, il metodo di studio e soluzione.