0

I am learning Arduino interruptions and I am unable to explain the behavior of a minimalistic interrupt driven project. The project is the following: an Arduino UNO is connected to two LEDs, on ports 9 and 10, and a button on port 2. The button is listened to for RISING interruptions, which are expected to trigger the following 3 seconds sequence: LED1 HIGH (one second); both LEDs HIGH (one second); LED2 HIGH (one second); during which pressing the button should have no effect.

#define led1Pin 10
#define led2Pin 9
#define buttonPin 2


void setup() {

  cli();//stop interrupts

  pinMode(led1Pin, OUTPUT);
  pinMode(led2Pin, OUTPUT);
  pinMode(buttonPin, INPUT);
  digitalWrite(led1Pin, LOW);
  digitalWrite(led2Pin, LOW);
  attachInterrupt(digitalPinToInterrupt(buttonPin), f, RISING);

  TCCR0A = 0;// set TCCR2A register to 0
  TCCR0B = 0;// set TCCR2B register to 0 (sets timer0 prescaler values to "disable timer")
  TCCR2A = 0;// set TCCR2A register to 0
  TCCR2B = 0;// set TCCR2B register to 0 (sets timer2 prescaler values to "disable timer")

  //set timer1 interrupt at 1Hz
  TCCR1A = 0;// set TCCR1A register to 0
  TCCR1B = 0;// set TCCR1B register to 0

  // set compare match register for 1hz increments
  OCR1A = 15624;// = (16*10^6) / (1*1024) - 1
  TCCR1B |= (1 << WGM12);   // turn on CTC mode 1 (compare to OCR1A)
  TCCR1B |= (1 << CS12) | (1 << CS10);// CS12|CS10 => Prescaler 1024
  TIMSK1 = 0;// disable timer compare interrupt

  sei();
}

volatile int index=0;

volatile byte previousState=LOW;
volatile byte state = LOW;

void loop() {
  if (state && !previousState) {
    digitalWrite(led1Pin, HIGH); index=1;
    TCNT1H = 0;
    TCNT1L = 0; //initialize counter value to 0
    TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
    previousState = HIGH;
  }
}

void f() {
  state = HIGH;
}

ISR(TIMER1_COMPA_vect){ //timer1 interrupt 1Hz
  switch(index) {
  case 1:
    digitalWrite(led2Pin, HIGH); index=2;
    break;
  case 2:
    digitalWrite(led1Pin, LOW); index=3;
    break;
  case 3:
    digitalWrite(led2Pin, LOW); index=0;
    state=LOW; previousState=LOW;
    TIMSK1 = 0;// disable timer compare interrupt
    break;
  default:
    break;
  }

}

Code adapted from https://www.instructables.com/id/Arduino-Timer-Interrupts/

Upon pressing the button, state is set to HIGH, which assuming previousState and state were LOW, should get the loop to perform a set of action once as it will set previousState HIGH.

These actions are the following: set led1Pin to HIGH, set TCNT1H and TCNT1L to 0, which should put the Timer1 counter to 0, and then set TIMSK1 to 0b00000010, that is, 1 << OCIE1A, to accept the interrupt when it will be triggered, supposedly one second from the instruction that set the counter to 0, as the timer fires interrupts at 1Hz.

But while the expected behaviour is LED 1 on for one second, both LEDs on for one second, then LED 2 on for one second, the observed behaviour is the following, from my tests:

  • First time pressing the button since Arduino reset : LED 1 and LED 2 on for one second, LED 2 on for one second.

  • Button presses properly ignored until LED 2 turns off.

  • Subsequent button presses : either proper behaviour (LED1 on, both LEDs on, LED2 on); or "first press" behaviour (both LEDs on, LED2 on). In both cases, the cycle sometimes instantly restart ([optional : LED1 on;] both LEDs on; LED2 on; LED1 on; both LEDs on; LED2 on), with each state taking one second.

The non-deterministic nature of the problem lets me think I'm mishandling interruptions. The project behaves as if enabling timer compare interrupts instantly triggered one, despite setting the relevant registers to 0 before that. I was unsuccessful in finding information regarding this that would explain the problem.

Is there asynchronous behaviour when setting the TCNT1H and TCNT1L registers to 0 ? Can a compare match interruption somehow be buffered ? And how comes the cycle sometimes execute twice ?

1 Answer 1

1

The project behaves as if enabling timer compare interrupts instantly triggered one

This can indeed happen, and it's likely what you are experiencing.

Explanation: an interrupt fires when three bits are simultaneously set:

  • the “interrupt flag” that signals the detection of some specific event
  • the “interrupt enable” bit associated with that particular interrupt
  • the “global interrupt enable” bit that is set by the sei() instruction

In your case, the relevant interrupt flag is OCF1A (Timer/Counter1, Output Compare A Match Flag), in register TIFR1 (Timer/Counter1 Interrupt Flag Register). This flag is raised by a “timer compare” event, i.e. when the timer value hits the value stored in OCR1A. If the interrupt fires, the flag is automatically cleared when the CPU starts executing the corresponding ISR. Alternatively, the flag can be cleared manually by overwriting it with a logic 1 (yes, that's kind of backwards). It is worth noting that the flag records the compare events whether or not the interrupt is enabled.

You normally expect the interrupt flag to be the last of these three bits to be set, so that the interrupt is triggered by the compare event. But this need not be the case. In your program, it is likely that the interrupt flag is raised one second after startup. The global interrupt enable is set by your sei() at the end of setup(). Thus, at this point, as soon as you set the interrupt enable bit the interrupt fires immediately.

The standard solution to this problem is to clear the interrupt flag right before setting the interrupt enable bit:

TIFR1  |= _BV(OCF1A);   // clear timer compare flag
TIMSK1 |= _BV(OCIE1A);  // enable timer compare interrupt

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.