Taking the circuit a depicted below, with the INTA pin of the MCP23S17 wired to one of the Pi’s PGIO’s we can register an event to the RPiGPIO library and get it to run a code block when it has been fired…
Before we jump into some coding lets consider the task at hand –
Upon interrupt we want to read the relevant GPIO expanders pins (end game is I will have 7 expanders encompassing 112 pins or 56 rotary switches ), then from that work out which switch changed, whether it was turned clockwise or not and from the latter we can increment or decrement a counter accordingly.
Now because this is an interrupt routine, we want to do the minimum amount of processing because whilst we are servicing the interrupt if another device (or indeed the same one we are reading) gets an interrupt then we will miss it which will result in that device locking up – the MC23S17 interrupt pins are only cleared by a reset or a read operation, so it one gets set and we miss it, it will remain latched up awaiting a read that will never come!
There are ways we can mitigate this, however given the vanishingly small probability of multiple light switches being operated simultaneously in a domestic house – I bet even in a 100 room hotel it’s still vanishingly small, the most likely reason for missing an interrupt would be if someone rotated the switch rapidly and whilst servicing the 1st one a 2nd arrived and we missed it. Some might argue that running this sort of code on a low power device like a Pi and using a non real-time operating system such as Linux Raspbian is asking for trouble, however in practice this appears to work quite well!
So in a Python shell lets import the drivers we need, instantiate our device and configure it to read the input pins, generate interrupts for any changes and finally to mirror the interrupt pins (the MCP23S17 logically comprise 2 x 8 bank GPIO’s so if we want to only use 1 Pi interrupt to handle ANY change in either bank we need to logically ‘OR’ them).
Finally we need to configure the Pi GPIO library to trigger a call to a ‘read the GPIO pins’ routine.
Now if we rotate the switch we should see the ISR trigger and print the GPIO register results – here I am rotating the encoder shaft clockwise one detent at a time and you see 4 lines of output per detent. After 2 detents I rotate it similarly counterclockwise 2 detents.
As you can see the 1st counter clockwise detent only produced 3 lines of output and the second detent I got 2!
This is why we need to add some logic to debounce the outputs.
One could for example easily double the number of GPIO chips supported by putting a binary demultiplexor on the 2 Pi CS pins and expand its capacity by allowing 4 banks of 8 MCP23S17 chips to be supported – that’s a staggering 512 inputs!
However one big advantage of using an ISR to handle the input capture is this – when no switch is being turned the application uses minimal CPU/Resources – the underlying code in the GPIO library that is handling the ISR is written in C, and the actual interrupt pins are provided by the BMC2835, so consume little resources. This is what makes this approach almost limitlessly expandable – here is the output of htop on a Pi-3 serving 7 MCP23S17’s, 4 instances of the RotaryEncoder and 3 instance of the RotarySwitch –