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