INT0 interrupt not being triggered without main() function

This is the code:

#define OC2A_PIN    PORTB3  // pin D11
#define OC2B_PIN    PORTD3  // pin D3
#define INT0_PIN    PORTD2  // pin D2
#define DEBUG_PIN   PORTD4  // pin D4
#define CONTROL_PIN PORTB4  // pin D12

#define INTF0_IS_SET   bit_is_set(EIFR,INTF0)
#define RISING_EDGE    bit_is_set(PIND,INT0_PIN)

#define CLEAR_INTF0   bitSet(EIFR, INTF0)
#define DEBUG_ON      bitSet(PORTD,DEBUG_PIN)
#define DEBUG_OFF     bitClear(PORTD,DEBUG_PIN)
#define TRIGGER_ON    bitSet(TCCR2A, COM2B1)      // Enable PWM
#define TRIGGER_OFF   bitClear(TCCR2A, COM2B1)      // Disable PWM deactivated

bool brakeOn =false;

int main() {
  setup();
  while (true) {
    loop();
  }
  return 0;
}

void setup() {
  setupPins();
  setupExtInt();
  setupTimer2();
}

void setupPins() {
  bitSet(DDRB, OC2A_PIN);
  bitSet(DDRD, OC2B_PIN);
  bitSet(DDRD, DEBUG_PIN);
}

void setupExtInt() {
  EICRA = 0;            // clear register
  bitSet(EICRA, ISC00); // Any logical change on INT1 generates an interrupt request
  EIMSK = 0;            // clear register
  bitSet(EIMSK, INT0);  // enable External Interrupt Request 0
  interrupts();
}

void setupTimer2() {
  TCCR2A = 0;             // clear register
  TCCR2B = 0;             // clear register
  bitSet(TCCR2A, WGM20);  // Mode 1, Phase Correct PWM, TOP = 0XFF, TOV Flag Set on BOTTOM
  bitSet(TCCR2B, CS20);   // No prescaling
  bitSet(TCCR2A, COM2A1); // Clear OC2A on Compare Match when up-counting.
  OCR2A = 127;            // set duty cycle
  OCR2B = 15;             // set duty cycle
};

void loop() {
  brakeOn = bit_is_set(PINB, CONTROL_PIN);
  if (INTF0_IS_SET) {
    CLEAR_INTF0;
    if (brakeOn) {
      if (RISING_EDGE) {
        TRIGGER_ON;
      }
      else {
       TRIGGER_OFF;
      }
    }
    else{
      TRIGGER_OFF;
    }
  }
}

It worked as expected when simulated with WOKWI [timer2_test].(timer2_test - Wokwi ESP32, STM32, Arduino Simulator)
The question is:
Why INT0 interrupt is not triggered and INTF0 is not set when function main() is commented out?
Placing sei(); or interrupts(); at the end of setup function did not solved this issue.

Look at the source code of the hidden main() function.

There may something your replacement neglects to do.

a7

Sure. Probably in the init() function. Any idea of might it be?

Where is the INT0 ISR?

I suggest to use the Arduino attachInterrupt function. It works as advertised.

Well, I wondered and chatGPT says something I can believe

If you do not define an ISR for INT0 (or any other interrupt), the default behavior is that the interrupt will occur, but no action will be taken.

There is a vector in the slot that points to a default handler that does nothing.

I am not sure what you are asking. If you wonder in what init() consists, read the code, same same for any other functions. You can google it up, but remember that all the source code that ever gets into your sketch object code is on your machine somewhere. It's good to know where that is.

a7

After some research, I concluded that the Arduino framework somehow prevents polling the External Interrupt flag (INTF0). The same hardware and code worked flawlessly when the main function was explicitly defined. I'll leave the "why" to the Arduino experts.

I'm sure they'll be all over it!

a7

I don't see how the code as posted could ever work, without an ISR for the external interrupt defined.

Undefined ISRs don't just "do nothing"; they invoke "bad_interrupt", which jumps back to the start address of the program.

Disassembly of section .text:

00000000 <__vectors>:
   0:   0c 94 34 00     jmp     0x68    ; 0x68 <__ctors_end>      [reset]
   4:   0c 94 46 00     jmp     0x8c    ; 0x8c <__bad_interrupt>  [int0]
   8:   0c 94 46 00     jmp     0x8c    ; 0x8c <__bad_interrupt>  [int1]
   c:   0c 94 46 00     jmp     0x8c    ; 0x8c <__bad_interrupt>  [pin change 0]
       : 
0000008c <__bad_interrupt>:
  8c:   0c 94 00 00     jmp     0       ; 0x0 <__vectors>

The main difference in the disassembly looks like the Arduino main()/init() will enable interrupts significantly earlier, so the program will restart before any of the pin manipulation is done, whereas with the custom main(), the code in setupPins() will run before interrupts are enabled.

I believe it's possible to poll the external interrupt pins without actually enabling the interrupt, in which case you wouldn't need an ISR for it.

I'm a bit confused as to what "working" vs "not working" means - how is the interrupt being triggered, and what is expected to happen (and/or doesn't happen.)

2 Likes

You have no basis for that conclusion, but it is certainly easier to blame the "Arduino framework" than your nonfunctional code.

1 Like

Before jumping to conclusions, I recommend trying this code simulation: Arduino Layer vs INTF0 Behaviour - Wokwi ESP32, STM32, Arduino Simulator.
It allows you to simulate the same hardware and code, both with and without the Arduino layer. The switch in the simulation does not have any bouncing.

It worked as you WANTED in Wokwi.
I'll stand by my statement that the sketch can't possibly work like that, with no ISR defined. Wokwi must have some bug(s) in their interrupt processing (or they use a different "arduino layer" code, that does something different than the hardware wrt un-handled interrupts.)
I don't see how to get wowki to show anything below the source code level, so it's tough to check.

1 Like

No need or interest to do so, because your code can't possibly work on the hardware. If the code does work on the simulation, the simulation is definitely buggy.

Anyway, do have fun!

Ok. that example never enables interrupts at the global level (which the Arduino startup code DOES do.)

This slightly modified version has a couple of changes, and should match the actual hardware behavior: Arduino Layer vs INTF0 Behaviour Copy - Wokwi ESP32, STM32, Arduino Simulator

  1. Blinks the builtin LED during setup, thus indicating whenever the chip is reset.
  2. Enables interrupts at the end of setup.

Like the actual hardware, it doesn't "work."

The wokwi example in the initial post also doesn't ever enable interrupts, although the source code quoted in the post DOES. Grr.

So: Not a bug in the arduino core, and not a wokwi bug. Just a difference in your code.

Placing a cli() function call at the last line of setup() function allowed the same code to run with and without the Arduino Frame. Case solved

I was thinking of suggesting that, at least as a test...

Note that this will break most of the functionality that would have been added by using the Arduino framework (ie millis() and Serial)

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