Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of SDFileSystem by
SDFileSystem.cpp@8:7b6acbb6739b, 2014-08-07 (annotated)
- Committer:
- neilt6
- Date:
- Thu Aug 07 16:44:42 2014 +0000
- Revision:
- 8:7b6acbb6739b
- Parent:
- 7:61db99e52c0d
- Child:
- 9:1906befe7f30
Minor syntax improvement
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
neilt6 | 0:2a6d8a096edc | 1 | /* SD/MMC File System Library |
neilt6 | 0:2a6d8a096edc | 2 | * Copyright (c) 2014 Neil Thiessen |
neilt6 | 0:2a6d8a096edc | 3 | * |
neilt6 | 0:2a6d8a096edc | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
neilt6 | 0:2a6d8a096edc | 5 | * you may not use this file except in compliance with the License. |
neilt6 | 0:2a6d8a096edc | 6 | * You may obtain a copy of the License at |
neilt6 | 0:2a6d8a096edc | 7 | * |
neilt6 | 0:2a6d8a096edc | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
neilt6 | 0:2a6d8a096edc | 9 | * |
neilt6 | 0:2a6d8a096edc | 10 | * Unless required by applicable law or agreed to in writing, software |
neilt6 | 0:2a6d8a096edc | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
neilt6 | 0:2a6d8a096edc | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
neilt6 | 0:2a6d8a096edc | 13 | * See the License for the specific language governing permissions and |
neilt6 | 0:2a6d8a096edc | 14 | * limitations under the License. |
neilt6 | 0:2a6d8a096edc | 15 | */ |
neilt6 | 0:2a6d8a096edc | 16 | |
neilt6 | 0:2a6d8a096edc | 17 | #include "SDFileSystem.h" |
neilt6 | 0:2a6d8a096edc | 18 | #include "diskio.h" |
neilt6 | 0:2a6d8a096edc | 19 | #include "CRC7.h" |
neilt6 | 0:2a6d8a096edc | 20 | #include "CRC16.h" |
neilt6 | 0:2a6d8a096edc | 21 | |
neilt6 | 7:61db99e52c0d | 22 | SDFileSystem::SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name, PinName cd, SwitchType cdtype, int hz) : FATFileSystem(name), m_Spi(mosi, miso, sclk), m_Cs(cs, 1), m_Cd(cd), m_CD_ASSERT((int)cdtype), m_FREQ(hz) |
neilt6 | 0:2a6d8a096edc | 23 | { |
neilt6 | 0:2a6d8a096edc | 24 | //Initialize the member variables |
neilt6 | 6:55a26a56046a | 25 | m_CardType = CARD_NONE; |
neilt6 | 7:61db99e52c0d | 26 | m_Crc = true; |
neilt6 | 6:55a26a56046a | 27 | m_LargeFrames = false; |
neilt6 | 0:2a6d8a096edc | 28 | m_Status = STA_NOINIT; |
neilt6 | 0:2a6d8a096edc | 29 | |
neilt6 | 0:2a6d8a096edc | 30 | //Configure the SPI bus |
neilt6 | 7:61db99e52c0d | 31 | m_Spi.format(8, 0); |
neilt6 | 0:2a6d8a096edc | 32 | |
neilt6 | 0:2a6d8a096edc | 33 | //Configure the card detect pin |
neilt6 | 7:61db99e52c0d | 34 | m_Cd.mode(PullUp); |
neilt6 | 1:25f4ba436b81 | 35 | if (cdtype == SWITCH_NO) |
neilt6 | 7:61db99e52c0d | 36 | m_Cd.rise(this, &SDFileSystem::checkSocket); |
neilt6 | 1:25f4ba436b81 | 37 | else |
neilt6 | 7:61db99e52c0d | 38 | m_Cd.fall(this, &SDFileSystem::checkSocket); |
neilt6 | 0:2a6d8a096edc | 39 | } |
neilt6 | 0:2a6d8a096edc | 40 | |
neilt6 | 0:2a6d8a096edc | 41 | SDFileSystem::CardType SDFileSystem::card_type() |
neilt6 | 0:2a6d8a096edc | 42 | { |
neilt6 | 0:2a6d8a096edc | 43 | //Check the card socket |
neilt6 | 0:2a6d8a096edc | 44 | checkSocket(); |
neilt6 | 0:2a6d8a096edc | 45 | |
neilt6 | 0:2a6d8a096edc | 46 | //If a card is present but not initialized, initialize it |
neilt6 | 0:2a6d8a096edc | 47 | if (!(m_Status & STA_NODISK) && (m_Status & STA_NOINIT)) |
neilt6 | 0:2a6d8a096edc | 48 | disk_initialize(); |
neilt6 | 0:2a6d8a096edc | 49 | |
neilt6 | 0:2a6d8a096edc | 50 | //Return the card type |
neilt6 | 0:2a6d8a096edc | 51 | return m_CardType; |
neilt6 | 0:2a6d8a096edc | 52 | } |
neilt6 | 0:2a6d8a096edc | 53 | |
neilt6 | 7:61db99e52c0d | 54 | bool SDFileSystem::crc() |
neilt6 | 6:55a26a56046a | 55 | { |
neilt6 | 6:55a26a56046a | 56 | //Return whether or not CRC is enabled |
neilt6 | 7:61db99e52c0d | 57 | return m_Crc; |
neilt6 | 6:55a26a56046a | 58 | } |
neilt6 | 6:55a26a56046a | 59 | |
neilt6 | 7:61db99e52c0d | 60 | void SDFileSystem::crc(bool enabled) |
neilt6 | 6:55a26a56046a | 61 | { |
neilt6 | 6:55a26a56046a | 62 | //Check the card socket |
neilt6 | 6:55a26a56046a | 63 | checkSocket(); |
neilt6 | 6:55a26a56046a | 64 | |
neilt6 | 6:55a26a56046a | 65 | //Just update the member variable if the card isn't initialized |
neilt6 | 6:55a26a56046a | 66 | if (m_Status & STA_NOINIT) { |
neilt6 | 7:61db99e52c0d | 67 | m_Crc = enabled; |
neilt6 | 6:55a26a56046a | 68 | return; |
neilt6 | 6:55a26a56046a | 69 | } |
neilt6 | 6:55a26a56046a | 70 | |
neilt6 | 6:55a26a56046a | 71 | //Enable or disable CRC |
neilt6 | 7:61db99e52c0d | 72 | if (enabled && !m_Crc) { |
neilt6 | 6:55a26a56046a | 73 | //Send CMD59(0x00000001) to enable CRC |
neilt6 | 7:61db99e52c0d | 74 | m_Crc = true; |
neilt6 | 6:55a26a56046a | 75 | writeCommand(CMD59, 0x00000001); |
neilt6 | 7:61db99e52c0d | 76 | } else if (!enabled && m_Crc) { |
neilt6 | 6:55a26a56046a | 77 | //Send CMD59(0x00000000) to disable CRC |
neilt6 | 6:55a26a56046a | 78 | writeCommand(CMD59, 0x00000000); |
neilt6 | 7:61db99e52c0d | 79 | m_Crc = false; |
neilt6 | 6:55a26a56046a | 80 | } |
neilt6 | 6:55a26a56046a | 81 | } |
neilt6 | 6:55a26a56046a | 82 | |
neilt6 | 6:55a26a56046a | 83 | bool SDFileSystem::large_frames() |
neilt6 | 6:55a26a56046a | 84 | { |
neilt6 | 6:55a26a56046a | 85 | //Return whether or not 16-bit frames are enabled |
neilt6 | 6:55a26a56046a | 86 | return m_LargeFrames; |
neilt6 | 6:55a26a56046a | 87 | } |
neilt6 | 6:55a26a56046a | 88 | |
neilt6 | 6:55a26a56046a | 89 | void SDFileSystem::large_frames(bool enabled) |
neilt6 | 6:55a26a56046a | 90 | { |
neilt6 | 6:55a26a56046a | 91 | //Set whether or not 16-bit frames are enabled |
neilt6 | 6:55a26a56046a | 92 | m_LargeFrames = enabled; |
neilt6 | 6:55a26a56046a | 93 | } |
neilt6 | 6:55a26a56046a | 94 | |
neilt6 | 0:2a6d8a096edc | 95 | int SDFileSystem::disk_initialize() |
neilt6 | 0:2a6d8a096edc | 96 | { |
neilt6 | 0:2a6d8a096edc | 97 | char resp; |
neilt6 | 0:2a6d8a096edc | 98 | |
neilt6 | 0:2a6d8a096edc | 99 | //Make sure there's a card in the socket before proceeding |
neilt6 | 0:2a6d8a096edc | 100 | checkSocket(); |
neilt6 | 0:2a6d8a096edc | 101 | if (m_Status & STA_NODISK) |
neilt6 | 0:2a6d8a096edc | 102 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 103 | |
neilt6 | 0:2a6d8a096edc | 104 | //Make sure we're not already initialized before proceeding |
neilt6 | 0:2a6d8a096edc | 105 | if (!(m_Status & STA_NOINIT)) |
neilt6 | 0:2a6d8a096edc | 106 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 107 | |
neilt6 | 0:2a6d8a096edc | 108 | //Set the SPI frequency to 100kHz for initialization |
neilt6 | 7:61db99e52c0d | 109 | m_Spi.frequency(100000); |
neilt6 | 0:2a6d8a096edc | 110 | |
neilt6 | 0:2a6d8a096edc | 111 | //Send 80 dummy clocks with /CS and DI held high |
neilt6 | 7:61db99e52c0d | 112 | m_Cs = 1; |
neilt6 | 0:2a6d8a096edc | 113 | for (int i = 0; i < 10; i++) |
neilt6 | 7:61db99e52c0d | 114 | m_Spi.write(0xFF); |
neilt6 | 0:2a6d8a096edc | 115 | |
neilt6 | 5:6befff2300d0 | 116 | //Write CMD0(0x00000000) to reset the card |
neilt6 | 5:6befff2300d0 | 117 | resp = writeCommand(CMD0, 0x00000000); |
neilt6 | 0:2a6d8a096edc | 118 | if (resp != 0x01) { |
neilt6 | 0:2a6d8a096edc | 119 | //Initialization failed |
neilt6 | 0:2a6d8a096edc | 120 | m_CardType = CARD_UNKNOWN; |
neilt6 | 0:2a6d8a096edc | 121 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 122 | } |
neilt6 | 0:2a6d8a096edc | 123 | |
neilt6 | 0:2a6d8a096edc | 124 | //Write CMD8(0x000001AA) to see if this is an SDCv2 card |
neilt6 | 0:2a6d8a096edc | 125 | resp = writeCommand(CMD8, 0x000001AA); |
neilt6 | 0:2a6d8a096edc | 126 | if (resp == 0x01) { |
neilt6 | 0:2a6d8a096edc | 127 | //This is an SDCv2 card, get the 32-bit return value and verify the voltage range/check pattern |
neilt6 | 0:2a6d8a096edc | 128 | if ((readReturn() & 0xFFF) != 0x1AA) { |
neilt6 | 0:2a6d8a096edc | 129 | //Initialization failed |
neilt6 | 0:2a6d8a096edc | 130 | m_CardType = CARD_UNKNOWN; |
neilt6 | 0:2a6d8a096edc | 131 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 132 | } |
neilt6 | 0:2a6d8a096edc | 133 | |
neilt6 | 5:6befff2300d0 | 134 | //Send CMD58(0x00000000) to read the OCR, and verify that the card supports 3.2-3.3V |
neilt6 | 5:6befff2300d0 | 135 | resp = writeCommand(CMD58, 0x00000000); |
neilt6 | 0:2a6d8a096edc | 136 | if (resp != 0x01 || !(readReturn() & (1 << 20))) { |
neilt6 | 0:2a6d8a096edc | 137 | //Initialization failed |
neilt6 | 0:2a6d8a096edc | 138 | m_CardType = CARD_UNKNOWN; |
neilt6 | 0:2a6d8a096edc | 139 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 140 | } |
neilt6 | 0:2a6d8a096edc | 141 | |
neilt6 | 0:2a6d8a096edc | 142 | //Send ACMD41(0x40100000) repeatedly for up to 1 second to initialize the card |
neilt6 | 0:2a6d8a096edc | 143 | for (int i = 0; i < 1000; i++) { |
neilt6 | 0:2a6d8a096edc | 144 | resp = writeCommand(ACMD41, 0x40100000); |
neilt6 | 0:2a6d8a096edc | 145 | if (resp != 0x01) |
neilt6 | 0:2a6d8a096edc | 146 | break; |
neilt6 | 0:2a6d8a096edc | 147 | wait_ms(1); |
neilt6 | 0:2a6d8a096edc | 148 | } |
neilt6 | 0:2a6d8a096edc | 149 | |
neilt6 | 0:2a6d8a096edc | 150 | //Check if the card initialized |
neilt6 | 0:2a6d8a096edc | 151 | if (resp != 0x00) { |
neilt6 | 0:2a6d8a096edc | 152 | //Initialization failed |
neilt6 | 0:2a6d8a096edc | 153 | m_CardType = CARD_UNKNOWN; |
neilt6 | 0:2a6d8a096edc | 154 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 155 | } |
neilt6 | 0:2a6d8a096edc | 156 | |
neilt6 | 5:6befff2300d0 | 157 | //Send CMD58(0x00000000) to read the OCR |
neilt6 | 5:6befff2300d0 | 158 | resp = writeCommand(CMD58, 0x00000000); |
neilt6 | 0:2a6d8a096edc | 159 | if (resp == 0x00) { |
neilt6 | 0:2a6d8a096edc | 160 | //Check the CCS bit to determine if this is a high capacity card |
neilt6 | 5:6befff2300d0 | 161 | if (readReturn() & (1 << 30)) |
neilt6 | 0:2a6d8a096edc | 162 | m_CardType = CARD_SDHC; |
neilt6 | 0:2a6d8a096edc | 163 | else |
neilt6 | 0:2a6d8a096edc | 164 | m_CardType = CARD_SD; |
neilt6 | 0:2a6d8a096edc | 165 | } else { |
neilt6 | 0:2a6d8a096edc | 166 | //Initialization failed |
neilt6 | 0:2a6d8a096edc | 167 | m_CardType = CARD_UNKNOWN; |
neilt6 | 0:2a6d8a096edc | 168 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 169 | } |
neilt6 | 0:2a6d8a096edc | 170 | } else { |
neilt6 | 0:2a6d8a096edc | 171 | //Didn't respond or illegal command, this is either an SDCv1 or MMC card |
neilt6 | 5:6befff2300d0 | 172 | //Send CMD58(0x00000000) to read the OCR, and verify that the card supports 3.2-3.3V |
neilt6 | 5:6befff2300d0 | 173 | resp = writeCommand(CMD58, 0x00000000); |
neilt6 | 0:2a6d8a096edc | 174 | if (resp != 0x01 || !(readReturn() & (1 << 20))) { |
neilt6 | 0:2a6d8a096edc | 175 | //Initialization failed |
neilt6 | 0:2a6d8a096edc | 176 | m_CardType = CARD_UNKNOWN; |
neilt6 | 0:2a6d8a096edc | 177 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 178 | } |
neilt6 | 0:2a6d8a096edc | 179 | |
neilt6 | 0:2a6d8a096edc | 180 | //Try to initialize the card using ACMD41(0x00100000) for 1 second |
neilt6 | 0:2a6d8a096edc | 181 | for (int i = 0; i < 1000; i++) { |
neilt6 | 0:2a6d8a096edc | 182 | resp = writeCommand(ACMD41, 0x00100000); |
neilt6 | 0:2a6d8a096edc | 183 | if (resp != 0x01) |
neilt6 | 0:2a6d8a096edc | 184 | break; |
neilt6 | 0:2a6d8a096edc | 185 | wait_ms(1); |
neilt6 | 0:2a6d8a096edc | 186 | } |
neilt6 | 0:2a6d8a096edc | 187 | |
neilt6 | 0:2a6d8a096edc | 188 | //Check if the card initialized |
neilt6 | 0:2a6d8a096edc | 189 | if (resp == 0x00) { |
neilt6 | 0:2a6d8a096edc | 190 | //This is an SDCv1 standard capacity card |
neilt6 | 0:2a6d8a096edc | 191 | m_CardType = CARD_SD; |
neilt6 | 0:2a6d8a096edc | 192 | } else { |
neilt6 | 0:2a6d8a096edc | 193 | //Try to initialize the card using CMD1(0x00100000) for 1 second |
neilt6 | 0:2a6d8a096edc | 194 | for (int i = 0; i < 1000; i++) { |
neilt6 | 0:2a6d8a096edc | 195 | resp = writeCommand(CMD1, 0x00100000); |
neilt6 | 0:2a6d8a096edc | 196 | if (resp != 0x01) |
neilt6 | 0:2a6d8a096edc | 197 | break; |
neilt6 | 0:2a6d8a096edc | 198 | wait_ms(1); |
neilt6 | 0:2a6d8a096edc | 199 | } |
neilt6 | 0:2a6d8a096edc | 200 | |
neilt6 | 0:2a6d8a096edc | 201 | //Check if the card initialized |
neilt6 | 0:2a6d8a096edc | 202 | if (resp == 0x00) { |
neilt6 | 0:2a6d8a096edc | 203 | //This is an MMCv3 card |
neilt6 | 0:2a6d8a096edc | 204 | m_CardType = CARD_MMC; |
neilt6 | 0:2a6d8a096edc | 205 | } else { |
neilt6 | 0:2a6d8a096edc | 206 | //Initialization failed |
neilt6 | 0:2a6d8a096edc | 207 | m_CardType = CARD_UNKNOWN; |
neilt6 | 0:2a6d8a096edc | 208 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 209 | } |
neilt6 | 0:2a6d8a096edc | 210 | } |
neilt6 | 0:2a6d8a096edc | 211 | } |
neilt6 | 0:2a6d8a096edc | 212 | |
neilt6 | 6:55a26a56046a | 213 | //Send CMD59(0x00000001) to enable CRC if necessary |
neilt6 | 7:61db99e52c0d | 214 | if (m_Crc) { |
neilt6 | 6:55a26a56046a | 215 | resp = writeCommand(CMD59, 0x00000001); |
neilt6 | 6:55a26a56046a | 216 | if (resp != 0x00) { |
neilt6 | 6:55a26a56046a | 217 | //Initialization failed |
neilt6 | 6:55a26a56046a | 218 | m_CardType = CARD_UNKNOWN; |
neilt6 | 6:55a26a56046a | 219 | return m_Status; |
neilt6 | 6:55a26a56046a | 220 | } |
neilt6 | 0:2a6d8a096edc | 221 | } |
neilt6 | 0:2a6d8a096edc | 222 | |
neilt6 | 0:2a6d8a096edc | 223 | //Send CMD16(0x00000200) to force the block size to 512B if necessary |
neilt6 | 0:2a6d8a096edc | 224 | if (m_CardType != CARD_SDHC) { |
neilt6 | 0:2a6d8a096edc | 225 | resp = writeCommand(CMD16, 0x00000200); |
neilt6 | 0:2a6d8a096edc | 226 | if (resp != 0x00) { |
neilt6 | 0:2a6d8a096edc | 227 | //Initialization failed |
neilt6 | 0:2a6d8a096edc | 228 | m_CardType = CARD_UNKNOWN; |
neilt6 | 0:2a6d8a096edc | 229 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 230 | } |
neilt6 | 0:2a6d8a096edc | 231 | } |
neilt6 | 0:2a6d8a096edc | 232 | |
neilt6 | 5:6befff2300d0 | 233 | //Send ACMD42(0x00000000) to disconnect the internal pull-up resistor on /CS if necessary |
neilt6 | 5:6befff2300d0 | 234 | if (m_CardType != CARD_MMC) { |
neilt6 | 5:6befff2300d0 | 235 | resp = writeCommand(ACMD42, 0x00000000); |
neilt6 | 5:6befff2300d0 | 236 | if (resp != 0x00) { |
neilt6 | 5:6befff2300d0 | 237 | //Initialization failed |
neilt6 | 5:6befff2300d0 | 238 | m_CardType = CARD_UNKNOWN; |
neilt6 | 5:6befff2300d0 | 239 | return m_Status; |
neilt6 | 5:6befff2300d0 | 240 | } |
neilt6 | 5:6befff2300d0 | 241 | } |
neilt6 | 5:6befff2300d0 | 242 | |
neilt6 | 0:2a6d8a096edc | 243 | //The card is now initialized |
neilt6 | 0:2a6d8a096edc | 244 | m_Status &= ~STA_NOINIT; |
neilt6 | 0:2a6d8a096edc | 245 | |
neilt6 | 0:2a6d8a096edc | 246 | //Increase the SPI frequency to full speed (limited to 20MHz for MMC, or 25MHz for SDC) |
neilt6 | 6:55a26a56046a | 247 | if (m_CardType == CARD_MMC && m_FREQ > 20000000) |
neilt6 | 7:61db99e52c0d | 248 | m_Spi.frequency(20000000); |
neilt6 | 6:55a26a56046a | 249 | else if (m_FREQ > 25000000) |
neilt6 | 7:61db99e52c0d | 250 | m_Spi.frequency(25000000); |
neilt6 | 0:2a6d8a096edc | 251 | else |
neilt6 | 7:61db99e52c0d | 252 | m_Spi.frequency(m_FREQ); |
neilt6 | 0:2a6d8a096edc | 253 | |
neilt6 | 0:2a6d8a096edc | 254 | //Return the device status |
neilt6 | 0:2a6d8a096edc | 255 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 256 | } |
neilt6 | 0:2a6d8a096edc | 257 | |
neilt6 | 0:2a6d8a096edc | 258 | int SDFileSystem::disk_status() |
neilt6 | 0:2a6d8a096edc | 259 | { |
neilt6 | 0:2a6d8a096edc | 260 | //Check if there's a card in the socket |
neilt6 | 0:2a6d8a096edc | 261 | checkSocket(); |
neilt6 | 0:2a6d8a096edc | 262 | |
neilt6 | 0:2a6d8a096edc | 263 | //Return the device status |
neilt6 | 0:2a6d8a096edc | 264 | return m_Status; |
neilt6 | 0:2a6d8a096edc | 265 | } |
neilt6 | 0:2a6d8a096edc | 266 | |
neilt6 | 0:2a6d8a096edc | 267 | int SDFileSystem::disk_read(uint8_t* buffer, uint64_t sector) |
neilt6 | 0:2a6d8a096edc | 268 | { |
neilt6 | 0:2a6d8a096edc | 269 | //Make sure the device is initialized before proceeding |
neilt6 | 0:2a6d8a096edc | 270 | if (m_Status & STA_NOINIT) |
neilt6 | 0:2a6d8a096edc | 271 | return RES_NOTRDY; |
neilt6 | 0:2a6d8a096edc | 272 | |
neilt6 | 0:2a6d8a096edc | 273 | //Convert from LBA to a byte address for standard capacity cards |
neilt6 | 0:2a6d8a096edc | 274 | if (m_CardType != CARD_SDHC) |
neilt6 | 0:2a6d8a096edc | 275 | sector *= 512; |
neilt6 | 0:2a6d8a096edc | 276 | |
neilt6 | 0:2a6d8a096edc | 277 | //Try to read the block up to 3 times |
neilt6 | 0:2a6d8a096edc | 278 | for (int i = 0; i < 3; i++) { |
neilt6 | 0:2a6d8a096edc | 279 | //Send CMD17(sector) to read a single block |
neilt6 | 8:7b6acbb6739b | 280 | if (writeCommand(CMD17, sector) == 0x00) { |
neilt6 | 0:2a6d8a096edc | 281 | //Try to read the sector, and return if successful |
neilt6 | 0:2a6d8a096edc | 282 | if (readData((char*)buffer, 512)) |
neilt6 | 0:2a6d8a096edc | 283 | return RES_OK; |
neilt6 | 0:2a6d8a096edc | 284 | } else { |
neilt6 | 0:2a6d8a096edc | 285 | //The command failed |
neilt6 | 0:2a6d8a096edc | 286 | return RES_ERROR; |
neilt6 | 0:2a6d8a096edc | 287 | } |
neilt6 | 0:2a6d8a096edc | 288 | } |
neilt6 | 0:2a6d8a096edc | 289 | |
neilt6 | 0:2a6d8a096edc | 290 | //The read operation failed 3 times (CRC most likely) |
neilt6 | 0:2a6d8a096edc | 291 | return RES_ERROR; |
neilt6 | 0:2a6d8a096edc | 292 | } |
neilt6 | 0:2a6d8a096edc | 293 | |
neilt6 | 0:2a6d8a096edc | 294 | int SDFileSystem::disk_write(const uint8_t* buffer, uint64_t sector) |
neilt6 | 0:2a6d8a096edc | 295 | { |
neilt6 | 0:2a6d8a096edc | 296 | //Make sure the device is initialized before proceeding |
neilt6 | 0:2a6d8a096edc | 297 | if (m_Status & STA_NOINIT) |
neilt6 | 0:2a6d8a096edc | 298 | return RES_NOTRDY; |
neilt6 | 0:2a6d8a096edc | 299 | |
neilt6 | 0:2a6d8a096edc | 300 | //Make sure the device isn't write protected before proceeding |
neilt6 | 0:2a6d8a096edc | 301 | if (m_Status & STA_PROTECT) |
neilt6 | 0:2a6d8a096edc | 302 | return RES_WRPRT; |
neilt6 | 0:2a6d8a096edc | 303 | |
neilt6 | 0:2a6d8a096edc | 304 | //Convert from LBA to a byte address for older cards |
neilt6 | 0:2a6d8a096edc | 305 | if (m_CardType != CARD_SDHC) |
neilt6 | 0:2a6d8a096edc | 306 | sector *= 512; |
neilt6 | 0:2a6d8a096edc | 307 | |
neilt6 | 0:2a6d8a096edc | 308 | //Try to write the block up to 3 times |
neilt6 | 0:2a6d8a096edc | 309 | for (int i = 0; i < 3; i++) { |
neilt6 | 0:2a6d8a096edc | 310 | //Send CMD24(sector) to write a single block |
neilt6 | 0:2a6d8a096edc | 311 | if (writeCommand(CMD24, sector) == 0x00) { |
neilt6 | 0:2a6d8a096edc | 312 | //Wait for up to 500ms for the card to become ready |
neilt6 | 0:2a6d8a096edc | 313 | if (!waitReady(500)) { |
neilt6 | 2:eec1db773e7d | 314 | //We timed out, deselect and loop again |
neilt6 | 0:2a6d8a096edc | 315 | deselect(); |
neilt6 | 0:2a6d8a096edc | 316 | continue; |
neilt6 | 0:2a6d8a096edc | 317 | } |
neilt6 | 0:2a6d8a096edc | 318 | |
neilt6 | 0:2a6d8a096edc | 319 | //Send the write data token |
neilt6 | 7:61db99e52c0d | 320 | m_Spi.write(0xFE); |
neilt6 | 0:2a6d8a096edc | 321 | |
neilt6 | 6:55a26a56046a | 322 | //Check if large frames are enabled or not |
neilt6 | 6:55a26a56046a | 323 | if (m_LargeFrames) { |
neilt6 | 6:55a26a56046a | 324 | //Switch to 16-bit frames for better performance |
neilt6 | 7:61db99e52c0d | 325 | m_Spi.format(16, 0); |
neilt6 | 6:55a26a56046a | 326 | |
neilt6 | 6:55a26a56046a | 327 | //Write the data block from the buffer |
neilt6 | 6:55a26a56046a | 328 | for (int b = 0; b < 512; b += 2) { |
neilt6 | 7:61db99e52c0d | 329 | m_Spi.write((buffer[b] << 8) | buffer[b + 1]); |
neilt6 | 6:55a26a56046a | 330 | } |
neilt6 | 6:55a26a56046a | 331 | |
neilt6 | 6:55a26a56046a | 332 | //Calculate the CRC16 checksum for the data block and send it (if enabled) |
neilt6 | 7:61db99e52c0d | 333 | m_Spi.write(m_Crc ? CRC16((char*)buffer, 512) : 0xFFFF); |
neilt6 | 0:2a6d8a096edc | 334 | |
neilt6 | 6:55a26a56046a | 335 | //Switch back to 8-bit frames |
neilt6 | 7:61db99e52c0d | 336 | m_Spi.format(8, 0); |
neilt6 | 6:55a26a56046a | 337 | } else { |
neilt6 | 6:55a26a56046a | 338 | //Write the data block from the buffer |
neilt6 | 6:55a26a56046a | 339 | for (int b = 0; b < 512; b++) |
neilt6 | 7:61db99e52c0d | 340 | m_Spi.write(buffer[b]); |
neilt6 | 6:55a26a56046a | 341 | |
neilt6 | 6:55a26a56046a | 342 | //Calculate the CRC16 checksum for the data block and send it (if enabled) |
neilt6 | 7:61db99e52c0d | 343 | unsigned short crc = m_Crc ? CRC16((char*)buffer, 512) : 0xFFFF; |
neilt6 | 7:61db99e52c0d | 344 | m_Spi.write(crc >> 8); |
neilt6 | 7:61db99e52c0d | 345 | m_Spi.write(crc); |
neilt6 | 6:55a26a56046a | 346 | } |
neilt6 | 0:2a6d8a096edc | 347 | |
neilt6 | 0:2a6d8a096edc | 348 | //Receive the data response, and deselect the card |
neilt6 | 7:61db99e52c0d | 349 | char resp = m_Spi.write(0xFF) & 0x1F; |
neilt6 | 0:2a6d8a096edc | 350 | deselect(); |
neilt6 | 0:2a6d8a096edc | 351 | |
neilt6 | 0:2a6d8a096edc | 352 | //Check the response |
neilt6 | 0:2a6d8a096edc | 353 | if (resp == 0x05) |
neilt6 | 0:2a6d8a096edc | 354 | return RES_OK; |
neilt6 | 0:2a6d8a096edc | 355 | else if (resp == 0x0D) |
neilt6 | 0:2a6d8a096edc | 356 | return RES_ERROR; |
neilt6 | 0:2a6d8a096edc | 357 | } else { |
neilt6 | 0:2a6d8a096edc | 358 | //The command failed |
neilt6 | 0:2a6d8a096edc | 359 | return RES_ERROR; |
neilt6 | 0:2a6d8a096edc | 360 | } |
neilt6 | 0:2a6d8a096edc | 361 | } |
neilt6 | 0:2a6d8a096edc | 362 | |
neilt6 | 0:2a6d8a096edc | 363 | //The operation either timed out 3 times, failed the CRC check 3 times, or experienced a write error |
neilt6 | 0:2a6d8a096edc | 364 | return RES_ERROR; |
neilt6 | 0:2a6d8a096edc | 365 | } |
neilt6 | 0:2a6d8a096edc | 366 | |
neilt6 | 0:2a6d8a096edc | 367 | int SDFileSystem::disk_sync() |
neilt6 | 0:2a6d8a096edc | 368 | { |
neilt6 | 0:2a6d8a096edc | 369 | //Select the card so we're forced to wait for the end of any internal write processes |
neilt6 | 0:2a6d8a096edc | 370 | bool ret = select(); |
neilt6 | 0:2a6d8a096edc | 371 | deselect(); |
neilt6 | 0:2a6d8a096edc | 372 | |
neilt6 | 0:2a6d8a096edc | 373 | //Return success/failure |
neilt6 | 0:2a6d8a096edc | 374 | return (ret) ? RES_OK : RES_ERROR; |
neilt6 | 0:2a6d8a096edc | 375 | } |
neilt6 | 0:2a6d8a096edc | 376 | |
neilt6 | 0:2a6d8a096edc | 377 | uint64_t SDFileSystem::disk_sectors() |
neilt6 | 0:2a6d8a096edc | 378 | { |
neilt6 | 0:2a6d8a096edc | 379 | //Make sure the device is initialized before proceeding |
neilt6 | 0:2a6d8a096edc | 380 | if (m_Status & STA_NOINIT) |
neilt6 | 0:2a6d8a096edc | 381 | return 0; |
neilt6 | 0:2a6d8a096edc | 382 | |
neilt6 | 0:2a6d8a096edc | 383 | //Try to read the CSD register up to 3 times |
neilt6 | 0:2a6d8a096edc | 384 | for (int i = 0; i < 3; i++) { |
neilt6 | 5:6befff2300d0 | 385 | //Send CMD9(0x00000000) to read the CSD register |
neilt6 | 5:6befff2300d0 | 386 | if (writeCommand(CMD9, 0x00000000) == 0x00) { |
neilt6 | 0:2a6d8a096edc | 387 | //Receive the 16B CSD data |
neilt6 | 0:2a6d8a096edc | 388 | char csd[16]; |
neilt6 | 0:2a6d8a096edc | 389 | if (readData(csd, 16)) { |
neilt6 | 0:2a6d8a096edc | 390 | //Calculate the sector count based on the card type |
neilt6 | 0:2a6d8a096edc | 391 | if ((csd[0] >> 6) == 0x01) { |
neilt6 | 0:2a6d8a096edc | 392 | //Calculate the sector count a high capacity card |
neilt6 | 0:2a6d8a096edc | 393 | uint64_t sectors = (((csd[7] & 0x3F) << 16) | (csd[8] << 8) | csd[9]) + 1; |
neilt6 | 0:2a6d8a096edc | 394 | return sectors << 10; |
neilt6 | 0:2a6d8a096edc | 395 | } else { |
neilt6 | 0:2a6d8a096edc | 396 | //Calculate the sector count standard capacity card |
neilt6 | 0:2a6d8a096edc | 397 | uint64_t sectors = (((csd[6] & 0x03) << 10) | (csd[7] << 2) | ((csd[8] & 0xC0) >> 6)) + 1; |
neilt6 | 0:2a6d8a096edc | 398 | sectors <<= ((((csd[9] & 0x03) << 1) | ((csd[10] & 0x80) >> 7)) + 2); |
neilt6 | 0:2a6d8a096edc | 399 | sectors <<= (csd[5] & 0x0F); |
neilt6 | 0:2a6d8a096edc | 400 | return sectors >> 9; |
neilt6 | 0:2a6d8a096edc | 401 | } |
neilt6 | 0:2a6d8a096edc | 402 | } |
neilt6 | 0:2a6d8a096edc | 403 | } else { |
neilt6 | 0:2a6d8a096edc | 404 | //The command failed |
neilt6 | 0:2a6d8a096edc | 405 | return 0; |
neilt6 | 0:2a6d8a096edc | 406 | } |
neilt6 | 0:2a6d8a096edc | 407 | } |
neilt6 | 0:2a6d8a096edc | 408 | |
neilt6 | 0:2a6d8a096edc | 409 | //The read operation failed 3 times (CRC most likely) |
neilt6 | 0:2a6d8a096edc | 410 | return 0; |
neilt6 | 0:2a6d8a096edc | 411 | } |
neilt6 | 0:2a6d8a096edc | 412 | |
neilt6 | 0:2a6d8a096edc | 413 | void SDFileSystem::checkSocket() |
neilt6 | 0:2a6d8a096edc | 414 | { |
neilt6 | 0:2a6d8a096edc | 415 | //Check if a card is in the socket |
neilt6 | 7:61db99e52c0d | 416 | if (m_Cd == m_CD_ASSERT) { |
neilt6 | 0:2a6d8a096edc | 417 | //The socket is occupied, clear the STA_NODISK flag |
neilt6 | 0:2a6d8a096edc | 418 | m_Status &= ~STA_NODISK; |
neilt6 | 0:2a6d8a096edc | 419 | } else { |
neilt6 | 0:2a6d8a096edc | 420 | //The socket is empty |
neilt6 | 0:2a6d8a096edc | 421 | m_Status |= (STA_NODISK | STA_NOINIT); |
neilt6 | 0:2a6d8a096edc | 422 | m_CardType = CARD_NONE; |
neilt6 | 0:2a6d8a096edc | 423 | } |
neilt6 | 0:2a6d8a096edc | 424 | } |
neilt6 | 0:2a6d8a096edc | 425 | |
neilt6 | 0:2a6d8a096edc | 426 | inline bool SDFileSystem::waitReady(int timeout) |
neilt6 | 0:2a6d8a096edc | 427 | { |
neilt6 | 0:2a6d8a096edc | 428 | //Wait for the specified timeout for the card to become ready |
neilt6 | 0:2a6d8a096edc | 429 | for (int i = 0; i < timeout; i++) { |
neilt6 | 7:61db99e52c0d | 430 | if (m_Spi.write(0xFF) == 0xFF) |
neilt6 | 0:2a6d8a096edc | 431 | return true; |
neilt6 | 0:2a6d8a096edc | 432 | wait_ms(1); |
neilt6 | 0:2a6d8a096edc | 433 | } |
neilt6 | 0:2a6d8a096edc | 434 | |
neilt6 | 0:2a6d8a096edc | 435 | //We timed out |
neilt6 | 0:2a6d8a096edc | 436 | return false; |
neilt6 | 0:2a6d8a096edc | 437 | } |
neilt6 | 0:2a6d8a096edc | 438 | |
neilt6 | 0:2a6d8a096edc | 439 | inline bool SDFileSystem::select() |
neilt6 | 0:2a6d8a096edc | 440 | { |
neilt6 | 0:2a6d8a096edc | 441 | //Pull /CS low |
neilt6 | 7:61db99e52c0d | 442 | m_Cs = 0; |
neilt6 | 0:2a6d8a096edc | 443 | |
neilt6 | 0:2a6d8a096edc | 444 | //Send a dummy clock to enable DO |
neilt6 | 7:61db99e52c0d | 445 | m_Spi.write(0xFF); |
neilt6 | 0:2a6d8a096edc | 446 | |
neilt6 | 0:2a6d8a096edc | 447 | //Wait for up to 500ms for the card to become ready |
neilt6 | 0:2a6d8a096edc | 448 | if (waitReady(500)) |
neilt6 | 0:2a6d8a096edc | 449 | return true; |
neilt6 | 0:2a6d8a096edc | 450 | |
neilt6 | 0:2a6d8a096edc | 451 | //We timed out, deselect and return false |
neilt6 | 0:2a6d8a096edc | 452 | deselect(); |
neilt6 | 0:2a6d8a096edc | 453 | return false; |
neilt6 | 0:2a6d8a096edc | 454 | } |
neilt6 | 0:2a6d8a096edc | 455 | |
neilt6 | 0:2a6d8a096edc | 456 | inline void SDFileSystem::deselect() |
neilt6 | 0:2a6d8a096edc | 457 | { |
neilt6 | 0:2a6d8a096edc | 458 | //Pull /CS high |
neilt6 | 7:61db99e52c0d | 459 | m_Cs = 1; |
neilt6 | 0:2a6d8a096edc | 460 | |
neilt6 | 0:2a6d8a096edc | 461 | //Send a dummy byte to release DO |
neilt6 | 7:61db99e52c0d | 462 | m_Spi.write(0xFF); |
neilt6 | 0:2a6d8a096edc | 463 | } |
neilt6 | 0:2a6d8a096edc | 464 | |
neilt6 | 0:2a6d8a096edc | 465 | char SDFileSystem::writeCommand(char cmd, unsigned int arg) |
neilt6 | 0:2a6d8a096edc | 466 | { |
neilt6 | 0:2a6d8a096edc | 467 | char resp; |
neilt6 | 0:2a6d8a096edc | 468 | |
neilt6 | 0:2a6d8a096edc | 469 | //Try to send the command up to 3 times |
neilt6 | 0:2a6d8a096edc | 470 | for (int i = 0; i < 3; i++) { |
neilt6 | 5:6befff2300d0 | 471 | //Send CMD55(0x00000000) prior to an application specific command |
neilt6 | 5:6befff2300d0 | 472 | if (cmd == ACMD41 || cmd == ACMD42) { |
neilt6 | 5:6befff2300d0 | 473 | resp = writeCommand(CMD55, 0x00000000); |
neilt6 | 0:2a6d8a096edc | 474 | if (resp > 0x01) |
neilt6 | 0:2a6d8a096edc | 475 | return resp; |
neilt6 | 0:2a6d8a096edc | 476 | } |
neilt6 | 0:2a6d8a096edc | 477 | |
neilt6 | 2:eec1db773e7d | 478 | //Select the card, and wait for ready |
neilt6 | 0:2a6d8a096edc | 479 | if (!select()) |
neilt6 | 0:2a6d8a096edc | 480 | return 0xFF; |
neilt6 | 0:2a6d8a096edc | 481 | |
neilt6 | 0:2a6d8a096edc | 482 | //Prepare the command packet |
neilt6 | 0:2a6d8a096edc | 483 | char cmdPacket[6]; |
neilt6 | 4:49b29888eca7 | 484 | cmdPacket[0] = cmd; |
neilt6 | 0:2a6d8a096edc | 485 | cmdPacket[1] = arg >> 24; |
neilt6 | 0:2a6d8a096edc | 486 | cmdPacket[2] = arg >> 16; |
neilt6 | 0:2a6d8a096edc | 487 | cmdPacket[3] = arg >> 8; |
neilt6 | 0:2a6d8a096edc | 488 | cmdPacket[4] = arg; |
neilt6 | 7:61db99e52c0d | 489 | if (m_Crc || cmd == CMD0 || cmd == CMD8) |
neilt6 | 6:55a26a56046a | 490 | cmdPacket[5] = (CRC7(cmdPacket, 5) << 1) | 0x01; |
neilt6 | 6:55a26a56046a | 491 | else |
neilt6 | 6:55a26a56046a | 492 | cmdPacket[5] = 0x01; |
neilt6 | 0:2a6d8a096edc | 493 | |
neilt6 | 0:2a6d8a096edc | 494 | //Send the command packet |
neilt6 | 0:2a6d8a096edc | 495 | for (int b = 0; b < 6; b++) |
neilt6 | 7:61db99e52c0d | 496 | m_Spi.write(cmdPacket[b]); |
neilt6 | 0:2a6d8a096edc | 497 | |
neilt6 | 0:2a6d8a096edc | 498 | //Allow up to 10 bytes of delay for the command response |
neilt6 | 0:2a6d8a096edc | 499 | for (int b = 0; b < 10; b++) { |
neilt6 | 7:61db99e52c0d | 500 | resp = m_Spi.write(0xFF); |
neilt6 | 0:2a6d8a096edc | 501 | if (!(resp & 0x80)) |
neilt6 | 0:2a6d8a096edc | 502 | break; |
neilt6 | 0:2a6d8a096edc | 503 | } |
neilt6 | 0:2a6d8a096edc | 504 | |
neilt6 | 2:eec1db773e7d | 505 | //Deselect the card on errors, or if the transaction is finished |
neilt6 | 2:eec1db773e7d | 506 | if (resp > 0x01 || !(cmd == CMD8 || cmd == CMD9 || cmd == CMD17 || cmd == CMD24 || cmd == CMD55 || cmd == CMD58)) |
neilt6 | 0:2a6d8a096edc | 507 | deselect(); |
neilt6 | 0:2a6d8a096edc | 508 | |
neilt6 | 2:eec1db773e7d | 509 | //Return the response unless there were CRC errors |
neilt6 | 0:2a6d8a096edc | 510 | if (resp == 0xFF || !(resp & (1 << 3))) |
neilt6 | 0:2a6d8a096edc | 511 | return resp; |
neilt6 | 0:2a6d8a096edc | 512 | } |
neilt6 | 0:2a6d8a096edc | 513 | |
neilt6 | 0:2a6d8a096edc | 514 | //The command failed 3 times due to CRC errors |
neilt6 | 0:2a6d8a096edc | 515 | return 0xFF; |
neilt6 | 0:2a6d8a096edc | 516 | } |
neilt6 | 0:2a6d8a096edc | 517 | |
neilt6 | 0:2a6d8a096edc | 518 | unsigned int SDFileSystem::readReturn() |
neilt6 | 0:2a6d8a096edc | 519 | { |
neilt6 | 0:2a6d8a096edc | 520 | unsigned int ret; |
neilt6 | 0:2a6d8a096edc | 521 | |
neilt6 | 0:2a6d8a096edc | 522 | //Read the 32-bit response value |
neilt6 | 7:61db99e52c0d | 523 | ret = (m_Spi.write(0xFF) << 24); |
neilt6 | 7:61db99e52c0d | 524 | ret |= (m_Spi.write(0xFF) << 16); |
neilt6 | 7:61db99e52c0d | 525 | ret |= (m_Spi.write(0xFF) << 8); |
neilt6 | 7:61db99e52c0d | 526 | ret |= m_Spi.write(0xFF); |
neilt6 | 0:2a6d8a096edc | 527 | |
neilt6 | 0:2a6d8a096edc | 528 | //Deselect the card |
neilt6 | 0:2a6d8a096edc | 529 | deselect(); |
neilt6 | 0:2a6d8a096edc | 530 | |
neilt6 | 0:2a6d8a096edc | 531 | //Return the response value |
neilt6 | 0:2a6d8a096edc | 532 | return ret; |
neilt6 | 0:2a6d8a096edc | 533 | } |
neilt6 | 0:2a6d8a096edc | 534 | |
neilt6 | 0:2a6d8a096edc | 535 | bool SDFileSystem::readData(char* buffer, int length) |
neilt6 | 0:2a6d8a096edc | 536 | { |
neilt6 | 0:2a6d8a096edc | 537 | char token; |
neilt6 | 6:55a26a56046a | 538 | unsigned short crc; |
neilt6 | 0:2a6d8a096edc | 539 | |
neilt6 | 0:2a6d8a096edc | 540 | //Wait for up to 200ms for the DataStart token to arrive |
neilt6 | 0:2a6d8a096edc | 541 | for (int i = 0; i < 200; i++) { |
neilt6 | 7:61db99e52c0d | 542 | token = m_Spi.write(0xFF); |
neilt6 | 0:2a6d8a096edc | 543 | if (token != 0xFF) |
neilt6 | 0:2a6d8a096edc | 544 | break; |
neilt6 | 0:2a6d8a096edc | 545 | wait_ms(1); |
neilt6 | 0:2a6d8a096edc | 546 | } |
neilt6 | 0:2a6d8a096edc | 547 | |
neilt6 | 0:2a6d8a096edc | 548 | //Make sure the token is valid |
neilt6 | 6:55a26a56046a | 549 | if (token != 0xFE) { |
neilt6 | 6:55a26a56046a | 550 | deselect(); |
neilt6 | 0:2a6d8a096edc | 551 | return false; |
neilt6 | 6:55a26a56046a | 552 | } |
neilt6 | 6:55a26a56046a | 553 | |
neilt6 | 6:55a26a56046a | 554 | //Check if large frames are enabled or not |
neilt6 | 6:55a26a56046a | 555 | if (m_LargeFrames) { |
neilt6 | 6:55a26a56046a | 556 | //Switch to 16-bit frames for better performance |
neilt6 | 7:61db99e52c0d | 557 | m_Spi.format(16, 0); |
neilt6 | 0:2a6d8a096edc | 558 | |
neilt6 | 6:55a26a56046a | 559 | //Read the data into the buffer |
neilt6 | 6:55a26a56046a | 560 | unsigned short dataWord; |
neilt6 | 6:55a26a56046a | 561 | for (int i = 0; i < length; i += 2) { |
neilt6 | 7:61db99e52c0d | 562 | dataWord = m_Spi.write(0xFFFF); |
neilt6 | 6:55a26a56046a | 563 | buffer[i] = dataWord >> 8; |
neilt6 | 6:55a26a56046a | 564 | buffer[i + 1] = dataWord; |
neilt6 | 6:55a26a56046a | 565 | } |
neilt6 | 6:55a26a56046a | 566 | |
neilt6 | 6:55a26a56046a | 567 | //Read the CRC16 checksum for the data block |
neilt6 | 7:61db99e52c0d | 568 | crc = m_Spi.write(0xFFFF); |
neilt6 | 0:2a6d8a096edc | 569 | |
neilt6 | 6:55a26a56046a | 570 | //Switch back to 8-bit frames |
neilt6 | 7:61db99e52c0d | 571 | m_Spi.format(8, 0); |
neilt6 | 6:55a26a56046a | 572 | } else { |
neilt6 | 6:55a26a56046a | 573 | //Read the data into the buffer |
neilt6 | 6:55a26a56046a | 574 | for (int i = 0; i < length; i++) |
neilt6 | 7:61db99e52c0d | 575 | buffer[i] = m_Spi.write(0xFF); |
neilt6 | 6:55a26a56046a | 576 | |
neilt6 | 6:55a26a56046a | 577 | //Read the CRC16 checksum for the data block |
neilt6 | 7:61db99e52c0d | 578 | crc = (m_Spi.write(0xFF) << 8); |
neilt6 | 7:61db99e52c0d | 579 | crc |= m_Spi.write(0xFF); |
neilt6 | 6:55a26a56046a | 580 | } |
neilt6 | 6:55a26a56046a | 581 | |
neilt6 | 6:55a26a56046a | 582 | //Deselect the card |
neilt6 | 0:2a6d8a096edc | 583 | deselect(); |
neilt6 | 0:2a6d8a096edc | 584 | |
neilt6 | 6:55a26a56046a | 585 | //Verify the CRC16 checksum (if enabled) |
neilt6 | 7:61db99e52c0d | 586 | return (!m_Crc || crc == CRC16(buffer, length)); |
neilt6 | 0:2a6d8a096edc | 587 | } |