CTC interrupt occurring too soon

Hi all,

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...

TIMSK1 &= ~(1 << ICF1);  // NEW LINE try clearing ICF bit prior to enabling

This won't help. As per the data sheet, you SET the bit in TIFR1 to clear the interrupt flag.

TIFR1 |= (1 << ICF1);  // clear any pending ICF interrupt prior to enabling

Thanks both for the suggestions.

TIFR1 |= (1 << ICF1); didn't solve my problem, but lead to me trying:

TIFR1 |= (1 << OCF1A); which works well: the first interrupt instance now has the right timing

Cheers.

Yes, that makes sense. ICF1 is the input capture flag. I didn't look closely enough at the mnemonic.

dancol:
TIFR1 |= (1 << OCF1A); which works well: the first interrupt instance now has the right timing

That is not the correct way to clear the flag. While it may "work" it also has a very undesirable side-effect.

@CodingBadly,

sorry, I'm lost. What would you recommend is the correct way to clear the flag ?

I haven't seen any undesirable side effects so far... what are they ?

Thanks

Hint #1: What about the other bits?

I don't see a problem, the instruction uses an OR operation to set the OCF1A bit only.

Hint #2: Expand the expression.

TIFR1 = TIFR1 | (1<<OCF1A);

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?

No. The way TIFR1 bits are cleared is atomic (it has to be or the processor is unreliable). There is no need to disable interrupts.

So what is the problem with that approach?

If TIFR1 is non-zero when that line of code is executed...

TIFR1 = TIFR1 | (1<<OCF1A);
...
TIFR1 = 0xFF | (1<<OCF1A);
...
TIFR1 = 0xFF;

Oops. Cleared way too many flags.

You still have me mystified!

The docs say that if the OCF1A bit in the flag register is set by an interrupt, "clear" the interrupt by writing a 1 to the OCF1A bit.

It never made sense, but I don't know any other way to perform that task!

Write zero to the other bits...

TIFR1 = (1<<OCF1A);

Hmmm, how would writing zeros, versus performing the OR operation, affect other possibly pending interrupts, like OCF1B?

I honestly have never seen this discussed.

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.