RISING and FALLING interrupt on same pin?

Can I use a single pin to tigger a RISING interrupt service routine, as well as a separate FALLING interrupt service routine?

1 Like

Use a pin change interrupt, or INT0 and INT1 can both be configured to interrupt on any logical change. Looks like it will be up to the code to determine rising or falling, but that should be straightforward. Read the pin first thing in the ISR, if it's low, then falling, if high then rising. So one ISR rather than two, but the same functionality can be accomplished I think.

Thats badically it, the external interrupts int0 and int1 which are on digital pins 2 and 3 can be set to interrupt on rising, falling or change which is the one you want.

For the other interrupts its a little more difficult, but there is a library that helps. here is a quick overview of the library 'pinchaneint'

Duane B

rcarduino.blogspot.com

ArduinoTom:
Can I use a single pin to tigger a RISING interrupt service routine, as well as a separate FALLING interrupt service routine?

The pin change interrupt isn't going to help any more than doing a CHANGE interrupt would. In any case, there is only one ISR.

Conceivably you could switch from RISING to FALLING inside the ISR, but why bother? By the time you have done that, you may as well have found out (by reading the pin) which one it is.

void setup()
{
  pinMode(pin, OUTPUT);
  attachInterrupt(0, isr, CHANGE);
}

void loop()
{
  digitalWrite(pin, state);
}

void isr()
{
  if (digitalRead(2) == HIGH) doRising();  // you can use direct port read to be faster - http://www.arduino.cc/en/Reference/PortManipulation -
  else doFalling();
}

doRising()
{
}

doFalling()
{
}

I realised a routine for starting an exposure timer, which has a single start button at INT1 and the same button should be used to store permanently the selected time (set by a rotary encoder on the remaining pin INT0) into the EEPROM for further usage, if found worthwhile.

Depending on the duration time the button is pressed, different actions will start accordingly. During these activities the interrupts are temporarily blocked from being interrupted by other interrupts.

Here is what I did to swap interrupt modes within different but corresponding ISRs. I hope it may help a bit.

Have fun...

InlineSkater :wink:

/*------------------------------------------------------------------+
| Question: Can we have an ISR on one pin with different reaction   |
| modes set? Answer: Yes, we can...                                 |
|                                                                   |
| Successfully tested with Arduino Nano in breadboard testbed       |
|                                                                   |
| Author   : InlineSkater                                           |
| Date     : 23.01.2016 20:30:28                                    |
| Revision : 1.0                                                    |
|                                                                   |
| Switch goes low at Pin 3: checked bei ISR, indicated by red LED   |
| ISR mode change realised within two corresponging ISRs to detect  |
| falling & subsequent rising edge of start pulse. Short & long     |
| pulse discriminated and corresponding action started.             |
+------------------------------------------------------------------*/

#define DEBUG
#define REDLED 13
#define BUTTON 3

long unsigned int startTime;
long unsigned int stopTime;
int delta_time;
int delta_store = 600;
int delta_start = 50;

void setup() {
#ifdef DEBUG
  Serial.begin(9600);
  Serial.println(">>> button_test4 >>>");
  while(!Serial) {
    // wait here for established serial connection 
  }
#endif
  pinMode(BUTTON, INPUT);  
  digitalWrite(BUTTON, HIGH);
  pinMode(REDLED, OUTPUT);
  digitalWrite(REDLED, HIGH);
  attachInterrupt(INT1, button_low_isr, FALLING);
}

void loop() {
  delta_time = stopTime - startTime;
    if (delta_time >= delta_store) {      // was it a short/long duration?
#ifdef DEBUG
      Serial.println("ACTION 1");
#endif
      action0_task();                    // it was a long duration pulse
    } else {
      if (delta_time >= delta_start) {
#ifdef DEBUG
        Serial.println("ACTION 0");
#endif
        action1_task();                  // it was a short duration pulse
      }    
    }
}

void action0_task() {        // store display time into EEPROM
  noInterrupts();            // as a semaphore: no ISR allowed
  
  // action code here: store digits with no interruption
  
  interrupts();
}

void action1_task() {        // start display time count down
  noInterrupts();            // as a semaphore: no ISR allowed
  
  // action code here: start timer with no interruption
  
  interrupts();
}

void button_low_isr() {      // 1st action: button goes low and we see it
  digitalWrite(REDLED, LOW);
  startTime = millis();      // time shall be noted & mode changed
  attachInterrupt(INT1, button_high_isr, RISING);
}

void button_high_isr() {    // 2nd action: button goes high and we see it
  digitalWrite(REDLED, HIGH);
  stopTime = millis();      // time shall be noted & mode changed
  attachInterrupt(INT1, button_low_isr, FALLING);
}

InlineSkater:
I realised a routine for starting an exposure timer, which has a single start button at INT1 and the same button should be used to store permanently the selected time (set by a rotary encoder on the remaining pin INT0) into the EEPROM for further usage, if found worthwhile.

Depending on the duration time the button is pressed, different actions will start accordingly. During these activities the interrupts are temporarily blocked from being interrupted by other interrupts.

Here is what I did to swap interrupt modes within different but corresponding ISRs. I hope it may help a bit.

Have fun...

InlineSkater :wink:

/*------------------------------------------------------------------+

| Question: Can we have an ISR on one pin with different reaction   |
| modes set? Answer: Yes, we can...                                 |
|                                                                   |
| Successfully tested with Arduino Nano in breadboard testbed       |
|                                                                   |
| Author   : InlineSkater                                           |
| Date     : 23.01.2016 20:30:28                                    |
| Revision : 1.0                                                    |
|                                                                   |
| Switch goes low at Pin 3: checked bei ISR, indicated by red LED   |
| ISR mode change realised within two corresponging ISRs to detect  |
| falling & subsequent rising edge of start pulse. Short & long     |
| pulse discriminated and corresponding action started.             |
+------------------------------------------------------------------*/

#define DEBUG
#define REDLED 13
#define BUTTON 3

long unsigned int startTime;
long unsigned int stopTime;
int delta_time;
int delta_store = 600;
int delta_start = 50;

void setup() {
#ifdef DEBUG
 Serial.begin(9600);
 Serial.println(">>> button_test4 >>>");
 while(!Serial) {
   // wait here for established serial connection
 }
#endif
 pinMode(BUTTON, INPUT);  
 digitalWrite(BUTTON, HIGH);
 pinMode(REDLED, OUTPUT);
 digitalWrite(REDLED, HIGH);
 attachInterrupt(INT1, button_low_isr, FALLING);
}

void loop() {
 delta_time = stopTime - startTime;
   if (delta_time >= delta_store) {      // was it a short/long duration?
#ifdef DEBUG
     Serial.println("ACTION 1");
#endif
     action0_task();                    // it was a long duration pulse
   } else {
     if (delta_time >= delta_start) {
#ifdef DEBUG
       Serial.println("ACTION 0");
#endif
       action1_task();                  // it was a short duration pulse
     }    
   }
}

void action0_task() {        // store display time into EEPROM
 noInterrupts();            // as a semaphore: no ISR allowed
 
 // action code here: store digits with no interruption
 
 interrupts();
}

void action1_task() {        // start display time count down
 noInterrupts();            // as a semaphore: no ISR allowed
 
 // action code here: start timer with no interruption
 
 interrupts();
}

void button_low_isr() {      // 1st action: button goes low and we see it
 digitalWrite(REDLED, LOW);
 startTime = millis();      // time shall be noted & mode changed
 attachInterrupt(INT1, button_high_isr, RISING);
}

void button_high_isr() {    // 2nd action: button goes high and we see it
 digitalWrite(REDLED, HIGH);
 stopTime = millis();      // time shall be noted & mode changed
 attachInterrupt(INT1, button_low_isr, FALLING);
}

I like this approach, i.e. re-attaching the interrupt pin to the opposite edge, but fear that it may be SLOWER to execute than just reading the state of the pin in a single interrupt routine, and using if...else construct

Is there any way, by some sort of reference documentation, perhaps, to determine which is the fastest method.

I am using interrupts to measure the width of 3 R/C receiver (servo) pulses, which vary from (circa) 1000uS to 2000uS.

I initially tried attaching both a RISING and FALLING interrupt routine for each of the 3 input pins (6 ISR's in total), but gave up when it didn't work, and reverted to 3 interrupts on CHANGE.

To be honest, my code is working exceptionally well, but my OCD kicks in and wonders if the 2-interrupt method would work better (i.e. faster).

Here is one of my 3 ISR's, measuring the width of the Stick Left/Right paddle pulse, I use the boolean state that the pulse is currently HIGH to prevent the subtraction in the main code of "end" and "start" micros, as it would give a strange and useless result....

// Hopper RC Signal Rising and Falling Interrupts
void ISR_Hopper() {
  if(digitalRead(StickL_R)) {
    Hop_micros_start = micros();
    Hop_micros_timing = true;
    }
  else { 
    Hop_micros_end = micros();
    Hop_micros_timing = false;
  }
}

I also have the problem that micros() "rolls-over" after about 72 minutes, and can give me a spurious reading of the pulse widths, but I can filter out any duff readings in the main code, unless anyone knows of a way to avoid or circumvent the roll-over.

Since you mention pulse width, there is yet another option. If you're using an UNO the 328P processor has a hardware counter with Input Capture capability. I have a sketch running the if/else inside the ISR which appears to measure pulse width down to 42 clock cycles.

I don't feel like anyone directly answered the very simple question in this very old post (which still appears first in a google search for: arduino rising falling interrupt same pin)

The answer is no, you cannot have RISING and FALLING interrupts on same pin. if you do

attachInterrupt(INTERRUPT_ID, functionA, RISING);
attachInterrupt(INTERRUPT_ID, functionB, FALLING);

the second call to attachInterrupt will override the first.

See also: Timing between rising and falling interrupt on the same pin - Programming Questions - Arduino Forum

2 Likes

I found this post helpful in 2020. The answer is no, you cannot.

Simple button switch example using CHANGE:

#define BTN_PIN 2
#define LED_PIN 13
#define INT_0 0

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(BTN_PIN, INPUT);

  attachInterrupt(INT_0, isr, CHANGE);
}

void isr() {
  digitalWrite(LED_PIN, !digitalRead(BTN_PIN));
}