Suggestion: re-write SDFileSystem

03 Jun 2014

Bold suggestion here: scrap the existing SDFileSystem library, and re-write it to handle things like card removal and errors properly. I've spent all morning trying to come up with a way of making it handle card removal and reinsertion, and I've had no success. I even tried calling sd.disk_initialize() twice several seconds after reinserting the card as suggested here, with no success. I should point out that I removed and reinserted the card before accessing it, so file pointers are not the issue here. I can't imagine a use case outside of general hobbying in which expecting an SD card to be present from power up to power down with no chance of it being removed would be desirable. Equally as bad is the fact that it currently spits out errors using debug(...) rather than error(...), making it useless on a production device, or even a platform that doesn't support stdio messages. I don't mean to sound overly critical, but I feel like this library is of beta quality at best compared to other official libraries like USBDevice, mbed-rtos, and let's not forget the excellent mbed library.

03 Jun 2014

An additional comment, throughout the documentation of SDFileSystem.cpp, there are comments like

/*
  *snip*
 * You should also indicate whether the host supports High Capicity cards,
 * and check whether the card is high capacity - i'll also ignore this
  *snip*
 */

Furthermore, boards like the FRDM-K64F have built-in SD host controllers that could greatly improve performance if appropriately targeted. The SD controller on the FRDM-K64F supports up to 200 Mbps SD/SDIO cards using 4 data lines at 50MHz in conjunction with "a fully configurable 128x32-bit FIFO for read/write data" and more.

It would be quite an undertaking, however. . .

11 Jun 2014

This post suggests that more people besides me are having issues with the state of this library. Clearly something needs to be done. From a hobbyist's perspective I'm sure this library is great. If a card doesn't work, keep trying alternatives until you find one that does. If you want to pull it out, shut off your device first. If you want to put it in and your device is already running tough, shut it off first. But from a production standpoint this is unacceptable. Consumers expect that all things with memory card slots are created equal and will work with all things that can be shoved into said slots no questions asked. I can't imagine the support calls I'd get if I used SDFileSystem in it's current state in a production device...

12 Jun 2014

I definately agree the SDFileSystem lib can use a much better error handling instead of what it currently uses. I don't know if that requires a complete rewrite.

However regarding the post you linked there, I don't know if the SDFileSystem lib is to blame. I have seen similar posts for pretty much every microcontroller platform. Which makes me wonder if the platforms all have issues, or if it is that not all SD cards have a proper SPI protocol built in, since virtually all consumer equipment uses the real SD interface instead of SPI.

12 Jun 2014

Erik - wrote:

I definately agree the SDFileSystem lib can use a much better error handling instead of what it currently uses. I don't know if that requires a complete rewrite.

However regarding the post you linked there, I don't know if the SDFileSystem lib is to blame. I have seen similar posts for pretty much every microcontroller platform. Which makes me wonder if the platforms all have issues, or if it is that not all SD cards have a proper SPI protocol built in, since virtually all consumer equipment uses the real SD interface instead of SPI.

That's a possibility, but like Thomas pointed out there are several areas in SDFileSystem that make assumptions and don't follow spec for the sake of simplicity. I would be more inclined to blame an incomplete driver before I blamed SD cards in general.

13 Feb 2015

Ok guys, after about 4 days of solid effort I've successfully written my own SDFileSystem library! I started from scratch, and used ChaN's MMC/SDC writeup, and this Microchip thread as starting points. I then searched around until I found this page, and studied the SD Simplified Specification v3.01 to fill in the gaps. The result is (what I believe to be) a fully functional driver, complete with CRC, that handles card removal and replacement without breaking, and works at high clock speeds (breakout board permitting, more on that later)!

Import librarySDFileSystem

A re-written SDFileSystem library with improved compatibility, CRC support, and card removal/replacement support.

So far I've tested it with two high capacity SDCv2 cards, and one standard capacity SDCv2 card. At the moment all I have to test with is a microSD breakout board, so I haven't been able to test any SDCv1 or SDXC cards yet, but I'm planning on acquiring a full size breakout board and testing those in the coming weeks. (If anybody wants to help me by testing as many cards as possible I'd greatly appreciate it!)

Getting Started

Using my library is almost the same as using the official SDFileSystem, but with a few exceptions:

  1. The socket's Card Detect switch must be wired up and specified in the constructor. The library will attach an InterruptIn to this pin so that it can detect card removal events and update the disk status appropriately. At the moment, only sockets with normally closed switches are supported (AKA the CD pin shorts to GND when no card is present). I'm working on that... The switch type is specified in the constructor as an optional argument, and defaults to normally open (AKA the CD pin shorts to GND when a card is present).
  2. You will need to pull MISO up to VDD with a 50-100kΩ resistor. This is standard practice for MMC/SDC since DO and DI idle high, and without it the busy wait logic won't work. You should also pull up MOSI on microcontrollers that don't reset with internal pullups enabled like LPC's do.
  3. The desired SPI clock frequency can be specified in the constructor as an optional argument. This is set to 1MHz by default, but you can set it as high as 20MHz for MMC cards, or 25MHz for SD cards. The library will automatically limit the frequency based on the card type, so feel free to go crazy. Note that some breakout boards use 5V level shifters to support Arduinos that might limit your maximum clock frequency. For example, my Adafruit microSD breakout board+ uses a lousy CD4050BM that stops working above 6MHz. The card slot on my Adafruit 128x128 color OLED display however, uses a much faster HC4050M that works up to 16MHz, so your mileage may vary.

I've written a simple test program that displays the card type and capacity, and performs a simple read/write test every time you push a button. Feel free to remove, reinsert, swap, replace, or otherwise tamper with the SD card in the slot; the library can handle it without locking up and requiring a reset. It will also format the card if you uncomment that part, but be warned: FATFileSystem's format method is very slow.

Import programSDFileSystem_HelloWorld

A simple serial test program for the re-written SDFileSystem library.

Ultimately, I'd like to see the official SDFileSystem library replaced with this code, but I'm not sure everyone will like the idea of having to use the Card Detect switch so I'm not going to submit a pull request. Yet... In the meantime however, anyone who's looking for code that actually works worth a darn that's production-friendly, look no further.

30 Jul 2014

First of all, very nice work. Only scrolled through the code, but it looks good. As you say however it would be nice if it could be compatible with normal SDFileSystem. Some random stuff, some which might be a good idea, others less good:

Shouldn't it be fairly simple to make the CardDetect interrupt optional (if NC is supplied)? Sure then you cannot change cards anymore, but everything else should still work fine.

Possibly then an 'eject' function to manually set the lib back to unitialized?

Or option B: If a time out happens during contact with the card set it back to unitialized (no idea if that has bad consequences).

A few of your numbers in code could be replaced by defines/enums. (For example your 500ms wait). Can make it a bit clearer.

Pull ups on MISO might be quite irritating, especially on standard shields which don't have them. Just checked seeedstudio SD shield, doesn't have them (and even has buffers in between, so you can't add them without soldering them directly on it). Same story for some of the recent TFTs with SD card holder, also no pull ups, and buffers. Maybe internal pull ups can be activated for those without buffer, but to make it official lib (which would be nice, since it looks alot better), it should also work without pull ups.

Finally something I wonder about, getting any troubles with the FATFileSystem buffer? I got the USBFileSystem lib, which writes to a shared medium with both FATFileSystem and USBMSD. It is made sure that they can't write at the same time. However still I ran in some situations into issues where FATFileSystem 'caches' directory sectors. Which it didn't re-read. And I don't see something in your code which tells it to reset itself (also not aware of possibilities to do it). I might try later if I can reproduce it with your lib if you haven't already taken that into account and I just haven't seen it.

I believe I solved it by setting FS_TINY in the FAT config file to 1.

30 Jul 2014

Erik - wrote:

First of all, very nice work. Only scrolled through the code, but it looks good.

Hey, thanks Erik! :)

Erik - wrote:

Shouldn't it be fairly simple to make the CardDetect interrupt optional (if NC is supplied)? Sure then you cannot change cards anymore, but everything else should still work fine.

Not exactly, as trying to create an InterruptIn using NC currently throws a nasty assertion error:

mbed assertation failed: pin != (PinName)NC, file: C:\work\mbed\github\libraries\mbed\targets\hal\TARGET_NXP\TARGET_LPC11UXX\pinmap.c, line 38

The only way around this, that I can see, would be to conditionally create an InterruptIn on the heap, and I try to avoid using the heap if at all possible. If somebody were to patch InterruptIn to gracefully ignore NC's this could work though. In fact, I've been having a lot of trouble with NC's in general since around release 85. Constructors that previously worked just fine with NC's and/or are encouraged to be used with NC's like RawSerial now hang. :/

Erik - wrote:

Possibly then an 'eject' function to manually set the lib back to unitialized?

Very possible, but you would have to use FatFs' f_mount() function to reset the file system object (which is owned by FATFileSystem). I might add a method to FATFileSystem for this and submit a pull request later. I've submit a pull request over at GitHub for this, more details below.

Erik - wrote:

A few of your numbers in code could be replaced by defines/enums. (For example your 500ms wait). Can make it a bit clearer.

Clarity is actually why I don't use defines for these numbers, I hate having to go look for them to see what their values are. Much better to use the actual number and comment on what's going on, in my opinion anyway. That is, unless such value is a tuning value that's used in many places throughout the code, in which case I would make that a private constant. Defines are just so... C... ;)

Erik - wrote:

Pull ups on MISO might be quite irritating, especially on standard shields which don't have them. Just checked seeedstudio SD shield, doesn't have them (and even has buffers in between, so you can't add them without soldering them directly on it). Same story for some of the recent TFTs with SD card holder, also no pull ups, and buffers. Maybe internal pull ups can be activated for those without buffer, but to make it official lib (which would be nice, since it looks alot better), it should also work without pull ups.

I hear you, but unfortunately the pullups are considered SD/MMC spec according to ChaN's SD/MMC writeup. Strictly speaking, only the DO pullup is required for correct operation. Without it, the card will continually fail the following busy check:

inline bool SDFileSystem::waitReady(int timeout)
{
    //Wait for the specified timeout for the card to become ready
    for (int i = 0; i < timeout; i++) {
        if (m_SPI.write(0xFF) == 0xFF)
            return true;
        wait_ms(1);
    }

    //We timed out
    return false;
}

According to ChaN's SD/MMC writeup, if the card is busy it will drive DO low as soon as it's selected, and release it when it's ready. It would have been fortunate if the card actually drove the pin high when ready, but that does not seem to be the case. I'm convinced that the busy check is the reason my library works at high clock frequencies where others fail, and ideally these breakout boards should be adhering to spec and proving pullups. The internal pullups would be perfect for this (especially to support shields), but that would require some patching to the mbed library. I'm not sure how the level shifters complicate this, since my Adafruit breakout board has one and I just added a 100kΩ pullup to 3.3V at the mbed pin. Although if the level shifter was connected to 5V I suppose DO could backfeed the 3.3V rail through the resistor... In this case, you would need to pull up to 5V.

Erik - wrote:

Finally something I wonder about, getting any troubles with the FATFileSystem buffer? I got the USBFileSystem lib, which writes to a shared medium with both FATFileSystem and USBMSD. It is made sure that they can't write at the same time. However still I ran in some situations into issues where FATFileSystem 'caches' directory sectors. Which it didn't re-read. And I don't see something in your code which tells it to reset itself (also not aware of possibilities to do it). I might try later if I can reproduce it with your lib if you haven't already taken that into account and I just haven't seen it.

I believe I solved it by setting FS_TINY in the FAT config file to 1.

Unfortunately, I don't think FatFs exposes it's buffers for public tampering... There's an f_sync() function that can be used to flush any write caches (FATFileSystem makes sure this calls disk_sync()), but nothing that I can see for dealing with the read cache. Explicitly unmounting the filesystem might fix this, see below for more details. At any rate, this would be a FATFileSystem limitation, and not an SDFileSystem limitation.

30 Jul 2014

I just submit a pull request over at GitHub that adds virtual mount() and unmount() methods to FATFileSystem. Once accepted, I'll override unmount() in SDFileSystem like so:

int SDFileSystem::unmount()
{
    //Unmount the filesystem
    FATFileSystem::unmount();

    //Change the status to not initialized, and the card type to none
    m_Status |= STA_NOINIT;
    m_CardType = CARD_NONE;

    //Always succeeds
    return 0;
}


That will allow people to explicitly mount and unmount cards between insertion and removal, thereby insuring filesystem integrity. It will also allow the library to work without the Card Detect switch, provided InterruptIn gets fixed... It may also fix Erik's read cache issue, but I didn't test for that. Accessing cards would now look like this:

main.cpp

#include "mbed.h"
#include "SDFileSystem.h"

DigitalIn button(p21, PullUp);
SDFileSystem sd(p5, p6, p7, p20, "sd", p22, SDFileSystem::SWITCH_NC, 6000000);

int main()
{
    while(1) {
        //Print the start message
        printf("\nPress the button to perform tests: ");

        //Wait for the button to be pressed
        while(button);

        //Display the card type and capacity
        printf("\nCard type: ");
        if (sd.card_type() == SDFileSystem::CARD_NONE)
            printf("None\n");
        else if (sd.card_type() == SDFileSystem::CARD_MMC)
            printf("MMC\n");
        else if (sd.card_type() == SDFileSystem::CARD_SD)
            printf("SD\n");
        else if (sd.card_type() == SDFileSystem::CARD_SDHC)
            printf("SDHC\n");
        else
            printf("Unknown\n");
        printf("Sectors: %llu\n", sd.disk_sectors());
        printf("Capacity: %.1fMB\n", (sd.disk_sectors() * 512) / 1048576.0);

        //Mount the SD card
        sd.mount();

        //Perform a write test
        printf("Writing to card...");
        FILE *fp = fopen("/sd/sdtest.txt", "w");
        if (fp != NULL) {
            fprintf(fp, "We're writing to an SD card!");
            fclose(fp);
            printf("success!\n");
        } else {
            printf("failed!\n");
        }

        //Perform a read test
        printf("Reading from card...");
        fp = fopen("/sd/sdtest.txt", "r");
        if (fp != NULL) {
            char c = fgetc(fp);
            if (c == 'W')
                printf("success!\n");
            else
                printf("incorrect char (%c)!\n", c);
            fclose(fp);
        } else {
            printf("failed!\n");
        }

        //Unmount the SD card
        sd.unmount();

        //Delay for 0.2 seconds for simple debouncing
        wait(0.2);
    }
}

Note the re-arranged constructor arguments. If InterruptIn ever gets fixed and CD can be set to NC, the new arguments can all be made optional, and the new library will be a drop-in replacement for the old one.

01 Aug 2014

After some performance experiments, I've discovered that switching to 16-bit frames during read/write operations yields a small performance boost. We're only talking a few μs here, and the improvement diminishes at higher clock speeds (I tested as high as 16MHz), but every little bit helps! I'm not going to merge this into the main code since that would break compatibility with the KL25Z, but here's the modified routines if anybody wants to use them:

int SDFileSystem::disk_write(const uint8_t* buffer, uint64_t sector)
{
    //Make sure the device is initialized before proceeding
    if (m_Status & STA_NOINIT)
        return RES_NOTRDY;

    //Make sure the device isn't write protected before proceeding
    if (m_Status & STA_PROTECT)
        return RES_WRPRT;

    //Convert from LBA to a byte address for older cards
    if (m_CardType != CARD_SDHC)
        sector *= 512;

    //Try to write the block up to 3 times
    for (int i = 0; i < 3; i++) {
        //Send CMD24(sector) to write a single block
        if (writeCommand(CMD24, sector) == 0x00) {
            //Wait for up to 500ms for the card to become ready
            if (!waitReady(500)) {
                //We timed out, deselect and loop again
                deselect();
                continue;
            }

            //Send the write data token
            m_SPI.write(0xFE);

            //Switch to 16-bit frames for performance
            m_SPI.format(16, 0);

            //Write the data block from the buffer
            for (int b = 0; b < 512; b += 2) {
                m_SPI.write((buffer[b] << 8) | buffer[b + 1]);
            }

            //Calculate the CRC16 checksum for the data block and send it
            m_SPI.write(CRC16((char*)buffer, 512));

            //Switch back to 8-bit frames
            m_SPI.format(8, 0);

            //Receive the data response, and deselect the card
            char resp = m_SPI.write(0xFF) & 0x1F;
            deselect();

            //Check the response
            if (resp == 0x05)
                return RES_OK;
            else if (resp == 0x0D)
                return RES_ERROR;
        } else {
            //The command failed
            return RES_ERROR;
        }
    }

    //The operation either timed out 3 times, failed the CRC check 3 times, or experienced a write error
    return RES_ERROR;
}


bool SDFileSystem::readData(char* buffer, int length)
{
    char token;

    //Wait for up to 200ms for the DataStart token to arrive
    for (int i = 0; i < 200; i++) {
        token = m_SPI.write(0xFF);
        if (token != 0xFF)
            break;
        wait_ms(1);
    }

    //Make sure the token is valid
    if (token != 0xFE)
        return false;

    //Switch to 16-bit frames for performance
    m_SPI.format(16, 0);

    //Read the data into the buffer
    unsigned short dataWord;
    for (int i = 0; i < length; i += 2) {
        dataWord = m_SPI.write(0xFFFF);
        buffer[i] = dataWord >> 8;
        buffer[i + 1] = dataWord;
    }

    //Read the CRC16 checksum for the data block
    dataWord = m_SPI.write(0xFFFF);

    //Switch back to 8-bit frames, and deselect the card
    m_SPI.format(8, 0);
    deselect();

    //Indicate whether the CRC16 checksum was valid or not
    if (dataWord == CRC16(buffer, length))
        return true;
    else
        return false;
}

P.S. If you're serious about improving performance, removing the CRC code will also help!

UPDATE: I just added the ability to enable/disable CRC, and enable/disable 16-bit frames using crc(bool), and large_frames(bool) respectively!

07 Aug 2014

Just tested my library against a Canon 32MB SDCv1 card, and it worked perfectly! Tomorrow I'm going to try a 64GB SDXC card, which should be functionally identical to a regular SDHC card. I haven't been able to track down any MMC cards, so if anyone wants to test one of those I'd be really grateful!

UPDATE: Just tested a 64GB SDXC card (formatted as FAT32 of course) and it worked perfectly!

15 Aug 2014

The latest version of my SDFileSystem library includes major performance improvements thanks to support for multiple block reads/writes! To do this I had to modify FATFileSystem in order to pass the count argument to the child class, which I've submit a pull request over at GitHub for. I should point out that single block write performance will be a bit slower than previous versions due to the library using CMD13 to verify that the write was successful, which is recommended by the SD specifications. This involves waiting for the programming to complete, where the previous versions would get out early. Multiple block write performance is extremely fast however, often 2 to 3 times faster than multiple block reads due to (apparent) on-chip buffering! I've updated my test program to do a simple performance test. Setting the buffer size above 512B will cause the card to use multiple block reads/writes. Additionally, picking numbers that are multiples of 512B will ensure sector alignment, and yield the fastest performance (assuming the card is formatted properly).

P.S. I also slipped mount/unmount support in since apparently FatFs will occasionally break on card changes if the filesystem isn't explicitly re-mounted. The test program demonstrates how to use this properly.

P.P.S. Once again, I'd be grateful if anyone encountering problems with certain cards would let me know. This update took two full days to bug fix! So far it's been tested against two standard SD cards, three SDHC cards, and one SDXC card on four different microcontrollers.

15 Aug 2014

@Neil, thanks for all the information provided plus the update!

17 Aug 2014

@Neil, your updated https://mbed.org/users/neilt6/code/SDFileSystem/ is impressive and a much needed refresh to mbed's SD card handling. You have taken this so far into a great direction!

After all your fantastic work, I believe one of the few remaining bottlenecks is mbed's default SPI handling. It doesn't achieve 25mhz speeds especially with 512 bytes of data, although your 16-bit trick is an innovative start. Besides the frequency maxing out at around 12mhz on the stock LPC1768 (to be verified), the other bottleneck is the space between the 8 or16-bit writes.

Now, I don't know how much farther you want to take this, but with DMA access and the SPI (SSP) peripheral, you could feasibly get the 512 bytes in/out in the theoretical limit of 25mhz with uninterrupted 512 byte bursts! This would more than double the current speed. The other bonus is that this is done in hardware, meaning another potential feature could be to optionally return code execution to the end user to do other things during the SPI burst (and even during the "wait" for the SD card to complete its operations). The library could call a user supplied function (like an event) to signal that new data is ready (OK now I'm really dreaming).

Andy Kirkham (the unofficial mbed "DMA king") has achieved 25mhz SPI bursting with flash memory, as discussed here and demonstrated here. Although it wasn't an SD card, the SPI mechanics used are the same I believe.

Of course, this might not be easy to implement in all the growing number of mbed microcontrollers, but it would be relatively doable on at least the LPC1768 mbed. I wish my C skills were good enough to do this myself, but sadly that is not the case.

Anyway, just putting it out there... perhaps these suggestions aren't fit for the "official library" but they would be very sweet for the @Neil super version :)

What do you say? ;)

17 Aug 2014

Not all mbeds can handle 16-bit SPI though, and not all have DMA in the first place. Also DMA is nice for non-blocking, but in blocking operation you can make it read/write just as fast as with DMA. The issue is the way the mbed lib works, where any SPI command is both read and write. Which makes perfect sense, but is inherently slower.

Btw if you want it with DMA, it should be fairly straightforward to combine this version of SDFileSystem with this one: http://mbed.org/users/Sissors/code/SDFileSystem-RTOS/. Then the write blocks are done using DMA. But only few targets supported atm. (I am slowly doing the F401 DMA, but then it would still need to also do the SPI part).

17 Aug 2014

Hey, thanks for the suggestions Robbie! Unfortunately, DMA is something I'm not sure I want to touch due to the fragmentation between platforms. Now, if the mbed library were to offer an official DMA API on the other hand (hint hint)... As a related side note, I researched FatFs implementations using DMA at one point, and found that on some platforms it can actually be slower to use DMA than not, the benefit being that it happens asynchronously. Which brings me to my next point: your asynchronous idea is great in theory, but in practice you would need to implement it at the FatFs layer. Technically, you could use interrupt transfers on non-DMA platforms, but it would still require a port for every single platform, and I would prefer to keep the library platform independent if possible.

Erik, a few weeks ago I spun off a derivative of my library using LPC1549-specific SPI code. I used the 32-bit Transmit Data and Control register in order to do one-way transmissions, control the CS pin, and change the frame size at will. After all that, I only achieved a measly 8% improvement. :( So it would seem that the mbed SPI API for the LPC1549 at least is fairly efficient.

Now for some good news: I'm currently beta testing a new revision that offers improved read performance thanks to Timer-based busy wait logic. This should also improve writes somewhat since FAT access will be faster. In my initial tests on the LPC1768, I'm getting 470KB/s on a Verbatim 2GB standard capacity card using 4KB reads at 6MHz, whereas the previous revision was hard pressed to achieve 200KB/s at 25MHz. I also added a way to disable CMD13, which will improve single block write performance at the expense of programming verification. I'm hoping to release the update tomorrow after I test it against some more cards.

19 Aug 2014

Good news guys! My performance-boosting count argument pull request over at GitHub got accepted! The FATFileSystem library hasn't been updated here yet, but as soon as it is I can switch back to it in my SDFileSystem library.

19 Aug 2014

@Neil that is excellent news.

And thanks to you as well as @Erik for your excellent replies.

I am still following this closely... :)

21 Oct 2014

Hello Neil, Thank you for your code. I try to run on the FRDM-K64F. I made the following changes in main.cpp:

// Test for FRDM-K64F
Serial pc(USBTX, USBRX);
#define printf pc.printf
DigitalIn button(SW2, PullUp);
SDFileSystem sd(PTE3, PTE1, PTE2, PTE4, "sd", PTE6, SDFileSystem::SWITCH_POS_NO, 25000000);

and:

int main()
{
    // Configure baud rate
    pc.baud(115200);
    //Configure CRC, small frames, and write validation
    sd.crc(true);
    sd.large_frames(false);     // SPI 16 bits not supported ???

I do not know why the SPI does not work in 16bit Mode, but in 8-bit mode, I get 280KB / s write and 415KB / s read, using this card http://www.samsung.com/fr/consumer/it/memory-card/sd-card/MB-SP16D/EU?subsubtype=evo-sd-card

In ff.cpp file, line 1689, the compiler tells me "unreachable code" ( w = (w << 8) + b; /* Create a DBC */)

thank you again for your code

Jean-Louis

21 Oct 2014

Hi Jean-Louis, good to hear. I don't think the K64F supports 16-bit frames unfortunately, but those are fantastic performance figures! The warning from ff.cpp can be safely ignored, it's a minor flaw within FatFs.

05 Jan 2015

The latest revision allows SDFileSystem to be used without the card detect switch thanks to a pull request from Joris Aerts. This allows it to be used as a drop-in replacement for the official library. However, bare in mind that card changes will not be handled properly unless SDFileSystem::mount() and SDFileSystem::unmount() are explicitly called before and after filesystem access. I've updated the embedded example to demonstrate this.

15 Jan 2015

Hi Neil and thanks for your great work! Just FYI, I've had no problems with a 2GB Kingston SD card while I had some with a Lexar 1GB SD card. The odd thing is that the standard library works well with both and, to make your library work with the Lexar card, I first had to read it using the standard library.

With the 2GB card I had no problems when unplugging and replugging "live" (I followed your indications of mounting and remounting after each operation) but, under the same conditions, unplugging and replugging the 1GB card brought the read and write to failure.

Any clue about it?

Thanks, Andrea

15 Jan 2015

Hi Andrea. I've heard of this happening in rare cases, but I've never been able to reproduce it myself. First of all, make sure your breadboard wires are as short and neat as possible, and make sure you have a 10μF capacitor near the breakout board. If everything is neat and tidy, but it still doesn't work, try picking a slower clock speed in the constructor. The official library runs at 1MHz, but mine allows you to run as high as 25MHz. Trouble is, most SD card breakout boards have 5V level shifters built in for Arduinos that can't handle 25MHz.

If you've ruled out any hardware problems, and it still doesn't work, try changing the initialization frequency to 100kHz on line 154 of SDFileSystem.cpp:

//Set the SPI frequency to 100kHz for initialization
m_Spi.frequency(100000);


If that doesn't work, try throwing in some extra dummy clocks on line 158:

//Send 128 dummy clocks with /CS deasserted and DI held high
m_Cs = 1;
for (int i = 0; i < 16; i++)
    m_Spi.write(0xFF);


If it still doesn't work, you'll need to put printf() statements at each failure point in SDFileSystem::disk_initialize() to try to figure out where it's failing. Once you've figured out which command is the culprit, let me know and I'll look into it.

10 Feb 2015

Hi Neil, this not an actual update...just want to say that, since my project schedule left me with no time for more trials, I had to switch to the standard SDFileSystem library that eventually worked, by paying attention to a couple of pitfalls.

Anyway before switching I went through all the suggestions you gave but I couldn't find where the issue is; I had to stop while printf'ing the several steps.

At present moment I have no hardware to test it but I'll come back to it as soon as I have another SD board.

Thanks again for all your help! Andrea

12 Feb 2015

Hi Andrea, I'm sorry to hear that. I wish I could reproduce the problem, but unfortunately I've yet to encounter it on any of my test boards. If I ever figure it out I'll be sure to patch the library and post about it here.

17 Feb 2015

Hi Neil,
SWITCH_POS_NO or SWITCH_POS_NC will not work when used with a KL05Z or KL25Z as these do not support interrupt mode.pulldown. Perhaps adding a more verbose message would be helpful (certainly for newbies)?

    //Configure the card detect pin
    if (cdtype == SWITCH_POS_NO) {
    #if defined (TARGET_KL25Z) || (TARGET_KL05Z)
        #error  SWITCH_POS_NO (Uses Interruptin PullDown) is not supported on KL05Z and KL25Z
    #endif
        m_Cd.mode(PullDown);
        m_CdAssert = 1;
        m_Cd.fall(this, &SDFileSystem::onCardRemoval);
    } else if (cdtype == SWITCH_POS_NC) {
        #if defined (TARGET_KL25Z) || (TARGET_KL05Z)
            #error  SWITCH_POS_NC (Uses Interruptin PullDown) is not supported on KL05Z and KL25Z
        #endif
        .....
18 Feb 2015

Hi Frank. This is more of an mbed library issue rather than an issue with my library. If I put compiler warnings for every target that doesn't support pull-down (or pull-up) resistors, I would have to update it every time a new target is added. Ideally the mbed library should be issuing errors if an unsupported mode is used.

18 Feb 2015

Agreed, but the problem remains that the compiler throws an error ("PullDown" is not defined) when compiled using the KL05Z or KL25Z platform. Older mbed libs allowed the use of (a non-functional) PullDown on KL25Z (eg : mbed-7e6c9f46b3bd) resulting in questions why this did not work.
Perhaps a little writeup at your library page would be helpful (will save you the trouble of being asked why the compiler throws an error):

Note

For mcu's that don't support PullDown - disable the m_Cd.mode(PullDown); lines in
SDFileSystem.cpp and use an external pull-down resistor.
OR
Do not use the CD pin, use mount() - unmount() instead.

But, if you want it to be a drop-in replacement for the old one, isn't it necessary to add guards?
For now, i solved it by adding guards : Throw a warning and disable each PullDown.
Not really the nicest soluiton but it compiles without errors - we do need to pay attention to the warning.

    //Configure the card detect pin
    if (cdtype == SWITCH_POS_NO) {
    #if defined (TARGET_KL25Z) || (TARGET_KL05Z)
        #warning  SWITCH_POS_NO/NC (Calls Interruptin PullDown) is NOT supported on KL05Z and KL25Z
    #endif
    #if !defined (TARGET_KL05Z) && !defined (TARGET_KL25Z)
        m_Cd.mode(PullDown);
    #endif
        m_CdAssert = 1;
        m_Cd.fall(this, &SDFileSystem::onCardRemoval);
    } else if (cdtype == SWITCH_POS_NC) {
    #if !defined (TARGET_KL05Z) && !defined (TARGET_KL25Z)
        m_Cd.mode(PullDown);
    #endif
    ....
25 Feb 2015

to Neil. Thank you for your work! I used your lib and test prog under LPCXpresso1549 with Transcend 32GB SDHC and Elsonic 8GB SDHC. It worked perfectly. I get 230kb/s write, 640kb/s read with Tanscend and 110kb/s write, 610kb/s read. with Elsonic.

I want to use this lib for my project. It is HD wav file recorder and player, like 96kHz and above. LPC1549 has very fast ADC(200Ms/s) , So I think LPC1549 and your lib are sufficient speed for 96kHz wav files. But if I want more speed, how do I hack your code? What is LPC1549-specific SPI code ? Do you have any idea?

25 Feb 2015

Hi Takuya, that sounds like a cool project! I was able to achieve significant performance improvements on the LPC1549 by using DMA to read/write data blocks. It was only a proof of concept, so the code is pretty crude, but if you want I can post a copy of it here.