@@ -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; |