RTC auf true
components/storage/blockdevice/COMPONENT_SD/SDBlockDevice.cpp@2:7aab896b1a3b, 2019-03-13 (annotated)
- Committer:
- kevman
- Date:
- Wed Mar 13 11:03:24 2019 +0000
- Revision:
- 2:7aab896b1a3b
2019-03-13
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
kevman | 2:7aab896b1a3b | 1 | /* mbed Microcontroller Library |
kevman | 2:7aab896b1a3b | 2 | * Copyright (c) 2006-2013 ARM Limited |
kevman | 2:7aab896b1a3b | 3 | * |
kevman | 2:7aab896b1a3b | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
kevman | 2:7aab896b1a3b | 5 | * you may not use this file except in compliance with the License. |
kevman | 2:7aab896b1a3b | 6 | * You may obtain a copy of the License at |
kevman | 2:7aab896b1a3b | 7 | * |
kevman | 2:7aab896b1a3b | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
kevman | 2:7aab896b1a3b | 9 | * |
kevman | 2:7aab896b1a3b | 10 | * Unless required by applicable law or agreed to in writing, software |
kevman | 2:7aab896b1a3b | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
kevman | 2:7aab896b1a3b | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
kevman | 2:7aab896b1a3b | 13 | * See the License for the specific language governing permissions and |
kevman | 2:7aab896b1a3b | 14 | * limitations under the License. |
kevman | 2:7aab896b1a3b | 15 | */ |
kevman | 2:7aab896b1a3b | 16 | |
kevman | 2:7aab896b1a3b | 17 | /* Introduction |
kevman | 2:7aab896b1a3b | 18 | * ------------ |
kevman | 2:7aab896b1a3b | 19 | * SD and MMC cards support a number of interfaces, but common to them all |
kevman | 2:7aab896b1a3b | 20 | * is one based on SPI. Since we already have the mbed SPI Interface, it will |
kevman | 2:7aab896b1a3b | 21 | * be used for SD cards. |
kevman | 2:7aab896b1a3b | 22 | * |
kevman | 2:7aab896b1a3b | 23 | * The main reference I'm using is Chapter 7, "SPI Mode" of: |
kevman | 2:7aab896b1a3b | 24 | * http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf |
kevman | 2:7aab896b1a3b | 25 | * |
kevman | 2:7aab896b1a3b | 26 | * SPI Startup |
kevman | 2:7aab896b1a3b | 27 | * ----------- |
kevman | 2:7aab896b1a3b | 28 | * The SD card powers up in SD mode. The start-up procedure is complicated |
kevman | 2:7aab896b1a3b | 29 | * by the requirement to support older SDCards in a backwards compatible |
kevman | 2:7aab896b1a3b | 30 | * way with the new higher capacity variants SDHC and SDHC. |
kevman | 2:7aab896b1a3b | 31 | * |
kevman | 2:7aab896b1a3b | 32 | * The following figures from the specification with associated text describe |
kevman | 2:7aab896b1a3b | 33 | * the SPI mode initialisation process: |
kevman | 2:7aab896b1a3b | 34 | * - Figure 7-1: SD Memory Card State Diagram (SPI mode) |
kevman | 2:7aab896b1a3b | 35 | * - Figure 7-2: SPI Mode Initialization Flow |
kevman | 2:7aab896b1a3b | 36 | * |
kevman | 2:7aab896b1a3b | 37 | * Firstly, a low initial clock should be selected (in the range of 100- |
kevman | 2:7aab896b1a3b | 38 | * 400kHZ). After initialisation has been completed, the switch to a |
kevman | 2:7aab896b1a3b | 39 | * higher clock speed can be made (e.g. 1MHz). Newer cards will support |
kevman | 2:7aab896b1a3b | 40 | * higher speeds than the default _transfer_sck defined here. |
kevman | 2:7aab896b1a3b | 41 | * |
kevman | 2:7aab896b1a3b | 42 | * Next, note the following from the SDCard specification (note to |
kevman | 2:7aab896b1a3b | 43 | * Figure 7-1): |
kevman | 2:7aab896b1a3b | 44 | * |
kevman | 2:7aab896b1a3b | 45 | * In any of the cases CMD1 is not recommended because it may be difficult for the host |
kevman | 2:7aab896b1a3b | 46 | * to distinguish between MultiMediaCard and SD Memory Card |
kevman | 2:7aab896b1a3b | 47 | * |
kevman | 2:7aab896b1a3b | 48 | * Hence CMD1 is not used for the initialisation sequence. |
kevman | 2:7aab896b1a3b | 49 | * |
kevman | 2:7aab896b1a3b | 50 | * The SPI interface mode is selected by asserting CS low and sending the |
kevman | 2:7aab896b1a3b | 51 | * reset command (CMD0). The card will respond with a (R1) response. |
kevman | 2:7aab896b1a3b | 52 | * In practice many cards initially respond with 0xff or invalid data |
kevman | 2:7aab896b1a3b | 53 | * which is ignored. Data is read until a valid response is received |
kevman | 2:7aab896b1a3b | 54 | * or the number of re-reads has exceeded a maximim count. If a valid |
kevman | 2:7aab896b1a3b | 55 | * response is not received then the CMD0 can be retried. This |
kevman | 2:7aab896b1a3b | 56 | * has been found to successfully initialise cards where the SPI master |
kevman | 2:7aab896b1a3b | 57 | * (on MCU) has been reset but the SDCard has not, so the first |
kevman | 2:7aab896b1a3b | 58 | * CMD0 may be lost. |
kevman | 2:7aab896b1a3b | 59 | * |
kevman | 2:7aab896b1a3b | 60 | * CMD8 is optionally sent to determine the voltage range supported, and |
kevman | 2:7aab896b1a3b | 61 | * indirectly determine whether it is a version 1.x SD/non-SD card or |
kevman | 2:7aab896b1a3b | 62 | * version 2.x. I'll just ignore this for now. |
kevman | 2:7aab896b1a3b | 63 | * |
kevman | 2:7aab896b1a3b | 64 | * ACMD41 is repeatedly issued to initialise the card, until "in idle" |
kevman | 2:7aab896b1a3b | 65 | * (bit 0) of the R1 response goes to '0', indicating it is initialised. |
kevman | 2:7aab896b1a3b | 66 | * |
kevman | 2:7aab896b1a3b | 67 | * You should also indicate whether the host supports High Capicity cards, |
kevman | 2:7aab896b1a3b | 68 | * and check whether the card is high capacity - i'll also ignore this |
kevman | 2:7aab896b1a3b | 69 | * |
kevman | 2:7aab896b1a3b | 70 | * SPI Protocol |
kevman | 2:7aab896b1a3b | 71 | * ------------ |
kevman | 2:7aab896b1a3b | 72 | * The SD SPI protocol is based on transactions made up of 8-bit words, with |
kevman | 2:7aab896b1a3b | 73 | * the host starting every bus transaction by asserting the CS signal low. The |
kevman | 2:7aab896b1a3b | 74 | * card always responds to commands, data blocks and errors. |
kevman | 2:7aab896b1a3b | 75 | * |
kevman | 2:7aab896b1a3b | 76 | * The protocol supports a CRC, but by default it is off (except for the |
kevman | 2:7aab896b1a3b | 77 | * first reset CMD0, where the CRC can just be pre-calculated, and CMD8) |
kevman | 2:7aab896b1a3b | 78 | * I'll leave the CRC off I think! |
kevman | 2:7aab896b1a3b | 79 | * |
kevman | 2:7aab896b1a3b | 80 | * Standard capacity cards have variable data block sizes, whereas High |
kevman | 2:7aab896b1a3b | 81 | * Capacity cards fix the size of data block to 512 bytes. I'll therefore |
kevman | 2:7aab896b1a3b | 82 | * just always use the Standard Capacity cards with a block size of 512 bytes. |
kevman | 2:7aab896b1a3b | 83 | * This is set with CMD16. |
kevman | 2:7aab896b1a3b | 84 | * |
kevman | 2:7aab896b1a3b | 85 | * You can read and write single blocks (CMD17, CMD25) or multiple blocks |
kevman | 2:7aab896b1a3b | 86 | * (CMD18, CMD25). For simplicity, I'll just use single block accesses. When |
kevman | 2:7aab896b1a3b | 87 | * the card gets a read command, it responds with a response token, and then |
kevman | 2:7aab896b1a3b | 88 | * a data token or an error. |
kevman | 2:7aab896b1a3b | 89 | * |
kevman | 2:7aab896b1a3b | 90 | * SPI Command Format |
kevman | 2:7aab896b1a3b | 91 | * ------------------ |
kevman | 2:7aab896b1a3b | 92 | * Commands are 6-bytes long, containing the command, 32-bit argument, and CRC. |
kevman | 2:7aab896b1a3b | 93 | * |
kevman | 2:7aab896b1a3b | 94 | * +---------------+------------+------------+-----------+----------+--------------+ |
kevman | 2:7aab896b1a3b | 95 | * | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 | |
kevman | 2:7aab896b1a3b | 96 | * +---------------+------------+------------+-----------+----------+--------------+ |
kevman | 2:7aab896b1a3b | 97 | * |
kevman | 2:7aab896b1a3b | 98 | * As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95) |
kevman | 2:7aab896b1a3b | 99 | * |
kevman | 2:7aab896b1a3b | 100 | * All Application Specific commands shall be preceded with APP_CMD (CMD55). |
kevman | 2:7aab896b1a3b | 101 | * |
kevman | 2:7aab896b1a3b | 102 | * SPI Response Format |
kevman | 2:7aab896b1a3b | 103 | * ------------------- |
kevman | 2:7aab896b1a3b | 104 | * The main response format (R1) is a status byte (normally zero). Key flags: |
kevman | 2:7aab896b1a3b | 105 | * idle - 1 if the card is in an idle state/initialising |
kevman | 2:7aab896b1a3b | 106 | * cmd - 1 if an illegal command code was detected |
kevman | 2:7aab896b1a3b | 107 | * |
kevman | 2:7aab896b1a3b | 108 | * +-------------------------------------------------+ |
kevman | 2:7aab896b1a3b | 109 | * R1 | 0 | arg | addr | seq | crc | cmd | erase | idle | |
kevman | 2:7aab896b1a3b | 110 | * +-------------------------------------------------+ |
kevman | 2:7aab896b1a3b | 111 | * |
kevman | 2:7aab896b1a3b | 112 | * R1b is the same, except it is followed by a busy signal (zeros) until |
kevman | 2:7aab896b1a3b | 113 | * the first non-zero byte when it is ready again. |
kevman | 2:7aab896b1a3b | 114 | * |
kevman | 2:7aab896b1a3b | 115 | * Data Response Token |
kevman | 2:7aab896b1a3b | 116 | * ------------------- |
kevman | 2:7aab896b1a3b | 117 | * Every data block written to the card is acknowledged by a byte |
kevman | 2:7aab896b1a3b | 118 | * response token |
kevman | 2:7aab896b1a3b | 119 | * |
kevman | 2:7aab896b1a3b | 120 | * +----------------------+ |
kevman | 2:7aab896b1a3b | 121 | * | xxx | 0 | status | 1 | |
kevman | 2:7aab896b1a3b | 122 | * +----------------------+ |
kevman | 2:7aab896b1a3b | 123 | * 010 - OK! |
kevman | 2:7aab896b1a3b | 124 | * 101 - CRC Error |
kevman | 2:7aab896b1a3b | 125 | * 110 - Write Error |
kevman | 2:7aab896b1a3b | 126 | * |
kevman | 2:7aab896b1a3b | 127 | * Single Block Read and Write |
kevman | 2:7aab896b1a3b | 128 | * --------------------------- |
kevman | 2:7aab896b1a3b | 129 | * |
kevman | 2:7aab896b1a3b | 130 | * Block transfers have a byte header, followed by the data, followed |
kevman | 2:7aab896b1a3b | 131 | * by a 16-bit CRC. In our case, the data will always be 512 bytes. |
kevman | 2:7aab896b1a3b | 132 | * |
kevman | 2:7aab896b1a3b | 133 | * +------+---------+---------+- - - -+---------+-----------+----------+ |
kevman | 2:7aab896b1a3b | 134 | * | 0xFE | data[0] | data[1] | | data[n] | crc[15:8] | crc[7:0] | |
kevman | 2:7aab896b1a3b | 135 | * +------+---------+---------+- - - -+---------+-----------+----------+ |
kevman | 2:7aab896b1a3b | 136 | */ |
kevman | 2:7aab896b1a3b | 137 | |
kevman | 2:7aab896b1a3b | 138 | /* If the target has no SPI support then SDCard is not supported */ |
kevman | 2:7aab896b1a3b | 139 | #ifdef DEVICE_SPI |
kevman | 2:7aab896b1a3b | 140 | |
kevman | 2:7aab896b1a3b | 141 | #include "SDBlockDevice.h" |
kevman | 2:7aab896b1a3b | 142 | #include "platform/mbed_debug.h" |
kevman | 2:7aab896b1a3b | 143 | #include "platform/mbed_wait_api.h" |
kevman | 2:7aab896b1a3b | 144 | #include <errno.h> |
kevman | 2:7aab896b1a3b | 145 | |
kevman | 2:7aab896b1a3b | 146 | #ifndef MBED_CONF_SD_CMD_TIMEOUT |
kevman | 2:7aab896b1a3b | 147 | #define MBED_CONF_SD_CMD_TIMEOUT 5000 /*!< Timeout in ms for response */ |
kevman | 2:7aab896b1a3b | 148 | #endif |
kevman | 2:7aab896b1a3b | 149 | |
kevman | 2:7aab896b1a3b | 150 | #ifndef MBED_CONF_SD_CMD0_IDLE_STATE_RETRIES |
kevman | 2:7aab896b1a3b | 151 | #define MBED_CONF_SD_CMD0_IDLE_STATE_RETRIES 5 /*!< Number of retries for sending CMDO */ |
kevman | 2:7aab896b1a3b | 152 | #endif |
kevman | 2:7aab896b1a3b | 153 | |
kevman | 2:7aab896b1a3b | 154 | #ifndef MBED_CONF_SD_INIT_FREQUENCY |
kevman | 2:7aab896b1a3b | 155 | #define MBED_CONF_SD_INIT_FREQUENCY 100000 /*!< Initialization frequency Range (100KHz-400KHz) */ |
kevman | 2:7aab896b1a3b | 156 | #endif |
kevman | 2:7aab896b1a3b | 157 | |
kevman | 2:7aab896b1a3b | 158 | |
kevman | 2:7aab896b1a3b | 159 | #define SD_COMMAND_TIMEOUT MBED_CONF_SD_CMD_TIMEOUT |
kevman | 2:7aab896b1a3b | 160 | #define SD_CMD0_GO_IDLE_STATE_RETRIES MBED_CONF_SD_CMD0_IDLE_STATE_RETRIES |
kevman | 2:7aab896b1a3b | 161 | #define SD_DBG 0 /*!< 1 - Enable debugging */ |
kevman | 2:7aab896b1a3b | 162 | #define SD_CMD_TRACE 0 /*!< 1 - Enable SD command tracing */ |
kevman | 2:7aab896b1a3b | 163 | |
kevman | 2:7aab896b1a3b | 164 | #define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */ |
kevman | 2:7aab896b1a3b | 165 | #define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */ |
kevman | 2:7aab896b1a3b | 166 | #define SD_BLOCK_DEVICE_ERROR_PARAMETER -5003 /*!< invalid parameter */ |
kevman | 2:7aab896b1a3b | 167 | #define SD_BLOCK_DEVICE_ERROR_NO_INIT -5004 /*!< uninitialized */ |
kevman | 2:7aab896b1a3b | 168 | #define SD_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */ |
kevman | 2:7aab896b1a3b | 169 | #define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */ |
kevman | 2:7aab896b1a3b | 170 | #define SD_BLOCK_DEVICE_ERROR_UNUSABLE -5007 /*!< unusable card */ |
kevman | 2:7aab896b1a3b | 171 | #define SD_BLOCK_DEVICE_ERROR_NO_RESPONSE -5008 /*!< No response from device */ |
kevman | 2:7aab896b1a3b | 172 | #define SD_BLOCK_DEVICE_ERROR_CRC -5009 /*!< CRC error */ |
kevman | 2:7aab896b1a3b | 173 | #define SD_BLOCK_DEVICE_ERROR_ERASE -5010 /*!< Erase error: reset/sequence */ |
kevman | 2:7aab896b1a3b | 174 | #define SD_BLOCK_DEVICE_ERROR_WRITE -5011 /*!< SPI Write error: !SPI_DATA_ACCEPTED */ |
kevman | 2:7aab896b1a3b | 175 | |
kevman | 2:7aab896b1a3b | 176 | #define BLOCK_SIZE_HC 512 /*!< Block size supported for SD card is 512 bytes */ |
kevman | 2:7aab896b1a3b | 177 | #define WRITE_BL_PARTIAL 0 /*!< Partial block write - Not supported */ |
kevman | 2:7aab896b1a3b | 178 | #define SPI_CMD(x) (0x40 | (x & 0x3f)) |
kevman | 2:7aab896b1a3b | 179 | |
kevman | 2:7aab896b1a3b | 180 | /* R1 Response Format */ |
kevman | 2:7aab896b1a3b | 181 | #define R1_NO_RESPONSE (0xFF) |
kevman | 2:7aab896b1a3b | 182 | #define R1_RESPONSE_RECV (0x80) |
kevman | 2:7aab896b1a3b | 183 | #define R1_IDLE_STATE (1 << 0) |
kevman | 2:7aab896b1a3b | 184 | #define R1_ERASE_RESET (1 << 1) |
kevman | 2:7aab896b1a3b | 185 | #define R1_ILLEGAL_COMMAND (1 << 2) |
kevman | 2:7aab896b1a3b | 186 | #define R1_COM_CRC_ERROR (1 << 3) |
kevman | 2:7aab896b1a3b | 187 | #define R1_ERASE_SEQUENCE_ERROR (1 << 4) |
kevman | 2:7aab896b1a3b | 188 | #define R1_ADDRESS_ERROR (1 << 5) |
kevman | 2:7aab896b1a3b | 189 | #define R1_PARAMETER_ERROR (1 << 6) |
kevman | 2:7aab896b1a3b | 190 | |
kevman | 2:7aab896b1a3b | 191 | // Types |
kevman | 2:7aab896b1a3b | 192 | #define SDCARD_NONE 0 /**< No card is present */ |
kevman | 2:7aab896b1a3b | 193 | #define SDCARD_V1 1 /**< v1.x Standard Capacity */ |
kevman | 2:7aab896b1a3b | 194 | #define SDCARD_V2 2 /**< v2.x Standard capacity SD card */ |
kevman | 2:7aab896b1a3b | 195 | #define SDCARD_V2HC 3 /**< v2.x High capacity SD card */ |
kevman | 2:7aab896b1a3b | 196 | #define CARD_UNKNOWN 4 /**< Unknown or unsupported card */ |
kevman | 2:7aab896b1a3b | 197 | |
kevman | 2:7aab896b1a3b | 198 | /* SIZE in Bytes */ |
kevman | 2:7aab896b1a3b | 199 | #define PACKET_SIZE 6 /*!< SD Packet size CMD+ARG+CRC */ |
kevman | 2:7aab896b1a3b | 200 | #define R1_RESPONSE_SIZE 1 /*!< Size of R1 response */ |
kevman | 2:7aab896b1a3b | 201 | #define R2_RESPONSE_SIZE 2 /*!< Size of R2 response */ |
kevman | 2:7aab896b1a3b | 202 | #define R3_R7_RESPONSE_SIZE 5 /*!< Size of R3/R7 response */ |
kevman | 2:7aab896b1a3b | 203 | |
kevman | 2:7aab896b1a3b | 204 | /* R1b Response */ |
kevman | 2:7aab896b1a3b | 205 | #define DEVICE_BUSY (0x00) |
kevman | 2:7aab896b1a3b | 206 | |
kevman | 2:7aab896b1a3b | 207 | /* R2 Response Format */ |
kevman | 2:7aab896b1a3b | 208 | #define R2_CARD_LOCKED (1 << 0) |
kevman | 2:7aab896b1a3b | 209 | #define R2_CMD_FAILED (1 << 1) |
kevman | 2:7aab896b1a3b | 210 | #define R2_ERROR (1 << 2) |
kevman | 2:7aab896b1a3b | 211 | #define R2_CC_ERROR (1 << 3) |
kevman | 2:7aab896b1a3b | 212 | #define R2_CC_FAILED (1 << 4) |
kevman | 2:7aab896b1a3b | 213 | #define R2_WP_VIOLATION (1 << 5) |
kevman | 2:7aab896b1a3b | 214 | #define R2_ERASE_PARAM (1 << 6) |
kevman | 2:7aab896b1a3b | 215 | #define R2_OUT_OF_RANGE (1 << 7) |
kevman | 2:7aab896b1a3b | 216 | |
kevman | 2:7aab896b1a3b | 217 | /* R3 Response : OCR Register */ |
kevman | 2:7aab896b1a3b | 218 | #define OCR_HCS_CCS (0x1 << 30) |
kevman | 2:7aab896b1a3b | 219 | #define OCR_LOW_VOLTAGE (0x01 << 24) |
kevman | 2:7aab896b1a3b | 220 | #define OCR_3_3V (0x1 << 20) |
kevman | 2:7aab896b1a3b | 221 | |
kevman | 2:7aab896b1a3b | 222 | /* R7 response pattern for CMD8 */ |
kevman | 2:7aab896b1a3b | 223 | #define CMD8_PATTERN (0xAA) |
kevman | 2:7aab896b1a3b | 224 | |
kevman | 2:7aab896b1a3b | 225 | /* CRC Enable */ |
kevman | 2:7aab896b1a3b | 226 | #define CRC_ENABLE (0) /*!< CRC 1 - Enable 0 - Disable */ |
kevman | 2:7aab896b1a3b | 227 | |
kevman | 2:7aab896b1a3b | 228 | /* Control Tokens */ |
kevman | 2:7aab896b1a3b | 229 | #define SPI_DATA_RESPONSE_MASK (0x1F) |
kevman | 2:7aab896b1a3b | 230 | #define SPI_DATA_ACCEPTED (0x05) |
kevman | 2:7aab896b1a3b | 231 | #define SPI_DATA_CRC_ERROR (0x0B) |
kevman | 2:7aab896b1a3b | 232 | #define SPI_DATA_WRITE_ERROR (0x0D) |
kevman | 2:7aab896b1a3b | 233 | #define SPI_START_BLOCK (0xFE) /*!< For Single Block Read/Write and Multiple Block Read */ |
kevman | 2:7aab896b1a3b | 234 | #define SPI_START_BLK_MUL_WRITE (0xFC) /*!< Start Multi-block write */ |
kevman | 2:7aab896b1a3b | 235 | #define SPI_STOP_TRAN (0xFD) /*!< Stop Multi-block write */ |
kevman | 2:7aab896b1a3b | 236 | |
kevman | 2:7aab896b1a3b | 237 | #define SPI_DATA_READ_ERROR_MASK (0xF) /*!< Data Error Token: 4 LSB bits */ |
kevman | 2:7aab896b1a3b | 238 | #define SPI_READ_ERROR (0x1 << 0) /*!< Error */ |
kevman | 2:7aab896b1a3b | 239 | #define SPI_READ_ERROR_CC (0x1 << 1) /*!< CC Error*/ |
kevman | 2:7aab896b1a3b | 240 | #define SPI_READ_ERROR_ECC_C (0x1 << 2) /*!< Card ECC failed */ |
kevman | 2:7aab896b1a3b | 241 | #define SPI_READ_ERROR_OFR (0x1 << 3) /*!< Out of Range */ |
kevman | 2:7aab896b1a3b | 242 | |
kevman | 2:7aab896b1a3b | 243 | SDBlockDevice::SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, uint64_t hz, bool crc_on) |
kevman | 2:7aab896b1a3b | 244 | : _sectors(0), _spi(mosi, miso, sclk), _cs(cs), _is_initialized(0), |
kevman | 2:7aab896b1a3b | 245 | _crc_on(crc_on), _init_ref_count(0), _crc16(0, 0, false, false) |
kevman | 2:7aab896b1a3b | 246 | { |
kevman | 2:7aab896b1a3b | 247 | _cs = 1; |
kevman | 2:7aab896b1a3b | 248 | _card_type = SDCARD_NONE; |
kevman | 2:7aab896b1a3b | 249 | |
kevman | 2:7aab896b1a3b | 250 | // Set default to 100kHz for initialisation and 1MHz for data transfer |
kevman | 2:7aab896b1a3b | 251 | MBED_STATIC_ASSERT(((MBED_CONF_SD_INIT_FREQUENCY >= 100000) && (MBED_CONF_SD_INIT_FREQUENCY <= 400000)), |
kevman | 2:7aab896b1a3b | 252 | "Initialization frequency should be between 100KHz to 400KHz"); |
kevman | 2:7aab896b1a3b | 253 | _init_sck = MBED_CONF_SD_INIT_FREQUENCY; |
kevman | 2:7aab896b1a3b | 254 | _transfer_sck = hz; |
kevman | 2:7aab896b1a3b | 255 | |
kevman | 2:7aab896b1a3b | 256 | // Only HC block size is supported. |
kevman | 2:7aab896b1a3b | 257 | _block_size = BLOCK_SIZE_HC; |
kevman | 2:7aab896b1a3b | 258 | _erase_size = BLOCK_SIZE_HC; |
kevman | 2:7aab896b1a3b | 259 | } |
kevman | 2:7aab896b1a3b | 260 | |
kevman | 2:7aab896b1a3b | 261 | SDBlockDevice::~SDBlockDevice() |
kevman | 2:7aab896b1a3b | 262 | { |
kevman | 2:7aab896b1a3b | 263 | if (_is_initialized) { |
kevman | 2:7aab896b1a3b | 264 | deinit(); |
kevman | 2:7aab896b1a3b | 265 | } |
kevman | 2:7aab896b1a3b | 266 | } |
kevman | 2:7aab896b1a3b | 267 | |
kevman | 2:7aab896b1a3b | 268 | int SDBlockDevice::_initialise_card() |
kevman | 2:7aab896b1a3b | 269 | { |
kevman | 2:7aab896b1a3b | 270 | // Detail debugging is for commands |
kevman | 2:7aab896b1a3b | 271 | _dbg = SD_DBG ? SD_CMD_TRACE : 0; |
kevman | 2:7aab896b1a3b | 272 | int32_t status = BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 273 | uint32_t response, arg; |
kevman | 2:7aab896b1a3b | 274 | |
kevman | 2:7aab896b1a3b | 275 | // Initialize the SPI interface: Card by default is in SD mode |
kevman | 2:7aab896b1a3b | 276 | _spi_init(); |
kevman | 2:7aab896b1a3b | 277 | |
kevman | 2:7aab896b1a3b | 278 | // The card is transitioned from SDCard mode to SPI mode by sending the CMD0 + CS Asserted("0") |
kevman | 2:7aab896b1a3b | 279 | if (_go_idle_state() != R1_IDLE_STATE) { |
kevman | 2:7aab896b1a3b | 280 | debug_if(SD_DBG, "No disk, or could not put SD card in to SPI idle state\n"); |
kevman | 2:7aab896b1a3b | 281 | return SD_BLOCK_DEVICE_ERROR_NO_DEVICE; |
kevman | 2:7aab896b1a3b | 282 | } |
kevman | 2:7aab896b1a3b | 283 | |
kevman | 2:7aab896b1a3b | 284 | // Send CMD8, if the card rejects the command then it's probably using the |
kevman | 2:7aab896b1a3b | 285 | // legacy protocol, or is a MMC, or just flat-out broken |
kevman | 2:7aab896b1a3b | 286 | status = _cmd8(); |
kevman | 2:7aab896b1a3b | 287 | if (BD_ERROR_OK != status && SD_BLOCK_DEVICE_ERROR_UNSUPPORTED != status) { |
kevman | 2:7aab896b1a3b | 288 | return status; |
kevman | 2:7aab896b1a3b | 289 | } |
kevman | 2:7aab896b1a3b | 290 | |
kevman | 2:7aab896b1a3b | 291 | if (_crc_on) { |
kevman | 2:7aab896b1a3b | 292 | // Enable CRC |
kevman | 2:7aab896b1a3b | 293 | status = _cmd(CMD59_CRC_ON_OFF, _crc_on); |
kevman | 2:7aab896b1a3b | 294 | } |
kevman | 2:7aab896b1a3b | 295 | |
kevman | 2:7aab896b1a3b | 296 | // Read OCR - CMD58 Response contains OCR register |
kevman | 2:7aab896b1a3b | 297 | if (BD_ERROR_OK != (status = _cmd(CMD58_READ_OCR, 0x0, 0x0, &response))) { |
kevman | 2:7aab896b1a3b | 298 | return status; |
kevman | 2:7aab896b1a3b | 299 | } |
kevman | 2:7aab896b1a3b | 300 | |
kevman | 2:7aab896b1a3b | 301 | // Check if card supports voltage range: 3.3V |
kevman | 2:7aab896b1a3b | 302 | if (!(response & OCR_3_3V)) { |
kevman | 2:7aab896b1a3b | 303 | _card_type = CARD_UNKNOWN; |
kevman | 2:7aab896b1a3b | 304 | status = SD_BLOCK_DEVICE_ERROR_UNUSABLE; |
kevman | 2:7aab896b1a3b | 305 | return status; |
kevman | 2:7aab896b1a3b | 306 | } |
kevman | 2:7aab896b1a3b | 307 | |
kevman | 2:7aab896b1a3b | 308 | // HCS is set 1 for HC/XC capacity cards for ACMD41, if supported |
kevman | 2:7aab896b1a3b | 309 | arg = 0x0; |
kevman | 2:7aab896b1a3b | 310 | if (SDCARD_V2 == _card_type) { |
kevman | 2:7aab896b1a3b | 311 | arg |= OCR_HCS_CCS; |
kevman | 2:7aab896b1a3b | 312 | } |
kevman | 2:7aab896b1a3b | 313 | |
kevman | 2:7aab896b1a3b | 314 | /* Idle state bit in the R1 response of ACMD41 is used by the card to inform the host |
kevman | 2:7aab896b1a3b | 315 | * if initialization of ACMD41 is completed. "1" indicates that the card is still initializing. |
kevman | 2:7aab896b1a3b | 316 | * "0" indicates completion of initialization. The host repeatedly issues ACMD41 until |
kevman | 2:7aab896b1a3b | 317 | * this bit is set to "0". |
kevman | 2:7aab896b1a3b | 318 | */ |
kevman | 2:7aab896b1a3b | 319 | _spi_timer.start(); |
kevman | 2:7aab896b1a3b | 320 | do { |
kevman | 2:7aab896b1a3b | 321 | status = _cmd(ACMD41_SD_SEND_OP_COND, arg, 1, &response); |
kevman | 2:7aab896b1a3b | 322 | } while ((response & R1_IDLE_STATE) && (_spi_timer.read_ms() < SD_COMMAND_TIMEOUT)); |
kevman | 2:7aab896b1a3b | 323 | _spi_timer.stop(); |
kevman | 2:7aab896b1a3b | 324 | |
kevman | 2:7aab896b1a3b | 325 | // Initialization complete: ACMD41 successful |
kevman | 2:7aab896b1a3b | 326 | if ((BD_ERROR_OK != status) || (0x00 != response)) { |
kevman | 2:7aab896b1a3b | 327 | _card_type = CARD_UNKNOWN; |
kevman | 2:7aab896b1a3b | 328 | debug_if(SD_DBG, "Timeout waiting for card\n"); |
kevman | 2:7aab896b1a3b | 329 | return status; |
kevman | 2:7aab896b1a3b | 330 | } |
kevman | 2:7aab896b1a3b | 331 | |
kevman | 2:7aab896b1a3b | 332 | if (SDCARD_V2 == _card_type) { |
kevman | 2:7aab896b1a3b | 333 | // Get the card capacity CCS: CMD58 |
kevman | 2:7aab896b1a3b | 334 | if (BD_ERROR_OK == (status = _cmd(CMD58_READ_OCR, 0x0, 0x0, &response))) { |
kevman | 2:7aab896b1a3b | 335 | // High Capacity card |
kevman | 2:7aab896b1a3b | 336 | if (response & OCR_HCS_CCS) { |
kevman | 2:7aab896b1a3b | 337 | _card_type = SDCARD_V2HC; |
kevman | 2:7aab896b1a3b | 338 | debug_if(SD_DBG, "Card Initialized: High Capacity Card \n"); |
kevman | 2:7aab896b1a3b | 339 | } else { |
kevman | 2:7aab896b1a3b | 340 | debug_if(SD_DBG, "Card Initialized: Standard Capacity Card: Version 2.x \n"); |
kevman | 2:7aab896b1a3b | 341 | } |
kevman | 2:7aab896b1a3b | 342 | } |
kevman | 2:7aab896b1a3b | 343 | } else { |
kevman | 2:7aab896b1a3b | 344 | _card_type = SDCARD_V1; |
kevman | 2:7aab896b1a3b | 345 | debug_if(SD_DBG, "Card Initialized: Version 1.x Card\n"); |
kevman | 2:7aab896b1a3b | 346 | } |
kevman | 2:7aab896b1a3b | 347 | |
kevman | 2:7aab896b1a3b | 348 | if (!_crc_on) { |
kevman | 2:7aab896b1a3b | 349 | // Disable CRC |
kevman | 2:7aab896b1a3b | 350 | status = _cmd(CMD59_CRC_ON_OFF, _crc_on); |
kevman | 2:7aab896b1a3b | 351 | } |
kevman | 2:7aab896b1a3b | 352 | return status; |
kevman | 2:7aab896b1a3b | 353 | } |
kevman | 2:7aab896b1a3b | 354 | |
kevman | 2:7aab896b1a3b | 355 | |
kevman | 2:7aab896b1a3b | 356 | int SDBlockDevice::init() |
kevman | 2:7aab896b1a3b | 357 | { |
kevman | 2:7aab896b1a3b | 358 | int err; |
kevman | 2:7aab896b1a3b | 359 | |
kevman | 2:7aab896b1a3b | 360 | lock(); |
kevman | 2:7aab896b1a3b | 361 | |
kevman | 2:7aab896b1a3b | 362 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 363 | _init_ref_count = 0; |
kevman | 2:7aab896b1a3b | 364 | } |
kevman | 2:7aab896b1a3b | 365 | |
kevman | 2:7aab896b1a3b | 366 | _init_ref_count++; |
kevman | 2:7aab896b1a3b | 367 | |
kevman | 2:7aab896b1a3b | 368 | if (_init_ref_count != 1) { |
kevman | 2:7aab896b1a3b | 369 | goto end; |
kevman | 2:7aab896b1a3b | 370 | } |
kevman | 2:7aab896b1a3b | 371 | |
kevman | 2:7aab896b1a3b | 372 | err = _initialise_card(); |
kevman | 2:7aab896b1a3b | 373 | _is_initialized = (err == BD_ERROR_OK); |
kevman | 2:7aab896b1a3b | 374 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 375 | debug_if(SD_DBG, "Fail to initialize card\n"); |
kevman | 2:7aab896b1a3b | 376 | unlock(); |
kevman | 2:7aab896b1a3b | 377 | return err; |
kevman | 2:7aab896b1a3b | 378 | } |
kevman | 2:7aab896b1a3b | 379 | debug_if(SD_DBG, "init card = %d\n", _is_initialized); |
kevman | 2:7aab896b1a3b | 380 | _sectors = _sd_sectors(); |
kevman | 2:7aab896b1a3b | 381 | // CMD9 failed |
kevman | 2:7aab896b1a3b | 382 | if (0 == _sectors) { |
kevman | 2:7aab896b1a3b | 383 | unlock(); |
kevman | 2:7aab896b1a3b | 384 | return BD_ERROR_DEVICE_ERROR; |
kevman | 2:7aab896b1a3b | 385 | } |
kevman | 2:7aab896b1a3b | 386 | |
kevman | 2:7aab896b1a3b | 387 | // Set block length to 512 (CMD16) |
kevman | 2:7aab896b1a3b | 388 | if (_cmd(CMD16_SET_BLOCKLEN, _block_size) != 0) { |
kevman | 2:7aab896b1a3b | 389 | debug_if(SD_DBG, "Set %d-byte block timed out\n", _block_size); |
kevman | 2:7aab896b1a3b | 390 | unlock(); |
kevman | 2:7aab896b1a3b | 391 | return BD_ERROR_DEVICE_ERROR; |
kevman | 2:7aab896b1a3b | 392 | } |
kevman | 2:7aab896b1a3b | 393 | |
kevman | 2:7aab896b1a3b | 394 | // Set SCK for data transfer |
kevman | 2:7aab896b1a3b | 395 | err = _freq(); |
kevman | 2:7aab896b1a3b | 396 | if (err) { |
kevman | 2:7aab896b1a3b | 397 | unlock(); |
kevman | 2:7aab896b1a3b | 398 | return err; |
kevman | 2:7aab896b1a3b | 399 | } |
kevman | 2:7aab896b1a3b | 400 | |
kevman | 2:7aab896b1a3b | 401 | end: |
kevman | 2:7aab896b1a3b | 402 | unlock(); |
kevman | 2:7aab896b1a3b | 403 | return BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 404 | } |
kevman | 2:7aab896b1a3b | 405 | |
kevman | 2:7aab896b1a3b | 406 | int SDBlockDevice::deinit() |
kevman | 2:7aab896b1a3b | 407 | { |
kevman | 2:7aab896b1a3b | 408 | lock(); |
kevman | 2:7aab896b1a3b | 409 | |
kevman | 2:7aab896b1a3b | 410 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 411 | _init_ref_count = 0; |
kevman | 2:7aab896b1a3b | 412 | goto end; |
kevman | 2:7aab896b1a3b | 413 | } |
kevman | 2:7aab896b1a3b | 414 | |
kevman | 2:7aab896b1a3b | 415 | _init_ref_count--; |
kevman | 2:7aab896b1a3b | 416 | |
kevman | 2:7aab896b1a3b | 417 | if (_init_ref_count) { |
kevman | 2:7aab896b1a3b | 418 | goto end; |
kevman | 2:7aab896b1a3b | 419 | } |
kevman | 2:7aab896b1a3b | 420 | |
kevman | 2:7aab896b1a3b | 421 | _is_initialized = false; |
kevman | 2:7aab896b1a3b | 422 | _sectors = 0; |
kevman | 2:7aab896b1a3b | 423 | |
kevman | 2:7aab896b1a3b | 424 | end: |
kevman | 2:7aab896b1a3b | 425 | unlock(); |
kevman | 2:7aab896b1a3b | 426 | return BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 427 | } |
kevman | 2:7aab896b1a3b | 428 | |
kevman | 2:7aab896b1a3b | 429 | |
kevman | 2:7aab896b1a3b | 430 | int SDBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size) |
kevman | 2:7aab896b1a3b | 431 | { |
kevman | 2:7aab896b1a3b | 432 | if (!is_valid_program(addr, size)) { |
kevman | 2:7aab896b1a3b | 433 | return SD_BLOCK_DEVICE_ERROR_PARAMETER; |
kevman | 2:7aab896b1a3b | 434 | } |
kevman | 2:7aab896b1a3b | 435 | |
kevman | 2:7aab896b1a3b | 436 | lock(); |
kevman | 2:7aab896b1a3b | 437 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 438 | unlock(); |
kevman | 2:7aab896b1a3b | 439 | return SD_BLOCK_DEVICE_ERROR_NO_INIT; |
kevman | 2:7aab896b1a3b | 440 | } |
kevman | 2:7aab896b1a3b | 441 | |
kevman | 2:7aab896b1a3b | 442 | const uint8_t *buffer = static_cast<const uint8_t *>(b); |
kevman | 2:7aab896b1a3b | 443 | int status = BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 444 | uint8_t response; |
kevman | 2:7aab896b1a3b | 445 | |
kevman | 2:7aab896b1a3b | 446 | // Get block count |
kevman | 2:7aab896b1a3b | 447 | bd_addr_t blockCnt = size / _block_size; |
kevman | 2:7aab896b1a3b | 448 | |
kevman | 2:7aab896b1a3b | 449 | // SDSC Card (CCS=0) uses byte unit address |
kevman | 2:7aab896b1a3b | 450 | // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit) |
kevman | 2:7aab896b1a3b | 451 | if (SDCARD_V2HC == _card_type) { |
kevman | 2:7aab896b1a3b | 452 | addr = addr / _block_size; |
kevman | 2:7aab896b1a3b | 453 | } |
kevman | 2:7aab896b1a3b | 454 | |
kevman | 2:7aab896b1a3b | 455 | // Send command to perform write operation |
kevman | 2:7aab896b1a3b | 456 | if (blockCnt == 1) { |
kevman | 2:7aab896b1a3b | 457 | // Single block write command |
kevman | 2:7aab896b1a3b | 458 | if (BD_ERROR_OK != (status = _cmd(CMD24_WRITE_BLOCK, addr))) { |
kevman | 2:7aab896b1a3b | 459 | unlock(); |
kevman | 2:7aab896b1a3b | 460 | return status; |
kevman | 2:7aab896b1a3b | 461 | } |
kevman | 2:7aab896b1a3b | 462 | |
kevman | 2:7aab896b1a3b | 463 | // Write data |
kevman | 2:7aab896b1a3b | 464 | response = _write(buffer, SPI_START_BLOCK, _block_size); |
kevman | 2:7aab896b1a3b | 465 | |
kevman | 2:7aab896b1a3b | 466 | // Only CRC and general write error are communicated via response token |
kevman | 2:7aab896b1a3b | 467 | if (response != SPI_DATA_ACCEPTED) { |
kevman | 2:7aab896b1a3b | 468 | debug_if(SD_DBG, "Single Block Write failed: 0x%x \n", response); |
kevman | 2:7aab896b1a3b | 469 | status = SD_BLOCK_DEVICE_ERROR_WRITE; |
kevman | 2:7aab896b1a3b | 470 | } |
kevman | 2:7aab896b1a3b | 471 | } else { |
kevman | 2:7aab896b1a3b | 472 | // Pre-erase setting prior to multiple block write operation |
kevman | 2:7aab896b1a3b | 473 | _cmd(ACMD23_SET_WR_BLK_ERASE_COUNT, blockCnt, 1); |
kevman | 2:7aab896b1a3b | 474 | |
kevman | 2:7aab896b1a3b | 475 | // Multiple block write command |
kevman | 2:7aab896b1a3b | 476 | if (BD_ERROR_OK != (status = _cmd(CMD25_WRITE_MULTIPLE_BLOCK, addr))) { |
kevman | 2:7aab896b1a3b | 477 | unlock(); |
kevman | 2:7aab896b1a3b | 478 | return status; |
kevman | 2:7aab896b1a3b | 479 | } |
kevman | 2:7aab896b1a3b | 480 | |
kevman | 2:7aab896b1a3b | 481 | // Write the data: one block at a time |
kevman | 2:7aab896b1a3b | 482 | do { |
kevman | 2:7aab896b1a3b | 483 | response = _write(buffer, SPI_START_BLK_MUL_WRITE, _block_size); |
kevman | 2:7aab896b1a3b | 484 | if (response != SPI_DATA_ACCEPTED) { |
kevman | 2:7aab896b1a3b | 485 | debug_if(SD_DBG, "Multiple Block Write failed: 0x%x \n", response); |
kevman | 2:7aab896b1a3b | 486 | break; |
kevman | 2:7aab896b1a3b | 487 | } |
kevman | 2:7aab896b1a3b | 488 | buffer += _block_size; |
kevman | 2:7aab896b1a3b | 489 | } while (--blockCnt); // Receive all blocks of data |
kevman | 2:7aab896b1a3b | 490 | |
kevman | 2:7aab896b1a3b | 491 | /* In a Multiple Block write operation, the stop transmission will be done by |
kevman | 2:7aab896b1a3b | 492 | * sending 'Stop Tran' token instead of 'Start Block' token at the beginning |
kevman | 2:7aab896b1a3b | 493 | * of the next block |
kevman | 2:7aab896b1a3b | 494 | */ |
kevman | 2:7aab896b1a3b | 495 | _spi.write(SPI_STOP_TRAN); |
kevman | 2:7aab896b1a3b | 496 | } |
kevman | 2:7aab896b1a3b | 497 | |
kevman | 2:7aab896b1a3b | 498 | _deselect(); |
kevman | 2:7aab896b1a3b | 499 | unlock(); |
kevman | 2:7aab896b1a3b | 500 | return status; |
kevman | 2:7aab896b1a3b | 501 | } |
kevman | 2:7aab896b1a3b | 502 | |
kevman | 2:7aab896b1a3b | 503 | int SDBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size) |
kevman | 2:7aab896b1a3b | 504 | { |
kevman | 2:7aab896b1a3b | 505 | if (!is_valid_read(addr, size)) { |
kevman | 2:7aab896b1a3b | 506 | return SD_BLOCK_DEVICE_ERROR_PARAMETER; |
kevman | 2:7aab896b1a3b | 507 | } |
kevman | 2:7aab896b1a3b | 508 | |
kevman | 2:7aab896b1a3b | 509 | lock(); |
kevman | 2:7aab896b1a3b | 510 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 511 | unlock(); |
kevman | 2:7aab896b1a3b | 512 | return SD_BLOCK_DEVICE_ERROR_PARAMETER; |
kevman | 2:7aab896b1a3b | 513 | } |
kevman | 2:7aab896b1a3b | 514 | |
kevman | 2:7aab896b1a3b | 515 | uint8_t *buffer = static_cast<uint8_t *>(b); |
kevman | 2:7aab896b1a3b | 516 | int status = BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 517 | bd_addr_t blockCnt = size / _block_size; |
kevman | 2:7aab896b1a3b | 518 | |
kevman | 2:7aab896b1a3b | 519 | // SDSC Card (CCS=0) uses byte unit address |
kevman | 2:7aab896b1a3b | 520 | // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit) |
kevman | 2:7aab896b1a3b | 521 | if (SDCARD_V2HC == _card_type) { |
kevman | 2:7aab896b1a3b | 522 | addr = addr / _block_size; |
kevman | 2:7aab896b1a3b | 523 | } |
kevman | 2:7aab896b1a3b | 524 | |
kevman | 2:7aab896b1a3b | 525 | // Write command ro receive data |
kevman | 2:7aab896b1a3b | 526 | if (blockCnt > 1) { |
kevman | 2:7aab896b1a3b | 527 | status = _cmd(CMD18_READ_MULTIPLE_BLOCK, addr); |
kevman | 2:7aab896b1a3b | 528 | } else { |
kevman | 2:7aab896b1a3b | 529 | status = _cmd(CMD17_READ_SINGLE_BLOCK, addr); |
kevman | 2:7aab896b1a3b | 530 | } |
kevman | 2:7aab896b1a3b | 531 | if (BD_ERROR_OK != status) { |
kevman | 2:7aab896b1a3b | 532 | unlock(); |
kevman | 2:7aab896b1a3b | 533 | return status; |
kevman | 2:7aab896b1a3b | 534 | } |
kevman | 2:7aab896b1a3b | 535 | |
kevman | 2:7aab896b1a3b | 536 | // receive the data : one block at a time |
kevman | 2:7aab896b1a3b | 537 | while (blockCnt) { |
kevman | 2:7aab896b1a3b | 538 | if (0 != _read(buffer, _block_size)) { |
kevman | 2:7aab896b1a3b | 539 | status = SD_BLOCK_DEVICE_ERROR_NO_RESPONSE; |
kevman | 2:7aab896b1a3b | 540 | break; |
kevman | 2:7aab896b1a3b | 541 | } |
kevman | 2:7aab896b1a3b | 542 | buffer += _block_size; |
kevman | 2:7aab896b1a3b | 543 | --blockCnt; |
kevman | 2:7aab896b1a3b | 544 | } |
kevman | 2:7aab896b1a3b | 545 | _deselect(); |
kevman | 2:7aab896b1a3b | 546 | |
kevman | 2:7aab896b1a3b | 547 | // Send CMD12(0x00000000) to stop the transmission for multi-block transfer |
kevman | 2:7aab896b1a3b | 548 | if (size > _block_size) { |
kevman | 2:7aab896b1a3b | 549 | status = _cmd(CMD12_STOP_TRANSMISSION, 0x0); |
kevman | 2:7aab896b1a3b | 550 | } |
kevman | 2:7aab896b1a3b | 551 | unlock(); |
kevman | 2:7aab896b1a3b | 552 | return status; |
kevman | 2:7aab896b1a3b | 553 | } |
kevman | 2:7aab896b1a3b | 554 | |
kevman | 2:7aab896b1a3b | 555 | bool SDBlockDevice::_is_valid_trim(bd_addr_t addr, bd_size_t size) |
kevman | 2:7aab896b1a3b | 556 | { |
kevman | 2:7aab896b1a3b | 557 | return ( |
kevman | 2:7aab896b1a3b | 558 | addr % _erase_size == 0 && |
kevman | 2:7aab896b1a3b | 559 | size % _erase_size == 0 && |
kevman | 2:7aab896b1a3b | 560 | addr + size <= this->size()); |
kevman | 2:7aab896b1a3b | 561 | } |
kevman | 2:7aab896b1a3b | 562 | |
kevman | 2:7aab896b1a3b | 563 | int SDBlockDevice::trim(bd_addr_t addr, bd_size_t size) |
kevman | 2:7aab896b1a3b | 564 | { |
kevman | 2:7aab896b1a3b | 565 | if (!_is_valid_trim(addr, size)) { |
kevman | 2:7aab896b1a3b | 566 | return SD_BLOCK_DEVICE_ERROR_PARAMETER; |
kevman | 2:7aab896b1a3b | 567 | } |
kevman | 2:7aab896b1a3b | 568 | |
kevman | 2:7aab896b1a3b | 569 | lock(); |
kevman | 2:7aab896b1a3b | 570 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 571 | unlock(); |
kevman | 2:7aab896b1a3b | 572 | return SD_BLOCK_DEVICE_ERROR_NO_INIT; |
kevman | 2:7aab896b1a3b | 573 | } |
kevman | 2:7aab896b1a3b | 574 | int status = BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 575 | |
kevman | 2:7aab896b1a3b | 576 | size -= _block_size; |
kevman | 2:7aab896b1a3b | 577 | // SDSC Card (CCS=0) uses byte unit address |
kevman | 2:7aab896b1a3b | 578 | // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit) |
kevman | 2:7aab896b1a3b | 579 | if (SDCARD_V2HC == _card_type) { |
kevman | 2:7aab896b1a3b | 580 | size = size / _block_size; |
kevman | 2:7aab896b1a3b | 581 | addr = addr / _block_size; |
kevman | 2:7aab896b1a3b | 582 | } |
kevman | 2:7aab896b1a3b | 583 | |
kevman | 2:7aab896b1a3b | 584 | // Start lba sent in start command |
kevman | 2:7aab896b1a3b | 585 | if (BD_ERROR_OK != (status = _cmd(CMD32_ERASE_WR_BLK_START_ADDR, addr))) { |
kevman | 2:7aab896b1a3b | 586 | unlock(); |
kevman | 2:7aab896b1a3b | 587 | return status; |
kevman | 2:7aab896b1a3b | 588 | } |
kevman | 2:7aab896b1a3b | 589 | |
kevman | 2:7aab896b1a3b | 590 | // End lba = addr+size sent in end addr command |
kevman | 2:7aab896b1a3b | 591 | if (BD_ERROR_OK != (status = _cmd(CMD33_ERASE_WR_BLK_END_ADDR, addr + size))) { |
kevman | 2:7aab896b1a3b | 592 | unlock(); |
kevman | 2:7aab896b1a3b | 593 | return status; |
kevman | 2:7aab896b1a3b | 594 | } |
kevman | 2:7aab896b1a3b | 595 | status = _cmd(CMD38_ERASE, 0x0); |
kevman | 2:7aab896b1a3b | 596 | unlock(); |
kevman | 2:7aab896b1a3b | 597 | return status; |
kevman | 2:7aab896b1a3b | 598 | } |
kevman | 2:7aab896b1a3b | 599 | |
kevman | 2:7aab896b1a3b | 600 | bd_size_t SDBlockDevice::get_read_size() const |
kevman | 2:7aab896b1a3b | 601 | { |
kevman | 2:7aab896b1a3b | 602 | return _block_size; |
kevman | 2:7aab896b1a3b | 603 | } |
kevman | 2:7aab896b1a3b | 604 | |
kevman | 2:7aab896b1a3b | 605 | bd_size_t SDBlockDevice::get_program_size() const |
kevman | 2:7aab896b1a3b | 606 | { |
kevman | 2:7aab896b1a3b | 607 | return _block_size; |
kevman | 2:7aab896b1a3b | 608 | } |
kevman | 2:7aab896b1a3b | 609 | |
kevman | 2:7aab896b1a3b | 610 | bd_size_t SDBlockDevice::size() const |
kevman | 2:7aab896b1a3b | 611 | { |
kevman | 2:7aab896b1a3b | 612 | return _block_size * _sectors; |
kevman | 2:7aab896b1a3b | 613 | } |
kevman | 2:7aab896b1a3b | 614 | |
kevman | 2:7aab896b1a3b | 615 | void SDBlockDevice::debug(bool dbg) |
kevman | 2:7aab896b1a3b | 616 | { |
kevman | 2:7aab896b1a3b | 617 | _dbg = dbg; |
kevman | 2:7aab896b1a3b | 618 | } |
kevman | 2:7aab896b1a3b | 619 | |
kevman | 2:7aab896b1a3b | 620 | int SDBlockDevice::frequency(uint64_t freq) |
kevman | 2:7aab896b1a3b | 621 | { |
kevman | 2:7aab896b1a3b | 622 | lock(); |
kevman | 2:7aab896b1a3b | 623 | _transfer_sck = freq; |
kevman | 2:7aab896b1a3b | 624 | int err = _freq(); |
kevman | 2:7aab896b1a3b | 625 | unlock(); |
kevman | 2:7aab896b1a3b | 626 | return err; |
kevman | 2:7aab896b1a3b | 627 | } |
kevman | 2:7aab896b1a3b | 628 | |
kevman | 2:7aab896b1a3b | 629 | // PRIVATE FUNCTIONS |
kevman | 2:7aab896b1a3b | 630 | int SDBlockDevice::_freq(void) |
kevman | 2:7aab896b1a3b | 631 | { |
kevman | 2:7aab896b1a3b | 632 | // Max frequency supported is 25MHZ |
kevman | 2:7aab896b1a3b | 633 | if (_transfer_sck <= 25000000) { |
kevman | 2:7aab896b1a3b | 634 | _spi.frequency(_transfer_sck); |
kevman | 2:7aab896b1a3b | 635 | return 0; |
kevman | 2:7aab896b1a3b | 636 | } else { // TODO: Switch function to be implemented for higher frequency |
kevman | 2:7aab896b1a3b | 637 | _transfer_sck = 25000000; |
kevman | 2:7aab896b1a3b | 638 | _spi.frequency(_transfer_sck); |
kevman | 2:7aab896b1a3b | 639 | return -EINVAL; |
kevman | 2:7aab896b1a3b | 640 | } |
kevman | 2:7aab896b1a3b | 641 | } |
kevman | 2:7aab896b1a3b | 642 | |
kevman | 2:7aab896b1a3b | 643 | uint8_t SDBlockDevice::_cmd_spi(SDBlockDevice::cmdSupported cmd, uint32_t arg) |
kevman | 2:7aab896b1a3b | 644 | { |
kevman | 2:7aab896b1a3b | 645 | uint8_t response; |
kevman | 2:7aab896b1a3b | 646 | char cmdPacket[PACKET_SIZE]; |
kevman | 2:7aab896b1a3b | 647 | uint32_t crc; |
kevman | 2:7aab896b1a3b | 648 | |
kevman | 2:7aab896b1a3b | 649 | // Prepare the command packet |
kevman | 2:7aab896b1a3b | 650 | cmdPacket[0] = SPI_CMD(cmd); |
kevman | 2:7aab896b1a3b | 651 | cmdPacket[1] = (arg >> 24); |
kevman | 2:7aab896b1a3b | 652 | cmdPacket[2] = (arg >> 16); |
kevman | 2:7aab896b1a3b | 653 | cmdPacket[3] = (arg >> 8); |
kevman | 2:7aab896b1a3b | 654 | cmdPacket[4] = (arg >> 0); |
kevman | 2:7aab896b1a3b | 655 | |
kevman | 2:7aab896b1a3b | 656 | if (_crc_on) { |
kevman | 2:7aab896b1a3b | 657 | _crc7.compute((void *)cmdPacket, 5, &crc); |
kevman | 2:7aab896b1a3b | 658 | cmdPacket[5] = (char)(crc | 0x01); |
kevman | 2:7aab896b1a3b | 659 | } else { |
kevman | 2:7aab896b1a3b | 660 | // CMD0 is executed in SD mode, hence should have correct CRC |
kevman | 2:7aab896b1a3b | 661 | // CMD8 CRC verification is always enabled |
kevman | 2:7aab896b1a3b | 662 | switch (cmd) { |
kevman | 2:7aab896b1a3b | 663 | case CMD0_GO_IDLE_STATE: |
kevman | 2:7aab896b1a3b | 664 | cmdPacket[5] = 0x95; |
kevman | 2:7aab896b1a3b | 665 | break; |
kevman | 2:7aab896b1a3b | 666 | case CMD8_SEND_IF_COND: |
kevman | 2:7aab896b1a3b | 667 | cmdPacket[5] = 0x87; |
kevman | 2:7aab896b1a3b | 668 | break; |
kevman | 2:7aab896b1a3b | 669 | default: |
kevman | 2:7aab896b1a3b | 670 | cmdPacket[5] = 0xFF; // Make sure bit 0-End bit is high |
kevman | 2:7aab896b1a3b | 671 | break; |
kevman | 2:7aab896b1a3b | 672 | } |
kevman | 2:7aab896b1a3b | 673 | } |
kevman | 2:7aab896b1a3b | 674 | |
kevman | 2:7aab896b1a3b | 675 | // send a command |
kevman | 2:7aab896b1a3b | 676 | for (int i = 0; i < PACKET_SIZE; i++) { |
kevman | 2:7aab896b1a3b | 677 | _spi.write(cmdPacket[i]); |
kevman | 2:7aab896b1a3b | 678 | } |
kevman | 2:7aab896b1a3b | 679 | |
kevman | 2:7aab896b1a3b | 680 | // The received byte immediataly following CMD12 is a stuff byte, |
kevman | 2:7aab896b1a3b | 681 | // it should be discarded before receive the response of the CMD12. |
kevman | 2:7aab896b1a3b | 682 | if (CMD12_STOP_TRANSMISSION == cmd) { |
kevman | 2:7aab896b1a3b | 683 | _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 684 | } |
kevman | 2:7aab896b1a3b | 685 | |
kevman | 2:7aab896b1a3b | 686 | // Loop for response: Response is sent back within command response time (NCR), 0 to 8 bytes for SDC |
kevman | 2:7aab896b1a3b | 687 | for (int i = 0; i < 0x10; i++) { |
kevman | 2:7aab896b1a3b | 688 | response = _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 689 | // Got the response |
kevman | 2:7aab896b1a3b | 690 | if (!(response & R1_RESPONSE_RECV)) { |
kevman | 2:7aab896b1a3b | 691 | break; |
kevman | 2:7aab896b1a3b | 692 | } |
kevman | 2:7aab896b1a3b | 693 | } |
kevman | 2:7aab896b1a3b | 694 | return response; |
kevman | 2:7aab896b1a3b | 695 | } |
kevman | 2:7aab896b1a3b | 696 | |
kevman | 2:7aab896b1a3b | 697 | int SDBlockDevice::_cmd(SDBlockDevice::cmdSupported cmd, uint32_t arg, bool isAcmd, uint32_t *resp) |
kevman | 2:7aab896b1a3b | 698 | { |
kevman | 2:7aab896b1a3b | 699 | int32_t status = BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 700 | uint32_t response; |
kevman | 2:7aab896b1a3b | 701 | |
kevman | 2:7aab896b1a3b | 702 | // Select card and wait for card to be ready before sending next command |
kevman | 2:7aab896b1a3b | 703 | // Note: next command will fail if card is not ready |
kevman | 2:7aab896b1a3b | 704 | _select(); |
kevman | 2:7aab896b1a3b | 705 | |
kevman | 2:7aab896b1a3b | 706 | // No need to wait for card to be ready when sending the stop command |
kevman | 2:7aab896b1a3b | 707 | if (CMD12_STOP_TRANSMISSION != cmd) { |
kevman | 2:7aab896b1a3b | 708 | if (false == _wait_ready(SD_COMMAND_TIMEOUT)) { |
kevman | 2:7aab896b1a3b | 709 | debug_if(SD_DBG, "Card not ready yet \n"); |
kevman | 2:7aab896b1a3b | 710 | } |
kevman | 2:7aab896b1a3b | 711 | } |
kevman | 2:7aab896b1a3b | 712 | |
kevman | 2:7aab896b1a3b | 713 | // Re-try command |
kevman | 2:7aab896b1a3b | 714 | for (int i = 0; i < 3; i++) { |
kevman | 2:7aab896b1a3b | 715 | // Send CMD55 for APP command first |
kevman | 2:7aab896b1a3b | 716 | if (isAcmd) { |
kevman | 2:7aab896b1a3b | 717 | response = _cmd_spi(CMD55_APP_CMD, 0x0); |
kevman | 2:7aab896b1a3b | 718 | // Wait for card to be ready after CMD55 |
kevman | 2:7aab896b1a3b | 719 | if (false == _wait_ready(SD_COMMAND_TIMEOUT)) { |
kevman | 2:7aab896b1a3b | 720 | debug_if(SD_DBG, "Card not ready yet \n"); |
kevman | 2:7aab896b1a3b | 721 | } |
kevman | 2:7aab896b1a3b | 722 | } |
kevman | 2:7aab896b1a3b | 723 | |
kevman | 2:7aab896b1a3b | 724 | // Send command over SPI interface |
kevman | 2:7aab896b1a3b | 725 | response = _cmd_spi(cmd, arg); |
kevman | 2:7aab896b1a3b | 726 | if (R1_NO_RESPONSE == response) { |
kevman | 2:7aab896b1a3b | 727 | debug_if(SD_DBG, "No response CMD:%d \n", cmd); |
kevman | 2:7aab896b1a3b | 728 | continue; |
kevman | 2:7aab896b1a3b | 729 | } |
kevman | 2:7aab896b1a3b | 730 | break; |
kevman | 2:7aab896b1a3b | 731 | } |
kevman | 2:7aab896b1a3b | 732 | |
kevman | 2:7aab896b1a3b | 733 | // Pass the response to the command call if required |
kevman | 2:7aab896b1a3b | 734 | if (NULL != resp) { |
kevman | 2:7aab896b1a3b | 735 | *resp = response; |
kevman | 2:7aab896b1a3b | 736 | } |
kevman | 2:7aab896b1a3b | 737 | |
kevman | 2:7aab896b1a3b | 738 | // Process the response R1 : Exit on CRC/Illegal command error/No response |
kevman | 2:7aab896b1a3b | 739 | if (R1_NO_RESPONSE == response) { |
kevman | 2:7aab896b1a3b | 740 | _deselect(); |
kevman | 2:7aab896b1a3b | 741 | debug_if(SD_DBG, "No response CMD:%d response: 0x%x\n", cmd, response); |
kevman | 2:7aab896b1a3b | 742 | return SD_BLOCK_DEVICE_ERROR_NO_DEVICE; // No device |
kevman | 2:7aab896b1a3b | 743 | } |
kevman | 2:7aab896b1a3b | 744 | if (response & R1_COM_CRC_ERROR) { |
kevman | 2:7aab896b1a3b | 745 | _deselect(); |
kevman | 2:7aab896b1a3b | 746 | debug_if(SD_DBG, "CRC error CMD:%d response 0x%x \n", cmd, response); |
kevman | 2:7aab896b1a3b | 747 | return SD_BLOCK_DEVICE_ERROR_CRC; // CRC error |
kevman | 2:7aab896b1a3b | 748 | } |
kevman | 2:7aab896b1a3b | 749 | if (response & R1_ILLEGAL_COMMAND) { |
kevman | 2:7aab896b1a3b | 750 | _deselect(); |
kevman | 2:7aab896b1a3b | 751 | debug_if(SD_DBG, "Illegal command CMD:%d response 0x%x\n", cmd, response); |
kevman | 2:7aab896b1a3b | 752 | if (CMD8_SEND_IF_COND == cmd) { // Illegal command is for Ver1 or not SD Card |
kevman | 2:7aab896b1a3b | 753 | _card_type = CARD_UNKNOWN; |
kevman | 2:7aab896b1a3b | 754 | } |
kevman | 2:7aab896b1a3b | 755 | return SD_BLOCK_DEVICE_ERROR_UNSUPPORTED; // Command not supported |
kevman | 2:7aab896b1a3b | 756 | } |
kevman | 2:7aab896b1a3b | 757 | |
kevman | 2:7aab896b1a3b | 758 | debug_if(_dbg, "CMD:%d \t arg:0x%x \t Response:0x%x \n", cmd, arg, response); |
kevman | 2:7aab896b1a3b | 759 | // Set status for other errors |
kevman | 2:7aab896b1a3b | 760 | if ((response & R1_ERASE_RESET) || (response & R1_ERASE_SEQUENCE_ERROR)) { |
kevman | 2:7aab896b1a3b | 761 | status = SD_BLOCK_DEVICE_ERROR_ERASE; // Erase error |
kevman | 2:7aab896b1a3b | 762 | } else if ((response & R1_ADDRESS_ERROR) || (response & R1_PARAMETER_ERROR)) { |
kevman | 2:7aab896b1a3b | 763 | // Misaligned address / invalid address block length |
kevman | 2:7aab896b1a3b | 764 | status = SD_BLOCK_DEVICE_ERROR_PARAMETER; |
kevman | 2:7aab896b1a3b | 765 | } |
kevman | 2:7aab896b1a3b | 766 | |
kevman | 2:7aab896b1a3b | 767 | // Get rest of the response part for other commands |
kevman | 2:7aab896b1a3b | 768 | switch (cmd) { |
kevman | 2:7aab896b1a3b | 769 | case CMD8_SEND_IF_COND: // Response R7 |
kevman | 2:7aab896b1a3b | 770 | debug_if(_dbg, "V2-Version Card\n"); |
kevman | 2:7aab896b1a3b | 771 | _card_type = SDCARD_V2; |
kevman | 2:7aab896b1a3b | 772 | // Note: No break here, need to read rest of the response |
kevman | 2:7aab896b1a3b | 773 | case CMD58_READ_OCR: // Response R3 |
kevman | 2:7aab896b1a3b | 774 | response = (_spi.write(SPI_FILL_CHAR) << 24); |
kevman | 2:7aab896b1a3b | 775 | response |= (_spi.write(SPI_FILL_CHAR) << 16); |
kevman | 2:7aab896b1a3b | 776 | response |= (_spi.write(SPI_FILL_CHAR) << 8); |
kevman | 2:7aab896b1a3b | 777 | response |= _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 778 | debug_if(_dbg, "R3/R7: 0x%x \n", response); |
kevman | 2:7aab896b1a3b | 779 | break; |
kevman | 2:7aab896b1a3b | 780 | |
kevman | 2:7aab896b1a3b | 781 | case CMD12_STOP_TRANSMISSION: // Response R1b |
kevman | 2:7aab896b1a3b | 782 | case CMD38_ERASE: |
kevman | 2:7aab896b1a3b | 783 | _wait_ready(SD_COMMAND_TIMEOUT); |
kevman | 2:7aab896b1a3b | 784 | break; |
kevman | 2:7aab896b1a3b | 785 | |
kevman | 2:7aab896b1a3b | 786 | case ACMD13_SD_STATUS: // Response R2 |
kevman | 2:7aab896b1a3b | 787 | response = _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 788 | debug_if(_dbg, "R2: 0x%x \n", response); |
kevman | 2:7aab896b1a3b | 789 | break; |
kevman | 2:7aab896b1a3b | 790 | |
kevman | 2:7aab896b1a3b | 791 | default: // Response R1 |
kevman | 2:7aab896b1a3b | 792 | break; |
kevman | 2:7aab896b1a3b | 793 | } |
kevman | 2:7aab896b1a3b | 794 | |
kevman | 2:7aab896b1a3b | 795 | // Pass the updated response to the command |
kevman | 2:7aab896b1a3b | 796 | if (NULL != resp) { |
kevman | 2:7aab896b1a3b | 797 | *resp = response; |
kevman | 2:7aab896b1a3b | 798 | } |
kevman | 2:7aab896b1a3b | 799 | |
kevman | 2:7aab896b1a3b | 800 | // Do not deselect card if read is in progress. |
kevman | 2:7aab896b1a3b | 801 | if (((CMD9_SEND_CSD == cmd) || (ACMD22_SEND_NUM_WR_BLOCKS == cmd) || |
kevman | 2:7aab896b1a3b | 802 | (CMD24_WRITE_BLOCK == cmd) || (CMD25_WRITE_MULTIPLE_BLOCK == cmd) || |
kevman | 2:7aab896b1a3b | 803 | (CMD17_READ_SINGLE_BLOCK == cmd) || (CMD18_READ_MULTIPLE_BLOCK == cmd)) |
kevman | 2:7aab896b1a3b | 804 | && (BD_ERROR_OK == status)) { |
kevman | 2:7aab896b1a3b | 805 | return BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 806 | } |
kevman | 2:7aab896b1a3b | 807 | // Deselect card |
kevman | 2:7aab896b1a3b | 808 | _deselect(); |
kevman | 2:7aab896b1a3b | 809 | return status; |
kevman | 2:7aab896b1a3b | 810 | } |
kevman | 2:7aab896b1a3b | 811 | |
kevman | 2:7aab896b1a3b | 812 | int SDBlockDevice::_cmd8() |
kevman | 2:7aab896b1a3b | 813 | { |
kevman | 2:7aab896b1a3b | 814 | uint32_t arg = (CMD8_PATTERN << 0); // [7:0]check pattern |
kevman | 2:7aab896b1a3b | 815 | uint32_t response = 0; |
kevman | 2:7aab896b1a3b | 816 | int32_t status = BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 817 | |
kevman | 2:7aab896b1a3b | 818 | arg |= (0x1 << 8); // 2.7-3.6V // [11:8]supply voltage(VHS) |
kevman | 2:7aab896b1a3b | 819 | |
kevman | 2:7aab896b1a3b | 820 | status = _cmd(CMD8_SEND_IF_COND, arg, 0x0, &response); |
kevman | 2:7aab896b1a3b | 821 | // Verify voltage and pattern for V2 version of card |
kevman | 2:7aab896b1a3b | 822 | if ((BD_ERROR_OK == status) && (SDCARD_V2 == _card_type)) { |
kevman | 2:7aab896b1a3b | 823 | // If check pattern is not matched, CMD8 communication is not valid |
kevman | 2:7aab896b1a3b | 824 | if ((response & 0xFFF) != arg) { |
kevman | 2:7aab896b1a3b | 825 | debug_if(SD_DBG, "CMD8 Pattern mismatch 0x%x : 0x%x\n", arg, response); |
kevman | 2:7aab896b1a3b | 826 | _card_type = CARD_UNKNOWN; |
kevman | 2:7aab896b1a3b | 827 | status = SD_BLOCK_DEVICE_ERROR_UNUSABLE; |
kevman | 2:7aab896b1a3b | 828 | } |
kevman | 2:7aab896b1a3b | 829 | } |
kevman | 2:7aab896b1a3b | 830 | return status; |
kevman | 2:7aab896b1a3b | 831 | } |
kevman | 2:7aab896b1a3b | 832 | |
kevman | 2:7aab896b1a3b | 833 | uint32_t SDBlockDevice::_go_idle_state() |
kevman | 2:7aab896b1a3b | 834 | { |
kevman | 2:7aab896b1a3b | 835 | uint32_t response; |
kevman | 2:7aab896b1a3b | 836 | |
kevman | 2:7aab896b1a3b | 837 | /* Reseting the MCU SPI master may not reset the on-board SDCard, in which |
kevman | 2:7aab896b1a3b | 838 | * case when MCU power-on occurs the SDCard will resume operations as |
kevman | 2:7aab896b1a3b | 839 | * though there was no reset. In this scenario the first CMD0 will |
kevman | 2:7aab896b1a3b | 840 | * not be interpreted as a command and get lost. For some cards retrying |
kevman | 2:7aab896b1a3b | 841 | * the command overcomes this situation. */ |
kevman | 2:7aab896b1a3b | 842 | for (int i = 0; i < SD_CMD0_GO_IDLE_STATE_RETRIES; i++) { |
kevman | 2:7aab896b1a3b | 843 | _cmd(CMD0_GO_IDLE_STATE, 0x0, 0x0, &response); |
kevman | 2:7aab896b1a3b | 844 | if (R1_IDLE_STATE == response) { |
kevman | 2:7aab896b1a3b | 845 | break; |
kevman | 2:7aab896b1a3b | 846 | } |
kevman | 2:7aab896b1a3b | 847 | wait_ms(1); |
kevman | 2:7aab896b1a3b | 848 | } |
kevman | 2:7aab896b1a3b | 849 | return response; |
kevman | 2:7aab896b1a3b | 850 | } |
kevman | 2:7aab896b1a3b | 851 | |
kevman | 2:7aab896b1a3b | 852 | int SDBlockDevice::_read_bytes(uint8_t *buffer, uint32_t length) |
kevman | 2:7aab896b1a3b | 853 | { |
kevman | 2:7aab896b1a3b | 854 | uint16_t crc; |
kevman | 2:7aab896b1a3b | 855 | |
kevman | 2:7aab896b1a3b | 856 | // read until start byte (0xFE) |
kevman | 2:7aab896b1a3b | 857 | if (false == _wait_token(SPI_START_BLOCK)) { |
kevman | 2:7aab896b1a3b | 858 | debug_if(SD_DBG, "Read timeout\n"); |
kevman | 2:7aab896b1a3b | 859 | _deselect(); |
kevman | 2:7aab896b1a3b | 860 | return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE; |
kevman | 2:7aab896b1a3b | 861 | } |
kevman | 2:7aab896b1a3b | 862 | |
kevman | 2:7aab896b1a3b | 863 | // read data |
kevman | 2:7aab896b1a3b | 864 | for (uint32_t i = 0; i < length; i++) { |
kevman | 2:7aab896b1a3b | 865 | buffer[i] = _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 866 | } |
kevman | 2:7aab896b1a3b | 867 | |
kevman | 2:7aab896b1a3b | 868 | // Read the CRC16 checksum for the data block |
kevman | 2:7aab896b1a3b | 869 | crc = (_spi.write(SPI_FILL_CHAR) << 8); |
kevman | 2:7aab896b1a3b | 870 | crc |= _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 871 | |
kevman | 2:7aab896b1a3b | 872 | if (_crc_on) { |
kevman | 2:7aab896b1a3b | 873 | uint32_t crc_result; |
kevman | 2:7aab896b1a3b | 874 | // Compute and verify checksum |
kevman | 2:7aab896b1a3b | 875 | _crc16.compute((void *)buffer, length, &crc_result); |
kevman | 2:7aab896b1a3b | 876 | if ((uint16_t)crc_result != crc) { |
kevman | 2:7aab896b1a3b | 877 | debug_if(SD_DBG, "_read_bytes: Invalid CRC received 0x%x result of computation 0x%x\n", |
kevman | 2:7aab896b1a3b | 878 | crc, crc_result); |
kevman | 2:7aab896b1a3b | 879 | _deselect(); |
kevman | 2:7aab896b1a3b | 880 | return SD_BLOCK_DEVICE_ERROR_CRC; |
kevman | 2:7aab896b1a3b | 881 | } |
kevman | 2:7aab896b1a3b | 882 | } |
kevman | 2:7aab896b1a3b | 883 | |
kevman | 2:7aab896b1a3b | 884 | _deselect(); |
kevman | 2:7aab896b1a3b | 885 | return 0; |
kevman | 2:7aab896b1a3b | 886 | } |
kevman | 2:7aab896b1a3b | 887 | |
kevman | 2:7aab896b1a3b | 888 | int SDBlockDevice::_read(uint8_t *buffer, uint32_t length) |
kevman | 2:7aab896b1a3b | 889 | { |
kevman | 2:7aab896b1a3b | 890 | uint16_t crc; |
kevman | 2:7aab896b1a3b | 891 | |
kevman | 2:7aab896b1a3b | 892 | // read until start byte (0xFE) |
kevman | 2:7aab896b1a3b | 893 | if (false == _wait_token(SPI_START_BLOCK)) { |
kevman | 2:7aab896b1a3b | 894 | debug_if(SD_DBG, "Read timeout\n"); |
kevman | 2:7aab896b1a3b | 895 | return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE; |
kevman | 2:7aab896b1a3b | 896 | } |
kevman | 2:7aab896b1a3b | 897 | |
kevman | 2:7aab896b1a3b | 898 | // read data |
kevman | 2:7aab896b1a3b | 899 | _spi.write(NULL, 0, (char *)buffer, length); |
kevman | 2:7aab896b1a3b | 900 | |
kevman | 2:7aab896b1a3b | 901 | // Read the CRC16 checksum for the data block |
kevman | 2:7aab896b1a3b | 902 | crc = (_spi.write(SPI_FILL_CHAR) << 8); |
kevman | 2:7aab896b1a3b | 903 | crc |= _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 904 | |
kevman | 2:7aab896b1a3b | 905 | if (_crc_on) { |
kevman | 2:7aab896b1a3b | 906 | uint32_t crc_result; |
kevman | 2:7aab896b1a3b | 907 | // Compute and verify checksum |
kevman | 2:7aab896b1a3b | 908 | _crc16.compute((void *)buffer, length, &crc_result); |
kevman | 2:7aab896b1a3b | 909 | if ((uint16_t)crc_result != crc) { |
kevman | 2:7aab896b1a3b | 910 | debug_if(SD_DBG, "_read_bytes: Invalid CRC received 0x%x result of computation 0x%x\n", |
kevman | 2:7aab896b1a3b | 911 | crc, crc_result); |
kevman | 2:7aab896b1a3b | 912 | return SD_BLOCK_DEVICE_ERROR_CRC; |
kevman | 2:7aab896b1a3b | 913 | } |
kevman | 2:7aab896b1a3b | 914 | } |
kevman | 2:7aab896b1a3b | 915 | |
kevman | 2:7aab896b1a3b | 916 | return 0; |
kevman | 2:7aab896b1a3b | 917 | } |
kevman | 2:7aab896b1a3b | 918 | |
kevman | 2:7aab896b1a3b | 919 | uint8_t SDBlockDevice::_write(const uint8_t *buffer, uint8_t token, uint32_t length) |
kevman | 2:7aab896b1a3b | 920 | { |
kevman | 2:7aab896b1a3b | 921 | |
kevman | 2:7aab896b1a3b | 922 | uint32_t crc = (~0); |
kevman | 2:7aab896b1a3b | 923 | uint8_t response = 0xFF; |
kevman | 2:7aab896b1a3b | 924 | |
kevman | 2:7aab896b1a3b | 925 | // indicate start of block |
kevman | 2:7aab896b1a3b | 926 | _spi.write(token); |
kevman | 2:7aab896b1a3b | 927 | |
kevman | 2:7aab896b1a3b | 928 | // write the data |
kevman | 2:7aab896b1a3b | 929 | _spi.write((char *)buffer, length, NULL, 0); |
kevman | 2:7aab896b1a3b | 930 | |
kevman | 2:7aab896b1a3b | 931 | if (_crc_on) { |
kevman | 2:7aab896b1a3b | 932 | // Compute CRC |
kevman | 2:7aab896b1a3b | 933 | _crc16.compute((void *)buffer, length, &crc); |
kevman | 2:7aab896b1a3b | 934 | } |
kevman | 2:7aab896b1a3b | 935 | |
kevman | 2:7aab896b1a3b | 936 | // write the checksum CRC16 |
kevman | 2:7aab896b1a3b | 937 | _spi.write(crc >> 8); |
kevman | 2:7aab896b1a3b | 938 | _spi.write(crc); |
kevman | 2:7aab896b1a3b | 939 | |
kevman | 2:7aab896b1a3b | 940 | |
kevman | 2:7aab896b1a3b | 941 | // check the response token |
kevman | 2:7aab896b1a3b | 942 | response = _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 943 | |
kevman | 2:7aab896b1a3b | 944 | // Wait for last block to be written |
kevman | 2:7aab896b1a3b | 945 | if (false == _wait_ready(SD_COMMAND_TIMEOUT)) { |
kevman | 2:7aab896b1a3b | 946 | debug_if(SD_DBG, "Card not ready yet \n"); |
kevman | 2:7aab896b1a3b | 947 | } |
kevman | 2:7aab896b1a3b | 948 | |
kevman | 2:7aab896b1a3b | 949 | return (response & SPI_DATA_RESPONSE_MASK); |
kevman | 2:7aab896b1a3b | 950 | } |
kevman | 2:7aab896b1a3b | 951 | |
kevman | 2:7aab896b1a3b | 952 | static uint32_t ext_bits(unsigned char *data, int msb, int lsb) |
kevman | 2:7aab896b1a3b | 953 | { |
kevman | 2:7aab896b1a3b | 954 | uint32_t bits = 0; |
kevman | 2:7aab896b1a3b | 955 | uint32_t size = 1 + msb - lsb; |
kevman | 2:7aab896b1a3b | 956 | for (uint32_t i = 0; i < size; i++) { |
kevman | 2:7aab896b1a3b | 957 | uint32_t position = lsb + i; |
kevman | 2:7aab896b1a3b | 958 | uint32_t byte = 15 - (position >> 3); |
kevman | 2:7aab896b1a3b | 959 | uint32_t bit = position & 0x7; |
kevman | 2:7aab896b1a3b | 960 | uint32_t value = (data[byte] >> bit) & 1; |
kevman | 2:7aab896b1a3b | 961 | bits |= value << i; |
kevman | 2:7aab896b1a3b | 962 | } |
kevman | 2:7aab896b1a3b | 963 | return bits; |
kevman | 2:7aab896b1a3b | 964 | } |
kevman | 2:7aab896b1a3b | 965 | |
kevman | 2:7aab896b1a3b | 966 | bd_size_t SDBlockDevice::_sd_sectors() |
kevman | 2:7aab896b1a3b | 967 | { |
kevman | 2:7aab896b1a3b | 968 | uint32_t c_size, c_size_mult, read_bl_len; |
kevman | 2:7aab896b1a3b | 969 | uint32_t block_len, mult, blocknr; |
kevman | 2:7aab896b1a3b | 970 | uint32_t hc_c_size; |
kevman | 2:7aab896b1a3b | 971 | bd_size_t blocks = 0, capacity = 0; |
kevman | 2:7aab896b1a3b | 972 | |
kevman | 2:7aab896b1a3b | 973 | // CMD9, Response R2 (R1 byte + 16-byte block read) |
kevman | 2:7aab896b1a3b | 974 | if (_cmd(CMD9_SEND_CSD, 0x0) != 0x0) { |
kevman | 2:7aab896b1a3b | 975 | debug_if(SD_DBG, "Didn't get a response from the disk\n"); |
kevman | 2:7aab896b1a3b | 976 | return 0; |
kevman | 2:7aab896b1a3b | 977 | } |
kevman | 2:7aab896b1a3b | 978 | uint8_t csd[16]; |
kevman | 2:7aab896b1a3b | 979 | if (_read_bytes(csd, 16) != 0) { |
kevman | 2:7aab896b1a3b | 980 | debug_if(SD_DBG, "Couldn't read csd response from disk\n"); |
kevman | 2:7aab896b1a3b | 981 | return 0; |
kevman | 2:7aab896b1a3b | 982 | } |
kevman | 2:7aab896b1a3b | 983 | |
kevman | 2:7aab896b1a3b | 984 | // csd_structure : csd[127:126] |
kevman | 2:7aab896b1a3b | 985 | int csd_structure = ext_bits(csd, 127, 126); |
kevman | 2:7aab896b1a3b | 986 | switch (csd_structure) { |
kevman | 2:7aab896b1a3b | 987 | case 0: |
kevman | 2:7aab896b1a3b | 988 | c_size = ext_bits(csd, 73, 62); // c_size : csd[73:62] |
kevman | 2:7aab896b1a3b | 989 | c_size_mult = ext_bits(csd, 49, 47); // c_size_mult : csd[49:47] |
kevman | 2:7aab896b1a3b | 990 | read_bl_len = ext_bits(csd, 83, 80); // read_bl_len : csd[83:80] - the *maximum* read block length |
kevman | 2:7aab896b1a3b | 991 | block_len = 1 << read_bl_len; // BLOCK_LEN = 2^READ_BL_LEN |
kevman | 2:7aab896b1a3b | 992 | mult = 1 << (c_size_mult + 2); // MULT = 2^C_SIZE_MULT+2 (C_SIZE_MULT < 8) |
kevman | 2:7aab896b1a3b | 993 | blocknr = (c_size + 1) * mult; // BLOCKNR = (C_SIZE+1) * MULT |
kevman | 2:7aab896b1a3b | 994 | capacity = blocknr * block_len; // memory capacity = BLOCKNR * BLOCK_LEN |
kevman | 2:7aab896b1a3b | 995 | blocks = capacity / _block_size; |
kevman | 2:7aab896b1a3b | 996 | debug_if(SD_DBG, "Standard Capacity: c_size: %d \n", c_size); |
kevman | 2:7aab896b1a3b | 997 | debug_if(SD_DBG, "Sectors: 0x%x : %llu\n", blocks, blocks); |
kevman | 2:7aab896b1a3b | 998 | debug_if(SD_DBG, "Capacity: 0x%x : %llu MB\n", capacity, (capacity / (1024U * 1024U))); |
kevman | 2:7aab896b1a3b | 999 | |
kevman | 2:7aab896b1a3b | 1000 | // ERASE_BLK_EN = 1: Erase in multiple of 512 bytes supported |
kevman | 2:7aab896b1a3b | 1001 | if (ext_bits(csd, 46, 46)) { |
kevman | 2:7aab896b1a3b | 1002 | _erase_size = BLOCK_SIZE_HC; |
kevman | 2:7aab896b1a3b | 1003 | } else { |
kevman | 2:7aab896b1a3b | 1004 | // ERASE_BLK_EN = 1: Erase in multiple of SECTOR_SIZE supported |
kevman | 2:7aab896b1a3b | 1005 | _erase_size = BLOCK_SIZE_HC * (ext_bits(csd, 45, 39) + 1); |
kevman | 2:7aab896b1a3b | 1006 | } |
kevman | 2:7aab896b1a3b | 1007 | break; |
kevman | 2:7aab896b1a3b | 1008 | |
kevman | 2:7aab896b1a3b | 1009 | case 1: |
kevman | 2:7aab896b1a3b | 1010 | hc_c_size = ext_bits(csd, 69, 48); // device size : C_SIZE : [69:48] |
kevman | 2:7aab896b1a3b | 1011 | blocks = (hc_c_size + 1) << 10; // block count = C_SIZE+1) * 1K byte (512B is block size) |
kevman | 2:7aab896b1a3b | 1012 | debug_if(SD_DBG, "SDHC/SDXC Card: hc_c_size: %d \n", hc_c_size); |
kevman | 2:7aab896b1a3b | 1013 | debug_if(SD_DBG, "Sectors: 0x%x : %llu\n", blocks, blocks); |
kevman | 2:7aab896b1a3b | 1014 | debug_if(SD_DBG, "Capacity: %llu MB\n", (blocks / (2048U))); |
kevman | 2:7aab896b1a3b | 1015 | // ERASE_BLK_EN is fixed to 1, which means host can erase one or multiple of 512 bytes. |
kevman | 2:7aab896b1a3b | 1016 | _erase_size = BLOCK_SIZE_HC; |
kevman | 2:7aab896b1a3b | 1017 | break; |
kevman | 2:7aab896b1a3b | 1018 | |
kevman | 2:7aab896b1a3b | 1019 | default: |
kevman | 2:7aab896b1a3b | 1020 | debug_if(SD_DBG, "CSD struct unsupported\r\n"); |
kevman | 2:7aab896b1a3b | 1021 | return 0; |
kevman | 2:7aab896b1a3b | 1022 | }; |
kevman | 2:7aab896b1a3b | 1023 | return blocks; |
kevman | 2:7aab896b1a3b | 1024 | } |
kevman | 2:7aab896b1a3b | 1025 | |
kevman | 2:7aab896b1a3b | 1026 | // SPI function to wait till chip is ready and sends start token |
kevman | 2:7aab896b1a3b | 1027 | bool SDBlockDevice::_wait_token(uint8_t token) |
kevman | 2:7aab896b1a3b | 1028 | { |
kevman | 2:7aab896b1a3b | 1029 | _spi_timer.reset(); |
kevman | 2:7aab896b1a3b | 1030 | _spi_timer.start(); |
kevman | 2:7aab896b1a3b | 1031 | |
kevman | 2:7aab896b1a3b | 1032 | do { |
kevman | 2:7aab896b1a3b | 1033 | if (token == _spi.write(SPI_FILL_CHAR)) { |
kevman | 2:7aab896b1a3b | 1034 | _spi_timer.stop(); |
kevman | 2:7aab896b1a3b | 1035 | return true; |
kevman | 2:7aab896b1a3b | 1036 | } |
kevman | 2:7aab896b1a3b | 1037 | } while (_spi_timer.read_ms() < 300); // Wait for 300 msec for start token |
kevman | 2:7aab896b1a3b | 1038 | _spi_timer.stop(); |
kevman | 2:7aab896b1a3b | 1039 | debug_if(SD_DBG, "_wait_token: timeout\n"); |
kevman | 2:7aab896b1a3b | 1040 | return false; |
kevman | 2:7aab896b1a3b | 1041 | } |
kevman | 2:7aab896b1a3b | 1042 | |
kevman | 2:7aab896b1a3b | 1043 | // SPI function to wait till chip is ready |
kevman | 2:7aab896b1a3b | 1044 | // The host controller should wait for end of the process until DO goes high (a 0xFF is received). |
kevman | 2:7aab896b1a3b | 1045 | bool SDBlockDevice::_wait_ready(uint16_t ms) |
kevman | 2:7aab896b1a3b | 1046 | { |
kevman | 2:7aab896b1a3b | 1047 | uint8_t response; |
kevman | 2:7aab896b1a3b | 1048 | _spi_timer.reset(); |
kevman | 2:7aab896b1a3b | 1049 | _spi_timer.start(); |
kevman | 2:7aab896b1a3b | 1050 | do { |
kevman | 2:7aab896b1a3b | 1051 | response = _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 1052 | if (response == 0xFF) { |
kevman | 2:7aab896b1a3b | 1053 | _spi_timer.stop(); |
kevman | 2:7aab896b1a3b | 1054 | return true; |
kevman | 2:7aab896b1a3b | 1055 | } |
kevman | 2:7aab896b1a3b | 1056 | } while (_spi_timer.read_ms() < ms); |
kevman | 2:7aab896b1a3b | 1057 | _spi_timer.stop(); |
kevman | 2:7aab896b1a3b | 1058 | return false; |
kevman | 2:7aab896b1a3b | 1059 | } |
kevman | 2:7aab896b1a3b | 1060 | |
kevman | 2:7aab896b1a3b | 1061 | // SPI function to wait for count |
kevman | 2:7aab896b1a3b | 1062 | void SDBlockDevice::_spi_wait(uint8_t count) |
kevman | 2:7aab896b1a3b | 1063 | { |
kevman | 2:7aab896b1a3b | 1064 | for (uint8_t i = 0; i < count; ++i) { |
kevman | 2:7aab896b1a3b | 1065 | _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 1066 | } |
kevman | 2:7aab896b1a3b | 1067 | } |
kevman | 2:7aab896b1a3b | 1068 | |
kevman | 2:7aab896b1a3b | 1069 | void SDBlockDevice::_spi_init() |
kevman | 2:7aab896b1a3b | 1070 | { |
kevman | 2:7aab896b1a3b | 1071 | _spi.lock(); |
kevman | 2:7aab896b1a3b | 1072 | // Set to SCK for initialization, and clock card with cs = 1 |
kevman | 2:7aab896b1a3b | 1073 | _spi.frequency(_init_sck); |
kevman | 2:7aab896b1a3b | 1074 | _spi.format(8, 0); |
kevman | 2:7aab896b1a3b | 1075 | _spi.set_default_write_value(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 1076 | // Initial 74 cycles required for few cards, before selecting SPI mode |
kevman | 2:7aab896b1a3b | 1077 | _cs = 1; |
kevman | 2:7aab896b1a3b | 1078 | _spi_wait(10); |
kevman | 2:7aab896b1a3b | 1079 | _spi.unlock(); |
kevman | 2:7aab896b1a3b | 1080 | } |
kevman | 2:7aab896b1a3b | 1081 | |
kevman | 2:7aab896b1a3b | 1082 | void SDBlockDevice::_select() |
kevman | 2:7aab896b1a3b | 1083 | { |
kevman | 2:7aab896b1a3b | 1084 | _spi.lock(); |
kevman | 2:7aab896b1a3b | 1085 | _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 1086 | _cs = 0; |
kevman | 2:7aab896b1a3b | 1087 | } |
kevman | 2:7aab896b1a3b | 1088 | |
kevman | 2:7aab896b1a3b | 1089 | void SDBlockDevice::_deselect() |
kevman | 2:7aab896b1a3b | 1090 | { |
kevman | 2:7aab896b1a3b | 1091 | _cs = 1; |
kevman | 2:7aab896b1a3b | 1092 | _spi.write(SPI_FILL_CHAR); |
kevman | 2:7aab896b1a3b | 1093 | _spi.unlock(); |
kevman | 2:7aab896b1a3b | 1094 | } |
kevman | 2:7aab896b1a3b | 1095 | |
kevman | 2:7aab896b1a3b | 1096 | #endif /* DEVICE_SPI */ |