Corso di programmazione PICMicro in C – Lezione 6 – Collegamento di pulsanti, pilotare un led in on/off


Scritto da Giovanni Bernardo in data 28 settembre 2009

led_e_pulsantiIn questa lezione vedremo come vanno collegati (e rilevati) correttamente i pulsanti al nostro microcontrollore. Scriveremo un semplice programma per pilotare 2 led mediante 2 pulsanti: un pulsante accenderà un led non appena premuto (e quindi il led si spegnerà non appena lo si rilascia).

L’altro pulsante, invece, piloterà il secondo led in on/off: premendolo la prima volta, il corrispondente led rimarrà acceso, premendolo la seconda volta, il led si spegnerà.

Anche se questa applicazione sembra una cosa banale, tutto ciò che c’è dietro è molto importante e costituisce la base da cui partire per innumerevoli applicazioni: come si collega un pulsante, il perchè di tale collegamento e il perchè di come se ne legge lo stato logico. Il led è ovviamente una scusa, come in tutte le altre lezioni, per verificare lo stato di funzionamento del programma: è ovvio che un pin che eroga un segnale logico può essere utilizzato per pilotare il relè che accende le luci della stanza o che attiva l’elettrovalvola dell’acqua. Vedremo poi come poter collegare un relè, nel frattempo cerchiamo di capire tutti i retroscena per non ritrovarci con circuiti che si comportano in maniera “bislacca”.

Diamo uno sguardo allo schema che andremo a realizzare:

schema_lezione_6_thumbnail

Come vedete, abbiamo due led (led 1 e led 2), collegati rispettivamente alle porte RD2 e RD3 con le loro resistenze di limitazione di corrente: fin qui nulla di strano, abbiamo già visto questo nelle lezioni precedenti.

Nello schema vediamo anche due pulsanti (del tipo normalmente aperto), identificati come BTN1 e BTN2, e collegati rispettivamente alle porte RD0 e RD1.

Ovviamente, se voi utilizzate una particolare scheda di sviluppo, potete collegare led e pulsanti alle porte che volete, basterà poi modificare il codice sorgente per poterlo adattare al vostro circuito. Lo scopo di avere un file header con unicamente i settaggi serve proprio a questo: avere sott’occhio tutti i parametri di configurazione in un unico luogo, in maniera tale da sapere subito dove mettere mano per fare degli adattamenti, lasciando invariato il programma principale: per adattare il programma all’una o all’altra scheda o all’uno o all’altro chip, basterà selezionare un settings.h diverso o modificare quello esistente.

Cerchiamo di capire come sono collegati questi due pulsanti e soprattutto perchè sono collegati così e non in un altro modo.

Prendiamo in analisi BTN1 (le stesse considerazioni varranno ovviamente anche per l’altro pulsante): vediamo che in condizioni di riposo (pulsante non premuto), il pin RD0 riceve uno stato logico alto tramite la resistenza R4 (da 1KΩ); premendo il pulsante tale linea viene portata a massa: pertanto in condizioni di pulsante premuto, il pin RD0 si troverà a livello logico basso.

La resistenza R4 (così come anche la R5, ma anche la R1 che tiene in condizione di livello logico alto il pin MCLR), viene chiamata resistenza di pull-up. Pull-Up vuol dire letteralmente: tirare verso l’alto. Tale resistenza, difatti, tiene a livello logico alto il pin a cui è collegata.

In alcune applicazioni si trovano anche le resistenze di pull-down, ovvero che tengono un pin in condizione di livello logico basso.

Tenere un pin di input a livello logico alto è importante per tanti motivi: innanzitutto si evita di rimanere il pin in condizioni “volanti”: la porta che utilizziamo come ingresso deve sempre ricevere uno stato logico ben definito: non lo si può lasciare collegato al nulla perchè non si sa come si comporterebbe; inoltre potrebbe captare disturbi che causerebbero comportamenti inaspettati e in alcuni casi anche dannosi reset.

Si capisce, inoltre, che il pin (RD0 nel nostro esempio), per essere tenuto in condizioni di livello logico alto, non può essere  collegato direttamente all’alimentazione: in tal caso, premendo il pulsante, l’alimentazione sarebbe cortocircuitata a massa (distruggendo tutto il circuito), inserendo invece una resistenza (R4) sulla linea che porta il livello alto, tale problema non sussiste in quanto la resistenza limita la corrente di cortocircuito quando il pulsante viene premuto.

Questo è pertanto il modo corretto di collegare un pulsante su un pin:  in condizioni di riposo, arriva sempre livello logico alto tramite una resistenza di pull-up, quando il pulsante viene premuto, questi fa arrivare il livello logico basso , per cui nel software che andremo a scrivere, per verificare se il pulsante è stato premuto, dovremo intercettare un livello logico basso sul pin al quale il pulsante risulta collegato.

Parlando del registro OPTION nella lezione precedente, abbiamo accennato al bit RBPU che abilita le resistenze di pull-up sulle porte B: se intendiamo utilizzare dei pulsanti collegati ad una o più porte B (attenzione: questo discorso  sul PIC16F877 vale solo per le porte B),  non sarà necessario inserire le resistenze di pull-up come in questo schema: basterà semplicemente collegare il pulsante da un lato al pin e dall’altro a massa e quindi abilitare tale bit nel registro option (oppure scrivere: RBPU=1;).

Dal momento che le porte D non hanno questa caratteristica (resistenze di pull-up integrate nel microcontrollore), siamo costretti ad inserire noi le resistenze nel circuito.

Vediamo ora come realizzare il software che si prefigge di fare quanto spiegato nell’introduzione. Come nella lezione precedente, anche in questa i settaggi saranno inclusi in un file header (settings.h) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define BTN1    RD0    // pulsante 1
#define BTN2    RD1    // pulsante 2
#define LED1    RD2    // led 1
#define LED2    RD3    // led 2
 
void settings(void)
 {
 TRISA=0;            // Tutte output
 TRISB=0;
 TRISC=0;
 TRISD=0b00000011;     // Le porte RD0 e RD1 devono essere input (1) perchè vi sono collegati i pulsanti, le altre output (0)
 TRISE=0;
 
 // All'avvio i 2 led devono essere spenti
 LED1=0;
 LED2=0;
 }

Tramite le direttive #define, abbiamo assegnato nuovi nomi ai pin che ci interessano, in maniera tale che sarà più facile ricordarseli.

Vi è quindi la funzione settings (che verrà poi richiamata nel main) che imposta porte e registri. In questa applicazione non abbiamo bisogno di nessun registro particolare a parte i tristato per le porte. Vediamo che, come sempre, settiamo tutte le porte non utilizzate come uscita, ma questa volta per le porte D dovremo fare un’eccezione: le porte RD0 (primo bit del registro tristato D, ovvero il bit più a destra) e RD1 (secondo bit del registro tristato D) dovranno essere impostate come ingressi (input) dal momento che devono ricevere il segnale inviato dal pulsante, pertanto per queste due porte, il relativo bit nel registro tristato deve essere impostato a 1:

11
TRISD=0b00000011;     // Le porte RD0 e RD1 devono essere input (1) perchè vi sono collegati i pulsanti, le altre output (0)

Come vedete i bit 1 (RD0) e 2 (RD1) li impostiamo a 1 (input), gli altri a 0 (output).

Infine, facciamo in modo che all’avvio del programma (momento in cui la funzione settings viene eseguita), i due led si trovino spenti, pertanto mettiamo a zero i relativi bit (le relative porte) :

15
16
LED1=0;
LED2=0;

Bene, passiamo al programma principale (main.c):

Nota: questa parte è stata aggiornata grazie ai preziosi suggerimenti sull’antirimbalzo forniti da Mauro Laurenti.

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
#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
__CONFIG (HS & WDTDIS & PWRTEN & BORDIS & LVPDIS & DUNPROT & WRTEN & DEBUGDIS & UNPROTECT);
#include "delay.c" // routine per ritardi
#include "settings.h" // settaggi 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)
    {
    // controllo pulsante 1
    if (!BTN1) // se pulsante1 premuto (quando è premuto, porta il pin allo stato logico basso)
	{
	DelayMs(100); // ritardo per antirimbalzo
	if (!BTN1) // se dopo 100ms il pulsante è ancora premuto, non si tratta di un rimbalzo
		{
		LED1=1; // accendo led 1
		}
	}
   else
	{
	LED1=0; // se pulsante 1 non premuto, spengo led 1
	}
 
   // controllo pulsante 2
   if (!BTN2) // se pulsante 2 premuto
	{
	DelayMs(100); // antirimbalzo
	if (!BTN2)
	        {
	        LED2=LED2^1; // inverto lo stato del led
		}
	}
    }// Fine ciclo continuo
 } // Fine main

Partiamo a spiegare  dalla riga 13 (penso che tutto ciò che viene prima abbiate imparato a capire a cosa serve): qui stiamo controllando lo stato della porta RD0 (BTN1). Il segno ! davanti a un bit indica negazione, in pratica scrivere

13
if (!BTN1)

equivale a scrivere:

13
if (BTN1==0)

è la stessa, identica, cosa.

Allo stesso modo, quando si deve verificare una condizione logica vera (livello logico alto), anzichè scrivere:

if (port==1)

in genere si utilizza la forma compatta:

if (port)

Ricordo, inoltre, ancora una volta (se non avete letto il manuale Tricky C) che nel linguaggio C, la verifica di uguaglianza viene effettuata col doppio uguale (==) e non con l’uguale singolo come in molti altri linguaggi. In C l’uguale singolo è un operatore di assegnazione e viene utilizzato unicamente per assegnare un valore ad una variabile, se vogliamo confrontare due variabili allora dobbiamo usare il doppio uguale.

Come detto prima, quindi, per verificare che il pulsante sia stato premuto, dobbiamo controllare se la porta a cui è collegato si trova a livello logico basso: se è così, il led1 viene acceso (LED1=1;).

Il ritardo ( DelayMS(100); ) inserito prima ha una funzione importantissima: si tratta di un antirimbalzo software. Cos’è un antirimbalzo?

Bisogna sapere che, quando si preme un pulsante, essendo esso costituito da un meccanismo a molla che mette in contatto due lamelle (che chiudono il contatto), quando lo si preme e poi lo si rilascia, le lamelle continueranno a vibrare (rimbalzare) per alcuni millisecondi andando in on/off centinaia, migliaia di volte nell’arco di un tempo brevissimo: in queste condizioni, essendo il nostro programma molto veloce, rileverebbe continuamente queste transizioni on/off, accendendo e spegnendo il led migliaia di volte al secondo, con il risultato che, alla fine di questo “trambusto” non si saprà se il led rimarrà acceso o spento (si verifica quindi una situazione random). Per tale motivo viene inserito un piccolo ritardo software il quale ci eviterà di ricontrollare lo stato del pulsante prima che i rimbalzi siano finiti. Spesso al posto del termine antirimbalzo si utilizza il termine anglosassone antibounce.

Generalmente un antirimbalzo di 100millisecondi è sufficiente (dipende comunque molto dalla qualità, dal meccanismo e dallo stato di usura del pulsante), molto spesso conviene aumentare tale ritardo per lavorare in sicurezza, oppure ricorrere ad espedienti hardware per evitare i rimbalzi del pulsante.

Come vediamo, dopo l’attesa di 100millisecondi, si ritorna a controllare che il pulsante sia premuto, se è così, allora il led si accenderà.

Per come è strutturata questa parte del programma, il led1 si accenderà non appena si preme il pulsante BTN1, appena lo si rilascia, il led1 si spegnerà, dal momento che abbiamo messo la condizione alternativa (else) che verifica la “non pressione” del pulsante.

Passiamo alla riga 24 nella quale andiamo a controllare lo stato del pulsante BTN2: qui ci comportiamo in maniera differente: invertiamo lo stato del led ad ogni pressione del pulsante, in tal modo premendo BTN2 la prima volta, il led 2 si accenderà e rimarrà acceso (difatti non andiamo a verificare la condizione di “pulsante non premuto” come abbiamo fatto per l’altro pulsante), premendo la seconda volta il pulsante, il led2 si spegnerà e rimarrà spento fino alla prossima pressione.

Come vedete è molto semplice interfacciare un pulsante: si utilizzano le resistenze di pull-up, oppure, se la porta a cui colleghiamo il pulsante ha la possibilità di attivare delle resistenze di pull-up interne, le cose sono ancora più facili a livello circuitale. Inoltre non dobbiamo mai dimenticarci di inserire un piccolo ritardo software per evitare i rimbalzi del pulsante.

Segue un video che illustra il funzionamento del circuito (led rosso=led1, led blu=led2):

come potete notare, al minuto 52, si nota chiaramente che il led2, che deve rimanere acceso, invece si spegne: è chiaro che l’antirimbalzo di 100mS non è stato sufficiente, in questo caso avremmo dovuto aumentarlo un po’ (es: 200/250mS : i pulsanti che ho utilizzato nel video in effetti sono un po’ scadenti).

Scarica i sorgenti, programma compilato e schema elettrico:

File di supporto alla sesta lezione del corso di programmazione picmicro in C (115)

Lasciate commenti, segnalate errori e/o suggerimenti, e offriteci un caffè se ne avete la possibilità.

» Vai all’ indice delle lezioni e delle risorse del corso di programmazione

Articoli che potrebbero interessarti:

L'articolo ti è piaciuto o ti è stato utile per risolvere un problema? Supporta e mantieni in vita questo sito, ci basta soltanto un caffè o una birra

  1. #1 da S.D.R. il 9 ottobre 2009

    Ciao , è da un pò che seguo questo sito e devo dire che è ben fatto .
    In particolare sto seguendo questo corso di programmazione pic in C , finalmente qualcuno che fa un bel corso :-)
    Complimenti perchè sei veramente chiaro nelle spiegazioni !

  2. #2 da S.D.R. il 9 ottobre 2009

    Ri ciao , primo problema :
    Io sto usando perchè al momento sono in possesso del piccolino PIC16F84 .
    Per le prime prove credo che vada bene lo stesso cambiando le direttive sulle porte .
    Il problema è che il compilatore non mi accetta i fuses di configurazione , infatti se elimino quella riga lo compila con successo .
    Come devo configurare i fuses su questo pic ?
    Grazie in anticipo .

  3. #3 da Gianni il 9 ottobre 2009

    Ciao S.D.R.,
    innanzitutto ti ringrazio dei complimenti.
    E’ ovvio che la config word del 16F877 non funziona con il 16F84 e ti dia errori: l’877 ha molte opzioni in più.

    Prova con:
    __CONFIG (HS & WDTDIS & PWRTEN & UNPROTECT);

    I nomi mnemonici dei registri del 16F84 li trovi nel file pic1684.h contenuto nella cartella include del compilatore (C:\Programmi\HI-TECH Software\PICC\PRO\9.65\include\). Ma alla fine i nomi utilizzati sono sempre gli stessi, l’unica differenza è che alcuni PICMicro hanno delle periferiche e altri no.

    Vedere che le lezioni vengono seguite anche con PIC differenti è interessante. Che programmatore utilizzi? Ti ricordo che comunque il 16F84 non è più in produzione ed è stato sostituito dal 16F628, che tra l’altro ha molte periferiche in più a bordo.

    Se vuoi puoi richiedere la freedom2 (scheda di sviluppo molto completa, l’ho appena ricevuta e devo dire che in giro penso non ci sia qualcosa di così ben fatto) e un pic16F877 a Mauro Laurenti (http://www.laurtec.com) previa donazione libera (Mauro sta facendo davvero un lavoro ottimo, a livelli altissimi).

    Te lo consiglio anche perchè più in la vorrei spiegare come si utilizzano l’UART per la comunicazione RS232, il PWM e i convertitori A/D, tutte periferiche che sul 16F84 non ci sono (anche se l’RS232 e il PWM potrebbero essere emulati via software).

  4. #4 da S.D.R. il 9 ottobre 2009

    uso il modulo icd2 , di solito quando la uso al lavoro e la connetto e mi vede subito il tipo di pic che è on board ma il 16f84 che sto usando ora non lo vede infatti mi da errore di programmazione :
    “ICD0161: Verify failed (MemType = Program, Address = 0×0, Expected Val = 0×2801, Val Read = 0×3FFF)
    ICD0275: Programming failed.”
    Avevo tolto la riga fuses ma ho settato con mplab il configuration bits .
    Grazie per l’aiuto .
    Più avanti se ingrano bene con il C mi attrezzo meglio per le prove ,per ora mi accontento di questo pic e una bread board .

  5. #5 da Gianni il 10 ottobre 2009

    I dispositivi della Mid-Range Family (come lo sono il 16F84 e il 16F877) non hanno la caratteristica dell’auto-detect: non possono essere riconosciuti in automatico dal programmatore: bisogna selezionarli manualmente. Adesso, io non ho mai usato l’ICD2, per cui su questo non posso esserti d’aiuto…
    In realtà quando crei il progetto e compili con mplab, selezioni da li il picmicro e quindi la compilazione avviene sempre correttamente: con l’ICD2 non lo so che passaggi fai, ma da qualche parte deve per forza esserci un sistema per selezionare manualmente il picmicro che stai utilizzando, dal momento appunto che alcuni picmicro non possono essere auto-rilevati. Potrei dirti di provare a includere all’inizio del codice:
    #define _16F84A
    fa una prova e facci sapere

  6. #6 da S.D.R. il 10 ottobre 2009

    Ciao , Ho deciso che lunedì prendo il 16f628 , al lavoro li uso e quindi posso prenderne uno ;-)
    Intanto ho 2 domandine da farti :
    1) il file DELAY.C lo hai creato tu o dove lo hai reperito ?
    2) per sapere che direttive Fuses posso usare a seconda del pic che sto usando dove devo guardare ?
    Ho provato tramite MPLAB dal menù “configure” -> “configuration bits…” a vedere che tipo di fuses ci sono sul pic ma usa nomi leggermente diversi dal C .
    Grazie in anticipo .

  7. #7 da Gianni il 10 ottobre 2009

    1 – il file delay.c si trova dappertutto, se fai una semplice ricerca con google ne trovi altri, ce ne sono anche versioni molto più precise che però occupano più memoria programma, per i miei scopi va bene quello li che si trova nelle lezioni. Un ottimo sito dove reperire parecchie routine scritte in C per i picmicro è http://www.microchipc.com

    2 – i fuses di configurazione si trovano nel datasheet di ogni pic, il nome mnemonico io in genere lo reperisco spulciando nei files .h della cartella include del compilatore.

    Si, puoi settare i fuses anche da mplab, in quel caso non viene tenuto conto delle impostazioni date dalla funzione config, i nomi indicati sono leggermente diversi si, ma non c’entra nulla: quelli li inseriti dalla funzione __config vengono presi dal file 16f84.H : in effetti gli si può dare il nome che uno vuole, basta che le locazioni di memoria e i valori siano quelli, in pratica voglio dire che puoi anche cambiare i nomi nei file .h, anche se non è una buona regola di programmazione dal momento che quei nomi sono riconosciuti universalmente

  8. #8 da S.D.R. il 17 ottobre 2009

    Ciao ;

    Più leggo quetsa guida più mi rendo conto che è proprio ben fatta , devo farti veramente i complimenti .

    Presto ti offriro il caffè perchè oltre ad avere fatto una ottima guida sei anche sempre disponibile a rispondere alle mie domande , grazie !
    Una domanda riguardo al file include settings.h , io posso includere al suo interno anche la riga 15,16 e 21 in maniera tale che se cambio anche tipo di PIC e quarzo vado a variare tutti le impostazioni da quel file ?

    A quando la prossima lezione , e che argomento tratterai ?

    Ciao e buon WeekEnd .

  9. #9 da Gianni il 17 ottobre 2009

    Ciao e grazie.
    Non capisco quali righe 15,16 e 21 intendi da voler includere in settings.h. Lo scopo principale di includere i settaggi in un file esterno è proprio questo: permettere di aggiustare velocemente il codice per poterlo utilizzare con un picmicro differente (a patto che si utilizzino periferiche che esistono in entrambi i picmicro). Quello che potresti includere in settings.h sono le righe da 1 a 4 presenti in main, questo si, in main rimani soltanto include “settings.h”.
    Nella prossima lezione vorrei trattare i display lcd, però dovrei fare prima una introduzione sui display alfanumerici, perchè capendo bene come si utilizzano e come funzionano, si può imparare anche ad utilizzare altri tipi di display (tipo a matrice di punti, che non hanno i font inclusi nella rom) – anche se io in effetti non ho ancora avuto la possibilità di utilizzare un display a matrice di punti e rientra in una di quelle cose che vorrei sperimentare appena ho disponibilità tempo/denaro. Non ti so dire quando scriverò il prossimo articolo ma non penso passerà più di una settimana da adesso ;)

  10. #10 da S.D.R. il 20 ottobre 2009

    Ciao , ho incominciato a fare un po’ di esperimenti ;
    Ho provato ha fare in modo che ogni volta che premo uno dei due pulsanti un cicalino(non auto oscillante) emetta un beep , la cosa ha funzionato però volevo un tuo parere su come ho modificato il listato .

    BUZ1è il cicalino .

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

    while(1)
    {
    // controllo pulsante 1
    if (!BTN1) // se pulsante1 premuto (quando è premuto, porta il pin allo stato logico basso)
    {
    DelayMs(100); // ritardo per antirimbalzo
    if (!BTN1) // se dopo 100ms il pulsante è ancora premuto, non si tratta di un rimbalzo
    {
    LED1=1; // accendo led 1
    DelayUs(200);
    BUZ1=1;
    DelayUs(200);
    BUZ1=0;
    DelayUs(200);
    BUZ1=1;
    DelayUs(200);
    BUZ1=0;
    DelayUs(200);
    BUZ1=1;
    DelayUs(200);
    BUZ1=0;
    }
    }
    else
    {
    LED1=0; // se pulsante 1 non premuto, spengo led 1
    }

    // controllo pulsante 2
    if (!BTN2) // se pulsante 2 premuto
    {
    DelayMs(100); // antirimbalzo
    if (!BTN2)
    {
    LED2=LED2^1; // inverto lo stato del led
    DelayUs(200);
    BUZ1=1;
    DelayUs(200);
    BUZ1=0;
    DelayUs(200);
    BUZ1=1;
    DelayUs(200);
    BUZ1=0;
    DelayUs(200);
    BUZ1=1;
    DelayUs(200);
    BUZ1=0;
    }
    }
    }// Fine ciclo continuo
    } // Fine main

  11. #11 da Gianni il 20 ottobre 2009

    Ok, va bene, anche se però si cerca di non ripetere mai 2 volte la stessa sequenza di operazioni: se ciò si verifica, allora si preferisce ricorrere ad una funzione. Nel tuo caso puoi creare una nuova funzione ( che scriverai dopo la chiusura del main):

    void suona(void)
    {
    for (char a=0; a<6; a++)
    {
    BUZ1=BUZ1^1;
    DelayUs(200);
    }
    }

    Anzichè ripetere il delay e l’inversione di BUZ1, effettuo un ciclo for: quello contenuto nel ciclo si ripeterà per 5 volte (da a=0 fino a che a rimane minore di 6, incrementando a di 1 ad ogni iterazione).
    Nel punto in cui vorrai far suonare il cicalino basterà che scrivi semplicemente:
    suona();
    In questo modo hai creato una funzione che, oltre a farti risparmiare un sacco di codice (e quindi memoria sul picmicro), la puoi anche riutilizzare facilmente in altre parti del codice senza riscrivere tutte le istruzioni.
    Prova questa modifica e fammi sapere, l’ho scritta a volo e potrei aver commesso qualche errore.

  12. #12 da S.D.R. il 21 ottobre 2009

    Non suona ,il ciclo for l’ho meso quì ,

    ……………}// Fine ciclo continuo
    ………} // Fine main
    void suona(void)
    ………{
    ………for (char a=0; a<6; a++)
    ……………{
    …………….BUZ1=BUZ1^1;
    …………….DelayUs(200);
    ……………}
    ………}

    ed ho richiamato il ciclo al posto delle ripetizioni come hai detto tu .
    Che ho sbagliato ?

  13. #13 da Giovanni Bernardo il 21 ottobre 2009

    Hai ragione. I problemi sono due (anzi dovrebbero essere 3, strano che il compilatore non ti dia errore). Innanzitutto la durata del ciclo: 6 è troppo bassa, facendo delle prove lo si sente bene da 20 in su, seconda cosa (di cui mi ero dimenticato) l’inversione del bit sul buzzer, non so perchè, non funziona! Con i led funziona, con il cicalino no, non ho la minima idea del perchè si verifica questo e purtroppo non possiedo un oscilloscopio per verificare cosa accade.

    Il codice così scritto funziona:

    void suona(void)
    {
    for (char a=0; a<40; a++)
    {
    BUZ1=1;
    DelayUs(200);
    BUZ1=0;
    DelayUs(200);
    }
    }

    (nota che come ultima opzione ho messo BUZ1=0 e non tutto il contrario, altrimenti alla fine del ciclo il cicalino, anche se non lo senti suonare, rimane alimentato fisso).

    Mi sembra strano che il compilatore non ti dia errore. Ti spiego brevemente il perchè: dal main richiamerai una funzione (suona) che però viene definita soltanto dopo il main, per cui al momento della compilazione, il compilatore si troverà davanti un simbolo (suona appunto) che non è stato definito per cui non lo riconosce e dovrebbe dare errore (così come si dichiarano le variabili, così andrebbero dichiarate pure le funzioni).

    La soluzione è quella di dichiarare i “prototipi di funzione” per fare in modo che il compilatore non si trovi davanti dei nomi che non sa cosa sono. Nel tuo caso prima del main scriverai:

    void suona(void);

    In pratica questo è un prototipo di funzione: stai dichiarando una funzione senza…. funzioni! Stai semplicemente dicendo al compilatore che, se prima o poi trova una scritta “suona” non deve spaventarsi perchè l’hai già avvisato prima della sua esistenza… (molto maccheronicamente).

    Dopo il main scriverai quindi la funzione vera e propria. E’ sempre bene dichiarare i prototipi di funzione per tutte le funzioni, anche del main e per quelle dell’ISR. Fin’ora nelle lezioni non ho accennato a questa cosa dal momento che non se n’è verificata l’occasione.
    Facci sapere

  14. #14 da Antonio il 22 ottobre 2009

    Ciao, io stò perdendo la testa con questa lezione;
    uso mcc18 in mplab ed utilizzo il pic 18f452; in fase di compilazione se metto “#include ” mi dà errori di riconoscimento dei vari RD0..etc e mi richiede un “lvalue”…se metto “#include ” chiaramente non lo trova perchè uso MCC18 ed mcc18 non include questo file….quindi non riesco ad andare avanti…mi dai un aiuto?

    Grazie tante, Antonio

  15. #15 da Antonio il 22 ottobre 2009

    era…

    #include -p18f452.h- (evito minore/maggiore sennò nn si vedono)

    oppure

    #include -pic.h-

  16. #16 da Antonio il 22 ottobre 2009

    Ciao, ho provato ad installare High Tech nella versione lite e ad utilizzare un pic 16f677; lì ho trovato “pic.h” e l’ho inserito con include nel programma, provo a compilare e mi dà questi errori:
    Error [192] C:\Users\Antonio\Desktop\Corso microchip Milano\prova high tech C compiler\settings.h; 11.1 undefined identifier “TRISD”
    Error [192] C:\Users\Antonio\Desktop\Corso microchip Milano\prova high tech C compiler\settings.h; 12.1 undefined identifier “TRISE”
    Error [192] C:\Users\Antonio\Desktop\Corso microchip Milano\prova high tech C compiler\settings.h; 15.1 undefined identifier “RD2″
    Error [192] C:\Users\Antonio\Desktop\Corso microchip Milano\prova high tech C compiler\settings.h; 16.1 undefined identifier “RD3″
    Warning [361] C:\Users\Antonio\Desktop\Corso microchip Milano\prova high tech C compiler\main.c; 9.1 function declared implicit int
    Error [192] C:\Users\Antonio\Desktop\Corso microchip Milano\prova high tech C compiler\main.c; 13.6 undefined identifier “RD0″
    Error [192] C:\Users\Antonio\Desktop\Corso microchip Milano\prova high tech C compiler\main.c; 18.1 undefined identifier “RD2″
    Error [192] C:\Users\Antonio\Desktop\Corso microchip Milano\prova high tech C compiler\main.c; 23.1 undefined identifier “RD2″
    Error [192] C:\Users\Antonio\Desktop\Corso microchip Milano\prova high tech C compiler\main.c; 27.6 undefined identifier “RD1″
    Error [192] C:\Users\Antonio\Desktop\Corso microchip Milano\prova high tech C compiler\main.c; 32.1 undefined identifier “RD3″

    Perchè tutto ciò?

    Grazie in anticipo

  17. #17 da Giovanni Bernardo il 22 ottobre 2009

    Se non erro, nel C18 per identificare le porte si utilizza una cosa del genere:

    PORTAbits.RA1 = 1;

    Cioè spefichi prima (PORTXbits) (dove X è la lettera del banco porte che interessa), seguito dal punto e quindi dalla singola porta, per i C18 non puoi assolutamente includere i file headers dei C16.

    Purtroppo non so dirti di più dal momento che il C18 non ho ancora iniziato a provarlo. Sul sito di Mauro Laurenti puoi trovare un ottimo tutorial in pdf sul C18:

    http://www.laurtec.com/Italiano/Tutorial/C18%20step%20by%20step/Tutorial.html

    E’ fatto molto bene e spiega passo passo come installare e compilare, leggilo per bene e sono sicuro che poi ti funzionerà tutto.

  18. #18 da S.D.R. il 22 ottobre 2009

    Ciao , ora funziona bene .
    Allora , ho provato a mettere void suona(void) prima del void main(void) e il ciclo for l’ho lasciato dopo la chiusura della main (modificandolo) come hai detto tu , non so , forse ho capito male quello che hai scritto , perchè non funzionava .

    Allora ho messo proprio tutto il ciclo for prima del void main(void) e così funziona però quando davo alimentazione al micro il cicalino rimaneva alimentato fino alla pressione di uno dei due tasti , quindi dopo aver suonato non rimaneva più alimentato e funzionava correttamente .
    Allora ho pensato di settare dal file settings.h BUZ1=0 (all’avvio) e così ho risolto il problema .
    Però ho fatto di più , ho messo tutto il ciclo for dentro al file settings.h e la cosa ha funzionato .
    Devo dire che l’ANSI C è tutto un altro mondo, ti semplifica moltissimo la vita , tempo fa avevo tentato a fare qualcosa con l’ASSEMBLER ma non ho avuto ottimi risultati poi ho scoperto il VISUAL PARSIC altro bellissimo programmino ,ma ha le sue limitazioni perchè sono anni che non lo aggiornano .

    Il prossimo esperimento che voglio fare è quello di comandare un motorino passo passo , con 3 pulsanti ,AVANTI , STOP, INDIETRO .
    Pilotare il motorino non credo sia difficile è come accendere in sequenza 4 led , però è il caso di mettere un ULN2003 perchè il pic non credo che riesca a alimentare il motorino , la cosa difficile sarà la logica dei tasti .

    Grazie per l’aiuto .

  19. #19 da Giovanni Bernardo il 22 ottobre 2009

    Il C è tutta un’altra storia, è molto potente e semplifica di molto la vita, tieni conto che in caso di “emergenze” puoi comunque includere porzioni di assembler nel codice utilizzando le direttive #asm #endasm.
    L’idea del comando di un motorino passo passo è interessante, forse ci faccio un pensiero. Non posso altro che consigliarti di dare un occhio all’ottima spiegazione di Vincenzo Villa sui motori stepper:

    http://www.vincenzov.net/tutorial/passopasso/stepper.htm

    E quindi non può mancare una citazione al grande Sergio Fiocco, questa lezione sul comando di uno stepper da picmicro in C ti può essere molto ma mooolto utile:

    http://www.fisertek.it/index_000013.html

    Se fai esperimenti, facci sapere, può tornare utile a tutti e non si sa mai che lo inseriamo in un prossimo tutorial.

  20. #20 da merloindiano il 30 gennaio 2010

    ciao giovanni!

    grazie per quello che stai facendo.

    dopo qualche tempo che leggo a destra e sinistra grazie alla tua guida mi è tornata la volgia di programmare in c anziche in assembler.
    io non sono un esperto, ma credo di avere un idea sul perche l’inversione del bit del buzzer non funzioni. (vedi commento 13)
    volendo provare un pwm multicanale via software non riuscivo ad accendere in ASM più di un led alla volta (stavo usando la scheda inclusa nel pickit2) fintanto che un forum mi ha detto di prestare attenzione al fatto che se setto un solo bit alla volta come facevo io il micro legge lo stato della porta, lo modifica e lo ripropone in uscita. se il carico è relativamente alto o il tempo è molto piccolo potrebbe succedere che anche se avevo appena portato a 1 l’uscita il sistema legga 0 per cui le istruzioni di bit set e bit clear non funzionavano. il problema si è risolto impostando tutta la porta in un solo colpo. Potrebbe essere che invertire un bit invece BUZ1=BUZ1^1 invece che assegnargli un valore con BUZ1=1 generi due codici diversi e nel primo caso si presenta il problema.
    la mia è solo un’ipotesi da verificare ma sono curioso di provare entrambe le situazioni e magari di provare a mettere tra il buzzer e il piedino di uscita qualcosa (come una porta o altro) per vedere che cosa succede.

    grazie di nuovo per tutto! proseguo a studiare!

    Mirco Ughi

  21. #21 da Giovanni Bernardo il 30 gennaio 2010

    Ciao e Grazie.
    In realtà ciò che dici dovrebbe essere corretto: dipende dalle istruzioni generate e quindi dalla velocità con cui si cambia lo stato. Difatti sui pic18 per controllare le porte, oltre ai registri TRIS e PORT hanno introdotto un terzo registro chiamato LAT che è direttamente connesso al latch che controlla lo stato della porta fisica. In realtà i registri LAT e PORT dovrebbero svolgere la stessa, identica, funzione ma operano a livelli diversi: può difatti succedere che impostiamo un pin a livello alto ma in realtà il circuito esterno, a causa di problemi, potrebbe non permettere questo per cui il pin rimane basso e quindi avremo che per quel pin i registri PORT e LAT porteranno due valori discordanti. Il cambiare stato del pin con un’istruzione o con l’altra potrebbe portare a questo tipo di problemi. Sarebbe interessante verificare con un’oscilloscopio quello che succede.

  22. #22 da McGyver86 il 7 febbraio 2010

    Ciao Giovanni, volevo renderti partecipe di una situazione strana che mi è capitata.. Premetto che per seguire le tue fantastiche lezioni utilizzo un pic16f84A con relativa demoboard da me costruita.. Vengo al dunque.. Volevo pilotare un led in on/off (premi il pulsante una volta e il led si accende, lo premi una seconda volta e il led si spegne), quindi mi è tornata utilissima questa tua lezione, dopo aver opportunamente modificato il tuo sorgente per il mio PICmicro, compilato e programmato il pic, mi succede che il led non si accende alla pressione del pulsante… Convinto di aver fatto le cose per il meglio mi rimetto a controllare il sorgente e mi accorgo che c’è qualcosa che non va nella riga 27:
    (!BTN2)

    come spieghi tu stesso il fatto di mettere il punto esclamativo davanti al simbolo BTN2 significa negazione… Allora ho cancellato il punto esclamativo
    perchè mi sembrava più giusto scrivere:

    se il pulsante viene premuto, inverti lo stato del led, quindi ho corretto in questo modo:

    while(1)
    {

    // controllo pulsante 2
    if (BTN2) // se pulsante 2 premuto
    {
    DelayMs(100); // antirimbalzo

    {
    LED2=LED2^1; // inverto lo stato del led
    }
    }
    }// Fine ciclo continuo

    naturalmente l’ho cancellato anche nella riga 13 altrimenti non funzionava..

    Non so sinceramente se questo fatto possa dipendere dal tipo di PICmicro utilizzato, quindi chiedo cortesemente delucidazioni in merito visto che sono un nuovissimo entrato nella programmazione di PICmicro.

    Grazie del tuo splendido lavoro e del tempo dedicatomi, un saluto!!

  23. #23 da Giovanni Bernardo il 7 febbraio 2010

    metto !BTN2 perchè il pulsante ha una resistenza di pullup e premendolo deve portare il segnale di massa quindi è corretto come ho scritto io, se a te funziona all’inverso vuol dire che è il circuito che hai montato tu che è fatto in maniera diversa, oppure stai utilizzando un pulsante normalmente chiuso anzichè uno normalmente aperto. Come già ho specificato, il modo più corretto di collegare un pulsante è questo qui: resistenza di pullup e premendo mi deve portare il segnale di massa.
    Se utilizzi un pulsante normalmente chiuso con questo circuito, premendo lo apri e ti porta il positivo anzichè la massa e quindi la logica del software va cambiata.

  24. #24 da McGyver86 il 7 febbraio 2010

    hai perfettamente ragione, pensavo di aver montato la demoboard con 4 pulsanti e relative resistenze di pull-up, mentre invece le avevo collegate a massa.. che figuraccia… scusami se ti ho fatto perdere tempo…

    Un saluto!!

  25. #25 da zuperone il 2 marzo 2010

    Ciao Giovanni, stai facendo veramente un bellissimo lavoro che sto apprezzando moltissimo. E’ da un po’ di tempo che volevo iniziare ad usare il C per la programmazione dei PICMicro, ma non mi decidevo mai. Quando ho trovato queste pagine mi sono immediatamente deciso ad iniziare perché trovo talmente chiaro e ben esposto l’argomento che non farlo mi torna impossibile. Vorrei chiederti una cosa in merito a questa lezione:
    - alla pressione di P2 il tuo intento è quello semplicemente di cambiare lo stato del led, ma così facendo si provoca un lampeggio del led se il pulsante viene mantenuto premuto. Probabilmente anche nel tuo filmato hai tenuto premuto il pulsante un po’ più a lungo facendo pensare ad un doppio impulso mentre probabilmente si tratta di una piccola svista.
    Non so se vedo giusto. Cosa ne pensi?
    Un saluto ed ancora complimenti.
    Claudio

  26. #26 da Giovanni Bernardo il 2 marzo 2010

    Tenendo premuto si verifica la commutazione continua, quindi il led lampeggia. Nella maggior parte delle applicazioni è sempre così. Quando si preme un pulsante normalmente non ci si tiene il dito sopra per più di mezzo/1 secondo. Se vuoi fare in modo che non si verifica il lampeggio se uno tiene premuto, ti setti un flag che blocca la verifica del pulsante fino a quando non cambia stato. Nel filmato la questione è diversa, ho messo un ritardo troppo piccolo per l’antirimbalzo.

  27. #27 da Luca il 5 marzo 2010

    Ciao, innanzitutto ti volevo ringraziare, il problema della lezione 3 è totalmente risolto, soltanto per il fatto dell’877a ho perso le speranze. Ti rispondo qui suggerendoti una miglioria al programma di sopra: inutile l’antibounce sul primo pulsante, perchè sollevato il dito in ogni caso il led tende a spegnersi superato il transitorio glitch del segnale. Sopratutto, la parte relativa al secondo pulsante la scriverei aggiungendo una soluzione al problema del lampeggio dovuto alla pressione prolungata del pulsante, basta una riga di codice:

    if (!BTN2)
    {
    DelayMs(antib);
    if (!BTN2)
    {
    LED2=LED2^1;
    do{ }while(!BTN2); //<—finchè il tasto rimane premuto si resta intrappolati in un ciclo vuoto, appena si solleva il dito il programma continua. Non ho provato ma un while è equivalente.
    }
    }
    }
    }

    Grazie mille ancora e Buona serata,
    L

  28. #28 da Giovanni Bernardo il 5 marzo 2010

    Bene, è un’ottima soluzione :)
    Mi fa piacere vedere persone che partecipano.

    Per l’877A è alquanto strano, non sarà rotto? O forse il programmatore che usi non lo supporta appieno

  29. #29 da Luca il 5 marzo 2010

    Non ne ho idea, poichè mi trovo benissimo col 628A, non è che mi ci sono soffermato molto, il programmatore funziona bene, a dir la verità anche il controllo và a buon fine, e facendo una lettura del codice interno vedo (anche con i miei occhi) che è tutto flashato… comunque penso che per comodità, appena avrò caricato il mio registro MNEY=0b00110010 vedrò di prendere il PICKit3 per sedare ogni dubbio e capire che era solo uno stupido errore ed avrò speso solo tanti euro per capire che era “quello stupido flag”…

    In ogni caso sarà sicuramente un problema risolvibile, magari quando non ho che fare; diciamo che mi sto fissando parecchio sul 628 perchè trovo sia un ottimo allenamento ad imparare a programmare un po di tutto, così quando dovrò cambiare pic per qualsiasi motivo, il tutto sarà molto più indolore!

    Comunque se ti può interessare, stavo leggendo la lezione successiva ho letto le tue “quotazioni”, ti consiglio artronicpl su ebay, ho fatto un mega acquisto, i 628 li paghi meno di 1.50€ gli lcd da 16×2 2.50€, 877a 4.00€…conveniente vero?

  30. #30 da Giovanni Bernardo il 5 marzo 2010

    €0×32 come prezzo per il pickit3 è buono, anche perchè lo si trova generalmente a qualcosa di più, ma solo perchè viene fornito con una scheda di sviluppo.
    La Polonia, dopo la Cina, è abbastanza conveniente e hai la roba in una settimana. Solo che gli LCD 16×2 non stanno meno di 4 euro (escluse spedizioni) devi aver letto male o hai una quotazione vecchia. In Cina comunque a quel prezzo li si trovano. E’ interessante questo venditore perchè ha pure questi formati strani, come ,8×1 a caratteri giganti, questo si che è caruccio e potrebbe sostituire in maniera degna un paio di display a 7 segmenti in un voltmetro ad esempio. Salvato tra i preferiti.

    Le mie comunque sono quotazioni medie, a volte preferisco spendere qualcosa di più per lo stesso prodotto ma averlo subito. Alcune cose, se comprate nei negozietti di elettronica, poi, costano da paura.

    Prova ad usare pure il 16F819 che, con lo stesso numero di pin, ti ritrovi pure il convertitore A/D che ti ci puoi divertire parecchio.

(non verrà pubblicata)
  1. Ancora nessun trackback

Fusion theme by digitalnature | Articoli (RSS) e Commenti (RSS) ^