aaa

Dependencies:   FATFileSystem

Dependents:   AutoFlight2017_now2 AutoFlight2018_Control sbus_test_2018 Autoflight2018_sbusread ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SDFileSystem.cpp Source File

SDFileSystem.cpp

00001 /* SD/MMC File System Library
00002  * Copyright (c) 2016 Neil Thiessen
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "SDFileSystem.h"
00018 #include "diskio.h"
00019 #include "pinmap.h"
00020 #include "SDCRC.h"
00021 
00022 SDFileSystem::SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name, PinName cd, SwitchType cdtype, int hz)
00023     : FATFileSystem(name),
00024       m_Spi(mosi, miso, sclk),
00025       m_Cs(cs, 1),
00026       m_Cd(cd),
00027       m_FREQ(hz)
00028 {
00029     //Initialize the member variables
00030     m_CardType = CARD_NONE;
00031     m_Crc = true;
00032     m_LargeFrames = false;
00033     m_WriteValidation = true;
00034     m_Status = STA_NOINIT;
00035 
00036     //Enable the internal pull-up resistor on MISO
00037     pin_mode(miso, PullUp);
00038 
00039     //Configure the SPI bus
00040     m_Spi.format(8, 0);
00041 
00042     //Configure the card detect pin
00043     if (cdtype == SWITCH_POS_NO) {
00044         m_Cd.mode(PullDown);
00045         m_CdAssert = 1;
00046         m_Cd.fall(this, &SDFileSystem::onCardRemoval);
00047     } else if (cdtype == SWITCH_POS_NC) {
00048         m_Cd.mode(PullDown);
00049         m_CdAssert = 0;
00050         m_Cd.rise(this, &SDFileSystem::onCardRemoval);
00051     } else if (cdtype == SWITCH_NEG_NO) {
00052         m_Cd.mode(PullUp);
00053         m_CdAssert = 0;
00054         m_Cd.rise(this, &SDFileSystem::onCardRemoval);
00055     } else if (cdtype == SWITCH_NEG_NC) {
00056         m_Cd.mode(PullUp);
00057         m_CdAssert = 1;
00058         m_Cd.fall(this, &SDFileSystem::onCardRemoval);
00059     } else {
00060         m_CdAssert = -1;
00061     }
00062 }
00063 
00064 bool SDFileSystem::card_present()
00065 {
00066     //Check the card socket
00067     checkSocket();
00068 
00069     //Return whether or not a card is present
00070     return !(m_Status & STA_NODISK);
00071 }
00072 
00073 SDFileSystem::CardType SDFileSystem::card_type()
00074 {
00075     //Check the card socket
00076     checkSocket();
00077 
00078     //Return the card type
00079     return m_CardType;
00080 }
00081 
00082 bool SDFileSystem::crc()
00083 {
00084     //Return whether or not CRC is enabled
00085     return m_Crc;
00086 }
00087 
00088 void SDFileSystem::crc(bool enabled)
00089 {
00090     //Check the card socket
00091     checkSocket();
00092 
00093     //Just update the member variable if the card isn't initialized
00094     if (m_Status & STA_NOINIT) {
00095         m_Crc = enabled;
00096         return;
00097     }
00098 
00099     //Enable or disable CRC
00100     if (enabled && !m_Crc) {
00101         //Send CMD59(0x00000001) to enable CRC
00102         m_Crc = true;
00103         commandTransaction(CMD59, 0x00000001);
00104     } else if (!enabled && m_Crc) {
00105         //Send CMD59(0x00000000) to disable CRC
00106         commandTransaction(CMD59, 0x00000000);
00107         m_Crc = false;
00108     }
00109 }
00110 
00111 bool SDFileSystem::large_frames()
00112 {
00113     //Return whether or not 16-bit frames are enabled
00114     return m_LargeFrames;
00115 }
00116 
00117 void SDFileSystem::large_frames(bool enabled)
00118 {
00119     //Set whether or not 16-bit frames are enabled
00120     m_LargeFrames = enabled;
00121 }
00122 
00123 bool SDFileSystem::write_validation()
00124 {
00125     //Return whether or not write validation is enabled
00126     return m_WriteValidation;
00127 }
00128 
00129 void SDFileSystem::write_validation(bool enabled)
00130 {
00131     //Set whether or not write validation is enabled
00132     m_WriteValidation = enabled;
00133 }
00134 
00135 int SDFileSystem::unmount()
00136 {
00137     //Unmount the filesystem
00138     FATFileSystem::unmount();
00139 
00140     //Change the status to not initialized, and the card type to unknown
00141     m_Status |= STA_NOINIT;
00142     m_CardType = CARD_UNKNOWN;
00143 
00144     //Always succeeds
00145     return 0;
00146 }
00147 
00148 int SDFileSystem::disk_initialize()
00149 {
00150     char token;
00151     unsigned int resp;
00152 
00153     //Make sure there's a card in the socket before proceeding
00154     checkSocket();
00155     if (m_Status & STA_NODISK)
00156         return m_Status;
00157 
00158     //Make sure we're not already initialized before proceeding
00159     if (!(m_Status & STA_NOINIT))
00160         return m_Status;
00161 
00162     //Set the SPI frequency to 400kHz for initialization
00163     m_Spi.frequency(400000);
00164 
00165     //Send 80 dummy clocks with /CS deasserted and DI held high
00166     m_Cs = 1;
00167     for (int i = 0; i < 10; i++)
00168         m_Spi.write(0xFF);
00169 
00170     //Send CMD0(0x00000000) to reset the card
00171     if (commandTransaction(CMD0, 0x00000000) != 0x01) {
00172         //Initialization failed
00173         m_CardType = CARD_UNKNOWN;
00174         return m_Status;
00175     }
00176 
00177     //Send CMD59(0x00000001) to enable CRC if necessary
00178     if (m_Crc) {
00179         if (commandTransaction(CMD59, 0x00000001) != 0x01) {
00180             //Initialization failed
00181             m_CardType = CARD_UNKNOWN;
00182             return m_Status;
00183         }
00184     }
00185 
00186     //Send CMD8(0x000001AA) to see if this is an SDCv2 card
00187     if (commandTransaction(CMD8, 0x000001AA, &resp) == 0x01) {
00188         //This is an SDCv2 card, get the 32-bit return value and verify the voltage range/check pattern
00189         if ((resp & 0xFFF) != 0x1AA) {
00190             //Initialization failed
00191             m_CardType = CARD_UNKNOWN;
00192             return m_Status;
00193         }
00194 
00195         //Send CMD58(0x00000000) to read the OCR, and verify that the card supports 3.2-3.3V
00196         if (commandTransaction(CMD58, 0x00000000, &resp) != 0x01 || !(resp & (1 << 20))) {
00197             //Initialization failed
00198             m_CardType = CARD_UNKNOWN;
00199             return m_Status;
00200         }
00201 
00202         //Try to initialize the card using ACMD41(0x00100000)
00203         for (int i = 0; i < 1000; i++) {
00204             token = commandTransaction(ACMD41, 0x40100000);
00205             if (token != 0x01) {
00206                 break;
00207             }
00208         }
00209 
00210         //Check if the card initialized
00211         if (token != 0x00) {
00212             //Initialization failed
00213             m_CardType = CARD_UNKNOWN;
00214             return m_Status;
00215         }
00216 
00217         //Send CMD58(0x00000000) to read the OCR
00218         if (commandTransaction(CMD58, 0x00000000, &resp) == 0x00) {
00219             //Check the CCS bit to determine if this is a high capacity card
00220             if (resp & (1 << 30))
00221                 m_CardType = CARD_SDHC;
00222             else
00223                 m_CardType = CARD_SD;
00224 
00225             //Increase the SPI frequency to full speed (up to 25MHz for SDCv2)
00226             if (m_FREQ > 25000000)
00227                 m_Spi.frequency(25000000);
00228             else
00229                 m_Spi.frequency(m_FREQ);
00230         } else {
00231             //Initialization failed
00232             m_CardType = CARD_UNKNOWN;
00233             return m_Status;
00234         }
00235     } else {
00236         //Didn't respond or illegal command, this is either an SDCv1 or MMC card
00237         //Send CMD58(0x00000000) to read the OCR, and verify that the card supports 3.2-3.3V
00238         if (commandTransaction(CMD58, 0x00000000, &resp) != 0x01 || !(resp & (1 << 20))) {
00239             //Initialization failed
00240             m_CardType = CARD_UNKNOWN;
00241             return m_Status;
00242         }
00243 
00244         //Try to initialize the card using ACMD41(0x00100000)
00245         for (int i = 0; i < 1000; i++) {
00246             token = commandTransaction(ACMD41, 0x40100000);
00247             if (token != 0x01) {
00248                 break;
00249             }
00250         }
00251 
00252         //Check if the card initialized
00253         if (token == 0x00) {
00254             //This is an SDCv1 standard capacity card
00255             m_CardType = CARD_SD;
00256 
00257             //Increase the SPI frequency to full speed (up to 25MHz for SDCv1)
00258             if (m_FREQ > 25000000)
00259                 m_Spi.frequency(25000000);
00260             else
00261                 m_Spi.frequency(m_FREQ);
00262         } else {
00263             //Try to initialize the card using CMD1(0x00100000)
00264             for (int i = 0; i < 1000; i++) {
00265                 token = commandTransaction(CMD1, 0x00100000);
00266                 if (token != 0x01) {
00267                     break;
00268                 }
00269             }
00270 
00271             //Check if the card initialized
00272             if (token == 0x00) {
00273                 //This is an MMCv3 card
00274                 m_CardType = CARD_MMC;
00275 
00276                 //Increase the SPI frequency to full speed (up to 20MHz for MMCv3)
00277                 if (m_FREQ > 20000000)
00278                     m_Spi.frequency(20000000);
00279                 else
00280                     m_Spi.frequency(m_FREQ);
00281             } else {
00282                 //Initialization failed
00283                 m_CardType = CARD_UNKNOWN;
00284                 return m_Status;
00285             }
00286         }
00287     }
00288 
00289     //Send ACMD42(0x00000000) to disconnect the internal pull-up resistor on pin 1 if necessary
00290     if (m_CardType != CARD_MMC) {
00291         if (commandTransaction(ACMD42, 0x00000000) != 0x00) {
00292             //Initialization failed
00293             m_CardType = CARD_UNKNOWN;
00294             return m_Status;
00295         }
00296     }
00297 
00298     //Send CMD16(0x00000200) to force the block size to 512B if necessary
00299     if (m_CardType != CARD_SDHC) {
00300         if (commandTransaction(CMD16, 0x00000200) != 0x00) {
00301             //Initialization failed
00302             m_CardType = CARD_UNKNOWN;
00303             return m_Status;
00304         }
00305     }
00306 
00307     //The card is now initialized
00308     m_Status &= ~STA_NOINIT;
00309 
00310     //Return the disk status
00311     return m_Status;
00312 }
00313 
00314 int SDFileSystem::disk_status()
00315 {
00316     //Check the card socket
00317     checkSocket();
00318 
00319     //Return the disk status
00320     return m_Status;
00321 }
00322 
00323 int SDFileSystem::disk_read(uint8_t* buffer, uint32_t sector, uint32_t count)
00324 {
00325     //Make sure the card is initialized before proceeding
00326     if (m_Status & STA_NOINIT)
00327         return RES_NOTRDY;
00328 
00329     //Read a single block, or multiple blocks
00330     if (count > 1) {
00331         return readBlocks((char*)buffer, sector, count) ? RES_OK : RES_ERROR;
00332     } else {
00333         return readBlock((char*)buffer, sector) ? RES_OK : RES_ERROR;
00334     }
00335 }
00336 
00337 int SDFileSystem::disk_write(const uint8_t* buffer, uint32_t sector, uint32_t count)
00338 {
00339     //Make sure the card is initialized before proceeding
00340     if (m_Status & STA_NOINIT)
00341         return RES_NOTRDY;
00342 
00343     //Make sure the card isn't write protected before proceeding
00344     if (m_Status & STA_PROTECT)
00345         return RES_WRPRT;
00346 
00347     //Write a single block, or multiple blocks
00348     if (count > 1) {
00349         return writeBlocks((const char*)buffer, sector, count) ? RES_OK : RES_ERROR;
00350     } else {
00351         return writeBlock((const char*)buffer, sector) ? RES_OK : RES_ERROR;
00352     }
00353 }
00354 
00355 int SDFileSystem::disk_sync()
00356 {
00357     //Select the card so we're forced to wait for the end of any internal write processes
00358     if (select()) {
00359         deselect();
00360         return RES_OK;
00361     } else {
00362         return RES_ERROR;
00363     }
00364 }
00365 
00366 uint32_t SDFileSystem::disk_sectors()
00367 {
00368     //Make sure the card is initialized before proceeding
00369     if (m_Status & STA_NOINIT)
00370         return 0;
00371 
00372     //Try to read the CSD register up to 3 times
00373     for (int f = 0; f < 3; f++) {
00374         //Select the card, and wait for ready
00375         if(!select())
00376             break;
00377 
00378         //Send CMD9(0x00000000) to read the CSD register
00379         if (writeCommand(CMD9, 0x00000000) == 0x00) {
00380             //Read the 16B CSD data block
00381             char csd[16];
00382             bool success = readData(csd, 16);
00383             deselect();
00384             if (success) {
00385                 //Calculate the sector count based on the card type
00386                 if ((csd[0] >> 6) == 0x01) {
00387                     //Calculate the sector count for a high capacity card
00388                     unsigned int size = (((csd[7] & 0x3F) << 16) | (csd[8] << 8) | csd[9]) + 1;
00389                     return size << 10;
00390                 } else {
00391                     //Calculate the sector count for a standard capacity card
00392                     unsigned int size = (((csd[6] & 0x03) << 10) | (csd[7] << 2) | ((csd[8] & 0xC0) >> 6)) + 1;
00393                     size <<= ((((csd[9] & 0x03) << 1) | ((csd[10] & 0x80) >> 7)) + 2);
00394                     size <<= (csd[5] & 0x0F);
00395                     return size >> 9;
00396                 }
00397             }
00398         } else {
00399             //The command failed, get out
00400             break;
00401         }
00402     }
00403 
00404     //The read operation failed 3 times
00405     deselect();
00406     return 0;
00407 }
00408 
00409 void SDFileSystem::onCardRemoval()
00410 {
00411     //Check the card socket
00412     checkSocket();
00413 }
00414 
00415 inline void SDFileSystem::checkSocket()
00416 {
00417     //Use the card detect switch (if available) to determine if the socket is occupied
00418     if (m_CdAssert != -1) {
00419         if (m_Status & STA_NODISK) {
00420             if (m_Cd == m_CdAssert) {
00421                 //The socket is now occupied
00422                 m_Status &= ~STA_NODISK;
00423                 m_CardType = CARD_UNKNOWN;
00424             }
00425         } else {
00426             if (m_Cd != m_CdAssert) {
00427                 //The socket is now empty
00428                 m_Status |= (STA_NODISK | STA_NOINIT);
00429                 m_CardType = CARD_NONE;
00430             }
00431         }
00432     }
00433 }
00434 
00435 inline bool SDFileSystem::waitReady(int timeout)
00436 {
00437     char resp;
00438 
00439     //Keep sending dummy clocks with DI held high until the card releases the DO line
00440     m_Timer.start();
00441     do {
00442         resp = m_Spi.write(0xFF);
00443     } while (resp == 0x00 && m_Timer.read_ms() < timeout);
00444     m_Timer.stop();
00445     m_Timer.reset();
00446 
00447     //Return success/failure
00448     return (resp > 0x00);
00449 }
00450 
00451 inline bool SDFileSystem::select()
00452 {
00453     //Assert /CS
00454     m_Cs = 0;
00455 
00456     //Send 8 dummy clocks with DI held high to enable DO
00457     m_Spi.write(0xFF);
00458 
00459     //Wait for up to 500ms for the card to become ready
00460     if (waitReady(500)) {
00461         return true;
00462     } else {
00463         //We timed out, deselect and return false
00464         deselect();
00465         return false;
00466     }
00467 }
00468 
00469 inline void SDFileSystem::deselect()
00470 {
00471     //Deassert /CS
00472     m_Cs = 1;
00473 
00474     //Send 8 dummy clocks with DI held high to disable DO
00475     m_Spi.write(0xFF);
00476 }
00477 
00478 inline char SDFileSystem::commandTransaction(char cmd, unsigned int arg, unsigned int* resp)
00479 {
00480     //Select the card, and wait for ready
00481     if(!select())
00482         return 0xFF;
00483 
00484     //Perform the command transaction
00485     char token = writeCommand(cmd, arg, resp);
00486 
00487     //Deselect the card, and return the R1 response token
00488     deselect();
00489     return token;
00490 }
00491 
00492 char SDFileSystem::writeCommand(char cmd, unsigned int arg, unsigned int* resp)
00493 {
00494     char token;
00495 
00496     //Try to send the command up to 3 times
00497     for (int f = 0; f < 3; f++) {
00498         //Send CMD55(0x00000000) prior to an application specific command
00499         if (cmd == ACMD22 || cmd == ACMD23 || cmd == ACMD41 || cmd == ACMD42) {
00500             token = writeCommand(CMD55, 0x00000000);
00501             if (token > 0x01)
00502                 return token;
00503 
00504             //Deselect and reselect the card between CMD55 and an ACMD
00505             deselect();
00506             if(!select())
00507                 return 0xFF;
00508         }
00509 
00510         //Prepare the command packet
00511         char cmdPacket[6];
00512         cmdPacket[0] = cmd;
00513         cmdPacket[1] = arg >> 24;
00514         cmdPacket[2] = arg >> 16;
00515         cmdPacket[3] = arg >> 8;
00516         cmdPacket[4] = arg;
00517         if (m_Crc || cmd == CMD0 || cmd == CMD8)
00518             cmdPacket[5] = (SDCRC::crc7(cmdPacket, 5) << 1) | 0x01;
00519         else
00520             cmdPacket[5] = 0x01;
00521 
00522         //Send the command packet
00523         for (int i = 0; i < 6; i++)
00524             m_Spi.write(cmdPacket[i]);
00525 
00526         //Discard the stuff byte immediately following CMD12
00527         if (cmd == CMD12)
00528             m_Spi.write(0xFF);
00529 
00530         //Allow up to 8 bytes of delay for the R1 response token
00531         for (int i = 0; i < 9; i++) {
00532             token = m_Spi.write(0xFF);
00533             if (!(token & 0x80))
00534                 break;
00535         }
00536 
00537         //Verify the R1 response token
00538         if (token == 0xFF) {
00539             //No data was received, get out early
00540             break;
00541         } else if (token & (1 << 3)) {
00542             //There was a CRC error, try again
00543             continue;
00544         } else if (token > 0x01) {
00545             //An error occured, get out early
00546             break;
00547         }
00548 
00549         //Handle R2 and R3/R7 response tokens
00550         if (cmd == CMD13 && resp != NULL) {
00551             //Read the R2 response value
00552             *resp = m_Spi.write(0xFF);
00553         } else if ((cmd == CMD8 || cmd == CMD58) && resp != NULL) {
00554             //Read the R3/R7 response value
00555             *resp = (m_Spi.write(0xFF) << 24);
00556             *resp |= (m_Spi.write(0xFF) << 16);
00557             *resp |= (m_Spi.write(0xFF) << 8);
00558             *resp |= m_Spi.write(0xFF);
00559         }
00560 
00561         //The command was successful
00562         break;
00563     }
00564 
00565     //Return the R1 response token
00566     return token;
00567 }
00568 
00569 bool SDFileSystem::readData(char* buffer, int length)
00570 {
00571     char token;
00572     unsigned short crc;
00573 
00574     //Wait for up to 500ms for a token to arrive
00575     m_Timer.start();
00576     do {
00577         token = m_Spi.write(0xFF);
00578     } while (token == 0xFF && m_Timer.read_ms() < 500);
00579     m_Timer.stop();
00580     m_Timer.reset();
00581 
00582     //Check if a valid start block token was received
00583     if (token != 0xFE)
00584         return false;
00585 
00586     //Check if large frames are enabled or not
00587     if (m_LargeFrames) {
00588         //Switch to 16-bit frames for better performance
00589         m_Spi.format(16, 0);
00590 
00591         //Read the data block into the buffer
00592         unsigned short dataWord;
00593         for (int i = 0; i < length; i += 2) {
00594             dataWord = m_Spi.write(0xFFFF);
00595             buffer[i] = dataWord >> 8;
00596             buffer[i + 1] = dataWord;
00597         }
00598 
00599         //Read the CRC16 checksum for the data block
00600         crc = m_Spi.write(0xFFFF);
00601 
00602         //Switch back to 8-bit frames
00603         m_Spi.format(8, 0);
00604     } else {
00605         //Read the data into the buffer
00606         for (int i = 0; i < length; i++)
00607             buffer[i] = m_Spi.write(0xFF);
00608 
00609         //Read the CRC16 checksum for the data block
00610         crc = (m_Spi.write(0xFF) << 8);
00611         crc |= m_Spi.write(0xFF);
00612     }
00613 
00614     //Return the validity of the CRC16 checksum (if enabled)
00615     return (!m_Crc || crc == SDCRC::crc16(buffer, length));
00616 }
00617 
00618 char SDFileSystem::writeData(const char* buffer, char token)
00619 {
00620     //Calculate the CRC16 checksum for the data block (if enabled)
00621     unsigned short crc = (m_Crc) ? SDCRC::crc16(buffer, 512) : 0xFFFF;
00622 
00623     //Wait for up to 500ms for the card to become ready
00624     if (!waitReady(500))
00625         return false;
00626 
00627     //Send the start block token
00628     m_Spi.write(token);
00629 
00630     //Check if large frames are enabled or not
00631     if (m_LargeFrames) {
00632         //Switch to 16-bit frames for better performance
00633         m_Spi.format(16, 0);
00634 
00635         //Write the data block from the buffer
00636         for (int i = 0; i < 512; i += 2)
00637             m_Spi.write((buffer[i] << 8) | buffer[i + 1]);
00638 
00639         //Send the CRC16 checksum for the data block
00640         m_Spi.write(crc);
00641 
00642         //Switch back to 8-bit frames
00643         m_Spi.format(8, 0);
00644     } else {
00645         //Write the data block from the buffer
00646         for (int i = 0; i < 512; i++)
00647             m_Spi.write(buffer[i]);
00648 
00649         //Send the CRC16 checksum for the data block
00650         m_Spi.write(crc >> 8);
00651         m_Spi.write(crc);
00652     }
00653 
00654     //Return the data response token
00655     return (m_Spi.write(0xFF) & 0x1F);
00656 }
00657 
00658 inline bool SDFileSystem::readBlock(char* buffer, unsigned int lba)
00659 {
00660     //Try to read the block up to 3 times
00661     for (int f = 0; f < 3; f++) {
00662         //Select the card, and wait for ready
00663         if(!select())
00664             break;
00665 
00666         //Send CMD17(block) to read a single block
00667         if (writeCommand(CMD17, (m_CardType == CARD_SDHC) ? lba : lba << 9) == 0x00) {
00668             //Try to read the block, and deselect the card
00669             bool success = readData(buffer, 512);
00670             deselect();
00671 
00672             //Return if successful
00673             if (success)
00674                 return true;
00675         } else {
00676             //The command failed, get out
00677             break;
00678         }
00679     }
00680 
00681     //The single block read failed
00682     deselect();
00683     return false;
00684 }
00685 
00686 inline bool SDFileSystem::readBlocks(char* buffer, unsigned int lba, unsigned int count)
00687 {
00688     //Try to read each block up to 3 times
00689     for (int f = 0; f < 3;) {
00690         //Select the card, and wait for ready
00691         if(!select())
00692             break;
00693 
00694         //Send CMD18(block) to read multiple blocks
00695         if (writeCommand(CMD18, (m_CardType == CARD_SDHC) ? lba : lba << 9) == 0x00) {
00696             //Try to read all of the data blocks
00697             do {
00698                 //Read the next block, and break on errors
00699                 if (!readData(buffer, 512)) {
00700                     f++;
00701                     break;
00702                 }
00703 
00704                 //Update the variables
00705                 lba++;
00706                 buffer += 512;
00707                 f = 0;
00708             } while (--count);
00709 
00710             //Send CMD12(0x00000000) to stop the transmission
00711             if (writeCommand(CMD12, 0x00000000) != 0x00) {
00712                 //The command failed, get out
00713                 break;
00714             }
00715 
00716             //Deselect the card, and return if successful
00717             deselect();
00718             if (count == 0)
00719                 return true;
00720         } else {
00721             //The command failed, get out
00722             break;
00723         }
00724     }
00725 
00726     //The multiple block read failed
00727     deselect();
00728     return false;
00729 }
00730 
00731 inline bool SDFileSystem::writeBlock(const char* buffer, unsigned int lba)
00732 {
00733     //Try to write the block up to 3 times
00734     for (int f = 0; f < 3; f++) {
00735         //Select the card, and wait for ready
00736         if(!select())
00737             break;
00738 
00739         //Send CMD24(block) to write a single block
00740         if (writeCommand(CMD24, (m_CardType == CARD_SDHC) ? lba : lba << 9) == 0x00) {
00741             //Try to write the block, and deselect the card
00742             char token = writeData(buffer, 0xFE);
00743             deselect();
00744 
00745             //Check the data response token
00746             if (token == 0x0A) {
00747                 //A CRC error occured, try again
00748                 continue;
00749             } else if (token == 0x0C) {
00750                 //A write error occured, get out
00751                 break;
00752             }
00753 
00754             //Send CMD13(0x00000000) to verify that the programming was successful if enabled
00755             if (m_WriteValidation) {
00756                 unsigned int resp;
00757                 if (commandTransaction(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) {
00758                     //Some manner of unrecoverable write error occured during programming, get out
00759                     break;
00760                 }
00761             }
00762 
00763             //The data was written successfully
00764             return true;
00765         } else {
00766             //The command failed, get out
00767             break;
00768         }
00769     }
00770 
00771     //The single block write failed
00772     deselect();
00773     return false;
00774 }
00775 
00776 inline bool SDFileSystem::writeBlocks(const char* buffer, unsigned int lba, unsigned int count)
00777 {
00778     char token;
00779     const char* currentBuffer = buffer;
00780     unsigned int currentLba = lba;
00781     int currentCount = count;
00782 
00783     //Try to write each block up to 3 times
00784     for (int f = 0; f < 3;) {
00785         //If this is an SD card, send ACMD23(count) to set the number of blocks to pre-erase
00786         if (m_CardType != CARD_MMC) {
00787             if (commandTransaction(ACMD23, currentCount) != 0x00) {
00788                 //The command failed, get out
00789                 break;
00790             }
00791         }
00792 
00793         //Select the card, and wait for ready
00794         if(!select())
00795             break;
00796 
00797         //Send CMD25(block) to write multiple blocks
00798         if (writeCommand(CMD25, (m_CardType == CARD_SDHC) ? currentLba : currentLba << 9) == 0x00) {
00799             //Try to write all of the data blocks
00800             do {
00801                 //Write the next block and break on errors
00802                 token = writeData(currentBuffer, 0xFC);
00803                 if (token != 0x05) {
00804                     f++;
00805                     break;
00806                 }
00807 
00808                 //Update the variables
00809                 currentBuffer += 512;
00810                 f = 0;
00811             } while (--currentCount);
00812 
00813             //Wait for up to 500ms for the card to finish processing the last block
00814             if (!waitReady(500))
00815                 break;
00816 
00817             //Finalize the transmission
00818             if (currentCount == 0) {
00819                 //Send the stop tran token, and deselect the card
00820                 m_Spi.write(0xFD);
00821                 deselect();
00822 
00823                 //Send CMD13(0x00000000) to verify that the programming was successful if enabled
00824                 if (m_WriteValidation) {
00825                     unsigned int resp;
00826                     if (commandTransaction(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) {
00827                         //Some manner of unrecoverable write error occured during programming, get out
00828                         break;
00829                     }
00830                 }
00831 
00832                 //The data was written successfully
00833                 return true;
00834             } else {
00835                 //Send CMD12(0x00000000) to abort the transmission
00836                 if (writeCommand(CMD12, 0x00000000) != 0x00) {
00837                     //The command failed, get out
00838                     break;
00839                 }
00840 
00841                 //Deselect the card
00842                 deselect();
00843 
00844                 //Check the error token
00845                 if (token == 0x0A) {
00846                     //Determine the number of well written blocks if possible
00847                     unsigned int writtenBlocks = 0;
00848                     if (m_CardType != CARD_MMC && select()) {
00849                         //Send ACMD22(0x00000000) to get the number of well written blocks
00850                         if (writeCommand(ACMD22, 0x00000000) == 0x00) {
00851                             //Read the data
00852                             char acmdData[4];
00853                             if (readData(acmdData, 4)) {
00854                                 //Extract the number of well written blocks
00855                                 writtenBlocks = acmdData[0] << 24;
00856                                 writtenBlocks |= acmdData[1] << 16;
00857                                 writtenBlocks |= acmdData[2] << 8;
00858                                 writtenBlocks |= acmdData[3];
00859                             }
00860                         }
00861                         deselect();
00862                     }
00863 
00864                     //Roll back the variables based on the number of well written blocks
00865                     currentBuffer = buffer + (writtenBlocks << 9);
00866                     currentLba = lba + writtenBlocks;
00867                     currentCount = count - writtenBlocks;
00868 
00869                     //Try again
00870                     continue;
00871                 } else {
00872                     //A write error occured, get out
00873                     break;
00874                 }
00875             }
00876         } else {
00877             //The command failed, get out
00878             break;
00879         }
00880     }
00881 
00882     //The multiple block write failed
00883     deselect();
00884     return false;
00885 }