Playing with Arduino
A page to record my playing with Arduino
attachInterrupt()

Abstract

The attachInterrupt() sets a function to be called when an external interrupt occurs.

The external interrupt is triggered by one of INT0, INT1, and PCINT0 to PCINT23. On the Arduino Uno, INT0(pin 2) and INT1(pin 3) can be used.

Controling interrupt

The registers EICRA and EIMSK controls the external interrupt.

EICRA

EICRA contrls how the interrupt is detected.

EICRA
Bit76543210
Name----ISC11ISC10ISC01ISC00

The combination of ISC11 and ISC10 controls the condition of INT1(pin 3), and ISC01 and ISC00 controls that of INT0(pin2).

ISC11/ISC01ISC10/ISC00Condition
00Low level
01Any bitwise change
10The falling edge
11The rising edge

EIMSK

The EIMSK(External Interrupt Mask Register) enables and disables external interrupt.

EIMSK
Bit76543210
Name------INT1INT0

When the bit is 1, it enables interrupt. Also I bit of the status register need to be 1.

Source Code

The attachInterrupt() is defined in hardware/arduino/avr/cores/arduino/WInterrupts.c as below. Only the souce code for Arduino UNO is quoted. The original source code supports many tips using #if’s.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static void nothing(void) {
}
 
static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS] = {
   nothing,
   nothing,
};
 
void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {
  if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {
    intFunc[interruptNum] = userFunc;
     
    // Configure the interrupt mode (trigger on low input, any change, rising
    // edge, or falling edge).  The mode constants were chosen to correspond
    // to the configuration bits in the hardware register, so we simply shift
    // the mode into place.
       
    // Enable the interrupt.
       
    switch (interruptNum) {
    case 0:
      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      EIMSK |= (1 << INT0);
       break;
    case 1:
      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      EIMSK |= (1 << INT1);
      break;   
    case 2:
      break;
    }
  }
}
The intFuc[] is an array to store functions when external interrupt occurs. The type of the function is defined as below in hardware/arduino/cores/arduino/wiring_private.h.

1
2
#define EXTERNAL_NUM_INTERRUPTS 2
typedef void (*voidFuncPtr)(void);

The voidFuncPtr is a pointer to function which has no argument and returns void. As EXTERNAL_NUM_INTERRUPTS is defined as 2, the array can hold 2 functions. Initial value is noghint() which dose nothing.

The input is interruptNum, userFunc and mode. The types are uint8_t, void(*)(void) and int. It has no output.

 9
10
void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {
  if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {

When the interrupt is less than EXTERNAL_NUM_INTERRUPTS, rest of the code is executed.

The function specified by an argument is stored to intFunc[].

11
intFunc[interruptNum] = userFunc;

Sets the registers related to interrupt number.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    // Configure the interrupt mode (trigger on low input, any change, rising
    // edge, or falling edge).  The mode constants were chosen to correspond
    // to the configuration bits in the hardware register, so we simply shift
    // the mode into place.
       
    // Enable the interrupt.
       
    switch (interruptNum) {
    case 0:
      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      EIMSK |= (1 << INT0);
       break;
    case 1:
      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      EIMSK |= (1 << INT1);
      break;   
    case 2:
      break;
    }
  }
}

The operation is a little bit difficult( to me), I will dissect them.

22
(1 << ISC00) | (1 << ISC01)

ISC00 and ISC01 are 0 and 1 respectively. 1 « ISC00 is 1(0b00000001) and 1«ISC01 is 2(0b00000010).

Bitwise OR of them is 0b00000011(=3).

22
(EICRA & ~((1 << ISC00) | (1 << ISC01)))

Next calculate the bitwise AND of EICRA and ~((1 « ISC00) | (1 « ISC01))).

As ((1 « ISC00) | (1 « ISC01))) is 0b00000011, the bitwise NOT is 0b11111100.

Then calculating bitwise AND of EICRA and 0b11111100 would clear lower 2 bits of EICRA.

Next calculate bitwise OR of mode « ISC00. As ISC00 is 0, it is same as calculate bitwise OR with mode.

22
(EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00)

The mode is one of the LOW, CHANGE, RISING and FALLING and they are 0, 1, 2, 3 respectively.

The value of mode is not checked, if we specify invalid value, it may do something bad.

INT0_vect and INT1_vect are Interrupt handllers.

1
2
3
4
5
6
7
#define IMPLEMENT_ISR(vect, interrupt) \
  ISR(vect) { \
    intFunc[interrupt](); \
  }
 
IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1)

ISR() is a macro to define interrupt handllers.

Version

Arduino 1.8.13

Last Update

June 19, 2020

inserted by FC2 system