Creative Commons BY-NC-ND 2.5Questo 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 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, fruibili da tutti senza limitazioni potete farlo tranquillamente.

Corso programmazione PICMicro in C – Lezione 5 – Timer0 e Prescaler: come si impostano per generare l’interrupt nei tempi che vogliamo, applicazione pratica

Autore: Giovanni Bernardo | Data pubblicazione: 26 settembre 2009
Categorie: PICmicro 10/12/16

quinta_lezione_programmazione_picmicro_in_cUna periferica sempre presente in tutti i PICMicro è il Timer0. Il Timer0 è un semplice contatore ad 8 bit, il suo conteggio viene incrementato automaticamente di 1 (essendo ad 8 bit va da 0 a 255) ad ogni ciclo di istruzioni.

Il ciclo di istruzioni in un PICMicro è pari alla frequenza di clock divisa per 4: ciò equivale a dire che una singola istruzione  (parliamo però di istruzioni assembler=linguaggio macchina e non di istruzioni C!) viene eseguita in 4 cicli di clock: indicando con Fosc la frequenza di clock (la frequenza del quarzo o dell’oscillatore in genere), la frequenza di un ciclo di istruzioni è quindi pari a Fosc/4.

In realtà, anche se la maggior parte delle istruzioni rispetta questa regola, ve ne sono alcune che richiedono più di 4 cicli di clock per essere eseguite, ma per ora soprassediamo su questo argomento. Quello che ci interessa sapere in questa lezione è che il Timer0 si incrementa di 1 unità ogni 4 colpi di clock, ovvero con una frequenza di  Fosc/4: se stiamo utilizzando un quarzo da 20Mhz, la frequenza di esecuzione delle istruzioni è pari a 20/4 = 5MHz, sappiamo che la frequenza è l’inverso del tempo (periodo), per cui ogni istruzione sarà eseguita nel tempo di 1/5 = 0,2 μS.

Fate molta attenzione alle unità di misura quando eseguite questi calcoli: se stiamo utilizzando una frequenza espressa in MHz, andandone a fare il reciproco per conoscere il tempo, il risultato sarà espresso in microsecondi (μS = secondi • 10-6), se facciamo il reciproco di una frequenza espressa in Hz, il risultato è in secondi.

Quindi, ricapitolando: utilizzando un quarzo da 20Mhz, ogni istruzione verrà eseguita nel tempo di 0,2 μS, per cui anche il Timer0 incrementerà di una unità ogni 0,2 μS. Bene.

Abbiamo accennato nella lezione precedente della possibilità di intercettare un interrupt sull’overflow del Timer0: in pratica quando il Timer0 va in overflow (cioè passa dal valore 255 al valore zero), è possibile fargli generare, e quindi possiamo intercettare, un interrupt: un “campanello” che ci permette di fermare momentaneamente l’esecuzione del programma principale ed eseguire altre funzioni apposite.

Utilizzando un quarzo da 20Mhz, abbiamo che il Timer0 va in overflow dopo 256*0,2μS = 51,2μS (ho utilizzato il valore 256 e non 255 perchè devo conteggiare anche lo zero). Tale tempo, per la stragrande maggioranza delle applicazioni, è troppo basso, generalmente abbiamo bisogno di tempi più “palpabili” per poter lavorare in tranquillità, o per ottenere temporizzazioni ben definite in base a ciò che vogliamo realizzare. Per tale motivo i PICMicro hanno a bordo un’utile dispositivo chiamato Prescaler.

Il Prescaler

Il Prescaler altro non è che un divisore di frequenza, ci permette cioè di dividere la frequenza (la velocità) del nostro ciclo di istruzioni (Fosc/4) in valori più piccoli, facendoci ottenere tempi di esecuzione più alti e quindi più facilmente gestibili o in ogni caso impostabili sui valori che desideriamo. Ovviamente il prescaler non influisce sulla velocità di esecuzione di tutte le istruzioni, (sarebbe assurdo!): le istruzioni continueranno ad essere eseguite ad una velocità di Fosc/4: esso difatti influisce unicamente sull’incremento del Timer0 (cioè non è completamente vero, difatti il prescaler può essere assegnato o al Timer0 o al Watchdog Timer, -o l’uno o l’altro-, di questo però non ci occuperemo: in questa sede ci occuperemo soltanto del prescaler assegnato al Timer0).

I valori su cui possiamo impostare il prescaler (ovvero il  divisore) sono : 2, 4, 8, 16, 32, 64, 128, 256. Come vedete il valore di 1 non è presente: in questo caso basta semplicemente non assegnare il prescaler al Timer0.

Otteniamo tempi precisi con il Timer0 e il Prescaler: formule

Facciamo un esempio pratico: stiamo utilizzando un quarzo da 20Mhz, abbiamo assegnato il Prescaler al Timer0 (vediamo dopo come si fa) e abbiamo impostato il prescaler a 32. In tali condizioni, la frequenza con cui il Timer0 si incrementa è pari a:

prescaler

(ricordo ancora che con Fosc indico la frequenza del Quarzo, con PS indico invece il valore del PreScaler)

ovvero: (20/4)/32 = 0,156Mhz

Ma noi vogliamo ragionare in termini di tempo, perchè così ci risulta più facile lavorare, quindi prendiamo la formula precedente e facciamone il reciproco per ottenere il valore di tempo:

prescalerovvero: (4/20)*32 = 6,4 μS

Quindi in tali condizioni, il nostro Timer0 incrementerà di una unità ogni 6,4 μS, andrà quindi in overflow dopo (6,4*256) = 1638,4 μS ovvero 1,63 mS … Come vedete il tempo è già aumentato.

Supponiamo adesso di voler ottenere un tempo ben preciso (che ci può servire per generare una ben determinata frequenza oppure semplicemente perchè più facile per fare i conti ecc ecc…).

Diciamo che vogliamo ottenere un interrupt ogni 1 millisecondi, ovvero ogni 1000μS. In questi casi si ricorre ad un trucchetto estremamente semplice: basta semplicemente non far partire il Timer0 dal valore 0:  impostiamo noi il valore da cui  partire… Come? Semplice:

Abbiamo appena detto che con queste impostazioni, il Timer0 incrementa di una unità ogni 6,4 μS (ovvero il suo ciclo di istruzioni, grazie al prescaler, adesso è di 6,4 μS/ciclo), ciò significa che un tempo di 1000 μS lo raggiunge dopo:

1000/6,4 = 156,25 cicli

teniamo però conto del valore intero: 156 (i cicli sono finiti, non possiamo certo usare il decimale). Bene, se con tali impostazioni, dopo 156 cicli otteniamo un millisecondo, è facile far generare l’interrupt giusto ad un millisecondo: basterà far partire il Timer0 da: 256-156 = 100

Partendo da 100, il Timer0 andrà in overflow dopo 156 cicli, che ci darà appunto il tempo di 1 millisecondo! Una volta generato l’interrupt reimposteremo di nuovo  il Timer0 a 100, e così ogni volta, ad ogni interrupt. Come avete visto è molto semplice fare i calcoli. In realtà, avendo tralasciato (per forza!) dei decimali, il tempo preciso non sarà di 1000 μS, calcoliamo il tempo reale che si ottiene :

ciclo istruzioni

sappiamo che:

ciclo istruzioni

quindi:

ciclo istruzioni

quindi, nel nostro esempio: Tempo = 156 * 6,4 = 998,4 μS

come vedete c’è un piccolo scarto rispetto ai 1000μS (-1,6μS) , ma generalmente tale errore ci sta bene, possiamo comunque correggerlo immettendo altre piccole temporizzazioni per riportarlo al valore corretto (ma in genere questo viene fatto soltanto in applicazioni di precisione).

Ricapitolando, per conoscere il valore su cui impostare il Timer0 possiamo quindi utilizzare la formula:

Preload Timer0

“Semplificando” un po’:

Preload Timer0

Dove:

PreloadTMR0 = valore da dare al Timer0 (valore intero: privo dei decimali!)
Td = Tempo desiderato
Fosc = Frequenza del quarzo
PS=Valore del prescaler (uno dei valori: 2, 4, 8, 16, 32, 64, 128, 256)

Fate attenzione alle unità di misura: se esprimiamo la frequenza del quarzo in Mhz, dovremo esprimere il tempo in microsecondi, se esprimiamo la frequenza in Hz, il tempo andrà espresso in secondi. Il tempo reale invece sarà dato dalla formula che abbiamo già visto sopra:

programmare_pic_lez5_formula09

Ricordo che c’è questa differenza tra tempo realmente ottenuto e tempo impostato per il motivo che sul valore di preload del Timer0 dobbiamo eliminare i decimali, andando quindi a fare il calcolo inverso tralasciando i decimali, ecco che si ha questo scostamento inevitabile.

C’è inoltre da fare un’ulteriore precisazione quando si effettuano queste operazioni: quando si va a scrivere un valore nel Timer0 (quando cioè lo impostiamo noi manualmente su un determinato valore), vengono persi 2 cicli di istruzioni, per cui in effetti, al posto di scrivere 100, dovremmo in realtà scrivere 102 (dal momento che 2 cicli vengono già persi nel momento in cui scriviamo, per cui dobbiamo far partire il Timer0 “più avanti”), ciò è chiaramente specificato nel PICMicro Mid-Range MCU Family al paragrafo 11.7 “Design tips” (pagina 177 con Adobe Reader). Ricordo che tale documento è scaricabile nella lezione 2.

Software per il calcolo del valore da assegnare al Prescaler e al Timer0

Per aiutarvi ad effettuare questi calcoli, ho realizzato un piccolo software: PICTimer, scaricabile gratuitamente dalla sezione software. In questo modo vi basterà inserire unicamente la frequenza del quarzo che intendete utilizzare e il tempo che desiderate per generare l’interrupt, il software effettuerà le varie combinazioni con i valori di Prescaler per ottenere il valore che più si avvicina mostrando anche il tempo realmente ottenuto e la percentuale di scarto. Il Software genera inoltre il codice (in Hitec-C) necessario per impostare Timer0 e Prescaler. Nel manuale incluso col software vengono date tutte le spiegazioni di funzionamento.

Impostare sul PICMicro il Prescaler e il Timer0

Vediamo adesso come vanno impostati Timer0 e Prescaler, andiamo a pag. 171 del Midrange MCU Family Reference Manual (ricordo: scaricabile dalla secona lezione), oppure, che è lo stesso, a pag.56 del Datasheet del PIC16F877 (ogni picmicro ha comunque nel datasheet tale pagina, dal momento che il Timer0 con il prescaler sono implementati in tutti i pic) e diamo uno sguardo alla tabella riportata in questa pagina:

assegnazione_prescalerIl funzionamento di Timer0 e l’impostazione del Prescaler vengono effettuati dal registro denominato OPTION_REG (nome mnemonico quando programmate in C : OPTION). Cominciamo la spiegazione dai bit meno significativi:

I Bits 0,1 e 2 (chiamati anche individualmente PS0 PS1 e PS2) servono per impostare il valore di divisione della frequenza (il valore del prescaler), come vediamo dalla tabella, impostando ad esempio questi 3 bit sul valore 101, otterremo un valore di divisione della frequenza del ciclo istruzioni pari a 64 (ovvero moltiplicheremo per 64 il relativo tempo).  In questa tabella vi sono due colonne: quella sinistra è relativa al valore di divisione che si ottiene se assegniamo il prescaler al Timer0, quella destra rappresenta il valore di divisione che si ottiene se invece il prescaler è assegnato al Watchdog timer (non ci interessa).

Bit3 (PSA) : bit di assegnazione del Prescaler, se impostiamo tale bit a 0, il prescaler viene assegnato al Timer0.

Bits 4 e 5 : qui c’è da fare una piccola considerazione. Abbiamo difatti detto che l’incremento del Timer0 è legato al quarzo che fornisce il clock al pic, in effetti possiamo anche fare in modo che l’incremento del Timer0 sia legato ad una sorgente di clock esterna (questo può essere utile quando dobbiamo ad esempio sviluppare un’applicazione di precisione e ottenere quindi un tempo estremamente preciso che non abbiamo modo di realizzare con i metodi fin qui visti). Se vogliamo che il Timer0 si incrementi tramite una sorgente esterna di clock, dobbiamo impostare il bit 5 ad 1 (in questo caso il clock andrà applicato al pin contrassegnato sul PICMicro con la sigla T0CKI), se invece vogliamo che il Timer0 si incrementi tramite la circuiteria interna del PIC (e quindi tramite il quarzo), allora imposteremo tale bit a zero. Il bit 4 stabilisce invece come deve avvenire l’incremento del Timer0 unicamente quando si sfrutta una sorgente esterna: se impostiamo ad uno, l’incremento si avrà nel momento in cui il segnale effettua il passaggio da stato logico alto a stato logico basso.

Bit 6 : Stabilisce il modo in cui deve avvenire l’interrupt sul pin RB0/INT quando è stata selezionata tale sorgente di interrupt. Se abilitiamo l’interrupt sul pin RB0/INT e impostiamo questo bit (il Bit 6 del registro OPTION – INTEDG) sul valore 1, avremo che l’interrupt sarà generato sul fronte di salita del segnale applicato ad RB0/INT, se impostiamo tale bit a zero, l’interrupt scatterà sul fronte di discesa del segnale. Ovviamente tale impostazione (come anche la successiva) non c’entra assolutamente nulla col Timer0, ma fa comunque parte del registro delle opzioni.

Bit 7 : abilita (0) oppure disabilita (1) le resistenze di pull-up sulle porte B. Non abbiamo ancora parlato di cosa sia una resistenza di pull-up, diciamo per ora che le resistenze di pull-up servono a fare in modo che un pin configurato come ingresso non rimanga “appeso” ovvero non rimanga senza segnali applicati ad esso (condizione che può portare a malfunzionamenti). Generalmente sui pin configurati come ingresso andremo sempre ad inserire una resistenza di pull-up che mantiene il pin a valore logico alto (up). Le porte B dei PIC hanno questa caratteristica: possiamo infatti evitare di mettere tali resistenze all’esterno, si può sfruttare la circuiteria interna del PIC ponendo questo bit a zero.

Ricapitoliamo facendo un esempio: vogliamo assegnare il prescaler al Timer0 e impostare una divisione di 32, in C andremo a scrivere (tenete sempre conto che il bit 0 è quello più a destra):

1
2
3
4
5
6
7
8
9
10
OPTION=0b10000100;
// Impostazione Registro Opzioni
// 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 al Timer0 (1=al Watchdog Timer)
// bit 4 -> T0SE   Incremento Timer0 su transizione: 0=low->high 1=high->low
// bit 5 -> T0CS   Clock Timer0 da clock interno (1=su transizione pin T0CKI)
// bit 6 -> INTEDG Interrupt (INT) edge (1=salita 0=discesa)
// bit 7 -> RBPU   Resistenze pull-up su porta B (1=off 0=on)

Il valore di partenza del Timer0 si imposta semplicemente così:

1
TMR0=100;

(ovviamente una volta andato in overflow, ripartirà da zero, a meno che non intercettiamo l’interrupt e reimpostiamo il Timer0 sul valore voluto).

Esempio pratico di un semplice sistema multitasking

Bene, passiamo ora a fare un esempio pratico di come sfruttare tutto questo: realizzeremo un circuito in cui un led lampeggia mentre un cicalino emette un suono ad una frequenza fissa. Il circuito che andremo a realizzare è praticamente uguale a quello della lezione 3 con la sola differenza che andremo a mettere un cicalino (con relativa resistenza in serie) sulla porta RE1. Se utilizzate la scheda Freedom di Mauro Laurenti, c’è già la predisposizione sullo stampato, altrimenti realizzando il circuito in altro modo, potete anche connettere il cicalino alla porta che più vi aggrada: modificare il codice sorgente non dovrebbe essere un problema.

Lo schema è pertanto il seguente:

PIC16F877: led+cicalino

Come vedete, in più rispetto al circuito della lezione 3, abbiamo messo una resistenza da 220Ω in serie ad un cicalino (sul terminale positivo), l’altro terminale del cicalino va a massa.

Nota sul cicalino: il cicalino utilizzato in questo esperimento non è un cicalino autooscillante, ma un semplice cicalino miniaturizzato di quelli che si trovano normalmente anche nei vecchi modem ISA o PCI per segnalare il tono sulla linea telefonica: tale tipo di cicalino non suona se gli si applica una tensione continua, per farlo suonare è necessario applicarvi un segnale periodico (si comporta in pratica come un altoparlante miniaturizzato). Se avessimo utilizzato un cicalino autooscillante (che tra l’altro costa anche molto di più, ed è più difficile da trovare), sarebbe stato troppo semplice. Dovremo difatti generare un’onda quadra per fargli emettere un tono. Questi sono, ad esempio, alcuni cicalini del tipo corretto che ho smontato da vecchi modem guasti per pc:

cicalini

Utilizzando tale tipo di cicalino, la resistenza in serie può anche essere omessa. Ogni cicalino ha una propria frequenza di risonanza (che si aggira normalmente intorno ai 2÷4KHz) alla quale il suono è più intenso: i cicalini difatti sono costituiti da materiale piezoelettrico, simile a quello utilizzato per la costruzione dei quarzi.
(un grazie a Mauro Laurenti)

Passiamo quindi al listato. Questa volta andremo a scrivere il programma mettendo le impostazioni in un file separato (file header) e le istruzioni nel file principale (.c), in questo modo ci abituiamo a scrivere codice riutilizzabile. Passiamo quindi a vedere il file delle impostazioni, che per logica chiameremo settings.h :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#define    TEMPOLED    250
#define    LED         RD0
#define    CICALINO    RE1
 
unsigned char TimerLed=0;    //Char è un tipo di dato a 8 bit, quindi può arrivare a contenere valori fino a 255, a noi serve massimo 250, quindi va bene
 
void settings(void)
 {
 // Tutte le porte come output
 TRISA=0;
 TRISB=0;
 TRISC=0;
 TRISD=0;
 TRISE=0;
 
 // Impostazione del registro OPTION (pag.55 del datasheet)
 OPTION=0b11000100;
 // bit 0 -> Prescaler Rate Select bit 0
 // bit 1 -> Prescaler Rate Select bit 0
 // bit 2 -> Prescaler Rate Select bit 0 (1:32)
 // bit 3 -> Prescaler assegnato al Timer0
 // bit 4 -> Non importa
 // bit 5 -> Clock per Timer0 derivato da ciclo di clock interno
 // bit 6 -> Non importa
 // bit 7 -> Resistenze di pull-up su porta B disattivate
 
 // Impostazione Interrupt
 INTCON=0b10100000;
 // bit 0 -> RBIF - Flag interrupt su porte B
 // bit 1 -> INTF - Flag interrupt su RB0/INT
 // bit 2 -> T0IF - Flag interrupt su Timer0
 // bit 3 -> RBIE, Interrupt su porte B disattivato
 // bit 4 -> INTE, Interrupt su porta RB0/INT disattivato
 // bit 5 -> TMR0IE, Interrupt su Timer0 attivato
 // bit 6 -> PEIE, Interrupt di periferica disattivato
 // bit 7 -> GIE, Gestione Interrupt attiva
 
 TMR0=100; // Imposto Timer0 a 100
 }

Nelle righe 1,2 e 3 stiamo soltanto utilizzando dei simboli: quando il compilatore nel programma troverà la parola “TEMPOLED” la sostituirà con il numero 250 e così via. In particolare: TEMPOLED verrà utilizzato per invertire lo stato del led ogni 250 millisecondi, le altre due definizioni ci servono ovviamente per identificare il led (collegato sulla porta RD0) e il cicalino (collegato sulla porta RD1).

Alla riga 5 troviamo qualcosa di molto interessante:

5
unsigned char TimerLed=0;

Stiamo in pratica definendo una variabile, alla quale abbiamo dato il nome TimerLed (ricordatevi di rispettare maiuscole e minuscole), abbiamo inoltre detto che questa variabile non deve avere segno (unsigned) ed è una variabile di tipo Char (ovvero ad 8 bit, quindi tale variabile può assumere tutti i valori interi compresi tra 0 e 255), abbiamo inoltre inizializzato tale variabile al valore 0.

Quando vengono dichiarate le variabili vanno sempre messi in sequenza:

unsigned oppure signed / tipo di dato / nome variabile / eventuale inizializzazione / ;

I tipi di dato sono stati elencati nella lezione 3 (tabella 3.1) e sono comunque disponibili nel manuale del compilatore.

Abbiamo quindi una funzione:

7
8
void settings(void)
 {

In questa funzione (che verrà richiamata in seguito nel programma principale), abbiamo inserito tutte le opzioni di configurazione. Come vedete abbiamo impostato il registro OPTION e il registro INTCON in maniera tale da attivare l’interrupt sul Timer0 ed impostare il prescaler sul valore 1:32, in questo modo, facendo partire il Timer0 dal valore 100, otterremo (con il quarzo da 20MHz) un tempo di interrupt pari a 1 millisecondo per quanto detto in precedenza.

Passiamo quindi al programma principale, contenuto nel file main.c :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#define  XTAL_FREQ 20MHZ // questo è utilizzato dalle routine di ritardo contenute in Delay.C
#include <pic.h> // contiene i nomi mnemonici di registri e porte
 
// Fuses di configurazione
__CONFIG (HS & WDTDIS & PWRTEN & BORDIS & LVPDIS & DUNPROT & WRTEN & DEBUGDIS & UNPROTECT);
 
#include "delay.c" // routine per ritardi
#include "settings.h" // settaggi del picmicro
 
// funzione principale, eseguita all'avvio del picmicro
void main(void)
 {
 
    settings(); // eseguo la funzione settings contenuta nel file header settings.h, così imposto le porte e i registri
 
    while(1) // eseguo un ciclo finito
    {
       /*
       L'unica cosa che eseguo durante questo ciclo infinito, è l'inversione
       dello stato del cicalino ogni 200microsecondi, in maniera tale da generare
       un'onda quadra di 2,5KHz che, applicata al cicalino, appunto,
       mi permette di farlo suonare facendogli emettere una nota a tale frequenza
       */
       DelayUs(200);
       CICALINO=1;
       DelayUs(200);
       CICALINO=0;
    }// Fine ciclo continuo
 
 } // Fine main
 
/*
Questa routine, avendo l'attributo "interrupt" prima del nome della routine stessa, viene chiamata in automatico
ogni qualvolta si verifica un interrupt. Essendo le sorgenti di interrupt di vari tipi, in questa routine dobbiamo
capire quale elemento ha generato l'interrupt. Con le impostazioni utilizzate, Timer0 genererà un interrupt ogni
millisecondo.
*/
void interrupt ISR (void)
 {
    if (T0IF) // L'interrupt è stato causato da un overflow del timer0 ?
       {
       TMR0 = 100; // Reimposto Timer0
       TimerLed++; // Incremento il Timer per il lampeggio del led
       if (TimerLed >= TEMPOLED) // Se il tempo è passato
          {
          LED=LED^1; // Inverto lo stato del led per farlo lampeggiare
          TimerLed=0; // Ricarico il timer del led per ricominciare daccapo
          }
       T0IF=0; // Resetto il flag interrupt su timer 0,
       } // fine che interrupt verificatosi su timer0
 } // fine interrupt service routine

Tralasciando le righe da 1 a 7 che dovrebbero essere chiare, vediamo che nella riga 8 richiamiamo il nostro file di configurazione, parte quindi il programma principale e subito, alla riga 13, viene richiamata la funzione definita in settings.h :

13
settings();

In pratica verranno eseguite tutte le istruzioni contenute nella funzione settings: saranno impostati i registri.

Notiamo in questo listato un altro modo di includere i commenti: fino ad ora includevamo i commenti mettendo un doppio slash // : questo tipo di notazione permette di mettere il commento su una sola riga, possiamo invece includere commenti multiriga mettendo /* prima del commento e */ alla fine.

Alla riga 15 viene quindi avviato il ciclo infinito, come vedete, tale ciclo non fa altro che eseguire di continuo le istruzioni:

23
24
25
26
DelayUs(200);
CICALINO=1;
DelayUs(200);
CICALINO=0;

In pratica il cicalino rimane acceso (CICALINO=1) per 200 microsecondi, e spento (CICALINO=0) per altri 200 microsecondi, per poi ricominciare daccapo (dal momento che si trova nel ciclo while(1) ). In pratica, se avessimo a disposizione un oscilloscopio, potremmo vedere che sul pin al quale è collegato il cicalino (ricordo: RE1 in questo esempio) fuoriesce un’onda quadra alla frequenza di 2,5KHz (200μS ON e 200μS OFF = periodo di 400μS  = 2500 Hertz, che come nota musicale corrisponde all’incirca ad un RE#7 – corrispondenza note/frequenze presa da: febat.com). Quindi come vedete, generare un’onda quadra ed ottenere una nota da applicare ad un altoparlante è abbastanza semplice. Ma se nel contempo che viene suonata la nota, volessimo fare altro?

Qui entra in gioco l’interrupt del Timer0: con le impostazioni che abbiamo dato in settings, abbiamo fatto in modo che ogni millisecondo, il programma main si interrompa momentaneamente ed esegua le istruzioni contenute nell’apposita funzione:

38
39
void interrupt ISR (void)
 {

In questa routine, richiamata al verificarsi di un interrupt, dovremo capire da chi è stata causata l’interruzione (come spiegato nella precedente lezione) , per tale motivo mettiamo la seguente condizione:

40
41
if (T0IF) // L'interrupt è stato causato da un overflow del timer0 ?
       {

(In realtà non ce ne sarebbe neanche bisogno dal momento che l’unica sorgente di interrupt che abbiamo abilitato è il Timer0, ma abituiamoci ad essere ordinati). Verificato quindi che l’interruzione è stata causata da TMR0, la prima cosa che facciamo è quella di reimpostare il Timer0 al valore di partenza:

42
TMR0 = 100; // Reimposto Timer0

Incrementiamo quindi la nostra variabile di conteggio di una unità (l’operatore ++ viene chiamato incremento unario):

43
TimerLed++;

Passiamo quindi a controllare che il valore di questo nostro contatore TimerLed abbia superato il valore di 250 (ricordate? in settings abbiamo definito TEMPOLED come uguale a 250):

44
45
if (TimerLed >= TEMPOLED) // Se il tempo è passato
          {

Se tale condizione viene verificata, invertiamo lo stato del led ed azzeriamo tale variabile:

46
47
48
          LED=LED^1; // Inverto lo stato del led per farlo lampeggiare
          TimerLed=0; // Ricarico il timer del led per ricominciare daccapo
          }

L’effetto che si otterrà è in pratica un led che lampeggia con un periodo di mezzo secondo (250ms ON e 250ms OFF). Passiamo quindi ad azzerare il flag di interrupt sul Timer0, altrimenti non potremo più intercettarlo:

49
50
51
       T0IF=0; // Resetto il flag interrupt su timer 0,
       } // fine che interrupt verificatosi su timer0
 } // fine interrupt service routine

L’interrupt service routine è quindi terminata e il controllo viene restituito al programma principale. Dal momento che tale sequenza di istruzioni (verifica delle condizioni, incremento del timer, inversione dello stato del led ecc), prende pochi microsecondi, l’effetto sarà quello di vedere il led lampeggiare mentre il cicalino suona: due task  eseguiti contemporaneamente (in realtà eseguiti in tempi separati, ma molto, molto rapidamente).

In sostanza: abbiamo definito una variabile (TimerLed) che tiene tempo di un conteggio: essendo l’interrupt service routine richiamata ogni millisecondo (come da nostre impostazioni), tale variabile sarà incrementata di una unità ogni millisecondo: ci siamo in pratica creati un contatore che obbedisce alle nostre regole! Sempre nell’ISR controlliamo il valore di questo nostro contatore per fargli eseguire una funzione non appena giunge ad un certo valore. Ovviamente sarà nostra cura reimpostare questo nostro contatore sul valore zero e reimpostare il Timer0 dal punto che abbiamo scelto per la partenza. Sfruttando quindi il Timer0 in abbinamento al prescaler, possiamo in pratica crearci tutti i contatori che vogliamo definendo delle variabile apposite da utilizzare come contatori.

Il modo per compilare il programma e caricarlo sul picmicro l’avete appreso nella lezione 3, per cui non dovreste avere difficoltà, anche in questo programma utilizzeremo le routine delay (incluse nel download). Per compilare il programma ricordo che nel progetto dovrete includere unicamente il file main.c

Downloads

Nota: i programmi di esempio sono stati sviluppati con una versione precedente dell’Hitec-C Compiler, per cui compilati con la nuova versione, restituiscono errori. Fate riferimento a questo articolo per maggiori informazioni su come adattare i vecchi programmi. Consiglio spassionato se volete davvero imparare a programmare: non utilizzate l’include legacy headers, ma imparate a cambiare i nomi mnemonici.

File di supporto alla quinta lezione del corso di programmazione picmicro in C (1097)

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.

  1. #1 da Piero Vigliocco il 26 settembre 2009

    Buon dì Gianni,effettivamente ogni tanto un buon caffè è quello che ci vuole ma chi è il sig Giovanni Bernardo a cui ho appena fatto una donazione ?
    Corrisponde veramente a Settorezero.com ?
    Era mia intenzione il caffè di ofrirlo a Te o a Voi di programmazione in linguaggio C ?
    Comunque sia la mia donazione è destinata al Tuo lavoro che è un ottimo lavoro.
    By Piero Vigliocco.

    • #2 da Gianni il 26 settembre 2009

      Salve,
      io davvero non so come ringraziarla dello splendido gesto. Bernardo Giovanni sono io, proprietario di settorezero.com, e autore della maggior parte degli articoli (difatti il mio nome lo legge comunque nei software e anche nei sorgenti). Grazie di cuore davvero.

  2. #3 da peter il 28 settembre 2009

    Avrei dovuto immaginarlo. Arrivo da una giornata di lavoro pesante e vorrei avere il potere di resettare il tempo per mettermi a studiare la Sua lezione n° 6 ma purtroppo dovrò rimandare perchè domani è molto vicino.
    Saluti e grazie, peter

  3. #4 da Gianluca il 29 settembre 2009

    Caffe pagato anche da me (in senso letterale perchè sul conto paypal avevo solo questi pochi spiccioli).
    Complimenti vivissimi per la guida
    Ciao
    Gianluca

    • #5 da Gianni il 17 ottobre 2009

      Ti ringrazio davvero tanto :)
      Fatemi sempre sapere come procede e se è tutto chiaro, se utilizzate picmicro diversi o diversi programmatori, in maniera da poter essere d’aiuto a tutti

  4. #6 da cimistrufoli il 6 novembre 2009

    Buondì Gianni,
    innanzitutto volevo farti i complimenti x queste splendide lezioni che, x chi come me parte da zero sia per quanto riguarda i pic, sia per quanto riguarda il linguaggio C, sono veramente ben spiegate e utilissime!! :)
    io sto utilizzando adesso un pic 18f452 (cm da te suggerito sono andato a vedere il tutorial di Mauro Laurenti sul C18) e almeno x quanto rigurda timer0 e timer1 è simile al16f877 a parte i comandi di registri per abilitare disabilitare le varie cose.
    Non riesco a capire però una cosa: quando utilizzo il timer1 come counter di un clock esterno (esattamente un quanrzo da 32768Hz) in modo asincrono, il conteggio verrà effettuato ogni ciclo istruzione, cioè fclock/4, cm nella tua spiegazione del timer0 o si incrementerà indipendentemente dal ciclo istruzioni e quindi per il calcolo del tempo di interrupt nn dovrò tener conto della divisione per 4?
    spero di esser riuscito a spiegarti il mio dubbio :)
    Grazie ancora
    ciao

    • #7 da Giovanni Bernardo il 6 novembre 2009

      Ciao, innanzitutto grazie di cuore dei complimenti. Quando utilizzi un timer con una sorgente di clock esterna si dice che il timer opera come contatore e non più come timer, per cui l’incremento non è più vincolato a Fosc/4 ma alla frequenza del clock utilizzato per il pilotaggio del timer dall’esterno.
      Prendo a riferimento il datasheet del pic16F877A: vai a pagina 58 (60 da adobe reader) e vedi la sezione 6.2 Timer1 Counter Operation:

      When Timer1 is being incremented via an external
      source, increments occur on a rising edge. After Timer1
      is enabled in Counter mode, the module must first have
      a falling edge before the counter begins to increment.

    • #8 da Giovanni Bernardo il 6 novembre 2009

      Dai un occhio al MidRange MCU Family Reference Manual, pagina 184: il clock esterno deve soddisfare alcuni requisiti.

  5. #9 da Giorgio il 10 novembre 2009

    Buongiorno Gianni,
    il caffè per tutta la settimana te lo sei strameritato.
    Hai risvegliato in me la passione per l’elettronica, ferma a qualche decennio fa. E pensare che sono partito dal voler riparare la scheda del freezer…(PIC16C505). Risultato: sostituita la scheda :-(
    …ma acquisterò il PicKit 3!
    Complimenti ancora,

    Giorgio

    • #10 da Giovanni Bernardo il 11 novembre 2009

      Ti ringrazio per il caffè anche se non è arrivato.
      Per quanto riguarda la scheda del freezer, dubito che si sarebbe potuta riparare (almeno la parte del pic), perchè a meno che non hai il firmware originale disponibile su file e un pic vergine da programmare, coi pic usciti di frabbrica ci si fa ben poco dato che vengono protetti e non possono essere ne letti ne riprogrammati. Se acquisti il pickit3 facci sapere come va.

  6. #11 da Giorgio il 12 novembre 2009

    Ho controllato i moviementi PAYPAL,
    a me risulta effettuato in data 20/10.
    Puoi per cortesia verificare e darmi conferma? Eventualmente confrontiamo i dati tramite mail.
    Per il pic hai ragione, non c’è nulla da fare. Infatti ho comprato la scheda nuova. Però, cuoriosando quà e là mi sono imbattuto in questo sito…

    ciao,
    Giorgio

    • #12 da Giovanni Bernardo il 12 novembre 2009

      Si, tutto ok ;) essendo ottobre non lo visualizzavo subito. Ti ringrazio di cuore, a buon rendere. Penso che la prossima lezione sarà sull’eeprom. Mi devo organizzare un po’.

  7. #13 da sassoferrato il 10 marzo 2010

    Ciao Gianni
    complimenti vivissimi per come spieghi gli argomenti, pensa che anche uno come me digiuno di programmazione e non piu giovanissimo riesce a seguirti.
    Il caffè te lo meriti corretto e per tutta la settimana dovrebbe essere gia nella tazza
    ti saluto
    Eridano (sassoferrato)

  8. #15 da gigio13 il 10 aprile 2010

    Ciao Gianni,
    complimenti per le lezioni sui pic. Sono veramente utilissime per chi si addentra nell’argomento. Volevo approfittarne per chiederti un aiutino. Ho provato il file della lezione 5 su mplab e volevo simularlo con mplab sim. Ma andando a tracciare le forme d’onda del cicalino e del led riesco a vedere solo l’onda quadra del led. Il cicalino rimane invece sempre a zero. Ho provato anche a cambiare un pò i tempi ma non riesco mai a vedere l’onda quadra del cicalino. Sai forse come mai? Devo forse impostare qualcosa di particolare? Grazie mille in anticipo!

  9. #16 da MayTs il 19 aprile 2010

    Ciao, ulteriore domanda(ormai sarai stufo di me… eheheh).
    utilizzo un quarzo da 8 mhz, impostato il prescaler 1:8 e il preload del timer0 a 8.
    Così facendo dovrei ottenere un interrupt esattamente ogni mS.
    Ho quindi realizzato un semplice orologio e andandolo a testare mi sono trovato sorpreso nel vedere che dopo circa 7 ore di funzionamento l’orologio è rimasto indietro di 3 minuti rispetto ad un altro orologio(che ho utilizzato appunto per controllare l’affidabilità del timer da me creato)…
    Sapresti dirmi il perché?

    ti allego il codice:
    if (T0IF)
    {
    TMR0 = 8; // Reimposto Timer0
    CicliSecondi++;
    if (CicliSecondi >= 1000)//quando passa un secondo
    {
    Secondi++;
    if (Secondi >= 60)
    {
    Minuti++;
    Secondi = 0;
    if (Minuti >= 60)
    {
    Ore++;
    Minuti = 0;
    if (Ore >= 24)
    {
    Ore = 0;
    }
    }
    }
    CicliSecondi = 0;
    }
    T0IF=0;
    }

    • #17 da Giovanni Bernardo il 19 aprile 2010

      Ma è assolutamente normale che troverai sempre uno scostamento, il problema è capire se è l’orologio che hai fatto tu che sgarra, o l’altro. In genere, però, per realizzare un’orologio non si utilizza mai il quarzo di sistema con gli interrupt ma si ricorre invece al Timer1 collegandoci come sorgente di clock un quarzo da 32768KHz. Quindi sul sistema avrai due quarzi: uno di sistema e l’altro come sorgente di clock per il timer1. Ci sono due pin apposta per collegare il quarzo del timer1, sono indicati sul datasheet come T1OSI e T1OSO (Timer1 Oscillator Input e Timer1 Oscillator Output).

      Anche se questo consente una precisione maggiore, c’è sempre il problema della deriva termica dei quarzi, ma in questo caso confrontando con l’altro orologio (che pure dovrebbe funzionare con lo stesso tipo di quarzo), lo scostamento sarà molto molto minore.

      Per fare le cose precise si ricorre generalmente ad un oscillatore esterno al posto del quarzo (sempre come base dei tempi per il timer1). La Maxim se non erro ha in catalogo appunto un circuito integrato che produce un’onda quadra a 32768KHz, ma ce l’hanno un po tutte le ditte che producono circuiti integrati.

      Perchè 32768KHz? 32768 = 2^15. Il timer1 è un timer a 16 bit che genera anch’esso un interrupt sull’overflow, precaricandolo opportunamente puoi ottenere appunto un interrupt ogni secondo, quindi a ogni interrupt incrementi il contatore dei secondi e ti fai tutti i calcoli. L’utilizzo del timer1 è semplicissimo ed è quasi uguale a quello del timer0 se non per il fatto che è a 16 bit per cui ha un registro alto e uno basso, sul datasheet ci sono tutte le informazioni del caso. Se hai capito bene questa lezione qui, per utilizzare il Timer1 non dovresti avere difficoltà.

      Per fare delle prove, se non trovi il quarzetto, lo puoi anche smontare da un vecchio orologio a parete o anche da altri dispositivi. Questo quarzetto viene prodotto in un formato particolare tutto suo: è un cilindretto metallico molto molto piccolo coi pin sottilissimi. In questo formato fanno soltanto i quarzi da 32768KHz e un’ altra frequenza poco utilizzata che ora non ricordo, ma in vita mia non l’ho mai incontrata quindi sarà solo una leggenda. Tale quarzo mi è capitato spesso di trovarlo anche nei termometri digitali, sul corpo non c’è scritto nulla o al limite qualche sigla inutile.

      Il timer1 viene abilitato portando a 1 il bit TMR1ON e l’oscillatore parte con TMR1OSCEN=1: in automatico i due pin destinati al quarzo del timer1 diventano ingressi indipendentemente da come stanno settati nel registro tristato relativo.

      L’interrupt viene abilitato col bit TMR1IE ma essendo una periferica esterna devi attivare anche (oltre al GIE) il PEIE.

      L’interrupt del timer1 avviene quando va in overflow (quindi da 0xFFFF a 0x0000 … come il timer0 ma qui sono 16 bit). Per precaricare il timer1 devi utilizzare due registri ad 8 bit TMR1L che contiene la parte bassa e TMR1H che contiene la parte alta (quindi devi scomporre il numero da caricare in due parti).

      Il timer1 deve operare come contatore anzichè come timer, quindi TMR1CS=1 e il timer1 conterà gli impulsi che giungono dal quarzetto esterno sul fronte di salita. Il timer1 ha un suo prescaler che in questo caso va settato a 1.

      La paginetta del datasheet che parla del timer1 è abbastanza chiara. Buon lavoro ;)

  10. #18 da Giovanni Bernardo il 19 aprile 2010

    Ah poi altra cosa… Invece di fare >=60 fai soltanto >59 e così per le altre verifiche

  11. #19 da Cristianoscr il 25 giugno 2010

    2#define LED RD0
    3#define CICALINO RE1

    4unsigned char TimerLed=0;

    Praticamente se ho capito bene qui hai definito Led il pin RDO, poi hai definito la variabile TimerLed. Ma se non avessi messo il define al led avresti dovuto scrivere TimerRdo? Se così fosse la lettera maiuscola si mette per indicare al programmatore di che timer stiamo parlando? E perchè hai impostato proprio unsigned? Se avessi messo signed cosa sarebbe cambiato? Grazie mille mi stai aiutando molto!

    • #20 da Giovanni Bernardo il 25 giugno 2010

      ho spiegato nelle lezioni precedenti che un define serve a definire un “simbolo”, mentre unsigned char definisce una variabile (una scatola in cui mettere dei numeri, il contenuto della scatola lo puoi cambiare a piacere nel corso del programma).

      Poi TimerLed (a parte il fatto che è una variabile) e LED sono due cose distinte e separate… Non è che il compilatore è talmente fesso da sostituire tutte le parole “led” che trova… sostituisce solo “LED” (LED senza scritto altri caratteri vicino)… eh si…. sai quante pistolate avrebbero avuto gli sviluppatori se faceva una cosa del genere!

      Unsigned definisce una variabile senza segno. Unsigned char definisce quindi una variabile senza segno di tipo char, ovvero con valori che vanno da 0 a 255, se la definivo signed voleva dire che in quella variabile ci volevo mettere valori che andavano da -127 a 128. Però a questo punto è meglio che ti leggi il Tricky C che puoi scaricare nell’area risorse.

  12. #21 da Cristianoscr il 25 giugno 2010

    Si mi sa che è meglio perchè ho detto un po di castronerie… PS ti ho lasciato una moretti da 33 qualche giorno fa, buona bevuta ;)

  13. #23 da Cristianoscr il 26 giugno 2010

    Ah ma figurati per tre euro, grazie a te che tieni in vita questo sito…

  14. #24 da erossinelli il 19 agosto 2010

    Ciao, ho seguito la tua guida ma mi rimane un dubbio. Sto lavorando con PICKit 2 e PIC16F690. Dal datasheet:
    20 MHz, 200ns instruction cycle, come previsto.
    Ho allora impostato il prescaler del tmr0 a 111 (256).
    Quindi, teoricamente, l’overflow si dovrebbe verificare ogni:
    0.2u * 256 = 51.2 us
    E l’interrupt dovrebbe essere sollevato ogni:
    51.2 u * 256 = 13ms circa
    Nella procedura dell’interrupt, utilizzo un variabile intera come contatore (che viene incrementata ogni volta), e cambio lo stato della porta RA0 se il valore del contatore supera 20. Quindi, ancora teoricamente, dovrei avere un cambio di stato ogni
    13m * 21 = 273 ms
    Ho un led collegato alla porta A0, ma il risultato non è quello aspettato: così a occhio (non ho strumenti di precisione) il led resta acceso per 1 secondo, e spento per 1 secondo, circa 4 volte il tempo atteso.
    Sapresti darmi indicazioni?

    • #25 da Giovanni Bernardo il 20 agosto 2010

      Scusa ma non sto capendo. Timer0 lo precarichi con qualche valore o lo imposti a zero? Metti il prescaler a 1:256.. ok… ma in timer0 cosa ci metti?

      • #26 da erossinelli il 20 agosto 2010

        precaricato a 0…

        • #27 da Giovanni Bernardo il 20 agosto 2010

          Si, l’interrupt con questi parametri deve scattare ogni 13mSec… Controlla che il quarzo sia collegato bene o prova a sostituirlo, se stai usando una breadboard è facile che ci sia qualche falso contatto, controlla anche che i condensatori (ceramici) dell’oscillatore siano del valore corretto (15 – 30pF). Assumo che il codice sia scritto bene.

  15. #28 da erossinelli il 20 agosto 2010

    non ho quarzi, utilizzo l’oscillatore interno!

  16. #30 da erossinelli il 20 agosto 2010

    ecco il setting:

    __CONFIG (HS & WDTDIS & PWRTEN & BORDIS & UNPROTECT);
    //Ho tolto quelli che mi davano errore..

    #define CICALINO RA0
    int c, stato;

    void main(void)
    {c=0; stato = 0;

    // imposto i registri tristato in maniera tale che tutte le porte siano configurate come pin di uscita
    TRISA=0b00000000;
    TRISB=0b00000000;
    TRISC=0b00000000;

    CICALINO = 0;

    INTCON = 0b10100000;
    OPTION = 0b00000111; //Prescaler a 256
    TMR0 = 0;
    while(1){}

    ed ecco la routine di interrupt:

    if (T0IF) {
    c++;

    if(c>20){
    if(stato == 0){
    //accendilo
    stato = 1;
    CICALINO = 1;
    }
    else{ CICALINO = 0; stato = 0; }
    c = 0;
    }
    T0IF=0; // Resetto il flag interrupt su timer 0,
    } // fine che interrupt verificatosi su timer0

    • #31 da Giovanni Bernardo il 20 agosto 2010

      Scusa un attimo… Tu prima mi dici che utilizzi il clock a 20MHz dopodichè viene fuori che utilizzi (o meglio vorresti utilizzare) il clock interno. Mi sono quindi andato a leggere il datasheet del 16F690, che è la cosa che avresti dovuto fare tu, e non mi risulta che il 16F690 abbia il clock interno a 20MHz ma ce l’ha selezionabile tra 8MHz a 31KHz (8 valori selezionabili).

      Nel datasheet, nella parte dove hai letto 20MHz, viene indicata la frequenza massima supportata dal dispositivo, non quella a cui funziona il dispositivo. Sei tu che devi dire al dispositivo come deve funzionare: lui non è in grado di leggerti nel pensiero.

      Quindi: usi il quarzo esterno a 20MHz o vorresti usare l’oscillatore interno ad 8MHz?

      Dal tuo codice non mi risulta che hai fatto la selezione del tipo di oscillatore da utilizzare. Per cui suppongo che il pic ti sia lavorando con l’oscillatore interno settato a 4MHz che è il valore di default.

      Per usare l’oscillatore interno devi specificare INTIO nella config word (elenco delle parole chiave nel file 16F685.h), dopodichè devi scegliere la frequenza di funzionamento dell’oscillatore interno settando opportunamente i 3 bit IRCF del registro OSCCON. Per usare 8MHz puoi aggiungere queste 3 righe prima del main:

      IRCF0=1;
      IRCF1=1;
      IRCF2=1;

      e magari, prima di eseguire il main, metterti in loop nell’attesa che l’oscillatore interno si stabilizzi controllando il bit HTS (HTS=1 => oscillatore stabile, HTS=0 => oscillatore non stabile)

  17. #32 da erossinelli il 21 agosto 2010

    Grazie davvero, non avevo fatto caso a queste impostazioni…

    • #33 da Giovanni Bernardo il 21 agosto 2010

      Facci sapere se così ti funziona. Magari, appena abbiamo tempo, si fa un articolo con delle linee guida per spiegare come adattare i codici all’uno o all’altro pic. Purtroppo io non è che ho a disposizione tutti i pic sul mercato e una cosa è la teoria e un’altra è la pratica :)

  18. #34 da Babbo il 18 settembre 2010

    Salve, innanzitutto complimenti per il sito.Veniamo al problema :)
    Premetto che utilizzo un pickit3 con pic 16f876 e demoboard autocostruita.
    Ho provato questo codice:

    #define XTAL_FREQ 4MHZ // questo è utilizzato dalle routine di ritardo contenute in Delay.C
    #include // contiene i nomi mnemonici di registri e porte

    // Fuses di configurazione
    __CONFIG (HS & WDTDIS & BORDIS & LVPDIS & DUNPROT & WRTEN & DEBUGDIS & UNPROTECT);

    #include “delay.c” // routine per ritardi
    #define LED RA0 // invece di scrivere RD0, scriverò LED, così mi è più facile ricordare

    int conteggio_timer_led = 0;

    // funzione principale, eseguita all’avvio del picmicro
    void main(void)
    {
    // imposto i registri tristato in maniera tale che tutte le porte siano configurate come pin di uscita
    TRISA=0b00000000;
    // Impostazione Registro Opzioni
    OPTION=0b00000100;
    // Impostazione Interrupt
    INTCON=0b10100000;
    TMR0 = 0;

    while(1)
    {
    NOP();
    NOP();
    NOP();
    }
    }

    void interrupt isr(void)
    {
    if (T0IF)
    {
    if(conteggio_timer_led = 60){
    LED = 1;
    DelayMs(100);
    LED = 0;
    DelayMs(100);
    conteggio_timer_led = 0;
    }
    else
    conteggio_timer_led++;
    // qui vanno le istruzioni da eseguire se l’interrupt è stato causato dal Timer0
    T0IF = 0; // devo resettare il flag altrimenti non mi sarà più possibile reintercettare tale interrupt
    }
    }

    mettendo un breakpoint o nel main o nella funzione dell’interrupt, il programma non si ferma mai come se non eseguisse il codice, ma nessuna parte di codice!?!? quale potrebbe essere l’errore?
    Grazie
    Saluti

    • #35 da Giovanni Bernardo il 18 settembre 2010

      Non capisco perchè cominciate tutti i vostri messaggi allo stesso modo… Prego ancora per i complimenti e ok… l’ho capito che usa una demoboard autocostruita basata sul 16F876… che devo dirle: bravo.
      Comunque sia… Nelle lezioni ho chiaramente specificato che nell’interrupt non vanno richiamate altre funzioni. DelayMs è una funzione e…. l’ha richiamata nell’interrupt: ergo: l’interrupt non viene eseguito, perchè nel frattempo che perde tempo ad eseguire un ritardo (DelayMs(100)), scatta un interrupt, per cui il ritardo non viene terminato e la funzione interrupt (che lei ben sa ogni quanto viene richiamata), viene richiamata. Si legga per bene tutto l’articolo.

  19. #36 da Babbo il 19 settembre 2010

    Buongiorno, ho iniziato in questo modo il messaggio perchè essendo il mio primo post ho ritenuto opportuno ringraziare l’autore dell’articolo, per rispetto lo faccio sempre. Mi sono accorto dopo che l’ altra domanda sulla lezione precedente era stata postata,non essendo loggato sul sito credevo non era possibile lasciare messaggi. Per la demoboard volevo solo metterla al corrente dell’ambiente di lavoro per poter capire meglio la situazione, non era assolutamente un pretesto per “esaltarmi”, non sono uno che si crede un eroe per aver collegato due fili :).
    Cmq come da consiglio, rileggo bene l’articolo, sistemo il codice e le faccio sapere. gRazie
    Saluti

    • #37 da Giovanni Bernardo il 19 settembre 2010

      Non si preoccupi, è che avendo scritto due volte la stessa cosa, cambiando solo la parte finale del messaggio… uno non sa cosa pensare. Per il problema che hai la questione è semplice:

      Metti che l’interrupt sul timer0 ti scatta ogni millisecondo, se tu nella funzione dell’interrupt ci metti un ritardo di 100millisecondi, cosa accade: quando il Delay sta conteggiando i millisecondi, dopo il primo millisecondo conteggiato (anche prima), ecco che la routine di interrupt viene richiamata di nuovo, con la conseguenza che il delay si interrompe al primo millisecondo e tutto quello che viene dopo non verrà mai eseguito. Per tale motivo è altamente consigliato non richiamare routine esterne dall’interrupt: l’interrupt deve essere il piu snello possibile: ci devono stare poche istruzioni, il minimo indispensabile da riuscire ad essere eseguito tra un interrupt e il successivo altrimenti la routine non verrà mai eseguita completamente. E poi mettere addirittura una routine di ritardo nell’interrupt è una cosa assurda anche per il motivo che un interrupt serve proprio ad evitare di utilizzare i ritardi!

      • #38 da Babbo il 19 settembre 2010

        Ok.. :)
        ho cambiato la routine ISR

        void interrupt isr(void){

        if (T0IF){

        if(conteggio_timer_led = 60){
        LED = LED^1;
        conteggio_timer_led = 0;
        }
        else
        conteggio_timer_led++;
        // qui vanno le istruzioni da eseguire se l’interrupt è stato causato dal Timer0
        T0IF = 0; // devo resettare il flag altrimenti non mi sarà più possibile reintercettare tale interrupt
        }
        }

        ma purtroppo ancora non funziona. Ho provato a mettere un breakpoit nel main, nel quale ci sono solo due NOP(), ma l’eseguzione del programma non si ferma, è come se non eseguisse nemmeno il main.

        • #39 da Giovanni Bernardo il 19 settembre 2010

          L’operatore = (un solo uguale) è un operatore di assegnazione, se vuoi fare il confronto si usa l’operatore == (doppio uguale). Poi l’incremento del conteggio_timer, fallo sempre, all’inizio dell’isr e non con l’else. Poi se l’interrupt scatta ogni millisecondo, invertire il led ogni 60 millisecondi te lo farà vedere sempre acceso, con intensità bassa ma sempre acceso.

          • #40 da Babbo il 20 settembre 2010

            L’= è stato un errore, se non ho commesso errori l’inteerupt scatta ogni 8ms (4/4 * 32 * 256) * 60 => controllo il LED ogni mezzo secondo. Ma quello che non mi spiego è che l’eseguzione del programma mettendo un break sulle NOP() del main non si ferma..

          • #41 da Giovanni Bernardo il 20 settembre 2010

            Nella routine di interrupt, appena dopo il if(T0IF) devi subito ricaricare il timer altrimenti ti resta in overflow e si blocca tutto.

          • #42 da Babbo il 20 settembre 2010

            Ho azzerato il timer, ma non è cambiato niente!!

          • #43 da Giovanni Bernardo il 20 settembre 2010

            Non lo devi azzerare, lo devi ricaricare al valore iniziale, calcolato per avere il ritardo che vuoi

          • #44 da Babbo il 21 settembre 2010

            Certo…il mio valore iniziale è TMR0 = 0

          • #45 da cironte il 1 aprile 2012

            Ciao a tutti.
            Per favore qualcuno mi aiuta a capire perchè ho questo errore dal compilatore?

            Executing: “C:\Program Files\HI-TECH Software\PICC\9.83\bin\picc.exe” –pass1 C:\Users\gigio\Desktop\varia1led\mainlednuovo_628.c -q –chip=16F628A -P –runtime=default –opt=default -D__DEBUG=1 -g –asmlist “–errformat=Error [%n] %f; %l.%c %s” “–msgformat=Advisory[%n] %s” “–warnformat=Warning [%n] %f; %l.%c %s”
            Error [314] C:\Users\gigio\Desktop\varia1led\mainlednuovo_628.c; 18.1 “;” expected

            ********** Build failed! **********

            #define XTAL_FREQ 20MHZ

            #include “delay.c” // routine per ritardi
            #include // contiene i nomi mnemonici di registri e porte

            // Fuses di configurazione
            __CONFIG (FOSC_INTOSCIO & WDTE_OFF & PWRTE_OFF & MCLRE_OFF & BOREN_OFF & LVP_OFF & CPD_OFF & CP_OFF);

            #include “settingslednuovo628.h”

            // funzione principale, eseguita all’avvio del picmicro

            void
            main (void)
            {
            settings(); // eseguo la funzione settings contenuta nel file header settings.h, così imposto le porte e i registri

            while(1) // eseguo un ciclo finito
            {
            DelayMs(100);
            LED2=1;
            DelayMs(400);
            LED2=0;
            }// Fine ciclo continuo

            La riga 18 è quella
            settings();

            ciao e grazie

          • #46 da Giovanni Bernardo il 1 aprile 2012

            L’errore sta probabilmente nella funzione settings

          • #47 da Davide il 27 dicembre 2010

            OTTIMO ALLORA! Gentilissimo, grazie del chiarimento! Perchè ho scritto un programmino per controllare un servocomando da un potenziometro ricavato da una radio per aereomodelli, e ho avuto diversi problemi di controllo fino a quando non ho tirato fuori una parte di codice dal ISR inserendolo nel main. Ti posto qualcosina almeno vedi..

            PIC 16F876A – 20000000 Hz Clock- Prescaler su 1. 0.2 uS ad incremento, ogni 50 Us ho un Overflow su TIMER0 facendo partire il TIMER0 da 8.
            Ogni 400 Overflow ho un tempo esatto di 20 mS. Tra 1 e 31 Nover (numero di overflow) attivo la semionda positiva. 1500 uS, 1,5 Ms

            void interrupt ISR (void)
            {
            if (T0IF) // L’interrupt è stato causato da un overflow del timer0 ?
            {
            Nover++; //incremento di uno il contatore del Numero degli Overflow
            if(Nover==1) //Inizio dell’onda positiva
            RB4=1;
            if(Nover==Dir) //Dir determina quando deve terminare l’onda positiva. E’ una variabile che modifico per controllare il movimento
            {
            RB4=0;
            }
            if(Nover==399)
            {
            RB4=0;
            Nover=0; }
            TMR0=8; // RICARICO Timer0
            T0IF=0; // FLAG INTERRUPT SU 0, che si setta automaticamente su 1 ad ogni overflow
            } // fine che interrupt verificatosi su timer0

            } // fine interrupt

            QUESTA VERSIONE DEL CODICE MI FUNZIONAVA MALE:…
            Però portando questa porzione di codice al main.

            if(Nover==1)
            RB4=1;
            if(Nover==Dir)
            {
            RB4=0;
            }
            if(Nover==399)
            {
            RB4=0;
            Nover=0; }

            FUNZIONAVA COME UN OROLOGIO SVIZZERO, SE VUOI MANDO PER MAIL TUTTO IL CODICE, NEL CASO VOLESSI METTERLO A DISPOSIZIONE SUL SITO.
            Saluti. Davide.

  20. #48 da Babbo il 22 settembre 2010

    Giovanni, ho provato a simulare il circuito con mplasim e tutto funziona, riesco ad entrare nella routine isr, mentre con il pickit3 no! Secondo te puo dipendere da qualche errore del circuito o prorpio dal pickit3?
    Grazie ciao

  21. #49 da Babbo il 29 settembre 2010

    Ciao Giovanni, comunque non sono ancora riuscito ad effettuare il debug con l’interrupt e il pickit3. Ho provato ad usare l’mplasim e il tutto funziona, anche programmando il pic funziona, quindi deduco che o dipende dal pickit o da quanche impostazione che manca? Sai dirmi qualcosa? grazie ciao

  22. #51 da Davide il 27 dicembre 2010

    void interrupt ISR (void)
    {
    if (T0IF) // L’interrupt è stato causato da un overflow del timer0 ?
    {
    TMR0 = 100; // Reimposto Timer0
    TimerLed++; // Incremento il Timer per il lampeggio del led
    if (TimerLed >= TEMPOLED) // Se il tempo è passato
    {
    LED=LED^1; // Inverto lo stato del led per farlo lampeggiare
    TimerLed=0; // Ricarico il timer del led per ricominciare daccapo
    }
    T0IF=0; // Resetto il flag interrupt su timer 0,
    } // fine che interrupt verificatosi su timer0
    } // fine interrupt service routine

    Volevo chiedere una precisazione, dato che l’ISR deve essere più corta possibile, non potresti eseguire una buona parte di questa funzione nel main??
    Io l’ISR lo utilizzo solo per far aumentare i contatori, che poi nel main vengono “analizzati in base al loro valore” Ad Es.[ if (contavalori==350) RB1=1; ] e così via… per poi azzerarli quando arrivano al valore limite che ho impostato.. Però sono tutte cose che faccio fare nel main.. non nell’ISR. Sbaglio a fare così?

    Non ne trarrebbe giovamento il programma se questa parte di codice venisse portata nel main??

    if (TimerLed >= TEMPOLED) // Se il tempo è passato
    {
    LED=LED^1; // Inverto lo stato del led per farlo lampeggiare
    TimerLed=0; // Ricarico il timer del led per ricominciare daccapo

    • #52 da Giovanni Bernardo il 27 dicembre 2010

      Si può fare pure nel main ma se ti fai le prove o lo metti nell’ISR o nel main non ti cambia niente: sono solo un paio di istruzioni e col quarzo da 20MHz tra un ms e l’altro puoi fare tanta roba. Se l’effetto lo vuoi ottenere all’istante, metti le istruzioni nell’ISR per avere la minore latenza possibile tra l’evento di interrupt e le operazioni da eseguire, mettendo nel main passerà piu tempo prima che le funzioni da eseguire sull’interrupt vengano eseguite. Trattandosi di un led da far lampeggiare ovviamente non andiamo di fretta e le istruzioni le ho messe li giusto per far capire… e poi sono solo una manciata di istruzioni e non ti cambia assolutamente niente. Tieni conto che questi sono esempi per far capire come/cosa fare. Io (ma non solo io, ma tutti i programmatori seri) insisto sul tenere l’ISR piu corta possibile perchè ci sono persone che ancora non hanno capito che cos’è un ‘ISR e addirittura ci mettono dentro i delay… roba da essere banditi dal mondo dell’elettronica digitale. Per non parlare di quelli che “è meglio il polling dell’interrupt” solo perchè gli interrupt non li sanno usare.

  23. #53 da Guido il 15 febbraio 2011

    Salve, come faccio a convertire (formula) la frequenza tipo: 523Hz, 1397Hz, 2093Hz, ecc. nel tempo? Trovato il tempo lo imposto nel PIC TIMER e inbase al quarzo che scielgo faccio i settaggi.
    Grazie .

  24. #57 da Guido il 16 febbraio 2011

    Per cortesia portami un esempio di come si calcola il periodo.
    Grazie

    • #58 da Giovanni Bernardo il 17 febbraio 2011

      è tanto difficile fare UNO DIVISO FREQUENZA ? Hai 500Hz ? Il periodo è 1/500 = 0,002 secondi. Ho linkato la pagina di wikipedia dove c’è scritto chiaramente.

  25. #59 da Guido il 19 febbraio 2011

    Scusami, un informazzione, solo impostando Delay si può impostare una frequenza, quindi collegando al Pic un altoparlante farlo suonare (sentire quella freq.)?
    Grazie

    • #60 da Giovanni Bernardo il 21 febbraio 2011

      Si… Ammesso che hai capito come si genera un’onda quadra, cos’è la frequenza, e quali sono le frequenze udibili dall’orecchio umano. Poi dato che gli altoparlanti hanno un’impedenza molto bassa, non puoi mettere l’uscita del pic direttamente sull’altoparlante.

  26. #61 da slavin89 il 15 marzo 2011

    buonasera a tutti..continuando nella titanica impresa di capirci qualcosa mi sono imbattuto in un nuovo ostacolo in questa lezione..e cioè la riga 5 del setting..ovvero ho capito cosa significa quella riga ma non ne capisco il motivo all’interno del programma; cioè da quello che ho capito e un valore che assegno al timer per l’accensione del led che poi andrò ad incrementare tramite la funzione timerled ++ ma non mi è chiaro perchè necessito di questi due passaggi.
    prego nella vostra bontà d’animo per spiegarmi e aiutarmi.

    complimenti di nuovo per questo capolavoro

    • #62 da Giovanni Bernardo il 16 marzo 2011

      La riga 5 di settings.h è un commento, la prossima volta cerca di essere preciso. Intendi la riga 17 dove c’è scritto:

      unsigned char TimerLed=0;

      ? Sto dichiarando una variabile di tipo unsigned char (valori da 0 a 255) che ho chiamato TimerLed e l’ho inizializzata con 0 come valore. Cos’è una variabile? Una porzione di memoria ram che uso per fare dei calcoli o per ricordami dei numeri. A cosa serve in questo programma? A mantenere il conteggio per far lampeggiare il led.
      All’inizio del corso io però l’ho detto di andarsi a leggere il Tricky C.

  27. #63 da slavin89 il 29 marzo 2011

    rieccomi qua..ciao a tutti;
    sono finalment eriuscito a creare da solo i primi due programmini descrittti fin qui però ancora una cosa non mi è chiara..le routine..in particolare non riesco a capire bene se quelle scaricabili qui dal sito sono delle routine create per questi programmi che descrivete nella guida oppure sono dei file che vanno bene inseriti in qualsiasi altro programma necessiti di routine.
    ho pro vato a vedere se ci capivo nel file di tricky ma la cosa purtroppo non mi è ancora chiara spero possiate aiutarmi

    saluti a tutti

  28. #65 da slavin89 il 29 marzo 2011

    quelle che includo con i file delay.c
    magari è errato il termine routine scusa

    • #66 da Giovanni Bernardo il 30 marzo 2011

      Quelle routine si includono qualora hai bisogno di generare ritardi nel programma che andrai a scrivere. Se non hai bisogno di ritardi non le includi. Essendo poi scritte per funzionare con l’hitech-c, le puoi usare solo con programmi scritti in hitech-C. Ovvio che sul tricky-c, ne su alcun manuale del c al mondo, potrai trovare scritto che devi includere le routine di delay… non ha senso… alcune routine si usa raggrupparle in files a parte in maniera da essere riutilizzabili anche su altri programmi.

  29. #67 da Faustino il 10 giugno 2011

    Ciao Giovanni, complimenti come sempre.
    noto che come hai già spiegato, e viene ripetuto varie volte, che anche se metto DelayMs(100) non sara’ 100 ms spaccato ma c’è un errore. nel mio caso faccio lampeggiare a 200 ms un led ma alla faccia dell’errore sarà almeno 700 ms, insomma un errore assurdo, cosi’ quando faccio lampeggiare gli altri e il cicalino mi fa’ un suono debole, insomma tutto porporzionato.
    Parlando con un mio collega che mi ha visto “giocare” mi ha detto che è tutta colpa del ICD3 che oltre il codice macchina invia anche molta schifezza, e anche della versione free-light dell’h-tech fa’ la sua parte non essendo una versione ad alto livello essendo free.

    Vorrei sapere cosa ne pensi
    Grazie di TUTTO (che è veramente tanto)
    Fausto

    • #68 da Giovanni Bernardo il 10 giugno 2011

      Quando sento dire certe cose rimango di stucco.

      Primo: la versione free dell’Hitec-C manca di alcune ottimizzazioni ma di certo non fa durare un ritardo piu del tempo dovuto, può avere qualche problema di certo, ma se il problema sta sulla versione free, sta anche su quella a pagamento, come si è dimostrato di recente con la versione 9.81.

      Secondo: Dai a me l’ICD3 e io ti do il pickit. Non capisco cosa puo voler dire “inviare schifezze”. Mettiamo che l’ICD3, un prodotto molto costoso e professionale, invii delle schifezze (che ora qui posso intendere come bit non desiderati). Allora la verifica del programma deve fallire per forza perchè compara l’hex sul pc con quello appena caricato/letto dal picmicro. La verifica ti fallisce? Non credo proprio.

      La microchip dovrebbe chiudere i battenti in entrambi i casi: uno perchè avrebbe fatto un programmatore inutile, che costa una cifra, due perchè ha acquistato l’hitech che farebbe un compilatore idiota.

      Quei delay come hai già letto sono sballati, ho fatto delle verifiche a suo tempo con l’oscilloscopio ma non ho avuto tutta questa discrepanza: nel peggiore dei casi il ritardo introdotto era del doppio, ma ho provato fino ad 8MHz, posso immaginare che salendo di frequenza l’errore aumenti sempre più. Quella routine difatti è molto vecchia ed oltre al fatto di non essere ottimizzata, era creata per girare a 2MHz come disse l’autore. Ma comunque pure a 2MHz sballa.

      Le soluzioni alternative sono varie: usa gli interrupt sui timer. Se ti servono semplici ritardi fissi, non variabili, puoi usare le routine di delay già presenti nell’hitech-C __delay_ms(costante) e __delay_us(costante) però devi definire la macro _XTAL_FREQ. Fai attenzione agli underscore. Ripeto: queste funzioni le puoi usare solo con una costante nell’argomento, non puoi usare una variabile. Altrimenti usi le routine enhanced precision che ho messo tra le librerie.

  30. #69 da marcobrucchietti il 23 settembre 2011

    Salve, ho uno starterkit2 con relativa scheda demoboard con sopra montato un PIC16F690. Ho modificato il codice della lezione, per adattarlo al pic e alla scheda.
    Come si vede dal codice lavoro con la frequenza interna del PIC, a 8 MHz. Facendo i conti per avere un interrupt ogni 1ms del timer0, ho utilizzato il prescaler a (1:32). Icicalino funziona come voluto, ne ho usato uno che emette un suono ogni volta che ha tensione. Il problema è sul led che rimanesempre acceso, non riasco a capire il perchè. Mi potete aiutare. Riporto il codice:
    #define _LEGACY_HEADERS
    #include
    #define TEMPOLED 250
    #define LED RC0
    #define CICALINO RA0
    #define _XTAL_FREQ 8000000 // Used from delay macro
    unsigned char TimerLed=0;
    __CONFIG(INTIO & // Use internal clock
    WDTDIS & // Disable watchdog
    PWRTEN & // Delay after power on enabled
    MCLRDIS & // Internal External Switch Over Mode Disable
    UNPROTECT & // Disable memory protection
    BORDIS & // Disable reset on low volage
    IESODIS & // Internal External Switch Over Mode Disable
    FCMDIS); // Disable Fail clock monitoring

    void main(void)
    {
    //Char è un tipo di dato a 8 bit, quindi può arrivare a contenere valori fino a 255, a noi serve massimo 250, quindi va bene

    OSCCON=0x70; // x111 0000 – 8 Mhz internal clock

    // Tutte le porte come output
    TRISA=0xfe;
    TRISB=0;
    TRISC=0xfe;
    // TRISD=0;
    //TRISE=0;

    // Impostazione del registro OPTION (pag.55 del datasheet)
    OPTION=0b11000100;
    // bit 0 -> Prescaler Rate Select bit 0
    // bit 1 -> Prescaler Rate Select bit 0
    // bit 2 -> Prescaler Rate Select bit 0 (1:32)
    // bit 3 -> Prescaler assegnato al Timer0
    // bit 4 -> Non importa
    // bit 5 -> Clock per Timer0 derivato da ciclo di clock interno
    // bit 6 -> Non importa
    // bit 7 -> Resistenze di pull-up su porta B disattivate

    // Impostazione Interrupt
    INTCON=0b10100000;
    // bit 0 -> RBIF – Flag interrupt su porte B
    // bit 1 -> INTF – Flag interrupt su RB0/INT
    // bit 2 -> T0IF – Flag interrupt su Timer0
    // bit 3 -> RBIE, Interrupt su porte B disattivato
    // bit 4 -> INTE, Interrupt su porta RB0/INT disattivato
    // bit 5 -> TMR0IE, Interrupt su Timer0 attivato
    // bit 6 -> PEIE, Interrupt di periferica disattivato
    // bit 7 -> GIE, Gestione Interrupt attiva

    TMR0=240; // Imposto Timer0 a 240

    while(1) // eseguo un ciclo finito
    {
    /*
    L’unica cosa che eseguo durante questo ciclo infinito, è l’inversione
    dello stato del cicalino ogni 200microsecondi, in maniera tale da generare
    un’onda quadra di 2,5KHz che, applicata al cicalino, appunto,
    mi permette di farlo suonare facendogli emettere una nota a tale frequenza
    */
    __delay_ms(1000);
    CICALINO=0;
    __delay_ms(1000);
    CICALINO=1;

    }// Fine ciclo continuo

    } // Fine main

    /*
    Questa routine, avendo l’attributo “interrupt” prima del nome della routine stessa, viene chiamata in automatico
    ogni qualvolta si verifica un interrupt. Essendo le sorgenti di interrupt di vari tipi, in questa routine dobbiamo
    capire quale elemento ha generato l’interrupt. Con le impostazioni utilizzate, Timer0 genererà un interrupt ogni
    millisecondo.
    */
    void interrupt ISR (void)
    {
    if (T0IF) // L’interrupt è stato causato da un overflow del timer0 ?
    {
    TMR0 = 240; // Reimposto Timer0
    TimerLed++; // Incremento il Timer per il lampeggio del led
    if (TimerLed >= TEMPOLED) // Se il tempo è passato
    {
    LED=LED^1;
    // Inverto lo stato del led per farlo lampeggiare
    TimerLed=0; // Ricarico il timer del led per ricominciare daccapo
    }
    T0IF=0; // Resetto il flag interrupt su timer 0,
    } // fine che interrupt verificatosi su timer0
    } // fine interrupt service ro

  31. #71 da marcobrucchietti il 24 settembre 2011

    Grazie mille per avermi risposto,ho risolto mettendo ANSEL=0.

  32. #72 da Angelo il 26 ottobre 2011

    ciao Gianni, mi unisco al coro di complimenti….ottimo lavoro!

    Una domanda: ho notato che hai messo “void interrupt ISR (void)” dopo la funzione main, ma l’interrupt non è da considerare una funzione a sè stante? Se si, non doveva essere dichiarata prima del main???

    • #73 da Giovanni Bernardo il 26 ottobre 2011

      Perchè, scritta così non è una funzione a se stante? Mi pare che il main ha una graffa di apertura e una di chiusura. Quindi dopo la graffa di chiusura posso scrivere altre funzioni. Dove sta scritto che le funzioni vanno dichiarate prima del main?

  33. #74 da giorivals il 1 novembre 2011

    Ciao Giovanni, ho un piccolo problema, di quelle cose stupide che ci perdi ore, poi ti dicono “ma no dai devi fare così!” tu lo fai e va tutto, e ti senti scemo.

    hai idea del perchè quando faccio il build del main il compilatore mi dice

    Error [499] ; 0. undefined symbol:
    _settaggi1(ledcicalino.obj)

    eppure il main è identico al tuo!

    già ti sono grato per quello che hai fatto e continui a fare, se chiarissi il dubbio ti osanno.

    ciao.

    • #75 da Giovanni Bernardo il 1 novembre 2011

      Se da quell’errore allora il codice ti assicuro che non è il mio, di sicuro hai fatto qualche modifica. Controlla che quel nome sia scritto correttamente (maiuscole/minuscole)

  34. #76 da Esposito Francesco il 3 novembre 2011

    Ciao Gianni,

    relativamente al programma di esempio, mi è sorto un dubbio. La variabile TimerLed non dovrebbe essere qualificata volatile, essendo usata dalla routine di gestione degli interrupt? Anche se con la versione Lite del compilatore Hi-Tech non dovrebbe esserci molta differenza, essendo adoperato un basso livello di ottimizzazione del codice sorgente.

    Ciao.

  35. #77 da Fabio il 15 novembre 2011

    Grazie per l’articolo
    L’avevo già letto e studiato (anche + di una volta) ma ogni volta riesco ad apprendere qualcosa di nuovo che dalla lettura precedente mi era sfuggito.
    Vorrei chiederti una cosa in merito all’inizializzazione di timer0 per ottenere un tempo preciso dell’interrupt.
    Quando viene spiegata la correzione di due valori per i cicli macchina che perdiamo durante la scrittura dobbiamo fare l’ipotesi che l’istruzione che assegna il valore a timer0 sia la prima della routine ISR?
    O comunque questa ipotesi di assegnare come prima cosa subito il valore di timer0 deve sempre essere valida altrimenti si “sporca” con il tempo trascorso dall’inizio dell’esecuzione della ISR fino a quando faccio l’assegnazione?
    Ti ringrazio
    Scusa se non mi dilungo con i dovuti complimenti ma adesso che ti scrivo sono le 2:38 di notte
    un saluto
    fabio milano

  36. #78 da Danyele il 10 febbraio 2012

    Salve, volevo segnalarle un errore di battitura relativa al Bit 7 del registro Option_Reg per l’impostazione del prescaler, in particolar modo scrive “abilita (0) oppure disabilita (1)” quando in realtà sia sul datasheet che nel programma da lei creato per la generazione del timer l’assegnazione di tale bit è corretta, quindi diventa “abilita (1) oppure disabilita (0)”.
    Saluti

  37. #79 da Danyele il 10 febbraio 2012

    Danyele :
    Salve, volevo segnalarle un errore di battitura relativa al Bit 7 del registro Option_Reg per l’impostazione del prescaler, in particolar modo scrive “abilita (0) oppure disabilita (1)” quando in realtà sia sul datasheet che nel programma da lei creato per la generazione del timer l’assegnazione di tale bit è corretta, quindi diventa “abilita (1) oppure disabilita (0)”.
    Saluti

    lo stesso errore è presente anche nel codice (ma in questo caso non ha influenza in quanto le porte B sono state impostate come uscite).
    Saluti

  38. #80 da cironte il 1 aprile 2012

    ok
    proprio mentre copiavo il file ho “notato” l’assenza della parentesi chiusa

    scusami, potevo essere più attento, ma ho continuato a focalizzare sul main
    comunque enormi complimenti

  39. #81 da cironte il 1 aprile 2012

    ciao ancora,
    cercando di inpratichirmi un pò sto “giocando” con più led, ma non riesco a crearmi i tempi giusti, allora chiedevo cortesemente una conferma se, dopo aver studiato, ho afferrato bene.
    Con il Pictimer ho conferma che, con il 16f628a a 20mhz interni, setto i primi 3 bit di OPTION_REG a 111 con ritardo di partenza del tmr0 di 61 ogni 10ms ho un intterrupt su timer0.
    Ora prendendo ad esempio i listati della lezione 5, se dico che TEMPOLED è 250, che incremento TimerLed ogni volta di uno, cioè ad ogni interrupt, allora al valore di TEMPOLED ci arrivo dopo 2500ms. giusto?
    Eppure non è giusto, perchè i led non si accendono con i tempi che desidero.
    cioa e grazie ancora

  40. #82 da gas_1988 il 14 maggio 2012

    Salve,
    sto provando a fare un timer (da implementare poi in un altro programma) ed ho qualche piccolo problema.
    In sostanza ho degli errori di visualizzazione sul display e non riesco a far fermare il timer quando arriva a zero

    #define XTAL_FREQ 20MHZ
    #include
    __CONFIG (HS & WDTDIS & PWRTEN & BORDIS & LVPDIS & DUNPROT & WRTEN & DEBUGDIS & UNPROTECT);

    #include “settings.h”
    #include “delay.c”
    #include “lcd.c”

    char minuti,secondi;
    int millisec;
    char maxmin=59, maxsec=59;
    int maxmilli=999;

    void main(void)
    {
    settings();
    LCD_INIT(); // ricordarsi sempre di inizializzare l’lcd
    LCD_CLEAR();
    millisec=999;
    secondi=20;
    minuti=0;
    while(1)
    {
    LCD_GOTO(1,2);
    LCD_PUTUN(minuti);
    LCD_GOTO(1,3);
    LCD_PUTS(“:”);
    LCD_PUTUN(secondi);
    LCD_GOTO(1,6);
    LCD_PUTS(“:”);
    LCD_PUTUN(millisec);

    }// Fine ciclo continuo
    }

    void interrupt ISR (void)
    {
    if (T0IF)
    {
    TMR0=100;
    millisec–;
    if(millisec==0)
    {
    millisec=maxmilli;
    secondi–;
    if(secondi==0){
    secondi=maxsec;
    minuti–;
    }
    }
    if(minuti==0 && secondi==0 && millisec==0)
    INTCON=0x00;
    else
    T0IF=0;
    }
    }

    Questo è il listato potreste aiutarmi??? sto impazzendo :D
    Grazieee ;)

  41. #83 da Guido8311 il 1 luglio 2013

    Salve sono nuovo e voglio farvi i complimenti x il corso
    premetto che sono un neofita nella programmazione dei pic io ho un pic 16f628a e vorrei pilotare un led rgb x visualizzare tutti i colori ma il mio pic ha solo una uscita pwm, in rete ho letto che si può creare anche via software tramite gli interrupt e il timer ma nn ho proprio idea del ragionamento da fer x realizzarlo se qualcuno può aiutarmi

  42. #84 da chip_x il 10 ottobre 2013

    Caffè pagati.

  43. #86 da feffe88 il 28 ottobre 2013

    Davvero complimenti per queste lezioni. Sono ORO per un neofita come me.
    Ti vorrei porre una domanda: nel caso volessi programmare il PIC per farlo stare in sleep a tempo indefinito e far si che si possa svegliare solo con un interrupt esterno sul pin apposito INT (che vuol dire credo di aver capito ricevere un valore 1 su quell’ingresso, giusto?) come potrei fare?
    In particolare per lo sleep devo fare impostazioni particolari? il risveglio è automatico se imposto l’interrupt? Inoltre per far si che il pin apposito a ricevere interrupt esterni (INT) sia correttamente settato cosa devo fare? devo impostarlo come input giusto?
    Potresti farmi qualche esempio di script?

    Ti ringrazio infinitamente in anticipo per la tua gentilezza e per la disponibilità! :-)

  44. #87 da sbtndr il 11 aprile 2014

    Ciao a tutti sono un appassionato di microcontrollori e sono alle prime armi. Avevo bisogno di un aiuto per quanto riguarda il funzionamento e la gestione degli interrupt con l’assembler. Ho fatto un programma con un led che si accende e si spegne nel programma principale e un altro led che si attiva con la generazione di un interrupt. La mia domanda è come fare funzionare interrupt e programma principale con il TMR0 in modo da poter fare lampeggiare 2 led a frequenza differente. Grazie a tutti

  45. #88 da iw5cdf il 22 aprile 2014

    Mi seviva rinfrescare un po il mio skill con i picmicro per fare un lavoretto per casa…
    Grazie ragazzi per i vostri manuali.
    Caffè appena pagato.

  46. #89 da stefranz il 24 marzo 2015

    Grazie davvero per queste dispense… sono un neofita totale nella programmazione dei pic e in un attimo sono riuscito a fare progettini molto belli.

    Ho un problema. Preso e tradotto in XC8 la gestione dell’interrupt e le librerie del display ho fatto un cronometro con 3 tasti start/stop/reset. Nulla di che come complessità ma ho il problema che resta indietro.
    In pratica genero un interrupt ogni 400us che incrementa una variabile tick(unsigned char). Quando arriva a 25 incrementa centesimi -> secondi -> minuti, mette a 1 la variabile isUpdate e si riazzera.
    Nel while se isUpdate=1 rimette a isUpdate=0 e con una sprintf(time, “%02d:%02d.%02d”, min, sec, cent) creo la stringa che mando al display.

    Verificato con un cronometro perdo più di10 secondi ogni minuto…un eternità. Come mai???

    Il pic è un PIC16F628A, display sulla porta B tasti sulla A. Compartatore A/D disabilitato. Quarzo a 20MHZ prescaler a 8 TMR0=6 (o 8 non cambia quasi nulla)

    Il progetto è fatto su breadbord!

    Grazie per l’aiuto!
    Stefano

  47. #90 da rockerbip il 10 novembre 2016

    Ottimo articolo Giovanni….. ti sei proprio meritato qualche caffè !! Bravo ! :-D

Devi essere collegato per lasciare un commento.

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.
Creative Commons BY-NC-ND 2.5
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.