Pin Change Interrupts -- how to I set them to interrupt only on LOW?

How do I set pin change interrupts to only interrupt on LOW? I'm not using attachInterrupt() but setting the bits via PCIFR/PCICR, since I couldn't find any examples of using pin change with attachInterrupt().

I'm using a Pro Mini 3.3V clone and have attached a power button to D5, and a reed switch to D9. I want to sleep the Arduino until either of these is pressed and when they are pressed, they will turn on a MOSFET that will turn on a Raspberry Pi. I didn't use the standard external interrupts since I want to add two additional hardware interrupts later. Using pin change instead.

At the moment it's configured to interrupt on every change, both LOW and HIGH. When I depress the button it interrupts, and when I release it, the interrupt runs again. Same with the reed switch.

How do I get it to fire on only LOW? I think it's via another bit() into PCICR but I'm lost how to do that. The datasheet and some examples seem to talk about using EICRA and ISC10 and 11 but I'm lost how to proceed.

#include <avr/interrupt.h>
#include <avr/sleep.h>

volatile byte pwrButtonPressed = 0;
volatile byte upReedState = 0;
volatile byte dnReedState = 0;
byte piPwr = 4;
byte pwrButton = 5;
byte upReed = 9;
char dnReed = A0;

void detachAll () {
  detachInterrupt (digitalPinToInterrupt (pwrButton));
  detachInterrupt (digitalPinToInterrupt (upReed));
  detachInterrupt (digitalPinToInterrupt (dnReed));
}

// Wake on power button
ISR (PCINT2_vect) {
  sleep_disable();
  detachAll();
  pwrButtonPressed = 1;
}  // End of wake()

// Wake on up reed
ISR (PCINT0_vect) {
  sleep_disable ();
  detachAll();
  upReedState = 1;
}  // End of wake()

// Wake on down reed
ISR (PCINT1_vect) {
  sleep_disable ();
  detachAll();
  dnReedState = 1;
}  // End of wake()

void setup() {
  // Up reed
  PCMSK0 |= bit (PCINT1);   // Pin D9
  PCIFR  |= bit (PCIF0);    // Clear any outstanding interrupts
  PCICR  |= bit (PCIE0);    // Enable pin change interrupts for D8 to D13

  // Down reed
  PCMSK1 |= bit (PCINT8);   // Pin A0
  PCIFR  |= bit (PCIF1);    // Clear any outstanding interrupts
  PCICR  |= bit (PCIE1);    // Enable pin change interrupts for A0 to A5

  // Power button
  PCMSK2 |= bit (PCINT21);  // Pin D5
  PCIFR  |= bit (PCIF2);    // Clear any outstanding interrupts
  PCICR  |= bit (PCIE2);    // Enable pin change interrupts for D0 to D7
  
  pinMode(piPwr, OUTPUT);
  pinMode(pwrButton, INPUT_PULLUP);
  pinMode(upReed, INPUT_PULLUP);
  pinMode(dnReed, INPUT_PULLUP);
  // FIXME Uncomment digitalWrite(piPwr, HIGH); // Boot up together
  Serial.begin(9600);
  Serial.println("Booted up.");
} // End of setup()

void loop() {
  if (pwrButtonPressed == 1) {
    Serial.println("Power button pressed. Powering up the Pi.");
    Serial.flush();
    digitalWrite(piPwr, HIGH);
    pwrButtonPressed = 0;
  }

  if (upReedState == 1) {
    Serial.println("Door is in the up position. Powering up the Pi.");
    Serial.flush();
    digitalWrite(piPwr, HIGH);
    upReedState = 0;
  }

  if (dnReedState == 1) {
    Serial.println("Door is in the down position. Powering up the Pi.");
    Serial.flush();
    digitalWrite(piPwr, HIGH);
    dnReedState = 0;
  }

  // FIXME Wrap around a serial read so I can send the shutdown and sleep 's' command
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable(); // Enables the sleep bit in the mcucr register
  Serial.println("Zzz...");
  Serial.flush();
  sleep_cpu(); // Here the device is actually put to sleep
  delay(1);
} // End of loop()

From my understanding of the datasheet, you need to do it manually, i.e. check the new state of the pin in the ISR and determine if it was triggered by a rising or falling edge.

Ooh. Which register do I check?

SlowBro904:
How do I get it to fire on only LOW?

You can't. Think about the name of it "pinChange" :slight_smile:

Read the Atmel datasheet for all the details.

If you set up a pinChange interrupt on a pin you can check the status of the pin with digitalRead() in your ISR.

...R

Ah. So in my ISRs, didgitalRead the pin and if not low ignore it?

Ah. So in my ISRs, didgitalRead the pin and if not low ignore it?

Yes, except that it's digitalRead()...

But the fact that you are diddling with PCIFR/PCICR already suggests that digitalRead() may be too slow for you.

Stupid iPhone autocorral. Is there a way to do pin change without dinking with PCIFR/PCICR?

Stupid iPhone autocorral.

If you didn't make spelling mistakes, like that one, it wouldn't be trying to correct them.

Is there a way to do pin change without dinking with PCIFR/PCICR?

If you are comfortable dinking with PCIFR/PCICR, then do it. Look at direct port manipulation for how to determine the state of a pin without using digitalRead(). Just be aware that doing so will make your code non-portable.

That dang iPhone auto contusion again.

I'm not comfortable with the PCICR, I just did it because I couldn't get attachInterrupt() working with pin change nor could I find any examples of it. Is it even possible?

because I couldn't get attachInterrupt() working with pin change

Well, of course not. attachInterrupt() is for external interrupts, and only on the pins that support external interrupts.
Which Arduino are you using? What, exactly, is connected to the pins being monitored? Which of the three inputs is supposed to wake the sleeping Arduino?

SlowBro904:
How do I set pin change interrupts to only interrupt on LOW? I'm not using attachInterrupt() but setting the bits via PCIFR/PCICR, since I couldn't find any examples of using pin change with attachInterrupt().

I admit it's difficult to find how to use the pinchange interrupt using this site; but google is your friend.

Playground - Simple Pin Change Interrupt on all pins

Thank you, everything is working now.