I have made a simple counter program for the uno and am having trouble with rates above ~148kHz or so... This could be a hardware limitation or something in my code that is bad... Ideally I would be happy counting up to 1 million pulses per second. -Thanks!
You can put a prescaler in front to get the count from 1 MHz to 100 kHz, safely within your range. Almost any msi divide x 10 will work, like a 74LS90.
The correct way to do what you want is to have the ISR increment the counter continuously.
Use millis() to manage timing as illustrated in Several Things at a Time. Your approach of if(millis() >= stop_time) will not work when the rollover arises.
When each interval has elapsed then your code in loop() needs to capture the latest value in the counter - something like this
So I modified my code. Now I'm having more problems. The reported counts are about 15% higher than they should be (using function generator as source and digital scope to measure freq). That is very weird. In my first code I agree than detaching the interrupts wasn't ideal, but I didn't want the button interrupt to be doing stuff/wasting time while counting...
@Robin2, I looked at your SeveralThingsAtTheSameTime code and do not understand why that is different/better than my stop_time solution. As I see it, your code would also break at the rollover of the millis() output (which takes about 49.7 days). Am I missing something here?
Anyway, it doesn't look like I can measure count rates from 1 Hz to 1 MHz using the uno (I guess due to hardware reasons). Maybe I'll dust off my Due and try it out.
Thanks!
// A simple counter program to count positive pulses which are >12? ns wide and >=2.75 volts amplitude
// doesn't seem to work at count rates above 148 kHz or so???
volatile unsigned long counts = 0; //goes from 0 to 4,294,967,295
unsigned long final_counts = 0;
const int dwell = 1000; //dwell in ms for couter, can be up to 32,767, or about 30 seconds
volatile unsigned long start_time;
volatile unsigned long stop_time;
volatile boolean button = false;
int measured_time;
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(115200);
pinMode(2,INPUT); // connect counter or function generator (postive pulses)
pinMode(3,INPUT_PULLUP); // connect momentary pushbutton switch to ground
attachInterrupt(digitalPinToInterrupt(2), detect_count, RISING); // counts to pin 2, UNO can do LOW, CHANGE, RISING, FALLING
attachInterrupt(digitalPinToInterrupt(3), detect_button, LOW); // button to pin 3, UNO can do LOW, CHANGE, RISING, FALLING
Serial.print("Dwell (ms)");
Serial.print("\t");
Serial.println("Counts");
}
void detect_button(){button = true;}
void detect_count(){counts++;}
void loop() {
if(button == true){
start_time = millis(); // get time since arduino started in ms, can go for 49.7 days before rolling over
stop_time = start_time + dwell;
noInterrupts();
counts = 0;
interrupts();
loopback:
if(millis() >= stop_time){
noInterrupts();
final_counts = counts;
interrupts();
measured_time = millis() - start_time;
Serial.print(measured_time); // report resulting counts
Serial.print("\t\t");
Serial.println(final_counts);
}
else{
goto loopback;
}
button = false;
}
}
@PaulS: You are right about not needing an interrupt to detect the button press. Fixed that, and it fixed my count accuracy problem!
The goto for me is a sweet sweet crutch, I am too dumb to do anything different currently. It seems to be a logical and elegant solution here, but I do agree that in bigger more complicated codes using multiple gotos would be confusing.
I thought I would put my finalized code up. Also, I tested it on the DUE. So with the UNO, max count rate is ~140 kHz. On the DUE, it is ~240 kHz. Not as big of a bump as I was expecting.
I have given a simplified version which does not count TCNT1 overflows and is limited to the 16 bit value. It also uses micros() instead of a timer for the dwell which would be more accurate, and it does not disable the timer0 millis() interrupts like Nick Gammon's code does.
//frequency counter using Timer1 counter without overflow count
//TCNT1 16 bit max value = 65,534
//20ms sample period gives frequency counter to a bit over 3.2 mhz
unsigned int dwell = 20000; // dwell in microseconds for counter
unsigned long final_counts;
unsigned long start_time;
unsigned long measured_time;
void setup()
{
Serial.begin(115200);
TCCR1A = 0; //initialize Timer1
TCCR1B = 0;
TCNT1 = 0;
pinMode( 5, INPUT_PULLUP); //external source pin for timer1
}
void loop()
{
start_time = micros();
TCNT1 = 0;//initialize counter
// External clock source on Timer1, pin (D5). Clock on rising edge.
// Setting bits starts timer
TCCR1B = bit (CS10) | bit (CS11) | bit (CS12); //external clock source pin D5 rising edge
while (micros() - start_time < dwell) {} // do nothing but wait and count during dwell time
TCCR1B = 0; //stop counter
final_counts = TCNT1; //frequency limited by unsigned int TCNT1 without rollover counts
measured_time = micros() - start_time;
Serial.print(measured_time); // report resulting counts
Serial.print("\t\t");
Serial.println(50 * final_counts); //20ms sample in Hz
}