Try SdFat, a Library for SD Cards

I am not sure what you mean by “CR at the end”. Ending lines with only a CR is unusual.

Windows uses CR followed by a LF.

Linux and Unix use a LF.

The old classic Mac OS was one of the few systems to use a single CR for end of line.

Could you please clarify with more details about the CSV files you want to write/read.

Perhaps you could provide some example lines.

1 Like

@whg I’ve been using your library for a bit now and had it working great for a while on 0.4.8 for the electron, but when I upgraded to 0.5.1, I get a hard fault (SOS then one blink) when my code gets to the SdFAT portion. I get this fault even when using the TryMeFirst sketch (CS = D5, SPI_CONFIGURATION == 1). It doesn’t seem to have the issue with SPI_CONFIGURATION ==0 or soft spi though. Any ideas?

I can't reproduce the problem with CS = D5, SPI_CONFIGURATION == 1

I ran TryMeFirst and bench.

I downloaded the 0.5.1 firmware from gitHub and updated a photon:

put the Photon in DFU mode
cd modules
make PLATFORM=photon clean all program-dfu

I edited the configuration for TryMeFirst like this:

// Pick an SPI configuration.
// See SPI configuration section below (comments are for photon).
#define SPI_CONFIGURATION 1   <------------------------------Change from zero to one
//------------------------------------------------------------------------------
// Setup SPI configuration.
#if SPI_CONFIGURATION == 0
// Primary SPI with DMA
// SCK => A3, MISO => A4, MOSI => A5, SS => A2 (default)
SdFat sd;
const uint8_t chipSelect = SS;
#elif SPI_CONFIGURATION == 1
// Secondary SPI with DMA
// SCK => D4, MISO => D3, MOSI => D2, SS => D1
SdFat sd(1);
//const uint8_t chipSelect = D1;
const uint8_t chipSelect = D5;   <-------------------- Use D5
#elif SPI_CONFIGURATION == 2

I did the same with bench. Here is the result:

FreeMemory: 29700
Type is FAT32
Card size: 15.93 GB (GB = 1E9 bytes)

Manufacturer ID: 0X3
OEM ID: SD
Product: SL16G
Version: 8.0
Serial number: 0X91203A25
Manufacturing date: 4/2014

File size 5 MB
Buffer size 32768 bytes
Starting write test, please wait.

write speed and latency
speed,max,min,avg
KB/Sec,usec,usec,usec
1571.21,105882,20108,20822

Starting read test, please wait.

read speed and latency
speed,max,min,avg
KB/Sec,usec,usec,usec
1699.91,20389,19242,19276

Done
Type any character to start

There have been major changes in the SPI driver. I have a bug fix/workaround in SdFat for a bug in 0.4.x.

You could remove it and see if that helps.

Edit SdSpiParticle.cpp and use the following code for the two functions with workarounds.

uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
  spiPtr[m_spiIf]->transfer(0, buf, n, 0);
  return 0;
}

void SdSpi::send(const uint8_t* buf , size_t n) {
  spiPtr[m_spiIf]->transfer(const_cast<uint8_t*>(buf), 0, n, 0);
}

I can confirm that this problem is not solved. I tried firmware V0.5.2 and V0.5.1, both with and without the code you suggested above. But the Electron keeps generating Hard faults. Is there something I can do to help you find the issue? (Except for sending you an Electron) ;). Can it have something to do with pin D5 also being the Slave select pin for the hardware SPI peripheral?

Edit: Nope, Using pin C5 instead of D5 does not solve the problem…

There is no way I can guess what is happening. The SdFat code does not have any code specifically for Electron.

The only places where the code might be different is where the SPI interface count is defined in SdFatConfig.h

#if defined(PLATFORM_ID)
#if Wiring_SPI1 && Wiring_SPI2
#define SPI_INTERFACE_COUNT 3
#elif Wiring_SPI1
#define SPI_INTERFACE_COUNT 2
#endif  // Wiring_SPI1 && Wiring_SPI2
#endif  // defined(PLATFORM_ID)

And where pointers to the SPI classes are defined in SdSpiParticle.cpp

static SPIClass* const spiPtr[] = {
  &SPI
#if Wiring_SPI1
  , &SPI1
#if  Wiring_SPI2
  , &SPI2
#endif  // Wiring_SPI2
#endif  // Wiring_SPI1
};

The Electron has Wiring_SPI2 defined as one. Photon does not.

I ordered an Electron 3G. I always wanted to check out Electron but I couldn’t get a 3G Electron when I was porting SdFat to Particle. Should be here soon.

1 Like

@whg I am trying to run the TryMeFirst example on an Electron with 0.5.2 and keep getting the following error:

Can’t access SD card. Do not reformat.
No card, wrong chip select pin, or SPI problem?
SD errorCode: 0X1,0XFF

I am using a 8GB Kingston micrSD card adapter wired to SCK => D4, MISO => D3, MOSI => D2, SS => D5 (SPI_CONFIGURATON 1)

#define SPI_CONFIGURATION 1

//------------------------------------------------------------------------------
// Setup SPI configuration.
#if SPI_CONFIGURATION == 0
// Primary SPI with DMA
// SCK => A3, MISO => A4, MOSI => A5, SS => A2 (default)
SdFat sd;
const uint8_t chipSelect = SS;
#elif SPI_CONFIGURATION == 1
// Secondary SPI with DMA
//SCK => D4, MISO => D3, MOSI => D2, SS => D1
SdFat sd;
const uint8_t chipSelect = D5;
#elif SPI_CONFIGURATION == 2

Notice that if have SdFat sd; and not sd(1); (in the rest of the code you use sd and not sd(1).

Have tried connecting to A1-A5 (SPI=0) and same error. I have tried with HALF and FULL SPEED and nothing changes. I have tried with CS on D1 and still same problem.

Any ideas?

Thanks much!

See the above. There is some problem with Electron using SPI_CONFIGURATION 1. I will able to investigate after I get access to an Electron.

On SPI0 with SCK => A3, MISO => A4, MOSI => A5, SS => A2 I get the following error

No card, wrong chip select pin, or SPI problem?
SD errorCode: 0X1,0X0 (instead of 0XFF)

I am going to try it with de SDcard on a breakout board.

Means the card does not reply to the first initialization command and MISO is high (the 0XFF).

The case 0X1,0X0 means no reply to the first command and MISO is low.

@whg Thanks for your support!

I finally got it to work using an Adafruit microSD Breakout board with SPI0 but with pin A1 and not A2 for SS.

The problem now is that the Accelerometer (I have an Asset Tracker) uses some of the same pins:

Adafruit_LIS3DH accel = Adafruit_LIS3DH(A2, A5, A4, A3)

and will probably be in conflict with the microSD card.
I tried with SPI1 (on Electron) and sure enough the SDcard does not work. Are you actually working on this fix?

Thanks much!

It appears that DMA transfers don't work for SPI1 on electron.

SPI seems to work but I need to do more tests.

You can try this fix, it disables DMA SPI transfers on Electron. It will be slow.

Replace exiting functions in SdSpiParticle.cpp whith these:

uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
#if PLATFORM_ID == 10 // Electron
  for (size_t i = 0; i < n; i++) {
    buf[i] = spiPtr[m_spiIf]->transfer(0xFF);
  }
  return 0;
#else  // PLATFORM_ID == 10
  SPI_DMA_TransferCompleted = false;
  spiPtr[m_spiIf]->transfer(0, buf, n, SD_SPI_DMA_TransferComplete_Callback);

  while (!SPI_DMA_TransferCompleted) {SysCall::yield();}
  if (bugDelay) {
    delayMicroseconds(bugDelay);
  }
  return 0;
#endif  //  PLATFORM_ID == 10  
}

void SdSpi::send(const uint8_t* buf , size_t n) {
#if PLATFORM_ID == 10 // Electron
  for (size_t i = 0; i < n; i++) {
      spiPtr[m_spiIf]->transfer(buf[i]);
  }
  return;
#else  //  PLATFORM_ID == 10
  SPI_DMA_TransferCompleted = false;
  spiPtr[m_spiIf]->transfer(const_cast<uint8_t*>(buf), 0, n,
                            SD_SPI_DMA_TransferComplete_Callback);

  while (!SPI_DMA_TransferCompleted) {SysCall::yield();}
  if (bugDelay) {
    delayMicroseconds(bugDelay);
  }
#endif  //  PLATFORM_ID == 10
}

That should not be a problem since SPI is meant to share the datalines - only the Slave Select needs to be exclusive.
BTW, does the LIS3DH library use hardware SPI? Then why do they need the pins stated explicitly?

I think the use of Software SPI for the LIS3DH in the AssetTracker library is a bug. Except since the AssetTracker doesn’t really use the LIS3DH accelerometer, no one ever noticed it. I noticed that when I did wake on move with the asset tracker and meant to fix it, but then completely forgot about it until you just mentioned it.

1 Like

@rickkas7, I’ve been using SdFat on an Electron (SPI) very successfully (thanks!). However, my code is designed to store files on the SD and then go to sleep using System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long seconds,SLEEP_NETWORK_STANDBY);

Since this uses an STM32 STOP, the code continues from where it left off after the call and RAM should be maintained. However, the SD does not work. Note that I do turn off power to the SD (via FET) before sleeping and then back on (with delay to allow it to start up). However, not sure that shouldn’t affect anything.

My question is, what’s the best approach for maintaining SD functionality through the sleep?

1 Like

Huh. I never tried that. I’ll give it a try and see, but my guess is that you’ll have to do all of the SPI setup again after waking up from a stop mode sleep.

@rickkas7, is that not done by begin()? I tried that and it did not work. To be fair, I didn’t exactly do any deep debugging yet.

I should have assumed that you already tried calling SPI.begin() again :slight_smile:

1 Like

@rickkas7, I’ll have to look at the constructors to see if any hardware stuff is going on there. I’m also going to try and NOT power off the SD to see if there’s an issue there.

The SD must be reinitialized if you remove power. You must close all files before removing power.

The SD has a 32-bit micro-controller and lots of RAM buffer so you can’t just power it off.

In many applications Just allowing the SD to sleep with power is better. Most microSD cards sleep at about 100 micro-amps. Cards quickly go through several levels of sleep.

@whg, understood. I do close all files before powering off. However, I find that the microSDs I’ve tried seem to sleep at 7ma or so and I don’t know why it isn’t going lower. In my Photon version, I deep sleep and power off the SD but on wake it does a full reset so I have no issues. However, not doing a full reset is the issue here. I’ll do my tests without powering off the SD and see what happens. I’m also going to measure the SD current just to be sure.