One shot timer interrupt - how?

I have messing around with timer interrupts on my UNO boards, but it appears I’ve hit a wall.
Here’s the thing: I have an external clock (at 200Hz) connected to pin 2, and using a hardware interrupt, and changing between two “operation modes” whenever the external clock changes. So, every 2.5ms pin 10 does different things.

  • Mode 1, it outputs a bit stream, which lasts approximately 1.8ms. After that it returns to 0 V. No problem.
  • Mode 2, it reads a char value from a BLE daughter board. So far, so good. The problem is that, while in mode 2, I want pin 10 to output a variable duty cycle pulse (of period 2.5ms), defined by some variable. I thought of doing it using a timer interrupt - that is, whenever I enter mode 2, pin10 is pulled high, the timer interrupt is enabled, it counts until the duty cycle is met, and when it enters the ISR() function, I pull pin 10 low.

How can I force the ISR to execute only once? Because for low duty cycle values, it’ll repeat over the 2.5ms period.

Below is the simplified code of what I’m doing.
I’m turning the timer on and off using the TIMSK1 register, but with no success.
16000-1 counts with no prescaling should give 1ms.
The mode 1 bit stream is simulated by the 1.8ms delay.

int mode=1;

void setup()
{
  DDRB |= 1 << 2; //pin 10 as output

  // initialize timer1 
  noInterrupts();          
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;
  OCR1A = 16000-1;            
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS10);    // no prescaler 
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // enable all interrupts

  //hardware interrupt
  attachInterrupt(0, int_loop, CHANGE);
}

void loop() {
}

void int_loop() {
  if (mode==1){
    TIMSK1 = 0;
    PORTB |= (1 << 2);
    delayMicroseconds(1800);
    PORTB &= ~(1 << 2);
    mode=2;
    return;
  }
  else if (mode==2){
    TIMSK1 |= (1 << OCIE1A);
    PORTB |= (1 << 2);

    // BLE code yadayada

    mode=1;
    return;
  }
  
}

ISR(TIMER1_COMPA_vect) 
{
    PORTB &= ~(1 << 2); 
}

Can’t get the timer to work. Can anyone point me in the right direction?
Thanks

DS0000.png

    delayMicroseconds(1800);

You should probably not delay 1.8 milliseconds while interrupts are disabled. Your millisecond timer will miss ticks.

Hmm, ok, but that delay was for simulating the bitstream only. It doesn't really need to be accurate, it was just for illustrating the overall program.

Thanks for the tip, though. But that raises a question, isn't the millisecond timer managed by Timer 0?
TIMSK1 enables/disables Timer 1 exclusively, right?

Does this apply?

Cheers,
/dev

If you only want a timer interrupt to run once, then disable the interrupt inside the ISR. It will run once and disable itself.

andrebispo:
But that raises a question, isn’t the millisecond timer managed by Timer 0?
TIMSK1 enables/disables Timer 1 exclusively, right?

When an ISR is in progress the global interrupt enable flag is cleared, delaying all interrupts until the ISR returns. If an interrupt flag is already set when the interrupt happens again the interrupt will only happen once when the global enable is turned back on. That is why ISR’s have to be short.

andrebispo:
I want pin 10 to output a variable duty cycle pulse (of period 2.5ms), defined by some variable.

2.5ms is a long time - 2500 usecs - so why not just use micros() to manage the pulse rather than interrupts.

Use the system in several things at a time, but with micros() rather than millis()

…R