Strange behaviour of Counter

Hi folks,
I'm trying to use an Arduino Nano as a simple frequency counter for up to about 220kHz.
I want the frequency to be shown on a little OLED. My Code didn't work so I splitted it to test it part per part. Because Serial seems to be broken after changing the settings of Timer 1 and 2, I left the display in the Code.
At 100Hz coming from a Rohde&Schwarz function generator, at 16MHz CPU clock, a prescaler of 256 and 0.1s for 100Hz signal I get 625 counts, meaning 2 overflows and a TCNT2 of (theoretically) 113. What I get is 1 ovf and a TCNT2 of very roughly 130 resulting in a frequency of about 160Hz
And it gets even better:
at 200Hz I still get frequencies from about 170-200Hz
at 400Hz I get frequencies of 170-200Hz

Your help is really appreciated, because I have no idea...

sketch_nov05b.ino (1.46 KB)

Your sketch, so that others might see it:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


//Pindefinitionen
#define SigInt    2   //INT1, Eingang für Periodendauer
#define SigTrig   5   //T1,   Eingang für Impulszählung
#define Multi     4   //HIGH für f<1000, LOW für f>1000


//Variabeln
volatile uint8_t ovf;
volatile uint8_t waitflag;
uint32_t frequenz;


#define OLED_RESET 12
Adafruit_SSD1306 display(OLED_RESET);


void setup()
{
  pinMode(Multi, INPUT_PULLUP);
  pinMode(A4, INPUT_PULLUP);
  pinMode(A5, INPUT_PULLUP);


  //Display initialisieren
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);


  //Display vorbereiten
  display.setTextColor(WHITE);
  display.setTextSize(2);


  TIMSK2 |= (1 << TOIE2);
}


void loop()
{
  ovf = 0;
  ///////////////
  //Periodendauer


  waitflag = 0;
  //CT2 zurücksetzen
  TCNT2 = 0;
  //INT1 steigende Flanke
  EICRA |= ((1 << ISC11) | (1 << ISC10));
  //INT1 ein
  EIMSK |= (1 << INT1);
  //warten
  while ( waitflag < 2)
  {
    ;   //wird in ISR inkrementiert
  }
  EIMSK &= ~(1 << INT1);


  display.clearDisplay();
  display.setCursor(0, 0);
  display.println(TCNT2);
  display.println(ovf);


  frequenz = 62500 / (256 * ovf + TCNT2);
  display.println(frequenz);
  display.display();
  delay(1000);
}


//ISR für INT0
ISR (INT1_vect)
{
  if (waitflag)
  {
    TCCR2B &= ~( (1 << CS22) | (1 << CS21) );
  }
  else
  {
    TCCR2B |= ( (1 << CS22) | (1 << CS21) );
  }
  ++waitflag;
}


//ISR für Überlauf CT2
ISR (TIMER2_OVF_vect)
{
  ++ovf;
}

I don't see where you initialize Timer2. You change TCCR2B in your INT1 (Pin 3?) vector but the other registers are left alone.

a) Hello and welcome to the Forum.

b) Timer1 and Timer2 have nothing to so with Serial.

c) You're setting up Timer2 to do exactly what Timer0 is already doing. Why not use Timer0?

You have your pulse coming in T1 which will be counted by Timer1, simply set up a loop to check the value of TCNT1 every 100ms and multiply the count by 10.

Ah, I'm sorry, that's an older version of the code :fearful: My bad...
Going to have a look tomorrow for what I've changed...

@johnwasser: Don't I'm initializing Timer2 by activating the OVF interrupt (TIMSK2 |= (1 << TOIE2)) and defining the clock in the ISR?
And of course it's pin3, not pin2. Had that changed to test if it worked on INT0, but changed back in the later version.

@DKWatson: I think Timer0 is used by delay() and I wasn't sure if it's used anywhere in the libs or if I will use it later, so I didn't want to interfere with it. And since Timer2 is not used...
Letting the pulses being counted by Timer1 will be the way I'm taking for higher frequencies (about 1kHz), because at 10Hz (my lowest frequency) I would only get 1 pulse in 0.1s. At 11Hz it's the same. That's why I'm measuring the accrual period

Anyway, thanks for your answers :slight_smile:

EGS:
@johnwasser: Don't I'm initializing Timer2 by activating the OVF interrupt (TIMSK2 |= (1 << TOIE2)) and defining the clock in the ISR?

I would not expect all of the Timer2 control registers to be zero on startup. They may be but if they aren't the timer will produce unexpected results.

This is what init() does to Timer2 on a 328:

  sbi(TCCR2B, CS22);
  sbi(TCCR2A, WGM20);

I would initialize Timer2 with:

  noInterrupts();
  // Set to Normal Mode (Mode 0), PWM outputs disabled, no clock selected (stopped)
  TCCR2A = 0; 
  TCCR2B = 0;
  TCNT2 = 0;  // Reset the Timer Count
  TIFR2 = 1 << TOV2;  // Clear the Timer Overflow Interrupt Flag
  TMSK2 = 1 << TOIE2;  // Enable Timer Overflow Interrupt
  interrupts();  // Re-enable interrupts

Then you can set the clock rate to 62.5 kHz the way you currently do:

    TCCR2B |= ( (1 << CS22) | (1 << CS21) );

Since no other TCR2B bits are set you can turn off the clock with:

    TCCR2B = 0;

You might also want to initialize the external interrupt more explicitly. I can't tell from your code if you want interrupts on LOW, CHANGE, FALLING, or RISING. If you want RISING or FALLING (one interrupt per cycle) and the bits are set for CHANGE you will be timing only half of the cycle. That would give you a smaller count (higher frequency) than expected.

  noInterrupts();
  EICRA |= ( (1 << ISC11) | (1 << ISC10) );  // INT1 on RISING
  EIFR = 1 << INTF1;  // Clear the flag
  EIMSK = 1 << INT1;  // Enable the interrupt
  interrupts();

Of course a much easier way to get the external interrupt right is:

  attachInterrupt(digitalPinToInterrupt(3), MyPin3ISR, RISING);

Good Morning,

Thank you guys for your help!

I added the hints you gave me yesterday and gave Serial another try, deleting everything linked to the display and it worked! My values are slightly off, but should get rid of that. Then I readded the display stuff and the values are...
At 100Hz I get roughly 170Hz measured
At 200Hz it goes from 200 to 10k

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SigInt    3   //INT1, Input for acruual period

volatile uint8_t ovf;
volatile uint8_t waitflag;
uint32_t frequency;

#define OLED_RESET 12
Adafruit_SSD1306 display(OLED_RESET);

void setup() {
  Serial.begin(9600);
  pinMode(A4, INPUT_PULLUP);
  pinMode(A5, INPUT_PULLUP);

  //Display initialisieren
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

  //Display vorbereiten
  display.setTextColor(WHITE);
  display.setTextSize(2);

  TIMSK2 = 0;
  TIMSK2 |= (1<<TOIE2);
}

void loop() {
  //initialise TIMER2 (in the loop, because later there will be a second mode using CTC)
  cli();
  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2  = 0;
  sei();
  
  ovf = 0;
  waitflag = 0;
  //INT1 rising edge
  EICRA |= ((1<<ISC11) | (1<<ISC10));
  //INT1 active
  EIMSK |= (1<<INT1);
  //wait
  while( waitflag < 2){
    ;   //waitflag is incremented in the ISR
  }
  EIMSK &= ~(1<<INT1);
  
  frequency = 62500 / (256 * ovf + TCNT2);
  display.clearDisplay();
  display.setCursor(0,0);
  display.println(TCNT2);
  display.println(ovf);
  display.println(frequency);
  display.display();
  
  Serial.println(TCNT2);
  Serial.println(ovf);
  Serial.println(frequency);
  delay(1000);
}

//ISR for INT1
ISR (INT1_vect){
  TCCR2B ^= ( (1<<CS22) | (1<<CS21) );
  ++waitflag; 
}

//ISR forOverflow TIMER2
ISR (TIMER2_OVF_vect){
  ++ovf;
}

As I said, kicking out everything related to the display and it works :o

@johnwasser:
In the old code in loop(), the 4th and 5th command and in the new one in 8th and 9th I'm initializing Timer1 like you're suggesting.


Sooo...I'm stopping Timer2 and disable the interrupt INT1, which should conserve their values and while it's waiting, nothing happens with the display, so what's the problem now?
I changed the ISR for INT1 back to toggling instead of the if-statements, but that shouldn't make a difference, I guess?