Interrupts from the MCP23017

A project log for 3-Chip Z80 Design

Combining a Z80 retro design with a modern PSoC CPU. 11/03/2019 at 23:400 Comments

The MCP23017 has two interrupt modes. One is to interrupt on level. The pin is compared to a default value and causes an interrupt when different. The interrupt will persist as long as the signal is different than the default value that was set. Reading the GPIO or INTCAP clears the interrupt . But, in this mode another interrupt will happen immediately if the pin is still different That is the mode I was running and and explains why interrupts hang around until the button (front panel or Expansion MCP23017) is released.

The other mode causes an interrupt on change of the pin. That's the mode I switched to and it works. Interrupts are generated when there's a change. The only downside is there is an interrupt when the button is pressed and another interrupt is generated when the key is released. There's no edge selection for the interrupt but that's easy enough to do in the PSoC software. It could possibly be useful to know when a button is released

The interrupt takes a bit over 500 uS total time. That's due to the 400 KHz I2C bus speed and the number of ports (6x2) that need to be read. No biggie really.

At this point it would be helpful to read the values from the ports and create three values from them. One would be a long value which is the state of the Front  Panelswitches. The other would be two bytes which indicate the value of the Expansion MCP23017 ports. The same caveats about debouncing apply here that applied when getting the Front Panel to work.

Here's the Polled mode reads of the JOYPAD from basic. The Z80 is reading the PIO Port A (that's where the JOYPAD is connected) and printing the value if it changes. This mode does not require interrupts since it's actually doing a read of the PIO.

Interrupts are funny things. When they happen at the wrong time bad things happen. The program above worked pretty good - some of the time.  Then they would kill the program on the Z80. Difficult to debug in this setup since I don't have visibility into the Z80 code without hooking up the logic analyzer.

Allowing Interrupts in a Time Slot

There's a fix for this issue by limiting time interrupts to only happen in a window. And the main( ) program loop is a convenient place to do that. The code looks something like this.

            // Give the I2CInt a time slot

This opens a short window for the interrupts and it cleared up the Z80 hangs. The BASIC program does a constant read of the PIO port so it's likely the interrupt was messing up that read by interrupting the I2C transfer for the polled read. Doing the interrupt in a time that the Z80 port is guaranteed to not being handled is a fairly clean solution.

The 2uS delay is not necessary. Just enabling the interrupt would let it sneak through. I tested it and it worked fine without it.


The PIO should not be directly read. No need to do that. The ISR can load the value of the port when the interrupt happens. After all, it is only reading the port when it needs to be read - when a button is pressed.

In the next log we'll take a look at Interrupts from the MCP23017 seen from the perspective of the PSoC and attempt to solve a problem that I am seeing.