Medir frecuencias

Hola, quería ver si alguien me podía ayudar. Estoy intentando leer la frecuencia de una onda cuadrada, para ello he escrito éste código:

//________________________________________________________________________________
int ICP=8;

//Contador de desbordamientos
volatile long contador_overflow;

//Flancos de subida
unsigned int flanco_subida;

//Definimos la frecuencia
volatile long frecuencia;

void setup()
{   
 Serial.begin(9600);
 pinMode(ICP, INPUT);
} 

//Rutina de Servicio de Interrupción del contador de desbordamientos
ISR(TIMER1_OVF_vect)
{
 contador_overflow++;
}

//Subrutina de Servicio de Interrupción de captura del Timer1
ISR(TIMER1_CAPT_vect)
{
 /**/
 if (ICP) //si hay nivel alto
 {
   //Un nuevo flanco de subida significa fin de periodo
   frecuencia=16000000/(abs(ICR1-flanco_subida)+contador_overflow*0xFF);
   // Recojo el tiempo para medida del nuevo pulso
   flanco_subida=ICR1;
   /*Reseteamos contador de desbordamientos*/
   contador_overflow=0;
   monitoriza(); 
 }
}

/*Funcion que muestra por pantalla la frecuencia*/
void monitoriza(){ 
 Serial.println();
 Serial.print("LA FRECUENCIA ES ");
 Serial.print(frecuencia);
 Serial.println(" Hz");
} 

int main(void)
{
 //Modo normal
 TCCR1A=0;
 //Habilitamos overflow e input capture interrupts
 TIMSK1=0x21;

 /*Noise canceller, sin prescaler, detecta flanco de subida*/
 TCCR1B=0xC1;

 //habilitamos interrupciones globales
 sei();

 for (;;)
 {
   //bucle cerrado de funcionamiento continuo
 }
}
//________________________________________________________________________________

Pero cuando introduzco por el pin8 digital la señal periódica cuadrada que quiero medir, el monitor serie no muestra los resultados y no sé qué pasa.

Muchas gracias, un saludo.

yo tengo un codigo para eso que me escribio nickgammon gran codigo y a frecuencias de 1khz es super exacto probado con gnerador de señales y oscilloscopio si me dejas buscarlo te lo paso

/ Frequency timer
// Author: Nick Gammon
// Date: 10th February 2012

volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;

// here on rising edge
void isr () 
{
  unsigned int counter = TCNT1;  // quickly save it
  
  // wait until we noticed last one
  if (triggered)
    return;

  if (first)
    {
    startTime = (overflowCount << 16) + counter;
    first = false;
    return;  
    }
    
  finishTime = (overflowCount << 16) + counter;
  triggered = true;
  detachInterrupt(0);   
}  // end of isr

// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect) 
{
  overflowCount++;
}  // end of TIMER1_OVF_vect


void prepareForInterrupts ()
  {
  // get ready for next time
  EIFR = _BV (INTF0);  // clear flag for interrupt 0
  first = true;
  triggered = false;  // re-arm for next time
  attachInterrupt(0, isr, RISING);     
  }  // end of prepareForInterrupts
  

void setup () {
  Serial.begin(115200);       
  Serial.println("Frequency Counter");
  
  // reset Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  // Timer 1 - interrupt on overflow
  TIMSK1 = _BV (TOIE1);   // enable Timer1 Interrupt
  // zero it
  TCNT1 = 0;     
  // start Timer 1
  TCCR1B =  _BV (CS20);  //  no prescaling

  // set up for interrupts
  prepareForInterrupts ();   
  
} // end of setup

void loop () 
  {

  if (!triggered)
    return;
 
  unsigned long elapsedTime = finishTime - startTime;
  float freq = 1.0 / ((float (elapsedTime) * 62.5e-9));  // each tick is 62.5 nS
  
  Serial.print ("Took: ");
  Serial.print (elapsedTime);
  Serial.print (" counts. ");

  Serial.print ("Frequency: ");
  Serial.print (freq);
  Serial.println (" Hz. ");

  // so we can read it  
  delay (500);

  prepareForInterrupts ();   
}   // end of loop

mira si te sirve este codigo lo escribio nick para una idea que tenia y basado en el datasheet del controlador, es increible podes casi llegar a 99,000 khz con este amigo pero ya genera un error

1 Like

Hola copachino, muchas gracias por tu respuesta. Lo he probado y funciona muy bien, GRACIAS.

Sólo un apunte que querría hacer:

TCCR1B =  _BV (CS20);  //  no prescaling

¿No sería?

TCCR1B =  _BV (CS10);  //  no prescaling

Porque estás usando el Timer 1.

Un saludo y gracias de nuevo. :wink:

electrounio:
Hola copachino, muchas gracias por tu respuesta. Lo he probado y funciona muy bien, GRACIAS.

Sólo un apunte que querría hacer:
TCCR1B = _BV (CS20); // no prescaling

¿No sería?
TCCR1B = _BV (CS10); // no prescaling
Porque estás usando el Timer 1.

Un saludo y gracias de nuevo. :wink:

la verdad si, tienes razon nunca me habia fijado en ese error, pero funciona a la perfección ese codigo, y con una exactitud increible

Interesante código...
tengo una duda.... que tipo de señal es capaz de medir, y que pin hay que usar de entrada?
imagino que señales cuadradas de 5v...

gracias..

Lisergio:
Interesante código...
tengo una duda.... que tipo de señal es capaz de medir, y que pin hay que usar de entrada?
imagino que señales cuadradas de 5v...

gracias..

pues lo he probado con varios tipos de frecuencia no solo cuadradas, y la entrada utiliza los pines de interuptores de la tarjeta

Ok! Voy a probar... Gracias!

Hola a todos, yo tambien estoy utilizando ese codigo para capturar la frecuencia de una señal de entrada procedente de un mando de RC. La lectura de la frecuencia Is perfect!! (62,5 Hz), pero cuando hago la inversa para obtener el periodo parece que no mide bien los tiempos, me da un periodo T=20ms, cuando deberia ser de 16 ms, sabeis porq puede ser eso??.

Gracias de antemano.

En cuantos lugares vas a preguntar por lo mismo? Mismo avatar que York, misma pregunta

Si he vuelto a preguntar es porq no consigo solucionarlo, y es el mismo codigo que estoy utilizando. :-X :disappointed_relieved: :roll_eyes:

No he trabajado nunca con Timer's supongo que no sera muy complicado pero la verdad es que stoy un poco verde. Estoy leyendo mucho para ver si lo acabo de entender bien y adquiero la capacidad de poder razonar en su forma de utilizacion.

Pero no puedes habrir 2,3 ,4 post del mismo tema.
Si no se soluciona ten paciencia o revisa las sugerencias.

Perdona, pero yo no he abierto 2,3 o 4 post, solo abri uno preguntando como se podia cambiar la frecuencia del pwm Y como tambien estoy tratando de ver la frecuencia de entrada de la señal para comprobar la periocidad de la señal y buscando por el foro o la red me salen estos post y pues pregunte aqui tambien.

Y nose a quien te refieres con lo de "York", si hay algun miembro del foro que tenga el mismo avatar que yo y que haya preguntado lo mismo puede ser. Pero vuelvo a repetir que no he abierto varios post preguntando lo mismo.

Siento si te a molestado algo, no es mi intencion molestar, solo intentar aprender un poco mas los entresijos de los timer's de arduino que es con lo que estoy atascado en estos momentos. :disappointed_relieved:

Ahm se me olvidaba, tambien reviso las sugerencias o los links que recomendais, pero aun asi, hay veces que no llego a entender la info.

Y muy agradecido de la info que se me da aqui en el foro, pero cuando pregunto no pregunto sin haber buscado informacion antes.

Hay personas, como yo, que a lo mejor no tenemos la cabeza tambien amueblada y nos cuesta un poco mas enterder las cosas.

Gracias de nuevo por la ayuda.

Bueno, he estado haciendo mediciones y con este código leo correctamente 62.5Hz

// Frequency timer
// Author: Nick Gammon
// Date: 10th February 2012

// Input: Pin D2

volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;

// here on rising edge
void isr () 
{
  unsigned int counter = TCNT1;  // quickly save it
  
  // wait until we noticed last one
  if (triggered)
    return;

  if (first)
    {
    startTime = (overflowCount << 16) + counter;
    first = false;
    return;  
    }
    
  finishTime = (overflowCount << 16) + counter;
  triggered = true;
  detachInterrupt(0);   
}  // end of isr

// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect) 
{
  overflowCount++;
}  // end of TIMER1_OVF_vect


void prepareForInterrupts ()
  {
  // get ready for next time
  EIFR = bit (INTF0);  // clear flag for interrupt 0
  first = true;
  triggered = false;  // re-arm for next time
  attachInterrupt(0, isr, RISING);     
  }  // end of prepareForInterrupts
  

void setup () 
  {
  Serial.begin(9600);       
  Serial.println("Frequency Counter");
  
  // reset Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  // Timer 1 - interrupt on overflow
  TIMSK1 = bit (TOIE1);   // enable Timer1 Interrupt
  // zero it
  TCNT1 = 0;  
  overflowCount = 0;  
  // start Timer 1
  TCCR1B =  bit (CS10);  //  no prescaling

  // set up for interrupts
  prepareForInterrupts ();   
  
  } // end of setup

void loop () 
  {

  if (!triggered)
    return;
 
  unsigned long elapsedTime = finishTime - startTime;
  float freq = F_CPU / float (elapsedTime);  // each tick is 62.5 nS at 16 MHz
  
  Serial.print ("Took: ");
  Serial.print (elapsedTime);
  Serial.print (" counts. ");

  Serial.print ("Frequency: ");
  Serial.print (freq);
  Serial.println (" Hz. ");

  // so we can read it  
  delay (500);

  prepareForInterrupts ();   
}   // end of loop