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

Dependencies:   FATFileSystem

Dependents:   xadow_m0_SD_Hello roam_v1 roam_v2 Polytech_tours ... more

Files at this revision

API Documentation at this revision

Comitter:
neilt6
Date:
Tue Jul 29 20:12:23 2014 +0000
Child:
1:25f4ba436b81
Commit message:
Initial commit

Changed in this revision

CRC16.cpp Show annotated file Show diff for this revision Revisions of this file
CRC16.h Show annotated file Show diff for this revision Revisions of this file
CRC7.cpp Show annotated file Show diff for this revision Revisions of this file
CRC7.h Show annotated file Show diff for this revision Revisions of this file
FATFileSystem.lib Show annotated file Show diff for this revision Revisions of this file
SDFileSystem.cpp Show annotated file Show diff for this revision Revisions of this file
SDFileSystem.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CRC16.cpp	Tue Jul 29 20:12:23 2014 +0000
@@ -0,0 +1,67 @@
+/* SD/MMC File System Library
+ * Copyright (c) 2014 Neil Thiessen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CRC16.h"
+
+namespace
+{
+const unsigned short m_CRC16Table[256] = {
+    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+    0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+    0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+    0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+    0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+    0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+    0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+    0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+    0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+    0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+    0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+    0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+    0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+    0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+    0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+    0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+    0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+    0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+    0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+    0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+    0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+    0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+    0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+    0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+    0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+    0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+    0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+    0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+    0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+    0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+    0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+    0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+}
+
+unsigned short CRC16(const char* data, int length)
+{
+    //Calculate the CRC16 checksum for the specified data block
+    unsigned short crc = 0;
+    for (int i = 0; i < length; i++) {
+        crc = (crc << 8) ^ m_CRC16Table[((crc >> 8) ^ data[i]) & 0x00FF];
+    }
+
+    //Return the calculated checksum
+    return crc;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CRC16.h	Tue Jul 29 20:12:23 2014 +0000
@@ -0,0 +1,24 @@
+/* SD/MMC File System Library
+ * Copyright (c) 2014 Neil Thiessen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRC16_H
+#define CRC16_H
+
+#include "mbed.h"
+
+unsigned short CRC16(const char* data, int length);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CRC7.cpp	Tue Jul 29 20:12:23 2014 +0000
@@ -0,0 +1,67 @@
+/* SD/MMC File System Library
+ * Copyright (c) 2014 Neil Thiessen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CRC7.h"
+
+namespace
+{
+const char m_CRC7Table[] = {
+    0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F,
+    0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77,
+    0x19, 0x10, 0x0B, 0x02, 0x3D, 0x34, 0x2F, 0x26,
+    0x51, 0x58, 0x43, 0x4A, 0x75, 0x7C, 0x67, 0x6E,
+    0x32, 0x3B, 0x20, 0x29, 0x16, 0x1F, 0x04, 0x0D,
+    0x7A, 0x73, 0x68, 0x61, 0x5E, 0x57, 0x4C, 0x45,
+    0x2B, 0x22, 0x39, 0x30, 0x0F, 0x06, 0x1D, 0x14,
+    0x63, 0x6A, 0x71, 0x78, 0x47, 0x4E, 0x55, 0x5C,
+    0x64, 0x6D, 0x76, 0x7F, 0x40, 0x49, 0x52, 0x5B,
+    0x2C, 0x25, 0x3E, 0x37, 0x08, 0x01, 0x1A, 0x13,
+    0x7D, 0x74, 0x6F, 0x66, 0x59, 0x50, 0x4B, 0x42,
+    0x35, 0x3C, 0x27, 0x2E, 0x11, 0x18, 0x03, 0x0A,
+    0x56, 0x5F, 0x44, 0x4D, 0x72, 0x7B, 0x60, 0x69,
+    0x1E, 0x17, 0x0C, 0x05, 0x3A, 0x33, 0x28, 0x21,
+    0x4F, 0x46, 0x5D, 0x54, 0x6B, 0x62, 0x79, 0x70,
+    0x07, 0x0E, 0x15, 0x1C, 0x23, 0x2A, 0x31, 0x38,
+    0x41, 0x48, 0x53, 0x5A, 0x65, 0x6C, 0x77, 0x7E,
+    0x09, 0x00, 0x1B, 0x12, 0x2D, 0x24, 0x3F, 0x36,
+    0x58, 0x51, 0x4A, 0x43, 0x7C, 0x75, 0x6E, 0x67,
+    0x10, 0x19, 0x02, 0x0B, 0x34, 0x3D, 0x26, 0x2F,
+    0x73, 0x7A, 0x61, 0x68, 0x57, 0x5E, 0x45, 0x4C,
+    0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D, 0x04,
+    0x6A, 0x63, 0x78, 0x71, 0x4E, 0x47, 0x5C, 0x55,
+    0x22, 0x2B, 0x30, 0x39, 0x06, 0x0F, 0x14, 0x1D,
+    0x25, 0x2C, 0x37, 0x3E, 0x01, 0x08, 0x13, 0x1A,
+    0x6D, 0x64, 0x7F, 0x76, 0x49, 0x40, 0x5B, 0x52,
+    0x3C, 0x35, 0x2E, 0x27, 0x18, 0x11, 0x0A, 0x03,
+    0x74, 0x7D, 0x66, 0x6F, 0x50, 0x59, 0x42, 0x4B,
+    0x17, 0x1E, 0x05, 0x0C, 0x33, 0x3A, 0x21, 0x28,
+    0x5F, 0x56, 0x4D, 0x44, 0x7B, 0x72, 0x69, 0x60,
+    0x0E, 0x07, 0x1C, 0x15, 0x2A, 0x23, 0x38, 0x31,
+    0x46, 0x4F, 0x54, 0x5D, 0x62, 0x6B, 0x70, 0x79
+};
+}
+
+char CRC7(const char* data, int length)
+{
+    //Calculate the CRC7 checksum for the specified data block
+    char crc = 0;
+    for (int i = 0; i < length; i++) {
+        crc = m_CRC7Table[(crc << 1) ^ data[i]];
+    }
+
+    //Return the calculated checksum
+    return crc;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CRC7.h	Tue Jul 29 20:12:23 2014 +0000
@@ -0,0 +1,24 @@
+/* SD/MMC File System Library
+ * Copyright (c) 2014 Neil Thiessen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRC7_H
+#define CRC7_H
+
+#include "mbed.h"
+
+char CRC7(const char* data, int length);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem.lib	Tue Jul 29 20:12:23 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/teams/mbed-official/code/FATFileSystem/#e960e2b81a3c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem.cpp	Tue Jul 29 20:12:23 2014 +0000
@@ -0,0 +1,496 @@
+/* SD/MMC File System Library
+ * Copyright (c) 2014 Neil Thiessen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SDFileSystem.h"
+#include "diskio.h"
+#include "CRC7.h"
+#include "CRC16.h"
+
+SDFileSystem::SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName cd, const char* name, int hz) : FATFileSystem(name), m_SPI(mosi, miso, sclk), m_CS(cs, 1), m_CD(cd)
+{
+    //Initialize the member variables
+    m_SpiFreq = hz;
+    m_Status = STA_NOINIT;
+    m_CardType = CARD_NONE;
+
+    //Configure the SPI bus
+    m_SPI.format(8, 0);
+
+    //Configure the card detect pin
+    m_CD.mode(PullUp);
+    m_CD.fall(this, &SDFileSystem::checkSocket);
+}
+
+SDFileSystem::CardType SDFileSystem::card_type()
+{
+    //Check the card socket
+    checkSocket();
+
+    //If a card is present but not initialized, initialize it
+    if (!(m_Status & STA_NODISK) && (m_Status & STA_NOINIT))
+        disk_initialize();
+
+    //Return the card type
+    return m_CardType;
+}
+
+int SDFileSystem::disk_initialize()
+{
+    char resp;
+
+    //Make sure there's a card in the socket before proceeding
+    checkSocket();
+    if (m_Status & STA_NODISK)
+        return m_Status;
+
+    //Make sure we're not already initialized before proceeding
+    if (!(m_Status & STA_NOINIT))
+        return m_Status;
+
+    //Set the SPI frequency to 100kHz for initialization
+    m_SPI.frequency(100000);
+
+    //Send 80 dummy clocks with /CS and DI held high
+    m_CS = 1;
+    for (int i = 0; i < 10; i++)
+        m_SPI.write(0xFF);
+
+    //Write CMD0(0), and check for a valid response
+    resp = writeCommand(CMD0, 0);
+    if (resp != 0x01) {
+        //Initialization failed
+        m_CardType = CARD_UNKNOWN;
+        return m_Status;
+    }
+
+    //Write CMD8(0x000001AA) to see if this is an SDCv2 card
+    resp = writeCommand(CMD8, 0x000001AA);
+    if (resp == 0x01) {
+        //This is an SDCv2 card, get the 32-bit return value and verify the voltage range/check pattern
+        if ((readReturn() & 0xFFF) != 0x1AA) {
+            //Initialization failed
+            m_CardType = CARD_UNKNOWN;
+            return m_Status;
+        }
+
+        //Send CMD58(0) to read the OCR, and verify that the card supports 3.2-3.3V
+        resp = writeCommand(CMD58, 0);
+        if (resp != 0x01 || !(readReturn() & (1 << 20))) {
+            //Initialization failed
+            deselect();
+            m_CardType = CARD_UNKNOWN;
+            return m_Status;
+        }
+
+        //Send ACMD41(0x40100000) repeatedly for up to 1 second to initialize the card
+        for (int i = 0; i < 1000; i++) {
+            resp = writeCommand(ACMD41, 0x40100000);
+            if (resp != 0x01)
+                break;
+            wait_ms(1);
+        }
+
+        //Check if the card initialized
+        if (resp != 0x00) {
+            //Initialization failed
+            m_CardType = CARD_UNKNOWN;
+            return m_Status;
+        }
+
+        //Send CMD58(0) to read the OCR
+        resp = writeCommand(CMD58, 0);
+        if (resp == 0x00) {
+            //Check the CCS bit to determine if this is a high capacity card
+            if (readReturn() & 0x40000000)
+                m_CardType = CARD_SDHC;
+            else
+                m_CardType = CARD_SD;
+        } else {
+            //Initialization failed
+            m_CardType = CARD_UNKNOWN;
+            return m_Status;
+        }
+    } else {
+        //Didn't respond or illegal command, this is either an SDCv1 or MMC card
+        deselect();
+
+        //Send CMD58(0) to read the OCR, and verify that the card supports 3.2-3.3V
+        resp = writeCommand(CMD58, 0);
+        if (resp != 0x01 || !(readReturn() & (1 << 20))) {
+            //Initialization failed
+            deselect();
+            m_CardType = CARD_UNKNOWN;
+            return m_Status;
+        }
+
+        //Try to initialize the card using ACMD41(0x00100000) for 1 second
+        for (int i = 0; i < 1000; i++) {
+            resp = writeCommand(ACMD41, 0x00100000);
+            if (resp != 0x01)
+                break;
+            wait_ms(1);
+        }
+
+        //Check if the card initialized
+        if (resp == 0x00) {
+            //This is an SDCv1 standard capacity card
+            m_CardType = CARD_SD;
+        } else {
+            //Try to initialize the card using CMD1(0x00100000) for 1 second
+            for (int i = 0; i < 1000; i++) {
+                resp = writeCommand(CMD1, 0x00100000);
+                if (resp != 0x01)
+                    break;
+                wait_ms(1);
+            }
+
+            //Check if the card initialized
+            if (resp == 0x00) {
+                //This is an MMCv3 card
+                m_CardType = CARD_MMC;
+            } else {
+                //Initialization failed
+                m_CardType = CARD_UNKNOWN;
+                return m_Status;
+            }
+        }
+    }
+
+    //Send CMD59(0x00000001) to re-enable CRC
+    resp = writeCommand(CMD59, 0x00000001);
+    if (resp != 0x00) {
+        //Initialization failed
+        m_CardType = CARD_UNKNOWN;
+        return m_Status;
+    }
+
+    //Send CMD16(0x00000200) to force the block size to 512B if necessary
+    if (m_CardType != CARD_SDHC) {
+        resp = writeCommand(CMD16, 0x00000200);
+        if (resp != 0x00) {
+            //Initialization failed
+            m_CardType = CARD_UNKNOWN;
+            return m_Status;
+        }
+    }
+
+    //The card is now initialized
+    m_Status &= ~STA_NOINIT;
+
+    //Increase the SPI frequency to full speed (limited to 20MHz for MMC, or 25MHz for SDC)
+    if (m_CardType == CARD_MMC && m_SpiFreq > 20000000)
+        m_SPI.frequency(20000000);
+    else if (m_SpiFreq > 25000000)
+        m_SPI.frequency(25000000);
+    else
+        m_SPI.frequency(m_SpiFreq);
+
+    //Return the device status
+    return m_Status;
+}
+
+int SDFileSystem::disk_status()
+{
+    //Check if there's a card in the socket
+    checkSocket();
+
+    //Return the device status
+    return m_Status;
+}
+
+int SDFileSystem::disk_read(uint8_t* buffer, uint64_t sector)
+{
+    //Make sure the device is initialized before proceeding
+    if (m_Status & STA_NOINIT)
+        return RES_NOTRDY;
+
+    //Convert from LBA to a byte address for standard capacity cards
+    if (m_CardType != CARD_SDHC)
+        sector *= 512;
+
+    //Try to read the block up to 3 times
+    for (int i = 0; i < 3; i++) {
+        //Send CMD17(sector) to read a single block
+        char resp = writeCommand(CMD17, sector);
+        if (resp == 0x00) {
+            //Try to read the sector, and return if successful
+            if (readData((char*)buffer, 512))
+                return RES_OK;
+        } else {
+            //The command failed
+            deselect();
+            return RES_ERROR;
+        }
+    }
+
+    //The read operation failed 3 times (CRC most likely)
+    return RES_ERROR;
+}
+
+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();
+                continue;
+            }
+
+            //Send the write data token
+            m_SPI.write(0xFE);
+
+            //Write the data block from the buffer
+            for (int b = 0; b < 512; b++)
+                m_SPI.write(buffer[b]);
+
+            //Calculate the CRC16 checksum for the data block and send it
+            unsigned short crc = CRC16((char*)buffer, 512);
+            m_SPI.write(crc >> 8);
+            m_SPI.write(crc);
+
+            //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
+            deselect();
+            return RES_ERROR;
+        }
+    }
+
+    //The operation either timed out 3 times, failed the CRC check 3 times, or experienced a write error
+    return RES_ERROR;
+}
+
+int SDFileSystem::disk_sync()
+{
+    //Select the card so we're forced to wait for the end of any internal write processes
+    bool ret = select();
+    deselect();
+
+    //Return success/failure
+    return (ret) ? RES_OK : RES_ERROR;
+}
+
+uint64_t SDFileSystem::disk_sectors()
+{
+    //Make sure the device is initialized before proceeding
+    if (m_Status & STA_NOINIT)
+        return 0;
+
+    //Try to read the CSD register up to 3 times
+    for (int i = 0; i < 3; i++) {
+        //Send CMD9(0) to read the CSD register
+        if (writeCommand(CMD9, 0) == 0x00) {
+            //Receive the 16B CSD data
+            char csd[16];
+            if (readData(csd, 16)) {
+                //Calculate the sector count based on the card type
+                if ((csd[0] >> 6) == 0x01) {
+                    //Calculate the sector count a high capacity card
+                    uint64_t sectors = (((csd[7] & 0x3F) << 16) | (csd[8] << 8) | csd[9]) + 1;
+                    return sectors << 10;
+                } else {
+                    //Calculate the sector count standard capacity card
+                    uint64_t sectors = (((csd[6] & 0x03) << 10) | (csd[7] << 2) | ((csd[8] & 0xC0) >> 6)) + 1;
+                    sectors <<= ((((csd[9] & 0x03) << 1) | ((csd[10] & 0x80) >> 7)) + 2);
+                    sectors <<= (csd[5] & 0x0F);
+                    return sectors >> 9;
+                }
+            }
+        } else {
+            //The command failed
+            deselect();
+            return 0;
+        }
+    }
+
+    //The read operation failed 3 times (CRC most likely)
+    return 0;
+}
+
+void SDFileSystem::checkSocket()
+{
+    //Check if a card is in the socket
+    if (m_CD) {
+        //The socket is occupied, clear the STA_NODISK flag
+        m_Status &= ~STA_NODISK;
+    } else {
+        //The socket is empty
+        m_Status |= (STA_NODISK | STA_NOINIT);
+        m_CardType = CARD_NONE;
+    }
+}
+
+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;
+}
+
+inline bool SDFileSystem::select()
+{
+    //Pull /CS low
+    m_CS = 0;
+
+    //Send a dummy clock to enable DO
+    m_SPI.write(0xFF);
+
+    //Wait for up to 500ms for the card to become ready
+    if (waitReady(500))
+        return true;
+
+    //We timed out, deselect and return false
+    deselect();
+    return false;
+}
+
+inline void SDFileSystem::deselect()
+{
+    //Pull /CS high
+    m_CS = 1;
+
+    //Send a dummy byte to release DO
+    m_SPI.write(0xFF);
+}
+
+char SDFileSystem::writeCommand(char cmd, unsigned int arg)
+{
+    char resp;
+
+    //Try to send the command up to 3 times
+    for (int i = 0; i < 3; i++) {
+        //Send a CMD55 prior to an ACMD
+        if (cmd == ACMD41) {
+            resp = writeCommand(CMD55, 0);
+            if (resp > 0x01)
+                return resp;
+        }
+
+        //Select the card and wait for ready
+        if (!select())
+            return 0xFF;
+
+        //Prepare the command packet
+        char cmdPacket[6];
+        cmdPacket[0] = 0x40 | cmd;
+        cmdPacket[1] = arg >> 24;
+        cmdPacket[2] = arg >> 16;
+        cmdPacket[3] = arg >> 8;
+        cmdPacket[4] = arg;
+        cmdPacket[5] = (CRC7(cmdPacket, 5) << 1) | 0x01;
+
+        //Send the command packet
+        for (int b = 0; b < 6; b++)
+            m_SPI.write(cmdPacket[b]);
+
+        //Allow up to 10 bytes of delay for the command response
+        for (int b = 0; b < 10; b++) {
+            resp = m_SPI.write(0xFF);
+            if (!(resp & 0x80))
+                break;
+        }
+
+        //Deselect the card unless there's more data to read/write
+        if (resp == 0xFF || (resp & (1 << 3)) || !(cmd == CMD8 || cmd == CMD9 || cmd == CMD17 || cmd == CMD24 || cmd == CMD55 || cmd == CMD58))
+            deselect();
+
+        //Return the response if there were no CRC errors
+        if (resp == 0xFF || !(resp & (1 << 3)))
+            return resp;
+    }
+
+    //The command failed 3 times due to CRC errors
+    return 0xFF;
+}
+
+unsigned int SDFileSystem::readReturn()
+{
+    unsigned int ret;
+
+    //Read the 32-bit response value
+    ret = (m_SPI.write(0xFF) << 24);
+    ret |= (m_SPI.write(0xFF) << 16);
+    ret |= (m_SPI.write(0xFF) << 8);
+    ret |= m_SPI.write(0xFF);
+
+    //Deselect the card
+    deselect();
+
+    //Return the response value
+    return ret;
+}
+
+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;
+
+    //Read the data into the buffer
+    for (int i = 0; i < length; i++)
+        buffer[i] = m_SPI.write(0xFF);
+
+    //Read the CRC16 checksum for the data block, and deselect the card
+    unsigned short crc = (m_SPI.write(0xFF) << 8);
+    crc |= m_SPI.write(0xFF);
+    deselect();
+
+    //Indicate whether the CRC16 checksum was valid or not
+    if (crc == CRC16(buffer, length))
+        return true;
+    else
+        return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem.h	Tue Jul 29 20:12:23 2014 +0000
@@ -0,0 +1,136 @@
+/* SD/MMC File System Library
+ * Copyright (c) 2014 Neil Thiessen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SD_FILE_SYSTEM_H
+#define SD_FILE_SYSTEM_H
+
+#include "mbed.h"
+#include "FATFileSystem.h"
+#include <stdint.h>
+
+/** SDFileSystem class.
+ *  Used for creating a virtual file system for accessing SD/MMC cards via SPI.
+ *
+ * Example:
+ * @code
+ * #include "mbed.h"
+ * #include "SDFileSystem.h"
+ *
+ * //Create an SDFileSystem object
+ * SDFileSystem sd(p5, p6, p7, p19, p20, "sd")
+ *
+ * int main()
+ * {
+ *     //Perform a write test
+ *     printf("\nWriting to SD 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 SD 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");
+ *     }
+ * }
+ * @endcode
+ */
+class SDFileSystem : public FATFileSystem
+{
+public:
+    /** Represents the different SD/MMC card types
+     */
+    enum CardType {
+        CARD_NONE,      /**< No card is present */
+        CARD_MMC,       /**< MMC card */
+        CARD_SD,        /**< Standard capacity SD card */
+        CARD_SDHC,      /**< High capacity SD card */
+        CARD_UNKNOWN    /**< Unknown or unsupported card */
+    };
+
+    /** Create a virtual file system for accessing SD/MMC cards via SPI
+     *
+     * @param mosi The SPI data out pin.
+     * @param miso The SPI data in pin.
+     * @param sclk The SPI clock pin.
+     * @param cs The SPI chip select pin.
+     * @param cd The active-high card detect pin.
+     * @param name The name used to access the virtual filesystem.
+     * @param hz The SPI bus frequency (defaults to 1MHz).
+     */
+    SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName cd, const char* name, int hz = 1000000);
+
+    /** Get the detected SD/MMC card type
+     *
+     * @returns The detected card type as a CardType enum.
+     */
+    SDFileSystem::CardType card_type();
+
+    virtual int disk_initialize();
+    virtual int disk_status();
+    virtual int disk_read(uint8_t* buffer, uint64_t sector);
+    virtual int disk_write(const uint8_t* buffer, uint64_t sector);
+    virtual int disk_sync();
+    virtual uint64_t disk_sectors();
+
+private:
+    //Commands
+    enum Command {
+        CMD0 = 0,       /**< GO_IDLE_STATE */
+        CMD1 = 1,       /**< SEND_OP_COND */
+        ACMD41 = 41,    /**< APP_SEND_OP_COND */
+        CMD8 = 8,       /**< SEND_IF_COND */
+        CMD9 = 9,       /**< SEND_CSD */
+        CMD16 = 16,     /**< SET_BLOCKLEN */
+        CMD17 = 17,     /**< READ_SINGLE_BLOCK */
+        CMD24 = 24,     /**< WRITE_BLOCK */
+        CMD55 = 55,     /**< APP_CMD */
+        CMD58 = 58,     /**< READ_OCR */
+        CMD59 = 59      /**< CRC_ON_OFF */
+    };
+
+    //Member variables
+    SPI m_SPI;
+    DigitalOut m_CS;
+    InterruptIn m_CD;
+    int m_SpiFreq;
+    int m_Status;
+    SDFileSystem::CardType m_CardType;
+
+    //Internal methods
+    void checkSocket();
+    bool waitReady(int timeout);
+    bool select();
+    void deselect();
+    char writeCommand(char cmd, unsigned int arg);
+    unsigned int readReturn();
+    bool readData(char* buffer, int length);
+};
+
+#endif