Dual SPI Advice

Status
Not open for further replies.

zander

New member
Searched around and couldn't find conclusive information regarding a detail when using two SPI devices.

Read about the better implementation (https://www.pjrc.com/better-spi-bus-design-in-3-steps/) and a question came to mind:

Is there an advantage in using two separate SPI ports in comparison to two devices on the same port (implemented correctly in software)?

Reason for asking the question:
Built a T3.6 remote using a 240x240pixel SPI display and an SPI transceiver.

Will switch to T4 and the second SPI port is on the bottom pads.
I would like to avoid soldering a ribbon cable if dual SPI on the same port has no hardware disadvantage.

Appreciate any info or experience related.

Thanks!
 
I use a Teensy 3.2 and I use dual SDI devices all the time 1) an ILI9341 display and 2) and SD card. Both are on the same SPI lines but different CS of course. Works fine in my data logging application.

I have recently experimented with dual ILI9341 displays on the same line (different DC and CS). I'm using the lib ILI9341_t3 and dual displays work but you are limited as to what CS pins can be used. I hear this lib is optimized and requires only certian pins for CS. I was not able to write different stuff to each display but had some weird update issue (1 display could not be updated 2x more frequent than the other) but I only spend a few hours messing with it.

I've never used completely different SPI busses for multiple devices.
 
Searched around and couldn't find conclusive information regarding a detail when using two SPI devices.

Read about the better implementation (https://www.pjrc.com/better-spi-bus-design-in-3-steps/) and a question came to mind:

Is there an advantage in using two separate SPI ports in comparison to two devices on the same port (implemented correctly in software)?

Reason for asking the question:
Built a T3.6 remote using a 240x240pixel SPI display and an SPI transceiver.

Will switch to T4 and the second SPI port is on the bottom pads.
I would like to avoid soldering a ribbon cable if dual SPI on the same port has no hardware disadvantage.

Appreciate any info or experience related.

Thanks!

It depends on how optimized the drivers are and whether the devices do things like tri-state MISO properly.

Assuming you have done all things in the quoted document (pull-up resistors on the CS pins, verified the device tri-states MISO, and verified that the driver uses SPI.beginTranasaction and SPI.endTransaction), if the drivers are dumb, then it may not matter if if you have one SPI device or two. I suspect it will do one device and then another.

However, if you have the proper driver with the appropriate magic, then it will speed things up, because the driver queues things up to do transfers in the background (using DMA) while it is free to do other things.

Note, while I am familiar with things at a high level, for details you want the experts (KurtE, mjs513, defragster, and of course Paul S.).

The 3.x world seems to be very different from the 4.0 world.

In the 3.x world you could have 5 high speed options, spread over 9 pins. Note, some pins internally used the same ports, so you could use on or the other pin with high speed SPI, but not both. The pins are:

Note, if your device uses a D/C pin, that must also come the list of fast pins.

One of the canonical programs that does use dual SPI is uncannyEyes, which draws eyes on two 128x128 displays as fast as it can. In uncannyEyes, you need 3 fast pins (one for the CS for each display, and the third for the shared D/C pin). In my current configuration, I tend to use pins 23/22 as the two CS pins and 15 as the D/C pin, but I may be changing this to use a more standard CS pins 10/9.

The author of uncannyEyes has been working on newer versions that target other microprocessors (Rasberry Pi, and the Adafruit Monster M4SK which uses an ARM Cortex M4). IIRC, in the Teensy version, he does one display, waits until it is finished and then does the other display. In the newer version that uses 240x240 displays instead of 128x128 displays (i.e. 4 times the data), I believe he does both SPI streams in parallel (using 2 SPI ports). If that is ever ported back to the Teensy 3.5/3.6, it would likely then need separate D/C pins for each display.

While the Teensy 3.5/3.6 have multiple SPI buses, I think the other SPI buses are limited to a single fast pin. This doesn't help if you need both CS and D/C pins to be fast pins.

Teensy 4.0 is a lot more complicated. The SPI internals changed, and also the memory caching behavior is causing some issues. My sense is for ultimate speed, you will devices on separate SPI buses. In addition to the pins meant to be soldered with a ribbon cable to connect to a SD drive (SPI3), there is a second SPI bus on the Teensy 4.0. You would need to solder two wires (or use a breakout shield) to pads #26 and 27 and use pins 0/1 to use SPI2. These pads are at standard 0.1" (2.54mm) spacing and should easier to solder than the SPI3 pins (1mm).

You might want to read this thread:

But the discussion has sprawled out to other high volume threads:

I use a Teensy 3.2 and I use dual SDI devices all the time 1) an ILI9341 display and 2) and SD card. Both are on the same SPI lines but different CS of course. Works fine in my data logging application.

I have recently experimented with dual ILI9341 displays on the same line (different DC and CS). I'm using the lib ILI9341_t3 and dual displays work but you are limited as to what CS pins can be used. I hear this lib is optimized and requires only certian pins for CS. I was not able to write different stuff to each display but had some weird update issue (1 display could not be updated 2x more frequent than the other) but I only spend a few hours messing with it.

I've never used completely different SPI busses for multiple devices.
Do you have pull-up resistors for the CS and D/C pins? Somebody else had a similar issue and pull-up resistors helped. For me, I was having screen corruption, and adding 2.2K pull-up resistors allowed me to raise the SPI bus speed when I was talking to two different displays. A pull-up resistor would be a resistor that goes between the pin and 3.3v in parallel to the normal wire. I've used 2.2K pull-up resistors, which I also use for i2c buses.
 
It depends on how optimized the drivers are and whether the devices do things like tri-state MISO properly.

Assuming you have done all things in the quoted document (pull-up resistors on the CS pins, verified the device tri-states MISO, and verified that the driver uses SPI.beginTranasaction and SPI.endTransaction), if the drivers are dumb, then it may not matter if if you have one SPI device or two. I suspect it will do one device and then another.

However, if you have the proper driver with the appropriate magic, then it will speed things up, because the driver queues things up to do transfers in the background (using DMA) while it is free to do other things.

Note, while I am familiar with things at a high level, for details you want the experts (KurtE, mjs513, defragster, and of course Paul S.).

The 3.x world seems to be very different from the 4.0 world.

In the 3.x world you could have 5 high speed options, spread over 9 pins. Note, some pins internally used the same ports, so you could use on or the other pin with high speed SPI, but not both. The pins are:

Note, if your device uses a D/C pin, that must also come the list of fast pins.

One of the canonical programs that does use dual SPI is uncannyEyes, which draws eyes on two 128x128 displays as fast as it can. In uncannyEyes, you need 3 fast pins (one for the CS for each display, and the third for the shared D/C pin). In my current configuration, I tend to use pins 23/22 as the two CS pins and 15 as the D/C pin, but I may be changing this to use a more standard CS pins 10/9.

The author of uncannyEyes has been working on newer versions that target other microprocessors (Rasberry Pi, and the Adafruit Monster M4SK which uses an ARM Cortex M4). IIRC, in the Teensy version, he does one display, waits until it is finished and then does the other display. In the newer version that uses 240x240 displays instead of 128x128 displays (i.e. 4 times the data), I believe he does both SPI streams in parallel (using 2 SPI ports). If that is ever ported back to the Teensy 3.5/3.6, it would likely then need separate D/C pins for each display.

While the Teensy 3.5/3.6 have multiple SPI buses, I think the other SPI buses are limited to a single fast pin. This doesn't help if you need both CS and D/C pins to be fast pins.

Teensy 4.0 is a lot more complicated. The SPI internals changed, and also the memory caching behavior is causing some issues. My sense is for ultimate speed, you will devices on separate SPI buses. In addition to the pins meant to be soldered with a ribbon cable to connect to a SD drive (SPI3), there is a second SPI bus on the Teensy 4.0. You would need to solder two wires (or use a breakout shield) to pads #26 and 27 and use pins 0/1 to use SPI2. These pads are at standard 0.1" (2.54mm) spacing and should easier to solder than the SPI3 pins (1mm).

You might want to read this thread:

But the discussion has sprawled out to other high volume threads:


Do you have pull-up resistors for the CS and D/C pins? Somebody else had a similar issue and pull-up resistors helped. For me, I was having screen corruption, and adding 2.2K pull-up resistors allowed me to raise the SPI bus speed when I was talking to two different displays. A pull-up resistor would be a resistor that goes between the pin and 3.3v in parallel to the normal wire. I've used 2.2K pull-up resistors, which I also use for i2c buses.

Hi Michael, very much appreciate the detailed response. Currently on the road but will do further research and I will be back with new findings and developments.
 
It depends on how optimized the drivers are and whether the devices do things like tri-state MISO properly.

Assuming you have done all things in the quoted document (pull-up resistors on the CS pins, verified the device tri-states MISO, and verified that the driver uses SPI.beginTranasaction and SPI.endTransaction), if the drivers are dumb, then it may not matter if if you have one SPI device or two. I suspect it will do one device and then another.

However, if you have the proper driver with the appropriate magic, then it will speed things up, because the driver queues things up to do transfers in the background (using DMA) while it is free to do other things.

Note, while I am familiar with things at a high level, for details you want the experts (KurtE, mjs513, defragster, and of course Paul S.).

The 3.x world seems to be very different from the 4.0 world.

In the 3.x world you could have 5 high speed options, spread over 9 pins. Note, some pins internally used the same ports, so you could use on or the other pin with high speed SPI, but not both. The pins are:

Note, if your device uses a D/C pin, that must also come the list of fast pins.

One of the canonical programs that does use dual SPI is uncannyEyes, which draws eyes on two 128x128 displays as fast as it can. In uncannyEyes, you need 3 fast pins (one for the CS for each display, and the third for the shared D/C pin). In my current configuration, I tend to use pins 23/22 as the two CS pins and 15 as the D/C pin, but I may be changing this to use a more standard CS pins 10/9.

The author of uncannyEyes has been working on newer versions that target other microprocessors (Rasberry Pi, and the Adafruit Monster M4SK which uses an ARM Cortex M4). IIRC, in the Teensy version, he does one display, waits until it is finished and then does the other display. In the newer version that uses 240x240 displays instead of 128x128 displays (i.e. 4 times the data), I believe he does both SPI streams in parallel (using 2 SPI ports). If that is ever ported back to the Teensy 3.5/3.6, it would likely then need separate D/C pins for each display.

While the Teensy 3.5/3.6 have multiple SPI buses, I think the other SPI buses are limited to a single fast pin. This doesn't help if you need both CS and D/C pins to be fast pins.

Teensy 4.0 is a lot more complicated. The SPI internals changed, and also the memory caching behavior is causing some issues. My sense is for ultimate speed, you will devices on separate SPI buses. In addition to the pins meant to be soldered with a ribbon cable to connect to a SD drive (SPI3), there is a second SPI bus on the Teensy 4.0. You would need to solder two wires (or use a breakout shield) to pads #26 and 27 and use pins 0/1 to use SPI2. These pads are at standard 0.1" (2.54mm) spacing and should easier to solder than the SPI3 pins (1mm).

You might want to read this thread:

But the discussion has sprawled out to other high volume threads:


Do you have pull-up resistors for the CS and D/C pins? Somebody else had a similar issue and pull-up resistors helped. For me, I was having screen corruption, and adding 2.2K pull-up resistors allowed me to raise the SPI bus speed when I was talking to two different displays. A pull-up resistor would be a resistor that goes between the pin and 3.3v in parallel to the normal wire. I've used 2.2K pull-up resistors, which I also use for i2c buses.

Hi MichaelMeissner,
I am currently working with a Teensy 4.0 and two different SPI devices: one ADC which is producing values at 18kHz (DR signal) and an SD Card.
For speed reasons I want to use the two SPI ports. ADC on pins 10,11,12,13 and SD Card on pins 34,35,36,37.

I am fairly new to software development and therefore I am not shure which adjustments to make to the different librarys. I have seen that pins_arduino.h does not define the second SPI bus. Do I need to make adjstments to the Sd2PinMap.h and add the second SPI pins? I do not know how to address the second SPI bus, since none of the methods (SPI.beginTransaction, SPI.transfer) is having a specifier for the different SPI busses.
Thank you very much for your advice!

Johannes
 
Hi MichaelMeissner,
I am currently working with a Teensy 4.0 and two different SPI devices: one ADC which is producing values at 18kHz (DR signal) and an SD Card.
For speed reasons I want to use the two SPI ports. ADC on pins 10,11,12,13 and SD Card on pins 34,35,36,37.

I am fairly new to software development and therefore I am not shure which adjustments to make to the different librarys. I have seen that pins_arduino.h does not define the second SPI bus. Do I need to make adjstments to the Sd2PinMap.h and add the second SPI pins? I do not know how to address the second SPI bus, since none of the methods (SPI.beginTransaction, SPI.transfer) is having a specifier for the different SPI busses.
Thank you very much for your advice!

Johannes

For the SPI #2 pins {34,35,36,37}, the best use of those "SD" pins for an SD Card follows 'BUILTIN_SDCARD' in the SD examples for 4 bit SDIO data transfers - like in example : "...\hardware\teensy\avr\libraries\SD\examples\CardInfo\CardInfo.ino"
 
CardInfo.ino for SDIO?

For the SPI #2 pins {34,35,36,37}, the best use of those "SD" pins for an SD Card follows 'BUILTIN_SDCARD' in the SD examples for 4 bit SDIO data transfers - like in example : "...\hardware\teensy\avr\libraries\SD\examples\CardInfo\CardInfo.ino"


Thanks for the quick response. When I look into the example and especially into SD2Card.h, i get the impression, that it is using SPI instead of SDIO.
Unfortunately my current SD Card Reader does not seem to support SDIO.

I will give it a try as soon as the new card reader arrived.

Thanks again.

Johannes
 
Again I am not sure if I saw this mentioned on this thread or not. But potentially another reason to use two SPI busses is if your code (including device libraries) are setup to allow multiple things to go on at the same time with the devices. Typically this is done by using DMA.

Example with several of our display drivers we have the ability to tell a display to update from the libraries internal "frame buffer" and to do so using DMA. And once you start up the DMA update your code is free to do something else, like startup a display update on another display. One such example of this is in the ST7735_t3 library examples with using uncanny eyes.

@JSCLandon - nope that device does not support SDIO.
 
Thanks for the quick response. When I look into the example and especially into SD2Card.h, i get the impression, that it is using SPI instead of SDIO.
Unfortunately my current SD Card Reader does not seem to support SDIO.

I will give it a try as soon as the new card reader arrived.

Thanks again.

Johannes

Opps … It takes a dedicated socket direct wired to the right pins to get SDIO - like the T_3.6 has.

Some DIY solutions on this thread : pjrc.com/threads/57913-Is-there-a-third-Teensy-4-0-breakout-board

Not sure the SPI2 interface to those pins was officially added. Through the Beta @KurtE did work with it for displays and other - but maybe as FlexSPI using those pins?

In some few weeks/months the T_3.6 sized NEW T_4.1 will hopefully be produced in quantity that will have the SD socket onboard. AFAIK Paul posted he is doing his best to get his design produced in these trying times.

If waiting isn't in the cards - maybe connecting the USBHost pins to a FLASH drive, or Hard Drive would be an easier and faster alternative. There is a thread for that working with T4's USB Host.
 
@KurtE and defragster: Thanks a lot. I am fascinated how viral this community is!
This is my plan now:
1. Connect the ADC to SPI0 (10,11,12,13) and talk to it via the SPI Library only.
2. Connect the (new) SD Card reader to the SDIO Pins (34,35,36,37,38,39) and talk to it through the SD Library.

Hope that this is going to work

I'll communicate the result!
Johannes
 
@KurtE and defragster: Thanks a lot. I am fascinated how viral this community is!
This is my plan now:
1. Connect the ADC to SPI0 (10,11,12,13) and talk to it via the SPI Library only.
2. Connect the (new) SD Card reader to the SDIO Pins (34,35,36,37,38,39) and talk to it through the SD Library.

Hope that this is going to work

I'll communicate the result!
Johannes

There are several different breakout boards to allow you to access the pins underneath the Teensy 4.0: https://github.com/TeensyUser/doc/wiki/Teensy-4.0-breakout-boards.

There are 2 different sets of pins underneath the Teensy 4.0.

Pins 24-33 are spaced at the standard 0.1" pitch, and are perhaps easier to access. 2 of the second SPI pins (MOSI1 == 26, SCK1 == 27) are in this lot. Unfortunately, the pinout card misses the other 2 pins (CS1 == 0, MISO1 == 1) that are needed for SPI1.

Pins 34-39 are arranged for a micro-SD card. They are 1mm pitch, and a little harder to solder since they are smaller. Unfortunately, you can't just plop a micro-SD card reader on these pins. The 2 common methods are to solder an 8 pin FFC (flat cable) to those pins, and bring it out to either a micro SD card reader or to a micro SD card reader attached to a connector so you can remove the Teensy. The other method is to use special PCB that brings out the SD card pins.
 
Thanks Michael,

I am not good in programming, but I have done quite some soldering. Therefore soldering to the SDIO (pin 34 - 39) is my choice as soon as the new card reader has arrived. I'll keep you posted.

Johannes
 
As promissed, I keep you updated on my efforts to attach an SD Card to my T4.0:

I still can not communicate with the SD Card.
- First I thought it has to do with dual SPI devices, since I am communicating with a ADC over SPI. That has been eliminated, after conncecting the SD Card to the SDIO interface.
- I have further isolated the problem by just trying the SD.h examples.
- example 1: CardInfo.ino works fine, identifies the card, all card info is correct, until the sketch tries to list the content. (line 124). No content can be read, no error message.
- example 2: Files.ino: SD.begin can not be called successfully and produces the error message: Initialization failed.

I did not change anything, but "const int chipSelect = BUILTIN_SDCARD;".

Thanks for help!
Johannes
 
T4 with SDIO.

First thing I would check is your SDIO connections. How do you have them connected? Do you have all of the signals? No shorts...

That is I have set to use a proper SD card holder with all six signals plus power and ground a few different ways. Like using the small ribbon with connectors and a few different castellated solder on boards. I have had some success with some of these and some not.

So the first things I would try include ringing out all of the IO pins to your SD. These days I use a sketch that @defragster and myself hacked up, which has been posted a couple of times (HILOW)
Code:
void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 4000 );
  Serial.println("Compile Time:: " __FILE__ " " __DATE__ " " __TIME__);
  Serial.printf("Num Digital Pins: %d\n", NUM_DIGITAL_PINS);

  testForShorts();
  
}

uint32_t cnt = 0;
void loop() {
  cnt++;
    allPinTest( cnt );
}

uint32_t pinLast[NUM_DIGITAL_PINS];
void allPinTest( uint32_t cnt ) {
  uint32_t ii, SET;
  Serial.print("PULLDOWN Start Vals:\n  ");
  SET = 0;
  Serial.print("PULLDOWN :: TEST to 3.3V\n  ");
  for ( ii = 0; ii < NUM_DIGITAL_PINS; ii++) {
    pinMode( ii, INPUT_PULLDOWN );
    delayMicroseconds( 5 );
    pinLast[ii] = digitalReadFast( ii );
    if (pinLast[ii]) {
      Serial.print("\nd#=");
      Serial.print( ii );
      Serial.print( " val=" );
    }
    Serial.print( pinLast[ii] );
    Serial.print(',');
  }
  Serial.println();
  Serial.println();
  while ( 1 ) {
    uint32_t jj, dd = 0, cc = 0, ee=4;
    cc = 0;
    for ( ii = 0; ii < NUM_DIGITAL_PINS; ii++) {
      jj = digitalReadFast( ii );
      if ( jj != pinLast[ii] ) {
        dd = 1;
        cc++;
        pinLast[ii] = jj;
        Serial.print("d#=");
        Serial.print( ii );
        if ( pinLast[ii] ) Serial.print( "\t" );
        Serial.print( " val=" );
        Serial.print( pinLast[ii] );
        Serial.print(',');
      }
      if ( cc > 1 && ee ) {
        Serial.println(">>> MULTI CHANGE !!");
        ee--;
      }
      if ( Serial.available() ) {
        while ( Serial.available() ) Serial.read();
        if ( 0 == SET ) {
          SET = 1;
          Serial.print("PULLUP :: TEST TO GND\n  ");
        }
        else {
          SET = 0;
          Serial.print("PULLDOWN :: TEST to 3.3V\n  ");
        }
        for ( ii = 0; ii < NUM_DIGITAL_PINS; ii++) {
          if ( 0 == SET )
            pinMode( ii, INPUT_PULLDOWN );
          else
            pinMode( ii, INPUT_PULLUP );
          delayMicroseconds( 20 );
          pinLast[ii] = digitalReadFast( ii );
          if (SET != pinLast[ii]) {
            Serial.print("d#=");
            Serial.print( ii );
            Serial.print( " val=" );
            Serial.println( pinLast[ii] );
          }
        }
      }
    }
    if ( dd ) {
      dd = 0;
      Serial.println();
      delay( 50 );
    }
  }
}

void testForShorts() {
  uint32_t ii;
  Serial.print("Quick Test for Shorts to adjacent pin");
  Serial.println("First pull pins down and see if the next one follows");
  for ( ii = 0; ii < NUM_DIGITAL_PINS-1; ii++) {
    pinMode( ii+1, INPUT_PULLDOWN );
    pinMode( ii, OUTPUT);
    digitalWrite(ii, HIGH);
    delayMicroseconds( 5 );
    if (digitalRead(ii+1)) {
      Serial.printf("%d:%d ", ii, ii+1);
    }
  }
  Serial.println("\n Now try Pull up and see if setting low follow");
  for ( ii = 0; ii < NUM_DIGITAL_PINS-1; ii++) {
    pinMode( ii+1, INPUT_PULLUP );
    pinMode( ii, OUTPUT);
    digitalWrite(ii, LOW);
    delayMicroseconds( 5 );
    if (!digitalRead(ii+1)) {
      Serial.printf("%d:%d ", ii, ii+1);
    }
  }
  Serial.println();  
}

It allows you to test pins from either 3.3v to the pin or GND to the pin. It also does a quick check of seeing if it thinks you might have a short, by setting one pin to INPUT_PULLDOWN and the pin next to it as OUTPUT, and then drive that one high and see if the pin that was set to pull down now has a high value...
 
Thanks KurtE,
your script looks great! Unfortunately I am travelling until Sunday. So I will give it a try on Monday.
Looking forward to it!
Johannes
 
Status
Not open for further replies.
Back
Top