How does ISR macro install interrupts

Hi! Im having trouble understanding this topic. I understand the attachinterrupt() approach, because we have to call it inside setup. But ISR macro is used outside setup andas loop. So, when using the macro, how andas when isr's are installed? Thanks in advance

You could read Nick’s blog for more details

Franq:
Hi! Im having trouble understanding this topic. I understand the attachinterrupt() approach, because we have to call it inside setup. But ISR macro is used outside setup andas loop. So, when using the macro, how andas when isr's are installed? Thanks in advance

An ISR is simply a block of code called by a particular interrupt.

Let's say you wanted to see what the watchdog timer does. Well, you would look at the datasheet for the AVR device and find that the name of the watchdog timer interrupt is "WDT_vect" (Watch Dog Timer [interrupt] Vector).

So, in your code you could do this:

// setup watchdog timer prescale and timeout value
WDTCSR |= _BV(WDIE); // enable watchdog timer interrupt

ISR (WDT_vect)
{
    Serial.println ("Got a WDT interrupt here!");
}

Now, this is just to show the concept. Normally you would not put things like a print statement inside an ISR... also the "code" shown above is incomplete.

Make sense?

Yes, very clear! But still nota understand when that ISR is installed so the processor recognizes It as ISR. Thats why i made the contrast with attachinterrupt, which we need to call in setup, using the macro we never call antes function yo installed the interrupt vector

Note how you can use the ISR macro only where you could define a function - ie, outside of any other functions. The ISR() macro tells the compiler that instead of being treated as a normal function that will be called by name from within the code, that it's an ISR, and to put the pointer to it into the vector table.

That's how attachInterrupt works - buried in the core libraries, the ISR() macro is used to create an ISR which checks if you've attached an interrupt to it with attachInterrupt(), and if so, calls it.

DrAzzy is correct. Your ISR is installed at compile time, and the direct jump to your function is inserted into the interrupt vector table and burned into flash when you upload the .hex file. Unlike attachInterrupt where it wraps your function and has to check at run time to make sure there is a function attached -- when you use ISR() it is absolutely attached and can't be detached. There is less overhead and it's more efficient.

Be aware, you still have to set the bits in control registers to enable the interrupt you're installing. That isn't done for you. But it will save all the registers and flags you use, and restore them when you're done. And it returns with the IRET instruction.

Franq:
Yes, very clear! But still nota understand when that ISR is installed so the processor recognizes It as ISR. Thats why i made the contrast with attachinterrupt, which we need to call in setup, using the macro we never call antes function yo installed the interrupt vector

That is because the core Arduino code already has ISR functions set up for the external interrupts.

#define IMPLEMENT_ISR(vect, interrupt) \
  ISR(vect) { \
    intFunc[interrupt](); \
  }

#if defined(__AVR_ATmega32U4__)

IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1)
IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_2)
IMPLEMENT_ISR(INT3_vect, EXTERNAL_INT_3)
IMPLEMENT_ISR(INT6_vect, EXTERNAL_INT_4)

#elif defined(EICRA) && defined(EICRB)

IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_2)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_3)
IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_4)
IMPLEMENT_ISR(INT3_vect, EXTERNAL_INT_5)
IMPLEMENT_ISR(INT4_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT5_vect, EXTERNAL_INT_1)
IMPLEMENT_ISR(INT6_vect, EXTERNAL_INT_6)
IMPLEMENT_ISR(INT7_vect, EXTERNAL_INT_7)

#else

IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1)

#if defined(EICRA) && defined(ISC20)
IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_2)
#endif

#endif

A simple ISR is made that calls a function pointer from a variable. Whether you use the external interrupts or not, these ISRs are compiled into your code in all sketches.

What attachInterrupt does is it enables the selected interrupt, and also puts a pointer to your chosen function in the intFunc array. When the interrupt is triggered, it goes to the ISR function, and the ISR function has a reference that points to your chosen function.

The ISR macro just defines a function:

#ifdef __cplusplus
#  define ISR(vector, ...)            \
    extern "C" void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
    void vector (void)
#else
#  define ISR(vector, ...)            \
    void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
    void vector (void)
#endif

If that function's name matches one of the ISR functions, the linker will put it's address in the interrupt vector table.
Fortunately, if you misspell the vector name, the compiler will generate a warning if it detects a suspicious looking name (i.e. one that after macro replacement does not start with "_vector").