I'm driving an SSD1306 display over I²C using the Adafruit_SSD1306 library. Most of time in my loop is spent transmitting the frame buffer to the display. (Source code that transmits I²C)
I would like to add eight rotary encoders using an MCP23017.
I believe using external interrupts is the only option here, because my loop is way too slow to poll.
The MCP23017 has interrupt outputs to indicate that the input registers have changed, so far so good.
However, the problem is that I cannot use I²C in the ISR. This is because the Wire library relies on interrupts to work correctly:
-
TwoWire::beginTransmission
just resets the buffer indices (source) -
TwoWire::write
writes data to the buffer and increments the index (source) -
TwoWire::endTransmission
callstwi_writeTo
(source) -
twi_writeTo
waits for any previous I²C transmissions to finish, configures the TWI FSM, configures the TWI interrupts, and finally waits until the transmission is complete (source) -
ISR(TWI_vect)
is where the actual transmission of the data happens, asynchronously (source)
Would it be possible to do the following:
- OLED library is transmitting I²C
- MCP23017 interrupt fires → inside of ISR
- Enable interrupts
- Wait for OLED I²C transmission to finish
- Start I²C transmission to read MCP23017 input registers
- Wait for MCP23017 I²C transmission to finish
- Disable interrupts
- Calculate encoder deltas and update positions
- Exit ISR
I've done some research online, and all of the posts seem to end with: "Don't do the I²C transmission in the ISR, just set a flag and handle it in the main loop", which is of course a valid point, but that's just not possible in this case, because the OLED I²C transmissions take up so much time in the loop.
Do you think trying to get this working is worth it (is it even possible at all?), or am I just wasting my time?
In case of the latter, what solution would you propose to be able to read many rotary encoders?
Thanks,
Pieter