From 4aa44ee0e0eb99707a0aa8d407444eb8c539a694 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Sun, 18 Jun 2017 18:40:40 +0200 Subject: [PATCH] added SDIO lib files --- STM32F4/libraries/SDIO/SdioF4.cpp | 801 ++++++++++++++++++++++++++++++ STM32F4/libraries/SDIO/SdioF4.h | 7 + 2 files changed, 808 insertions(+) create mode 100644 STM32F4/libraries/SDIO/SdioF4.cpp create mode 100644 STM32F4/libraries/SDIO/SdioF4.h diff --git a/STM32F4/libraries/SDIO/SdioF4.cpp b/STM32F4/libraries/SDIO/SdioF4.cpp new file mode 100644 index 0000000..1d533a0 --- /dev/null +++ b/STM32F4/libraries/SDIO/SdioF4.cpp @@ -0,0 +1,801 @@ +/* Arduino SdCard Library + * Copyright (C) 2016 by William Greiman + * + * This file is part of the Arduino SdSpiCard Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdSpiCard Library. If not, see + * . + */ + +#include "SdioF4.h" +#include +#include +#include + +#define USE_DEBUG_MODE 0 + +//============================================================================== +//#define SDHC_PROCTL_DTW_4BIT 0x01 +#define CMD8_RETRIES 10 +#define BUSY_TIMEOUT_MILLIS 500 +//============================================================================== +#define CMD_RESP_NONE SDIO_CMD_WAIT_NO_RESP +#define CMD_RESP_R1 SDIO_CMD_WAIT_SHORT_RESP // normal response +#define CMD_RESP_R1b SDIO_CMD_WAIT_SHORT_RESP // normal response + busy data line (optional) +#define CMD_RESP_R2 SDIO_CMD_WAIT_LONG_RESP // CID, CSD +#define CMD_RESP_R3 SDIO_CMD_WAIT_SHORT_RESP // OCR register, response to ACMD41 +#define CMD_RESP_R6 SDIO_CMD_WAIT_SHORT_RESP // published RCA response, response to CMD3 +#define CMD_RESP_R7 SDIO_CMD_WAIT_SHORT_RESP // response to CMD8 + +#define CMD0_XFERTYP (uint16_t)( CMD0 | CMD_RESP_NONE ) +#define CMD2_XFERTYP (uint16_t)( CMD2 | CMD_RESP_R2 ) +#define CMD3_XFERTYP (uint16_t)( CMD3 | CMD_RESP_R6 ) +#define CMD6_XFERTYP (uint16_t)( CMD6 | CMD_RESP_R1 ) +#define ACMD6_XFERTYP (uint16_t)( ACMD6 | CMD_RESP_R1 ) +#define CMD7_XFERTYP (uint16_t)( CMD7 | CMD_RESP_R1b ) +#define CMD8_XFERTYP (uint16_t)( CMD8 | CMD_RESP_R7 ) +#define CMD9_XFERTYP (uint16_t)( CMD9 | CMD_RESP_R2 ) +#define CMD10_XFERTYP (uint16_t)( CMD10 | CMD_RESP_R2 ) +#define CMD12_XFERTYP (uint16_t)( CMD12 | CMD_RESP_R1b ) +#define CMD13_XFERTYP (uint16_t)( CMD13 | CMD_RESP_R1 ) +#define CMD17_XFERTYP (uint16_t)( CMD17 | CMD_RESP_R1 ) +#define CMD18_XFERTYP (uint16_t)( CMD18 | CMD_RESP_R1 ) +#define ACMD23_XFERTYP (uint16_t)( ACMD23 | CMD_RESP_R1 ) +#define CMD24_XFERTYP (uint16_t)( CMD24 | CMD_RESP_R1 ) +#define CMD25_XFERTYP (uint16_t)( CMD25 | CMD_RESP_R1 ) +#define CMD32_XFERTYP (uint16_t)( CMD32 | CMD_RESP_R1 ) +#define CMD33_XFERTYP (uint16_t)( CMD32 | CMD_RESP_R1 ) +#define CMD38_XFERTYP (uint16_t)( CMD38 | CMD_RESP_R1b ) +#define ACMD41_XFERTYP (uint16_t)( ACMD41 | CMD_RESP_R3 ) + +#define CMD55_XFERTYP (uint16_t)( CMD55 | CMD_RESP_R1 ) + +//============================================================================= +//static void enableGPIO(bool enable); +//static void enableDmaIrs(); +static void initSDHC(void); +static bool isBusyCMD13(void); +static bool isBusyTransferComplete(void); +//static bool isBusyCommandComplete(); +//static bool isBusyCommandInhibit(); +static bool readReg16(uint32_t xfertyp, void* data); +//static void setSdclk(uint32_t kHzMax); +static bool yieldTimeout(bool (*fcn)(void)); +static bool waitDmaStatus(void); +static bool waitTimeout(bool (*fcn)(void)); +//----------------------------------------------------------------------------- +#define TRX_RD 0 +#define TRX_WR 1 +static uint8_t m_dir = TRX_RD; +static bool (*m_busyFcn)() = 0; +static bool m_initDone = false; +static uint32_t m_lba; // for raw non-DMA read(write)Start, read(write)Data, read(write)Stop +static uint32_t m_cnt; // for raw non-DMA read(write)Start, read(write)Data, read(write)Stop +static bool m_version2; +static bool m_highCapacity; +static uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED; +static uint32_t m_errorLine = 0; +static uint32_t m_rca; +//static volatile bool m_dmaBusy = false; +static volatile uint32_t m_irqstat; +static uint32_t m_sdClkKhz = 0; +static uint32_t m_ocr; +static cid_t m_cid; +static csd_t m_csd; +static uint32_t t = 0; +//============================================================================= +#if USE_DEBUG_MODE +#define DBG_PRINT() { \ + Serial.write('_'); Serial.print(__FUNCTION__); Serial.write('_'); Serial.print(__LINE__); Serial.print(": "); \ + Serial.print("DMA->LISR: "); Serial.print(DMA2->regs->LISR, HEX); \ + /*Serial.print("DMA->HISR: "); Serial.println(DMA2->regs->HISR, HEX);*/ \ + Serial.print(", DMA->CR: "); Serial.print(DMA2->regs->STREAM[DMA_STREAM3].CR, HEX); \ + Serial.print(", DMA->NDTR: "); Serial.print(DMA2->regs->STREAM[DMA_STREAM3].NDTR, HEX); \ + /**/Serial.print(", DMA->PAR: "); Serial.print(DMA2->regs->STREAM[DMA_STREAM3].PAR, HEX); \ + /**/Serial.print(", DMA->M0AR: "); Serial.print(DMA2->regs->STREAM[DMA_STREAM3].M0AR, HEX); \ + Serial.print(", DMA->FCR: "); Serial.print(DMA2->regs->STREAM[DMA_STREAM3].FCR, HEX); \ + \ + /*Serial.print(" SDIO->POWER: "); Serial.println(SDIO->POWER, HEX);*/ \ + Serial.print(", SDIO->CLKCR: "); Serial.print(SDIO->CLKCR, HEX); \ + Serial.print(", SDIO->DTIMER: "); Serial.print(SDIO->DTIMER, HEX); \ + Serial.print(", SDIO->DCTRL: "); Serial.print(SDIO->DCTRL, HEX); \ + /**/Serial.print(", SDIO->DLEN: "); Serial.print(SDIO->DLEN); \ + Serial.print(", SDIO->DCOUNT: "); Serial.print(SDIO->DCOUNT); \ + Serial.print(", SDIO->STA: "); Serial.println(SDIO->STA, HEX); \ + /*delay(1);*/ \ +} +#define DBG_PIN PD0 + +#else // USE_DEBUG_MODE +#define DBG_PRINT() +#endif // USE_DEBUG_MODE + +/*****************************************************************************/ +static void _panic(const char *message, uint32_t code) +{ + Serial.print(message); Serial.println(code, HEX); + //Block the execution with blinky leds + pinMode(BOARD_LED_PIN, OUTPUT); + pinMode(BOARD_LED2_PIN, OUTPUT); + while (1) { + digitalWrite(BOARD_LED_PIN, HIGH); + digitalWrite(BOARD_LED2_PIN, LOW); + delay(250); + digitalWrite(BOARD_LED_PIN, LOW); + digitalWrite(BOARD_LED2_PIN, HIGH); + delay(250); + } +} +/*=========================================================================== +void yield(void) +{ + uint32_t val = SDIO->STA; + if ( val & SDIO_STA_TRX_ERROR_FLAGS ) { + Serial.print("SDIO ERROR:: SDIO->CLKCR: "); Serial.print(SDIO->CLKCR, HEX); \ + Serial.print(", SDIO->DTIMER: "); Serial.print(SDIO->DTIMER, HEX); \ + Serial.print(", SDIO->DCTRL: "); Serial.print(SDIO->DCTRL, HEX); \ + Serial.print(", SDIO->DLEN: "); Serial.print(SDIO->DLEN); \ + Serial.print(", SDIO->DCOUNT: "); Serial.print(SDIO->DCOUNT); \ + Serial.print(", SDIO->STA: "); Serial.print(SDIO->STA, HEX); \ + Serial.print(", SDIO->RESP0: "); Serial.println(SDIO->RESP[0], HEX); \ + if (val & SDIO_STA_STBITERR) Serial.print(" STBITERR"); + if (val & SDIO_STA_RXOVERR) Serial.print(" RXOVERR"); + if (val & SDIO_STA_TXUNDERR) Serial.print(" TXUNDERR"); + if (val & SDIO_STA_DTIMEOUT) Serial.print(" DTIMEOUT"); + if (val & SDIO_STA_DCRCFAIL) Serial.print(" DCRCFAIL"); + _panic(" - SDIO: Data Transmission Error ", val); + } + + val = dma_get_isr_bit(DMA2, DMA_STREAM3, DMA_ISR_ERROR_BITS); + if ( val & DMA_ISR_FEIF ) { + val ^= DMA_ISR_FEIF; + dma_clear_isr_bit(DMA2, DMA_STREAM3, DMA_ISR_FEIF); + } + if ( val ) { + if (val & DMA_ISR_TEIF) Serial.print(" TEIF"); + if (val & DMA_ISR_DMEIF) Serial.print(" DMEIF"); + //if (val & DMA_ISR_FEIF) Serial.print(" FEIF"); + _panic(" - DMA: Data Transmission Error ", val); + } + //Serial.write('.'); +}*/ +//============================================================================= +// Error function and macro. +inline bool setSdErrorCode(uint8_t code, uint32_t line) { + m_errorCode = code; + m_errorLine = line; + return false; // setSdErrorCode +} +#define sdError(code) setSdErrorCode(code, __LINE__) +//============================================================================= +/* ISR +void sdhc_isr() { + SDHC_IRQSIGEN = 0; + m_irqstat = SDHC_IRQSTAT; + SDHC_IRQSTAT = m_irqstat; + m_dmaBusy = false; +}*/ +//============================================================================= +// Static functions. +//----------------------------------------------------------------------------- +static bool cardCommand(uint16_t xfertyp, uint32_t arg) +{ +#if USE_DEBUG_MODE + Serial.print("cardCommand: "); Serial.print(xfertyp&SDIO_CMD_CMDINDEX); Serial.print(", arg: "); Serial.print(arg, HEX); +#endif + uint8_t resp = sdio_cmd_send(xfertyp, arg); // returns non-zero if fails, zero if OK +#if USE_DEBUG_MODE + Serial.print(", resp: "); Serial.print(resp, HEX); + Serial.print(", SDIO->STA: "); Serial.print(SDIO->STA, HEX); Serial.print(", cmd_resp: "); Serial.print(SDIO->RESP[0], HEX); + if ( (xfertyp&SDIO_CMD_WAIT_LONG_RESP)==SDIO_CMD_WAIT_LONG_RESP ) { + Serial.print(", "); Serial.print(SDIO->RESP[1], HEX); Serial.print(", "); Serial.print(SDIO->RESP[2], HEX); Serial.print(", "); Serial.println(SDIO->RESP[3], HEX); + } else Serial.println(); +#endif + return resp; // return non-zero when OK +} +//----------------------------------------------------------------------------- +static bool cardAcmd(uint32_t rca, uint32_t xfertyp, uint32_t arg) { + return cardCommand(CMD55_XFERTYP, rca) && cardCommand((uint16_t)xfertyp, arg); +} +/*---------------------------------------------------------------------------*/ +static bool cardCMD6(uint32_t arg, uint8_t* status) { + // CMD6 returns 64 bytes. + // use polling only for the moment + if (waitTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_CMD13); + } + // get the 64 bytes over data lines + sdio_setup_transfer(80000, 64, (SDIO_BLOCKSIZE_64 | SDIO_DIR_RX | SDIO_DCTRL_DTEN)); + + cardCommand(CMD6_XFERTYP, arg); + + // wait data Rx response + if (waitTimeout(isBusyTransferComplete)) { + return sdError(SD_CARD_ERROR_CMD6); + } +DBG_PRINT(); + // copy 64 bytes as 16 words from FIFO to buffer + for (uint8_t i=0; i<16; i++) { + *(uint32_t*)(&status[i<<2]) = SDIO->FIFO; + } + //SDIO->DCTRL = 0; // disable data controller + +#if USE_DEBUG_MODE + Serial.print("read data: "); for (uint8_t i=0; i<17; i++) { Serial.print(status[i], HEX); Serial.print(", "); } Serial.println(); +#endif +return true; +} +//----------------------------------------------------------------------------- +static void initSDHC(void) +{ + sdio_begin(); + DBG_PRINT(); +} +/*---------------------------------------------------------------------------*/ +static bool isBusyCMD13(void) { + if (!cardCommand(CMD13_XFERTYP, m_rca)) { // SEND_STATUS + // Caller will timeout. + return true; + } + return !(SDIO->RESP[0] & CARD_STATUS_READY_FOR_DATA); +} +/*---------------------------------------------------------------------------*/ +static bool isBusyDMA(void) +{ + uint8_t isr = dma_get_isr_bit(DMA2, DMA_STREAM3, (DMA_ISR_TCIF | DMA_ISR_ERROR_BITS)); + //if (isr&DMA_ISR_TCIF) dma_disable(DMA2, DMA_STREAM3); + return !(isr^DMA_ISR_FEIF); // ignore transfer error flag +} +/*---------------------------------------------------------------------------*/ +static bool isBusyTransferComplete(void) +{ + uint32_t mask = SDIO->STA &(SDIO_STA_DATAEND | SDIO_STA_TRX_ERROR_FLAGS); +#if USE_DEBUG_MODE + if ( mask & SDIO_STA_TRX_ERROR_FLAGS ) { + Serial.print("XFER ERROR: SDIO->STA: "); Serial.print(SDIO->STA, HEX); + if (mask & SDIO_STA_STBITERR) Serial.print(" STBITERR"); + if (mask & SDIO_STA_RXOVERR) Serial.print(" RXOVERR"); + if (mask & SDIO_STA_TXUNDERR) Serial.print(" TXUNDERR"); + if (mask & SDIO_STA_DTIMEOUT) Serial.print(" DTIMEOUT"); + if (mask & SDIO_STA_DCRCFAIL) Serial.print(" DCRCFAIL"); + Serial.println(); + } +#endif + if (mask) { + dma_disable(DMA2, DMA_STREAM3); + return false; + } + return true; +} +/*---------------------------------------------------------------------------*/ +static void trxStart(uint8_t* buf, uint32_t n, uint8_t dir) +{ + m_dir = dir; + uint32_t flags = (SDIO_BLOCKSIZE_512 | SDIO_DCTRL_DTEN); + if (dir==TRX_RD) flags |= SDIO_DIR_RX; + // setup SDIO to transfer n blocks of 512 bytes + sdio_setup_transfer(0x00FFFFFF, n, flags); +} +/*---------------------------------------------------------------------------*/ +static bool trxStop() +{ + if (!cardCommand(CMD12_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD12); + } + if ( t ) { + Serial.print(", in "); Serial.println(millis()-t); + t = 0; + } + return true; +} +/*---------------------------------------------------------------------------*/ +static bool dmaTrxStart(uint8_t* buf, uint32_t n, uint8_t dir) +{ + m_dir = dir; + if ((3 & (uint32_t)buf) || n == 0) { // check alignment + _panic("- transferStart: unaligned buffer address ", (uint32_t)buf); + return sdError(SD_CARD_ERROR_DMA); + } + if (dir==TRX_RD && yieldTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_CMD13); + } + uint32_t flags = (SDIO_BLOCKSIZE_512 | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN); + if (dir==TRX_RD) flags |= SDIO_DIR_RX; + // setup SDIO to transfer n blocks of 512 bytes + sdio_setup_transfer(0x00FFFFFF, n, flags); + // setup DMA2 stream 3 channel 4 + dma_init(DMA2); + flags = (DMA_PERIF_CTRL | DMA_BURST_INCR4 | DMA_MINC_MODE | DMA_PRIO_VERY_HIGH); + if (dir==TRX_RD) flags |= DMA_FROM_PER; // read + else flags |= DMA_FROM_MEM; // write + dma_setup_transfer(DMA2, DMA_STREAM3, DMA_CH4, DMA_SIZE_32BITS, &SDIO->FIFO, buf, NULL, flags); + dma_set_num_transfers(DMA2, DMA_STREAM3, n); + dma_set_fifo_flags(DMA2, DMA_STREAM3, (DMA_FCR_DMDIS | DMA_FCR_FTH_FULL)); // disable direct mode | threshold FULL + dma_clear_isr_bits(DMA2, DMA_STREAM3); + dma_enable(DMA2, DMA_STREAM3); + return true; +} +/*---------------------------------------------------------------------------*/ +static bool dmaTrxEnd(bool multi_block) +{ + if ( !waitDmaStatus() ) { + return sdError(SD_CARD_ERROR_DMA); + } + if ( waitTimeout(isBusyTransferComplete) ) { + if (m_dir==TRX_RD) + return sdError(SD_CARD_ERROR_READ_TIMEOUT); + else + return sdError(SD_CARD_ERROR_WRITE_TIMEOUT); + } + if (multi_block) { + return trxStop(); + } else { + if ( t ) { + Serial.print(", in "); Serial.println(millis()-t); + t = 0; + } + return true; + } +} +//----------------------------------------------------------------------------- +// Read 16 byte CID or CSD register. +static bool readReg16(uint32_t xfertyp, void* data) +{ + uint8_t* d = reinterpret_cast(data); + if (!cardCommand(xfertyp, m_rca)) { + return false; // Caller will set errorCode. + } + *(uint32_t*)(&d[0]) = __builtin_bswap32(SDIO->RESP[0]); + *(uint32_t*)(&d[4]) = __builtin_bswap32(SDIO->RESP[1]); + *(uint32_t*)(&d[8]) = __builtin_bswap32(SDIO->RESP[2]); + *(uint32_t*)(&d[12]) = __builtin_bswap32(SDIO->RESP[3]); + d[15] = 0; + return true; +} +/*---------------------------------------------------------------------------*/ +// Return true if timeout occurs. +static bool yieldTimeout(bool (*fcn)()) { + uint32_t m = millis(); + while (fcn()) { + if ((millis() - m) > BUSY_TIMEOUT_MILLIS) { + return true; + } + yield(); + } + return false; // Caller will set errorCode. +} +/*---------------------------------------------------------------------------*/ +static bool waitDmaStatus(void) +{ + if (yieldTimeout(isBusyDMA)) { + return false; // Caller will set errorCode. + } + return true; +} +/*---------------------------------------------------------------------------*/ +// Return true if timeout occurs. +static bool waitTimeout(bool (*fcn)(void)) { + uint32_t m = millis(); + while (fcn()) { + if ((millis() - m) > BUSY_TIMEOUT_MILLIS) { + return true; + } + delayMicroseconds(1); + } + return false; // Caller will set errorCode. +} + +uint32_t aligned[128]; // temporary buffer for misaligned buffers +//============================================================================= +bool SdioCard::begin(void) +{ + m_initDone = false; + m_errorCode = SD_CARD_ERROR_NONE; + m_highCapacity = false; + m_version2 = false; + +#if USE_DEBUG_MODE +pinMode(DBG_PIN, OUTPUT); +digitalWrite(DBG_PIN, HIGH); +delay(100); +#endif + // initialize controller. + initSDHC(); + + if (!cardCommand(CMD0_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD0); + } + // Try several times for case of reset delay. + for (uint32_t i = 0; i < CMD8_RETRIES; i++) { + if (cardCommand(CMD8_XFERTYP, 0X1AA)) { + if (SDIO->RESP[0] != 0X1AA) { + return sdError(SD_CARD_ERROR_CMD8); + } + m_version2 = true; + break; + } + } + uint32_t arg = m_version2 ? 0X40300000 : 0x00300000; + uint32_t m = millis(); + do { + if (!cardAcmd(0, ACMD41_XFERTYP, arg) || + ((millis() - m) > BUSY_TIMEOUT_MILLIS)) { + return sdError(SD_CARD_ERROR_ACMD41); + } + } while ((SDIO->RESP[0] & 0x80000000) == 0); + + m_ocr = SDIO->RESP[0]; + if (m_ocr & 0x40000000) { + // Is high capacity. + m_highCapacity = true; + } + if (!cardCommand(CMD2_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD2); + } + if (!cardCommand(CMD3_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD3); + } + m_rca = SDIO->RESP[0] & 0xFFFF0000; + if (!readReg16(CMD9_XFERTYP, &m_csd)) { + return sdError(SD_CARD_ERROR_CMD9); + } + if (!readReg16(CMD10_XFERTYP, &m_cid)) { + return sdError(SD_CARD_ERROR_CMD10); + } + if (!cardCommand(CMD7_XFERTYP, m_rca)) { + return sdError(SD_CARD_ERROR_CMD7); + } + // Set card to bus width four. + if (!cardAcmd(m_rca, ACMD6_XFERTYP, 2)) { + return sdError(SD_CARD_ERROR_ACMD6); + } + sdio_set_dbus_width(SDIO_CLKCR_WIDBUS_4BIT); + + // Determine if High Speed mode is supported and set frequency. + uint8_t status[64]; + // see "Physical Layer Simplified Specification Version 6.00", chapter 4.3.10, Table 4-13. + // Support Bits of Functions in Function Group 1: bits 415:400, which are bytes [12][13] + // Function Selection of Function Group 1: bits 379:376, which is low nibble of byte [16] + if (cardCMD6(0X00FFFFFF, status) && (2 & status[13]) && + cardCMD6(0X80FFFFF1, status) && (status[16] & 0XF) == 1) { + //Serial.println("\n*** 50MHz clock supported ***"); + } else { + _panic("*** Only 25MHz clock supported! ***", 0); + } + + m_sdClkKhz = 24000; + sdio_set_clock(m_sdClkKhz*1000); // set clock to 24MHz + + m_initDone = true; + return true; +} +//----------------------------------------------------------------------------- +uint32_t SdioCard::cardSize(void) { + return sdCardCapacity(&m_csd); +} +/*---------------------------------------------------------------------------*/ +bool SdioCard::erase(uint32_t firstBlock, uint32_t lastBlock) { + // check for single block erase + if (!m_csd.v1.erase_blk_en) { + // erase size mask + uint8_t m = (m_csd.v1.sector_size_high << 1) | m_csd.v1.sector_size_low; + if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) { + // error card can't erase specified area + return sdError(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); + } + } + if (!m_highCapacity) { + firstBlock <<= 9; + lastBlock <<= 9; + } + if (!cardCommand(CMD32_XFERTYP, firstBlock)) { + return sdError(SD_CARD_ERROR_CMD32); + } + if (!cardCommand(CMD33_XFERTYP, lastBlock)) { + return sdError(SD_CARD_ERROR_CMD33); + } + if (!cardCommand(CMD38_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD38); + } + if (waitTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_ERASE_TIMEOUT); + } + return true; +} +//----------------------------------------------------------------------------- +uint8_t SdioCard::errorCode() { + return m_errorCode; +} +//----------------------------------------------------------------------------- +uint32_t SdioCard::errorData() { + return m_irqstat; +} +//----------------------------------------------------------------------------- +uint32_t SdioCard::errorLine() { + return m_errorLine; +} +//----------------------------------------------------------------------------- +bool SdioCard::isBusy() { + return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13(); +} +//----------------------------------------------------------------------------- +uint32_t SdioCard::kHzSdClk() { + return m_sdClkKhz; +} +/*---------------------------------------------------------------------------*/ +bool SdioCard::readBlock(uint32_t lba, uint8_t* buf) +{ + // prepare SDIO and DMA for data read transfer + dmaTrxStart((uint32_t)buf & 3 ? (uint8_t*)aligned : buf, 512, TRX_RD); + // send command to start data transfer + if ( !cardCommand(CMD17_XFERTYP, (m_highCapacity ? lba : 512*lba)) ) { + return sdError(SD_CARD_ERROR_CMD17); + } + if ( dmaTrxEnd(0)) { + if ( (uint32_t)buf & 3 ) { + //memcpy(buf, aligned, 512); + register uint8_t * dst = buf; + register uint8_t * src = (uint8_t *)aligned; + register uint16_t i = 64; + while ( i-- ) { // do 8 byte copies, is much faster than single byte copy + *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; + *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; + } + } + return true; + } + return false; +} +/*---------------------------------------------------------------------------*/ +bool SdioCard::readBlocks(uint32_t lba, uint8_t* buf, size_t n) +{ + if ((uint32_t)buf & 3) { + for (size_t i = 0; i < n; i++, lba++, buf += 512) { + if (!readBlock(lba, buf)) { + return false; // readBlock will set errorCode. + } + } + return true; + } + // prepare SDIO and DMA for data read transfer + dmaTrxStart(buf, 512*n, TRX_RD); + // send command to start data transfer + if ( !cardCommand(CMD18_XFERTYP, (m_highCapacity ? lba : 512*lba)) ) { + return sdError(SD_CARD_ERROR_CMD18); + } + return dmaTrxEnd(1); +} +//----------------------------------------------------------------------------- +bool SdioCard::readCID(void* cid) { + memcpy(cid, &m_cid, 16); + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::readCSD(void* csd) { + memcpy(csd, &m_csd, 16); + return true; +} +/*---------------------------------------------------------------------------*/ +bool SdioCard::readData(uint8_t *dst) +{ + //Serial.print("readData: "); Serial.print(m_lba); Serial.print(", m_cnt: "); Serial.println(m_cnt); + if ( m_cnt==0 ) return false; + if (yieldTimeout(isBusyCMD13)) { // wait for previous transmission end + return sdError(SD_CARD_ERROR_CMD13); + } + // non-DMA block read + trxStart(dst, 512, TRX_RD); + // send command to start data transfer + if ( !cardCommand(CMD17_XFERTYP, (m_highCapacity ? m_lba : 512*m_lba)) ) { + return sdError(SD_CARD_ERROR_CMD17); + } + // Receive a data block from the SDIO + register uint32_t STA; // to speed up SDIO flags checking + register uint16_t cnt = 512; + register uint32_t * ptr = (uint32_t *)dst; + // ----> TIME CRITICAL SECTION BEGIN <---- + do { + STA = SDIO->STA; + if (STA & SDIO_STA_RXFIFOHF) { + // Receive FIFO half full, there are at least 8 words in it + noInterrupts(); + *ptr++ = SDIO->FIFO; *ptr++ = SDIO->FIFO; *ptr++ = SDIO->FIFO; *ptr++ = SDIO->FIFO; + *ptr++ = SDIO->FIFO; *ptr++ = SDIO->FIFO; *ptr++ = SDIO->FIFO; *ptr++ = SDIO->FIFO; + interrupts(); + cnt -= 8; + } + } while ( !(STA & (SDIO_STA_DATAEND | SDIO_STA_TRX_ERROR_FLAGS)) ); + // <---- TIME CRITICAL SECTION END ----> + + // read data still available in FIFO + while ( (SDIO->STA & SDIO_STA_RXDAVL) && (cnt--) ) { + *ptr++ = SDIO->FIFO; + } + // check error, temporary stuff, remove for final version + if ( SDIO->STA & SDIO_STA_TRX_ERROR_FLAGS ) { + _panic("ERROR: non-DMA read error ", SDIO->STA); + return false; + } + m_lba++; + m_cnt--; + return !(SDIO->STA&SDIO_STA_TRX_ERROR_FLAGS); +} +//----------------------------------------------------------------------------- +bool SdioCard::readOCR(uint32_t* ocr) { + *ocr = m_ocr; + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::readStart(uint32_t lba) +{ + m_lba = lba; + m_cnt = 1024; + return true; +} +/*---------------------------------------------------------------------------*/ +// SDHC will do Auto CMD12 after count blocks. +bool SdioCard::readStart(uint32_t lba, uint32_t count) +{ + //Serial.print("readStart: "); Serial.print(lba); Serial.print(", cnt: "); Serial.println(count); + m_lba = lba; + m_cnt = count; + return true; +} +/*---------------------------------------------------------------------------*/ +bool SdioCard::readStop() +{ + //Serial.println("readStop."); + m_lba = 0; + m_cnt = 0; + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::syncBlocks() { + return true; +} +//----------------------------------------------------------------------------- +uint8_t SdioCard::type() { + return m_version2 ? m_highCapacity ? + SD_CARD_TYPE_SDHC : SD_CARD_TYPE_SD2 : SD_CARD_TYPE_SD1; +} +/*---------------------------------------------------------------------------*/ +bool SdioCard::writeBlock(uint32_t lba, const uint8_t* buf) +{ + uint8_t * ptr = (uint8_t *)buf; + if (3 & (uint32_t)ptr) { + //Serial.print("writeBlock: "); Serial.print(lba); + Serial.print(", buf: "); Serial.print((uint32_t)ptr, HEX); + //memcpy(aligned, buf, 512); + register uint8_t * src = (uint8_t *)ptr; + ptr = (uint8_t *)aligned; + register uint8_t * dst = ptr; + register uint16_t i = 64; + while ( i-- ) { // do 8 byte copies, is much faster than single byte copy + *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; + *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; + } + } + if (yieldTimeout(isBusyCMD13)) { // wait for previous transmission end + return sdError(SD_CARD_ERROR_CMD13); + } + // send command to start data transfer + if ( !cardCommand(CMD24_XFERTYP, (m_highCapacity ? lba : 512*lba)) ) { + return sdError(SD_CARD_ERROR_CMD24); + } + // prepare SDIO and DMA for data transfer + dmaTrxStart(ptr, 512, TRX_WR); // 1 block, write transfer + + return dmaTrxEnd(0); +} +/*---------------------------------------------------------------------------*/ +bool SdioCard::writeBlocks(uint32_t lba, const uint8_t* buf, size_t n) +{ + if (3 & (uint32_t)buf) { // misaligned buffer address, write single blocks + for (size_t i = 0; i < n; i++, lba++, buf += 512) { + if (!writeBlock(lba, buf)) { + return false; // writeBlock will set errorCode. + } + } + return true; + } + //Serial.print("writeBlocks: "); Serial.print(lba); Serial.print(", "); Serial.print(n); + if (yieldTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_CMD13); + } +#if 0 + // set number of blocks to write - this can speed up block write + if ( !cardAcmd(m_rca, ACMD23_XFERTYP, n) ) { + return sdError(SD_CARD_ERROR_ACMD23); + } +#endif + // send command to start data transfer + if ( !cardCommand(CMD25_XFERTYP, (m_highCapacity ? lba : 512*lba)) ) { + return sdError(SD_CARD_ERROR_CMD25); + } + // prepare SDIO and DMA for data transfer + dmaTrxStart((uint8_t *)buf, 512*n, TRX_WR); // n blocks, write transfer + + return dmaTrxEnd(1); +} +/*---------------------------------------------------------------------------*/ +bool SdioCard::writeData(const uint8_t* src) +{ + //Serial.print("writeData: "); Serial.print(m_lba); Serial.print(", cnt: "); Serial.println(m_cnt); + if ( !m_cnt ) return false; + if (yieldTimeout(isBusyCMD13)) { // wait for previous transmission end + return sdError(SD_CARD_ERROR_CMD13); + } + // send command to start block data transfer + if ( !cardCommand(CMD24_XFERTYP, (m_highCapacity ? m_lba : 512*m_lba)) ) { + return sdError(SD_CARD_ERROR_CMD24); + } + // non-DMA block write + trxStart((uint8_t*)src, 512, TRX_WR); + // Receive a data block from the SDIO + register uint32_t STA; // to speed up SDIO flags checking + register uint16_t cnt = 512; + register uint32_t * ptr = (uint32_t*)src; + // pre-fill up the FIFO with 32 words + noInterrupts(); + while ( (cnt--)>(512-32) ) SDIO->FIFO = *ptr++; + interrupts(); + // ----> TIME CRITICAL SECTION BEGIN <---- + do { + STA = SDIO->STA; + if (STA & SDIO_STA_TXFIFOHE) { + // Transmit FIFO half empty, fill up the remaining 16 words + noInterrupts(); + SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; + SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; + SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; + SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; SDIO->FIFO = *ptr++; + interrupts(); + cnt -= 16; + } + } while ( !(STA & (SDIO_STA_DATAEND | SDIO_STA_TRX_ERROR_FLAGS)) && cnt); + // <---- TIME CRITICAL SECTION END ----> + if ( waitTimeout(isBusyTransferComplete) ) { + return sdError(SD_CARD_ERROR_WRITE_TIMEOUT); + } + m_lba++; + m_cnt--; + if (SDIO->STA&SDIO_STA_TRX_ERROR_FLAGS) { + _panic("writeData error: ", SDIO->STA); + } + return !(SDIO->STA&SDIO_STA_TRX_ERROR_FLAGS); +} +//----------------------------------------------------------------------------- +bool SdioCard::writeStart(uint32_t lba) +{ + m_lba = lba; + m_cnt = 1024; + return true; +} +/*---------------------------------------------------------------------------*/ +// SDHC will do Auto CMD12 after count blocks. +bool SdioCard::writeStart(uint32_t lba, uint32_t count) +{ + //Serial.print("writeStart: "); Serial.print(lba); Serial.print(", cnt: "); Serial.println(count); + m_lba = lba; + m_cnt = count; + return true; +} +/*---------------------------------------------------------------------------*/ +bool SdioCard::writeStop() +{ + //Serial.println("writeStop."); + m_lba = 0; + m_cnt = 0; + return true; +} diff --git a/STM32F4/libraries/SDIO/SdioF4.h b/STM32F4/libraries/SDIO/SdioF4.h new file mode 100644 index 0000000..c995b1c --- /dev/null +++ b/STM32F4/libraries/SDIO/SdioF4.h @@ -0,0 +1,7 @@ + +#ifndef _SDIOF4_H_ +#define _SDIOF4_H_ + +#include + +#endif