Frequenzimetro Arduino Leonardo

Ciao a tutti!
Ho necessità di conoscere la frequenza di un segnale da pochi Hz fino a 700Khz.
Ho visto che esiste una libreria chiamata FreqCounter Page has moved , utilizzata per arduino uno.
Ho provato a modificare la libreria per arduino leonardo, ma non riesco a farla funzionare
Qualcuno può aiutarmi grazie

#include <FreqCounter.h>

unsigned long FreqCounter::f_freq;

volatile unsigned char FreqCounter::f_ready;
volatile unsigned char FreqCounter::f_mlt;
volatile unsigned int FreqCounter::f_tics;
volatile unsigned int FreqCounter::f_period;
volatile unsigned int FreqCounter::f_comp;

void FreqCounter::start(int ms) {

#if defined (__AVR_ATmega32U4__)//(__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || (__AVR_ATmega1280__)


    TIMSK0 &=~(1<<TOIE0);       // disable Timer0  //disable  millis and delay
    delayMicroseconds(50);      // wait if any ints are pending
    
    f_period=ms;
  
  
    if (f_comp ==0) f_comp=1;  // 0 is not allowed in del us
	
    // hardware counter setup ( refer atmega32u4)
    TCCR1A=0;                  // reset timer/counter1 control register A
    TCCR1B=0;              	   // reset timer/counter1 control register A
    TCNT1=0;           		   // counter value = 0
	TCNT3=0;
    // set timer/counter1 hardware as counter , counts events on pin T1 ( arduino pin 12)
    // normal mode, wgm10 .. wgm13 = 0
    
    TCCR1B |=  (1<<CS10) ;//CLOCK ESTERNO su fronte di salita
    TCCR1B |=  (1<<CS11) ;
    TCCR1B |=  (1<<CS12) ;
    
    // timer3 setup / usato per la generazione del tempo di lettura
    TCCR3A=0;
    TCCR3B=0;
    
    // timer3 presaler set to 64 / timer3 clock = 16Mhz / 64 = 250.000 Hz
    TCCR3B &= ~(1<<CS30) ;
    TCCR3B |=  (1<<CS31) ;
    TCCR3B |=  (1<<CS32) ;

    //setta il timer3 to CTC(Clear Timer on Compare)  Mode with OCR3A is top counter value (arrivato a OCR3A si resetta)
    TCCR3A &= ~(1<<WGM30) ;
    TCCR3A &= ~(1<<WGM31) ;
    TCCR3A |=  (1<<WGM32) ;
	TCCR3A &= ~(1<<WGM33) ;
    OCR3A = 249;                // CTC divider by 250
   
    f_ready=0;                  // reset period measure flag
    f_tics=0;                   // reset interrupt counter
    GTCCR = (1<<PSRASY);        // reset presacler counting (perchè resettarlo?)
    TCNT3=0;                    // timer3=0
    TCNT1=0;                    // Counter1 = 0
    
    TIMSK3 |=(1<<OCIE3A);       // enable Timer3 Interrupt (interrupt di Timer/Counter3 Compare Match A)
    
                                // External clock source on T1 pin. Clock on rising edge.
    TCCR1B |= (1<<CS12) | (1<<CS11) | (1<<CS10);        //   start counting now     
    #endif
}
//******************************************************************
//  Timer3 Interrupt Service is invoked by hardware Timer3 every 1ms = 1000 Hz
//  16Mhz/64/250 = 1000 Hz
//  here the gatetime generation for freq. measurement takes place: 
//

ISR(TIMER3_COMPA_vect) {
										// multiple 2ms = gate time = 100 ms
if (FreqCounter::f_tics >= FreqCounter::f_period) {         	
                            			// end of gate time, measurement ready

   										// GateCalibration Value, set to zero error with reference frequency counter
    //  delayMicroseconds(FreqCounter::f_comp); // 0.01=1/ 0.1=12 / 1=120 sec 
    delayMicroseconds(FreqCounter::f_comp);
    TCCR1B = TCCR1B & ~7;   			// Gate Off  / Counter T1 stopped 
    TIMSK3 &= ~(1<<OCIE3A);    			// disable Timer3 Interrupt(timer del gate)
    TIMSK0 |=(1<<TOIE0);     			// enable Timer0 again // millis and delay
    FreqCounter::f_ready=1;             // set global flag for end count period
    
                                        // calculate now frequeny value
    FreqCounter::f_freq=0x10000 * FreqCounter::f_mlt;  // mult #overflows by 65636
    FreqCounter::f_freq += TCNT1;      	// add counter1 value
    FreqCounter::f_mlt=0;
    
    }
    FreqCounter::f_tics++;            	// count number of interrupt events
    if (TIFR1 & 1) {          			// if Timer/Counter 1 overflow flag
    FreqCounter::f_mlt++;               // count number of Counter1 overflows
    TIFR1 =(1<<TOV1);        			// clear Timer/Counter 1 overflow flag
    }
    // PORTB = PORTB ^ 32;  				// int activity test
}

Quella lib sfrutta l'Atmega328 alle radici, ossia lavora direttamente con i timer. Per portarla su un altro micro devi adattarla. La freqCounter nella fattispecie usa il timer 1 ed il timer 2. Il timer 1 se non ricordo male è usato in modalità input capture e misura il tempo di arrivo del segnale sul pin T1, mentre il timer 2 viene usato come temporizzatore per leggere il timer 1. Il timer 1 è a 16 bit, il timer 2 è a 8 bit.

Forse, il problema non è tanto il timer 1, che è simile, ma il timer 3, che tu stai usando a 16 bit. Prova a metterlo ad 8 bit.

PS: che errori ti da, nella fattispecie?

dariq88: Ho necessità di conoscere la frequenza di un segnale da pochi Hz fino a 700Khz.

--> http://forum.arduino.cc/index.php?topic=145059.15 Disponibile anche come progetto su Elettronica In.

Ciao grazie dell'aiuto! Per leo72: Si sono andato a leggermi il datasheet del 32u4 per poter andare a lavorare sui registri. Il T1 lavora da contatore ed è legato al pin 12 del micro che corrisponde anche al pin 12 di arduino leonardo. Il T2 non lo posso utilizzare perchè nel micro non è proprio presente, allora ho usato il timer 3. Il T3 dovrebbe lavorare in con un fine corsa imposta da OCR3A , arrivato a il numero presente nel registro scatta il flag di interrupt e da cosi vado a leggere il T1 che conteggia gli impulsi esterni. Io la libreria l'ho modificata a mano e poi ho sostituito il file Cpp della libreria con questo. Una volta che scarico un programma su Leonardo che sfrutta questa libreria, non riesco a vedere perchè non va... Per PaoloP: Ho visto,è un bel progetto ma non parla di firmware

Se puoi/vuoi, pubblica il codice che hai scritto

Non so se riesci ad arrivare fino a 700KHz. Nel caso devi usare un prescaler.

Come prescaler super-economico puoi anche usare un 74LS90, utilizzato come divisore x10.

In teoria dovrebbe poter leggere segnali con frequenze di qualche MHz, 700 KHz non dovrebbero essere un problema a gestirli.

Ecco il codice di prova

#include <FreqCounter.h>


unsigned long frq;
int cnt;

int pin2=10;
int pin3=13;

void setup() {

  pinMode(pin2, OUTPUT); 
   pinMode(pin3, OUTPUT); 

 // Serial.begin(115200);        // connect to the serial port

 // Serial.println("Frequency Counter");

}

void loop() {
  
  // wait if any serial is going on
  FreqCounter::f_comp=10;   // Cal Value / Calibrate with professional Freq Counter
  FreqCounter::start(100);  // 100 ms Gate Time

  while (FreqCounter::f_ready == 0) //rimani qua fino a che f_ready non è a 1
   
  frq=FreqCounter::f_freq;       //se FreqCounter::f_ready == 1
  //Serial.print(cnt++);
  //Serial.print("  Freq: ");
 // Serial.println(frq);
    
    if(frq >= 200){
    digitalWrite( pin2,HIGH);  // blink Led
   }else {
   digitalWrite( pin2,LOW) ;  // blink Led
   }
   digitalWrite( pin3,HIGH) ;  // blink Led
  delay(20);

}

Forse ho trovato il problema,ma non so come risolverlo.
Ho inserito digitalWrite( pin3,HIGH) per vedere dove il programma passa, l’ho inserito dentro il ciclo while e il led non si accende mai, quindi penso che il problema sia la libreria che non attivi mai FreqCounter::f_ready.Dove sbaglio?

Non è facile adattare una lib che usa timer ed interrupt da una MCU ad un'altra. Ricontrolla per bene il codice. Metti anche il digitalWrite PRIMA del while, per capire se il micro è piantato oppure no.

CIao a tutti e spero che abbiate passato buone feste, sono riuscito a far andare la libreria.
Ho utilizzato il timer0 anziche il timer tre perchè il timer1 e il timer3 utilizzano lo stesso prescaler e credo che gli abbia dato fastidio, in realtà senza utilizzare un debugger è difficile capire dove fosse esattamente il problema.

#include "FreqCounter.h"

volatile unsigned long FreqCounter::f_freq;

volatile unsigned char FreqCounter::f_ready;
volatile unsigned char FreqCounter::f_mlt;
volatile unsigned int FreqCounter::f_tics;
volatile unsigned int FreqCounter::f_period;
volatile unsigned int FreqCounter::f_comp;

void FreqCounter::start(int ms) {

#if defined (__AVR_ATmega32U4__)

    f_period=ms/2;
    f_ready=0;                  // reset period measure flag
    f_tics=0;                   // reset interrupt counter
  
  
    if (f_comp ==0) f_comp=1;  // 0 is not allowed in del us
	
    // hardware counter setup ( refer atmega32u4)
    TCCR1A=0;                  // reset timer/counter1 control register A
    TCCR1B=0;              	   // reset timer/counter1 control register A
    TCNT1=0;           		   // counter value = 0
	
    // set timer/counter1 hardware as counter , counts events on pin T1 ( arduino leonardo pin 12)
    // normal mode, wgm10 .. wgm13 = 0
    
    TCCR1B |=  (1<<CS10) ;//CLOCK ESTERNO su fronte di salita
    TCCR1B |=  (1<<CS11) ;
    TCCR1B |=  (1<<CS12) ;
    
    // timer0 setup / usato per la generazione del tempo di lettura
    TCCR0A=0;
    TCCR0B=0;
    
    // timer3 presaler set to 256 / timer3 clock = 16Mhz / 256 = 62.500 Hz
    TCCR0B = (1<<CS02); 

    //setta il timer0 to CTC(Clear Timer on Compare)  Mode with OCR3A is top counter value (arrivato a OCR3A si resetta)
	    TCCR0A = (1<<WGM01);  // CTC mode, normal (no Output compare)
	    GTCCR |= (1<<PSRASY);  // reset prescaler (undocumented bit in mega32u4?)
	    TCCR0B = (1<<CS02);  // timer0 on clk/256 == 16MHZ / 256 = 62500hz..
	    OCR0A = 124;  // 125 counts of 62500 Hz before an output compare interrupt...

	    TCNT1 = 0;
	    TCNT0 = 0;

	    TIMSK0 = (1<<OCIE0A);
    #endif
		
}
//******************************************************************
//  Timer3 Interrupt Service is invoked by hardware Timer3 every 1ms = 1000 Hz
//  16Mhz/256/124 = 500 Hz (corretto con il dimezzamento del period)
//  here the gatetime generation for freq. measurement takes place: 
//

ISR(TIMER0_COMPA_vect) {
										// multiple 2ms = gate time = 100 ms
if (FreqCounter::f_tics >= FreqCounter::f_period) {         	
                            			// end of gate time, measurement ready

   										// GateCalibration Value, set to zero error with reference frequency counter
    //  delayMicroseconds(FreqCounter::f_comp); // 0.01=1/ 0.1=12 / 1=120 sec 
    delayMicroseconds(FreqCounter::f_comp);
    TCCR1B = TCCR1B & ~7;   			// Gate Off  / Counter T1 stopped 
    TIMSK0 &= ~(1<<OCIE0A);    			// disable Timer3 Interrupt(timer del gate)
    //TIMSK0 |=(1<<TOIE0);     			// enable Timer0 again // millis and delay
    FreqCounter::f_ready=1;             // set global flag for end count period
    
                                        // calculate now frequeny value
    FreqCounter::f_freq=0x10000 * FreqCounter::f_mlt;  // mult #overflows by 65636
    FreqCounter::f_freq += TCNT1;      	// add counter1 value
    FreqCounter::f_mlt=0;
    
    }
    FreqCounter::f_tics++;            	// count number of interrupt events
    if (TIFR1 & 1) {          			// if Timer/Counter 1 overflow flag
    FreqCounter::f_mlt++;               // count number of Counter1 overflows
    TIFR1 =(1<<TOV1);        			// clear Timer/Counter 1 overflow flag
    }
    
}

Se funziona, ricordati di aggiornarla anche qui --> http://forum.arduino.cc/index.php?topic=64219.30 :grin:

Hai modificato anche il codice della libreria? Tempo fa la studiai un pò, se non ricordo male in alcuni punti fermava il timer 0.

Esatto fermava il timer0 per bloccare altri eventuali interrupt e poi dava un ritardo di x millisecondi. Ora anziche utilizzare il timer 2 per il andare a contare gli impulsi sull'ingresso utilizza il timer 0. Io per farla ho utilizzato la libreria per l'arduino 1 in modo da aver già i percorsi su Ide arduino e poi ho sostituito il file cpp. Se avete leonardo potete testarla

Una curiosità, nel programma in cui si sfrutta la libreria, si utilizza un while che ferma il programma fino all'avvenuta scansione della frequenza, ho provato ad utilizzare un if con la stessa condizione per poter far svolgere altre funzioni all'arduino, ma cosi non sembra funzionare....Qualcuno che ha già utilizzato la libreria mi sa dare qualche info?

dariq88: Per PaoloP: Ho visto,è un bel progetto ma non parla di firmware

Il progetto completo l'ho presentato su Elettronica IN, come diceva Paolo, ed è tutto OPEN, dallo schema elettrico al firmware, l'avevo annunciato qui e qui, quindi sempre nel Topic indicato da Paolo; la libreria l'ho usata nella forma originale, anche perché io mi sono basato su un ATmega328P che è il micro di Arduino UNO/Duemilanove, sui quali essa è stata elaborata. Il solo micro, con tutti gli accorgimenti hardware che ho adottato, legge fino a circa 6-6,5MHz, tutto l'hardware circostante invece è servito per tirare fuori uno strumento che credo di poter definire semi-professionale anche se ha prestazioni migliori di alcuni Frequenzimetri professionali che ho avuto modo di usare per confronto. Vista la frequenza assima di tuo interesse, a maggior ragione se sei interessato solo a segnali digitali, probabilmente non avrai alcun interesse in tal senso, in caso contrario lo schedino di ingresso esterno rappresenta il cuore delle prestazioni eccezionali di questo strumento, basterebbe un banalissimotraslatore di livelli sul connettore di collegamento con la tua Leonardo per farla diventare un Frequenzimetro degno di tale nome. ;)

dariq88: Una curiosità, nel programma in cui si sfrutta la libreria, si utilizza un while che ferma il programma fino all'avvenuta scansione della frequenza, ho provato ad utilizzare un if con la stessa condizione per poter far svolgere altre funzioni all'arduino, ma cosi non sembra funzionare....Qualcuno che ha già utilizzato la libreria mi sa dare qualche info?

Perché l'if ed il while hanno 2 logiche di funzionamento completamente differenti. Col while resti nel ciclo fino al verificarsi della condizione mentre con l'if esegui il codice quando la condizione è vera. Andrebbe poi visto se il programma può uscire dal while, se cioè serve proprio restare in attesa oppure no.

Per Michele Menniti: Complimenti un gran lavoro, ma nello specifico avevo bisogno di dritte per poter fare un porting della libreria su un altro micro e grazie a dio non ho avuto bisogno di controllare frequenze così elevate. Per Leo72: la differenza tra if e while mi è nota, ma in una gestione a flag non dovrebbe esserci problemi ad utilizzare un if anzichè un while, il programma dovrebbe ciclare il programma di continuo e in teoria ritornare all'if con il flag attivo...Poteva essere utile per far compiere altre operazioni al programma senza dover aspettare la lettura della frequenza

Senza vedere il codice dei 2 casi non si può dire perché si comporta in un modo piuttosto che in un altro.