greiman / SdFat

Arduino FAT16/FAT32 exFAT Library
MIT License
1.05k stars 497 forks source link

exFat sd cards not working with Sdinfo.ino example, only FAT32 card #485

Closed JacobDel closed 1 month ago

JacobDel commented 1 month ago

Example source code: https://github.com/greiman/SdFat/blob/master/examples/SdInfo/SdInfo.ino

The only change made in the script was to change the CS pin (see full script below):

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

microcontroller: arduino nano

sd card extension board (simple level shifters): Catalex MicroSD Card Adapter v1.0 11/01/2013 v MicroSD Card Adapter

Working card (see serial logs below):

Not working cards:

  1. SanDisk Ultra 128GB A1 - (factory formatted) exFat format
  2. Samsung Pro Plus 128GB A2 - exFat formatted

128GB cards with exFat format:

SD initialization failed.
Do not reformat the card!
Is the card correctly inserted?
Is there a wiring/soldering problem?
Is SD_CS_PIN set to the correct value?
Does another SPI device need to be disabled?
SD errorCode: SD_CARD_ERROR_INVALID_CARD_CONFIG = 0x2b
SD errorData = 0x0

type any character to start

64GB card with FAT32 format

init time: 439 ms

Card type: SDXC
sdSpecVer: 3.00
HighSpeedMode: true

Manufacturer ID: 0X74
OEM ID: JE
Product: USDU1
Revision: 0.2
Serial number: 0XC8105DF6
Manufacturing date: 9/2014
CID HEX: 744A45555344553102C8105DF600E955

cardSize: 63383.27 MB (MB = 1,000,000 bytes)
flashEraseSize: 128 blocks
eraseSingleBlock: true
dataAfterErase: zeros
CSD HEX: 400E00325B590001D83D7F800A4000A5

OCR: 0XC0FF8000

SD Partition Table
part,boot,bgnCHS[3],type,endCHS[3],start,length
1,0X0,0XA,0X9,0X2,0XC,0XFE,0XFF,0XFF,32768,123762688
2,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0,0
3,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0,0
4,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0,0

Scanning FAT, please wait.

Volume is FAT32
sectorsPerCluster: 64
fatStartSector:    32800
dataStartSector:   63010
clusterCount:      1933319
freeClusterCount:  885415

setup image (note: the module in the middle is not used and all voltage levels are checked and correct): IMG_3529

Full script:

/*
 * This program attempts to initialize an SD card and analyze its structure.
 * The CID and CSD registers are also printed in HEX for use in online
 * decoders like these.
 *
 * https://gurumeditation.org/1342/sd-memory-card-register-decoder/
 * https://archive.goughlui.com/static/multicid.htm
 */
#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 = SS;
// #else   // SDCARD_SS_PIN
// const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
// #endif  // SDCARD_SS_PIN
const uint8_t SD_CS_PIN = 4;

// 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, SD_SCK_MHZ(16))
#else  // HAS_SDIO_CLASS
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(16))
#endif  // HAS_SDIO_CLASS

//------------------------------------------------------------------------------
SdFs sd;
cid_t cid;
csd_t csd;
scr_t scr;
uint8_t cmd6Data[64];
uint32_t eraseSize;
uint32_t ocr;
static ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
void hexDmp(void* reg, uint8_t size) {
  uint8_t* u8 = reinterpret_cast<uint8_t*>(reg);
  cout << hex << noshowbase;
  for (size_t i = 0; i < size; i++) {
    cout << setw(2) << setfill('0') << int(u8[i]);
  }
  cout << dec << endl;
}
//------------------------------------------------------------------------------
void cidDmp() {
  cout << F("\nManufacturer ID: ");
  cout << uppercase << showbase << hex << int(cid.mid) << dec << endl;
  cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
  cout << F("Product: ");
  for (uint8_t i = 0; i < 5; i++) {
    cout << cid.pnm[i];
  }
  cout << F("\nRevision: ") << cid.prvN() << '.' << cid.prvM() << endl;
  cout << F("Serial number: ") << hex << cid.psn() << dec << endl;
  cout << F("Manufacturing date: ");
  cout << cid.mdtMonth() << '/' << cid.mdtYear() << endl;
  cout << F("CID HEX: ");
  hexDmp(&cid, sizeof(cid));
}
//------------------------------------------------------------------------------
void clearSerialInput() {
  uint32_t m = micros();
  do {
    if (Serial.read() >= 0) {
      m = micros();
    }
  } while (micros() - m < 10000);
}
//------------------------------------------------------------------------------
void csdDmp() {
  eraseSize = csd.eraseSize();
  cout << F("\ncardSize: ") << 0.000512 * csd.capacity();
  cout << F(" MB (MB = 1,000,000 bytes)\n");

  cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
  cout << F("eraseSingleBlock: ");
  if (csd.eraseSingleBlock()) {
    cout << F("true\n");
  } else {
    cout << F("false\n");
  }
  cout << F("dataAfterErase: ");
  if (scr.dataAfterErase()) {
    cout << F("ones\n");
  } else {
    cout << F("zeros\n");
  }
  cout << F("CSD HEX: ");
  hexDmp(&csd, sizeof(csd));
}
//------------------------------------------------------------------------------
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()) << dec << 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) > csd.capacity()) {
      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");
  int32_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("fatStartSector:    ") << sd.fatStartSector() << endl;
  cout << F("dataStartSector:   ") << sd.dataStartSector() << endl;
  cout << F("clusterCount:      ") << sd.clusterCount() << endl;
  cout << F("freeClusterCount:  ");
  if (freeClusterCount >= 0) {
    cout << freeClusterCount << endl;
  } else {
    cout << F("failed\n");
    errorPrint();
  }
}
//------------------------------------------------------------------------------
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 (csd.capacity() < 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(115200);
  // Wait for USB Serial
  while (!Serial) {
    yield();
  }
  cout << F("SdFat version: ") << SD_FAT_VERSION_STR << endl;
  printConfig(SD_CONFIG);
}
//------------------------------------------------------------------------------
void loop() {
  // Read any existing Serial data.
  clearSerialInput();

  // F stores strings in flash to save RAM
  cout << F("\ntype any character to start\n");
  while (!Serial.available()) {
    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: ") << dec << t << " ms" << endl;

  if (!sd.card()->readCID(&cid) || !sd.card()->readCSD(&csd) ||
      !sd.card()->readOCR(&ocr) || !sd.card()->readSCR(&scr)) {
    cout << F("readInfo failed\n");
    errorPrint();
    return;
  }
  printCardType();
  cout << F("sdSpecVer: ") << 0.01 * scr.sdSpecVer() << endl;
  cout << F("HighSpeedMode: ");
  if (scr.sdSpecVer() > 101 && sd.card()->cardCMD6(0X00FFFFFF, cmd6Data) &&
      (2 & cmd6Data[13])) {
    cout << F("true\n");
  } else {
    cout << F("false\n");
  }
  cidDmp();
  csdDmp();
  cout << F("\nOCR: ") << uppercase << showbase;
  cout << hex << ocr << dec << endl;
  if (!mbrDmp()) {
    return;
  }
  if (!sd.volumeBegin()) {
    cout << F("\nvolumeBegin failed. Is the card formatted?\n");
    errorPrint();
    return;
  }
  dmpVol();
}
greiman commented 1 month ago

Did you edit SdFatConfig.h and modify SPI_DRIVER_SELECT here?

The error SD_CARD_ERROR_INVALID_CARD_CONFIG happens when SPI_DRIVER_SELECT is >= 2 and no spi pointer is provided in SdSpiConfig().

Here is where the error occurs.

JacobDel commented 1 month ago

Thank you Greiman for the quick response.

I did not modify SPI_DRIVER_SELECT in SdFatConfig.h. Something I forgot to mention is the library version, 2.2.3., which should be the latest as I pulled it straight from git.

The error SD_CARD_ERROR_INVALID_CARD_CONFIG happens when SPI_DRIVER_SELECT is >= 2 and no spi pointer is provided in SdSpiConfig().

The error is not triggered in SdSpiCard.cpp. The error comes from here instead.

Proof

Changes made to the code:

  /** \return SD card error code. */
  uint8_t sdErrorCode() {
    if (m_card) {
      return m_card->errorCode();
    }
    return 123; //SD_CARD_ERROR_INVALID_CARD_CONFIG;
  }

Serial output with the format32 card:

SdFat version: 2.2.3

Assuming the SD is the only SPI device.
Edit DISABLE_CS_PIN to disable an SPI device.

Assuming the SD chip select pin is: 4
Edit SD_CS_PIN to change the SD chip select pin.

type any character to start
init time: 488 ms

Card type: SDXC
sdSpecVer: 3.00
HighSpeedMode: true

Manufacturer ID: 0X74
OEM ID: JE
Product: USDU1
Revision: 0.2
Serial number: 0XC8105DF6
Manufacturing date: 9/2014
CID HEX: 744A45555344553102C8105DF600E955

cardSize: 63383.27 MB (MB = 1,000,000 bytes)
flashEraseSize: 128 blocks
eraseSingleBlock: true
dataAfterErase: zeros
CSD HEX: 400E00325B590001D83D7F800A4000A5

OCR: 0XC0FF8000

SD Partition Table
part,boot,bgnCHS[3],type,endCHS[3],start,length
1,0X0,0XA,0X9,0X2,0XC,0XFE,0XFF,0XFF,32768,123762688
2,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0,0
3,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0,0
4,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0,0

Scanning FAT, please wait.

Volume is FAT32
sectorsPerCluster: 64
fatStartSector:    32800
dataStartSector:   63010
clusterCount:      1933319
freeClusterCount:  885415

Serial output with the exFat card:

type any character to start

SD initialization failed.
Do not reformat the card!
Is the card correctly inserted?
Is there a wiring/soldering problem?
Is SD_CS_PIN set to the correct value?
Does another SPI device need to be disabled?
SD errorCode: SD_CARD_ERROR_UNKNOWN = 0X7B
SD errorData = 0X0
greiman commented 1 month ago

There is a bug for the error code when SdFs fails.

Please change this function to:

  SdCard* newCard(SdSpiConfig config) {
    m_spiCard.begin(config);
     return &m_spiCard;
  }

Rerun the Sdinfo example and post the error code.

greiman commented 1 month ago

The above had the wrong function. I edited it and hope it is correct.

JacobDel commented 1 month ago

Serial output after proposed modifications:

SdFat version: 2.2.3

Assuming the SD is the only SPI device.
Edit DISABLE_CS_PIN to disable an SPI device.

Assuming the SD chip select pin is: 4
Edit SD_CS_PIN to change the SD chip select pin.

type any character to start

SD initialization failed.
Do not reformat the card!
Is the card correctly inserted?
Is there a wiring/soldering problem?
Is SD_CS_PIN set to the correct value?
Does another SPI device need to be disabled?
SD errorCode: SD_CARD_ERROR_ACMD41 = 0x17
SD errorData = 0x1

type any character to start
greiman commented 1 month ago

The failure is here. This is the most common failure for SD sockets with marginal level shifters or marginal wiring.

CMD8 may have failed first which causes miss identification of the card. At this point I would use a microSD sniffer and a scope to checkout the SPI signals.

I used this setup with an old nano and a proven SD breakout with level shifters. The photo is at an angle but CS is pin 4 and SCK is pin32.

nano

I tested with these exFAT cards:

cards

All worked. Here is typical output:

SdFat version: 2.2.3

Assuming the SD is the only SPI device. Edit DISABLE_CS_PIN to disable an SPI device.

Assuming the SD chip select pin is: 4 Edit SD_CS_PIN to change the SD chip select pin.

type any character to start

init time: 132 ms

Card type: SDXC sdSpecVer: 6.00 HighSpeedMode: true

Manufacturer ID: 0X1B OEM ID: SM Product: FD4Q5 Revision: 3.0 Serial number: 0X9287551E Manufacturing date: 9/2021 CID HEX: 1B534D4644345135309287551EA159BD

cardSize: 128177.93 MB (MB = 1,000,000 bytes) flashEraseSize: 128 blocks eraseSingleBlock: true dataAfterErase: zeros CSD HEX: 400E0032DB790003BAFF7F800A40002D

OCR: 0XC0FF8000

SD Partition Table part,boot,bgnCHS[3],type,endCHS[3],start,length 1,0X0,0XA,0X9,0X2,0X7,0XFE,0XFF,0XFF,32768,250314752 2,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0,0 3,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0,0 4,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0X0,0,0

Scanning FAT, please wait.

Volume is exFAT sectorsPerCluster: 256 fatStartSector: 49152 dataStartSector: 65536 clusterCount: 977664 freeClusterCount: 977619

I can't recommend any other things to try since the failure is long before reading the card. Can't be the format and ACMD41 happens at low clock speed.

I will post a fix so the correct error code is returned for SdFs.

JacobDel commented 1 month ago

Thanks! I'll close the issue until I can test this with other sd card adapters/sniffers.