Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
ddowling opened this issue Jan 5, 2024 · 10 comments · Fixed by #49
Closed

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

ddowling opened this issue Jan 5, 2024 · 10 comments · Fixed by #49
Assignees
Labels
documentation Improvements or additions to documentation enhancement New feature or request

Comments

@ddowling
Copy link

ddowling commented Jan 5, 2024

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 RobTillaart self-assigned this Jan 5, 2024
@RobTillaart RobTillaart added documentation Improvements or additions to documentation enhancement New feature or request labels Jan 5, 2024
@RobTillaart
Copy link
Owner

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
Copy link
Owner

A separate example is of course welcome

@RobTillaart
Copy link
Owner

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
Copy link
Owner

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
Copy link
Author

ddowling commented Jan 8, 2024

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
Copy link
Owner

Thanks for testing and sharing.
Insightful

@RobTillaart
Copy link
Owner

Today I will

  • add some notes to the readme.md (rewrite of the above discussion)
  • add a minimal example to show how to "catch missed interrupts" by using an integer counter instead of a boolean flag.

@RobTillaart RobTillaart linked a pull request Jan 8, 2024 that will close this issue
@RobTillaart
Copy link
Owner

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

@RobTillaart
Copy link
Owner

PCF8575 has same behavior.

@RobTillaart
Copy link
Owner

Merged the PR,
Thanks again for the insights!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants