How to use INT0 interrupt and Timer0 interrupt at the same time

Hi. I am using Arduino UNO R3 and I'm trying to write a program with a HW interrupt using INT0 (pin2) and a SW interrupt using timer0. They work fine separately, but the HW interrupt stopped working as soon as I enable the timer interrupt. I'm suspecting that the issue comes from setting the clock for timer0 that affects detecting the edge on the hardware pin.

Below is the snippet of code where the problem occurs.

#define startButton 2  // Yellow
#define emergencyButton 3 // Green

static unsigned long last_interrupt_time = 0;
void setup() {
  timerConfig();
  attachInterrupt(digitalPinToInterrupt(emergencyButton), emergencyStopISR, RISING);  // setup emergency button interrupt pin, mode=RISING/FALLING (trigger at rising edge)
  attachInterrupt(digitalPinToInterrupt(startButton), startupISR, RISING);
}

void loop() {
 // main loop
}

// setup flag to signal stop sequence
void emergencyStopISR(){
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 100) { // debouncing, ignore any triggers within 100ms
    // set up stop flag
  }
  last_interrupt_time = interrupt_time;
}

// setup flag to enable functions
void startupISR(){
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 100) { // debouncing, ignore any triggers within 100ms
    // set up start flag
  }
  last_interrupt_time = interrupt_time;
}

void timerConfig() {
  // disable all interrupts  
  cli();

  // configure timer0 to the frequency for x-drive (100 Hz)
  TCCR0A = 0; // reset registerA for timer0
  TCCR0B = 0; // reset registerB for timer0
  TCNT0 = 0;  // reset timer0 counter to 0
  OCR0A = 155; // set compare match register trigger value
  TCCR0A |= (1 << WGM01); // turn on CTC mode
  TCCR0B |= (1 << CS02) | (1 << CS00); // Set CS02, CS00 bits for 1024 prescaler
  TIMSK0 |= (1 << OCIE0A); // enable timer compare interrupt
  // enable all interrupts
  sei(); 
}

unsigned int counterA = 0;
const unsigned int DIVIDER = 5;  // divider of 100Hz --> 20Hz

// ISR for timer0 interrupt:
ISR(TIMER0_COMPA_vect){
  counterA++;
  if (counterA == DIVIDER){
    // isr code
    counterA = 0;
  }
}

Thank you!

Below is the snippet of code

Always post ALL the code. The error is often in the code you did not post. Or post the minimum code that will compile, run and demonstrate the problem.

Timer0 has nothing to do with interrupt detection on the INT0 pin.

What led you to the conclusion about why it was failing to react to INT0? How do you have the switches wired?

You are re-configuring the millis() timer timer0 which you are also relying on in the ISRs. Maybe millis() no longer works. Also look at the volatile storage qualifier for global variables updated in an ISR.

Thank you for replying.
As per @jremington's request. Here's the full working version of the code.
I have tested with commenting out timerConfig() and it worked normally, but when I run that piece of configuration, it stopped reacting to the INT0 interrupt.

Responding to @6v6gt's comment, I've done a little research and noticed that millis() will be blocked in ISR. This should not matter because the ISR only takes the value from millis() once. However, I also noticed that millis() is using timer0, but since I'm using the compare register on Timer 0 and not altering its frequency, I don't think that will change millis() either.

*The part where I use millis() is for debouncing and I'm not sure if there's other simple ways to do it. It would be greatly appreciated if anyone can share some other interesting way to implement a debouncing on buttons!

Thank you!

Lastly, here's the full version of the code that I'm running.

#define startButton 2  // Yellow
#define emergencyButton 3 // Green
#define relay 4

// global flags
volatile int startFlag = 0;
volatile int stopFlag = 0;
enum state { STOP, NORMAL, IDLING };
volatile state currState = IDLING;

static unsigned long last_interrupt_time = 0;

void setup() {
  Serial.begin(9600);
  pinMode(relay, OUTPUT);
  digitalWrite(relay, HIGH);  // turn off relay
  Serial.println(digitalRead(relay));
  attachInterrupt(digitalPinToInterrupt(emergencyButton), emergencyStopISR, RISING);  // setup emergency button interrupt pin, mode=RISING/FALLING (trigger at rising edge)
  attachInterrupt(digitalPinToInterrupt(startButton), startupISR, RISING);
  timerConfig();
}

void loop() {
  switch (currState) {
    case IDLING:
      // state transition logic
      if (stopFlag) {
        currState = STOP;
      } else if (startFlag) {
        currState = NORMAL;
      }
      // main code
      Serial.println("I");
      break;
    case NORMAL:
      // state transition logic
      if (stopFlag) {
        currState = STOP;
      }
      // main code
      Serial.println("N");
      break;
    case STOP:
      // main code
      Serial.println("S");
      break;
  }
}
// setup flag to signal stop sequence
void emergencyStopISR(){
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 100) { // debouncing, ignore any triggers within 100ms
    // code for ISR
    stopFlag = 1;
  }
  last_interrupt_time = interrupt_time;
}

// setup flag to enable functions
void startupISR(){
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 100) { // debouncing, ignore any triggers within 100ms
    // code for ISR
    if (currState == STOP) {  // reset state
      currState = IDLING;
      startFlag = 0;
      stopFlag = 0;
    } else {
      startFlag = 1;
    }
  }
  last_interrupt_time = interrupt_time;
}

// setup timer interrupt for x, y drive
void timerConfig() {
  // disable all interrupts  
  cli();

  // configure timer0 to the frequency for x-drive (100 Hz)
  TCCR0A = 0; // reset registerA for timer0
  TCCR0B = 0; // reset registerB for timer0
  TCNT0 = 0;  // reset timer0 counter to 0
  OCR0A = 155; // set compare match register trigger value, increment motor by 5
  TCCR0A |= (1 << WGM01); // turn on CTC mode
  TCCR0B |= (1 << CS02) | (1 << CS00); // Set CS02, CS00 bits for 1024 prescaler
  TIMSK0 |= (1 << OCIE0A); // enable timer compare interrupt

  // enable all interrupts
  sei(); 
}

unsigned int counterA, counterB = 0;
const unsigned int DIVIDER = 5;  // divider of 100Hz --> 20Hz

// ISR for timer0 interrupt: X-drive motor control -- too fast, will not allocate time to regular sequence
ISR(TIMER0_COMPA_vect){
  counterA++;
  if (counterA == DIVIDER){
    Serial.println("triggered");
    counterA = 0;
  }
}

That is clear but if your changes to timer0 prevent the millis() timer from advancing, then :

will never be true.
Try using timer2 instead.

As long as you check startFlag and stopFlag only inside loop() no interrupts are required at all.

It is not a SW (software Interrupt) interrupt. It is an Internally generated hardware interrupt.

For independent start and stop buttons, there is no need to debounce.

You haven't answered my questions about why you think INT0 is not being acknowledged or how the buttons are wired.

Interrupt variables MUST be declared volatile:
unsigned int counterA, counterB = 0;

Do not do ANY serial I/O in an interrupt service routine.

    Serial.println("triggered");

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.