Teensy 4.0 - I2C & SPI on ANY Pins?


With the exception of features like analog and PWM, to me it's long seemed like you can program just about any pin to do anything. Based upon my [flawed?] reasoning, it seems like it should be possible to map the SPI and I2C functionality to any desired pin.

So when I look at the pretty colored chart showing where different interfaces are defined, I've assumed these were defaults, overridable if needed. But I'm guessing I'm wrong on this...

The reason I ask: I'm designing a pinball machine I/O controller based around the Teensy 4.0. Because pinball machines are complex, electrically noisy environments, I see a need for multiple I2C and SPI busses.

Multiple SPI busses would be helpful mainly from a wiring perspective, since these devices must be daisy chained. I'd like to have the IO controller central on the playfield, and then optional expanded LED drivers connected via SPI. For wiring convenience, I'd like to be able to connect one set going up the playfield, and another set going the opposite direction. Having a 2nd SPI port would prevent me from having a daisy chain running loops around the long playfield.

Multiple I2C busses are really just to do noise isolation. In my testing I've observed a noisy servo controller occasionally interfering with On/Off states of an I/O expander on the same bus. Since the perfect functioning of the I/O expander is critical, I'd like to have it on a separate I2C bus to give it the best possible chance of noise-free operation.

Looking at the Teensy 4.0 I/O map, it's got two I2C on the top pins, which is probably enough. But if I wanted a third, it looks like I'd have to access the small pads 24 & 25 on the bottom.

Similarly, there's only one SPI defined on the top pins, and if I wanted a 2nd I'd have to utilize the super tiny, closely spaced pads 34-37 on the bottom.

This is not appealing to me, as I'm trying to create a custom I/O breakout carrier board, for which the Teensy 4.0 simply plugs in, and is easily swappable in case of a failure, or even simply to reprogram it outside of the pinball machine. I don't think pogo pins would be a good solution, especially as much as a pinball machine vibrates. I don't know if there are any spring contact pads that I could utilize, but that seems both complex and expensive. Soldering additional connections to Teensy is not optimum for a commercially viable product.

Ideally, I'd prefer to simply remap these features to the top pins. I've got 8 unused header pins in my design. So is this possible?

Yes, I know the Teensy 4.1 directly resolves these specific criteria. I'm just trying to understand if this is possible with the 4.0, which is perfect for my application in all other regards.

Thanks for any help!
Simple answer is no. Each pin has specific capabilities.
The Card that comes with the Teensy has most of these details:

From my excel document I have more of the details.

You can see there are multiple SPI and Wire objects. Some may have multiple pins that can do the same function. However with the
T4, I am not seeing very many alternative pins for the Wire objects.

Longer answer is that you might be able to do some of this using FlexIO pins. For example, we have a few classes to create some Serial objects similar to Serial1 or the like, You can create these using the FlexIO pins that are shown in yellow... Likewise I have an SPI one, which I have not tried to do much with...
Thank you Kurt, that's an extremely detailed and immensely help reply.

Your excel chart is amazing. By any chance do you have one for the Teensy 4.1 as well?

What really caught my eye is that CS1 & MISO1 are available on pins 0 & 1, that detail is missing from the provided card. Since I only need SPI output, and since I'm currently not using these two pins, this is almost a homerun. All that is missing is SCK1.

I don't know how special the SPI clock is, but it sounds like you think I can use a FlexIO pin for SCK1. Any thoughts on which pin would be best? What are my next steps, i.e. where do I find the classes?

Assuming I go with just two I2C busses, setting up a pin for SCK1 is my last real hurdle.

By the way, this is a USB client device, it will be communicating with a host PC that runs all the pinball software, so I can't sacrifice USB 2.0 connectivity or speed. Thought I should mention this in case there was any conflict.

Your help is greatly appreciated!!!
Yes the excel document is up on my github (kurte) under project name like teens_documents...

A different page in the file.

You cannot mix the pins between the flexIO version and normal I2C code. SCK1 is on one of the bottom pins on T4.0.
The FlexIO_T4 library is installed with your Teensy installs. Unfortunately, I have not documented some of this as well as it should be:

There is a simple SPI example with the library.

I have played around more with the Serial object one. And Paul then later extended the Serial object code, to have it and the normal HardwareSerial share a common base class...
Well, that turned out way easier than I expected! The SPI example was short and sweet.

Thanks for the guidance to not try mixing FlexIO with normal hardware SPI, you correctly inferred that was my first goal, so you saved me a lot of wasted time.

The FlexIO_t4h and FlexIOSPI.h worked their magic. I assigned pins 14-17. It adjusted speed to 30MHz, which seems plenty reasonable.

I cloned my TLC5947 LED Driver setup to make an 2nd version, TLC2, ran it just like normal, and I now have a fully function 2nd SPI bus using the Teensy 4.0 header pins, successfully communicating with 12 (yes, twelve) TLC5947's for 288 LED outputs. Signal integrity and latency seem close enough to identical that I couldn't differentiate between HW SPI and FlexSPI.

I even split the TLC5947's up, half on the real HW SPI and half on the FlexSPI, wrote to both simultaneously, and both strings of LEDs lit up in sync with zero observable latency between the two methods.

Big thanks to you and Paul for putting these libraries together, you guys are amazing!!

I'll need to move it to some different pins, to free up 16 & 17 for my 2nd I2C, but that's child's play.

Just curious, what does SPI in hardware bring to the table that FlexSPI in software lacks? Lower CPU utilization? Higher transfer speeds? More robust communications? Anything for which I should be on the lookout?
Okay, I messed up. Totally my fault. I originally breadboard tested FlexSPI on pins 14-17. Laying out my PCB, I changed these to pins 0, 1, and 11 (with the intention of skipping the MOSI pin altogether, I simply don't need it). I forgot to test, and my PCB's are in the mail, arriving in a couple days. I just realized I made a few mistakes.

Neither 0 and 1 are FlexIO (my dumb luck, I picked the only 2 non-FlexIO pins). And apparently FlexIO freaks out if you don't put MOSI, MISO, and Clock in the same FlexIO Group (is Group the right term?), so all Group 1, or all Group 2, or all Group 3.

I know these PCB's are probably a write-off, but I'm hanging onto a little hope that perhaps I can fix this in code.

I used pin 1 for Latch, and that works just fine. I used pin 11 (FlexIO 2:2) for Clock, and that should work as long as I keep MOSI and MISO in the same FlexIO Group #2.

I'm wondering if I can use the Crossbar to redirect pin 0 to another FlexIO Group 2 pin. I'm already using all 24 header pins on my breakout board, so I think this means the only remaining candidate is pad 32, which is FlexIO 2:12. Pad 32 is also listed as Xbar 10, and pin 0 is Xbar 17. Does this mean I can use the crossbar to have pad 32 remapped to pin 0 for my FlexIO MISO pin?

If that works, then I still have an issue with MOSI, which I was trying to omit. I tried passing -1 to FlexIO, and it failed. And it seems I can't use FlexIO Groups 1 or 3, I'll have to stay in Group 2 because of pin 11. But there's no more Group 2 pins, all my header pins are in use for other tasks, and pad 32 was the only FlexIO Group 2 pad. Is there a way I can make the MOSI pin optional?

I took a look at FlexIOSPI.h and FlexIO_t4.h, and I didn't see a way to skip the MOSI pin.

I think I read somewhere I can only use the crossbar once, to remap one set of IO, is that right?