Teensy 4.1 and MCP23017 - Interrupt Problem


I'm trying to track a ball through an XY matrix made up of laser emitters and receivers. Currently there is 1 MCP for the X, and 1 for the Y. I have the A/B interrupts on each one mirrored and using Teensy pins 2/3 for interrupts - waiting for the lines to go low. Communication is via I2C.
The two ISRs do nothing but set a flag and disable its associated interrupt.
The loop currently just waits for both ISRs to set their flags (a ball has registered in both X and Y planes). I reset those flags (for the next ball), read and serial print the A/B GPIO, INTCAP and INTF registers for each MCP. I then re-attach both interrupts and hope to read the next ball to pass through.
The problem I'm having is that once the MCP interrupt pins go low, I can't figure out how to reset them for the next ball. I am monitoring the 2 MCP interrupt pins with a scope and I see them drop from high to low as I drop a ball thru the matrix. The only way I can get them to go high again is to unplug/plug the Teensy.
The data sheet says to clear the interrupts you need to read either the GPIO or INTCAP registers. I read both but the interrupt pins never reset. Any ideas on what I'm doing wrong??



  • Serial_output.txt
    460 bytes · Views: 23
  • TestOnlyInterrupt_11_13_2023.txt
    10.2 KB · Views: 23
Last edited:
Before changing, or changing to, a library for the MCP, I thought I'd go back to my previous setup using an Arduino Uno. I thought things were working correctly then but I can't remember 10 minutes ago, much less several days :) So, I moved the wiring from the Teensy to the Uno - pin for pin - and it works correctly on the Uno. As soon as I read both INTCAP registers on an MCP, the interrupt pin goes back high, as expected. Between the processor and the MCPs I'm only using SCL, SDA, pins 2 and 3 for the interrupts, Gnd and +5V (Uno) +3.3V (Teensy). Plus I change the voltage source to my receivers from 5V to 3.3V, depending on which processor I'm using. I do have resistors on the SDA and SCL lines. The question of speed just popped into my mind. I just found a note that says the MCP23017 maximum I2C speed is limited to 400kHz when running off 3.3V (1.7mHz at 5V). My Arduino IDE Tools, CPU Speed is set to 600mHz for the Teensy 4.1. I don't know how that relates to the I2C speed for the Teensy but I'll have to slow that down tomorrow and see if it makes any difference. Doesn't explain why I can read the registers at the higher speed but not reset the INT pins..... But at this point anything is worth a try!!
Try using LOW instead of FALLING? The Teensy might behave differently if the pins are already low when you attach the interrupt handler (no falling edge will occur if the output is already low).
Try using LOW instead of FALLING? The Teensy might behave differently if the pins are already low when you attach the interrupt handler (no falling edge will occur if the output is already low).
I tried LOW but it made no difference....
Since I was set up with the Uno from last night, I played with the I2C speed some. If I set it to 100000, everything worked as expected. I could drop a ball thru the matrix, interrupts go low, read and display the interrupt related registers, interrupts go back high for the next attempt. Works time after time. Changed speed to 2 and 300000 with no change. When I got to 400000 it started acting like it did with the Teensy.

Connected the Teensy back up and tried various speeds but still no joy. First ball drops and the interrupts go low, read and display the registers and that's it. I put delays in my loop, tried all sorts of things but it never got better than this.

I finally commented out the two flags that get set by the ISR - and that the whole loop is based on. Once they went true after the first ball (and never got changed to false) the loop would go on forever. After 2 iterations through the loop, just reading and printing the registers and reattaching the interrupts, the interrupt pins reset as expected. But why did it have to go thru twice?? I modified the loop to repeat every step twice - basically copied everything in the loop and pasted to the bottom, inside the loop, re-eneabled my two flags and it still would not work. Watching the scope and the printout I could tell that once the original loop was traversed 2 times, the interrupt pins would reset. However, it just kept going thru the loop and printing the registers. I added a counter that would allow the loop to run 2 times before resetting the flags used in my ISRs (to denote to the program that interrupts had occurred).

Now, it will wait on an initial ball drop. Interrupt pins go low and the program prints that each interrupt has fired (the only other thing the ISRs do is print: "X (or Y) interrupt"). The loop will process twice, each time printing the status of the registers and then the interrupt pins reset for the next ball drop. This will work perfectly all day. It's also fine that it goes through the loop twice. In the end these results will never be printed, just used to identify the location of a ball on a LED screen.

I just don't understand why, with the Teensy, it has to go through the loop twice in order to get things to work properly. I suspect it's a timeing issue but I have no idea what it is. Or just some way that the Teensy operates differently than the Uno.

Anyway, I can move forward with my testing since this "works"! :)


  • Works_Wih_Arduini_11_14.ino
    11.5 KB · Views: 15
  • Serial_monitor_output.txt
    3.9 KB · Views: 11
Probably it takes a certain amount of time for the interrupt outputs from the MCP23017 to update after the registers are read, for example the datasheet lists "Interrupt Pin Disable Time (tINTD) - 600ns". I'm not sure if this is actually the relevant value we're looking for but the Teensy is definitely fast enough to re-execute the loop within that amount of time whereas a slower microprocessor may not, and slowing the I2C speed would also have an influence on that with all the register reads happening in there.

Another possibility: did the MCP23017s come from a reliable source?
Pretty sure the MCP23017s were the cheapest ones I could find on Amazon! If it gets to the point where I think this project might succeed, I will probably use two Macetech Centipedes in the final design, one for the X and one for the Y planes.

Thinking the if() statement might be running too fast to allow the INT pins to reset after the GPIO and INTCAP registers were read, I put multiple delays in multiple places in the body of the if() statement. I also made multiple attempts while running at different I2C clock rates. Nothing helped.

I may rewrite what I've done so far as a function that just returns the XY coordinates where the ball passed thru the matrix. That's the way I envision it in the final product. I'm curious as to how that might affect how it works - regarding having to let the if() statement run twice before getting the expected result and making that happen in a standalone function instead of being part of the main loop(). Will I just have to call it twice?? Or will I have to reinvent the "working" if() statement using the dreaded goto to get it to cycle twice?? I don't know yet and I'll keep trying to figure out why it currently doesn't work as I expected (and the way it does work with the Uno). I'm just happy - for the moment :) ) that I can throw a ball thru as hard as I can and get the position where it passed thru the matrix! I was beginning to wonder.... :)
Than you for your input!!!!
If it works at 100kHz but not 400kHz, perhaps you need stronger (lower resistance) pullups on the SDA and SCL lines? (just a blind guess... didn't see details on the hardware used)