Luka Danilovic
/
ELEC351_10497267_SUBMISSION
ELEC351 SUBMISSION - Same as on the DLE
sd-driver/SDBlockDevice.h@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 | #ifndef MBED_SD_BLOCK_DEVICE_H |
Luka_Danilovic | 0:c66224a27cf8 | 18 | #define MBED_SD_BLOCK_DEVICE_H |
Luka_Danilovic | 0:c66224a27cf8 | 19 | |
Luka_Danilovic | 0:c66224a27cf8 | 20 | /* If the target has no SPI support then SDCard is not supported */ |
Luka_Danilovic | 0:c66224a27cf8 | 21 | #ifdef DEVICE_SPI |
Luka_Danilovic | 0:c66224a27cf8 | 22 | |
Luka_Danilovic | 0:c66224a27cf8 | 23 | #include "BlockDevice.h" |
Luka_Danilovic | 0:c66224a27cf8 | 24 | #include "mbed.h" |
Luka_Danilovic | 0:c66224a27cf8 | 25 | #include "platform/PlatformMutex.h" |
Luka_Danilovic | 0:c66224a27cf8 | 26 | |
Luka_Danilovic | 0:c66224a27cf8 | 27 | /** Access an SD Card using SPI |
Luka_Danilovic | 0:c66224a27cf8 | 28 | * |
Luka_Danilovic | 0:c66224a27cf8 | 29 | * @code |
Luka_Danilovic | 0:c66224a27cf8 | 30 | * #include "mbed.h" |
Luka_Danilovic | 0:c66224a27cf8 | 31 | * #include "SDBlockDevice.h" |
Luka_Danilovic | 0:c66224a27cf8 | 32 | * |
Luka_Danilovic | 0:c66224a27cf8 | 33 | * SDBlockDevice sd(p5, p6, p7, p12); // mosi, miso, sclk, cs |
Luka_Danilovic | 0:c66224a27cf8 | 34 | * uint8_t block[512] = "Hello World!\n"; |
Luka_Danilovic | 0:c66224a27cf8 | 35 | * |
Luka_Danilovic | 0:c66224a27cf8 | 36 | * int main() { |
Luka_Danilovic | 0:c66224a27cf8 | 37 | * sd.init(); |
Luka_Danilovic | 0:c66224a27cf8 | 38 | * sd.write(block, 0, 512); |
Luka_Danilovic | 0:c66224a27cf8 | 39 | * sd.read(block, 0, 512); |
Luka_Danilovic | 0:c66224a27cf8 | 40 | * printf("%s", block); |
Luka_Danilovic | 0:c66224a27cf8 | 41 | * sd.deinit(); |
Luka_Danilovic | 0:c66224a27cf8 | 42 | * } |
Luka_Danilovic | 0:c66224a27cf8 | 43 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 44 | class SDBlockDevice : public BlockDevice { |
Luka_Danilovic | 0:c66224a27cf8 | 45 | public: |
Luka_Danilovic | 0:c66224a27cf8 | 46 | /** Lifetime of an SD card |
Luka_Danilovic | 0:c66224a27cf8 | 47 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 48 | SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, uint64_t hz=1000000); |
Luka_Danilovic | 0:c66224a27cf8 | 49 | virtual ~SDBlockDevice(); |
Luka_Danilovic | 0:c66224a27cf8 | 50 | |
Luka_Danilovic | 0:c66224a27cf8 | 51 | /** Initialize a block device |
Luka_Danilovic | 0:c66224a27cf8 | 52 | * |
Luka_Danilovic | 0:c66224a27cf8 | 53 | * @return 0 on success or a negative error code on failure |
Luka_Danilovic | 0:c66224a27cf8 | 54 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 55 | virtual int init(); |
Luka_Danilovic | 0:c66224a27cf8 | 56 | |
Luka_Danilovic | 0:c66224a27cf8 | 57 | /** Deinitialize a block device |
Luka_Danilovic | 0:c66224a27cf8 | 58 | * |
Luka_Danilovic | 0:c66224a27cf8 | 59 | * @return 0 on success or a negative error code on failure |
Luka_Danilovic | 0:c66224a27cf8 | 60 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 61 | virtual int deinit(); |
Luka_Danilovic | 0:c66224a27cf8 | 62 | |
Luka_Danilovic | 0:c66224a27cf8 | 63 | /** Read blocks from a block device |
Luka_Danilovic | 0:c66224a27cf8 | 64 | * |
Luka_Danilovic | 0:c66224a27cf8 | 65 | * @param buffer Buffer to write blocks to |
Luka_Danilovic | 0:c66224a27cf8 | 66 | * @param addr Address of block to begin reading from |
Luka_Danilovic | 0:c66224a27cf8 | 67 | * @param size Size to read in bytes, must be a multiple of read block size |
Luka_Danilovic | 0:c66224a27cf8 | 68 | * @return 0 on success, negative error code on failure |
Luka_Danilovic | 0:c66224a27cf8 | 69 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 70 | virtual int read(void *buffer, bd_addr_t addr, bd_size_t size); |
Luka_Danilovic | 0:c66224a27cf8 | 71 | |
Luka_Danilovic | 0:c66224a27cf8 | 72 | /** Program blocks to a block device |
Luka_Danilovic | 0:c66224a27cf8 | 73 | * |
Luka_Danilovic | 0:c66224a27cf8 | 74 | * The blocks must have been erased prior to being programmed |
Luka_Danilovic | 0:c66224a27cf8 | 75 | * |
Luka_Danilovic | 0:c66224a27cf8 | 76 | * @param buffer Buffer of data to write to blocks |
Luka_Danilovic | 0:c66224a27cf8 | 77 | * @param addr Address of block to begin writing to |
Luka_Danilovic | 0:c66224a27cf8 | 78 | * @param size Size to write in bytes, must be a multiple of program block size |
Luka_Danilovic | 0:c66224a27cf8 | 79 | * @return 0 on success, negative error code on failure |
Luka_Danilovic | 0:c66224a27cf8 | 80 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 81 | virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size); |
Luka_Danilovic | 0:c66224a27cf8 | 82 | |
Luka_Danilovic | 0:c66224a27cf8 | 83 | /** Mark blocks as no longer in use |
Luka_Danilovic | 0:c66224a27cf8 | 84 | * |
Luka_Danilovic | 0:c66224a27cf8 | 85 | * This function provides a hint to the underlying block device that a region of blocks |
Luka_Danilovic | 0:c66224a27cf8 | 86 | * is no longer in use and may be erased without side effects. Erase must still be called |
Luka_Danilovic | 0:c66224a27cf8 | 87 | * before programming, but trimming allows flash-translation-layers to schedule erases when |
Luka_Danilovic | 0:c66224a27cf8 | 88 | * the device is not busy. |
Luka_Danilovic | 0:c66224a27cf8 | 89 | * |
Luka_Danilovic | 0:c66224a27cf8 | 90 | * @param addr Address of block to mark as unused |
Luka_Danilovic | 0:c66224a27cf8 | 91 | * @param size Size to mark as unused in bytes, must be a multiple of erase block size |
Luka_Danilovic | 0:c66224a27cf8 | 92 | * @return 0 on success, negative error code on failure |
Luka_Danilovic | 0:c66224a27cf8 | 93 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 94 | virtual int trim(bd_addr_t addr, bd_size_t size); |
Luka_Danilovic | 0:c66224a27cf8 | 95 | |
Luka_Danilovic | 0:c66224a27cf8 | 96 | /** Get the size of a readable block |
Luka_Danilovic | 0:c66224a27cf8 | 97 | * |
Luka_Danilovic | 0:c66224a27cf8 | 98 | * @return Size of a readable block in bytes |
Luka_Danilovic | 0:c66224a27cf8 | 99 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 100 | virtual bd_size_t get_read_size() const; |
Luka_Danilovic | 0:c66224a27cf8 | 101 | |
Luka_Danilovic | 0:c66224a27cf8 | 102 | /** Get the size of a programable block |
Luka_Danilovic | 0:c66224a27cf8 | 103 | * |
Luka_Danilovic | 0:c66224a27cf8 | 104 | * @return Size of a programable block in bytes |
Luka_Danilovic | 0:c66224a27cf8 | 105 | * @note Must be a multiple of the read size |
Luka_Danilovic | 0:c66224a27cf8 | 106 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 107 | virtual bd_size_t get_program_size() const; |
Luka_Danilovic | 0:c66224a27cf8 | 108 | |
Luka_Danilovic | 0:c66224a27cf8 | 109 | /** Get the total size of the underlying device |
Luka_Danilovic | 0:c66224a27cf8 | 110 | * |
Luka_Danilovic | 0:c66224a27cf8 | 111 | * @return Size of the underlying device in bytes |
Luka_Danilovic | 0:c66224a27cf8 | 112 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 113 | virtual bd_size_t size() const; |
Luka_Danilovic | 0:c66224a27cf8 | 114 | |
Luka_Danilovic | 0:c66224a27cf8 | 115 | /** Enable or disable debugging |
Luka_Danilovic | 0:c66224a27cf8 | 116 | * |
Luka_Danilovic | 0:c66224a27cf8 | 117 | * @param State of debugging |
Luka_Danilovic | 0:c66224a27cf8 | 118 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 119 | virtual void debug(bool dbg); |
Luka_Danilovic | 0:c66224a27cf8 | 120 | |
Luka_Danilovic | 0:c66224a27cf8 | 121 | /** Set the transfer frequency |
Luka_Danilovic | 0:c66224a27cf8 | 122 | * |
Luka_Danilovic | 0:c66224a27cf8 | 123 | * @param Transfer frequency |
Luka_Danilovic | 0:c66224a27cf8 | 124 | * @note Max frequency supported is 25MHZ |
Luka_Danilovic | 0:c66224a27cf8 | 125 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 126 | virtual int frequency(uint64_t freq); |
Luka_Danilovic | 0:c66224a27cf8 | 127 | |
Luka_Danilovic | 0:c66224a27cf8 | 128 | |
Luka_Danilovic | 0:c66224a27cf8 | 129 | private: |
Luka_Danilovic | 0:c66224a27cf8 | 130 | /* Commands : Listed below are commands supported |
Luka_Danilovic | 0:c66224a27cf8 | 131 | * in SPI mode for SD card : Only Mandatory ones |
Luka_Danilovic | 0:c66224a27cf8 | 132 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 133 | enum cmdSupported { |
Luka_Danilovic | 0:c66224a27cf8 | 134 | CMD_NOT_SUPPORTED = -1, /**< Command not supported error */ |
Luka_Danilovic | 0:c66224a27cf8 | 135 | CMD0_GO_IDLE_STATE = 0, /**< Resets the SD Memory Card */ |
Luka_Danilovic | 0:c66224a27cf8 | 136 | CMD1_SEND_OP_COND = 1, /**< Sends host capacity support */ |
Luka_Danilovic | 0:c66224a27cf8 | 137 | CMD6_SWITCH_FUNC = 6, /**< Check and Switches card function */ |
Luka_Danilovic | 0:c66224a27cf8 | 138 | CMD8_SEND_IF_COND = 8, /**< Supply voltage info */ |
Luka_Danilovic | 0:c66224a27cf8 | 139 | CMD9_SEND_CSD = 9, /**< Provides Card Specific data */ |
Luka_Danilovic | 0:c66224a27cf8 | 140 | CMD10_SEND_CID = 10, /**< Provides Card Identification */ |
Luka_Danilovic | 0:c66224a27cf8 | 141 | CMD12_STOP_TRANSMISSION = 12, /**< Forces the card to stop transmission */ |
Luka_Danilovic | 0:c66224a27cf8 | 142 | CMD13_SEND_STATUS = 13, /**< Card responds with status */ |
Luka_Danilovic | 0:c66224a27cf8 | 143 | CMD16_SET_BLOCKLEN = 16, /**< Length for SC card is set */ |
Luka_Danilovic | 0:c66224a27cf8 | 144 | CMD17_READ_SINGLE_BLOCK = 17, /**< Read single block of data */ |
Luka_Danilovic | 0:c66224a27cf8 | 145 | CMD18_READ_MULTIPLE_BLOCK = 18, /**< Card transfers data blocks to host until interrupted |
Luka_Danilovic | 0:c66224a27cf8 | 146 | by a STOP_TRANSMISSION command */ |
Luka_Danilovic | 0:c66224a27cf8 | 147 | CMD24_WRITE_BLOCK = 24, /**< Write single block of data */ |
Luka_Danilovic | 0:c66224a27cf8 | 148 | CMD25_WRITE_MULTIPLE_BLOCK = 25, /**< Continuously writes blocks of data until |
Luka_Danilovic | 0:c66224a27cf8 | 149 | 'Stop Tran' token is sent */ |
Luka_Danilovic | 0:c66224a27cf8 | 150 | CMD27_PROGRAM_CSD = 27, /**< Programming bits of CSD */ |
Luka_Danilovic | 0:c66224a27cf8 | 151 | CMD32_ERASE_WR_BLK_START_ADDR = 32, /**< Sets the address of the first write |
Luka_Danilovic | 0:c66224a27cf8 | 152 | block to be erased. */ |
Luka_Danilovic | 0:c66224a27cf8 | 153 | CMD33_ERASE_WR_BLK_END_ADDR = 33, /**< Sets the address of the last write |
Luka_Danilovic | 0:c66224a27cf8 | 154 | block of the continuous range to be erased.*/ |
Luka_Danilovic | 0:c66224a27cf8 | 155 | CMD38_ERASE = 38, /**< Erases all previously selected write blocks */ |
Luka_Danilovic | 0:c66224a27cf8 | 156 | CMD55_APP_CMD = 55, /**< Extend to Applications specific commands */ |
Luka_Danilovic | 0:c66224a27cf8 | 157 | CMD56_GEN_CMD = 56, /**< General Purpose Command */ |
Luka_Danilovic | 0:c66224a27cf8 | 158 | CMD58_READ_OCR = 58, /**< Read OCR register of card */ |
Luka_Danilovic | 0:c66224a27cf8 | 159 | CMD59_CRC_ON_OFF = 59, /**< Turns the CRC option on or off*/ |
Luka_Danilovic | 0:c66224a27cf8 | 160 | // App Commands |
Luka_Danilovic | 0:c66224a27cf8 | 161 | ACMD6_SET_BUS_WIDTH = 6, |
Luka_Danilovic | 0:c66224a27cf8 | 162 | ACMD13_SD_STATUS = 13, |
Luka_Danilovic | 0:c66224a27cf8 | 163 | ACMD22_SEND_NUM_WR_BLOCKS = 22, |
Luka_Danilovic | 0:c66224a27cf8 | 164 | ACMD23_SET_WR_BLK_ERASE_COUNT = 23, |
Luka_Danilovic | 0:c66224a27cf8 | 165 | ACMD41_SD_SEND_OP_COND = 41, |
Luka_Danilovic | 0:c66224a27cf8 | 166 | ACMD42_SET_CLR_CARD_DETECT = 42, |
Luka_Danilovic | 0:c66224a27cf8 | 167 | ACMD51_SEND_SCR = 51, |
Luka_Danilovic | 0:c66224a27cf8 | 168 | }; |
Luka_Danilovic | 0:c66224a27cf8 | 169 | |
Luka_Danilovic | 0:c66224a27cf8 | 170 | uint8_t _card_type; |
Luka_Danilovic | 0:c66224a27cf8 | 171 | int _cmd(SDBlockDevice::cmdSupported cmd, uint32_t arg, bool isAcmd=0, uint32_t *resp=NULL); |
Luka_Danilovic | 0:c66224a27cf8 | 172 | int _cmd8(); |
Luka_Danilovic | 0:c66224a27cf8 | 173 | |
Luka_Danilovic | 0:c66224a27cf8 | 174 | /* Move the SDCard into the SPI Mode idle state |
Luka_Danilovic | 0:c66224a27cf8 | 175 | * |
Luka_Danilovic | 0:c66224a27cf8 | 176 | * The card is transitioned from SDCard mode to SPI mode by sending the |
Luka_Danilovic | 0:c66224a27cf8 | 177 | * CMD0 (GO_IDLE_STATE) command with CS asserted. See the notes in the |
Luka_Danilovic | 0:c66224a27cf8 | 178 | * "SPI Startup" section of the comments at the head of the |
Luka_Danilovic | 0:c66224a27cf8 | 179 | * implementation file for further details and specification references. |
Luka_Danilovic | 0:c66224a27cf8 | 180 | * |
Luka_Danilovic | 0:c66224a27cf8 | 181 | * @return Response form the card. R1_IDLE_STATE (0x1), the successful |
Luka_Danilovic | 0:c66224a27cf8 | 182 | * response from CMD0. R1_XXX_XXX for more response |
Luka_Danilovic | 0:c66224a27cf8 | 183 | */ |
Luka_Danilovic | 0:c66224a27cf8 | 184 | uint32_t _go_idle_state(); |
Luka_Danilovic | 0:c66224a27cf8 | 185 | int _initialise_card(); |
Luka_Danilovic | 0:c66224a27cf8 | 186 | |
Luka_Danilovic | 0:c66224a27cf8 | 187 | bd_size_t _sectors; |
Luka_Danilovic | 0:c66224a27cf8 | 188 | bd_size_t _sd_sectors(); |
Luka_Danilovic | 0:c66224a27cf8 | 189 | |
Luka_Danilovic | 0:c66224a27cf8 | 190 | bool _is_valid_trim(bd_addr_t addr, bd_size_t size); |
Luka_Danilovic | 0:c66224a27cf8 | 191 | |
Luka_Danilovic | 0:c66224a27cf8 | 192 | /* SPI functions */ |
Luka_Danilovic | 0:c66224a27cf8 | 193 | Timer _spi_timer; /**< Timer Class object used for busy wait */ |
Luka_Danilovic | 0:c66224a27cf8 | 194 | uint32_t _init_sck; /**< Intial SPI frequency */ |
Luka_Danilovic | 0:c66224a27cf8 | 195 | uint32_t _transfer_sck; /**< SPI frequency during data transfer/after initialization */ |
Luka_Danilovic | 0:c66224a27cf8 | 196 | SPI _spi; /**< SPI Class object */ |
Luka_Danilovic | 0:c66224a27cf8 | 197 | |
Luka_Danilovic | 0:c66224a27cf8 | 198 | /* SPI initialization function */ |
Luka_Danilovic | 0:c66224a27cf8 | 199 | void _spi_init(); |
Luka_Danilovic | 0:c66224a27cf8 | 200 | uint8_t _cmd_spi(SDBlockDevice::cmdSupported cmd, uint32_t arg); |
Luka_Danilovic | 0:c66224a27cf8 | 201 | void _spi_wait(uint8_t count); |
Luka_Danilovic | 0:c66224a27cf8 | 202 | |
Luka_Danilovic | 0:c66224a27cf8 | 203 | bool _wait_token(uint8_t token); /**< Wait for token */ |
Luka_Danilovic | 0:c66224a27cf8 | 204 | bool _wait_ready(uint16_t ms=300); /**< 300ms default wait for card to be ready */ |
Luka_Danilovic | 0:c66224a27cf8 | 205 | int _read(uint8_t * buffer, uint32_t length); |
Luka_Danilovic | 0:c66224a27cf8 | 206 | int _read_bytes(uint8_t * buffer, uint32_t length); |
Luka_Danilovic | 0:c66224a27cf8 | 207 | uint8_t _write(const uint8_t *buffer,uint8_t token, uint32_t length); |
Luka_Danilovic | 0:c66224a27cf8 | 208 | int _freq(void); |
Luka_Danilovic | 0:c66224a27cf8 | 209 | |
Luka_Danilovic | 0:c66224a27cf8 | 210 | /* Chip Select and SPI mode select */ |
Luka_Danilovic | 0:c66224a27cf8 | 211 | DigitalOut _cs; |
Luka_Danilovic | 0:c66224a27cf8 | 212 | void _select(); |
Luka_Danilovic | 0:c66224a27cf8 | 213 | void _deselect(); |
Luka_Danilovic | 0:c66224a27cf8 | 214 | |
Luka_Danilovic | 0:c66224a27cf8 | 215 | virtual void lock() { |
Luka_Danilovic | 0:c66224a27cf8 | 216 | _mutex.lock(); |
Luka_Danilovic | 0:c66224a27cf8 | 217 | } |
Luka_Danilovic | 0:c66224a27cf8 | 218 | |
Luka_Danilovic | 0:c66224a27cf8 | 219 | virtual void unlock() { |
Luka_Danilovic | 0:c66224a27cf8 | 220 | _mutex.unlock(); |
Luka_Danilovic | 0:c66224a27cf8 | 221 | } |
Luka_Danilovic | 0:c66224a27cf8 | 222 | |
Luka_Danilovic | 0:c66224a27cf8 | 223 | PlatformMutex _mutex; |
Luka_Danilovic | 0:c66224a27cf8 | 224 | bd_size_t _block_size; |
Luka_Danilovic | 0:c66224a27cf8 | 225 | bd_size_t _erase_size; |
Luka_Danilovic | 0:c66224a27cf8 | 226 | bool _is_initialized; |
Luka_Danilovic | 0:c66224a27cf8 | 227 | bool _dbg; |
Luka_Danilovic | 0:c66224a27cf8 | 228 | }; |
Luka_Danilovic | 0:c66224a27cf8 | 229 | |
Luka_Danilovic | 0:c66224a27cf8 | 230 | #endif /* DEVICE_SPI */ |
Luka_Danilovic | 0:c66224a27cf8 | 231 | |
Luka_Danilovic | 0:c66224a27cf8 | 232 | #endif /* MBED_SD_BLOCK_DEVICE_H */ |