SD Card Using SPI2

Working libraries, libraries being ported and related hardware
Post Reply
toogooda
Posts: 10
Joined: Sun Jul 19, 2020 9:47 pm
Answers: 1

SD Card Using SPI2

Post by toogooda »

Hi all I have been bouncing around Google trying to work out how to get SD cards working with SPI and specifically with SPI2. I have managed it with Atmel and ESP but just can't get it going with STM anyone managed it?

What I have tried so far:

The standard Arduino SD library https://www.arduino.cc/en/Reference/SD
This uses SPI but you can not change the default SPI, I looked for some time without luck in changing this,

The Ported STM32 version here https://www.arduino.cc/reference/en/lib ... o-stm32sd/ but the library is specifically designed to work with SDIO/SDMMC-hardware not SPI.

I also found reference to an old Adafruit https://github.com/adafruit/SD version that was SPI and you could specify all the SPI pins in the constructor but they stopped supporting it two years ago and it no longer compiles, after lots of trying I could not get this to work either.

Can anyone please help me to get SPI SD working with STM32?

Thanks in advance for your help...
by TFTLCDCyg » Wed Aug 11, 2021 1:06 pm
Have you tried using the SdFat library? The latest version can be used with STM32 boards. You just have to configure the correct pinout and the appropriate call for the type of microSD on the SPI bus that you want to use. For example, for exFat cards, on a F411CE board connecting the reader on the SPI2 bus, with the STM32 core 1.9.0:

Code: Select all

   #define SD_PIN       PB12
   #define SetSDSpeed   48

   static SPIClass SPI_2 (PB15, PB14, PB13);
   #define SD_CONFIG SdSpiConfig (SD_PIN, DEDICATED_SPI, SD_SCK_MHZ (SetSDSpeed), &SPI_2)

   SdFs SD; // exFat
For a Nucleo F767, with SD reader on the SPI3 bus:

Code: Select all

   #define SD_PIN       PB11
   #define SetSDSpeed   48

   static SPIClass SPI_3(PB2, PB4, PB3);
   #define SD_CONFIG SdSpiConfig(SD_PIN, DEDICATED_SPI, SD_SCK_MHZ(SetSDSpeed), &SPI_3)

   SdFs SD;  //exFat
microSD size: 128 Gb
Image

Code: Select all

/*
 * This program attempts to initialize an SD card and analyze its structure.
 */
#include "SdFat.h"
#include "sdios.h"
/*
  Set DISABLE_CS_PIN to disable a second SPI device.
  For example, with the Ethernet shield, set DISABLE_CS_PIN
  to 10 to disable the Ethernet controller.
*/
const int8_t DISABLE_CS_PIN = -1;


/*
  Change the value of SD_CS_PIN if you are using SPI
  and your hardware does not use the default value, SS.
  Common values are:
  Arduino Ethernet shield: pin 4
  Sparkfun SD shield: pin 8
  Adafruit SD shields and modules: pin 10
*/
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
#ifndef SDCARD_SS_PIN
const uint8_t SD_CS_PIN = PB11;  //Nucleo F767 SPI3

#else  // SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
#endif  // SDCARD_SS_PIN

// SPI3 on Nucleo F767 Setup for SdFat beta
static SPIClass mySPI3(PB2, PB4, PB3);
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(48), &mySPI3)

// SPI2 on F103C8
//static SPIClass mySPI2(PB15, PB14, PB13);
//#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)

//------------------------------------------------------------------------------
SdFs sd;
cid_t m_cid;
csd_t m_csd;
uint32_t m_eraseSize;
uint32_t m_ocr;
static ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
bool cidDmp() {
  cout << F("\nManufacturer ID: ");
  cout << uppercase << showbase << hex << int(m_cid.mid) << dec << endl;
  cout << F("OEM ID: ") << m_cid.oid[0] << m_cid.oid[1] << endl;
  cout << F("Product: ");
  for (uint8_t i = 0; i < 5; i++) {
    cout << m_cid.pnm[i];
  }
  cout << F("\nVersion: ");
  cout << int(m_cid.prv_n) << '.' << int(m_cid.prv_m) << endl;
  cout << F("Serial number: ") << hex << m_cid.psn << dec << endl;
  cout << F("Manufacturing date: ");
  cout << int(m_cid.mdt_month) << '/';
  cout << (2000 + m_cid.mdt_year_low + 10 * m_cid.mdt_year_high) << endl;
  cout << endl;
  return true;
}
//------------------------------------------------------------------------------
bool csdDmp() {
  bool eraseSingleBlock;
  if (m_csd.v1.csd_ver == 0) {
    eraseSingleBlock = m_csd.v1.erase_blk_en;
    m_eraseSize = (m_csd.v1.sector_size_high << 1) | m_csd.v1.sector_size_low;
  } else if (m_csd.v2.csd_ver == 1) {
    eraseSingleBlock = m_csd.v2.erase_blk_en;
    m_eraseSize = (m_csd.v2.sector_size_high << 1) | m_csd.v2.sector_size_low;
  } else {
    cout << F("m_csd version error\n");
    return false;
  }
  m_eraseSize++;
  cout << F("cardSize: ") << 0.000512 * sdCardCapacity(&m_csd);
  cout << F(" MB (MB = 1,000,000 bytes)\n");

  cout << F("flashEraseSize: ") << int(m_eraseSize) << F(" blocks\n");
  cout << F("eraseSingleBlock: ");
  if (eraseSingleBlock) {
    cout << F("true\n");
  } else {
    cout << F("false\n");
  }
  return true;
}
//------------------------------------------------------------------------------
void errorPrint() {
  if (sd.sdErrorCode()) {
    cout << F("SD errorCode: ") << hex << showbase;
    printSdErrorSymbol(&Serial, sd.sdErrorCode());
    cout << F(" = ") << int(sd.sdErrorCode()) << endl;
    cout << F("SD errorData = ") << int(sd.sdErrorData()) << endl;
  }
}
//------------------------------------------------------------------------------
bool mbrDmp() {
  MbrSector_t mbr;
  bool valid = true;
  if (!sd.card()->readSector(0, (uint8_t*)&mbr)) {
    cout << F("\nread MBR failed.\n");
    errorPrint();
    return false;
  }
  cout << F("\nSD Partition Table\n");
  cout << F("part,boot,bgnCHS[3],type,endCHS[3],start,length\n");
  for (uint8_t ip = 1; ip < 5; ip++) {
    MbrPart_t *pt = &mbr.part[ip - 1];
    if ((pt->boot != 0 && pt->boot != 0X80) ||
        getLe32(pt->relativeSectors) > sdCardCapacity(&m_csd)) {
      valid = false;
    }
    cout << int(ip) << ',' << uppercase << showbase << hex;
    cout << int(pt->boot) << ',';
    for (int i = 0; i < 3; i++ ) {
      cout << int(pt->beginCHS[i]) << ',';
    }
    cout << int(pt->type) << ',';
    for (int i = 0; i < 3; i++ ) {
      cout << int(pt->endCHS[i]) << ',';
    }
    cout << dec << getLe32(pt->relativeSectors) << ',';
    cout << getLe32(pt->totalSectors) << endl;
  }
  if (!valid) {
    cout << F("\nMBR not valid, assuming Super Floppy format.\n");
  }
  return true;
}
//------------------------------------------------------------------------------
void dmpVol() {
  cout << F("\nScanning FAT, please wait.\n");
  uint32_t freeClusterCount = sd.freeClusterCount();
  if (sd.fatType() <= 32) {
    cout << F("\nVolume is FAT") << int(sd.fatType()) << endl;
  } else {
    cout << F("\nVolume is exFAT\n");
  }
  cout << F("sectorsPerCluster: ") << sd.sectorsPerCluster() << endl;
  cout << F("clusterCount:      ") << sd.clusterCount() << endl;
  cout << F("freeClusterCount:  ") << freeClusterCount << endl;
  cout << F("fatStartSector:    ") << sd.fatStartSector() << endl;
  cout << F("dataStartSector:   ") << sd.dataStartSector() << endl;
  if (sd.dataStartSector() % m_eraseSize) {
    cout << F("Data area is not aligned on flash erase boundary!\n");
    cout << F("Download and use formatter from www.sdcard.org!\n");
  }
}
//------------------------------------------------------------------------------
void printCardType() {

  cout << F("\nCard type: ");

  switch (sd.card()->type()) {
    case SD_CARD_TYPE_SD1:
      cout << F("SD1\n");
      break;

    case SD_CARD_TYPE_SD2:
      cout << F("SD2\n");
      break;

    case SD_CARD_TYPE_SDHC:
      if (sdCardCapacity(&m_csd) < 70000000) {
        cout << F("SDHC\n");
      } else {
        cout << F("SDXC\n");
      }
      break;

    default:
      cout << F("Unknown\n");
  }
}
//------------------------------------------------------------------------------
void printConfig(SdSpiConfig config) {
  if (DISABLE_CS_PIN < 0) {
    cout << F(
           "\nAssuming the SD is the only SPI device.\n"
           "Edit DISABLE_CS_PIN to disable an SPI device.\n");
  } else {
    cout << F("\nDisabling SPI device on pin ");
    cout << int(DISABLE_CS_PIN) << endl;
    pinMode(DISABLE_CS_PIN, OUTPUT);
    digitalWrite(DISABLE_CS_PIN, HIGH);
  }
  cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin);
  cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n");
}
//------------------------------------------------------------------------------
void printConfig(SdioConfig config) {
  (void)config;
  cout << F("Assuming an SDIO interface.\n");
}
//-----------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  // Wait for USB Serial
  while (!Serial) {
    SysCall::yield();
  }
  cout << F("SdFat version: ") << SD_FAT_VERSION << endl;
  printConfig(SD_CONFIG);

}
//------------------------------------------------------------------------------
void loop() {
  // Read any existing Serial data.
  do {
    delay(10);
  } while (Serial.available() && Serial.read() >= 0);

  // F stores strings in flash to save RAM
  cout << F("\ntype any character to start\n");
  while (!Serial.available()) {
    SysCall::yield();
  }
  uint32_t t = millis();
  if (!sd.cardBegin(SD_CONFIG)) {
    cout << F(
           "\nSD initialization failed.\n"
           "Do not reformat the card!\n"
           "Is the card correctly inserted?\n"
           "Is there a wiring/soldering problem?\n");
    if (isSpi(SD_CONFIG)) {
      cout << F(
           "Is SD_CS_PIN set to the correct value?\n"
           "Does another SPI device need to be disabled?\n"
           );
    }
    errorPrint();
    return;
  }
  t = millis() - t;
  cout << F("init time: ") << t << " ms" << endl;

  if (!sd.card()->readCID(&m_cid) ||
      !sd.card()->readCSD(&m_csd) ||
      !sd.card()->readOCR(&m_ocr)) {
    cout << F("readInfo failed\n");
    errorPrint();
    return;
  }
  printCardType();
  cidDmp();
  csdDmp();
  cout << F("\nOCR: ") << uppercase << showbase;
  cout << hex << m_ocr << dec << endl;
  if (!mbrDmp()) {
    return;
  }
  if (!sd.volumeBegin()) {
    cout << F("\nvolumeBegin failed. Is the card formatted?\n");
    errorPrint();
    return;
  }
  dmpVol();
}
Go to full post
User avatar
fpiSTM
Posts: 1745
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 91
Location: Le Mans
Contact:

Re: SD Card Using SPI2

Post by fpiSTM »

Hi,
Refers to the wiki: https://github.com/stm32duino/wiki/wiki ... tance-pins

Simply change default SPI instance pins to the SPI2 peripherals before the SPI.Begin().
TFTLCDCyg
Posts: 26
Joined: Tue Jan 07, 2020 9:50 pm
Answers: 1

Re: SD Card Using SPI2

Post by TFTLCDCyg »

Have you tried using the SdFat library? The latest version can be used with STM32 boards. You just have to configure the correct pinout and the appropriate call for the type of microSD on the SPI bus that you want to use. For example, for exFat cards, on a F411CE board connecting the reader on the SPI2 bus, with the STM32 core 1.9.0:

Code: Select all

   #define SD_PIN       PB12
   #define SetSDSpeed   48

   static SPIClass SPI_2 (PB15, PB14, PB13);
   #define SD_CONFIG SdSpiConfig (SD_PIN, DEDICATED_SPI, SD_SCK_MHZ (SetSDSpeed), &SPI_2)

   SdFs SD; // exFat
For a Nucleo F767, with SD reader on the SPI3 bus:

Code: Select all

   #define SD_PIN       PB11
   #define SetSDSpeed   48

   static SPIClass SPI_3(PB2, PB4, PB3);
   #define SD_CONFIG SdSpiConfig(SD_PIN, DEDICATED_SPI, SD_SCK_MHZ(SetSDSpeed), &SPI_3)

   SdFs SD;  //exFat
microSD size: 128 Gb
Image

Code: Select all

/*
 * This program attempts to initialize an SD card and analyze its structure.
 */
#include "SdFat.h"
#include "sdios.h"
/*
  Set DISABLE_CS_PIN to disable a second SPI device.
  For example, with the Ethernet shield, set DISABLE_CS_PIN
  to 10 to disable the Ethernet controller.
*/
const int8_t DISABLE_CS_PIN = -1;


/*
  Change the value of SD_CS_PIN if you are using SPI
  and your hardware does not use the default value, SS.
  Common values are:
  Arduino Ethernet shield: pin 4
  Sparkfun SD shield: pin 8
  Adafruit SD shields and modules: pin 10
*/
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
#ifndef SDCARD_SS_PIN
const uint8_t SD_CS_PIN = PB11;  //Nucleo F767 SPI3

#else  // SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
#endif  // SDCARD_SS_PIN

// SPI3 on Nucleo F767 Setup for SdFat beta
static SPIClass mySPI3(PB2, PB4, PB3);
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(48), &mySPI3)

// SPI2 on F103C8
//static SPIClass mySPI2(PB15, PB14, PB13);
//#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)

//------------------------------------------------------------------------------
SdFs sd;
cid_t m_cid;
csd_t m_csd;
uint32_t m_eraseSize;
uint32_t m_ocr;
static ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
bool cidDmp() {
  cout << F("\nManufacturer ID: ");
  cout << uppercase << showbase << hex << int(m_cid.mid) << dec << endl;
  cout << F("OEM ID: ") << m_cid.oid[0] << m_cid.oid[1] << endl;
  cout << F("Product: ");
  for (uint8_t i = 0; i < 5; i++) {
    cout << m_cid.pnm[i];
  }
  cout << F("\nVersion: ");
  cout << int(m_cid.prv_n) << '.' << int(m_cid.prv_m) << endl;
  cout << F("Serial number: ") << hex << m_cid.psn << dec << endl;
  cout << F("Manufacturing date: ");
  cout << int(m_cid.mdt_month) << '/';
  cout << (2000 + m_cid.mdt_year_low + 10 * m_cid.mdt_year_high) << endl;
  cout << endl;
  return true;
}
//------------------------------------------------------------------------------
bool csdDmp() {
  bool eraseSingleBlock;
  if (m_csd.v1.csd_ver == 0) {
    eraseSingleBlock = m_csd.v1.erase_blk_en;
    m_eraseSize = (m_csd.v1.sector_size_high << 1) | m_csd.v1.sector_size_low;
  } else if (m_csd.v2.csd_ver == 1) {
    eraseSingleBlock = m_csd.v2.erase_blk_en;
    m_eraseSize = (m_csd.v2.sector_size_high << 1) | m_csd.v2.sector_size_low;
  } else {
    cout << F("m_csd version error\n");
    return false;
  }
  m_eraseSize++;
  cout << F("cardSize: ") << 0.000512 * sdCardCapacity(&m_csd);
  cout << F(" MB (MB = 1,000,000 bytes)\n");

  cout << F("flashEraseSize: ") << int(m_eraseSize) << F(" blocks\n");
  cout << F("eraseSingleBlock: ");
  if (eraseSingleBlock) {
    cout << F("true\n");
  } else {
    cout << F("false\n");
  }
  return true;
}
//------------------------------------------------------------------------------
void errorPrint() {
  if (sd.sdErrorCode()) {
    cout << F("SD errorCode: ") << hex << showbase;
    printSdErrorSymbol(&Serial, sd.sdErrorCode());
    cout << F(" = ") << int(sd.sdErrorCode()) << endl;
    cout << F("SD errorData = ") << int(sd.sdErrorData()) << endl;
  }
}
//------------------------------------------------------------------------------
bool mbrDmp() {
  MbrSector_t mbr;
  bool valid = true;
  if (!sd.card()->readSector(0, (uint8_t*)&mbr)) {
    cout << F("\nread MBR failed.\n");
    errorPrint();
    return false;
  }
  cout << F("\nSD Partition Table\n");
  cout << F("part,boot,bgnCHS[3],type,endCHS[3],start,length\n");
  for (uint8_t ip = 1; ip < 5; ip++) {
    MbrPart_t *pt = &mbr.part[ip - 1];
    if ((pt->boot != 0 && pt->boot != 0X80) ||
        getLe32(pt->relativeSectors) > sdCardCapacity(&m_csd)) {
      valid = false;
    }
    cout << int(ip) << ',' << uppercase << showbase << hex;
    cout << int(pt->boot) << ',';
    for (int i = 0; i < 3; i++ ) {
      cout << int(pt->beginCHS[i]) << ',';
    }
    cout << int(pt->type) << ',';
    for (int i = 0; i < 3; i++ ) {
      cout << int(pt->endCHS[i]) << ',';
    }
    cout << dec << getLe32(pt->relativeSectors) << ',';
    cout << getLe32(pt->totalSectors) << endl;
  }
  if (!valid) {
    cout << F("\nMBR not valid, assuming Super Floppy format.\n");
  }
  return true;
}
//------------------------------------------------------------------------------
void dmpVol() {
  cout << F("\nScanning FAT, please wait.\n");
  uint32_t freeClusterCount = sd.freeClusterCount();
  if (sd.fatType() <= 32) {
    cout << F("\nVolume is FAT") << int(sd.fatType()) << endl;
  } else {
    cout << F("\nVolume is exFAT\n");
  }
  cout << F("sectorsPerCluster: ") << sd.sectorsPerCluster() << endl;
  cout << F("clusterCount:      ") << sd.clusterCount() << endl;
  cout << F("freeClusterCount:  ") << freeClusterCount << endl;
  cout << F("fatStartSector:    ") << sd.fatStartSector() << endl;
  cout << F("dataStartSector:   ") << sd.dataStartSector() << endl;
  if (sd.dataStartSector() % m_eraseSize) {
    cout << F("Data area is not aligned on flash erase boundary!\n");
    cout << F("Download and use formatter from www.sdcard.org!\n");
  }
}
//------------------------------------------------------------------------------
void printCardType() {

  cout << F("\nCard type: ");

  switch (sd.card()->type()) {
    case SD_CARD_TYPE_SD1:
      cout << F("SD1\n");
      break;

    case SD_CARD_TYPE_SD2:
      cout << F("SD2\n");
      break;

    case SD_CARD_TYPE_SDHC:
      if (sdCardCapacity(&m_csd) < 70000000) {
        cout << F("SDHC\n");
      } else {
        cout << F("SDXC\n");
      }
      break;

    default:
      cout << F("Unknown\n");
  }
}
//------------------------------------------------------------------------------
void printConfig(SdSpiConfig config) {
  if (DISABLE_CS_PIN < 0) {
    cout << F(
           "\nAssuming the SD is the only SPI device.\n"
           "Edit DISABLE_CS_PIN to disable an SPI device.\n");
  } else {
    cout << F("\nDisabling SPI device on pin ");
    cout << int(DISABLE_CS_PIN) << endl;
    pinMode(DISABLE_CS_PIN, OUTPUT);
    digitalWrite(DISABLE_CS_PIN, HIGH);
  }
  cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin);
  cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n");
}
//------------------------------------------------------------------------------
void printConfig(SdioConfig config) {
  (void)config;
  cout << F("Assuming an SDIO interface.\n");
}
//-----------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  // Wait for USB Serial
  while (!Serial) {
    SysCall::yield();
  }
  cout << F("SdFat version: ") << SD_FAT_VERSION << endl;
  printConfig(SD_CONFIG);

}
//------------------------------------------------------------------------------
void loop() {
  // Read any existing Serial data.
  do {
    delay(10);
  } while (Serial.available() && Serial.read() >= 0);

  // F stores strings in flash to save RAM
  cout << F("\ntype any character to start\n");
  while (!Serial.available()) {
    SysCall::yield();
  }
  uint32_t t = millis();
  if (!sd.cardBegin(SD_CONFIG)) {
    cout << F(
           "\nSD initialization failed.\n"
           "Do not reformat the card!\n"
           "Is the card correctly inserted?\n"
           "Is there a wiring/soldering problem?\n");
    if (isSpi(SD_CONFIG)) {
      cout << F(
           "Is SD_CS_PIN set to the correct value?\n"
           "Does another SPI device need to be disabled?\n"
           );
    }
    errorPrint();
    return;
  }
  t = millis() - t;
  cout << F("init time: ") << t << " ms" << endl;

  if (!sd.card()->readCID(&m_cid) ||
      !sd.card()->readCSD(&m_csd) ||
      !sd.card()->readOCR(&m_ocr)) {
    cout << F("readInfo failed\n");
    errorPrint();
    return;
  }
  printCardType();
  cidDmp();
  csdDmp();
  cout << F("\nOCR: ") << uppercase << showbase;
  cout << hex << m_ocr << dec << endl;
  if (!mbrDmp()) {
    return;
  }
  if (!sd.volumeBegin()) {
    cout << F("\nvolumeBegin failed. Is the card formatted?\n");
    errorPrint();
    return;
  }
  dmpVol();
}
toogooda
Posts: 10
Joined: Sun Jul 19, 2020 9:47 pm
Answers: 1

Re: SD Card Using SPI2

Post by toogooda »

Thanks your version is working well on a F103RET6 however the STM32_Test example for that library does not compile as the have used:

Code: Select all

static SPIClass mySPI2(2);
Instead of how you have done it:

Code: Select all

static SPIClass SPI_2 (PB15, PB14, PB13);
The example compile error shows that there is no constructor where you can pass just the SPI number.
Should I log this with the library to get fixed?

Thanks again for your help :)
toogooda
Posts: 10
Joined: Sun Jul 19, 2020 9:47 pm
Answers: 1

Re: SD Card Using SPI2

Post by toogooda »

fpiSTM wrote: Wed Aug 11, 2021 10:13 am Hi,
Refers to the wiki: https://github.com/stm32duino/wiki/wiki ... tance-pins

Simply change default SPI instance pins to the SPI2 peripherals before the SPI.Begin().
Thank you this was also useful for for other libraries with fixed SPIs :)
TFTLCDCyg
Posts: 26
Joined: Tue Jan 07, 2020 9:50 pm
Answers: 1

Re: SD Card Using SPI2

Post by TFTLCDCyg »

It is not as simple as replacing one line with another, you must create another constructor but with the pins of the second port, so that the STM32Test example points to the correct pinout of the other SPI bus (2, 3, etc).

In this example coded for a Nucleo F767, the first port is the SPI1 bus, the second port points to the SPI3 bus. It should be noted, the index 2 in file2 or in sd2 or in SD2_CONFIG, points to the second port that we want to use, this port can be the SPI2 bus or the SPI3 bus.

Code: Select all

/* This example is for https://github.com/rogerclarkmelbourne/Arduino_STM32
 *
 * Example use of two SPI ports on an STM32 board.
 * Note SPI speed is limited to 18 MHz.
 */
#include <SPI.h>
#include "SdFat.h"
#include "FreeStack.h"

// Chip select PA4, shared SPI, 18 MHz, port 1 --> bus SPI1
#define SD1_CONFIG SdSpiConfig(PA4, SHARED_SPI, SD_SCK_MHZ(18), &SPI)
SdFs sd1;
FsFile file1;

// Use mySPI2 since SPI2 is used in SPI.h as a different type.
//static SPIClass mySPI2(2);
// Chip select PB12, dedicated SPI, 18 MHz, port 2.
//#if ENABLE_DEDICATED_SPI
//#define SD2_CONFIG SdSpiConfig(PB12, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)
//#else  // ENABLE_DEDICATED_SPI
//#define SD2_CONFIG SdSpiConfig(PB12, SHARED_SPI, SD_SCK_MHZ(18), &mySPI2)
//#endif  // ENABLE_DEDICATED_SPI

//port 2  --> bus SPI3
static SPIClass mySPI2(PB2, PB4, PB3);
#define SD2_CONFIG SdSpiConfig(PB11, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)

SdFs sd2;
FsFile file2;

const uint8_t BUF_DIM = 100;
uint8_t buf[BUF_DIM];

const uint32_t FILE_SIZE = 1000000;
const uint32_t NWRITE = FILE_SIZE/BUF_DIM;
//------------------------------------------------------------------------------
// print error msg, any SD error codes, and halt.
// store messages in flash
#define error(msg) {Serial.println(msg); errorHalt();}
void errorHalt() {
  if (sd1.sdErrorCode()) {
    sd1.errorHalt();
  }
  sd2.errorHalt();
}
//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  // Wait for USB Serial
  while (!Serial) {
    SysCall::yield();
  }
  Serial.print(F("FreeStack: "));
  Serial.println(FreeStack());

  // fill buffer with known data
  for (size_t i = 0; i < sizeof(buf); i++) {
    buf[i] = i;
  }

  Serial.println(F("type any character to start"));
  while (!Serial.available()) {
    SysCall::yield();
  }

  // initialize the first card
  if (!sd1.begin(SD1_CONFIG)) {
    error("sd1.begin");
  }
  // create Dir1 on sd1 if it does not exist
  if (!sd1.exists("/Dir1")) {
    if (!sd1.mkdir("/Dir1")) {
      error("sd1.mkdir");
    }
  }
  // Make Dir1 the working directory on sd1.
  if (!sd1.chdir("Dir1")) {
     error("dsd1.chdir");
  }
  // initialize the second card
  if (!sd2.begin(SD2_CONFIG)) {
    error("sd2.begin");
  }
// create Dir2 on sd2 if it does not exist
  if (!sd2.exists("/Dir2")) {
    if (!sd2.mkdir("/Dir2")) {
      error("sd2.mkdir");
    }
  }
  // Make Dir2 the working directory on sd2.
  if (!sd2.chdir("Dir2")) {
     error("sd2.chdir");
  }
  // remove test.bin from /Dir1 directory of sd1
  if (sd1.exists("test.bin")) {
    if (!sd1.remove("test.bin")) {
      error("remove test.bin");
    }
  }
  // remove rename.bin from /Dir2 directory of sd2
  if (sd2.exists("rename.bin")) {
    if (!sd2.remove("rename.bin")) {
      error("remove rename.bin");
    }
  }
  // list directories.
  Serial.println(F("------sd1 Dir1-------"));
  sd1.ls("/", LS_R | LS_SIZE);
  Serial.println(F("------sd2 Dir2-------"));
  sd2.ls("/", LS_R | LS_SIZE);
  Serial.println(F("---------------------"));

  // create or open /Dir1/test.bin and truncate it to zero length
  if (!file1.open(&sd1, "test.bin", O_RDWR | O_CREAT | O_TRUNC)) {
    error("file1.open");
  }
  Serial.println(F("Writing test.bin to sd1"));

  // write data to /Dir1/test.bin on sd1
  for (uint32_t i = 0; i < NWRITE; i++) {
    if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
      error("file1.write");
    }
  }

  // create or open /Dir2/copy.bin and truncate it to zero length
  if (!file2.open(&sd2, "copy.bin", O_WRONLY | O_CREAT | O_TRUNC)) {
    error("file2.open");
  }
  Serial.println(F("Copying test.bin to copy.bin"));

  // copy file1 to file2
  file1.rewind();
  uint32_t t = millis();

  while (1) {
    int n = file1.read(buf, sizeof(buf));
    if (n < 0) {
      error("file1.read");
    }
    if (n == 0) {
      break;
    }
    if ((int)file2.write(buf, n) != n) {
      error("file2.write");
    }
  }
  t = millis() - t;
  Serial.print(F("File size: "));
  Serial.println(file2.fileSize());
  Serial.print(F("Copy time: "));
  Serial.print(t);
  Serial.println(F(" millis"));
  // close test.bin
  file1.close();
  // sync copy.bin so ls works.
  file2.close();
  // list directories.
  Serial.println(F("------sd1 -------"));
  sd1.ls("/", LS_R | LS_SIZE);
  Serial.println(F("------sd2 -------"));
  sd2.ls("/", LS_R | LS_SIZE);
  Serial.println(F("---------------------"));
  Serial.println(F("Renaming copy.bin"));
  // Rename copy.bin. The renamed file will be in Dir2.
  if (!sd2.rename("copy.bin", "rename.bin")) {
    error("rename copy.bin");
  }
  file2.close();
  // list directories.
  Serial.println(F("------sd1 -------"));
  sd1.ls("/", LS_R | LS_SIZE);
  Serial.println(F("------sd2 -------"));
  sd2.ls("/", LS_R | LS_SIZE);
  Serial.println(F("---------------------"));
  Serial.println(F("Done"));
}
//------------------------------------------------------------------------------
void loop() {}
I should note that the example is not up to date with the latest modifications implemented within the SdFat library; just follow the breadcrumb trail.

I have not verified the operation of the above code since I am working with a teensy 4.1 board and a 3.5 "FT813 TFT, but I must say that it compiles without errors
picclock
Posts: 20
Joined: Sat Aug 14, 2021 8:21 am

Re: SD Card Using SPI2

Post by picclock »

Problem below solved by using SDFat library rather than Adafruit SDFat fork, using stm core library. Have a problem with line 26 :
#define SD2_CONFIG SdSpiConfig(PB11, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)
as PB11 not recognised. I am not using chip select so setting it to zero gets it to compile ok.

Hi
Thanks for the example and the insight.
I've tried to compile the above code but the following errors occur. Any help fixing them would be very much appreciated. I've tried the STM core, and Richard's maple core to no avail,(Generic stm32f series + Generic F401ccux, and Blackpill stm32f401ccu6 selected).

sketch_aug14a:12:1: error: 'SdFs' does not name a type
12 | SdFs sd1;
| ^~~~
sketch_aug14a:13:1: error: 'FsFile' does not name a type; did you mean 'SdFile'?
13 | FsFile file1;

Any help or pointers much appreciated.
Best Regards
picclock
TFTLCDCyg
Posts: 26
Joined: Tue Jan 07, 2020 9:50 pm
Answers: 1

Re: SD Card Using SPI2

Post by TFTLCDCyg »

For the F401 Board, the SPI2 bus pins are different from those of the Nucleo F767. I think you can use these lines for F401CC:

Code: Select all

//port 2  --> bus SPI2   on F411CE or F401CC
static SPIClass mySPI2(PB15, PB14, PB13);
#define SD2_CONFIG SdSpiConfig(PB12, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)
Remember that you must have the latest version of the SdFat library installed
picclock
Posts: 20
Joined: Sat Aug 14, 2021 8:21 am

Re: SD Card Using SPI2

Post by picclock »

Thankyou for that post. It saved the day :D , and possibly my sanity.

for the first time I have a fully functioning SDFat on SPI2.

Definitely worth a virtual Beer !!!

Best Regards

picclock
Post Reply

Return to “Libraries & Hardware”