Hi fellow developers,
I'm using and Arduino Mega 2560 and I'm attempting to measure a signal period using timer 4 in input capture mode.
I'm using an example from Nick Gammon as a base that I have altered.
(example found here : Gammon Forum : Electronics : Microprocessors : Timers and counters)
Things that I changed:
Example was meant for timer 1 I switch the registers to use the ones for timer 4.
I created my own simple square wave generation for testing (could be the culprit)
Added a simple counter to see how many period are gathered successfully.
Hardware: I have D47 and D49 connected together and I have a logic analyzer snooping the signal to compare the actual period of the signal.
So my problem is that the code is working at first, but after around 2-3 seconds it seems that the interrupt vector TIMER4_CAPT_vect stops being raised but, the code is not stuck. Interrupt TIMER4_OVF_vect is still being raised (I checked with a global counter being print in the main loop) and the main loop is still executing. Performing a reset makes the code working again for a similar amount of time (as expected).
Here is the code:
void setup()
{
pinMode(47, OUTPUT);
Serial3.begin(115200);
prepareForInterrupts ();
Serial3.print ("Starting test");
}
volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;
volatile uint16_t TimerCaptCounter = 0;
// timer overflows (every 65536 counts)
ISR (TIMER4_OVF_vect)
{
overflowCount++;
} // end of TIMER4_OVF_vect
ISR (TIMER4_CAPT_vect)
{
TimerCaptCounter ++;
// grab counter value before it changes any more
unsigned int timer4CounterValue;
timer4CounterValue = ICR4; // see datasheet, page 117 (accessing 16-bit registers)
unsigned long overflowCopy = overflowCount;
// if just missed an overflow
if ((TIFR4 & bit (TOV4)) && timer4CounterValue < 0x7FFF)
overflowCopy++;
// wait until we noticed last one
if (triggered)
return;
if (first)
{
startTime = (overflowCopy << 16) + timer4CounterValue;
first = false;
return;
}
finishTime = (overflowCopy << 16) + timer4CounterValue;
triggered = true;
TIMSK4 = 0; // no more interrupts for now
} // end of TIMER4_CAPT_vect
void prepareForInterrupts ()
{
noInterrupts (); // protected code
first = true;
triggered = false; // re-arm for next time
// reset Timer 4
TCCR4A = 0;
TCCR4B = 0;
TIFR4 = bit (ICF4) | bit (TOV4); // clear flags so we don't get a bogus interrupt
TCNT4 = 0; // Counter to zero
overflowCount = 0; // Therefore no overflows yet
// Timer 4 - counts clock pulses
TIMSK4 = bit (TOIE4) | bit (ICIE4); // interrupt on Timer 4 overflow and input capture
// start Timer 4, no prescaler
TCCR4B = bit (CS40) | bit (ICES4); // plus Input Capture Edge Select (rising on D8)
interrupts ();
} // end of prepareForInterrupts
bool flip = false;
uint16_t Counter = 0;
void loop()
{
// wait till we have a reading
if (triggered)
{
// period is elapsed time
unsigned long elapsedTime = finishTime - startTime;
Counter ++;
Serial3.print ("Took: ");
Serial3.print (float (elapsedTime) * 62.5e-9 * 1e6); // convert to microseconds
Serial3.print (" uS. ");
Serial3.println(Counter);
prepareForInterrupts ();
}
if(flip)
{
flip = false;
digitalWrite(47, HIGH);
}
else
{
flip = true;
digitalWrite(47, LOW);
}
delay(10);
}
I have tried adding a timeout where if I don't have a period after a certain amount of time
I recall the prepareForInterrupts () function but it doesn't seem to be helping in anyway.
Here are some answers to some of the questions that you might be wondering about.
No I don't always get the exact same number of periods before the code fails.
Changing the period of my square wave, lets say from 20 ms to 120ms means that the overall time until it stops working is the same but I get a smaller number of periods.
I have added some snippets of the testing runs bellow. As you can see the last couple of measures always seems to lose accuracy compared the the rest.
Here are my questions:
Do you know what could cause this behavior ?
Do you think it's interrupt related or am I missing a register that should be cleared periodically ?
Any help would be greatly appreciated
Here are results of a few runs: