I'm using a 16MHz Uno to count irregular pulses from an external source (connected to pin 3).
Using interrupts my code takes around 7us per pulse. Can anybody think of a significantly faster way of doing this?
It looks like timer 1 can be driven (clocked) by pin T1 just by changing the Clock Select. It's implied by Figure 17-1 that timer 2 can also be driven by a pin but I can't find how to enable it.
I suspect you can replace the trigger in your code with the "Input Capture" part of timer 1 and have nearly the entire solution in hardware.
You will need to read and understand how sampling is performed. I believe, if the pulse is too small, that it can be missed. You should assume that a pulse less than 2 / F_CPU = 0.125 microseconds is going to be difficult or even impossible to count.
I'm not in the mood (or with time) to google stuff you didn't look for. But, if you go through the Arduino forum, or find a post by antonisk from Greece, you'll see a reply from me showing how to set up one of the timers as a counter for pulses and avoid the example given by Rob.
It works great for pulses that come once in a while, but if you get a lot of pulses, your software is going to grind into a halt (like antonisk's example).
Have a look at that code and see what you make of it. If you need any help, give us a shout.
By the way, I wouldn't count pulses on change. It's worse for the overall performance of the program.
Robtillaart, thanks for taking the time to look at the code. The bottle-neck is in calling count_inc(), so adding the if statement actually slows it down slightly.
Coding Badly, the timers look very promising. If I understand the docs, these are free-running counters independent from the main CPU clock, and can run at up to 20MHz. I need to read some more and run some tests, but if I can get into the MHz range I shall be very happy!
OK, I have now chewed through more than 70 pages of timer documentation. Phew!
And I'm happy to report that it works very, very nicely. Right now I have Timer-1 counting pulses down to 250ns apart, with no dropped counts. Not one! So, thanks very much to Coding Badly for the idea.
Next I need to deal with overflows, and test higher pulse-rates. The external clock is sampled in sync with the CPU clock, which will limit me to less than half the CPU-frequency (p141-2 of the ATMEL datasheet). Here's the code, in case anybody else ever needs a reliable and CPU-independent pulse-counter:
/*
Count pulses on pin 5 via Timer-1
Start with rising edge on pin 2
Stop with falling edge on pin 2
ToDo:
- deal with overflows
*/
#include <Streaming.h>
void setup() {
Serial.begin(19200);
pinMode(2, INPUT); digitalWrite(2, HIGH); // trigger on pin-2 = PD2 = interrupt-0
pinMode(5, INPUT); digitalWrite(5, HIGH); // pulses on pin-5 = PD5 = T1
TCCR0A=0; // setup timer-0
TCCR0B=0;
TIMSK0=0;
TCCR2A=0; // setup timer-2
TCCR2B=0;
TIMSK2=0;
TCCR1A=0; // setup timer-1
TCCR1C=0;
TIMSK1=0;
GTCCR=0;
counting_stop();
}
void loop() {
Serial << "Counting:" << endl;
while (1) {}
}
void counting_start() {
Serial << "Ready ... ";
TCCR1B=7; // start
attachInterrupt(0, counting_stop, FALLING); // ready to stop
}
void counting_stop() {
unsigned long counts;
TCCR1B=0; // stop
counts = TCNT1;
TCNT1=0;
attachInterrupt(0, counting_start, RISING); // ready to start
Serial << counts << " pulses" << endl;
}