Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

Pic16F84A e Bottone Toggle

Raccolta di codici sorgenti

Moderatore: Foto UtentePaolino

0
voti

[1] Pic16F84A e Bottone Toggle

Messaggioda Foto Utentecryptos » 27 apr 2010, 1:19

Salve, l'obiettivo del circuito è quello di fare in modo che quando schiaccio il bottone il led si accende, quando lo rischiaccio il led si spegne e così via... implementare una sorta di flip-flop toggle. Quindi ho costruito questo semplice schemino e scritto il programma in allegato.
Il problema è che non funziona come dovrebbe, nel senso che il led durante il tempo che schiaccio il bottone commuta velocemente stato e quindi il suo prossimo stato sarà del tutto casuale (dipendente dal tempo che ho tenuto schiacciato il pulsante). Come dovrei modificare il programma e/o lo schema ?

Grazie!

Codice: Seleziona tutto
#include <16F84.h>

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES HS                     
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading

#use delay(clock=6000000)

void main()
{
   unsigned int led_on = 0;
   set_tris_a(1); //Set A0 as input
   while(1){
      if(led_on == 1 && input(PIN_A0)) {
         led_on = 0;
         output_low(PIN_B1);
      }
      if(led_on == 0 && input(PIN_A0)){
         led_on = 1;
         output_high(PIN_B1);
      }
   }
}
Allegati
Immagine.png
Immagine.png (22.85 KiB) Osservato 2581 volte
Avatar utente
Foto Utentecryptos
0 2
 
Messaggi: 32
Iscritto il: 7 apr 2010, 22:46

0
voti

[2] Re: Pic16F84A e Bottone Toggle

Messaggioda Foto Utentecryptos » 27 apr 2010, 1:23

penso di aver trovato la soluzione da solo :)
basta mandare il PIC in loop infinito mentre il bottone e schiacciato.

Grazie comunque!
Avatar utente
Foto Utentecryptos
0 2
 
Messaggi: 32
Iscritto il: 7 apr 2010, 22:46

0
voti

[3] Re: Pic16F84A e Bottone Toggle

Messaggioda Foto UtenteTardoFreak » 27 apr 2010, 2:35

La tua soluzione e' valida, basta inserire la linea
Codice: Seleziona tutto
while(input(PIN_A0));

e lui si blocca in attesa del rilascio.
Ma non e' una soluzione elegante.
Altra soluzione e' creare una variabile che memorizzi lo stato precedente del pulsante.

Partiamo dall' inzio.

In generale il programma di un microcontrollore e' un loop infinito all' interno del quale vengono svolti alcuni processi. Affinche' ogni processo possa essere svolto, ogni processo deve evitare come la morte la presenza di loops che possano bloccare l' esecuzione. Bisogna quindi scrivere i processi in modo che nessuno di questi possa bloccare l' esecuzione del loop principale. Per questo scopo si usano dei flags che possono essere delle variabili booleane, magari dei soli bit, che si ricordano di alcune cose. Io di solito, se non ho problemi di memoria, uso dei bytes e finito il chiasso. :mrgreen:
Creo un programma tipo questo:
Codice: Seleziona tutto
// dichiarazione variabili globali visibili a tutto il programma

void main(void)
{
  // dichiarazione variabili visibili solo al main

  // inizializzazione hardware
  // init. processo 1
  // init. processo 2
  // init. ...
  // init. processo n

  // Loop generale

  while(1)
  {
    // processo 1
    // processo 2
    // ...
    // processo n
  }
}

In pratica si eseguono i vari processi in modo che sembri che vengano eseguiti contemporaneamente.
Nel tuo caso devi considerare la transizione da 1 a 0 di un ingresso. Il tuo processo deve quindi ricordarsi lo stato che l' ingresso aveva nel ciclo precendente, vedere se e' cambiato e, nel caso sia cambiato, fare un qualcosa (commutare l' uscita). Per semplificare il discorso io faccio conto di avere una funzione che mi legge l' ingresso che chiamero' leggi_ingresso() che mi rende un valore 0 o 1 che corrisponde allo stato dell' ingresso (sai che fantasia!). Allo stesso modo usero' una funzione che chiamero' scrivi_uscita che, indovina un po', mi scrive il valore di una variabile (0 o diverso da 0) nella mia uscita. Tu poi, con calma, sotituirai queste due funzioni con le operazioni necessarie per leggere l' ingresso e scrivere l' uscita.
E' ovvio che serve una variabile che tenga memoria dello stato precedente dell' ingresso, quindi la dichiariamo come StPr1.
Per evitare casini, nell' inizializzazione del processo leggeremo lo stato del pulsante. Nessuno dice che il pulsante deve per forza essere in stato inattivo nel momento in cui il micro viene acceso. Puo' essere che il micro venga acceso mentre il pulsante e' premuto. Quindi ci mettiamo tranquilli e lo leggiamo almeno una volta prima di iniziare tutto l' ambaradan del ciclo principale. Ora siamo tranquilli e possiamo pensare a cosa fare nel nostro processo. Ma vediamo a che punto siamo con il programma prima di andare avanti:
Codice: Seleziona tutto
// dichiarazione variabili globali visibili a tutto il programma

void main(void)
{
  // dichiarazione variabili visibili solo al main
  char StPr1;  // stato precedente delll' ingresso nel processo 1

  // inizializzazione hardware
  // init. processo 1
  StPr1 = leggi_ingresso();

  // init. processo 2
  // init. ...
  // init. processo n

  // Loop generale

  while(1)
  {
    // processo 1
    // processo 2
    // ...
    // processo n
  }
}

Ora viene il bello. Dobbiamo fare qualcosa solo se il nostro pulsante e' passato da iniattivo ad attivo, in tutti gli altri casi passiamo oltre, skippiamo tutto e lasciamo l' esecuzione agli altri processi. Quindi faremo qualcosa se StPr1 = 1 (stato precedente inattivo) e se lo stato attuale e' a 0 (attivo). Per lavorare tranquillo mi creo una variabile StAtt1 che mi rappresenta lo stato attuale dell' ingresso e che aggiorno all' inizio del processo. In ogni caso, sia che decidiamo di fare qualcosa o no, dovremo ricordarci, alla fine del processo, di memorizzare lo stato dell' ingresso nello stato precedente. Il nostro programma diventera':
Codice: Seleziona tutto
// dichiarazione variabili globali visibili a tutto il programma

void main(void)
{
  // dichiarazione variabili visibili solo al main
  char StPr1;   // stato precedente dell' ingresso nel processo 1
  char StAtt1;  // stato attuale dell' ingresso nel processo 1

  // inizializzazione hardware
  // init. processo 1
  StPr1 = leggi_ingresso();

  // init. processo 2
  // init. ...
  // init. processo n

  // Loop generale

  while(1)
  {
    // processo 1
    StAtt1 = leggi_ingresso();
    if ((StPr1 == 1) && (StAtt == 0))
    {
      // eseguo la gestione della transizione
    }
    StPr1 = StAtt1;  // Aggiorno lo stato precedente

    // processo 2
    // ...
    // processo n
  }
}


Da adesso in poi la strada e' in discesa: possiamo pensare solo a quello che dobbiamo fare, cioe' a commutare l' uscita. Eppero' non abbiamo ancora deciso se l' uscita, all' atto dell' accensione del micro, dovra' essere a 1 o a 0. Poco male, inseriamo l' inizializzazione dell' uscita in questo momento al posto giusto, cioe' prima della prima lettura (scusa il gioco di parole :mrgreen: ) dell' ingresso. Decidiamo che questa, all' atto dell' accensione, vale 0 (led spento).
Cio' detto,
Passiamo a commutare questa benedetta uscita. Beh, qui possiamo fare la cosa in modo canonico con una if ... else che controlla lo stato precedente o possiamo fare i fighetti negando l' uscita. Facciamo i fighetti (anche perche' e' tardi e sto per andare a nanna :mrgreen: ) ma facciamolo bene e creiamoci anche una variabile che tiene il valore dell' uscita. Quindi la dichiariamo, la inizializziamo etc etc. ed otteniamo questo sorgente:
Codice: Seleziona tutto
// dichiarazione variabili globali visibili a tutto il programma

void main(void)
{
  // dichiarazione variabili visibili solo al main
  char StPr1;   // stato precedente dell' ingresso nel processo 1
  char StAtt1;  // stato attuale dell' ingresso nel processo 1
  char Uscita1; // stato dell' uscita nel processo 1

  // inizializzazione hardware
  // init. processo 1
  Uscita1 = 0;
  scrivi_uscita(Uscita1);
  StPr1 = leggi_ingresso();

  // init. processo 2
  // init. ...
  // init. processo n

  // Loop generale

  while(1)
  {
    // processo 1
    StAtt1 = leggi_ingresso();
    if ((StPr1 == 1) && (StAtt == 0))
    {
      // eseguo la gestione della transizione
      Uscita1 != Uscita1;
      scrivi_uscita(Uscita1);
    }
    StPr1 = StAtt1;  // Aggiorno lo stato precedente

    // processo 2
    // ...
    // processo n
  }
}


E il gioco e' fatto.

Domani mattina riguardero' quello che ho scritto e se ci sono degli errori lo correggero'. Non massacratemi se ho scritto fesserie ma sono molto stanco. :?
"La follia sta nel fare sempre la stessa cosa aspettandosi risultati diversi".
"Parla soltanto quando sei sicuro che quello che dirai è più bello del silenzio".
Rispondere è cortesia, ma lasciare l'ultima parola ai cretini è arte.
Avatar utente
Foto UtenteTardoFreak
73,9k 8 12 13
-EY Legend-
-EY Legend-
 
Messaggi: 15754
Iscritto il: 16 dic 2009, 11:10
Località: Torino - 3° pianeta del Sistema Solare

0
voti

[4] Re: Pic16F84A e Bottone Toggle

Messaggioda Foto Utentecryptos » 27 apr 2010, 11:46

Grazie della risposta! Effettivamente penso che la tua soluzione sia piu' elegante rispetto a quella dei loop infiniti. Ho trovato un solo errore in quello che hai scritto: Uscita1 != Uscita1; penso che volessi scrivere Uscita1 = !Uscita1. Comunque grazie, seguendo il tuo codice sono arrivato a questa soluzione:

Codice: Seleziona tutto
char leggi_ingresso(){
   return input(PIN_A0);
}

void scrivi_uscita(char value){
   if (value == 0)
      output_low(PIN_B1);
   else
      output_high(PIN_B1);
}
   
void main(){
   char StPr1;   
   char StAtt1; 
   char Uscita1;
   Uscita1 = 0;
   scrivi_uscita(Uscita1);
   StPr1 = leggi_ingresso();
   while(1){
      StAtt1 = leggi_ingresso();
         if ((StPr1 == 1) && (StAtt1 == 0)){
            Uscita1 = !Uscita1;
            scrivi_uscita(Uscita1);
         }
      StPr1 = StAtt1;  // Aggiorno lo stato precedente
   }
}
Avatar utente
Foto Utentecryptos
0 2
 
Messaggi: 32
Iscritto il: 7 apr 2010, 22:46

0
voti

[5] Re: Pic16F84A e Bottone Toggle

Messaggioda Foto UtenteTardoFreak » 27 apr 2010, 11:59

Grazie per aver trovato l' errore.
Aggiungo solo un paio di particolari.
1 - Con questa struttura creare reli logiche diventa facilissimo, basta leggere gli ingressi all' inizio del processo, eseguire le operazioni una dietro l' altra senza loops ed aggiornare le uscite alla fine del processo.
2 - Questo modo di trattare i processi permette di creare macchine a stati in modo impeccabile. Ogni processo ha la sua variabile che ne indica lo stato, ed ogni processo ha la sua istruzione "case" che decide quale stato deve essere eseguito. Alla fine di ogni passata lo stato puo' uscire cambiato o meno, al passaggio successivo il flusso di esecuzione saraì diverso ma la scansione restera' la stessa: tutti i processi verranno eseguiti in modo concorrente.

Nota estetica: come avrai notato io uso mettere le graffe in un certo modo che deriva dalla programmazione in Pascal e permette di dare migliore visibilita' grafica ai blocchi. Ora, io non so da quanto tempo programmi ma se sei agli inizi ti suggerisco di considerare questo metodo. Quando i programmi diventano grandi ti permette di leggerli e gestirli meglio.
"La follia sta nel fare sempre la stessa cosa aspettandosi risultati diversi".
"Parla soltanto quando sei sicuro che quello che dirai è più bello del silenzio".
Rispondere è cortesia, ma lasciare l'ultima parola ai cretini è arte.
Avatar utente
Foto UtenteTardoFreak
73,9k 8 12 13
-EY Legend-
-EY Legend-
 
Messaggi: 15754
Iscritto il: 16 dic 2009, 11:10
Località: Torino - 3° pianeta del Sistema Solare


Torna a Firmware e programmazione

Chi c’è in linea

Visitano il forum: Nessuno e 15 ospiti