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