| @@ -1,5 +1,5 @@ | |||
| name=SD | |||
| version=1.0.8 | |||
| version=1.2.2 | |||
| author=Arduino, SparkFun | |||
| maintainer=Paul Stoffregen | |||
| sentence=Enables reading and writing on SD cards. This version is optimized for Teensy. | |||
| @@ -9,7 +9,8 @@ | |||
| #if defined(__MK64FX512__) || defined(__MK66FX1M0__) | |||
| #include "kinetis.h" | |||
| #include "core_pins.h" // testing only | |||
| //#include "core_pins.h" // testing only | |||
| //#include "HardwareSerial.h" // testing only | |||
| // Missing in Teensyduino 1.30 | |||
| #ifndef MPU_CESR_VLD_MASK | |||
| @@ -29,6 +30,19 @@ enum { | |||
| SDHC_RESULT_NO_RESPONSE /* 5: No Response */ // from old diskio.h | |||
| }; | |||
| /*void print_result(int n) | |||
| { | |||
| switch (n) { | |||
| case SDHC_RESULT_OK: serial_print("OK\n"); break; | |||
| case SDHC_RESULT_ERROR: serial_print("R/W Error\n"); break; | |||
| case SDHC_RESULT_WRPRT: serial_print("Write Protect\n"); break; | |||
| case SDHC_RESULT_NOT_READY: serial_print("Not Ready\n"); break; | |||
| case SDHC_RESULT_PARERR: serial_print("Invalid Param\n"); break; | |||
| case SDHC_RESULT_NO_RESPONSE: serial_print("No Response\n"); break; | |||
| default: serial_print("Unknown result\n"); | |||
| } | |||
| }*/ | |||
| #define SDHC_STATUS_NOINIT 0x01 /* Drive not initialized */ | |||
| #define SDHC_STATUS_NODISK 0x02 /* No medium in the drive */ | |||
| #define SDHC_STATUS_PROTECT 0x04 /* Write protected */ | |||
| @@ -112,9 +126,13 @@ enum { | |||
| // prescale can be 2, 4, 8, 16, 32, 64, 128, 256 | |||
| // divisor can be 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 | |||
| #define SDHC_SYSCTL_DIVISOR(prescale, divisor) \ | |||
| (SDHC_SYSCTL_DVS((prescale)>>1)|SDHC_SYSCTL_SDCLKFS((divisor)-1)) | |||
| (SDHC_SYSCTL_SDCLKFS((prescale)>>1)|SDHC_SYSCTL_DVS((divisor)-1)) | |||
| #if (F_CPU == 240000000) | |||
| #if (F_CPU == 256000000) | |||
| #define SDHC_SYSCTL_400KHZ SDHC_SYSCTL_DIVISOR(64, 10) // 400 kHz | |||
| #define SDHC_SYSCTL_25MHZ SDHC_SYSCTL_DIVISOR(2, 6) // 21.3 MHz | |||
| #define SDHC_SYSCTL_50MHZ SDHC_SYSCTL_DIVISOR(2, 3) // 42.6 MHz | |||
| #elif (F_CPU == 240000000) | |||
| #define SDHC_SYSCTL_400KHZ SDHC_SYSCTL_DIVISOR(64, 10) // 375 kHz | |||
| #define SDHC_SYSCTL_25MHZ SDHC_SYSCTL_DIVISOR(2, 5) // 24 MHz | |||
| #define SDHC_SYSCTL_50MHZ SDHC_SYSCTL_DIVISOR(2, 3) // 40 MHz | |||
| @@ -214,6 +232,7 @@ static SD_CARD_DESCRIPTOR sdCardDesc; | |||
| * Private functions | |||
| ******************************************************************************/ | |||
| static uint8_t SDHC_Init(void); | |||
| static void SDHC_InitGPIO(void); | |||
| static void SDHC_ReleaseGPIO(void); | |||
| static void SDHC_SetClock(uint32_t sysctl); | |||
| @@ -245,8 +264,10 @@ static int SDHC_ACMD41_SendOperationCond(uint32_t cond); | |||
| // initialize the SDHC Controller | |||
| // returns status of initialization(OK, nonInit, noCard, CardProtected) | |||
| uint8_t SDHC_Init(void) | |||
| static uint8_t SDHC_Init(void) | |||
| { | |||
| int i; | |||
| // Enable clock to SDHC peripheral | |||
| SIM_SCGC3 |= SIM_SCGC3_SDHC; | |||
| @@ -256,7 +277,7 @@ uint8_t SDHC_Init(void) | |||
| SIM_SCGC6 |= SIM_SCGC6_DMAMUX; | |||
| SIM_SCGC7 |= SIM_SCGC7_DMA; | |||
| // Switch of MPU unit (maybe bug of silicon) | |||
| // Enable DMA access via MPU (not currently used) | |||
| MPU_CESR &= ~MPU_CESR_VLD_MASK; | |||
| // De-init GPIO - to prevent unwanted clocks on bus | |||
| @@ -290,9 +311,13 @@ uint8_t SDHC_Init(void) | |||
| SDHC_IRQSTATEN_BRRSEN | SDHC_IRQSTATEN_BWRSEN | SDHC_IRQSTATEN_DINTSEN | | |||
| SDHC_IRQSTATEN_CRMSEN | SDHC_IRQSTATEN_TCSEN | SDHC_IRQSTATEN_CCSEN; | |||
| /* 80 initial clocks */ | |||
| SDHC_SYSCTL |= SDHC_SYSCTL_INITA; | |||
| while (SDHC_SYSCTL & SDHC_SYSCTL_INITA) { }; | |||
| // initial clocks... SD spec says only 74 clocks are needed, but if Teensy rebooted | |||
| // while the card was in middle of an operation, thousands of clock cycles can be | |||
| // needed to get the card to complete a prior command and return to a usable state. | |||
| for (i=0; i < 500; i++) { | |||
| SDHC_SYSCTL |= SDHC_SYSCTL_INITA; | |||
| while (SDHC_SYSCTL & SDHC_SYSCTL_INITA) { }; | |||
| } | |||
| // to do - check if this needed | |||
| SDHC_IRQSTAT |= SDHC_IRQSTAT_CRM; | |||
| @@ -339,17 +364,17 @@ uint8_t KinetisSDHC_InitCard(void) | |||
| } | |||
| resR = SDHC_CMD8_SetInterface(0x000001AA); // 3.3V and AA check pattern | |||
| if (resR > 0) { | |||
| sdCardDesc.status = SDHC_STATUS_NOINIT; | |||
| return SDHC_STATUS_NOINIT; | |||
| } | |||
| if (resR == 0) { | |||
| if (resR == SDHC_RESULT_OK) { | |||
| if (SDHC_CMDRSP0 != 0x000001AA) { | |||
| sdCardDesc.status = SDHC_STATUS_NOINIT; | |||
| return SDHC_STATUS_NOINIT; | |||
| } | |||
| sdCardDesc.highCapacity = 1; | |||
| } else if (resR == SDHC_RESULT_NO_RESPONSE) { | |||
| // version 1 cards do not respond to CMD8 | |||
| } else { | |||
| sdCardDesc.status = SDHC_STATUS_NOINIT; | |||
| return SDHC_STATUS_NOINIT; | |||
| } | |||
| if (SDHC_ACMD41_SendOperationCond(0)) { | |||
| @@ -540,15 +565,16 @@ static void SDHC_InitGPIO(void) | |||
| // release the SDHC Controller signals | |||
| static void SDHC_ReleaseGPIO(void) | |||
| { | |||
| PORTE_PCR0 = 0; | |||
| PORTE_PCR1 = 0; | |||
| PORTE_PCR2 = 0; | |||
| PORTE_PCR3 = 0; | |||
| PORTE_PCR4 = 0; | |||
| PORTE_PCR5 = 0; | |||
| PORTE_PCR0 = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; /* PULLUP SDHC.D1 */ | |||
| PORTE_PCR1 = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; /* PULLUP SDHC.D0 */ | |||
| PORTE_PCR2 = 0; /* SDHC.CLK */ | |||
| PORTE_PCR3 = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; /* PULLUP SDHC.CMD */ | |||
| PORTE_PCR4 = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; /* PULLUP SDHC.D3 */ | |||
| PORTE_PCR5 = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; /* PULLUP SDHC.D2 */ | |||
| } | |||
| static void SDHC_SetClock(uint32_t sysctl) | |||
| { | |||
| uint32_t n, timeout; | |||
| @@ -143,8 +143,40 @@ static void spiSend(uint8_t b) { | |||
| } | |||
| /** SPI send multiple bytes */ | |||
| #elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #define USE_TEENSY4_SPI | |||
| static void spiInit(uint8_t spiRate) { | |||
| switch (spiRate) { | |||
| // the top 2 speeds are set to 24 MHz, for the SD library defaults | |||
| case 0: settings = SPISettings(25200000, MSBFIRST, SPI_MODE0); break; | |||
| case 1: settings = SPISettings(24000000, MSBFIRST, SPI_MODE0); break; | |||
| case 2: settings = SPISettings(8000000, MSBFIRST, SPI_MODE0); break; | |||
| case 3: settings = SPISettings(4000000, MSBFIRST, SPI_MODE0); break; | |||
| case 4: settings = SPISettings(3000000, MSBFIRST, SPI_MODE0); break; | |||
| case 5: settings = SPISettings(2000000, MSBFIRST, SPI_MODE0); break; | |||
| default: settings = SPISettings(400000, MSBFIRST, SPI_MODE0); | |||
| } | |||
| SPI.begin(); | |||
| } | |||
| static void spiSend(uint8_t b) { | |||
| SPI.transfer(b); | |||
| } | |||
| static uint8_t spiRec(void) { | |||
| return SPI.transfer(0xff); | |||
| } | |||
| static void spiRec(uint8_t* buf, size_t len) { | |||
| SPI.transfer(buf, len); | |||
| } | |||
| static void spiRecIgnore(size_t len) { | |||
| for (size_t i=0; i < len; i++) | |||
| SPI.transfer(0xff); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| #else | |||
| // functions for hardware SPI | |||
| @@ -224,16 +256,20 @@ uint8_t Sd2Card::SD_init(uint8_t sckRateID, uint8_t chipSelectPin) { | |||
| type_ = 0; | |||
| chipSelectPin_ = chipSelectPin; | |||
| // 16-bit init start time allows over a minute | |||
| uint16_t t0 = (uint16_t)millis(); | |||
| unsigned int t0 = millis(); | |||
| uint32_t arg; | |||
| digitalWrite(chipSelectPin_, HIGH); | |||
| pinMode(chipSelectPin_, OUTPUT); | |||
| digitalWrite(chipSelectPin_, HIGH); | |||
| #ifdef USE_TEENSY3_SPI | |||
| #if defined(USE_TEENSY3_SPI) | |||
| spiBegin(); | |||
| spiInit(6); | |||
| #elif defined(USE_TEENSY4_SPI) | |||
| spiInit(6); | |||
| pinMode(SS_PIN, OUTPUT); | |||
| digitalWrite(SS_PIN, HIGH); // disable any SPI device using hardware SS pin | |||
| #else | |||
| // set pin modes | |||
| pinMode(SPI_MISO_PIN, INPUT); | |||
| @@ -250,7 +286,6 @@ uint8_t Sd2Card::SD_init(uint8_t sckRateID, uint8_t chipSelectPin) { | |||
| settings = SPISettings(250000, MSBFIRST, SPI_MODE0); | |||
| #endif | |||
| #endif // not USE_TEENSY3_SPI | |||
| // must supply min of 74 clock cycles with CS high. | |||
| #ifdef SPI_HAS_TRANSACTION | |||
| SPI.beginTransaction(settings); | |||
| @@ -259,12 +294,11 @@ uint8_t Sd2Card::SD_init(uint8_t sckRateID, uint8_t chipSelectPin) { | |||
| #ifdef SPI_HAS_TRANSACTION | |||
| SPI.endTransaction(); | |||
| #endif | |||
| chipSelectLow(); | |||
| // command to go idle in SPI mode | |||
| while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) { | |||
| if (((uint16_t)(millis() - t0)) > SD_INIT_TIMEOUT) { | |||
| unsigned int d = millis() - t0; | |||
| if (d > SD_INIT_TIMEOUT) { | |||
| goto fail; // SD_CARD_ERROR_CMD0 | |||
| } | |||
| } | |||
| @@ -281,10 +315,10 @@ uint8_t Sd2Card::SD_init(uint8_t sckRateID, uint8_t chipSelectPin) { | |||
| } | |||
| // initialize card and send host supports SDHC if SD2 | |||
| arg = (type_ == SD_CARD_TYPE_SD2) ? 0X40000000 : 0; | |||
| while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) { | |||
| // check for timeout | |||
| if (((uint16_t)(millis() - t0)) > SD_INIT_TIMEOUT) { | |||
| unsigned int d = millis() - t0; | |||
| if (d > SD_INIT_TIMEOUT) { | |||
| goto fail; // SD_CARD_ERROR_ACMD41 | |||
| } | |||
| } | |||
| @@ -325,7 +359,7 @@ uint8_t Sd2Card::SD_readBlock(uint32_t block, uint8_t* dst) | |||
| if (!waitStartBlock()) { | |||
| goto fail; | |||
| } | |||
| #ifdef USE_TEENSY3_SPI | |||
| #if defined(USE_TEENSY3_SPI) | defined(USE_TEENSY4_SPI) | |||
| spiRec(dst, 512); | |||
| spiRecIgnore(2); | |||
| #else // OPTIMIZE_HARDWARE_SPI | |||
| @@ -373,7 +407,7 @@ uint8_t Sd2Card::SD_readBlock(uint32_t block, uint8_t* dst) | |||
| * false, is returned for an invalid value of \a sckRateID. | |||
| */ | |||
| uint8_t Sd2Card::setSckRate(uint8_t sckRateID) { | |||
| #ifdef USE_TEENSY3_SPI | |||
| #if defined(USE_TEENSY3_SPI) || defined(USE_TEENSY4_SPI) | |||
| spiInit(sckRateID); | |||
| return true; | |||
| #else | |||
| @@ -403,20 +437,23 @@ uint8_t Sd2Card::setSckRate(uint8_t sckRateID) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // wait for card to go not busy | |||
| uint8_t Sd2Card::waitNotBusy(uint16_t timeoutMillis) { | |||
| uint16_t t0 = millis(); | |||
| uint8_t Sd2Card::waitNotBusy(unsigned int timeoutMillis) { | |||
| unsigned int t0 = millis(); | |||
| unsigned int d; | |||
| do { | |||
| if (spiRec() == 0XFF) return true; | |||
| d = millis() - t0; | |||
| } | |||
| while (((uint16_t)millis() - t0) < timeoutMillis); | |||
| while (d < timeoutMillis); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Wait for start block token */ | |||
| uint8_t Sd2Card::waitStartBlock(void) { | |||
| uint16_t t0 = millis(); | |||
| unsigned int t0 = millis(); | |||
| while ((status_ = spiRec()) == 0XFF) { | |||
| if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) { | |||
| unsigned int d = millis() - t0; | |||
| if (d > SD_READ_TIMEOUT) { | |||
| return false; // SD_CARD_ERROR_READ_TIMEOUT | |||
| } | |||
| } | |||
| @@ -468,7 +505,7 @@ uint8_t Sd2Card::SD_writeBlock(uint32_t blockNumber, const uint8_t* src) { | |||
| //------------------------------------------------------------------------------ | |||
| // send one block of data for write block or write multiple blocks | |||
| uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) { | |||
| #ifdef OPTIMIZE_HARDWARE_SPI | |||
| #if defined(OPTIMIZE_HARDWARE_SPI) && !defined(USE_TEENSY4_SPI) | |||
| // send data - optimized loop | |||
| SPDR = token; | |||
| @@ -487,7 +524,7 @@ uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) { | |||
| #else // OPTIMIZE_HARDWARE_SPI | |||
| spiSend(token); | |||
| for (uint16_t i = 0; i < 512; i++) { | |||
| spiSend(src[i]); | |||
| spiSend(src[i]); | |||
| } | |||
| #endif // OPTIMIZE_HARDWARE_SPI | |||
| spiSend(0xff); // dummy crc | |||
| @@ -58,13 +58,13 @@ uint8_t const SPI_SCK_PIN = SCK_PIN; | |||
| /** Protect block zero from write if nonzero */ | |||
| #define SD_PROTECT_BLOCK_ZERO 1 | |||
| /** init timeout ms */ | |||
| uint16_t const SD_INIT_TIMEOUT = 2000; | |||
| const unsigned int SD_INIT_TIMEOUT = 2000; | |||
| /** erase timeout ms */ | |||
| uint16_t const SD_ERASE_TIMEOUT = 10000; | |||
| const unsigned int SD_ERASE_TIMEOUT = 10000; | |||
| /** read timeout ms */ | |||
| uint16_t const SD_READ_TIMEOUT = 300; | |||
| const unsigned int SD_READ_TIMEOUT = 300; | |||
| /** write time out ms */ | |||
| uint16_t const SD_WRITE_TIMEOUT = 600; | |||
| const unsigned int SD_WRITE_TIMEOUT = 600; | |||
| //------------------------------------------------------------------------------ | |||
| // card types | |||
| /** Standard capacity V1 SD card */ | |||
| @@ -141,7 +141,7 @@ class Sd2Card { | |||
| uint8_t sendWriteCommand(uint32_t blockNumber, uint32_t eraseCount); | |||
| void chipSelectHigh(void); | |||
| void chipSelectLow(void); | |||
| uint8_t waitNotBusy(uint16_t timeoutMillis); | |||
| uint8_t waitNotBusy(unsigned int timeoutMillis); | |||
| uint8_t writeData(uint8_t token, const uint8_t* src); | |||
| uint8_t waitStartBlock(void); | |||
| uint8_t setSckRate(uint8_t sckRateID); | |||
| @@ -21,7 +21,7 @@ | |||
| #ifndef Sd2PinMap_h | |||
| #define Sd2PinMap_h | |||
| #if defined(__arm__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) | |||
| #if defined(__arm__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #include <Arduino.h> | |||
| @@ -238,7 +238,7 @@ class SdFile : public Print { | |||
| uint8_t b; | |||
| return read(&b, 1) == 1 ? b : -1; | |||
| } | |||
| int16_t read(void* buf, uint16_t nbyte); | |||
| int32_t read(void* buf, size_t nbyte); | |||
| int8_t readDir(dir_t* dir); | |||
| static uint8_t remove(SdFile* dirFile, const char* fileName); | |||
| uint8_t remove(void); | |||
| @@ -284,7 +284,7 @@ class SdFile : public Print { | |||
| /** \return SdVolume that contains this file. */ | |||
| SdVolume* volume(void) const {return vol_;} | |||
| size_t write(uint8_t b); | |||
| size_t write(const void* buf, uint16_t nbyte); | |||
| size_t write(const void* buf, size_t nbyte); | |||
| size_t write(const char* str); | |||
| void write_P(PGM_P str); | |||
| void writeln_P(PGM_P str); | |||
| @@ -657,7 +657,7 @@ void SdFile::printTwoDigits(uint8_t v) { | |||
| * read() called before a file has been opened, corrupt file system | |||
| * or an I/O error occurred. | |||
| */ | |||
| int16_t SdFile::read(void* buf, uint16_t nbyte) { | |||
| int32_t SdFile::read(void* buf, size_t nbyte) { | |||
| uint8_t* dst = reinterpret_cast<uint8_t*>(buf); | |||
| // error if not open or write only | |||
| @@ -667,7 +667,7 @@ int16_t SdFile::read(void* buf, uint16_t nbyte) { | |||
| if (nbyte > (fileSize_ - curPosition_)) nbyte = fileSize_ - curPosition_; | |||
| // amount left to read | |||
| uint16_t toRead = nbyte; | |||
| uint32_t toRead = nbyte; | |||
| while (toRead > 0) { | |||
| uint32_t block; // raw device block number | |||
| uint16_t offset = curPosition_ & 0X1FF; // offset in block | |||
| @@ -687,7 +687,7 @@ int16_t SdFile::read(void* buf, uint16_t nbyte) { | |||
| } | |||
| block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; | |||
| } | |||
| uint16_t n = toRead; | |||
| int32_t n = toRead; | |||
| // amount to be read from current block | |||
| if (n > (512 - offset)) n = 512 - offset; | |||
| @@ -1120,12 +1120,12 @@ uint8_t SdFile::truncate(uint32_t length) { | |||
| * for a read-only file, device is full, a corrupt file system or an I/O error. | |||
| * | |||
| */ | |||
| size_t SdFile::write(const void* buf, uint16_t nbyte) { | |||
| size_t SdFile::write(const void* buf, size_t nbyte) { | |||
| // convert void* to uint8_t* - must be before goto statements | |||
| const uint8_t* src = reinterpret_cast<const uint8_t*>(buf); | |||
| // number of bytes left to write - must be before goto statements | |||
| uint16_t nToWrite = nbyte; | |||
| size_t nToWrite = nbyte; | |||
| // error if not a normal file or is read-only | |||
| if (!isFile() || !(flags_ & O_WRITE)) goto writeErrorReturn; | |||