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