Contagiri da 120 a 18000 Giri al Minuto

clapperContagiri con Microcontrollore PIC18F per Misurare i Giri al Minuto di un Motore. Dispone di Autosetting per Avere un Valore Preciso da 120 a 18000 Giri…


 
 
 
 




 

INTRO

Per contare i giri al minuto di un motore vi sono due soluzioni, la prima è quella di misurare i giri in un certo periodo di tempo, come ad esempio calcolare i giri in 10  secondi e poi moltiplicare per 6 ottenendo i giri al minuto, l’altra soluzione è quella di misurare il periodo di un segnale con un fronte di salita ad ogni giro e poi ricavare dal periodo i numeri di giri al minuto. La prima soluzione ha lo svantaggio che si misurano i giri medi del motore, anche se necessita un software molto semplice. La seconda soluzione richiede un sistema per autosettare la precisione del sistema visto che se aumenta il numero di giri, diminuisce il tempo tra i fronti di salita e quindi necessita un prescaler sempre più veloce, di contro, se si usa un prescaler fisso molto veloce, se i giri sono pochi al minuto, il timer counter può andare in overflow.

 




 

SEGNALE GENERATO IN BASE AI GIRI

Per avere un segnale periodico che dipende dalla velocità del motore si usa una versione molto semplice di encoder ottico formato da un sensore infrarosso composto da un led infrarosso e un transistor ricevitore infrarosso, in modo tale da creare un raggio infrarosso. Se si polarizza il diodo in modo tale da creare un fascio infrarosso, e se si polarizza il transistor ricevitore, si avrà che quando si interrompe il raggio il transistor è un ramo aperto, se invece non si interrompe il raggio si ha un corto circuito o viceversa, dipende dal tipo utilizzato. Con questo transistor, si può creare una rete di pull up o pull down e avere un segnale alto o basso quando cortocircuitato o viceversa.

encoder ottico infrarosso contagiri emettitore rivecitore

Per interrompere il fascio luminoso si possono utilizzare varie soluzioni; la prima, quella in figura, consiste in un disco in qui vi è una finestra o foro. Il disco può essere sia trasparente che nero, l’importante che la finestra o foro sia di diversa composizione dell’intero disco. La soluzione più semplice è quella di avere un disco nero nel quale vi è un solo foro, in questo modo il raggio infrarosso è interrotto normalmente mentre non lo è al passaggio della finestra, generando un segnale simile a quello di figura.

L’altra soluzione sarebbe quella di usare un semicerchio di materiale nero, in questo modo per metà giro il raggio è interrotto e per metà no, creando un segnale con duty cycle di circa il 50%. Un’altra soluzione è quella di avere una linguetta di materiale assorbente sull’asse dal motore in modo tale da avere il raggio sempre presente e interromperlo solo al passaggio di questa linguetta. Il problema di queste soluzioni e che possono squilibrare il motore e creare oscillazioni. Si può adottare qualsiasi soluzione, l’importante è creare un solo fronte di salita ad ogni giro.

Per quanto riguarda il sensore infrarosso, può essere formato da emettitore e ricevitore incapsulati in un unico package, come quelli dei vecchi mouse a rotellina o come quelli delle stampanti, oppure può essere formato da un led infrarossi e un ricevitore. La connessione è in ogni caso quella di figura.

Per quanto riguarda il ricevitore, c’è ne sono di due tipi; il primo tipo può comportarsi da corto circuito se è colpito da raggio infrarosso e circuito aperto in caso contrario, mentre il secondo tipo, ha un comportamento contrario a quello appena descritto. Al fine del progetto non ha importanza quale si usa, vista l’alternanza di fasi con raggio e senza, si viene a creare in ogni caso un segnale ad onda quadra.

 

PRINCIPIO DI FUNZIONAMENTO

Il principio di funzionamento è molto semplice, si va a misurare il tempo tra due fronti di salita e quindi conoscendo questo tempo con la semplice operazione 60/(tempo tra due fronti) si possono conoscere i giri al minuti del motore. Per misurare il tempo tra due fronti si utilizza il modulo Capture con uno dei timer counter presenti. Conoscendo il numero nel contatore, la frequenza e il prescaler si può conoscere il tempo tra due fronti di salita se si resetta il timer counter ad un fronte e si cattura il valore al fronte successivo.

Il problema di questa soluzione è che utilizzando un prescaler fisso si riescono ad avere valori precisi solo per un certo range di giri al minuto. Quindi è necessario un codice che setta il prescaler in base ai giri. Visto che si utilizza un timer counter da 16 bit, il valore massimo contabile è 65535 dopo di che si va in over flow. Se il valore conato è minore della metà del valore contabile, si potrebbe ottenere un valore ancora più preciso dimezzando il prescaler. Se si ha un valore di prescaler basso e il timer counter va in overflow bisogna aumentare il prescaler.

Per conoscere il valore di giri al minuti, la formula è la seguente:

formula calcolo giri
 




 

CODICE

Il codice per realizzare il contagiri è il seguente:

sbit LCD_RS at RB0_bit;                 //Definizione libreria LCD
sbit LCD_EN at RB1_bit;
sbit LCD_D4 at RB2_bit;
sbit LCD_D5 at RB3_bit;
sbit LCD_D6 at RB4_bit;
sbit LCD_D7 at RB5_bit;                 //Fine definizione libreria LCD

sbit LCD_RS_Direction at TRISB0_bit;    //Configurazione I/O per LCD
sbit LCD_EN_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB4_bit;
sbit LCD_D7_Direction at TRISB5_bit;    //Fine configurazione I/O per LCD

unsigned short int Stato;               //variabile che tiene conto del cambio di precisione
float Giri;                             //variabile per convertire da conteggio a giri
bit E;                                  //bit per eseguire i cambi di stato
unsigned int GiriMin;                   //variabile per contenere il valore di giri
char TxtGiri[7];                        //testo che contiene i Giri al minuti

void display(float Giri){               //subroutine per visualizzare il valore dei giri
  GiriMin = Giri;                       //passo da floating point a intero
  WordToStr(GiriMin, TxtGiri);          //trasformo da intero a stringa
  Lcd_cmd(_LCD_CLEAR);                  //pulisci lcd
  Lcd_out(2, 3, TxtGiri);               //visualizzo la stringa
  Lcd_out(1, 3, "Giri/min");            //mostra la stringa "giri/min" sulla riga 1
  T1CON.TMR1ON = 1;                     //riattivo il timer counter
}                                       //fine subroutine

void TMR_INT() iv 0x0008 ics ICS_AUTO { //interrupt di overflow
  PORTB.B7 = 1;                         //accendo il led di overflow
  T1CON = 0b10110001;                   //timer 1 con prescaler 8
  CCP1CON = 0b00000101;                 //capture ogni fronte di salita
  TMR1H = 0;                            //resetto il timer counter 1
  TMR1L = 0;
  CCPR1 = 0;                            //resetto il valore di capture
  PIR1.CCP1IF = 0;                      //reset del flag di capture
  PIR1.TMR1IF = 0;                      //resetto il flag di overflow
  Stato = 1;                            //riporto lo Stato a quello iniziale
}                                       //fine subroutine interrupt

void main() {                  //programma principale

CCP1CON = 0b00000101;          //modalità capture ogni fronte di salita

TRISB = 0;                     //portb uscite
TRISC.B2 = 1;                  //ingresso modulo CCP1
Stato = 1;                     //inizializzo stato=1 (presc 8 e capture ogni fronte)

RCON.IPEN = 0;                 //disabilito la priorità degli interrupt
INTCON.GIE = 1;                //attivo gli interrupt
INTCON.PEIE = 1;               //attivo gli interrupt delle periferie

PIE1.TMR1IE = 1;               //attivo l'interrupt di overflow di tmr1
IPR1.TMR1IP = 0;               //disabilito la priorità dell'interrupt

Lcd_init();                    //inizializza lcd
Lcd_cmd(_LCD_CLEAR);           //pulisci lcd
Lcd_cmd(_LCD_CURSOR_OFF);      //disattiva cursore lcd

T1CON = 0b10110001;            //timer 1 on, clock interno prescaler 8

  while(1){                                   //ciclo infinito
    if(PIR1.CCP1IF==1){                       //se si ha l'evento di capture
      PORTB.B7 = 0;                           //resetto il led di overflow
      PIR1.CCP1IF = 0;                        //resetto il flag di capture
      T1CON.TMR1ON = 0;                       //tmr1 off per non avere overflow
      E = 1;

      switch(Stato){                          //switch per selezionare lo stato
        case 1:while(E==1){                   //esegui una volta sola se Stato = 1
                 if(CCPR1 < 32700){           //se stato=1 e valore piccolo
                   T1CON = 0b10100001;        //timer 1 on, clock interno, prescaler 4
                   Stato = 2;                 
                 }                            //prescaler equivalente 4
                 else{                        //se il valore è opportuno
                   Giri = 7500000/CCPR1;      //giri = ((1/tempoconteggio*)60)/CCPR1
                   display(Giri);             //visualizza i giri
                 }
                E = 0;                        //resetta "E" così da non rieseguire
               }

        case 2:while(E==1){                   //esegui una volta sola se Stato=2
                 if(CCPR1 < 32700){           //se stato=2 e valore piccolo
                   T1CON = 0b10010001;        //timer 1 on, clock interno, prescaler 2
                   Stato = 3;                 //stato= 3 (prescaler = 2, captureogni fronte)
                 }                            //prewscaler equivalente 2
                 else{                        //se il valore è opportuno
                   Giri = 15000000/CCPR1;     //giri = ((1/tempoconteggio*)60)/CCPR1
                   display(Giri);             //visualizza i giri
                 }
                E = 0;                        //resetta "E" così da non rieseguire
               }
        case 3:while(E==1){                   //esegui una volta sola se Stato=3
                 if(CCPR1 < 32700){           //se stato=3 e valore piccolo
                  T1CON = 0b10000001;         //timer 1 on, clock interno, prescaler 1
                  Stato = 4;                  //stato= 4 (prescaler = 1, capture ogni fronte)
                }                             //prescaler equivalente 1
                else{                         //altrimenti se valore opportuno
                  Giri = 30000000/CCPR1;      //giri = ((1/tempoconteggio*)60)/CCPR1
                  display(Giri);              //visualizza i giri
                }
                E = 0;                        //resetta "E" così da non rieseguire
               }

        case 4:while(E==1){                   //esegui una volta sola se Stato=2
                 if(CCPR1 < 32700){           //se stato=4 e valore piccolo
                   T1CON = 0b10110001;        //timer 1 on, clock interno prescaler 8
                   CCP1CON = 0b00000111;      //modalità capture ogni 16 fronte di salita
                   Stato = 5;                 //stato= 5 (prescaler = 4, capture ogni fronte)
                 }                            //prescaler equivalente 0.5
                else{                         //se si ha un valore opportuno
                  Giri = 60000000/CCPR1;      //giri = ((1/tempoconteggio*)60)/CCPR1
                  display(Giri);              //visualizza i giri
                }
                E = 0;                        //resetta "E" così da non rieseguire
               }

        case 5:while(E==1){                   //esegui una volta sola se Stato=2
                 if(CCPR1 < 32700){           //se stato=5 e valore piccolo
                   T1CON = 0b10100001;        //timer 1 on, clock interno prescaler 4
                   CCP1CON = 0b00000111;      //modalità capture ogni 16 fronte di salita
                   Stato = 6;                 
                 }                            //prescaler euivalente 0.25
                else{                         //se si ha un valore opportuno
                  Giri = 120000000/CCPR1;     //giri = ((1/tempoconteggio*)60)/CCPR1
                  display(Giri);              //visualizza i giri
                }
                E = 0;                        //resetta "E" così da non rieseguire
               }
               
        case 6:while(E==1){                   //esegui una volta sola se Stato=2
                 if(CCPR1 < 32700){           //se stato=6 e valore piccolo
                   T1CON = 0b10010001;        //timer 1 on, clock interno prescaler 2
                   CCP1CON = 0b00000111;      //modalità capture ogni 16 fronte di salita
                   Stato = 7;                 //stato= 7 (prescaler = 4, capture ogni 4 fronti
                 }                            //prescaler equivalente 0.125
                 else{                        //altrimenti se si ha un valore opportuno
                   Giri = 240000000/CCPR1;    //giri = ((1/tempoconteggio*)60)/CCPR1
                   display(Giri);             //visualizza i giri
                 }
                E = 0;                        //resetta "E" così da non rieseguire
               }
               
        case 7:while(E==1){                   //se sono nell'ultimo stato...
               Giri = 480000000/CCPR1;        //giri = ((1/tempoconteggio*)60)/CCPR1
               display(Giri);                 //visualizza i giri
               E = 0;                         //resetta "E" così da non rieseguire
             }
      }

      PIR1.CCP1IF = 0;                        //resetta flag di capture
      
      while(PIR1.CCP1IF == 0 ){               //se non si ha l'inizio di un ciclo
        TMR1H = 0;                            //rimani bloccato in questo ciclo
        TMR1L = 0;                            //e resetta sempre il timer
      }
      PIR1.CCP1IF = 0;                        //reseta il flag per iniziare un nuovo ciclo
    }//fine if
  }//fine ciclo infinito
}//fine

Per visualizzare il valore dei giri al minuto si utilizza un LCD, quindi il primo passo del codice è quello di scrivere la configurazione tra LCD e microcontrollore, e in particolare si utilizza la PORTB. Dopo di che servono alcune variabili, la prima tiene conto di quale prescaler si ha, poi vi è il numero di giri, una variabile per l’esecuzione di una parte di programma, poi si ha la variabile per il valore convertito in giri al minuti e infine vi è il testo per visualizzare il valore di giri.

Poi vi è una subroutine che riceve il numero di giri in floating, lo trasforma in un numero senza virgola e lo trasforma in stringa, infine questa stringa viene visualizzata sull’LCD insieme alla stringa “giri/min” e infine resetta il valore del timer counter.

L’altra subroutine è una subroutine di interrupt,  ovvero dell’interrupt di over flow. Quando si ha over flow si attiva un led che avverte dell’overflow, poi si riporta il prescaler al valore 8 e poi si porta nello stato iniziale e infine  si resettano il timer counter e i flag. Purtroppo nella subroutine di interrupt non è possibile inserire le stringhe per visualizzare l’overflow su LCD, quindi per questo si utilizza il LED.

Nel codice principale si inizializzano le porte, l’interrupt, l’LCD, il timer counter con prescaler 8 e il modulo capture con catturo ad ogni fronte di salita. Dopo di che si ha un ciclo infinito.

Nel ciclo infinito si controlla se si ha un evento di capture. Se questo è avvenuto si va a spegnere il timer counter. Dopo di che si va a controllare lo stato, inizialmente si parte da prescaler 8, dopo di che si va a controllare il valore catturato. Se questo è minore di metà del massimo, potrei ottenere una precisione maggiore dimezzando il prescaler, ed è proprio quello che si fa, e se si setta un prescaler 4 si va nello stato 2. Se il valore è opportuno si va a dividere 750000 ((4000000*60)/4*8) per il valore catturato.

Dopo di che si continua questo controllo finchè non si ha un valore opportuno di prescaler con un valore catturato opportuno.

Purtroppo dentro i case non si riesce ad inserire un if else a meno che non si racchiude tutto in un while. Per fare ciò si utilizza una variabile “E” che viene inizializzata a 1 prima del case e poi viene resettata dopo l’if else.

Finito il case si resetta il flag del modulo capture, visto che durante i processi del case potrebbe avvenire un evento di capture. Finchè il flag è 0 si tiene resettato il timer counter, in questo modo si riesce a contare perfettamente tra due fronti di salita.
 

SCHEMA

Il circuito è il seguente:

schema contagiri DA 120 A 18000 GIRI AL MINUTOPer pilotare il led infrarosso si utilizza un led da 220ohm mentre per pilotare il transistor fotosensibile si utilizza una resistenza di pull up da 1Kohm. Il circuito di oscillazione è formato da un cristallo da 4MHz e due condensatori da 22pF. L’alimentazione è da 5V e vi è la resistenza da 10K che tiene non resettato il microcontrollore. La resistenza RV1 è la resistenza per il contrasto dell’LCD. I pin di alimentazione del microcontrollore sono i pin 8 e 19 per GND mentre 20 per VCC. Il microcontrollore scelto è il PIC18F252 e potrebbe lavorare anche a frequenze più elevate però aumenta il numero di giri minimi e massimi misurabili, mentre se si riduce a frequenza si riduce anche il numero minimo e massimo di giri misurabili.

È inoltre necessario un led con resistenza di protezione sul pin 28 ovvero PB7 se si vuole avere l’informazione di quando si ha overflow. Lo schema realizzato su breadboard è il seguente:

schema contagiri montatu su breadboard
 

DOWNLOAD

Per il download del codice, dell’HEX da inserire nel microcontrollore e per la simulazione cliccate sul seguente LINK!!!





 

[Voti Totali: 0 Media Voti: 0]
Segui la Nostra Pagina Facebook: Facebook

3 pensieri su “Contagiri da 120 a 18000 Giri al Minuto

    • Ciao,
      hai controllato che il LED e photodiodo funzionano? potrebbe essere che il segnale del photodiodo non viene letto bene dal micro

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *