/* * FreeRTOS+FAT DOS Compatible Embedded FAT File System * https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_FAT/index.html * ported to Cypress CY8C6347BZI-BLD53. * * This code borrows heavily from the Mbed SDBlockDevice: * https://os.mbed.com/docs/mbed-os/v5.15/apis/sdblockdevice.html * mbed-os/components/storage/blockdevice/COMPONENT_SD/SDBlockDevice.cpp * * Editor: Carl Kugler (carlk3@gmail.com) */ /* * FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab! * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. * Authors include James Walmsley, Hein Tibosch and Richard Barry * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * */ /* mbed Microcontroller Library * Copyright (c) 2006-2013 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Introduction * ------------ * SD and MMC cards support a number of interfaces, but common to them all * is one based on SPI. Since we already have the mbed SPI Interface, it will * be used for SD cards. * * The main reference I'm using is Chapter 7, "SPI Mode" of: * http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf * * SPI Startup * ----------- * The SD card powers up in SD mode. The start-up procedure is complicated * by the requirement to support older SDCards in a backwards compatible * way with the new higher capacity variants SDHC and SDHC. * * The following figures from the specification with associated text describe * the SPI mode initialisation process: * - Figure 7-1: SD Memory Card State Diagram (SPI mode) * - Figure 7-2: SPI Mode Initialization Flow * * Firstly, a low initial clock should be selected (in the range of 100- * 400kHZ). After initialisation has been completed, the switch to a * higher clock speed can be made (e.g. 1MHz). Newer cards will support * higher speeds than the default _transfer_sck defined here. * * Next, note the following from the SDCard specification (note to * Figure 7-1): * * In any of the cases CMD1 is not recommended because it may be difficult for the host * to distinguish between MultiMediaCard and SD Memory Card * * Hence CMD1 is not used for the initialisation sequence. * * The SPI interface mode is selected by asserting CS low and sending the * reset command (CMD0). The card will respond with a (R1) response. * In practice many cards initially respond with 0xff or invalid data * which is ignored. Data is read until a valid response is received * or the number of re-reads has exceeded a maximim count. If a valid * response is not received then the CMD0 can be retried. This * has been found to successfully initialise cards where the SPI master * (on MCU) has been reset but the SDCard has not, so the first * CMD0 may be lost. * * CMD8 is optionally sent to determine the voltage range supported, and * indirectly determine whether it is a version 1.x SD/non-SD card or * version 2.x. I'll just ignore this for now. * * ACMD41 is repeatedly issued to initialise the card, until "in idle" * (bit 0) of the R1 response goes to '0', indicating it is initialised. * * You should also indicate whether the host supports High Capicity cards, * and check whether the card is high capacity - i'll also ignore this * * SPI Protocol * ------------ * The SD SPI protocol is based on transactions made up of 8-bit words, with * the host starting every bus transaction by asserting the CS signal low. The * card always responds to commands, data blocks and errors. * * The protocol supports a CRC, but by default it is off (except for the * first reset CMD0, where the CRC can just be pre-calculated, and CMD8) * I'll leave the CRC off I think! * * Standard capacity cards have variable data block sizes, whereas High * Capacity cards fix the size of data block to 512 bytes. I'll therefore * just always use the Standard Capacity cards with a block size of 512 bytes. * This is set with CMD16. * * You can read and write single blocks (CMD17, CMD25) or multiple blocks * (CMD18, CMD25). For simplicity, I'll just use single block accesses. When * the card gets a read command, it responds with a response token, and then * a data token or an error. * * SPI Command Format * ------------------ * Commands are 6-bytes long, containing the command, 32-bit argument, and CRC. * * +---------------+------------+------------+-----------+----------+--------------+ * | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 | * +---------------+------------+------------+-----------+----------+--------------+ * * As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95) * * All Application Specific commands shall be preceded with APP_CMD (CMD55). * * SPI Response Format * ------------------- * The main response format (R1) is a status byte (normally zero). Key flags: * idle - 1 if the card is in an idle state/initialising * cmd - 1 if an illegal command code was detected * * +-------------------------------------------------+ * R1 | 0 | arg | addr | seq | crc | cmd | erase | idle | * +-------------------------------------------------+ * * R1b is the same, except it is followed by a busy signal (zeros) until * the first non-zero byte when it is ready again. * * Data Response Token * ------------------- * Every data block written to the card is acknowledged by a byte * response token * * +----------------------+ * | xxx | 0 | status | 1 | * +----------------------+ * 010 - OK! * 101 - CRC Error * 110 - Write Error * * Single Block Read and Write * --------------------------- * * Block transfers have a byte header, followed by the data, followed * by a 16-bit CRC. In our case, the data will always be 512 bytes. * * +------+---------+---------+- - - -+---------+-----------+----------+ * | 0xFE | data[0] | data[1] | | data[n] | crc[15:8] | crc[7:0] | * +------+---------+---------+- - - -+---------+-----------+----------+ */ /* Standard includes. */ //#include //#include //#include //#include #include #include /* FreeRTOS includes. */ #include "FreeRTOS.h" //#include "task.h" //#include "semphr.h" //#include "portmacro.h" /* FreeRTOS+FAT includes. */ #include "ff_sddisk.h" #include "ff_sys.h" #include "project.h" #include "cy_result.h" #include "cy_sysclk.h" /* Disk Status Bits (DSTATUS) */ enum { STA_NOINIT = 0x01, /* Drive not initialized */ STA_NODISK = 0x02, /* No medium in the drive */ STA_PROTECT = 0x04 /* Write protected */ }; /* Control Tokens */ #define SPI_DATA_RESPONSE_MASK (0x1F) #define SPI_DATA_ACCEPTED (0x05) #define SPI_DATA_CRC_ERROR (0x0B) #define SPI_DATA_WRITE_ERROR (0x0D) #define SPI_START_BLOCK (0xFE) /*!< For Single Block Read/Write and Multiple Block Read */ #define SPI_START_BLK_MUL_WRITE (0xFC) /*!< Start Multi-block write */ #define SPI_STOP_TRAN (0xFD) /*!< Stop Multi-block write */ #define SPI_DATA_READ_ERROR_MASK (0xF) /*!< Data Error Token: 4 LSB bits */ #define SPI_READ_ERROR (0x1 << 0) /*!< Error */ #define SPI_READ_ERROR_CC (0x1 << 1) /*!< CC Error*/ #define SPI_READ_ERROR_ECC_C (0x1 << 2) /*!< Card ECC failed */ #define SPI_READ_ERROR_OFR (0x1 << 3) /*!< Out of Range */ /** Represents the different SD/MMC card types */ // Types #define SDCARD_NONE 0 /**< No card is present */ #define SDCARD_V1 1 /**< v1.x Standard Capacity */ #define SDCARD_V2 2 /**< v2.x Standard capacity SD card */ #define SDCARD_V2HC 3 /**< v2.x High capacity SD card */ #define CARD_UNKNOWN 4 /**< Unknown or unsupported card */ typedef enum { CMD_NOT_SUPPORTED = -1, /**< Command not supported error */ CMD0_GO_IDLE_STATE = 0, /**< Resets the SD Memory Card */ CMD1_SEND_OP_COND = 1, /**< Sends host capacity support */ CMD6_SWITCH_FUNC = 6, /**< Check and Switches card function */ CMD8_SEND_IF_COND = 8, /**< Supply voltage info */ CMD9_SEND_CSD = 9, /**< Provides Card Specific data */ CMD10_SEND_CID = 10, /**< Provides Card Identification */ CMD12_STOP_TRANSMISSION = 12, /**< Forces the card to stop transmission */ CMD13_SEND_STATUS = 13, /**< Card responds with status */ CMD16_SET_BLOCKLEN = 16, /**< Length for SC card is set */ CMD17_READ_SINGLE_BLOCK = 17, /**< Read single block of data */ CMD18_READ_MULTIPLE_BLOCK = 18, /**< Card transfers data blocks to host until interrupted by a STOP_TRANSMISSION command */ CMD24_WRITE_BLOCK = 24, /**< Write single block of data */ CMD25_WRITE_MULTIPLE_BLOCK = 25, /**< Continuously writes blocks of data until 'Stop Tran' token is sent */ CMD27_PROGRAM_CSD = 27, /**< Programming bits of CSD */ CMD32_ERASE_WR_BLK_START_ADDR = 32, /**< Sets the address of the first write block to be erased. */ CMD33_ERASE_WR_BLK_END_ADDR = 33, /**< Sets the address of the last write block of the continuous range to be erased.*/ CMD38_ERASE = 38, /**< Erases all previously selected write blocks */ CMD55_APP_CMD = 55, /**< Extend to Applications specific commands */ CMD56_GEN_CMD = 56, /**< General Purpose Command */ CMD58_READ_OCR = 58, /**< Read OCR register of card */ CMD59_CRC_ON_OFF = 59, /**< Turns the CRC option on or off*/ // App Commands ACMD6_SET_BUS_WIDTH = 6, ACMD13_SD_STATUS = 13, ACMD22_SEND_NUM_WR_BLOCKS = 22, ACMD23_SET_WR_BLK_ERASE_COUNT = 23, ACMD41_SD_SEND_OP_COND = 41, ACMD42_SET_CLR_CARD_DETECT = 42, ACMD51_SEND_SCR = 51, } cmdSupported; enum { CYHAL_SPI_RSLT_BAD_ARGUMENT, CYHAL_SPI_RSLT_CLOCK_ERROR, CYHAL_SPI_RSLT_TRANSFER_ERROR, CYHAL_SPI_RSLT_CLOCK_NOT_SUPPORTED, CYHAL_SPI_RSLT_PIN_CONFIG_NOT_SUPPORTED, CYHAL_SPI_RSLT_INVALID_PIN_API_NOT_SUPPORTED, CYHAL_SPI_RSLT_ERR_INVALID_PIN }; #define SPI_OVERSAMPLE_MIN 4 #define SPI_OVERSAMPLE_MAX 16 /* Assign pins for SPI on SCB1: P10[0], P10[1], P10[2] and P10[3] */ #define SPI_PORT GPIO_PRT12 #define SPI_MISO_NUM P12_1_NUM #define SPI_MOSI_NUM P12_0_NUM #define SPI_SCLK_NUM P12_2_NUM #define SPI_SS_NUM P12_3_NUM /* R1 Response Format */ #define R1_NO_RESPONSE (0xFF) #define R1_RESPONSE_RECV (0x80) #define R1_IDLE_STATE (1 << 0) #define R1_ERASE_RESET (1 << 1) #define R1_ILLEGAL_COMMAND (1 << 2) #define R1_COM_CRC_ERROR (1 << 3) #define R1_ERASE_SEQUENCE_ERROR (1 << 4) #define R1_ADDRESS_ERROR (1 << 5) #define R1_PARAMETER_ERROR (1 << 6) #define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */ #define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */ #define SD_BLOCK_DEVICE_ERROR_PARAMETER -5003 /*!< invalid parameter */ #define SD_BLOCK_DEVICE_ERROR_NO_INIT -5004 /*!< uninitialized */ #define SD_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */ #define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */ #define SD_BLOCK_DEVICE_ERROR_UNUSABLE -5007 /*!< unusable card */ #define SD_BLOCK_DEVICE_ERROR_NO_RESPONSE -5008 /*!< No response from device */ #define SD_BLOCK_DEVICE_ERROR_CRC -5009 /*!< CRC error */ #define SD_BLOCK_DEVICE_ERROR_ERASE -5010 /*!< Erase error: reset/sequence */ #define SD_BLOCK_DEVICE_ERROR_WRITE -5011 /*!< SPI Write error: !SPI_DATA_ACCEPTED */ // Only HC block size is supported. Making this a static constant reduces code size. #define BLOCK_SIZE_HC 512 /*!< Block size supported for SD card is 512 bytes */ static const uint32_t _block_size = BLOCK_SIZE_HC; static uint32_t _erase_size; static uint64_t _sectors; static int m_Status = 0; static int _card_type = 0; /* --------------- BEGIN -------------------- */ // mbed-os/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_hw_types.h /** Available clock divider types */ typedef cy_en_divider_types_t cyhal_clock_divider_types_t; /** @brief Clock divider object */ typedef struct { cyhal_clock_divider_types_t div_type; uint8_t div_num; } cyhal_clock_divider_t; /** @brief Event callback data object */ typedef struct { cy_israddress callback; void* callback_arg; } cyhal_event_callback_data_t; typedef struct { CySCB_Type* base; // cyhal_resource_inst_t resource; // cyhal_gpio_t pin_miso; // cyhal_gpio_t pin_mosi; // cyhal_gpio_t pin_sclk; // cyhal_gpio_t pin_ssel; uint32_t pin_ssel; cyhal_clock_divider_t clock; // cy_en_scb_spi_sclk_mode_t clk_mode; // uint8_t mode; // uint8_t data_bits; bool is_slave; // bool alloc_clock; uint8_t oversample_value; // bool msb_first; // cy_stc_scb_spi_context_t context; cy_stc_scb_spi_context_t *pContext; uint32_t irq_cause; uint16_t pending; void *rx_buffer; uint32_t rx_buffer_size; const void *tx_buffer; uint32_t tx_buffer_size; bool is_async; cyhal_event_callback_data_t callback_data; // //// mbed-os/targets/TARGET_Cypress/TARGET_PSOC6/objects.h //struct spi_s { // cyhal_spi_t hal_spi; // cyhal_spi_cfg_t cfg; void (*async_handler)(void); int async_events; int async_event_mask; // cyhal_gpio_t mosi; // cyhal_gpio_t miso; // cyhal_gpio_t sclk; // cyhal_gpio_t ssel; int hz; bool async_in_progress; //}; } cyhal_spi_t; /* --------------- END -------------------- */ static cyhal_spi_t theSpi; /* Default SPI configuration */ static cy_stc_scb_spi_config_t default_spi_config; #if 0 static cy_rslt_t cyhal_int_spi_frequency(cyhal_spi_t *obj, uint32_t hz, uint8_t *over_sample_val) { CY_ASSERT(NULL != obj); cy_rslt_t result = CY_RSLT_SUCCESS; uint8_t oversample_value; uint32_t divider_value; uint32_t last_diff = 0xFFFFFFFF; uint8_t last_ovrsmpl_val = 0; uint32_t last_dvdr_val = 0; uint32_t oversampled_freq = 0; uint32_t divided_freq = 0; uint32_t diff = 0; Cy_SysClk_PeriphDisableDivider(obj->clock.div_type, obj->clock.div_num); if (!obj->is_slave) { for (oversample_value = SPI_OVERSAMPLE_MIN; oversample_value <= SPI_OVERSAMPLE_MAX; oversample_value++) { oversampled_freq = hz * oversample_value; // if ((hz * oversample_value > Cy_SysClk_ClkPeriGetFrequency()) && (SPI_OVERSAMPLE_MIN == oversample_value)) // uint32_t Cy_SysClk_PeriphGetFrequency(cy_en_divider_types_t dividerType, uint32_t dividerNum); // if ((hz * oversample_value // > Cy_SysClk_PeriphGetFrequency(SPI_1_SCBCLK_DIV_TYPE, // SPI_1_SCBCLK_DIV_NUM)) // && (SPI_OVERSAMPLE_MIN == oversample_value)) { if ((hz * oversample_value > Cy_SysClk_ClkPeriGetFrequency()) && (SPI_OVERSAMPLE_MIN == oversample_value)) { return CYHAL_SPI_RSLT_CLOCK_ERROR; } else if (hz * oversample_value > Cy_SysClk_PeriphGetFrequency(SPI_1_SCBCLK_DIV_TYPE, SPI_1_SCBCLK_DIV_NUM)) { continue; } divider_value = cyhal_divider_value(hz * oversample_value, 0); divided_freq = Cy_SysClk_ClkPeriGetFrequency() / (divider_value + 1); diff = MAX(oversampled_freq, divided_freq) - MIN(oversampled_freq, divided_freq); if (diff < last_diff) { last_diff = diff; last_ovrsmpl_val = oversample_value; last_dvdr_val = divider_value; if (0 == diff) { break; } } } *over_sample_val = last_ovrsmpl_val; } else { /* Slave requires such frequency: fclk_scb = N / ((0.5 * tclk_scb) – 20 nsec - tDSI, * N is 3 when "Enable Input Glitch Filter" is false and 4 when true. * tDSI Is external master delay which is assumed to be 16.66 nsec */ float desired_period_us = 1 / (float) hz * 1e6; uint32_t required_frequency = (uint32_t)( 3e6 / (0.5f * desired_period_us - 36.66f / 1e3)); if (required_frequency > Cy_SysClk_PeriphGetFrequency(SPI_1_SCBCLK_DIV_TYPE, SPI_1_SCBCLK_DIV_NUM)) { return CYHAL_SPI_RSLT_CLOCK_ERROR; } /* Use maximum available clock for slave to make it able to work with any master environment */ last_dvdr_val = 1; } result = Cy_SysClk_PeriphSetDivider(obj->clock.div_type, obj->clock.div_num, last_dvdr_val); if (CY_SYSCLK_SUCCESS != result) { result = CYHAL_SPI_RSLT_CLOCK_ERROR; } if (CY_RSLT_SUCCESS == result) { Cy_SysClk_PeriphEnableDivider(obj->clock.div_type, obj->clock.div_num); } return result; } #endif static void spi_go_high_frequency() { SPI_1_Disable(); SPI_1_SCBCLK_Disable(); // In TopDesign.cysch, initial clock setting is // Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_8_BIT, 1u, 124u); // as seen in cyfitter_cfg.c // For 1 Mhz: SPI_1_SCBCLK_SetDivider(12u); SPI_1_SCBCLK_Enable(); SPI_1_Enable(); } /** Integer representation of no connect pin (required to exist in all BSPs) */ // mbed-os/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_gpio.h #define CYHAL_NC_PIN_VALUE ((uint32_t)0xFFFFFFFF) #define SSEL_ACTIVE ( 0 ) #define SSEL_INACTIVE ( 1 ) // mbed-os/drivers/SPI.h: class SPI // mbed-os/drivers/source/SPI.cpp static void spi_select() { // Assert /SS Cy_GPIO_Write(SPI_PORT, SPI_SS_NUM, 0); } static void spi_deselect() { // Unassert /SS Cy_GPIO_Write(SPI_PORT, SPI_SS_NUM, 1); } /* Combine master error statuses in single mask */ #define MASTER_ERROR_MASK (CY_SCB_SPI_SLAVE_TRANSFER_ERR | CY_SCB_SPI_TRANSFER_OVERFLOW | \ CY_SCB_SPI_TRANSFER_UNDERFLOW) // If the data that will be received is not important, pass NULL as rxBuffer. // If the data that will be transmitted is not important, // pass NULL as txBuffer and then the CY_SCB_SPI_DEFAULT_TX is sent out as each data element. static bool spi_transfer(const uint8_t *tx, uint8_t *rx, size_t length) { cy_en_scb_spi_status_t errorStatus; // // cy_rslt_t cyhal_spi_transfer(cyhal_spi_t *obj, const uint8_t *tx, size_t tx_length, uint8_t *rx, size_t rx_length, uint8_t write_fill) // if (CY_RSLT_SUCCESS != cyhal_spi_transfer(&theSpi, (const uint8_t *)&value, 1, (uint8_t *)&received, 1, 0xff)) { // //MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_SPI, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_spi_transfer"); // FF_PRINTF("FAILED_OPERATION): cyhal_spi_transfer"); // } /* Initiate SPI Master write and read transaction. */ errorStatus = Cy_SCB_SPI_Transfer(SPI_1_HW, (void *) tx, rx, length, &SPI_1_context); /* If no error wait till master sends data in Tx FIFO */ if (errorStatus != CY_SCB_SPI_SUCCESS) { return false; } else { uint32_t masterStatus; /* Timeout 1 sec */ uint32_t timeOut = 1000UL; /* Wait until master complete read transfer or time out has occured */ do { masterStatus = Cy_SCB_SPI_GetTransferStatus(SPI_1_HW, &SPI_1_context); // Cy_SysLib_Delay(CY_SCB_WAIT_1_UNIT); vTaskDelay(pdMS_TO_TICKS(1)); timeOut--; } while ((0UL != (masterStatus & CY_SCB_SPI_TRANSFER_ACTIVE)) && (timeOut > 0UL)); // if ((0UL == (MASTER_ERROR_MASK & masterStatus)) && // (1 == Cy_SCB_SPI_GetNumTransfered(SPI_1_HW, &SPI_1_context))) // { // status = TRANSFER_CMPLT; // // } // else // { // HandleError(); // } } return true; } static char spi_write(const uint8_t value) { uint8_t received = 0xFF; cy_en_scb_spi_status_t rc; spi_select(); rc = Cy_SCB_SPI_Transfer(SPI_1_HW, (void *) &value, &received, 1, &SPI_1_context); if (CY_SCB_SPI_SUCCESS != rc) { FF_PRINTF("Cy_SCB_SPI_Transfer: %d\n", rc); } while ( CY_SCB_SPI_TRANSFER_ACTIVE & Cy_SCB_SPI_GetTransferStatus(SPI_1_HW, &SPI_1_context)) { }; spi_deselect(); return received; } /* SIZE in Bytes */ #define PACKET_SIZE 6 /*!< SD Packet size CMD+ARG+CRC */ #define R1_RESPONSE_SIZE 1 /*!< Size of R1 response */ #define R2_RESPONSE_SIZE 2 /*!< Size of R2 response */ #define R3_R7_RESPONSE_SIZE 5 /*!< Size of R3/R7 response */ /* R3 Response : OCR Register */ #define OCR_HCS_CCS (0x1 << 30) #define OCR_LOW_VOLTAGE (0x01 << 24) #define OCR_3_3V (0x1 << 20) #define SPI_CMD(x) (0x40 | (x & 0x3f)) #define SPI_FILL_CHAR (0xFF) static uint8_t fs_cmd_spi(cmdSupported cmd, uint32_t arg) { uint8_t response; char cmdPacket[PACKET_SIZE]; // Prepare the command packet cmdPacket[0] = SPI_CMD(cmd); cmdPacket[1] = (arg >> 24); cmdPacket[2] = (arg >> 16); cmdPacket[3] = (arg >> 8); cmdPacket[4] = (arg >> 0); #if MBED_CONF_SD_CRC_ENABLED uint32_t crc; if (_crc_on) { _crc7.compute((void *)cmdPacket, 5, &crc); cmdPacket[5] = (char)(crc | 0x01); } else #endif { // CMD0 is executed in SD mode, hence should have correct CRC // CMD8 CRC verification is always enabled switch (cmd) { case CMD0_GO_IDLE_STATE: cmdPacket[5] = 0x95; break; case CMD8_SEND_IF_COND: cmdPacket[5] = 0x87; break; default: cmdPacket[5] = 0xFF; // Make sure bit 0-End bit is high break; } } // send a command for (int i = 0; i < PACKET_SIZE; i++) { spi_write(cmdPacket[i]); } // The received byte immediataly following CMD12 is a stuff byte, // it should be discarded before receive the response of the CMD12. if (CMD12_STOP_TRANSMISSION == cmd) { spi_write(SPI_FILL_CHAR); } // Loop for response: Response is sent back within command response time (NCR), 0 to 8 bytes for SDC for (int i = 0; i < 0x10; i++) { response = spi_write(SPI_FILL_CHAR); // Got the response if (!(response & R1_RESPONSE_RECV)) { break; } } return response; } static bool fs_wait_ready(int timeout) { char resp; //Keep sending dummy clocks with DI held high until the card releases the DO line // m_Timer.start(); TickType_t xStart = xTaskGetTickCount(); do { // resp = m_Spi.write(0xFF); resp = spi_write(0xFF); } while (resp == 0x00 && (xTaskGetTickCount() - xStart) < pdMS_TO_TICKS(timeout)); //Return success/failure return (resp > 0x00); } static const char m_Crc7Table[] = { 0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F, 0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77, 0x19, 0x10, 0x0B, 0x02, 0x3D, 0x34, 0x2F, 0x26, 0x51, 0x58, 0x43, 0x4A, 0x75, 0x7C, 0x67, 0x6E, 0x32, 0x3B, 0x20, 0x29, 0x16, 0x1F, 0x04, 0x0D, 0x7A, 0x73, 0x68, 0x61, 0x5E, 0x57, 0x4C, 0x45, 0x2B, 0x22, 0x39, 0x30, 0x0F, 0x06, 0x1D, 0x14, 0x63, 0x6A, 0x71, 0x78, 0x47, 0x4E, 0x55, 0x5C, 0x64, 0x6D, 0x76, 0x7F, 0x40, 0x49, 0x52, 0x5B, 0x2C, 0x25, 0x3E, 0x37, 0x08, 0x01, 0x1A, 0x13, 0x7D, 0x74, 0x6F, 0x66, 0x59, 0x50, 0x4B, 0x42, 0x35, 0x3C, 0x27, 0x2E, 0x11, 0x18, 0x03, 0x0A, 0x56, 0x5F, 0x44, 0x4D, 0x72, 0x7B, 0x60, 0x69, 0x1E, 0x17, 0x0C, 0x05, 0x3A, 0x33, 0x28, 0x21, 0x4F, 0x46, 0x5D, 0x54, 0x6B, 0x62, 0x79, 0x70, 0x07, 0x0E, 0x15, 0x1C, 0x23, 0x2A, 0x31, 0x38, 0x41, 0x48, 0x53, 0x5A, 0x65, 0x6C, 0x77, 0x7E, 0x09, 0x00, 0x1B, 0x12, 0x2D, 0x24, 0x3F, 0x36, 0x58, 0x51, 0x4A, 0x43, 0x7C, 0x75, 0x6E, 0x67, 0x10, 0x19, 0x02, 0x0B, 0x34, 0x3D, 0x26, 0x2F, 0x73, 0x7A, 0x61, 0x68, 0x57, 0x5E, 0x45, 0x4C, 0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D, 0x04, 0x6A, 0x63, 0x78, 0x71, 0x4E, 0x47, 0x5C, 0x55, 0x22, 0x2B, 0x30, 0x39, 0x06, 0x0F, 0x14, 0x1D, 0x25, 0x2C, 0x37, 0x3E, 0x01, 0x08, 0x13, 0x1A, 0x6D, 0x64, 0x7F, 0x76, 0x49, 0x40, 0x5B, 0x52, 0x3C, 0x35, 0x2E, 0x27, 0x18, 0x11, 0x0A, 0x03, 0x74, 0x7D, 0x66, 0x6F, 0x50, 0x59, 0x42, 0x4B, 0x17, 0x1E, 0x05, 0x0C, 0x33, 0x3A, 0x21, 0x28, 0x5F, 0x56, 0x4D, 0x44, 0x7B, 0x72, 0x69, 0x60, 0x0E, 0x07, 0x1C, 0x15, 0x2A, 0x23, 0x38, 0x31, 0x46, 0x4F, 0x54, 0x5D, 0x62, 0x6B, 0x70, 0x79 }; static const unsigned short m_Crc16Table[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 }; #if MBED_CONF_SD_CRC_ENABLED static char crc7(const char* data, int length) { //Calculate the CRC7 checksum for the specified data block char crc = 0; for (int i = 0; i < length; i++) { crc = m_Crc7Table[(crc << 1) ^ data[i]]; } //Return the calculated checksum return crc; } #endif #if MBED_CONF_SD_CRC_ENABLED static unsigned short crc16(const char* data, int length) { //Calculate the CRC16 checksum for the specified data block unsigned short crc = 0; for (int i = 0; i < length; i++) { crc = (crc << 8) ^ m_Crc16Table[((crc >> 8) ^ data[i]) & 0x00FF]; } //Return the calculated checksum return crc; } #endif static void fs_select() { // _spi.lock(); // spi_write(SPI_FILL_CHAR); spi_write(0xFF); // _cs = 0; spi_select(); } static void fs_deselect() { // _cs = 1; spi_deselect(); // _spi.write(SPI_FILL_CHAR); spi_write(0xFF); // _spi.unlock(); } #define SD_COMMAND_TIMEOUT 5000 /*!< Timeout in ms for response */ #if 0 static char fs_cmd(char cmd, unsigned int arg, unsigned int* resp) { char token; //Try to send the command up to 3 times for (int f = 0; f < 3; f++) { //Send CMD55(0x00000000) prior to an application specific command if (cmd == ACMD22 || cmd == ACMD23 || cmd == ACMD41 || cmd == ACMD42) { token = fs_cmd(CMD55, 0x00000000, NULL); if (token > 0x01) return token; //Deselect and reselect the card between CMD55 and an ACMD fs_deselect(); fs_select(); if(!fs_wait_ready(SD_COMMAND_TIMEOUT)) return 0xFF; } //Prepare the command packet char cmdPacket[6]; cmdPacket[0] = cmd; cmdPacket[1] = arg >> 24; cmdPacket[2] = arg >> 16; cmdPacket[3] = arg >> 8; cmdPacket[4] = arg; if (m_Crc || cmd == CMD0 || cmd == CMD8) cmdPacket[5] = (crc7(cmdPacket, 5) << 1) | 0x01; else cmdPacket[5] = 0x01; //Send the command packet for (int i = 0; i < 6; i++) spi_write(cmdPacket[i]); //Discard the stuff byte immediately following CMD12 if (cmd == CMD12) spi_write(0xFF); //Allow up to 8 bytes of delay for the R1 response token for (int i = 0; i < 9; i++) { token = spi_write(0xFF); if (!(token & 0x80)) break; } //Verify the R1 response token if (token == 0xFF) { //No data was received, get out early break; } else if (token & (1 << 3)) { //There was a CRC error, try again continue; } else if (token > 0x01) { //An error occured, get out early break; } //Handle R2 and R3/R7 response tokens if (cmd == CMD13 && resp != NULL) { //Read the R2 response value *resp = spi_write(0xFF); } else if ((cmd == CMD8 || cmd == CMD58) && resp != NULL) { //Read the R3/R7 response value *resp = (spi_write(0xFF) << 24); *resp |= (spi_write(0xFF) << 16); *resp |= (spi_write(0xFF) << 8); *resp |= spi_write(0xFF); } //The command was successful break; } //Return the R1 response token return token; } #endif /** Enum of standard error codes * * @enum bd_error */ enum bd_error { BD_ERROR_OK = 0, /*!< no error */ BD_ERROR_DEVICE_ERROR = -4001, /*!< device specific error */ }; static int fs_cmd(cmdSupported cmd, uint32_t arg, bool isAcmd, uint32_t *resp) { int32_t status = BD_ERROR_OK; uint32_t response; // Select card and wait for card to be ready before sending next command // Note: next command will fail if card is not ready fs_select(); // No need to wait for card to be ready when sending the stop command if (CMD12_STOP_TRANSMISSION != cmd) { if (false == fs_wait_ready(SD_COMMAND_TIMEOUT)) { FF_PRINTF("Card not ready yet\n"); } } // Re-try command for (int i = 0; i < 3; i++) { // Send CMD55 for APP command first if (isAcmd) { response = fs_cmd_spi(CMD55_APP_CMD, 0x0); // Wait for card to be ready after CMD55 if (false == fs_wait_ready(SD_COMMAND_TIMEOUT)) { FF_PRINTF("Card not ready yet\n"); } } // Send command over SPI interface response = fs_cmd_spi(cmd, arg); if (R1_NO_RESPONSE == response) { FF_PRINTF("No response CMD:%d\n", cmd); continue; } break; } // Pass the response to the command call if required if (NULL != resp) { *resp = response; } // Process the response R1 : Exit on CRC/Illegal command error/No response if (R1_NO_RESPONSE == response) { fs_deselect(); FF_PRINTF("No response CMD:%d response: 0x%" PRIx32 "\n", cmd, response); return SD_BLOCK_DEVICE_ERROR_NO_DEVICE; // No device } if (response & R1_COM_CRC_ERROR) { fs_deselect(); FF_PRINTF("CRC error CMD:%d response 0x%" PRIx32 "\n", cmd, response); return SD_BLOCK_DEVICE_ERROR_CRC; // CRC error } if (response & R1_ILLEGAL_COMMAND) { fs_deselect(); FF_PRINTF("Illegal command CMD:%d response 0x%" PRIx32 "\n", cmd, response); if (CMD8_SEND_IF_COND == cmd) { // Illegal command is for Ver1 or not SD Card _card_type = CARD_UNKNOWN; } return SD_BLOCK_DEVICE_ERROR_UNSUPPORTED; // Command not supported } // FF_PRINTF("CMD:%d \t arg:0x%" PRIx32 " \t Response:0x%" PRIx32 "\n", cmd, arg, response); // Set status for other errors if ((response & R1_ERASE_RESET) || (response & R1_ERASE_SEQUENCE_ERROR)) { status = SD_BLOCK_DEVICE_ERROR_ERASE; // Erase error } else if ((response & R1_ADDRESS_ERROR) || (response & R1_PARAMETER_ERROR)) { // Misaligned address / invalid address block length status = SD_BLOCK_DEVICE_ERROR_PARAMETER; } // Get rest of the response part for other commands switch (cmd) { case CMD8_SEND_IF_COND: // Response R7 FF_PRINTF("V2-Version Card\n"); _card_type = SDCARD_V2; // fallthrough // Note: No break here, need to read rest of the response case CMD58_READ_OCR: // Response R3 response = (spi_write(SPI_FILL_CHAR) << 24); response |= (spi_write(SPI_FILL_CHAR) << 16); response |= (spi_write(SPI_FILL_CHAR) << 8); response |= spi_write(SPI_FILL_CHAR); // FF_PRINTF("R3/R7: 0x%" PRIx32 "\n", response); break; case CMD12_STOP_TRANSMISSION: // Response R1b case CMD38_ERASE: fs_wait_ready(SD_COMMAND_TIMEOUT); break; case ACMD13_SD_STATUS: // Response R2 response = spi_write(SPI_FILL_CHAR); FF_PRINTF("R2: 0x%" PRIx32 "\n", response); break; default: // Response R1 break; } // Pass the updated response to the command if (NULL != resp) { *resp = response; } // Do not deselect card if read is in progress. if (((CMD9_SEND_CSD == cmd) || (ACMD22_SEND_NUM_WR_BLOCKS == cmd) || (CMD24_WRITE_BLOCK == cmd) || (CMD25_WRITE_MULTIPLE_BLOCK == cmd) || (CMD17_READ_SINGLE_BLOCK == cmd) || (CMD18_READ_MULTIPLE_BLOCK == cmd)) && (BD_ERROR_OK == status)) { return BD_ERROR_OK; } // Deselect card fs_deselect(); return status; } /* Return non-zero if the SD-card is present. The parameter 'pxDisk' may be null, unless device locking is necessary. */ BaseType_t FF_SDDiskDetect(FF_Disk_t *pxDisk) { (void) pxDisk; /*!< Check GPIO to detect SD */ if (Cy_GPIO_Read( configSD_DETECT_GPIO_PORT, configSD_DETECT_PIN) == 0) { // if( Cy_GPIO_Read( P12_5_PORT, P12_5_NUM ) == 0 ) { // /* The card will pull the GPIO signal down. */ // //The socket is now occupied m_Status &= ~STA_NODISK; _card_type = CARD_UNKNOWN; return pdTRUE; } else { /* The internal pull-up makes the signal high. */ //The socket is now empty m_Status |= (STA_NODISK | STA_NOINIT); _card_type = SDCARD_NONE; FF_PRINTF("No SD card detected!\n"); return pdFALSE; } } #if 0 static bool fs_readData(char* buffer, int length) { char token; unsigned short crc; //Wait for up to 500ms for a token to arrive TickType_t xStart = xTaskGetTickCount(); do { token = spi_write(0xFF); }while (token == 0xFF && (xTaskGetTickCount() - xStart) < pdMS_TO_TICKS(500)); //Check if a valid start block token was received if (token != 0xFE) return false; //Check if large frames are enabled or not // if (m_LargeFrames) { // //Switch to 16-bit frames for better performance // spi_writeformat(16, 0); // // //Read the data block into the buffer // unsigned short dataWord; // for (int i = 0; i < length; i += 2) { // dataWord = spi_write(0xFFFF); // buffer[i] = dataWord >> 8; // buffer[i + 1] = dataWord; // } // // //Read the CRC16 checksum for the data block // crc = spi_write(0xFFFF); // // //Switch back to 8-bit frames // m_Spi.format(8, 0); // } else { //Read the data into the buffer for (int i = 0; i < length; i++) buffer[i] = spi_write(0xFF); //Read the CRC16 checksum for the data block crc = (spi_write(0xFF) << 8); crc |= spi_write(0xFF); // } //Return the validity of the CRC16 checksum (if enabled) return (!m_Crc || crc == crc16(buffer, length)); } #endif enum Command { CMD6 = (0x40 | 6) /**< SWITCH_FUNC */ }; #if 0 static bool fs_enableHighSpeedMode() { //Try to issue CMD6 up to 3 times for (int f = 0; f < 3; f++) { //Select the card, and wait for ready fs_select(); if(!fs_wait_ready(SD_COMMAND_TIMEOUT)) break; //Send CMD6(0x80FFFFF1) to change the access mode to high speed if (fs_cmd(CMD6, 0x80FFFFF1, NULL) == 0x00) { //Read the 64B status data block char status[64]; bool success = fs_readData(status, 64); fs_deselect(); if (success) { //Return whether or not the operation was successful return ((status[16] & 0x0F) == 0x1); } } else { //The command failed, get out break; } } //The operation failed 3 times fs_deselect(); return false; } #endif #define SPI_EVENT_ERROR (1 << 1) #define SPI_EVENT_COMPLETE (1 << 2) #define SPI_EVENT_RX_OVERFLOW (1 << 3) #define SPI_EVENT_ALL (SPI_EVENT_ERROR | SPI_EVENT_COMPLETE | SPI_EVENT_RX_OVERFLOW) #define SPI_EVENT_INTERNAL_TRANSFER_COMPLETE (1 << 30) // Internal flag to report that an event occurred #define SPI_FILL_WORD (0xFFFF) #define SPI_FILL_CHAR (0xFF) #define SD_CMD0_GO_IDLE_STATE_RETRIES 5 /*!< Number of retries for sending CMDO */ /** SPI interrupt triggers */ // typedef enum { /** All transfer data has been moved into data FIFO */ CYHAL_SPI_IRQ_DATA_IN_FIFO = 1 << 1, /** Transfer complete. */ CYHAL_SPI_IRQ_DONE = 1 << 2, /** An error occurred while transferring data */ CYHAL_SPI_IRQ_ERROR = 1 << 3, } cyhal_spi_event_t; static bool spi_init() { theSpi.base = SPI_1_HW; theSpi.pin_ssel = SSEL_INACTIVE; theSpi.clock.div_num = SPI_1_SCBCLK_DIV_NUM; theSpi.clock.div_type = SPI_1_SCBCLK_DIV_TYPE; theSpi.is_slave = false; theSpi.oversample_value = SPI_1_config.oversample; theSpi.pContext = &SPI_1_context; theSpi.irq_cause = 0; theSpi.pending = 0; theSpi.rx_buffer = 0; theSpi.rx_buffer_size = 0; theSpi.tx_buffer = 0; theSpi.tx_buffer_size = 0; theSpi.is_async = false; theSpi.callback_data.callback = 0; theSpi.callback_data.callback_arg = 0; default_spi_config = SPI_1_config; cy_en_scb_spi_status_t initStatus; cy_en_sysint_status_t sysSpistatus; /* Configure component */ initStatus = Cy_SCB_SPI_Init(SPI_1_HW, &SPI_1_config, &SPI_1_context); if (initStatus != CY_SCB_SPI_SUCCESS) { FF_PRINTF("FAILED_OPERATION: Cy_SCB_SPI_Init\n"); return false; } /* Set active slave select to line 0 */ Cy_SCB_SPI_SetActiveSlaveSelect(SPI_1_HW, SPI_1_SPI_SLAVE_SELECT0); /* Hook interrupt service routine */ sysSpistatus = Cy_SysInt_Init(&SPI_1_SCB_IRQ_cfg, &SPI_1_Interrupt); if (sysSpistatus != CY_SYSINT_SUCCESS) { FF_PRINTF("FAILED_OPERATION): Cy_SysInt_Init\n"); return false; } /* Enable interrupt in NVIC */ NVIC_EnableIRQ((IRQn_Type) SPI_1_SCB_IRQ_cfg.intrSrc); /* Enable SPI master hardware. */ Cy_SCB_SPI_Enable(SPI_1_HW); // __enable_irq(); /* Enable global interrupts. */ return true; } uint32_t fs_go_idle_state() { uint32_t response; /* Reseting the MCU SPI master may not reset the on-board SDCard, in which * case when MCU power-on occurs the SDCard will resume operations as * though there was no reset. In this scenario the first CMD0 will * not be interpreted as a command and get lost. For some cards retrying * the command overcomes this situation. */ for (int i = 0; i < SD_CMD0_GO_IDLE_STATE_RETRIES; i++) { fs_cmd(CMD0_GO_IDLE_STATE, 0x0, 0x0, &response); if (R1_IDLE_STATE == response) { break; } vTaskDelay(1); } return response; } /* R7 response pattern for CMD8 */ #define CMD8_PATTERN (0xAA) int fs_cmd8() { uint32_t arg = (CMD8_PATTERN << 0); // [7:0]check pattern uint32_t response = 0; int32_t status = BD_ERROR_OK; arg |= (0x1 << 8); // 2.7-3.6V // [11:8]supply voltage(VHS) status = fs_cmd(CMD8_SEND_IF_COND, arg, 0x0, &response); // Verify voltage and pattern for V2 version of card if ((BD_ERROR_OK == status) && (SDCARD_V2 == _card_type)) { // If check pattern is not matched, CMD8 communication is not valid if ((response & 0xFFF) != arg) { FF_PRINTF("CMD8 Pattern mismatch 0x%" PRIx32 " : 0x%" PRIx32 "\n", arg, response); _card_type = CARD_UNKNOWN; status = SD_BLOCK_DEVICE_ERROR_UNUSABLE; } } return status; } int fs_initialise_card() { int32_t status = BD_ERROR_OK; uint32_t response, arg; // The card is transitioned from SDCard mode to SPI mode by sending the CMD0 + CS Asserted("0") if (fs_go_idle_state() != R1_IDLE_STATE) { FF_PRINTF("%s", "No disk, or could not put SD card in to SPI idle state\n"); return SD_BLOCK_DEVICE_ERROR_NO_DEVICE; } // Send CMD8, if the card rejects the command then it's probably using the // legacy protocol, or is a MMC, or just flat-out broken status = fs_cmd8(); if (BD_ERROR_OK != status && SD_BLOCK_DEVICE_ERROR_UNSUPPORTED != status) { return status; } #if MBED_CONF_SD_CRC_ENABLED if (_crc_on) { // Enable CRC status = _cmd(CMD59_CRC_ON_OFF, _crc_on); } #endif // Read OCR - CMD58 Response contains OCR register if (BD_ERROR_OK != (status = fs_cmd(CMD58_READ_OCR, 0x0, 0x0, &response))) { return status; } // Check if card supports voltage range: 3.3V if (!(response & OCR_3_3V)) { _card_type = CARD_UNKNOWN; status = SD_BLOCK_DEVICE_ERROR_UNUSABLE; return status; } // HCS is set 1 for HC/XC capacity cards for ACMD41, if supported arg = 0x0; if (SDCARD_V2 == _card_type) { arg |= OCR_HCS_CCS; } /* Idle state bit in the R1 response of ACMD41 is used by the card to inform the host * if initialization of ACMD41 is completed. "1" indicates that the card is still initializing. * "0" indicates completion of initialization. The host repeatedly issues ACMD41 until * this bit is set to "0". */ TickType_t xStart = xTaskGetTickCount(); do { status = fs_cmd(ACMD41_SD_SEND_OP_COND, arg, 1, &response); } while ((response & R1_IDLE_STATE) && (xTaskGetTickCount() - xStart) < pdMS_TO_TICKS(SD_COMMAND_TIMEOUT)); // Initialization complete: ACMD41 successful if ((BD_ERROR_OK != status) || (0x00 != response)) { _card_type = CARD_UNKNOWN; FF_PRINTF("Timeout waiting for card\n"); return status; } if (SDCARD_V2 == _card_type) { // Get the card capacity CCS: CMD58 if (BD_ERROR_OK == (status = fs_cmd(CMD58_READ_OCR, 0x0, 0x0, &response))) { // High Capacity card if (response & OCR_HCS_CCS) { _card_type = SDCARD_V2HC; FF_PRINTF("Card Initialized: High Capacity Card\n"); } else { FF_PRINTF("%s", "Card Initialized: Standard Capacity Card: Version 2.x\n"); } } } else { _card_type = SDCARD_V1; FF_PRINTF("Card Initialized: Version 1.x Card\n"); } #if MBED_CONF_SD_CRC_ENABLED if (!_crc_on) { // Disable CRC status = _cmd(CMD59_CRC_ON_OFF, _crc_on); } #else status = fs_cmd(CMD59_CRC_ON_OFF, 0, 0, 0); #endif return status; } static uint32_t ext_bits(unsigned char *data, int msb, int lsb) { uint32_t bits = 0; uint32_t size = 1 + msb - lsb; for (uint32_t i = 0; i < size; i++) { uint32_t position = lsb + i; uint32_t byte = 15 - (position >> 3); uint32_t bit = position & 0x7; uint32_t value = (data[byte] >> bit) & 1; bits |= value << i; } return bits; } static int fs_read_bytes(uint8_t *buffer, uint32_t length); static uint64_t fs_sd_sectors() { uint32_t c_size, c_size_mult, read_bl_len; uint32_t block_len, mult, blocknr; uint32_t hc_c_size; uint64_t blocks = 0, capacity = 0; // CMD9, Response R2 (R1 byte + 16-byte block read) if (fs_cmd(CMD9_SEND_CSD, 0x0, 0, 0) != 0x0) { FF_PRINTF("Didn't get a response from the disk\n"); return 0; } uint8_t csd[16]; if (fs_read_bytes(csd, 16) != 0) { FF_PRINTF("Couldn't read csd response from disk\n"); return 0; } // csd_structure : csd[127:126] int csd_structure = ext_bits(csd, 127, 126); switch (csd_structure) { case 0: c_size = ext_bits(csd, 73, 62); // c_size : csd[73:62] c_size_mult = ext_bits(csd, 49, 47); // c_size_mult : csd[49:47] read_bl_len = ext_bits(csd, 83, 80); // read_bl_len : csd[83:80] - the *maximum* read block length block_len = 1 << read_bl_len; // BLOCK_LEN = 2^READ_BL_LEN mult = 1 << (c_size_mult + 2); // MULT = 2^C_SIZE_MULT+2 (C_SIZE_MULT < 8) blocknr = (c_size + 1) * mult; // BLOCKNR = (C_SIZE+1) * MULT capacity = (uint64_t) blocknr * block_len; // memory capacity = BLOCKNR * BLOCK_LEN blocks = capacity / _block_size; FF_PRINTF("Standard Capacity: c_size: %" PRIu32 "\n", c_size); FF_PRINTF("Sectors: 0x%" PRIx64 " : %" PRIu64 "\n", blocks, blocks); FF_PRINTF("Capacity: 0x%" PRIx64 " : %" PRIu64 " MB\n", capacity, (capacity / (1024U * 1024U))); // ERASE_BLK_EN = 1: Erase in multiple of 512 bytes supported if (ext_bits(csd, 46, 46)) { _erase_size = BLOCK_SIZE_HC; } else { // ERASE_BLK_EN = 1: Erase in multiple of SECTOR_SIZE supported _erase_size = BLOCK_SIZE_HC * (ext_bits(csd, 45, 39) + 1); } break; case 1: hc_c_size = ext_bits(csd, 69, 48); // device size : C_SIZE : [69:48] blocks = (hc_c_size + 1) << 10; // block count = C_SIZE+1) * 1K byte (512B is block size) FF_PRINTF("SDHC/SDXC Card: hc_c_size: %" PRIu32 "\n", hc_c_size); FF_PRINTF("Sectors: 0x%" PRIx64 "x : %" PRIu64 "\n", blocks, blocks); FF_PRINTF("Capacity: %" PRIu64 " MB\n", (blocks / (2048U))); // ERASE_BLK_EN is fixed to 1, which means host can erase one or multiple of 512 bytes. _erase_size = BLOCK_SIZE_HC; break; default: FF_PRINTF("CSD struct unsupported\n"); return 0; }; return blocks; } #define EINVAL 22 /* Invalid argument */ static int fs_driver_init() { int err = BD_ERROR_OK; //Initialize the member variables _card_type = SDCARD_NONE; // m_Crc = true; // m_LargeFrames = false; // m_WriteValidation = true; m_Status = STA_NOINIT; //Make sure there's a card in the socket before proceeding FF_SDDiskDetect(0); if (m_Status & STA_NODISK) return m_Status; //Make sure we're not already initialized before proceeding if (!(m_Status & STA_NOINIT)) return m_Status; if (!spi_init()) return m_Status; err = fs_initialise_card(); if (!(err == BD_ERROR_OK)) { FF_PRINTF("Fail to initialize card\n"); // unlock(); return m_Status; } FF_PRINTF("SD card initialized\n"); _sectors = fs_sd_sectors(); // CMD9 failed if (0 == _sectors) { // unlock(); return BD_ERROR_DEVICE_ERROR; } // Set block length to 512 (CMD16) if (fs_cmd(CMD16_SET_BLOCKLEN, _block_size, 0, 0) != 0) { FF_PRINTF("Set %" PRIu32 "-byte block timed out\n", _block_size); // unlock(); return BD_ERROR_DEVICE_ERROR; } // Set SCK for data transfer spi_go_high_frequency(); //The card is now initialized m_Status &= ~STA_NOINIT; //Return the disk status return m_Status; } // SPI function to wait till chip is ready and sends start token bool fs_wait_token(uint8_t token) { TickType_t xStart = xTaskGetTickCount(); do { if (token == spi_write(SPI_FILL_CHAR)) { return true; } } while ((xTaskGetTickCount() - xStart) < pdMS_TO_TICKS(300)); // Wait for 300 msec for start token FF_PRINTF("_wait_token: timeout\n"); return false; } #define SPI_START_BLOCK (0xFE) /*!< For Single Block Read/Write and Multiple Block Read */ static int fs_read_bytes(uint8_t *buffer, uint32_t length) { uint16_t crc; // read until start byte (0xFE) if (false == fs_wait_token(SPI_START_BLOCK)) { FF_PRINTF("Read timeout\n"); fs_deselect(); return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE; } // read data for (uint32_t i = 0; i < length; i++) { buffer[i] = spi_write(SPI_FILL_CHAR); } // Read the CRC16 checksum for the data block crc = (spi_write(SPI_FILL_CHAR) << 8); crc |= spi_write(SPI_FILL_CHAR); #if MBED_CONF_SD_CRC_ENABLED if (_crc_on) { uint32_t crc_result; // Compute and verify checksum _crc16.compute((void *)buffer, length, &crc_result); if ((uint16_t)crc_result != crc) { debug_if(SD_DBG, "_read_bytes: Invalid CRC received 0x%" PRIx16 " result of computation 0x%" PRIx16 "\n", crc, (uint16_t)crc_result); _deselect(); return SD_BLOCK_DEVICE_ERROR_CRC; } } #endif fs_deselect(); return 0; } #if 0 static bool fs_readBlocks(char* buffer, unsigned int lba, unsigned int count) { //Try to read each block up to 3 times for (int f = 0; f < 3;) { //Select the card, and wait for ready fs_select(); if(!fs_wait_ready(SD_COMMAND_TIMEOUT)) break; //Send CMD18(block) to read multiple blocks if (fs_cmd(CMD18, (_card_type == CARD_SDHC) ? lba : lba << 9, NULL) == 0x00) { //Try to read all of the data blocks do { //Read the next block, and break on errors if (!fs_readData(buffer, 512)) { f++; break; } //Update the variables lba++; buffer += 512; f = 0; }while (--count); //Send CMD12(0x00000000) to stop the transmission if (fs_cmd(CMD12, 0x00000000, NULL) != 0x00) { //The command failed, get out break; } //Deselect the card, and return if successful fs_deselect(); if (count == 0) return true; } else { //The command failed, get out break; } } //The multiple block read failed fs_deselect(); return false; } #endif //static int spi_write_buffer(const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length) //{ // spi_select(); // int ret = spi_master_block_write(&_peripheral->spi, tx_buffer, tx_length, rx_buffer, rx_length, _write_fill); // spi_deselect(); // return ret; //} // static int fs_read_block(uint8_t *buffer, uint32_t length) { uint16_t crc; // read until start byte (0xFE) if (false == fs_wait_token(SPI_START_BLOCK)) { FF_PRINTF("Read timeout\n"); return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE; } // read data // bool spi_transfer(const uint8_t *tx, uint8_t *rx, size_t length) spi_transfer(NULL, buffer, length); // Read the CRC16 checksum for the data block crc = (spi_write(SPI_FILL_CHAR) << 8); crc |= spi_write(SPI_FILL_CHAR); #if MBED_CONF_SD_CRC_ENABLED if (_crc_on) { uint32_t crc_result; // Compute and verify checksum _crc16.compute((void *)buffer, length, &crc_result); if ((uint16_t)crc_result != crc) { debug_if(SD_DBG, "_read_bytes: Invalid CRC received 0x%" PRIx16 " result of computation 0x%" PRIx16 "\n", crc, (uint16_t)crc_result); return SD_BLOCK_DEVICE_ERROR_CRC; } } #endif return 0; } static int fs_read_blocks(void *b, uint32_t ulSectorNumber, uint32_t ulSectorCount) { if (ulSectorNumber + ulSectorCount > _sectors) { return SD_BLOCK_DEVICE_ERROR_PARAMETER; } // lock(); // if (!_is_initialized) { // unlock(); // return SD_BLOCK_DEVICE_ERROR_PARAMETER; // } if (m_Status & STA_NOINIT) return SD_BLOCK_DEVICE_ERROR_PARAMETER; uint8_t *buffer = (uint8_t *) b; int status = BD_ERROR_OK; size_t blockCnt = ulSectorCount; // SDSC Card (CCS=0) uses byte unit address // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit) if (SDCARD_V2HC != _card_type) { ulSectorNumber = ulSectorNumber * _block_size; } // Write command ro receive data if (ulSectorCount > 1) { status = fs_cmd(CMD18_READ_MULTIPLE_BLOCK, ulSectorNumber, 0, 0); } else { status = fs_cmd(CMD17_READ_SINGLE_BLOCK, ulSectorNumber, 0, 0); } if (BD_ERROR_OK != status) { // unlock(); return status; } // receive the data : one block at a time while (blockCnt) { if (0 != fs_read_block(buffer, _block_size)) { status = SD_BLOCK_DEVICE_ERROR_NO_RESPONSE; break; } buffer += _block_size; --blockCnt; } fs_deselect(); // Send CMD12(0x00000000) to stop the transmission for multi-block transfer if (ulSectorCount > 1) { status = fs_cmd(CMD12_STOP_TRANSMISSION, 0x0, 0, 0); } // unlock(); return status; } int32_t prvFFRead(uint8_t *pucDestination, /* Destination for data being read. */ uint32_t ulSectorNumber, /* Sector from which to start reading data. */ uint32_t ulSectorCount, /* Number of sectors to read. */ FF_Disk_t *pxDisk) /* Describes the disk being read from. */ { (void) pxDisk; if (fs_read_blocks(pucDestination, ulSectorNumber, ulSectorCount)) { return FF_ERR_NONE; } else { return FF_ERR_DEVICE_DRIVER_FAILED; } } #if 0 static char fs_writeData(const char* buffer, char token) { //Calculate the CRC16 checksum for the data block (if enabled) unsigned short crc = (m_Crc) ? crc16(buffer, 512) : 0xFFFF; //Wait for up to 500ms for the card to become ready if (!fs_wait_ready(500)) return false; //Send the start block token spi_write(token); // //Check if large frames are enabled or not // if (m_LargeFrames) { // //Switch to 16-bit frames for better performance // m_Spi.format(16, 0); // // //Write the data block from the buffer // for (int i = 0; i < 512; i += 2) // m_Spi.write((buffer[i] << 8) | buffer[i + 1]); // // //Send the CRC16 checksum for the data block // m_Spi.write(crc); // // //Switch back to 8-bit frames // m_Spi.format(8, 0); // } else { //Write the data block from the buffer for (int i = 0; i < 512; i++) spi_write(buffer[i]); //Send the CRC16 checksum for the data block spi_write(crc >> 8); spi_write(crc); // } //Return the data response token return (spi_write(0xFF) & 0x1F); } #endif #if 0 static bool fs_writeBlocks(const char* buffer, unsigned int lba, unsigned int count) { char token; const char* currentBuffer = buffer; unsigned int currentLba = lba; int currentCount = count; //Try to write each block up to 3 times for (int f = 0; f < 3;) { //If this is an SD card, send ACMD23(count) to set the number of blocks to pre-erase if (_card_type != CARD_MMC) { if (fs_cmd(ACMD23, currentCount, NULL) != 0x00) { //The command failed, get out break; } } //Select the card, and wait for ready fs_select(); if(!fs_wait_ready(SD_COMMAND_TIMEOUT)) break; //Send CMD25(block) to write multiple blocks if (fs_cmd(CMD25, (_card_type == CARD_SDHC) ? currentLba : currentLba << 9, NULL) == 0x00) { //Try to write all of the data blocks do { //Write the next block and break on errors token = fs_writeData(currentBuffer, 0xFC); if (token != 0x05) { f++; break; } //Update the variables currentBuffer += 512; f = 0; }while (--currentCount); //Wait for up to 500ms for the card to finish processing the last block if (!fs_wait_ready(500)) break; //Finalize the transmission if (currentCount == 0) { //Send the stop tran token, and deselect the card //m_Spi.write(0xFD); spi_write(0xFD); fs_deselect(); //Send CMD13(0x00000000) to verify that the programming was successful if enabled if (m_WriteValidation) { unsigned int resp; if (fs_cmd(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) { //Some manner of unrecoverable write error occured during programming, get out break; } } //The data was written successfully return true; } else { //Send CMD12(0x00000000) to abort the transmission if (fs_cmd(CMD12, 0x00000000, NULL) != 0x00) { //The command failed, get out break; } //Deselect the card fs_deselect(); //Check the error token if (token == 0x0A) { //Determine the number of well written blocks if possible unsigned int writtenBlocks = 0; fs_select(); if (_card_type != CARD_MMC && fs_wait_ready(SD_COMMAND_TIMEOUT)) { //Send ACMD22(0x00000000) to get the number of well written blocks if (fs_cmd(ACMD22, 0x00000000, NULL) == 0x00) { //Read the data char acmdData[4]; if (fs_readData(acmdData, 4)) { //Extract the number of well written blocks writtenBlocks = acmdData[0] << 24; writtenBlocks |= acmdData[1] << 16; writtenBlocks |= acmdData[2] << 8; writtenBlocks |= acmdData[3]; } } fs_deselect(); } //Roll back the variables based on the number of well written blocks currentBuffer = buffer + (writtenBlocks << 9); currentLba = lba + writtenBlocks; currentCount = count - writtenBlocks; //Try again continue; } else { //A write error occured, get out break; } } } else { //The command failed, get out break; } } //The multiple block write failed fs_deselect(); return false; } #endif int spi_write_block(const uint8_t *tx_buffer, size_t length) { spi_select(); // bool spi_transfer(const uint8_t *tx, uint8_t *rx, size_t length) int ret = spi_transfer(tx_buffer, NULL, length); spi_deselect(); return ret; } uint8_t fs_write_block(const uint8_t *buffer, uint8_t token, uint32_t length) { uint32_t crc = (~0); uint8_t response = 0xFF; // indicate start of block spi_write(token); // write the data spi_write_block(buffer, length); #if MBED_CONF_SD_CRC_ENABLED if (_crc_on) { // Compute CRC _crc16.compute((void *)buffer, length, &crc); } #endif // write the checksum CRC16 spi_write(crc >> 8); spi_write(crc); // check the response token response = spi_write(SPI_FILL_CHAR); // Wait for last block to be written if (false == fs_wait_ready(SD_COMMAND_TIMEOUT)) { FF_PRINTF("Card not ready yet \n"); } return (response & SPI_DATA_RESPONSE_MASK); } /** Program blocks to a block device * * @note The blocks must be erased prior to programming * * @param buffer Buffer of data to write to blocks * @param addr Address of block to begin writing to * @param size Size to write in bytes. Must be a multiple of program block size * @return BD_ERROR_OK(0) - success * SD_BLOCK_DEVICE_ERROR_NO_DEVICE - device (SD card) is missing or not connected * SD_BLOCK_DEVICE_ERROR_CRC - crc error * SD_BLOCK_DEVICE_ERROR_PARAMETER - invalid parameter * SD_BLOCK_DEVICE_ERROR_UNSUPPORTED - unsupported command * SD_BLOCK_DEVICE_ERROR_NO_INIT - device is not initialized * SD_BLOCK_DEVICE_ERROR_WRITE - SPI write error * SD_BLOCK_DEVICE_ERROR_ERASE - erase error */ int fs_program(const uint8_t *b, uint32_t ulSectorNumber, uint32_t ulSectorCount) { /** Convenience function for checking block program validity * * @param addr Address of block to begin writing to * @param size Size to write in bytes * @return True if program is valid for underlying block device */ // addr % get_program_size() == 0 && // size % get_program_size() == 0 && // addr + size <= this->size()); if (ulSectorNumber + ulSectorCount > _sectors) { return SD_BLOCK_DEVICE_ERROR_PARAMETER; } // lock(); // if (!_is_initialized) { // unlock(); // return SD_BLOCK_DEVICE_ERROR_NO_INIT; // } if (m_Status & STA_NOINIT) return SD_BLOCK_DEVICE_ERROR_PARAMETER; const uint8_t *buffer = b; int status = BD_ERROR_OK; uint8_t response; // Get block count size_t blockCnt = ulSectorCount; // size / _block_size; // SDSC Card (CCS=0) uses byte unit address // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit) if (SDCARD_V2HC != _card_type) { blockCnt = blockCnt * _block_size; } // Send command to perform write operation if (ulSectorCount == 1) { // Single block write command if (BD_ERROR_OK != (status = fs_cmd(CMD24_WRITE_BLOCK, blockCnt, 0, 0))) { // unlock(); return status; } // Write data response = fs_write_block(buffer, SPI_START_BLOCK, _block_size); // Only CRC and general write error are communicated via response token if (response != SPI_DATA_ACCEPTED) { FF_PRINTF("Single Block Write failed: 0x%x \n", response); status = SD_BLOCK_DEVICE_ERROR_WRITE; } } else { // Pre-erase setting prior to multiple block write operation fs_cmd(ACMD23_SET_WR_BLK_ERASE_COUNT, blockCnt, 1, 0); // Multiple block write command if (BD_ERROR_OK != (status = fs_cmd(CMD25_WRITE_MULTIPLE_BLOCK, ulSectorNumber, 0, 0))) { // unlock(); return status; } // Write the data: one block at a time do { response = fs_write_block(buffer, SPI_START_BLK_MUL_WRITE, _block_size); if (response != SPI_DATA_ACCEPTED) { FF_PRINTF("Multiple Block Write failed: 0x%x\n", response); break; } buffer += _block_size; } while (--blockCnt); // Receive all blocks of data /* In a Multiple Block write operation, the stop transmission will be done by * sending 'Stop Tran' token instead of 'Start Block' token at the beginning * of the next block */ spi_write(SPI_STOP_TRAN); } fs_deselect(); // unlock(); return status; } int32_t FFWrite(uint8_t *pucSource, /* Source of data to be written. */ uint32_t ulSectorNumber, /* The first sector being written to. */ uint32_t ulSectorCount, /* The number of sectors to write. */ FF_Disk_t *pxDisk) /* Describes the disk being written to. */ { (void) pxDisk; int status = fs_program(pucSource, ulSectorNumber, ulSectorCount); if (BD_ERROR_OK == status) { return FF_ERR_NONE; } else { FF_PRINTF("fs_program returned %ux/n", status); return FF_ERR_DEVICE_DRIVER_FAILED; } } FF_Disk_t *FF_SDDiskInit( const char *pcName ) { (void)pcName; FF_Error_t xError = 0; FF_Disk_t *pxDisk = NULL; FF_CreationParameters_t xParameters; const BaseType_t SECTOR_SIZE = 512; const uint32_t xIOManagerCacheSize = 2 * SECTOR_SIZE; /* Check the validity of the xIOManagerCacheSize parameter. */ configASSERT((xIOManagerCacheSize % SECTOR_SIZE) == 0); configASSERT((xIOManagerCacheSize >= (2 * SECTOR_SIZE))); /* Attempt to allocated the FF_Disk_t structure. */ pxDisk = (FF_Disk_t *) pvPortMalloc(sizeof(FF_Disk_t)); if (pxDisk != NULL) { // Initialize the media driver if (fs_driver_init()) { // Couldn't init vPortFree(pxDisk); return NULL; } /* It is advisable to clear the entire structure to zero after it has been allocated – that way the media driver will be compatible with future FreeRTOS+FAT versions, in which the FF_Disk_t structure may include additional members. */ memset(pxDisk, 0, sizeof(FF_Disk_t)); /* The pvTag member of the FF_Disk_t structure allows the structure to be extended to also include media specific parameters. The only media specific data that needs to be stored in the FF_Disk_t structure for a RAM disk is the location of the RAM buffer itself – so this is stored directly in the FF_Disk_t’s pvTag member. */ // pxDisk->pvTag = ( void * ) pucDataBuffer; /* The signature is used by the disk read and disk write functions to ensure the disk being accessed is a RAM disk. */ // pxDisk->ulSignature = ramSIGNATURE; /* The number of sectors is recorded for bounds checking in the read and write functions. */ pxDisk->ulNumberOfSectors = _sectors; /* Create the IO manager that will be used to control the RAM disk – the FF_CreationParameters_t structure completed with the required parameters, then passed into the FF_CreateIOManager() function. */ memset(&xParameters, 0, sizeof xParameters); xParameters.pucCacheMemory = NULL; xParameters.ulMemorySize = xIOManagerCacheSize; xParameters.ulSectorSize = SECTOR_SIZE; xParameters.fnWriteBlocks = FFWrite; xParameters.fnReadBlocks = prvFFRead; xParameters.pxDisk = pxDisk; xParameters.pvSemaphore = (void *) xSemaphoreCreateRecursiveMutex(); xParameters.xBlockDeviceIsReentrant = pdFALSE; pxDisk->pxIOManager = FF_CreateIOManger(&xParameters, &xError); if ((pxDisk->pxIOManager != NULL) && ( FF_isERR( xError ) == pdFALSE)) { /* Record that the RAM disk has been initialised. */ pxDisk->xStatus.bIsInitialised = pdTRUE; } else { /* The disk structure was allocated, but the disk’s IO manager could not be allocated, so free the disk again. */ // FF_RAMDiskDelete( pxDisk ); pxDisk = NULL; FF_PRINTF( "FF_RAMDiskInit: FF_CreateIOManger: %s\n", ( const char * ) FF_GetErrMessage( xError ) ); configASSERT(!"disk's IO manager could not be allocated!"); } } return pxDisk; } #if 0 BaseType_t FF_SDDiskReinit(FF_Disk_t *pxDisk) { (void) pxDisk; return pdFALSE; } /* Unmount the volume */ BaseType_t FF_SDDiskUnmount(FF_Disk_t *pDisk) { (void) pDisk; return pdFALSE; } /* Mount the volume */ BaseType_t FF_SDDiskMount(FF_Disk_t *pDisk) { (void) pDisk; return pdFALSE; } /* Release all resources */ BaseType_t FF_SDDiskDelete(FF_Disk_t *pDisk) { (void) pDisk; return pdFALSE; } #endif #define HUNDRED_64_BIT 100ULL #define SECTOR_SIZE 512UL #define PARTITION_NUMBER 0 /* Only a single partition is used. */ #define BYTES_PER_KB ( 1024ull ) #define SECTORS_PER_KB ( BYTES_PER_KB / 512ull ) /* Show some partition information */ BaseType_t FF_SDDiskShowPartition(FF_Disk_t *pxDisk) { FF_Error_t xError; uint64_t ullFreeSectors; uint32_t ulTotalSizeKB, ulFreeSizeKB; int iPercentageFree; FF_IOManager_t *pxIOManager; const char *pcTypeName = "unknown type"; BaseType_t xReturn = pdPASS; if( pxDisk == NULL ) { xReturn = pdFAIL; } else { pxIOManager = pxDisk->pxIOManager; FF_PRINTF( "Reading FAT and calculating Free Space\n" ); switch( pxIOManager->xPartition.ucType ) { case FF_T_FAT12: pcTypeName = "FAT12"; break; case FF_T_FAT16: pcTypeName = "FAT16"; break; case FF_T_FAT32: pcTypeName = "FAT32"; break; default: pcTypeName = "UNKOWN"; break; } FF_GetFreeSize( pxIOManager, &xError ); ullFreeSectors = pxIOManager->xPartition.ulFreeClusterCount * pxIOManager->xPartition.ulSectorsPerCluster; if( pxIOManager->xPartition.ulDataSectors == ( uint32_t )0 ) { iPercentageFree = 0; } else { iPercentageFree = ( int ) ( ( HUNDRED_64_BIT * ullFreeSectors + pxIOManager->xPartition.ulDataSectors / 2 ) / ( ( uint64_t )pxIOManager->xPartition.ulDataSectors ) ); } ulTotalSizeKB = pxIOManager->xPartition.ulDataSectors / SECTORS_PER_KB; ulFreeSizeKB = ( uint32_t ) ( ullFreeSectors / SECTORS_PER_KB ); /* It is better not to use the 64-bit format such as %Lu because it might not be implemented. */ FF_PRINTF( "Partition Nr %8u\n", pxDisk->xStatus.bPartitionNumber ); FF_PRINTF( "Type %8u (%s)\n", pxIOManager->xPartition.ucType, pcTypeName ); FF_PRINTF( "VolLabel '%8s' \n", pxIOManager->xPartition.pcVolumeLabel ); FF_PRINTF( "TotalSectors %8lu\n", pxIOManager->xPartition.ulTotalSectors ); FF_PRINTF( "SecsPerCluster %8lu\n", pxIOManager->xPartition.ulSectorsPerCluster ); FF_PRINTF( "Size %8lu KB\n", ulTotalSizeKB ); FF_PRINTF( "FreeSize %8lu KB ( %d perc free )\n", ulFreeSizeKB, iPercentageFree ); } return xReturn; } #if 0 /* Flush changes from the driver's buf to disk */ void FF_SDDiskFlush(FF_Disk_t *pDisk) { (void) pDisk; } /* Format a given partition on an SD-card. */ BaseType_t FF_SDDiskFormat(FF_Disk_t *pxDisk, BaseType_t aPart) { (void) pxDisk; (void) aPart; return pdFALSE; } /* Return non-zero if an SD-card is detected in a given slot. */ BaseType_t FF_SDDiskInserted(BaseType_t xDriveNr) { (void) xDriveNr; return pdFALSE; } #endif /*-----------------------------------------------------------*/