SPI pins teensy 4.1

mah

Well-known member
I'm using a teensy 4.1, and got some questions about SPI:

Is it possible to use both "miso,mosi,sck,cs" (pins: 12,11,13,10) and "miso1,mosi1,sck1,cs1" (pins: 1,26,27,0) at the same time? connected to different peripherals or are they just alternate pins? Maybe it would not do any good as an mpu can only do one instruction at a time anyway, (not really true with teensy 4 and upwards)
Or if possible, what advantages would it have?

Also there are three CS pins and two CS1 pins, could I use all these to select different peripherals if they all use the the same spi miso,mosi,sck pins? Or are they tied to the spi pins with the same number? Like miso,mosi,sck can only use the pins labeled cs and not cs1?

Also,(if the question above is true) is the maximum amount of spi devices 5? as that's the number of cs pins available? I guess digital pins could work but would be slower?
 
Yes, you have 3 SPI busses on the T4.1 which I show in my excel document: Screen shot
T4.1-Cardlike.jpg

And again you access the pins like SPI.transfer will use your fist set of pins and likewise SPI1.transfer uses the other pins...

Now more to your questions.

If you do something like:
Code:
uint8_t ret = SPI.transfer(0);
And most of the other methods like this in the SPI library, they work by putting one thing on the queue and wait for the thing to transfer and the results come back, so it is only doing one thing at a time...

That is the nature of those APIs as they are setup to be synchronous, so their transfer is done before it returns.

However we did add a version of transfer to the SPI library to allow asyncronous transfers, that looks like:
Code:
bool SPIClass::transfer(const void *buf, void *retbuf, size_t count, EventResponderRef event_responder) {

We have the event responder object that can be setup to query to see if done, or setup to have it call your function when done, which can be called immediately, or in yield or ...
So if you do something like:

Code:
SPI.transfer(buff1, buff2, size1, event1);
SPI1.transfer(buff3, buff4, size2, event2);
Then both transfers can be happening at the same time, as well as your own user code...

Or you can do it at the lower levels, which is what many of our display drivers do. Easiest thing to see how that is done is to look at one of our drivers like the st7735_t3 code.

Chip Select pins - Note they work differently on T3.x than T4.x.

Now if your code is doing some normal simple things like use the SPI library to do transfers, you typically can use any digital pin as the CS pin for a device.
If you have 3 devices, you simply need to make sure that the right one is asserted before you do the transfer.

However if you are using a driver that tries to speed things up by using the hardware CS pin(pins), than again a lot different on T3.x than T4.x.
Why?
T3.x the stuff is put in the FIFO queue by using the PUSHR register where the lower 16 bits is the data (maybe only 8) and the upper 16 bits encodes other data including which CTAR to use (which usually controls how many bits of data) as well as a mask of which of hardware CS pin/pins to assert/deassert as part of the transfer and if they should be continue to be asserted after the transfer. Note: in this we can have multiple CS pins asserted. Some of our drivers like ili9341_t3 use this to encode both CS and DC pin, which speeds things up. Why? because you can queue up multiple different configurations of data onto the FIFO without having to wait for previous one to complete and not having to wait for completion before changing the CS pin states speeds things up.

it is different on T4.x. Their output FIFO is handled by two different registers. You put stuff to output on the queue using TDR register, which allows you to transfer 1-32 bits of data per transfer. And there is another register TCR which allows you to change things like Word size and which CS pin to assert. And in this case only one CS pin can be asserted.

There is speed ups with some of these drivers if the DC pin is on the hardware CS pin, but most of them don't require you to use it, as for example T4 and T4.1 the main CS pins is pin 10 and most people have driver boards with pin 10 being the CS pin not DC... So was confusing to have to change those.

And on my chart shown you will see that T4.1 added some additional alternate CS pins. Some of which translate to the same logical signal (only one can be used on the system) and others setup as the CSx-1 or CSX-2... which are other unique CS pins for that SPI buss.

Note: one exception to all of this is if you are trying to do a slave device. In this case, which SPI library does not do, you need to use the hardware CS pin to be signaled by the Master SPI device of the transfers and I believe it must be CSx-0 (first defined CS ins...)

Hope that helps
Kurt
 
wow thx! cleared up quite a lot of things!

Just to be clear, this should theoretically be the most efficient method of connecting SPI devices?
---------------------
SPI0:
MISO0
MOSI0
SCK0
CS0
CS0-1
CS0-2
---------------------
SPI1:
MISO1
MOSI1
SCK1
CS1-0 (why is this in read?)
CS1
---------------------
 
Yes should be, if you have systems that know how to make use of the CS pins... But for the most part the majority of libraries don't they simply do:

digitalWrite(CS, LOW);
Do my SPI stuff
digitalWrite(CS, HIGH);


Whey Red... CS1-0 is the same logically as CS-1
That is if you look at the TCR register 47.4.1.15.3... You see the field PCS which is 2 bits and is what index to use for the CS... Both of these are index 0.
There is another register you need to set in the system on which of these pins to actually route the signal.

Most of the information about this is built into the "hardware" table passed into the constructor for each of the SPI objects.
 
Old thread.. but I have a relevant question. I understand the Teensy 4.1 has 3 SPI with corresponding pins.. but do you have to use those pins or can you use any digital out pins? Im controlling a slow slave device and dont need the fastest implementation.
 
Old thread.. but I have a relevant question. I understand the Teensy 4.1 has 3 SPI with corresponding pins.. but do you have to use those pins or can you use any digital out pins? Im controlling a slow slave device and dont need the fastest implementation.

If you want to use hardware SPI, then you do need to use the hardware SPI pins for MISO, MOSI, and SCLK. The CS pins can typically be any pin. There are some hardware optimizations if you use the special CS pins, but I'm not sure if any of the packages take advantage of it.

Some people have implemented SPI by manually toggling the pins (usually called bit-banging), but that tends to be fairly slow. If your devices are capable of being tri-stated and you have pull-up resistors on the CS pins, you can probably put multiple items on the same SPI bus. See the following article:
 
Which SPI technic is faster?

Hello and congratulations for this forum!
I would like to ask, which method of the following is faster to receive data from two spi modules (ADS1256) on Teensy 4.1
a) Using one SPI bus with two CS pins, or
b) Using two different SPI buses on teensy 4.1
The sampling rate stability is very important for my project
Thank you very match for your time.
 
@Xenos - It depends...

That is having them on the same or different SPI buses, it really depends on how the software is setup.
Most of the SPI transfer code in most libraries is synchronous. That is if your code does something like:

uint16_t new_data = SPI.transfer16(0);
The code will wait until the data is fully transferred and the results are ready before it returns.

So having one on SPI1 and hte other on SPI for example:
val1 = SPI.transfer16(0);
val2 = SPI1.transfer16(0);

Speed wise versus on same one, the time difference is probably noticeable to on same buss with quick digitalWrite or better yet digtialWriteFast() between calls.

However if for example your code is setup to do DMA operations for the reads, either continuous or timer triggered, where transfers can happen at the same time and allow other code to run, then it can be a nice benefit to having them on different SPI buses.

Hope that makes sense.
 
@Xenos - It depends...

That is having them on the same or different SPI buses, it really depends on how the software is setup.
Most of the SPI transfer code in most libraries is synchronous. That is if your code does something like:

uint16_t new_data = SPI.transfer16(0);
The code will wait until the data is fully transferred and the results are ready before it returns.

So having one on SPI1 and hte other on SPI for example:
val1 = SPI.transfer16(0);
val2 = SPI1.transfer16(0);

Speed wise versus on same one, the time difference is probably noticeable to on same buss with quick digitalWrite or better yet digtialWriteFast() between calls.

However if for example your code is setup to do DMA operations for the reads, either continuous or timer triggered, where transfers can happen at the same time and allow other code to run, then it can be a nice benefit to having them on different SPI buses.

Hope that makes sense.

Thank you very much, I will try to apply
 
Hallo,
I tried to use LPSPI3 instead of the LPSPI4 which seems to be the standard SPI for all libraries.
Since I use the audio shield with audio library this will use the standard SPI and probably uses most of the time respective LPSPI4 resources.
So for using the ILI9341 display with ILI9341_t3 and XPT2046_Touchscreen library this will obviously using the standard SPI - means LPSPI4 - as well.
To dont disturb the audio performance with graphical stuff I wanted to switch the display to LPSPI3 by changing the constructor on start of the graphictest example to see how that works.
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, 255, 26, 27, 1); with TFT_CS = 0
I changed the pin conections and wondered that nothing works anymore.
Obviously the pins are still on the standard LPSPI.
Unfortunately I dont have much experience with OOP so I dont know if it is possible to switch the library to a different SPI and what to do for it.
I was astonished to read that most libraries will work in polling mode and dont use an interrupt for data exchange - that is really old fashion.
What about the libraries mentioned above (audio and display) ?
I understood there are methods spi.transfer with EventResponder which obviously will use an interrupt, but I dont know if the libraries will make use of it.
Once this will work according my expectations I want to start working with the ethernet library to test internet capabilities for my project - this will be the next big construction place :D
Thx a lot in advance for some hints regarding the SPI for now :)
 
Last edited:
Not a single person who has a hint if it is possible to use LPSPI3 for the ILI9341 lib ILI9341_t3 ... and how to switch to it from the standard SPI (LPSPI4) ?
I already tried to understand the coding of SPI ... currently not really understood ...
Any help would be appreciated ...
 
I could be wrong, but I don't believe that the changes for using different SPI busses was ever merged into this library.

You can always use my version of the library. https://github.com/kurte/ili9341_t3n
Which has the constructor:
Code:
ILI9341_t3n(uint8_t _CS, uint8_t _DC, uint8_t _RST = 255, uint8_t _MOSI = 11,
              uint8_t _SCLK = 13, uint8_t _MISO = 12);
if MOSI and SCLK or to a different SPI buss, it will choose that one. If Miso is specified (not 0x255 or (uint8_t)-1) it must also be from the same SPI buss.
 
Thank you very much ... yes, I assumed the same when studying the ILI9341 files ...
I will check your files and will try to use them :)
Thx a lot
 
After struggling some time with the touch functionality - the display itself already worked - I read that I simply have to set parameter SPI1 in the ts.begin method.
Now the ILI9341 works as intended on LPSPI3 :)
Thank you very much for your awesome work on the library ... I tried to understand what you have done but I have to confess that I still have to learn a lot of OOP XD
 
Last edited:
Obviously I dont need the second CS for the touch functionality ... it works without connecting that pin.
The touch interrupt pin is also not connected but I can see the pulses with the oszilloscope when touching the surface.
Just MISO, MOSI and SCKL are connected of the 5 additional touch pins of the ILI9341 - strange ...
 
Not sure what you are asking or exactly what the issue is, but will take a guess...

If you have multiple SPI devices on the same SPI buss, each with their own CS pin. In most cases it should work. However note there are some displays or the like, whose MISO pin does not work properly, in that they will drive the value either high or low, which does not work well with others... Assuning this is not the issue. BUT...

Often times there can be a startup issue with the devices, when multiple CS pins might be logically low, and as such multiple devices think that some SPI communication is for them, which can cause the messages to get garbled or mis-interpreted... So you need to get all of the CS pins to logically high. This can be done a few different ways.

Have external Pull up resistors on the CS pins. Some SPI devices have them built-in.

Explicitly do it: For example if you have defines like TFT_CS is 10 and TOUCH_CS is 4 (or such...)

You do something like:
Code:
void setup() {
  ...
  pinMode(TOUCH_CS, OUTPUT);
  digitalWriteFast(TOUCH_CS, HIGH);
  pinMode(TFT_CS, OUTPUT);
  digitalWriteFast(TFT_CS, HIGH);
  tft.begin();
...
  touch.begin();
}

The exact calls and the like will differ depending on library...
Also you probably skip the explicit setting of the CS pin of the first device you initialize as it will likely redo it anyway, but just to be safe...

Again I am only guessing on what your issue might be.
 
Thx a lot for your hint, Kurt :)
Sorry for not expressed in the right way - it was not an issue as all worked as intended :)
I just wondered that I dont have to connect touch CS to get it work - I also assumed that maybe the CS is permanently on GND and to be addressed in that way continuously.
As far as I can remember I measured with scope but I am not sure.
Most important :
I was not aware that the library which uses a port pin does not initialise it ...
So thx a lot for the hint .... I will add that now in any case ...
When I am using the HW SPI pins ... do I have to initialise any of the used pins i.e. MISO, MOSI, SCK as well?
I will check the chapter in the manual ... hopefully I will find something ... 3500 pages ... but I am often missing more explanations there :D
Anyway thx a lot :)
 
Back
Top