diff --git a/doc/SdErrorCodes.txt b/doc/SdErrorCodes.txt
index 133c0a2..359ee00 100644
--- a/doc/SdErrorCodes.txt
+++ b/doc/SdErrorCodes.txt
@@ -1,8 +1,7 @@
-2019-12-10
+2021-01-06
Run the SdErrorCode example to produce an updated list.
-C
Code,Symbol - failed operation
0X00,SD_CARD_ERROR_NONE - No error
0X01,SD_CARD_ERROR_CMD0 - Card reset failed
@@ -35,16 +34,17 @@ Code,Symbol - failed operation
0X1C,SD_CARD_ERROR_READ_START - Bad readStart argument
0X1D,SD_CARD_ERROR_READ_TIMEOUT - Read data timeout
0X1E,SD_CARD_ERROR_STOP_TRAN - Multiple block stop failed
-0X1F,SD_CARD_ERROR_WRITE_DATA - Write data not accepted
-0X20,SD_CARD_ERROR_WRITE_FIFO - SDIO fifo write timeout
-0X21,SD_CARD_ERROR_WRITE_START - Bad writeStart argument
-0X22,SD_CARD_ERROR_WRITE_PROGRAMMING - Flash programming
-0X23,SD_CARD_ERROR_WRITE_TIMEOUT - Write timeout
-0X24,SD_CARD_ERROR_DMA - DMA transfer failed
-0X25,SD_CARD_ERROR_ERASE - Card did not accept erase commands
-0X26,SD_CARD_ERROR_ERASE_SINGLE_SECTOR - Card does not support erase
-0X27,SD_CARD_ERROR_ERASE_TIMEOUT - Erase command timeout
-0X28,SD_CARD_ERROR_INIT_NOT_CALLED - Card has not been initialized
-0X29,SD_CARD_ERROR_INVALID_CARD_CONFIG - Invalid card config
-0X2A,SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED - Unsupported SDIO command
-0X2B,SD_CARD_ERROR_UNKNOWN - Unknown error
+0X1F,SD_CARD_ERROR_TRANSFER_COMPLETE - SDIO transfer complete
+0X20,SD_CARD_ERROR_WRITE_DATA - Write data not accepted
+0X21,SD_CARD_ERROR_WRITE_FIFO - SDIO fifo write timeout
+0X22,SD_CARD_ERROR_WRITE_START - Bad writeStart argument
+0X23,SD_CARD_ERROR_WRITE_PROGRAMMING - Flash programming
+0X24,SD_CARD_ERROR_WRITE_TIMEOUT - Write timeout
+0X25,SD_CARD_ERROR_DMA - DMA transfer failed
+0X26,SD_CARD_ERROR_ERASE - Card did not accept erase commands
+0X27,SD_CARD_ERROR_ERASE_SINGLE_SECTOR - Card does not support erase
+0X28,SD_CARD_ERROR_ERASE_TIMEOUT - Erase command timeout
+0X29,SD_CARD_ERROR_INIT_NOT_CALLED - Card has not been initialized
+0X2A,SD_CARD_ERROR_INVALID_CARD_CONFIG - Invalid card config
+0X2B,SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED - Unsupported SDIO command
+0X2C,SD_CARD_ERROR_UNKNOWN - Unknown error
\ No newline at end of file
diff --git a/doc/ZipMsg/index.html b/doc/ZipMsg/index.html
new file mode 100644
index 0000000..5488789
--- /dev/null
+++ b/doc/ZipMsg/index.html
@@ -0,0 +1,4 @@
+
Replace the content of the html folder by unzipping html.zip.
+I have zipped the documentation since Doxygen changes every file each time it runs.
+This makes viewing changes on GitHub difficult.
+
\ No newline at end of file
diff --git a/doc/html.zip b/doc/html.zip
index e76ee73..d708df6 100644
Binary files a/doc/html.zip and b/doc/html.zip differ
diff --git a/doc/html/index.html b/doc/html/index.html
index 6903ea3..5488789 100644
--- a/doc/html/index.html
+++ b/doc/html/index.html
@@ -1,835 +1,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Replace the
-content of the html folder by unzipping html.zip.
-
-
I have
-zipped the documentation since Doxygen changes every
-file each time it runs.
-
-
This makes viewing changes on GitHub difficult.
-
-
-
-
-
-
-
-
-
+Replace the content of the html folder by unzipping html.zip.
+I have zipped the documentation since Doxygen changes every file each time it runs.
+This makes viewing changes on GitHub difficult.
+
\ No newline at end of file
diff --git a/doc/html/index_files/colorschememapping.xml b/doc/html/index_files/colorschememapping.xml
deleted file mode 100644
index 6a0069c..0000000
--- a/doc/html/index_files/colorschememapping.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/doc/html/index_files/filelist.xml b/doc/html/index_files/filelist.xml
deleted file mode 100644
index 7dd4458..0000000
--- a/doc/html/index_files/filelist.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/doc/html/index_files/themedata.thmx b/doc/html/index_files/themedata.thmx
deleted file mode 100644
index 3d2a437..0000000
Binary files a/doc/html/index_files/themedata.thmx and /dev/null differ
diff --git a/examples/ExFatFormatter/ExFatFormatter.ino b/examples/ExFatFormatter/ExFatFormatter.ino
deleted file mode 100644
index b496f5f..0000000
--- a/examples/ExFatFormatter/ExFatFormatter.ino
+++ /dev/null
@@ -1,85 +0,0 @@
-// Force exFAT formatting for all SD cards larger than 512MB.
-#include "SdFat.h"
-
-/*
- Change the value of SD_CS_PIN if you are using SPI and
- your hardware does not use the default value, SS.
- Common values are:
- Arduino Ethernet shield: pin 4
- Sparkfun SD shield: pin 8
- Adafruit SD shields and modules: pin 10
-*/
-
-// SDCARD_SS_PIN is defined for the built-in SD on some boards.
-#ifndef SDCARD_SS_PIN
-const uint8_t SD_CS_PIN = SS;
-#else // SDCARD_SS_PIN
-// Assume built-in SD is used.
-const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
-#endif // SDCARD_SS_PIN
-
-// Select fastest interface.
-#if HAS_SDIO_CLASS
-// SD config for Teensy 3.6 SDIO.
-#define SD_CONFIG SdioConfig(FIFO_SDIO)
-//#define SD_CONFIG SdioConfig(DMA_SDIO)
-#elif ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
-#else // HAS_SDIO_CLASS
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
-#endif // HAS_SDIO_CLASS
-
-SdExFat sd;
-//------------------------------------------------------------------------------
-void clearSerialInput() {
- uint32_t m = micros();
- do {
- if (Serial.read() >= 0) {
- m = micros();
- }
- } while (micros() - m < 10000);
-}
-//------------------------------------------------------------------------------
-void errorHalt() {
- sd.printSdError(&Serial);
- SysCall::halt();
-}
-#define error(s) (Serial.println(F(s)),errorHalt())
-//------------------------------------------------------------------------------
-void setup() {
- Serial.begin(9600);
- while (!Serial) {}
- Serial.println(F("Type any character to begin"));
-
- while (!Serial.available()) {
- yield();
- }
- clearSerialInput();
- Serial.println();
- Serial.println(F(
- "Your SD will be formated exFAT.\r\n"
- "All data on the SD will be lost.\r\n"
- "Type 'Y' to continue.\r\n"));
-
- while (!Serial.available()) {
- yield();
- }
- if (Serial.read() != 'Y') {
- Serial.println(F("Exiting, 'Y' not typed."));
- return;
- }
- if (!sd.cardBegin(SD_CONFIG)) {
- error("cardBegin failed");
- }
- if(!sd.format(&Serial)) {
- error("format failed");
- }
- if (!sd.volumeBegin()) {
- error("volumeBegin failed");
- }
- Serial.print(F("Bytes per cluster: "));
- Serial.println(sd.bytesPerCluster());
- Serial.println(F("Done"));
-}
-void loop() {
-}
\ No newline at end of file
diff --git a/examples/STM32Test/STM32Test.ino b/examples/STM32Test/STM32Test.ino
index 860e89d..403916f 100644
--- a/examples/STM32Test/STM32Test.ino
+++ b/examples/STM32Test/STM32Test.ino
@@ -14,7 +14,12 @@ FsFile file1;
// Use mySPI2 since SPI2 is used in SPI.h as a different type.
static SPIClass mySPI2(2);
// Chip select PB21, dedicated SPI, 18 MHz, port 2.
+#if ENABLE_DEDICATED_SPI
#define SD2_CONFIG SdSpiConfig(PB12, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)
+#else // ENABLE_DEDICATED_SPI
+#define SD2_CONFIG SdSpiConfig(PB12, SHARED_SPI, SD_SCK_MHZ(18), &mySPI2)
+#endif // ENABLE_DEDICATED_SPI
+
SdFs sd2;
FsFile file2;
diff --git a/examples/SoftwareSpi/SoftwareSpi.ino b/examples/SoftwareSpi/SoftwareSpi.ino
index b7a753f..5cc7537 100644
--- a/examples/SoftwareSpi/SoftwareSpi.ino
+++ b/examples/SoftwareSpi/SoftwareSpi.ino
@@ -21,7 +21,11 @@ const uint8_t SOFT_SCK_PIN = 13;
// SdFat software SPI template
SoftSpiDriver softSpi;
// Speed argument is ignored for software SPI.
+#if ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(0), &softSpi)
+#else // ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(0), &softSpi)
+#endif // ENABLE_DEDICATED_SPI
#if SD_FAT_TYPE == 0
SdFat sd;
diff --git a/examples/TeensyDmaAdcLogger/TeensyDmaAdcLogger.ino b/examples/TeensyDmaAdcLogger/TeensyDmaAdcLogger.ino
new file mode 100644
index 0000000..f017a19
--- /dev/null
+++ b/examples/TeensyDmaAdcLogger/TeensyDmaAdcLogger.ino
@@ -0,0 +1,247 @@
+// Test of Teensy exFAT DMA ADC logger.
+// This is mainly to test use of RingBuf in an ISR.
+// You should modify it for serious use as a data logger.
+//
+#include "DMAChannel.h"
+#include "SdFat.h"
+#include "FreeStack.h"
+#include "RingBuf.h"
+
+// 400 sector RingBuf - could be larger on Teensy 4.1.
+const size_t RING_BUF_SIZE = 400*512;
+
+// Preallocate 8GiB file.
+const uint64_t PRE_ALLOCATE_SIZE = 8ULL << 30;
+
+// Use FIFO SDIO.
+#define SD_CONFIG SdioConfig(FIFO_SDIO)
+
+DMAChannel dma(true);
+
+SdFs sd;
+
+FsFile file;
+//------------------------------------------------------------------------------
+// Ping-pong DMA buffer.
+DMAMEM static uint16_t __attribute__((aligned(32))) dmaBuf[2][256];
+size_t dmaCount;
+
+// RingBuf for 512 byte sectors.
+RingBuf rb;
+
+// Shared between ISR and background.
+volatile size_t maxBytesUsed;
+
+volatile bool overrun;
+//------------------------------------------------------------------------------
+//ISR.
+static void isr() {
+ if (rb.bytesFreeIsr() >= 512 && !overrun) {
+ rb.memcpyIn(dmaBuf[dmaCount & 1], 512);
+ dmaCount++;
+ if (rb.bytesUsed() > maxBytesUsed) {
+ maxBytesUsed = rb.bytesUsed();
+ }
+ } else {
+ overrun = true;
+ }
+ dma.clearComplete();
+ dma.clearInterrupt();
+#if defined(__IMXRT1062__)
+ // Handle clear interrupt glitch in Teensy 4.x!
+ asm("DSB");
+#endif // defined(__IMXRT1062__)
+}
+//------------------------------------------------------------------------------
+// Over-clocking will degrade quality - use only for stress testing.
+void overclock() {
+#if defined(__IMXRT1062__) // Teensy 4.0
+ ADC1_CFG =
+ // High Speed Configuration
+ ADC_CFG_ADHSC |
+ // Sample period 3 clocks
+ ADC_CFG_ADSTS(0) |
+ // Input clock
+ ADC_CFG_ADIV(0) |
+ // Not selected - Long Sample Time Configuration
+ // ADC_CFG_ADLSMP |
+ // 12-bit
+ ADC_CFG_MODE(2) |
+ // Asynchronous clock
+ ADC_CFG_ADICLK(3);
+#else // defined(__IMXRT1062__)
+ // Set 12 bit mode and max over-clock
+ ADC0_CFG1 =
+ // Clock divide select, 0=direct, 1=div2, 2=div4, 3=div8
+ ADC_CFG1_ADIV(0) |
+ // Sample time configuration, 0=Short, 1=Long
+ // ADC_CFG1_ADLSMP |
+ // Conversion mode, 0=8 bit, 1=12 bit, 2=10 bit, 3=16 bit
+ ADC_CFG1_MODE(1) |
+ // Input clock, 0=bus, 1=bus/2, 2=OSCERCLK, 3=async
+ ADC_CFG1_ADICLK(0);
+
+ ADC0_CFG2 = ADC_CFG2_MUXSEL | ADC_CFG2_ADLSTS(3);
+#endif // defined(__IMXRT1062__)
+}
+//------------------------------------------------------------------------------
+#if defined(__IMXRT1062__) // Teensy 4.0
+#define SOURCE_SADDR ADC1_R0
+#define SOURCE_EVENT DMAMUX_SOURCE_ADC1
+#else
+#define SOURCE_SADDR ADC0_RA
+#define SOURCE_EVENT DMAMUX_SOURCE_ADC0
+#endif
+//------------------------------------------------------------------------------
+// Should replace ADC stuff with calls to Teensy ADC library.
+// https://github.com/pedvide/ADC
+static void init(uint8_t pin) {
+ uint32_t adch;
+ uint32_t i, sum = 0;
+ // Actually, do many normal reads, to start with a nice DC level
+ for (i=0; i < 1024; i++) {
+ sum += analogRead(pin);
+ }
+#if defined(__IMXRT1062__) // Teensy 4.0
+ // save channel
+ adch = ADC1_HC0 & 0x1F;
+ // Continuous conversion , DMA enable
+ ADC1_GC = ADC_GC_ADCO | ADC_GC_DMAEN;
+ // start conversion
+ ADC1_HC0 = adch;
+#else // defined(__IMXRT1062__) // Teensy 4.0
+ // save channel
+ adch = ADC0_SC1A & 0x1F;
+ // DMA enable
+ ADC0_SC2 |= ADC_SC2_DMAEN;
+ // Continuous conversion enable
+ ADC0_SC3 = ADC_SC3_ADCO;
+ // Start ADC
+ ADC0_SC1A = adch;
+ #endif // defined(__IMXRT1062__) // Teensy 4.0
+ // set up a DMA channel to store the ADC data
+ dma.attachInterrupt(isr);
+ dma.begin();
+ dma.source((volatile const signed short &)SOURCE_SADDR);
+ dma.destinationBuffer((volatile uint16_t*)dmaBuf, sizeof(dmaBuf));
+ dma.interruptAtHalf();
+ dma.interruptAtCompletion();
+ dma.triggerAtHardwareEvent(SOURCE_EVENT);
+ dma.enable();
+}
+//------------------------------------------------------------------------------
+void stopDma() {
+#if defined(__IMXRT1062__) // Teensy 4.0
+ ADC1_GC = 0;
+#else // defined(__IMXRT1062__)
+ ADC0_SC3 = 0;
+#endif // defined(__IMXRT1062__)
+ dma.disable();
+}
+//------------------------------------------------------------------------------
+void printTest(Print* pr) {
+ if (file.fileSize() < 1024*2) {
+ return;
+ }
+ file.rewind();
+ rb.begin(&file);
+ // Could readIn RING_BUF_SIZE bytes and write to a csv file in a loop.
+ if (rb.readIn(2048) != 2048) {
+ sd.errorHalt("rb.readIn failed");
+ }
+ uint16_t data;
+ for (size_t i = 0; i < 1024; i++) {
+ pr->print(i);
+ pr->print(',');
+ rb.memcpyOut(&data, 2);
+ pr->println(data);
+ }
+}
+//------------------------------------------------------------------------------
+void runTest(uint8_t pin) {
+ dmaCount = 0;
+ maxBytesUsed = 0;
+ overrun = false;
+ do {
+ delay(10);
+ } while (Serial.read() >= 0);
+
+ if (!file.open("IsrLoggerTest.bin", O_CREAT | O_TRUNC | O_RDWR)) {
+ sd.errorHalt("file.open failed");
+ }
+ if (!file.preAllocate(PRE_ALLOCATE_SIZE)) {
+ sd.errorHalt("file.preAllocate failed");
+ }
+ rb.begin(&file);
+ Serial.println("Type any character to stop\n");
+
+ init(pin);
+ uint32_t samplingTime = micros();
+ while (!overrun && !Serial.available()) {
+ size_t n = rb.bytesUsed();
+ if ((n + file.curPosition()) >= (PRE_ALLOCATE_SIZE - 512)) {
+ Serial.println("File full - stopping");
+ break;
+ }
+ if (n >= 512) {
+ if (rb.writeOut(512) != 512) {
+ Serial.println("writeOut() failed");
+ file.close();
+ return;
+ }
+ }
+ }
+ stopDma();
+ samplingTime = (micros() - samplingTime);
+ if (!file.truncate()) {
+ sd.errorHalt("truncate failed");
+ }
+ if (overrun) {
+ Serial.println("Overrun ERROR!!");
+ }
+ Serial.print("RingBufSize ");
+ Serial.println(RING_BUF_SIZE);
+ Serial.print("maxBytesUsed ");
+ Serial.println(maxBytesUsed);
+ Serial.print("fileSize ");
+ Serial.println((uint32_t)file.fileSize());
+ Serial.print(0.000001*samplingTime);
+ Serial.println(" seconds");
+ Serial.print(1.0*file.fileSize()/samplingTime, 3);
+ Serial.println(" MB/sec\n");
+ printTest(&Serial);
+ file.close();
+}
+//------------------------------------------------------------------------------
+void waitSerial(const char* msg) {
+ uint32_t m = micros();
+ do {
+ if (Serial.read() >= 0) {
+ m = micros();
+ }
+ } while (micros() - m < 10000);
+ Serial.println(msg);
+ while (!Serial.available()) {}
+ Serial.println();
+}
+//------------------------------------------------------------------------------
+void setup() {
+ Serial.begin(9600);
+ while (!Serial) {
+ yield();
+ }
+ waitSerial("Type any character to begin");
+ Serial.print("FreeStack: ");
+ Serial.println(FreeStack());
+}
+//------------------------------------------------------------------------------
+void loop() {
+ if (!sd.begin(SD_CONFIG)) {
+ sd.initErrorHalt(&Serial);
+ }
+//analogReadAveraging(1);
+//analogReadResolution(12);
+//overclock(); // 3 Msps on Teensy 3.6 - requires high quality card.
+ runTest(A0);
+ waitSerial("Type any character to run test again");
+}
diff --git a/examples/TeensySdioDemo/TeensySdioDemo.ino b/examples/TeensySdioDemo/TeensySdioDemo.ino
index 652086c..cf79f7e 100644
--- a/examples/TeensySdioDemo/TeensySdioDemo.ino
+++ b/examples/TeensySdioDemo/TeensySdioDemo.ino
@@ -197,10 +197,15 @@ void loop() {
}
Serial.println("\nDMA SDIO mode - slow for small transfers.");
} else if (c == '3') {
+#if ENABLE_DEDICATED_SPI
if (!sd.begin(SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50)))) {
errorHalt("begin failed");
}
Serial.println("\nDedicated SPI mode.");
+#else // ENABLE_DEDICATED_SPI
+ Serial.println("ENABLE_DEDICATED_SPI must be non-zero.");
+ return;
+#endif // ENABLE_DEDICATED_SPI
} else if (c == '4') {
if (!sd.begin(SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50)))) {
errorHalt("begin failed");
diff --git a/examples/TeensySdioLogger/TeensySdioLogger.ino b/examples/TeensySdioLogger/TeensySdioLogger.ino
new file mode 100644
index 0000000..0bd58d1
--- /dev/null
+++ b/examples/TeensySdioLogger/TeensySdioLogger.ino
@@ -0,0 +1,148 @@
+// Test Teensy SDIO with write busy in a data logger demo.
+//
+// The driver writes to the uSDHC controller's FIFO then returns
+// while the controller writes the data to the SD. The first sector
+// puts the controller in write mode and takes about 11 usec on a
+// Teensy 4.1. About 5 usec is required to write a sector when the
+// controller is in write mode.
+
+#include "SdFat.h"
+#include "RingBuf.h"
+
+// Use Teensy SDIO
+#define SD_CONFIG SdioConfig(FIFO_SDIO)
+
+// Interval between points for 25 ksps.
+#define LOG_INTERVAL_USEC 40
+
+// Size to log 10 byte lines at 25 kHz for more than ten minutes.
+#define LOG_FILE_SIZE 10*25000*600 // 150,000,000 bytes.
+
+// Space to hold more than 800 ms of data for 10 byte lines at 25 ksps.
+#define RING_BUF_CAPACITY 400*512
+#define LOG_FILENAME "SdioLogger.csv"
+
+SdFs sd;
+FsFile file;
+
+// RingBuf for File type FsFile.
+RingBuf rb;
+
+void logData() {
+ // Initialize the SD.
+ if (!sd.begin(SD_CONFIG)) {
+ sd.initErrorHalt(&Serial);
+ }
+ // Open or create file - truncate existing file.
+ if (!file.open(LOG_FILENAME, O_RDWR | O_CREAT | O_TRUNC)) {
+ Serial.println("open failed\n");
+ return;
+ }
+ // File must be pre-allocated to avoid huge
+ // delays searching for free clusters.
+ if (!file.preAllocate(LOG_FILE_SIZE)) {
+ Serial.println("preAllocate failed\n");
+ file.close();
+ return;
+ }
+ // initialize the RingBuf.
+ rb.begin(&file);
+ Serial.println("Type any character to stop");
+
+ // Max RingBuf used bytes. Useful to understand RingBuf overrun.
+ size_t maxUsed = 0;
+
+ // Min spare micros in loop.
+ int32_t minSpareMicros = INT32_MAX;
+
+ // Start time.
+ uint32_t logTime = micros();
+ // Log data until Serial input or file full.
+ while (!Serial.available()) {
+ // Amount of data in ringBuf.
+ size_t n = rb.bytesUsed();
+ if ((n + file.curPosition()) > (LOG_FILE_SIZE - 20)) {
+ Serial.println("File full - quiting.");
+ break;
+ }
+ if (n > maxUsed) {
+ maxUsed = n;
+ }
+ if (n >= 512 && !file.isBusy()) {
+ // Not busy only allows one sector before possible busy wait.
+ // Write one sector from RingBuf to file.
+ if (512 != rb.writeOut(512)) {
+ Serial.println("writeOut failed");
+ break;
+ }
+ }
+ // Time for next point.
+ logTime += LOG_INTERVAL_USEC;
+ int32_t spareMicros = logTime - micros();
+ if (spareMicros < minSpareMicros) {
+ minSpareMicros = spareMicros;
+ }
+ if (spareMicros <= 0) {
+ Serial.print("Rate too fast ");
+ Serial.println(spareMicros);
+ break;
+ }
+ // Wait until time to log data.
+ while (micros() < logTime) {}
+
+ // Read ADC0 - about 17 usec on Teensy 4, Teensy 3.6 is faster.
+ uint16_t adc = analogRead(0);
+ // Print spareMicros into the RingBuf as test data.
+ rb.print(spareMicros);
+ rb.write(',');
+ // Print adc into RingBuf.
+ rb.println(adc);
+ if (rb.getWriteError()) {
+ // Error caused by too few free bytes in RingBuf.
+ Serial.println("WriteError");
+ break;
+ }
+ }
+ // Write any RingBuf data to file.
+ rb.sync();
+ file.truncate();
+ file.rewind();
+ // Print first twenty lines of file.
+ Serial.println("spareMicros,ADC0");
+ for (uint8_t n = 0; n < 20 && file.available();) {
+ int c = file.read();
+ if (c < 0) {
+ break;
+ }
+ Serial.write(c);
+ if (c == '\n') n++;
+ }
+ Serial.print("fileSize: ");
+ Serial.println((uint32_t)file.fileSize());
+ Serial.print("maxBytesUsed: ");
+ Serial.println(maxUsed);
+ Serial.print("minSpareMicros: ");
+ Serial.println(minSpareMicros);
+ file.close();
+}
+void clearSerialInput() {
+ for (uint32_t m = micros(); micros() - m < 10000;) {
+ if (Serial.read() >= 0) {
+ m = micros();
+ }
+ }
+}
+void setup() {
+ Serial.begin(9600);
+ while (!Serial) {}
+ // Go faster or log more channels. ADC quality will suffer.
+ // analogReadAveraging(1);
+}
+
+void loop() {
+ clearSerialInput();
+ Serial.println("Type any character to start");
+ while (!Serial.available()) {};
+ clearSerialInput();
+ logData();
+}
\ No newline at end of file
diff --git a/examples/UserSPIDriver/UserSPIDriver.ino b/examples/UserSPIDriver/UserSPIDriver.ino
index 1317c85..c86e32e 100644
--- a/examples/UserSPIDriver/UserSPIDriver.ino
+++ b/examples/UserSPIDriver/UserSPIDriver.ino
@@ -59,8 +59,11 @@ class MySpiClass : public SdSpiBaseClass {
private:
SPISettings m_spiSettings;
} mySpi;
-
+#if ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50), &mySpi)
+#else // ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50), &mySpi)
+#endif // ENABLE_DEDICATED_SPI
SdFat sd;
//------------------------------------------------------------------------------
diff --git a/library.properties b/library.properties
index 83d84fd..f5ee31a 100644
--- a/library.properties
+++ b/library.properties
@@ -1,5 +1,5 @@
name=SdFat
-version=2.0.4
+version=2.0.5
license=MIT
author=Bill Greiman
maintainer=Bill Greiman
diff --git a/src/ExFatLib/ExFatDbg.cpp b/src/ExFatLib/ExFatDbg.cpp
index 73f602a..658af29 100644
--- a/src/ExFatLib/ExFatDbg.cpp
+++ b/src/ExFatLib/ExFatDbg.cpp
@@ -405,7 +405,7 @@ bool ExFatPartition::printDir(print_t* pr, ExFatFile* file) {
uint16_t calcHash = 0;
uint16_t nameHash = 0;
uint16_t setChecksum = 0;
- uint16_t calcChecksum = 0;;
+ uint16_t calcChecksum = 0;
uint8_t nameLength = 0;
uint8_t setCount = 0;
uint8_t nUnicode;
diff --git a/src/ExFatLib/ExFatFile.cpp b/src/ExFatLib/ExFatFile.cpp
index ef9fdbd..7b7832d 100644
--- a/src/ExFatLib/ExFatFile.cpp
+++ b/src/ExFatLib/ExFatFile.cpp
@@ -156,10 +156,10 @@ size_t ExFatFile::getName(ExChar_t* name, size_t length) {
goto fail;
}
for (uint8_t in = 0; in < 15; in++) {
- if ((n + 1) >= length) {
+ uint16_t c = getLe16(dn->unicode + 2*in);
+ if (c == 0 || (n + 1) >= length) {
goto done;
}
- uint16_t c = getLe16(dn->unicode + 2*in);
name[n++] = sizeof(ExChar_t) > 1 || c < 0X7F ? c : '?';
}
}
diff --git a/src/ExFatLib/ExFatFile.h b/src/ExFatLib/ExFatFile.h
index 0982044..629a5b5 100644
--- a/src/ExFatLib/ExFatFile.h
+++ b/src/ExFatLib/ExFatFile.h
@@ -91,7 +91,17 @@ struct ExFatPos_t {
class ExFatFile {
public:
/** Create an instance. */
- ExFatFile() : m_attributes(FILE_ATTR_CLOSED), m_error(0), m_flags(0) {}
+ ExFatFile() {}
+ /** Create a file object and open it in the current working directory.
+ *
+ * \param[in] path A path for a file to be opened.
+ *
+ * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
+ * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t).
+ */
+ ExFatFile(const char* path, oflag_t oflag) {
+ open(path, oflag);
+ }
#if DESTRUCTOR_CLOSES_FILE
~ExFatFile() {
@@ -108,12 +118,6 @@ class ExFatFile {
operator bool() {
return isOpen();
}
- /** \return The number of bytes available from the current position
- * to EOF for normal files. Zero is returned for directory files.
- */
- uint64_t available64() {
- return isFile() ? fileSize() - curPosition() : 0;
- }
/** \return The number of bytes available from the current position
* to EOF for normal files. INT_MAX is returned for very large files.
*
@@ -126,6 +130,20 @@ class ExFatFile {
uint64_t n = available64();
return n > INT_MAX ? INT_MAX : n;
}
+ /** \return The number of bytes available from the current position
+ * to EOF for normal files. Zero is returned for directory files.
+ */
+ uint64_t available64() {
+ return isFile() ? fileSize() - curPosition() : 0;
+ }
+ /** Clear all error bits. */
+ void clearError() {
+ m_error = 0;
+ }
+ /** Clear writeError. */
+ void clearWriteError() {
+ m_error &= ~WRITE_ERROR;
+ }
/** Close a file and force cached data and directory information
* to be written to the storage device.
*
@@ -200,22 +218,6 @@ class ExFatFile {
void fsetpos(const fspos_t* pos);
/** Arduino name for sync() */
void flush() {sync();}
- /**
- * Get a file's name followed by a zero byte.
- *
- * \param[out] name An array of characters for the file's name.
- * \param[in] size The size of the array in characters.
- * \return the name length.
- */
- size_t getName(ExChar_t* name, size_t size);
- /** Clear all error bits. */
- void clearError() {
- m_error = 0;
- }
- /** Clear writeError. */
- void clearWriteError() {
- m_error &= ~WRITE_ERROR;
- }
/** Get a file's access date and time.
*
* \param[out] pdate Packed date for directory entry.
@@ -244,6 +246,14 @@ class ExFatFile {
* \return true for success or false for failure.
*/
bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime);
+ /**
+ * Get a file's name followed by a zero byte.
+ *
+ * \param[out] name An array of characters for the file's name.
+ * \param[in] size The size of the array in characters.
+ * \return the name length.
+ */
+ size_t getName(ExChar_t* name, size_t size);
/** \return value of writeError */
bool getWriteError() const {
return isOpen() ? m_error & WRITE_ERROR : true;
@@ -266,12 +276,12 @@ class ExFatFile {
bool isOpen() const {return m_attributes;}
/** \return True if file is read-only */
bool isReadOnly() const {return m_attributes & FILE_ATTR_READ_ONLY;}
- /** \return True if this is a subdirectory. */
- bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
/** \return True if this is the root directory. */
bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
- /** \return True file is writable. */
+ /** \return True file is readable. */
bool isReadable() const {return m_flags & FILE_FLAG_READ;}
+ /** \return True if this is a subdirectory. */
+ bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
/** \return True file is writable. */
bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
/** List directory contents.
@@ -381,6 +391,16 @@ class ExFatFile {
* \return true for success or false for failure.
*/
bool open(ExFatFile* dirFile, uint32_t index, oflag_t oflag);
+ /** Open a file in the current working directory.
+ *
+ * \param[in] path A path with a valid name for a file to be opened.
+ *
+ * \param[in] oflag bitwise-inclusive OR of open flags.
+ * See see ExFatFile::open(ExFatFile*, const char*, uint8_t).
+ *
+ * \return true for success or false for failure.
+ */
+ bool open(const ExChar_t* path, int oflag = O_RDONLY);
/** Open the next file or subdirectory in a directory.
*
* \param[in] dirFile An open instance for the directory
@@ -392,16 +412,6 @@ class ExFatFile {
* \return true for success or false for failure.
*/
bool openNext(ExFatFile* dirFile, oflag_t oflag = O_RDONLY);
- /** Open a file in the current working directory.
- *
- * \param[in] path A path with a valid name for a file to be opened.
- *
- * \param[in] oflag bitwise-inclusive OR of open flags.
- * See see ExFatFile::open(ExFatFile*, const char*, uint8_t).
- *
- * \return true for success or false for failure.
- */
- bool open(const ExChar_t* path, int oflag = O_RDONLY);
/** Open a volume's root directory.
*
* \param[in] vol The FAT volume containing the root directory to be opened.
@@ -425,6 +435,20 @@ class ExFatFile {
* \return true for success or false for failure.
*/
bool preAllocate(uint64_t length);
+ /** Print a file's access date and time
+ *
+ * \param[in] pr Print stream for output.
+ *
+ * \return true for success or false for failure.
+ */
+ size_t printAccessDateTime(print_t* pr);
+ /** Print a file's creation date and time
+ *
+ * \param[in] pr Print stream for output.
+ *
+ * \return true for success or false for failure.
+ */
+ size_t printCreateDateTime(print_t* pr);
/** Print a number followed by a field terminator.
* \param[in] value The number to be printed.
* \param[in] term The field terminator. Use '\\n' for CR LF.
@@ -488,21 +512,6 @@ class ExFatFile {
* \return The number of bytes printed.
*/
size_t printFileSize(print_t* pr);
- /** Print a file's access date and time
- *
- * \param[in] pr Print stream for output.
- *
- * \return true for success or false for failure.
- */
- size_t printAccessDateTime(print_t* pr);
- /** Print a file's creation date and time
- *
- * \param[in] pr Print stream for output.
- *
- * \return true for success or false for failure.
- */
- size_t printCreateDateTime(print_t* pr);
-
/** Print a file's modify date and time
*
* \param[in] pr Print stream for output.
@@ -780,9 +789,9 @@ class ExFatFile {
ExFatVolume* m_vol;
DirPos_t m_dirPos;
uint8_t m_setCount;
- uint8_t m_attributes;
- uint8_t m_error;
- uint8_t m_flags;
+ uint8_t m_attributes = FILE_ATTR_CLOSED;
+ uint8_t m_error = 0;
+ uint8_t m_flags = 0;
};
#include "../common/ArduinoFiles.h"
diff --git a/src/ExFatLib/ExFatFilePrint.cpp b/src/ExFatLib/ExFatFilePrint.cpp
index 5b10df4..8b301c1 100644
--- a/src/ExFatLib/ExFatFilePrint.cpp
+++ b/src/ExFatLib/ExFatFilePrint.cpp
@@ -167,7 +167,7 @@ size_t ExFatFile::printName(print_t* pr) {
for (in = 0; in < 15; in++) {
uint16_t c = getLe16(dn->unicode + 2*in);
if (!c) {
- break;;
+ break;
}
buf[in] = c < 0X7f ? c : '?';
n++;
diff --git a/src/ExFatLib/ExFatPartition.h b/src/ExFatLib/ExFatPartition.h
index 4b6cc9a..e2f0bcf 100644
--- a/src/ExFatLib/ExFatPartition.h
+++ b/src/ExFatLib/ExFatPartition.h
@@ -45,7 +45,7 @@ class ExFatFile;
*/
class ExFatPartition {
public:
- ExFatPartition() : m_fatType(0) {}
+ ExFatPartition() {}
/** \return the number of bytes in a cluster. */
uint32_t bytesPerCluster() const {return m_bytesPerCluster;}
/** \return the power of two for bytesPerCluster. */
@@ -203,7 +203,7 @@ class ExFatPartition {
uint32_t m_clusterMask;
uint32_t m_bytesPerCluster;
BlockDevice* m_blockDev;
- uint8_t m_fatType;
+ uint8_t m_fatType = 0;
uint8_t m_sectorsPerClusterShift;
};
#endif // ExFatPartition_h
diff --git a/src/ExFatLib/ExFatVolume.h b/src/ExFatLib/ExFatVolume.h
index fc4fe2a..254bd32 100644
--- a/src/ExFatLib/ExFatVolume.h
+++ b/src/ExFatLib/ExFatVolume.h
@@ -33,8 +33,7 @@
*/
class ExFatVolume : public ExFatPartition {
public:
- ExFatVolume() {
- }
+ ExFatVolume() {}
/**
* Initialize an FatVolume object.
* \param[in] dev Device block driver.
@@ -49,7 +48,7 @@ class ExFatVolume : public ExFatPartition {
if (!chdir()) {
return false;
}
- if (setCwv) {
+ if (setCwv || !m_cwv) {
m_cwv = this;
}
return true;
diff --git a/src/FatLib/FatFile.h b/src/FatLib/FatFile.h
index 73fd503..0e3b030 100644
--- a/src/FatLib/FatFile.h
+++ b/src/FatLib/FatFile.h
@@ -110,8 +110,7 @@ const uint8_t FNAME_FLAG_LC_EXT = FAT_CASE_LC_EXT;
class FatFile {
public:
/** Create an instance. */
- FatFile() : m_attributes(FILE_ATTR_CLOSED), m_error(0), m_flags(0) {}
-
+ FatFile() {}
/** Create a file object and open it in the current working directory.
*
* \param[in] path A path for a file to be opened.
@@ -120,11 +119,10 @@ class FatFile {
* OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t).
*/
FatFile(const char* path, oflag_t oflag) {
- m_attributes = FILE_ATTR_CLOSED;
- m_error = 0;
open(path, oflag);
}
#if DESTRUCTOR_CLOSES_FILE
+ /** Destructor */
~FatFile() {
if (isOpen()) {
close();
@@ -135,11 +133,25 @@ class FatFile {
*
* \return true if a file is open.
*/
- operator bool() {
- return isOpen();
+ operator bool() const {return isOpen();}
+ /** \return The number of bytes available from the current position
+ * to EOF for normal files. INT_MAX is returned for very large files.
+ *
+ * available32() is recomended for very large files.
+ *
+ * Zero is returned for directory files.
+ *
+ */
+ int available() const {
+ uint32_t n = available32();
+ return n > INT_MAX ? INT_MAX : n;
+ }
+ /** \return The number of bytes available from the current position
+ * to EOF for normal files. Zero is returned for directory files.
+ */
+ uint32_t available32() const {
+ return isFile() ? fileSize() - curPosition() : 0;
}
- /** Arduino name for sync() */
- void flush() {sync();}
/** Clear all error bits. */
void clearError() {
m_error = 0;
@@ -148,8 +160,117 @@ class FatFile {
void clearWriteError() {
m_error &= ~WRITE_ERROR;
}
+ /** Close a file and force cached data and directory information
+ * to be written to the storage device.
+ *
+ * \return true for success or false for failure.
+ */
+ bool close();
+ /** Check for contiguous file and return its raw sector range.
+ *
+ * \param[out] bgnSector the first sector address for the file.
+ * \param[out] endSector the last sector address for the file.
+ *
+ * Set the contiguous flag if the file is contiguous.
+ * The parameters may be nullptr to only set the flag.
+ * \return true for success or false for failure.
+ */
+ bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector);
+ /** Create and open a new contiguous file of a specified size.
+ *
+ * \param[in] dirFile The directory where the file will be created.
+ * \param[in] path A path with a valid file name.
+ * \param[in] size The desired file size.
+ *
+ * \return true for success or false for failure.
+ */
+ bool createContiguous(FatFile* dirFile,
+ const char* path, uint32_t size);
+ /** Create and open a new contiguous file of a specified size.
+ *
+ * \param[in] path A path with a valid file name.
+ * \param[in] size The desired file size.
+ *
+ * \return true for success or false for failure.
+ */
+ bool createContiguous(const char* path, uint32_t size);
+ /** \return The current cluster number for a file or directory. */
+ uint32_t curCluster() const {return m_curCluster;}
+
+ /** \return The current position for a file or directory. */
+ uint32_t curPosition() const {return m_curPosition;}
+ /** Return a file's directory entry.
+ *
+ * \param[out] dir Location for return of the file's directory entry.
+ *
+ * \return true for success or false for failure.
+ */
+ bool dirEntry(DirFat_t* dir);
/** \return Directory entry index. */
uint16_t dirIndex() const {return m_dirIndex;}
+ /** \return The number of bytes allocated to a directory or zero
+ * if an error occurs.
+ */
+ uint32_t dirSize();
+ /** Dump file in Hex
+ * \param[in] pr Print stream for list.
+ * \param[in] pos Start position in file.
+ * \param[in] n number of locations to dump.
+ */
+ void dmpFile(print_t* pr, uint32_t pos, size_t n);
+ /** Test for the existence of a file in a directory
+ *
+ * \param[in] path Path of the file to be tested for.
+ *
+ * The calling instance must be an open directory file.
+ *
+ * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory
+ * dirFile.
+ *
+ * \return True if the file exists.
+ */
+ bool exists(const char* path) {
+ FatFile file;
+ return file.open(this, path, O_RDONLY);
+ }
+ /** get position for streams
+ * \param[out] pos struct to receive position
+ */
+ void fgetpos(fspos_t* pos) const;
+ /**
+ * Get a string from a file.
+ *
+ * fgets() reads bytes from a file into the array pointed to by \a str, until
+ * \a num - 1 bytes are read, or a delimiter is read and transferred to
+ * \a str, or end-of-file is encountered. The string is then terminated
+ * with a null byte.
+ *
+ * fgets() deletes CR, '\\r', from the string. This insures only a '\\n'
+ * terminates the string for Windows text files which use CRLF for newline.
+ *
+ * \param[out] str Pointer to the array where the string is stored.
+ * \param[in] num Maximum number of characters to be read
+ * (including the final null byte). Usually the length
+ * of the array \a str is used.
+ * \param[in] delim Optional set of delimiters. The default is "\n".
+ *
+ * \return For success fgets() returns the length of the string in \a str.
+ * If no data is read, fgets() returns zero for EOF or -1 if an error
+ * occurred.
+ */
+ int fgets(char* str, int num, char* delim = nullptr);
+ /** \return The total number of bytes in a file. */
+ uint32_t fileSize() const {return m_fileSize;}
+ /** \return first sector of file or zero for empty file. */
+ uint32_t firstBlock() const {return firstSector();}
+ /** \return Address of first sector or zero for empty file. */
+ uint32_t firstSector() const;
+ /** Arduino name for sync() */
+ void flush() {sync();}
+ /** set position for streams
+ * \param[in] pos struct with value for new position
+ */
+ void fsetpos(const fspos_t* pos);
/** Get a file's access date.
*
* \param[out] pdate Packed date for directory entry.
@@ -191,139 +312,6 @@ class FatFile {
* \return true for success or false for failure.
*/
bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime);
- /** \return value of writeError */
- bool getWriteError() const {
- return isOpen() ? m_error & WRITE_ERROR : true;
- }
- /** get position for streams
- * \param[out] pos struct to receive position
- */
- void fgetpos(fspos_t* pos) const;
- /** set position for streams
- * \param[in] pos struct with value for new position
- */
- void fsetpos(const fspos_t* pos);
- /** \return The number of bytes available from the current position
- * to EOF for normal files. Zero is returned for directory files.
- */
- uint32_t available32() {
- return isFile() ? fileSize() - curPosition() : 0;
- }
- /** \return The number of bytes available from the current position
- * to EOF for normal files. INT_MAX is returned for very large files.
- *
- * available64() is recomended for very large files.
- *
- * Zero is returned for directory files.
- *
- */
- int available() {
- uint32_t n = available32();
- return n > INT_MAX ? INT_MAX : n;
- }
- /** Close a file and force cached data and directory information
- * to be written to the storage device.
- *
- * \return true for success or false for failure.
- */
- bool close();
- /** Check for contiguous file and return its raw sector range.
- *
- * \param[out] bgnSector the first sector address for the file.
- * \param[out] endSector the last sector address for the file.
- *
- * Set the contiguous flag if the file is contiguous.
- * The parameters may be nullptr to only set the flag.
- * \return true for success or false for failure.
- */
- bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector);
-
- /** Create and open a new contiguous file of a specified size.
- *
- * \param[in] dirFile The directory where the file will be created.
- * \param[in] path A path with a valid file name.
- * \param[in] size The desired file size.
- *
- * \return true for success or false for failure.
- */
- bool createContiguous(FatFile* dirFile,
- const char* path, uint32_t size);
- /** Create and open a new contiguous file of a specified size.
- *
- * \param[in] path A path with a valid file name.
- * \param[in] size The desired file size.
- *
- * \return true for success or false for failure.
- */
- bool createContiguous(const char* path, uint32_t size);
-
- /** \return The current cluster number for a file or directory. */
- uint32_t curCluster() const {return m_curCluster;}
-
- /** \return The current position for a file or directory. */
- uint32_t curPosition() const {return m_curPosition;}
-
- /** Return a file's directory entry.
- *
- * \param[out] dir Location for return of the file's directory entry.
- *
- * \return true for success or false for failure.
- */
- bool dirEntry(DirFat_t* dir);
- /** \return The number of bytes allocated to a directory or zero
- * if an error occurs.
- */
- uint32_t dirSize();
- /** Dump file in Hex
- * \param[in] pr Print stream for list.
- * \param[in] pos Start position in file.
- * \param[in] n number of locations to dump.
- */
- void dmpFile(print_t* pr, uint32_t pos, size_t n);
- /** Test for the existence of a file in a directory
- *
- * \param[in] path Path of the file to be tested for.
- *
- * The calling instance must be an open directory file.
- *
- * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory
- * dirFile.
- *
- * \return True if the file exists.
- */
- bool exists(const char* path) {
- FatFile file;
- return file.open(this, path, O_RDONLY);
- }
- /**
- * Get a string from a file.
- *
- * fgets() reads bytes from a file into the array pointed to by \a str, until
- * \a num - 1 bytes are read, or a delimiter is read and transferred to
- * \a str, or end-of-file is encountered. The string is then terminated
- * with a null byte.
- *
- * fgets() deletes CR, '\\r', from the string. This insures only a '\\n'
- * terminates the string for Windows text files which use CRLF for newline.
- *
- * \param[out] str Pointer to the array where the string is stored.
- * \param[in] num Maximum number of characters to be read
- * (including the final null byte). Usually the length
- * of the array \a str is used.
- * \param[in] delim Optional set of delimiters. The default is "\n".
- *
- * \return For success fgets() returns the length of the string in \a str.
- * If no data is read, fgets() returns zero for EOF or -1 if an error
- * occurred.
- */
- int fgets(char* str, int num, char* delim = nullptr);
-
- /** \return The total number of bytes in a file. */
- uint32_t fileSize() const {return m_fileSize;}
- /** \return first sector of file or zero for empty file. */
- uint32_t firstBlock() const {return firstSector();}
- /** \return Address of first sector or zero for empty file. */
- uint32_t firstSector() const;
/**
* Get a file's name followed by a zero byte.
*
@@ -331,9 +319,9 @@ class FatFile {
* \param[in] size The size of the array in bytes. The array
* must be at least 13 bytes long. The file's name will be
* truncated if the file's name is too long.
- * \return true for success or false for failure.
+ * \return length for success or zero for failure.
*/
- bool getName(char* name, size_t size);
+ size_t getName(char* name, size_t size);
/**
* Get a file's Short File Name followed by a zero byte.
*
@@ -341,7 +329,11 @@ class FatFile {
* The array must be at least 13 bytes long.
* \return true for success or false for failure.
*/
- bool getSFN(char* name);
+ size_t getSFN(char* name);
+ /** \return value of writeError */
+ bool getWriteError() const {
+ return isOpen() ? m_error & WRITE_ERROR : true;
+ }
/**
* Check for BlockDevice busy.
*
@@ -362,21 +354,21 @@ class FatFile {
bool isLFN() const {return m_lfnOrd;}
/** \return True if this is an open file/directory. */
bool isOpen() const {return m_attributes;}
+ /** \return True file is readable. */
+ bool isReadable() const {return m_flags & FILE_FLAG_READ;}
+ /** \return True if file is read-only */
+ bool isReadOnly() const {return m_attributes & FILE_ATTR_READ_ONLY;}
/** \return True if this is the root directory. */
bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
/** \return True if this is the FAT32 root directory. */
bool isRoot32() const {return m_attributes & FILE_ATTR_ROOT32;}
/** \return True if this is the FAT12 of FAT16 root directory. */
bool isRootFixed() const {return m_attributes & FILE_ATTR_ROOT_FIXED;}
- /** \return True if file is read-only */
- bool isReadOnly() const {return m_attributes & FILE_ATTR_READ_ONLY;}
/** \return True if this is a subdirectory. */
bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
/** \return True if this is a system file. */
bool isSystem() const {return m_attributes & FILE_ATTR_SYSTEM;}
/** \return True file is writable. */
- bool isReadable() const {return m_flags & FILE_FLAG_READ;}
- /** \return True file is writable. */
bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
/** Check for a legal 8.3 character.
* \param[in] c Character to be checked.
@@ -430,7 +422,14 @@ class FatFile {
* \return true for success or false for failure.
*/
bool mkdir(FatFile* dir, const char* path, bool pFlag = true);
-
+ /** No longer implemented due to Long File Names.
+ *
+ * Use getName(char* name, size_t size).
+ * \return a pointer to replacement suggestion.
+ */
+ const char* name() const {
+ return "use getName()";
+ }
/** Open a file in the volume root directory.
*
* \param[in] vol Volume where the file is located.
@@ -464,7 +463,7 @@ class FatFile {
*
* \param[in] path A path with a valid name for a file to be opened.
*
- * \param[in] oflag Values for \a oflag are constructed by a
+ * \param[in] oflag Values for \a oflag are constructed by a
* bitwise-inclusive OR of flags from the following list.
* Only one of O_RDONLY, O_READ, O_WRONLY, O_WRITE, or
* O_RDWR is allowed.
@@ -536,7 +535,7 @@ class FatFile {
* \return The byte if no error and not at eof else -1;
*/
int peek();
- /** Allocate contiguous clusters to an empty file.
+ /** Allocate contiguous clusters to an empty file.
*
* The file must be empty with no clusters allocated.
*
@@ -546,13 +545,29 @@ class FatFile {
* \return true for success or false for failure.
*/
bool preAllocate(uint32_t length);
+ /** Print a file's access date
+ *
+ * \param[in] pr Print stream for output.
+ *
+ * \return The number of characters printed.
+ */
+ size_t printAccessDate(print_t* pr);
+ /** Print a file's access date
+ *
+ * \param[in] pr Print stream for output.
+ *
+ * \return The number of characters printed.
+ */
+ size_t printAccessDateTime(print_t* pr) {
+ return printAccessDate(pr);
+ }
/** Print a file's creation date and time
*
* \param[in] pr Print stream for output.
*
* \return The number of bytes printed.
*/
- size_t printCreateDateTime(print_t* pr);
+ size_t printCreateDateTime(print_t* pr);
/** %Print a directory date field.
*
* Format is yyyy-mm-dd.
@@ -627,22 +642,14 @@ class FatFile {
}
return write(str, &buf[sizeof(buf)] - str);
}
- /** Print a file's access date
+ /** Print a file's size.
*
* \param[in] pr Print stream for output.
*
- * \return The number of characters printed.
+ * \return The number of characters printed is returned
+ * for success and zero is returned for failure.
*/
- size_t printAccessDate(print_t* pr);
- /** Print a file's access date
- *
- * \param[in] pr Print stream for output.
- *
- * \return The number of characters printed.
- */
- size_t printAccessDateTime(print_t* pr) {
- return printAccessDate(pr);
- }
+ size_t printFileSize(print_t* pr);
/** Print a file's modify date and time
*
* \param[in] pr Print stream for output.
@@ -657,14 +664,7 @@ class FatFile {
* \return true for success or false for failure.
*/
size_t printName(print_t* pr);
- /** Print a file's size.
- *
- * \param[in] pr Print stream for output.
- *
- * \return The number of characters printed is returned
- * for success and zero is returned for failure.
- */
- size_t printFileSize(print_t* pr);
+
/** Print a file's Short File Name.
*
* \param[in] pr Print stream for output.
@@ -731,10 +731,6 @@ class FatFile {
* \return true for success or false for failure.
*/
bool remove(const char* path);
- /** Set the file's current position to zero. */
- void rewind() {
- seekSet(0);
- }
/** Rename a file or subdirectory.
* \note the renamed file will be moved to the current volume working
* directory.
@@ -752,6 +748,10 @@ class FatFile {
* \return true for success or false for failure.
*/
bool rename(FatFile* dirFile, const char* newPath);
+ /** Set the file's current position to zero. */
+ void rewind() {
+ seekSet(0);
+ }
/** Remove a directory file.
*
* The directory file will be removed only if it is empty and is not the
@@ -802,14 +802,12 @@ class FatFile {
* \return true for success or false for failure.
*/
bool seekSet(uint32_t pos);
-
/** The sync() call causes all modified data and directory fields
* to be written to the storage device.
*
* \return true for success or false for failure.
*/
bool sync();
-
/** Set a file's timestamps in its directory entry.
*
* \param[in] flags Values for \a flags are constructed by a bitwise-inclusive
@@ -862,7 +860,6 @@ class FatFile {
bool truncate(uint32_t length) {
return seekSet(length) && truncate();
}
-
/** Write a string to a file. Used by the Arduino Print class.
* \param[in] str Pointer to the string.
* Use getWriteError to check for errors.
@@ -980,9 +977,9 @@ class FatFile {
static const uint8_t WRITE_ERROR = 0X1;
static const uint8_t READ_ERROR = 0X2;
- uint8_t m_attributes; // File attributes
- uint8_t m_error; // Error bits.
- uint8_t m_flags; // See above for definition of m_flags bits
+ uint8_t m_attributes = FILE_ATTR_CLOSED;
+ uint8_t m_error = 0; // Error bits.
+ uint8_t m_flags = 0; // See above for definition of m_flags bits
uint8_t m_lfnOrd;
uint16_t m_dirIndex; // index of directory entry in dir file
FatVolume* m_vol; // volume where file is located
diff --git a/src/FatLib/FatFileLFN.cpp b/src/FatLib/FatFileLFN.cpp
index 02ddd3d..26e8c49 100644
--- a/src/FatLib/FatFileLFN.cpp
+++ b/src/FatLib/FatFileLFN.cpp
@@ -70,23 +70,22 @@ static uint16_t lfnGetChar(DirLfn_t* ldir, uint8_t i) {
return 0;
}
//------------------------------------------------------------------------------
-static bool lfnGetName(DirLfn_t* ldir, char* name, size_t n) {
+static size_t lfnGetName(DirLfn_t* ldir, char* name, size_t n) {
uint8_t i;
size_t k = 13*((ldir->order & 0X1F) - 1);
for (i = 0; i < 13; i++) {
uint16_t c = lfnGetChar(ldir, i);
- if (c == 0 || k >= n) {
+ if (c == 0 || k >= (n - 1)) {
break;
}
name[k++] = c >= 0X7F ? '?' : c;
}
- // Terminate with zero byte if name fits.
- if (k < n && (ldir->order & FAT_ORDER_LAST_LONG_ENTRY)) {
- name[k] = 0;
+ // Terminate with zero byte.
+ if (k >= n) {
+ k = n - 1;
}
- // Truncate if name is too long.
- name[n - 1] = 0;
- return true;
+ name[k] = '\0';
+ return k;
}
//------------------------------------------------------------------------------
inline bool lfnLegalChar(uint8_t c) {
@@ -122,7 +121,8 @@ static void lfnPutName(DirLfn_t* ldir, const char* name, size_t n) {
}
}
//==============================================================================
-bool FatFile::getName(char* name, size_t size) {
+size_t FatFile::getName(char* name, size_t size) {
+ size_t n = 0;
FatFile dirFile;
DirLfn_t* ldir;
if (!isOpen() || size < 13) {
@@ -154,20 +154,21 @@ bool FatFile::getName(char* name, size_t size) {
DBG_FAIL_MACRO;
goto fail;
}
- if (!lfnGetName(ldir, name, size)) {
+ n = lfnGetName(ldir, name, size);
+ if (n == 0) {
DBG_FAIL_MACRO;
goto fail;
}
if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
- return true;
+ return n;
}
}
// Fall into fail.
DBG_FAIL_MACRO;
fail:
- name[0] = 0;
- return false;
+ name[0] = '\0';
+ return 0;
}
//------------------------------------------------------------------------------
bool FatFile::openCluster(FatFile* file) {
@@ -643,7 +644,7 @@ bool FatFile::remove() {
//------------------------------------------------------------------------------
bool FatFile::lfnUniqueSfn(fname_t* fname) {
const uint8_t FIRST_HASH_SEQ = 2; // min value is 2
- uint8_t pos = fname->seqPos;;
+ uint8_t pos = fname->seqPos;
DirFat_t* dir;
uint16_t hex;
diff --git a/src/FatLib/FatFileSFN.cpp b/src/FatLib/FatFileSFN.cpp
index 821a94c..194bf8b 100644
--- a/src/FatLib/FatFileSFN.cpp
+++ b/src/FatLib/FatFileSFN.cpp
@@ -28,7 +28,7 @@
#include "FatFile.h"
#include "FatVolume.h"
//------------------------------------------------------------------------------
-bool FatFile::getSFN(char* name) {
+size_t FatFile::getSFN(char* name) {
uint8_t j = 0;
uint8_t lcBit = FAT_CASE_LC_BASE;
DirFat_t* dir;
@@ -40,7 +40,7 @@ bool FatFile::getSFN(char* name) {
if (isRoot()) {
name[0] = '/';
name[1] = '\0';
- return true;
+ return 1;
}
// cache entry
dir = reinterpret_cast(cacheDirEntry(FsCache::CACHE_FOR_READ));
@@ -64,11 +64,12 @@ bool FatFile::getSFN(char* name) {
}
name[j++] = c;
}
- name[j] = 0;
- return true;
+ name[j] = '\0';
+ return j;
fail:
- return false;
+ name[0] = '\0';
+ return 0;
}
//------------------------------------------------------------------------------
size_t FatFile::printSFN(print_t* pr) {
@@ -84,7 +85,7 @@ size_t FatFile::printSFN(print_t* pr) {
}
#if !USE_LONG_FILE_NAMES
//------------------------------------------------------------------------------
-bool FatFile::getName(char* name, size_t size) {
+size_t FatFile::getName(char* name, size_t size) {
return size < 13 ? 0 : getSFN(name);
}
//------------------------------------------------------------------------------
diff --git a/src/FatLib/FatPartition.h b/src/FatLib/FatPartition.h
index ff71638..0c019fb 100644
--- a/src/FatLib/FatPartition.h
+++ b/src/FatLib/FatPartition.h
@@ -67,26 +67,26 @@ class FatPartition {
public:
/** Create an instance of FatPartition
*/
- FatPartition() : m_fatType(0) {}
+ FatPartition() {}
/** \return The shift count required to multiply by bytesPerCluster. */
- uint8_t bytesPerClusterShift() {
+ uint8_t bytesPerClusterShift() const {
return m_sectorsPerClusterShift + m_bytesPerSectorShift;
}
/** \return Number of bytes in a cluster. */
- uint16_t bytesPerCluster() {
+ uint16_t bytesPerCluster() const {
return m_bytesPerSector << m_sectorsPerClusterShift;
}
/** \return Number of bytes per sector. */
- uint16_t bytesPerSector() {
+ uint16_t bytesPerSector() const {
return m_bytesPerSector;
}
/** \return The shift count required to multiply by bytesPerCluster. */
- uint8_t bytesPerSectorShift() {
+ uint8_t bytesPerSectorShift() const {
return m_bytesPerSectorShift;
}
/** \return Mask for sector offset. */
- uint16_t sectorMask() {
+ uint16_t sectorMask() const {
return m_sectorMask;
}
/** \return The volume's cluster size in sectors. */
@@ -120,7 +120,7 @@ class FatPartition {
return m_dataStartSector;
}
/** \return The number of File Allocation Tables. */
- uint8_t fatCount() {
+ uint8_t fatCount() const {
return 2;
}
/** \return The logical sector number for the start of the first FAT. */
@@ -195,7 +195,7 @@ class FatPartition {
uint8_t m_sectorsPerCluster; // Cluster size in sectors.
uint8_t m_clusterSectorMask; // Mask to extract sector of cluster.
uint8_t m_sectorsPerClusterShift; // Cluster count to sector count shift.
- uint8_t m_fatType; // Volume type (12, 16, OR 32).
+ uint8_t m_fatType = 0; // Volume type (12, 16, OR 32).
uint16_t m_rootDirEntryCount; // Number of entries in FAT16 root dir.
uint32_t m_allocSearchStart; // Start cluster for alloc search.
uint32_t m_sectorsPerFat; // FAT size in sectors
diff --git a/src/FatLib/FatVolume.h b/src/FatLib/FatVolume.h
index 6217be8..df8e4c1 100644
--- a/src/FatLib/FatVolume.h
+++ b/src/FatLib/FatVolume.h
@@ -51,7 +51,7 @@ class FatVolume : public FatPartition {
if (!chdir()) {
return false;
}
- if (setCwv) {
+ if (setCwv || !m_cwv) {
m_cwv = this;
}
return true;
diff --git a/src/FsLib/FsFile.cpp b/src/FsLib/FsFile.cpp
index 928f967..e3a6da4 100644
--- a/src/FsLib/FsFile.cpp
+++ b/src/FsLib/FsFile.cpp
@@ -90,7 +90,6 @@ bool FsBaseFile::open(FsVolume* vol, const char* path, oflag_t oflag) {
return true;
}
m_fFile = nullptr;
- return false;
} else if (vol->m_xVol) {
m_xFile = new (m_fileMem) ExFatFile;
if (m_xFile && m_xFile->open(vol->m_xVol, path, oflag)) {
@@ -155,6 +154,27 @@ bool FsBaseFile::openNext(FsBaseFile* dir, oflag_t oflag) {
return false;
}
//------------------------------------------------------------------------------
+bool FsBaseFile::openRoot(FsVolume* vol) {
+ if (!vol) {
+ return false;
+ }
+ close();
+ if (vol->m_fVol) {
+ m_fFile = new (m_fileMem) FatFile;
+ if (m_fFile && m_fFile->openRoot(vol->m_fVol)) {
+ return true;
+ }
+ m_fFile = nullptr;
+ } else if (vol->m_xVol) {
+ m_xFile = new (m_fileMem) ExFatFile;
+ if (m_xFile && m_xFile->openRoot(vol->m_xVol)) {
+ return true;
+ }
+ m_xFile = nullptr;
+ }
+ return false;
+}
+//------------------------------------------------------------------------------
bool FsBaseFile::remove() {
if (m_fFile) {
if (m_fFile->remove()) {
diff --git a/src/FsLib/FsFile.h b/src/FsLib/FsFile.h
index 2671964..3d8e7a1 100644
--- a/src/FsLib/FsFile.h
+++ b/src/FsLib/FsFile.h
@@ -37,7 +37,18 @@
*/
class FsBaseFile {
public:
- FsBaseFile() : m_fFile(nullptr), m_xFile(nullptr) {}
+ /** Create an instance. */
+ FsBaseFile() {}
+ /** Create a file object and open it in the current working directory.
+ *
+ * \param[in] path A path for a file to be opened.
+ *
+ * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
+ * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t).
+ */
+ FsBaseFile(const char* path, oflag_t oflag) {
+ open(path, oflag);
+ }
~FsBaseFile() {close();}
/** Copy constructor.
@@ -58,11 +69,17 @@ class FsBaseFile {
/** \return number of bytes available from the current position to EOF
* or INT_MAX if more than INT_MAX bytes are available.
*/
- int available() {
+ int available() const {
return m_fFile ? m_fFile->available() :
m_xFile ? m_xFile->available() : 0;
}
-
+ /** \return The number of bytes available from the current position
+ * to EOF for normal files. Zero is returned for directory files.
+ */
+ uint64_t available64() const {
+ return m_fFile ? m_fFile->available32() :
+ m_xFile ? m_xFile->available64() : 0;
+ }
/** Clear writeError. */
void clearWriteError() {
if (m_fFile) m_fFile->clearWriteError();
@@ -186,7 +203,7 @@ class FsBaseFile {
m_xFile ? m_xFile->getCreateDateTime(pdate, ptime) : false;
}
/** \return All error bits. */
- uint8_t getError() {
+ uint8_t getError() const {
return m_fFile ? m_fFile->getError() :
m_xFile ? m_xFile->getError() : 0XFF;
}
@@ -248,6 +265,11 @@ class FsBaseFile {
* \return true if the file is a directory.
*/
bool isDirectory() const {return isDir();}
+ /** \return True if this is a normal file. */
+ bool isFile() const {
+ return m_fFile ? m_fFile->isFile() :
+ m_xFile ? m_xFile->isFile() : false;
+ }
/** \return True if this is a hidden file else false. */
bool isHidden() const {
return m_fFile ? m_fFile->isHidden() :
@@ -255,11 +277,26 @@ class FsBaseFile {
}
/** \return True if this is an open file/directory else false. */
bool isOpen() const {return m_fFile || m_xFile;}
+ /** \return True file is readable. */
+ bool isReadable() const {
+ return m_fFile ? m_fFile->isReadable() :
+ m_xFile ? m_xFile->isReadable() : false;
+ }
+ /** \return True if file is read-only */
+ bool isReadOnly() const {
+ return m_fFile ? m_fFile->isReadOnly() :
+ m_xFile ? m_xFile->isReadOnly() : false;
+ }
/** \return True if this is a subdirectory file else false. */
bool isSubDir() const {
return m_fFile ? m_fFile->isSubDir() :
m_xFile ? m_xFile->isSubDir() : false;
}
+ /** \return True file is writable. */
+ bool isWritable() const {
+ return m_fFile ? m_fFile->isWritable() :
+ m_xFile ? m_xFile->isWritable() : false;
+ }
#if ENABLE_ARDUINO_SERIAL
/** List directory contents.
*
@@ -412,6 +449,15 @@ class FsBaseFile {
* \return a file object.
*/
bool openNext(FsBaseFile* dir, oflag_t oflag = O_RDONLY);
+ /** Open a volume's root directory.
+ *
+ * \param[in] vol The SdFs volume containing the root directory to be opened.
+ *
+ * \return true for success or false for failure.
+ */
+ bool openRoot(FsVolume* vol);
+ /** \return the current file position. */
+ uint64_t position() const {return curPosition();}
/** Return the next available byte without consuming it.
*
* \return The byte if no error and not at eof else -1;
@@ -420,6 +466,21 @@ class FsBaseFile {
return m_fFile ? m_fFile->peek() :
m_xFile ? m_xFile->peek() : -1;
}
+ /** Allocate contiguous clusters to an empty file.
+ *
+ * The file must be empty with no clusters allocated.
+ *
+ * The file will contain uninitialized data for FAT16/FAT32 files.
+ * exFAT files will have zero validLength and dataLength will equal
+ * the requested length.
+ *
+ * \param[in] length size of the file in bytes.
+ * \return true for success or false for failure.
+ */
+ bool preAllocate(uint64_t length) {
+ return m_fFile ? length < (1ULL << 32) && m_fFile->preAllocate(length) :
+ m_xFile ? m_xFile->preAllocate(length) : false;
+ }
/** Print a file's access date and time
*
* \param[in] pr Print stream for output.
@@ -440,55 +501,7 @@ class FsBaseFile {
return m_fFile ? m_fFile->printCreateDateTime(pr) :
m_xFile ? m_xFile->printCreateDateTime(pr) : 0;
}
- /** Print a file's modify date and time
- *
- * \param[in] pr Print stream for output.
- *
- * \return true for success or false for failure.
- */
- size_t printModifyDateTime(print_t* pr) {
- return m_fFile ? m_fFile->printModifyDateTime(pr) :
- m_xFile ? m_xFile->printModifyDateTime(pr) : 0;
- }
- /** Print a file's name
- *
- * \param[in] pr Print stream for output.
- *
- * \return true for success or false for failure.
- */
- size_t printName(print_t* pr) {
- return m_fFile ? m_fFile->printName(pr) :
- m_xFile ? m_xFile->printName(pr) : 0;
- }
- /** Print a file's size.
- *
- * \param[in] pr Print stream for output.
- *
- * \return The number of characters printed is returned
- * for success and zero is returned for failure.
- */
- size_t printFileSize(print_t* pr) {
- return m_fFile ? m_fFile->printFileSize(pr) :
- m_xFile ? m_xFile->printFileSize(pr) : 0;
- }
- /** Allocate contiguous clusters to an empty file.
- *
- * The file must be empty with no clusters allocated.
- *
- * The file will contain uninitialized data for FAT16/FAT32 files.
- * exFAT files will have zero validLength and dataLength will equal
- * the requested length.
- *
- * \param[in] length size of the file in bytes.
- * \return true for success or false for failure.
- */
- bool preAllocate(uint64_t length) {
- return m_fFile ? length < (1ULL << 32) && m_fFile->preAllocate(length) :
- m_xFile ? m_xFile->preAllocate(length) : false;
- }
- /** \return the current file position. */
- uint64_t position() const {return curPosition();}
- /** Print a number followed by a field terminator.
+ /** Print a number followed by a field terminator.
* \param[in] value The number to be printed.
* \param[in] term The field terminator. Use '\\n' for CR LF.
* \param[in] prec Number of digits after decimal point.
@@ -517,6 +530,37 @@ class FsBaseFile {
return m_fFile ? m_fFile->printField(value, term) :
m_xFile ? m_xFile->printField(value, term) : 0;
}
+ /** Print a file's size.
+ *
+ * \param[in] pr Print stream for output.
+ *
+ * \return The number of characters printed is returned
+ * for success and zero is returned for failure.
+ */
+ size_t printFileSize(print_t* pr) {
+ return m_fFile ? m_fFile->printFileSize(pr) :
+ m_xFile ? m_xFile->printFileSize(pr) : 0;
+ }
+ /** Print a file's modify date and time
+ *
+ * \param[in] pr Print stream for output.
+ *
+ * \return true for success or false for failure.
+ */
+ size_t printModifyDateTime(print_t* pr) {
+ return m_fFile ? m_fFile->printModifyDateTime(pr) :
+ m_xFile ? m_xFile->printModifyDateTime(pr) : 0;
+ }
+ /** Print a file's name
+ *
+ * \param[in] pr Print stream for output.
+ *
+ * \return true for success or false for failure.
+ */
+ size_t printName(print_t* pr) {
+ return m_fFile ? m_fFile->printName(pr) :
+ m_xFile ? m_xFile->printName(pr) : 0;
+ }
/** Read the next byte from a file.
*
* \return For success return the next byte in the file as an int.
@@ -698,7 +742,6 @@ class FsBaseFile {
m_xFile->timestamp(flags, year, month, day, hour, minute, second) :
false;
}
-
/** Truncate a file to the current position.
*
* \return true for success or false for failure.
@@ -746,8 +789,8 @@ class FsBaseFile {
private:
newalign_t m_fileMem[FS_ALIGN_DIM(ExFatFile, FatFile)];
- FatFile* m_fFile;
- ExFatFile* m_xFile;
+ FatFile* m_fFile = nullptr;
+ ExFatFile* m_xFile = nullptr;
};
/**
* \class FsFile
diff --git a/src/FsLib/FsVolume.h b/src/FsLib/FsVolume.h
index a5b4b51..bcac166 100644
--- a/src/FsLib/FsVolume.h
+++ b/src/FsLib/FsVolume.h
@@ -39,7 +39,7 @@ class FsFile;
*/
class FsVolume {
public:
- FsVolume() : m_fVol(nullptr), m_xVol(nullptr) {}
+ FsVolume() {}
~FsVolume() {end();}
@@ -49,42 +49,15 @@ class FsVolume {
* \return true for success or false for failure.
*/
bool begin(BlockDevice* blockDev);
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ // Use sectorsPerCluster(). blocksPerCluster() will be removed in the future.
+ uint32_t blocksPerCluster() __attribute__ ((deprecated)) {return sectorsPerCluster();} //NOLINT
+#endif // DOXYGEN_SHOULD_SKIP_THIS
/** \return the number of bytes in a cluster. */
uint32_t bytesPerCluster() const {
return m_fVol ? m_fVol->bytesPerCluster() :
m_xVol ? m_xVol->bytesPerCluster() : 0;
}
- /** Change global working volume to this volume. */
- void chvol() {m_cwv = this;}
- /** \return The total number of clusters in the volume. */
- uint32_t clusterCount() const {
- return m_fVol ? m_fVol->clusterCount() :
- m_xVol ? m_xVol->clusterCount() : 0;
- }
- /** \return The logical sector number for the start of file data. */
- uint32_t dataStartSector() const {
- return m_fVol ? m_fVol->dataStartSector() :
- m_xVol ? m_xVol->clusterHeapStartSector() : 0;
- }
- /** \return The logical sector number for the start of the first FAT. */
- uint32_t fatStartSector() const {
- return m_fVol ? m_fVol->fatStartSector() :
- m_xVol ? m_xVol->fatStartSector() : 0;
- }
- /** \return the free cluster count. */
- uint32_t freeClusterCount() const {
- return m_fVol ? m_fVol->freeClusterCount() :
- m_xVol ? m_xVol->freeClusterCount() : 0;
- }
- /** \return The volume's cluster size in sectors. */
- uint32_t sectorsPerCluster() const {
- return m_fVol ? m_fVol->sectorsPerCluster() :
- m_xVol ? m_xVol->sectorsPerCluster() : 0;
- }
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- // Use sectorsPerCluster(). blocksPerCluster() will be removed in the future.
- uint32_t blocksPerCluster() __attribute__ ((deprecated)) {return sectorsPerCluster();} //NOLINT
-#endif // DOXYGEN_SHOULD_SKIP_THIS
/**
* Set volume working directory to root.
* \return true for success or false for failure.
@@ -102,6 +75,18 @@ class FsVolume {
return m_fVol ? m_fVol->chdir(path) :
m_xVol ? m_xVol->chdir(path) : false;
}
+ /** Change global working volume to this volume. */
+ void chvol() {m_cwv = this;}
+ /** \return The total number of clusters in the volume. */
+ uint32_t clusterCount() const {
+ return m_fVol ? m_fVol->clusterCount() :
+ m_xVol ? m_xVol->clusterCount() : 0;
+ }
+ /** \return The logical sector number for the start of file data. */
+ uint32_t dataStartSector() const {
+ return m_fVol ? m_fVol->dataStartSector() :
+ m_xVol ? m_xVol->clusterHeapStartSector() : 0;
+ }
/** free dynamic memory and end access to volume */
void end() {
m_fVol = nullptr;
@@ -117,6 +102,11 @@ class FsVolume {
return m_fVol ? m_fVol->exists(path) :
m_xVol ? m_xVol->exists(path) : false;
}
+ /** \return The logical sector number for the start of the first FAT. */
+ uint32_t fatStartSector() const {
+ return m_fVol ? m_fVol->fatStartSector() :
+ m_xVol ? m_xVol->fatStartSector() : 0;
+ }
/** \return Partition type, FAT_TYPE_EXFAT, FAT_TYPE_FAT32,
* FAT_TYPE_FAT16, or zero for error.
*/
@@ -124,6 +114,20 @@ class FsVolume {
return m_fVol ? m_fVol->fatType() :
m_xVol ? m_xVol->fatType() : 0;
}
+ /** \return the free cluster count. */
+ uint32_t freeClusterCount() const {
+ return m_fVol ? m_fVol->freeClusterCount() :
+ m_xVol ? m_xVol->freeClusterCount() : 0;
+ }
+ /**
+ * Check for BlockDevice busy.
+ *
+ * \return true if busy else false.
+ */
+ bool isBusy() {
+ return m_fVol ? m_fVol->isBusy() :
+ m_xVol ? m_xVol->isBusy() : false;
+ }
/** List directory contents.
*
* \param[in] pr Print object.
@@ -227,6 +231,11 @@ class FsVolume {
return m_fVol ? m_fVol->rmdir(path) :
m_xVol ? m_xVol->rmdir(path) : false;
}
+ /** \return The volume's cluster size in sectors. */
+ uint32_t sectorsPerCluster() const {
+ return m_fVol ? m_fVol->sectorsPerCluster() :
+ m_xVol ? m_xVol->sectorsPerCluster() : 0;
+ }
#if ENABLE_ARDUINO_SERIAL
/** List directory contents.
* \return true for success or false for failure.
@@ -369,8 +378,8 @@ class FsVolume {
FsVolume& operator=(const FsVolume& from);
static FsVolume* m_cwv;
- FatVolume* m_fVol;
- ExFatVolume* m_xVol;
+ FatVolume* m_fVol = nullptr;
+ ExFatVolume* m_xVol = nullptr;
BlockDevice* m_blockDev;
};
#endif // FsVolume_h
diff --git a/src/RingBuf.h b/src/RingBuf.h
new file mode 100644
index 0000000..eb4cd60
--- /dev/null
+++ b/src/RingBuf.h
@@ -0,0 +1,358 @@
+/**
+ * Copyright (c) 2011-2020 Bill Greiman
+ * This file is part of the SdFat library for SD memory cards.
+ *
+ * MIT License
+ *
+ * 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.
+ */
+#ifndef RingBuf_h
+#define RingBuf_h
+/**
+ * \file
+ * \brief Ring buffer for data loggers.
+ */
+#include "Arduino.h"
+#include "common/FmtNumber.h"
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+// Teensy 3.5/3.6 has hard fault at 0x20000000 for unaligned memcpy.
+#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
+inline bool is_aligned(const void* ptr, uintptr_t alignment) {
+ auto iptr = reinterpret_cast(ptr);
+ return !(iptr % alignment);
+}
+inline void memcpyBuf(void* dst, const void* src, size_t len) {
+ const uint8_t* b = reinterpret_cast(0X20000000UL);
+ uint8_t* d = reinterpret_cast(dst);
+ const uint8_t *s = reinterpret_cast(src);
+ if ((is_aligned(d, 4) && is_aligned(s, 4) && (len & 3) == 0) ||
+ !((d < b && b <= (d + len)) || (s < b && b <= (s + len)))) {
+ memcpy(dst, src, len);
+ } else {
+ while (len--) {
+ *d++ = *s++;
+ }
+ }
+}
+#else // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+inline void memcpyBuf(void* dst, const void* src, size_t len) {
+ memcpy(dst, src, len);
+}
+#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+#endif // DOXYGEN_SHOULD_SKIP_THIS
+/**
+ * \class RingBuf
+ * \brief Ring buffer for data loggers.
+ *
+ * This ring buffer may be used in ISRs. bytesFreeIsr(), bytesUsedIsr(),
+ * memcopyIn(), and memcopyOut() are ISR callable. For ISR use call
+ * memcopyIn() in the ISR and use writeOut() in non-interrupt code
+ * to write data to a file. readIn() and memcopyOut can be use in a
+ * similar way to provide file data to an ISR.
+ *
+ * Print into a RingBuf in an ISR should also work but has not been verified.
+ */
+template
+class RingBuf : public Print {
+ public:
+ /**
+ * RingBuf Constructor.
+ */
+ RingBuf() {}
+ /**
+ * Initialize RingBuf.
+ * \param[in] file Underlying file.
+ */
+ void begin(F* file) {
+ m_file = file;
+ m_count = 0;
+ m_head = 0;
+ m_tail = 0;
+ clearWriteError();
+ }
+ /**
+ *
+ * \return the RingBuf free space in bytes. Not ISR callable.
+ */
+ size_t bytesFree() const {
+ size_t count;
+ noInterrupts();
+ count = m_count;
+ interrupts();
+ return Size - count;
+ }
+ /**
+ * \return the RingBuf free space in bytes. ISR callable.
+ */
+ size_t bytesFreeIsr() const {
+ return Size - m_count;
+ }
+ /**
+ * \return the RingBuf used space in bytes. Not ISR callable.
+ */
+ size_t bytesUsed() const {
+ size_t count;
+ noInterrupts();
+ count = m_count;
+ interrupts();
+ return count;
+ }
+ /**
+ * \return the RingBuf used space in bytes. ISR callable.
+ */
+ size_t bytesUsedIsr() const {
+ return m_count;
+ }
+ /**
+ * Copy data to the RingBuf from buf.
+ * The number of bytes copied may be less than count if
+ * count is greater than bytesFree.
+ *
+ * This function may be used in an ISR with writeOut()
+ * in non-interrupt code.
+ *
+ * \param[in] buf Location of data to be copied.
+ * \param[in] count number of bytes to be copied.
+ * \return Number of bytes actually copied.
+ */
+ size_t memcpyIn(const void* buf, size_t count) {
+ const uint8_t* src = (const uint8_t*)buf;
+ size_t n = Size - m_count;
+ if (count > n) {
+ count = n;
+ }
+ size_t nread = 0;
+ while (nread != count) {
+ n = minSize(Size - m_head, count - nread);
+ memcpyBuf(m_buf + m_head, src + nread, n);
+ m_head = advance(m_head, n);
+ nread += n;
+ }
+ m_count += nread;
+ return nread;
+ }
+ /**
+ * Copy date from the RingBuf to buf.
+ * The number of bytes copied may be less than count if
+ * bytesUsed is less than count.
+ *
+ * This function may be used in an ISR with readIn() in
+ * non-interrupt code.
+ *
+ * \param[out] buf Location to receive the data.
+ * \param[in] count number of bytes to be copied.
+ * \return Number of bytes actually copied.
+ */
+ size_t memcpyOut(void* buf, size_t count) {
+ uint8_t* dst = reinterpret_cast(buf);
+ size_t nwrite = 0;
+ size_t n = m_count;
+ if (count > n) {
+ count = n;
+ }
+ while (nwrite != count) {
+ n = minSize(Size - m_tail, count - nwrite);
+ memcpyBuf(dst + nwrite, m_buf + m_tail, n);
+ m_tail = advance(m_tail, n);
+ nwrite += n;
+ }
+ m_count -= nwrite;
+ return nwrite;
+ }
+ /** Print a number followed by a field terminator.
+ * \param[in] value The number to be printed.
+ * \param[in] term The field terminator. Use '\\n' for CR LF.
+ * \param[in] prec Number of digits after decimal point.
+ * \return The number of bytes written.
+ */
+ size_t printField(double value, char term, uint8_t prec = 2) {
+ char buf[24];
+ char* str = buf + sizeof(buf);
+ if (term) {
+ *--str = term;
+ if (term == '\n') {
+ *--str = '\r';
+ }
+ }
+ str = fmtDouble(str, value, prec, false);
+ return write(str, buf + sizeof(buf) - str);
+ }
+ /** Print a number followed by a field terminator.
+ * \param[in] value The number to be printed.
+ * \param[in] term The field terminator. Use '\\n' for CR LF.
+ * \param[in] prec Number of digits after decimal point.
+ * \return The number of bytes written or -1 if an error occurs.
+ */
+ size_t printField(float value, char term, uint8_t prec = 2) {
+ return printField(static_cast(value), term, prec);
+ }
+ /** Print a number followed by a field terminator.
+ * \param[in] value The number to be printed.
+ * \param[in] term The field terminator. Use '\\n' for CR LF.
+ * \return The number of bytes written or -1 if an error occurs.
+ */
+ template
+ size_t printField(Type value, char term) {
+ char sign = 0;
+ char buf[3*sizeof(Type) + 3];
+ char* str = buf + sizeof(buf);
+
+ if (term) {
+ *--str = term;
+ if (term == '\n') {
+ *--str = '\r';
+ }
+ }
+ if (value < 0) {
+ value = -value;
+ sign = '-';
+ }
+ if (sizeof(Type) < 4) {
+ str = fmtBase10(str, (uint16_t)value);
+ } else {
+ str = fmtBase10(str, (uint32_t)value);
+ }
+ if (sign) {
+ *--str = sign;
+ }
+ return write((const uint8_t*)str, &buf[sizeof(buf)] - str);
+ }
+ /**
+ * Read data into the RingBuf from the underlying file.
+ * the number of bytes read may be less than count if
+ * bytesFree is less than count.
+ *
+ * This function may be used in non-interrupt code with
+ * memcopyOut() in an ISR.
+ *
+ * \param[in] count number of bytes to be read.
+ * \return Number of bytes actually read.
+ */
+ size_t readIn(size_t count) {
+ size_t nread = 0;
+ size_t n = bytesFree(); // Protected from interrupts.
+ if (count > n) {
+ count = n;
+ }
+ while (nread != count) {
+ n = minSize(Size - m_head, count - nread);
+ if ((size_t)m_file->read(m_buf + m_head, n) != n) {
+ return nread;
+ }
+ m_head = advance(m_head, n);
+ nread += n;
+ }
+ noInterrupts();
+ m_count += nread;
+ interrupts();
+ return nread;
+ }
+ /**
+ * Write all data in the RingBuf to the underlying file.
+ * \param[in] data Byte to be written.
+ * \return Number of bytes actually written.
+ */
+ bool sync() {
+ size_t n = bytesUsed();
+ return writeOut(n) == n;
+ }
+ /**
+ * Copy data to the RingBuf from buf.
+ *
+ * The number of bytes copied may be less than count if
+ * count is greater than bytesFree.
+ * Use getWriteError() to check for print errors and
+ * clearWriteError() to clear error.
+ *
+ * \param[in] buf Location of data to be written.
+ * \param[in] count number of bytes to be written.
+ * \return Number of bytes actually written.
+ */
+ size_t write(const void* buf, size_t count) {
+ if (count > bytesFree()) {
+ setWriteError();
+ }
+ return memcpyIn(buf, count);
+ }
+ /**
+ * Override virtual function in Print for efficiency.
+ *
+ * \param[in] buf Location of data to be written.
+ * \param[in] count number of bytes to be written.
+ * \return Number of bytes actually written.
+ */
+ size_t write(const uint8_t* buf, size_t count) override {
+ return write((const void*)buf, count);
+ }
+ /**
+ * Required function for Print.
+ * \param[in] data Byte to be written.
+ * \return Number of bytes actually written.
+ */
+ size_t write(uint8_t data) override {
+ return write(&data, 1);
+ }
+ /**
+ * Write data to file from RingBuf buffer.
+ * \param[in] count number of bytes to be written.
+ *
+ * The number of bytes written may be less than count if
+ * bytesUsed is less than count or if an error occurs.
+ *
+ * This function may be used in non-interrupt code with
+ * memcopyIn() in an ISR.
+ *
+ * \return Number of bytes actually written.
+ */
+ size_t writeOut(size_t count) {
+ size_t n = bytesUsed(); // Protected from interrupts;
+ if (count > n) {
+ count = n;
+ }
+ size_t nwrite = 0;
+ while (nwrite != count) {
+ n = minSize(Size - m_tail, count - nwrite);
+ if (m_file->write(m_buf + m_tail, n) != n) {
+ break;
+ }
+ m_tail = advance(m_tail, n);
+ nwrite += n;
+ }
+ noInterrupts();
+ m_count -= nwrite;
+ interrupts();
+ return nwrite;
+ }
+
+ private:
+ uint8_t __attribute__((aligned(4))) m_buf[Size];
+ F* m_file = nullptr;
+ volatile size_t m_count;
+ size_t m_head;
+ size_t m_tail;
+
+ size_t advance(size_t index, size_t n) {
+ index += n;
+ return index < Size ? index : index - Size;
+ }
+ // avoid macro MIN
+ size_t minSize(size_t a, size_t b) {return a < b ? a : b;}
+};
+#endif // RingBuf_h
diff --git a/src/SdCard/SdCardInfo.h b/src/SdCard/SdCardInfo.h
index eb0f959..d84f6cd 100644
--- a/src/SdCard/SdCardInfo.h
+++ b/src/SdCard/SdCardInfo.h
@@ -71,6 +71,7 @@
SD_CARD_ERROR(READ_START, "Bad readStart argument")\
SD_CARD_ERROR(READ_TIMEOUT, "Read data timeout")\
SD_CARD_ERROR(STOP_TRAN, "Multiple block stop failed")\
+ SD_CARD_ERROR(TRANSFER_COMPLETE, "SDIO transfer complete")\
SD_CARD_ERROR(WRITE_DATA, "Write data not accepted")\
SD_CARD_ERROR(WRITE_FIFO, "SDIO fifo write timeout")\
SD_CARD_ERROR(WRITE_START, "Bad writeStart argument")\
@@ -84,7 +85,6 @@
SD_CARD_ERROR(INVALID_CARD_CONFIG, "Invalid card config")\
SD_CARD_ERROR(FUNCTION_NOT_SUPPORTED, "Unsupported SDIO command")
-
enum {
#define SD_CARD_ERROR(e, m) SD_CARD_ERROR_##e,
SD_ERROR_CODE_LIST
diff --git a/src/SdCard/SdSpiCard.cpp b/src/SdCard/SdSpiCard.cpp
index 81a9063..dcae8f7 100644
--- a/src/SdCard/SdSpiCard.cpp
+++ b/src/SdCard/SdSpiCard.cpp
@@ -222,6 +222,7 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
//------------------------------------------------------------------------------
bool SdSpiCard::begin(SdSpiConfig spiConfig) {
SdMillis_t t0 = SysCall::curTimeMS();
+ m_spiActive = false;
m_errorCode = SD_CARD_ERROR_NONE;
m_type = 0;
m_csPin = spiConfig.csPin;
@@ -238,16 +239,12 @@ bool SdSpiCard::begin(SdSpiConfig spiConfig) {
spiBegin(spiConfig);
uint32_t arg;
#if ENABLE_DEDICATED_SPI
- m_sharedSpi = !(spiConfig.options & DEDICATED_SPI);
- m_spiActive = false;
m_curState = IDLE_STATE;
+ m_sharedSpi = spiOptionShared(spiConfig.options);
#else // ENABLE_DEDICATED_SPI
- if (spiConfig.options & DEDICATED_SPI) {
- error(SD_CARD_ERROR_INVALID_CARD_CONFIG);
- goto fail;
- }
+ // m_sharedSpi is a static const bool in this case.
+ static_assert(m_sharedSpi == true, "m_sharedSpi bug");
#endif // ENABLE_DEDICATED_SPI
-
spiStart();
// must supply min of 74 clock cycles with CS high.
@@ -333,6 +330,11 @@ bool SdSpiCard::begin(SdSpiConfig spiConfig) {
//------------------------------------------------------------------------------
// send command and return error code. Return zero for OK
uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
+#if ENABLE_DEDICATED_SPI
+ if (m_curState != IDLE_STATE && !syncDevice()) {
+ return 0XFF;
+ }
+#endif // ENABLE_DEDICATED_SPI
// select card
if (!m_spiActive) {
spiStart();
@@ -430,6 +432,11 @@ bool SdSpiCard::eraseSingleSectorEnable() {
}
//------------------------------------------------------------------------------
bool SdSpiCard::isBusy() {
+#if ENABLE_DEDICATED_SPI
+ if (m_curState == READ_STATE) {
+ return false;
+ }
+#endif // ENABLE_DEDICATED_SPI
bool rtn = true;
bool spiActive = m_spiActive;
if (!spiActive) {
@@ -508,7 +515,6 @@ bool SdSpiCard::readData(uint8_t* dst, size_t count) {
//------------------------------------------------------------------------------
bool SdSpiCard::readOCR(uint32_t* ocr) {
uint8_t* p = reinterpret_cast(ocr);
- syncDevice();
if (cardCommand(CMD58, 0)) {
error(SD_CARD_ERROR_CMD58);
goto fail;
@@ -527,7 +533,6 @@ bool SdSpiCard::readOCR(uint32_t* ocr) {
/** read CID or CSR register */
bool SdSpiCard::readRegister(uint8_t cmd, void* buf) {
uint8_t* dst = reinterpret_cast(buf);
- syncDevice();
if (cardCommand(cmd, 0)) {
error(SD_CARD_ERROR_READ_REG);
goto fail;
@@ -599,33 +604,32 @@ bool SdSpiCard::readStatus(uint8_t* status) {
bool SdSpiCard::readSectors(uint32_t sector, uint8_t* dst, size_t ns) {
#if ENABLE_DEDICATED_SPI
if (m_curState != READ_STATE || sector != m_curSector) {
- if (!syncDevice()) {
- return false;
- }
- if (!SdSpiCard::readStart(sector)) {
- return false;
+ if (!readStart(sector)) {
+ goto fail;
}
m_curSector = sector;
m_curState = READ_STATE;
}
for (size_t i = 0; i < ns; i++, dst += 512) {
if (!readData(dst, 512)) {
- return false;
+ goto fail;
}
}
m_curSector += ns;
return m_sharedSpi ? syncDevice() : true;
#else // ENABLE_DEDICATED_SPI
if (!readStart(sector)) {
- return false;
+ goto fail;
}
for (size_t i = 0; i < ns; i++, dst += 512) {
if (!readData(dst, 512)) {
- return false;
+ goto fail;
}
}
return readStop();
#endif // ENABLE_DEDICATED_SPI
+ fail:
+ return false;
}
//------------------------------------------------------------------------------
bool SdSpiCard::readStop() {
@@ -665,16 +669,15 @@ void SdSpiCard::spiStop() {
//------------------------------------------------------------------------------
bool SdSpiCard::syncDevice() {
#if ENABLE_DEDICATED_SPI
- if (m_curState == READ_STATE) {
- if (!SdSpiCard::readStop()) {
- return false;
- }
- } else if (m_curState == WRITE_STATE) {
- if (!SdSpiCard::writeStop()) {
- return false;
- }
- }
+ // Insure no recursive loop with cardCommand().
+ uint8_t state = m_curState;
m_curState = IDLE_STATE;
+ if (state == WRITE_STATE) {
+ return writeStop();
+ }
+ if (state == READ_STATE) {
+ return readStop();
+ }
#endif // ENABLE_DEDICATED_SPI
return true;
}
@@ -780,20 +783,17 @@ bool SdSpiCard::writeSingle(uint32_t sector, const uint8_t* src) {
}
//------------------------------------------------------------------------------
bool SdSpiCard::writeSectors(uint32_t sector, const uint8_t* src, size_t ns) {
- #if ENABLE_DEDICATED_SPI
+#if ENABLE_DEDICATED_SPI
if (m_curState != WRITE_STATE || m_curSector != sector) {
- if (!syncDevice()) {
- return false;
- }
if (!writeStart(sector)) {
- return false;
+ goto fail;
}
m_curSector = sector;
m_curState = WRITE_STATE;
}
for (size_t i = 0; i < ns; i++, src += 512) {
if (!writeData(src)) {
- return false;
+ goto fail;
}
}
m_curSector += ns;
@@ -808,11 +808,11 @@ bool SdSpiCard::writeSectors(uint32_t sector, const uint8_t* src, size_t ns) {
}
}
return writeStop();
+#endif // ENABLE_DEDICATED_SPI
fail:
spiStop();
return false;
-#endif // ENABLE_DEDICATED_SPI
}
//------------------------------------------------------------------------------
bool SdSpiCard::writeStart(uint32_t sector) {
@@ -831,7 +831,7 @@ bool SdSpiCard::writeStart(uint32_t sector) {
return false;
}
//------------------------------------------------------------------------------
-bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
+bool SdSpiCard::writeStart(uint32_t sector, uint32_t eraseCount) {
// send pre-erase count
if (cardAcmd(ACMD23, eraseCount)) {
error(SD_CARD_ERROR_ACMD23);
@@ -839,9 +839,9 @@ bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
}
// use address if not SDHC card
if (type() != SD_CARD_TYPE_SDHC) {
- blockNumber <<= 9;
+ sector <<= 9;
}
- if (cardCommand(CMD25, blockNumber)) {
+ if (cardCommand(CMD25, sector)) {
error(SD_CARD_ERROR_CMD25);
goto fail;
}
diff --git a/src/SdCard/SdSpiCard.h b/src/SdCard/SdSpiCard.h
index 3c09ec8..a1ce6ce 100644
--- a/src/SdCard/SdSpiCard.h
+++ b/src/SdCard/SdSpiCard.h
@@ -47,7 +47,7 @@ class SdSpiCard {
#endif // HAS_SDIO_CLASS
public:
/** Construct an instance of SdSpiCard. */
- SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {}
+ SdSpiCard() {}
/** Initialize the SD card.
* \param[in] spiConfig SPI card configuration.
* \return true for success or false for failure.
@@ -214,11 +214,11 @@ class SdSpiCard {
* \return true for success or false for failure.
*/
bool writeSector(uint32_t sector, const uint8_t* src) {
-#if ENABLE_DEDICATED_SPI
- return writeSectors(sector, src, 1);
-#else // ENABLE_DEDICATED_SPI
- return writeSingle(sector, src);
-#endif // ENABLE_DEDICATED_SPI
+ if (m_sharedSpi) {
+ return writeSingle(sector, src);
+ } else {
+ return writeSectors(sector, src, 1);
+ }
}
/**
* Writes a 512 byte sector to an SD card.
@@ -355,12 +355,14 @@ class SdSpiCard {
static const uint8_t WRITE_STATE = 2;
uint32_t m_curSector;
uint8_t m_curState;
- bool m_sharedSpi;
+ bool m_sharedSpi = true;
+#else // ENABLE_DEDICATED_SPI
+ static const bool m_sharedSpi = true;
#endif // ENABLE_DEDICATED_SPI
SdCsPin_t m_csPin;
- uint8_t m_errorCode;
+ uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED;
bool m_spiActive;
uint8_t m_status;
- uint8_t m_type;
+ uint8_t m_type = 0;
};
#endif // SdSpiCard_h
diff --git a/src/SdCard/SdioCard.h b/src/SdCard/SdioCard.h
index d7ca687..447248d 100644
--- a/src/SdCard/SdioCard.h
+++ b/src/SdCard/SdioCard.h
@@ -35,7 +35,7 @@
*/
class SdioConfig {
public:
- SdioConfig() : m_options(FIFO_SDIO) {}
+ SdioConfig() {}
/**
* SdioConfig constructor.
* \param[in] opt SDIO options.
@@ -46,7 +46,7 @@ class SdioConfig {
/** \return true if DMA_SDIO. */
bool useDma() {return m_options & DMA_SDIO;}
private:
- uint8_t m_options;
+ uint8_t m_options = FIFO_SDIO;
};
//------------------------------------------------------------------------------
/**
@@ -64,13 +64,7 @@ class SdioCard : public SdCardInterface {
* \return false - not implemented.
*/
bool end() {return false;}
- /**
- * Determine the size of an SD flash memory card.
- *
- * \return The number of 512 byte data sectors in the card
- * or zero if an error occurs.
- */
- uint32_t sectorCount();
+
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// Use sectorCount(). cardSize() will be removed in the future.
uint32_t cardSize() __attribute__ ((deprecated)) {return sectorCount();}
@@ -180,6 +174,21 @@ class SdioCard : public SdCardInterface {
bool readStop();
/** \return SDIO card status. */
uint32_t status();
+ /**
+ * Determine the size of an SD flash memory card.
+ *
+ * \return The number of 512 byte data sectors in the card
+ * or zero if an error occurs.
+ */
+ uint32_t sectorCount();
+ /**
+ * Send CMD12 to stop read or write.
+ *
+ * \param[in] blocking If true, wait for command complete.
+ *
+ * \return true for success or false for failure.
+ */
+ bool stopTransmission(bool blocking);
/** \return success if sync successful. Not for user apps. */
bool syncDevice();
/** Return the card type: SD V1, SD V2 or SDHC
@@ -241,6 +250,6 @@ class SdioCard : public SdCardInterface {
static const uint8_t WRITE_STATE = 2;
uint32_t m_curSector;
SdioConfig m_sdioConfig;
- uint8_t m_curState;
+ uint8_t m_curState = IDLE_STATE;
};
#endif // SdioCard_h
diff --git a/src/SdCard/SdioTeensy.cpp b/src/SdCard/SdioTeensy.cpp
index 8954c08..1f4cdf6 100644
--- a/src/SdCard/SdioTeensy.cpp
+++ b/src/SdCard/SdioTeensy.cpp
@@ -28,10 +28,7 @@
#include "SdioCard.h"
//==============================================================================
// limit of K66 due to errata KINETIS_K_0N65N.
-const uint32_t MAX_SDHC_COUNT = 0XFFFF;
-
-// Max RU is 1024 sectors.
-const uint32_t RU_MASK = 0X03FF;
+const uint32_t MAX_BLKCNT = 0XFFFF;
//==============================================================================
#define SDHC_PROCTL_DTW_4BIT 0x01
const uint32_t FIFO_WML = 16;
@@ -201,6 +198,9 @@ static bool (*m_busyFcn)() = 0;
static bool m_initDone = false;
static bool m_version2;
static bool m_highCapacity;
+#if ENABLE_TEENSY_SDIO_MOD
+static bool m_transferActive = false;
+#endif // ENABLE_TEENSY_SDIO_MOD
static uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED;
static uint32_t m_errorLine = 0;
static uint32_t m_rca;
@@ -216,20 +216,53 @@ static csd_t m_csd;
#if USE_DEBUG_MODE
#define DBG_IRQSTAT() if (SDHC_IRQSTAT) {Serial.print(__LINE__);\
Serial.print(" IRQSTAT "); Serial.println(SDHC_IRQSTAT, HEX);}
-
static void printRegs(uint32_t line) {
- Serial.print(line);
- Serial.print(" SDHC_BLKATTR ");
- Serial.print(SDHC_BLKATTR, HEX);
- Serial.print(" XFERTYP ");
- Serial.print(SDHC_XFERTYP, HEX);
- Serial.print(" PRSSTAT ");
- Serial.print(SDHC_PRSSTAT, HEX);
- Serial.print(" PROCTL ");
- Serial.print(SDHC_PROCTL, HEX);
- Serial.print(" IRQSTAT ");
- Serial.print(SDHC_IRQSTAT, HEX);
- Serial.print(" m_irqstat ");
+ uint32_t blkattr = SDHC_BLKATTR;
+ uint32_t xfertyp = SDHC_XFERTYP;
+ uint32_t prsstat = SDHC_PRSSTAT;
+ uint32_t proctl = SDHC_PROCTL;
+ uint32_t irqstat = SDHC_IRQSTAT;
+ Serial.print("\nLINE: ");
+ Serial.println(line);
+ Serial.print("BLKATTR ");
+ Serial.println(blkattr, HEX);
+ Serial.print("XFERTYP ");
+ Serial.print(xfertyp, HEX);
+ Serial.print(" CMD");
+ Serial.print(xfertyp >> 24);
+ Serial.print(" TYP");
+ Serial.print((xfertyp >> 2) & 3);
+ if (xfertyp & SDHC_XFERTYP_DPSEL) {Serial.print(" DPSEL");}
+ Serial.println();
+ Serial.print("PRSSTAT ");
+ Serial.print(prsstat, HEX);
+ if (prsstat & SDHC_PRSSTAT_BREN) {Serial.print(" BREN");}
+ if (prsstat & SDHC_PRSSTAT_BWEN) {Serial.print(" BWEN");}
+ if (prsstat & SDHC_PRSSTAT_RTA) {Serial.print(" RTA");}
+ if (prsstat & SDHC_PRSSTAT_WTA) {Serial.print(" WTA");}
+ if (prsstat & SDHC_PRSSTAT_SDOFF) {Serial.print(" SDOFF");}
+ if (prsstat & SDHC_PRSSTAT_PEROFF) {Serial.print(" PEROFF");}
+ if (prsstat & SDHC_PRSSTAT_HCKOFF) {Serial.print(" HCKOFF");}
+ if (prsstat & SDHC_PRSSTAT_IPGOFF) {Serial.print(" IPGOFF");}
+ if (prsstat & SDHC_PRSSTAT_SDSTB) {Serial.print(" SDSTB");}
+ if (prsstat & SDHC_PRSSTAT_DLA) {Serial.print(" DLA");}
+ if (prsstat & SDHC_PRSSTAT_CDIHB) {Serial.print(" CDIHB");}
+ if (prsstat & SDHC_PRSSTAT_CIHB) {Serial.print(" CIHB");}
+ Serial.println();
+ Serial.print("PROCTL ");
+ Serial.print(proctl, HEX);
+ if (proctl & SDHC_PROCTL_SABGREQ) Serial.print(" SABGREQ");
+ Serial.print(" EMODE");
+ Serial.print((proctl >>4) & 3);
+ Serial.print(" DWT");
+ Serial.print((proctl >>1) & 3);
+ Serial.println();
+ Serial.print("IRQSTAT ");
+ Serial.print(irqstat, HEX);
+ if (irqstat & SDHC_IRQSTAT_BGE) {Serial.print(" BGE");}
+ if (irqstat & SDHC_IRQSTAT_TC) {Serial.print(" TC");}
+ if (irqstat & SDHC_IRQSTAT_CC) {Serial.print(" CC");}
+ Serial.print("\nm_irqstat ");
Serial.println(m_irqstat, HEX);
}
#else // USE_DEBUG_MODE
@@ -299,7 +332,7 @@ static void gpioMux(uint8_t mode) {
static void enableGPIO(bool enable) {
const uint32_t CLOCK_MASK = IOMUXC_SW_PAD_CTL_PAD_PKE |
#if defined(ARDUINO_TEENSY41)
- IOMUXC_SW_PAD_CTL_PAD_DSE(1) |
+ IOMUXC_SW_PAD_CTL_PAD_DSE(7) |
#else // defined(ARDUINO_TEENSY41)
IOMUXC_SW_PAD_CTL_PAD_DSE(4) | ///// WHG
#endif // defined(ARDUINO_TEENSY41)
@@ -402,7 +435,7 @@ static void initSDHC() {
#if defined (__IMXRT1062__)
SDHC_MIX_CTRL |= 0x80000000;
-#endif
+#endif // (__IMXRT1062__)
// Reset SDHC. Use default Water Mark Level of 16.
SDHC_SYSCTL |= SDHC_SYSCTL_RSTA | SDHC_SYSCTL_SDCLKFS(0x80);
@@ -433,11 +466,7 @@ static uint32_t statusCMD13() {
}
//------------------------------------------------------------------------------
static bool isBusyCMD13() {
- if (!cardCommand(CMD13_XFERTYP, m_rca)) {
- // Caller will timeout.
- return true;
- }
- return !(SDHC_CMDRSP0 & CARD_STATUS_READY_FOR_DATA);
+ return !(statusCMD13() & CARD_STATUS_READY_FOR_DATA);
}
//------------------------------------------------------------------------------
static bool isBusyCommandComplete() {
@@ -448,6 +477,10 @@ static bool isBusyCommandInhibit() {
return SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB;
}
//------------------------------------------------------------------------------
+static bool isBusyDat() {
+ return SDHC_PRSSTAT & (1 << 24) ? false : true;
+}
+//------------------------------------------------------------------------------
static bool isBusyDMA() {
return m_dmaBusy;
}
@@ -536,20 +569,26 @@ static void setSdclk(uint32_t kHzMax) {
}
//------------------------------------------------------------------------------
static bool transferStop() {
+ // This fix allows CDIHB to be cleared in Tennsy 3.x without a reset.
+ SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ;
if (!cardCommand(CMD12_XFERTYP, 0)) {
return sdError(SD_CARD_ERROR_CMD12);
}
- if (yieldTimeout(isBusyCMD13)) {
+// if (yieldTimeout(isBusyCMD13)) {
+ if (yieldTimeout(isBusyDat)) {
return sdError(SD_CARD_ERROR_CMD13);
}
- // Save registers before reset DAT lines.
- uint32_t irqsststen = SDHC_IRQSTATEN;
- uint32_t proctl = SDHC_PROCTL & ~SDHC_PROCTL_SABGREQ;
- // Do reset to clear CDIHB. Should be a better way!
- SDHC_SYSCTL |= SDHC_SYSCTL_RSTD;
- // Restore registers.
- SDHC_IRQSTATEN = irqsststen;
- SDHC_PROCTL = proctl;
+ if (SDHC_PRSSTAT & SDHC_PRSSTAT_CDIHB) {
+ // This should not happen after above fix.
+ // Save registers before reset DAT lines.
+ uint32_t irqsststen = SDHC_IRQSTATEN;
+ uint32_t proctl = SDHC_PROCTL & ~SDHC_PROCTL_SABGREQ;
+ // Do reset to clear CDIHB. Should be a better way!
+ SDHC_SYSCTL |= SDHC_SYSCTL_RSTD;
+ // Restore registers.
+ SDHC_IRQSTATEN = irqsststen;
+ SDHC_PROCTL = proctl;
+ }
return true;
}
//------------------------------------------------------------------------------
@@ -585,6 +624,22 @@ static bool waitTimeout(bool (*fcn)()) {
}
return false; // Caller will set errorCode.
}
+#if ENABLE_TEENSY_SDIO_MOD
+//------------------------------------------------------------------------------
+static bool waitTransferComplete() {
+ if (!m_transferActive) {
+ return true;
+ }
+ bool timeOut = waitTimeout(isBusyTransferComplete);
+ m_transferActive = false;
+ m_irqstat = SDHC_IRQSTAT;
+ SDHC_IRQSTAT = m_irqstat;
+ if (timeOut || (m_irqstat & SDHC_IRQSTAT_ERROR)) {
+ return sdError(SD_CARD_ERROR_TRANSFER_COMPLETE);
+ }
+ return true;
+}
+#endif // ENABLE_TEENSY_SDIO_MOD
//==============================================================================
// Start of SdioCard member functions.
//==============================================================================
@@ -675,6 +730,11 @@ bool SdioCard::begin(SdioConfig sdioConfig) {
}
//------------------------------------------------------------------------------
bool SdioCard::erase(uint32_t firstSector, uint32_t lastSector) {
+#if ENABLE_TEENSY_SDIO_MOD
+ if (m_curState != IDLE_STATE && !syncDevice()) {
+ return false;
+ }
+#endif // ENABLE_TEENSY_SDIO_MOD
// check for single sector erase
if (!m_csd.v1.erase_blk_en) {
// erase size mask
@@ -716,7 +776,31 @@ uint32_t SdioCard::errorLine() const {
}
//------------------------------------------------------------------------------
bool SdioCard::isBusy() {
+#if ENABLE_TEENSY_SDIO_MOD
+ if (m_sdioConfig.useDma()) {
+ return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13();
+ } else {
+ if (m_transferActive) {
+ if (isBusyTransferComplete()) {
+ return true;
+ }
+#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
+ if ((SDHC_BLKATTR & 0XFFFF0000) != 0) {
+ return false;
+ }
+ m_transferActive = false;
+ stopTransmission(false);
+ return true;
+#else // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+ return false;
+#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+ }
+ // Use DAT0 low as busy.
+ return SDHC_PRSSTAT & (1 << 24) ? false : true;
+ }
+#else // ENABLE_TEENSY_SDIO_MOD
return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13();
+#endif // ENABLE_TEENSY_SDIO_MOD
}
//------------------------------------------------------------------------------
uint32_t SdioCard::kHzSdClk() {
@@ -781,6 +865,11 @@ bool SdioCard::readSector(uint32_t sector, uint8_t* dst) {
memcpy(dst, aligned, 512);
}
} else {
+#if ENABLE_TEENSY_SDIO_MOD
+ if (!waitTransferComplete()) {
+ return false;
+ }
+#endif // ENABLE_TEENSY_SDIO_MOD
if (m_curState != READ_STATE || sector != m_curSector) {
if (!syncDevice()) {
return false;
@@ -841,7 +930,7 @@ bool SdioCard::readStart(uint32_t sector) {
SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512);
#else // defined(__IMXRT1062__)
// Errata - can't do infinite transfer.
- SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(0XFFFF) | SDHC_BLKATTR_BLKSIZE(512);
+ SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(MAX_BLKCNT) | SDHC_BLKATTR_BLKSIZE(512);
#endif // defined(__IMXRT1062__)
if (!cardCommand(CMD18_PGM_XFERTYP, m_highCapacity ? sector : 512*sector)) {
@@ -862,7 +951,30 @@ uint32_t SdioCard::status() {
return statusCMD13();
}
//------------------------------------------------------------------------------
+bool SdioCard::stopTransmission(bool blocking) {
+ m_curState = IDLE_STATE;
+ // This fix allows CDIHB to be cleared in Tennsy 3.x without a reset.
+ SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ;
+ if (!cardCommand(CMD12_XFERTYP, 0)) {
+ return sdError(SD_CARD_ERROR_CMD12);
+ }
+ if (blocking) {
+ if (yieldTimeout(isBusyDat)) {
+ return sdError(SD_CARD_ERROR_CMD13);
+ }
+ }
+ return true;
+}
+//------------------------------------------------------------------------------
bool SdioCard::syncDevice() {
+#if ENABLE_TEENSY_SDIO_MOD
+ if (!waitTransferComplete()) {
+ return false;
+ }
+ if (m_curState != IDLE_STATE) {
+ return stopTransmission(true);
+ }
+#else // ENABLE_TEENSY_SDIO_MOD
if (m_curState == READ_STATE) {
m_curState = IDLE_STATE;
if (!readStop()) {
@@ -874,6 +986,7 @@ bool SdioCard::syncDevice() {
return false;
}
}
+#endif // ENABLE_TEENSY_SDIO_MOD
return true;
}
//------------------------------------------------------------------------------
@@ -884,6 +997,11 @@ uint8_t SdioCard::type() const {
//------------------------------------------------------------------------------
bool SdioCard::writeData(const uint8_t* src) {
DBG_IRQSTAT();
+#if ENABLE_TEENSY_SDIO_MOD
+ if (!waitTransferComplete()) {
+ return false;
+ }
+#endif // ENABLE_TEENSY_SDIO_MOD
const uint32_t* p32 = reinterpret_cast(src);
if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_WTA)) {
SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ;
@@ -901,12 +1019,17 @@ bool SdioCard::writeData(const uint8_t* src) {
}
p32 += FIFO_WML;
}
+#if ENABLE_TEENSY_SDIO_MOD
+ m_transferActive = true;
+ return true;
+#else // ENABLE_TEENSY_SDIO_MOD
if (waitTimeout(isBusyTransferComplete)) {
return sdError(SD_CARD_ERROR_WRITE_TIMEOUT);
}
m_irqstat = SDHC_IRQSTAT;
SDHC_IRQSTAT = m_irqstat;
return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR);
+#endif // ENABLE_TEENSY_SDIO_MOD
}
//------------------------------------------------------------------------------
bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
@@ -919,10 +1042,23 @@ bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
} else {
ptr = const_cast(src);
}
- if (!rdWrSectors(CMD24_DMA_XFERTYP, sector, ptr, 1)) {
+ if (!rdWrSectors(CMD24_DMA_XFERTYP, sector, ptr, 1)) {
return sdError(SD_CARD_ERROR_CMD24);
}
} else {
+#if ENABLE_TEENSY_SDIO_MOD
+ if (!waitTransferComplete()) {
+ return false;
+ }
+#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
+ // End transfer with CMD12 if required.
+ if ((SDHC_BLKATTR & 0XFFFF0000) == 0) {
+ if (!syncDevice()) {
+ return false;
+ }
+ }
+#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+#endif // ENABLE_TEENSY_SDIO_MOD
if (m_curState != WRITE_STATE || m_curSector != sector) {
if (!syncDevice()) {
return false;
@@ -937,6 +1073,7 @@ bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
return false;
}
m_curSector++;
+#if !ENABLE_TEENSY_SDIO_MOD
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
// End transfer with CMD12 if required.
if ((SDHC_BLKATTR & 0XFFFF0000) == 0) {
@@ -945,6 +1082,7 @@ bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
}
}
#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+#endif // !ENABLE_TEENSY_SDIO_MOD
}
return true;
}
@@ -984,7 +1122,7 @@ bool SdioCard::writeStart(uint32_t sector) {
SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512);
#else // defined(__IMXRT1062__)
// Errata - can't do infinite transfer.
- SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(0XFFFF) | SDHC_BLKATTR_BLKSIZE(512);
+ SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(MAX_BLKCNT) | SDHC_BLKATTR_BLKSIZE(512);
#endif // defined(__IMXRT1062__)
if (!cardCommand(CMD25_PGM_XFERTYP, m_highCapacity ? sector : 512*sector)) {
return sdError(SD_CARD_ERROR_CMD25);
diff --git a/src/SdFat.h b/src/SdFat.h
index ac52333..2360527 100644
--- a/src/SdFat.h
+++ b/src/SdFat.h
@@ -38,9 +38,9 @@
#endif // INCLUDE_SDIOS
//------------------------------------------------------------------------------
/** SdFat version for cpp use. */
-#define SD_FAT_VERSION 20004
+#define SD_FAT_VERSION 20005
/** SdFat version as string. */
-#define SD_FAT_VERSION_STR "2.0.4"
+#define SD_FAT_VERSION_STR "2.0.5"
//==============================================================================
/**
* \class SdBase
diff --git a/src/SdFatConfig.h b/src/SdFatConfig.h
index aa9e6bb..3b83fb7 100644
--- a/src/SdFatConfig.h
+++ b/src/SdFatConfig.h
@@ -39,6 +39,9 @@
/** For Debug - must be one */
#define ENABLE_ARDUINO_STRING 1
//------------------------------------------------------------------------------
+/** Set zero to disable mod for non-blocking write. */
+#define ENABLE_TEENSY_SDIO_MOD 1
+//------------------------------------------------------------------------------
/** Set USE_BLOCK_DEVICE_INTERFACE nonzero to use generic block device */
#define USE_BLOCK_DEVICE_INTERFACE 0
//------------------------------------------------------------------------------
@@ -82,7 +85,7 @@
#endif // defined(__AVR__) && FLASHEND < 0X8000
//------------------------------------------------------------------------------
/**
- * Set ENABLE_DEDICATED_SPI to enable dedicated use of the SPI bus.
+ * Set ENABLE_DEDICATED_SPI non-zero to enable dedicated use of the SPI bus.
* Selecting dedicated SPI in SdSpiConfig() will produce better
* performance by using very large multi-block transfers to and
* from the SD card.
@@ -96,7 +99,7 @@
// All other boards.
#define ENABLE_DEDICATED_SPI 1
#endif // defined(__AVR__) && FLASHEND < 0X8000
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
/**
* If the symbol SPI_DRIVER_SELECT is:
*
@@ -110,6 +113,13 @@
* 3 - An external SPI driver derived from SdSpiBaseClass is always used.
*/
#define SPI_DRIVER_SELECT 0
+/**
+ * If USE_SPI_ARRAY_TRANSFER is non-zero and the standard SPI library is
+ * use, the array transfer function, transfer(buf, size), will be used.
+ * This option will allocate up to a 512 byte temporary buffer for send.
+ * This may be faster for some boards. Do not use this with AVR boards.
+ */
+#define USE_SPI_ARRAY_TRANSFER 0
//------------------------------------------------------------------------------
/**
* SD_CHIP_SELECT_MODE defines how the functions
@@ -181,7 +191,7 @@ typedef uint8_t SdCsPin_t;
* Some cards will not sleep in low power mode unless CHECK_FLASH_PROGRAMMING
* is non-zero.
*/
-#define CHECK_FLASH_PROGRAMMING 1
+#define CHECK_FLASH_PROGRAMMING 0
//------------------------------------------------------------------------------
/**
* Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters
diff --git a/src/SpiDriver/SdSpiDriver.h b/src/SpiDriver/SdSpiDriver.h
index 2f58626..95b5bf9 100644
--- a/src/SpiDriver/SdSpiDriver.h
+++ b/src/SpiDriver/SdSpiDriver.h
@@ -43,6 +43,24 @@ void sdCsInit(SdCsPin_t pin);
*/
void sdCsWrite(SdCsPin_t pin, bool level);
//------------------------------------------------------------------------------
+/** SPI bus is share with other devices. */
+const uint8_t SHARED_SPI = 0;
+#if ENABLE_DEDICATED_SPI
+/** The SD is the only device on the SPI bus. */
+const uint8_t DEDICATED_SPI = 1;
+/**
+ * \param[in] opt option field of SdSpiConfig.
+ * \return true for shared SPI.
+ */
+inline bool spiOptionShared(uint8_t opt) {return !(opt & DEDICATED_SPI);}
+#else // ENABLE_DEDICATED_SPI
+/**
+ * \param[in] opt option field of SdSpiConfig.
+ * \return true for shared SPI.
+ */
+inline bool spiOptionShared(uint8_t opt) {(void)opt; return true;}
+#endif // ENABLE_DEDICATED_SPI
+//------------------------------------------------------------------------------
/** SPISettings for SCK frequency in Hz. */
#define SD_SCK_HZ(maxSpeed) (maxSpeed)
/** SPISettings for SCK frequency in MHz. */
@@ -63,10 +81,6 @@ void sdCsWrite(SdCsPin_t pin, bool level);
/** Set SCK rate to 500 kHz for AVR. */
#define SPI_SIXTEENTH_SPEED SD_SCK_HZ(500000)
//------------------------------------------------------------------------------
-/** The SD is the only device on the SPI bus. */
-#define DEDICATED_SPI 0X80
-/** SPI bus is share with other devices. */
-#define SHARED_SPI 0
#if SPI_DRIVER_SELECT < 2
#include "SPI.h"
/** Port type for Arduino SPI hardware driver. */
@@ -106,29 +120,27 @@ class SdSpiConfig {
* \param[in] maxSpeed Maximum SCK frequency.
*/
SdSpiConfig(SdCsPin_t cs, uint8_t opt, uint32_t maxSpeed) :
- csPin(cs), options(opt), maxSck(maxSpeed), spiPort(nullptr) {}
+ csPin(cs), options(opt), maxSck(maxSpeed) {}
/** SdSpiConfig constructor.
*
* \param[in] cs Chip select pin.
* \param[in] opt Options.
*/
- SdSpiConfig(SdCsPin_t cs, uint8_t opt) :
- csPin(cs), options(opt), maxSck(SD_SCK_MHZ(50)), spiPort(nullptr) {}
+ SdSpiConfig(SdCsPin_t cs, uint8_t opt) : csPin(cs), options(opt) {}
/** SdSpiConfig constructor.
*
* \param[in] cs Chip select pin.
*/
- explicit SdSpiConfig(SdCsPin_t cs) : csPin(cs), options(SHARED_SPI),
- maxSck(SD_SCK_MHZ(50)), spiPort(nullptr) {}
+ explicit SdSpiConfig(SdCsPin_t cs) : csPin(cs) {}
/** Chip select pin. */
const SdCsPin_t csPin;
/** Options */
- const uint8_t options;
+ const uint8_t options = 0;
/** Max SCK frequency */
- const uint32_t maxSck;
+ const uint32_t maxSck = SD_SCK_MHZ(50);
/** SPI port */
- SpiPort_t* spiPort;
+ SpiPort_t* spiPort = nullptr;
};
#if SPI_DRIVER_SELECT < 2
#include "SdSpiArduinoDriver.h"
diff --git a/src/SpiDriver/SdSpiESP.cpp b/src/SpiDriver/SdSpiESP.cpp
index f4b54c5..73bc24e 100644
--- a/src/SpiDriver/SdSpiESP.cpp
+++ b/src/SpiDriver/SdSpiESP.cpp
@@ -28,46 +28,54 @@
#define ESP_UNALIGN_OK 1
//------------------------------------------------------------------------------
void SdSpiArduinoDriver::activate() {
- SPI.beginTransaction(m_spiSettings);
+ m_spi->beginTransaction(m_spiSettings);
}
//------------------------------------------------------------------------------
void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) {
- (void)spiConfig;
- SPI.begin();
+ if (spiConfig.spiPort) {
+ m_spi = spiConfig.spiPort;
+#if defined(SDCARD_SPI) && defined(SDCARD_SS_PIN)
+ } else if (spiConfig.csPin == SDCARD_SS_PIN) {
+ m_spi = &SDCARD_SPI;
+#endif // defined(SDCARD_SPI) && defined(SDCARD_SS_PIN)
+ } else {
+ m_spi = &SPI;
+ }
+ m_spi->begin();
}
//------------------------------------------------------------------------------
void SdSpiArduinoDriver::deactivate() {
- SPI.endTransaction();
+ m_spi->endTransaction();
}
//------------------------------------------------------------------------------
uint8_t SdSpiArduinoDriver::receive() {
- return SPI.transfer(0XFF);
+ return m_spi->transfer(0XFF);
}
//------------------------------------------------------------------------------
uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) {
#if ESP_UNALIGN_OK
- SPI.transferBytes(nullptr, buf, count);
+ m_spi->transferBytes(nullptr, buf, count);
#else // ESP_UNALIGN_OK
// Adjust to 32-bit alignment.
while ((reinterpret_cast(buf) & 0X3) && count) {
- *buf++ = SPI.transfer(0xff);
+ *buf++ = m_spi->transfer(0xff);
count--;
}
// Do multiple of four byte transfers.
size_t n4 = 4*(count/4);
if (n4) {
- SPI.transferBytes(nullptr, buf, n4);
+ m_spi->transferBytes(nullptr, buf, n4);
}
// Transfer up to three remaining bytes.
for (buf += n4, count -= n4; count; count--) {
- *buf++ = SPI.transfer(0xff);
+ *buf++ = m_spi->transfer(0xff);
}
#endif // ESP_UNALIGN_OK
return 0;
}
//------------------------------------------------------------------------------
void SdSpiArduinoDriver::send(uint8_t data) {
- SPI.transfer(data);
+ m_spi->transfer(data);
}
//------------------------------------------------------------------------------
void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) {
@@ -78,6 +86,7 @@ void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) {
count--;
}
#endif // #if ESP_UNALIGN_OK
- SPI.transferBytes(const_cast(buf), nullptr, count);
+
+ m_spi->transferBytes(const_cast(buf), nullptr, count);
}
-#endif // defined(SD_USE_CUSTOM_SPI) && defined(ESP8266)
+#endif // defined(SD_USE_CUSTOM_SPI) && (defined(ESP8266) || defined(ESP32))
diff --git a/src/SpiDriver/SdSpiLibDriver.h b/src/SpiDriver/SdSpiLibDriver.h
index 8bf09d2..c5d96d6 100644
--- a/src/SpiDriver/SdSpiLibDriver.h
+++ b/src/SpiDriver/SdSpiLibDriver.h
@@ -55,9 +55,14 @@ inline uint8_t SdSpiArduinoDriver::receive() {
}
//------------------------------------------------------------------------------
inline uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) {
+#if USE_SPI_ARRAY_TRANSFER
+ memset(buf, 0XFF, count);
+ m_spi->transfer(buf, count);
+#else // USE_SPI_ARRAY_TRANSFER
for (size_t i = 0; i < count; i++) {
buf[i] = m_spi->transfer(0XFF);
}
+#endif // USE_SPI_ARRAY_TRANSFER
return 0;
}
//------------------------------------------------------------------------------
@@ -66,6 +71,14 @@ inline void SdSpiArduinoDriver::send(uint8_t data) {
}
//------------------------------------------------------------------------------
inline void SdSpiArduinoDriver::send(const uint8_t* buf, size_t count) {
+#if USE_SPI_ARRAY_TRANSFER
+ if (count <= 512) {
+ uint8_t tmp[count]; // NOLINT
+ memcpy(tmp, buf, count);
+ m_spi->transfer(tmp, count);
+ return;
+ }
+#endif // USE_SPI_ARRAY_TRANSFER
for (size_t i = 0; i < count; i++) {
m_spi->transfer(buf[i]);
}
diff --git a/src/common/DebugMacros.h b/src/common/DebugMacros.h
index 1efbdb1..e01f609 100644
--- a/src/common/DebugMacros.h
+++ b/src/common/DebugMacros.h
@@ -41,7 +41,7 @@ static void dbgPrint(uint16_t line) {
#define DBG_PRINT_IF(b) if (b) {Serial.print(F(__FILE__));\
Serial.println(__LINE__);}
-#define DBG_HALT_IF(b) if (b) { Serial.print(F("DBG_HALT "));\
+#define DBG_HALT_IF(b) if (b) {Serial.print(F("DBG_HALT "));\
Serial.print(F(__FILE__)); Serial.println(__LINE__);\
while (true) {}}
#define DBG_FAIL_MACRO dbgPrint(__LINE__);
diff --git a/src/common/PrintTemplates.h b/src/common/PrintTemplates.h
index 7cff151..e6f75b5 100644
--- a/src/common/PrintTemplates.h
+++ b/src/common/PrintTemplates.h
@@ -377,7 +377,7 @@ int vmprintf(F* file, const char *fmt, va_list ap) {
break;
default:
- *--str = c;;
+ *--str = c;
break;
}
ns = ptr - str;
@@ -468,7 +468,7 @@ int vmprintf(F file, const __FlashStringHelper *ifsh, va_list ap) {
break;
default:
- *--str = c;;
+ *--str = c;
break;
}
ns = ptr - str;
diff --git a/src/iostream/StdioStream.h b/src/iostream/StdioStream.h
index 185c072..e43fe04 100644
--- a/src/iostream/StdioStream.h
+++ b/src/iostream/StdioStream.h
@@ -114,11 +114,7 @@ class StdioStream : private StreamBaseFile {
/** Constructor
*
*/
- StdioStream() {
- m_w = m_r = 0;
- m_p = m_buf;
- m_status = 0;
- }
+ StdioStream() {}
//----------------------------------------------------------------------------
/** Clear the stream's end-of-file and error indicators. */
void clearerr() {
@@ -657,11 +653,11 @@ class StdioStream : private StreamBaseFile {
static const uint8_t S_EOF = 0x10; // found EOF
static const uint8_t S_ERR = 0x20; // found error
//----------------------------------------------------------------------------
- uint8_t m_status;
- uint8_t* m_p;
- uint8_t m_r;
- uint8_t m_w;
uint8_t m_buf[STREAM_BUF_SIZE];
+ uint8_t m_status = 0;
+ uint8_t* m_p = m_buf;
+ uint8_t m_r = 0;
+ uint8_t m_w;
};
//------------------------------------------------------------------------------
#endif // StdioStream_h
diff --git a/src/iostream/bufstream.h b/src/iostream/bufstream.h
index 6221886..3291a0d 100644
--- a/src/iostream/bufstream.h
+++ b/src/iostream/bufstream.h
@@ -38,7 +38,7 @@
class ibufstream : public istream {
public:
/** Constructor */
- ibufstream() : m_buf(nullptr), m_len(0) {}
+ ibufstream() {}
/** Constructor
* \param[in] str pointer to string to be parsed
* Warning: The string will not be copied so must stay in scope.
@@ -89,8 +89,8 @@ class ibufstream : public istream {
}
/// @endcond
private:
- const char* m_buf;
- size_t m_len;
+ const char* m_buf = nullptr;
+ size_t m_len = 0;
size_t m_pos;
};
//==============================================================================
@@ -101,7 +101,7 @@ class ibufstream : public istream {
class obufstream : public ostream {
public:
/** constructor */
- obufstream() : m_in(0) {}
+ obufstream() {}
/** Constructor
* \param[in] buf buffer for formatted string
* \param[in] size buffer size
@@ -131,7 +131,7 @@ class obufstream : public ostream {
protected:
/// @cond SHOW_PROTECTED
void putch(char c) {
- if (m_in >= (m_size - 1)) {
+ if ((m_in + 1) >= m_size) {
setstate(badbit);
return;
}
@@ -159,14 +159,13 @@ class obufstream : public ostream {
bool sync() {
return true;
}
-
pos_type tellpos() {
return m_in;
}
/// @endcond
private:
- char *m_buf;
- size_t m_size;
- size_t m_in;
+ char *m_buf = nullptr;
+ size_t m_size = 0;
+ size_t m_in = 0;
};
#endif // bufstream_h
diff --git a/src/iostream/ios.h b/src/iostream/ios.h
index 4c0e362..cac0bdb 100644
--- a/src/iostream/ios.h
+++ b/src/iostream/ios.h
@@ -389,7 +389,7 @@ inline ios_base& uppercase(ios_base& str) {
class ios : public ios_base {
public:
/** Create ios with no error flags set */
- ios() : m_iostate(0) {}
+ ios() {}
/** \return null pointer if fail() is true. */
operator const void*() const {
@@ -443,6 +443,6 @@ class ios : public ios_base {
}
private:
- iostate m_iostate;
+ iostate m_iostate = 0;
};
#endif // ios_h