SPI1 (MOSI1, MISO1, SCK1, CS1) Not Initializing on Teensy 4.1 + Audio Shield

ForYourHealth

New member
Hello All!!! I'm new to the Teensy 4.1 and this forum!

I wish to switch between the Teensy 4.1 on board SD slot on SPI2 (MOSI2, MISO2, SCK1, CS2), the Audio Shield on board SD slot on the first SPI (MOSI, MISO, SCK, CS) and add an external third SD card slot on SPI1 (MOSI1, MISO1, SCK1, CS1). (I'd like to use all three SD cards simultaneously, but I know that's probably too much to ask.)

I can get the first two SD card slot reading perfectly. However, I cannot get the third external SD card to be recognized on SPI1 (MOSI1, MISO1, SCK1, CS1), as it will not initialize.

For reference only, I've tried two different SD card readers linked here:

https://www.adafruit.com/product/4682

https://www.adafruit.com/product/254

Here's my simple test source code and wiring:

Code:
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

#define SDCARD_CS_PIN    0   //CS1 ---> CS Pin
#define SDCARD_MISO_PIN  1   //MISO1 ---> DO Pin
#define SDCARD_MOSI_PIN  26  //MOSI1 ---> DI Pin
#define SDCARD_SCK_PIN   27  //SCK1 ---> CLK Pin

void setup() {
  // put your setup code here, to run once:

  Serial.begin(9600);

  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    while (1) {
      Serial.println("Unable to access the SD card!");
      delay(2000);
    }
  }
  Serial.println("Successfully access the SD card!");

}

void loop() {
  // put your main code here, to run repeatedly:

}

My gut tells me I am either missing something simple, or it's some special functionality/requirement I am completely unaware of.

Any thoughts on this and how to get this third SD slot working you be much appreciated!

Thank you in advance for any comments provided!
 
Last edited:
First, make sure you have the latest Teensyduino (version 1.56). Versions older than 1.54 can't possibly use SPI1. In Arduino, click Help > About to check.

You'll need to use the SdFat begin() function to access SPI1. Sadly, there aren't any really good examples. The closest to at least get started in File > Examples > SD > SdFat_Usage.
 
I've updated the SdFat_Usage example to show how to use SPI1.

https://github.com/PaulStoffregen/SD/commit/9912b57a0fa05de883ae8a0953386cbaf054b37b

Here is a ready to go copy.

Code:
/*
  SdFat usage from SD library

  Starting with Teensyduino 1.54, the SD library is a thin wrapper for SdFat.

  You can access the main SdFat filesystem with "SD.sdfs".  You may wish to
  use SD.sdfs.begin() to cause SdFat to access the SD card using faster
  drivers than the default.  You may also wish to open files as FsFile to
  gain access to SdFat's special file functions which are not available using
  the simpler SD File.

  This example shows some of the ways to select optimized SdFat drivers and
  how to use special file truncation and pre-allocation for optimized data
  logging.

  This example code is in the public domain.
*/
#include <SD.h>

// On Teensy 2.0, SdFat's files are "File32" rather than "FsFat"
#ifdef __AVR__
#define FsFile File32
#endif

void setup()
{
  //Uncomment these lines for Teensy 3.x Audio Shield (Rev C)
  //SPI.setMOSI(7);  // Audio shield has MOSI on pin 7
  //SPI.setSCK(14);  // Audio shield has SCK on pin 14

  Serial.begin(9600);
  while (!Serial); // wait for Arduino Serial Monitor

  Serial.print("Initializing SD card...");
  bool ok;
  const int chipSelect = 10;

  // Instead of the usual SD.begin(pin), you can access the underlying
  // SdFat library for much more control over how the SD card is
  // accessed.  Uncomment one of these, or craft your own if you wish
  // to use SdFat's many special features.

  // Faster SPI frequency.  16 MHz is default for longer / messy wiring.
  //ok = SD.sdfs.begin(SdSpiConfig(chipSelect, SHARED_SPI, SD_SCK_MHZ(24)));

  // Very slow SPI frequency.  May be useful for hardware with slow buffers.
  //ok = SD.sdfs.begin(SdSpiConfig(chipSelect, SHARED_SPI, SD_SCK_MHZ(4)));

  // Different SPI port (Teensy 4.1 SPI1 is MOSI:pin26, MISO:pin1, SCL:pin27)
  ok = SD.sdfs.begin(SdSpiConfig(chipSelect, SHARED_SPI, SD_SCK_MHZ(16), &SPI1));

  // SdFat offers DEDICATED_SPI optimation when no other SPI chips are
  // connected.  More CPU time is used and results may vary depending on
  // interrupts, but for many cases speed is much faster.
  //ok = SD.sdfs.begin(SdSpiConfig(chipSelect, DEDICATED_SPI, SD_SCK_MHZ(16)));

  // Access the built in SD card on Teensy 3.5, 3.6, 4.1 using FIFO
  //ok = SD.sdfs.begin(SdioConfig(FIFO_SDIO));

  // Access the built in SD card on Teensy 3.5, 3.6, 4.1 using DMA (maybe faster)
  //ok = SD.sdfs.begin(SdioConfig(DMA_SDIO));

  if (!ok) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  Serial.println();

  // After the SD card is initialized, you can access is using the ordinary
  // SD library functions, regardless of whether it was initialized by
  // SD library SD.begin() or SdFat library SD.sdfs.begin().
  //
  Serial.println("Print directory using SD functions");
  File root = SD.open("/");
  while (true) {
    File entry = root.openNextFile();
    if (!entry) break; // no more files
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
    } else {
      printSpaces(40 - strlen(entry.name()));
      Serial.print("  ");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }

  // You can also access the SD card with SdFat's functions
  //
  Serial.println();
  Serial.println("Print directory using SdFat ls() function");
  SD.sdfs.ls();

  // You can access files using SdFat which uses "FsFile" for open files
  // FsFile offers more capability than regular SD "File".  As shown in this
  // example, you can truncate tiles.  You can also pre-allocate a file on
  // the SD card (if it does not yet have any data, the reason we truncate
  // first).  Pre-allocation impoves the speed of writes within the already
  // allocated space while data logging or performing other small writes.
  //
  Serial.println();
  Serial.println("Writing to datalog.bin using SdFat functions");
  FsFile myfile = SD.sdfs.open("datalog.bin", O_WRITE | O_CREAT);
  unsigned int len = myfile.fileSize();
  Serial.print("datalog.bin started with ");
  Serial.print(len);
  Serial.println(" bytes");
  if (len > 0) {
    // reduce the file to zero if it already had data
    myfile.truncate();
  }
  if (myfile.preAllocate(40*1024*1024)) {
    Serial.print("  Allocate 40 megabytes for datalog.bin");
  } else {
    Serial.print("  unable to preallocate this file");
  }
  myfile.print("Just some test data written to the file (by SdFat functions)");
  myfile.write('\0'); // add a null byte to mark end of string
  myfile.close();

  // You can also use regular SD functions, even to access the same file.  Just
  // remember to close the SdFat FsFile before opening as a regular SD File.
  //
  Serial.println();
  Serial.println("Reading to datalog.bin using SD functions");
  File f = SD.open("datalog.bin");
  if (f) {
    char mybuffer[100];
    int index = 0;
    while (f.available()) {
      char c = f.read();
      mybuffer[index] = c;
      if (c == 0) break;  // end of string
      index = index + 1;
      if (index == 99) break; // buffer full
    }
    mybuffer[index] = 0;
    Serial.print("  Read from file: ");
    Serial.println(mybuffer);
  } else {
    Serial.println("unable to open datalog.bin :(");
  }
  f.close();

  // When mixing SD and SdFat file access, remember for writing that
  // SD defaults to appending if you open with FILE_WRITE.  You must
  // use FILE_WRITE_BEGIN if you wish to overwrite the file from the
  // start.  With SdFat, O_WRITE or O_RDWR starts overwriting from the
  // beginning.  You must add O_AT_END if you wish to appead.
}

void loop()
{
  // nothing happens after setup finishes.
}


void printSpaces(int num) {
  for (int i = 0; i < num; i++) {
    Serial.print(" ");
  }
}

I tested this just now on a Teensy 4.1 with SD card connected to SPI1 (but using pin 10 for CS). It definitely does work.

spi1.jpg
 
Thank you for the excellent advice @KurtE and @PaulStoffregen! Everything is working as expected after integrating your suggestions!

And thank you again @PaulStoffregen for updating the SdFat_Usage example for SPI1.

Thanks again!:)
 
@PaulStoffregen Thank you for your answer. I just can't figure this, I followed the same schema as yours and I used the exact same code, but it just doesn't work. I use the arduino default microsd card adapter. If anyone has any idea of what could be going wrong, I would be graceful. Thanks.
(by the way your library sdfat is just insane : )
 

Attachments

  • IMG_20240712_143509.jpg
    IMG_20240712_143509.jpg
    252.1 KB · Views: 37
That looks like one of the problematic SD card adapters. Many people have experienced problems with those.

Power is the first issue. Hopefully you can find good documentation about the its power requirements. But often these are made by Asian companies who give little or no info, or they copy some other design but sometimes substitute cheaper parts. Ask the seller where you bought it for info. The main thing you need to know is whether it is meant to be powered by 3.3V as you've wired in this photo, or if it has a 3.3V regulator on-board and if so, what input voltage does it need. Some with regulators claim to accept a wide range, but cheap copies have different voltage regulator chips that basically need 5 volts to properly create 3.3V for the SD card. If you have one of those models and you attempt to power with 3.3V, the SD card ends up getting about 2 volts and can't work properly.

The other common problem is slow buffer chips. I believe we reduced the default SPI clock to only 16 MHz in recent versions, so this shouldn't be as much of a problem as it was when we had 24 MHz as the default. But some of the really cheap models are only able to work at the 8 MHz speed used on 8 bit Arduino. We have a way to control the SPI clock speed. In Arduino, click File > Examples > SD > SdFat_Usage for the special begin() functions that give you more control over SPI settings.

While you're trying to just get the hardware working at all, I'd recommend wiring it up to the main SPI port (pins 10, 11, 12, 13). Then it should work with the normal examples. You can always just move the 4 signal wires later, after you've made it work on the main SPI port.
 
@PaulStoffregen Thank you very much now it's working. I changed the microsd card adapter as you suggested and it's fine. I use a code that you provided in an example and I modified it a bit. But your code is meant for controlling the sd card slot that is built in in the teensy 4.1. It's quite hard to understand the parameters that I have to change on top of the program to make it work for my external microsd slot. Could you help me ? Thank you.

C++:
#include <SdFat.h>
#include <sdios.h>
#define SD_FAT_TYPE 3

// SDCARD_SS_PIN is defined for the built-in SD on some boards.
#ifndef SDCARD_SS_PIN
//const uint8_t SD_CS_PIN = SS;
const uint8_t SD_CS_PIN = 10;
#else  // SDCARD_SS_PIN
// Assume built-in SD is used.
//const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
const uint8_t SD_CS_PIN = 10;
#endif  // SDCARD_SS_PIN

// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
#define SPI_CLOCK SD_SCK_MHZ(50)

// Try to select the best SD card configuration.
#if HAS_SDIO_CLASS
#define SD_CONFIG SdioConfig(FIFO_SDIO)
#elif  ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
#else  // HAS_SDIO_CLASS
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
#endif  // HAS_SDIO_CLASS

#if SD_FAT_TYPE == 0
SdFat sd;
File file;
File defaultFile;
#elif SD_FAT_TYPE == 1
SdFat32 sd;
File32 file;
File32 defaultFile;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile file;
ExFile defaultFile;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile file;
FsFile defaultFile;
#else  // SD_FAT_TYPE
#error Invalid SD_FAT_TYPE
#endif  // SD_FAT_TYPE

// Serial print stream
ArduinoOutStream cout(Serial);
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(&Serial, F(s))

int data;

String numberDefault = "1";

void setup() {
  String fileName = numberDefault+".nc";

  Serial.begin(9600);

  while(!Serial){;}
 
  cout << F("Insert an empty SD.  Type any character to start.") << endl;

  while (!Serial.available()) {
    yield();
  }

  // Initialize at the highest speed supported by the board that is
  // not over 50 MHz. Try a lower speed if SPI errors occur.
  if (!sd.begin(SD_CONFIG)) {
    sd.initErrorHalt(&Serial);
  }
 
  if(sd.exists("sd_default.nc")){
    cout << F("Deleting sd_default.nc") << endl;
    if(!sd.remove("sd_default.nc")){
      error("couldn't remove sd_default.nc");
    }
  }
  if (!file.open(fileName.c_str(), FILE_READ)) {
    error("couldn't open the number file.");
  }
  if (!defaultFile.open("sd_default.nc", FILE_WRITE)){
    error("couldn't open the sd_default file");
  }

  Serial.println("writing the original file into the destination");
  while ((data = file.read()) >= 0) {
    defaultFile.write(data);
  }
  Serial.println("finished duplicating the data");

  file.close();
  defaultFile.close();
  Serial.println("End");
}

void loop() {
  // put your main code here, to run repeatedly:

}

I already changed the SD_CS_PIN which I suppose is corresponding to the chipselect but it's not working as expected. I get this :
begin() failed
Do not reformat the SD.
SdError: 0X17,0X0
 
It's quite hard to understand the parameters that I have to change on top of the program to make it work for my external microsd slot.

This code uses SdFat. It is complicated.

I recommend using the simpler SD library. It is much easier to use and understand.

Since Teensyduino 1.54, SD.h is merely a thin wrapper which really uses SdFat internally. So if you use the simpler SD.h, you are not giving up the performance of SdFat. Both really use SdFat. But the SD.h way is much easier.
 
Back
Top