Reading a PCF8575 on an Interrupt

Hi there,

I am trying to set up a custom User Interface for a Project. It includes 5 pushbuttons and a rotary encoder which are connected to an arduino mega over I2C Bus. They should only be read when a user input has occured. Therefore an Interrupt routine has been set up.
Currently it only sets one Bit that is constantly polled to trigger the Wire read routine because Wire read cant be used in the ISR Routine.

This works, but is definitly not an ideal way, as it will lead top timing problems soon.

Is there a better way?

Here is some test code i have written. It works, but it does not in my final code. Serial Output will be replaced by other functions.

#define pcf8575adress 0x20

//is set to true when the ISR has been triggered
volatile bool buttonPressed = 0;
//For storing the current turning Direction
bool encplus = true;
bool encminus = true;

void pcf8575ISR() {

  buttonPressed = 1;

}


void setup() {
  Wire.begin();
  Serial.begin(9600);

  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), pcf8575ISR, FALLING);
  Wire.requestFrom(pcf8575adress, 1);


}

void loop() {
  if (buttonPressed) {
    Wire.requestFrom(pcf8575adress, 1);
    byte incomingByte = Wire.read();


    if (incomingByte == 0b00000011) {
       encplus = true;
       encminus = true;
    }
    if ((incomingByte == 0b00000001) && encminus) {
      Serial.println("Enc+");
      encplus = false;
    }
    if ((incomingByte == 0b00000010) && encplus ) {
      Serial.println("Enc-");
      encminus = false;
    }
    if (incomingByte == 0b00000111) Serial.println("Enc Click");
    if (incomingByte == 0b00001011) Serial.println("Button 1");
    if (incomingByte == 0b00010011) Serial.println("Button 2");
    if (incomingByte == 0b00100011) Serial.println("Button 3");
    if (incomingByte == 0b01000011) Serial.println("Button 4");
    if (incomingByte == 0b10000011) Serial.println("Home");
    buttonPressed = 0;
  }

}
1 Like

Is there a better way?

No, not to my knowledge.

This works, but is definitly not an ideal way, as it will lead top timing problems soon.

Explain why! Usually this doesn't pose problems if you structure your code correctly and avoid any delay() calls.

That's how I normally do it. My ISRs are just to set flags. After the flag, the processing would be done in the loop function. That way, I don't spend too long in the ISR. For those that don't know, if you run I2C read in an ISR, it will hang the system which is why you don't want to do an I2C operation from an ISR.

Explain why! Usually this doesn't pose problems if you structure your code correctly and avoid any delay() calls.
[/quote]

The problem is that im using time intense functions. In my code its the U8G2 library that uses about 100 ms for a complete refresh of an 256*64 display. This leads to inputs not being recognized when the display is currently loading.

The problem is that im using time intense functions. In my code its the U8G2 library that uses about 100 ms for a complete refresh of an 256*64 display. This leads to inputs not being recognized when the display is currently loading.

So you have to restructure your code to run more efficiently or check the flags more often. It even may end in not using the U8G2 library if it consumes to much processing power without the possibility to do other stuff.

Calling any I2C function inside an interrupt handler is not possible as the I2C code itself depends on interrupts to happen. The Wire library waits in many situations in while loops until an interrupt set some flags. As interrupts are disabled during interrupt handlers, these while loops will never end and your code freezes.

If you post your code we might find a way to make it more cooperative to user input. The code posted above won't have the described problem.