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...
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 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
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.
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:
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?