Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SdFat::begin() improvement suggestion #111

Open
SapientHetero opened this issue Aug 18, 2018 · 15 comments
Open

SdFat::begin() improvement suggestion #111

SapientHetero opened this issue Aug 18, 2018 · 15 comments

Comments

@SapientHetero
Copy link

Over the weekend I got a Class 10 SD card to work with the Metro M4 and Adafruit WINC1500 shield by slowing the SPI clock down to 12 MHz. This week I integrated the Seeed Ethernet 5500 shield with my sketch.

My sketch initialized a 2GB card with the Seeed shield mounted on an Arduino Mega with the SPI clock reduced to 4MHz, but wouldn't initialize the same card in the same shield on a Metro M4 at any clock speed. The Seeed shield advertises that it supports SPI MODE 0, 3.

The sketch works with the same 2GB SD card using an Adafruit WINC1500 shield and Metro M4, with SPI clock set to 12 MHz.

I noticed that SdFat's SdSpiCard::begin initially sets the SPI clock to 250K [m_spiDriver->setSpiSettings(SD_SCK_HZ(250000));] regardless of the clock rate requested. When I removed a zero from this setting, thus making it 25K, my sketch worked. I'm sure there's room for refinement there (it takes a long time to time out if no card is present at this setting), but it allows the Metro M4/Seeed Ethernet shield combination to successfully initialize a card.

I suspect the clock settings used in the library don't translate well to the 120MHz ATSAMD51G19A used on the Metro M4. The speed settings in SdInfo.h translate as follows on the M4:

SPI_DIV3_SPEED = 40,000,000
SPI_HALF_SPEED = 30,000,000
SPI_DIV6_SPEED = 20,000,000
SPI_QUARTER_SPEED = 15,000,000
SPI_EIGHTH_SPEED = 7,500,000
SPI_SIXTEENTH_SPEED = 3,750,000

@greiman
Copy link
Owner

greiman commented Aug 18, 2018

I don't want to slow the clock that much for initialization since the standard specifies that the clock must be in the range 100-400 kHz for initialization.

Issue continuous clock in the frequency range of 100 kHz-400 kHz.

Some SD cards will not initialize with very slow clock. I picked 250 kHz since it is the middle of the range.

I have tried various values over the years and 250 kHz works best.

I can't make the default settings match just some old SD cards and very slow shields.

Many people use much faster processors than your Metro M4. I use Teensy 3.6 at 240 MHz with no problems. With SDIO, I get transfer rates of about 20 MB/sec.

I may put a symbol in SdFatConfig.h for initialization speed. For now you will need to set it to a value that will work for your shield in the source.

Symbols like SPI_DIV3_SPEED , HALF_SPEED originated before SPISettings was added to the SPI library. I defined them for backward compatibility. I have removed them from the examples and will soon remove them from in SdInfo.h.

SPISetting is nice since you can set a SCK speed and it will work for all boards. All modern SD cards support 50 MHz SPI clock. SPISetting at 50MHz chooses the highest speed <= 50MHz that is also supported by the CPU. This will be 8MHz on an Uno, 48 MHz on a Due.

I looked at the Metro M4 SPI driver. Looks like it may have a bug in the baud generator associated with SPISettings. It may be unreliable if you request an SPI rate greater than 24 MHz.

@SapientHetero
Copy link
Author

SapientHetero commented Aug 28, 2018 via email

@SapientHetero
Copy link
Author

SapientHetero commented Aug 28, 2018 via email

@greiman
Copy link
Owner

greiman commented Aug 28, 2018

The most common problems I see in cases like this are:

Multiple devices on the SPI bus and one or more devices is not disabled before initializing a device.

Be sure you have code like this for all devices not being initialize, the WiFi in your case.

  pinMode(CS_WIFI, OUTPUT);
  digitalWrite(CS_WIFI, HIGH);

The second one is to use the wrong CS_PIN for the SD so the correct pin is floating. On some SD shields this sort of works. I suspect this is not your problem.

I don't have much confidence in the Adafruit SPI driver.

In SPI mode the SERCOM BAUD rate register is 8-bits. See the ATSAMD51G19A data sheet Table 33-2. Baud Rate Equations.

The SPI clock, f, must satisfy these equations. F_REF is 48 Mhz for the Metro board, not F_CPU.

f <= F_REF/2 so the max SPI rate is 24 MHz.

f = F_REF/(2*(BAUD + 1)) so the min SPI rate is 93750 Hz for BAUD = 255.

I can't find a test for rates that are too low so who knows what you get.

Here is a buggy line that tests for high frequencies.
this->clockFreq = (clock >= (F_CPU / SPI_MIN_CLOCK_DIVIDER) ? F_CPU / SPI_MIN_CLOCK_DIVIDER : clock);

F_CPU should be SERCOM_FREQ_REF. The max SPI frequency for SERCOM on ATSAMD51G19A is 24 MHz. This tests for 60 MHz since F_CPU is 120 MHz. Who knows what happens at higher frequencies.

I really can't help you when I can't trust the Adafruit driver. If trash is on the SPI bus, a card can lockup in the wrong mode and can only be reset by cycling power.

Edit:
If you select a rate below 100 kHz or above 24 MHz you may have unstable results.

You may have unstable results in other cases since this driver was written for SAM21 not SAM51. Who know what other problems exist. Looks like there are minimal mods for SAM51.

@greiman
Copy link
Owner

greiman commented Aug 28, 2018

I noticed one more thing. Your test is not valid unless you cycle power before each rate test. If a card is initialized, it goes through different initialization steps internally.

A power cycled card may take almost a second to initialize. To reinitialize a card may take only a few milliseconds if power is not cycled.

One more note. The standard SD.h library uses a 10 year old version of SdFat. Here is the code for initialization speed.

  SDCARD_SPI.begin();
  settings = SPISettings(250000, MSBFIRST, SPI_MODE0);
  // ...
  SDCARD_SPI.beginTransaction(settings);

SD card are initialized at 250 kHz for every user of the standard SD.h library.

The actual SPI rate will be 24 Mhz divided by an integer. 24, 12, 8, 6, 4.8, 4.0 ... MHz.

@SapientHetero
Copy link
Author

SapientHetero commented Aug 29, 2018 via email

@greiman
Copy link
Owner

greiman commented Aug 30, 2018

Anything could be happening since who knows what SPI frequency you have been using with the broken SPI driver and trying SPI frequencies that can not be generated by the ATSAMD51G19A .

With the poor level shifters on the SD socket, which distort the SPI signal, I can believe the SDHC test failed at the actual frequency if it is too high.

Also, using a software debugger to breakpoint a driver gives meaningless results since it totally changes timing and timeouts. Use a scope/logic analyzer on the SPI bus.

By the way, a SDHC card is also a type 2 card so that statement should be executed.

If you specify a frequency lower than 100 kHz, the BAUD rate register will be calculated to be greater than 8-bits and the low 8-bits will be loaded into the BAUD rate register so you will get a random result.

If your card won't run at 250 kHz it does not satisfy the SD standard so you need a new card. I have no interest in trying to find some magic SPI rate other than 250 kHz.

I can't help you with this mess.

@greiman
Copy link
Owner

greiman commented Aug 30, 2018

My last post on this subject.

You have a SPI driver and or SD socket/ level shifter problem.

Only old type 1 SD cards actually require the 100 - 400 kHz range. Most Type 2 cards initialize at speed up to 25 MHz.

A modern UHS-I card will initialize at any SPI speed up to 50 MHz.

Here is proof.

I am using a Due and a SD socket with no level shifters connected with short wires to the SPI pins.

I selected a Samsung 32 GB UHS-I SD and formatted it with the SD Association Formatter since the SD Association Formatter produces the correct standard SD format. Linux, Windows, and Mac OSes may not produce the optimum format.

I made this mod to SdFat at line 132 of SdSpiCard.cpp.

  m_spiDriver->begin(csPin);
//  m_spiDriver->setSpiSettings(SD_SCK_HZ(250000));
m_spiDriver->setSpiSettings(SD_SCK_MHZ(50));
  spiStart();

The Due driver will run at 48 MHz when I select 50 MHz.

I ran the SdInfo example. At 48 MHz my card initialized in 1 ms or less after power cycle. I tried the test a number of times and it never failed.

I removed the card, waited 10 seconds, and reinserted it before each test.

Here is the output.

SdFat version: 1.0.7

Assuming the SD is the only SPI device.
Edit DISABLE_CHIP_SELECT to disable another device.

Assuming the SD chip select pin is: 10
Edit SD_CHIP_SELECT to change the SD chip select pin.

type any character to start

init time: 1 ms

Card type: SDHC

Manufacturer ID: 0X1B
OEM ID: SM
Product: 00000
Version: 1.0
Serial number: 0X16309620
Manufacturing date: 6/2014

cardSize: 32009.36 MB (MB = 1,000,000 bytes)
flashEraseSize: 128 blocks
eraseSingleBlock: true
OCR: 0XC0FF8000

SD Partition Table
part,boot,type,start,length
1,0X0,0XC,8192,62510080
2,0X0,0X0,0,0
3,0X0,0X0,0,0
4,0X0,0X0,0,0

Volume is FAT32
blocksPerCluster: 64
clusterCount: 976464
freeClusters: 976461
freeSpace: 31996.67 MB (MB = 1,000,000 bytes)
fatStartBlock: 9318
fatCount: 2
blocksPerFat: 7629
rootDirStart: 2
dataStartBlock: 24576

@SapientHetero
Copy link
Author

SapientHetero commented Sep 1, 2018 via email

@greiman
Copy link
Owner

greiman commented Sep 1, 2018

I have no idea if any of your tests were meaningful. Did you ever run the SdInfo example?

Warning: Don't try to use the SdInfo code in your apps. It is designed to diagnose problems.

To try the SdInfo example. Use a fresh unmodified version of SdFat. Only make the following mods.

Edit the following. For the AdaFruit WINC1500 shield, the default SD chip select pin is 4 and the default WiFi chip select pin is 10.

Change SS to the SD chip select pin on your shield at line 18.
const uint8_t SD_CHIP_SELECT = SS;

Disable the WiFi SPI access. It may interfere since it won't be initialize. Replace the -1 with the WiFi chip select pin in at line 24.
const int8_t DISABLE_CHIP_SELECT = -1;

Limit the SPI rate to 4 MHz max by editing line 194 of the example. Replace the 50 with 4.
if (!sd.cardBegin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) {

This should work on the Metro since it uses the same initialization and operating rates as the old SD.h library. You should not hit the Metro SPI driver bugs.

SD cards are reliable, that's not your problem. Many billions of devices use modern SD cards. I have the code for Samsung smart phones and other devices. These devices all are reliable because the modern SD card and phone hardware/software conforms to the SD standard and are reliable.

SD shields have been a problem with Arduino since I wrote the first libraries over ten years ago. The 5V to 3.3V signal conversion was first implemented with resistor dividers. I put a scope on the SD card pins and the SPI signals were terrible. I sent traces to Limor Fried at Adafruit and she finally redesigned her shield with semiconductor buffers.

This was fine for AVR boards with a max rate of 8MHz. She tried to design the shield so it would work with 3.3V processors like the Due. This worked because the SPI rate was limited to 4 MHz in the old version of SdFat in SD.h

The Due is capable of 48 MHz and the Metro can run at 24 MHz. The rise time of the buffers on shields with an SD socket are too long for these speeds. These shields must run at slow SPI speeds.

Never use an SPI rate over 12 MHz on the WINC1500. 4 MHz would be better. If you need faster rates get better hardware. Software workarounds never fix bad hardware.

I never use buffered SD sockets on 3.3V boards.

@SapientHetero
Copy link
Author

SapientHetero commented Sep 4, 2018 via email

@SapientHetero
Copy link
Author

SapientHetero commented Sep 4, 2018 via email

@greiman
Copy link
Owner

greiman commented Sep 5, 2018

If you use SPI you must disable all devices on the SPI bus other than the first to be initialized.

So disable the SD if you initialize the WiFi/net first. Some SD card may interfere with the WiFi if SD chip select floats low.

@paultanner
Copy link

paultanner commented Mar 18, 2019

I'd like to make a further suggestion that there be a simple way of choosing nonstandard pins for all 4 SPI lines. This is useful where the Arduino is a slave to another subsystem and a master to the SD card.
This was easy enough to do on the old SD library by adding parameters to begin(). Not sure how best to do it on this one.

@greiman
Copy link
Owner

greiman commented Mar 18, 2019

I don't support parameters to begin() since this is very slow using software SPI.

There is a faster template version of software SPI. See this example.

This is the key line.

// SdFat software SPI template
SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd;

You must edit SdFatConfig.h to enable Software SPI and it will increase the size of the hardware version of SdFat since virtual classes will be used.

The SD chips select is defined in the begin call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants