Adafruit MCP23017 Interrupts Sketch

Morning all,
I've had this long going, evolving project - for which I have recieved some help on this forum which has been educational and greatly recieved.....however it bought up the concept of interrupts.... you know it's an itch I got to scratch, but it's not sinking in.

Below is a pic of the overall project which I have posted before - on this part of the process everything has been removed except for one MCP23017 DFRobot board and two toggle switches which are attached to the MCP23017 on pins 0, 1, bank A and pins 8 and 9 bank B.

I was using the DFRobot sketch as a learning tool but came across an issue which their support has sent off to their software team; in the interim, following advice form another member, I looked at the Adafruit library.

I used their sketch, mcp23xxx_interrupt.ino,

However it do not produce, initally, any output in the serial monitor. Reading on the forums other people who had similar issues, from what I could glean it was preferable to change the mcp.setupInterrupts signal from LOW to CHANGE and the mcp.setupInterruptPin the same from LOW to CHANGE. Even then had no serail output.

I found if I removed the '!' from the (!digitalRead(INT_PIN) statement I started to get an output. In my very limited knowledge, this seems to be showing as I toggle the switches the binary readout for the assoicate pin changes form 1 to 0, 0 to 1?

Two things are going on here that I am tring to understand:

Firstly, why is it reporting pin 255? The only reference I found to this was when another persson had the wrong library remarked out - their issue was solved quickly.

Secondly, even if I do not flip a toggle switch, in the serial monitor it keeps reporting an interrupt event . I had to put in the test code a dely of 2000 - just to see what was going on.
When repeating the there are no reflected changes in the binary output so - really stuck on this. As always, and help in pigeon english greatfully recieved.

Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111111011111111
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111111011111111
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111110111111111
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111110111111111
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111111011111111
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111110111111111
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111110111111111
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111111011111111
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111111011111101
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111111011111101
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111111011111110
Interrupt detected on pin: 255
Pin states at time of interrupt: 0b1111111011111101

At this point if I do nothing, the last state of the pins is reported over and over, in this instance it would be this last line.

#include <Adafruit_MCP23X17.h>

#define BUTTON_PIN0 0
#define BUTTON_PIN1 1  // MCP23XXX pin used for interrupt
#define BUTTON_PIN8 8
#define BUTTON_PIN9 9
#define INT_PIN 2  // microcontroller pin attached to INTA/B


Adafruit_MCP23X17 mcp;

void setup() {
  Serial.begin(9600);
  //while (!Serial);
  Serial.println("MCP23xxx Interrupt Test!");

  // uncomment appropriate mcp.begin
  if (!mcp.begin_I2C(0x27)) {
    //if (!mcp.begin_SPI(CS_PIN)) {
    Serial.println("Error.");
    while (1)
      ;
  }

  // configure MCU pin that will read INTA/B state
  pinMode(INT_PIN, INPUT_PULLUP);

  // OPTIONAL - call this to override defaults
  // mirror INTA/B so only one wire required
  // active drive so INTA/B will not be floating
  // INTA/B will be signaled with a CHANGE
  mcp.setupInterrupts(true, false, CHANGE);

  // configure button pin for input with pull up
  mcp.pinMode(BUTTON_PIN0, INPUT_PULLUP);
  mcp.pinMode(BUTTON_PIN1, INPUT_PULLUP);
  mcp.pinMode(BUTTON_PIN8, INPUT_PULLUP);
  mcp.pinMode(BUTTON_PIN9, INPUT_PULLUP);

  // enable interrupt on button_pin
  mcp.setupInterruptPin(BUTTON_PIN0, CHANGE);
  mcp.setupInterruptPin(BUTTON_PIN1, CHANGE);
  mcp.setupInterruptPin(BUTTON_PIN8, CHANGE);
  mcp.setupInterruptPin(BUTTON_PIN9, CHANGE);

  Serial.println("Looping...");
}

void loop() {
  if (digitalRead(INT_PIN)) {
    Serial.print("Interrupt detected on pin: ");
    Serial.println(mcp.getLastInterruptPin());
    Serial.print("Pin states at time of interrupt: 0b");
    Serial.println(mcp.getCapturedInterrupt(), 2);
    delay(2000);  // debounce
    // NOTE: If using DEFVAL, INT clears only if interrupt
    // condition does not exist.
    // See Fig 1-7 in datasheet.
    mcp.clearInterrupts();  // clear
  }
}

255 is returned from the function call mcp.getLastInterruptPin()

In the library, it looks like "if no pin found, return ERR"

This link should take you to the line from Adafruit-MCP23017-Arduino-Library/src/Adafruit_MCP23XXX.cpp at master · adafruit/Adafruit-MCP23017-Arduino-Library · GitHub

/**************************************************************************/
/*!
  @brief Gets the pin that caused the latest interrupt, from INTF, without
  clearing any interrupt flags.
  @returns Pin that caused lastest interrupt.
*/
/**************************************************************************/
uint8_t Adafruit_MCP23XXX::getLastInterruptPin() {
  uint8_t intf;

  // Port A
  Adafruit_BusIO_Register INTFA(i2c_dev, spi_dev, MCP23XXX_SPIREG,
                                getRegister(MCP23XXX_INTF, 0));
  INTFA.read(&intf);
  for (uint8_t pin = 0; pin < 8; pin++) {
    if (intf & (1 << pin)) {
      return pin;
    }
  }

  // Port B ?
  if (pinCount > 8) {
    Adafruit_BusIO_Register INTFB(i2c_dev, spi_dev, MCP23XXX_SPIREG,
                                  getRegister(MCP23XXX_INTF, 1));
    INTFB.read(&intf);
    for (uint8_t pin = 0; pin < 8; pin++) {
      if (intf & (1 << pin)) {
        return pin + 8;
      }
    }
  }

  return MCP23XXX_INT_ERR;
}

And the error is from Adafruit-MCP23017-Arduino-Library/src/Adafruit_MCP23XXX.h at master · adafruit/Adafruit-MCP23017-Arduino-Library · GitHub

#define MCP23XXX_INT_ERR 255 //!< Interrupt error

getLastInterruptPin() further calls this generic function... Adafruit-MCP23017-Arduino-Library/src/Adafruit_MCP23XXX.cpp at master · adafruit/Adafruit-MCP23017-Arduino-Library · GitHub

uint16_t Adafruit_MCP23XXX::getRegister(uint8_t baseAddress, uint8_t port) {
  // MCP23x08
  uint16_t reg = baseAddress;
  // MCP23x17 BANK=0
  if (pinCount > 8) {
    reg <<= 1;
    reg |= port;
  }
  // for SPI, add opcode as high byte
  return (spi_dev) ? (0x4000 | (hw_addr << 9) | reg) : reg;
}

It looks like it tries to find the interrupt above 8 bits/pins, then uses that in the return to the calling function.

Could the I2C address of the board cause the interrupt pin return error? (I2C address is used in the last called function).

255(0xff) is actually -1 for a signed 8 bit integer. Since asking for a pin number would likely return a number in the range 0-15, it's logical that some other number would be expected for "error", or "there wasn't one", and -1 is a very traditional "oops" indicator.

1 Like

It is exactly what it sounds like. Imagine you're watching a video, and suddenly there's a loud bang. You immediately pause the video to investigate the noise. Once you've figured out what caused it, you return and resume watching. The bang interrupted you, prompting you to stop what you were doing to handle the issue. That’s essentially how an interrupt works.

Similarly, when a processor receives an interrupt, it pauses its current task to handle the new, more urgent task. While it's servicing the interrupt just like you checking the noise it can't do anything else. Once the interrupt is resolved, the processor returns to what it was doing before.

Thank you all - finally got it working - by definition everything is connected and I'm not mising pulses from the rotaties - result!

I dare say the sketch isn't pretty, but, it's certainly functional in doing what I ask of it!

Many thanks for explaining the pin 255 - everyday is a learning oppertunity!

1 Like