Frequency meter

God morning,

I'm trying to do a simple frequency meter with a Arduino Uno and a 4 digit 7 segments display display with TM1637 driver.
Pulses enter by pin 2 set as external interrupt 0. Timer 1 generates 1 Hz interrupt so that the result in the display is updated each second.

I know it' s very simple and that is unable to measure frequencies lower than 1Hz or frequencies with non integer part (24.4Hz for example will be measured as 24Hz).

It works fine for frequencies lower than 200Hz but it gives 0 for frequencies above 1kHz and I do not know the reaon.

Using external interrupt means that it must be able to catch all the pulses.

Can anybody help me?
What is wrong in my code?

I have attached an schematic with the connections.
Comments in the code are in catalan, my language.

Thank you in advance.

/*-------------------------------------------------------------------------------
PROGRAMA: Frecuenimetre1637
VERSIO:  2.0
DATA:  05/12/19
DESCRIPCIO: Contador de polsos que arriben al pin 2, usant interrupcions externes
            Usant la interrupció del Timer1 acumula polos durant un segon per cal-
            cular la frecuencia. El resultat es mostra actualitat cada segon en un
            display de 7 segments cuadruple controlat per TM1637.
            Limitacions, la frecuencia ha de ser major de 1Hz i no pot mesurar
            frecuencies amb decimals com podría ser 1,2Hz
            Afegeix la rutina de caratula() que passa de num. a un únic caràcter
            de màscara alternatiu
            Fallen els delay()
---------------------------------------------------------------------------------*/
#include <TM1637Display.h>

const byte CLK=6;                //pin CLK del display
const byte DIO=7;                //pin DIO del display
const byte entrada=2;            //pin per on entraran els polsos
const int temps=500;             //mig segon
const byte kilo=13;              //pin on va conectat el led de kilohertz 

                  
TM1637Display display(CLK,DIO);  //Definim una variable de tipus TM1637Display
volatile int polsosvol;          //mesura dels polsos a la ISR
int polsos=0;                    //mesura dels polsos al programa
volatile bool flag=false;        //flag per saber quan mostrar els resultats

void setup()
{
 
  pinMode(entrada,INPUT_PULLUP);                //Declarem el pin com a entrada
  pinMode(kilo,OUTPUT);                         //Declarem el pin per al led de kilohertzs
  digitalWrite(kilo,LOW);                       //Apaguem el led de kilohertzs
   display.showNumberDec(polsos,false,4,0);      //Mostrem "   0"
  delay(1000);                                  //esperem 1 segon
  flag=false; 
  //crida al servei d'interrupci del pin digital 2
  attachInterrupt(digitalPinToInterrupt(entrada),servei,RISING);
  //Timer 1 configurat per generar interrupcions cada segon
  //set timer1 interrupt at 1Hz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 1hz increments
  OCR1A = 15624;// = (16*10^6) / (1*1024) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS10 and CS12 bits for 1024 prescaler
  TCCR1B |= (1 << CS12) | (1 << CS10);  
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  Serial.begin(9600);           
 // sei();                                        //Activem totes les inrerrupcions 
}

void loop()
{
  if (flag==true)
  {
    Serial.println(polsos);
    digitalWrite(kilo,LOW);   //Apaguem el led de kilohertzs
    if (polsos<10000)
    {
      display.showNumberDec(polsos,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra  
    }
    if (polsos>=10000 && polsos<100000)
    {
      polsos=polsos/10;           //Ignorarem el darrer digit
      digitalWrite(kilo,HIGH);   //Encenem el led de kilohertzs
      display.showNumberDecEx(polsos,0b01000000,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra i coma al digit1 
    }
    flag=false;
  }
}
/*-------------------------------------------------------------------------------------------------
 *servei de la interrupció externa 0
 *-----------------------------------------------------------------------------------------------*/
void servei()
{
  polsosvol++;          //incrementem el contador
}
/*-------------------------------------------------------------------------------------------------
 *servei de la interrupció timer 1
 *-----------------------------------------------------------------------------------------------*/
ISR(TIMER1_COMPA_vect)
{
  flag=true;            //Activem el flag perqué es mostri el valor
  polsos=polsosvol;     //Actualitcem el valor
  polsosvol=0;          //Reiniciem el contador
}

int polsos=0;

should bevolatile int polsos=0;

Does Serial Monitor show the right value for 'polsos' when the frequency is >1 kHz?

Two critical issues, besides what johnwasser pointed out:

  1. You must disable interrupts when processing a multi-byte variable in the main loop that is set from an interrupt.
  2. polsosvol and polsos are declared as int. They will never be able to hold more than a value of 32767 without rolling over. If you want to measure up to 100K you will need to declare these as long.

Here is how I would code the main loop:

void loop()
{
  if (flag==true)
  {
    long temp_polsos;
    noInterrupts();
    temp_polsos = polsos;
    interrupts();
    Serial.println(temp_polsos);
    
    if (temp_polsos<10000)
    {
      digitalWrite(kilo,LOW);   //Apaguem el led de kilohertzs
      display.showNumberDec(temp_polsos,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra  
    }
    else if (temp_polsos<100000)
    {
      temp_polsos=temp_polsos/10;           //Ignorarem el darrer digit
      digitalWrite(kilo,HIGH);   //Encenem el led de kilohertzs
      display.showNumberDecEx(temp_polsos,0b01000000,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra i coma al digit1 
    }
    flag=false;
  }
}

johnwasser:

int polsos=0;

should bevolatile int polsos=0;

Does Serial Monitor show the right value for 'polsos' when the frequency is >1 kHz?

Thank you for your answer.

I forgot delete serial monitor. As my program did not work fine, I used the serial monitor to troubleshoot what was happenning, because above 200hertz it displayed wrong values and above 1kHz it displayed 0 hertz.

The original idea was use the 4 digit seven segment display to show the value.

ToddL1962:
Two critical issues, besides what johnwasser pointed out:

  1. You must disable interrupts when processing a multi-byte variable in the main loop that is set from an interrupt.
  2. polsosvol and polsos are declared as int. They will never be able to hold more than a value of 32767 without rolling over. If you want to measure up to 100K you will need to declare these as long.

Here is how I would code the main loop:

void loop()

{
  if (flag==true)
  {
    long temp_polsos;
    noInterrupts();
    temp_polsos = polsos;
    interrupts();
    Serial.println(temp_polsos);
   
    if (temp_polsos<10000)
    {
      digitalWrite(kilo,LOW);  //Apaguem el led de kilohertzs
      display.showNumberDec(temp_polsos,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra 
    }
    else if (temp_polsos<100000)
    {
      temp_polsos=temp_polsos/10;          //Ignorarem el darrer digit
      digitalWrite(kilo,HIGH);  //Encenem el led de kilohertzs
      display.showNumberDecEx(temp_polsos,0b01000000,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra i coma al digit1
    }
    flag=false;
  }
}

Thank you for your help.

I will follow your advice and johnwasser's one.

Change:

volatile int polsosvol;          //mesura dels polsos a la ISR
int polsos=0;                    //mesura dels polsos al programa

to:

volatile long polsosvol;          //mesura dels polsos a la ISR
volatile long polsos=0L;                    //mesura dels polsos al programa

so that your counts are all volatile and long enough to hold large numbers.

Change:

void loop()
{
  if (flag==true)
  {
    Serial.println(polsos);
    digitalWrite(kilo,LOW);   //Apaguem el led de kilohertzs
    if (polsos<10000)
    {
      display.showNumberDec(polsos,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra 
    }
    if (polsos>=10000 && polsos<100000)
    {
      polsos=polsos/10;           //Ignorarem el darrer digit
      digitalWrite(kilo,HIGH);   //Encenem el led de kilohertzs
      display.showNumberDecEx(polsos,0b01000000,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra i coma al digit1
    }
    flag=false;
  }
}

to:

void loop()
{
  if (flag==true)
  {
    noInterrupts() ;
    long my_polsos = polsos;   // read variable once in a critical section so its not garbled
    interrupts() ;

    Serial.println(my_polsos);
    digitalWrite(kilo,LOW);   //Apaguem el led de kilohertzs
    if (my_polsos<10000)
    {
      display.showNumberDec(my_polsos,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra 
    }
    if (my_polsos>=10000 && my_polsos<100000L)
    {
      my_polsos /= 10;           //Ignorarem el darrer digit
      digitalWrite(kilo,HIGH);   //Encenem el led de kilohertzs
      display.showNumberDecEx(my_polsos,0b01000000,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra i coma al digit1
    }
    flag=false;
  }
}

MarkT:
Change:

volatile int polsosvol;          //mesura dels polsos a la ISR

int polsos=0;                    //mesura dels polsos al programa



to:


volatile long polsosvol;          //mesura dels polsos a la ISR
volatile long polsos=0L;                    //mesura dels polsos al programa



so that your counts are all volatile and long enough to hold large numbers.

Change:


void loop()
{
  if (flag==true)
  {
    Serial.println(polsos);
    digitalWrite(kilo,LOW);  //Apaguem el led de kilohertzs
    if (polsos<10000)
    {
      display.showNumberDec(polsos,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra
    }
    if (polsos>=10000 && polsos<100000)
    {
      polsos=polsos/10;          //Ignorarem el darrer digit
      digitalWrite(kilo,HIGH);  //Encenem el led de kilohertzs
      display.showNumberDecEx(polsos,0b01000000,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra i coma al digit1
    }
    flag=false;
  }
}



to:


void loop()
{
  if (flag==true)
  {
    noInterrupts() ;
    long my_polsos = polsos;  // read variable once in a critical section so its not garbled
    interrupts() ;

Serial.println(my_polsos);
    digitalWrite(kilo,LOW);  //Apaguem el led de kilohertzs
    if (my_polsos<10000)
    {
      display.showNumberDec(my_polsos,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra
    }
    if (my_polsos>=10000 && my_polsos<100000L)
    {
      my_polsos /= 10;          //Ignorarem el darrer digit
      digitalWrite(kilo,HIGH);  //Encenem el led de kilohertzs
      display.showNumberDecEx(my_polsos,0b01000000,false,4,0);
    //mostrem el numero sense ceros devant amb 4 digits i començant per l'esquerra i coma al digit1
    }
    flag=false;
  }
}

Thank you for your help