RobTillaart / PCF8574

Arduino library for PCF8574 - I2C IO expander
MIT License
119 stars 34 forks source link

Fast polling of PCF8574 does not need to hook the interrupt #48

Closed ddowling closed 9 months ago

ddowling commented 9 months ago

Thanks for including an example of how to quickly poll the PCF8574 when reading a rotary encoder. This code is really useful if you are building a front panel display board with some rotary encoders and push buttons.

I notice in the PCF8574_rotaryEncoder.ino example that the interrupt handler is just setting a flag variable and the actual I2C polling of the PCF8574 is done from the loop() function. I considered moving the actual I2C reads into the interrupt handler but as I2C is relatively slow this did not seem like the smartest move. My application is also using interrupts to drive some stepper motor controllers and these are highly sensitive to jitter.

The other option I ended up choosing is to just dispense with the PCF8574 interrupt handler altogether and in the loop() function check the GPIO for the INT pin directly. i.e.:

if (digitalRead(2) == 0)

instead of:

if (flag)

Reading from a GPIO port is almost as fast as testing the flag variable so there is not real performance penalty for just checking the value of INT in loop(). The INT signal from the PCF8574 is just an indication that some input has changed and it will by reset to high again when we perform the I2C read from the device.

I though I would add this comment as it might be useful to others wanting to quickly poll a PCF8574 without having to resort to an interrupt handler or continuous I2C reads. I am happy to provide another example program if this is useful.

RobTillaart commented 9 months ago

Thanks for your remarks. They make sense and are valuable especially for faster processors eg Esp32. Have to think if and how to incorporate them.

RobTillaart commented 9 months ago

A separate example is of course welcome

RobTillaart commented 9 months ago

Need to check if the INT stays high until the device is read. If not polling might miss an INT.

On the other hand, if the INT is a pulse and loop does not read before a new INT pulse comes you also miss an event. This latter can be detected with a second flag. If INT happens and the flag is still set a read has been missed..

Need to check datasheet on this.

For a relative slow application it doesn't matter but it might affect some other if loop has a high load. (Faster processor needed).

RobTillaart commented 9 months ago

Read a datasheet again to see what behavior to expect.

An interrupt is generated by any rising or falling edge of the port inputs in the input mode. After time, tiv, INT is valid. Resetting and reactivating the interrupt circuit is achieved when data on the port is changed to the original setting or data is read from, or written to, the port that generated the interrupt. Resetting occurs in the read mode at the acknowledge bit after the rising edge of the SCL signal, or in the write mode at the acknowledge bit after the high-to-low transition of the SCL signal.

So there are three scenarios the interrupt flag is reset.

  1. pins revert to original state (not seen yet)
  2. read from (known)
  3. write to (known)

If (1) does indeed reset the INT, the polling you propose could miss a change and back if this happens between two polls.

Q: As you have a setup (assumption), can you verify this?

It would also mean that a boolean flag in the interrupt routine should be a counter. If this counter is larger than 1 the main loop() had missed processing an interrupt.

0 would mean no INT has passed, or INT has been processed. 1 would mean INT has occurred and is not processed yet 2 would mean an INT occurred before the previous was processed.

ddowling commented 9 months ago

I can confirm that the INT pin will be de-asserted if the input pins return to their original state. This means it is possible to miss an event if the input quickly returns to its original state. However even with the original interrupt based solution we would detect that an event had happened but when we queried the PCF8574 in loop() it would just read the current input state and it would not be possible to tell what bits had quickly changed. In my application this is not so much of an issue as short pulses from encoders or push buttons are contact bounce noise and need to be suppressed anyway.

RobTillaart commented 9 months ago

Thanks for testing and sharing. Insightful

RobTillaart commented 9 months ago

Today I will

RobTillaart commented 9 months ago

PR created with develop branch. Will be merged later, first I have to check the PCF8575 if it behaves similar

RobTillaart commented 9 months ago

PCF8575 has same behavior.

RobTillaart commented 9 months ago

Merged the PR, Thanks again for the insights!