This really is a hard one. The meta-stability issue is handled as well as possible in the arduino hardware with some sync flip flops. Ditto the capture when you read it. If you stop the counter to read it, you'd miss counts. If you don't stop interrupts, you might see an overflow between when your read the counter hardware and get the total - right in the middle of a line of code. To quote some obscure Joe Jackson tune "everything gives you cancer, there's no cure, there's no answer". This used to take a good solid page of schiz for old TTL to get close to this good, actually.
Here's a thread on the newer double speed clone hardware. http://www.coultersmithing.com/forums/v ... 49&start=0
This uses counter 1 in an uno or compatible (including the newer lg8t) to do the initial count capture at rates up to whatever the arduino will synchronize (half the system cpu clock?) - at any rate, fast for what I use these for - geiger and scintillation counting.
When T1 overflows, it interrupts the cpu and there's an interrupt service routine to update the upper half of an unsigned long representing the total count.
That's not all, though. There are a couple of other unsigned longs I use here - one to save the previous value, and one to report the difference with, so you get "counts since I last checked" as an output if you like - often more useful - but you can output whichever of the variables you want (and skip some computation if you're not going to use the result).
This uses some rarely documented registers in the hardware, so here's at least an example I adapted from ??? - it's been awhile.
There are a few variables to declare, and set to zero from setup (I use a subroutine called init_ram() here), a counter setup routine, and a counter read routine you can call whenever you want "counts since last time" updated.
So here it is. Variables first:
You can either trust the arduino runtime to zero these or do it at setup() - I do it manually as I also have a software reset in many sketches...
Code: Select all
volatile unsigned long c1counts; // total counts from (hardware) counter 1, d5 pin
// top half incremented by timer overflow
volatile unsigned long c1z; // previous count for subtraction
volatile unsigned long curcount; // what we'll report, this - last total
Code: Select all
////////////////////////////////////////////////
void t1set() // setup timer1 as a counter that interrupts on overflow
{
TCCR1A = 0; // turn off all the weird compare/capture modes etc
TCCR1B = 6; // clock on falling edge (7 for rising)
TIFR1 = _BV(TOV1); // clear initial overflow - shouldn't need to, but do.
TCNT1 = 0; //zero out timer
TIMSK1 = _BV(TOIE1);// sets the timer overflow interrupt enable bit
}
Now to read the counter - and this is potentially flawed as there's not even a theoretical way to be perfect here (this IS right on average if you skip that one crazy case, or just use totals till overflow occurs - the code in theory handles those overflows as it is...tradeoffs).
Code: Select all
////////////////////////////////////////////////
void readCounts()
{ // just fast hardware counter (d5 pin), interrupts us only every 65k counts
/* Disable interrupts */
noInterrupts();
/* Read TCNT1 into c1counts low part */
c1counts &= 0xffff0000;
c1counts |= TCNT1; // from the hardware register @@@ there's a better capture register
curcount = c1counts - c1z; // just the new ones for this time interval
c1z = c1counts; // for next time
/* Restore global interrupt flag */
interrupts();
}
Code: Select all
////////////////////////////////////////////////
//////// manual isr setup, you bet, saves a whole library
// actual isr for hardware counter 1
ISR(TIMER1_OVF_vect)
{
c1counts += 0x00010000; // add one to top half of longword
}
//////////////////////////////////
curcount is what I report at intervals in my much more complex code (my own opsys, scheduler etc) - this is abstracted so you can use it in more simple cases.
I'm testing this now on the new nano double speed clone, I've only tried up to 1.11 mhz but it's fine there (this overflows counter1 on every 100ms reporting interval).