Page 1 of 1

MCP23017 Interrupts Not Working

Posted: Sat Sep 16, 2023 9:48 pm
by kepadz
Hello,

I'm working on a pretty intense project that will send output to multiple LCD displays, a matrix of LEDs, and audio. It will also need to receive inputs from at least 4 different gamepad controllers. Part of the solution can be to use the mcp23017 to expand the GPIO ports on an esp32. The biggest reason the MCP23017 looks appealing is because it has interrupt pins that can signal to the esp32 when a pin changes state. I checked the examples from the MCP23017 arduino library that Adafruit provides, but the interrupt example does not use ISR functions. Here is the example code provided by Adafruit:

Code: Select all

// NOTE: This is a simple example that only reads the INTA or INTB pin
//       state. No actual interrupts are used on the host microcontroller.
//       MCP23XXX supports the following interrupt modes:
//           * CHANGE - interrupt occurs if pin changes to opposite state
//           * LOW - interrupt occurs while pin state is LOW
//           * HIGH - interrupt occurs while pin state is HIGH

// ok to include only the one needed
// both included here to make things simple for example
#include <Adafruit_MCP23X08.h>
#include <Adafruit_MCP23X17.h>

#define BUTTON_PIN 1   // MCP23XXX pin used for interrupt

#define INT_PIN 7      // microcontroller pin attached to INTA/B

// only used for SPI
#define CS_PIN 6

// uncomment appropriate line
Adafruit_MCP23X08 mcp;
//Adafruit_MCP23X17 mcp;

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

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

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

  // 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 LOW
  mcp.setupInterrupts(true, false, LOW);

  // configure button pin for input with pull up
  mcp.pinMode(BUTTON_PIN, INPUT_PULLUP);

  // enable interrupt on button_pin
  mcp.setupInterruptPin(BUTTON_PIN, LOW);

  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(250);  // debounce
    // NOTE: If using DEFVAL, INT clears only if interrupt
    // condition does not exist.
    // See Fig 1-7 in datasheet.
    mcp.clearInterrupts();  // clear
  }
}
This code is fine, but I want to optimize it. I would like to trigger an interrupt and run a quick ISR function whenever a button changes state because I don't want to use the main loop to constantly poll the mcp23017 for input changes. But I cannot get my code to work. Here is my code:

Code: Select all

// ok to include only the one needed
// both included here to make things simple for example
// #include <Adafruit_MCP23X08.h>
#include <Adafruit_MCP23X17.h>

#define LEFT_BUTTON_PIN 0  // MCP23XXX pin button is attached to
#define UP_BUTTON_PIN 1  // MCP23XXX pin button is attached to
#define DOWN_BUTTON_PIN 2  // MCP23XXX pin button is attached to
#define RIGHT_BUTTON_PIN 3  // MCP23XXX pin button is attached to
#define A_BUTTON_PIN 4  // MCP23XXX pin button is attached to
#define B_BUTTON_PIN 5  // MCP23XXX pin button is attached to
#define INT_PIN 12      // microcontroller pin attached to INTA/B


// only used for SPI
#define CS_PIN 6

// uncomment appropriate line
// Adafruit_MCP23X08 mcp;
Adafruit_MCP23X17 mcp;

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

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

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

  // configure button pins for input with pull up
  mcp.pinMode(LEFT_BUTTON_PIN, INPUT_PULLUP);
  mcp.pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
  mcp.pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP);
  mcp.pinMode(RIGHT_BUTTON_PIN, INPUT_PULLUP);
  mcp.pinMode(A_BUTTON_PIN, INPUT_PULLUP);
  mcp.pinMode(B_BUTTON_PIN, INPUT_PULLUP);

  // enable interrupt on button_pins
  mcp.setupInterruptPin(LEFT_BUTTON_PIN, CHANGE);
  mcp.setupInterruptPin(UP_BUTTON_PIN, CHANGE);
  mcp.setupInterruptPin(DOWN_BUTTON_PIN, CHANGE);
  mcp.setupInterruptPin(RIGHT_BUTTON_PIN, CHANGE);
  mcp.setupInterruptPin(A_BUTTON_PIN, CHANGE);
  mcp.setupInterruptPin(B_BUTTON_PIN, CHANGE);

  attachInterrupt(digitalPinToInterrupt(INT_PIN), mcpInterrupt, FALLING);

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

void loop() {
}

void mcpInterrupt() 
{
  // static unsigned long last_interrupt_time = 0;
  // unsigned long interrupt_time = millis();
  // // If interrupts come faster than 200ms, assume it's a bounce and ignore
  // if (interrupt_time - last_interrupt_time > 200) 
  // {
    Serial.print("Interrupt detected on pin: ");
    Serial.println(mcp.getLastInterruptPin());
    Serial.print("Pin states at time of interrupt: 0b");
    Serial.println(mcp.getCapturedInterrupt(), 2);
    mcp.clearInterrupts();  // clear
  // }
  // last_interrupt_time = interrupt_time;
  
}
When checking the Serial Output, it only shows the following message:

�E�����lb@P�AA�h�MCP23xxx Combo Test!
Looping...

I don't think the corrupted portion of the message matters (perhaps it does, I don't know). But no matter which buttons I press, it does not show the message in the ISR function. I believe I have a deep misunderstanding on how the mcp23017 interrupts work. I would love to know what I am doing wrong. Thanks!

Re: MCP23017 Interrupts Not Working

Posted: Sun Sep 17, 2023 2:51 pm
by dastels
A big problem is the use of Serial in the ISR.

Dave

Re: MCP23017 Interrupts Not Working

Posted: Mon Sep 18, 2023 5:01 pm
by kepadz
Yeah, I think a big problem to my planned approach was that I was hoping to do more in the ISR than is practical. I was planning on communicating with the MCP23017 via I2C inside of the ISR, which is just the wrong place to do it. An ISR should be as quick as possible, normally it will only have one or two lines of code. Adafruit built their library with this method in mind and I will correct my code to follow this philosophy