Example of using the SDFileSystem library on a K64F to write data to files and also read data into dynamically created arrays.
SDFileSystem/SDFileSystem.cpp@1:caceb9d9d17a, 2020-02-03 (annotated)
- Committer:
- eencae
- Date:
- Mon Feb 03 12:06:21 2020 +0000
- Revision:
- 1:caceb9d9d17a
Updated Mbed library. Removed serial. Confirmed working Jan 2020.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
eencae | 1:caceb9d9d17a | 1 | /* SD/MMC File System Library |
eencae | 1:caceb9d9d17a | 2 | * Copyright (c) 2016 Neil Thiessen |
eencae | 1:caceb9d9d17a | 3 | * |
eencae | 1:caceb9d9d17a | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
eencae | 1:caceb9d9d17a | 5 | * you may not use this file except in compliance with the License. |
eencae | 1:caceb9d9d17a | 6 | * You may obtain a copy of the License at |
eencae | 1:caceb9d9d17a | 7 | * |
eencae | 1:caceb9d9d17a | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
eencae | 1:caceb9d9d17a | 9 | * |
eencae | 1:caceb9d9d17a | 10 | * Unless required by applicable law or agreed to in writing, software |
eencae | 1:caceb9d9d17a | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
eencae | 1:caceb9d9d17a | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
eencae | 1:caceb9d9d17a | 13 | * See the License for the specific language governing permissions and |
eencae | 1:caceb9d9d17a | 14 | * limitations under the License. |
eencae | 1:caceb9d9d17a | 15 | */ |
eencae | 1:caceb9d9d17a | 16 | |
eencae | 1:caceb9d9d17a | 17 | #include "SDFileSystem.h" |
eencae | 1:caceb9d9d17a | 18 | #include "diskio.h" |
eencae | 1:caceb9d9d17a | 19 | #include "pinmap.h" |
eencae | 1:caceb9d9d17a | 20 | #include "SDCRC.h" |
eencae | 1:caceb9d9d17a | 21 | |
eencae | 1:caceb9d9d17a | 22 | SDFileSystem::SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name, PinName cd, SwitchType cdtype, int hz) |
eencae | 1:caceb9d9d17a | 23 | : FATFileSystem(name), |
eencae | 1:caceb9d9d17a | 24 | m_Spi(mosi, miso, sclk), |
eencae | 1:caceb9d9d17a | 25 | m_Cs(cs, 1), |
eencae | 1:caceb9d9d17a | 26 | m_Cd(cd), |
eencae | 1:caceb9d9d17a | 27 | m_FREQ(hz) |
eencae | 1:caceb9d9d17a | 28 | { |
eencae | 1:caceb9d9d17a | 29 | //Initialize the member variables |
eencae | 1:caceb9d9d17a | 30 | m_CardType = CARD_NONE; |
eencae | 1:caceb9d9d17a | 31 | m_Crc = true; |
eencae | 1:caceb9d9d17a | 32 | m_LargeFrames = false; |
eencae | 1:caceb9d9d17a | 33 | m_WriteValidation = true; |
eencae | 1:caceb9d9d17a | 34 | m_Status = STA_NOINIT; |
eencae | 1:caceb9d9d17a | 35 | |
eencae | 1:caceb9d9d17a | 36 | //Enable the internal pull-up resistor on MISO |
eencae | 1:caceb9d9d17a | 37 | pin_mode(miso, PullUp); |
eencae | 1:caceb9d9d17a | 38 | |
eencae | 1:caceb9d9d17a | 39 | //Configure the SPI bus |
eencae | 1:caceb9d9d17a | 40 | m_Spi.format(8, 0); |
eencae | 1:caceb9d9d17a | 41 | |
eencae | 1:caceb9d9d17a | 42 | //Configure the card detect pin |
eencae | 1:caceb9d9d17a | 43 | if (cdtype == SWITCH_POS_NO) { |
eencae | 1:caceb9d9d17a | 44 | m_Cd.mode(PullDown); |
eencae | 1:caceb9d9d17a | 45 | m_CdAssert = 1; |
eencae | 1:caceb9d9d17a | 46 | m_Cd.fall(this, &SDFileSystem::onCardRemoval); |
eencae | 1:caceb9d9d17a | 47 | } else if (cdtype == SWITCH_POS_NC) { |
eencae | 1:caceb9d9d17a | 48 | m_Cd.mode(PullDown); |
eencae | 1:caceb9d9d17a | 49 | m_CdAssert = 0; |
eencae | 1:caceb9d9d17a | 50 | m_Cd.rise(this, &SDFileSystem::onCardRemoval); |
eencae | 1:caceb9d9d17a | 51 | } else if (cdtype == SWITCH_NEG_NO) { |
eencae | 1:caceb9d9d17a | 52 | m_Cd.mode(PullUp); |
eencae | 1:caceb9d9d17a | 53 | m_CdAssert = 0; |
eencae | 1:caceb9d9d17a | 54 | m_Cd.rise(this, &SDFileSystem::onCardRemoval); |
eencae | 1:caceb9d9d17a | 55 | } else if (cdtype == SWITCH_NEG_NC) { |
eencae | 1:caceb9d9d17a | 56 | m_Cd.mode(PullUp); |
eencae | 1:caceb9d9d17a | 57 | m_CdAssert = 1; |
eencae | 1:caceb9d9d17a | 58 | m_Cd.fall(this, &SDFileSystem::onCardRemoval); |
eencae | 1:caceb9d9d17a | 59 | } else { |
eencae | 1:caceb9d9d17a | 60 | m_CdAssert = -1; |
eencae | 1:caceb9d9d17a | 61 | } |
eencae | 1:caceb9d9d17a | 62 | } |
eencae | 1:caceb9d9d17a | 63 | |
eencae | 1:caceb9d9d17a | 64 | bool SDFileSystem::card_present() |
eencae | 1:caceb9d9d17a | 65 | { |
eencae | 1:caceb9d9d17a | 66 | //Check the card socket |
eencae | 1:caceb9d9d17a | 67 | checkSocket(); |
eencae | 1:caceb9d9d17a | 68 | |
eencae | 1:caceb9d9d17a | 69 | //Return whether or not a card is present |
eencae | 1:caceb9d9d17a | 70 | return !(m_Status & STA_NODISK); |
eencae | 1:caceb9d9d17a | 71 | } |
eencae | 1:caceb9d9d17a | 72 | |
eencae | 1:caceb9d9d17a | 73 | SDFileSystem::CardType SDFileSystem::card_type() |
eencae | 1:caceb9d9d17a | 74 | { |
eencae | 1:caceb9d9d17a | 75 | //Check the card socket |
eencae | 1:caceb9d9d17a | 76 | checkSocket(); |
eencae | 1:caceb9d9d17a | 77 | |
eencae | 1:caceb9d9d17a | 78 | //Return the card type |
eencae | 1:caceb9d9d17a | 79 | return m_CardType; |
eencae | 1:caceb9d9d17a | 80 | } |
eencae | 1:caceb9d9d17a | 81 | |
eencae | 1:caceb9d9d17a | 82 | bool SDFileSystem::crc() |
eencae | 1:caceb9d9d17a | 83 | { |
eencae | 1:caceb9d9d17a | 84 | //Return whether or not CRC is enabled |
eencae | 1:caceb9d9d17a | 85 | return m_Crc; |
eencae | 1:caceb9d9d17a | 86 | } |
eencae | 1:caceb9d9d17a | 87 | |
eencae | 1:caceb9d9d17a | 88 | void SDFileSystem::crc(bool enabled) |
eencae | 1:caceb9d9d17a | 89 | { |
eencae | 1:caceb9d9d17a | 90 | //Check the card socket |
eencae | 1:caceb9d9d17a | 91 | checkSocket(); |
eencae | 1:caceb9d9d17a | 92 | |
eencae | 1:caceb9d9d17a | 93 | //Just update the member variable if the card isn't initialized |
eencae | 1:caceb9d9d17a | 94 | if (m_Status & STA_NOINIT) { |
eencae | 1:caceb9d9d17a | 95 | m_Crc = enabled; |
eencae | 1:caceb9d9d17a | 96 | return; |
eencae | 1:caceb9d9d17a | 97 | } |
eencae | 1:caceb9d9d17a | 98 | |
eencae | 1:caceb9d9d17a | 99 | //Enable or disable CRC |
eencae | 1:caceb9d9d17a | 100 | if (enabled && !m_Crc) { |
eencae | 1:caceb9d9d17a | 101 | //Send CMD59(0x00000001) to enable CRC |
eencae | 1:caceb9d9d17a | 102 | m_Crc = true; |
eencae | 1:caceb9d9d17a | 103 | commandTransaction(CMD59, 0x00000001); |
eencae | 1:caceb9d9d17a | 104 | } else if (!enabled && m_Crc) { |
eencae | 1:caceb9d9d17a | 105 | //Send CMD59(0x00000000) to disable CRC |
eencae | 1:caceb9d9d17a | 106 | commandTransaction(CMD59, 0x00000000); |
eencae | 1:caceb9d9d17a | 107 | m_Crc = false; |
eencae | 1:caceb9d9d17a | 108 | } |
eencae | 1:caceb9d9d17a | 109 | } |
eencae | 1:caceb9d9d17a | 110 | |
eencae | 1:caceb9d9d17a | 111 | bool SDFileSystem::large_frames() |
eencae | 1:caceb9d9d17a | 112 | { |
eencae | 1:caceb9d9d17a | 113 | //Return whether or not 16-bit frames are enabled |
eencae | 1:caceb9d9d17a | 114 | return m_LargeFrames; |
eencae | 1:caceb9d9d17a | 115 | } |
eencae | 1:caceb9d9d17a | 116 | |
eencae | 1:caceb9d9d17a | 117 | void SDFileSystem::large_frames(bool enabled) |
eencae | 1:caceb9d9d17a | 118 | { |
eencae | 1:caceb9d9d17a | 119 | //Set whether or not 16-bit frames are enabled |
eencae | 1:caceb9d9d17a | 120 | m_LargeFrames = enabled; |
eencae | 1:caceb9d9d17a | 121 | } |
eencae | 1:caceb9d9d17a | 122 | |
eencae | 1:caceb9d9d17a | 123 | bool SDFileSystem::write_validation() |
eencae | 1:caceb9d9d17a | 124 | { |
eencae | 1:caceb9d9d17a | 125 | //Return whether or not write validation is enabled |
eencae | 1:caceb9d9d17a | 126 | return m_WriteValidation; |
eencae | 1:caceb9d9d17a | 127 | } |
eencae | 1:caceb9d9d17a | 128 | |
eencae | 1:caceb9d9d17a | 129 | void SDFileSystem::write_validation(bool enabled) |
eencae | 1:caceb9d9d17a | 130 | { |
eencae | 1:caceb9d9d17a | 131 | //Set whether or not write validation is enabled |
eencae | 1:caceb9d9d17a | 132 | m_WriteValidation = enabled; |
eencae | 1:caceb9d9d17a | 133 | } |
eencae | 1:caceb9d9d17a | 134 | |
eencae | 1:caceb9d9d17a | 135 | int SDFileSystem::unmount() |
eencae | 1:caceb9d9d17a | 136 | { |
eencae | 1:caceb9d9d17a | 137 | //Unmount the filesystem |
eencae | 1:caceb9d9d17a | 138 | FATFileSystem::unmount(); |
eencae | 1:caceb9d9d17a | 139 | |
eencae | 1:caceb9d9d17a | 140 | //Change the status to not initialized, and the card type to unknown |
eencae | 1:caceb9d9d17a | 141 | m_Status |= STA_NOINIT; |
eencae | 1:caceb9d9d17a | 142 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 143 | |
eencae | 1:caceb9d9d17a | 144 | //Always succeeds |
eencae | 1:caceb9d9d17a | 145 | return 0; |
eencae | 1:caceb9d9d17a | 146 | } |
eencae | 1:caceb9d9d17a | 147 | |
eencae | 1:caceb9d9d17a | 148 | int SDFileSystem::disk_initialize() |
eencae | 1:caceb9d9d17a | 149 | { |
eencae | 1:caceb9d9d17a | 150 | char token; |
eencae | 1:caceb9d9d17a | 151 | unsigned int resp; |
eencae | 1:caceb9d9d17a | 152 | Timer timer; |
eencae | 1:caceb9d9d17a | 153 | |
eencae | 1:caceb9d9d17a | 154 | //Make sure there's a card in the socket before proceeding |
eencae | 1:caceb9d9d17a | 155 | checkSocket(); |
eencae | 1:caceb9d9d17a | 156 | if (m_Status & STA_NODISK) |
eencae | 1:caceb9d9d17a | 157 | return m_Status; |
eencae | 1:caceb9d9d17a | 158 | |
eencae | 1:caceb9d9d17a | 159 | //Make sure we're not already initialized before proceeding |
eencae | 1:caceb9d9d17a | 160 | if (!(m_Status & STA_NOINIT)) |
eencae | 1:caceb9d9d17a | 161 | return m_Status; |
eencae | 1:caceb9d9d17a | 162 | |
eencae | 1:caceb9d9d17a | 163 | //Set the SPI frequency to 400kHz for initialization |
eencae | 1:caceb9d9d17a | 164 | m_Spi.frequency(400000); |
eencae | 1:caceb9d9d17a | 165 | |
eencae | 1:caceb9d9d17a | 166 | //Try to reset the card up to 3 times |
eencae | 1:caceb9d9d17a | 167 | for (int f = 0; f < 3; f++) { |
eencae | 1:caceb9d9d17a | 168 | //Send 80 dummy clocks with /CS deasserted and DI held high |
eencae | 1:caceb9d9d17a | 169 | m_Cs = 1; |
eencae | 1:caceb9d9d17a | 170 | for (int i = 0; i < 10; i++) { |
eencae | 1:caceb9d9d17a | 171 | m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 172 | } |
eencae | 1:caceb9d9d17a | 173 | |
eencae | 1:caceb9d9d17a | 174 | //Send CMD0(0x00000000) to reset the card |
eencae | 1:caceb9d9d17a | 175 | token = commandTransaction(CMD0, 0x00000000); |
eencae | 1:caceb9d9d17a | 176 | if (token == 0x01) { |
eencae | 1:caceb9d9d17a | 177 | break; |
eencae | 1:caceb9d9d17a | 178 | } |
eencae | 1:caceb9d9d17a | 179 | } |
eencae | 1:caceb9d9d17a | 180 | |
eencae | 1:caceb9d9d17a | 181 | //Check if the card reset |
eencae | 1:caceb9d9d17a | 182 | if (token != 0x01) { |
eencae | 1:caceb9d9d17a | 183 | //Initialization failed |
eencae | 1:caceb9d9d17a | 184 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 185 | return m_Status; |
eencae | 1:caceb9d9d17a | 186 | } |
eencae | 1:caceb9d9d17a | 187 | |
eencae | 1:caceb9d9d17a | 188 | //Send CMD59(0x00000001) to enable CRC if necessary |
eencae | 1:caceb9d9d17a | 189 | if (m_Crc) { |
eencae | 1:caceb9d9d17a | 190 | if (commandTransaction(CMD59, 0x00000001) != 0x01) { |
eencae | 1:caceb9d9d17a | 191 | //Initialization failed |
eencae | 1:caceb9d9d17a | 192 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 193 | return m_Status; |
eencae | 1:caceb9d9d17a | 194 | } |
eencae | 1:caceb9d9d17a | 195 | } |
eencae | 1:caceb9d9d17a | 196 | |
eencae | 1:caceb9d9d17a | 197 | //Send CMD8(0x000001AA) to see if this is an SDCv2 card |
eencae | 1:caceb9d9d17a | 198 | if (commandTransaction(CMD8, 0x000001AA, &resp) == 0x01) { |
eencae | 1:caceb9d9d17a | 199 | //This is an SDCv2 card, get the 32-bit return value and verify the voltage range/check pattern |
eencae | 1:caceb9d9d17a | 200 | if ((resp & 0xFFF) != 0x1AA) { |
eencae | 1:caceb9d9d17a | 201 | //Initialization failed |
eencae | 1:caceb9d9d17a | 202 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 203 | return m_Status; |
eencae | 1:caceb9d9d17a | 204 | } |
eencae | 1:caceb9d9d17a | 205 | |
eencae | 1:caceb9d9d17a | 206 | //Send CMD58(0x00000000) to read the OCR, and verify that the card supports 3.2-3.3V |
eencae | 1:caceb9d9d17a | 207 | if (commandTransaction(CMD58, 0x00000000, &resp) != 0x01 || !(resp & (1 << 20))) { |
eencae | 1:caceb9d9d17a | 208 | //Initialization failed |
eencae | 1:caceb9d9d17a | 209 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 210 | return m_Status; |
eencae | 1:caceb9d9d17a | 211 | } |
eencae | 1:caceb9d9d17a | 212 | |
eencae | 1:caceb9d9d17a | 213 | //Try to initialize the card using ACMD41(0x40100000) for up to 2 seconds |
eencae | 1:caceb9d9d17a | 214 | timer.start(); |
eencae | 1:caceb9d9d17a | 215 | do { |
eencae | 1:caceb9d9d17a | 216 | token = commandTransaction(ACMD41, 0x40100000); |
eencae | 1:caceb9d9d17a | 217 | } while (token == 0x01 && timer.read_ms() < 2000); |
eencae | 1:caceb9d9d17a | 218 | timer.stop(); |
eencae | 1:caceb9d9d17a | 219 | timer.reset(); |
eencae | 1:caceb9d9d17a | 220 | |
eencae | 1:caceb9d9d17a | 221 | //Check if the card initialized |
eencae | 1:caceb9d9d17a | 222 | if (token != 0x00) { |
eencae | 1:caceb9d9d17a | 223 | //Initialization failed |
eencae | 1:caceb9d9d17a | 224 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 225 | return m_Status; |
eencae | 1:caceb9d9d17a | 226 | } |
eencae | 1:caceb9d9d17a | 227 | |
eencae | 1:caceb9d9d17a | 228 | //Send CMD58(0x00000000) to read the OCR |
eencae | 1:caceb9d9d17a | 229 | if (commandTransaction(CMD58, 0x00000000, &resp) == 0x00) { |
eencae | 1:caceb9d9d17a | 230 | //Check the CCS bit to determine if this is a high capacity card |
eencae | 1:caceb9d9d17a | 231 | if (resp & (1 << 30)) |
eencae | 1:caceb9d9d17a | 232 | m_CardType = CARD_SDHC; |
eencae | 1:caceb9d9d17a | 233 | else |
eencae | 1:caceb9d9d17a | 234 | m_CardType = CARD_SD; |
eencae | 1:caceb9d9d17a | 235 | |
eencae | 1:caceb9d9d17a | 236 | //Increase the SPI frequency to full speed (up to 50MHz for SDCv2) |
eencae | 1:caceb9d9d17a | 237 | if (m_FREQ > 25000000) { |
eencae | 1:caceb9d9d17a | 238 | if (enableHighSpeedMode()) { |
eencae | 1:caceb9d9d17a | 239 | if (m_FREQ > 50000000) { |
eencae | 1:caceb9d9d17a | 240 | m_Spi.frequency(50000000); |
eencae | 1:caceb9d9d17a | 241 | } else { |
eencae | 1:caceb9d9d17a | 242 | m_Spi.frequency(m_FREQ); |
eencae | 1:caceb9d9d17a | 243 | } |
eencae | 1:caceb9d9d17a | 244 | } else { |
eencae | 1:caceb9d9d17a | 245 | m_Spi.frequency(25000000); |
eencae | 1:caceb9d9d17a | 246 | } |
eencae | 1:caceb9d9d17a | 247 | } else { |
eencae | 1:caceb9d9d17a | 248 | m_Spi.frequency(m_FREQ); |
eencae | 1:caceb9d9d17a | 249 | } |
eencae | 1:caceb9d9d17a | 250 | } else { |
eencae | 1:caceb9d9d17a | 251 | //Initialization failed |
eencae | 1:caceb9d9d17a | 252 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 253 | return m_Status; |
eencae | 1:caceb9d9d17a | 254 | } |
eencae | 1:caceb9d9d17a | 255 | } else { |
eencae | 1:caceb9d9d17a | 256 | //Didn't respond or illegal command, this is either an SDCv1 or MMC card |
eencae | 1:caceb9d9d17a | 257 | //Send CMD58(0x00000000) to read the OCR, and verify that the card supports 3.2-3.3V |
eencae | 1:caceb9d9d17a | 258 | if (commandTransaction(CMD58, 0x00000000, &resp) != 0x01 || !(resp & (1 << 20))) { |
eencae | 1:caceb9d9d17a | 259 | //Initialization failed |
eencae | 1:caceb9d9d17a | 260 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 261 | return m_Status; |
eencae | 1:caceb9d9d17a | 262 | } |
eencae | 1:caceb9d9d17a | 263 | |
eencae | 1:caceb9d9d17a | 264 | //Try to initialize the card using ACMD41(0x40100000) for up to 2 seconds |
eencae | 1:caceb9d9d17a | 265 | timer.start(); |
eencae | 1:caceb9d9d17a | 266 | do { |
eencae | 1:caceb9d9d17a | 267 | token = commandTransaction(ACMD41, 0x40100000); |
eencae | 1:caceb9d9d17a | 268 | } while (token == 0x01 && timer.read_ms() < 2000); |
eencae | 1:caceb9d9d17a | 269 | timer.stop(); |
eencae | 1:caceb9d9d17a | 270 | timer.reset(); |
eencae | 1:caceb9d9d17a | 271 | |
eencae | 1:caceb9d9d17a | 272 | //Check if the card initialized |
eencae | 1:caceb9d9d17a | 273 | if (token == 0x00) { |
eencae | 1:caceb9d9d17a | 274 | //This is an SDCv1 standard capacity card |
eencae | 1:caceb9d9d17a | 275 | m_CardType = CARD_SD; |
eencae | 1:caceb9d9d17a | 276 | |
eencae | 1:caceb9d9d17a | 277 | //Increase the SPI frequency to full speed (up to 25MHz for SDCv1) |
eencae | 1:caceb9d9d17a | 278 | if (m_FREQ > 25000000) |
eencae | 1:caceb9d9d17a | 279 | m_Spi.frequency(25000000); |
eencae | 1:caceb9d9d17a | 280 | else |
eencae | 1:caceb9d9d17a | 281 | m_Spi.frequency(m_FREQ); |
eencae | 1:caceb9d9d17a | 282 | } else { |
eencae | 1:caceb9d9d17a | 283 | //Try to initialize the card using CMD1(0x00100000) for up to 2 seconds |
eencae | 1:caceb9d9d17a | 284 | timer.start(); |
eencae | 1:caceb9d9d17a | 285 | do { |
eencae | 1:caceb9d9d17a | 286 | token = commandTransaction(CMD1, 0x00100000); |
eencae | 1:caceb9d9d17a | 287 | } while (token == 0x01 && timer.read_ms() < 2000); |
eencae | 1:caceb9d9d17a | 288 | timer.stop(); |
eencae | 1:caceb9d9d17a | 289 | timer.reset(); |
eencae | 1:caceb9d9d17a | 290 | |
eencae | 1:caceb9d9d17a | 291 | //Check if the card initialized |
eencae | 1:caceb9d9d17a | 292 | if (token == 0x00) { |
eencae | 1:caceb9d9d17a | 293 | //This is an MMCv3 card |
eencae | 1:caceb9d9d17a | 294 | m_CardType = CARD_MMC; |
eencae | 1:caceb9d9d17a | 295 | |
eencae | 1:caceb9d9d17a | 296 | //Increase the SPI frequency to full speed (up to 20MHz for MMCv3) |
eencae | 1:caceb9d9d17a | 297 | if (m_FREQ > 20000000) |
eencae | 1:caceb9d9d17a | 298 | m_Spi.frequency(20000000); |
eencae | 1:caceb9d9d17a | 299 | else |
eencae | 1:caceb9d9d17a | 300 | m_Spi.frequency(m_FREQ); |
eencae | 1:caceb9d9d17a | 301 | } else { |
eencae | 1:caceb9d9d17a | 302 | //Initialization failed |
eencae | 1:caceb9d9d17a | 303 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 304 | return m_Status; |
eencae | 1:caceb9d9d17a | 305 | } |
eencae | 1:caceb9d9d17a | 306 | } |
eencae | 1:caceb9d9d17a | 307 | } |
eencae | 1:caceb9d9d17a | 308 | |
eencae | 1:caceb9d9d17a | 309 | //Send ACMD42(0x00000000) to disconnect the internal pull-up resistor on pin 1 if necessary |
eencae | 1:caceb9d9d17a | 310 | if (m_CardType != CARD_MMC) { |
eencae | 1:caceb9d9d17a | 311 | if (commandTransaction(ACMD42, 0x00000000) != 0x00) { |
eencae | 1:caceb9d9d17a | 312 | //Initialization failed |
eencae | 1:caceb9d9d17a | 313 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 314 | return m_Status; |
eencae | 1:caceb9d9d17a | 315 | } |
eencae | 1:caceb9d9d17a | 316 | } |
eencae | 1:caceb9d9d17a | 317 | |
eencae | 1:caceb9d9d17a | 318 | //Send CMD16(0x00000200) to force the block size to 512B if necessary |
eencae | 1:caceb9d9d17a | 319 | if (m_CardType != CARD_SDHC) { |
eencae | 1:caceb9d9d17a | 320 | if (commandTransaction(CMD16, 0x00000200) != 0x00) { |
eencae | 1:caceb9d9d17a | 321 | //Initialization failed |
eencae | 1:caceb9d9d17a | 322 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 323 | return m_Status; |
eencae | 1:caceb9d9d17a | 324 | } |
eencae | 1:caceb9d9d17a | 325 | } |
eencae | 1:caceb9d9d17a | 326 | |
eencae | 1:caceb9d9d17a | 327 | //The card is now initialized |
eencae | 1:caceb9d9d17a | 328 | m_Status &= ~STA_NOINIT; |
eencae | 1:caceb9d9d17a | 329 | |
eencae | 1:caceb9d9d17a | 330 | //Return the disk status |
eencae | 1:caceb9d9d17a | 331 | return m_Status; |
eencae | 1:caceb9d9d17a | 332 | } |
eencae | 1:caceb9d9d17a | 333 | |
eencae | 1:caceb9d9d17a | 334 | int SDFileSystem::disk_status() |
eencae | 1:caceb9d9d17a | 335 | { |
eencae | 1:caceb9d9d17a | 336 | //Check the card socket |
eencae | 1:caceb9d9d17a | 337 | checkSocket(); |
eencae | 1:caceb9d9d17a | 338 | |
eencae | 1:caceb9d9d17a | 339 | //Return the disk status |
eencae | 1:caceb9d9d17a | 340 | return m_Status; |
eencae | 1:caceb9d9d17a | 341 | } |
eencae | 1:caceb9d9d17a | 342 | |
eencae | 1:caceb9d9d17a | 343 | int SDFileSystem::disk_read(uint8_t* buffer, uint32_t sector, uint32_t count) |
eencae | 1:caceb9d9d17a | 344 | { |
eencae | 1:caceb9d9d17a | 345 | //Make sure the card is initialized before proceeding |
eencae | 1:caceb9d9d17a | 346 | if (m_Status & STA_NOINIT) |
eencae | 1:caceb9d9d17a | 347 | return RES_NOTRDY; |
eencae | 1:caceb9d9d17a | 348 | |
eencae | 1:caceb9d9d17a | 349 | //Read a single block, or multiple blocks |
eencae | 1:caceb9d9d17a | 350 | if (count > 1) { |
eencae | 1:caceb9d9d17a | 351 | return readBlocks((char*)buffer, sector, count) ? RES_OK : RES_ERROR; |
eencae | 1:caceb9d9d17a | 352 | } else { |
eencae | 1:caceb9d9d17a | 353 | return readBlock((char*)buffer, sector) ? RES_OK : RES_ERROR; |
eencae | 1:caceb9d9d17a | 354 | } |
eencae | 1:caceb9d9d17a | 355 | } |
eencae | 1:caceb9d9d17a | 356 | |
eencae | 1:caceb9d9d17a | 357 | int SDFileSystem::disk_write(const uint8_t* buffer, uint32_t sector, uint32_t count) |
eencae | 1:caceb9d9d17a | 358 | { |
eencae | 1:caceb9d9d17a | 359 | //Make sure the card is initialized before proceeding |
eencae | 1:caceb9d9d17a | 360 | if (m_Status & STA_NOINIT) |
eencae | 1:caceb9d9d17a | 361 | return RES_NOTRDY; |
eencae | 1:caceb9d9d17a | 362 | |
eencae | 1:caceb9d9d17a | 363 | //Make sure the card isn't write protected before proceeding |
eencae | 1:caceb9d9d17a | 364 | if (m_Status & STA_PROTECT) |
eencae | 1:caceb9d9d17a | 365 | return RES_WRPRT; |
eencae | 1:caceb9d9d17a | 366 | |
eencae | 1:caceb9d9d17a | 367 | //Write a single block, or multiple blocks |
eencae | 1:caceb9d9d17a | 368 | if (count > 1) { |
eencae | 1:caceb9d9d17a | 369 | return writeBlocks((const char*)buffer, sector, count) ? RES_OK : RES_ERROR; |
eencae | 1:caceb9d9d17a | 370 | } else { |
eencae | 1:caceb9d9d17a | 371 | return writeBlock((const char*)buffer, sector) ? RES_OK : RES_ERROR; |
eencae | 1:caceb9d9d17a | 372 | } |
eencae | 1:caceb9d9d17a | 373 | } |
eencae | 1:caceb9d9d17a | 374 | |
eencae | 1:caceb9d9d17a | 375 | int SDFileSystem::disk_sync() |
eencae | 1:caceb9d9d17a | 376 | { |
eencae | 1:caceb9d9d17a | 377 | //Select the card so we're forced to wait for the end of any internal write processes |
eencae | 1:caceb9d9d17a | 378 | if (select()) { |
eencae | 1:caceb9d9d17a | 379 | deselect(); |
eencae | 1:caceb9d9d17a | 380 | return RES_OK; |
eencae | 1:caceb9d9d17a | 381 | } else { |
eencae | 1:caceb9d9d17a | 382 | return RES_ERROR; |
eencae | 1:caceb9d9d17a | 383 | } |
eencae | 1:caceb9d9d17a | 384 | } |
eencae | 1:caceb9d9d17a | 385 | |
eencae | 1:caceb9d9d17a | 386 | uint32_t SDFileSystem::disk_sectors() |
eencae | 1:caceb9d9d17a | 387 | { |
eencae | 1:caceb9d9d17a | 388 | //Make sure the card is initialized before proceeding |
eencae | 1:caceb9d9d17a | 389 | if (m_Status & STA_NOINIT) |
eencae | 1:caceb9d9d17a | 390 | return 0; |
eencae | 1:caceb9d9d17a | 391 | |
eencae | 1:caceb9d9d17a | 392 | //Try to read the CSD register up to 3 times |
eencae | 1:caceb9d9d17a | 393 | for (int f = 0; f < 3; f++) { |
eencae | 1:caceb9d9d17a | 394 | //Select the card, and wait for ready |
eencae | 1:caceb9d9d17a | 395 | if(!select()) |
eencae | 1:caceb9d9d17a | 396 | break; |
eencae | 1:caceb9d9d17a | 397 | |
eencae | 1:caceb9d9d17a | 398 | //Send CMD9(0x00000000) to read the CSD register |
eencae | 1:caceb9d9d17a | 399 | if (writeCommand(CMD9, 0x00000000) == 0x00) { |
eencae | 1:caceb9d9d17a | 400 | //Read the 16B CSD data block |
eencae | 1:caceb9d9d17a | 401 | char csd[16]; |
eencae | 1:caceb9d9d17a | 402 | bool success = readData(csd, 16); |
eencae | 1:caceb9d9d17a | 403 | deselect(); |
eencae | 1:caceb9d9d17a | 404 | if (success) { |
eencae | 1:caceb9d9d17a | 405 | //Calculate the sector count based on the card type |
eencae | 1:caceb9d9d17a | 406 | if ((csd[0] >> 6) == 0x01) { |
eencae | 1:caceb9d9d17a | 407 | //Calculate the sector count for a high capacity card |
eencae | 1:caceb9d9d17a | 408 | unsigned int size = (((csd[7] & 0x3F) << 16) | (csd[8] << 8) | csd[9]) + 1; |
eencae | 1:caceb9d9d17a | 409 | return size << 10; |
eencae | 1:caceb9d9d17a | 410 | } else { |
eencae | 1:caceb9d9d17a | 411 | //Calculate the sector count for a standard capacity card |
eencae | 1:caceb9d9d17a | 412 | unsigned int size = (((csd[6] & 0x03) << 10) | (csd[7] << 2) | ((csd[8] & 0xC0) >> 6)) + 1; |
eencae | 1:caceb9d9d17a | 413 | size <<= ((((csd[9] & 0x03) << 1) | ((csd[10] & 0x80) >> 7)) + 2); |
eencae | 1:caceb9d9d17a | 414 | size <<= (csd[5] & 0x0F); |
eencae | 1:caceb9d9d17a | 415 | return size >> 9; |
eencae | 1:caceb9d9d17a | 416 | } |
eencae | 1:caceb9d9d17a | 417 | } |
eencae | 1:caceb9d9d17a | 418 | } else { |
eencae | 1:caceb9d9d17a | 419 | //The command failed, get out |
eencae | 1:caceb9d9d17a | 420 | break; |
eencae | 1:caceb9d9d17a | 421 | } |
eencae | 1:caceb9d9d17a | 422 | } |
eencae | 1:caceb9d9d17a | 423 | |
eencae | 1:caceb9d9d17a | 424 | //The read operation failed 3 times |
eencae | 1:caceb9d9d17a | 425 | deselect(); |
eencae | 1:caceb9d9d17a | 426 | return 0; |
eencae | 1:caceb9d9d17a | 427 | } |
eencae | 1:caceb9d9d17a | 428 | |
eencae | 1:caceb9d9d17a | 429 | void SDFileSystem::onCardRemoval() |
eencae | 1:caceb9d9d17a | 430 | { |
eencae | 1:caceb9d9d17a | 431 | //Check the card socket |
eencae | 1:caceb9d9d17a | 432 | checkSocket(); |
eencae | 1:caceb9d9d17a | 433 | } |
eencae | 1:caceb9d9d17a | 434 | |
eencae | 1:caceb9d9d17a | 435 | inline void SDFileSystem::checkSocket() |
eencae | 1:caceb9d9d17a | 436 | { |
eencae | 1:caceb9d9d17a | 437 | //Use the card detect switch (if available) to determine if the socket is occupied |
eencae | 1:caceb9d9d17a | 438 | if (m_CdAssert != -1) { |
eencae | 1:caceb9d9d17a | 439 | if (m_Status & STA_NODISK) { |
eencae | 1:caceb9d9d17a | 440 | if (m_Cd == m_CdAssert) { |
eencae | 1:caceb9d9d17a | 441 | //The socket is now occupied |
eencae | 1:caceb9d9d17a | 442 | m_Status &= ~STA_NODISK; |
eencae | 1:caceb9d9d17a | 443 | m_CardType = CARD_UNKNOWN; |
eencae | 1:caceb9d9d17a | 444 | } |
eencae | 1:caceb9d9d17a | 445 | } else { |
eencae | 1:caceb9d9d17a | 446 | if (m_Cd != m_CdAssert) { |
eencae | 1:caceb9d9d17a | 447 | //The socket is now empty |
eencae | 1:caceb9d9d17a | 448 | m_Status |= (STA_NODISK | STA_NOINIT); |
eencae | 1:caceb9d9d17a | 449 | m_CardType = CARD_NONE; |
eencae | 1:caceb9d9d17a | 450 | } |
eencae | 1:caceb9d9d17a | 451 | } |
eencae | 1:caceb9d9d17a | 452 | } |
eencae | 1:caceb9d9d17a | 453 | } |
eencae | 1:caceb9d9d17a | 454 | |
eencae | 1:caceb9d9d17a | 455 | inline bool SDFileSystem::waitReady(int timeout) |
eencae | 1:caceb9d9d17a | 456 | { |
eencae | 1:caceb9d9d17a | 457 | char resp; |
eencae | 1:caceb9d9d17a | 458 | |
eencae | 1:caceb9d9d17a | 459 | //Keep sending dummy clocks with DI held high until the card releases the DO line |
eencae | 1:caceb9d9d17a | 460 | m_Timer.start(); |
eencae | 1:caceb9d9d17a | 461 | do { |
eencae | 1:caceb9d9d17a | 462 | resp = m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 463 | } while (resp == 0x00 && m_Timer.read_ms() < timeout); |
eencae | 1:caceb9d9d17a | 464 | m_Timer.stop(); |
eencae | 1:caceb9d9d17a | 465 | m_Timer.reset(); |
eencae | 1:caceb9d9d17a | 466 | |
eencae | 1:caceb9d9d17a | 467 | //Return success/failure |
eencae | 1:caceb9d9d17a | 468 | return (resp > 0x00); |
eencae | 1:caceb9d9d17a | 469 | } |
eencae | 1:caceb9d9d17a | 470 | |
eencae | 1:caceb9d9d17a | 471 | inline bool SDFileSystem::select() |
eencae | 1:caceb9d9d17a | 472 | { |
eencae | 1:caceb9d9d17a | 473 | //Assert /CS |
eencae | 1:caceb9d9d17a | 474 | m_Cs = 0; |
eencae | 1:caceb9d9d17a | 475 | |
eencae | 1:caceb9d9d17a | 476 | //Send 8 dummy clocks with DI held high to enable DO |
eencae | 1:caceb9d9d17a | 477 | m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 478 | |
eencae | 1:caceb9d9d17a | 479 | //Wait for up to 500ms for the card to become ready |
eencae | 1:caceb9d9d17a | 480 | if (waitReady(500)) { |
eencae | 1:caceb9d9d17a | 481 | return true; |
eencae | 1:caceb9d9d17a | 482 | } else { |
eencae | 1:caceb9d9d17a | 483 | //We timed out, deselect and return false |
eencae | 1:caceb9d9d17a | 484 | deselect(); |
eencae | 1:caceb9d9d17a | 485 | return false; |
eencae | 1:caceb9d9d17a | 486 | } |
eencae | 1:caceb9d9d17a | 487 | } |
eencae | 1:caceb9d9d17a | 488 | |
eencae | 1:caceb9d9d17a | 489 | inline void SDFileSystem::deselect() |
eencae | 1:caceb9d9d17a | 490 | { |
eencae | 1:caceb9d9d17a | 491 | //Deassert /CS |
eencae | 1:caceb9d9d17a | 492 | m_Cs = 1; |
eencae | 1:caceb9d9d17a | 493 | |
eencae | 1:caceb9d9d17a | 494 | //Send 8 dummy clocks with DI held high to disable DO |
eencae | 1:caceb9d9d17a | 495 | m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 496 | } |
eencae | 1:caceb9d9d17a | 497 | |
eencae | 1:caceb9d9d17a | 498 | inline char SDFileSystem::commandTransaction(char cmd, unsigned int arg, unsigned int* resp) |
eencae | 1:caceb9d9d17a | 499 | { |
eencae | 1:caceb9d9d17a | 500 | //Select the card, and wait for ready |
eencae | 1:caceb9d9d17a | 501 | if(!select()) |
eencae | 1:caceb9d9d17a | 502 | return 0xFF; |
eencae | 1:caceb9d9d17a | 503 | |
eencae | 1:caceb9d9d17a | 504 | //Perform the command transaction |
eencae | 1:caceb9d9d17a | 505 | char token = writeCommand(cmd, arg, resp); |
eencae | 1:caceb9d9d17a | 506 | |
eencae | 1:caceb9d9d17a | 507 | //Deselect the card, and return the R1 response token |
eencae | 1:caceb9d9d17a | 508 | deselect(); |
eencae | 1:caceb9d9d17a | 509 | return token; |
eencae | 1:caceb9d9d17a | 510 | } |
eencae | 1:caceb9d9d17a | 511 | |
eencae | 1:caceb9d9d17a | 512 | char SDFileSystem::writeCommand(char cmd, unsigned int arg, unsigned int* resp) |
eencae | 1:caceb9d9d17a | 513 | { |
eencae | 1:caceb9d9d17a | 514 | char token; |
eencae | 1:caceb9d9d17a | 515 | |
eencae | 1:caceb9d9d17a | 516 | //Try to send the command up to 3 times |
eencae | 1:caceb9d9d17a | 517 | for (int f = 0; f < 3; f++) { |
eencae | 1:caceb9d9d17a | 518 | //Send CMD55(0x00000000) prior to an application specific command |
eencae | 1:caceb9d9d17a | 519 | if (cmd == ACMD22 || cmd == ACMD23 || cmd == ACMD41 || cmd == ACMD42) { |
eencae | 1:caceb9d9d17a | 520 | token = writeCommand(CMD55, 0x00000000); |
eencae | 1:caceb9d9d17a | 521 | if (token > 0x01) |
eencae | 1:caceb9d9d17a | 522 | return token; |
eencae | 1:caceb9d9d17a | 523 | |
eencae | 1:caceb9d9d17a | 524 | //Deselect and reselect the card between CMD55 and an ACMD |
eencae | 1:caceb9d9d17a | 525 | deselect(); |
eencae | 1:caceb9d9d17a | 526 | if(!select()) |
eencae | 1:caceb9d9d17a | 527 | return 0xFF; |
eencae | 1:caceb9d9d17a | 528 | } |
eencae | 1:caceb9d9d17a | 529 | |
eencae | 1:caceb9d9d17a | 530 | //Prepare the command packet |
eencae | 1:caceb9d9d17a | 531 | char cmdPacket[6]; |
eencae | 1:caceb9d9d17a | 532 | cmdPacket[0] = cmd; |
eencae | 1:caceb9d9d17a | 533 | cmdPacket[1] = arg >> 24; |
eencae | 1:caceb9d9d17a | 534 | cmdPacket[2] = arg >> 16; |
eencae | 1:caceb9d9d17a | 535 | cmdPacket[3] = arg >> 8; |
eencae | 1:caceb9d9d17a | 536 | cmdPacket[4] = arg; |
eencae | 1:caceb9d9d17a | 537 | if (m_Crc || cmd == CMD0 || cmd == CMD8) |
eencae | 1:caceb9d9d17a | 538 | cmdPacket[5] = (SDCRC::crc7(cmdPacket, 5) << 1) | 0x01; |
eencae | 1:caceb9d9d17a | 539 | else |
eencae | 1:caceb9d9d17a | 540 | cmdPacket[5] = 0x01; |
eencae | 1:caceb9d9d17a | 541 | |
eencae | 1:caceb9d9d17a | 542 | //Send the command packet |
eencae | 1:caceb9d9d17a | 543 | for (int i = 0; i < 6; i++) |
eencae | 1:caceb9d9d17a | 544 | m_Spi.write(cmdPacket[i]); |
eencae | 1:caceb9d9d17a | 545 | |
eencae | 1:caceb9d9d17a | 546 | //Discard the stuff byte immediately following CMD12 |
eencae | 1:caceb9d9d17a | 547 | if (cmd == CMD12) |
eencae | 1:caceb9d9d17a | 548 | m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 549 | |
eencae | 1:caceb9d9d17a | 550 | //Allow up to 8 bytes of delay for the R1 response token |
eencae | 1:caceb9d9d17a | 551 | for (int i = 0; i < 9; i++) { |
eencae | 1:caceb9d9d17a | 552 | token = m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 553 | if (!(token & 0x80)) |
eencae | 1:caceb9d9d17a | 554 | break; |
eencae | 1:caceb9d9d17a | 555 | } |
eencae | 1:caceb9d9d17a | 556 | |
eencae | 1:caceb9d9d17a | 557 | //Verify the R1 response token |
eencae | 1:caceb9d9d17a | 558 | if (token == 0xFF) { |
eencae | 1:caceb9d9d17a | 559 | //No data was received, get out early |
eencae | 1:caceb9d9d17a | 560 | break; |
eencae | 1:caceb9d9d17a | 561 | } else if (token & (1 << 3)) { |
eencae | 1:caceb9d9d17a | 562 | //There was a CRC error, try again |
eencae | 1:caceb9d9d17a | 563 | continue; |
eencae | 1:caceb9d9d17a | 564 | } else if (token > 0x01) { |
eencae | 1:caceb9d9d17a | 565 | //An error occured, get out early |
eencae | 1:caceb9d9d17a | 566 | break; |
eencae | 1:caceb9d9d17a | 567 | } |
eencae | 1:caceb9d9d17a | 568 | |
eencae | 1:caceb9d9d17a | 569 | //Handle R2 and R3/R7 response tokens |
eencae | 1:caceb9d9d17a | 570 | if (cmd == CMD13 && resp != NULL) { |
eencae | 1:caceb9d9d17a | 571 | //Read the R2 response value |
eencae | 1:caceb9d9d17a | 572 | *resp = m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 573 | } else if ((cmd == CMD8 || cmd == CMD58) && resp != NULL) { |
eencae | 1:caceb9d9d17a | 574 | //Read the R3/R7 response value |
eencae | 1:caceb9d9d17a | 575 | *resp = (m_Spi.write(0xFF) << 24); |
eencae | 1:caceb9d9d17a | 576 | *resp |= (m_Spi.write(0xFF) << 16); |
eencae | 1:caceb9d9d17a | 577 | *resp |= (m_Spi.write(0xFF) << 8); |
eencae | 1:caceb9d9d17a | 578 | *resp |= m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 579 | } |
eencae | 1:caceb9d9d17a | 580 | |
eencae | 1:caceb9d9d17a | 581 | //The command was successful |
eencae | 1:caceb9d9d17a | 582 | break; |
eencae | 1:caceb9d9d17a | 583 | } |
eencae | 1:caceb9d9d17a | 584 | |
eencae | 1:caceb9d9d17a | 585 | //Return the R1 response token |
eencae | 1:caceb9d9d17a | 586 | return token; |
eencae | 1:caceb9d9d17a | 587 | } |
eencae | 1:caceb9d9d17a | 588 | |
eencae | 1:caceb9d9d17a | 589 | bool SDFileSystem::readData(char* buffer, int length) |
eencae | 1:caceb9d9d17a | 590 | { |
eencae | 1:caceb9d9d17a | 591 | char token; |
eencae | 1:caceb9d9d17a | 592 | unsigned short crc; |
eencae | 1:caceb9d9d17a | 593 | |
eencae | 1:caceb9d9d17a | 594 | //Wait for up to 500ms for a token to arrive |
eencae | 1:caceb9d9d17a | 595 | m_Timer.start(); |
eencae | 1:caceb9d9d17a | 596 | do { |
eencae | 1:caceb9d9d17a | 597 | token = m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 598 | } while (token == 0xFF && m_Timer.read_ms() < 500); |
eencae | 1:caceb9d9d17a | 599 | m_Timer.stop(); |
eencae | 1:caceb9d9d17a | 600 | m_Timer.reset(); |
eencae | 1:caceb9d9d17a | 601 | |
eencae | 1:caceb9d9d17a | 602 | //Check if a valid start block token was received |
eencae | 1:caceb9d9d17a | 603 | if (token != 0xFE) |
eencae | 1:caceb9d9d17a | 604 | return false; |
eencae | 1:caceb9d9d17a | 605 | |
eencae | 1:caceb9d9d17a | 606 | //Check if large frames are enabled or not |
eencae | 1:caceb9d9d17a | 607 | if (m_LargeFrames) { |
eencae | 1:caceb9d9d17a | 608 | //Switch to 16-bit frames for better performance |
eencae | 1:caceb9d9d17a | 609 | m_Spi.format(16, 0); |
eencae | 1:caceb9d9d17a | 610 | |
eencae | 1:caceb9d9d17a | 611 | //Read the data block into the buffer |
eencae | 1:caceb9d9d17a | 612 | unsigned short dataWord; |
eencae | 1:caceb9d9d17a | 613 | for (int i = 0; i < length; i += 2) { |
eencae | 1:caceb9d9d17a | 614 | dataWord = m_Spi.write(0xFFFF); |
eencae | 1:caceb9d9d17a | 615 | buffer[i] = dataWord >> 8; |
eencae | 1:caceb9d9d17a | 616 | buffer[i + 1] = dataWord; |
eencae | 1:caceb9d9d17a | 617 | } |
eencae | 1:caceb9d9d17a | 618 | |
eencae | 1:caceb9d9d17a | 619 | //Read the CRC16 checksum for the data block |
eencae | 1:caceb9d9d17a | 620 | crc = m_Spi.write(0xFFFF); |
eencae | 1:caceb9d9d17a | 621 | |
eencae | 1:caceb9d9d17a | 622 | //Switch back to 8-bit frames |
eencae | 1:caceb9d9d17a | 623 | m_Spi.format(8, 0); |
eencae | 1:caceb9d9d17a | 624 | } else { |
eencae | 1:caceb9d9d17a | 625 | //Read the data into the buffer |
eencae | 1:caceb9d9d17a | 626 | for (int i = 0; i < length; i++) |
eencae | 1:caceb9d9d17a | 627 | buffer[i] = m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 628 | |
eencae | 1:caceb9d9d17a | 629 | //Read the CRC16 checksum for the data block |
eencae | 1:caceb9d9d17a | 630 | crc = (m_Spi.write(0xFF) << 8); |
eencae | 1:caceb9d9d17a | 631 | crc |= m_Spi.write(0xFF); |
eencae | 1:caceb9d9d17a | 632 | } |
eencae | 1:caceb9d9d17a | 633 | |
eencae | 1:caceb9d9d17a | 634 | //Return the validity of the CRC16 checksum (if enabled) |
eencae | 1:caceb9d9d17a | 635 | return (!m_Crc || crc == SDCRC::crc16(buffer, length)); |
eencae | 1:caceb9d9d17a | 636 | } |
eencae | 1:caceb9d9d17a | 637 | |
eencae | 1:caceb9d9d17a | 638 | char SDFileSystem::writeData(const char* buffer, char token) |
eencae | 1:caceb9d9d17a | 639 | { |
eencae | 1:caceb9d9d17a | 640 | //Calculate the CRC16 checksum for the data block (if enabled) |
eencae | 1:caceb9d9d17a | 641 | unsigned short crc = (m_Crc) ? SDCRC::crc16(buffer, 512) : 0xFFFF; |
eencae | 1:caceb9d9d17a | 642 | |
eencae | 1:caceb9d9d17a | 643 | //Wait for up to 500ms for the card to become ready |
eencae | 1:caceb9d9d17a | 644 | if (!waitReady(500)) |
eencae | 1:caceb9d9d17a | 645 | return false; |
eencae | 1:caceb9d9d17a | 646 | |
eencae | 1:caceb9d9d17a | 647 | //Send the start block token |
eencae | 1:caceb9d9d17a | 648 | m_Spi.write(token); |
eencae | 1:caceb9d9d17a | 649 | |
eencae | 1:caceb9d9d17a | 650 | //Check if large frames are enabled or not |
eencae | 1:caceb9d9d17a | 651 | if (m_LargeFrames) { |
eencae | 1:caceb9d9d17a | 652 | //Switch to 16-bit frames for better performance |
eencae | 1:caceb9d9d17a | 653 | m_Spi.format(16, 0); |
eencae | 1:caceb9d9d17a | 654 | |
eencae | 1:caceb9d9d17a | 655 | //Write the data block from the buffer |
eencae | 1:caceb9d9d17a | 656 | for (int i = 0; i < 512; i += 2) |
eencae | 1:caceb9d9d17a | 657 | m_Spi.write((buffer[i] << 8) | buffer[i + 1]); |
eencae | 1:caceb9d9d17a | 658 | |
eencae | 1:caceb9d9d17a | 659 | //Send the CRC16 checksum for the data block |
eencae | 1:caceb9d9d17a | 660 | m_Spi.write(crc); |
eencae | 1:caceb9d9d17a | 661 | |
eencae | 1:caceb9d9d17a | 662 | //Switch back to 8-bit frames |
eencae | 1:caceb9d9d17a | 663 | m_Spi.format(8, 0); |
eencae | 1:caceb9d9d17a | 664 | } else { |
eencae | 1:caceb9d9d17a | 665 | //Write the data block from the buffer |
eencae | 1:caceb9d9d17a | 666 | for (int i = 0; i < 512; i++) |
eencae | 1:caceb9d9d17a | 667 | m_Spi.write(buffer[i]); |
eencae | 1:caceb9d9d17a | 668 | |
eencae | 1:caceb9d9d17a | 669 | //Send the CRC16 checksum for the data block |
eencae | 1:caceb9d9d17a | 670 | m_Spi.write(crc >> 8); |
eencae | 1:caceb9d9d17a | 671 | m_Spi.write(crc); |
eencae | 1:caceb9d9d17a | 672 | } |
eencae | 1:caceb9d9d17a | 673 | |
eencae | 1:caceb9d9d17a | 674 | //Return the data response token |
eencae | 1:caceb9d9d17a | 675 | return (m_Spi.write(0xFF) & 0x1F); |
eencae | 1:caceb9d9d17a | 676 | } |
eencae | 1:caceb9d9d17a | 677 | |
eencae | 1:caceb9d9d17a | 678 | inline bool SDFileSystem::readBlock(char* buffer, unsigned int lba) |
eencae | 1:caceb9d9d17a | 679 | { |
eencae | 1:caceb9d9d17a | 680 | //Try to read the block up to 3 times |
eencae | 1:caceb9d9d17a | 681 | for (int f = 0; f < 3; f++) { |
eencae | 1:caceb9d9d17a | 682 | //Select the card, and wait for ready |
eencae | 1:caceb9d9d17a | 683 | if(!select()) |
eencae | 1:caceb9d9d17a | 684 | break; |
eencae | 1:caceb9d9d17a | 685 | |
eencae | 1:caceb9d9d17a | 686 | //Send CMD17(block) to read a single block |
eencae | 1:caceb9d9d17a | 687 | if (writeCommand(CMD17, (m_CardType == CARD_SDHC) ? lba : lba << 9) == 0x00) { |
eencae | 1:caceb9d9d17a | 688 | //Try to read the block, and deselect the card |
eencae | 1:caceb9d9d17a | 689 | bool success = readData(buffer, 512); |
eencae | 1:caceb9d9d17a | 690 | deselect(); |
eencae | 1:caceb9d9d17a | 691 | |
eencae | 1:caceb9d9d17a | 692 | //Return if successful |
eencae | 1:caceb9d9d17a | 693 | if (success) |
eencae | 1:caceb9d9d17a | 694 | return true; |
eencae | 1:caceb9d9d17a | 695 | } else { |
eencae | 1:caceb9d9d17a | 696 | //The command failed, get out |
eencae | 1:caceb9d9d17a | 697 | break; |
eencae | 1:caceb9d9d17a | 698 | } |
eencae | 1:caceb9d9d17a | 699 | } |
eencae | 1:caceb9d9d17a | 700 | |
eencae | 1:caceb9d9d17a | 701 | //The single block read failed |
eencae | 1:caceb9d9d17a | 702 | deselect(); |
eencae | 1:caceb9d9d17a | 703 | return false; |
eencae | 1:caceb9d9d17a | 704 | } |
eencae | 1:caceb9d9d17a | 705 | |
eencae | 1:caceb9d9d17a | 706 | inline bool SDFileSystem::readBlocks(char* buffer, unsigned int lba, unsigned int count) |
eencae | 1:caceb9d9d17a | 707 | { |
eencae | 1:caceb9d9d17a | 708 | //Try to read each block up to 3 times |
eencae | 1:caceb9d9d17a | 709 | for (int f = 0; f < 3;) { |
eencae | 1:caceb9d9d17a | 710 | //Select the card, and wait for ready |
eencae | 1:caceb9d9d17a | 711 | if(!select()) |
eencae | 1:caceb9d9d17a | 712 | break; |
eencae | 1:caceb9d9d17a | 713 | |
eencae | 1:caceb9d9d17a | 714 | //Send CMD18(block) to read multiple blocks |
eencae | 1:caceb9d9d17a | 715 | if (writeCommand(CMD18, (m_CardType == CARD_SDHC) ? lba : lba << 9) == 0x00) { |
eencae | 1:caceb9d9d17a | 716 | //Try to read all of the data blocks |
eencae | 1:caceb9d9d17a | 717 | do { |
eencae | 1:caceb9d9d17a | 718 | //Read the next block, and break on errors |
eencae | 1:caceb9d9d17a | 719 | if (!readData(buffer, 512)) { |
eencae | 1:caceb9d9d17a | 720 | f++; |
eencae | 1:caceb9d9d17a | 721 | break; |
eencae | 1:caceb9d9d17a | 722 | } |
eencae | 1:caceb9d9d17a | 723 | |
eencae | 1:caceb9d9d17a | 724 | //Update the variables |
eencae | 1:caceb9d9d17a | 725 | lba++; |
eencae | 1:caceb9d9d17a | 726 | buffer += 512; |
eencae | 1:caceb9d9d17a | 727 | f = 0; |
eencae | 1:caceb9d9d17a | 728 | } while (--count); |
eencae | 1:caceb9d9d17a | 729 | |
eencae | 1:caceb9d9d17a | 730 | //Send CMD12(0x00000000) to stop the transmission |
eencae | 1:caceb9d9d17a | 731 | if (writeCommand(CMD12, 0x00000000) != 0x00) { |
eencae | 1:caceb9d9d17a | 732 | //The command failed, get out |
eencae | 1:caceb9d9d17a | 733 | break; |
eencae | 1:caceb9d9d17a | 734 | } |
eencae | 1:caceb9d9d17a | 735 | |
eencae | 1:caceb9d9d17a | 736 | //Deselect the card, and return if successful |
eencae | 1:caceb9d9d17a | 737 | deselect(); |
eencae | 1:caceb9d9d17a | 738 | if (count == 0) |
eencae | 1:caceb9d9d17a | 739 | return true; |
eencae | 1:caceb9d9d17a | 740 | } else { |
eencae | 1:caceb9d9d17a | 741 | //The command failed, get out |
eencae | 1:caceb9d9d17a | 742 | break; |
eencae | 1:caceb9d9d17a | 743 | } |
eencae | 1:caceb9d9d17a | 744 | } |
eencae | 1:caceb9d9d17a | 745 | |
eencae | 1:caceb9d9d17a | 746 | //The multiple block read failed |
eencae | 1:caceb9d9d17a | 747 | deselect(); |
eencae | 1:caceb9d9d17a | 748 | return false; |
eencae | 1:caceb9d9d17a | 749 | } |
eencae | 1:caceb9d9d17a | 750 | |
eencae | 1:caceb9d9d17a | 751 | inline bool SDFileSystem::writeBlock(const char* buffer, unsigned int lba) |
eencae | 1:caceb9d9d17a | 752 | { |
eencae | 1:caceb9d9d17a | 753 | //Try to write the block up to 3 times |
eencae | 1:caceb9d9d17a | 754 | for (int f = 0; f < 3; f++) { |
eencae | 1:caceb9d9d17a | 755 | //Select the card, and wait for ready |
eencae | 1:caceb9d9d17a | 756 | if(!select()) |
eencae | 1:caceb9d9d17a | 757 | break; |
eencae | 1:caceb9d9d17a | 758 | |
eencae | 1:caceb9d9d17a | 759 | //Send CMD24(block) to write a single block |
eencae | 1:caceb9d9d17a | 760 | if (writeCommand(CMD24, (m_CardType == CARD_SDHC) ? lba : lba << 9) == 0x00) { |
eencae | 1:caceb9d9d17a | 761 | //Try to write the block, and deselect the card |
eencae | 1:caceb9d9d17a | 762 | char token = writeData(buffer, 0xFE); |
eencae | 1:caceb9d9d17a | 763 | deselect(); |
eencae | 1:caceb9d9d17a | 764 | |
eencae | 1:caceb9d9d17a | 765 | //Check the data response token |
eencae | 1:caceb9d9d17a | 766 | if (token == 0x0A) { |
eencae | 1:caceb9d9d17a | 767 | //A CRC error occured, try again |
eencae | 1:caceb9d9d17a | 768 | continue; |
eencae | 1:caceb9d9d17a | 769 | } else if (token == 0x0C) { |
eencae | 1:caceb9d9d17a | 770 | //A write error occured, get out |
eencae | 1:caceb9d9d17a | 771 | break; |
eencae | 1:caceb9d9d17a | 772 | } |
eencae | 1:caceb9d9d17a | 773 | |
eencae | 1:caceb9d9d17a | 774 | //Send CMD13(0x00000000) to verify that the programming was successful if enabled |
eencae | 1:caceb9d9d17a | 775 | if (m_WriteValidation) { |
eencae | 1:caceb9d9d17a | 776 | unsigned int resp; |
eencae | 1:caceb9d9d17a | 777 | if (commandTransaction(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) { |
eencae | 1:caceb9d9d17a | 778 | //Some manner of unrecoverable write error occured during programming, get out |
eencae | 1:caceb9d9d17a | 779 | break; |
eencae | 1:caceb9d9d17a | 780 | } |
eencae | 1:caceb9d9d17a | 781 | } |
eencae | 1:caceb9d9d17a | 782 | |
eencae | 1:caceb9d9d17a | 783 | //The data was written successfully |
eencae | 1:caceb9d9d17a | 784 | return true; |
eencae | 1:caceb9d9d17a | 785 | } else { |
eencae | 1:caceb9d9d17a | 786 | //The command failed, get out |
eencae | 1:caceb9d9d17a | 787 | break; |
eencae | 1:caceb9d9d17a | 788 | } |
eencae | 1:caceb9d9d17a | 789 | } |
eencae | 1:caceb9d9d17a | 790 | |
eencae | 1:caceb9d9d17a | 791 | //The single block write failed |
eencae | 1:caceb9d9d17a | 792 | deselect(); |
eencae | 1:caceb9d9d17a | 793 | return false; |
eencae | 1:caceb9d9d17a | 794 | } |
eencae | 1:caceb9d9d17a | 795 | |
eencae | 1:caceb9d9d17a | 796 | inline bool SDFileSystem::writeBlocks(const char* buffer, unsigned int lba, unsigned int count) |
eencae | 1:caceb9d9d17a | 797 | { |
eencae | 1:caceb9d9d17a | 798 | char token; |
eencae | 1:caceb9d9d17a | 799 | const char* currentBuffer = buffer; |
eencae | 1:caceb9d9d17a | 800 | unsigned int currentLba = lba; |
eencae | 1:caceb9d9d17a | 801 | int currentCount = count; |
eencae | 1:caceb9d9d17a | 802 | |
eencae | 1:caceb9d9d17a | 803 | //Try to write each block up to 3 times |
eencae | 1:caceb9d9d17a | 804 | for (int f = 0; f < 3;) { |
eencae | 1:caceb9d9d17a | 805 | //If this is an SD card, send ACMD23(count) to set the number of blocks to pre-erase |
eencae | 1:caceb9d9d17a | 806 | if (m_CardType != CARD_MMC) { |
eencae | 1:caceb9d9d17a | 807 | if (commandTransaction(ACMD23, currentCount) != 0x00) { |
eencae | 1:caceb9d9d17a | 808 | //The command failed, get out |
eencae | 1:caceb9d9d17a | 809 | break; |
eencae | 1:caceb9d9d17a | 810 | } |
eencae | 1:caceb9d9d17a | 811 | } |
eencae | 1:caceb9d9d17a | 812 | |
eencae | 1:caceb9d9d17a | 813 | //Select the card, and wait for ready |
eencae | 1:caceb9d9d17a | 814 | if(!select()) |
eencae | 1:caceb9d9d17a | 815 | break; |
eencae | 1:caceb9d9d17a | 816 | |
eencae | 1:caceb9d9d17a | 817 | //Send CMD25(block) to write multiple blocks |
eencae | 1:caceb9d9d17a | 818 | if (writeCommand(CMD25, (m_CardType == CARD_SDHC) ? currentLba : currentLba << 9) == 0x00) { |
eencae | 1:caceb9d9d17a | 819 | //Try to write all of the data blocks |
eencae | 1:caceb9d9d17a | 820 | do { |
eencae | 1:caceb9d9d17a | 821 | //Write the next block and break on errors |
eencae | 1:caceb9d9d17a | 822 | token = writeData(currentBuffer, 0xFC); |
eencae | 1:caceb9d9d17a | 823 | if (token != 0x05) { |
eencae | 1:caceb9d9d17a | 824 | f++; |
eencae | 1:caceb9d9d17a | 825 | break; |
eencae | 1:caceb9d9d17a | 826 | } |
eencae | 1:caceb9d9d17a | 827 | |
eencae | 1:caceb9d9d17a | 828 | //Update the variables |
eencae | 1:caceb9d9d17a | 829 | currentBuffer += 512; |
eencae | 1:caceb9d9d17a | 830 | f = 0; |
eencae | 1:caceb9d9d17a | 831 | } while (--currentCount); |
eencae | 1:caceb9d9d17a | 832 | |
eencae | 1:caceb9d9d17a | 833 | //Wait for up to 500ms for the card to finish processing the last block |
eencae | 1:caceb9d9d17a | 834 | if (!waitReady(500)) |
eencae | 1:caceb9d9d17a | 835 | break; |
eencae | 1:caceb9d9d17a | 836 | |
eencae | 1:caceb9d9d17a | 837 | //Finalize the transmission |
eencae | 1:caceb9d9d17a | 838 | if (currentCount == 0) { |
eencae | 1:caceb9d9d17a | 839 | //Send the stop tran token, and deselect the card |
eencae | 1:caceb9d9d17a | 840 | m_Spi.write(0xFD); |
eencae | 1:caceb9d9d17a | 841 | deselect(); |
eencae | 1:caceb9d9d17a | 842 | |
eencae | 1:caceb9d9d17a | 843 | //Send CMD13(0x00000000) to verify that the programming was successful if enabled |
eencae | 1:caceb9d9d17a | 844 | if (m_WriteValidation) { |
eencae | 1:caceb9d9d17a | 845 | unsigned int resp; |
eencae | 1:caceb9d9d17a | 846 | if (commandTransaction(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) { |
eencae | 1:caceb9d9d17a | 847 | //Some manner of unrecoverable write error occured during programming, get out |
eencae | 1:caceb9d9d17a | 848 | break; |
eencae | 1:caceb9d9d17a | 849 | } |
eencae | 1:caceb9d9d17a | 850 | } |
eencae | 1:caceb9d9d17a | 851 | |
eencae | 1:caceb9d9d17a | 852 | //The data was written successfully |
eencae | 1:caceb9d9d17a | 853 | return true; |
eencae | 1:caceb9d9d17a | 854 | } else { |
eencae | 1:caceb9d9d17a | 855 | //Send CMD12(0x00000000) to abort the transmission |
eencae | 1:caceb9d9d17a | 856 | if (writeCommand(CMD12, 0x00000000) != 0x00) { |
eencae | 1:caceb9d9d17a | 857 | //The command failed, get out |
eencae | 1:caceb9d9d17a | 858 | break; |
eencae | 1:caceb9d9d17a | 859 | } |
eencae | 1:caceb9d9d17a | 860 | |
eencae | 1:caceb9d9d17a | 861 | //Deselect the card |
eencae | 1:caceb9d9d17a | 862 | deselect(); |
eencae | 1:caceb9d9d17a | 863 | |
eencae | 1:caceb9d9d17a | 864 | //Check the error token |
eencae | 1:caceb9d9d17a | 865 | if (token == 0x0A) { |
eencae | 1:caceb9d9d17a | 866 | //Determine the number of well written blocks if possible |
eencae | 1:caceb9d9d17a | 867 | unsigned int writtenBlocks = 0; |
eencae | 1:caceb9d9d17a | 868 | if (m_CardType != CARD_MMC && select()) { |
eencae | 1:caceb9d9d17a | 869 | //Send ACMD22(0x00000000) to get the number of well written blocks |
eencae | 1:caceb9d9d17a | 870 | if (writeCommand(ACMD22, 0x00000000) == 0x00) { |
eencae | 1:caceb9d9d17a | 871 | //Read the data |
eencae | 1:caceb9d9d17a | 872 | char acmdData[4]; |
eencae | 1:caceb9d9d17a | 873 | if (readData(acmdData, 4)) { |
eencae | 1:caceb9d9d17a | 874 | //Extract the number of well written blocks |
eencae | 1:caceb9d9d17a | 875 | writtenBlocks = acmdData[0] << 24; |
eencae | 1:caceb9d9d17a | 876 | writtenBlocks |= acmdData[1] << 16; |
eencae | 1:caceb9d9d17a | 877 | writtenBlocks |= acmdData[2] << 8; |
eencae | 1:caceb9d9d17a | 878 | writtenBlocks |= acmdData[3]; |
eencae | 1:caceb9d9d17a | 879 | } |
eencae | 1:caceb9d9d17a | 880 | } |
eencae | 1:caceb9d9d17a | 881 | deselect(); |
eencae | 1:caceb9d9d17a | 882 | } |
eencae | 1:caceb9d9d17a | 883 | |
eencae | 1:caceb9d9d17a | 884 | //Roll back the variables based on the number of well written blocks |
eencae | 1:caceb9d9d17a | 885 | currentBuffer = buffer + (writtenBlocks << 9); |
eencae | 1:caceb9d9d17a | 886 | currentLba = lba + writtenBlocks; |
eencae | 1:caceb9d9d17a | 887 | currentCount = count - writtenBlocks; |
eencae | 1:caceb9d9d17a | 888 | |
eencae | 1:caceb9d9d17a | 889 | //Try again |
eencae | 1:caceb9d9d17a | 890 | continue; |
eencae | 1:caceb9d9d17a | 891 | } else { |
eencae | 1:caceb9d9d17a | 892 | //A write error occured, get out |
eencae | 1:caceb9d9d17a | 893 | break; |
eencae | 1:caceb9d9d17a | 894 | } |
eencae | 1:caceb9d9d17a | 895 | } |
eencae | 1:caceb9d9d17a | 896 | } else { |
eencae | 1:caceb9d9d17a | 897 | //The command failed, get out |
eencae | 1:caceb9d9d17a | 898 | break; |
eencae | 1:caceb9d9d17a | 899 | } |
eencae | 1:caceb9d9d17a | 900 | } |
eencae | 1:caceb9d9d17a | 901 | |
eencae | 1:caceb9d9d17a | 902 | //The multiple block write failed |
eencae | 1:caceb9d9d17a | 903 | deselect(); |
eencae | 1:caceb9d9d17a | 904 | return false; |
eencae | 1:caceb9d9d17a | 905 | } |
eencae | 1:caceb9d9d17a | 906 | |
eencae | 1:caceb9d9d17a | 907 | bool SDFileSystem::enableHighSpeedMode() |
eencae | 1:caceb9d9d17a | 908 | { |
eencae | 1:caceb9d9d17a | 909 | //Try to issue CMD6 up to 3 times |
eencae | 1:caceb9d9d17a | 910 | for (int f = 0; f < 3; f++) { |
eencae | 1:caceb9d9d17a | 911 | //Select the card, and wait for ready |
eencae | 1:caceb9d9d17a | 912 | if(!select()) |
eencae | 1:caceb9d9d17a | 913 | break; |
eencae | 1:caceb9d9d17a | 914 | |
eencae | 1:caceb9d9d17a | 915 | //Send CMD6(0x80FFFFF1) to change the access mode to high speed |
eencae | 1:caceb9d9d17a | 916 | if (writeCommand(CMD6, 0x80FFFFF1) == 0x00) { |
eencae | 1:caceb9d9d17a | 917 | //Read the 64B status data block |
eencae | 1:caceb9d9d17a | 918 | char status[64]; |
eencae | 1:caceb9d9d17a | 919 | bool success = readData(status, 64); |
eencae | 1:caceb9d9d17a | 920 | deselect(); |
eencae | 1:caceb9d9d17a | 921 | if (success) { |
eencae | 1:caceb9d9d17a | 922 | //Return whether or not the operation was successful |
eencae | 1:caceb9d9d17a | 923 | return ((status[16] & 0x0F) == 0x1); |
eencae | 1:caceb9d9d17a | 924 | } |
eencae | 1:caceb9d9d17a | 925 | } else { |
eencae | 1:caceb9d9d17a | 926 | //The command failed, get out |
eencae | 1:caceb9d9d17a | 927 | break; |
eencae | 1:caceb9d9d17a | 928 | } |
eencae | 1:caceb9d9d17a | 929 | } |
eencae | 1:caceb9d9d17a | 930 | |
eencae | 1:caceb9d9d17a | 931 | //The operation failed 3 times |
eencae | 1:caceb9d9d17a | 932 | deselect(); |
eencae | 1:caceb9d9d17a | 933 | return false; |
eencae | 1:caceb9d9d17a | 934 | } |