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