My goal is for an Arduino Uno timer to trigger external electronics with a limited number of precision pulses (ie. not an ongoing stream as would be obtained with PWM). While the CTC interrupt timing is generally working as expected, I don't understand why the first interrupt call is immediately after the CTC interrupt is defined and enabled. As I set TCNT1 to zero, shouldn't the first interrupt call occur after TCNT1 is incremented all the way up to the OCR1A value ?
The program and CRO image below demonstrates the issue. The CRO image shows the Blue line is sent HIGH from setup() immediately before the interrupt is enabled. What surprises me is then the Red line goes HIGH (indicating the interrupt has triggered) immediately afterwards. I expected a gap of 1milliSecond, as per the subsequent operation of the interrupt (Blue pulses). I've included an arbitrary timing delay to prove that the issue isn't a timing fluke; it occurs whatever the starting point is.
How can I make the first interrupt occur 1milliSecond after the CTC is enabled ?
Many thanks.
#define ledPin 13 // Blue line on CRO
#define readyPin 12 // Red line on CRO
int done=0;
volatile int counter=0;
void setup()
{
pinMode(ledPin, OUTPUT);
pinMode(readyPin, OUTPUT);
digitalWrite(ledPin, LOW);
digitalWrite(readyPin, LOW);
delayMicroseconds(2535); // an arbitrary delay to show the issue is not a timing fluke
noInterrupts(); // disable all interrupts
TCCR1A = 0; //reset all bits
TCCR1B = 0; // reset all bits
OCR1A = 1999; // compare match register is 1ms duration.
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS11); // 8 prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
digitalWrite(ledPin, HIGH);
TCNT1 = 0; // set counter to 0
interrupts(); // enable all interrupts
}
ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine
{
if (counter==0) digitalWrite(readyPin, HIGH); //show on CRO when the interrupt is first called.
if (counter<5) {
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); // toggle LED pin
counter=counter+1;
}
else TIMSK1 &= ~(1 << OCIE1A); // disable CTC interrupt
}
void loop()
{
}
Like you I am surprised at this but I would try to stop the initial interrupt by clearing the ICF1 bit in the TIFR1 register just prior to your "enable timer compare interrupt" statement.
Edit : just taken another look at the Atmel documentation; that should - be cleared by setting the ICF1 bit !
Thanks stowite, I've just tried inserting the new line shown below - sorry, didn't make any difference
TCCR1B |= (1 << CS11); // 8 prescaler
TIMSK1 &= ~(1 << ICF1); // NEW LINE try clearing ICF bit prior to enabling
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
The only other thing I can think of is that the default counter direction might be such a way that upon the first increment/decrement, the interrupt is immediately triggered. But the only references I could see on the Atmel datasheet relating to counter direction related to PWM, so not sure where to go with this idea...
I do this all the time, and have never have problems, but I imagine that an interrupt could set a bit asynchronously. Are you suggesting to disable interrupts before the operation?
Our intent is to clear just OCF1A. The logical-or version clears all flags. That is the definition of a bug.
For most Arduino code, it is almost a distinction without a difference. There will rarely be side-effects. If Output Compare B Match Interrupt (OCF1B) is enabled, the interrupt service routine will nearly always fire before our flag-clearing code finishes.
But, "rarely" in the world of software is sometimes equivalent to "race condition". Which is exactly what the logical-or version creates. After an interrupt service routine returns, one machine instruction is executed before the next interrupt service routine fires. If that one machine instruction just happens to be the TIFR1 assignment, an unintended flag can be cleared; an interrupt could be missed.
If the logical-or version is executed with interrupts disabled an unintended flag is much more likely to be inadvertently cleared.
While I have not seen it used in Arduino code, there is a valid use for polling the OCF1A / OCF1B flags. If we are polling then there is a high probability that an unintended flag will be cleared.
In other words, in all situations the logical-or version can cause problems.
OK, I see it now. Thanks!
Strange how long it took to sink in. But then, the logic of clearing a bit by setting it is completely backwards, and I have been using that line for perhaps 20 years without thinking about it.