Browse Source

Added Long File Names

main
Bill Greiman 10 years ago
parent
commit
d60a3ab342
100 changed files with 5793 additions and 3448 deletions
  1. +0
    -62
      ArduinoDue.txt
  2. +0
    -13
      MultipleCards.txt
  3. +0
    -21
      QuickStart.txt
  4. +0
    -23
      SPI_Transactions.txt
  5. +3
    -1
      SdFat/MinimumSerial.cpp
  6. +7
    -7
      SdFat/MinimumSerial.h
  7. +68
    -33
      SdFat/SdFat.h
  8. +3
    -5
      SdFat/SdFatBase.cpp
  9. +59
    -67
      SdFat/SdFatConfig.h
  10. +3
    -14
      SdFat/SdFatUtil.cpp
  11. +14
    -3
      SdFat/SdFatUtil.h
  12. +151
    -47
      SdFat/SdFatmainpage.h
  13. +59
    -42
      SdFat/SdFile.h
  14. +3
    -3
      SdFat/SdInfo.h
  15. +86
    -83
      SdFat/SdSpi.h
  16. +104
    -48
      SdFat/SdSpiCard.cpp
  17. +72
    -45
      SdFat/SdSpiCard.h
  18. +22
    -20
      SdFat/SdSpiSAM3X.cpp
  19. +17
    -15
      SdFat/SdVolume.h
  20. +39
    -24
      SdFat/examples/#attic/AnalogLogger/AnalogLogger.ino
  21. +38
    -0
      SdFat/examples/#attic/BaseExtCaseTest/BaseExtCaseTest.ino
  22. +1
    -1
      SdFat/examples/#attic/HelloWorld/HelloWorld.ino
  23. +20
    -12
      SdFat/examples/#attic/PrintBenchmarkSD/PrintBenchmarkSD.ino
  24. +2
    -2
      SdFat/examples/#attic/SD_Size/SD_Size.ino
  25. +4
    -4
      SdFat/examples/#attic/SdFatSize/SdFatSize.ino
  26. +21
    -13
      SdFat/examples/#attic/append/append.ino
  27. +15
    -9
      SdFat/examples/#attic/average/average.ino
  28. +20
    -11
      SdFat/examples/#attic/benchSD/benchSD.ino
  29. +2
    -2
      SdFat/examples/#attic/bufstream/bufstream.ino
  30. +13
    -9
      SdFat/examples/#attic/eventlog/eventlog.ino
  31. +50
    -36
      SdFat/examples/#attic/fgetsRewrite/fgetsRewrite.ino
  32. +14
    -8
      SdFat/examples/#attic/readlog/readlog.ino
  33. +114
    -76
      SdFat/examples/AnalogBinLogger/AnalogBinLogger.ino
  34. +31
    -24
      SdFat/examples/LongFileName/LongFileName.ino
  35. +0
    -0
      SdFat/examples/LongFileName/testFiles/With.Two dots.txt
  36. +72
    -48
      SdFat/examples/LowLatencyLogger/LowLatencyLogger.ino
  37. +7
    -4
      SdFat/examples/OpenNext/OpenNext.ino
  38. +41
    -35
      SdFat/examples/PrintBenchmark/PrintBenchmark.ino
  39. +62
    -60
      SdFat/examples/QuickStart/QuickStart.ino
  40. +30
    -25
      SdFat/examples/RawWrite/RawWrite.ino
  41. +88
    -0
      SdFat/examples/ReadWrite/ReadWrite.ino
  42. +12
    -8
      SdFat/examples/ReadWriteSdFat/ReadWriteSdFat.ino
  43. +101
    -77
      SdFat/examples/SdFormatter/SdFormatter.ino
  44. +74
    -68
      SdFat/examples/SdInfo/SdInfo.ino
  45. +14
    -12
      SdFat/examples/SoftwareSpi/SoftwareSpi.ino
  46. +52
    -48
      SdFat/examples/StdioBench/StdioBench.ino
  47. +7
    -7
      SdFat/examples/StreamParseInt/StreamParseInt.ino
  48. +114
    -74
      SdFat/examples/ThreeCards/ThreeCards.ino
  49. +27
    -23
      SdFat/examples/Timestamp/Timestamp.ino
  50. +70
    -43
      SdFat/examples/TwoCards/TwoCards.ino
  51. +50
    -39
      SdFat/examples/bench/bench.ino
  52. +1
    -1
      SdFat/examples/cin_cout/cin_cout.ino
  53. +31
    -23
      SdFat/examples/dataLogger/dataLogger.ino
  54. +54
    -40
      SdFat/examples/directoryFunctions/directoryFunctions.ino
  55. +33
    -27
      SdFat/examples/fgets/fgets.ino
  56. +6
    -4
      SdFat/examples/formatting/formatting.ino
  57. +13
    -11
      SdFat/examples/getline/getline.ino
  58. +46
    -32
      SdFat/examples/readCSV/readCSV.ino
  59. +50
    -26
      SdFat/examples/rename/rename.ino
  60. +32
    -14
      SdFat/utility/ArduinoStream.h
  61. +19
    -13
      SdFat/utility/DigitalPin.h
  62. +7
    -5
      SdFat/utility/FatApiConstants.h
  63. +497
    -530
      SdFat/utility/FatFile.cpp
  64. +258
    -175
      SdFat/utility/FatFile.h
  65. +593
    -212
      SdFat/utility/FatFileLFN.cpp
  66. +30
    -73
      SdFat/utility/FatFilePrint.cpp
  67. +273
    -0
      SdFat/utility/FatFileSFN.cpp
  68. +54
    -29
      SdFat/utility/FatFileSystem.h
  69. +27
    -0
      SdFat/utility/FatLibConfig.h
  70. +354
    -361
      SdFat/utility/FatStructs.h
  71. +154
    -52
      SdFat/utility/FatVolume.cpp
  72. +123
    -46
      SdFat/utility/FatVolume.h
  73. +64
    -24
      SdFat/utility/FmtNumber.cpp
  74. +7
    -1
      SdFat/utility/FmtNumber.h
  75. +16
    -8
      SdFat/utility/SoftSPI.h
  76. +132
    -54
      SdFat/utility/StdioStream.cpp
  77. +44
    -28
      SdFat/utility/StdioStream.h
  78. +25
    -9
      SdFat/utility/bufstream.h
  79. +64
    -46
      SdFat/utility/fstream.cpp
  80. +81
    -29
      SdFat/utility/fstream.h
  81. +37
    -13
      SdFat/utility/ios.h
  82. +53
    -19
      SdFat/utility/istream.cpp
  83. +93
    -85
      SdFat/utility/istream.h
  84. +21
    -7
      SdFat/utility/ostream.cpp
  85. +12
    -4
      SdFat/utility/ostream.h
  86. +9
    -9
      SdFatTestSuite/SdFatTestSuite.cpp
  87. +9
    -7
      SdFatTestSuite/examples/TestMkdir/TestMkdir.ino
  88. +1
    -1
      SdFatTestSuite/examples/TestRmdir/TestRmdir.ino
  89. +35
    -0
      SdFatTestSuite/examples/lfnSize/lfnSize.ino
  90. +235
    -0
      SdFatTestSuite/examples/lfnTest/lfnTest.ino
  91. +219
    -0
      SdFatTestSuite/examples/lfnTestCout/lfnTestCout.ino
  92. BIN
      SdLevel.png
  93. +0
    -15
      SoftwareSPI.txt
  94. +0
    -44
      WorkingDirectory.txt
  95. +4
    -0
      changes.txt
  96. +2
    -2
      html/_arduino_stream_8h.html
  97. BIN
      html/_arduino_stream_8h__incl.png
  98. +1
    -1
      html/_digital_pin_8h.html
  99. +98
    -14
      html/_fat_file_8h.html
  100. +0
    -0
      html/_fat_file_8h__incl.png

+ 0
- 62
ArduinoDue.txt View File

Support has been added for the Arduino Due.

You must connect your SD socket to the 6-pin "ISP connector". You must have short
wires or a custom shield to run at full speed, 42 MHz.

If you have problems use a lower SPI speed. You can also check for SPI
errors by editing SdFatCobfig.h to enable CRC checking.

You should be be able to use any digital pin for SD chip select. The default
pin is SS which is pin 10 for Due.

The default SPI rate is 42 MHz. You can set SD chip select and the SPI rate
by calling:

bool SdFat::begin(uint8_t chipSelectPin, uint8_t spiRateID);

The second argument, spiRateID, sets the SCK rate and can be these symbols:

SPI_FULL_SPEED - 42 MHz

SPI_DIV3_SPEED - 28 MHz

SPI_HALF_SPEED - 21 MHz

SPI_DIV6_SPEED - 14 MHz

SPI_QUARTER_SPEED 10.5 MHz

SPI_EIGHTH_SPEED 5.25 MHz

Large reads and writes use fast multi-block SD read/write commands. For optimal
speed, use records that are a multiple of 512 bytes.

Run the bench.ino example to explore large read/write speed.

Replace this line:

#define BUF_SIZE 100

With a large size like this:

#define BUF_SIZE 8192

For best results the record size should be a power of two (512, 1024, 2048,
4096, 8192). In this case records will be aligned with FAT cluster boundaries.

Since Due is fast, increase the test file size by editing this line:

#define FILE_SIZE_MB 5

Run the PrintBenchmark.ino example to compare text formatting speed of Due
with AVR boards.

A number of options are available to configure SPI for the Due board.

You can use the standard SPI.h library by editing SdFatConfig.h and set
USE_ARDUINO_SPI_LIBRARY nonzero. You must include SPI.h in your sketch.

Several options can be set in Sd2Card.cpp in the USE_NATIVE_SAM3X_SPI
section. These include USE_SAM3X_DMAC to control use of DMA and
USE_SAM3X_BUS_MATRIX_FIX to change Bus Matrix operation. Most people
will not need to change these.

+ 0
- 13
MultipleCards.txt View File

SdFat has support for multiple SD cards. This requires multiple instances
of SdFat objects.

You must edit SdFatConfig.h to enable multiple instances of SdFat. Set
USE_MULTIPLE_CARDS nonzero like this:

#define USE_MULTIPLE_CARDS 1

Look at TwoCards.pde in the SdFat/examples folder. This example demonstrates
use of two SD cards.

Read WorkingDirectory.txt for more information on volume working
directories and the current working directory.

+ 0
- 21
QuickStart.txt View File

For those who don't like too much documentation.

To use this library place the SdFat folder into the libraries
subfolder in your main sketches folder. You may need to
create the libraries folder. Restart the Arduino IDE if
it was open.

Run the QuickStart.ino sketch from the
libraries/SdFat/examples/QuickStart folder. Click the
IDE up-arrow icon then -> libraries -> SdFat -> QuickStart.

You can also click File -> Examples -> SdFat -> QuickStart.

If problems occur try reading more documentation and use these
forums for help:

http://forums.adafruit.com/

http://arduino.cc/forum/

If QuickStart.ino runs successfully try more examples.

+ 0
- 23
SPI_Transactions.txt View File

To enable support for SPI transactions, edit SfFatCinfig.h and modify these
defines.

//------------------------------------------------------------------------------
/**
* Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature
* of the standard Arduino SPI library. You must include SPI.h in your
* sketches when ENABLE_SPI_TRANSACTION is nonzero.
*/
#define ENABLE_SPI_TRANSACTION 0
//------------------------------------------------------------------------------
/**
* Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during
* SD card busy waits.
*
* This will allow interrupt routines to access the SPI bus if
* ENABLE_SPI_TRANSACTION is nonzero.
*
* Setting ENABLE_SPI_YIELD will introduce some extra overhead and will
* slightly slow transfer rates. A few older SD cards may fail when
* ENABLE_SPI_YIELD is nonzero.
*/
#define ENABLE_SPI_YIELD 0

+ 3
- 1
SdFat/MinimumSerial.cpp View File

} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int MinimumSerial::read() { int MinimumSerial::read() {
if (UCSR0A & (1 << RXC0)) return UDR0;
if (UCSR0A & (1 << RXC0)) {
return UDR0;
}
return -1; return -1;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

+ 7
- 7
SdFat/MinimumSerial.h View File

/** /**
* Unbuffered read * Unbuffered read
* \return -1 if no character is available or an available character. * \return -1 if no character is available or an available character.
*/
*/
int read(); int read();
/**
* Unbuffered write
*
* \param[in] b byte to write.
* \return 1
*/
/**
* Unbuffered write
*
* \param[in] b byte to write.
* \return 1
*/
size_t write(uint8_t b); size_t write(uint8_t b);
using Print::write; using Print::write;
}; };

+ 68
- 33
SdFat/SdFat.h View File

#include "utility/FatLib.h" #include "utility/FatLib.h"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** SdFat version YYYYMMDD */ /** SdFat version YYYYMMDD */
#define SD_FAT_VERSION 20141115
#define SD_FAT_VERSION 20141204
//============================================================================== //==============================================================================
/** /**
* \class SdFatBase * \class SdFatBase
*/ */
bool begin(SdSpiCard::m_spi_t* spi, uint8_t csPin = SS, uint8_t divisor = 2) { bool begin(SdSpiCard::m_spi_t* spi, uint8_t csPin = SS, uint8_t divisor = 2) {
return m_sdCard.begin(spi, csPin, divisor) && return m_sdCard.begin(spi, csPin, divisor) &&
FatFileSystem::begin(&m_vwd);
FatFileSystem::begin(&m_vwd);
} }
/** \return Pointer to SD card object */ /** \return Pointer to SD card object */
SdSpiCard *card() {return &m_sdCard;}
SdSpiCard *card() {
return &m_sdCard;
}
/** %Print any SD error code to Serial and halt. */ /** %Print any SD error code to Serial and halt. */
void errorHalt() {errorHalt(&Serial);}
void errorHalt() {
errorHalt(&Serial);
}
/** %Print any SD error code and halt. /** %Print any SD error code and halt.
* *
* \param[in] pr Print destination. * \param[in] pr Print destination.
* *
* \param[in] msg Message to print. * \param[in] msg Message to print.
*/ */
void errorHalt(char const* msg) {errorHalt(&Serial, msg);}
void errorHalt(char const* msg) {
errorHalt(&Serial, msg);
}
/** %Print msg, any SD error code, and halt. /** %Print msg, any SD error code, and halt.
* *
* \param[in] pr Print destination. * \param[in] pr Print destination.
* *
* \param[in] msg Message to print. * \param[in] msg Message to print.
*/ */
void errorHalt(const __FlashStringHelper* msg) {errorHalt(&Serial, msg);}
void errorHalt(const __FlashStringHelper* msg) {
errorHalt(&Serial, msg);
}
/** %Print msg, any SD error code, and halt. /** %Print msg, any SD error code, and halt.
* *
* \param[in] pr Print destination. * \param[in] pr Print destination.
*/ */
void errorHalt(Print* pr, const __FlashStringHelper* msg); void errorHalt(Print* pr, const __FlashStringHelper* msg);
/** %Print any SD error code to Serial */ /** %Print any SD error code to Serial */
void errorPrint() {errorPrint(&Serial);}
void errorPrint() {
errorPrint(&Serial);
}
/** %Print any SD error code. /** %Print any SD error code.
* \param[in] pr Print device. * \param[in] pr Print device.
*/ */
* *
* \param[in] msg Message to print. * \param[in] msg Message to print.
*/ */
void errorPrint(const char* msg) {errorPrint(&Serial, msg);}
void errorPrint(const char* msg) {
errorPrint(&Serial, msg);
}
/** %Print msg, any SD error code. /** %Print msg, any SD error code.
* *
* \param[in] pr Print destination. * \param[in] pr Print destination.
* *
* \param[in] msg Message to print. * \param[in] msg Message to print.
*/ */
void errorPrint(const __FlashStringHelper* msg) {errorPrint(&Serial, msg);}
void errorPrint(const __FlashStringHelper* msg) {
errorPrint(&Serial, msg);
}
/** %Print msg, any SD error code. /** %Print msg, any SD error code.
* *
* \param[in] pr Print destination. * \param[in] pr Print destination.
* \param[in] msg Message to print. * \param[in] msg Message to print.
*/ */
void errorPrint(Print* pr, const __FlashStringHelper* msg); void errorPrint(Print* pr, const __FlashStringHelper* msg);
/** Diagnostic call to initialize FatFileSystem.
/** Diagnostic call to initialize FatFileSystem - use for
* diagnostic purposes only.
* \return true for success else false. * \return true for success else false.
*/ */
bool fsBegin() {return FatFileSystem::begin(&m_vwd);}
bool fsBegin() {
return FatFileSystem::begin(&m_vwd);
}
/** %Print any SD error code and halt. */ /** %Print any SD error code and halt. */
void initErrorHalt() {initErrorHalt(&Serial);}
void initErrorHalt() {
initErrorHalt(&Serial);
}
/** %Print error details and halt after begin fails. /** %Print error details and halt after begin fails.
* *
* \param[in] pr Print destination. * \param[in] pr Print destination.
* *
* \param[in] msg Message to print. * \param[in] msg Message to print.
*/ */
void initErrorHalt(char const *msg) {initErrorHalt(&Serial, msg);}
void initErrorHalt(char const *msg) {
initErrorHalt(&Serial, msg);
}
/**Print message, error details, and halt after SdFatBase::init() fails. /**Print message, error details, and halt after SdFatBase::init() fails.
* \param[in] pr Print device. * \param[in] pr Print device.
* \param[in] msg Message to print. * \param[in] msg Message to print.
*/ */
void initErrorHalt(Print* pr, char const *msg); void initErrorHalt(Print* pr, char const *msg);
/**Print message, error details, and halt after SdFat::init() fails.
*
* \param[in] msg Message to print.
*/
/**Print message, error details, and halt after SdFat::init() fails.
*
* \param[in] msg Message to print.
*/
void initErrorHalt(const __FlashStringHelper* msg) { void initErrorHalt(const __FlashStringHelper* msg) {
initErrorHalt(&Serial, msg); initErrorHalt(&Serial, msg);
} }
/**Print message, error details, and halt after SdFatBase::init() fails.
* \param[in] pr Print device for message.
* \param[in] msg Message to print.
*/
void initErrorHalt(Print* pr, const __FlashStringHelper* msg); void initErrorHalt(Print* pr, const __FlashStringHelper* msg);
/** Print error details after SdFat::init() fails. */ /** Print error details after SdFat::init() fails. */
void initErrorPrint() {initErrorPrint(&Serial);}
void initErrorPrint() {
initErrorPrint(&Serial);
}
/** Print error details after SdFatBase::init() fails. /** Print error details after SdFatBase::init() fails.
* *
* \param[in] pr Print destination. * \param[in] pr Print destination.
* *
* \param[in] msg Message to print. * \param[in] msg Message to print.
*/ */
void initErrorPrint(char const *msg) {initErrorPrint(&Serial, msg);}
void initErrorPrint(char const *msg) {
initErrorPrint(&Serial, msg);
}
/**Print message and error details and halt after SdFatBase::init() fails. /**Print message and error details and halt after SdFatBase::init() fails.
* *
* \param[in] pr Print destination. * \param[in] pr Print destination.
*/ */
File open(const char *path, uint8_t mode = FILE_READ); File open(const char *path, uint8_t mode = FILE_READ);
/** \return a pointer to the volume working directory. */ /** \return a pointer to the volume working directory. */
SdBaseFile* vwd() {return &m_vwd;}
SdBaseFile* vwd() {
return &m_vwd;
}


using FatFileSystem::ls; using FatFileSystem::ls;


private: private:
uint8_t cardErrorCode() {return m_sdCard.errorCode();}
uint8_t cardErrorData() {return m_sdCard.errorData();}
uint8_t cardErrorCode() {
return m_sdCard.errorCode();
}
uint8_t cardErrorData() {
return m_sdCard.errorData();
}
bool readBlock(uint32_t block, uint8_t* dst) { bool readBlock(uint32_t block, uint8_t* dst) {
return m_sdCard.readBlock(block, dst); return m_sdCard.readBlock(block, dst);
} }
bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { bool begin(uint8_t csPin = SS, uint8_t divisor = 2) {
return SdFatBase::begin(&m_spi, csPin, divisor); return SdFatBase::begin(&m_spi, csPin, divisor);
} }
/** Initialize SD card - use for diagnostic purposes.
/** Diagnostic call to initialize SD card - use for diagnostic purposes only.
* \param[in] csPin SD card chip select pin. * \param[in] csPin SD card chip select pin.
* \param[in] divisor SPI divisor. * \param[in] divisor SPI divisor.
* \return true for success else false. * \return true for success else false.
SpiDefault_t m_spi; SpiDefault_t m_spi;
}; };
//============================================================================== //==============================================================================
#if USE_MULTIPLE_SPI_TYPES || defined(DOXYGEN)
#if SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN)
/** /**
* \class SdFatLibSpi * \class SdFatLibSpi
* \brief SdFat class using the standard Arduino SPI library. * \brief SdFat class using the standard Arduino SPI library.
*/ */
class SdFatLibSpi: public SdFatBase { class SdFatLibSpi: public SdFatBase {
public: public:
/** Initialize SD card and file system.
*
* \param[in] csPin SD card chip select pin.
* \param[in] divisor SPI divisor.
* \return true for success else false.
*/
/** Initialize SD card and file system.
*
* \param[in] csPin SD card chip select pin.
* \param[in] divisor SPI divisor.
* \return true for success else false.
*/
bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { bool begin(uint8_t csPin = SS, uint8_t divisor = 2) {
return SdFatBase::begin(&m_spi, csPin, divisor); return SdFatBase::begin(&m_spi, csPin, divisor);
} }
/** Initialize SD card - use for diagnostic purposes.
/** Diagnostic call to initialize SD card - use for diagnostic purposes only.
* \param[in] csPin SD card chip select pin. * \param[in] csPin SD card chip select pin.
* \param[in] divisor SPI divisor. * \param[in] divisor SPI divisor.
* \return true for success else false. * \return true for success else false.
bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { bool begin(uint8_t csPin = SS, uint8_t divisor = 2) {
return SdFatBase::begin(&m_spi, csPin, divisor); return SdFatBase::begin(&m_spi, csPin, divisor);
} }
/** Initialize SD card - use for diagnostic purposes.
/** Diagnostic call to initialize SD card - use for diagnostic purposes only.
* \param[in] csPin SD card chip select pin. * \param[in] csPin SD card chip select pin.
* \param[in] divisor SPI divisor. * \param[in] divisor SPI divisor.
* \return true for success else false. * \return true for success else false.
private: private:
SdSpiSoft<MisoPin, MosiPin, SckPin> m_spi; SdSpiSoft<MisoPin, MosiPin, SckPin> m_spi;
}; };
#endif // USE_MULTIPLE_SPI_TYPES
#endif /// SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN)
#endif // SdFat_h #endif // SdFat_h

+ 3
- 5
SdFat/SdFatBase.cpp View File

} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void SdFatBase::errorPrint(Print* pr) { void SdFatBase::errorPrint(Print* pr) {
if (!cardErrorCode()) return;
if (!cardErrorCode()) {
return;
}
pr->print(F("SD errorCode: 0X")); pr->print(F("SD errorCode: 0X"));
pr->print(cardErrorCode(), HEX); pr->print(cardErrorCode(), HEX);
pr->print(F(",0X")); pr->print(F(",0X"));
initErrorHalt(pr); initErrorHalt(pr);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/**Print message, error details, and halt after SdFatBase::init() fails.
* \param[in] pr Print device for message.
* \param[in] msg Message to print.
*/
void SdFatBase::initErrorHalt(Print* pr, const __FlashStringHelper* msg) { void SdFatBase::initErrorHalt(Print* pr, const __FlashStringHelper* msg) {
pr->println(msg); pr->println(msg);
initErrorHalt(pr); initErrorHalt(pr);

+ 59
- 67
SdFat/SdFatConfig.h View File

#endif // __AVR__ #endif // __AVR__
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
* Set USE_MULTIPLE_SPI_TYPES nonzero to enable the SdFatSoftSpi and
* SdFatLibSpi classes. SdFatSoftSpi uses software SPI and SdFatLibSpi
* uses the standard Arduino SPI library.
* Set USE_LONG_FILE_NAMES nonzero to use long file names (LFN).
* Long File Name are limited to a maximum length of 255 characters.
*
* This implementation allows 7-bit characters in the range
* 0X20 to 0X7E except the following characters are not allowed:
*
* < (less than)
* > (greater than)
* : (colon)
* " (double quote)
* / (forward slash)
* \ (backslash)
* | (vertical bar or pipe)
* ? (question mark)
* * (asterisk)
*
*/ */
#define USE_MULTIPLE_SPI_TYPES 0
#define USE_LONG_FILE_NAMES 1
//------------------------------------------------------------------------------
/**
* The symbol SD_SPI_CONFIGURATION defines SPI access to the SD card.
*
* IF SD_SPI_CONFIGUTATION is define to be zero, only the SdFat class
* is define and SdFat uses a fast custom SPI implementation.
*
* If SD_SPI_CONFIGURATION is define to be one, only the SdFat class is
* define and SdFat uses the standard Arduino SD.h library.
*
* If SD_SPI_CONFIGURATION is define to be two, only the SdFat class is
* define and SdFat uses software SPI on the pins defined below.
*
* If SD_SPI_CONFIGURATION is define to be three, the three classes, SdFat,
* SdFatLibSpi, and SdFatSoftSpi are defined. SdFat uses the fast
* custom SPI implementation. SdFatLibSpi uses the standard Arduino SPI
* library. SdFatSoftSpi is a template class that uses Software SPI. The
* template parameters define the software SPI pins. See the ThreeCard
* example for simultaneous use of all three classes.
*/
#define SD_SPI_CONFIGURATION 0
//------------------------------------------------------------------------------
/**
* If SD_SPI_CONFIGURATION is defined to be two, these definitions
* will define the pins used for software SPI.
*
* The default definition allows Uno shields to be used on other boards.
*/
/** Software SPI Master Out Slave In pin */
uint8_t const SOFT_SPI_MOSI_PIN = 11;
/** Software SPI Master In Slave Out pin */
uint8_t const SOFT_SPI_MISO_PIN = 12;
/** Software SPI Clock pin */
uint8_t const SOFT_SPI_SCK_PIN = 13;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
* To enable SD card CRC checking set USE_SD_CRC nonzero. * To enable SD card CRC checking set USE_SD_CRC nonzero.
/** /**
* Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature * Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature
* of the standard Arduino SPI library. You must include SPI.h in your * of the standard Arduino SPI library. You must include SPI.h in your
* sketches when ENABLE_SPI_TRANSACTION is nonzero.
* programs when ENABLE_SPI_TRANSACTION is nonzero.
*/ */
#define ENABLE_SPI_TRANSACTION 0 #define ENABLE_SPI_TRANSACTION 0
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
* Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during * Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during
* SD card busy waits.
* SD card busy waits.
* *
* This will allow interrupt routines to access the SPI bus if
* This will allow interrupt routines to access the SPI bus if
* ENABLE_SPI_TRANSACTION is nonzero. * ENABLE_SPI_TRANSACTION is nonzero.
*
*
* Setting ENABLE_SPI_YIELD will introduce some extra overhead and will * Setting ENABLE_SPI_YIELD will introduce some extra overhead and will
* slightly slow transfer rates. A few older SD cards may fail when
* slightly slow transfer rates. A few older SD cards may fail when
* ENABLE_SPI_YIELD is nonzero. * ENABLE_SPI_YIELD is nonzero.
*/ */
#define ENABLE_SPI_YIELD 0 #define ENABLE_SPI_YIELD 0
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/**
* Set USE_ARDUINO_SPI_LIBRARY nonzero to force use of the Arduino Standard
* SPI library in the SdFat class. This will override native and software
* SPI for all boards.
*/
#define USE_ARDUINO_SPI_LIBRARY 0
//------------------------------------------------------------------------------
/**
* Set AVR_SOFT_SPI nonzero to use software SPI in the SdFat class
* on all AVR Arduinos. Set the soft SPI pins below.
*/
#define AVR_SOFT_SPI 0
//------------------------------------------------------------------------------
/**
* Set DUE_SOFT_SPI nonzero to use software SPI in the SdFat class
* on Due Arduinos. Set the soft SPI pins below.
*/
#define DUE_SOFT_SPI 0
//------------------------------------------------------------------------------
/**
* Set LEONARDO_SOFT_SPI nonzero to use software SPI in the SdFat class
* on Leonardo Arduinos. Set the soft SPI pins below.
*
* LEONARDO_SOFT_SPI allows an unmodified 328 Shield to be used
* on Leonardo Arduinos.
*/
#define LEONARDO_SOFT_SPI 0
//------------------------------------------------------------------------------
/**
* Set MEGA_SOFT_SPI nonzero to use software SPI in the SdFat class
* on Mega Arduinos. Set the soft SPI pins below.
*
* MEGA_SOFT_SPI allows an unmodified 328 Shield to be used
* on Mega Arduinos. Set the soft SPI pins below.
*/
#define MEGA_SOFT_SPI 0
//------------------------------------------------------------------------------
/**
* Set TEENSY3_SOFT_SPI nonzero to use software SPI in the SdFat class
* on Teensy 3.x boards. Set the soft SPI pins below.
*/
#define TEENSY3_SOFT_SPI 0
//------------------------------------------------------------------------------
/**
* Define software SPI pins. Default allows Uno shields to be used on other
* boards.
*/
// define software SPI pins
/** Software SPI Master Out Slave In pin */
uint8_t const SOFT_SPI_MOSI_PIN = 11;
/** Software SPI Master In Slave Out pin */
uint8_t const SOFT_SPI_MISO_PIN = 12;
/** Software SPI Clock pin */
uint8_t const SOFT_SPI_SCK_PIN = 13;
//------------------------------------------------------------------------------
/** /**
* Set FAT12_SUPPORT nonzero to enable use if FAT12 volumes. * Set FAT12_SUPPORT nonzero to enable use if FAT12 volumes.
* FAT12 has not been well tested and requires additional flash. * FAT12 has not been well tested and requires additional flash.
/** /**
* Set SD_FILE_USES_STREAM nonzero to use Stream instead of Print for SdFile. * Set SD_FILE_USES_STREAM nonzero to use Stream instead of Print for SdFile.
* Using Stream will use more flash and may cause compatibility problems * Using Stream will use more flash and may cause compatibility problems
* with code written for older versions of SdFat.
* with code written for older versions of SdFat.
*/ */
#define SD_FILE_USES_STREAM 0 #define SD_FILE_USES_STREAM 0
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
* Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache * Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache
* for FAT table entries. Improves performance for large writes that
* are not a multiple of 512 bytes.
* for FAT table entries. This improves performance for large writes
* that are not a multiple of 512 bytes.
*/ */
#ifdef __arm__ #ifdef __arm__
#define USE_SEPARATE_FAT_CACHE 1 #define USE_SEPARATE_FAT_CACHE 1

+ 3
- 14
SdFat/SdFatUtil.cpp View File

//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifdef __arm__ #ifdef __arm__
extern "C" char* sbrk(int incr); extern "C" char* sbrk(int incr);
/** Amount of free RAM
* \return The number of free bytes.
*/
int SdFatUtil::FreeRam() { int SdFatUtil::FreeRam() {
char top; char top;
return &top - reinterpret_cast<char*>(sbrk(0)); return &top - reinterpret_cast<char*>(sbrk(0));
} }
#endif // __arm #endif // __arm
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** %Print a string in flash memory.
*
* \param[in] pr Print object for output.
* \param[in] str Pointer to string stored in flash memory.
*/
void SdFatUtil::print_P(Print* pr, PGM_P str) { void SdFatUtil::print_P(Print* pr, PGM_P str) {
for (uint8_t c; (c = pgm_read_byte(str)); str++) pr->write(c);
for (uint8_t c; (c = pgm_read_byte(str)); str++) {
pr->write(c);
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** %Print a string in flash memory followed by a CR/LF.
*
* \param[in] pr Print object for output.
* \param[in] str Pointer to string stored in flash memory.
*/
void SdFatUtil::println_P(Print* pr, PGM_P str) { void SdFatUtil::println_P(Print* pr, PGM_P str) {
print_P(pr, str); print_P(pr, str);
pr->println(); pr->println();

+ 14
- 3
SdFat/SdFatUtil.h View File

* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with the Arduino SdFat Library. If not, see * along with the Arduino SdFat Library. If not, see
* <http://www.gnu.org/licenses/>. * <http://www.gnu.org/licenses/>.
#define PgmPrintln(x) SerialPrintln_P(PSTR(x)) #define PgmPrintln(x) SerialPrintln_P(PSTR(x))


namespace SdFatUtil { namespace SdFatUtil {
/** Amount of free RAM
* \return The number of free bytes.
*/
int FreeRam(); int FreeRam();
/** %Print a string in flash memory.
*
* \param[in] pr Print object for output.
* \param[in] str Pointer to string stored in flash memory.
*/
void print_P(Print* pr, PGM_P str); void print_P(Print* pr, PGM_P str);
/** %Print a string in flash memory followed by a CR/LF.
*
* \param[in] pr Print object for output.
* \param[in] str Pointer to string stored in flash memory.
*/
void println_P(Print* pr, PGM_P str); void println_P(Print* pr, PGM_P str);
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** %Print a string in flash memory to Serial. /** %Print a string in flash memory to Serial.
inline void SerialPrintln_P(PGM_P str) { inline void SerialPrintln_P(PGM_P str) {
println_P(&Serial, str); println_P(&Serial, str);
} }
void SerialPrint_P(PGM_P str);
void SerialPrintln_P(PGM_P str);
} // namespace SdFatUtil } // namespace SdFatUtil
using namespace SdFatUtil; // NOLINT using namespace SdFatUtil; // NOLINT
#endif // #define SdFatUtil_h #endif // #define SdFatUtil_h

+ 151
- 47
SdFat/SdFatmainpage.h View File

/* Arduino SdFat Library /* Arduino SdFat Library
* Copyright (C) 2012 by William Greiman * Copyright (C) 2012 by William Greiman
*
*
* This file is part of the Arduino SdFat Library * This file is part of the Arduino SdFat Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
*
*
* This Library is distributed in the hope that it will be useful, * This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Experimental support for FAT12 can be enabled by setting FAT12_SUPPORT Experimental support for FAT12 can be enabled by setting FAT12_SUPPORT
nonzero in SdFatConfig.h. nonzero in SdFatConfig.h.


The %SdFat library only supports short 8.3 names. Limited Long %File Name
access is provided by \ref SdFile::openNextLFN. See the LongFileName example.
The %SdFat library supports Long %File Names or short 8.3 names.
Edit the SdFatConfig.h file to select short or long file names.


The main classes in %SdFat are SdFat, SdFatSoftSpi, SdFatLibSpi, The main classes in %SdFat are SdFat, SdFatSoftSpi, SdFatLibSpi,
SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream, SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream,
a current working directory, and simplifies initialization of other classes. a current working directory, and simplifies initialization of other classes.
The SdFat class uses a fast custom hardware SPI implementation. The The SdFat class uses a fast custom hardware SPI implementation. The
SdFatLibSpi class uses the standard Arduino SPI library. The SdFatSoftSpi SdFatLibSpi class uses the standard Arduino SPI library. The SdFatSoftSpi
class uses software SPI.
class uses software SPI.


The SdBaseFile class provides basic file access functions such as open(), The SdBaseFile class provides basic file access functions such as open(),
binary read(), binary write(), close(), remove(), and sync(). SdBaseFile binary read(), binary write(), close(), remove(), and sync(). SdBaseFile
Print class functions. Print class functions.


The File class has all the SdBaseFile functions plus the functions in The File class has all the SdBaseFile functions plus the functions in
the Arduino SD.h File class. This provides compatibility with the
the Arduino SD.h File class. This provides compatibility with the
Arduino SD.h library. Arduino SD.h library.


The StdioStream class implements functions similar to Linux/Unix standard The StdioStream class implements functions similar to Linux/Unix standard
buffered input/output.
buffered input/output.


The \ref fstream class implements C++ iostreams for both reading and writing The \ref fstream class implements C++ iostreams for both reading and writing
text files. text files.


The \ref ofstream class implements C++ iostreams for writing text files. The \ref ofstream class implements C++ iostreams for writing text files.


The classes \ref ifstream, \ref ofstream, \ref istream, and \ref ostream
The classes \ref ifstream, \ref ofstream, \ref istream, and \ref ostream
follow the C++ \ref iostream standard when possible. follow the C++ \ref iostream standard when possible.


There are many tutorials and much documentation about using C++ iostreams There are many tutorials and much documentation about using C++ iostreams
\section Install Installation \section Install Installation


You must manually install SdFat by copying the SdFat folder from the download You must manually install SdFat by copying the SdFat folder from the download
package to the Arduino libraries folder in you sketch book.
package to the Arduino libraries folder in you sketch folder.


See the Manual installation section of this guide. See the Manual installation section of this guide.


\section SDconfig SdFat Configuration \section SDconfig SdFat Configuration


Several configuration options may be changed by editing the SdFatConfig.h Several configuration options may be changed by editing the SdFatConfig.h
file in the SdFat folder.
file in the %SdFat folder.


Set USE_MULTIPLE_SPI_TYPES nonzero to enable the SdFatSoftSpi and
SdFatLibSpi classes. SdFatSoftSpi uses software SPI and SdFatLibSpi
uses the standard Arduino SPI library.
Set USE_LONG_FILE_NAMES nonzero to enable Long %File Names. By default,
Long %File Names are enabled. For the leanest fastest library disable
Long %File Names. Long %File names require extra flash but no extra RAM.
Opening Long %File Names can be slower than opening Short %File Names.
Data read and write performance is not changed by the type of %File Name.

Set SD_SPI_CONFIGURATION to enable various SPI options. The SdFatSoftSpi
and SdFatLibSpi classes can be enabled. SdFatLibSpi uses the standard
Arduino SPI library and SdFatSoftSpi uses software SPI.


To enable SD card CRC checking set USE_SD_CRC nonzero. To enable SD card CRC checking set USE_SD_CRC nonzero.


Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes.
FAT12 has not been well tested and requires additional flash. FAT12 has not been well tested and requires additional flash.


Set USE_ARDUINO_SPI_LIBRARY nonzero to force use of the Arduino Standard
SPI library in the SdFat class. This will override native and software
SPI for all boards.

Use of software SPI can be enabled in the SdFat class for selected boards
by setting the symbols AVR_SOFT_SPI, DUE_SOFT_SPI, LEONARDO_SOFT_SPI,
MEGA_SOFT_SPI, and TEENSY3_SOFT_SPI.

Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature
of the standard Arduino SPI library. You must include SPI.h in your of the standard Arduino SPI library. You must include SPI.h in your
sketches when ENABLE_SPI_TRANSACTION is nonzero.
programs when ENABLE_SPI_TRANSACTION is nonzero.


Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during
SD card busy waits.
SD card busy waits.

\section SDPath Paths and Working Directories

Relative paths in SdFat are resolved in a manner similar to Windows.

Each instance of SdFat has a current directory. In SdFat this directory
is called the volume working directory, vwd. Initially this directory is
the root directory for the volume.

The volume working directory is changed by calling SdFat::chdir(path).

The call sd.chdir("/2014") will change the volume working directory
for sd to "/2014", assuming "/2014" exists.

Relative paths for SdFat member functions are resolved by starting at
the volume working directory.

For example, the call sd.mkdir("April") will create the directory
"/2014/April" assuming the volume working directory is "/2014".

SdFat has a current working directory, cwd, that is used to resolve paths
for file.open() calls.

For a single SD card the current working directory is always the volume
working directory for that card.

For multiple SD cards the current working directory is set to the volume
working directory of a card by calling the SdFat::chvol() member function.
The chvol() call is like the Windows \<drive letter>: command.

The call sd2.chvol() will set the current working directory to the volume
working directory for sd2.

If the volume working directory for sd2 is "/music" the call

file.open("BigBand.wav", O_READ);

will then open "/music/BigBand.wav" on sd2.

The following functions are used to change or get current directories.
See the html documentation for more information.
@code
bool SdFat::chdir(bool set_cwd = false);
bool SdFat::chdir(const char* path, bool set_cwd = false);
void SdFat::chvol();
SdBaseFile* SdFat::vwd();
static SdBaseFile* SdBaseFile::cwd();
@endcode


\section SDcard SD\SDHC Cards \section SDcard SD\SDHC Cards


\section Hardware Hardware Configuration \section Hardware Hardware Configuration


%SdFat was developed using an %SdFat was developed using an
<A HREF = "http://www.adafruit.com/"> Adafruit Industries</A>
<A HREF = "http://www.adafruit.com/"> Adafruit Industries</A>
Data Logging Shield. Data Logging Shield.


The hardware interface to the SD card should not use a resistor based level The hardware interface to the SD card should not use a resistor based level
74LCX245. 74LCX245.


If you are using a resistor based level shifter and are having problems try If you are using a resistor based level shifter and are having problems try
setting the SPI bus frequency to 4 MHz. This can be done by using
setting the SPI bus frequency to 4 MHz. This can be done by using
card.init(SPI_HALF_SPEED) to initialize the SD card. card.init(SPI_HALF_SPEED) to initialize the SD card.


A feature to use software SPI is available. Software SPI is slower A feature to use software SPI is available. Software SPI is slower
than hardware SPI but allows any digital pins to be used. See than hardware SPI but allows any digital pins to be used. See
SdFatConfig.h for software SPI definitions. SdFatConfig.h for software SPI definitions.


An many shields designed for an Uno can be use on an Arduino Mega
by defining MEGA_SOFT_SPI in SdFatConfig.h.

\section comment Bugs and Comments \section comment Bugs and Comments


If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net.
If you wish to report bugs or have comments, send email to
fat16lib@sbcglobal.net. If possible, include a simple program that illustrates
the bug or problem.

\section Trouble Troubleshooting

The two example programs QuickStart, and SdInfo are useful for troubleshooting.

A message like this from SdInfo with erorCode 0X1 indicates the SD card
is not seen by SdFat. This is often caused by a wiring error and reformatting
the card will not solve the problem.
<PRE>
cardBegin failed
SD errorCode: 0X1
SD errorData: 0XFF
</PRE>
Here is a similar message from QuickStart:
<PRE>
SD initialization failed.
Do not reformat the card!
Is the card correctly inserted?
Is chipSelect set to the correct value?
Does another SPI device need to be disabled?
Is there a wiring/soldering problem?

errorCode: 0x1, errorData: 0xff
</PRE>
Here is a message from QuickStart that indicates a formatting problem:
<PRE>
Card successfully initialized.
Can't find a valid FAT16/FAT32 partition.
Try reformatting the card. For best results use
the SdFormatter program in SdFat/examples or download
and use SDFormatter from www.sdcard.org/downloads.
</PRE>

The best source of recent information and help is the Arduino forum.

http://arduino.cc/forum/

Also search the Adafruit forum.

http://forums.adafruit.com/

If you are using a Teensy try.

http://forum.pjrc.com/forum.php


\section SdFatClass SdFat Usage \section SdFatClass SdFat Usage


SdFat supports Long File Names. Long names in SdFat are limited to 7-bit
ASCII characters in the range 0X20 - 0XFE The following are reserved characters:
<ul>
<li>< (less than)
<li>> (greater than)
<li>: (colon)
<li>" (double quote)
<li>/ (forward slash)
<li>\ (backslash)
<li>| (vertical bar or pipe)
<li>? (question mark)
<li>* (asterisk)
</ul>
%SdFat uses a slightly restricted form of short names. %SdFat uses a slightly restricted form of short names.
Short names are limited to 8 characters followed by an optional period (.) Short names are limited to 8 characters followed by an optional period (.)
and extension of up to 3 characters. The characters may be any combination and extension of up to 3 characters. The characters may be any combination
$ % ' - _ @ ~ ` ! ( ) { } ^ # & $ % ' - _ @ ~ ` ! ( ) { } ^ # &


Short names are always converted to upper case and their original case Short names are always converted to upper case and their original case
value is lost.
value is lost. Files that have a base-name where all characters have the
same case and an extension where all characters have the same case will
display properly. Examples this type name are UPPER.low, lower.TXT,
UPPER.TXT, and lower.txt.


An application which writes to a file using print(), println() or An application which writes to a file using print(), println() or
\link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink
at the appropriate time to force data and directory information to be written
to the SD Card. Data and directory information are also written to the SD card
when \link SdFile::close() close() \endlink is called.
\link SdFile::write write() \endlink must close the file or call
\link SdFile::sync() sync() \endlink at the appropriate time to
force data and directory information to be written to the SD Card.


Applications must use care calling \link SdFile::sync() sync() \endlink Applications must use care calling \link SdFile::sync() sync() \endlink
since 2048 bytes of I/O is required to update file and since 2048 bytes of I/O is required to update file and


http://www.sdcard.org/downloads http://www.sdcard.org/downloads


A formatter sketch, SdFormatter.ino, is included in the
%SdFat/examples/SdFormatter directory. This sketch attempts to
A formatter program, SdFormatter.ino, is included in the
%SdFat/examples/SdFormatter directory. This program attempts to
emulate SD Association's SDFormatter. emulate SD Association's SDFormatter.


SDFormatter aligns flash erase boundaries with file SDFormatter aligns flash erase boundaries with file
formatting of small cards. formatting of small cards.


Do not format the SD card with an OS utility, OS utilities do not format SD Do not format the SD card with an OS utility, OS utilities do not format SD
cards in conformance with the SD standard.
cards in conformance with the SD standard.


You should use a freshly formatted SD card for best performance. FAT You should use a freshly formatted SD card for best performance. FAT
file systems become slower if many files have been created and deleted. file systems become slower if many files have been created and deleted.
See the html documentation for a list. See the html documentation for a list.


To access these examples from the Arduino development environment To access these examples from the Arduino development environment
go to: %File -> Examples -> %SdFat -> \<Sketch Name\>
go to: %File -> Examples -> %SdFat -> \<program Name\>


Compile, upload to your Arduino and click on Serial Monitor to run Compile, upload to your Arduino and click on Serial Monitor to run
the example. the example.


getline - Example of getline from section 27.7.1.3 of the C++ standard. getline - Example of getline from section 27.7.1.3 of the C++ standard.


LongFileName - Example use of openNextLFN and open by index.
LongFileName - Example use of openNext, printName, and open by index.


LowLatencyLogger - A modifiable data logger for higher data rates. LowLatencyLogger - A modifiable data logger for higher data rates.




PrintBenchmark - A simple benchmark for printing to a text file. PrintBenchmark - A simple benchmark for printing to a text file.


QuickStart - A sketch to quickly test your SD card and SD shield/module.
QuickStart - A program to quickly test your SD card and SD shield/module.


RawWrite - A test of raw write functions for contiguous files. RawWrite - A test of raw write functions for contiguous files.




rename - A demo of SdFat::rename(old, new) and SdFile::rename(dirFile, newPath). rename - A demo of SdFat::rename(old, new) and SdFile::rename(dirFile, newPath).


SdFormatter - This sketch will format an SD or SDHC card.
SdFormatter - This program will format an SD or SDHC card.


SoftwareSpi - Simple demonstration of the SdFatSoftSpi template class. SoftwareSpi - Simple demonstration of the SdFatSoftSpi template class.
SdInfo - Initialize an SD card and analyze its structure for trouble shooting. SdInfo - Initialize an SD card and analyze its structure for trouble shooting.


StdioBench - Demo and test of stdio style stream. StdioBench - Demo and test of stdio style stream.
StreamParseInt - Demo of the SD.h API and the File class parseInt() function. StreamParseInt - Demo of the SD.h API and the File class parseInt() function.


ThreeCards - Demonstrate simultaneous use of SdFat, SdFatLibSpi, SdFatSoftSpi. ThreeCards - Demonstrate simultaneous use of SdFat, SdFatLibSpi, SdFatSoftSpi.
Timestamp - Sets file create, modify, and access timestamps. Timestamp - Sets file create, modify, and access timestamps.


TwoCards - Example using two SD cards. TwoCards - Example using two SD cards.
*/
*/

+ 59
- 42
SdFat/SdFile.h View File

* \param[in] path File location and name. * \param[in] path File location and name.
* \param[in] oflag File open mode. * \param[in] oflag File open mode.
*/ */
SdBaseFile(const char* path, uint8_t oflag) {open(path, oflag);}
SdBaseFile(const char* path, uint8_t oflag) {
open(path, oflag);
}
using FatFile::ls; using FatFile::ls;
using FatFile::printFatDate; using FatFile::printFatDate;
using FatFile::printFatTime; using FatFile::printFatTime;
} }
/** Print a file's name. /** Print a file's name.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
size_t printName() { size_t printName() {
return FatFile::printName(&Serial); return FatFile::printName(&Serial);
return n > INT_MAX ? INT_MAX : n; return n > INT_MAX ? INT_MAX : n;
} }
/** Ensure that any bytes written to the file are saved to the SD card. */ /** Ensure that any bytes written to the file are saved to the SD card. */
void flush() {SdBaseFile::sync();}
void flush() {
SdBaseFile::sync();
}
/** Return the next available byte without consuming it. /** Return the next available byte without consuming it.
* *
* \return The byte if no error and not at eof else -1; * \return The byte if no error and not at eof else -1;
*/ */
int peek() {return SdBaseFile::peek();}
int peek() {
return SdBaseFile::peek();
}
/** Read the next byte from a file. /** Read the next byte from a file.
* *
* \return For success return the next byte in the file as an int. * \return For success return the next byte in the file as an int.
* If an error occurs or end of file is reached return -1. * If an error occurs or end of file is reached return -1.
*/ */
int read() {return SdBaseFile::read();}
int read() {
return SdBaseFile::read();
}
/** Write a byte to a file. Required by the Arduino Print class. /** Write a byte to a file. Required by the Arduino Print class.
* \param[in] b the byte to be written. * \param[in] b the byte to be written.
* Use getWriteError to check for errors. * Use getWriteError to check for errors.
* \return 1 for success and 0 for failure. * \return 1 for success and 0 for failure.
*/ */
size_t write(uint8_t b) {return SdBaseFile::write(b);}
size_t write(uint8_t b) {
return SdBaseFile::write(b);
}
/** Write a string to a file. Used by the Arduino Print class. /** Write a string to a file. Used by the Arduino Print class.
* \param[in] str Pointer to the string. * \param[in] str Pointer to the string.
* Use getWriteError to check for errors. * Use getWriteError to check for errors.
size_t write(const uint8_t *buf, size_t size) { size_t write(const uint8_t *buf, size_t size) {
return SdBaseFile::write(buf, size); return SdBaseFile::write(buf, size);
} }
/** Write a PROGMEM string to a file.
* \param[in] str Pointer to the PROGMEM string.
* Use getWriteError to check for errors.
*/
void write_P(PGM_P str) {
for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c);
}
/** Write a PROGMEM string followed by CR/LF to a file.
* \param[in] str Pointer to the PROGMEM string.
* Use getWriteError to check for errors.
*/
void writeln_P(PGM_P str) {
write_P(str);
write_P(PSTR("\r\n"));
}
}; };
//============================================================================== //==============================================================================
/** /**
* bitwise-inclusive OR of open flags. see * bitwise-inclusive OR of open flags. see
* SdBaseFile::open(SdBaseFile*, const char*, uint8_t). * SdBaseFile::open(SdBaseFile*, const char*, uint8_t).
*/ */
File(const char* path, uint8_t oflag) {open(path, oflag);}
File(const char* path, uint8_t oflag) {
open(path, oflag);
}
using SdBaseFile::clearWriteError; using SdBaseFile::clearWriteError;
using SdBaseFile::getWriteError; using SdBaseFile::getWriteError;
/** The parenthesis operator.
*
* \return true if a file is open.
*/
operator bool() {return isOpen();}
/** The parenthesis operator.
*
* \return true if a file is open.
*/
operator bool() {
return isOpen();
}
/** \return number of bytes available from the current position to EOF /** \return number of bytes available from the current position to EOF
* or INT_MAX if more than INT_MAX bytes are available. * or INT_MAX if more than INT_MAX bytes are available.
*/ */
return n > INT_MAX ? INT_MAX : n; return n > INT_MAX ? INT_MAX : n;
} }
/** Ensure that any bytes written to the file are saved to the SD card. */ /** Ensure that any bytes written to the file are saved to the SD card. */
void flush() {SdBaseFile::sync();}
/** This function reports if the current file is a directory or not.
* \return true if the file is a directory.
*/
bool isDirectory() {return isDir();}
/** \return a pointer to the file's name. */
void flush() {
SdBaseFile::sync();
}
/** This function reports if the current file is a directory or not.
* \return true if the file is a directory.
*/
bool isDirectory() {
return isDir();
}
/** \return a pointer to the file's short name. */
char* name() { char* name() {
m_name[0] = 0; m_name[0] = 0;
getFilename(m_name);
getSFN(m_name);
return m_name; return m_name;
} }
/** Return the next available byte without consuming it. /** Return the next available byte without consuming it.
* *
* \return The byte if no error and not at eof else -1; * \return The byte if no error and not at eof else -1;
*/ */
int peek() {return SdBaseFile::peek();}
int peek() {
return SdBaseFile::peek();
}
/** \return the current file position. */ /** \return the current file position. */
uint32_t position() {return curPosition();}
uint32_t position() {
return curPosition();
}
/** Opens the next file or folder in a directory. /** Opens the next file or folder in a directory.
* *
* \param[in] mode open mode flags. * \param[in] mode open mode flags.
* \return For success return the next byte in the file as an int. * \return For success return the next byte in the file as an int.
* If an error occurs or end of file is reached return -1. * If an error occurs or end of file is reached return -1.
*/ */
int read() {return SdBaseFile::read();}
int read() {
return SdBaseFile::read();
}
/** Rewind a file if it is a directory */ /** Rewind a file if it is a directory */
void rewindDirectory() { void rewindDirectory() {
if (isDir()) rewind();
if (isDir()) {
rewind();
}
} }
/** /**
* Seek to a new position in the file, which must be between * Seek to a new position in the file, which must be between
* \param[in] pos the new file position. * \param[in] pos the new file position.
* \return true for success else false. * \return true for success else false.
*/ */
bool seek(uint32_t pos) {return seekSet(pos);}
bool seek(uint32_t pos) {
return seekSet(pos);
}
/** \return the file's size. */ /** \return the file's size. */
uint32_t size() {return fileSize();}
uint32_t size() {
return fileSize();
}
/** Write a byte to a file. Required by the Arduino Print class. /** Write a byte to a file. Required by the Arduino Print class.
* \param[in] b the byte to be written. * \param[in] b the byte to be written.
* Use getWriteError to check for errors. * Use getWriteError to check for errors.
* \return 1 for success and 0 for failure. * \return 1 for success and 0 for failure.
*/ */
size_t write(uint8_t b) {return SdBaseFile::write(b);}
size_t write(uint8_t b) {
return SdBaseFile::write(b);
}
/** Write data to an open file. Form required by Print. /** Write data to an open file. Form required by Print.
* *
* \note Data is moved to the cache but may not be written to the * \note Data is moved to the cache but may not be written to the

+ 3
- 3
SdFat/SdInfo.h View File

unsigned char always1 : 1; unsigned char always1 : 1;
/** CRC7 checksum */ /** CRC7 checksum */
unsigned char crc : 7; unsigned char crc : 7;
}__attribute__((packed)) cid_t;
} __attribute__((packed)) cid_t;
//============================================================================== //==============================================================================
/** /**
* \class CSDV1 * \class CSDV1
// byte 15 // byte 15
unsigned char always1 : 1; unsigned char always1 : 1;
unsigned char crc : 7; unsigned char crc : 7;
}__attribute__((packed)) csd1_t;
} __attribute__((packed)) csd1_t;
//============================================================================== //==============================================================================
/** /**
* \class CSDV2 * \class CSDV2
unsigned char always1 : 1; unsigned char always1 : 1;
/** checksum */ /** checksum */
unsigned char crc : 7; unsigned char crc : 7;
}__attribute__((packed)) csd2_t;
} __attribute__((packed)) csd2_t;
//============================================================================== //==============================================================================
/** /**
* \class csd_t * \class csd_t

+ 86
- 83
SdFat/SdSpi.h View File

* along with the Arduino SdSpi Library. If not, see * along with the Arduino SdSpi Library. If not, see
* <http://www.gnu.org/licenses/>. * <http://www.gnu.org/licenses/>.
*/ */
/**
* \file
* \brief SdSpi class for V2 SD/SDHC cards
*/
/**
* \file
* \brief SdSpi class for V2 SD/SDHC cards
*/
#ifndef SdSpi_h #ifndef SdSpi_h
#define SdSpi_h #define SdSpi_h
#include <Arduino.h> #include <Arduino.h>
#include "SdFatConfig.h" #include "SdFatConfig.h"
#include "utility/SoftSPI.h" #include "utility/SoftSPI.h"
#if !USE_ARDUINO_SPI_LIBRARY
// AVR Arduinos
#ifdef __AVR__
#if AVR_SOFT_SPI
#define USE_SOFTWARE_SPI 1
#elif LEONARDO_SOFT_SPI && defined(__AVR_ATmega32U4__) && !defined(CORE_TEENSY)
#define USE_SOFTWARE_SPI 1
#elif MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)\
|| defined(__AVR_ATmega2560__))
#define USE_SOFTWARE_SPI 1
#endif // USE_SOFTWARE_SPI
#endif // __AVR__
// Due
#if DUE_SOFT_SPI && defined(__arm__) && !defined(CORE_TEENSY)
#define USE_SOFTWARE_SPI 1
#endif // DUE_SOFT_SPI && defined(__arm__) && !defined(CORE_TEENSY)
// Teensy 3.x
#if TEENSY3_SOFT_SPI && defined(__arm__) && defined(CORE_TEENSY)
#define USE_SOFTWARE_SPI 1
#endif // TEENSY3_SOFT_SPI && defined(__arm__) && defined(CORE_TEENSY)
#endif // !USE_ARDUINO_SPI_LIBRARY
#ifndef USE_SOFTWARE_SPI
/** Default is no software SPI */
#define USE_SOFTWARE_SPI 0
#endif // USE_SOFTWARE_SPI
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
* \class SdSpiBase * \class SdSpiBase
* \param[in] data Byte to send * \param[in] data Byte to send
*/ */
virtual void send(uint8_t data) = 0; virtual void send(uint8_t data) = 0;
/** Send multiple bytes.
*
* \param[in] buf Buffer for data to be sent.
* \param[in] n Number of bytes to send.
*/
/** Send multiple bytes.
*
* \param[in] buf Buffer for data to be sent.
* \param[in] n Number of bytes to send.
*/
virtual void send(const uint8_t* buf, size_t n) = 0; virtual void send(const uint8_t* buf, size_t n) = 0;
/** \return true if hardware SPI else false */ /** \return true if hardware SPI else false */
virtual bool useSpiTransactions() = 0; virtual bool useSpiTransactions() = 0;
* \class SdSpi * \class SdSpi
* \brief SPI class for access to SD and SDHC flash memory cards. * \brief SPI class for access to SD and SDHC flash memory cards.
*/ */
#if USE_MULTIPLE_SPI_TYPES
#if SD_SPI_CONFIGURATION >= 3
class SdSpi : public SdSpiBase { class SdSpi : public SdSpiBase {
#else
#else // SD_SPI_CONFIGURATION >= 3
class SdSpi { class SdSpi {
#endif
#endif // SD_SPI_CONFIGURATION >= 3
public: public:
/** Initialize the SPI bus */ /** Initialize the SPI bus */
void begin(); void begin();
*/ */
void send(const uint8_t* buf, size_t n); void send(const uint8_t* buf, size_t n);
/** \return true - uses SPI transactions */ /** \return true - uses SPI transactions */
bool useSpiTransactions() {return true;}
bool useSpiTransactions() {
return true;
}
}; };

//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if USE_MULTIPLE_SPI_TYPES || USE_ARDUINO_SPI_LIBRARY || defined(DOXYGEN)
#include <SPI.h>
/** /**
* \class SdSpiLib * \class SdSpiLib
* \brief Arduino SPI library class for access to SD and SDHC flash * \brief Arduino SPI library class for access to SD and SDHC flash
* memory cards. * memory cards.
*/ */
#if USE_MULTIPLE_SPI_TYPES
#if SD_SPI_CONFIGURATION >= 3 || SD_SPI_CONFIGURATION == 1 || defined(DOXYGEN)
#include <SPI.h>
#if SD_SPI_CONFIGURATION >= 3
class SdSpiLib : public SdSpiBase { class SdSpiLib : public SdSpiBase {
#else
#else // SD_SPI_CONFIGURATION >= 3
class SdSpiLib { class SdSpiLib {
#endif
#endif // SD_SPI_CONFIGURATION >= 3
public: public:
/** /**
* Initialize SPI pins. * Initialize SPI pins.
*/ */
void begin() {SPI.begin();}
void begin() {
SPI.begin();
}
/** Set SPI options for access to SD/SDHC cards. /** Set SPI options for access to SD/SDHC cards.
* *
* \param[in] divisor SCK clock divider relative to the system clock. * \param[in] divisor SCK clock divider relative to the system clock.
void init(uint8_t divisor) { void init(uint8_t divisor) {
SPI.setBitOrder(MSBFIRST); SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0); SPI.setDataMode(SPI_MODE0);
#ifndef SPI_CLOCK_DIV128
#ifndef SPI_CLOCK_DIV128
SPI.setClockDivider(divisor); SPI.setClockDivider(divisor);
#else // SPI_CLOCK_DIV128
#else // SPI_CLOCK_DIV128
int v; int v;
if (divisor <= 2) v = SPI_CLOCK_DIV2;
else if (divisor <= 4) v = SPI_CLOCK_DIV4;
else if (divisor <= 8) v = SPI_CLOCK_DIV8;
else if (divisor <= 16) v = SPI_CLOCK_DIV16;
else if (divisor <= 32) v = SPI_CLOCK_DIV32;
else if (divisor <= 64) v = SPI_CLOCK_DIV64;
else v = SPI_CLOCK_DIV128;
if (divisor <= 2) {
v = SPI_CLOCK_DIV2;
} else if (divisor <= 4) {
v = SPI_CLOCK_DIV4;
} else if (divisor <= 8) {
v = SPI_CLOCK_DIV8;
} else if (divisor <= 16) {
v = SPI_CLOCK_DIV16;
} else if (divisor <= 32) {
v = SPI_CLOCK_DIV32;
} else if (divisor <= 64) {
v = SPI_CLOCK_DIV64;
} else {
v = SPI_CLOCK_DIV128;
}
SPI.setClockDivider(v); SPI.setClockDivider(v);
#endif // SPI_CLOCK_DIV128
#endif // SPI_CLOCK_DIV128
} }
/** Receive a byte. /** Receive a byte.
* *
* \return The byte. * \return The byte.
*/ */
uint8_t receive() {return SPI.transfer(0XFF);}
uint8_t receive() {
return SPI.transfer(0XFF);
}
/** Receive multiple bytes. /** Receive multiple bytes.
* *
* \param[out] buf Buffer to receive the data. * \param[out] buf Buffer to receive the data.
} }
} }
/** \return true - uses SPI transactions */ /** \return true - uses SPI transactions */
bool useSpiTransactions() {return true;}
bool useSpiTransactions() {
return true;
}
}; };
#endif // USE_MULTIPLE_SPI_TYPES || USE_ARDUINO_SPI_LIBRARY
#endif // SD_SPI_CONFIGURATION >= 3 || SD_SPI_CONFIGURATION == 1
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
* \class SdSpiSoft * \class SdSpiSoft
* \brief Software SPI class for access to SD and SDHC flash memory cards. * \brief Software SPI class for access to SD and SDHC flash memory cards.
*/ */
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin>
#if USE_MULTIPLE_SPI_TYPES
class SdSpiSoft : public SdSpiBase { class SdSpiSoft : public SdSpiBase {
#else
class SdSpiSoft {
#endif
public: public:
/** /**
* initialize SPI pins * initialize SPI pins
*/ */
void begin() {m_spi.begin();}
void begin() {
m_spi.begin();
}
/** /**
* Initialize hardware SPI - dummy for soft SPI * Initialize hardware SPI - dummy for soft SPI
* \param[in] divisor SCK divisor - ignored. * \param[in] divisor SCK divisor - ignored.
* *
* \return The byte. * \return The byte.
*/ */
uint8_t receive() {return m_spi.receive();}
/** Receive multiple bytes.
*
* \param[out] buf Buffer to receive the data.
* \param[in] n Number of bytes to receive.
*
* \return Zero for no error or nonzero error code.
*/
uint8_t receive() {
return m_spi.receive();
}
/** Receive multiple bytes.
*
* \param[out] buf Buffer to receive the data.
* \param[in] n Number of bytes to receive.
*
* \return Zero for no error or nonzero error code.
*/
uint8_t receive(uint8_t* buf, size_t n) { uint8_t receive(uint8_t* buf, size_t n) {
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
buf[i] = receive(); buf[i] = receive();
* *
* \param[in] data Byte to send * \param[in] data Byte to send
*/ */
void send(uint8_t data) {m_spi.send(data);}
void send(uint8_t data) {
m_spi.send(data);
}
/** Send multiple bytes. /** Send multiple bytes.
* *
* \param[in] buf Buffer for data to be sent. * \param[in] buf Buffer for data to be sent.
} }
} }
/** \return false - no SPI transactions */ /** \return false - no SPI transactions */
bool useSpiTransactions() {return false;}
bool useSpiTransactions() {
return false;
}

private: private:
SoftSPI<MisoPin, MosiPin, SckPin, 0> m_spi; SoftSPI<MisoPin, MosiPin, SckPin, 0> m_spi;
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if USE_ARDUINO_SPI_LIBRARY
#if SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3
/** Default is custom fast SPI. */
typedef SdSpi SpiDefault_t;
#elif SD_SPI_CONFIGURATION == 1
/** Default is Arduino library SPI. */ /** Default is Arduino library SPI. */
typedef SdSpiLib SpiDefault_t; typedef SdSpiLib SpiDefault_t;
#elif USE_SOFTWARE_SPI
#elif SD_SPI_CONFIGURATION == 2
/** Default is software SPI. */ /** Default is software SPI. */
typedef SdSpiSoft<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN> typedef SdSpiSoft<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN>
SpiDefault_t;
#else // USE_ARDUINO_SPI_LIBRARY
/** Default is custom fast SPI. */
typedef SdSpi SpiDefault_t;
#endif
SpiDefault_t;
#else // SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3
#error bad SD_SPI_CONFIGURATION
#endif // SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Use of in-line for AVR to save flash. // Use of in-line for AVR to save flash.
#ifdef __AVR__ #ifdef __AVR__
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) { inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
if (n-- == 0) return 0;
if (n-- == 0) {
return 0;
}
SPDR = 0XFF; SPDR = 0XFF;
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
while (!(SPSR & (1 << SPIF))) {} while (!(SPSR & (1 << SPIF))) {}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
inline void SdSpi::send(const uint8_t* buf , size_t n) { inline void SdSpi::send(const uint8_t* buf , size_t n) {
if (n == 0) return;
if (n == 0) {
return;
}
SPDR = buf[0]; SPDR = buf[0];
if (n > 1) { if (n > 1) {
uint8_t b = buf[1]; uint8_t b = buf[1];
while (1) { while (1) {
while (!(SPSR & (1 << SPIF))) {} while (!(SPSR & (1 << SPIF))) {}
SPDR = b; SPDR = b;
if (i == n) break;
if (i == n) {
break;
}
b = buf[i++]; b = buf[i++];
} }
} }

+ 104
- 48
SdFat/SdSpiCard.cpp View File

*/ */
#include "SdSpiCard.h" #include "SdSpiCard.h"
#include "SdSpi.h" #include "SdSpi.h"
#if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION)
#if ENABLE_SPI_TRANSACTION
#include <SPI.h> #include <SPI.h>
#endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION)
#endif // ENABLE_SPI_TRANSACTION
// debug trace macro // debug trace macro
#define SD_TRACE(m, b) #define SD_TRACE(m, b)
// #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); // #define SD_TRACE(m, b) Serial.print(m);Serial.println(b);
uint8_t d = data[i]; uint8_t d = data[i];
for (uint8_t j = 0; j < 8; j++) { for (uint8_t j = 0; j < 8; j++) {
crc <<= 1; crc <<= 1;
if ((d & 0x80) ^ (crc & 0x80)) crc ^= 0x09;
if ((d & 0x80) ^ (crc & 0x80)) {
crc ^= 0x09;
}
d <<= 1; d <<= 1;
} }
} }
spiInit(m_sckDivisor); spiInit(m_sckDivisor);


// must supply min of 74 clock cycles with CS high. // must supply min of 74 clock cycles with CS high.
for (uint8_t i = 0; i < 10; i++) spiSend(0XFF);
for (uint8_t i = 0; i < 10; i++) {
spiSend(0XFF);
}


// command to go idle in SPI mode // command to go idle in SPI mode
while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { while (cardCommand(CMD0, 0) != R1_IDLE_STATE) {
type(SD_CARD_TYPE_SD1); type(SD_CARD_TYPE_SD1);
break; break;
} }
for (uint8_t i = 0; i < 4; i++) m_status = spiReceive();
for (uint8_t i = 0; i < 4; i++) {
m_status = spiReceive();
}
if (m_status == 0XAA) { if (m_status == 0XAA) {
type(SD_CARD_TYPE_SD2); type(SD_CARD_TYPE_SD2);
break; break;
error(SD_CARD_ERROR_CMD58); error(SD_CARD_ERROR_CMD58);
goto fail; goto fail;
} }
if ((spiReceive() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC);
if ((spiReceive() & 0XC0) == 0XC0) {
type(SD_CARD_TYPE_SDHC);
}
// Discard rest of ocr - contains allowed voltage range. // Discard rest of ocr - contains allowed voltage range.
for (uint8_t i = 0; i < 3; i++) spiReceive();
for (uint8_t i = 0; i < 3; i++) {
spiReceive();
}
} }
chipSelectHigh(); chipSelectHigh();
m_sckDivisor = sckDivisor; m_sckDivisor = sckDivisor;
return true; return true;


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
d[5] = CRC7(d, 5); d[5] = CRC7(d, 5);


// send message // send message
for (uint8_t k = 0; k < 6; k++) spiSend(d[k]);
for (uint8_t k = 0; k < 6; k++) {
spiSend(d[k]);
}
#else // USE_SD_CRC #else // USE_SD_CRC
// send command // send command
spiSend(cmd | 0x40); spiSend(cmd | 0x40);


// send argument // send argument
for (int8_t i = 3; i >= 0; i--) spiSend(pa[i]);
for (int8_t i = 3; i >= 0; i--) {
spiSend(pa[i]);
}


// send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA // send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA
spiSend(cmd == CMD0 ? 0X95 : 0X87); spiSend(cmd == CMD0 ? 0X95 : 0X87);
#endif // USE_SD_CRC #endif // USE_SD_CRC


// skip stuff byte for stop read // skip stuff byte for stop read
if (cmd == CMD12) spiReceive();
if (cmd == CMD12) {
spiReceive();
}


// wait for response // wait for response
for (uint8_t i = 0; ((m_status = spiReceive()) & 0X80) && i != 0XFF; i++) { for (uint8_t i = 0; ((m_status = spiReceive()) & 0X80) && i != 0XFF; i++) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
uint32_t SdSpiCard::cardSize() { uint32_t SdSpiCard::cardSize() {
csd_t csd; csd_t csd;
if (!readCSD(&csd)) return 0;
if (!readCSD(&csd)) {
return 0;
}
if (csd.v1.csd_ver == 0) { if (csd.v1.csd_ver == 0) {
uint8_t read_bl_len = csd.v1.read_bl_len; uint8_t read_bl_len = csd.v1.read_bl_len;
uint16_t c_size = (csd.v1.c_size_high << 10) uint16_t c_size = (csd.v1.c_size_high << 10)
// insure MISO goes high impedance // insure MISO goes high impedance
spiSend(0XFF); spiSend(0XFF);
#if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) #if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION)
if (useSpiTransactions()) SPI.endTransaction();
if (useSpiTransactions()) {
SPI.endTransaction();
}
#endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) #endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION)
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void SdSpiCard::chipSelectLow() { void SdSpiCard::chipSelectLow() {
#if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) #if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION)
if (useSpiTransactions()) SPI.beginTransaction(SPISettings());
if (useSpiTransactions()) {
SPI.beginTransaction(SPISettings());
}
#endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) #endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION)
spiInit(m_sckDivisor); spiInit(m_sckDivisor);
digitalWrite(m_chipSelectPin, LOW); digitalWrite(m_chipSelectPin, LOW);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) {
csd_t csd; csd_t csd;
if (!readCSD(&csd)) goto fail;
if (!readCSD(&csd)) {
goto fail;
}
// check for single block erase // check for single block erase
if (!csd.v1.erase_blk_en) { if (!csd.v1.erase_blk_en) {
// erase size mask // erase size mask
lastBlock <<= 9; lastBlock <<= 9;
} }
if (cardCommand(CMD32, firstBlock) if (cardCommand(CMD32, firstBlock)
|| cardCommand(CMD33, lastBlock)
|| cardCommand(CMD38, 0)) {
error(SD_CARD_ERROR_ERASE);
goto fail;
|| cardCommand(CMD33, lastBlock)
|| cardCommand(CMD38, 0)) {
error(SD_CARD_ERROR_ERASE);
goto fail;
} }
if (!waitNotBusy(SD_ERASE_TIMEOUT)) { if (!waitNotBusy(SD_ERASE_TIMEOUT)) {
error(SD_CARD_ERROR_ERASE_TIMEOUT); error(SD_CARD_ERROR_ERASE_TIMEOUT);
chipSelectHigh(); chipSelectHigh();
return true; return true;


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
chipSelectLow(); chipSelectLow();
for (uint8_t i = 0; i < 8; i++) { for (uint8_t i = 0; i < 8; i++) {
rtn = spiReceive() != 0XFF; rtn = spiReceive() != 0XFF;
if (!rtn) break;
if (!rtn) {
break;
}
} }
chipSelectHigh(); chipSelectHigh();
return rtn; return rtn;
bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) {
SD_TRACE("RB", blockNumber); SD_TRACE("RB", blockNumber);
// use address if not SDHC card // use address if not SDHC card
if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9;
if (type()!= SD_CARD_TYPE_SDHC) {
blockNumber <<= 9;
}
if (cardCommand(CMD17, blockNumber)) { if (cardCommand(CMD17, blockNumber)) {
error(SD_CARD_ERROR_CMD17); error(SD_CARD_ERROR_CMD17);
goto fail; goto fail;
} }
return readData(dst, 512); return readData(dst, 512);


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool SdSpiCard::readBlocks(uint32_t block, uint8_t* dst, size_t count) { bool SdSpiCard::readBlocks(uint32_t block, uint8_t* dst, size_t count) {
if (!readStart(block)) return false;
if (!readStart(block)) {
return false;
}
for (uint16_t b = 0; b < count; b++, dst += 512) { for (uint16_t b = 0; b < count; b++, dst += 512) {
if (!readData(dst)) return false;
if (!readData(dst)) {
return false;
}
} }
return readStop(); return readStop();
} }
chipSelectHigh(); chipSelectHigh();
return true; return true;


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
error(SD_CARD_ERROR_CMD58); error(SD_CARD_ERROR_CMD58);
goto fail; goto fail;
} }
for (uint8_t i = 0; i < 4; i++) p[3-i] = spiReceive();
for (uint8_t i = 0; i < 4; i++) {
p[3-i] = spiReceive();
}


chipSelectHigh(); chipSelectHigh();
return true; return true;


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
} }
return readData(dst, 16); return readData(dst, 16);


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool SdSpiCard::readStart(uint32_t blockNumber) { bool SdSpiCard::readStart(uint32_t blockNumber) {
SD_TRACE("RS", blockNumber); SD_TRACE("RS", blockNumber);
if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9;
if (type()!= SD_CARD_TYPE_SDHC) {
blockNumber <<= 9;
}
if (cardCommand(CMD18, blockNumber)) { if (cardCommand(CMD18, blockNumber)) {
error(SD_CARD_ERROR_CMD18); error(SD_CARD_ERROR_CMD18);
goto fail; goto fail;
chipSelectHigh(); chipSelectHigh();
return true; return true;


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
chipSelectHigh(); chipSelectHigh();
return true; return true;


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
bool SdSpiCard::waitNotBusy(uint16_t timeoutMillis) { bool SdSpiCard::waitNotBusy(uint16_t timeoutMillis) {
uint16_t t0 = millis(); uint16_t t0 = millis();
while (spiReceive() != 0XFF) { while (spiReceive() != 0XFF) {
if (((uint16_t)millis() - t0) >= timeoutMillis) goto fail;
if (((uint16_t)millis() - t0) >= timeoutMillis) {
goto fail;
}
spiYield(); spiYield();
} }
return true; return true;


fail:
fail:
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) {
SD_TRACE("WB", blockNumber); SD_TRACE("WB", blockNumber);
// use address if not SDHC card // use address if not SDHC card
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
if (type() != SD_CARD_TYPE_SDHC) {
blockNumber <<= 9;
}
if (cardCommand(CMD24, blockNumber)) { if (cardCommand(CMD24, blockNumber)) {
error(SD_CARD_ERROR_CMD24); error(SD_CARD_ERROR_CMD24);
goto fail; goto fail;
} }
if (!writeData(DATA_START_BLOCK, src)) goto fail;
if (!writeData(DATA_START_BLOCK, src)) {
goto fail;
}


#define CHECK_PROGRAMMING 0 #define CHECK_PROGRAMMING 0
#if CHECK_PROGRAMMING #if CHECK_PROGRAMMING
chipSelectHigh(); chipSelectHigh();
return true; return true;


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) {
if (!writeStart(block, count)) return false;
if (!writeStart(block, count)) {
return false;
}
for (size_t b = 0; b < count; b++, src += 512) { for (size_t b = 0; b < count; b++, src += 512) {
if (!writeData(src)) return false;
if (!writeData(src)) {
return false;
}
} }
return writeStop(); return writeStop();
} }
bool SdSpiCard::writeData(const uint8_t* src) { bool SdSpiCard::writeData(const uint8_t* src) {
chipSelectLow(); chipSelectLow();
// wait for previous write to finish // wait for previous write to finish
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
if (!writeData(WRITE_MULTIPLE_TOKEN, src)) goto fail;
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
goto fail;
}
if (!writeData(WRITE_MULTIPLE_TOKEN, src)) {
goto fail;
}
chipSelectHigh(); chipSelectHigh();
return true; return true;


fail:
fail:
error(SD_CARD_ERROR_WRITE_MULTIPLE); error(SD_CARD_ERROR_WRITE_MULTIPLE);
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
return true; return true;


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
goto fail; goto fail;
} }
// use address if not SDHC card // use address if not SDHC card
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
if (type() != SD_CARD_TYPE_SDHC) {
blockNumber <<= 9;
}
if (cardCommand(CMD25, blockNumber)) { if (cardCommand(CMD25, blockNumber)) {
error(SD_CARD_ERROR_CMD25); error(SD_CARD_ERROR_CMD25);
goto fail; goto fail;
chipSelectHigh(); chipSelectHigh();
return true; return true;


fail:
fail:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool SdSpiCard::writeStop() { bool SdSpiCard::writeStop() {
chipSelectLow(); chipSelectLow();
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
goto fail;
}
spiSend(STOP_TRAN_TOKEN); spiSend(STOP_TRAN_TOKEN);
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
goto fail;
}
chipSelectHigh(); chipSelectHigh();
return true; return true;


fail:
fail:
error(SD_CARD_ERROR_STOP_TRAN); error(SD_CARD_ERROR_STOP_TRAN);
chipSelectHigh(); chipSelectHigh();
return false; return false;

+ 72
- 45
SdFat/SdSpiCard.h View File

class SdSpiCard { class SdSpiCard {
public: public:
/** typedef for SPI class. */ /** typedef for SPI class. */
#if USE_MULTIPLE_SPI_TYPES
typedef SdSpiBase m_spi_t;
#else // USE_MULTIPLE_SPI_TYPES
#if SD_SPI_CONFIGURATION < 3
typedef SpiDefault_t m_spi_t; typedef SpiDefault_t m_spi_t;
#endif // USE_MULTIPLE_SPI_TYPES
#else // SD_SPI_CONFIGURATION < 3
typedef SdSpiBase m_spi_t;
#endif // SD_SPI_CONFIGURATION < 3
/** Construct an instance of SdSpiCard. */ /** Construct an instance of SdSpiCard. */
SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {}
/** Initialize the SD card. /** Initialize the SD card.
* \return true for success else false. * \return true for success else false.
*/ */
bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS, bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS,
uint8_t sckDivisor = SPI_FULL_SPEED);
uint8_t sckDivisor = SPI_FULL_SPEED);
/** /**
* Determine the size of an SD flash memory card. * Determine the size of an SD flash memory card.
* *
* either 0 or 1, depends on the card vendor. The card must support * either 0 or 1, depends on the card vendor. The card must support
* single block erase. * single block erase.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool erase(uint32_t firstBlock, uint32_t lastBlock); bool erase(uint32_t firstBlock, uint32_t lastBlock);
/** Determine if card supports single block erase. /** Determine if card supports single block erase.
* Set SD error code. * Set SD error code.
* \param[in] code value for error code. * \param[in] code value for error code.
*/ */
void error(uint8_t code) {m_errorCode = code;}
void error(uint8_t code) {
m_errorCode = code;
}
/** /**
* \return code for the last error. See SdSpiCard.h for a list of error codes. * \return code for the last error. See SdSpiCard.h for a list of error codes.
*/ */
int errorCode() const {return m_errorCode;}
int errorCode() const {
return m_errorCode;
}
/** \return error data for last error. */ /** \return error data for last error. */
int errorData() const {return m_status;}
int errorData() const {
return m_status;
}
/** /**
* Check for busy. MISO low indicates the card is busy. * Check for busy. MISO low indicates the card is busy.
* *
* *
* \param[in] block Logical block to be read. * \param[in] block Logical block to be read.
* \param[out] dst Pointer to the location that will receive the data. * \param[out] dst Pointer to the location that will receive the data.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool readBlock(uint32_t block, uint8_t* dst); bool readBlock(uint32_t block, uint8_t* dst);
/** /**
* \param[in] block Logical block to be read. * \param[in] block Logical block to be read.
* \param[in] count Number of blocks to be read. * \param[in] count Number of blocks to be read.
* \param[out] dst Pointer to the location that will receive the data. * \param[out] dst Pointer to the location that will receive the data.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool readBlocks(uint32_t block, uint8_t* dst, size_t count); bool readBlocks(uint32_t block, uint8_t* dst, size_t count);
/** /**
* *
* \param[out] dst Pointer to the location for the data to be read. * \param[out] dst Pointer to the location for the data to be read.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool readData(uint8_t *dst); bool readData(uint8_t *dst);
/** Read OCR register. /** Read OCR register.
* \note This function is used with readData() and readStop() for optimized * \note This function is used with readData() and readStop() for optimized
* multiple block reads. SPI chipSelect must be low for the entire sequence. * multiple block reads. SPI chipSelect must be low for the entire sequence.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool readStart(uint32_t blockNumber); bool readStart(uint32_t blockNumber);
/** End a read multiple blocks sequence. /** End a read multiple blocks sequence.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool readStop(); bool readStop();
/** Return SCK divisor. /** Return SCK divisor.
* *
* \return Requested SCK divisor. * \return Requested SCK divisor.
*/ */
uint8_t sckDivisor() {return m_sckDivisor;}
uint8_t sckDivisor() {
return m_sckDivisor;
}
/** Return the card type: SD V1, SD V2 or SDHC /** Return the card type: SD V1, SD V2 or SDHC
* \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC.
*/ */
int type() const {return m_type;}
int type() const {
return m_type;
}
/** /**
* Writes a 512 byte block to an SD card. * Writes a 512 byte block to an SD card.
* *
* \param[in] blockNumber Logical block to be written. * \param[in] blockNumber Logical block to be written.
* \param[in] src Pointer to the location of the data to be written. * \param[in] src Pointer to the location of the data to be written.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool writeBlock(uint32_t blockNumber, const uint8_t* src); bool writeBlock(uint32_t blockNumber, const uint8_t* src);
/** /**
* \param[in] block Logical block to be written. * \param[in] block Logical block to be written.
* \param[in] count Number of blocks to be written. * \param[in] count Number of blocks to be written.
* \param[in] src Pointer to the location of the data to be written. * \param[in] src Pointer to the location of the data to be written.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool writeBlocks(uint32_t block, const uint8_t* src, size_t count); bool writeBlocks(uint32_t block, const uint8_t* src, size_t count);
/** Write one data block in a multiple block write sequence /** Write one data block in a multiple block write sequence
* \param[in] src Pointer to the location of the data to be written. * \param[in] src Pointer to the location of the data to be written.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool writeData(const uint8_t* src); bool writeData(const uint8_t* src);
/** Start a write multiple blocks sequence. /** Start a write multiple blocks sequence.
* \note This function is used with writeData() and writeStop() * \note This function is used with writeData() and writeStop()
* for optimized multiple block writes. * for optimized multiple block writes.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool writeStart(uint32_t blockNumber, uint32_t eraseCount); bool writeStart(uint32_t blockNumber, uint32_t eraseCount);
/** End a write multiple blocks sequence. /** End a write multiple blocks sequence.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool writeStop(); bool writeStop();


void chipSelectHigh(); void chipSelectHigh();
void chipSelectLow(); void chipSelectLow();
void spiYield(); void spiYield();
void type(uint8_t value) {m_type = value;}
void type(uint8_t value) {
m_type = value;
}
bool waitNotBusy(uint16_t timeoutMillis); bool waitNotBusy(uint16_t timeoutMillis);
bool writeData(uint8_t token, const uint8_t* src); bool writeData(uint8_t token, const uint8_t* src);
void spiBegin() {m_spi->begin();}
void spiInit(uint8_t spiDivisor) {m_spi->init(spiDivisor);}
uint8_t spiReceive() {return m_spi->receive();}
uint8_t spiReceive(uint8_t* buf, size_t n) {return m_spi->receive(buf, n);}
void spiSend(uint8_t data) {m_spi->send(data);}
void spiSend(const uint8_t* buf, size_t n) {m_spi->send(buf, n);}
bool useSpiTransactions() {return m_spi->useSpiTransactions();}

void spiBegin() {
m_spi->begin();
}
void spiInit(uint8_t spiDivisor) {
m_spi->init(spiDivisor);
}
uint8_t spiReceive() {
return m_spi->receive();
}
uint8_t spiReceive(uint8_t* buf, size_t n) {
return m_spi->receive(buf, n);
}
void spiSend(uint8_t data) {
m_spi->send(data);
}
void spiSend(const uint8_t* buf, size_t n) {
m_spi->send(buf, n);
}
bool useSpiTransactions() {
return m_spi->useSpiTransactions();
}
m_spi_t* m_spi; m_spi_t* m_spi;
uint8_t m_chipSelectPin; uint8_t m_chipSelectPin;
uint8_t m_errorCode; uint8_t m_errorCode;
* \param[in] chipSelectPin SD chip select pin. * \param[in] chipSelectPin SD chip select pin.
* \param[in] sckDivisor SPI clock divisor. * \param[in] sckDivisor SPI clock divisor.
* \return true for success else false. * \return true for success else false.
*/
*/
bool begin(uint8_t chipSelectPin = SS, uint8_t sckDivisor = 2) { bool begin(uint8_t chipSelectPin = SS, uint8_t sckDivisor = 2) {
return SdSpiCard::begin(&m_spi, chipSelectPin, sckDivisor); return SdSpiCard::begin(&m_spi, chipSelectPin, sckDivisor);
} }
* \param[in] chipSelectPin SD chip select pin. * \param[in] chipSelectPin SD chip select pin.
* \param[in] sckDivisor SPI clock divisor. * \param[in] sckDivisor SPI clock divisor.
* \return true for success else false. * \return true for success else false.
*/
*/
bool init(uint8_t sckDivisor = 2, uint8_t chipSelectPin = SS) { bool init(uint8_t sckDivisor = 2, uint8_t chipSelectPin = SS) {
return begin(chipSelectPin, sckDivisor); return begin(chipSelectPin, sckDivisor);
} }
private: private:
bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS,
uint8_t sckDivisor = SPI_FULL_SPEED) {return false;}
bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS,
uint8_t sckDivisor = SPI_FULL_SPEED) {
return false;
}
SpiDefault_t m_spi; SpiDefault_t m_spi;
}; };
#endif // SpiCard_h #endif // SpiCard_h

+ 22
- 20
SdFat/SdSpiSAM3X.cpp View File

//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void SdSpi::begin() { void SdSpi::begin() {
PIO_Configure( PIO_Configure(
g_APinDescription[PIN_SPI_MOSI].pPort,
g_APinDescription[PIN_SPI_MOSI].ulPinType,
g_APinDescription[PIN_SPI_MOSI].ulPin,
g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration);
g_APinDescription[PIN_SPI_MOSI].pPort,
g_APinDescription[PIN_SPI_MOSI].ulPinType,
g_APinDescription[PIN_SPI_MOSI].ulPin,
g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration);
PIO_Configure( PIO_Configure(
g_APinDescription[PIN_SPI_MISO].pPort,
g_APinDescription[PIN_SPI_MISO].ulPinType,
g_APinDescription[PIN_SPI_MISO].ulPin,
g_APinDescription[PIN_SPI_MISO].ulPinConfiguration);
g_APinDescription[PIN_SPI_MISO].pPort,
g_APinDescription[PIN_SPI_MISO].ulPinType,
g_APinDescription[PIN_SPI_MISO].ulPin,
g_APinDescription[PIN_SPI_MISO].ulPinConfiguration);
PIO_Configure( PIO_Configure(
g_APinDescription[PIN_SPI_SCK].pPort,
g_APinDescription[PIN_SPI_SCK].ulPinType,
g_APinDescription[PIN_SPI_SCK].ulPin,
g_APinDescription[PIN_SPI_SCK].ulPinConfiguration);
g_APinDescription[PIN_SPI_SCK].pPort,
g_APinDescription[PIN_SPI_SCK].ulPinType,
g_APinDescription[PIN_SPI_SCK].ulPin,
g_APinDescription[PIN_SPI_SCK].ulPinConfiguration);
pmc_enable_periph_clk(ID_SPI0); pmc_enable_periph_clk(ID_SPI0);
#if USE_SAM3X_DMAC #if USE_SAM3X_DMAC
pmc_enable_periph_clk(ID_DMAC); pmc_enable_periph_clk(ID_DMAC);
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DADDR = (uint32_t)dst; DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DADDR = (uint32_t)dst;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DSCR = 0; DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DSCR = 0;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLA = count | DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLA = count |
DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR | DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR |
DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_PER2MEM_DMA_FC |
DMAC_CTRLB_SRC_INCR_FIXED | DMAC_CTRLB_DST_INCR_INCREMENTING;
DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_PER2MEM_DMA_FC |
DMAC_CTRLB_SRC_INCR_FIXED | DMAC_CTRLB_DST_INCR_INCREMENTING;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CFG = DMAC_CFG_SRC_PER(SPI_RX_IDX) | DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CFG = DMAC_CFG_SRC_PER(SPI_RX_IDX) |
DMAC_CFG_SRC_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG;
DMAC_CFG_SRC_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG;
dmac_channel_enable(SPI_DMAC_RX_CH); dmac_channel_enable(SPI_DMAC_RX_CH);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DADDR = (uint32_t)&SPI0->SPI_TDR; DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DADDR = (uint32_t)&SPI0->SPI_TDR;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DSCR = 0; DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DSCR = 0;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLA = count | DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLA = count |
DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;


DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR | DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR |
DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_MEM2PER_DMA_FC |
src_incr | DMAC_CTRLB_DST_INCR_FIXED;
DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_MEM2PER_DMA_FC |
src_incr | DMAC_CTRLB_DST_INCR_FIXED;


DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CFG = DMAC_CFG_DST_PER(SPI_TX_IDX) | DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CFG = DMAC_CFG_DST_PER(SPI_TX_IDX) |
DMAC_CFG_DST_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ALAP_CFG; DMAC_CFG_DST_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ALAP_CFG;
break; break;
} }
} }
if (pSpi->SPI_SR & SPI_SR_OVRES) rtn |= 1;
if (pSpi->SPI_SR & SPI_SR_OVRES) {
rtn |= 1;
}
#else // USE_SAM3X_DMAC #else // USE_SAM3X_DMAC
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
pSpi->SPI_TDR = 0XFF; pSpi->SPI_TDR = 0XFF;

+ 17
- 15
SdFat/SdVolume.h View File

//============================================================================== //==============================================================================
/** /**
* \class SdVolume * \class SdVolume
* \brief SdVolume used in Quick start. Soon to be removed.
* \brief SdVolume Soon to be removed.
*/ */
class SdVolume : public FatVolume { class SdVolume : public FatVolume {
public: public:
/** Initialize a FAT volume. Try partition one first then try super
* floppy format.
*
* \param[in] dev The Sd2Card where the volume is located.
*
* \return true for success else false.
*/
bool init(Sd2Card* dev) {return init(dev, 1) ? true : init(dev, 0);}
/** Initialize a FAT volume.
*
* \param[in] dev The Sd2Card where the volume is located.
* \param[in] part the partition to use. Zero for super floppy or 1-4.
* \return true for success else false.
*/
/** Initialize a FAT volume. Try partition one first then try super
* floppy format.
*
* \param[in] dev The Sd2Card where the volume is located.
*
* \return true for success else false.
*/
bool init(Sd2Card* dev) {
return init(dev, 1) ? true : init(dev, 0);
}
/** Initialize a FAT volume.
*
* \param[in] dev The Sd2Card where the volume is located.
* \param[in] part the partition to use. Zero for super floppy or 1-4.
* \return true for success else false.
*/
bool init(Sd2Card* dev, uint8_t part) { bool init(Sd2Card* dev, uint8_t part) {
m_sdCard = dev; m_sdCard = dev;
return FatVolume::init(part); return FatVolume::init(part);

+ 39
- 24
SdFat/examples/#attic/AnalogLogger/AnalogLogger.ino View File

// A simple data logger for the Arduino analog pins with optional DS1307 // A simple data logger for the Arduino analog pins with optional DS1307
// uses RTClib from https://github.com/adafruit/RTClib // uses RTClib from https://github.com/adafruit/RTClib
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
#include <SdFatUtil.h> // define FreeRam() #include <SdFatUtil.h> // define FreeRam()


//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// call back for file timestamps // call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) { void dateTime(uint16_t* date, uint16_t* time) {
DateTime now = RTC.now();
DateTime now = RTC.now();


// return date using FAT_DATE macro to format fields // return date using FAT_DATE macro to format fields
*date = FAT_DATE(now.year(), now.month(), now.day()); *date = FAT_DATE(now.year(), now.month(), now.day());
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
while (!Serial){} // wait for Leonardo
// pstr stores strings in flash to save RAM
cout << endl << pstr("FreeRam: ") << FreeRam() << endl;
while (!Serial) {} // wait for Leonardo
// F() stores strings in flash to save RAM
cout << endl << F("FreeRam: ") << FreeRam() << endl;


#if WAIT_TO_START #if WAIT_TO_START
cout << pstr("Type any character to start\n");
cout << F("Type any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem
while (Serial.read() >= 0) {}
#endif // WAIT_TO_START #endif // WAIT_TO_START


#if USE_DS1307 #if USE_DS1307
// connect to RTC // connect to RTC
Wire.begin(); Wire.begin();
if (!RTC.begin()) error("RTC failed");
if (!RTC.begin()) {
error("RTC failed");
}


// set date time callback function // set date time callback function
SdFile::dateTimeCallback(dateTime); SdFile::dateTimeCallback(dateTime);
#endif // USE_DS1307 #endif // USE_DS1307


// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}


// create a new file in root, the current working directory // create a new file in root, the current working directory
char name[] = "LOGGER00.CSV";
char name[] = "logger00.csv";


for (uint8_t i = 0; i < 100; i++) { for (uint8_t i = 0; i < 100; i++) {
name[6] = i/10 + '0'; name[6] = i/10 + '0';
name[7] = i%10 + '0'; name[7] = i%10 + '0';
if (sd.exists(name)) continue;
if (sd.exists(name)) {
continue;
}
logfile.open(name); logfile.open(name);
break; break;
} }
if (!logfile.is_open()) error("file.open");
if (!logfile.is_open()) {
error("file.open");
}

cout << F("Logging to: ") << name << endl;
cout << F("Type any character to stop\n\n");


cout << pstr("Logging to: ") << name << endl;
cout << pstr("Type any character to stop\n\n");
// format header in buffer // format header in buffer
obufstream bout(buf, sizeof(buf)); obufstream bout(buf, sizeof(buf));


bout << pstr("millis");
bout << F("millis");


#if USE_DS1307 #if USE_DS1307
bout << pstr(",date,time");
bout << F(",date,time");
#endif // USE_DS1307 #endif // USE_DS1307


for (uint8_t i = 0; i < SENSOR_COUNT; i++) { for (uint8_t i = 0; i < SENSOR_COUNT; i++) {
bout << pstr(",sens") << int(i);
bout << F(",sens") << int(i);
} }
logfile << buf << endl; logfile << buf << endl;


logfile << buf << flush; logfile << buf << flush;


// check for error // check for error
if (!logfile) error("write data failed");
if (!logfile) {
error("write data failed");
}


#if ECHO_TO_SERIAL #if ECHO_TO_SERIAL
cout << buf; cout << buf;
#endif // ECHO_TO_SERIAL #endif // ECHO_TO_SERIAL


// don't log two points in the same millis // don't log two points in the same millis
if (m == millis()) delay(1);
if (!Serial.available()) return;
if (m == millis()) {
delay(1);
}

if (!Serial.available()) {
return;
}
logfile.close(); logfile.close();
cout << pstr("Done!");
cout << F("Done!");
while (1); while (1);
}
}

+ 38
- 0
SdFat/examples/#attic/BaseExtCaseTest/BaseExtCaseTest.ino View File

/*
* Program to test Short File Name character case flags.
*/
#include <SPI.h>
#include <SdFat.h>

SdFat sd;

SdFile file;
char* name[] = {
"low.low", "low.Mix", "low.UP",
"Mix.low", "Mix.Mix", "Mix.UP",
"UP.low", "UP.Mix", "UP.UP"
};
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
while (!Serial) {} // wait for Leonardo
Serial.println("type any character to start");
while (Serial.read() < 0) {}
if (!sd.begin()) {
Serial.println("begin failed");
return;
}
for (uint8_t i = 0; i < 9; i++) {
sd.remove(name[i]);
if (!file.open(name[i], O_RDWR | O_CREAT | O_EXCL)) {
sd.errorHalt(name[i]);
}
file.println(name[i]);

file.close();
}
sd.ls(LS_DATE|LS_SIZE);
Serial.println("Done");
}
//------------------------------------------------------------------------------
void loop() {}

+ 1
- 1
SdFat/examples/#attic/HelloWorld/HelloWorld.ino View File



void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo
delay(2000); delay(2000);



+ 20
- 12
SdFat/examples/#attic/PrintBenchmarkSD/PrintBenchmarkSD.ino View File

/* /*
* This sketch is a simple Print benchmark.
* This program is a simple Print benchmark.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SD.h> #include <SD.h>


// SD chip select pin // SD chip select pin


while (Serial.read() >= 0) { while (Serial.read() >= 0) {
} }
// pstr stores strings in flash to save RAM
// F() stores strings in flash to save RAM
Serial.println(F("Type any character to start")); Serial.println(F("Type any character to start"));
while (Serial.read() <= 0) { while (Serial.read() <= 0) {
} }




// initialize the SD card // initialize the SD card
if (!SD.begin(chipSelect)) error("begin");


if (!SD.begin(chipSelect)) {
error("begin");
}


Serial.println(F("Starting print test. Please wait.\n")); Serial.println(F("Starting print test. Please wait.\n"));


// do write test // do write test
for (int test = 0; test < 2; test++) { for (int test = 0; test < 2; test++) {
file = SD.open("BENCH.TXT", FILE_WRITE);
if (!file) error("open failed");
file = SD.open("bench.txt", FILE_WRITE);

if (!file) {
error("open failed");
}
switch(test) { switch(test) {
case 0: case 0:
Serial.println(F("Test of println(uint16_t)")); Serial.println(F("Test of println(uint16_t)"));
error("write failed"); error("write failed");
} }
m = micros() - m; m = micros() - m;
if (maxLatency < m) maxLatency = m;
if (minLatency > m) minLatency = m;
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m; totalLatency += m;
} }
file.flush(); file.flush();
Serial.print(F(" usec, Avg Latency: ")); Serial.print(F(" usec, Avg Latency: "));
Serial.print(totalLatency/N_PRINT); Serial.print(totalLatency/N_PRINT);
Serial.println(F(" usec\n")); Serial.println(F(" usec\n"));
SD.remove("BENCH.TXT");
SD.remove("bench.txt");
} }
file.close(); file.close();
Serial.println(F("Done!\n")); Serial.println(F("Done!\n"));

+ 2
- 2
SdFat/examples/#attic/SD_Size/SD_Size.ino View File

/* /*
* Sketch to compare size of Arduino SD library with SdFat V2.
* See SdFatSize.pde for SdFat sketch.
* Program to compare size of Arduino SD library with SdFat.
* See SdFatSize.ino for SdFat program.
*/ */
#include <SPI.h> #include <SPI.h>
#include <SD.h> #include <SD.h>

+ 4
- 4
SdFat/examples/#attic/SdFatSize/SdFatSize.ino View File

/* /*
* Sketch to compare size of SdFat V2 with Arduino SD library.
* See SD_Size.pde for Arduino SD sketch.
* Program to compare size of SdFat with Arduino SD library.
* See SD_Size.ino for Arduino SD program.
* *
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


SdFat sd; SdFat sd;
Serial.println("begin failed"); Serial.println("begin failed");
return; return;
} }
file.open("SIZE_TST.TXT", O_RDWR | O_CREAT | O_AT_END);
file.open("SizeTest.txt", O_RDWR | O_CREAT | O_AT_END);


file.println("Hello"); file.println("Hello");



+ 21
- 13
SdFat/examples/#attic/append/append.ino View File

/* /*
* Append Example * Append Example
* *
* This sketch shows how to use open for append.
* The sketch will append 100 line each time it opens the file.
* The sketch will open and close the file 100 times.
* This program shows how to use open for append.
* The program will append 100 line each time it opens the file.
* The program will open and close the file 100 times.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD chip select pin // SD chip select pin
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup() {
// filename for this example // filename for this example
char name[] = "APPEND.TXT";
char name[] = "append.txt";


Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo


// pstr() stores strings in flash to save RAM
cout << endl << pstr("Type any character to start\n");
// F() stores strings in flash to save RAM
cout << endl << F("Type any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // Catch Due reset problem delay(400); // Catch Due reset problem


// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}

cout << F("Appending to: ") << name;


cout << pstr("Appending to: ") << name;
for (uint8_t i = 0; i < 100; i++) { for (uint8_t i = 0; i < 100; i++) {
// open stream for append // open stream for append
ofstream sdout(name, ios::out | ios::app); ofstream sdout(name, ios::out | ios::app);
if (!sdout) error("open failed");
if (!sdout) {
error("open failed");
}


// append 100 lines to the file // append 100 lines to the file
for (uint8_t j = 0; j < 100; j++) { for (uint8_t j = 0; j < 100; j++) {
// close the stream // close the stream
sdout.close(); sdout.close();


if (!sdout) error("append data failed");
if (!sdout) {
error("append data failed");
}


// output progress indicator // output progress indicator
if (i % 25 == 0) cout << endl;
if (i % 25 == 0) {
cout << endl;
}
cout << '.'; cout << '.';
} }
cout << endl << "Done" << endl; cout << endl << "Done" << endl;

+ 15
- 9
SdFat/examples/#attic/average/average.ino View File

/* /*
* Calculate the sum and average of a list of floating point numbers
* Calculate the sum and average of a list of floating point numbers
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD chip select pin // SD chip select pin
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void writeTestFile() { void writeTestFile() {
// open the output file // open the output file
ofstream sdout("AVG_TEST.TXT");
ofstream sdout("AvgTest.txt");


// write a series of float numbers // write a series of float numbers
for (int16_t i = -1001; i < 2000; i += 13) { for (int16_t i = -1001; i < 2000; i += 13) {
sdout << 0.1 * i << endl; sdout << 0.1 * i << endl;
} }
if (!sdout) sd.errorHalt("sdout failed");
if (!sdout) {
sd.errorHalt("sdout failed");
}


sdout.close(); sdout.close();
} }
double sum = 0; // sum of input numbers double sum = 0; // sum of input numbers


// open the input file // open the input file
ifstream sdin("AVG_TEST.TXT");
ifstream sdin("AvgTest.txt");


// check for an open failure // check for an open failure
if (!sdin) sd.errorHalt("sdin failed");
if (!sdin) {
sd.errorHalt("sdin failed");
}


// read and sum the numbers // read and sum the numbers
while (sdin >> num) { while (sdin >> num) {
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo


// pstr stores strings in flash to save RAM
cout << pstr("Type any character to start\n");
// F() stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // Catch Due reset problem delay(400); // Catch Due reset problem


// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}


// write the test file // write the test file
writeTestFile(); writeTestFile();

+ 20
- 11
SdFat/examples/#attic/benchSD/benchSD.ino View File

/* /*
* This sketch is a simple binary write/read benchmark
* This program is a simple binary write/read benchmark
* for the standard Arduino SD.h library. * for the standard Arduino SD.h library.
*/ */
#include <SPI.h> #include <SPI.h>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
while (!Serial){} // wait for Leonardo
while (!Serial) {} // wait for Leonardo
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void loop() { void loop() {
// discard any input // discard any input
while (Serial.read() >= 0) {} while (Serial.read() >= 0) {}


// pstr stores strings in flash to save RAM
// F() stores strings in flash to save RAM
Serial.println(F("Type any character to start")); Serial.println(F("Type any character to start"));
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem
if (!SD.begin(chipSelect)) error("begin");

if (!SD.begin(chipSelect)) {
error("begin");
}


// open or create file - truncate existing file. // open or create file - truncate existing file.
file = SD.open("BENCH.DAT", FILE_WRITE | O_TRUNC);
// file = SD.open("BENCH.DAT", O_CREAT | O_TRUNC | O_CREAT);
file = SD.open("Bench.dat", O_CREAT | O_TRUNC | O_CREAT);
if (!file) { if (!file) {
error("open failed"); error("open failed");
} }
error("write failed"); error("write failed");
} }
m = micros() - m; m = micros() - m;
if (maxLatency < m) maxLatency = m;
if (minLatency > m) minLatency = m;
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m; totalLatency += m;
} }
file.flush(); file.flush();
error("read failed"); error("read failed");
} }
m = micros() - m; m = micros() - m;
if (maxLatency < m) maxLatency = m;
if (minLatency > m) minLatency = m;
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m; totalLatency += m;
if (buf[BUF_SIZE-1] != '\n') { if (buf[BUF_SIZE-1] != '\n') {
error("data check"); error("data check");

+ 2
- 2
SdFat/examples/#attic/bufstream/bufstream.ino View File

/* /*
* Use of ibufsteam to parse a line and obufstream to format a line * Use of ibufsteam to parse a line and obufstream to format a line
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// create a serial output stream // create a serial output stream
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo
delay(2000); delay(2000);
// initialize input string // initialize input string
ibufstream bin("123 456 789"); ibufstream bin("123 456 789");



+ 13
- 9
SdFat/examples/#attic/eventlog/eventlog.ino View File

/* /*
* Append a line to a file - demo of pathnames and streams * Append a line to a file - demo of pathnames and streams
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD chip select pin // SD chip select pin
ArduinoOutStream cout(Serial); ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/* /*
* Append a line to LOGFILE.TXT
* Append a line to logfile.txt
*/ */
void logEvent(const char *msg) { void logEvent(const char *msg) {
// create dir if needed // create dir if needed
sd.mkdir("LOGS/2011/JAN");
sd.mkdir("logs/2014/Jan");


// create or open a file for append // create or open a file for append
ofstream sdlog("LOGS/2011/JAN/LOGFILE.TXT", ios::out | ios::app);
ofstream sdlog("logs/2014/Jan/logfile.txt", ios::out | ios::app);


// append a line to the file // append a line to the file
sdlog << msg << endl; sdlog << msg << endl;


// check for errors // check for errors
if (!sdlog) sd.errorHalt("append failed");
if (!sdlog) {
sd.errorHalt("append failed");
}


sdlog.close(); sdlog.close();
} }
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo


// pstr stores strings in flash to save RAM
cout << pstr("Type any character to start\n");
// F() stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem


// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}


// append a line to the logfile // append a line to the logfile
logEvent("Another line for the logfile"); logEvent("Another line for the logfile");


cout << "Done - check /LOGS/2011/JAN/LOGFILE.TXT on the SD" << endl;
cout << F("Done - check /logs/2014/Jan/logfile.txt on the SD") << endl;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void loop() {} void loop() {}

+ 50
- 36
SdFat/examples/#attic/fgetsRewrite/fgetsRewrite.ino View File

// Demo of rewriting a line read by fgets // Demo of rewriting a line read by fgets
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD card chip select pin // SD card chip select pin
char line[25]; char line[25];
int c; int c;
uint32_t pos; uint32_t pos;
// open test file // open test file
SdFile rdfile("FGETS.TXT", O_RDWR);
SdFile rdfile("fgets.txt", O_RDWR);
// check for open error // check for open error
if (!rdfile.isOpen()) error("demoFgets");
if (!rdfile.isOpen()) {
error("demoFgets");
}

// list file // list file
cout << pstr("-----Before Rewrite\r\n");
while ((c = rdfile.read()) >= 0) Serial.write(c);
rdfile.rewind();
cout << F("-----Before Rewrite\r\n");
while ((c = rdfile.read()) >= 0) {
Serial.write(c);
}

rdfile.rewind();
// read lines from the file to get position // read lines from the file to get position
while (1) { while (1) {
pos = rdfile.curPosition(); pos = rdfile.curPosition();
error("Line not found"); error("Line not found");
} }
// find line that contains "Line C" // find line that contains "Line C"
if (strstr(line, "Line C"))break;
if (strstr(line, "Line C")) {
break;
}
}

// rewrite line with 'C'
if (!rdfile.seekSet(pos)) {
error("seekSet");
} }
// rewrite line with 'C'
if (!rdfile.seekSet(pos))error("seekSet");
rdfile.println("Line R"); rdfile.println("Line R");
rdfile.rewind(); rdfile.rewind();

// list file // list file
cout << pstr("\r\n-----After Rewrite\r\n");
while ((c = rdfile.read()) >= 0) Serial.write(c);
cout << F("\r\n-----After Rewrite\r\n");
while ((c = rdfile.read()) >= 0) {
Serial.write(c);
}

// close so rewrite is not lost // close so rewrite is not lost
rdfile.close(); rdfile.close();
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void makeTestFile() { void makeTestFile() {
// create or open test file // create or open test file
SdFile wrfile("FGETS.TXT", O_WRITE | O_CREAT | O_TRUNC);
SdFile wrfile("fgets.txt", O_WRITE | O_CREAT | O_TRUNC);
// check for open error // check for open error
if (!wrfile.isOpen()) error("MakeTestFile");
if (!wrfile.isOpen()) {
error("MakeTestFile");
}

// write test file // write test file
wrfile.print(F( wrfile.print(F(
"Line A\r\n"
"Line B\r\n"
"Line C\r\n"
"Line D\r\n"
"Line E\r\n"
));
"Line A\r\n"
"Line B\r\n"
"Line C\r\n"
"Line D\r\n"
"Line E\r\n"
));
wrfile.close(); wrfile.close();
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup(void) { void setup(void) {
Serial.begin(9600); Serial.begin(9600);
while (!Serial){} // wait for Leonardo
while (!Serial) {} // wait for Leonardo


cout << pstr("Type any character to start\n");
cout << F("Type any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}

makeTestFile(); makeTestFile();
demoFgets(); demoFgets();
cout << pstr("\nDone\n");
cout << F("\nDone\n");
} }
void loop(void) {} void loop(void) {}

+ 14
- 8
SdFat/examples/#attic/readlog/readlog.ino View File

/* /*
* Read the logfile created by the eventlog.pde example.
* Read the logfile created by the eventlog.ino example.
* Demo of pathnames and working directories * Demo of pathnames and working directories
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD chip select pin // SD chip select pin


// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}


// set current working directory // set current working directory
if (!sd.chdir("LOGS/2011/JAN/")) {
sd.errorHalt("chdir failed. Did you run eventlog.pde?");
if (!sd.chdir("logs/2014/Jan/")) {
sd.errorHalt("chdir failed. Did you run eventlog.ino?");
} }
// open file in current working directory // open file in current working directory
ifstream file("LOGFILE.TXT");
ifstream file("logfile.txt");


if (!file.is_open()) sd.errorHalt("open failed");
if (!file.is_open()) {
sd.errorHalt("open failed");
}


// copy the file to Serial // copy the file to Serial
while ((c = file.get()) >= 0) cout << (char)c;
while ((c = file.get()) >= 0) {
cout << (char)c;
}


cout << "Done" << endl; cout << "Done" << endl;
} }

+ 114
- 76
SdFat/examples/AnalogBinLogger/AnalogBinLogger.ino View File

* Samples are logged at regular intervals. Each Sample consists of the ADC * Samples are logged at regular intervals. Each Sample consists of the ADC
* values for the analog pins defined in the PIN_LIST array. The pins numbers * values for the analog pins defined in the PIN_LIST array. The pins numbers
* may be in any order. * may be in any order.
*
*
* Edit the configuration constants below to set the sample pins, sample rate, * Edit the configuration constants below to set the sample pins, sample rate,
* and other configuration values. * and other configuration values.
* *
* Data is written to the file using a SD multiple block write command. * Data is written to the file using a SD multiple block write command.
*/ */
#ifdef __AVR__ #ifdef __AVR__
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
#include <SdFatUtil.h> #include <SdFatUtil.h>
#include "AnalogBinLogger.h" #include "AnalogBinLogger.h"
// //
// You can select an ADC clock rate by defining the symbol ADC_PRESCALER to // You can select an ADC clock rate by defining the symbol ADC_PRESCALER to
// one of these values. You must choose an appropriate ADC clock rate for // one of these values. You must choose an appropriate ADC clock rate for
// your sample interval.
// your sample interval.
// #define ADC_PRESCALER 7 // F_CPU/128 125 kHz on an Uno // #define ADC_PRESCALER 7 // F_CPU/128 125 kHz on an Uno
// #define ADC_PRESCALER 6 // F_CPU/64 250 kHz on an Uno // #define ADC_PRESCALER 6 // F_CPU/64 250 kHz on an Uno
// #define ADC_PRESCALER 5 // F_CPU/32 500 kHz on an Uno // #define ADC_PRESCALER 5 // F_CPU/32 500 kHz on an Uno
const uint32_t FILE_BLOCK_COUNT = 256000; const uint32_t FILE_BLOCK_COUNT = 256000;


// log file base name. Must be six characters or less. // log file base name. Must be six characters or less.
#define FILE_BASE_NAME "ANALOG"
#define FILE_BASE_NAME "analog"


// Set RECORD_EIGHT_BITS non-zero to record only the high 8-bits of the ADC. // Set RECORD_EIGHT_BITS non-zero to record only the high 8-bits of the ADC.
#define RECORD_EIGHT_BITS 0 #define RECORD_EIGHT_BITS 0
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Buffer definitions. // Buffer definitions.
// //
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional
// buffers. QUEUE_DIM must be a power of two larger than // buffers. QUEUE_DIM must be a power of two larger than
//(BUFFER_BLOCK_COUNT + 1). //(BUFFER_BLOCK_COUNT + 1).
// //
// End of configuration constants. // End of configuration constants.
//============================================================================== //==============================================================================
// Temporary log file. Will be deleted if a reset or power failure occurs. // Temporary log file. Will be deleted if a reset or power failure occurs.
#define TMP_FILE_NAME "TMP_LOG.BIN"
#define TMP_FILE_NAME "tmp_log.bin"


// Size of file base name. Must not be larger than six. // Size of file base name. Must not be larger than six.
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;


SdBaseFile binFile; SdBaseFile binFile;


char binName[13] = FILE_BASE_NAME "00.BIN";
char binName[13] = FILE_BASE_NAME "00.bin";


#if RECORD_EIGHT_BITS #if RECORD_EIGHT_BITS
const size_t SAMPLES_PER_BLOCK = DATA_DIM8/PIN_COUNT; const size_t SAMPLES_PER_BLOCK = DATA_DIM8/PIN_COUNT;
uint8_t fullTail; uint8_t fullTail;


// queueNext assumes QUEUE_DIM is a power of two // queueNext assumes QUEUE_DIM is a power of two
inline uint8_t queueNext(uint8_t ht) {return (ht + 1) & (QUEUE_DIM -1);}
inline uint8_t queueNext(uint8_t ht) {
return (ht + 1) & (QUEUE_DIM -1);
}
//============================================================================== //==============================================================================
// Interrupt Service Routines // Interrupt Service Routines


#if RECORD_EIGHT_BITS #if RECORD_EIGHT_BITS
uint8_t d = ADCH; uint8_t d = ADCH;
#else // RECORD_EIGHT_BITS #else // RECORD_EIGHT_BITS
// This will access ADCL first.
// This will access ADCL first.
uint16_t d = ADC; uint16_t d = ADC;
#endif // RECORD_EIGHT_BITS #endif // RECORD_EIGHT_BITS


if (isrBufNeeded && emptyHead == emptyTail) { if (isrBufNeeded && emptyHead == emptyTail) {
// no buffers - count overrun // no buffers - count overrun
if (isrOver < 0XFFFF) isrOver++;
if (isrOver < 0XFFFF) {
isrOver++;
}

// Avoid missed timer error. // Avoid missed timer error.
timerFlag = false; timerFlag = false;
return; return;
ADMUX = adcmux[adcindex]; ADMUX = adcmux[adcindex];
ADCSRB = adcsrb[adcindex]; ADCSRB = adcsrb[adcindex];
ADCSRA = adcsra[adcindex]; ADCSRA = adcsra[adcindex];
if (adcindex == 0) timerFlag = false;
if (adcindex == 0) {
timerFlag = false;
}
adcindex = adcindex < (PIN_COUNT - 1) ? adcindex + 1 : 0; adcindex = adcindex < (PIN_COUNT - 1) ? adcindex + 1 : 0;
} else { } else {
timerFlag = false; timerFlag = false;
} }
// Check for buffer needed. // Check for buffer needed.
if (isrBufNeeded) {
if (isrBufNeeded) {
// Remove buffer from empty queue. // Remove buffer from empty queue.
isrBuf = emptyQueue[emptyTail]; isrBuf = emptyQueue[emptyTail];
emptyTail = queueNext(emptyTail); emptyTail = queueNext(emptyTail);
isrBuf->count = 0; isrBuf->count = 0;
isrBuf->overrun = isrOver; isrBuf->overrun = isrOver;
isrBufNeeded = false;
isrBufNeeded = false;
} }
// Store ADC data. // Store ADC data.
isrBuf->data[isrBuf->count++] = d; isrBuf->data[isrBuf->count++] = d;


// Check for buffer full. // Check for buffer full.
if (isrBuf->count >= PIN_COUNT*SAMPLES_PER_BLOCK) { if (isrBuf->count >= PIN_COUNT*SAMPLES_PER_BLOCK) {
// Put buffer isrIn full queue.
// Put buffer isrIn full queue.
uint8_t tmp = fullHead; // Avoid extra fetch of volatile fullHead. uint8_t tmp = fullHead; // Avoid extra fetch of volatile fullHead.
fullQueue[tmp] = (block_t*)isrBuf; fullQueue[tmp] = (block_t*)isrBuf;
fullHead = queueNext(tmp); fullHead = queueNext(tmp);
// Set buffer needed and clear overruns. // Set buffer needed and clear overruns.
isrBufNeeded = true; isrBufNeeded = true;
isrOver = 0; isrOver = 0;
// timer1 interrupt to clear OCF1B // timer1 interrupt to clear OCF1B
ISR(TIMER1_COMPB_vect) { ISR(TIMER1_COMPB_vect) {
// Make sure ADC ISR responded to timer event. // Make sure ADC ISR responded to timer event.
if (timerFlag) timerError = true;
if (timerFlag) {
timerError = true;
}
timerFlag = true; timerFlag = true;
} }
//============================================================================== //==============================================================================
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// initialize ADC and timer1 // initialize ADC and timer1
void adcInit(metadata_t* meta) { void adcInit(metadata_t* meta) {
uint8_t adps; // prescaler bits for ADCSRA
uint8_t adps; // prescaler bits for ADCSRA
uint32_t ticks = F_CPU*SAMPLE_INTERVAL + 0.5; // Sample interval cpu cycles. uint32_t ticks = F_CPU*SAMPLE_INTERVAL + 0.5; // Sample interval cpu cycles.


if (ADC_REF & ~((1 << REFS0) | (1 << REFS1))) { if (ADC_REF & ~((1 << REFS0) | (1 << REFS1))) {
#else // ADC_PRESCALER #else // ADC_PRESCALER
// Allow extra cpu cycles to change ADC settings if more than one pin. // Allow extra cpu cycles to change ADC settings if more than one pin.
int32_t adcCycles = (ticks - ISR_TIMER0)/PIN_COUNT; int32_t adcCycles = (ticks - ISR_TIMER0)/PIN_COUNT;
- (PIN_COUNT > 1 ? ISR_SETUP_ADC : 0);
- (PIN_COUNT > 1 ? ISR_SETUP_ADC : 0);
for (adps = 7; adps > 0; adps--) { for (adps = 7; adps > 0; adps--) {
if (adcCycles >= (MIN_ADC_CYCLES << adps)) break;
if (adcCycles >= (MIN_ADC_CYCLES << adps)) {
break;
}
} }
#endif // ADC_PRESCALER #endif // ADC_PRESCALER
meta->adcFrequency = F_CPU >> adps; meta->adcFrequency = F_CPU >> adps;
} }
meta->pinCount = PIN_COUNT; meta->pinCount = PIN_COUNT;
meta->recordEightBits = RECORD_EIGHT_BITS; meta->recordEightBits = RECORD_EIGHT_BITS;
for (int i = 0; i < PIN_COUNT; i++) { for (int i = 0; i < PIN_COUNT; i++) {
uint8_t pin = PIN_LIST[i]; uint8_t pin = PIN_LIST[i];
if (pin >= NUM_ANALOG_INPUTS) error("Invalid Analog pin number");
if (pin >= NUM_ANALOG_INPUTS) {
error("Invalid Analog pin number");
}
meta->pinNumber[i] = pin; meta->pinNumber[i] = pin;
// Set ADC reference and low three bits of analog pin number.
// Set ADC reference and low three bits of analog pin number.
adcmux[i] = (pin & 7) | ADC_REF; adcmux[i] = (pin & 7) | ADC_REF;
if (RECORD_EIGHT_BITS) adcmux[i] |= 1 << ADLAR;
if (RECORD_EIGHT_BITS) {
adcmux[i] |= 1 << ADLAR;
}

// If this is the first pin, trigger on timer/counter 1 compare match B. // If this is the first pin, trigger on timer/counter 1 compare match B.
adcsrb[i] = i == 0 ? (1 << ADTS2) | (1 << ADTS0) : 0; adcsrb[i] = i == 0 ? (1 << ADTS2) | (1 << ADTS0) : 0;
#ifdef MUX5 #ifdef MUX5
if (pin > 7) adcsrb[i] |= (1 << MUX5);
if (pin > 7) {
adcsrb[i] |= (1 << MUX5);
}
#endif // MUX5 #endif // MUX5
adcsra[i] = (1 << ADEN) | (1 << ADIE) | adps; adcsra[i] = (1 << ADEN) | (1 << ADIE) | adps;
adcsra[i] |= i == 0 ? 1 << ADATE : 1 << ADSC; adcsra[i] |= i == 0 ? 1 << ADATE : 1 << ADSC;
ICR1 = ticks - 1; ICR1 = ticks - 1;
// compare for ADC start // compare for ADC start
OCR1B = 0; OCR1B = 0;
// multiply by prescaler // multiply by prescaler
ticks <<= tshift; ticks <<= tshift;
// Sample interval in CPU clock ticks. // Sample interval in CPU clock ticks.
meta->sampleInterval = ticks; meta->sampleInterval = ticks;
meta->cpuFrequency = F_CPU; meta->cpuFrequency = F_CPU;
Serial.print(' '); Serial.print(' ');
Serial.print(meta->pinNumber[i], DEC); Serial.print(meta->pinNumber[i], DEC);
} }
Serial.println();
Serial.println();
Serial.print(F("ADC bits: ")); Serial.print(F("ADC bits: "));
Serial.println(meta->recordEightBits ? 8 : 10); Serial.println(meta->recordEightBits ? 8 : 10);
Serial.print(F("ADC clock kHz: ")); Serial.print(F("ADC clock kHz: "));
Serial.println(meta->adcFrequency/1000); Serial.println(meta->adcFrequency/1000);
Serial.print(F("Sample Rate: ")); Serial.print(F("Sample Rate: "));
Serial.println(sampleRate);
Serial.println(sampleRate);
Serial.print(F("Sample interval usec: ")); Serial.print(F("Sample interval usec: "));
Serial.println(1000000.0/sampleRate, 4);
Serial.println(1000000.0/sampleRate, 4);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// enable ADC and timer1 interrupts // enable ADC and timer1 interrupts


// Clear any pending interrupt. // Clear any pending interrupt.
ADCSRA |= 1 << ADIF; ADCSRA |= 1 << ADIF;
// Setup for first pin. // Setup for first pin.
ADMUX = adcmux[0]; ADMUX = adcmux[0];
ADCSRB = adcsrb[0]; ADCSRB = adcsrb[0];
ADCSRA = 0; ADCSRA = 0;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Convert binary file to CSV file.
// Convert binary file to csv file.
void binaryToCsv() { void binaryToCsv() {
uint8_t lastPct = 0; uint8_t lastPct = 0;
block_t buf; block_t buf;
uint32_t t0 = millis(); uint32_t t0 = millis();
char csvName[13]; char csvName[13];
StdioStream csvStream; StdioStream csvStream;
if (!binFile.isOpen()) { if (!binFile.isOpen()) {
Serial.println(F("No current binary file")); Serial.println(F("No current binary file"));
return; return;
} }
binFile.rewind(); binFile.rewind();
if (!binFile.read(&buf , 512) == 512) error("Read metadata failed");
// Create a new CSV file.
if (!binFile.read(&buf , 512) == 512) {
error("Read metadata failed");
}
// Create a new csv file.
strcpy(csvName, binName); strcpy(csvName, binName);
strcpy_P(&csvName[BASE_NAME_SIZE + 3], PSTR("CSV"));
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");


if (!csvStream.fopen(csvName, "w")) { if (!csvStream.fopen(csvName, "w")) {
error("open csvStream failed");
error("open csvStream failed");
} }
Serial.println(); Serial.println();
Serial.print(F("Writing: ")); Serial.print(F("Writing: "));
csvStream.print(intervalMicros, 4); csvStream.print(intervalMicros, 4);
csvStream.println(F(",usec")); csvStream.println(F(",usec"));
for (uint8_t i = 0; i < pm->pinCount; i++) { for (uint8_t i = 0; i < pm->pinCount; i++) {
if (i) csvStream.putc(',');
if (i) {
csvStream.putc(',');
}
csvStream.print(F("pin")); csvStream.print(F("pin"));
csvStream.print(pm->pinNumber[i]); csvStream.print(pm->pinNumber[i]);
} }
csvStream.println();
csvStream.println();
uint32_t tPct = millis(); uint32_t tPct = millis();
while (!Serial.available() && binFile.read(&buf, 512) == 512) { while (!Serial.available() && binFile.read(&buf, 512) == 512) {
uint16_t i; uint16_t i;
if (buf.count == 0) break;
if (buf.count == 0) {
break;
}
if (buf.overrun) { if (buf.overrun) {
csvStream.print(F("OVERRUN,")); csvStream.print(F("OVERRUN,"));
csvStream.println(buf.overrun);
csvStream.println(buf.overrun);
} }
for (uint16_t j = 0; j < buf.count; j += PIN_COUNT) { for (uint16_t j = 0; j < buf.count; j += PIN_COUNT) {
for (uint16_t i = 0; i < PIN_COUNT; i++) { for (uint16_t i = 0; i < PIN_COUNT; i++) {
if (i) csvStream.putc(',');
csvStream.print(buf.data[i + j]);
if (i) {
csvStream.putc(',');
}
csvStream.print(buf.data[i + j]);
} }
csvStream.println(); csvStream.println();
} }
Serial.println('%'); Serial.println('%');
} }
} }
if (Serial.available()) break;
if (Serial.available()) {
break;
}
} }
csvStream.fclose();
csvStream.fclose();
Serial.print(F("Done: ")); Serial.print(F("Done: "));
Serial.print(0.001*(millis() - t0)); Serial.print(0.001*(millis() - t0));
Serial.println(F(" Seconds")); Serial.println(F(" Seconds"));
block_t buf; block_t buf;
uint32_t bgnBlock, endBlock; uint32_t bgnBlock, endBlock;
uint32_t bn = 0; uint32_t bn = 0;
if (!binFile.isOpen()) { if (!binFile.isOpen()) {
Serial.println(F("No current binary file")); Serial.println(F("No current binary file"));
return; return;
} }
bn++; bn++;
while (binFile.read(&buf, 512) == 512) { while (binFile.read(&buf, 512) == 512) {
if (buf.count == 0) break;
if (buf.count == 0) {
break;
}
if (buf.overrun) { if (buf.overrun) {
if (!headerPrinted) { if (!headerPrinted) {
Serial.println(); Serial.println();
Serial.println(F("Type any character to stop")); Serial.println(F("Type any character to stop"));
delay(1000); delay(1000);
while (!Serial.available() && binFile.read(&buf , 512) == 512) { while (!Serial.available() && binFile.read(&buf , 512) == 512) {
if (buf.count == 0) break;
if (buf.count == 0) {
break;
}
if (buf.overrun) { if (buf.overrun) {
Serial.print(F("OVERRUN,")); Serial.print(F("OVERRUN,"));
Serial.println(buf.overrun); Serial.println(buf.overrun);
uint32_t const ERASE_SIZE = 262144L; uint32_t const ERASE_SIZE = 262144L;
void logData() { void logData() {
uint32_t bgnBlock, endBlock; uint32_t bgnBlock, endBlock;
// Allocate extra buffer space. // Allocate extra buffer space.
block_t block[BUFFER_BLOCK_COUNT]; block_t block[BUFFER_BLOCK_COUNT];
Serial.println(); Serial.println();
// Initialize ADC and timer1. // Initialize ADC and timer1.
adcInit((metadata_t*) &block[0]); adcInit((metadata_t*) &block[0]);
// Find unused file name. // Find unused file name.
if (BASE_NAME_SIZE > 6) { if (BASE_NAME_SIZE > 6) {
error("FILE_BASE_NAME too long"); error("FILE_BASE_NAME too long");
Serial.println(F("Creating new file")); Serial.println(F("Creating new file"));
binFile.close(); binFile.close();
if (!binFile.createContiguous(sd.vwd(), if (!binFile.createContiguous(sd.vwd(),
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
error("createContiguous failed"); error("createContiguous failed");
} }
// Get the address of the file on the SD. // Get the address of the file on the SD.
} }
// Use SdFat's internal buffer. // Use SdFat's internal buffer.
uint8_t* cache = (uint8_t*)sd.vol()->cacheClear(); uint8_t* cache = (uint8_t*)sd.vol()->cacheClear();
if (cache == 0) error("cacheClear failed");
if (cache == 0) {
error("cacheClear failed");
}

// Flash erase all data in the file. // Flash erase all data in the file.
Serial.println(F("Erasing all data")); Serial.println(F("Erasing all data"));
uint32_t bgnErase = bgnBlock; uint32_t bgnErase = bgnBlock;
uint32_t endErase; uint32_t endErase;
while (bgnErase < endBlock) { while (bgnErase < endBlock) {
endErase = bgnErase + ERASE_SIZE; endErase = bgnErase + ERASE_SIZE;
if (endErase > endBlock) endErase = endBlock;
if (endErase > endBlock) {
endErase = endBlock;
}
if (!sd.card()->erase(bgnErase, endErase)) { if (!sd.card()->erase(bgnErase, endErase)) {
error("erase failed"); error("erase failed");
} }
// Write metadata. // Write metadata.
if (!sd.card()->writeData((uint8_t*)&block[0])) { if (!sd.card()->writeData((uint8_t*)&block[0])) {
error("Write metadata failed"); error("Write metadata failed");
}
}
// Initialize queues. // Initialize queues.
emptyHead = emptyTail = 0; emptyHead = emptyTail = 0;
fullHead = fullTail = 0; fullHead = fullTail = 0;
// Use SdFat buffer for one block. // Use SdFat buffer for one block.
emptyQueue[emptyHead] = (block_t*)cache; emptyQueue[emptyHead] = (block_t*)cache;
emptyHead = queueNext(emptyHead); emptyHead = queueNext(emptyHead);
// Put rest of buffers in the empty queue. // Put rest of buffers in the empty queue.
for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) {
emptyQueue[emptyHead] = &block[i]; emptyQueue[emptyHead] = &block[i];
if (fullHead != fullTail) { if (fullHead != fullTail) {
// Get address of block to write. // Get address of block to write.
block_t* pBlock = fullQueue[fullTail]; block_t* pBlock = fullQueue[fullTail];
// Write block to SD. // Write block to SD.
uint32_t usec = micros(); uint32_t usec = micros();
if (!sd.card()->writeData((uint8_t*)pBlock)) { if (!sd.card()->writeData((uint8_t*)pBlock)) {
} }
usec = micros() - usec; usec = micros() - usec;
t1 = millis(); t1 = millis();
if (usec > maxLatency) maxLatency = usec;
if (usec > maxLatency) {
maxLatency = usec;
}
count += pBlock->count; count += pBlock->count;
// Add overruns and possibly light LED.
// Add overruns and possibly light LED.
if (pBlock->overrun) { if (pBlock->overrun) {
overruns += pBlock->overrun; overruns += pBlock->overrun;
if (ERROR_LED_PIN >= 0) { if (ERROR_LED_PIN >= 0) {
fullHead = queueNext(fullHead); fullHead = queueNext(fullHead);
isrBuf = 0; isrBuf = 0;
} }
if (fullHead == fullTail) break;
if (fullHead == fullTail) {
break;
}
} }
} }
if (!sd.card()->writeStop()) { if (!sd.card()->writeStop()) {
error("writeStop failed"); error("writeStop failed");
} }
// Truncate file if recording stopped early. // Truncate file if recording stopped early.
if (bn != FILE_BLOCK_COUNT) {
if (bn != FILE_BLOCK_COUNT) {
Serial.println(F("Truncating file")); Serial.println(F("Truncating file"));
if (!binFile.truncate(512L * bn)) { if (!binFile.truncate(512L * bn)) {
error("Can't truncate file"); error("Can't truncate file");
} }
} }
if (!binFile.rename(sd.vwd(), binName)) { if (!binFile.rename(sd.vwd(), binName)) {
error("Can't rename file");
}
error("Can't rename file");
}
Serial.print(F("File renamed: ")); Serial.print(F("File renamed: "));
Serial.println(binName); Serial.println(binName);
Serial.print(F("Max block write usec: ")); Serial.print(F("Max block write usec: "));
pinMode(ERROR_LED_PIN, OUTPUT); pinMode(ERROR_LED_PIN, OUTPUT);
} }
Serial.begin(9600); Serial.begin(9600);
// Read the first sample pin to init the ADC. // Read the first sample pin to init the ADC.
analogRead(PIN_LIST[0]); analogRead(PIN_LIST[0]);
Serial.print(F("FreeRam: ")); Serial.print(F("FreeRam: "));
Serial.println(FreeRam()); Serial.println(FreeRam());


while (Serial.read() >= 0) {} while (Serial.read() >= 0) {}
Serial.println(); Serial.println();
Serial.println(F("type:")); Serial.println(F("type:"));
Serial.println(F("c - convert file to CSV"));
Serial.println(F("d - dump data to Serial"));
Serial.println(F("c - convert file to csv"));
Serial.println(F("d - dump data to Serial"));
Serial.println(F("e - overrun error details")); Serial.println(F("e - overrun error details"));
Serial.println(F("r - record ADC data")); Serial.println(F("r - record ADC data"));


do { do {
delay(10); delay(10);
} while (Serial.read() >= 0); } while (Serial.read() >= 0);
if (c == 'c') { if (c == 'c') {
binaryToCsv(); binaryToCsv();
} else if (c == 'd') { } else if (c == 'd') {
dumpData(); dumpData();
} else if (c == 'e') {
} else if (c == 'e') {
checkOverrun(); checkOverrun();
} else if (c == 'r') { } else if (c == 'r') {
logData(); logData();

+ 31
- 24
SdFat/examples/LongFileName/LongFileName.ino View File

// Example use of openNextLFN and open by index.
// You can use test files located in
// Example use of lfnOpenNext and open by index.
// You can use test files located in
// SdFat/examples/LongFileName/testFiles. // SdFat/examples/LongFileName/testFiles.
#include<SPI.h> #include<SPI.h>
#include <SdFat.h> #include <SdFat.h>


SdFat sd; SdFat sd;
SdFile file; SdFile file;
SdFile dirFile;


// Number of files found. // Number of files found.
uint16_t n = 0; uint16_t n = 0;
uint16_t dirIndex[nMax]; uint16_t dirIndex[nMax];
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup() {
const size_t NAME_DIM = 50;
char name[NAME_DIM];
dir_t dir;
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} while (!Serial) {}
delay(1000);

// Print the location of some test files. // Print the location of some test files.
Serial.println(F("\r\n" Serial.println(F("\r\n"
"You can use test files located in\r\n"
"You can use test files located in\r\n"
"SdFat/examples/LongFileName/testFiles")); "SdFat/examples/LongFileName/testFiles"));
if (!sd.begin(SD_CS_PIN)) sd.initErrorHalt();

if (!sd.begin(SD_CS_PIN)) {
sd.initErrorHalt();
}
Serial.print(F("Free RAM: ")); Serial.print(F("Free RAM: "));
Serial.println(FreeRam()); Serial.println(FreeRam());
Serial.println(); Serial.println();
// List files in root directory. Volume working directory is initially root.
sd.vwd()->rewind();
while (n < nMax && file.openNextLFN(sd.vwd(), name, NAME_DIM, O_READ) > 0) {

// List files in root directory.
if (!dirFile.open("/", O_READ)) {
sd.errorHalt("open root failed");
}
while (n < nMax && file.openNext(&dirFile, O_READ)) {

// Skip directories and hidden files. // Skip directories and hidden files.
if (!file.isSubDir() && !file.isHidden()) { if (!file.isSubDir() && !file.isHidden()) {
// Save dirIndex of file in directory. // Save dirIndex of file in directory.
dirIndex[n] = file.dirIndex(); dirIndex[n] = file.dirIndex();
// Print the file number and name. // Print the file number and name.
Serial.print(n++); Serial.print(n++);
Serial.write(' '); Serial.write(' ');
Serial.println(name);
file.printName(&Serial);
Serial.println();
} }
file.close(); file.close();
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void loop() { void loop() {
int c; int c;
// Discard any Serial input. // Discard any Serial input.
while (Serial.read() > 0) {} while (Serial.read() > 0) {}
Serial.print(F("\r\nEnter File Number: ")); Serial.print(F("\r\nEnter File Number: "));
while ((c = Serial.read()) < 0) {}; while ((c = Serial.read()) < 0) {};
if (!isdigit(c) || (c -= '0') >= n) { if (!isdigit(c) || (c -= '0') >= n) {
Serial.println(F("Invald number")); Serial.println(F("Invald number"));
return; return;
} }
Serial.println(c); Serial.println(c);
if (!file.open(sd.vwd(), dirIndex[c], O_READ)) {
if (!file.open(&dirFile, dirIndex[c], O_READ)) {
sd.errorHalt(F("open")); sd.errorHalt(F("open"));
} }
Serial.println(); Serial.println();
char last; char last;
// Copy up to 500 characters to Serial. // Copy up to 500 characters to Serial.
for (int i = 0; i < 500 && (c = file.read()) > 0; i++) { for (int i = 0; i < 500 && (c = file.read()) > 0; i++) {
Serial.write(last = (char)c); Serial.write(last = (char)c);
} }
// Add new line if missing from last line. // Add new line if missing from last line.
if (last != '\n') Serial.println();
if (last != '\n') {
Serial.println();
}
file.close(); file.close();
Serial.flush();
delay(100);
} }

SdFat/examples/LongFileName/testFiles/With Two.dots.txt → SdFat/examples/LongFileName/testFiles/With.Two dots.txt View File


+ 72
- 48
SdFat/examples/LowLatencyLogger/LowLatencyLogger.ino View File

/** /**
* This program logs data to a binary file. Functions are included * This program logs data to a binary file. Functions are included
* to convert the binary file to a CSV text file.
* to convert the binary file to a csv text file.
* *
* Samples are logged at regular intervals. The maximum logging rate * Samples are logged at regular intervals. The maximum logging rate
* depends on the quality of your SD card and the time required to * depends on the quality of your SD card and the time required to
* *
* Data is written to the file using a SD multiple block write command. * Data is written to the file using a SD multiple block write command.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
#include <SdFatUtil.h> #include <SdFatUtil.h>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void printData(Print* pr, data_t* data) { void printData(Print* pr, data_t* data) {
pr->print(data->time); pr->print(data->time);
for (int i = 0; i < ADC_DIM; i++) { for (int i = 0; i < ADC_DIM; i++) {
pr->write(',');
pr->write(',');
pr->print(data->adc[i]); pr->print(data->adc[i]);
} }
pr->println(); pr->println();
// Digital pin to indicate an error, set to -1 if not used. // Digital pin to indicate an error, set to -1 if not used.
// The led blinks for fatal errors. The led goes on solid for SD write // The led blinks for fatal errors. The led goes on solid for SD write
// overrun errors and logging continues. // overrun errors and logging continues.
const int8_t ERROR_LED_PIN = 3;
const int8_t ERROR_LED_PIN = -1;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// File definitions. // File definitions.
// //
const uint32_t FILE_BLOCK_COUNT = 256000; const uint32_t FILE_BLOCK_COUNT = 256000;


// log file base name. Must be six characters or less. // log file base name. Must be six characters or less.
#define FILE_BASE_NAME "DATA"
#define FILE_BASE_NAME "data"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Buffer definitions. // Buffer definitions.
// //
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional
// buffers. // buffers.
// //
#ifndef RAMEND #ifndef RAMEND
// End of configuration constants. // End of configuration constants.
//============================================================================== //==============================================================================
// Temporary log file. Will be deleted if a reset or power failure occurs. // Temporary log file. Will be deleted if a reset or power failure occurs.
#define TMP_FILE_NAME "TMP_LOG.BIN"
#define TMP_FILE_NAME "tmp_log.bin"


// Size of file base name. Must not be larger than six. // Size of file base name. Must not be larger than six.
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;


SdBaseFile binFile; SdBaseFile binFile;


char binName[13] = FILE_BASE_NAME "00.BIN";
char binName[13] = FILE_BASE_NAME "00.bin";


// Number of data records in a block. // Number of data records in a block.
const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t);
uint8_t fullTail; uint8_t fullTail;


// Advance queue index. // Advance queue index.
inline uint8_t queueNext(uint8_t ht) {return ht < (QUEUE_DIM - 1) ? ht + 1 : 0;}
inline uint8_t queueNext(uint8_t ht) {
return ht < (QUEUE_DIM - 1) ? ht + 1 : 0;
}
//============================================================================== //==============================================================================
// Error messages stored in flash. // Error messages stored in flash.
#define error(msg) errorFlash(F(msg)) #define error(msg) errorFlash(F(msg))
} }
} }
//============================================================================== //==============================================================================
// Convert binary file to CSV file.
// Convert binary file to csv file.
void binaryToCsv() { void binaryToCsv() {
uint8_t lastPct = 0; uint8_t lastPct = 0;
block_t block; block_t block;
uint32_t syncCluster = 0; uint32_t syncCluster = 0;
SdFile csvFile; SdFile csvFile;
char csvName[13]; char csvName[13];
if (!binFile.isOpen()) { if (!binFile.isOpen()) {
Serial.println(); Serial.println();
Serial.println(F("No current binary file")); Serial.println(F("No current binary file"));
binFile.rewind(); binFile.rewind();
// Create a new csvFile. // Create a new csvFile.
strcpy(csvName, binName); strcpy(csvName, binName);
strcpy_P(&csvName[BASE_NAME_SIZE + 3], PSTR("CSV"));
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");


if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) {
error("open csvFile failed");
error("open csvFile failed");
} }
Serial.println(); Serial.println();
Serial.print(F("Writing: ")); Serial.print(F("Writing: "));
uint32_t tPct = millis(); uint32_t tPct = millis();
while (!Serial.available() && binFile.read(&block, 512) == 512) { while (!Serial.available() && binFile.read(&block, 512) == 512) {
uint16_t i; uint16_t i;
if (block.count == 0) break;
if (block.count == 0) {
break;
}
if (block.overrun) { if (block.overrun) {
csvFile.print(F("OVERRUN,")); csvFile.print(F("OVERRUN,"));
csvFile.println(block.overrun); csvFile.println(block.overrun);
Serial.println('%'); Serial.println('%');
} }
} }
if (Serial.available()) break;
if (Serial.available()) {
break;
}
} }
csvFile.close(); csvFile.close();
Serial.print(F("Done: ")); Serial.print(F("Done: "));
block_t block; block_t block;
uint32_t bgnBlock, endBlock; uint32_t bgnBlock, endBlock;
uint32_t bn = 0; uint32_t bn = 0;
if (!binFile.isOpen()) { if (!binFile.isOpen()) {
Serial.println(); Serial.println();
Serial.println(F("No current binary file")); Serial.println(F("No current binary file"));
Serial.println(); Serial.println();
Serial.println(F("Checking overrun errors - type any character to stop")); Serial.println(F("Checking overrun errors - type any character to stop"));
while (binFile.read(&block, 512) == 512) { while (binFile.read(&block, 512) == 512) {
if (block.count == 0) break;
if (block.count == 0) {
break;
}
if (block.overrun) { if (block.overrun) {
if (!headerPrinted) { if (!headerPrinted) {
Serial.println(); Serial.println();
delay(1000); delay(1000);
printHeader(&Serial); printHeader(&Serial);
while (!Serial.available() && binFile.read(&block , 512) == 512) { while (!Serial.available() && binFile.read(&block , 512) == 512) {
if (block.count == 0) break;
if (block.count == 0) {
break;
}
if (block.overrun) { if (block.overrun) {
Serial.print(F("OVERRUN,")); Serial.print(F("OVERRUN,"));
Serial.println(block.overrun); Serial.println(block.overrun);
uint32_t const ERASE_SIZE = 262144L; uint32_t const ERASE_SIZE = 262144L;
void logData() { void logData() {
uint32_t bgnBlock, endBlock; uint32_t bgnBlock, endBlock;
// Allocate extra buffer space. // Allocate extra buffer space.
block_t block[BUFFER_BLOCK_COUNT]; block_t block[BUFFER_BLOCK_COUNT];
block_t* curBlock = 0; block_t* curBlock = 0;
Serial.println(); Serial.println();
// Find unused file name. // Find unused file name.
if (BASE_NAME_SIZE > 6) { if (BASE_NAME_SIZE > 6) {
error("FILE_BASE_NAME too long"); error("FILE_BASE_NAME too long");
Serial.println(F("Creating new file")); Serial.println(F("Creating new file"));
binFile.close(); binFile.close();
if (!binFile.createContiguous(sd.vwd(), if (!binFile.createContiguous(sd.vwd(),
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
error("createContiguous failed"); error("createContiguous failed");
} }
// Get the address of the file on the SD. // Get the address of the file on the SD.
} }
// Use SdFat's internal buffer. // Use SdFat's internal buffer.
uint8_t* cache = (uint8_t*)sd.vol()->cacheClear(); uint8_t* cache = (uint8_t*)sd.vol()->cacheClear();
if (cache == 0) error("cacheClear failed");
if (cache == 0) {
error("cacheClear failed");
}

// Flash erase all data in the file. // Flash erase all data in the file.
Serial.println(F("Erasing all data")); Serial.println(F("Erasing all data"));
uint32_t bgnErase = bgnBlock; uint32_t bgnErase = bgnBlock;
uint32_t endErase; uint32_t endErase;
while (bgnErase < endBlock) { while (bgnErase < endBlock) {
endErase = bgnErase + ERASE_SIZE; endErase = bgnErase + ERASE_SIZE;
if (endErase > endBlock) endErase = endBlock;
if (endErase > endBlock) {
endErase = endBlock;
}
if (!sd.card()->erase(bgnErase, endErase)) { if (!sd.card()->erase(bgnErase, endErase)) {
error("erase failed"); error("erase failed");
} }
// Initialize queues. // Initialize queues.
emptyHead = emptyTail = 0; emptyHead = emptyTail = 0;
fullHead = fullTail = 0; fullHead = fullTail = 0;
// Use SdFat buffer for one block. // Use SdFat buffer for one block.
emptyQueue[emptyHead] = (block_t*)cache; emptyQueue[emptyHead] = (block_t*)cache;
emptyHead = queueNext(emptyHead); emptyHead = queueNext(emptyHead);
// Put rest of buffers in the empty queue. // Put rest of buffers in the empty queue.
for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) {
emptyQueue[emptyHead] = &block[i]; emptyQueue[emptyHead] = &block[i];
while (1) { while (1) {
// Time for next data record. // Time for next data record.
logTime += LOG_INTERVAL_USEC; logTime += LOG_INTERVAL_USEC;
if (Serial.available()) closeFile = true;
if (Serial.available()) {
closeFile = true;
}

if (closeFile) { if (closeFile) {
if (curBlock != 0 && curBlock->count >= 0) {
if (curBlock != 0 && curBlock->count >= 0) {
// Put buffer in full queue. // Put buffer in full queue.
fullQueue[fullHead] = curBlock; fullQueue[fullHead] = curBlock;
fullHead = queueNext(fullHead); fullHead = queueNext(fullHead);
curBlock = 0; curBlock = 0;
}
}
} else { } else {
if (curBlock == 0 && emptyTail != emptyHead) { if (curBlock == 0 && emptyTail != emptyHead) {
curBlock = emptyQueue[emptyTail]; curBlock = emptyQueue[emptyTail];
do { do {
diff = logTime - micros(); diff = logTime - micros();
} while(diff > 0); } while(diff > 0);
if (diff < -10) error("LOG_INTERVAL_USEC too small");
if (diff < -10) {
error("LOG_INTERVAL_USEC too small");
}
if (curBlock == 0) { if (curBlock == 0) {
overrun++; overrun++;
} else { } else {
acquireData(&curBlock->data[curBlock->count++]); acquireData(&curBlock->data[curBlock->count++]);
if (curBlock->count == DATA_DIM) { if (curBlock->count == DATA_DIM) {
fullQueue[fullHead] = curBlock; fullQueue[fullHead] = curBlock;
fullHead = queueNext(fullHead);
fullHead = queueNext(fullHead);
curBlock = 0; curBlock = 0;
} }
} }
} }
if (fullHead == fullTail) { if (fullHead == fullTail) {
// Exit loop if done. // Exit loop if done.
if (closeFile) break;
if (closeFile) {
break;
}
} else if (!sd.card()->isBusy()) { } else if (!sd.card()->isBusy()) {
// Get address of block to write. // Get address of block to write.
block_t* pBlock = fullQueue[fullTail]; block_t* pBlock = fullQueue[fullTail];
fullTail = queueNext(fullTail);
fullTail = queueNext(fullTail);
// Write block to SD. // Write block to SD.
uint32_t usec = micros(); uint32_t usec = micros();
if (!sd.card()->writeData((uint8_t*)pBlock)) { if (!sd.card()->writeData((uint8_t*)pBlock)) {
} }
usec = micros() - usec; usec = micros() - usec;
t1 = millis(); t1 = millis();
if (usec > maxLatency) maxLatency = usec;
if (usec > maxLatency) {
maxLatency = usec;
}
count += pBlock->count; count += pBlock->count;
// Add overruns and possibly light LED.
// Add overruns and possibly light LED.
if (pBlock->overrun) { if (pBlock->overrun) {
overrunTotal += pBlock->overrun; overrunTotal += pBlock->overrun;
if (ERROR_LED_PIN >= 0) { if (ERROR_LED_PIN >= 0) {
error("writeStop failed"); error("writeStop failed");
} }
// Truncate file if recording stopped early. // Truncate file if recording stopped early.
if (bn != FILE_BLOCK_COUNT) {
if (bn != FILE_BLOCK_COUNT) {
Serial.println(F("Truncating file")); Serial.println(F("Truncating file"));
if (!binFile.truncate(512L * bn)) { if (!binFile.truncate(512L * bn)) {
error("Can't truncate file"); error("Can't truncate file");
} }
} }
if (!binFile.rename(sd.vwd(), binName)) { if (!binFile.rename(sd.vwd(), binName)) {
error("Can't rename file");
}
error("Can't rename file");
}
Serial.print(F("File renamed: ")); Serial.print(F("File renamed: "));
Serial.println(binName); Serial.println(binName);
Serial.print(F("Max block write usec: ")); Serial.print(F("Max block write usec: "));
} }
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} while (!Serial) {}
Serial.print(F("FreeRam: ")); Serial.print(F("FreeRam: "));
Serial.println(FreeRam()); Serial.println(FreeRam());
Serial.print(F("Records/block: ")); Serial.print(F("Records/block: "));
Serial.println(DATA_DIM); Serial.println(DATA_DIM);
if (sizeof(block_t) != 512) error("Invalid block size");
if (sizeof(block_t) != 512) {
error("Invalid block size");
}
// initialize file system. // initialize file system.
if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) {
sd.initErrorPrint(); sd.initErrorPrint();
while (Serial.read() >= 0) {} while (Serial.read() >= 0) {}
Serial.println(); Serial.println();
Serial.println(F("type:")); Serial.println(F("type:"));
Serial.println(F("c - convert file to CSV"));
Serial.println(F("d - dump data to Serial"));
Serial.println(F("c - convert file to csv"));
Serial.println(F("d - dump data to Serial"));
Serial.println(F("e - overrun error details")); Serial.println(F("e - overrun error details"));
Serial.println(F("r - record data")); Serial.println(F("r - record data"));


while(!Serial.available()) {} while(!Serial.available()) {}
char c = tolower(Serial.read()); char c = tolower(Serial.read());
// Discard extra Serial data. // Discard extra Serial data.
do { do {
delay(10); delay(10);
} while (Serial.read() >= 0); } while (Serial.read() >= 0);
if (ERROR_LED_PIN >= 0) { if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, LOW); digitalWrite(ERROR_LED_PIN, LOW);
} }
binaryToCsv(); binaryToCsv();
} else if (c == 'd') { } else if (c == 'd') {
dumpData(); dumpData();
} else if (c == 'e') {
} else if (c == 'e') {
checkOverrun(); checkOverrun();
} else if (c == 'r') { } else if (c == 'r') {
logData(); logData();

+ 7
- 4
SdFat/examples/OpenNext/OpenNext.ino View File

/* /*
* Print size, modify date/time, and name for all files in root. * Print size, modify date/time, and name for all files in root.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD chip select pin // SD chip select pin
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo
delay(1000); delay(1000);
Serial.println();

// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}


// open next file in root. The volume working directory, vwd, is root // open next file in root. The volume working directory, vwd, is root
while (file.openNext(sd.vwd(), O_READ)) { while (file.openNext(sd.vwd(), O_READ)) {
file.printFileSize(&Serial); file.printFileSize(&Serial);
Serial.write(' '); Serial.write(' ');
file.printModifyDateTime(&Serial); file.printModifyDateTime(&Serial);
Serial.write(' ');
Serial.write(' ');
file.printName(&Serial); file.printName(&Serial);
if (file.isDir()) { if (file.isDir()) {
// Indicate a directory. // Indicate a directory.

+ 41
- 35
SdFat/examples/PrintBenchmark/PrintBenchmark.ino View File

/* /*
* This sketch is a simple Print benchmark.
* This program is a simple Print benchmark.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
#include <SdFatUtil.h> #include <SdFatUtil.h>


while (Serial.read() >= 0) { while (Serial.read() >= 0) {
} }
// pstr stores strings in flash to save RAM // pstr stores strings in flash to save RAM
cout << pstr("Type any character to start\n");
cout << F("Type any character to start\n");
while (Serial.read() <= 0) { while (Serial.read() <= 0) {
} }
delay(400); // catch Due reset problem delay(400); // catch Due reset problem


cout << pstr("Free RAM: ") << FreeRam() << endl;
cout << F("Free RAM: ") << FreeRam() << endl;


// initialize the SD card at SPI_FULL_SPEED for best performance. // initialize the SD card at SPI_FULL_SPEED for best performance.
// try SPI_HALF_SPEED if bus errors occur. // try SPI_HALF_SPEED if bus errors occur.
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) {
sd.initErrorHalt();
}


cout << pstr("Type is FAT") << int(sd.vol()->fatType()) << endl;
cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl;


cout << pstr("Starting print test. Please wait.\n\n");
cout << F("Starting print test. Please wait.\n\n");


// do write test // do write test
for (int test = 0; test < 6; test++) { for (int test = 0; test < 6; test++) {
char fileName[13] = "BENCH0.TXT";
char fileName[13] = "bench0.txt";
fileName[5] = '0' + test; fileName[5] = '0' + test;
// open or create file - truncate existing file. // open or create file - truncate existing file.
if (!file.open(fileName, O_CREAT | O_TRUNC | O_RDWR)) { if (!file.open(fileName, O_CREAT | O_TRUNC | O_RDWR)) {
totalLatency = 0; totalLatency = 0;
switch(test) { switch(test) {
case 0: case 0:
cout << pstr("Test of println(uint16_t)\n");
cout << F("Test of println(uint16_t)\n");
break; break;


case 1: case 1:
cout << pstr("Test of printField(uint16_t, char)\n");
cout << F("Test of printField(uint16_t, char)\n");
break; break;


case 2: case 2:
cout << pstr("Test of println(uint32_t)\n");
cout << F("Test of println(uint32_t)\n");
break; break;
case 3: case 3:
cout << pstr("Test of printField(uint32_t, char)\n");
break;
cout << F("Test of printField(uint32_t, char)\n");
break;
case 4: case 4:
cout << pstr("Test of println(float)\n");
break;
cout << F("Test of println(float)\n");
break;
case 5: case 5:
cout << pstr("Test of printField(float, char)\n");
break;
cout << F("Test of printField(float, char)\n");
break;
} }
uint32_t t = millis(); uint32_t t = millis();
for (uint16_t i = 0; i < N_PRINT; i++) { for (uint16_t i = 0; i < N_PRINT; i++) {
uint32_t m = micros(); uint32_t m = micros();


case 2: case 2:
file.println(12345678UL + i); file.println(12345678UL + i);
break;
break;
case 3: case 3:
file.printField(12345678UL + i, '\n'); file.printField(12345678UL + i, '\n');
break;
break;
case 4: case 4:
file.println((float)0.01*i); file.println((float)0.01*i);
break; break;
case 5: case 5:
file.printField((float)0.01*i, '\n'); file.printField((float)0.01*i, '\n');
break; break;
error("write failed"); error("write failed");
} }
m = micros() - m; m = micros() - m;
if (maxLatency < m) maxLatency = m;
if (minLatency > m) minLatency = m;
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m; totalLatency += m;
} }
file.close(); file.close();
t = millis() - t; t = millis() - t;
double s = file.fileSize(); double s = file.fileSize();
cout << pstr("Time ") << 0.001*t << pstr(" sec\n");
cout << pstr("File size ") << 0.001*s << pstr(" KB\n");
cout << pstr("Write ") << s/t << pstr(" KB/sec\n");
cout << pstr("Maximum latency: ") << maxLatency;
cout << pstr(" usec, Minimum Latency: ") << minLatency;
cout << pstr(" usec, Avg Latency: ");
cout << totalLatency/N_PRINT << pstr(" usec\n\n");
cout << F("Time ") << 0.001*t << F(" sec\n");
cout << F("File size ") << 0.001*s << F(" KB\n");
cout << F("Write ") << s/t << F(" KB/sec\n");
cout << F("Maximum latency: ") << maxLatency;
cout << F(" usec, Minimum Latency: ") << minLatency;
cout << F(" usec, Avg Latency: ");
cout << totalLatency/N_PRINT << F(" usec\n\n");
} }
cout << pstr("Done!\n\n");
cout << F("Done!\n\n");
} }

+ 62
- 60
SdFat/examples/QuickStart/QuickStart.ino View File

// Quick hardware test. // Quick hardware test.
// //
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
// //
// Set DISABLE_CHIP_SELECT to disable a second SPI device. // Set DISABLE_CHIP_SELECT to disable a second SPI device.
int chipSelect; int chipSelect;


void cardOrSpeed() { void cardOrSpeed() {
cout << pstr("Try another SD card or reduce the SPI bus speed.\n");
cout << pstr("Edit spiSpeed in this sketch to change it.\n");
cout << F("Try another SD card or reduce the SPI bus speed.\n");
cout << F("Edit spiSpeed in this program to change it.\n");
} }


void reformatMsg() { void reformatMsg() {
cout << pstr("Try reformatting the card. For best results use\n");
cout << pstr("the SdFormatter sketch in SdFat/examples or download\n");
cout << pstr("and use SDFormatter from www.sdcard.org/downloads.\n");
cout << F("Try reformatting the card. For best results use\n");
cout << F("the SdFormatter program in SdFat/examples or download\n");
cout << F("and use SDFormatter from www.sdcard.org/downloads.\n");
} }


void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // Wait for Leonardo. while (!Serial) {} // Wait for Leonardo.
cout << pstr("\nSPI pins:\n");
cout << pstr("MISO: ") << int(MISO) << endl;
cout << pstr("MOSI: ") << int(MOSI) << endl;
cout << pstr("SCK: ") << int(SCK) << endl;
cout << pstr("SS: ") << int(SS) << endl;
cout << F("\nSPI pins:\n");
cout << F("MISO: ") << int(MISO) << endl;
cout << F("MOSI: ") << int(MOSI) << endl;
cout << F("SCK: ") << int(SCK) << endl;
cout << F("SS: ") << int(SS) << endl;
if (DISABLE_CHIP_SELECT < 0) { if (DISABLE_CHIP_SELECT < 0) {
cout << pstr(
"\nBe sure to edit DISABLE_CHIP_SELECT if you have\n"
"a second SPI device. For example, with the Ethernet\n"
"shield, DISABLE_CHIP_SELECT should be set to 10\n"
"to disable the Ethernet controller.\n");
cout << F(
"\nBe sure to edit DISABLE_CHIP_SELECT if you have\n"
"a second SPI device. For example, with the Ethernet\n"
"shield, DISABLE_CHIP_SELECT should be set to 10\n"
"to disable the Ethernet controller.\n");
} }
cout << pstr(
"\nSD chip select is the key hardware option.\n"
"Common values are:\n"
"Arduino Ethernet shield, pin 4\n"
"Sparkfun SD shield, pin 8\n"
"Adafruit SD shields and modules, pin 10\n");
cout << F(
"\nSD chip select is the key hardware option.\n"
"Common values are:\n"
"Arduino Ethernet shield, pin 4\n"
"Sparkfun SD shield, pin 8\n"
"Adafruit SD shields and modules, pin 10\n");
} }


bool firstTry = true; bool firstTry = true;
// read any existing Serial data // read any existing Serial data
while (Serial.read() >= 0) {} while (Serial.read() >= 0) {}


if (!firstTry) cout << pstr("\nRestarting\n");
if (!firstTry) {
cout << F("\nRestarting\n");
}
firstTry = false; firstTry = false;


cout << pstr("\nEnter the chip select pin number: ");
cout << F("\nEnter the chip select pin number: ");
while (!Serial.available()) {} while (!Serial.available()) {}
delay(400); // catch Due restart problem
delay(400); // catch Due restart problem
cin.readline(); cin.readline();
if (cin >> chipSelect) { if (cin >> chipSelect) {
cout << chipSelect << endl; cout << chipSelect << endl;
} else { } else {
cout << pstr("\nInvalid pin number\n");
cout << F("\nInvalid pin number\n");
return; return;
} }
if (DISABLE_CHIP_SELECT < 0) { if (DISABLE_CHIP_SELECT < 0) {
cout << pstr(
"\nAssuming the SD is the only SPI device.\n"
"Edit DISABLE_CHIP_SELECT to disable another device.\n");
cout << F(
"\nAssuming the SD is the only SPI device.\n"
"Edit DISABLE_CHIP_SELECT to disable another device.\n");
} else { } else {
cout << pstr("\nDisabling SPI device on pin ");
cout << F("\nDisabling SPI device on pin ");
cout << int(DISABLE_CHIP_SELECT) << endl; cout << int(DISABLE_CHIP_SELECT) << endl;
pinMode(DISABLE_CHIP_SELECT, OUTPUT); pinMode(DISABLE_CHIP_SELECT, OUTPUT);
digitalWrite(DISABLE_CHIP_SELECT, HIGH); digitalWrite(DISABLE_CHIP_SELECT, HIGH);
} }
if (!sd.begin(chipSelect, spiSpeed)) { if (!sd.begin(chipSelect, spiSpeed)) {
if (sd.card()->errorCode()) { if (sd.card()->errorCode()) {
cout << pstr(
"\nSD initialization failed.\n"
"Do not reformat the card!\n"
"Is the card correctly inserted?\n"
"Is chipSelect set to the correct value?\n"
"Does another SPI device need to be disabled?\n"
"Is there a wiring/soldering problem?\n");
cout << pstr("\nerrorCode: ") << hex << showbase;
cout << F(
"\nSD initialization failed.\n"
"Do not reformat the card!\n"
"Is the card correctly inserted?\n"
"Is chipSelect set to the correct value?\n"
"Does another SPI device need to be disabled?\n"
"Is there a wiring/soldering problem?\n");
cout << F("\nerrorCode: ") << hex << showbase;
cout << int(sd.card()->errorCode()); cout << int(sd.card()->errorCode());
cout << pstr(", errorData: ") << int(sd.card()->errorData());
cout << F(", errorData: ") << int(sd.card()->errorData());
cout << dec << noshowbase << endl; cout << dec << noshowbase << endl;
return; return;
} }
cout << pstr("\nCard successfully initialized.\n");
cout << F("\nCard successfully initialized.\n");
if (sd.vol()->fatType() == 0) { if (sd.vol()->fatType() == 0) {
cout << pstr("Can't find a valid FAT16/FAT32 partition.\n");
cout << F("Can't find a valid FAT16/FAT32 partition.\n");
reformatMsg(); reformatMsg();
return; return;
}
}
if (!sd.vwd()->isOpen()) { if (!sd.vwd()->isOpen()) {
cout << pstr("Can't open root directory.\n");
cout << F("Can't open root directory.\n");
reformatMsg(); reformatMsg();
return; return;
}
cout << pstr("Can't determine error type\n");
}
cout << F("Can't determine error type\n");
return; return;
} }
cout << pstr("\nCard successfully initialized.\n");
cout << F("\nCard successfully initialized.\n");
cout << endl; cout << endl;


uint32_t size = sd.card()->cardSize(); uint32_t size = sd.card()->cardSize();
if (size == 0) { if (size == 0) {
cout << pstr("Can't determine the card size.\n");
cout << F("Can't determine the card size.\n");
cardOrSpeed(); cardOrSpeed();
return; return;
} }
uint32_t sizeMB = 0.000512 * size + 0.5; uint32_t sizeMB = 0.000512 * size + 0.5;
cout << pstr("Card size: ") << sizeMB;
cout << pstr(" MB (MB = 1,000,000 bytes)\n");
cout << F("Card size: ") << sizeMB;
cout << F(" MB (MB = 1,000,000 bytes)\n");
cout << endl; cout << endl;
cout << pstr("Volume is FAT") << int(sd.vol()->fatType());
cout << pstr(", Cluster size (bytes): ") << 512L * sd.vol()->blocksPerCluster();
cout << F("Volume is FAT") << int(sd.vol()->fatType());
cout << F(", Cluster size (bytes): ") << 512L * sd.vol()->blocksPerCluster();
cout << endl << endl; cout << endl << endl;


cout << pstr("Files found (name date time size):\n");
cout << F("Files found (date time size name):\n");
sd.ls(LS_R | LS_DATE | LS_SIZE); sd.ls(LS_R | LS_DATE | LS_SIZE);


if ((sizeMB > 1100 && sd.vol()->blocksPerCluster() < 64) if ((sizeMB > 1100 && sd.vol()->blocksPerCluster() < 64)
|| (sizeMB < 2200 && sd.vol()->fatType() == 32)) {
cout << pstr("\nThis card should be reformatted for best performance.\n");
cout << pstr("Use a cluster size of 32 KB for cards larger than 1 GB.\n");
cout << pstr("Only cards larger than 2 GB should be formatted FAT32.\n");
|| (sizeMB < 2200 && sd.vol()->fatType() == 32)) {
cout << F("\nThis card should be reformatted for best performance.\n");
cout << F("Use a cluster size of 32 KB for cards larger than 1 GB.\n");
cout << F("Only cards larger than 2 GB should be formatted FAT32.\n");
reformatMsg(); reformatMsg();
return; return;
} }
// read any existing Serial data // read any existing Serial data
while (Serial.read() >= 0) {} while (Serial.read() >= 0) {}
cout << pstr("\nSuccess! Type any character to restart.\n");
cout << F("\nSuccess! Type any character to restart.\n");
while (Serial.read() < 0) {} while (Serial.read() < 0) {}
} }

+ 30
- 25
SdFat/examples/RawWrite/RawWrite.ino View File

/* /*
* This sketch illustrates raw write functions in SdFat that
* can be used for high speed data logging.
* This program illustrates raw write functions in SdFat that
* can be used for high speed data logging.
* *
* This sketch simulates logging from a source that produces
* This program simulates logging from a source that produces
* data at a constant rate of one block every MICROS_PER_BLOCK. * data at a constant rate of one block every MICROS_PER_BLOCK.
* *
* If a high quality SanDisk card is used with this sketch
* If a high quality SanDisk card is used with this program
* no overruns occur and the maximum block write time is * no overruns occur and the maximum block write time is
* under 2000 micros. * under 2000 micros.
* *
* a few seconds to erase a 500 MB file since the card only * a few seconds to erase a 500 MB file since the card only
* marks the blocks as erased; no data transfer is required. * marks the blocks as erased; no data transfer is required.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
#include <SdFatUtil.h> #include <SdFatUtil.h>


void loop(void) { void loop(void) {
while (Serial.read() >= 0) {} while (Serial.read() >= 0) {}
// pstr stores strings in flash to save RAM // pstr stores strings in flash to save RAM
cout << pstr("Type any character to start\n");
cout << F("Type any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem


cout << pstr("Free RAM: ") << FreeRam() << endl;
cout << F("Free RAM: ") << FreeRam() << endl;


// initialize the SD card at SPI_FULL_SPEED for best performance. // initialize the SD card at SPI_FULL_SPEED for best performance.
// try SPI_HALF_SPEED if bus errors occur. // try SPI_HALF_SPEED if bus errors occur.
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) {
sd.initErrorHalt();
}


// delete possible existing file // delete possible existing file
sd.remove("RAW.TXT");
sd.remove("RawWrite.txt");


// create a contiguous file // create a contiguous file
if (!file.createContiguous(sd.vwd(), "RAW.TXT", 512UL*BLOCK_COUNT)) {
if (!file.createContiguous(sd.vwd(), "RawWrite.txt", 512UL*BLOCK_COUNT)) {
error("createContiguous failed"); error("createContiguous failed");
} }
// get the location of the file's blocks // get the location of the file's blocks
pCache[i + 63] = '\n'; pCache[i + 63] = '\n';
} }


cout << pstr("Start raw write of ") << file.fileSize() << pstr(" bytes at\n");
cout << 512000000UL/MICROS_PER_BLOCK << pstr(" bytes per second\n");
cout << pstr("Please wait ") << (BLOCK_COUNT*MICROS_PER_BLOCK)/1000000UL;
cout << pstr(" seconds\n");
cout << F("Start raw write of ") << file.fileSize() << F(" bytes at\n");
cout << 512000000UL/MICROS_PER_BLOCK << F(" bytes per second\n");
cout << F("Please wait ") << (BLOCK_COUNT*MICROS_PER_BLOCK)/1000000UL;
cout << F(" seconds\n");


// tell card to setup for multiple block write with pre-erase // tell card to setup for multiple block write with pre-erase
if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) { if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) {


// put block number at start of first line in block // put block number at start of first line in block
uint32_t n = b; uint32_t n = b;
for (int8_t d = 5; d >= 0; d--){
for (int8_t d = 5; d >= 0; d--) {
pCache[d] = n || d == 5 ? n % 10 + '0' : ' '; pCache[d] = n || d == 5 ? n % 10 + '0' : ' ';
n /= 10; n /= 10;
} }
// write a 512 byte block // write a 512 byte block
uint32_t tw = micros(); uint32_t tw = micros();
if (!sd.card()->writeData(pCache)) error("writeData failed");
if (!sd.card()->writeData(pCache)) {
error("writeData failed");
}
tw = micros() - tw; tw = micros() - tw;


// check for max write time // check for max write time
overruns++; overruns++;
// advance time to reflect overrun // advance time to reflect overrun
tNext = micros(); tNext = micros();
}
else {
} else {
// wait for time to write next block // wait for time to write next block
while(micros() < tNext); while(micros() < tNext);
} }
t = micros() - t; t = micros() - t;


// end multiple block write mode // end multiple block write mode
if (!sd.card()->writeStop()) error("writeStop failed");
if (!sd.card()->writeStop()) {
error("writeStop failed");
}


cout << pstr("Done\n");
cout << pstr("Elapsed time: ") << setprecision(3)<< 1.e-6*t;
cout << pstr(" seconds\n");
cout << pstr("Max write time: ") << maxWriteTime << pstr(" micros\n");
cout << pstr("Overruns: ") << overruns << endl;
cout << F("Done\n");
cout << F("Elapsed time: ") << setprecision(3)<< 1.e-6*t;
cout << F(" seconds\n");
cout << F("Max write time: ") << maxWriteTime << F(" micros\n");
cout << F("Overruns: ") << overruns << endl;
if (overruns) { if (overruns) {
uint8_t n = overruns > OVER_DIM ? OVER_DIM : overruns; uint8_t n = overruns > OVER_DIM ? OVER_DIM : overruns;
cout << pstr("fileBlock,micros") << endl;
cout << F("fileBlock,micros") << endl;
for (uint8_t i = 0; i < n; i++) { for (uint8_t i = 0; i < n; i++) {
cout << over[i].block << ',' << over[i].micros << endl; cout << over[i].block << ',' << over[i].micros << endl;
} }

+ 88
- 0
SdFat/examples/ReadWrite/ReadWrite.ino View File

/*
SD card read/write

This example shows how to read and write data to and from an SD card file
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4

created Nov 2010
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe

This example code is in the public domain.

*/
#define SD_CS_PIN SS
#include <SPI.h>
//#include <SD.h>
#include <SdFat.h>
SdFat SD;

File myFile;

void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}


Serial.print("Initializing SD card...");
// On the Ethernet Shield, CS is pin 4. It's set as an output by default.
// Note that even if it's not used as the CS pin, the hardware SS pin
// (10 on most Arduino boards, 53 on the Mega) must be left as an output
// or the SD library functions will not work.
pinMode(10, OUTPUT);

if (!SD.begin(SD_CS_PIN)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");

// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open("test.txt", FILE_WRITE);

// if the file opened okay, write to it:
if (myFile) {
Serial.print("Writing to test.txt...");
myFile.println("testing 1, 2, 3.");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}

// re-open the file for reading:
myFile = SD.open("test.txt");
if (myFile) {
Serial.println("test.txt:");

// read from the file until there's nothing else in it:
while (myFile.available()) {
Serial.write(myFile.read());
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
}

void loop()
{
// nothing happens after setup
}



+ 12
- 8
SdFat/examples/ReadWriteSdFat/ReadWriteSdFat.ino View File

const int chipSelect = 4; const int chipSelect = 4;
/* /*
SD card read/write SD card read/write
This example shows how to read and write data to and from an SD card file
This example shows how to read and write data to and from an SD card file
The circuit: The circuit:
* SD card attached to SPI bus as follows: * SD card attached to SPI bus as follows:
** MOSI - pin 11 ** MOSI - pin 11
** MISO - pin 12 ** MISO - pin 12
** CLK - pin 13 ** CLK - pin 13
** CS - pin 4 ** CS - pin 4
created Nov 2010 created Nov 2010
by David A. Mellis by David A. Mellis
updated 2 Dec 2010 updated 2 Dec 2010
by Tom Igoe by Tom Igoe
modified by Bill Greiman 11 Apr 2011 modified by Bill Greiman 11 Apr 2011
This example code is in the public domain. This example code is in the public domain.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
SdFat sd; SdFat sd;
SdFile myFile; SdFile myFile;
Serial.println("Type any character to start"); Serial.println("Type any character to start");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem
// Initialize SdFat or print a detailed error message and halt // Initialize SdFat or print a detailed error message and halt
// Use half speed like the native library. // Use half speed like the native library.
// change to SPI_FULL_SPEED for more performance. // change to SPI_FULL_SPEED for more performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}


// open the file for write at end like the Native SD library // open the file for write at end like the Native SD library
if (!myFile.open("test.txt", O_RDWR | O_CREAT | O_AT_END)) { if (!myFile.open("test.txt", O_RDWR | O_CREAT | O_AT_END)) {


// read from the file until there's nothing else in it: // read from the file until there's nothing else in it:
int data; int data;
while ((data = myFile.read()) >= 0) Serial.write(data);
while ((data = myFile.read()) >= 0) {
Serial.write(data);
}
// close the file: // close the file:
myFile.close(); myFile.close();
} }

+ 101
- 77
SdFat/examples/SdFormatter/SdFormatter.ino View File

/* /*
* This sketch will format an SD or SDHC card.
* This program will format an SD or SDHC card.
* Warning all data will be deleted! * Warning all data will be deleted!
* *
* For SD/SDHC cards larger than 64 MB this * For SD/SDHC cards larger than 64 MB this
* sketch attempts to match the format
* program attempts to match the format
* generated by SDFormatter available here: * generated by SDFormatter available here:
* *
* http://www.sdcard.org/consumers/formatter/ * http://www.sdcard.org/consumers/formatter/
* *
* For smaller cards this sketch uses FAT16
* For smaller cards this program uses FAT16
* and SDFormatter uses FAT12. * and SDFormatter uses FAT12.
*/ */
// Print extra info for debug if DEBUG_PRINT is nonzero // Print extra info for debug if DEBUG_PRINT is nonzero
#define DEBUG_PRINT 0 #define DEBUG_PRINT 0
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
#if DEBUG_PRINT #if DEBUG_PRINT
#include <SdFatUtil.h> #include <SdFatUtil.h>
char fat16str[] = "FAT16 "; char fat16str[] = "FAT16 ";
char fat32str[] = "FAT32 "; char fat32str[] = "FAT32 ";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#define sdError(msg) sdError_P(PSTR(msg))
#define sdError(msg) sdError_F(F(msg))


void sdError_P(const char* str) {
cout << pstr("error: ");
cout << pgm(str) << endl;
void sdError_F(const __FlashStringHelper* str) {
cout << F("error: ");
cout << str << endl;
if (card.errorCode()) { if (card.errorCode()) {
cout << pstr("SD error: ") << hex << int(card.errorCode());
cout << F("SD error: ") << hex << int(card.errorCode());
cout << ',' << int(card.errorData()) << dec << endl; cout << ',' << int(card.errorData()) << dec << endl;
} }
while (1); while (1);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if DEBUG_PRINT #if DEBUG_PRINT
void debugPrint() { void debugPrint() {
cout << pstr("FreeRam: ") << FreeRam() << endl;
cout << pstr("partStart: ") << relSector << endl;
cout << pstr("partSize: ") << partSize << endl;
cout << pstr("reserved: ") << reservedSectors << endl;
cout << pstr("fatStart: ") << fatStart << endl;
cout << pstr("fatSize: ") << fatSize << endl;
cout << pstr("dataStart: ") << dataStart << endl;
cout << pstr("clusterCount: ");
cout << F("FreeRam: ") << FreeRam() << endl;
cout << F("partStart: ") << relSector << endl;
cout << F("partSize: ") << partSize << endl;
cout << F("reserved: ") << reservedSectors << endl;
cout << F("fatStart: ") << fatStart << endl;
cout << F("fatSize: ") << fatSize << endl;
cout << F("dataStart: ") << dataStart << endl;
cout << F("clusterCount: ");
cout << ((relSector + partSize - dataStart)/sectorsPerCluster) << endl; cout << ((relSector + partSize - dataStart)/sectorsPerCluster) << endl;
cout << endl; cout << endl;
cout << pstr("Heads: ") << int(numberOfHeads) << endl;
cout << pstr("Sectors: ") << int(sectorsPerTrack) << endl;
cout << pstr("Cylinders: ");
cout << F("Heads: ") << int(numberOfHeads) << endl;
cout << F("Sectors: ") << int(sectorsPerTrack) << endl;
cout << F("Cylinders: ");
cout << cardSizeBlocks/(numberOfHeads*sectorsPerTrack) << endl; cout << cardSizeBlocks/(numberOfHeads*sectorsPerTrack) << endl;
} }
#endif // DEBUG_PRINT #endif // DEBUG_PRINT
sectorsPerCluster = 128; sectorsPerCluster = 128;
} }


cout << pstr("Blocks/Cluster: ") << int(sectorsPerCluster) << endl;
cout << F("Blocks/Cluster: ") << int(sectorsPerCluster) << endl;
// set fake disk geometry // set fake disk geometry
sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63; sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63;


sdError("Clear FAT/DIR writeStart failed"); sdError("Clear FAT/DIR writeStart failed");
} }
for (uint32_t i = 0; i < count; i++) { for (uint32_t i = 0; i < count; i++) {
if ((i & 0XFF) == 0) cout << '.';
if ((i & 0XFF) == 0) {
cout << '.';
}
if (!card.writeData(cache.data)) { if (!card.writeData(cache.data)) {
sdError("Clear FAT/DIR writeData failed"); sdError("Clear FAT/DIR writeData failed");
} }
part_t* p = cache.mbr.part; part_t* p = cache.mbr.part;
p->boot = 0; p->boot = 0;
uint16_t c = lbnToCylinder(relSector); uint16_t c = lbnToCylinder(relSector);
if (c > 1023) sdError("MBR CHS");
if (c > 1023) {
sdError("MBR CHS");
}
p->beginCylinderHigh = c >> 8; p->beginCylinderHigh = c >> 8;
p->beginCylinderLow = c & 0XFF; p->beginCylinderLow = c & 0XFF;
p->beginHead = lbnToHead(relSector); p->beginHead = lbnToHead(relSector);
} }
p->firstSector = relSector; p->firstSector = relSector;
p->totalSectors = partSize; p->totalSectors = partSize;
if (!writeCache(0)) sdError("write MBR");
if (!writeCache(0)) {
sdError("write MBR");
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// generate serial number from card size and micros since boot // generate serial number from card size and micros since boot
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster; nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
fatSize = (nc + 2 + 255)/256; fatSize = (nc + 2 + 255)/256;
uint32_t r = BU16 + 1 + 2 * fatSize + 32; uint32_t r = BU16 + 1 + 2 * fatSize + 32;
if (dataStart < r) continue;
if (dataStart < r) {
continue;
}
relSector = dataStart - r + BU16; relSector = dataStart - r + BU16;
break; break;
} }
// check valid cluster count for FAT16 volume // check valid cluster count for FAT16 volume
if (nc < 4085 || nc >= 65525) sdError("Bad cluster count");
if (nc < 4085 || nc >= 65525) {
sdError("Bad cluster count");
}
reservedSectors = 1; reservedSectors = 1;
fatStart = relSector + reservedSectors; fatStart = relSector + reservedSectors;
partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32; partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32;
cache.fat16[1] = 0XFFFF; cache.fat16[1] = 0XFFFF;
// write first block of FAT and backup for reserved clusters // write first block of FAT and backup for reserved clusters
if (!writeCache(fatStart) if (!writeCache(fatStart)
|| !writeCache(fatStart + fatSize)) {
|| !writeCache(fatStart + fatSize)) {
sdError("FAT16 reserve failed"); sdError("FAT16 reserve failed");
} }
} }
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster; nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
fatSize = (nc + 2 + 127)/128; fatSize = (nc + 2 + 127)/128;
uint32_t r = relSector + 9 + 2 * fatSize; uint32_t r = relSector + 9 + 2 * fatSize;
if (dataStart >= r) break;
if (dataStart >= r) {
break;
}
} }
// error if too few clusters in FAT32 volume // error if too few clusters in FAT32 volume
if (nc < 65525) sdError("Bad cluster count");
if (nc < 65525) {
sdError("Bad cluster count");
}
reservedSectors = dataStart - relSector - 2 * fatSize; reservedSectors = dataStart - relSector - 2 * fatSize;
fatStart = relSector + reservedSectors; fatStart = relSector + reservedSectors;
partSize = nc * sectorsPerCluster + dataStart - relSector; partSize = nc * sectorsPerCluster + dataStart - relSector;
memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType)); memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType));
// write partition boot sector and backup // write partition boot sector and backup
if (!writeCache(relSector) if (!writeCache(relSector)
|| !writeCache(relSector + 6)) {
|| !writeCache(relSector + 6)) {
sdError("FAT32 write PBS failed"); sdError("FAT32 write PBS failed");
} }
clearCache(true); clearCache(true);
// write extra boot area and backup // write extra boot area and backup
if (!writeCache(relSector + 2) if (!writeCache(relSector + 2)
|| !writeCache(relSector + 8)) {
|| !writeCache(relSector + 8)) {
sdError("FAT32 PBS ext failed"); sdError("FAT32 PBS ext failed");
} }
fat32_fsinfo_t* pf = &cache.fsinfo; fat32_fsinfo_t* pf = &cache.fsinfo;
pf->nextFree = 0XFFFFFFFF; pf->nextFree = 0XFFFFFFFF;
// write FSINFO sector and backup // write FSINFO sector and backup
if (!writeCache(relSector + 1) if (!writeCache(relSector + 1)
|| !writeCache(relSector + 7)) {
|| !writeCache(relSector + 7)) {
sdError("FAT32 FSINFO failed"); sdError("FAT32 FSINFO failed");
} }
clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster); clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster);
cache.fat32[2] = 0x0FFFFFFF; cache.fat32[2] = 0x0FFFFFFF;
// write first block of FAT and backup for reserved clusters // write first block of FAT and backup for reserved clusters
if (!writeCache(fatStart) if (!writeCache(fatStart)
|| !writeCache(fatStart + fatSize)) {
|| !writeCache(fatStart + fatSize)) {
sdError("FAT32 reserve failed"); sdError("FAT32 reserve failed");
} }
} }
// flash erase all data // flash erase all data
uint32_t const ERASE_SIZE = 262144L; uint32_t const ERASE_SIZE = 262144L;
void eraseCard() { void eraseCard() {
cout << endl << pstr("Erasing\n");
cout << endl << F("Erasing\n");
uint32_t firstBlock = 0; uint32_t firstBlock = 0;
uint32_t lastBlock; uint32_t lastBlock;
uint16_t n = 0; uint16_t n = 0;


do { do {
lastBlock = firstBlock + ERASE_SIZE - 1; lastBlock = firstBlock + ERASE_SIZE - 1;
if (lastBlock >= cardSizeBlocks) lastBlock = cardSizeBlocks - 1;
if (!card.erase(firstBlock, lastBlock)) sdError("erase failed");
if (lastBlock >= cardSizeBlocks) {
lastBlock = cardSizeBlocks - 1;
}
if (!card.erase(firstBlock, lastBlock)) {
sdError("erase failed");
}
cout << '.'; cout << '.';
if ((n++)%32 == 31) cout << endl;
if ((n++)%32 == 31) {
cout << endl;
}
firstBlock += ERASE_SIZE; firstBlock += ERASE_SIZE;
} while (firstBlock < cardSizeBlocks); } while (firstBlock < cardSizeBlocks);
cout << endl; cout << endl;


if (!card.readBlock(0, cache.data)) sdError("readBlock");
if (!card.readBlock(0, cache.data)) {
sdError("readBlock");
}
cout << hex << showbase << setfill('0') << internal; cout << hex << showbase << setfill('0') << internal;
cout << pstr("All data set to ") << setw(4) << int(cache.data[0]) << endl;
cout << F("All data set to ") << setw(4) << int(cache.data[0]) << endl;
cout << dec << noshowbase << setfill(' ') << right; cout << dec << noshowbase << setfill(' ') << right;
cout << pstr("Erase done\n");
cout << F("Erase done\n");
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void formatCard() { void formatCard() {
cout << endl; cout << endl;
cout << pstr("Formatting\n");
cout << F("Formatting\n");
initSizes(); initSizes();
if (card.type() != SD_CARD_TYPE_SDHC) { if (card.type() != SD_CARD_TYPE_SDHC) {
cout << pstr("FAT16\n");
cout << F("FAT16\n");
makeFat16(); makeFat16();
} else { } else {
cout << pstr("FAT32\n");
cout << F("FAT32\n");
makeFat32(); makeFat32();
} }
#if DEBUG_PRINT #if DEBUG_PRINT
debugPrint(); debugPrint();
#endif // DEBUG_PRINT #endif // DEBUG_PRINT
cout << pstr("Format done\n");
cout << F("Format done\n");
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo


cout << pstr(
"\n"
"This sketch can erase and/or format SD/SDHC cards.\n"
"\n"
"Erase uses the card's fast flash erase command.\n"
"Flash erase sets all data to 0X00 for most cards\n"
"and 0XFF for a few vendor's cards.\n"
"\n"
"Cards larger than 2 GB will be formatted FAT32 and\n"
"smaller cards will be formatted FAT16.\n"
"\n"
"Warning, all data on the card will be erased.\n"
"Enter 'Y' to continue: ");
cout << F(
"\n"
"This program can erase and/or format SD/SDHC cards.\n"
"\n"
"Erase uses the card's fast flash erase command.\n"
"Flash erase sets all data to 0X00 for most cards\n"
"and 0XFF for a few vendor's cards.\n"
"\n"
"Cards larger than 2 GB will be formatted FAT32 and\n"
"smaller cards will be formatted FAT16.\n"
"\n"
"Warning, all data on the card will be erased.\n"
"Enter 'Y' to continue: ");
while (!Serial.available()) {} while (!Serial.available()) {}
delay(400); // catch Due restart problem delay(400); // catch Due restart problem
c = Serial.read(); c = Serial.read();
cout << c << endl; cout << c << endl;
if (c != 'Y') { if (c != 'Y') {
cout << pstr("Quiting, you did not enter 'Y'.\n");
cout << F("Quiting, you did not enter 'Y'.\n");
return; return;
} }
// read any existing Serial data // read any existing Serial data
while (Serial.read() >= 0) {} while (Serial.read() >= 0) {}
cout << pstr(
"\n"
"Options are:\n"
"E - erase the card and skip formatting.\n"
"F - erase and then format the card. (recommended)\n"
"Q - quick format the card without erase.\n"
"\n"
"Enter option: ");
cout << F(
"\n"
"Options are:\n"
"E - erase the card and skip formatting.\n"
"F - erase and then format the card. (recommended)\n"
"Q - quick format the card without erase.\n"
"\n"
"Enter option: ");
while (!Serial.available()) {} while (!Serial.available()) {}
c = Serial.read(); c = Serial.read();
cout << c << endl; cout << c << endl;
if (!strchr("EFQ", c)) { if (!strchr("EFQ", c)) {
cout << pstr("Quiting, invalid option entered.") << endl;
cout << F("Quiting, invalid option entered.") << endl;
return; return;
} }


if (!card.begin(chipSelect, spiSpeed)) { if (!card.begin(chipSelect, spiSpeed)) {
cout << pstr(
"\nSD initialization failure!\n"
"Is the SD card inserted correctly?\n"
"Is chip select correct at the top of this sketch?\n");
cout << F(
"\nSD initialization failure!\n"
"Is the SD card inserted correctly?\n"
"Is chip select correct at the top of this program?\n");
sdError("card.begin failed"); sdError("card.begin failed");
} }
cardSizeBlocks = card.cardSize(); cardSizeBlocks = card.cardSize();
if (cardSizeBlocks == 0) sdError("cardSize");
if (cardSizeBlocks == 0) {
sdError("cardSize");
}
cardCapacityMB = (cardSizeBlocks + 2047)/2048; cardCapacityMB = (cardSizeBlocks + 2047)/2048;


cout << pstr("Card Size: ") << cardCapacityMB;
cout << pstr(" MB, (MB = 1,048,576 bytes)") << endl;
cout << F("Card Size: ") << cardCapacityMB;
cout << F(" MB, (MB = 1,048,576 bytes)") << endl;


if (c == 'E' || c == 'F') { if (c == 'E' || c == 'F') {
eraseCard(); eraseCard();

+ 74
- 68
SdFat/examples/SdInfo/SdInfo.ino View File

/* /*
* This sketch attempts to initialize an SD card and analyze its structure.
* This program attempts to initialize an SD card and analyze its structure.
*/ */
#include <SPI.h> #include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
uint32_t eraseSize; uint32_t eraseSize;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// store error strings in flash // store error strings in flash
#define sdErrorMsg(msg) sdErrorMsg_P(PSTR(msg));
void sdErrorMsg_P(const char* str) {
cout << pgm(str) << endl;
#define sdErrorMsg(msg) sdErrorMsg_F(F(msg));
void sdErrorMsg_F(const __FlashStringHelper* str) {
cout << str << endl;
if (sd.card()->errorCode()) { if (sd.card()->errorCode()) {
cout << pstr("SD errorCode: ");
cout << F("SD errorCode: ");
cout << hex << int(sd.card()->errorCode()) << endl; cout << hex << int(sd.card()->errorCode()) << endl;
cout << pstr("SD errorData: ");
cout << F("SD errorData: ");
cout << int(sd.card()->errorData()) << dec << endl; cout << int(sd.card()->errorData()) << dec << endl;
} }
} }
sdErrorMsg("readCID failed"); sdErrorMsg("readCID failed");
return false; return false;
} }
cout << pstr("\nManufacturer ID: ");
cout << F("\nManufacturer ID: ");
cout << hex << int(cid.mid) << dec << endl; cout << hex << int(cid.mid) << dec << endl;
cout << pstr("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
cout << pstr("Product: ");
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
cout << F("Product: ");
for (uint8_t i = 0; i < 5; i++) { for (uint8_t i = 0; i < 5; i++) {
cout << cid.pnm[i]; cout << cid.pnm[i];
} }
cout << pstr("\nVersion: ");
cout << F("\nVersion: ");
cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl; cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
cout << pstr("Serial number: ") << hex << cid.psn << dec << endl;
cout << pstr("Manufacturing date: ");
cout << F("Serial number: ") << hex << cid.psn << dec << endl;
cout << F("Manufacturing date: ");
cout << int(cid.mdt_month) << '/'; cout << int(cid.mdt_month) << '/';
cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl; cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
cout << endl; cout << endl;
eraseSingleBlock = csd.v2.erase_blk_en; eraseSingleBlock = csd.v2.erase_blk_en;
eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low; eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low;
} else { } else {
cout << pstr("csd version error\n");
cout << F("csd version error\n");
return false; return false;
} }
eraseSize++; eraseSize++;
cout << pstr("cardSize: ") << 0.000512*cardSize;
cout << pstr(" MB (MB = 1,000,000 bytes)\n");
cout << F("cardSize: ") << 0.000512*cardSize;
cout << F(" MB (MB = 1,000,000 bytes)\n");


cout << pstr("flashEraseSize: ") << int(eraseSize) << pstr(" blocks\n");
cout << pstr("eraseSingleBlock: ");
cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
cout << F("eraseSingleBlock: ");
if (eraseSingleBlock) { if (eraseSingleBlock) {
cout << pstr("true\n");
cout << F("true\n");
} else { } else {
cout << pstr("false\n");
cout << F("false\n");
} }
return true; return true;
} }
return false; return false;
} }
if (!sd.card()->readBlock(0, p->data)) { if (!sd.card()->readBlock(0, p->data)) {
sdErrorMsg("read MBR failed");
return false;
sdErrorMsg("read MBR failed");
return false;
} }
for (uint8_t ip = 1; ip < 5; ip++) { for (uint8_t ip = 1; ip < 5; ip++) {
part_t *pt = &p->mbr.part[ip - 1]; part_t *pt = &p->mbr.part[ip - 1];
if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) { if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) {
cout << pstr("\nNo MBR. Assuming Super Floppy format.\n");
cout << F("\nNo MBR. Assuming Super Floppy format.\n");
return true; return true;
} }
} }
cout << pstr("\nSD Partition Table\n");
cout << pstr("part,boot,type,start,length\n");
cout << F("\nSD Partition Table\n");
cout << F("part,boot,type,start,length\n");
for (uint8_t ip = 1; ip < 5; ip++) { for (uint8_t ip = 1; ip < 5; ip++) {
part_t *pt = &p->mbr.part[ip - 1]; part_t *pt = &p->mbr.part[ip - 1];
cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type); cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void volDmp() { void volDmp() {
cout << pstr("\nVolume is FAT") << int(sd.vol()->fatType()) << endl;
cout << pstr("blocksPerCluster: ") << int(sd.vol()->blocksPerCluster()) << endl;
cout << pstr("clusterCount: ") << sd.vol()->clusterCount() << endl;
cout << pstr("freeClusters: ");
cout << F("\nVolume is FAT") << int(sd.vol()->fatType()) << endl;
cout << F("blocksPerCluster: ") << int(sd.vol()->blocksPerCluster()) << endl;
cout << F("clusterCount: ") << sd.vol()->clusterCount() << endl;
cout << F("freeClusters: ");
uint32_t volFree = sd.vol()->freeClusterCount(); uint32_t volFree = sd.vol()->freeClusterCount();
cout << volFree << endl; cout << volFree << endl;
float fs = 0.000512*volFree*sd.vol()->blocksPerCluster(); float fs = 0.000512*volFree*sd.vol()->blocksPerCluster();
cout << pstr("freeSpace: ") << fs << pstr(" MB (MB = 1,000,000 bytes)\n");
cout << pstr("fatStartBlock: ") << sd.vol()->fatStartBlock() << endl;
cout << pstr("fatCount: ") << int(sd.vol()->fatCount()) << endl;
cout << pstr("blocksPerFat: ") << sd.vol()->blocksPerFat() << endl;
cout << pstr("rootDirStart: ") << sd.vol()->rootDirStart() << endl;
cout << pstr("dataStartBlock: ") << sd.vol()->dataStartBlock() << endl;
cout << F("freeSpace: ") << fs << F(" MB (MB = 1,000,000 bytes)\n");
cout << F("fatStartBlock: ") << sd.vol()->fatStartBlock() << endl;
cout << F("fatCount: ") << int(sd.vol()->fatCount()) << endl;
cout << F("blocksPerFat: ") << sd.vol()->blocksPerFat() << endl;
cout << F("rootDirStart: ") << sd.vol()->rootDirStart() << endl;
cout << F("dataStartBlock: ") << sd.vol()->dataStartBlock() << endl;
if (sd.vol()->dataStartBlock() % eraseSize) { if (sd.vol()->dataStartBlock() % eraseSize) {
cout << pstr("Data area is not aligned on flash erase boundaries!\n");
cout << pstr("Download and use formatter from www.sdsd.card()->org/consumer!\n");
cout << F("Data area is not aligned on flash erase boundaries!\n");
cout << F("Download and use formatter from www.sdsd.card()->org/consumer!\n");
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
cout << uppercase << showbase << endl; cout << uppercase << showbase << endl;


// pstr stores strings in flash to save RAM // pstr stores strings in flash to save RAM
cout << pstr("SdFat version: ") << SD_FAT_VERSION << endl;
cout << F("SdFat version: ") << SD_FAT_VERSION << endl;
if (DISABLE_CHIP_SELECT < 0) { if (DISABLE_CHIP_SELECT < 0) {
cout << pstr(
"\nAssuming the SD is the only SPI device.\n"
"Edit DISABLE_CHIP_SELECT to disable another device.\n");
cout << F(
"\nAssuming the SD is the only SPI device.\n"
"Edit DISABLE_CHIP_SELECT to disable another device.\n");
} else { } else {
cout << pstr("\nDisabling SPI device on pin ");
cout << F("\nDisabling SPI device on pin ");
cout << int(DISABLE_CHIP_SELECT) << endl; cout << int(DISABLE_CHIP_SELECT) << endl;
pinMode(DISABLE_CHIP_SELECT, OUTPUT); pinMode(DISABLE_CHIP_SELECT, OUTPUT);
digitalWrite(DISABLE_CHIP_SELECT, HIGH); digitalWrite(DISABLE_CHIP_SELECT, HIGH);
} }
cout << pstr("\nAssuming the SD chip select pin is: ") <<int(SD_CHIP_SELECT);
cout << pstr("\nEdit SD_CHIP_SELECT to change the SD chip select pin.\n");
cout << F("\nAssuming the SD chip select pin is: ") <<int(SD_CHIP_SELECT);
cout << F("\nEdit SD_CHIP_SELECT to change the SD chip select pin.\n");
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void loop() { void loop() {
while (Serial.read() >= 0) {} while (Serial.read() >= 0) {}


// pstr stores strings in flash to save RAM // pstr stores strings in flash to save RAM
cout << pstr("\ntype any character to start\n");
cout << F("\ntype any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem
uint32_t t = millis(); uint32_t t = millis();
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
return; return;
} }
t = millis() - t; t = millis() - t;
cardSize = sd.card()->cardSize(); cardSize = sd.card()->cardSize();
if (cardSize == 0) { if (cardSize == 0) {
sdErrorMsg("cardSize failed"); sdErrorMsg("cardSize failed");
return; return;
} }
cout << pstr("\ninit time: ") << t << " ms" << endl;
cout << pstr("\nCard type: ");
cout << F("\ninit time: ") << t << " ms" << endl;
cout << F("\nCard type: ");
switch (sd.card()->type()) { switch (sd.card()->type()) {
case SD_CARD_TYPE_SD1:
cout << pstr("SD1\n");
break;
case SD_CARD_TYPE_SD1:
cout << F("SD1\n");
break;


case SD_CARD_TYPE_SD2:
cout << pstr("SD2\n");
break;
case SD_CARD_TYPE_SD2:
cout << F("SD2\n");
break;


case SD_CARD_TYPE_SDHC:
if (cardSize < 70000000) {
cout << pstr("SDHC\n");
} else {
cout << pstr("SDXC\n");
}
break;
case SD_CARD_TYPE_SDHC:
if (cardSize < 70000000) {
cout << F("SDHC\n");
} else {
cout << F("SDXC\n");
}
break;


default:
cout << pstr("Unknown\n");
default:
cout << F("Unknown\n");
}
if (!cidDmp()) {
return;
}
if (!csdDmp()) {
return;
} }
if (!cidDmp()) return;
if (!csdDmp()) return;
uint32_t ocr; uint32_t ocr;
if (!sd.card()->readOCR(&ocr)) { if (!sd.card()->readOCR(&ocr)) {
sdErrorMsg("\nreadOCR failed"); sdErrorMsg("\nreadOCR failed");
return;
return;
}
cout << F("OCR: ") << hex << ocr << dec << endl;
if (!partDmp()) {
return;
} }
cout << pstr("OCR: ") << hex << ocr << dec << endl;
if (!partDmp()) return;
if (!sd.fsBegin()) { if (!sd.fsBegin()) {
sdErrorMsg("\nFile System initialization failed.\n"); sdErrorMsg("\nFile System initialization failed.\n");
return; return;

+ 14
- 12
SdFat/examples/SoftwareSpi/SoftwareSpi.ino View File

// //
#include <SPI.h> #include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
#if USE_MULTIPLE_SPI_TYPES // Must be nonzero in SdFat/SdFatConfig.h
#if SD_SPI_CONFIGURATION >= 3 // Must be set in SdFat/SdFatConfig.h
// //
// Pin numbers in templates must be constants. // Pin numbers in templates must be constants.
const uint8_t SOFT_MISO_PIN = 12; const uint8_t SOFT_MISO_PIN = 12;


void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // Wait for Leonardo
while (!Serial) {} // Wait for Leonardo
Serial.println("Type any character to start"); Serial.println("Type any character to start");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
if (!sd.begin(SD_CHIP_SELECT_PIN)) sd.initErrorHalt();
if (!file.open("SOFT_SPI.TXT", O_CREAT | O_RDWR)) {

if (!sd.begin(SD_CHIP_SELECT_PIN)) {
sd.initErrorHalt();
}

if (!file.open("SoftSPI.txt", O_CREAT | O_RDWR)) {
sd.errorHalt(F("open failed")); sd.errorHalt(F("open failed"));
} }
file.println(F("This line was printed using software SPI.")); file.println(F("This line was printed using software SPI."));
file.close(); file.close();
Serial.println(F("Done.")); Serial.println(F("Done."));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void loop() {} void loop() {}
#else // USE_MULTIPLE_SPI_TYPES
#error USE_MULTIPLE_SPI_TYPES must be set nonzero in SdFat/SdFatConfig.h
#endif //USE_MULTIPLE_SPI_TYPES
#else // SD_SPI_CONFIGURATION >= 3
#error SD_SPI_CONFIGURATION must be set to 3 in SdFat/SdFatConfig.h
#endif //SD_SPI_CONFIGURATION >= 3

+ 52
- 48
SdFat/examples/StdioBench/StdioBench.ino View File

// Benchmark comparing SdFile and StdioStream. // Benchmark comparing SdFile and StdioStream.
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// Define PRINT_FIELD nonzero to use printField. // Define PRINT_FIELD nonzero to use printField.


float f[100]; float f[100];
char buf[20]; char buf[20];
char* label[] =
{"uint8_t 0 to 255, 100 times ", "uint16_t 0 to 20000",
char* label[] =
{ "uint8_t 0 to 255, 100 times ", "uint16_t 0 to 20000",
"uint32_t 0 to 20000", "uint32_t 1000000000 to 1000010000", "uint32_t 0 to 20000", "uint32_t 1000000000 to 1000010000",
"float nnn.ffff, 10000 times"};
"float nnn.ffff, 10000 times"
};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup() {
uint32_t m; uint32_t m;
uint32_t stdioSize; uint32_t stdioSize;
uint32_t printTime; uint32_t printTime;
uint32_t stdioTime; uint32_t stdioTime;
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} while (!Serial) {}
Serial.println(F("Type any character to start")); Serial.println(F("Type any character to start"));
while (!Serial.available()); while (!Serial.available());
Serial.println(F("Starting test")); Serial.println(F("Starting test"));
if (!sd.begin(SD_CS_PIN)) sd.errorHalt();
if (!sd.begin(SD_CS_PIN)) {
sd.errorHalt();
}


for (uint8_t i = 0; i < 100; i++) { for (uint8_t i = 0; i < 100; i++) {
f[i] = 123.0 + 0.1234*i; f[i] = 123.0 + 0.1234*i;
}
}


for (uint8_t dataType = 0; dataType < 5; dataType++) { for (uint8_t dataType = 0; dataType < 5; dataType++) {
for (uint8_t fileType = 0; fileType < 2; fileType++) { for (uint8_t fileType = 0; fileType < 2; fileType++) {
if (!fileType) { if (!fileType) {
if (!printFile.open("PRRINT.TXT", O_CREAT | O_RDWR | O_TRUNC)) {
if (!printFile.open("print.txt", O_CREAT | O_RDWR | O_TRUNC)) {
Serial.println("open fail"); Serial.println("open fail");
return;
return;
} }
printTime = millis(); printTime = millis();
switch (dataType) { switch (dataType) {
for (uint8_t j = 0; j < 255; j++) { for (uint8_t j = 0; j < 255; j++) {
printFile.println(j); printFile.println(j);
} }
}
}
break; break;
case 1: case 1:
for (uint16_t i = 0; i < 20000; i++) { for (uint16_t i = 0; i < 20000; i++) {
printFile.println(i); printFile.println(i);
} }
break; break;
case 2: case 2:
for (uint32_t i = 0; i < 20000; i++) { for (uint32_t i = 0; i < 20000; i++) {
printFile.println(i); printFile.println(i);
} }
break; break;
case 3: case 3:
for (uint16_t i = 0; i < 10000; i++) { for (uint16_t i = 0; i < 10000; i++) {
printFile.println(i + 1000000000UL); printFile.println(i + 1000000000UL);
} }
break; break;
case 4: case 4:
for (int j = 0; j < 100; j++) { for (int j = 0; j < 100; j++) {
for (uint8_t i = 0; i < 100; i++) { for (uint8_t i = 0; i < 100; i++) {
printFile.println(f[i], 4); printFile.println(f[i], 4);
} }
} }
break;
break;
default: default:
break; break;
} }
printFile.sync();
printFile.sync();
printTime = millis() - printTime; printTime = millis() - printTime;
printFile.rewind(); printFile.rewind();
printSize = printFile.fileSize();
printSize = printFile.fileSize();


} else { } else {
if (!stdioFile.fopen("STREAM.TXT", "w+")) {
if (!stdioFile.fopen("stream.txt", "w+")) {
Serial.println("fopen fail"); Serial.println("fopen fail");
return; return;
} }
stdioTime = millis(); stdioTime = millis();
switch (dataType) {
switch (dataType) {
case 0: case 0:
for (uint16_t i =0; i < 100; i++) { for (uint16_t i =0; i < 100; i++) {
for (uint8_t j = 0; j < 255; j++) { for (uint8_t j = 0; j < 255; j++) {
#if PRINT_FIELD
#if PRINT_FIELD
stdioFile.printField(j, '\n'); stdioFile.printField(j, '\n');
#else // PRINT_FIELD
#else // PRINT_FIELD
stdioFile.println(j); stdioFile.println(j);
#endif // PRINT_FIELD
#endif // PRINT_FIELD
} }
}
}
break; break;
case 1: case 1:
for (uint16_t i = 0; i < 20000; i++) { for (uint16_t i = 0; i < 20000; i++) {
#if PRINT_FIELD
#if PRINT_FIELD
stdioFile.printField(i, '\n'); stdioFile.printField(i, '\n');
#else // PRINT_FIELD
#else // PRINT_FIELD
stdioFile.println(i); stdioFile.println(i);
#endif // PRINT_FIELD
#endif // PRINT_FIELD
} }
break; break;
case 2: case 2:
for (uint32_t i = 0; i < 20000; i++) { for (uint32_t i = 0; i < 20000; i++) {
#if PRINT_FIELD
#if PRINT_FIELD
stdioFile.printField(i, '\n'); stdioFile.printField(i, '\n');
#else // PRINT_FIELD
#else // PRINT_FIELD
stdioFile.println(i); stdioFile.println(i);
#endif // PRINT_FIELD
#endif // PRINT_FIELD
} }
break; break;
case 3: case 3:
for (uint16_t i = 0; i < 10000; i++) { for (uint16_t i = 0; i < 10000; i++) {
#if PRINT_FIELD
#if PRINT_FIELD
stdioFile.printField(i + 1000000000UL, '\n'); stdioFile.printField(i + 1000000000UL, '\n');
#else // PRINT_FIELD
#else // PRINT_FIELD
stdioFile.println(i + 1000000000UL); stdioFile.println(i + 1000000000UL);
#endif // PRINT_FIELD
#endif // PRINT_FIELD
} }
break; break;
case 4: case 4:
for (int j = 0; j < 100; j++) { for (int j = 0; j < 100; j++) {
for (uint8_t i = 0; i < 100; i++) { for (uint8_t i = 0; i < 100; i++) {
#if PRINT_FIELD
#if PRINT_FIELD
stdioFile.printField(f[i], '\n', 4); stdioFile.printField(f[i], '\n', 4);
#else // PRINT_FIELD
stdioFile.println(f[i], 4);
#endif // PRINT_FIELD
#else // PRINT_FIELD
stdioFile.println(f[i], 4);
#endif // PRINT_FIELD
} }
} }
break;
break;
default: default:
break; break;
} }
stdioFile.fflush(); stdioFile.fflush();
stdioTime = millis() - stdioTime; stdioTime = millis() - stdioTime;
stdioSize = stdioFile.ftell();
stdioSize = stdioFile.ftell();
if (STDIO_LIST_COUNT) { if (STDIO_LIST_COUNT) {
size_t len; size_t len;
stdioFile.rewind(); stdioFile.rewind();
for (int i = 0; i < STDIO_LIST_COUNT; i++) { for (int i = 0; i < STDIO_LIST_COUNT; i++) {
stdioFile.fgets(buf, sizeof(buf), &len); stdioFile.fgets(buf, sizeof(buf), &len);
Serial.print(len);Serial.print(',');
Serial.print(len);
Serial.print(',');
Serial.print(buf); Serial.print(buf);
} }
} }


} }
} }
Serial.println(label[dataType]);
Serial.println(label[dataType]);
if (VERIFY_CONTENT && printSize == stdioSize) { if (VERIFY_CONTENT && printSize == stdioSize) {
printFile.rewind(); printFile.rewind();
stdioFile.rewind(); stdioFile.rewind();
Serial.print(printSize); Serial.print(printSize);
Serial.print(" != "); Serial.print(" != ");
} }
Serial.println(stdioSize);
Serial.println(stdioSize);
Serial.print("print millis: "); Serial.print("print millis: ");
Serial.println(printTime); Serial.println(printTime);
Serial.print("stdio millis: "); Serial.print("stdio millis: ");
Serial.print("ratio: "); Serial.print("ratio: ");
Serial.println((float)printTime/(float)stdioTime); Serial.println((float)printTime/(float)stdioTime);
Serial.println(); Serial.println();
printFile.close();
stdioFile.fclose();
printFile.close();
stdioFile.fclose();
} }
Serial.println("Done"); Serial.println("Done");
} }

+ 7
- 7
SdFat/examples/StreamParseInt/StreamParseInt.ino View File

while(!Serial) {} while(!Serial) {}
Serial.println(F("Type any character to start")); Serial.println(F("Type any character to start"));
while (!Serial.available()) {} while (!Serial.available()) {}
// Initialize the SD. // Initialize the SD.
if (!SD.begin(csPin)) { if (!SD.begin(csPin)) {
Serial.println(F("begin error")); Serial.println(F("begin error"));
return; return;
} }
// Create and open the file. Use flag to truncate an existing file. // Create and open the file. Use flag to truncate an existing file.
file = SD.open("stream.txt", O_RDWR|O_CREAT|O_TRUNC);
if (!file) {
Serial.println(F("open error"));
return;
file = SD.open("stream.txt", O_RDWR|O_CREAT|O_TRUNC);
if (!file) {
Serial.println(F("open error"));
return;
} }
// Write a test number to the file. // Write a test number to the file.
file.println("12345"); file.println("12345");
// Rewind the file and read the number with parseInt(). // Rewind the file and read the number with parseInt().
file.seek(0); file.seek(0);
int i = file.parseInt(); int i = file.parseInt();
Serial.print(F("parseInt: ")); Serial.print(F("parseInt: "));
Serial.println(i);
Serial.println(i);
file.close(); file.close();
} }



+ 114
- 74
SdFat/examples/ThreeCards/ThreeCards.ino View File

#include <SPI.h> #include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
#include <SdFatUtil.h> #include <SdFatUtil.h>
#if USE_MULTIPLE_SPI_TYPES // Must be nonzero in SdFat/SdFatConfig.h
#if SD_SPI_CONFIGURATION >= 3 // Must be set in SdFat/SdFatConfig.h


// SD1 is a microSD on hardware SPI pins 50-52 // SD1 is a microSD on hardware SPI pins 50-52
// Using my fast custom SPI // Using my fast custom SPI
SdFat sd1; SdFat sd1;
const uint8_t SD1_CS = 53; const uint8_t SD1_CS = 53;
// SD2 is a Catalex shield on hardware SPI pins 50-52 // SD2 is a Catalex shield on hardware SPI pins 50-52
// Using the standard Arduino SPI library // Using the standard Arduino SPI library
SdFatLibSpi sd2; SdFatLibSpi sd2;
#define initError(msg) initErrorHalt(F(msg)) #define initError(msg) initErrorHalt(F(msg))
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void list() { void list() {
// list current directory on both cards
// list current directory on all cards
Serial.println(F("------sd1-------")); Serial.println(F("------sd1-------"));
sd1.ls("/", LS_SIZE|LS_R); sd1.ls("/", LS_SIZE|LS_R);
Serial.println(F("------sd2-------")); Serial.println(F("------sd2-------"));
sd2.ls("/", LS_SIZE|LS_R); sd2.ls("/", LS_SIZE|LS_R);
Serial.println(F("------sd3-------")); Serial.println(F("------sd3-------"));
sd3.ls("/", LS_SIZE|LS_R);
Serial.println(F("---------------------"));
sd3.ls("/", LS_SIZE|LS_R);
Serial.println(F("---------------------"));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup() {
Serial.print(F("FreeRam: ")); Serial.print(F("FreeRam: "));


Serial.println(FreeRam()); Serial.println(FreeRam());
// fill buffer with known data // fill buffer with known data
for (int i = 0; i < sizeof(buf); i++) buf[i] = i;
for (int i = 0; i < sizeof(buf); i++) {
buf[i] = i;
}

Serial.println(F("type any character to start")); Serial.println(F("type any character to start"));
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}


// disable sd2 while initializing sd1 // disable sd2 while initializing sd1
pinMode(SD2_CS, OUTPUT); pinMode(SD2_CS, OUTPUT);
digitalWrite(SD2_CS, HIGH); digitalWrite(SD2_CS, HIGH);
// initialize the first card // initialize the first card
if (!sd1.begin(SD1_CS)) sd1.initError("sd1:");
if (!sd1.begin(SD1_CS)) {
sd1.initError("sd1:");
}

// initialize the second card // initialize the second card
if (!sd2.begin(SD2_CS)) sd2.initError("sd2:");
if (!sd2.begin(SD2_CS)) {
sd2.initError("sd2:");
}

// initialize the third card // initialize the third card
if (!sd3.begin(SD3_CS)) sd3.initError("sd3:");
if (!sd3.begin(SD3_CS)) {
sd3.initError("sd3:");
}

Serial.println(F("Cards OK - creating directories")); Serial.println(F("Cards OK - creating directories"));
// create DIR1 on sd1 if it does not exist
if (!sd1.exists("/DIR1")) {
if (!sd1.mkdir("/DIR1")) sd1.errorExit("sd1.mkdir");
}
// make /DIR1 the default directory for sd1
if (!sd1.chdir("/DIR1")) sd1.errorExit("sd1.chdir");
// create DIR2 on sd2 if it does not exist
if (!sd2.exists("/DIR2")) {
if (!sd2.mkdir("/DIR2")) sd2.errorExit("sd2.mkdir");
}
// make /DIR2 the default directory for sd2
if (!sd2.chdir("/DIR2")) sd2.errorExit("sd2.chdir");
// create DIR3 on sd3 if it does not exist
if (!sd3.exists("/DIR3")) {
if (!sd3.mkdir("/DIR3")) sd2.errorExit("sd3.mkdir");
}
// make /DIR3 the default directory for sd3
if (!sd3.chdir("/DIR3")) sd3.errorExit("sd3.chdir");

// create Dir1 on sd1 if it does not exist
if (!sd1.exists("/Dir1")) {
if (!sd1.mkdir("/Dir1")) {
sd1.errorExit("sd1.mkdir");
}
}
// make /Dir1 the default directory for sd1
if (!sd1.chdir("/Dir1")) {
sd1.errorExit("sd1.chdir");
}

// create Dir2 on sd2 if it does not exist
if (!sd2.exists("/Dir2")) {
if (!sd2.mkdir("/Dir2")) {
sd2.errorExit("sd2.mkdir");
}
}
// make /Dir2 the default directory for sd2
if (!sd2.chdir("/Dir2")) {
sd2.errorExit("sd2.chdir");
}

// create Dir3 on sd3 if it does not exist
if (!sd3.exists("/Dir3")) {
if (!sd3.mkdir("/Dir3")) {
sd2.errorExit("sd3.mkdir");
}
}
// make /Dir3 the default directory for sd3
if (!sd3.chdir("/Dir3")) {
sd3.errorExit("sd3.chdir");
}

Serial.println(F("Directories created - removing old files")); Serial.println(F("Directories created - removing old files"));
if (sd1.exists("TEST1.BIN")) {
if (!sd1.remove("TEST1.BIN")) sd1.errorExit("sd1.remove");
}
if (sd2.exists("TEST2.BIN")) {
if (!sd2.remove("TEST2.BIN")) sd2.errorExit("sd2.remove");
}
if (sd3.exists("TEST3.BIN")) {
if (!sd3.remove("TEST3.BIN")) sd2.errorExit("sd3.remove");
}

if (sd1.exists("TEST1.bin")) {
if (!sd1.remove("TEST1.bin")) {
sd1.errorExit("sd1.remove");
}
}
if (sd2.exists("TEST2.bin")) {
if (!sd2.remove("TEST2.bin")) {
sd2.errorExit("sd2.remove");
}
}
if (sd3.exists("TEST3.bin")) {
if (!sd3.remove("TEST3.bin")) {
sd2.errorExit("sd3.remove");
}
}
Serial.println("Initial SD directories"); Serial.println("Initial SD directories");
list(); list();


// create or open /DIR1/TEST1.BIN and truncate it to zero length
// create or open /Dir1/TEST1.bin and truncate it to zero length
SdFile file1; SdFile file1;
if (!file1.open(&sd1, "TEST1.BIN", O_RDWR | O_CREAT | O_TRUNC)) {
if (!file1.open(&sd1, "TEST1.bin", O_RDWR | O_CREAT | O_TRUNC)) {
sd1.errorExit("file1"); sd1.errorExit("file1");
} }
Serial.println(F("Writing SD1:/DIR1/TEST1.BIN"));
// write data to /DIR1/TEST.BIN on sd1
Serial.println(F("Writing SD1:/Dir1/TEST1.bin"));
// write data to /Dir1/TEST1.bin on sd1
for (int i = 0; i < NWRITE; i++) { for (int i = 0; i < NWRITE; i++) {
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
sd1.errorExit("sd1.write"); sd1.errorExit("sd1.write");
} }
file1.sync(); file1.sync();
list(); list();
// create or open /DIR2/TEST2.BIN and truncate it to zero length
// create or open /Dir2/TEST2.bin and truncate it to zero length
SdFile file2; SdFile file2;
if (!file2.open(&sd2, "TEST2.BIN", O_RDWR | O_CREAT | O_TRUNC)) {
if (!file2.open(&sd2, "TEST2.bin", O_RDWR | O_CREAT | O_TRUNC)) {
sd2.errorExit("file2"); sd2.errorExit("file2");
} }
Serial.println(F("Copying SD1:/DIR1/TEST1.BIN to SD2::/DIR2/TEST2.BIN"));
Serial.println(F("Copying SD1:/Dir1/TEST1.bin to SD2::/Dir2/TEST2.bin"));
// copy file1 to file2 // copy file1 to file2
file1.rewind(); file1.rewind();
uint32_t t = millis(); uint32_t t = millis();


while (1) { while (1) {
int n = file1.read(buf, sizeof(buf)); int n = file1.read(buf, sizeof(buf));
if (n < 0) sd1.errorExit("read1");
if (n == 0) break;
if (file2.write(buf, n) != n) sd2.errorExit("write3");
if (n < 0) {
sd1.errorExit("read1");
}
if (n == 0) {
break;
}
if (file2.write(buf, n) != n) {
sd2.errorExit("write3");
}
} }
t = millis() - t; t = millis() - t;
file2.sync(); file2.sync();
Serial.print(t); Serial.print(t);
Serial.println(F(" millis")); Serial.println(F(" millis"));
list(); list();
// create or open /DIR3/TEST3.BIN and truncate it to zero length
// create or open /Dir3/TEST3.bin and truncate it to zero length
SdFile file3; SdFile file3;
if (!file3.open(&sd3, "TEST3.BIN", O_RDWR | O_CREAT | O_TRUNC)) {
if (!file3.open(&sd3, "TEST3.bin", O_RDWR | O_CREAT | O_TRUNC)) {
sd3.errorExit("file3"); sd3.errorExit("file3");
} }
file2.rewind(); file2.rewind();
Serial.println(F("Copying SD2:/DIR2/TEST2.BIN to SD3:/DIR3/TEST3.BIN"));
Serial.println(F("Copying SD2:/Dir2/TEST2.bin to SD3:/Dir3/TEST3.bin"));
while (1) { while (1) {
int n = file2.read(buf, sizeof(buf)); int n = file2.read(buf, sizeof(buf));
if (n == 0) break;
if (n != sizeof(buf)) sd2.errorExit("read2");
if (file3.write(buf, n) != n) sd3.errorExit("write2");
if (n == 0) {
break;
}
if (n != sizeof(buf)) {
sd2.errorExit("read2");
}
if (file3.write(buf, n) != n) {
sd3.errorExit("write2");
}
} }
file3.sync(); file3.sync();
list(); list();
// Verify content of file3 // Verify content of file3
file3.rewind(); file3.rewind();
Serial.println(F("Verifying content of TEST3.BIN"));
Serial.println(F("Verifying content of TEST3.bin"));
for (int i = 0; i < NWRITE; i++) { for (int i = 0; i < NWRITE; i++) {
if (file3.read(buf, sizeof(buf)) != sizeof(buf)) { if (file3.read(buf, sizeof(buf)) != sizeof(buf)) {
sd3.errorExit("sd3.read"); sd3.errorExit("sd3.read");
} }
for (int j = 0; j < sizeof(buf); j++) { for (int j = 0; j < sizeof(buf); j++) {
if (j != buf[j]) sd3.errorExit("Verify error");
if (j != buf[j]) {
sd3.errorExit("Verify error");
}
} }
} }
Serial.println(F("Done - Verify OK")); Serial.println(F("Done - Verify OK"));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void loop() {} void loop() {}
#else // USE_MULTIPLE_SPI_TYPES
#error USE_MULTIPLE_SPI_TYPES must be set nonzero in SdFat/SdFatConfig.h
#endif //USE_MULTIPLE_SPI_TYPES
#else // SD_SPI_CONFIGURATION >= 3
#error SD_SPI_CONFIGURATION must be set to 3 in SdFat/SdFatConfig.h
#endif //SD_SPI_CONFIGURATION >= 3

+ 27
- 23
SdFat/examples/Timestamp/Timestamp.ino View File

/* /*
* This sketch tests the dateTimeCallback() function
* This program tests the dateTimeCallback() function
* and the timestamp() function. * and the timestamp() function.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


SdFat sd; SdFat sd;
*/ */
void printTimestamps(SdFile& f) { void printTimestamps(SdFile& f) {
dir_t d; dir_t d;
if (!f.dirEntry(&d)) error("f.dirEntry failed");
if (!f.dirEntry(&d)) {
error("f.dirEntry failed");
}


cout << pstr("Creation: ");
cout << F("Creation: ");
f.printFatDate(d.creationDate); f.printFatDate(d.creationDate);
cout << ' '; cout << ' ';
f.printFatTime(d.creationTime); f.printFatTime(d.creationTime);
cout << endl; cout << endl;


cout << pstr("Modify: ");
cout << F("Modify: ");
f.printFatDate(d.lastWriteDate); f.printFatDate(d.lastWriteDate);
cout <<' '; cout <<' ';
f.printFatTime(d.lastWriteTime); f.printFatTime(d.lastWriteTime);
cout << endl; cout << endl;


cout << pstr("Access: ");
cout << F("Access: ");
f.printFatDate(d.lastAccessDate); f.printFatDate(d.lastAccessDate);
cout << endl; cout << endl;
} }
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo


cout << pstr("Type any character to start\n");
cout << F("Type any character to start\n");
while (!Serial.available()); while (!Serial.available());
delay(400); // catch Due reset problem delay(400); // catch Due reset problem
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}


// remove files if they exist // remove files if they exist
sd.remove("CALLBACK.TXT");
sd.remove("DEFAULT.TXT");
sd.remove("STAMP.TXT");
sd.remove("callback.txt");
sd.remove("default.txt");
sd.remove("stamp.txt");


// create a new file with default timestamps // create a new file with default timestamps
if (!file.open("DEFAULT.TXT", O_CREAT | O_WRITE)) {
error("open DEFAULT.TXT failed");
if (!file.open("default.txt", O_CREAT | O_WRITE)) {
error("open default.txt failed");
} }
cout << pstr("\nOpen with default times\n");
cout << F("\nOpen with default times\n");
printTimestamps(file); printTimestamps(file);


// close file // close file
SdFile::dateTimeCallback(dateTime); SdFile::dateTimeCallback(dateTime);


// create a new file with callback timestamps // create a new file with callback timestamps
if (!file.open("CALLBACK.TXT", O_CREAT | O_WRITE)) {
error("open CALLBACK.TXT failed");
if (!file.open("callback.txt", O_CREAT | O_WRITE)) {
error("open callback.txt failed");
} }
cout << ("\nOpen with callback times\n"); cout << ("\nOpen with callback times\n");
printTimestamps(file); printTimestamps(file);
// force dir update // force dir update
file.sync(); file.sync();


cout << pstr("\nTimes after write\n");
cout << F("\nTimes after write\n");
printTimestamps(file); printTimestamps(file);


// close file // close file
SdFile::dateTimeCallbackCancel(); SdFile::dateTimeCallbackCancel();


// create a new file with default timestamps // create a new file with default timestamps
if (!file.open("STAMP.TXT", O_CREAT | O_WRITE)) {
error("open STAMP.TXT failed");
if (!file.open("stamp.txt", O_CREAT | O_WRITE)) {
error("open stamp.txt failed");
} }
// set creation date time // set creation date time
if (!file.timestamp(T_CREATE, 2014, 11, 10, 1, 2, 3)) { if (!file.timestamp(T_CREATE, 2014, 11, 10, 1, 2, 3)) {
if (!file.timestamp(T_ACCESS, 2014, 11, 12, 7, 8, 9)) { if (!file.timestamp(T_ACCESS, 2014, 11, 12, 7, 8, 9)) {
error("set access time failed"); error("set access time failed");
} }
cout << pstr("\nTimes after timestamp() calls\n");
cout << F("\nTimes after timestamp() calls\n");
printTimestamps(file); printTimestamps(file);


file.close(); file.close();
cout << pstr("\nDone\n");
cout << F("\nDone\n");
} }


void loop(void){}
void loop(void) {}

+ 70
- 43
SdFat/examples/TwoCards/TwoCards.ino View File

const uint8_t SD1_CS = 10; // chip select for sd1 const uint8_t SD1_CS = 10; // chip select for sd1


SdFat sd2; SdFat sd2;
const uint8_t SD2_CS = 9; // chip select for sd2
const uint8_t SD2_CS = 4; // chip select for sd2


const uint8_t BUF_DIM = 100; const uint8_t BUF_DIM = 100;
uint8_t buf[BUF_DIM]; uint8_t buf[BUF_DIM];
Serial.print(F("FreeRam: ")); Serial.print(F("FreeRam: "));


Serial.println(FreeRam()); Serial.println(FreeRam());
// fill buffer with known data // fill buffer with known data
for (int i = 0; i < sizeof(buf); i++) buf[i] = i;
for (int i = 0; i < sizeof(buf); i++) {
buf[i] = i;
}

Serial.println(F("type any character to start")); Serial.println(F("type any character to start"));
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem
// disable sd2 while initializing sd1 // disable sd2 while initializing sd1
pinMode(SD2_CS, OUTPUT); pinMode(SD2_CS, OUTPUT);
digitalWrite(SD2_CS, HIGH); digitalWrite(SD2_CS, HIGH);
// initialize the first card // initialize the first card
if (!sd1.begin(SD1_CS)) { if (!sd1.begin(SD1_CS)) {
sd1.initError("sd1:"); sd1.initError("sd1:");
} }
// create DIR1 on sd1 if it does not exist
if (!sd1.exists("/DIR1")) {
if (!sd1.mkdir("/DIR1")) sd1.errorExit("sd1.mkdir");
// create Dir1 on sd1 if it does not exist
if (!sd1.exists("/Dir1")) {
if (!sd1.mkdir("/Dir1")) {
sd1.errorExit("sd1.mkdir");
}
} }
// initialize the second card // initialize the second card
if (!sd2.begin(SD2_CS)) { if (!sd2.begin(SD2_CS)) {
sd2.initError("sd2:"); sd2.initError("sd2:");
} }
// create DIR2 on sd2 if it does not exist
if (!sd2.exists("/DIR2")) {
if (!sd2.mkdir("/DIR2")) sd2.errorExit("sd2.mkdir");
// create Dir2 on sd2 if it does not exist
if (!sd2.exists("/Dir2")) {
if (!sd2.mkdir("/Dir2")) {
sd2.errorExit("sd2.mkdir");
}
} }
// list root directory on both cards // list root directory on both cards
Serial.println(F("------sd1 root-------")); Serial.println(F("------sd1 root-------"));
Serial.println(F("------sd2 root-------")); Serial.println(F("------sd2 root-------"));
sd2.ls(); sd2.ls();


// make /DIR1 the default directory for sd1
if (!sd1.chdir("/DIR1")) sd1.errorExit("sd1.chdir");
// make /DIR2 the default directory for sd2
if (!sd2.chdir("/DIR2")) sd2.errorExit("sd2.chdir");
// make /Dir1 the default directory for sd1
if (!sd1.chdir("/Dir1")) {
sd1.errorExit("sd1.chdir");
}

// make /Dir2 the default directory for sd2
if (!sd2.chdir("/Dir2")) {
sd2.errorExit("sd2.chdir");
}

// list current directory on both cards // list current directory on both cards
Serial.println(F("------sd1 DIR1-------"));
Serial.println(F("------sd1 Dir1-------"));
sd1.ls(); sd1.ls();
Serial.println(F("------sd2 DIR2-------"));
Serial.println(F("------sd2 Dir2-------"));
sd2.ls(); sd2.ls();
Serial.println(F("---------------------")); Serial.println(F("---------------------"));
// remove RENAME.BIN from /DIR2 directory of sd2
if (sd2.exists("RENAME.BIN")) {
if (!sd2.remove("RENAME.BIN")) {
sd2.errorExit("remove RENAME.BIN");
// remove rename.bin from /Dir2 directory of sd2
if (sd2.exists("rename.bin")) {
if (!sd2.remove("rename.bin")) {
sd2.errorExit("remove rename.bin");
} }
} }
// set the current working directory for open() to sd1 // set the current working directory for open() to sd1
sd1.chvol(); sd1.chvol();
// create or open /DIR1/TEST.BIN and truncate it to zero length
// create or open /Dir1/test.bin and truncate it to zero length
SdFile file1; SdFile file1;
if (!file1.open("TEST.BIN", O_RDWR | O_CREAT | O_TRUNC)) {
if (!file1.open("test.bin", O_RDWR | O_CREAT | O_TRUNC)) {
sd1.errorExit("file1"); sd1.errorExit("file1");
} }
Serial.println(F("Writing TEST.BIN to sd1"));
// write data to /DIR1/TEST.BIN on sd1
Serial.println(F("Writing test.bin to sd1"));
// write data to /Dir1/test.bin on sd1
for (int i = 0; i < NWRITE; i++) { for (int i = 0; i < NWRITE; i++) {
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
sd1.errorExit("sd1.write"); sd1.errorExit("sd1.write");
} }
// set the current working directory for open() to sd2 // set the current working directory for open() to sd2
sd2.chvol(); sd2.chvol();
// create or open /DIR2/COPY.BIN and truncate it to zero length
// create or open /Dir2/copy.bin and truncate it to zero length
SdFile file2; SdFile file2;
if (!file2.open("COPY.BIN", O_WRITE | O_CREAT | O_TRUNC)) {
if (!file2.open("copy.bin", O_WRITE | O_CREAT | O_TRUNC)) {
sd2.errorExit("file2"); sd2.errorExit("file2");
} }
Serial.println(F("Copying TEST.BIN to COPY.BIN"));
Serial.println(F("Copying test.bin to copy.bin"));
// copy file1 to file2 // copy file1 to file2
file1.rewind(); file1.rewind();
uint32_t t = millis(); uint32_t t = millis();


while (1) { while (1) {
int n = file1.read(buf, sizeof(buf)); int n = file1.read(buf, sizeof(buf));
if (n < 0) sd1.errorExit("read1");
if (n == 0) break;
if (file2.write(buf, n) != n) sd2.errorExit("write2");
if (n < 0) {
sd1.errorExit("read1");
}
if (n == 0) {
break;
}
if (file2.write(buf, n) != n) {
sd2.errorExit("write2");
}
} }
t = millis() - t; t = millis() - t;
Serial.print(F("File size: ")); Serial.print(F("File size: "));
Serial.print(F("Copy time: ")); Serial.print(F("Copy time: "));
Serial.print(t); Serial.print(t);
Serial.println(F(" millis")); Serial.println(F(" millis"));
// close TEST.BIN
// close test.bin
file1.close(); file1.close();
file2.close();
// list current directory on both cards
Serial.println(F("------sd1 -------"));
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("------sd2 -------"));
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("---------------------"));
Serial.println(F("Renaming copy.bin"));
// rename the copy // rename the copy
file2.close();
if (!sd2.rename("COPY.BIN", "RENAME.BIN")) {
if (!sd2.rename("copy.bin", "rename.bin")) {
sd2.errorExit("sd2.rename"); sd2.errorExit("sd2.rename");
} }
// list current directory on both cards
Serial.println(F("------sd1 -------"));
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("------sd2 -------"));
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("---------------------"));
Serial.println(F("Done")); Serial.println(F("Done"));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

+ 50
- 39
SdFat/examples/bench/bench.ino View File

/* /*
* This sketch is a simple binary write/read benchmark.
* This program is a simple binary write/read benchmark.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
#include <SdFatUtil.h> #include <SdFatUtil.h>


if (!sd.card()->readCID(&cid)) { if (!sd.card()->readCID(&cid)) {
error("readCID failed"); error("readCID failed");
} }
cout << pstr("\nManufacturer ID: ");
cout << F("\nManufacturer ID: ");
cout << hex << int(cid.mid) << dec << endl; cout << hex << int(cid.mid) << dec << endl;
cout << pstr("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
cout << pstr("Product: ");
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
cout << F("Product: ");
for (uint8_t i = 0; i < 5; i++) { for (uint8_t i = 0; i < 5; i++) {
cout << cid.pnm[i]; cout << cid.pnm[i];
} }
cout << pstr("\nVersion: ");
cout << F("\nVersion: ");
cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl; cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
cout << pstr("Serial number: ") << hex << cid.psn << dec << endl;
cout << pstr("Manufacturing date: ");
cout << F("Serial number: ") << hex << cid.psn << dec << endl;
cout << F("Manufacturing date: ");
cout << int(cid.mdt_month) << '/'; cout << int(cid.mdt_month) << '/';
cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl; cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
cout << endl; cout << endl;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
while (!Serial){} // wait for Leonardo
cout << pstr("\nUse a freshly formatted SD for best performance.\n");
while (!Serial) {} // wait for Leonardo
delay(1000);
cout << F("\nUse a freshly formatted SD for best performance.\n");

// use uppercase in hex and use 0X base prefix // use uppercase in hex and use 0X base prefix
cout << uppercase << showbase << endl; cout << uppercase << showbase << endl;
} }
// discard any input // discard any input
while (Serial.read() >= 0) {} while (Serial.read() >= 0) {}


// pstr stores strings in flash to save RAM
cout << pstr("Type any character to start\n");
// F( stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem
cout << pstr("Free RAM: ") << FreeRam() << endl;
cout << F("Free RAM: ") << FreeRam() << endl;


// initialize the SD card at SPI_FULL_SPEED for best performance. // initialize the SD card at SPI_FULL_SPEED for best performance.
// try SPI_HALF_SPEED if bus errors occur. // try SPI_HALF_SPEED if bus errors occur.
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) {
sd.initErrorHalt();
}

cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl;
cout << F("Card size: ") << sd.card()->cardSize()*512E-9;
cout << F(" GB (GB = 1E9 bytes)") << endl;


cout << pstr("Type is FAT") << int(sd.vol()->fatType()) << endl;
cout << pstr("Card size: ") << sd.card()->cardSize()*512E-9;
cout << pstr(" GB (GB = 1E9 bytes)") << endl;
cidDmp(); cidDmp();

// open or create file - truncate existing file. // open or create file - truncate existing file.
if (!file.open("BENCH.DAT", O_CREAT | O_TRUNC | O_RDWR)) {
if (!file.open("bench.dat", O_CREAT | O_TRUNC | O_RDWR)) {
error("open failed"); error("open failed");
} }


buf[BUF_SIZE-2] = '\r'; buf[BUF_SIZE-2] = '\r';
buf[BUF_SIZE-1] = '\n'; buf[BUF_SIZE-1] = '\n';


cout << pstr("File size ") << FILE_SIZE_MB << pstr(" MB\n");
cout << pstr("Buffer size ") << BUF_SIZE << pstr(" bytes\n");
cout << pstr("Starting write test, please wait.") << endl << endl;
cout << F("File size ") << FILE_SIZE_MB << F(" MB\n");
cout << F("Buffer size ") << BUF_SIZE << F(" bytes\n");
cout << F("Starting write test, please wait.") << endl << endl;


// do write test // do write test
uint32_t n = FILE_SIZE/sizeof(buf); uint32_t n = FILE_SIZE/sizeof(buf);
cout <<pstr("write speed and latency") << endl;
cout << pstr("speed,max,min,avg") << endl;
cout << pstr("KB/Sec,usec,usec,usec") << endl;
cout <<F("write speed and latency") << endl;
cout << F("speed,max,min,avg") << endl;
cout << F("KB/Sec,usec,usec,usec") << endl;
for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) { for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) {
file.truncate(0); file.truncate(0);
maxLatency = 0; maxLatency = 0;
error("write failed"); error("write failed");
} }
m = micros() - m; m = micros() - m;
if (maxLatency < m) maxLatency = m;
if (minLatency > m) minLatency = m;
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m; totalLatency += m;
} }
file.sync(); file.sync();
t = millis() - t; t = millis() - t;
s = file.fileSize(); s = file.fileSize();
cout << s/t <<',' << maxLatency << ',' << minLatency;
cout << s/t <<',' << maxLatency << ',' << minLatency;
cout << ',' << totalLatency/n << endl; cout << ',' << totalLatency/n << endl;
} }


cout << endl << pstr("Starting read test, please wait.") << endl;
cout << endl <<pstr("read speed and latency") << endl;
cout << pstr("speed,max,min,avg") << endl;
cout << pstr("KB/Sec,usec,usec,usec") << endl;
cout << endl << F("Starting read test, please wait.") << endl;
cout << endl <<F("read speed and latency") << endl;
cout << F("speed,max,min,avg") << endl;
cout << F("KB/Sec,usec,usec,usec") << endl;
// do read test // do read test
for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) { for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) {
file.rewind(); file.rewind();
error("read failed"); error("read failed");
} }
m = micros() - m; m = micros() - m;
if (maxLatency < m) maxLatency = m;
if (minLatency > m) minLatency = m;
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m; totalLatency += m;
if (buf[BUF_SIZE-1] != '\n') { if (buf[BUF_SIZE-1] != '\n') {
error("data check"); error("data check");
} }
} }
t = millis() - t; t = millis() - t;
cout << s/t <<',' << maxLatency << ',' << minLatency;
cout << s/t <<',' << maxLatency << ',' << minLatency;
cout << ',' << totalLatency/n << endl; cout << ',' << totalLatency/n << endl;
} }
cout << endl << pstr("Done") << endl;
cout << endl << F("Done") << endl;
file.close(); file.close();
} }

+ 1
- 1
SdFat/examples/cin_cout/cin_cout.ino View File

/* /*
* Demo of ArduinoInStream and ArduinoOutStream * Demo of ArduinoInStream and ArduinoOutStream
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// create serial output stream // create serial output stream

+ 31
- 23
SdFat/examples/dataLogger/dataLogger.ino View File

/* /*
* Simple data logger. * Simple data logger.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD chip select pin. Be sure to disable any other SPI devices such as Enet. // SD chip select pin. Be sure to disable any other SPI devices such as Enet.
const uint8_t chipSelect = SS; const uint8_t chipSelect = SS;


// Interval between data records in milliseconds.
// Interval between data records in milliseconds.
// The interval must be greater than the maximum SD write latency plus the // The interval must be greater than the maximum SD write latency plus the
// time to acquire and write data to the SD to avoid overrun errors. // time to acquire and write data to the SD to avoid overrun errors.
// Run the bench example to check the quality of your SD card. // Run the bench example to check the quality of your SD card.
const uint32_t SAMPLE_INTERVAL_MS = 200; const uint32_t SAMPLE_INTERVAL_MS = 200;


// Log file base name. Must be six characters or less. // Log file base name. Must be six characters or less.
#define FILE_BASE_NAME "DATA"
#define FILE_BASE_NAME "Data"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// File system object. // File system object.
SdFat sd; SdFat sd;
// Log a data record. // Log a data record.
void logData() { void logData() {
uint16_t data[ANALOG_COUNT]; uint16_t data[ANALOG_COUNT];
// Read all channels to avoid SD write latency between readings. // Read all channels to avoid SD write latency between readings.
for (uint8_t i = 0; i < ANALOG_COUNT; i++) { for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
data[i] = analogRead(i); data[i] = analogRead(i);
} }
// Write data to file. Start with log time in micros. // Write data to file. Start with log time in micros.
file.print(logTime); file.print(logTime);
// Write ADC data to CSV record. // Write ADC data to CSV record.
for (uint8_t i = 0; i < ANALOG_COUNT; i++) { for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
file.write(','); file.write(',');
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup() {
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
char fileName[13] = FILE_BASE_NAME "00.CSV";
char fileName[13] = FILE_BASE_NAME "00.csv";
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo
delay(1000); delay(1000);
Serial.println(F("Type any character to start")); Serial.println(F("Type any character to start"));
while (!Serial.available()) {} while (!Serial.available()) {}
// Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}

// Find an unused file name. // Find an unused file name.
if (BASE_NAME_SIZE > 6) { if (BASE_NAME_SIZE > 6) {
error("FILE_BASE_NAME too long"); error("FILE_BASE_NAME too long");
error("Can't create file name"); error("Can't create file name");
} }
} }
if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) error("file.open");
if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) {
error("file.open");
}
do { do {
delay(10); delay(10);
} while (Serial.read() >= 0); } while (Serial.read() >= 0);
Serial.print(F("Logging to: ")); Serial.print(F("Logging to: "));
Serial.println(fileName); Serial.println(fileName);
Serial.println(F("Type any character to stop")); Serial.println(F("Type any character to stop"));
// Write data header. // Write data header.
writeHeader(); writeHeader();
// Start on a multiple of the sample interval. // Start on a multiple of the sample interval.
logTime = micros()/(1000UL*SAMPLE_INTERVAL_MS) + 1; logTime = micros()/(1000UL*SAMPLE_INTERVAL_MS) + 1;
logTime *= 1000UL*SAMPLE_INTERVAL_MS; logTime *= 1000UL*SAMPLE_INTERVAL_MS;
void loop() { void loop() {
// Time for next record. // Time for next record.
logTime += 1000UL*SAMPLE_INTERVAL_MS; logTime += 1000UL*SAMPLE_INTERVAL_MS;
// Wait for log time. // Wait for log time.
int32_t diff; int32_t diff;
do { do {
diff = micros() - logTime; diff = micros() - logTime;
} while (diff < 0); } while (diff < 0);
// Check for data rate too high.
if (diff > 10) error("Missed data record");

// Check for data rate too high.
if (diff > 10) {
error("Missed data record");
}

logData(); logData();
// Force data to SD and update the directory entry to avoid data loss. // Force data to SD and update the directory entry to avoid data loss.
if (!file.sync() || file.getWriteError()) error("write error");
if (!file.sync() || file.getWriteError()) {
error("write error");
}

if (Serial.available()) { if (Serial.available()) {
// Close file and stop. // Close file and stop.
file.close(); file.close();

+ 54
- 40
SdFat/examples/directoryFunctions/directoryFunctions.ino View File

/* /*
* Example use of chdir(), ls(), mkdir(), and rmdir(). * Example use of chdir(), ls(), mkdir(), and rmdir().
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>
// SD card chip select pin. // SD card chip select pin.
const uint8_t SD_CHIP_SELECT = SS; const uint8_t SD_CHIP_SELECT = SS;
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo
delay(1000); delay(1000);
cout << pstr("Type any character to start\n");
cout << F("Type any character to start\n");
// Wait for input line and discard. // Wait for input line and discard.
cin.readline(); cin.readline();


// Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}

// Check for empty SD. // Check for empty SD.
if (file.openNext(sd.vwd(), O_READ)) { if (file.openNext(sd.vwd(), O_READ)) {
cout << pstr("Found files/folders in the root directory.\n");
cout << F("Found files/folders in the root directory.\n");
if (!ALLOW_WIPE) { if (!ALLOW_WIPE) {
error("SD not empty, use a blank SD or set ALLOW_WIPE true.");
error("SD not empty, use a blank SD or set ALLOW_WIPE true.");
} else { } else {
cout << pstr("Type: 'WIPE' to delete all SD files.\n");
cout << F("Type: 'WIPE' to delete all SD files.\n");
char buf[10]; char buf[10];
cin.readline(); cin.readline();
cin.get(buf, sizeof(buf)); cin.get(buf, sizeof(buf));
error("Invalid WIPE input"); error("Invalid WIPE input");
} }
file.close(); file.close();
sd.vwd()->rmRfStar();
cout << pstr("***SD wiped clean.***\n\n");
if (!sd.vwd()->rmRfStar()) {
error("wipe failed");
}
cout << F("***SD wiped clean.***\n\n");
} }
} }
// Create a new folder. // Create a new folder.
if (!sd.mkdir("FOLDER1")) error("Create FOLDER1 failed");
cout << pstr("Created FOLDER1\n");
// Create a file in FOLDER1 using a path.
if (!file.open("FOLDER1/FILE1.TXT", O_CREAT | O_WRITE)) {
error("create FOLDER1/FILE1.TXT failed");
if (!sd.mkdir("Folder1")) {
error("Create Folder1 failed");
}
cout << F("Created Folder1\n");

// Create a file in Folder1 using a path.
if (!file.open("Folder1/file1.txt", O_CREAT | O_WRITE)) {
error("create Folder1/file1.txt failed");
} }
file.close(); file.close();
cout << pstr("Created FOLDER1/FILE1.TXT\n");
// Change volume working directory to FOLDER1.
if (!sd.chdir("FOLDER1")) error("chdir failed for FOLDER1.\n");
cout << pstr("chdir to FOLDER1\n");
// Create FILE2.TXT in current directory.
if (!file.open("FILE2.TXT", O_CREAT | O_WRITE)) {
error("create FILE2.TXT failed");
cout << F("Created Folder1/file1.txt\n");

// Change volume working directory to Folder1.
if (!sd.chdir("Folder1")) {
error("chdir failed for Folder1.\n");
}
cout << F("chdir to Folder1\n");

// Create File2.txt in current directory.
if (!file.open("File2.txt", O_CREAT | O_WRITE)) {
error("create File2.txt failed");
} }
file.close(); file.close();
cout << pstr("Created FILE2.TXT in current directory\n");
cout << pstr("List of files on the SD.\n");
cout << F("Created File2.txt in current directory\n");
cout << F("List of files on the SD.\n");
sd.ls("/", LS_R); sd.ls("/", LS_R);


// Remove files from current directory. // Remove files from current directory.
if (!sd.remove("FILE1.TXT") || !sd.remove("FILE2.TXT")) error("remove failed");
cout << pstr("\nFILE1.TXT and FILE2.TXT removed.\n");
if (!sd.remove("file1.txt") || !sd.remove("File2.txt")) {
error("remove failed");
}
cout << F("\nfile1.txt and File2.txt removed.\n");


// Change current directory to root. // Change current directory to root.
if (!sd.chdir()) error("chdir to root failed.\n");
cout << pstr("List of files on the SD.\n");
if (!sd.chdir()) {
error("chdir to root failed.\n");
}

cout << F("List of files on the SD.\n");
sd.ls(LS_R); sd.ls(LS_R);
// Remove FOLDER1.
if (!sd.rmdir("FOLDER1")) error("rmdir for FOLDER1 failed\n");
cout << pstr("\nFOLDER1 removed, SD empty.\n");
cout << pstr("Done!\n");

// Remove Folder1.
if (!sd.rmdir("Folder1")) {
error("rmdir for Folder1 failed\n");
}

cout << F("\nFolder1 removed, SD empty.\n");
cout << F("Done!\n");
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Nothing happens in loop. // Nothing happens in loop.

+ 33
- 27
SdFat/examples/fgets/fgets.ino View File

// Demo of fgets function to read lines from a file. // Demo of fgets function to read lines from a file.
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD chip select pin // SD chip select pin
char line[25]; char line[25];
int n; int n;
// open test file // open test file
SdFile rdfile("FGETS.TXT", O_READ);
SdFile rdfile("fgets.txt", O_READ);
// check for open error // check for open error
if (!rdfile.isOpen()) error("demoFgets");
cout << endl << pstr(
"Lines with '>' end with a '\\n' character\n"
"Lines with '#' do not end with a '\\n' character\n"
"\n");
if (!rdfile.isOpen()) {
error("demoFgets");
}

cout << endl << F(
"Lines with '>' end with a '\\n' character\n"
"Lines with '#' do not end with a '\\n' character\n"
"\n");

// read lines from the file // read lines from the file
while ((n = rdfile.fgets(line, sizeof(line))) > 0) { while ((n = rdfile.fgets(line, sizeof(line))) > 0) {
if (line[n - 1] == '\n') { if (line[n - 1] == '\n') {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void makeTestFile() { void makeTestFile() {
// create or open test file // create or open test file
SdFile wrfile("FGETS.TXT", O_WRITE | O_CREAT | O_TRUNC);
SdFile wrfile("fgets.txt", O_WRITE | O_CREAT | O_TRUNC);
// check for open error // check for open error
if (!wrfile.isOpen()) error("MakeTestFile");
if (!wrfile.isOpen()) {
error("MakeTestFile");
}

// write test file // write test file
wrfile.print(F( wrfile.print(F(
"Line with CRLF\r\n"
"Line with only LF\n"
"Long line that will require an extra read\n"
"\n" // empty line
"Line at EOF without NL"
));
"Line with CRLF\r\n"
"Line with only LF\n"
"Long line that will require an extra read\n"
"\n" // empty line
"Line at EOF without NL"
));
wrfile.close(); wrfile.close();
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // Wait for Leonardo while (!Serial) {} // Wait for Leonardo


cout << pstr("Type any character to start\n");
cout << F("Type any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}

makeTestFile(); makeTestFile();
demoFgets(); demoFgets();
cout << pstr("\nDone\n");
cout << F("\nDone\n");
} }
void loop(void) {} void loop(void) {}

+ 6
- 4
SdFat/examples/formatting/formatting.ino View File



for (int row = 1; row <= max; row++) { for (int row = 1; row <= max; row++) {
for (int col = 1; col <= max; col++) { for (int col = 1; col <= max; col++) {
cout << setw(width) << row * col << (col == max ? '\n' : ' ');
cout << setw(width) << row * col << (col == max ? '\n' : ' ');
} }
} }
cout << endl; cout << endl;
// shows how to set and restore the fill character // shows how to set and restore the fill character
void showDate(int m, int d, int y) { void showDate(int m, int d, int y) {
// convert two digit year // convert two digit year
if (y < 100) y += 2000;
if (y < 100) {
y += 2000;
}


// set new fill to '0' save old fill character // set new fill to '0' save old fill character
char old = cout.fill('0'); char old = cout.fill('0');
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup(void) { void setup(void) {
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo
delay(2000); delay(2000);
cout << endl << "default formatting" << endl; cout << endl << "default formatting" << endl;
example(); example();



+ 13
- 11
SdFat/examples/getline/getline.ino View File

* Note: This example is meant to demonstrate subtleties the standard and * Note: This example is meant to demonstrate subtleties the standard and
* may not the best way to read a file. * may not the best way to read a file.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD chip select pin // SD chip select pin
ArduinoOutStream cout(Serial); ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void makeTestFile() { void makeTestFile() {
ofstream sdout("GETLINE.TXT");
ofstream sdout("getline.txt");
// use flash for text to save RAM // use flash for text to save RAM
sdout << pstr(
"short line\n"
"\n"
"17 character line\n"
"too long for buffer\n"
"line with no nl");
sdout << F(
"short line\n"
"\n"
"17 character line\n"
"too long for buffer\n"
"line with no nl");


sdout.close(); sdout.close();
} }
void testGetline() { void testGetline() {
const int line_buffer_size = 18; const int line_buffer_size = 18;
char buffer[line_buffer_size]; char buffer[line_buffer_size];
ifstream sdin("GETLINE.TXT");
ifstream sdin("getline.txt");
int line_number = 0; int line_number = 0;


while (sdin.getline(buffer, line_buffer_size, '\n') || sdin.gcount()) { while (sdin.getline(buffer, line_buffer_size, '\n') || sdin.gcount()) {
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo


// pstr stores strings in flash to save RAM // pstr stores strings in flash to save RAM
cout << pstr("Type any character to start\n");
cout << F("Type any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem


// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}


// make the test file // make the test file
makeTestFile(); makeTestFile();

+ 46
- 32
SdFat/examples/readCSV/readCSV.ino View File

/* /*
* This example reads a simple CSV, comma-separated values, file. * This example reads a simple CSV, comma-separated values, file.
* Each line of the file has three values, a long and two floats.
* Each line of the file has a label and three values, a long and two floats.
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD chip select pin // SD chip select pin
// create Serial stream // create Serial stream
ArduinoOutStream cout(Serial); ArduinoOutStream cout(Serial);


char fileName[] = "TESTFILE.CSV";
char fileName[] = "testfile.csv";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// store error strings in flash to save RAM // store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s)) #define error(s) sd.errorHalt(F(s))
float f1, f2; float f1, f2;
char text[10]; char text[10];
char c1, c2, c3; // space for commas. char c1, c2, c3; // space for commas.
// open input file // open input file
ifstream sdin(fileName); ifstream sdin(fileName);
// check for open error // check for open error
if (!sdin.is_open()) error("open");
if (!sdin.is_open()) {
error("open");
}

// read until input fails // read until input fails
while (1) { while (1) {
// Get text field. // Get text field.
sdin.get(text, sizeof(text), ','); sdin.get(text, sizeof(text), ',');
// Assume EOF if fail. // Assume EOF if fail.
if (sdin.fail()) break;
if (sdin.fail()) {
break;
}

// Get commas and numbers. // Get commas and numbers.
sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2; sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2;
// Skip CR/LF. // Skip CR/LF.
sdin.skipWhite(); sdin.skipWhite();
if (sdin.fail()) error("bad input");

if (sdin.fail()) {
error("bad input");
}

// error in line if not commas // error in line if not commas
if (c1 != ',' || c2 != ',' || c3 != ',') error("comma");
if (c1 != ',' || c2 != ',' || c3 != ',') {
error("comma");
}

// print in six character wide columns // print in six character wide columns
cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl; cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl;
} }
// Error in an input line if file is not at EOF. // Error in an input line if file is not at EOF.
if (!sdin.eof()) error("readFile");
if (!sdin.eof()) {
error("readFile");
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// write test file // write test file


// create or open and truncate output file // create or open and truncate output file
ofstream sdout(fileName); ofstream sdout(fileName);
// write file from string stored in flash // write file from string stored in flash
sdout << pstr(
"Line 1,1,2.3,4.5\n"
"Line 2,6,7.8,9.0\n"
"Line 3,9,8.7,6.5\n"
"Line 4,-4,-3.2,-1\n") << flush;
sdout << F(
"Line 1,1,2.3,4.5\n"
"Line 2,6,7.8,9.0\n"
"Line 3,9,8.7,6.5\n"
"Line 4,-4,-3.2,-1\n") << flush;


// check for any errors // check for any errors
if (!sdout) error("writeFile");
if (!sdout) {
error("writeFile");
}

sdout.close(); sdout.close();
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo
cout << pstr("Type any character to start\n");
cout << F("Type any character to start\n");
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance // breadboards. use SPI_FULL_SPEED for better performance
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}

// create test file // create test file
writeFile(); writeFile();
cout << endl; cout << endl;


// read and print test // read and print test
readFile();
readFile();
cout << "\nDone!" << endl; cout << "\nDone!" << endl;
} }
void loop() {} void loop() {}

+ 50
- 26
SdFat/examples/rename/rename.ino View File

/* /*
* This sketch demonstrates use of SdFile::rename()
* This program demonstrates use of SdFile::rename()
* and SdFat::rename(). * and SdFat::rename().
*/ */
#include <SPI.h>
#include <SPI.h>
#include <SdFat.h> #include <SdFat.h>


// SD chip select pin // SD chip select pin
Serial.begin(9600); Serial.begin(9600);
while (!Serial) {} // wait for Leonardo while (!Serial) {} // wait for Leonardo


cout << pstr("Insert an empty SD. Type any character to start.") << endl;
cout << F("Insert an empty SD. Type any character to start.") << endl;
while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(400); // catch Due reset problem delay(400); // catch Due reset problem


// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance. // breadboards. use SPI_FULL_SPEED for better performance.
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}


// Remove file/dirs from previous run.
if (sd.exists("dir2/DIR3/NAME3.txt")) {
cout << F("Removing /dir2/DIR3/NAME3.txt") << endl;
if (!sd.remove("dir2/DIR3/NAME3.txt") ||
!sd.rmdir("dir2/DIR3/") ||
!sd.rmdir("dir2/")) {
error("remove/rmdir failed");
}
}
// create a file and write one line to the file // create a file and write one line to the file
SdFile file("NAME1.TXT", O_WRITE | O_CREAT);
if (!file.isOpen()) error("NAME1");
file.println("A test line for NAME1.TXT");
SdFile file("Name1.txt", O_WRITE | O_CREAT);
if (!file.isOpen()) {
error("Name1.txt");
}
file.println("A test line for Name1.txt");


// rename the file NAME2.TXT and add a line.
// rename the file name2.txt and add a line.
// sd.vwd() is the volume working directory, root. // sd.vwd() is the volume working directory, root.
if (!file.rename(sd.vwd(), "NAME2.TXT")) error("NAME2");
file.println("A test line for NAME2.TXT");
if (!file.rename(sd.vwd(), "name2.txt")) {
error("name2.txt");
}
file.println("A test line for name2.txt");


// list files // list files
cout << pstr("------") << endl;
cout << F("------") << endl;
sd.ls(LS_R); sd.ls(LS_R);


// make a new directory - "DIR1"
if (!sd.mkdir("DIR1")) error("DIR1");
// make a new directory - "Dir1"
if (!sd.mkdir("Dir1")) {
error("Dir1");
}


// move file into DIR1, rename it NAME3.TXT and add a line
if (!file.rename(sd.vwd(), "DIR1/NAME3.TXT")) error("NAME3");
file.println("A line for DIR1/NAME3.TXT");
// move file into Dir1, rename it NAME3.txt and add a line
if (!file.rename(sd.vwd(), "Dir1/NAME3.txt")) {
error("NAME3.txt");
}
file.println("A line for Dir1/NAME3.txt");


// list files // list files
cout << pstr("------") << endl;
cout << F("------") << endl;
sd.ls(LS_R); sd.ls(LS_R);


// make directory "DIR2"
if (!sd.mkdir("DIR2")) error("DIR2");
// make directory "dir2"
if (!sd.mkdir("dir2")) {
error("dir2");
}


// close file before rename(oldPath, newPath) // close file before rename(oldPath, newPath)
file.close(); file.close();


// move DIR1 into DIR2 and rename it DIR3
if (!sd.rename("DIR1", "DIR2/DIR3")) error("DIR2/DIR3");
// move Dir1 into dir2 and rename it DIR3
if (!sd.rename("Dir1", "dir2/DIR3")) {
error("dir2/DIR3");
}


// open file for append in new location and add a line // open file for append in new location and add a line
if (!file.open("DIR2/DIR3/NAME3.TXT", O_WRITE | O_APPEND)) {
error("DIR2/DIR3/NAME3.TXT");
if (!file.open("dir2/DIR3/NAME3.txt", O_WRITE | O_APPEND)) {
error("dir2/DIR3/NAME3.txt");
} }
file.println("A line for DIR2/DIR3/NAME3.TXT");
file.println("A line for dir2/DIR3/NAME3.txt");
file.close();


// list files // list files
cout << pstr("------") << endl;
cout << F("------") << endl;
sd.ls(LS_R); sd.ls(LS_R);


cout << pstr("Done") << endl;
cout << F("Done") << endl;
} }
void loop() {} void loop() {}

+ 32
- 14
SdFat/utility/ArduinoStream.h View File

while (1) { while (1) {
t = millis(); t = millis();
while (!m_hw->available()) { while (!m_hw->available()) {
if ((millis() - t) > 10) goto done;
if ((millis() - t) > 10) {
goto done;
}
} }
if (i >= (m_size - 1)) { if (i >= (m_size - 1)) {
setstate(failbit); setstate(failbit);
m_line[i++] = m_hw->read(); m_line[i++] = m_hw->read();
m_line[i] = '\0'; m_line[i] = '\0';
} }
done:
done:
init(m_line); init(m_line);
} }


* \param[in] way * \param[in] way
* \return true/false. * \return true/false.
*/ */
bool seekoff(off_type off, seekdir way) {return false;}
/** Internal - do not use.
* \param[in] pos
* \return true/false.
*/
bool seekpos(pos_type pos) {return false;}
bool seekoff(off_type off, seekdir way) {
return false;
}
/** Internal - do not use.
* \param[in] pos
* \return true/false.
*/
bool seekpos(pos_type pos) {
return false;
}


private: private:
char *m_line; char *m_line;
* \param[in] c * \param[in] c
*/ */
void putch(char c) { void putch(char c) {
if (c == '\n') m_pr->write('\r');
if (c == '\n') {
m_pr->write('\r');
}
m_pr->write(c); m_pr->write(c);
} }
void putstr(const char* str) {m_pr->write(str);}
bool seekoff(off_type off, seekdir way) {return false;}
bool seekpos(pos_type pos) {return false;}
bool sync() {return true;}
pos_type tellpos() {return 0;}
void putstr(const char* str) {
m_pr->write(str);
}
bool seekoff(off_type off, seekdir way) {
return false;
}
bool seekpos(pos_type pos) {
return false;
}
bool sync() {
return true;
}
pos_type tellpos() {
return 0;
}
/// @endcond /// @endcond
private: private:
ArduinoOutStream() {} ArduinoOutStream() {}

+ 19
- 13
SdFat/utility/DigitalPin.h View File

fastDigitalWrite(pin, !fastDigitalRead(pin)); fastDigitalWrite(pin, !fastDigitalRead(pin));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
inline void fastPinMode(uint8_t pin, bool mode) {pinMode(pin, mode);}
inline void fastPinMode(uint8_t pin, bool mode) {
pinMode(pin, mode);
}
#else // __arm__ #else // __arm__
#include <avr/io.h> #include <avr/io.h>
#include <util/atomic.h> #include <util/atomic.h>
//============================================================================== //==============================================================================
/** generate bad pin number error */ /** generate bad pin number error */
void badPinNumber(void) void badPinNumber(void)
__attribute__((error("Pin number is too large or not a constant")));
__attribute__((error("Pin number is too large or not a constant")));
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** Check for valid pin number /** Check for valid pin number
* @param[in] pin Number of pin to be checked. * @param[in] pin Number of pin to be checked.
static inline __attribute__((always_inline)) static inline __attribute__((always_inline))
void badPinCheck(uint8_t pin) { void badPinCheck(uint8_t pin) {
if (!__builtin_constant_p(pin) || pin >= digitalPinCount) { if (!__builtin_constant_p(pin) || pin >= digitalPinCount) {
badPinNumber();
badPinNumber();
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static inline __attribute__((always_inline)) static inline __attribute__((always_inline))
void fastDigitalToggle(uint8_t pin) { void fastDigitalToggle(uint8_t pin) {
badPinCheck(pin); badPinCheck(pin);
if (pinMap[pin].pin > reinterpret_cast<uint8_t*>(0X5F)) {
// must write bit to high address port
*pinMap[pin].pin = 1 << pinMap[pin].bit;
} else {
// will compile to sbi and PIN register will not be read.
*pinMap[pin].pin |= 1 << pinMap[pin].bit;
}
if (pinMap[pin].pin > reinterpret_cast<uint8_t*>(0X5F)) {
// must write bit to high address port
*pinMap[pin].pin = 1 << pinMap[pin].bit;
} else {
// will compile to sbi and PIN register will not be read.
*pinMap[pin].pin |= 1 << pinMap[pin].bit;
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** Set pin value /** Set pin value
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** set pin configuration /** set pin configuration
* @param[in] mode If true set output mode else input mode * @param[in] mode If true set output mode else input mode
* @param[in] level If mode is output, set level high/low. If mode
* @param[in] level If mode is output, set level high/low. If mode
* is input, enable or disable the pin's 20K pull-up. * is input, enable or disable the pin's 20K pull-up.
*/ */
inline __attribute__((always_inline)) inline __attribute__((always_inline))
* Set pin level high if output mode or enable 20K pull-up if input mode. * Set pin level high if output mode or enable 20K pull-up if input mode.
*/ */
inline __attribute__((always_inline)) inline __attribute__((always_inline))
void high() {write(true);}
void high() {
write(true);
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** /**
* Set pin level low if output mode or disable 20K pull-up if input mode. * Set pin level low if output mode or disable 20K pull-up if input mode.
*/ */
inline __attribute__((always_inline)) inline __attribute__((always_inline))
void low() {write(false);}
void low() {
write(false);
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** /**
* Set pin mode * Set pin mode

+ 7
- 5
SdFat/utility/FatApiConstants.h View File



// FatFile class static and const definitions // FatFile class static and const definitions
// flags for ls() // flags for ls()
/** ls() flag to print modify date */
uint8_t const LS_DATE = 1;
/** ls() flag to print file size */
uint8_t const LS_SIZE = 2;
/** ls() flag for list all files including hidden. */
uint8_t const LS_A = 1;
/** ls() flag to print modify. date */
uint8_t const LS_DATE = 2;
/** ls() flag to print file size. */
uint8_t const LS_SIZE = 4;
/** ls() flag for recursive list of subdirectories */ /** ls() flag for recursive list of subdirectories */
uint8_t const LS_R = 4;
uint8_t const LS_R = 8;


// flags for timestamp // flags for timestamp
/** set the file's last access date */ /** set the file's last access date */

+ 497
- 530
SdFat/utility/FatFile.cpp
File diff suppressed because it is too large
View File


+ 258
- 175
SdFat/utility/FatFile.h View File

* \file * \file
* \brief FatFile class * \brief FatFile class
*/ */
#include <ctype.h>
// #include <ctype.h>
#include <string.h> #include <string.h>
#include <stddef.h> #include <stddef.h>
#include <limits.h> #include <limits.h>
#include "FatVolume.h" #include "FatVolume.h"
class FatFileSystem; class FatFileSystem;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(ARDUINO) || defined(DOXYGEN)
#include <Arduino.h>
/** Use Print on Arduino */
typedef Print print_t;
#else // ARDUINO
// Arduino type for flash string.
class __FlashStringHelper;
/**
* \class CharWriter
* \brief Character output - often serial port.
*/
class CharWriter {
public:
virtual size_t write(char c) = 0;
virtual size_t write(const char* s) = 0;
};
typedef Print print_t;
#endif // ARDUINO
//------------------------------------------------------------------------------
// Stuff to store strings in AVR flash. // Stuff to store strings in AVR flash.
#ifdef __AVR__ #ifdef __AVR__
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
uint32_t cluster; uint32_t cluster;
FatPos_t() : position(0), cluster(0) {} FatPos_t() : position(0), cluster(0) {}
}; };
//------------------------------------------------------------------------------
/** Expression for path name separator. */
#define isDirSeparator(c) ((c) == '/')
//------------------------------------------------------------------------------
/**
* \struct fname_t
* \brief Internal type for Short File Name - do not use in user apps.
*/
struct fname_t {
/** Flags for base and extension character case and LFN. */
uint8_t flags;
/** length of Long File Name */
size_t len;
/** Long File Name start. */
const char* lfn;
/** position for sequence number */
uint8_t seqPos;
/** Short File Name */
uint8_t sfn[11];
};
/** Derived from a LFN with loss or conversion of characters. */
const uint8_t FNAME_FLAG_LOST_CHARS = 0X01;
/** Base-name or extension has mixed case. */
const uint8_t FNAME_FLAG_MIXED_CASE = 0X02;
/** LFN entries are required for file name. */
const uint8_t FNAME_FLAG_NEED_LFN =
FNAME_FLAG_LOST_CHARS | FNAME_FLAG_MIXED_CASE;
/** Filename base-name is all lower case */
const uint8_t FNAME_FLAG_LC_BASE = DIR_NT_LC_BASE;
/** Filename extension is all lower case. */
const uint8_t FNAME_FLAG_LC_EXT = DIR_NT_LC_EXT;
//============================================================================== //==============================================================================
/** /**
* \class FatFile * \class FatFile
*/ */
class FatFile { class FatFile {
public: public:
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// development tests - DO NOT USE!
bool createLfn(FatFile* dirFile, ////////////////////////////////////////////////////
uint16_t bgnIndex, char* name, uint8_t oflag); ///////////////////////
bool findLfn(const char* name, uint16_t* bgnIndex, uint16_t *endIndex); /////////////
bool findSfn(uint8_t* sfn, uint16_t* index); ////////////////////////////////////////
#endif // DOXYGEN_SHOULD_SKIP_THIS
/** Create an instance. */ /** Create an instance. */
FatFile() : m_writeError(false), m_attr(FILE_ATTR_CLOSED) {}
FatFile() : m_attr(FILE_ATTR_CLOSED), m_error(0) {}
/** Create a file object and open it in the current working directory. /** Create a file object and open it in the current working directory.
* *
* \param[in] path A path with a valid 8.3 DOS name for a file to be opened. * \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
*/ */
FatFile(const char* path, uint8_t oflag) { FatFile(const char* path, uint8_t oflag) {
m_attr = FILE_ATTR_CLOSED; m_attr = FILE_ATTR_CLOSED;
m_writeError = false;
m_error = 0;
open(path, oflag); open(path, oflag);
} }
#if DESTRUCTOR_CLOSES_FILE #if DESTRUCTOR_CLOSES_FILE
~FatFile() {if(isOpen()) close();}
~FatFile() {
if (isOpen()) {
close();
}
}
#endif // DESTRUCTOR_CLOSES_FILE #endif // DESTRUCTOR_CLOSES_FILE


/** \return value of writeError */ /** \return value of writeError */
bool getWriteError() {return m_writeError;}
bool getWriteError() {
return m_error & WRITE_ERROR;
}
/** Set writeError to zero */ /** Set writeError to zero */
void clearWriteError() {m_writeError = 0;}
//----------------------------------------------------------------------------
// helpers for stream classes
void clearWriteError() {
m_error &= ~WRITE_ERROR;
}
/** Clear all error bits. */
void clearError() {
m_error = 0;
}
/** \return All error bits. */
uint8_t getError() {
return m_error;
}
/** get position for streams /** get position for streams
* \param[out] pos struct to receive position * \param[out] pos struct to receive position
*/ */
* \param[out] pos struct with value for new position * \param[out] pos struct with value for new position
*/ */
void setpos(FatPos_t* pos); void setpos(FatPos_t* pos);
//----------------------------------------------------------------------------
/** \return number of bytes available from the current position to EOF */
uint32_t available() {return fileSize() - curPosition();}
/** \return The number of bytes available from the current position
* to EOF for normal files. Zero is returned for directory files.
*/
uint32_t available() {
return isFile() ? fileSize() - curPosition() : 0;
}
/** Close a file and force cached data and directory information /** Close a file and force cached data and directory information
* to be written to the storage device. * to be written to the storage device.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include no file is open or an I/O error.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool close(); bool close();
/** Check for contiguous file and return its raw block range. /** Check for contiguous file and return its raw block range.
* \param[out] bgnBlock the first block address for the file. * \param[out] bgnBlock the first block address for the file.
* \param[out] endBlock the last block address for the file. * \param[out] endBlock the last block address for the file.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include file is not contiguous, file has zero length
* or an I/O error occurred.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
/** Create and open a new contiguous file of a specified size. /** Create and open a new contiguous file of a specified size.
* \param[in] path A path with a valid DOS 8.3 file name. * \param[in] path A path with a valid DOS 8.3 file name.
* \param[in] size The desired file size. * \param[in] size The desired file size.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include \a path contains
* an invalid DOS 8.3 file name, the FAT volume has not been initialized,
* a file is already open, the file already exists, the root
* directory is full or an I/O error.
*
* \return The value true is returned for success and
* the value false, is returned for failure.
*/ */
bool createContiguous(FatFile* dirFile, bool createContiguous(FatFile* dirFile,
const char* path, uint32_t size);
const char* path, uint32_t size);
/** \return The current cluster number for a file or directory. */ /** \return The current cluster number for a file or directory. */
uint32_t curCluster() const {return m_curCluster;}
uint32_t curCluster() const {
return m_curCluster;
}
/** \return The current position for a file or directory. */ /** \return The current position for a file or directory. */
uint32_t curPosition() const {return m_curPosition;}
uint32_t curPosition() const {
return m_curPosition;
}
/** \return Current working directory */ /** \return Current working directory */
static FatFile* cwd() {return m_cwd;}
static FatFile* cwd() {
return m_cwd;
}
/** Set the date/time callback function /** Set the date/time callback function
* *
* \param[in] dateTime The user's call back function. The callback * \param[in] dateTime The user's call back function. The callback
m_dateTime = dateTime; m_dateTime = dateTime;
} }
/** Cancel the date/time callback function. */ /** Cancel the date/time callback function. */
static void dateTimeCallbackCancel() {m_dateTime = 0;}
static void dateTimeCallbackCancel() {
m_dateTime = 0;
}
/** Return a file's directory entry. /** Return a file's directory entry.
* *
* \param[out] dir Location for return of the file's directory entry. * \param[out] dir Location for return of the file's directory entry.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool dirEntry(dir_t* dir); bool dirEntry(dir_t* dir);

/** /**
* \return The index of this file in it's directory. * \return The index of this file in it's directory.
*/ */
uint16_t dirIndex() {return m_dirIndex;}
uint16_t dirIndex() {
return m_dirIndex;
}
/** Format the name field of \a dir into the 13 byte array /** Format the name field of \a dir into the 13 byte array
* \a name in standard 8.3 short name format. * \a name in standard 8.3 short name format.
* *
* \return length of the name. * \return length of the name.
*/ */
static uint8_t dirName(const dir_t* dir, char* name); static uint8_t dirName(const dir_t* dir, char* name);
/** \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 /** Test for the existence of a file in a directory
* *
* \param[in] path Path of the file to be tested for. * \param[in] path Path of the file to be tested for.
* If no data is read, fgets() returns zero for EOF or -1 if an error occurred. * If no data is read, fgets() returns zero for EOF or -1 if an error occurred.
*/ */
int16_t fgets(char* str, int16_t num, char* delim = 0); int16_t fgets(char* str, int16_t num, char* delim = 0);
/** \return The total number of bytes in a file or directory. */
uint32_t fileSize() const {return m_fileSize;}
/** \return The total number of bytes in a file. */
uint32_t fileSize() const {
return m_fileSize;
}
/** \return The first cluster number for a file or directory. */ /** \return The first cluster number for a file or directory. */
uint32_t firstCluster() const {return m_firstCluster;}
/** Get a file's name
*
* \param[out] name An array of 13 characters for the file's name.
uint32_t firstCluster() const {
return m_firstCluster;
}
/**
* Get a file's name followed by a zero byte.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \param[out] name An array of characters for the file's name.
* \param[in] size The size of the array in bytes. The array
* must be at least 13 bytes long. The file name will be
* truncated if it is too long.
* \return The value true, is returned for success and
* the value false, is returned for failure.
*/ */
bool getFilename(char* name);

bool getFilename(char* name, size_t size);
/**
* Get a file's Short File Name followed by a zero byte.
*
* \param[out] name An array of characters for the file's name.
* The array must be at least 13 bytes long.
* \return The value true, is returned for success and
* the value false, is returned for failure.
*/
bool getSFN(char* name);
/** \return True if this is a directory else false. */ /** \return True if this is a directory else false. */
bool isDir() const {return m_attr & FILE_ATTR_DIR;}
bool isDir() const {
return m_attr & FILE_ATTR_DIR;
}
/** \return True if this is a normal file else false. */ /** \return True if this is a normal file else false. */
bool isFile() const {return !isDir();}
bool isFile() const {
return m_attr & FILE_ATTR_FILE;
}
/** \return True if this is a hidden file else false. */ /** \return True if this is a hidden file else false. */
bool isHidden() const {return m_attr & FILE_ATTR_HIDDEN;}
bool isHidden() const {
return m_attr & FILE_ATTR_HIDDEN;
}
/** \return true if this file has a Long File Name. */
bool isLFN() const {
return m_lfnOrd;
}
/** \return True if this is an open file/directory else false. */ /** \return True if this is an open file/directory else false. */
bool isOpen() const {return m_attr & FILE_ATTR_IS_OPEN;}
bool isOpen() const {
return m_attr;
}
/** \return True if this is the root directory. */ /** \return True if this is the root directory. */
bool isRoot() const {return m_attr & FILE_ATTR_ROOT;}
/** \return True if file is read-only */
bool isReadOnly() const {return m_attr & FILE_ATTR_READ_ONLY;}
bool isRoot() const {
return m_attr & FILE_ATTR_ROOT;
}
/** \return True if this is the FAT32 root directory. */
bool isRoot32() const {
return m_attr & FILE_ATTR_ROOT32;
}
/** \return True if this is the FAT12 of FAT16 root directory. */ /** \return True if this is the FAT12 of FAT16 root directory. */
bool isRootFixed() const {return m_attr & FILE_ATTR_ROOT_FIXED;}
bool isRootFixed() const {
return m_attr & FILE_ATTR_ROOT_FIXED;
}
/** \return True if file is read-only */
bool isReadOnly() const {
return m_attr & FILE_ATTR_READ_ONLY;
}
/** \return True if this is a subdirectory else false. */ /** \return True if this is a subdirectory else false. */
bool isSubDir() const {return m_attr & FILE_ATTR_SUBDIR;}
bool isSubDir() const {
return m_attr & FILE_ATTR_SUBDIR;
}
/** \return True if this is a system file else false. */ /** \return True if this is a system file else false. */
bool isSystem() const {return m_attr & FILE_ATTR_SYSTEM;}

bool isSystem() const {
return m_attr & FILE_ATTR_SYSTEM;
}
/** Check for a legal 8.3 character.
* \param[in] c Character to be checked.
* \return true for a legal 8.3 character else false.
*/
static bool legal83Char(uint8_t c) {
if (c == '"' || c == '|') {
return false;
}
// *+,./
if (0X2A <= c && c <= 0X2F && c != 0X2D) {
return false;
}
// :;<=>?
if (0X3A <= c && c <= 0X3F) {
return false;
}
// [\]
if (0X5B <= c && c <= 0X5D) {
return false;
}
return 0X20 < c && c < 0X7F;
}
/** List directory contents. /** List directory contents.
* *
* \param[in] pr Print stream for list. * \param[in] pr Print stream for list.
* *
* \param[in] pFlag Create missing parent directories if true. * \param[in] pFlag Create missing parent directories if true.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include this file is already open, \a parent is not a
* directory, \a path is invalid or already exists in \a parent.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool mkdir(FatFile* dir, const char* path, bool pFlag = true); bool mkdir(FatFile* dir, const char* path, bool pFlag = true);
/** Open a file in the volume working directory of a FatFileSystem. /** Open a file in the volume working directory of a FatFileSystem.
* \param[in] oflag bitwise-inclusive OR of open mode flags. * \param[in] oflag bitwise-inclusive OR of open mode flags.
* See see FatFile::open(FatFile*, const char*, uint8_t). * See see FatFile::open(FatFile*, const char*, uint8_t).
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool open(FatFileSystem* fs, const char* path, uint8_t oflag); bool open(FatFileSystem* fs, const char* path, uint8_t oflag);
/** Open a file by index. /** Open a file by index.
* \note Directory files must be opened read only. Write and truncation is * \note Directory files must be opened read only. Write and truncation is
* not allowed for directory files. * not allowed for directory files.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include this file is already open, \a dirFile is not
* a directory, \a path is invalid, the file does not exist
* or can't be opened in the access mode specified by oflag.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool open(FatFile* dirFile, const char* path, uint8_t oflag); bool open(FatFile* dirFile, const char* path, uint8_t oflag);
/** Open a file in the current working directory. /** Open a file in the current working directory.
* \param[in] oflag bitwise-inclusive OR of open mode flags. * \param[in] oflag bitwise-inclusive OR of open mode flags.
* See see FatFile::open(FatFile*, const char*, uint8_t). * See see FatFile::open(FatFile*, const char*, uint8_t).
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool open(const char* path, uint8_t oflag = O_READ) { bool open(const char* path, uint8_t oflag = O_READ) {
return open(m_cwd, path, oflag); return open(m_cwd, path, oflag);
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool openNext(FatFile* dirFile, uint8_t oflag = O_READ); bool openNext(FatFile* dirFile, uint8_t oflag = O_READ);
/** Open the next file or subdirectory in a directory and return the
* file name. The Long %File Name, LFN, is returned if present otherwise
* the 8.3 short name will be returned.
*
* \param[in] dirFile An open FatFile instance for the directory
* containing the file to be opened.
*
* \param[out] name Location that will receive the name.
*
* \param[in] size The size of the name array.
*
* \param[in] oflag bitwise-inclusive OR of open mode flags.
* See see FatFile::open(FatFile*, const char*, uint8_t).
*
* \return For success, the length of the returned name. The name
* will be truncated to size - 1 bytes followed by a zero byte.
* For EOF, zero will be returned. If an error occurs, -1 is
* returned.
*/
int openNextLFN(FatFile* dirFile, char* name, size_t size, uint8_t oflag);
/** Open a volume's root directory. /** Open a volume's root directory.
* *
* \param[in] vol The FAT volume containing the root directory to be opened. * \param[in] vol The FAT volume containing the root directory to be opened.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include the file is already open, the FAT volume has
* not been initialized or it a FAT12 volume.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool openRoot(FatVolume* vol); bool openRoot(FatVolume* vol);
/** Return the next available byte without consuming it. /** Return the next available byte without consuming it.
* *
* \param[in] pr Print stream for output. * \param[in] pr Print stream for output.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool printCreateDateTime(print_t* pr); bool printCreateDateTime(print_t* pr);
/** %Print a directory date field. /** %Print a directory date field.
* *
* \param[in] pr Print stream for output. * \param[in] pr Print stream for output.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool printModifyDateTime(print_t* pr); bool printModifyDateTime(print_t* pr);
/** Print a file's name /** Print a file's name
* *
* \param[in] pr Print stream for output. * \param[in] pr Print stream for output.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
size_t printName(print_t* pr); size_t printName(print_t* pr);
/** Print a file's size. /** Print a file's size.
* *
* \param[in] pr Print stream for output. * \param[in] pr Print stream for output.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The number of characters printed is returned
* for success and zero is returned for failure.
*/ */
size_t printFileSize(print_t* pr); size_t printFileSize(print_t* pr);
/** Print a file's Short File Name.
*
* \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 printSFN(print_t* pr);
/** Read the next byte from a file. /** Read the next byte from a file.
* *
* \return For success read returns the next byte in the file as an int. * \return For success read returns the next byte in the file as an int.
* file that has a long name. For example if a file has the long name * file that has a long name. For example if a file has the long name
* "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include the file read-only, is a directory,
* or an I/O error occurred.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool remove(); bool remove();
/** Remove a file. /** Remove a file.
* file that has a long name. For example if a file has the long name * file that has a long name. For example if a file has the long name
* "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include the file is a directory, is read only,
* \a dirFile is not a directory, \a path is not found
* or an I/O error occurred.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
static bool remove(FatFile* dirFile, const char* path); static bool remove(FatFile* dirFile, const char* path);
/** Set the file's current position to zero. */ /** Set the file's current position to zero. */
void rewind() {seekSet(0);}
void rewind() {
seekSet(0);
}
/** Rename a file or subdirectory. /** Rename a file or subdirectory.
* *
* \param[in] dirFile Directory for the new path. * \param[in] dirFile Directory for the new path.
* \param[in] newPath New path name for the file/directory. * \param[in] newPath New path name for the file/directory.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include \a dirFile is not open or is not a directory
* file, newPath is invalid or already exists, or an I/O error occurs.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool rename(FatFile* dirFile, const char* newPath); bool rename(FatFile* dirFile, const char* newPath);
/** Remove a directory file. /** Remove a directory file.
* directory that has a long name. For example if a directory has the * directory that has a long name. For example if a directory has the
* long name "New folder" you should not delete the 8.3 name "NEWFOL~1". * long name "New folder" you should not delete the 8.3 name "NEWFOL~1".
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include the file is not a directory, is the root
* directory, is not empty, or an I/O error occurred.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool rmdir(); bool rmdir();
/** Recursively delete a directory and all contained files. /** Recursively delete a directory and all contained files.
* \note This function should not be used to delete the 8.3 version of * \note This function should not be used to delete the 8.3 version of
* a directory that has a long name. See remove() and rmdir(). * a directory that has a long name. See remove() and rmdir().
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool rmRfStar(); bool rmRfStar();
/** Set the files position to current position + \a pos. See seekSet(). /** Set the files position to current position + \a pos. See seekSet().
return seekSet(m_curPosition + offset); return seekSet(m_curPosition + offset);
} }
/** Set the files position to end-of-file + \a offset. See seekSet(). /** Set the files position to end-of-file + \a offset. See seekSet().
* Can't be used for directory files since file size is not defined.
* \param[in] offset The new position in bytes from end-of-file. * \param[in] offset The new position in bytes from end-of-file.
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool seekEnd(int32_t offset = 0) {return seekSet(m_fileSize + offset);}
bool seekEnd(int32_t offset = 0) {
return isFile() ? seekSet(m_fileSize + offset) : false;
}
/** Sets a file's position. /** Sets a file's position.
* *
* \param[in] pos The new position in bytes from the beginning of the file. * \param[in] pos The new position in bytes from the beginning of the file.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool seekSet(uint32_t pos); bool seekSet(uint32_t pos);
/** Set the current working directory. /** Set the current working directory.
* \return true for success else false. * \return true for success else false.
*/ */
static bool setCwd(FatFile* dir) { static bool setCwd(FatFile* dir) {
if (!dir->isDir()) return false;
if (!dir->isDir()) {
return false;
}
m_cwd = dir; m_cwd = dir;
return true; return true;
} }
/** The sync() call causes all modified data and directory fields /** The sync() call causes all modified data and directory fields
* to be written to the storage device. * to be written to the storage device.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include a call to sync() before a file has been
* opened or an I/O error.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool sync(); bool sync();
/** Copy a file's timestamps /** Copy a file's timestamps
* Modify and access timestamps may be overwritten if a date time callback * Modify and access timestamps may be overwritten if a date time callback
* function has been set by dateTimeCallback(). * function has been set by dateTimeCallback().
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool timestamp(FatFile* file); bool timestamp(FatFile* file);
/** Set a file's timestamps in its directory entry. /** Set a file's timestamps in its directory entry.
* Modify and access timestamps may be overwritten if a date time callback * Modify and access timestamps may be overwritten if a date time callback
* function has been set by dateTimeCallback(). * function has been set by dateTimeCallback().
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool timestamp(uint8_t flags, uint16_t year, uint8_t month, uint8_t day, bool timestamp(uint8_t flags, uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute, uint8_t second);
uint8_t hour, uint8_t minute, uint8_t second);
/** Type of file. You should use isFile() or isDir() instead of fileType() /** Type of file. You should use isFile() or isDir() instead of fileType()
* if possible. * if possible.
* *
* \return The file or directory type. * \return The file or directory type.
*/ */
uint8_t fileAttr() const {return m_attr;}
uint8_t fileAttr() const {
return m_attr;
}
/** Truncate a file to a specified length. The current file position /** Truncate a file to a specified length. The current file position
* will be maintained if it is less than or equal to \a length otherwise * will be maintained if it is less than or equal to \a length otherwise
* it will be set to end of file. * it will be set to end of file.
* *
* \param[in] length The desired length for the file. * \param[in] length The desired length for the file.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include file is read only, file is a directory,
* \a length is greater than the current file size or an I/O error occurs.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool truncate(uint32_t length); bool truncate(uint32_t length);
/** \return FatVolume that contains this file. */ /** \return FatVolume that contains this file. */
FatVolume* volume() const {return m_vol;}
FatVolume* volume() const {
return m_vol;
}
/** Write a single byte. /** Write a single byte.
* \param[in] b The byte to be written. * \param[in] b The byte to be written.
* \return +1 for success or -1 for failure. * \return +1 for success or -1 for failure.
*/ */
int write(uint8_t b) {return write(&b, 1);}
int write(uint8_t b) {
return write(&b, 1);
}
/** Write data to an open file. /** Write data to an open file.
* *
* \note Data is moved to the cache but may not be written to the * \note Data is moved to the cache but may not be written to the
/** Entry is for a system file. */ /** Entry is for a system file. */
static const uint8_t FILE_ATTR_SYSTEM = DIR_ATT_SYSTEM; static const uint8_t FILE_ATTR_SYSTEM = DIR_ATT_SYSTEM;
/** Entry for normal data file */ /** Entry for normal data file */
static const uint8_t FILE_ATTR_IS_OPEN = 0X08;
static const uint8_t FILE_ATTR_FILE = 0X08;
/** Entry is for a subdirectory */ /** Entry is for a subdirectory */
static const uint8_t FILE_ATTR_SUBDIR = DIR_ATT_DIRECTORY; static const uint8_t FILE_ATTR_SUBDIR = DIR_ATT_DIRECTORY;
/** A FAT12 or FAT16 root directory */ /** A FAT12 or FAT16 root directory */
DIR_ATT_SYSTEM | DIR_ATT_DIRECTORY; DIR_ATT_SYSTEM | DIR_ATT_DIRECTORY;


/** experimental don't use */ /** experimental don't use */

bool openParent(FatFile* dir); bool openParent(FatFile* dir);


// private functions // private functions
bool addCluster(); bool addCluster();
bool addDirCluster(); bool addDirCluster();
dir_t* cacheDirEntry(uint8_t action); dir_t* cacheDirEntry(uint8_t action);
int8_t lsPrintNext(print_t* pr, uint8_t flags, uint8_t indent);
static bool make83Name(const char* str, uint8_t* name, const char** ptr);
bool mkdir(FatFile* parent, const uint8_t dname[11]);
bool open(FatFile* dirFile, const uint8_t dname[11], uint8_t oflag);
bool openCachedEntry(FatFile* dirFile, uint16_t cacheIndex, uint8_t oflag);
static uint8_t lfnChecksum(uint8_t* name);
bool lfnUniqueSfn(fname_t* fname);
bool openCluster(FatFile* file);
static bool parsePathName(const char* str, fname_t* fname, const char** ptr);
bool mkdir(FatFile* parent, fname_t* fname);
bool open(FatFile* dirFile, fname_t* fname, uint8_t oflag);
bool openCachedEntry(FatFile* dirFile, uint16_t cacheIndex, uint8_t oflag,
uint8_t lfnOrd);
bool readLBN(uint32_t* lbn); bool readLBN(uint32_t* lbn);
dir_t* readDirCache();
dir_t* readDirCache(bool skipReadOk = false);
bool setDirSize(); bool setDirSize();


// bits defined in m_flags // bits defined in m_flags
// data time callback function // data time callback function
static void (*m_dateTime)(uint16_t* date, uint16_t* time); static void (*m_dateTime)(uint16_t* date, uint16_t* time);
// private data // private data
bool m_writeError; // Set when a write error occurs
static const uint8_t WRITE_ERROR = 0X1;
static const uint8_t READ_ERROR = 0X2;
uint8_t m_attr; // File attributes uint8_t m_attr; // File attributes
uint8_t m_error; // Error bits.
uint8_t m_flags; // See above for definition of m_flags bits uint8_t m_flags; // See above for definition of m_flags bits
uint8_t m_lfnOrd;
uint16_t m_dirIndex; // index of directory entry in dir file uint16_t m_dirIndex; // index of directory entry in dir file
FatVolume* m_vol; // volume where file is located FatVolume* m_vol; // volume where file is located
uint32_t m_dirCluster;
uint32_t m_curCluster; // cluster for current file position uint32_t m_curCluster; // cluster for current file position
uint32_t m_curPosition; // current file position uint32_t m_curPosition; // current file position
uint32_t m_dirBlock; // block for this files directory entry uint32_t m_dirBlock; // block for this files directory entry
uint32_t m_dirFirstCluster; // first cluster of this file's directory
uint32_t m_fileSize; // file size in bytes uint32_t m_fileSize; // file size in bytes
uint32_t m_firstCluster; // first cluster of file uint32_t m_firstCluster; // first cluster of file
}; };

+ 593
- 212
SdFat/utility/FatFileLFN.cpp View File

* along with the FatLib Library. If not, see * along with the FatLib Library. If not, see
* <http://www.gnu.org/licenses/>. * <http://www.gnu.org/licenses/>.
*/ */
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#include "FatFile.h" #include "FatFile.h"
bool FatFile::findSfn(uint8_t sfn[11], uint16_t* index) {
uint16_t free = 0XFFFF;
uint16_t idx;
dir_t* dir;

if (!isDir()) {
//------------------------------------------------------------------------------
//
uint8_t FatFile::lfnChecksum(uint8_t* name) {
uint8_t sum = 0;
for (uint8_t i = 0; i < 11; i++) {
sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + name[i];
}
return sum;
}
#if USE_LONG_FILE_NAMES
//------------------------------------------------------------------------------
// Saves about 90 bytes of flash on 328 over tolower().
inline char lfnToLower(char c) {
return 'A' <= c && c <= 'Z' ? c + 'a' - 'A' : c;
}
//------------------------------------------------------------------------------
// Daniel Bernstein University of Illinois at Chicago.
// Original had + instead of ^
static uint16_t Bernstein(uint16_t hash, const char *str, size_t len) {
for (size_t i = 0; i < len; i++) {
// hash = hash * 33 ^ str[i];
hash = ((hash << 5) + hash) ^ str[i];
}
return hash;
}
//------------------------------------------------------------------------------
/**
* Fetch a 16-bit long file name character.
*
* \param[in] ldir Pointer to long file name directory entry.
* \param[in] i Index of character.
* \return The 16-bit character.
*/
static uint16_t lfnGetChar(ldir_t *ldir, uint8_t i) {
if (i < LDIR_NAME1_DIM) {
return ldir->name1[i];
} else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM)) {
return ldir->name2[i - LDIR_NAME1_DIM];
} else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM + LDIR_NAME2_DIM)) {
return ldir->name3[i - LDIR_NAME1_DIM - LDIR_NAME2_DIM];
}
return 0;
}
//------------------------------------------------------------------------------
static bool lfnGetName(ldir_t *ldir, char* name, size_t n) {
uint8_t i;
size_t k = 13*((ldir->ord & 0X1F) - 1);
for (i = 0; i < 13; i++) {
uint16_t c = lfnGetChar(ldir, i);
if (c == 0 || k >= n) {
break;
}
name[k++] = c >= 0X7F ? '?' : c;
}
// Terminate with zero byte if name fits.
if (k < n && (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY)) {
name[k] = 0;
}
// Truncate if name is too long.
name[n-1] = 0;
return true;
}
//------------------------------------------------------------------------------
inline bool lfnLegalChar(char c) {
if (c == '/' || c == '\\' || c == '"' || c == '*' ||
c == ':' || c == '<' || c == '>' || c == '?' || c == '|') {
return false;
}
return 0X1F < c && c < 0X7F;
}
//------------------------------------------------------------------------------
/**
* Store a 16-bit long file name character.
*
* \param[in] ldir Pointer to long file name directory entry.
* \param[in] i Index of character.
* \param[in] c The 16-bit character.
*/
static void lfnPutChar(ldir_t *ldir, uint8_t i, uint16_t c) {
if (i < LDIR_NAME1_DIM) {
ldir->name1[i] = c;
} else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM)) {
ldir->name2[i - LDIR_NAME1_DIM] = c;
} else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM + LDIR_NAME2_DIM)) {
ldir->name3[i - LDIR_NAME1_DIM - LDIR_NAME2_DIM] = c;
}
}
//------------------------------------------------------------------------------
static void lfnPutName(ldir_t *ldir, const char* name, size_t n) {
size_t k = 13*((ldir->ord & 0X1F) - 1);
for (uint8_t i = 0; i < 13; i++, k++) {
uint16_t c = k < n ? name[k] : k == n ? 0 : 0XFFFF;
lfnPutChar(ldir, i, c);
}
}
//==============================================================================
bool FatFile::getFilename(char* name, size_t size) {
FatFile dirFile;
ldir_t* ldir;
if (!isOpen() || size < 13) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
rewind();
while (m_curPosition < m_fileSize) {
idx = m_curPosition/32;
if ((0XF & idx) == 0) {
dir = readDirCache();
if (!dir) {
DBG_FAIL_MACRO;
goto fail;
}
} else {
m_curPosition += 32;
dir++;
if (!isLFN()) {
return getSFN(name);
}
if (!dirFile.openCluster(this)) {
DBG_FAIL_MACRO;
goto fail;
}
for (uint8_t ord = 1; ord <= m_lfnOrd; ord++) {
if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) {
DBG_FAIL_MACRO;
goto fail;
} }
// done if last entry
if (dir->name[0] == DIR_NAME_FREE || dir->name[0] == DIR_NAME_DELETED) {
if (free == 0XFFFF) free = idx;
if (dir->name[0] == DIR_NAME_FREE) goto fail;
} else if (DIR_IS_FILE_OR_SUBDIR(dir)) {
if (!memcmp(sfn, dir->name, 11)) {
*index = idx;
return true;
}
ldir = reinterpret_cast<ldir_t*>(dirFile.readDirCache());
if (!ldir) {
DBG_FAIL_MACRO;
goto fail;
}
if (ldir->attr != DIR_ATT_LONG_NAME) {
DBG_FAIL_MACRO;
goto fail;
}
if (ord != (ldir->ord & 0X1F)) {
DBG_FAIL_MACRO;
goto fail;
}
if (!lfnGetName(ldir, name, size)) {
DBG_FAIL_MACRO;
goto fail;
}
if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) {
return true;
} }
} }
// Fall into fail.
DBG_FAIL_MACRO;


fail:
*index = free;
fail:
name[0] = 0;
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool FatFile::createLfn(FatFile* dirFile,
uint16_t bgnIndex, char* name, uint8_t oflag) {
return false;
bool FatFile::openCluster(FatFile* file) {
if (file->m_dirCluster == 0) {
return openRoot(file->m_vol);
}
memset(this, 0, sizeof(FatFile));
m_attr = FILE_ATTR_SUBDIR;
m_flags = O_READ;
m_vol = file->m_vol;
m_firstCluster = file->m_dirCluster;
return true;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool FatFile::findLfn(const char* name,
uint16_t *bgnIndex, uint16_t* endIndex) {
bool fill;
bool haveLong = false;
uint8_t ndir;
uint8_t test;
uint16_t lfnBgn;
uint16_t curIndex = 0XFFFF;
uint16_t freeIndex = 0XFFFF;
uint8_t freeCount = 0;
bool FatFile::parsePathName(const char* path,
fname_t* fname, const char** ptr) {
char c;
bool is83;
uint8_t bit = DIR_NT_LC_BASE;
uint8_t lc = 0;
uint8_t uc = 0;
uint8_t i = 0;
uint8_t in = 7;
size_t end;
size_t len = 0;
int si;
int dot;

// Skip leading spaces.
while (*path == ' ') {
path++;
}
fname->lfn = path;

for (len = 0; ; len++) {
c = path[len];
if (c == 0 || isDirSeparator(c)) {
break;
}
if (!lfnLegalChar(c)) {
return false;
}
}
// Advance to next path component.
for (end = len; path[end] == ' ' || isDirSeparator(path[end]); end++) {}
*ptr = &path[end];

// Back over spaces and dots.
while (len) {
c = path[len - 1];
if (c != '.' && c != ' ') {
break;
}
len--;
}
// Max length of LFN is 255.
if (len > 255) {
return false;
}
fname->len = len;
// Blank file short name.
for (uint8_t k = 0; k < 11; k++) {
fname->sfn[k] = ' ';
}
// skip leading spaces and dots.
for (si = 0; path[si] == '.' || path[si] == ' '; si++) {}
// Not 8.3 if leading dot or space.
is83 = !si;

// find last dot.
for (dot = len - 1; dot >= 0 && path[dot] != '.'; dot--) {}
for (; si < len; si++) {
c = path[si];
if (c == ' ' || (c == '.' && dot != si)) {
is83 = false;
continue;
}
if (!legal83Char(c) && si != dot) {
is83 = false;
c = '_';
}
if (si == dot || i > in) {
if (in == 10) {
// Done - extension longer than three characters.
is83 = false;
break;
}
if (si != dot) {
is83 = false;
}
// Break if no dot and base-name is longer than eight characters.
if (si > dot) {
break;
}
si = dot;
in = 10; // Max index for full 8.3 name.
i = 8; // Place for extension.
bit = DIR_NT_LC_EXT; // bit for extension.
} else {
if ('a' <= c && c <= 'z') {
c += 'A' - 'a';
lc |= bit;
} else if ('A' <= c && c <= 'Z') {
uc |= bit;
}
fname->sfn[i++] = c;
if (i < 7) {
fname->seqPos = i;
}
}
}
if (fname->sfn[0] == ' ') {
return false;
}

if (is83) {
fname->flags = lc & uc ? FNAME_FLAG_MIXED_CASE : lc;
} else {
fname->flags = FNAME_FLAG_LOST_CHARS;
fname->sfn[fname->seqPos] = '~';
fname->sfn[fname->seqPos + 1] = '1';
}
return true;
}
//------------------------------------------------------------------------------
bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) {
bool fnameFound = false;
uint8_t lfnOrd = 0;
uint8_t freeNeed; uint8_t freeNeed;
uint8_t freeFound = 0;
uint8_t ord;
uint8_t chksum;
uint16_t freeIndex;
uint16_t curIndex;
dir_t* dir; dir_t* dir;
size_t len;
bool is83;
bool foundFree = false;
const char* ptr;
uint8_t name83[11];
if (!isDir()) {
ldir_t* ldir;
size_t len = fname->len;

if (!dirFile->isDir() || isOpen()) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
is83 = make83Name(name, name83, &ptr);
for (len = 0; name[len] !=0 && name[len] != '/'; len++) {}
// Assume LFN.
freeNeed = (len + 12)/13 + 1;
rewind();
// Number of directory entries needed.
freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + (len + 12)/13 : 1;

dirFile->rewind();
while (1) { while (1) {
curIndex = m_curPosition/32;
if (m_curPosition == m_fileSize) {
DBG_FAIL_MACRO;
goto fail;
}
// read entry into cache
dir = readDirCache();
curIndex = dirFile->m_curPosition/32;
dir = dirFile->readDirCache(true);
if (!dir) { if (!dir) {
DBG_FAIL_MACRO;
goto fail;
if (dirFile->getError()) {
DBG_FAIL_MACRO;
goto fail;
}
// At EOF
goto create;
} }
#if 1
if (dir->name[0] == DIR_NAME_DELETED) {
if (!foundFree) {
if (freeIndex == 0XFFFF) {
freeIndex = curIndex;
freeCount = 0;
}
if (++freeCount == freeNeed) {
foundFree = true;
}
if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == DIR_NAME_FREE) {
if (freeFound == 0) {
freeIndex = curIndex;
}
if (freeFound < freeNeed) {
freeFound++;
}
if (dir->name[0] == DIR_NAME_FREE) {
goto create;
} }
continue;
} else { } else {
if (!foundFree) freeIndex = 0XFFFF;
}
#endif
if (dir->name[0] == DIR_NAME_FREE) {
DBG_FAIL_MACRO;
goto fail;
if (freeFound < freeNeed) {
freeFound = 0;
}
} }
// skip empty slot or '.' or '..' // skip empty slot or '.' or '..'
if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
haveLong = false;
continue;
}
if (DIR_IS_LONG_NAME(dir)) {
lfnOrd = 0;
} else if (DIR_IS_LONG_NAME(dir)) {
ldir_t *ldir = reinterpret_cast<ldir_t*>(dir); ldir_t *ldir = reinterpret_cast<ldir_t*>(dir);
if (!haveLong) {
if ((ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) == 0) continue;
ndir = ldir->ord & ~LDIR_ORD_LAST_LONG_ENTRY;
if (ndir < 1 || ndir > 20) continue;
test = ldir->chksum;
haveLong = true;
lfnBgn = curIndex;
fill = true;
} else if (ldir->ord != --ndir || test != ldir->chksum) {
haveLong = false;
if (!lfnOrd) {
if ((ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) == 0) {
continue;
}
lfnOrd = ord = ldir->ord & 0X1F;
chksum = ldir->chksum;
} else if (ldir->ord != --ord || chksum != ldir->chksum) {
lfnOrd = 0;
continue; continue;
} }
size_t nOff = 13*(ndir -1);
for (int i = 12; i >= 0; i--) {
uint16_t u = lfnChar(ldir, i);

if (fill) {
if (u == 0 || u == 0XFFFF) {
continue;
}
if (len != (nOff + i +1)) {
haveLong = false;
break;
size_t k = 13*(ord - 1);
if (k >= len) {
// Not found.
lfnOrd = 0;
continue;
}
for (uint8_t i = 0; i < 13; i++) {
uint16_t u = lfnGetChar(ldir, i);
if (k == len) {
if (u != 0) {
// Not found.
lfnOrd = 0;
} }
fill = false;
break;
} }
if (u > 255 || toupper(u) != toupper(name[nOff + i])) {
haveLong = false;
if (u > 255 || lfnToLower(u) != lfnToLower(fname->lfn[k++])) {
// Not found.
lfnOrd = 0;
break; break;
} }
} }
} else if (DIR_IS_FILE_OR_SUBDIR(dir)) { } else if (DIR_IS_FILE_OR_SUBDIR(dir)) {
if (haveLong) {
uint8_t sum = 0;
for (uint8_t i = 0; i < 11; i++) {
sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + dir->name[i];
}
if (sum != test || ndir != 1) {
haveLong = false;
if (lfnOrd) {
if (1 == ord && lfnChecksum(dir->name) == chksum) {
goto found;
} }
DBG_FAIL_MACRO;
goto fail;
} }
if (haveLong) goto done;
if (is83 && !memcmp(dir->name, name83, sizeof(name83))) {
goto done;
if (!memcmp(dir->name, fname->sfn, sizeof(fname->sfn))) {
if (!(fname->flags & FNAME_FLAG_LOST_CHARS)) {
goto found;
}
fnameFound = true;
} }
} else { } else {
haveLong = false;
lfnOrd = 0;
} }
} }


done:
*bgnIndex = haveLong ? lfnBgn : curIndex;
*endIndex = curIndex;
found:
// Don't open if create only.
if (oflag & O_EXCL) {
DBG_FAIL_MACRO;
goto fail;
}
goto open;

create:
// don't create unless O_CREAT and O_WRITE
if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) {
DBG_FAIL_MACRO;
goto fail;
}
// If at EOF start in next cluster.
if (freeFound == 0) {
freeIndex = curIndex;
}

while (freeFound < freeNeed) {
dir = dirFile->readDirCache();
if (!dir) {
if (dirFile->getError()) {
DBG_FAIL_MACRO;
goto fail;
}
// EOF if no error.
break;
}
freeFound++;
}
while (freeFound < freeNeed) {
// Will fail if FAT16 root.
if (!dirFile->addDirCluster()) {
DBG_FAIL_MACRO;
goto fail;
}
// Done if more than one block per cluster. Max freeNeed is 21.
if (dirFile->m_vol->blocksPerCluster() > 1) {
break;
}
freeFound += 16;
}
if (fnameFound) {
if (!dirFile->lfnUniqueSfn(fname)) {
goto fail;
}
}
if (!dirFile->seekSet(32UL*freeIndex)) {
DBG_FAIL_MACRO;
goto fail;
}
lfnOrd = freeNeed - 1;
for (uint8_t ord = lfnOrd ; ord ; ord--) {
ldir = reinterpret_cast<ldir_t*>(dirFile->readDirCache());
if (!ldir) {
DBG_FAIL_MACRO;
goto fail;
}
dirFile->m_vol->cacheDirty();
ldir->ord = ord == lfnOrd ? LDIR_ORD_LAST_LONG_ENTRY | ord : ord;
ldir->attr = DIR_ATT_LONG_NAME;
ldir->type = 0;
ldir->chksum = lfnChecksum(fname->sfn);
ldir->mustBeZero = 0;
lfnPutName(ldir, fname->lfn, len);
}
curIndex = dirFile->m_curPosition/32;
dir = dirFile->readDirCache();
if (!dir) {
DBG_FAIL_MACRO;
goto fail;
}
// initialize as empty file
memset(dir, 0, sizeof(dir_t));
memcpy(dir->name, fname->sfn, 11);

// Set base-name and extension lower case bits.
dir->reservedNT = (DIR_NT_LC_BASE | DIR_NT_LC_EXT) & fname->flags;

// set timestamps
if (m_dateTime) {
// call user date/time function
m_dateTime(&dir->creationDate, &dir->creationTime);
} else {
// use default date/time
dir->creationDate = FAT_DEFAULT_DATE;
dir->creationTime = FAT_DEFAULT_TIME;
}
dir->lastAccessDate = dir->creationDate;
dir->lastWriteDate = dir->creationDate;
dir->lastWriteTime = dir->creationTime;

// Force write of entry to device.
dirFile->m_vol->cacheDirty();

open:
// open entry in cache.
if (!openCachedEntry(dirFile, curIndex, oflag, lfnOrd)) {
DBG_FAIL_MACRO;
goto fail;
}
return true; return true;


fail:
*bgnIndex = foundFree ? freeIndex : curIndex;
fail:
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int FatFile::openNextLFN(FatFile* dirFile,
char* name, size_t size, uint8_t oflag) {
bool fill;
bool haveLong = false;
size_t lfnIn;
uint8_t ndir;
uint8_t test;
size_t FatFile::printName(print_t* pr) {
FatFile dirFile;
uint16_t u;
size_t n = 0;
ldir_t* ldir;

if (!isLFN()) {
return printSFN(pr);
}
if (!dirFile.openCluster(this)) {
DBG_FAIL_MACRO;
goto fail;
}
for (uint8_t ord = 1; ord <= m_lfnOrd; ord++) {
if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) {
DBG_FAIL_MACRO;
goto fail;
}
ldir = reinterpret_cast<ldir_t*>(dirFile.readDirCache());
if (!ldir) {
DBG_FAIL_MACRO;
goto fail;
}
if (ldir->attr !=DIR_ATT_LONG_NAME ||
ord != (ldir->ord & 0X1F)) {
DBG_FAIL_MACRO;
goto fail;
}
for (uint8_t i = 0; i < 13; i++) {
u = lfnGetChar(ldir, i);
if (u == 0) {
// End of name.
break;
}
if (u > 0X7E) {
u = '?';
}
pr->write(static_cast<char>(u));
n++;
}
if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) {
return n;
}
}
// Fall into fail;
DBG_FAIL_MACRO;

fail:
return 0;
}
//------------------------------------------------------------------------------
bool FatFile::remove() {
bool last;
uint8_t chksum;
uint8_t ord;
FatFile dirFile;
dir_t* dir; dir_t* dir;
uint16_t index;
int rtn;
ldir_t* ldir;


// Check for valid directory and file is not open.
if (!dirFile->isDir() || isOpen() || size < 13) {
// Cant' remove not open for write.
if (!isFile() || !(m_flags & O_WRITE)) {
DBG_FAIL_MACRO;
goto fail;
}
// Free any clusters.
if (m_firstCluster && !m_vol->freeChain(m_firstCluster)) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
// Cache directory entry.
dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
if (!dir) {
DBG_FAIL_MACRO;
goto fail;
}
chksum = lfnChecksum(dir->name);


while (1) {
// Check for EOF.
if (dirFile->curPosition() == dirFile->fileSize()) goto done;
index = dirFile->curPosition()/32;
// read entry into cache
dir = dirFile->readDirCache();
if (!dir) {
// Mark entry deleted.
dir->name[0] = DIR_NAME_DELETED;

// Set this file closed.
m_attr = FILE_ATTR_CLOSED;

// Write entry to device.
if (!m_vol->cacheSync()) {
DBG_FAIL_MACRO;
goto fail;
}
if (!isLFN()) {
// Done, no LFN entries.
return true;
}
if (!dirFile.openCluster(this)) {
DBG_FAIL_MACRO;
goto fail;
}
for (ord = 1; ord <= m_lfnOrd; ord++) {
if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }

// done if last entry
if (dir->name[0] == DIR_NAME_FREE) {
ldir = reinterpret_cast<ldir_t*>(dirFile.readDirCache());
if (!ldir) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
return 0;
goto fail;
} }
// skip empty slot or '.' or '..'
if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
haveLong = false;
continue;
if (ldir->attr != DIR_ATT_LONG_NAME ||
ord != (ldir->ord & 0X1F) ||
chksum != ldir->chksum) {
DBG_FAIL_MACRO;
goto fail;
} }
if (DIR_IS_LONG_NAME(dir)) {
ldir_t *ldir = reinterpret_cast<ldir_t*>(dir);
if (!haveLong) {
if ((ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) == 0) continue;
ndir = ldir->ord & ~LDIR_ORD_LAST_LONG_ENTRY;
if (ndir < 1 || ndir > 20) continue;
test = ldir->chksum;
haveLong = true;
rtn = 0;
fill = true;
lfnIn = 13*ndir;
} else if (ldir->ord != --ndir || test != ldir->chksum) {
haveLong = false;
continue;
last = ldir->ord & LDIR_ORD_LAST_LONG_ENTRY;
ldir->ord = DIR_NAME_DELETED;
m_vol->cacheDirty();
if (last) {
if (!m_vol->cacheSync()) {
DBG_FAIL_MACRO;
goto fail;
} }
for (int i = 12; i >= 0; i--) {
uint16_t u = lfnChar(ldir, i);
if (fill) {
if (rtn == 0 && u != 0 && u != 0XFFFF) rtn = lfnIn;
if (u == 0 || u == 0XFFFF || lfnIn >= size) {
lfnIn--;
continue;
}
name[lfnIn] = 0;
fill = false;
}
if (lfnIn == 0 || u > 255) {
haveLong = false;
break;
}
name[--lfnIn] = u;
return true;
}
}
// Fall into fail.
DBG_FAIL_MACRO;

fail:
return false;
}
//------------------------------------------------------------------------------
bool FatFile::lfnUniqueSfn(fname_t* fname) {
const uint8_t FIRST_HASH_SEQ = 2; // min value is 2
uint8_t pos = fname->seqPos;;
dir_t *dir;
uint16_t hex;

DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS));
DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1');

for (uint8_t seq = 2; seq < 100; seq++) {
if (seq < FIRST_HASH_SEQ) {
fname->sfn[pos + 1] = '0' + seq;
} else {
DBG_PRINT_IF(seq > FIRST_HASH_SEQ);
hex = Bernstein(seq + fname->len, fname->lfn, fname->len);
if (pos > 3) {
// Make space in name for ~HHHH.
pos = 3;
} }
} else if (DIR_IS_FILE_OR_SUBDIR(dir)) {
if (haveLong) {
uint8_t sum = 0;
for (uint8_t i = 0; i < 11; i++) {
sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + dir->name[i];
}
if (sum != test || ndir != 1) {
haveLong = false;
}
for (uint8_t i = pos + 4 ; i > pos; i--) {
uint8_t h = hex & 0XF;
fname->sfn[i] = h < 10 ? h + '0' : h + 'A' - 10;
hex >>= 4;
} }
if (!haveLong) {
rtn = dirName(dir, name);
if (dir->reservedNT) {
uint8_t lowerTest = 0X08;
for (char *ptr = name; *ptr; ptr++) {
if (*ptr == '.') {
lowerTest = 0X10;
continue;
}
if (dir->reservedNT & lowerTest) {
*ptr = tolower(*ptr);
}
}
}
fname->sfn[pos] = '~';
rewind();
while (1) {
dir = readDirCache(true);
if (!dir) {
if (!getError()) {
// At EOF and name not found if no error.
goto done;
} }
}
if (!openCachedEntry(dirFile, index, oflag)) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
return rtn;
} else {
haveLong = false;
if (dir->name[0] == DIR_NAME_FREE) {
goto done;
}
if (DIR_IS_FILE_OR_SUBDIR(dir) && !memcmp(fname->sfn, dir->name, 11)) {
// Name found - try another.
break;
}
} }
} }
// fall inti fail - too many tries.
DBG_FAIL_MACRO;


done:
return 0;
fail:
return false;


fail:
return -1;
done:
return true;
} }
#endif // DOXYGEN_SHOULD_SKIP_THIS
#endif // #if USE_LONG_FILE_NAMES

+ 30
- 73
SdFat/utility/FatFilePrint.cpp View File

} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void FatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) { void FatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) {
if (!isDir()) return;
FatFile file;
rewind(); rewind();
int8_t status;
while ((status = lsPrintNext(pr, flags, indent))) {
if (status > 1 && (flags & LS_R)) {
uint16_t index = curPosition()/32 - 1;
FatFile s;
if (s.open(this, index, O_READ)) s.ls(pr, flags, indent + 2);
seekSet(32 * (index + 1));
while (file.openNext(this, O_READ)) {
// indent for dir level
if (!file.isHidden() && !(flags & LS_A)) {
for (uint8_t i = 0; i < indent; i++) {
pr->write(' ');
}
if (flags & LS_DATE) {
file.printModifyDateTime(pr);
pr->write(' ');
}
if (flags & LS_SIZE) {
file.printFileSize(pr);
pr->write(' ');
}
file.printName(pr);
if (file.isDir()) {
pr->write('/');
}
pr->write('\r');
pr->write('\n');
if ((flags & LS_R) && file.isDir()) {
file.ls(pr, flags, indent + 2);
}
} }
file.close();
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// saves 32 bytes on stack for ls recursion
// return 0 - EOF, 1 - normal file, or 2 - directory
int8_t FatFile::lsPrintNext(print_t* pr, uint8_t flags, uint8_t indent) {
dir_t dir;
uint8_t w = 0;

while (1) {
if (read(&dir, sizeof(dir)) != sizeof(dir)) return 0;
if (dir.name[0] == DIR_NAME_FREE) return 0;

// skip deleted entry and entries for . and ..
if (dir.name[0] != DIR_NAME_DELETED && dir.name[0] != '.'
&& DIR_IS_FILE_OR_SUBDIR(&dir)) break;
}
// indent for dir level
for (uint8_t i = 0; i < indent; i++) pr->write(' ');

// print name
for (uint8_t i = 0; i < 11; i++) {
if (dir.name[i] == ' ')continue;
if (i == 8) {
pr->write('.');
w++;
}
pr->write(dir.name[i]);
w++;
}
if (DIR_IS_SUBDIR(&dir)) {
pr->write('/');
w++;
}
if (flags & (LS_DATE | LS_SIZE)) {
while (w++ < 14) pr->write(' ');
}
// print modify date/time if requested
if (flags & LS_DATE) {
pr->write(' ');
printFatDate(pr, dir.lastWriteDate);
pr->write(' ');
printFatTime(pr, dir.lastWriteTime);
}
// print size if requested
if (!DIR_IS_SUBDIR(&dir) && (flags & LS_SIZE)) {
pr->write(' ');
printU32(pr, dir.fileSize);
}
pr->write('\r');
pr->write('\n');
return DIR_IS_FILE(&dir) ? 1 : 2;
}
//------------------------------------------------------------------------------
bool FatFile::printCreateDateTime(print_t* pr) { bool FatFile::printCreateDateTime(print_t* pr) {
dir_t dir; dir_t dir;
if (!dirEntry(&dir)) { if (!dirEntry(&dir)) {
printFatTime(pr, dir.creationTime); printFatTime(pr, dir.creationTime);
return true; return true;


fail:
fail:
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
printFatTime(pr, dir.lastWriteTime); printFatTime(pr, dir.lastWriteTime);
return true; return true;


fail:
fail:
return false; return false;
} }
//------------------------------------------------------------------------------
size_t FatFile::printName(print_t* pr) {
char name[13];
if (!getFilename(name)) {
DBG_FAIL_MACRO;
goto fail;
}
return pr->write(name);


fail:
return 0;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
size_t FatFile::printFileSize(print_t* pr) { size_t FatFile::printFileSize(print_t* pr) {
char buf[11]; char buf[11];
char *ptr = buf + sizeof(buf); char *ptr = buf + sizeof(buf);
*--ptr = 0; *--ptr = 0;
ptr = fmtDec(fileSize(), ptr); ptr = fmtDec(fileSize(), ptr);
while (ptr > buf) *--ptr = ' ';
while (ptr > buf) {
*--ptr = ' ';
}
return pr->write(buf); return pr->write(buf);
} }

+ 273
- 0
SdFat/utility/FatFileSFN.cpp View File

/* FatLib Library
* Copyright (C) 2012 by William Greiman
*
* This file is part of the FatLib Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the FatLib Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "FatFile.h"
#include "FatFileSystem.h"
//------------------------------------------------------------------------------
bool FatFile::getSFN(char* name) {
dir_t* dir;
if (!isOpen()) {
DBG_FAIL_MACRO;
goto fail;
}
if (isRoot()) {
name[0] = '/';
name[1] = '\0';
return true;
}
// cache entry
dir = cacheDirEntry(FatCache::CACHE_FOR_READ);
if (!dir) {
DBG_FAIL_MACRO;
goto fail;
}
// format name
dirName(dir, name);
return true;

fail:
return false;
}
//------------------------------------------------------------------------------
size_t FatFile::printSFN(print_t* pr) {
char name[13];
if (!getSFN(name)) {
DBG_FAIL_MACRO;
goto fail;
}
return pr->write(name);

fail:
return 0;
}
#if !USE_LONG_FILE_NAMES
//------------------------------------------------------------------------------
bool FatFile::getFilename(char* name, size_t size) {
return size < 13 ? 0 : getSFN(name);
}
//------------------------------------------------------------------------------
// format directory name field from a 8.3 name string
bool FatFile::parsePathName(const char* path, fname_t* fname,
const char** ptr) {
uint8_t uc = 0;
uint8_t lc = 0;
uint8_t bit = FNAME_FLAG_LC_BASE;
// blank fill name and extension
for (uint8_t i = 0; i < 11; i++) {
fname->sfn[i] = ' ';
}

for (uint8_t i = 0, n = 7;; path++) {
uint8_t c = *path;
if (c == 0 || isDirSeparator(c)) {
// Done.
break;
}
if (c == '.' && n == 7) {
n = 10; // max index for full 8.3 name
i = 8; // place for extension

// bit for extension.
bit = FNAME_FLAG_LC_EXT;
} else {
if (!legal83Char(c) || i > n) {
DBG_FAIL_MACRO;
goto fail;
}
if ('a' <= c && c <= 'z') {
c += 'A' - 'a';
lc |= bit;
} else if ('A' <= c && c <= 'Z') {
uc |= bit;
}
fname->sfn[i++] = c;
}
}
// must have a file name, extension is optional
if (fname->sfn[0] == ' ') {
DBG_FAIL_MACRO;
goto fail;
}
// Set base-name and extension bits.
fname->flags = lc & uc ? 0 : lc;
while (isDirSeparator(*path)) {
path++;
}
*ptr = path;
return true;

fail:
return false;
}
//------------------------------------------------------------------------------
// open with filename in fname
#define SFN_OPEN_USES_CHKSUM 0
bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) {
bool emptyFound = false;
#if SFN_OPEN_USES_CHKSUM
uint8_t chksum;
#endif
uint8_t lfnOrd = 0;
uint16_t emptyIndex;
uint16_t index = 0;
dir_t* dir;
ldir_t* ldir;

dirFile->rewind();
while (1) {
if (!emptyFound) {
emptyIndex = index;
}
dir = dirFile->readDirCache(true);
if (!dir) {
if (dirFile->getError()) {
DBG_FAIL_MACRO;
goto fail;
}
// At EOF if no error.
break;
}
if (dir->name[0] == DIR_NAME_FREE) {
emptyFound = true;
break;
}
if (dir->name[0] == DIR_NAME_DELETED) {
lfnOrd = 0;
emptyFound = true;
} else if (DIR_IS_FILE_OR_SUBDIR(dir)) {
if (!memcmp(fname->sfn, dir->name, 11)) {
// don't open existing file if O_EXCL
if (oflag & O_EXCL) {
DBG_FAIL_MACRO;
goto fail;
}
#if SFN_OPEN_USES_CHKSUM
if (lfnOrd && chksum != lfnChecksum(dir->name)) {
DBG_FAIL_MACRO;
goto fail;
}
#endif // SFN_OPEN_USES_CHKSUM
if (!openCachedEntry(dirFile, index, oflag, lfnOrd)) {
DBG_FAIL_MACRO;
goto fail;
}
return true;
} else {
lfnOrd = 0;
}
} else if (DIR_IS_LONG_NAME(dir)) {
ldir = (ldir_t*)dir;
if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) {
lfnOrd = ldir->ord & 0X1F;
#if SFN_OPEN_USES_CHKSUM
chksum = ldir->chksum;
#endif // SFN_OPEN_USES_CHKSUM
}
} else {
lfnOrd = 0;
}
index++;
}
// don't create unless O_CREAT and O_WRITE
if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) {
DBG_FAIL_MACRO;
goto fail;
}
if (emptyFound) {
index = emptyIndex;
} else {
if (!dirFile->addDirCluster()) {
DBG_FAIL_MACRO;
goto fail;
}
}
if (!dirFile->seekSet(32UL*index)) {
DBG_FAIL_MACRO;
goto fail;
}
dir = dirFile->readDirCache();
if (!dir) {
DBG_FAIL_MACRO;
goto fail;
}
// initialize as empty file
memset(dir, 0, sizeof(dir_t));
memcpy(dir->name, fname->sfn, 11);

// Set base-name and extension lower case bits.
dir->reservedNT = (DIR_NT_LC_BASE | DIR_NT_LC_EXT) & fname->flags;

// set timestamps
if (m_dateTime) {
// call user date/time function
m_dateTime(&dir->creationDate, &dir->creationTime);
} else {
// use default date/time
dir->creationDate = FAT_DEFAULT_DATE;
dir->creationTime = FAT_DEFAULT_TIME;
}
dir->lastAccessDate = dir->creationDate;
dir->lastWriteDate = dir->creationDate;
dir->lastWriteTime = dir->creationTime;

// Force write of entry to device.
dirFile->m_vol->cacheDirty();

// open entry in cache.
return openCachedEntry(dirFile, index, oflag, 0);

fail:
return false;
}
//------------------------------------------------------------------------------
size_t FatFile::printName(print_t* pr) {
return printSFN(pr);
}
//------------------------------------------------------------------------------
bool FatFile::remove() {
dir_t* dir;
// Can't remove if LFN or not open for write.
if (!isFile() || isLFN() || !(m_flags & O_WRITE)) {
DBG_FAIL_MACRO;
goto fail;
}
// Free any clusters.
if (m_firstCluster && !m_vol->freeChain(m_firstCluster)) {
DBG_FAIL_MACRO;
goto fail;
}
// Cache directory entry.
dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
if (!dir) {
DBG_FAIL_MACRO;
goto fail;
}
// Mark entry deleted.
dir->name[0] = DIR_NAME_DELETED;

// Set this file closed.
m_attr = FILE_ATTR_CLOSED;

// Write entry to device.
return m_vol->cacheSync();

fail:
return false;
}
#endif // !USE_LONG_FILE_NAMES

+ 54
- 29
SdFat/utility/FatFileSystem.h View File

*/ */
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** FatFileSystem version YYYYMMDD */ /** FatFileSystem version YYYYMMDD */
#define FAT_LIB_VERSION 20141115
#define FAT_LIB_VERSION 20141201
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
* \class FatFileSystem * \class FatFileSystem
/** /**
* Initialize an FatFileSystem object. * Initialize an FatFileSystem object.
* \param[in] d Volume Working Directory. * \param[in] d Volume Working Directory.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool begin(FatFile* d) { bool begin(FatFile* d) {
m_vwd = d; m_vwd = d;
* \param[in] set_cwd Set the current working directory to this volume's * \param[in] set_cwd Set the current working directory to this volume's
* working directory if true. * working directory if true.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool chdir(bool set_cwd = false) { bool chdir(bool set_cwd = false) {
vwd()->close(); vwd()->close();
* \param[in] set_cwd Set the current working directory to this volume's * \param[in] set_cwd Set the current working directory to this volume's
* working directory if true. * working directory if true.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool chdir(const char *path, bool set_cwd = false) { bool chdir(const char *path, bool set_cwd = false) {
FatFile dir; FatFile dir;
if (path[0] == '/' && path[1] == '\0') return chdir(set_cwd);
if (!dir.open(vwd(), path, O_READ)) goto fail;
if (!dir.isDir()) goto fail;
if (path[0] == '/' && path[1] == '\0') {
return chdir(set_cwd);
}
if (!dir.open(vwd(), path, O_READ)) {
goto fail;
}
if (!dir.isDir()) {
goto fail;
}
*m_vwd = dir; *m_vwd = dir;
if (set_cwd) FatFile::setCwd(vwd());
if (set_cwd) {
FatFile::setCwd(vwd());
}
return true; return true;


fail:
fail:
return false; return false;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
* *
* \param[in] pFlag Create missing parent directories if true. * \param[in] pFlag Create missing parent directories if true.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool mkdir(const char* path, bool pFlag = true) { bool mkdir(const char* path, bool pFlag = true) {
FatFile sub; FatFile sub;
* *
* \param[in] path A path with a valid 8.3 DOS name for the file. * \param[in] path A path with a valid 8.3 DOS name for the file.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool remove(const char* path) { bool remove(const char* path) {
return FatFile::remove(vwd(), path); return FatFile::remove(vwd(), path);
* moved and file system corruption could occur if the file is accessed by * moved and file system corruption could occur if the file is accessed by
* a file object that was opened before the rename() call. * a file object that was opened before the rename() call.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool rename(const char *oldPath, const char *newPath) { bool rename(const char *oldPath, const char *newPath) {
FatFile file; FatFile file;
if (!file.open(vwd(), oldPath, O_READ)) return false;
if (!file.open(vwd(), oldPath, O_READ)) {
return false;
}
return file.rename(vwd(), newPath); return file.rename(vwd(), newPath);
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
* *
* The subdirectory file will be removed only if it is empty. * The subdirectory file will be removed only if it is empty.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool rmdir(const char* path) { bool rmdir(const char* path) {
FatFile sub; FatFile sub;
if (!sub.open(vwd(), path, O_READ)) return false;
if (!sub.open(vwd(), path, O_READ)) {
return false;
}
return sub.rmdir(); return sub.rmdir();
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
* \param[in] path A path with a valid 8.3 DOS name for the file. * \param[in] path A path with a valid 8.3 DOS name for the file.
* \param[in] length The desired length for the file. * \param[in] length The desired length for the file.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* Reasons for failure include file is read only, file is a directory,
* \a length is greater than the current file size or an I/O error occurs.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool truncate(const char* path, uint32_t length) { bool truncate(const char* path, uint32_t length) {
FatFile file; FatFile file;
if (!file.open(vwd(), path, O_WRITE)) return false;
if (!file.open(vwd(), path, O_WRITE)) {
return false;
}
return file.truncate(length); return file.truncate(length);
} }
/** \return a pointer to the FatVolume object. */ /** \return a pointer to the FatVolume object. */
FatVolume* vol() {return this;}
FatVolume* vol() {
return this;
}
/** \return a pointer to the volume working directory. */ /** \return a pointer to the volume working directory. */
FatFile* vwd() {return m_vwd;}
FatFile* vwd() {
return m_vwd;
}
/** Wipe all data from the volume. You must reinitialize the volume before
* accessing it again.
* \param[in] pr print stream for status dots.
* \return true for success else false.
*/
bool wipe(print_t* pr = 0) {
m_vwd->close();
return FatVolume::wipe(pr);
}


private: private:
FatFile* m_vwd; FatFile* m_vwd;

+ 27
- 0
SdFat/utility/FatLibConfig.h View File

*/ */
#ifndef FatLibConfig_h #ifndef FatLibConfig_h
#define FatLibConfig_h #define FatLibConfig_h

/** Use SdFatConfig.h if nonzero */ /** Use SdFatConfig.h if nonzero */
#define USE_SDFAT_CONFIG 1 #define USE_SDFAT_CONFIG 1
#if USE_SDFAT_CONFIG #if USE_SDFAT_CONFIG
#include "../SdFatConfig.h" #include "../SdFatConfig.h"
#if !defined(USE_LONG_FILE_NAMES) || !defined(USE_SEPARATE_FAT_CACHE) || \
!defined(USE_MULTI_BLOCK_IO) || !defined(DESTRUCTOR_CLOSES_FILE) || \
!defined(ENDL_CALLS_FLUSH) || !defined(FAT12_SUPPORT)
#error Undefined congiguration symbols.
#endif // Configuration symbols
#else // USE_SDFAT_CONFIG #else // USE_SDFAT_CONFIG
#include <stdint.h> #include <stdint.h>
#ifdef __AVR__ #ifdef __AVR__
#include <avr/io.h> #include <avr/io.h>
#endif // __AVR__ #endif // __AVR__
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/**
* Set USE_LONG_FILE_NAMES nonzero to use long file names (LFN).
* Long File Name are limited to a maximum length of 255 characters.
*
* This implementation allows 7-bit characters in the range
* 0X20 to 0X7E. The following characters are not allowed:
*
* < (less than)
* > (greater than)
* : (colon)
* " (double quote)
* / (forward slash)
* \ (backslash)
* | (vertical bar or pipe)
* ? (question mark)
* * (asterisk)
*
*/
#define USE_LONG_FILE_NAMES 1
//------------------------------------------------------------------------------
/** /**
* Set USE_SEPARATE_FAT_CACHE non-zero to use a second 512 byte cache * Set USE_SEPARATE_FAT_CACHE non-zero to use a second 512 byte cache
* for FAT table entries. Improves performance for large writes that * for FAT table entries. Improves performance for large writes that
* are not a multiple of 512 bytes. * are not a multiple of 512 bytes.
*/ */

#ifdef __arm__ #ifdef __arm__
#define USE_SEPARATE_FAT_CACHE 1 #define USE_SEPARATE_FAT_CACHE 1
#else // __arm__ #else // __arm__

+ 354
- 361
SdFat/utility/FatStructs.h View File

* The MBR partition table has four entries. * The MBR partition table has four entries.
*/ */
struct partitionTable { struct partitionTable {
/**
* Boot Indicator . Indicates whether the volume is the active
* partition. Legal values include: 0X00. Do not use for booting.
* 0X80 Active partition.
*/
/**
* Boot Indicator . Indicates whether the volume is the active
* partition. Legal values include: 0X00. Do not use for booting.
* 0X80 Active partition.
*/
uint8_t boot; uint8_t boot;
/**
* Head part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 0-255. Only used in old PC BIOS.
*/
/**
* Head part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 0-255. Only used in old PC BIOS.
*/
uint8_t beginHead; uint8_t beginHead;
/**
* Sector part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
/**
* Sector part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
unsigned beginSector : 6; unsigned beginSector : 6;
/** High bits cylinder for first block in partition. */
/** High bits cylinder for first block in partition. */
unsigned beginCylinderHigh : 2; unsigned beginCylinderHigh : 2;
/**
* Combine beginCylinderLow with beginCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
/**
* Combine beginCylinderLow with beginCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
uint8_t beginCylinderLow; uint8_t beginCylinderLow;
/**
* Partition type. See defines that begin with PART_TYPE_ for
* some Microsoft partition types.
*/
/**
* Partition type. See defines that begin with PART_TYPE_ for
* some Microsoft partition types.
*/
uint8_t type; uint8_t type;
/**
* head part of cylinder-head-sector address of the last sector in the
* partition. Legal values are 0-255. Only used in old PC BIOS.
*/
/**
* head part of cylinder-head-sector address of the last sector in the
* partition. Legal values are 0-255. Only used in old PC BIOS.
*/
uint8_t endHead; uint8_t endHead;
/**
* Sector part of cylinder-head-sector address of the last sector in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
/**
* Sector part of cylinder-head-sector address of the last sector in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
unsigned endSector : 6; unsigned endSector : 6;
/** High bits of end cylinder */
/** High bits of end cylinder */
unsigned endCylinderHigh : 2; unsigned endCylinderHigh : 2;
/**
* Combine endCylinderLow with endCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
/**
* Combine endCylinderLow with endCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
uint8_t endCylinderLow; uint8_t endCylinderLow;
/** Logical block address of the first block in the partition. */
/** Logical block address of the first block in the partition. */
uint32_t firstSector; uint32_t firstSector;
/** Length of the partition, in blocks. */
/** Length of the partition, in blocks. */
uint32_t totalSectors; uint32_t totalSectors;
}__attribute__((packed));
} __attribute__((packed));
/** Type name for partitionTable */ /** Type name for partitionTable */
typedef struct partitionTable part_t; typedef struct partitionTable part_t;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
* The first block of a storage device that is formatted with a MBR. * The first block of a storage device that is formatted with a MBR.
*/ */
struct masterBootRecord { struct masterBootRecord {
/** Code Area for master boot program. */
/** Code Area for master boot program. */
uint8_t codeArea[440]; uint8_t codeArea[440];
/** Optional Windows NT disk signature. May contain boot code. */
/** Optional Windows NT disk signature. May contain boot code. */
uint32_t diskSignature; uint32_t diskSignature;
/** Usually zero but may be more boot code. */
/** Usually zero but may be more boot code. */
uint16_t usuallyZero; uint16_t usuallyZero;
/** Partition tables. */
/** Partition tables. */
part_t part[4]; part_t part[4];
/** First MBR signature byte. Must be 0X55 */
/** First MBR signature byte. Must be 0X55 */
uint8_t mbrSig0; uint8_t mbrSig0;
/** Second MBR signature byte. Must be 0XAA */
/** Second MBR signature byte. Must be 0XAA */
uint8_t mbrSig1; uint8_t mbrSig1;
}__attribute__((packed));
} __attribute__((packed));
/** Type name for masterBootRecord */ /** Type name for masterBootRecord */
typedef struct masterBootRecord mbr_t; typedef struct masterBootRecord mbr_t;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
* *
*/ */
struct fat_boot { struct fat_boot {
/**
* The first three bytes of the boot sector must be valid,
* executable x 86-based CPU instructions. This includes a
* jump instruction that skips the next non-executable bytes.
*/
/**
* The first three bytes of the boot sector must be valid,
* executable x 86-based CPU instructions. This includes a
* jump instruction that skips the next non-executable bytes.
*/
uint8_t jump[3]; uint8_t jump[3];
/**
* This is typically a string of characters that identifies
* the operating system that formatted the volume.
*/
/**
* This is typically a string of characters that identifies
* the operating system that formatted the volume.
*/
char oemId[8]; char oemId[8];
/**
* The size of a hardware sector. Valid decimal values for this
* field are 512, 1024, 2048, and 4096. For most disks used in
* the United States, the value of this field is 512.
*/
/**
* The size of a hardware sector. Valid decimal values for this
* field are 512, 1024, 2048, and 4096. For most disks used in
* the United States, the value of this field is 512.
*/
uint16_t bytesPerSector; uint16_t bytesPerSector;
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
*/
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
*/
uint8_t sectorsPerCluster; uint8_t sectorsPerCluster;
/**
* The number of sectors preceding the start of the first FAT,
* including the boot sector. The value of this field is always 1.
*/
/**
* The number of sectors preceding the start of the first FAT,
* including the boot sector. The value of this field is always 1.
*/
uint16_t reservedSectorCount; uint16_t reservedSectorCount;
/**
* The number of copies of the FAT on the volume.
* The value of this field is always 2.
*/
/**
* The number of copies of the FAT on the volume.
* The value of this field is always 2.
*/
uint8_t fatCount; uint8_t fatCount;
/**
* For FAT12 and FAT16 volumes, this field contains the count of
* 32-byte directory entries in the root directory. For FAT32 volumes,
* this field must be set to 0. For FAT12 and FAT16 volumes, this
* value should always specify a count that when multiplied by 32
* results in a multiple of bytesPerSector. FAT16 volumes should
* use the value 512.
*/
/**
* For FAT12 and FAT16 volumes, this field contains the count of
* 32-byte directory entries in the root directory. For FAT32 volumes,
* this field must be set to 0. For FAT12 and FAT16 volumes, this
* value should always specify a count that when multiplied by 32
* results in a multiple of bytesPerSector. FAT16 volumes should
* use the value 512.
*/
uint16_t rootDirEntryCount; uint16_t rootDirEntryCount;
/**
* This field is the old 16-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then totalSectors32
* must be non-zero. For FAT32 volumes, this field must be 0. For
* FAT12 and FAT16 volumes, this field contains the sector count, and
* totalSectors32 is 0 if the total sector count fits
* (is less than 0x10000).
*/
/**
* This field is the old 16-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then totalSectors32
* must be non-zero. For FAT32 volumes, this field must be 0. For
* FAT12 and FAT16 volumes, this field contains the sector count, and
* totalSectors32 is 0 if the total sector count fits
* (is less than 0x10000).
*/
uint16_t totalSectors16; uint16_t totalSectors16;
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (non-removable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (non-removable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
uint8_t mediaType; uint8_t mediaType;
/**
* Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
/**
* Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
uint16_t sectorsPerFat16; uint16_t sectorsPerFat16;
/** Sectors per track for interrupt 0x13. Not used otherwise. */
/** Sectors per track for interrupt 0x13. Not used otherwise. */
uint16_t sectorsPerTrack; uint16_t sectorsPerTrack;
/** Number of heads for interrupt 0x13. Not used otherwise. */
/** Number of heads for interrupt 0x13. Not used otherwise. */
uint16_t headCount; uint16_t headCount;
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
uint32_t hidddenSectors; uint32_t hidddenSectors;
/**
* This field is the new 32-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then
* totalSectors16 must be non-zero.
*/
/**
* This field is the new 32-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then
* totalSectors16 must be non-zero.
*/
uint32_t totalSectors32; uint32_t totalSectors32;
/**
* Related to the BIOS physical drive number. Floppy drives are
* identified as 0x00 and physical hard disks are identified as
* 0x80, regardless of the number of physical disk drives.
* Typically, this value is set prior to issuing an INT 13h BIOS
* call to specify the device to access. The value is only
* relevant if the device is a boot device.
*/
/**
* Related to the BIOS physical drive number. Floppy drives are
* identified as 0x00 and physical hard disks are identified as
* 0x80, regardless of the number of physical disk drives.
* Typically, this value is set prior to issuing an INT 13h BIOS
* call to specify the device to access. The value is only
* relevant if the device is a boot device.
*/
uint8_t driveNumber; uint8_t driveNumber;
/** used by Windows NT - should be zero for FAT */
/** used by Windows NT - should be zero for FAT */
uint8_t reserved1; uint8_t reserved1;
/** 0X29 if next three fields are valid */
/** 0X29 if next three fields are valid */
uint8_t bootSignature; uint8_t bootSignature;
/**
* A random serial number created when formatting a disk,
* which helps to distinguish between disks.
* Usually generated by combining date and time.
*/
/**
* A random serial number created when formatting a disk,
* which helps to distinguish between disks.
* Usually generated by combining date and time.
*/
uint32_t volumeSerialNumber; uint32_t volumeSerialNumber;
/**
* A field once used to store the volume label. The volume label
* is now stored as a special file in the root directory.
*/
/**
* A field once used to store the volume label. The volume label
* is now stored as a special file in the root directory.
*/
char volumeLabel[11]; char volumeLabel[11];
/**
* A field with a value of either FAT, FAT12 or FAT16,
* depending on the disk format.
*/
/**
* A field with a value of either FAT, FAT12 or FAT16,
* depending on the disk format.
*/
char fileSystemType[8]; char fileSystemType[8];
/** X86 boot code */
/** X86 boot code */
uint8_t bootCode[448]; uint8_t bootCode[448];
/** must be 0X55 */
/** must be 0X55 */
uint8_t bootSectorSig0; uint8_t bootSectorSig0;
/** must be 0XAA */
/** must be 0XAA */
uint8_t bootSectorSig1; uint8_t bootSectorSig1;
}__attribute__((packed));
} __attribute__((packed));
/** Type name for FAT Boot Sector */ /** Type name for FAT Boot Sector */
typedef struct fat_boot fat_boot_t; typedef struct fat_boot fat_boot_t;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
* *
*/ */
struct fat32_boot { struct fat32_boot {
/**
* The first three bytes of the boot sector must be valid,
* executable x 86-based CPU instructions. This includes a
* jump instruction that skips the next non-executable bytes.
*/
/**
* The first three bytes of the boot sector must be valid,
* executable x 86-based CPU instructions. This includes a
* jump instruction that skips the next non-executable bytes.
*/
uint8_t jump[3]; uint8_t jump[3];
/**
* This is typically a string of characters that identifies
* the operating system that formatted the volume.
*/
/**
* This is typically a string of characters that identifies
* the operating system that formatted the volume.
*/
char oemId[8]; char oemId[8];
/**
* The size of a hardware sector. Valid decimal values for this
* field are 512, 1024, 2048, and 4096. For most disks used in
* the United States, the value of this field is 512.
*/
/**
* The size of a hardware sector. Valid decimal values for this
* field are 512, 1024, 2048, and 4096. For most disks used in
* the United States, the value of this field is 512.
*/
uint16_t bytesPerSector; uint16_t bytesPerSector;
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
*/
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
*/
uint8_t sectorsPerCluster; uint8_t sectorsPerCluster;
/**
* The number of sectors preceding the start of the first FAT,
* including the boot sector. Must not be zero
*/
/**
* The number of sectors preceding the start of the first FAT,
* including the boot sector. Must not be zero
*/
uint16_t reservedSectorCount; uint16_t reservedSectorCount;
/**
* The number of copies of the FAT on the volume.
* The value of this field is always 2.
*/
/**
* The number of copies of the FAT on the volume.
* The value of this field is always 2.
*/
uint8_t fatCount; uint8_t fatCount;
/**
* FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0.
*/
/**
* FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0.
*/
uint16_t rootDirEntryCount; uint16_t rootDirEntryCount;
/**
* For FAT32 volumes, this field must be 0.
*/
/**
* For FAT32 volumes, this field must be 0.
*/
uint16_t totalSectors16; uint16_t totalSectors16;
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (non-removable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (non-removable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
uint8_t mediaType; uint8_t mediaType;
/**
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
/**
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
uint16_t sectorsPerFat16; uint16_t sectorsPerFat16;
/** Sectors per track for interrupt 0x13. Not used otherwise. */
/** Sectors per track for interrupt 0x13. Not used otherwise. */
uint16_t sectorsPerTrack; uint16_t sectorsPerTrack;
/** Number of heads for interrupt 0x13. Not used otherwise. */
/** Number of heads for interrupt 0x13. Not used otherwise. */
uint16_t headCount; uint16_t headCount;
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
uint32_t hidddenSectors; uint32_t hidddenSectors;
/**
* Contains the total number of sectors in the FAT32 volume.
*/
/**
* Contains the total number of sectors in the FAT32 volume.
*/
uint32_t totalSectors32; uint32_t totalSectors32;
/**
* Count of sectors occupied by one FAT on FAT32 volumes.
*/
/**
* Count of sectors occupied by one FAT on FAT32 volumes.
*/
uint32_t sectorsPerFat32; uint32_t sectorsPerFat32;
/**
* This field is only defined for FAT32 media and does not exist on
* FAT12 and FAT16 media.
* Bits 0-3 -- Zero-based number of active FAT.
* Only valid if mirroring is disabled.
* Bits 4-6 -- Reserved.
* Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
* -- 1 means only one FAT is active; it is the one referenced
* in bits 0-3.
* Bits 8-15 -- Reserved.
*/
/**
* This field is only defined for FAT32 media and does not exist on
* FAT12 and FAT16 media.
* Bits 0-3 -- Zero-based number of active FAT.
* Only valid if mirroring is disabled.
* Bits 4-6 -- Reserved.
* Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
* -- 1 means only one FAT is active; it is the one referenced
* in bits 0-3.
* Bits 8-15 -- Reserved.
*/
uint16_t fat32Flags; uint16_t fat32Flags;
/**
* FAT32 version. High byte is major revision number.
* Low byte is minor revision number. Only 0.0 define.
*/
/**
* FAT32 version. High byte is major revision number.
* Low byte is minor revision number. Only 0.0 define.
*/
uint16_t fat32Version; uint16_t fat32Version;
/**
* Cluster number of the first cluster of the root directory for FAT32.
* This usually 2 but not required to be 2.
*/
/**
* Cluster number of the first cluster of the root directory for FAT32.
* This usually 2 but not required to be 2.
*/
uint32_t fat32RootCluster; uint32_t fat32RootCluster;
/**
* Sector number of FSINFO structure in the reserved area of the
* FAT32 volume. Usually 1.
*/
/**
* Sector number of FSINFO structure in the reserved area of the
* FAT32 volume. Usually 1.
*/
uint16_t fat32FSInfo; uint16_t fat32FSInfo;
/**
* If non-zero, indicates the sector number in the reserved area
* of the volume of a copy of the boot record. Usually 6.
* No value other than 6 is recommended.
*/
/**
* If non-zero, indicates the sector number in the reserved area
* of the volume of a copy of the boot record. Usually 6.
* No value other than 6 is recommended.
*/
uint16_t fat32BackBootBlock; uint16_t fat32BackBootBlock;
/**
* Reserved for future expansion. Code that formats FAT32 volumes
* should always set all of the bytes of this field to 0.
*/
/**
* Reserved for future expansion. Code that formats FAT32 volumes
* should always set all of the bytes of this field to 0.
*/
uint8_t fat32Reserved[12]; uint8_t fat32Reserved[12];
/**
* Related to the BIOS physical drive number. Floppy drives are
* identified as 0x00 and physical hard disks are identified as
* 0x80, regardless of the number of physical disk drives.
* Typically, this value is set prior to issuing an INT 13h BIOS
* call to specify the device to access. The value is only
* relevant if the device is a boot device.
*/
/**
* Related to the BIOS physical drive number. Floppy drives are
* identified as 0x00 and physical hard disks are identified as
* 0x80, regardless of the number of physical disk drives.
* Typically, this value is set prior to issuing an INT 13h BIOS
* call to specify the device to access. The value is only
* relevant if the device is a boot device.
*/
uint8_t driveNumber; uint8_t driveNumber;
/** used by Windows NT - should be zero for FAT */
/** used by Windows NT - should be zero for FAT */
uint8_t reserved1; uint8_t reserved1;
/** 0X29 if next three fields are valid */
/** 0X29 if next three fields are valid */
uint8_t bootSignature; uint8_t bootSignature;
/**
* A random serial number created when formatting a disk,
* which helps to distinguish between disks.
* Usually generated by combining date and time.
*/
/**
* A random serial number created when formatting a disk,
* which helps to distinguish between disks.
* Usually generated by combining date and time.
*/
uint32_t volumeSerialNumber; uint32_t volumeSerialNumber;
/**
* A field once used to store the volume label. The volume label
* is now stored as a special file in the root directory.
*/
/**
* A field once used to store the volume label. The volume label
* is now stored as a special file in the root directory.
*/
char volumeLabel[11]; char volumeLabel[11];
/**
* A text field with a value of FAT32.
*/
/**
* A text field with a value of FAT32.
*/
char fileSystemType[8]; char fileSystemType[8];
/** X86 boot code */
/** X86 boot code */
uint8_t bootCode[420]; uint8_t bootCode[420];
/** must be 0X55 */
/** must be 0X55 */
uint8_t bootSectorSig0; uint8_t bootSectorSig0;
/** must be 0XAA */
/** must be 0XAA */
uint8_t bootSectorSig1; uint8_t bootSectorSig1;
}__attribute__((packed));
} __attribute__((packed));
/** Type name for FAT32 Boot Sector */ /** Type name for FAT32 Boot Sector */
typedef struct fat32_boot fat32_boot_t; typedef struct fat32_boot fat32_boot_t;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
* *
*/ */
struct fat32_fsinfo { struct fat32_fsinfo {
/** must be 0X52, 0X52, 0X61, 0X41 */
/** must be 0X52, 0X52, 0X61, 0X41 */
uint32_t leadSignature; uint32_t leadSignature;
/** must be zero */
/** must be zero */
uint8_t reserved1[480]; uint8_t reserved1[480];
/** must be 0X72, 0X72, 0X41, 0X61 */
/** must be 0X72, 0X72, 0X41, 0X61 */
uint32_t structSignature; uint32_t structSignature;
/**
* Contains the last known free cluster count on the volume.
* If the value is 0xFFFFFFFF, then the free count is unknown
* and must be computed. Any other value can be used, but is
* not necessarily correct. It should be range checked at least
* to make sure it is <= volume cluster count.
*/
/**
* Contains the last known free cluster count on the volume.
* If the value is 0xFFFFFFFF, then the free count is unknown
* and must be computed. Any other value can be used, but is
* not necessarily correct. It should be range checked at least
* to make sure it is <= volume cluster count.
*/
uint32_t freeCount; uint32_t freeCount;
/**
* This is a hint for the FAT driver. It indicates the cluster
* number at which the driver should start looking for free clusters.
* If the value is 0xFFFFFFFF, then there is no hint and the driver
* should start looking at cluster 2.
*/
/**
* This is a hint for the FAT driver. It indicates the cluster
* number at which the driver should start looking for free clusters.
* If the value is 0xFFFFFFFF, then there is no hint and the driver
* should start looking at cluster 2.
*/
uint32_t nextFree; uint32_t nextFree;
/** must be zero */
/** must be zero */
uint8_t reserved2[12]; uint8_t reserved2[12];
/** must be 0X00, 0X00, 0X55, 0XAA */
/** must be 0X00, 0X00, 0X55, 0XAA */
uint8_t tailSignature[4]; uint8_t tailSignature[4];
}__attribute__((packed));
} __attribute__((packed));
/** Type name for FAT32 FSINFO Sector */ /** Type name for FAT32 FSINFO Sector */
typedef struct fat32_fsinfo fat32_fsinfo_t; typedef struct fat32_fsinfo fat32_fsinfo_t;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
* \brief FAT short directory entry * \brief FAT short directory entry
* *
* Short means short 8.3 name, not the entry size. * Short means short 8.3 name, not the entry size.
*
* Date Format. A FAT directory entry date stamp is a 16-bit field that is
*
* Date Format. A FAT directory entry date stamp is a 16-bit field that is
* basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the * basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the
* format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the
* format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the
* 16-bit word): * 16-bit word):
*
* Bits 9-15: Count of years from 1980, valid value range 0-127
*
* Bits 9-15: Count of years from 1980, valid value range 0-127
* inclusive (1980-2107). * inclusive (1980-2107).
*
*
* Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive. * Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive.
* *
* Bits 0-4: Day of month, valid value range 1-31 inclusive. * Bits 0-4: Day of month, valid value range 1-31 inclusive.
* *
* Time Format. A FAT directory entry time stamp is a 16-bit field that has * Time Format. A FAT directory entry time stamp is a 16-bit field that has
* a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the
* a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the
* 16-bit word, bit 15 is the MSB of the 16-bit word). * 16-bit word, bit 15 is the MSB of the 16-bit word).
*
*
* Bits 11-15: Hours, valid value range 0-23 inclusive. * Bits 11-15: Hours, valid value range 0-23 inclusive.
*
*
* Bits 5-10: Minutes, valid value range 0-59 inclusive. * Bits 5-10: Minutes, valid value range 0-59 inclusive.
*
*
* Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds). * Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds).
*
*
* The valid time range is from Midnight 00:00:00 to 23:59:58. * The valid time range is from Midnight 00:00:00 to 23:59:58.
*/ */
struct directoryEntry { struct directoryEntry {
/** Short 8.3 name.
*
* The first eight bytes contain the file name with blank fill.
* The last three bytes contain the file extension with blank fill.
*/
/** Short 8.3 name.
*
* The first eight bytes contain the file name with blank fill.
* The last three bytes contain the file extension with blank fill.
*/
uint8_t name[11]; uint8_t name[11];
/** Entry attributes.
*
* The upper two bits of the attribute byte are reserved and should
* always be set to 0 when a file is created and never modified or
* looked at after that. See defines that begin with DIR_ATT_.
*/
/** Entry attributes.
*
* The upper two bits of the attribute byte are reserved and should
* always be set to 0 when a file is created and never modified or
* looked at after that. See defines that begin with DIR_ATT_.
*/
uint8_t attributes; uint8_t attributes;
/**
* Reserved for use by Windows NT. Set value to 0 when a file is
* created and never modify or look at it after that.
*/
/**
* Reserved for use by Windows NT. Set value to 0 when a file is
* created and never modify or look at it after that.
*/
uint8_t reservedNT; uint8_t reservedNT;
/**
* The granularity of the seconds part of creationTime is 2 seconds
* so this field is a count of tenths of a second and its valid
* value range is 0-199 inclusive. (WHG note - seems to be hundredths)
*/
/**
* The granularity of the seconds part of creationTime is 2 seconds
* so this field is a count of tenths of a second and its valid
* value range is 0-199 inclusive. (WHG note - seems to be hundredths)
*/
uint8_t creationTimeTenths; uint8_t creationTimeTenths;
/** Time file was created. */
/** Time file was created. */
uint16_t creationTime; uint16_t creationTime;
/** Date file was created. */
/** Date file was created. */
uint16_t creationDate; uint16_t creationDate;
/**
* Last access date. Note that there is no last access time, only
* a date. This is the date of last read or write. In the case of
* a write, this should be set to the same date as lastWriteDate.
*/
/**
* Last access date. Note that there is no last access time, only
* a date. This is the date of last read or write. In the case of
* a write, this should be set to the same date as lastWriteDate.
*/
uint16_t lastAccessDate; uint16_t lastAccessDate;
/**
* High word of this entry's first cluster number (always 0 for a
* FAT12 or FAT16 volume).
*/
/**
* High word of this entry's first cluster number (always 0 for a
* FAT12 or FAT16 volume).
*/
uint16_t firstClusterHigh; uint16_t firstClusterHigh;
/** Time of last write. File creation is considered a write. */
/** Time of last write. File creation is considered a write. */
uint16_t lastWriteTime; uint16_t lastWriteTime;
/** Date of last write. File creation is considered a write. */
/** Date of last write. File creation is considered a write. */
uint16_t lastWriteDate; uint16_t lastWriteDate;
/** Low word of this entry's first cluster number. */
/** Low word of this entry's first cluster number. */
uint16_t firstClusterLow; uint16_t firstClusterLow;
/** 32-bit unsigned holding this file's size in bytes. */
/** 32-bit unsigned holding this file's size in bytes. */
uint32_t fileSize; uint32_t fileSize;
}__attribute__((packed));
} __attribute__((packed));
/** Type name for directoryEntry */ /** Type name for directoryEntry */
typedef struct directoryEntry dir_t; typedef struct directoryEntry dir_t;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** Mask for file/subdirectory tests */ /** Mask for file/subdirectory tests */
uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);


/** Filename base-name is all lower case */
const uint8_t DIR_NT_LC_BASE = 0X08;
/** Filename extension is all lower case.*/
const uint8_t DIR_NT_LC_EXT = 0X10;


/** Directory entry is for a file /** Directory entry is for a file
* \param[in] dir Pointer to a directory entry. * \param[in] dir Pointer to a directory entry.
* *
* \return true if the entry is for part of a long name else false. * \return true if the entry is for part of a long name else false.
*/ */
static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) {
return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
return dir->attributes == DIR_ATT_LONG_NAME;
} }
/** Directory entry is hidden
/** Directory entry is hidden
* \param[in] dir Pointer to a directory entry. * \param[in] dir Pointer to a directory entry.
* *
* \return true if the entry is hidden else false. * \return true if the entry is hidden else false.
static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) { static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
} }
/** Directory entry is system type
/** Directory entry is system type
* \param[in] dir Pointer to a directory entry. * \param[in] dir Pointer to a directory entry.
* *
* \return true if the entry is system else false. * \return true if the entry is system else false.
* \brief FAT long directory entry * \brief FAT long directory entry
*/ */
struct longDirectoryEntry { struct longDirectoryEntry {
/**
* The order of this entry in the sequence of long dir entries
* associated with the short dir entry at the end of the long dir set.
*
* If masked with 0x40 (LAST_LONG_ENTRY), this indicates the
* entry is the last long dir entry in a set of long dir entries.
* All valid sets of long dir entries must begin with an entry having
* this mask.
*/
/**
* The order of this entry in the sequence of long dir entries
* associated with the short dir entry at the end of the long dir set.
*
* If masked with 0X40 (LAST_LONG_ENTRY), this indicates the
* entry is the last long dir entry in a set of long dir entries.
* All valid sets of long dir entries must begin with an entry having
* this mask.
*/
uint8_t ord; uint8_t ord;
/** Characters 1-5 of the long-name sub-component in this entry. */
/** Characters 1-5 of the long-name sub-component in this entry. */
uint16_t name1[LDIR_NAME1_DIM]; uint16_t name1[LDIR_NAME1_DIM];
/** Attributes - must be ATTR_LONG_NAME */
/** Attributes - must be ATTR_LONG_NAME */
uint8_t attr; uint8_t attr;
/**
* If zero, indicates a directory entry that is a sub-component of a
* long name. NOTE: Other values reserved for future extensions.
*
* Non-zero implies other directory entry types.
*/
/**
* If zero, indicates a directory entry that is a sub-component of a
* long name. NOTE: Other values reserved for future extensions.
*
* Non-zero implies other directory entry types.
*/
uint8_t type; uint8_t type;
/**
* Checksum of name in the short dir entry at the end of the
* long dir set.
*/
/**
* Checksum of name in the short dir entry at the end of the
* long dir set.
*/
uint8_t chksum; uint8_t chksum;
/** Characters 6-11 of the long-name sub-component in this entry. */
/** Characters 6-11 of the long-name sub-component in this entry. */
uint16_t name2[LDIR_NAME2_DIM]; uint16_t name2[LDIR_NAME2_DIM];
/** Must be ZERO. This is an artifact of the FAT "first cluster" */
/** Must be ZERO. This is an artifact of the FAT "first cluster" */
uint16_t mustBeZero; uint16_t mustBeZero;
/** Characters 6-11 of the long-name sub-component in this entry. */
/** Characters 12 and 13 of the long-name sub-component in this entry. */
uint16_t name3[LDIR_NAME3_DIM]; uint16_t name3[LDIR_NAME3_DIM];
}__attribute__((packed));
} __attribute__((packed));
/** Type name for longDirectoryEntry */ /** Type name for longDirectoryEntry */
typedef struct longDirectoryEntry ldir_t; typedef struct longDirectoryEntry ldir_t;
/** /**
* begin with an entry having this mask. * begin with an entry having this mask.
*/ */
const uint8_t LDIR_ORD_LAST_LONG_ENTRY = 0X40; const uint8_t LDIR_ORD_LAST_LONG_ENTRY = 0X40;
/**
* Fetch a 16-bit long file name character.
*
* \param[in] ldir Pointer to long file name directory entry.
* \param[in] i Index of character to return;
* \return The 16-bit field.
*/
inline uint16_t lfnChar(ldir_t *ldir, uint8_t i) {
return i < LDIR_NAME1_DIM ? ldir->name1[i] :
i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM) ?
ldir->name2[i - LDIR_NAME1_DIM] :
ldir->name3[i - (LDIR_NAME1_DIM + LDIR_NAME2_DIM)];
}
#endif // FatStructs_h #endif // FatStructs_h

+ 154
- 52
SdFat/utility/FatVolume.cpp View File

* along with the FatLib Library. If not, see * along with the FatLib Library. If not, see
* <http://www.gnu.org/licenses/>. * <http://www.gnu.org/licenses/>.
*/ */
#include <string.h>
#include "FatVolume.h" #include "FatVolume.h"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
cache_t* FatCache::read(uint32_t lbn, uint8_t option) { cache_t* FatCache::read(uint32_t lbn, uint8_t option) {
m_status |= option & CACHE_STATUS_MASK; m_status |= option & CACHE_STATUS_MASK;
return &m_block; return &m_block;


fail:
fail:
return 0; return 0;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
} }
return true; return true;


fail:
fail:
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
while (1) { while (1) {
find++; find++;
// If at end of FAT go to beginning of FAT. // If at end of FAT go to beginning of FAT.
if (find > m_lastCluster) find = 2;
if (find > m_lastCluster) {
find = 2;
}


uint32_t f; uint32_t f;
if (!fatGet(find, &f)) {
int8_t fg = fatGet(find, &f);
if (fg < 0) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
if (f == 0) break;
if (fg && f == 0) {
break;
}
if (find == start) { if (find == start) {
// Can't find space checked all clusters. // Can't find space checked all clusters.
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
*next = find; *next = find;
return true; return true;


fail:
fail:
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bgnCluster = endCluster = 2; bgnCluster = endCluster = 2;
} }
uint32_t f; uint32_t f;
if (!fatGet(endCluster, &f)) {
int8_t fg = fatGet(endCluster, &f);
if (fg < 0) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
if (f != 0) {
// don't update search start if unallocated clusters before endCluster.
if (bgnCluster != endCluster) setStart = false;

if (f || fg == 0) {
// cluster in use try next cluster as bgnCluster // cluster in use try next cluster as bgnCluster
bgnCluster = endCluster + 1; bgnCluster = endCluster + 1;

// don't update search start if unallocated clusters before endCluster.
if (bgnCluster != endCluster) {
setStart = false;
}
} else if ((endCluster - bgnCluster + 1) == count) { } else if ((endCluster - bgnCluster + 1) == count) {
// done - found space // done - found space
break; break;
endCluster++; endCluster++;
} }
// remember possible next free cluster // remember possible next free cluster
if (setStart) m_allocSearchStart = endCluster + 1;
if (setStart) {
m_allocSearchStart = endCluster + 1;
}


// mark end of chain // mark end of chain
if (!fatPutEOC(endCluster)) { if (!fatPutEOC(endCluster)) {
*firstCluster = bgnCluster; *firstCluster = bgnCluster;
return true; return true;


fail:
fail:
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Fetch a FAT entry
bool FatVolume::fatGet(uint32_t cluster, uint32_t* value) {
// Fetch a FAT entry - return -1 error, 0 EOC, else 1.
int8_t FatVolume::fatGet(uint32_t cluster, uint32_t* value) {
uint32_t lba; uint32_t lba;
uint32_t next;
cache_t* pc; cache_t* pc;


// error if reserved cluster of beyond FAT // error if reserved cluster of beyond FAT
if (cluster < 2 || cluster > m_lastCluster) {
DBG_FAIL_MACRO;
goto fail;
}
DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster);


if (m_fatType == 32) { if (m_fatType == 32) {
lba = m_fatStartBlock + (cluster >> 7); lba = m_fatStartBlock + (cluster >> 7);
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
*value = pc->fat32[cluster & 0X7F] & FAT32MASK;
return true;
next = pc->fat32[cluster & 0X7F] & FAT32MASK;
goto done;
} }


if (m_fatType == 16) { if (m_fatType == 16) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
*value = pc->fat16[cluster & 0XFF];
return true;
next = pc->fat16[cluster & 0XFF];
goto done;
} }
if (FAT12_SUPPORT && m_fatType == 12) { if (FAT12_SUPPORT && m_fatType == 12) {
uint16_t index = cluster; uint16_t index = cluster;
index = 0; index = 0;
} }
tmp |= pc->data[index] << 8; tmp |= pc->data[index] << 8;
*value = cluster & 1 ? tmp >> 4 : tmp & 0XFFF;
return true;
next = cluster & 1 ? tmp >> 4 : tmp & 0XFFF;
goto done;
} else { } else {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
done:
if (isEOC(next)) {
return 0;
}
*value = next;
return 1;


fail:
return false;
fail:
return -1;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Store a FAT entry // Store a FAT entry
cache_t* pc; cache_t* pc;


// error if reserved cluster of beyond FAT // error if reserved cluster of beyond FAT
if (cluster < 2 || cluster > m_lastCluster) {
DBG_FAIL_MACRO;
goto fail;
}
DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster);


if (m_fatType == 32) { if (m_fatType == 32) {
lba = m_fatStartBlock + (cluster >> 7);
lba = m_fatStartBlock + (cluster >> 7);
pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE);
if (!pc) { if (!pc) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }


fail:
fail:
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// free a cluster chain // free a cluster chain
bool FatVolume::freeChain(uint32_t cluster) { bool FatVolume::freeChain(uint32_t cluster) {
uint32_t next; uint32_t next;
int8_t fg;
do { do {
if (!fatGet(cluster, &next)) {
fg = fatGet(cluster, &next);
if (fg < 0) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
if (cluster < m_allocSearchStart) m_allocSearchStart = cluster;
if (cluster < m_allocSearchStart) {
m_allocSearchStart = cluster;
}
cluster = next; cluster = next;
} while (!isEOC(cluster));
} while (fg);


return true; return true;


fail:
fail:
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
if (FAT12_SUPPORT && m_fatType == 12) { if (FAT12_SUPPORT && m_fatType == 12) {
for (unsigned i = 2; i < todo; i++) { for (unsigned i = 2; i < todo; i++) {
uint32_t c; uint32_t c;
if (!fatGet(i, &c)) {
int8_t fg = fatGet(i, &c);
if (fg < 0) {
DBG_FAIL_MACRO; DBG_FAIL_MACRO;
goto fail; goto fail;
} }
if (c == 0) free++;
if (fg && c == 0) {
free++;
}
} }
} else if (m_fatType == 16 || m_fatType == 32) { } else if (m_fatType == 16 || m_fatType == 32) {
lba = m_fatStartBlock; lba = m_fatStartBlock;
goto fail; goto fail;
} }
n = m_fatType == 16 ? 256 : 128; n = m_fatType == 16 ? 256 : 128;
if (todo < n) n = todo;
if (todo < n) {
n = todo;
}
if (m_fatType == 16) { if (m_fatType == 16) {
for (uint16_t i = 0; i < n; i++) { for (uint16_t i = 0; i < n; i++) {
if (pc->fat16[i] == 0) free++;
if (pc->fat16[i] == 0) {
free++;
}
} }
} else { } else {
for (uint16_t i = 0; i < n; i++) { for (uint16_t i = 0; i < n; i++) {
if (pc->fat32[i] == 0) free++;
if (pc->fat32[i] == 0) {
free++;
}
} }
} }
todo -= n; todo -= n;
} }
return free; return free;


fail:
fail:
return -1; return -1;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
} }
fbs = &(pc->fbs32); fbs = &(pc->fbs32);
if (fbs->bytesPerSector != 512 || if (fbs->bytesPerSector != 512 ||
fbs->fatCount != 2 ||
fbs->reservedSectorCount == 0) {
// not valid FAT volume
DBG_FAIL_MACRO;
goto fail;
fbs->fatCount != 2 ||
fbs->reservedSectorCount == 0) {
// not valid FAT volume
DBG_FAIL_MACRO;
goto fail;
} }
m_blocksPerCluster = fbs->sectorsPerCluster; m_blocksPerCluster = fbs->sectorsPerCluster;
m_clusterBlockMask = m_blocksPerCluster - 1; m_clusterBlockMask = m_blocksPerCluster - 1;
} }


m_blocksPerFat = fbs->sectorsPerFat16 ? m_blocksPerFat = fbs->sectorsPerFat16 ?
fbs->sectorsPerFat16 : fbs->sectorsPerFat32;
fbs->sectorsPerFat16 : fbs->sectorsPerFat32;


m_fatStartBlock = volumeStartBlock + fbs->reservedSectorCount; m_fatStartBlock = volumeStartBlock + fbs->reservedSectorCount;




// total blocks for FAT16 or FAT32 // total blocks for FAT16 or FAT32
totalBlocks = fbs->totalSectors16 ? totalBlocks = fbs->totalSectors16 ?
fbs->totalSectors16 : fbs->totalSectors32;
fbs->totalSectors16 : fbs->totalSectors32;
// total data blocks // total data blocks
clusterCount = totalBlocks - (m_dataStartBlock - volumeStartBlock); clusterCount = totalBlocks - (m_dataStartBlock - volumeStartBlock);


// divide by cluster size to get cluster count // divide by cluster size to get cluster count
clusterCount >>= m_clusterSizeShift; clusterCount >>= m_clusterSizeShift;
m_lastCluster = clusterCount + 1; m_lastCluster = clusterCount + 1;

// FAT type is determined by cluster count // FAT type is determined by cluster count
if (clusterCount < 4085) { if (clusterCount < 4085) {
m_fatType = 12; m_fatType = 12;
} }
return true; return true;


fail:
fail:
return false;
}
//------------------------------------------------------------------------------
bool FatVolume::wipe(print_t* pr) {
cache_t* cache;
uint16_t count;
uint32_t lbn;
if (!m_fatType) {
DBG_FAIL_MACRO;
goto fail;
}
cache = cacheClear();
if (!cache) {
DBG_FAIL_MACRO;
goto fail;
}
memset(cache->data, 0, 512);
// Zero root.
if (m_fatType == 32) {
lbn = clusterStartBlock(m_rootDirStart);
count = m_blocksPerCluster;
} else {
lbn = m_rootDirStart;
count = m_rootDirEntryCount/16;
}
for (uint32_t n = 0; n < count; n++) {
if (!writeBlock(lbn + n, cache->data)) {
DBG_FAIL_MACRO;
goto fail;
}
}
// Clear FATs.
count = 2*m_blocksPerFat;
lbn = m_fatStartBlock;
for (uint32_t nb = 0; nb < count; nb++) {
if (pr && (nb & 0XFF) == 0) {
pr->write('.');
}
if (!writeBlock(lbn + nb, cache->data)) {
DBG_FAIL_MACRO;
goto fail;
}
}
// Reserve first two clusters.
if (m_fatType == 32) {
cache->fat32[0] = 0x0FFFFFF8;
cache->fat32[1] = 0x0FFFFFFF;
} else if (m_fatType == 16) {
cache->fat16[0] = 0XFFF8;
cache->fat16[1] = 0XFFFF;
} else if (FAT12_SUPPORT && m_fatType == 12) {
cache->fat32[0] = 0XFFFFF8;
} else {
DBG_FAIL_MACRO;
goto fail;
}
if (!writeBlock(m_fatStartBlock, cache->data) ||
!writeBlock(m_fatStartBlock + m_blocksPerFat, cache->data)) {
DBG_FAIL_MACRO;
goto fail;
}
if (m_fatType == 32) {
// Reserve root cluster.
if (!fatPutEOC(m_rootDirStart) || !cacheSync()) {
DBG_FAIL_MACRO;
goto fail;
}
}
if (pr) {
pr->write('\r');
pr->write('\n');
}
m_fatType = 0;
return true;

fail:
m_fatType = 0;
return false; return false;
} }

+ 123
- 46
SdFat/utility/FatVolume.h View File

#include "FatLibConfig.h" #include "FatLibConfig.h"
#include "FatStructs.h" #include "FatStructs.h"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifndef DOXYGEN_SHOULD_SKIP_THIS
/** Macro for debug. */ /** Macro for debug. */
// #include <Arduino.h>
#define DBG_FAIL_MACRO // Serial.print(__FILE__);Serial.println(__LINE__)
#define DEBUG_MODE 0
#if DEBUG_MODE
#include <Arduino.h>
#define DBG_FAIL_MACRO Serial.print(F(__FILE__)); Serial.println(__LINE__)
#define DBG_PRINT_IF(b) if (b) {Serial.println(F(#b)); DBG_FAIL_MACRO;}
#define DBG_HALT_IF(b) if (b) {Serial.println(F(#b));\
DBG_FAIL_MACRO; while (1);}
#else // DEBUG_MODE
#define DBG_FAIL_MACRO
#define DBG_PRINT_IF(b)
#define DBG_HALT_IF(b)
#endif // DEBUG_MODE
#endif // DOXYGEN_SHOULD_SKIP_THIS
//------------------------------------------------------------------------------
#if defined(ARDUINO) || defined(DOXYGEN)
#include <Arduino.h>
/** Use Print on Arduino */
typedef Print print_t;
#else // ARDUINO
// Arduino type for flash string.
class __FlashStringHelper;
/**
* \class CharWriter
* \brief Character output - often serial port.
*/
class CharWriter {
public:
virtual size_t write(char c) = 0;
virtual size_t write(const char* s) = 0;
};
typedef Print print_t;
#endif // ARDUINO
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Forward declaration of FatVolume. // Forward declaration of FatVolume.
class FatVolume; class FatVolume;
* \brief Cache for an raw data block. * \brief Cache for an raw data block.
*/ */
union cache_t { union cache_t {
/** Used to access cached file data blocks. */
/** Used to access cached file data blocks. */
uint8_t data[512]; uint8_t data[512];
/** Used to access cached FAT16 entries. */
/** Used to access cached FAT16 entries. */
uint16_t fat16[256]; uint16_t fat16[256];
/** Used to access cached FAT32 entries. */
/** Used to access cached FAT32 entries. */
uint32_t fat32[128]; uint32_t fat32[128];
/** Used to access cached directory entries. */
/** Used to access cached directory entries. */
dir_t dir[16]; dir_t dir[16];
/** Used to access a cached Master Boot Record. */
/** Used to access a cached Master Boot Record. */
mbr_t mbr; mbr_t mbr;
/** Used to access to a cached FAT boot sector. */
/** Used to access to a cached FAT boot sector. */
fat_boot_t fbs; fat_boot_t fbs;
/** Used to access to a cached FAT32 boot sector. */
/** Used to access to a cached FAT32 boot sector. */
fat32_boot_t fbs32; fat32_boot_t fbs32;
/** Used to access to a cached FAT32 FSINFO sector. */
/** Used to access to a cached FAT32 FSINFO sector. */
fat32_fsinfo_t fsinfo; fat32_fsinfo_t fsinfo;
}; };
//============================================================================== //==============================================================================
static const uint8_t CACHE_STATUS_MIRROR_FAT = 2; static const uint8_t CACHE_STATUS_MIRROR_FAT = 2;
/** Cache block status bits */ /** Cache block status bits */
static const uint8_t CACHE_STATUS_MASK static const uint8_t CACHE_STATUS_MASK
= CACHE_STATUS_DIRTY | CACHE_STATUS_MIRROR_FAT;
= CACHE_STATUS_DIRTY | CACHE_STATUS_MIRROR_FAT;
/** Sync existing block but do not read new block. */ /** Sync existing block but do not read new block. */
static const uint8_t CACHE_OPTION_NO_READ = 4; static const uint8_t CACHE_OPTION_NO_READ = 4;
/** Cache block for read. */ /** Cache block for read. */
static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY;
/** Reserve cache block for write - do not read from block device. */ /** Reserve cache block for write - do not read from block device. */
static uint8_t const CACHE_RESERVE_FOR_WRITE static uint8_t const CACHE_RESERVE_FOR_WRITE
= CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ;
= CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ;
/** \return Cache block address. */ /** \return Cache block address. */
cache_t* block() {return &m_block;}
cache_t* block() {
return &m_block;
}
/** Set current block dirty. */ /** Set current block dirty. */
void dirty() {m_status |= CACHE_STATUS_DIRTY;}
void dirty() {
m_status |= CACHE_STATUS_DIRTY;
}
/** Initialize the cache. /** Initialize the cache.
* \param[in] vol FatVolume that owns this FatCache. * \param[in] vol FatVolume that owns this FatCache.
*/ */
m_lbn = 0XFFFFFFFF; m_lbn = 0XFFFFFFFF;
} }
/** \return Logical block number for cached block. */ /** \return Logical block number for cached block. */
uint32_t lbn() {return m_lbn;}
uint32_t lbn() {
return m_lbn;
}
/** Read a block into the cache. /** Read a block into the cache.
* \param[in] lbn Block to read. * \param[in] lbn Block to read.
* \param[in] option mode for cached block. * \param[in] option mode for cached block.
FatVolume() : m_fatType(0) {} FatVolume() : m_fatType(0) {}


/** \return The volume's cluster size in blocks. */ /** \return The volume's cluster size in blocks. */
uint8_t blocksPerCluster() const {return m_blocksPerCluster;}
uint8_t blocksPerCluster() const {
return m_blocksPerCluster;
}
/** \return The number of blocks in one FAT. */ /** \return The number of blocks in one FAT. */
uint32_t blocksPerFat() const {return m_blocksPerFat;}
uint32_t blocksPerFat() const {
return m_blocksPerFat;
}
/** Clear the cache and returns a pointer to the cache. Not for normal apps. /** Clear the cache and returns a pointer to the cache. Not for normal apps.
* \return A pointer to the cache buffer or zero if an error occurs. * \return A pointer to the cache buffer or zero if an error occurs.
*/ */
cache_t* cacheClear() { cache_t* cacheClear() {
if (!cacheSync()) return 0;
if (!cacheSync()) {
return 0;
}
m_cache.invalidate(); m_cache.invalidate();
return m_cache.block(); return m_cache.block();
} }
/** \return The total number of clusters in the volume. */ /** \return The total number of clusters in the volume. */
uint32_t clusterCount() const {return m_lastCluster - 1;}
uint32_t clusterCount() const {
return m_lastCluster - 1;
}
/** \return The shift count required to multiply by blocksPerCluster. */ /** \return The shift count required to multiply by blocksPerCluster. */
uint8_t clusterSizeShift() const {return m_clusterSizeShift;}
uint8_t clusterSizeShift() const {
return m_clusterSizeShift;
}
/** \return The logical block number for the start of file data. */ /** \return The logical block number for the start of file data. */
uint32_t dataStartBlock() const {return m_dataStartBlock;}
uint32_t dataStartBlock() const {
return m_dataStartBlock;
}
/** \return The number of File Allocation Tables. */ /** \return The number of File Allocation Tables. */
uint8_t fatCount() {return 2;}
uint8_t fatCount() {
return 2;
}
/** \return The logical block number for the start of the first FAT. */ /** \return The logical block number for the start of the first FAT. */
uint32_t fatStartBlock() const {return m_fatStartBlock;}
uint32_t fatStartBlock() const {
return m_fatStartBlock;
}
/** \return The FAT type of the volume. Values are 12, 16 or 32. */ /** \return The FAT type of the volume. Values are 12, 16 or 32. */
uint8_t fatType() const {return m_fatType;}
uint8_t fatType() const {
return m_fatType;
}
/** Volume free space in clusters. /** Volume free space in clusters.
* *
* \return Count of free clusters for success or -1 if an error occurs. * \return Count of free clusters for success or -1 if an error occurs.
/** Initialize a FAT volume. Try partition one first then try super /** Initialize a FAT volume. Try partition one first then try super
* floppy format. * floppy format.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure. Reasons for
* failure include not finding a valid partition, not finding a valid
* FAT file system or an I/O error.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool init() { return init(1) ? true : init(0);}
bool init() {
return init(1) ? true : init(0);
}
/** Initialize a FAT volume. /** Initialize a FAT volume.


* \param[in] part The partition to be used. Legal values for \a part are * \param[in] part The partition to be used. Legal values for \a part are
* a MBR, Master Boot Record, or zero if the device is formatted as * a MBR, Master Boot Record, or zero if the device is formatted as
* a super floppy with the FAT boot sector in block zero. * a super floppy with the FAT boot sector in block zero.
* *
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure. Reasons for
* failure include not finding a valid partition, not finding a valid
* FAT file system in the specified partition or an I/O error.
* \return The value true is returned for success and
* the value false is returned for failure.
*/ */
bool init(uint8_t part); bool init(uint8_t part);
/** \return The number of entries in the root directory for FAT16 volumes. */ /** \return The number of entries in the root directory for FAT16 volumes. */
uint32_t rootDirEntryCount() const {return m_rootDirEntryCount;}
uint16_t rootDirEntryCount() const {
return m_rootDirEntryCount;
}
/** \return The logical block number for the start of the root directory /** \return The logical block number for the start of the root directory
on FAT16 volumes or the first cluster number on FAT32 volumes. */ on FAT16 volumes or the first cluster number on FAT32 volumes. */
uint32_t rootDirStart() const {return m_rootDirStart;}
uint32_t rootDirStart() const {
return m_rootDirStart;
}
/** Wipe all data from the volume.
* \param[in] pr print stream for status dots.
* \return true for success else false.
*/
bool wipe(print_t* pr = 0);
/** Debug access to FAT table /** Debug access to FAT table
* *
* \param[in] n cluster number. * \param[in] n cluster number.
* \param[out] v value of entry * \param[out] v value of entry
* \return true for success or false for failure * \return true for success or false for failure
*/ */
bool dbgFat(uint32_t n, uint32_t* v) {return fatGet(n, v);}
int8_t dbgFat(uint32_t n, uint32_t* v) {
return fatGet(n, v);
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
private: private:
// Allow FatFile and FatCache access to FatVolume private functions. // Allow FatFile and FatCache access to FatVolume private functions.
return m_fatCache.read(blockNumber, return m_fatCache.read(blockNumber,
options | FatCache::CACHE_STATUS_MIRROR_FAT); options | FatCache::CACHE_STATUS_MIRROR_FAT);
} }
bool cacheSync() {return m_cache.sync() && m_fatCache.sync();}
bool cacheSync() {
return m_cache.sync() && m_fatCache.sync();
}
#else // #else //
cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) {
return cacheFetchData(blockNumber, return cacheFetchData(blockNumber,
options | FatCache::CACHE_STATUS_MIRROR_FAT); options | FatCache::CACHE_STATUS_MIRROR_FAT);
} }
bool cacheSync() {return m_cache.sync();}
bool cacheSync() {
return m_cache.sync();
}
#endif // USE_SEPARATE_FAT_CACHE #endif // USE_SEPARATE_FAT_CACHE
cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) {
return m_cache.read(blockNumber, options); return m_cache.read(blockNumber, options);
void cacheInvalidate() { void cacheInvalidate() {
m_cache.invalidate(); m_cache.invalidate();
} }
bool cacheSyncData() {return m_cache.sync();}
cache_t *cacheAddress() {return m_cache.block();}
uint32_t cacheBlockNumber() {return m_cache.lbn();}
void cacheDirty() {m_cache.dirty();}
bool cacheSyncData() {
return m_cache.sync();
}
cache_t *cacheAddress() {
return m_cache.block();
}
uint32_t cacheBlockNumber() {
return m_cache.lbn();
}
void cacheDirty() {
m_cache.dirty();
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool allocateCluster(uint32_t current, uint32_t* next); bool allocateCluster(uint32_t current, uint32_t* next);
bool allocContiguous(uint32_t count, uint32_t* firstCluster); bool allocContiguous(uint32_t count, uint32_t* firstCluster);
uint8_t blockOfCluster(uint32_t position) const { uint8_t blockOfCluster(uint32_t position) const {
return (position >> 9) & m_clusterBlockMask;}
return (position >> 9) & m_clusterBlockMask;
}
uint32_t clusterStartBlock(uint32_t cluster) const; uint32_t clusterStartBlock(uint32_t cluster) const;
bool fatGet(uint32_t cluster, uint32_t* value);
int8_t fatGet(uint32_t cluster, uint32_t* value);
bool fatPut(uint32_t cluster, uint32_t value); bool fatPut(uint32_t cluster, uint32_t value);
bool fatPutEOC(uint32_t cluster) { bool fatPutEOC(uint32_t cluster) {
return fatPut(cluster, 0x0FFFFFFF); return fatPut(cluster, 0x0FFFFFFF);
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Virtual block I/O functions. // Virtual block I/O functions.
virtual bool readBlock(uint32_t block, uint8_t* dst) = 0; virtual bool readBlock(uint32_t block, uint8_t* dst) = 0;
virtual bool writeBlock(uint32_t block, const uint8_t* dst) = 0;
virtual bool writeBlock(uint32_t block, const uint8_t* src) = 0;
#if USE_MULTI_BLOCK_IO #if USE_MULTI_BLOCK_IO
virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0; virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0;
virtual bool writeBlocks(uint32_t block, const uint8_t* dst, size_t nb) = 0;
virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0;
#endif // USE_MULTI_BLOCK_IO #endif // USE_MULTI_BLOCK_IO
}; };
#endif // FatVolume #endif // FatVolume

+ 64
- 24
SdFat/utility/FmtNumber.cpp View File

n &= 63; n &= 63;
for (uint8_t i = 0; n; n >>= 1, i++) { for (uint8_t i = 0; n; n >>= 1, i++) {
#ifdef __AVR__ #ifdef __AVR__
if (n & 1) v *= pgm_read_float(&s[i]);
if (n & 1) {
v *= pgm_read_float(&s[i]);
}
#else // __AVR__ #else // __AVR__
if (n & 1) v *= s[i];
if (n & 1) {
v *= s[i];
}
#endif // __AVR__ #endif // __AVR__
} }
return v; return v;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
char* fmtFloat(float value, char* p, uint8_t prec) { char* fmtFloat(float value, char* p, uint8_t prec) {
char sign = value < 0 ? '-' : 0; char sign = value < 0 ? '-' : 0;
if (sign) value = -value;
if (sign) {
value = -value;
}


if (isnan(value)) { if (isnan(value)) {
*--p = 'n'; *--p = 'n';
*--p = 'o'; *--p = 'o';
return p; return p;
} }
if (prec > 9) prec = 9;
if (prec > 9) {
prec = 9;
}
value += scale10(0.5, -prec); value += scale10(0.5, -prec);


uint32_t whole = value; uint32_t whole = value;
char* tmp = p - prec; char* tmp = p - prec;
uint32_t fraction = scale10(value - whole, prec); uint32_t fraction = scale10(value - whole, prec);
p = fmtDec(fraction, p); p = fmtDec(fraction, p);
while (p > tmp) *--p = '0';
while (p > tmp) {
*--p = '0';
}
*--p = '.'; *--p = '.';
} }
p = fmtDec(whole, p); p = fmtDec(whole, p);
if (sign) *--p = sign;
if (sign) {
*--p = sign;
}
return p; return p;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
*/ */
char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) { char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) {
bool neg = value < 0; bool neg = value < 0;
if (neg) value = -value;
if (neg) {
value = -value;
}


// check for nan inf ovf // check for nan inf ovf
if (isnan(value)) { if (isnan(value)) {
*--ptr = 'o'; *--ptr = 'o';
return ptr; return ptr;
} }
if (prec > 9) prec = 9;
if (prec > 9) {
prec = 9;
}
float round = scale10(0.5, -prec); float round = scale10(0.5, -prec);
if (expChar) { if (expChar) {
int8_t exp = 0; int8_t exp = 0;
exp++; exp++;
} }
expNeg = exp < 0; expNeg = exp < 0;
if (expNeg) exp = -exp;
if (expNeg) {
exp = -exp;
}
} }
ptr = fmtDec((uint16_t)exp, ptr); ptr = fmtDec((uint16_t)exp, ptr);
if (exp < 10) *--ptr = '0';
if (exp < 10) {
*--ptr = '0';
}
*--ptr = expNeg ? '-' : '+'; *--ptr = expNeg ? '-' : '+';
*--ptr = expChar; *--ptr = expChar;
} else { } else {
char* tmp = ptr - prec; char* tmp = ptr - prec;
uint32_t fraction = scale10(value - whole, prec); uint32_t fraction = scale10(value - whole, prec);
ptr = fmtDec(fraction, ptr); ptr = fmtDec(fraction, ptr);
while (ptr > tmp) *--ptr = '0';
while (ptr > tmp) {
*--ptr = '0';
}
*--ptr = '.'; *--ptr = '.';
} }
ptr = fmtDec(whole, ptr); ptr = fmtDec(whole, ptr);
if (neg) *--ptr = '-';
if (neg) {
*--ptr = '-';
}
return ptr; return ptr;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
float v; float v;
const char* successPtr; const char* successPtr;


if (ptr) *ptr = const_cast<char*>(str);
if (ptr) {
*ptr = const_cast<char*>(str);
}


while (isspace((c = *str++))) {}
while (isSpace((c = *str++))) {}
neg = c == '-'; neg = c == '-';
if (c == '-' || c == '+') c = *str++;
if (c == '-' || c == '+') {
c = *str++;
}
// Skip leading zeros // Skip leading zeros
while (c == '0') { while (c == '0') {
c = *str++; c = *str++;
digit = true; digit = true;
} }
for (;;) { for (;;) {
if (isdigit(c)) {
if (isDigit(c)) {
digit = true; digit = true;
if (nd < 9) { if (nd < 9) {
fract = 10*fract + c - '0'; fract = 10*fract + c - '0';
nd++; nd++;
if (dot) fracExp--;
if (dot) {
fracExp--;
}
} else { } else {
if (!dot) fracExp++;
if (!dot) {
fracExp++;
}
} }
} else if (c == '.') { } else if (c == '.') {
if (dot) goto fail;
if (dot) {
goto fail;
}
dot = true; dot = true;
} else { } else {
if (!digit) goto fail;
if (!digit) {
goto fail;
}
break; break;
} }
successPtr = str; successPtr = str;
if (c == '-' || c == '+') { if (c == '-' || c == '+') {
c = *str++; c = *str++;
} }
while (isdigit(c)) {
if (exp > EXP_LIMIT) goto fail;
while (isDigit(c)) {
if (exp > EXP_LIMIT) {
goto fail;
}
exp = 10*exp + c - '0'; exp = 10*exp + c - '0';
successPtr = str; successPtr = str;
c = *str++; c = *str++;
} }
fracExp += expNeg ? -exp : exp; fracExp += expNeg ? -exp : exp;
} }
if (ptr) *ptr = const_cast<char*>(successPtr);
if (ptr) {
*ptr = const_cast<char*>(successPtr);
}
v = scale10(static_cast<float>(fract), fracExp); v = scale10(static_cast<float>(fract), fracExp);
return neg ? -v: v; return neg ? -v: v;


fail:
fail:
return 0; return 0;
} }



+ 7
- 1
SdFat/utility/FmtNumber.h View File

*/ */
#ifndef FmtNumber_h #ifndef FmtNumber_h
#define FmtNumber_h #define FmtNumber_h
#include <ctype.h>
// #include <ctype.h>
inline bool isDigit(char c) {
return '0' <= c && c <= '9';
}
inline bool isSpace(char c) {
return c == ' ' || (0X9 <= c && c <= 0XD);
}
#include <math.h> #include <math.h>
#include <stdint.h> #include <stdint.h>
char* fmtDec(uint16_t n, char* p); char* fmtDec(uint16_t n, char* p);

+ 16
- 8
SdFat/utility/SoftSPI.h View File

* <http://www.gnu.org/licenses/>. * <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file
* @file
* @brief Software SPI. * @brief Software SPI.
* *
* @defgroup softSPI Software SPI * @defgroup softSPI Software SPI
private: private:
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
inline __attribute__((always_inline)) inline __attribute__((always_inline))
bool MODE_CPHA(uint8_t mode) {return (mode & 1) != 0;}
bool MODE_CPHA(uint8_t mode) {
return (mode & 1) != 0;
}
inline __attribute__((always_inline)) inline __attribute__((always_inline))
bool MODE_CPOL(uint8_t mode) {return (mode & 2) != 0;}
bool MODE_CPOL(uint8_t mode) {
return (mode & 2) != 0;
}
inline __attribute__((always_inline)) inline __attribute__((always_inline))
void receiveBit(uint8_t bit, uint8_t* data) { void receiveBit(uint8_t bit, uint8_t* data) {
if (MODE_CPHA(Mode)) { if (MODE_CPHA(Mode)) {
nop; nop;
nop; nop;
fastDigitalWrite(SckPin, fastDigitalWrite(SckPin,
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
if (fastDigitalRead(MisoPin)) *data |= 1 << bit;
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
if (fastDigitalRead(MisoPin)) {
*data |= 1 << bit;
}
if (!MODE_CPHA(Mode)) { if (!MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, MODE_CPOL(Mode)); fastDigitalWrite(SckPin, MODE_CPOL(Mode));
} }
} }
fastDigitalWrite(MosiPin, data & (1 << bit)); fastDigitalWrite(MosiPin, data & (1 << bit));
fastDigitalWrite(SckPin, fastDigitalWrite(SckPin,
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
nop; nop;
nop; nop;
if (!MODE_CPHA(Mode)) { if (!MODE_CPHA(Mode)) {
} }
fastDigitalWrite(MosiPin, txData & (1 << bit)); fastDigitalWrite(MosiPin, txData & (1 << bit));
fastDigitalWrite(SckPin, fastDigitalWrite(SckPin,
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
if (fastDigitalRead(MisoPin)) *rxData |= 1 << bit;
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
if (fastDigitalRead(MisoPin)) {
*rxData |= 1 << bit;
}
if (!MODE_CPHA(Mode)) { if (!MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, MODE_CPOL(Mode)); fastDigitalWrite(SckPin, MODE_CPOL(Mode));
} }

+ 132
- 54
SdFat/utility/StdioStream.cpp View File

return EOF; return EOF;
} }
if (m_flags & F_SWR) { if (m_flags & F_SWR) {
if (!flushBuf()) rtn = EOF;
if (!flushBuf()) {
rtn = EOF;
}
}
if (!FatFile::close()) {
rtn = EOF;
} }
if (!FatFile::close()) rtn = EOF;
m_r = 0; m_r = 0;
m_w = 0; m_w = 0;
m_flags = 0; m_flags = 0;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int StdioStream::fflush() { int StdioStream::fflush() {
if ((m_flags & (F_SWR | F_SRW)) && !(m_flags & F_SRD)) { if ((m_flags & (F_SWR | F_SRW)) && !(m_flags & F_SRD)) {
if (flushBuf() && FatFile::sync()) return 0;
if (flushBuf() && FatFile::sync()) {
return 0;
}
} }
return EOF; return EOF;
} }
char* StdioStream::fgets(char* str, size_t num, size_t* len) { char* StdioStream::fgets(char* str, size_t num, size_t* len) {
char* s = str; char* s = str;
size_t n; size_t n;
if (num-- == 0) return 0;
if (num-- == 0) {
return 0;
}
while (num) { while (num) {
if ((n = m_r) == 0) { if ((n = m_r) == 0) {
if (!fillBuf()) { if (!fillBuf()) {
if (s == str) return 0;
if (s == str) {
return 0;
}
break; break;
} }
n = m_r; n = m_r;
} }
if (n > num) n = num;
if (n > num) {
n = num;
}
uint8_t* end = reinterpret_cast<uint8_t*>(memchr(m_p, '\n', n)); uint8_t* end = reinterpret_cast<uint8_t*>(memchr(m_p, '\n', n));
if (end != 0) { if (end != 0) {
n = ++end - m_p; n = ++end - m_p;
num -= n; num -= n;
} }
*s = 0; *s = 0;
if (len) *len = s - str;
if (len) {
*len = s - str;
}
return str; return str;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
goto fail; goto fail;
} }
} }
if ((oflag & O_EXCL) && !(oflag & O_WRITE)) goto fail;
if (!FatFile::open(filename, oflag)) goto fail;
if ((oflag & O_EXCL) && !(oflag & O_WRITE)) {
goto fail;
}
if (!FatFile::open(filename, oflag)) {
goto fail;
}
m_r = 0; m_r = 0;
m_w = 0; m_w = 0;
m_p = m_buf; m_p = m_buf;
return true; return true;


fail:
fail:
m_flags = 0; m_flags = 0;
return false; return false;
} }
int StdioStream::fputs_P(PGM_P str) { int StdioStream::fputs_P(PGM_P str) {
PGM_P bgn = str; PGM_P bgn = str;
for (char c; (c = pgm_read_byte(str)); str++) { for (char c; (c = pgm_read_byte(str)); str++) {
if (putc(c) < 0) return EOF;
if (putc(c) < 0) {
return EOF;
}
} }
return str - bgn; return str - bgn;
} }
size_t StdioStream::fread(void* ptr, size_t size, size_t count) { size_t StdioStream::fread(void* ptr, size_t size, size_t count) {
uint8_t* dst = reinterpret_cast<uint8_t*>(ptr); uint8_t* dst = reinterpret_cast<uint8_t*>(ptr);
size_t total = size*count; size_t total = size*count;
if (total == 0) return 0;
if (total == 0) {
return 0;
}
size_t need = total; size_t need = total;
while (need > m_r) { while (need > m_r) {
memcpy(dst, m_p, m_r); memcpy(dst, m_p, m_r);
m_p = m_buf; m_p = m_buf;
return 0; return 0;


fail:
fail:
return EOF; return EOF;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int32_t StdioStream::ftell() { int32_t StdioStream::ftell() {
uint32_t pos = FatFile::curPosition(); uint32_t pos = FatFile::curPosition();
if (m_flags & F_SRD) { if (m_flags & F_SRD) {
if (m_r > pos) return -1L;
if (m_r > pos) {
return -1L;
}
pos -= m_r; pos -= m_r;
} else if (m_flags & F_SWR) { } else if (m_flags & F_SWR) {
pos += m_p - m_buf; pos += m_p - m_buf;
#if 0 //////////////////////////////////////////////////////////////////////////////////// #if 0 ////////////////////////////////////////////////////////////////////////////////////
const uint8_t* src = static_cast<const uint8_t*>(ptr); const uint8_t* src = static_cast<const uint8_t*>(ptr);
size_t total = count*size; size_t total = count*size;
if (total == 0) return 0;
if (total == 0) {
return 0;
}
size_t todo = total; size_t todo = total;


while (todo > m_w) { while (todo > m_w) {
#endif ////////////////////////////////////////////////////////////////////////////////// #endif //////////////////////////////////////////////////////////////////////////////////
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int StdioStream::write(const void* buf, size_t count) {
int StdioStream::write(const void* buf, size_t count) {
const uint8_t* src = static_cast<const uint8_t*>(buf); const uint8_t* src = static_cast<const uint8_t*>(buf);
size_t todo = count; size_t todo = count;


m_p += m_w; m_p += m_w;
src += m_w; src += m_w;
todo -= m_w; todo -= m_w;
if (!flushBuf()) return EOF;
if (!flushBuf()) {
return EOF;
}
} }
memcpy(m_p, src, todo); memcpy(m_p, src, todo);
m_p += todo; m_p += todo;
const char *p = (const char PROGMEM *)str; const char *p = (const char PROGMEM *)str;
uint8_t c; uint8_t c;
while ((c = pgm_read_byte(p))) { while ((c = pgm_read_byte(p))) {
if (putc(c) < 0) return 0;
if (putc(c) < 0) {
return 0;
}
p++; p++;
} }
return p - (const char PROGMEM *)str; return p - (const char PROGMEM *)str;
} }
// check for NaN INF OVF // check for NaN INF OVF
if (isnan(value)) { if (isnan(value)) {
if (fputs_P(PSTR("nan")) < 0) return -1;
if (fputs_P(PSTR("nan")) < 0) {
return -1;
}
rtn += 3; rtn += 3;
} else if (isinf(value)) { } else if (isinf(value)) {
if (fputs_P(PSTR("inf")) < 0) return -1;
if (fputs_P(PSTR("inf")) < 0) {
return -1;
}
rtn += 3; rtn += 3;
} else if (value > 4294967040.0) { } else if (value > 4294967040.0) {
if (fputs_P(PSTR("ovf")) < 0) return -1;;
if (fputs_P(PSTR("ovf")) < 0) {
return -1;
}
rtn += 3; rtn += 3;
} else { } else {
if (sign) { if (sign) {
if (putc(sign) < 0) return -1;
if (putc(sign) < 0) {
return -1;
}
rtn++; rtn++;
} }
if (prec > 9) prec = 9;
if (prec > 9) {
prec = 9;
}


/*
uint32_t s = 1;
for (uint8_t i = 0; i < prec; i++) {
// s *= 10;
s = ((s << 2) + s) << 1;
}
// round value
value += 0.5/s;
*/
/*
uint32_t s = 1;
for (uint8_t i = 0; i < prec; i++) {
// s *= 10;
s = ((s << 2) + s) << 1;
}
// round value
value += 0.5/s;
*/
value += scale10(0.5, -prec); value += scale10(0.5, -prec);
uint32_t whole = value; uint32_t whole = value;
int np; int np;
if ((np = printDec(whole)) < 0) return -1;
if ((np = printDec(whole)) < 0) {
return -1;
}
rtn += np; rtn += np;
if (prec) { if (prec) {
if (putc('.') < 0) return -1;
if (putc('.') < 0) {
return -1;
}
char* str = fmtSpace(prec); char* str = fmtSpace(prec);
if (!str) return -1;
if (!str) {
return -1;
}
char* tmp = str - prec; char* tmp = str - prec;


// uint32_t fraction = s*(value - whole);
// uint32_t fraction = s*(value - whole);
uint32_t fraction = scale10(value - whole, prec); uint32_t fraction = scale10(value - whole, prec);
ptr = fmtDec(fraction, str); ptr = fmtDec(fraction, str);
while (ptr > tmp) *--ptr = '0';
while (ptr > tmp) {
*--ptr = '0';
}
rtn += prec + 1; rtn += prec + 1;
} }
} }
return rtn; return rtn;
#endif
#endif
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int StdioStream::printDec(signed char n) { int StdioStream::printDec(signed char n) {
uint8_t s = 0; uint8_t s = 0;
if (n < 0) { if (n < 0) {
if (fputc('-') < 0) return -1;
if (fputc('-') < 0) {
return -1;
}
n = -n; n = -n;
s = 1; s = 1;
} }
int s; int s;
uint8_t rtn = 0; uint8_t rtn = 0;
if (n < 0) { if (n < 0) {
if (fputc('-') < 0) return -1;
if (fputc('-') < 0) {
return -1;
}
n = -n; n = -n;
rtn++; rtn++;
} }
if ((s = printDec((uint16_t)n)) < 0) return s;
if ((s = printDec((uint16_t)n)) < 0) {
return s;
}
return rtn; return rtn;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
len = n < 1000 ? 3 : n < 10000 ? 4 : 5; len = n < 1000 ? 3 : n < 10000 ? 4 : 5;
} }
char* str = fmtSpace(len); char* str = fmtSpace(len);
if (!str) return -1;
if (!str) {
return -1;
}
fmtDec(n, str); fmtDec(n, str);
return len; return len;
#endif #endif
int StdioStream::printDec(int32_t n) { int StdioStream::printDec(int32_t n) {
uint8_t s = 0; uint8_t s = 0;
if (n < 0) { if (n < 0) {
if (fputc('-') < 0) return -1;
if (fputc('-') < 0) {
return -1;
}
n = -n; n = -n;
s = 1; s = 1;
} }
} }


char* str = fmtSpace(len); char* str = fmtSpace(len);
if (!str) return -1;
if (!str) {
return -1;
}
fmtDec(n, str); fmtDec(n, str);
return len; return len;
#endif #endif
len = n < 0X100000 ? 5 : n < 0X1000000 ? 6 : n < 0X10000000 ? 7 : 8; len = n < 0X100000 ? 5 : n < 0X1000000 ? 6 : n < 0X10000000 ? 7 : 8;
} }
char* str = fmtSpace(len); char* str = fmtSpace(len);
if (!str) return -1;
if (!str) {
return -1;
}


do { do {
uint8_t h = n & 0XF; uint8_t h = n & 0XF;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool StdioStream::rewind() { bool StdioStream::rewind() {
if (m_flags & F_SWR) { if (m_flags & F_SWR) {
if (!flushBuf()) return false;
if (!flushBuf()) {
return false;
}
} }
FatFile::seekSet(0); FatFile::seekSet(0);
m_r = 0; m_r = 0;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int StdioStream::ungetc(int c) { int StdioStream::ungetc(int c) {
// error if EOF. // error if EOF.
if (c == EOF) return EOF;
if (c == EOF) {
return EOF;
}
// error if not reading. // error if not reading.
if ((m_flags & F_SRD) == 0) return EOF;
if ((m_flags & F_SRD) == 0) {
return EOF;
}
// error if no space. // error if no space.
if (m_p == m_buf) return EOF;
if (m_p == m_buf) {
return EOF;
}
m_r++; m_r++;
m_flags &= ~F_EOF; m_flags &= ~F_EOF;
return *--m_p = (uint8_t)c; return *--m_p = (uint8_t)c;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// private // private
bool StdioStream::fillBuf() { bool StdioStream::fillBuf() {
if (!(m_flags & F_SRD)) { /////////////check for F_ERR and F_EOF ??/////////////////
if (!(m_flags &
F_SRD)) { /////////////check for F_ERR and F_EOF ??/////////////////
if (!(m_flags & F_SRW)) { if (!(m_flags & F_SRW)) {
m_flags |= F_ERR; m_flags |= F_ERR;
return false; return false;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// private // private
bool StdioStream::flushBuf() { bool StdioStream::flushBuf() {
if (!(m_flags & F_SWR)) { /////////////////check for F_ERR ??////////////////////////
if (!(m_flags &
F_SWR)) { /////////////////check for F_ERR ??////////////////////////
if (!(m_flags & F_SRW)) { if (!(m_flags & F_SRW)) {
m_flags |= F_ERR; m_flags |= F_ERR;
return false; return false;
uint8_t n = m_p - m_buf; uint8_t n = m_p - m_buf;
m_p = m_buf; m_p = m_buf;
m_w = sizeof(m_buf); m_w = sizeof(m_buf);
if (FatFile::write(m_buf, n) == n) return true;
if (FatFile::write(m_buf, n) == n) {
return true;
}
m_flags |= F_ERR; m_flags |= F_ERR;
return false; return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int StdioStream::flushPut(uint8_t c) { int StdioStream::flushPut(uint8_t c) {
if (!flushBuf()) return EOF;
if (!flushBuf()) {
return EOF;
}
m_w--; m_w--;
return *m_p++ = c; return *m_p++ = c;
} }
return 0; return 0;
} }
} }
if (len > m_w) return 0;
if (len > m_w) {
return 0;
}
m_p += len; m_p += len;
m_w -= len; m_w -= len;
return reinterpret_cast<char*>(m_p); return reinterpret_cast<char*>(m_p);

+ 44
- 28
SdFat/utility/StdioStream.h View File

*/ */
class StdioStream : private FatFile { class StdioStream : private FatFile {
public: public:
/** Constructor
*
*/
/** Constructor
*
*/
StdioStream() { StdioStream() {
m_w = m_r = 0; m_w = m_r = 0;
m_p = m_buf; m_p = m_buf;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Clear the stream's end-of-file and error indicators. */ /** Clear the stream's end-of-file and error indicators. */
void clearerr() {m_flags &= ~(F_ERR | F_EOF);}
void clearerr() {
m_flags &= ~(F_ERR | F_EOF);
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Close a stream. /** Close a stream.
* *
/** Test the stream's end-of-file indicator. /** Test the stream's end-of-file indicator.
* \return non-zero if and only if the end-of-file indicator is set. * \return non-zero if and only if the end-of-file indicator is set.
*/ */
int feof() {return (m_flags & F_EOF) != 0;}
int feof() {
return (m_flags & F_EOF) != 0;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Test the stream's error indicator. /** Test the stream's error indicator.
* \return return non-zero if and only if the error indicator is set. * \return return non-zero if and only if the error indicator is set.
*/ */
int ferror() {return (m_flags & F_ERR) != 0;}
int ferror() {
return (m_flags & F_ERR) != 0;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Flush the stream. /** Flush the stream.
* *
* set and the fgetc function returns EOF. Otherwise, the fgetc function * set and the fgetc function returns EOF. Otherwise, the fgetc function
* returns the next character from the input stream. * returns the next character from the input stream.
*/ */
int fgetc() {return m_r-- == 0 ? fillGet() : *m_p++;}
int fgetc() {
return m_r-- == 0 ? fillGet() : *m_p++;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Get a string from a stream. /** Get a string from a stream.
* *
* has written. Otherwise, it returns EOF and sets the error indicator for * has written. Otherwise, it returns EOF and sets the error indicator for
* the stream. * the stream.
*/ */
int fputc(int c) {return m_w-- == 0 ? flushPut(c) : *m_p++ = c;}
int fputc(int c) {
return m_w-- == 0 ? flushPut(c) : *m_p++ = c;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Write a string to a stream. /** Write a string to a stream.
* *
* returns the next character from the input stream. * returns the next character from the input stream.
*/ */
inline __attribute__((always_inline)) inline __attribute__((always_inline))
int getc() {return m_r-- == 0 ? fillGet() : *m_p++;}
int getc() {
return m_r-- == 0 ? fillGet() : *m_p++;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Write a byte to a stream. /** Write a byte to a stream.
* *
* the stream. * the stream.
*/ */
inline __attribute__((always_inline)) inline __attribute__((always_inline))
int putc(int c) {return m_w-- == 0 ? flushPut(c) : *m_p++ = c;}
int putc(int c) {
return m_w-- == 0 ? flushPut(c) : *m_p++ = c;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Write a CR/LF. /** Write a CR/LF.
* *
inline __attribute__((always_inline)) inline __attribute__((always_inline))
int putCRLF() { int putCRLF() {
if (m_w < 2) { if (m_w < 2) {
if (!flushBuf()) return -1;
if (!flushBuf()) {
return -1;
}
} }
*m_p++ = '\r'; *m_p++ = '\r';
*m_p++ = '\n'; *m_p++ = '\n';
*/ */
int printDec(float value, uint8_t prec); int printDec(float value, uint8_t prec);
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Print a number followed by a field terminator.
* \param[in] value The number to be printed.
* \param[in] term The field terminator.
* \param[in] prec Number of digits after decimal point.
* \return The number of bytes written or -1 if an error occurs.
*/
/** Print a number followed by a field terminator.
* \param[in] value The number to be printed.
* \param[in] term The field terminator.
* \param[in] prec Number of digits after decimal point.
* \return The number of bytes written or -1 if an error occurs.
*/
int printField(double value, char term, uint8_t prec = 2) { int printField(double value, char term, uint8_t prec = 2) {
return printField(static_cast<float>(value), term, prec) > 0; return printField(static_cast<float>(value), term, prec) > 0;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Print a number followed by a field terminator.
* \param[in] value The number to be printed.
* \param[in] term The field terminator.
* \param[in] prec Number of digits after decimal point.
* \return The number of bytes written or -1 if an error occurs.
*/
/** Print a number followed by a field terminator.
* \param[in] value The number to be printed.
* \param[in] term The field terminator.
* \param[in] prec Number of digits after decimal point.
* \return The number of bytes written or -1 if an error occurs.
*/
int printField(float value, char term, uint8_t prec = 2) { int printField(float value, char term, uint8_t prec = 2) {
int rtn = printDec(value, prec); int rtn = printDec(value, prec);
return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1; return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Print a number followed by a field terminator.
* \param[in] value The number to be printed.
* \param[in] term The field terminator.
* \return The number of bytes written or -1 if an error occurs.
*/
/** Print a number followed by a field terminator.
* \param[in] value The number to be printed.
* \param[in] term The field terminator.
* \return The number of bytes written or -1 if an error occurs.
*/
template <typename T> template <typename T>
int printField(T value, char term) { int printField(T value, char term) {
int rtn = printDec(value); int rtn = printDec(value);

+ 25
- 9
SdFat/utility/bufstream.h View File

* Warning: The string will not be copied so must stay in scope. * Warning: The string will not be copied so must stay in scope.
*/ */
explicit ibufstream(const char* str) { explicit ibufstream(const char* str) {
init(str);
init(str);
} }
/** Initialize an ibufstream /** Initialize an ibufstream
* \param[in] str pointer to string to be parsed * \param[in] str pointer to string to be parsed
protected: protected:
/// @cond SHOW_PROTECTED /// @cond SHOW_PROTECTED
int16_t getch() { int16_t getch() {
if (m_pos < m_len) return m_buf[m_pos++];
if (m_pos < m_len) {
return m_buf[m_pos++];
}
setstate(eofbit); setstate(eofbit);
return -1; return -1;
} }
void getpos(FatPos_t *pos) { void getpos(FatPos_t *pos) {
pos->position = m_pos; pos->position = m_pos;
} }
bool seekoff(off_type off, seekdir way) {return false;}
bool seekoff(off_type off, seekdir way) {
return false;
}
bool seekpos(pos_type pos) { bool seekpos(pos_type pos) {
if (pos < m_len) { if (pos < m_len) {
m_pos = pos; m_pos = pos;
m_in = 0; m_in = 0;
} }
/** \return a pointer to the buffer */ /** \return a pointer to the buffer */
char* buf() {return m_buf;}
char* buf() {
return m_buf;
}
/** \return the length of the formatted string */ /** \return the length of the formatted string */
size_t length() {return m_in;}
size_t length() {
return m_in;
}


protected: protected:
/// @cond SHOW_PROTECTED /// @cond SHOW_PROTECTED
m_buf[m_in]= '\0'; m_buf[m_in]= '\0';
} }
void putstr(const char *str) { void putstr(const char *str) {
while (*str) putch(*str++);
while (*str) {
putch(*str++);
}
}
bool seekoff(off_type off, seekdir way) {
return false;
} }
bool seekoff(off_type off, seekdir way) {return false;}
bool seekpos(pos_type pos) { bool seekpos(pos_type pos) {
if (pos > m_in) return false;
if (pos > m_in) {
return false;
}
m_in = pos; m_in = pos;
m_buf[m_in] = '\0'; m_buf[m_in] = '\0';
return true; return true;
} }
bool sync() {return true;}
bool sync() {
return true;
}


pos_type tellpos() { pos_type tellpos() {
return m_in; return m_in;

+ 64
- 46
SdFat/utility/fstream.cpp View File

*/ */
#include "fstream.h" #include "fstream.h"
//============================================================================== //==============================================================================
/// @cond SHOW_PROTECTED
/// @cond SHOW_PROTECTED
int16_t FatStreamBase::getch() { int16_t FatStreamBase::getch() {
uint8_t c; uint8_t c;
int8_t s = read(&c, 1); int8_t s = read(&c, 1);
} }
return -1; return -1;
} }
if (c != '\r' || (getmode() & ios::binary)) return c;
if (c != '\r' || (getmode() & ios::binary)) {
return c;
}
s = read(&c, 1); s = read(&c, 1);
if (s == 1 && c == '\n') return c;
if (s == 1) seekCur(-1);
if (s == 1 && c == '\n') {
return c;
}
if (s == 1) {
seekCur(-1);
}
return '\r'; return '\r';
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void FatStreamBase::open(const char* path, ios::openmode mode) { void FatStreamBase::open(const char* path, ios::openmode mode) {
uint8_t flags;
uint8_t flags;
switch (mode & (app | in | out | trunc)) { switch (mode & (app | in | out | trunc)) {
case app | in:
case app | in | out:
flags = O_RDWR | O_APPEND | O_CREAT;
break;
case app | in:
case app | in | out:
flags = O_RDWR | O_APPEND | O_CREAT;
break;


case app:
case app | out:
flags = O_WRITE | O_APPEND | O_CREAT;
break;
case app:
case app | out:
flags = O_WRITE | O_APPEND | O_CREAT;
break;


case in:
flags = O_READ;
break;
case in:
flags = O_READ;
break;


case in | out:
flags = O_RDWR;
break;
case in | out:
flags = O_RDWR;
break;


case in | out | trunc:
flags = O_RDWR | O_TRUNC | O_CREAT;
break;
case in | out | trunc:
flags = O_RDWR | O_TRUNC | O_CREAT;
break;


case out:
case out | trunc:
flags = O_WRITE | O_TRUNC | O_CREAT;
break;
case out:
case out | trunc:
flags = O_WRITE | O_TRUNC | O_CREAT;
break;


default:
goto fail;
default:
goto fail;
}
if (mode & ios::ate) {
flags |= O_AT_END;
}
if (!FatFile::open(path, flags)) {
goto fail;
} }
if (mode & ios::ate) flags |= O_AT_END;
if (!FatFile::open(path, flags)) goto fail;
setmode(mode); setmode(mode);
clear(); clear();
return; return;


fail:
fail:
FatFile::close(); FatFile::close();
setstate(failbit); setstate(failbit);
return; return;
write('\r'); write('\r');
} }
write(c); write(c);
if (getWriteError()) setstate(badbit);
if (getWriteError()) {
setstate(badbit);
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void FatStreamBase::putstr(const char* str) { void FatStreamBase::putstr(const char* str) {
while (1) { while (1) {
char c = str[n]; char c = str[n];
if (c == '\0' || (c == '\n' && !(getmode() & ios::binary))) { if (c == '\0' || (c == '\n' && !(getmode() & ios::binary))) {
if (n > 0) write(str, n);
if (c == '\0') break;
if (n > 0) {
write(str, n);
}
if (c == '\0') {
break;
}
write('\r'); write('\r');
str += n; str += n;
n = 0; n = 0;
} }
n++; n++;
} }
if (getWriteError()) setstate(badbit);
if (getWriteError()) {
setstate(badbit);
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** Internal do not use /** Internal do not use
bool FatStreamBase::seekoff(off_type off, seekdir way) { bool FatStreamBase::seekoff(off_type off, seekdir way) {
pos_type pos; pos_type pos;
switch (way) { switch (way) {
case beg:
pos = off;
break;
case beg:
pos = off;
break;


case cur:
pos = curPosition() + off;
break;
case cur:
pos = curPosition() + off;
break;


case end:
pos = fileSize() + off;
break;
case end:
pos = fileSize() + off;
break;


default:
return false;
default:
return false;
} }
return seekpos(pos); return seekpos(pos);
} }

+ 81
- 29
SdFat/utility/fstream.h View File

/** Internal do not use /** Internal do not use
* \return mode * \return mode
*/ */
ios::openmode getmode() {return m_mode;}
ios::openmode getmode() {
return m_mode;
}
/** Internal do not use /** Internal do not use
* \param[in] mode * \param[in] mode
*/ */
void setmode(ios::openmode mode) {m_mode = mode;}
void setmode(ios::openmode mode) {
m_mode = mode;
}
bool seekoff(off_type off, seekdir way); bool seekoff(off_type off, seekdir way);
bool seekpos(pos_type pos); bool seekpos(pos_type pos);
int write(const void* buf, size_t n); int write(const void* buf, size_t n);
/** Close a file and force cached data and directory information /** Close a file and force cached data and directory information
* to be written to the storage device. * to be written to the storage device.
*/ */
void close() {FatFile::close();}
void close() {
FatFile::close();
}
/** Open a fstream /** Open a fstream
* \param[in] path file to open * \param[in] path file to open
* \param[in] mode open mode * \param[in] mode open mode
FatStreamBase::open(path, mode); FatStreamBase::open(path, mode);
} }
/** \return True if stream is open else false. */ /** \return True if stream is open else false. */
bool is_open () {return FatFile::isOpen();}
bool is_open() {
return FatFile::isOpen();
}


protected: protected:
/// @cond SHOW_PROTECTED /// @cond SHOW_PROTECTED
/** Internal - do not use /** Internal - do not use
* \return * \return
*/ */
int16_t getch() {return FatStreamBase::getch();}
/** Internal - do not use
* \param[out] pos
*/
void getpos(FatPos_t* pos) {FatFile::getpos(pos);}
int16_t getch() {
return FatStreamBase::getch();
}
/** Internal - do not use
* \param[out] pos
*/
void getpos(FatPos_t* pos) {
FatFile::getpos(pos);
}
/** Internal - do not use /** Internal - do not use
* \param[in] c * \param[in] c
*/ */
void putch(char c) {FatStreamBase::putch(c);}
void putch(char c) {
FatStreamBase::putch(c);
}
/** Internal - do not use /** Internal - do not use
* \param[in] str * \param[in] str
*/ */
void putstr(const char *str) {FatStreamBase::putstr(str);}
void putstr(const char *str) {
FatStreamBase::putstr(str);
}
/** Internal - do not use /** Internal - do not use
* \param[in] pos * \param[in] pos
*/ */
bool seekoff(off_type off, seekdir way) { bool seekoff(off_type off, seekdir way) {
return FatStreamBase::seekoff(off, way); return FatStreamBase::seekoff(off, way);
} }
bool seekpos(pos_type pos) {return FatStreamBase::seekpos(pos);}
void setpos(FatPos_t* pos) {FatFile::setpos(pos);}
bool sync() {return FatStreamBase::sync();}
pos_type tellpos() {return FatStreamBase::curPosition();}
bool seekpos(pos_type pos) {
return FatStreamBase::seekpos(pos);
}
void setpos(FatPos_t* pos) {
FatFile::setpos(pos);
}
bool sync() {
return FatStreamBase::sync();
}
pos_type tellpos() {
return FatStreamBase::curPosition();
}
/// @endcond /// @endcond
}; };
//============================================================================== //==============================================================================
/** Close a file and force cached data and directory information /** Close a file and force cached data and directory information
* to be written to the storage device. * to be written to the storage device.
*/ */
void close() {FatFile::close();}
void close() {
FatFile::close();
}
/** \return True if stream is open else false. */ /** \return True if stream is open else false. */
bool is_open() {return FatFile::isOpen();}
bool is_open() {
return FatFile::isOpen();
}
/** Open an ifstream /** Open an ifstream
* \param[in] path file to open * \param[in] path file to open
* \param[in] mode open mode * \param[in] mode open mode
/** Internal - do not use /** Internal - do not use
* \return * \return
*/ */
int16_t getch() {return FatStreamBase::getch();}
int16_t getch() {
return FatStreamBase::getch();
}
/** Internal - do not use /** Internal - do not use
* \param[out] pos * \param[out] pos
*/ */
void getpos(FatPos_t* pos) {FatFile::getpos(pos);}
void getpos(FatPos_t* pos) {
FatFile::getpos(pos);
}
/** Internal - do not use /** Internal - do not use
* \param[in] pos * \param[in] pos
*/ */
bool seekoff(off_type off, seekdir way) { bool seekoff(off_type off, seekdir way) {
return FatStreamBase::seekoff(off, way); return FatStreamBase::seekoff(off, way);
} }
bool seekpos(pos_type pos) {return FatStreamBase::seekpos(pos);}
void setpos(FatPos_t* pos) {FatFile::setpos(pos);}
pos_type tellpos() {return FatStreamBase::curPosition();}
bool seekpos(pos_type pos) {
return FatStreamBase::seekpos(pos);
}
void setpos(FatPos_t* pos) {
FatFile::setpos(pos);
}
pos_type tellpos() {
return FatStreamBase::curPosition();
}
/// @endcond /// @endcond
}; };
//============================================================================== //==============================================================================
/** Close a file and force cached data and directory information /** Close a file and force cached data and directory information
* to be written to the storage device. * to be written to the storage device.
*/ */
void close() {FatFile::close();}
void close() {
FatFile::close();
}
/** Open an ofstream /** Open an ofstream
* \param[in] path file to open * \param[in] path file to open
* \param[in] mode open mode * \param[in] mode open mode
FatStreamBase::open(path, mode | out); FatStreamBase::open(path, mode | out);
} }
/** \return True if stream is open else false. */ /** \return True if stream is open else false. */
bool is_open() {return FatFile::isOpen();}
bool is_open() {
return FatFile::isOpen();
}


protected: protected:
/// @cond SHOW_PROTECTED /// @cond SHOW_PROTECTED
* Internal do not use * Internal do not use
* \param[in] c * \param[in] c
*/ */
void putch(char c) {FatStreamBase::putch(c);}
void putstr(const char* str) {FatStreamBase::putstr(str);}
void putch(char c) {
FatStreamBase::putch(c);
}
void putstr(const char* str) {
FatStreamBase::putstr(str);
}
bool seekoff(off_type off, seekdir way) { bool seekoff(off_type off, seekdir way) {
return FatStreamBase::seekoff(off, way); return FatStreamBase::seekoff(off, way);
} }
bool seekpos(pos_type pos) {return FatStreamBase::seekpos(pos);}
bool seekpos(pos_type pos) {
return FatStreamBase::seekpos(pos);
}
/** /**
* Internal do not use * Internal do not use
* \param[in] b * \param[in] b
*/ */
bool sync() {return FatStreamBase::sync();}
pos_type tellpos() {return FatStreamBase::curPosition();}
bool sync() {
return FatStreamBase::sync();
}
pos_type tellpos() {
return FatStreamBase::curPosition();
}
/// @endcond /// @endcond
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

+ 37
- 13
SdFat/utility/ios.h View File

#define ios_h #define ios_h
#include "FatFile.h" #include "FatFile.h"
/** /**
* \file
* \file
* \brief \ref ios_base and \ref ios classes * \brief \ref ios_base and \ref ios classes
*/ */
//============================================================================== //==============================================================================
ios_base() : m_fill(' '), m_fmtflags(dec | right | skipws) ios_base() : m_fill(' '), m_fmtflags(dec | right | skipws)
, m_precision(2), m_width(0) {} , m_precision(2), m_width(0) {}
/** \return fill character */ /** \return fill character */
char fill() {return m_fill;}
char fill() {
return m_fill;
}
/** Set fill character /** Set fill character
* \param[in] c new fill character * \param[in] c new fill character
* \return old fill character * \return old fill character
return r; return r;
} }
/** \return format flags */ /** \return format flags */
fmtflags flags() const {return m_fmtflags;}
fmtflags flags() const {
return m_fmtflags;
}
/** set format flags /** set format flags
* \param[in] fl new flag * \param[in] fl new flag
* \return old flags * \return old flags
return tmp; return tmp;
} }
/** \return precision */ /** \return precision */
int precision() const {return m_precision;}
int precision() const {
return m_precision;
}
/** set precision /** set precision
* \param[in] n new precision * \param[in] n new precision
* \return old precision * \return old precision
m_fmtflags &= ~fl; m_fmtflags &= ~fl;
} }
/** \return width */ /** \return width */
unsigned width() {return m_width;}
unsigned width() {
return m_width;
}
/** set width /** set width
* \param[in] n new width * \param[in] n new width
* \return old width * \return old width
return !fail() ? reinterpret_cast<const void*>(this) : 0; return !fail() ? reinterpret_cast<const void*>(this) : 0;
} }
/** \return true if fail() else false. */ /** \return true if fail() else false. */
bool operator!() const {return fail();}
bool operator!() const {
return fail();
}
/** \return The iostate flags for this file. */ /** \return The iostate flags for this file. */
iostate rdstate() const {return m_iostate;}
iostate rdstate() const {
return m_iostate;
}
/** \return True if no iostate flags are set else false. */ /** \return True if no iostate flags are set else false. */
bool good() const {return m_iostate == goodbit;}
bool good() const {
return m_iostate == goodbit;
}
/** \return true if end of file has been reached else false. /** \return true if end of file has been reached else false.
* *
* Warning: An empty file returns false before the first read. * Warning: An empty file returns false before the first read.
* Moral: eof() is only useful in combination with fail(), to find out * Moral: eof() is only useful in combination with fail(), to find out
* whether EOF was the cause for failure * whether EOF was the cause for failure
*/ */
bool eof() const {return m_iostate & eofbit;}
bool eof() const {
return m_iostate & eofbit;
}
/** \return true if any iostate bit other than eof are set else false. */ /** \return true if any iostate bit other than eof are set else false. */
bool fail() const {return m_iostate & (failbit | badbit);}
bool fail() const {
return m_iostate & (failbit | badbit);
}
/** \return true if bad bit is set else false. */ /** \return true if bad bit is set else false. */
bool bad() const {return m_iostate & badbit;}
bool bad() const {
return m_iostate & badbit;
}
/** Clear iostate bits. /** Clear iostate bits.
* *
* \param[in] state The flags you want to set after clearing all flags. * \param[in] state The flags you want to set after clearing all flags.
**/ **/
void clear(iostate state = goodbit) {m_iostate = state;}
void clear(iostate state = goodbit) {
m_iostate = state;
}
/** Set iostate bits. /** Set iostate bits.
* *
* \param[in] state Bitts to set. * \param[in] state Bitts to set.
**/ **/
void setstate(iostate state) {m_iostate |= state;}
void setstate(iostate state) {
m_iostate |= state;
}


private: private:
iostate m_iostate; iostate m_iostate;

+ 53
- 19
SdFat/utility/istream.cpp View File

* along with the FatLib Library. If not, see * along with the FatLib Library. If not, see
* <http://www.gnu.org/licenses/>. * <http://www.gnu.org/licenses/>.
*/ */
#include <ctype.h>
// #include <ctype.h>
#include <float.h> #include <float.h>
#include "istream.h" #include "istream.h"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
istream& istream::get(char& c) { istream& istream::get(char& c) {
int tmp = get(); int tmp = get();
if (tmp >= 0) c = tmp;
if (tmp >= 0) {
c = tmp;
}
return *this; return *this;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
} }
str[m_gcount++] = c; str[m_gcount++] = c;
} }
if (n > 0) str[m_gcount] = '\0';
if (m_gcount == 0) setstate(failbit);
if (n > 0) {
str[m_gcount] = '\0';
}
if (m_gcount == 0) {
setstate(failbit);
}
return *this; return *this;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
while (1) { while (1) {
falseOk = falseOk && c == pgm_read_byte(falsePtr + i); falseOk = falseOk && c == pgm_read_byte(falsePtr + i);
trueOk = trueOk && c == pgm_read_byte(truePtr + i); trueOk = trueOk && c == pgm_read_byte(truePtr + i);
if (trueOk == false && falseOk == false) break;
if (trueOk == false && falseOk == false) {
break;
}
i++; i++;
if (trueOk && i == true_len) { if (trueOk && i == true_len) {
*b = true; *b = true;
got_digit = true; got_digit = true;
if (frac < uint32_max/10) { if (frac < uint32_max/10) {
frac = frac * 10 + (c - '0'); frac = frac * 10 + (c - '0');
if (got_dot) fracExp--;
if (got_dot) {
fracExp--;
}
} else { } else {
if (!got_dot) fracExp++;
if (!got_dot) {
fracExp++;
}
} }
} else if (!got_dot && c == '.') { } else if (!got_dot && c == '.') {
got_dot = true; got_dot = true;
} else { } else {
break; break;
} }
if (fracExp < -EXP_LIMIT || fracExp > EXP_LIMIT) goto fail;
if (fracExp < -EXP_LIMIT || fracExp > EXP_LIMIT) {
goto fail;
}
c = getch(&endPos); c = getch(&endPos);
} }
if (!got_digit) goto fail;
if (!got_digit) {
goto fail;
}
if (c == 'e' || c == 'E') { if (c == 'e' || c == 'E') {
c = getch(); c = getch();
expNeg = c == '-'; expNeg = c == '-';
c = getch(); c = getch();
} }
while (isdigit(c)) { while (isdigit(c)) {
if (exp > EXP_LIMIT) goto fail;
if (exp > EXP_LIMIT) {
goto fail;
}
exp = exp * 10 + (c - '0'); exp = exp * 10 + (c - '0');
c = getch(&endPos); c = getch(&endPos);
} }
v = static_cast<double>(frac); v = static_cast<double>(frac);
exp = expNeg ? fracExp - exp : fracExp + exp; exp = expNeg ? fracExp - exp : fracExp + exp;
expNeg = exp < 0; expNeg = exp < 0;
if (expNeg) exp = -exp;
if (expNeg) {
exp = -exp;
}
pow10 = 10.0; pow10 = 10.0;
while (exp) { while (exp) {
if (exp & 1) { if (exp & 1) {
if (expNeg) { if (expNeg) {
// check for underflow // check for underflow
if (v < FLT_MIN * pow10 && frac != 0) goto fail;
if (v < FLT_MIN * pow10 && frac != 0) {
goto fail;
}
v /= pow10; v /= pow10;
} else { } else {
// check for overflow // check for overflow
if (v > FLT_MAX / pow10) goto fail;
if (v > FLT_MAX / pow10) {
goto fail;
}
v *= pow10; v *= pow10;
} }
} }
*value = neg ? -v : v; *value = neg ? -v : v;
return true; return true;


fail:
fail:
// error restore position to last good place // error restore position to last good place
setpos(&endPos); setpos(&endPos);
setstate(failbit); setstate(failbit);
FatPos_t pos; FatPos_t pos;
int c; int c;
m_gcount = 0; m_gcount = 0;
if (n > 0) str[0] = '\0';
if (n > 0) {
str[0] = '\0';
}
while (1) { while (1) {
c = getch(&pos); c = getch(&pos);
if (c < 0) { if (c < 0) {
str[m_gcount++] = c; str[m_gcount++] = c;
str[m_gcount] = '\0'; str[m_gcount] = '\0';
} }
if (m_gcount == 0) setstate(failbit);
if (m_gcount == 0) {
setstate(failbit);
}
return *this; return *this;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
} }
} }
str[i] = '\0'; str[i] = '\0';
if (i == 0) setstate(failbit);
if (i == 0) {
setstate(failbit);
}
width(0); width(0);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
break; break;
} }
m_gcount++; m_gcount++;
if (c == delim) break;
if (c == delim) {
break;
}
} }
return *this; return *this;
} }
getpos(&pos); getpos(&pos);
c = getch(); c = getch();
if (c < 0) { if (c < 0) {
if (!bad()) setstate(eofbit);
if (!bad()) {
setstate(eofbit);
}
} else { } else {
setpos(&pos); setpos(&pos);
} }

+ 93
- 85
SdFat/utility/istream.h View File

pf(*this); pf(*this);
return *this; return *this;
} }
/**
* Extract a character string
* \param[out] str location to store the string.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a character string
* \param[out] str location to store the string.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream& operator>>(char *str) { istream& operator>>(char *str) {
getStr(str); getStr(str);
return *this; return *this;
} }
/**
* Extract a character
* \param[out] ch location to store the character.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a character
* \param[out] ch location to store the character.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream& operator>>(char& ch) { istream& operator>>(char& ch) {
getChar(&ch); getChar(&ch);
return *this; return *this;
} }
/**
* Extract a character string
* \param[out] str location to store the string.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a character string
* \param[out] str location to store the string.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream& operator>>(signed char *str) { istream& operator>>(signed char *str) {
getStr(reinterpret_cast<char*>(str)); getStr(reinterpret_cast<char*>(str));
return *this; return *this;
} }
/**
* Extract a character
* \param[out] ch location to store the character.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a character
* \param[out] ch location to store the character.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream& operator>>(signed char& ch) { istream& operator>>(signed char& ch) {
getChar(reinterpret_cast<char*>(&ch)); getChar(reinterpret_cast<char*>(&ch));
return *this; return *this;
} }
/**
* Extract a character string
* \param[out] str location to store the string.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a character string
* \param[out] str location to store the string.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream& operator>>(unsigned char *str) { istream& operator>>(unsigned char *str) {
getStr(reinterpret_cast<char*>(str)); getStr(reinterpret_cast<char*>(str));
return *this; return *this;
} }
/**
* Extract a character
* \param[out] ch location to store the character.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a character
* \param[out] ch location to store the character.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream& operator>>(unsigned char& ch) { istream& operator>>(unsigned char& ch) {
getChar(reinterpret_cast<char*>(&ch)); getChar(reinterpret_cast<char*>(&ch));
return *this; return *this;
} }
/**
* Extract a value of type bool.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a value of type bool.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream& operator>>(bool& arg) { istream& operator>>(bool& arg) {
getBool(&arg); getBool(&arg);
return *this; return *this;
} }
/**
* Extract a value of type short.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a value of type short.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream &operator>>(short& arg) { // NOLINT istream &operator>>(short& arg) { // NOLINT
getNumber(&arg); getNumber(&arg);
return *this; return *this;
} }
/**
* Extract a value of type unsigned short.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a value of type unsigned short.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream &operator>>(unsigned short& arg) { // NOLINT istream &operator>>(unsigned short& arg) { // NOLINT
getNumber(&arg); getNumber(&arg);
return *this; return *this;
} }
/**
* Extract a value of type int.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a value of type int.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream &operator>>(int& arg) { istream &operator>>(int& arg) {
getNumber(&arg); getNumber(&arg);
return *this; return *this;
} }
/**
* Extract a value of type unsigned int.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a value of type unsigned int.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream &operator>>(unsigned int& arg) { istream &operator>>(unsigned int& arg) {
getNumber(&arg); getNumber(&arg);
return *this; return *this;
} }
/**
* Extract a value of type long.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a value of type long.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream &operator>>(long& arg) { // NOLINT istream &operator>>(long& arg) { // NOLINT
getNumber(&arg); getNumber(&arg);
return *this; return *this;
} }
/**
* Extract a value of type unsigned long.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a value of type unsigned long.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream &operator>>(unsigned long& arg) { // NOLINT istream &operator>>(unsigned long& arg) { // NOLINT
getNumber(&arg); getNumber(&arg);
return *this; return *this;
} }
/**
/**
* Extract a value of type double. * Extract a value of type double.
* \param[out] arg location to store the value. * \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this. * \return Is always *this. Failure is indicated by the state of *this.
getDouble(&arg); getDouble(&arg);
return *this; return *this;
} }
/**
* Extract a value of type float.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
/**
* Extract a value of type float.
* \param[out] arg location to store the value.
* \return Is always *this. Failure is indicated by the state of *this.
*/
istream &operator>> (float& arg) { istream &operator>> (float& arg) {
double v; double v;
getDouble(&v); getDouble(&v);
* \return The number of characters extracted by the last unformatted * \return The number of characters extracted by the last unformatted
* input function. * input function.
*/ */
streamsize gcount() const {return m_gcount;}
streamsize gcount() const {
return m_gcount;
}
/** /**
* Extract a character if one is available. * Extract a character if one is available.
* *
* \return The character or -1 if a failure occurs. A failure is indicated * \return The character or -1 if a failure occurs. A failure is indicated
* by the stream state. * by the stream state.
*/
*/
int get(); int get();
/** /**
* Extract a character if one is available. * Extract a character if one is available.
* \param[out] ch location to receive the extracted character. * \param[out] ch location to receive the extracted character.
* *
* \return always returns *this. A failure is indicated by the stream state. * \return always returns *this. A failure is indicated by the stream state.
*/
*/
istream& get(char& ch); istream& get(char& ch);
/** /**
* Extract characters.
* Extract characters.
* *
* \param[out] str Location to receive extracted characters. * \param[out] str Location to receive extracted characters.
* \param[in] n Size of str. * \param[in] n Size of str.
* failbit is set. If end-of-file occurs the eofbit is set. * failbit is set. If end-of-file occurs the eofbit is set.
* *
* \return always returns *this. A failure is indicated by the stream state. * \return always returns *this. A failure is indicated by the stream state.
*/
*/
istream& get(char *str, streamsize n, char delim = '\n'); istream& get(char *str, streamsize n, char delim = '\n');
/** /**
* Extract characters * Extract characters
* *
* \return *this * \return *this
* *
*/
*/
istream& ignore(streamsize n = 1, int delim= -1); istream& ignore(streamsize n = 1, int delim= -1);
/** /**
* Return the next available character without consuming it. * Return the next available character without consuming it.
* *
* \return The character if the stream state is good else -1; * \return The character if the stream state is good else -1;
* *
*/
*/
int peek(); int peek();
// istream& read(char *str, streamsize count); // istream& read(char *str, streamsize count);
// streamsize readsome(char *str, streamsize count); // streamsize readsome(char *str, streamsize count);
/** /**
* \return the stream position * \return the stream position
*/ */
pos_type tellg() {return tellpos();}
pos_type tellg() {
return tellpos();
}
/** /**
* Set the stream position * Set the stream position
* \param[in] pos The absolute position in which to move the read pointer. * \param[in] pos The absolute position in which to move the read pointer.
* \return Is always *this. Failure is indicated by the state of *this. * \return Is always *this. Failure is indicated by the state of *this.
*/ */
istream& seekg(pos_type pos) { istream& seekg(pos_type pos) {
if (!seekpos(pos)) setstate(failbit);
if (!seekpos(pos)) {
setstate(failbit);
}
return *this; return *this;
} }
/** /**
* \return Is always *this. Failure is indicated by the state of *this. * \return Is always *this. Failure is indicated by the state of *this.
*/ */
istream& seekg(off_type off, seekdir way) { istream& seekg(off_type off, seekdir way) {
if (!seekoff(off, way)) setstate(failbit);
if (!seekoff(off, way)) {
setstate(failbit);
}
return *this; return *this;
} }
void skipWhite(); void skipWhite();


protected: protected:
/// @cond SHOW_PROTECTED /// @cond SHOW_PROTECTED
/**
* Internal - do not use
* \return
*/
/**
* Internal - do not use
* \return
*/
virtual int16_t getch() = 0; virtual int16_t getch() = 0;
/** /**
* Internal - do not use * Internal - do not use

+ 21
- 7
SdFat/utility/ostream.cpp View File

#endif #endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void ostream::do_fill(unsigned len) { void ostream::do_fill(unsigned len) {
for (; len < width(); len++) putch(fill());
for (; len < width(); len++) {
putch(fill());
}
width(0); width(0);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
return; return;
} }
// round up and separate int and fraction parts // round up and separate int and fraction parts
for (uint8_t i = 0; i < nd; ++i) round *= 0.1;
for (uint8_t i = 0; i < nd; ++i) {
round *= 0.1;
}
n += round; n += round;
uint32_t intPart = n; uint32_t intPart = n;
double fractionPart = n - intPart; double fractionPart = n - intPart;


// format intPart and decimal point // format intPart and decimal point
if (nd || (flags() & showpoint)) *--str = '.';
if (nd || (flags() & showpoint)) {
*--str = '.';
}
str = fmtNum(intPart, str, 10); str = fmtNum(intPart, str, 10);


// calculate length for fill // calculate length for fill
// extract adjust field // extract adjust field
fmtflags adj = flags() & adjustfield; fmtflags adj = flags() & adjustfield;
if (adj == internal) { if (adj == internal) {
if (sign) putch(sign);
if (sign) {
putch(sign);
}
do_fill(len); do_fill(len);
} else { } else {
// do fill for internal or right // do fill for internal or right
fill_not_left(len); fill_not_left(len);
if (sign) *--str = sign;
if (sign) {
*--str = sign;
}
} }
putstr(str); putstr(str);
// output fraction // output fraction
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void ostream::putNum(int32_t n) { void ostream::putNum(int32_t n) {
bool neg = n < 0 && flagsToBase() == 10; bool neg = n < 0 && flagsToBase() == 10;
if (neg) n = -n;
if (neg) {
n = -n;
}
putNum(n, neg); putNum(n, neg);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
uint8_t len = end - str; uint8_t len = end - str;
fmtflags adj = flags() & adjustfield; fmtflags adj = flags() & adjustfield;
if (adj == internal) { if (adj == internal) {
while (str < num) putch(*str++);
while (str < num) {
putch(*str++);
}
} }
if (adj != left) { if (adj != left) {
do_fill(len); do_fill(len);

+ 12
- 4
SdFat/utility/ostream.h View File

* \return A reference to the ostream object. * \return A reference to the ostream object.
*/ */
ostream& flush() { ostream& flush() {
if (!sync()) setstate(badbit);
if (!sync()) {
setstate(badbit);
}
return *this; return *this;
} }
/** /**
* \return the stream position * \return the stream position
*/ */
pos_type tellp() {return tellpos();}
pos_type tellp() {
return tellpos();
}
/** /**
* Set the stream position * Set the stream position
* \param[in] pos The absolute position in which to move the write pointer. * \param[in] pos The absolute position in which to move the write pointer.
* \return Is always *this. Failure is indicated by the state of *this. * \return Is always *this. Failure is indicated by the state of *this.
*/ */
ostream& seekp(pos_type pos) { ostream& seekp(pos_type pos) {
if (!seekpos(pos)) setstate(failbit);
if (!seekpos(pos)) {
setstate(failbit);
}
return *this; return *this;
} }
/** /**
* \return Is always *this. Failure is indicated by the state of *this. * \return Is always *this. Failure is indicated by the state of *this.
*/ */
ostream& seekp(off_type off, seekdir way) { ostream& seekp(off_type off, seekdir way) {
if (!seekoff(off, way)) setstate(failbit);
if (!seekoff(off, way)) {
setstate(failbit);
}
return *this; return *this;
} }



+ 9
- 9
SdFatTestSuite/SdFatTestSuite.cpp View File

while (Serial.read() <= 0) {} while (Serial.read() <= 0) {}
delay(200); // Catch Due reset problem delay(200); // Catch Due reset problem


print_P(testOut, PSTR("FreeRam: "));
testOut->print(F("FreeRam: "));
testOut->println(FreeRam()); testOut->println(FreeRam());
testOut->println(); testOut->println();
failCount = 0; failCount = 0;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void testEnd() { void testEnd() {
testOut->println(); testOut->println();
println_P(testOut, PSTR("Compiled: " __DATE__ " " __TIME__));
print_P(testOut, PSTR("FreeRam: "));
testOut->println(F("Compiled: " __DATE__ " " __TIME__));
testOut->print(F("FreeRam: "));
testOut->println(FreeRam()); testOut->println(FreeRam());
print_P(testOut, PSTR("Test count: "));
testOut->print(F("Test count: "));
testOut->println(testCount); testOut->println(testCount);
print_P(testOut, PSTR("Fail count: "));
testOut->print(F("Fail count: "));
testOut->println(failCount); testOut->println(failCount);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static void testResult(bool b, uint8_t n) { static void testResult(bool b, uint8_t n) {
while (n++ < 60) testOut->write(' '); while (n++ < 60) testOut->write(' ');
if (b) { if (b) {
println_P(testOut, PSTR("..ok"));
testOut->println(F("..ok"));
} else { } else {
println_P(testOut, PSTR("FAIL"));
testOut->println(F("FAIL"));
failCount++; failCount++;
} }
testCount++; testCount++;
testOut->write('"'); testOut->write('"');
testOut->print(result); testOut->print(result);
testOut->print("\",\""); testOut->print("\",\"");
print_P(testOut, expect);
testOut->print((const __FlashStringHelper*)expect);
testOut->write('"'); testOut->write('"');
uint8_t n = strlen(result) + strlenPGM(expect) + 5; uint8_t n = strlen(result) + strlenPGM(expect) + 5;
testResult(!strcmp_P(result, expect), n); testResult(!strcmp_P(result, expect), n);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void testVerify_P(bool b, PGM_P msg) { void testVerify_P(bool b, PGM_P msg) {
print_P(testOut, msg);
testOut->print((const __FlashStringHelper*)msg);
uint8_t n = strlenPGM(msg); uint8_t n = strlenPGM(msg);
testResult(b, n); testResult(b, n);
} }

+ 9
- 7
SdFatTestSuite/examples/TestMkdir/TestMkdir.ino View File

* create enough files to force a cluster to be allocated to dir. * create enough files to force a cluster to be allocated to dir.
*/ */
void dirAllocTest(SdBaseFile* dir) { void dirAllocTest(SdBaseFile* dir) {
char buf[13], name[13];
char buf[32], name[32];
SdFile file; SdFile file;
uint16_t n; uint16_t n;
uint32_t size = dir->fileSize();
uint32_t size = dir->dirSize();
// create files and write name to file // create files and write name to file
for (n = 0; ; n++){ for (n = 0; ; n++){
Serial.println(t2 - t1); Serial.println(t2 - t1);
// directory size will change when a cluster is added // directory size will change when a cluster is added
if (dir->fileSize() != size) break;
if (dir->curPosition() > size) break;
} }


// read files and check content // read files and check content
// open end time and read start time // open end time and read start time
uint32_t t1 = millis(); uint32_t t1 = millis();
int16_t nr = file.read(buf, 13);
int16_t nr = file.read(buf, sizeof(buf));
if (nr < 5) error("file.read failed"); if (nr < 5) error("file.read failed");
// read end time // read end time
// try SPI_HALF_SPEED if bus errors occur. // try SPI_HALF_SPEED if bus errors occur.
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) sd.initErrorHalt(); if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) sd.initErrorHalt();
uint32_t m = millis();
// write files to root if FAT32 // write files to root if FAT32
if (sd.vol()->fatType() == 32) { if (sd.vol()->fatType() == 32) {
Serial.println(F("Writing files to root")); Serial.println(F("Writing files to root"));
if (!sub2.mkdir(&sub1, "SUB2")) error("mkdir SUB2 failed"); if (!sub2.mkdir(&sub1, "SUB2")) error("mkdir SUB2 failed");
Serial.println(F("Writing files to SUB2")); Serial.println(F("Writing files to SUB2"));
dirAllocTest(&sub2); dirAllocTest(&sub2);
Serial.println(F("Done"));
m = millis() - m;
Serial.print(F("Done millis: "));
Serial.println(m);
} }


void loop() { }
void loop() { }

+ 1
- 1
SdFatTestSuite/examples/TestRmdir/TestRmdir.ino View File

* remove all files in dir. * remove all files in dir.
*/ */
void deleteFiles(SdBaseFile* dir) { void deleteFiles(SdBaseFile* dir) {
char name[13];
char name[32];
SdFile file; SdFile file;
// open and delete files // open and delete files

+ 35
- 0
SdFatTestSuite/examples/lfnSize/lfnSize.ino View File

// Program to compare size of SdFat with the SD.h library.
// Select the test library by commenting out one of the following two lines.
// #include <SD.h>
#include <SdFat.h>

// SD chip select pin.
const uint8_t SD_CS_PIN = SS;

#ifdef __SD_H__
File file;
#else // __SD_H__
SdFat SD;
SdFile file;
#endif // __SD_H__

void setup() {
Serial.begin(9600);
while (!Serial) {} // wait for Leonardo

if (!SD.begin(SD_CS_PIN)) {
Serial.println("begin failed");
return;
}
#ifdef __SD_H__
file = SD.open("SFN_file.txt", FILE_WRITE);
#else // __SD_H__
file.open("LFN_file.txt", O_RDWR | O_CREAT);
#endif // __SD_H__
file.println("Hello");
file.close();
Serial.println("Done");
}
//------------------------------------------------------------------------------
void loop() {}

+ 235
- 0
SdFatTestSuite/examples/lfnTest/lfnTest.ino View File

#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
const uint8_t SD_CS_PIN = SS;
SdFat sd;
SdFile file;
char name[260];

//------------------------------------------------------------------------------
char* testName[] = {
"low.low",
"low.Mix",
"low.UP",
"Mix.low",
"Mix.Mix",
"Mix.UP",
"UP.low",
"UP.Mix",
"UP.UP",
".dot",
".dot.dot",
"A b c . txt",
" Leading space and no extension",
"Trailing dots and space . . .",
"Long extension.extension",
"Space after dot. txt",
"Dot.dot.test.txt",
"Dot.dot.test.seq.txt",
"LOW.LOW",
"MIX.MIX",
"Invalid character *.test"
};
//------------------------------------------------------------------------------
bool checkName(char first, size_t len) {
size_t i;
if (len < 5 || len > sizeof(name)) {
return false;
}
if ( name[0] != first) {
return false;
}
for (i = 1; i < (len - 4); i++) {
if (name[i] != ('0' + (i + 1) %10)) {
return false;
}
}
char* p = ".txt";
while (*p) {
if (name[i++] != *p++) {
return false;
}
}
return name[i] == 0;
}
//------------------------------------------------------------------------------
void makeName(char first, size_t len) {
size_t i;
if (len > sizeof(name)) {
len = 255;
}
if (len < 5) {
len = 5;
}
name[0] = first;
for (i = 1; i < (len - 4); i++) {
name[i] = '0' + (i + 1) %10;
}
char* p = ".txt";
while (*p) name[i++] = *p++;
name[i] = 0;
}
//------------------------------------------------------------------------------
// test open, remove, getFilename, and ls.
void basicTest() {
size_t i;
size_t n = sd.vol()->fatType() == 32 ? 255 : 99;
uint16_t index;
uint16_t maxIndex = 0;
makeName('Z', 256);
if (!file.open(name, O_RDWR | O_CREAT)) {
Serial.println(F("255 limit OK"));
} else {
sd.errorHalt(F("255 limit"));
}
for (i = 5; i <= n; i++) {
makeName('A', i);

if (!file.open(name, O_RDWR | O_CREAT)) {
sd.errorHalt(F("open A"));
}
file.println(name);
Serial.print(i);
Serial.write(' ');
Serial.print(file.dirIndex());
Serial.write(' ');
Serial.print(file.fileSize());
Serial.println(F(" open A"));
if (file.fileSize() != (i + 2)) {
sd.errorHalt(F("file size A"));
}
if (file.dirIndex() >= maxIndex) {
maxIndex = file.dirIndex();
} else {
Serial.print(maxIndex); Serial.print(',');Serial.println(file.dirIndex());
sd.errorHalt(F("dirIndex"));
}
file.close();
if (!file.open(sd.vwd(), maxIndex, O_READ)) {
sd.errorHalt(F("open by index"));
}
memset(name, 0, sizeof(name));
if (!file.getFilename(name, sizeof(name))) {
sd.errorHalt(F("getFilename"));
}
if (!checkName('A', i)) {
Serial.println(name);
sd.errorHalt(F("checkName"));
}
file.close();
}
for (i = n; i >= 5; i -= 2) {
makeName('A', i);
Serial.print(i);
Serial.println(F( " rm A"));
if (!sd.remove(name)) {
sd.errorHalt(F("remove A"));
}
}
for (i = n; i >= 5; i -= 2) {
makeName('B', i);
if (!file.open(name, O_RDWR | O_CREAT)) {
sd.errorHalt(F("open B"));
}
file.println(name);
Serial.print(i);
Serial.write(' ');
Serial.print(file.dirIndex());
Serial.write(' ');
Serial.print(file.fileSize());
Serial.println(F(" open B"));
if (file.fileSize() != (i + 2)) {
sd.errorHalt(F("file size B"));
}
if (file.dirIndex() > maxIndex) {
sd.errorHalt(F("maxIndex"));
}
file.close();
}
Serial.println(F("----- ls ------"));
sd.ls();
for (i = 5; i <= n; i++) {
char fc = i & 1 ? 'B' : 'A';
makeName(fc, i);
Serial.print(i);
Serial.print(F(" rm "));
Serial.println(fc);
if (!sd.remove(name)) {
sd.errorHalt(F("remove A/B"));
}
}
if (file.openNext(sd.vwd())) {
sd.errorHalt(F("remove all"));
}
Serial.println();
Serial.println(F("basicTest done"));
}
//------------------------------------------------------------------------------
void nameTest() {
Serial.println();
uint8_t n = sizeof(testName)/sizeof(char*);
for (uint8_t i = 0; i < n; i++) {
Serial.print(F("Name: "));
Serial.write('"');
Serial.print(testName[i]);
Serial.println('"');
if(!file.open(testName[i], O_CREAT | O_RDWR)) {
Serial.println(F("Open failed"));
} else {
file.println(testName[i]);
if (!file.getFilename(name, sizeof(name))) {
sd.errorHalt(F("getFilemame"));
}
file.println(name);
Serial.print(F("LFN: "));
Serial.write('"');
Serial.print(name);
Serial.println('"');
Serial.print(F("SFN: "));
Serial.write('"');
file.printSFN(&Serial);
Serial.println('"');
Serial.print(F("Index: "));
if (file.dirIndex() < 10) {
Serial.write(' ');
}
Serial.println(file.dirIndex());
file.close();
}
Serial.println();
}
Serial.println(F("----- ls ------"));
sd.ls();
Serial.println();
Serial.println(F("nameTest done"));
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
while(!Serial);
Serial.print(F("\r\nFreeRam: "));
Serial.println(FreeRam());
Serial.println(F("Type any character to start."));
while (Serial.read() < 0) {}
if (!sd.begin(SD_CS_PIN)) sd.initErrorHalt();
if (file.openNext(sd.vwd())) {
file.close();
delay(100);
while (Serial.read() >= 0) {}
Serial.print(F("Type 'W' to wipe the card: "));
int c;
while ((c = Serial.read()) < 0) {}
if (c != 'W') {
sd.errorHalt(F("Invalid"));
}
Serial.println((char)c);
if (!sd.wipe(&Serial) || !sd.begin(SD_CS_PIN)) {
sd.errorHalt(F("wipe failed"));
}
}
basicTest();
nameTest();
}
//------------------------------------------------------------------------------
void loop() {}

+ 219
- 0
SdFatTestSuite/examples/lfnTestCout/lfnTestCout.ino View File

#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
const uint8_t SD_CS_PIN = SS;
SdFat sd;
SdFile file;
char name[260];

// Serial output stream
ArduinoOutStream cout(Serial);

// Serial in buffer.
char cinBuf[10];

// Serial input stream
ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
//------------------------------------------------------------------------------
char* testName[] = {
"low.low",
"low.Mix",
"low.UP",
"Mix.low",
"Mix.Mix",
"Mix.UP",
"UP.low",
"UP.Mix",
"UP.UP",
".dot",
".dot.dot",
"A b c . txt",
" Leading space and no extension",
"Trailing dots and space . . .",
"Long extension.extension",
"Space after dot. txt",
"Dot.dot.test.txt",
"Dot.dot.test.seq.txt",
"LOW.LOW",
"MIX.MIX",
"Invalid character *.test"
};
//------------------------------------------------------------------------------
bool checkName(char first, size_t len) {
size_t i;
if (len < 5 || len > sizeof(name)) {
return false;
}
if ( name[0] != first) {
return false;
}
for (i = 1; i < (len - 4); i++) {
if (name[i] != ('0' + (i + 1) %10)) {
return false;
}
}
char* p = ".txt";
while (*p) {
if (name[i++] != *p++) {
return false;
}
}
return name[i] == 0;
}
//------------------------------------------------------------------------------
void makeName(char first, size_t len) {
size_t i;
if (len > sizeof(name)) {
len = 255;
}
if (len < 5) {
len = 5;
}
name[0] = first;
for (i = 1; i < (len - 4); i++) {
name[i] = '0' + (i + 1) %10;
}
char* p = ".txt";
while (*p) name[i++] = *p++;
name[i] = 0;
}
//------------------------------------------------------------------------------
// test open, remove, getFilename, and ls.
void basicTest() {
size_t i;
size_t n = sd.vol()->fatType() == 32 ? 255 : 99;
uint16_t index;
uint16_t maxIndex = 0;
makeName('Z', 256);
if (!file.open(name, O_RDWR | O_CREAT)) {
cout << F("255 limit OK") << endl;
} else {
sd.errorHalt(F("255 limit"));
}
for (i = 5; i <= n; i++) {
makeName('A', i);

if (!file.open(name, O_RDWR | O_CREAT)) {
sd.errorHalt(F("open A"));
}
file.println(name);
cout << setw(3) << i << setw(5) << file.dirIndex() << F(" open A") << endl;
if (file.fileSize() != (i + 2)) {
sd.errorHalt(F("file size A"));
}
if (file.dirIndex() >= maxIndex) {
maxIndex = file.dirIndex();
} else {
sd.errorHalt(F("dirIndex"));
}
file.close();
if (!file.open(sd.vwd(), maxIndex, O_READ)) {
sd.errorHalt(F("open by index"));
}
memset(name, 0, sizeof(name));
if (!file.getFilename(name, sizeof(name))) {
sd.errorHalt(F("getFilename"));
}
if (!checkName('A', i)) {
cout << name << endl;
sd.errorHalt(F("checkName"));
}
file.close();
}
for (i = n; i >= 5; i -= 2) {
makeName('A', i);
cout << setw(3) << i << F( " rm A") << endl;
if (!sd.remove(name)) {
sd.errorHalt(F("remove A"));
}
}
for (i = n; i >= 5; i -= 2) {
makeName('B', i);
if (!file.open(name, O_RDWR | O_CREAT)) {
sd.errorHalt(F("open B"));
}
file.println(name);
cout << setw(3) << i << setw(5) << file.dirIndex() << F(" open B") << endl;
if (file.fileSize() != (i + 2)) {
sd.errorHalt(F("file size B"));
}
if (file.dirIndex() > maxIndex) {
sd.errorHalt(F("maxIndex"));
}
file.close();
}
cout << endl << F("----- ls ------") << endl;
sd.ls();
for (i = 5; i <= n; i++) {
char fc = i & 1 ? 'B' : 'A';
makeName(fc, i);
cout << setw(3) << i << F(" rm ") << fc << endl;
if (!sd.remove(name)) {
sd.errorHalt(F("remove A/B"));
}
}
if (file.openNext(sd.vwd())) {
sd.errorHalt(F("remove all"));
}
cout << endl << F("basicTest done") << endl;
}
//------------------------------------------------------------------------------
void nameTest() {
cout << endl;
uint8_t n = sizeof(testName)/sizeof(char*);
for (uint8_t i = 0; i < n; i++) {
cout << F("Name: \"") << testName[i] << '"' << endl;
if(!file.open(testName[i], O_CREAT | O_RDWR)) {
cout <<F("Open failed") << endl;
} else {
file.println(testName[i]);
if (!file.getFilename(name, sizeof(name))) {
sd.errorHalt(F("getFilemame"));
}
cout << F("LFN: \"") << name << '"' << endl;
cout << F("SFN: \"");
file.printSFN(&Serial);
cout << '"' << endl;
cout <<F("Index: ") << setw(2) << file.dirIndex() << endl;
file.close();
}
cout << endl;
}
cout << F("----- ls ------") << endl;
sd.ls();
cout << endl << F("nameTest done") << endl;

}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
while(!Serial); // Wait for USB Serial.
cout << endl << F("FreeRam: ") << FreeRam() << endl;
cout << F("Type any character to start.") << endl;
cin.readline();
if (!sd.begin(SD_CS_PIN)) {
sd.initErrorHalt();
}
if (file.openNext(sd.vwd())) {
file.close();
cout << F("Type 'W' to wipe the card: ");
cin.readline();
char c = cin.get();
cout << c << endl;
if (c != 'W') {
sd.errorHalt(F("Invalid"));
}
if (!sd.wipe(&Serial) || !sd.begin(SD_CS_PIN)) {
sd.errorHalt(F("wipe failed"));
}
}
basicTest();
nameTest();
}
//------------------------------------------------------------------------------
void loop() {}

BIN
SdLevel.png View File

Before After
Width: 695  |  Height: 668  |  Size: 21KB

+ 0
- 15
SoftwareSPI.txt View File

Software SPI is now supported on AVR, Due, and Teensy 3.x boards.

The most general approach is to set USE_MULTIPLE_SPI_TYPES nonzero
in SdFatConfig.h.
//------------------------------------------------------------------------------
/**
* Set USE_MULTIPLE_SPI_TYPES nonzero to enable the SdFatSoftSpi and
* SdFatLibSpi classes. SdFatSoftSpi uses software SPI and SdFatLibSpi
* uses the standard Arduino SPI library.
*/
#define USE_MULTIPLE_SPI_TYPES 0


You can also edit other configuration options in SdFatConfig.h to
change the type of SPI used by the SdFat class.

+ 0
- 44
WorkingDirectory.txt View File

Relative paths in SdFat are resolved in a manner similar to Windows.

Each instance of SdFat has a current directory. In SdFat this directory
is called the volume working directory, vwd. Initially this directory is
the root directory for the volume.

The volume working directory is changed by calling SdFat::chdir(path).

The call sd.chdir("/2011") will change the volume working directory
for sd to "/2011", assuming "/2011" exists.

Relative paths for SdFat member functions are resolved by starting at
the volume working directory.

For example, the call sd.mkdir("APRIL") will create the directory
"/2011/APRIL" assuming the volume working directory is "/2011".

SdFat has a current working directory, cwd, that is used to resolve paths
for file.open() calls.

For a single SD card the current working directory is always the volume
working directory for that card.

For multiple SD cards the current working directory is set to the volume
working directory of a card by calling the SdFat::chvol() member function.
The chvol() call is like the Windows <drive letter>: command.

The call sd2.chvol() will set the current working directory to the volume
working directory for sd2.

If the volume working directory for sd2 is "/MUSIC" the call

file.open("BIGBAND.WAV", O_READ);

will then open "/MUSIC/BIGBAND.WAV" on sd2.

The following functions are used to change or get current directories.
See the html documentation for more information.

bool SdFat::chdir(bool set_cwd = false);
bool SdFat::chdir(const char* path, bool set_cwd = false);
void SdFat::chvol();
SdBaseFile* SdFat::vwd();
static SdBaseFile* SdBaseFile::cwd();

+ 4
- 0
changes.txt View File

04 Dec 2014

Added support for Long File Names.

14 Nov 2014 14 Nov 2014


Replaced the core SdFat code with FatLib, a generic FAT12/FAT16/FAT32 Replaced the core SdFat code with FatLib, a generic FAT12/FAT16/FAT32

+ 2
- 2
html/_arduino_stream_8h.html View File

<div class="dyncontent"> <div class="dyncontent">
<div class="center"><img src="_arduino_stream_8h__incl.png" border="0" usemap="#_arduino_2libraries_2_sd_fat_2utility_2_arduino_stream_8h" alt=""/></div> <div class="center"><img src="_arduino_stream_8h__incl.png" border="0" usemap="#_arduino_2libraries_2_sd_fat_2utility_2_arduino_stream_8h" alt=""/></div>
<map name="_arduino_2libraries_2_sd_fat_2utility_2_arduino_stream_8h" id="_arduino_2libraries_2_sd_fat_2utility_2_arduino_stream_8h"> <map name="_arduino_2libraries_2_sd_fat_2utility_2_arduino_stream_8h" id="_arduino_2libraries_2_sd_fat_2utility_2_arduino_stream_8h">
<area shape="rect" id="node5" href="bufstream_8h.html" title="ibufstream and obufstream classes " alt="" coords="301,96,392,123"/> <area shape="rect" id="node9" href="iostream_8h.html" title="iostream class " alt="" coords="305,171,388,197"/> <area shape="rect" id="node11" href="istream_8h.html" title="istream class " alt="" coords="255,245,331,272"/> <area shape="rect" id="node38" href="ostream_8h.html" title="ostream class " alt="" coords="356,245,436,272"/> <area shape="rect" id="node13" href="ios_8h.html" title="ios_base and ios classes " alt="" coords="320,320,371,347"/> <area shape="rect" id="node15" href="_fat_file_8h.html" title="FatFile class. " alt="" coords="309,395,381,421"/> <area shape="rect" id="node24" href="_fat_lib_config_8h.html" title="configuration definitions " alt="" coords="425,544,529,571"/> <area shape="rect" id="node30" href="_fat_structs_8h.html" title="FAT file structures. " alt="" coords="115,544,209,571"/> <area shape="rect" id="node32" href="_fat_volume_8h.html" title="FatVolume class. " alt="" coords="157,469,253,496"/> <area shape="rect" id="node26" href="_sd_fat_config_8h.html" title="configuration definitions " alt="" coords="419,619,535,645"/> </map>
<area shape="rect" id="node5" href="bufstream_8h.html" title="ibufstream and obufstream classes " alt="" coords="80,96,171,123"/> <area shape="rect" id="node9" href="iostream_8h.html" title="iostream class " alt="" coords="151,171,233,197"/> <area shape="rect" id="node11" href="istream_8h.html" title="istream class " alt="" coords="255,245,331,272"/> <area shape="rect" id="node36" href="ostream_8h.html" title="ostream class " alt="" coords="151,245,231,272"/> <area shape="rect" id="node13" href="ios_8h.html" title="ios_base and ios classes " alt="" coords="256,320,307,347"/> <area shape="rect" id="node15" href="_fat_file_8h.html" title="FatFile class. " alt="" coords="245,395,317,421"/> <area shape="rect" id="node22" href="_fat_lib_config_8h.html" title="configuration definitions " alt="" coords="427,544,532,571"/> <area shape="rect" id="node28" href="_fat_structs_8h.html" title="FAT file structures. " alt="" coords="179,544,273,571"/> <area shape="rect" id="node30" href="_fat_volume_8h.html" title="FatVolume class. " alt="" coords="221,469,317,496"/> <area shape="rect" id="node24" href="_sd_fat_config_8h.html" title="configuration definitions " alt="" coords="422,619,537,645"/> </map>
</div> </div>
</div><table class="memberdecls"> </div><table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a> <tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a>
</div></div><!-- contents --> </div></div><!-- contents -->
<!-- start footer part --> <!-- start footer part -->
<hr class="footer"/><address class="footer"><small> <hr class="footer"/><address class="footer"><small>
Generated on Sat Nov 15 2014 10:05:35 for SdFat by &#160;<a href="http://www.doxygen.org/index.html">
Generated on Thu Dec 4 2014 08:53:20 for SdFat by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/> <img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.8 </a> 1.8.8
</small></address> </small></address>

BIN
html/_arduino_stream_8h__incl.png View File

Before After
Width: 628  |  Height: 725  |  Size: 39KB Width: 543  |  Height: 725  |  Size: 33KB

+ 1
- 1
html/_digital_pin_8h.html View File

</div></div><!-- contents --> </div></div><!-- contents -->
<!-- start footer part --> <!-- start footer part -->
<hr class="footer"/><address class="footer"><small> <hr class="footer"/><address class="footer"><small>
Generated on Sat Nov 15 2014 10:05:35 for SdFat by &#160;<a href="http://www.doxygen.org/index.html">
Generated on Thu Dec 4 2014 08:53:20 for SdFat by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/> <img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.8 </a> 1.8.8
</small></address> </small></address>

+ 98
- 14
html/_fat_file_8h.html View File

<div class="summary"> <div class="summary">
<a href="#nested-classes">Classes</a> &#124; <a href="#nested-classes">Classes</a> &#124;
<a href="#define-members">Macros</a> &#124; <a href="#define-members">Macros</a> &#124;
<a href="#typedef-members">Typedefs</a> </div>
<a href="#var-members">Variables</a> </div>
<div class="headertitle"> <div class="headertitle">
<div class="title">FatFile.h File Reference</div> </div> <div class="title">FatFile.h File Reference</div> </div>
</div><!--header--> </div><!--header-->


<p><a class="el" href="class_fat_file.html" title="Basic file class. ">FatFile</a> class. <p><a class="el" href="class_fat_file.html" title="Basic file class. ">FatFile</a> class.
<a href="#details">More...</a></p> <a href="#details">More...</a></p>
<div class="textblock"><code>#include &lt;ctype.h&gt;</code><br />
<code>#include &lt;string.h&gt;</code><br />
<div class="textblock"><code>#include &lt;string.h&gt;</code><br />
<code>#include &lt;stddef.h&gt;</code><br /> <code>#include &lt;stddef.h&gt;</code><br />
<code>#include &lt;limits.h&gt;</code><br /> <code>#include &lt;limits.h&gt;</code><br />
<code>#include &quot;<a class="el" href="_fat_lib_config_8h.html">FatLibConfig.h</a>&quot;</code><br /> <code>#include &quot;<a class="el" href="_fat_lib_config_8h.html">FatLibConfig.h</a>&quot;</code><br />
<code>#include &quot;FatApiConstants.h&quot;</code><br /> <code>#include &quot;FatApiConstants.h&quot;</code><br />
<code>#include &quot;<a class="el" href="_fat_structs_8h.html">FatStructs.h</a>&quot;</code><br /> <code>#include &quot;<a class="el" href="_fat_structs_8h.html">FatStructs.h</a>&quot;</code><br />
<code>#include &quot;<a class="el" href="_fat_volume_8h.html">FatVolume.h</a>&quot;</code><br /> <code>#include &quot;<a class="el" href="_fat_volume_8h.html">FatVolume.h</a>&quot;</code><br />
<code>#include &lt;Arduino.h&gt;</code><br />
</div><div class="textblock"><div class="dynheader"> </div><div class="textblock"><div class="dynheader">
Include dependency graph for FatFile.h:</div> Include dependency graph for FatFile.h:</div>
<div class="dyncontent"> <div class="dyncontent">
<div class="center"><img src="_fat_file_8h__incl.png" border="0" usemap="#_arduino_2libraries_2_sd_fat_2utility_2_fat_file_8h" alt=""/></div> <div class="center"><img src="_fat_file_8h__incl.png" border="0" usemap="#_arduino_2libraries_2_sd_fat_2utility_2_fat_file_8h" alt=""/></div>
<map name="_arduino_2libraries_2_sd_fat_2utility_2_fat_file_8h" id="_arduino_2libraries_2_sd_fat_2utility_2_fat_file_8h"> <map name="_arduino_2libraries_2_sd_fat_2utility_2_fat_file_8h" id="_arduino_2libraries_2_sd_fat_2utility_2_fat_file_8h">
<area shape="rect" id="node11" href="_fat_lib_config_8h.html" title="configuration definitions " alt="" coords="249,171,353,197"/> <area shape="rect" id="node17" href="_fat_structs_8h.html" title="FAT file structures. " alt="" coords="378,171,472,197"/> <area shape="rect" id="node19" href="_fat_volume_8h.html" title="FatVolume class. " alt="" coords="235,96,331,123"/> <area shape="rect" id="node13" href="_sd_fat_config_8h.html" title="configuration definitions " alt="" coords="243,245,359,272"/> </map>
<area shape="rect" id="node9" href="_fat_lib_config_8h.html" title="configuration definitions " alt="" coords="106,171,211,197"/> <area shape="rect" id="node15" href="_fat_structs_8h.html" title="FAT file structures. " alt="" coords="338,171,432,197"/> <area shape="rect" id="node17" href="_fat_volume_8h.html" title="FatVolume class. " alt="" coords="196,96,292,123"/> <area shape="rect" id="node11" href="_sd_fat_config_8h.html" title="configuration definitions " alt="" coords="101,245,216,272"/> </map>
</div> </div>
</div><div class="textblock"><div class="dynheader"> </div><div class="textblock"><div class="dynheader">
This graph shows which files directly or indirectly include this file:</div> This graph shows which files directly or indirectly include this file:</div>
<tr class="memitem:"><td class="memItemLeft" align="right" valign="top">struct &#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="struct_fat_pos__t.html">FatPos_t</a></td></tr> <tr class="memitem:"><td class="memItemLeft" align="right" valign="top">struct &#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="struct_fat_pos__t.html">FatPos_t</a></td></tr>
<tr class="memdesc:"><td class="mdescLeft">&#160;</td><td class="mdescRight">Internal type for file position - do not use in user apps. <a href="struct_fat_pos__t.html#details">More...</a><br /></td></tr> <tr class="memdesc:"><td class="mdescLeft">&#160;</td><td class="mdescRight">Internal type for file position - do not use in user apps. <a href="struct_fat_pos__t.html#details">More...</a><br /></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2">&#160;</td></tr> <tr class="separator:"><td class="memSeparator" colspan="2">&#160;</td></tr>
<tr class="memitem:"><td class="memItemLeft" align="right" valign="top">struct &#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="structfname__t.html">fname_t</a></td></tr>
<tr class="memdesc:"><td class="mdescLeft">&#160;</td><td class="mdescRight">Internal type for Short <a class="el" href="class_file.html" title="Arduino SD.h style File API. ">File</a> Name - do not use in user apps. <a href="structfname__t.html#details">More...</a><br /></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2">&#160;</td></tr>
</table><table class="memberdecls"> </table><table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="define-members"></a> <tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="define-members"></a>
Macros</h2></td></tr> Macros</h2></td></tr>
<tr class="memitem:a9f85580ad6f1dfc86fff09a58ff0a1c0"><td class="memItemLeft" align="right" valign="top">#define&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a9f85580ad6f1dfc86fff09a58ff0a1c0">isDirSeparator</a>(c)&#160;&#160;&#160;((c) == '/')</td></tr>
<tr class="separator:a9f85580ad6f1dfc86fff09a58ff0a1c0"><td class="memSeparator" colspan="2">&#160;</td></tr>
<tr class="memitem:a963f816fc88a5d8479c285ed4c630229"><td class="memItemLeft" align="right" valign="top">#define&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a963f816fc88a5d8479c285ed4c630229">PGM_P</a>&#160;&#160;&#160;const char*</td></tr> <tr class="memitem:a963f816fc88a5d8479c285ed4c630229"><td class="memItemLeft" align="right" valign="top">#define&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a963f816fc88a5d8479c285ed4c630229">PGM_P</a>&#160;&#160;&#160;const char*</td></tr>
<tr class="separator:a963f816fc88a5d8479c285ed4c630229"><td class="memSeparator" colspan="2">&#160;</td></tr> <tr class="separator:a963f816fc88a5d8479c285ed4c630229"><td class="memSeparator" colspan="2">&#160;</td></tr>
<tr class="memitem:a48c60b057902adf805797f183286728d"><td class="memItemLeft" align="right" valign="top">#define&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a48c60b057902adf805797f183286728d">pgm_read_byte</a>(addr)&#160;&#160;&#160;(*(const unsigned char*)(addr))</td></tr> <tr class="memitem:a48c60b057902adf805797f183286728d"><td class="memItemLeft" align="right" valign="top">#define&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a48c60b057902adf805797f183286728d">pgm_read_byte</a>(addr)&#160;&#160;&#160;(*(const unsigned char*)(addr))</td></tr>
<tr class="memitem:a9c00057fd19e916cc1aa0a5949336beb"><td class="memItemLeft" align="right" valign="top">#define&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a9c00057fd19e916cc1aa0a5949336beb">PSTR</a>(x)&#160;&#160;&#160;(x)</td></tr> <tr class="memitem:a9c00057fd19e916cc1aa0a5949336beb"><td class="memItemLeft" align="right" valign="top">#define&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a9c00057fd19e916cc1aa0a5949336beb">PSTR</a>(x)&#160;&#160;&#160;(x)</td></tr>
<tr class="separator:a9c00057fd19e916cc1aa0a5949336beb"><td class="memSeparator" colspan="2">&#160;</td></tr> <tr class="separator:a9c00057fd19e916cc1aa0a5949336beb"><td class="memSeparator" colspan="2">&#160;</td></tr>
</table><table class="memberdecls"> </table><table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="typedef-members"></a>
Typedefs</h2></td></tr>
<tr class="memitem:ac62f6449331cfe1a71f29be30efe7890"><td class="memItemLeft" align="right" valign="top">typedef Print&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#ac62f6449331cfe1a71f29be30efe7890">print_t</a></td></tr>
<tr class="separator:ac62f6449331cfe1a71f29be30efe7890"><td class="memSeparator" colspan="2">&#160;</td></tr>
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="var-members"></a>
Variables</h2></td></tr>
<tr class="memitem:a79e43960e1b4eecf274f5faea9c3168c"><td class="memItemLeft" align="right" valign="top">const uint8_t&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a79e43960e1b4eecf274f5faea9c3168c">FNAME_FLAG_LC_BASE</a> = <a class="el" href="_fat_structs_8h.html#a39f9b8960dba007b537e9b71c25384fe">DIR_NT_LC_BASE</a></td></tr>
<tr class="separator:a79e43960e1b4eecf274f5faea9c3168c"><td class="memSeparator" colspan="2">&#160;</td></tr>
<tr class="memitem:a135b7572768b09661aa38afaceec7296"><td class="memItemLeft" align="right" valign="top">const uint8_t&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a135b7572768b09661aa38afaceec7296">FNAME_FLAG_LC_EXT</a> = <a class="el" href="_fat_structs_8h.html#a8766a8bbab6ad3da38c1b308545d7572">DIR_NT_LC_EXT</a></td></tr>
<tr class="separator:a135b7572768b09661aa38afaceec7296"><td class="memSeparator" colspan="2">&#160;</td></tr>
<tr class="memitem:acd45286b7dfc5ba68be18c8c3a9d298d"><td class="memItemLeft" align="right" valign="top">const uint8_t&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#acd45286b7dfc5ba68be18c8c3a9d298d">FNAME_FLAG_LOST_CHARS</a> = 0X01</td></tr>
<tr class="separator:acd45286b7dfc5ba68be18c8c3a9d298d"><td class="memSeparator" colspan="2">&#160;</td></tr>
<tr class="memitem:a63994c21f3b723a55247f063a1b01c9c"><td class="memItemLeft" align="right" valign="top">const uint8_t&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a63994c21f3b723a55247f063a1b01c9c">FNAME_FLAG_MIXED_CASE</a> = 0X02</td></tr>
<tr class="separator:a63994c21f3b723a55247f063a1b01c9c"><td class="memSeparator" colspan="2">&#160;</td></tr>
<tr class="memitem:a1a041207a19d2fd9a1e2739343ccb29b"><td class="memItemLeft" align="right" valign="top">const uint8_t&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a1a041207a19d2fd9a1e2739343ccb29b">FNAME_FLAG_NEED_LFN</a></td></tr>
<tr class="separator:a1a041207a19d2fd9a1e2739343ccb29b"><td class="memSeparator" colspan="2">&#160;</td></tr>
</table> </table>
<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2> <a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
<div class="textblock"><p><a class="el" href="class_fat_file.html" title="Basic file class. ">FatFile</a> class. </p> <div class="textblock"><p><a class="el" href="class_fat_file.html" title="Basic file class. ">FatFile</a> class. </p>
</div><h2 class="groupheader">Macro Definition Documentation</h2> </div><h2 class="groupheader">Macro Definition Documentation</h2>
<a class="anchor" id="a9f85580ad6f1dfc86fff09a58ff0a1c0"></a>
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">#define isDirSeparator</td>
<td>(</td>
<td class="paramtype">&#160;</td>
<td class="paramname">c</td><td>)</td>
<td>&#160;&#160;&#160;((c) == '/')</td>
</tr>
</table>
</div><div class="memdoc">
<p>Expression for path name separator. </p>

</div>
</div>
<a class="anchor" id="a963f816fc88a5d8479c285ed4c630229"></a> <a class="anchor" id="a963f816fc88a5d8479c285ed4c630229"></a>
<div class="memitem"> <div class="memitem">
<div class="memproto"> <div class="memproto">


</div> </div>
</div> </div>
<h2 class="groupheader">Typedef Documentation</h2>
<a class="anchor" id="ac62f6449331cfe1a71f29be30efe7890"></a>
<h2 class="groupheader">Variable Documentation</h2>
<a class="anchor" id="a79e43960e1b4eecf274f5faea9c3168c"></a>
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">const uint8_t FNAME_FLAG_LC_BASE = <a class="el" href="_fat_structs_8h.html#a39f9b8960dba007b537e9b71c25384fe">DIR_NT_LC_BASE</a></td>
</tr>
</table>
</div><div class="memdoc">
<p>Filename base-name is all lower case </p>

</div>
</div>
<a class="anchor" id="a135b7572768b09661aa38afaceec7296"></a>
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">const uint8_t FNAME_FLAG_LC_EXT = <a class="el" href="_fat_structs_8h.html#a8766a8bbab6ad3da38c1b308545d7572">DIR_NT_LC_EXT</a></td>
</tr>
</table>
</div><div class="memdoc">
<p>Filename extension is all lower case. </p>

</div>
</div>
<a class="anchor" id="acd45286b7dfc5ba68be18c8c3a9d298d"></a>
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">const uint8_t FNAME_FLAG_LOST_CHARS = 0X01</td>
</tr>
</table>
</div><div class="memdoc">
<p>Derived from a LFN with loss or conversion of characters. </p>

</div>
</div>
<a class="anchor" id="a63994c21f3b723a55247f063a1b01c9c"></a>
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">const uint8_t FNAME_FLAG_MIXED_CASE = 0X02</td>
</tr>
</table>
</div><div class="memdoc">
<p>Base-name or extension has mixed case. </p>

</div>
</div>
<a class="anchor" id="a1a041207a19d2fd9a1e2739343ccb29b"></a>
<div class="memitem"> <div class="memitem">
<div class="memproto"> <div class="memproto">
<table class="memname"> <table class="memname">
<tr> <tr>
<td class="memname">typedef Print <a class="el" href="_fat_file_8h.html#ac62f6449331cfe1a71f29be30efe7890">print_t</a></td>
<td class="memname">const uint8_t FNAME_FLAG_NEED_LFN</td>
</tr> </tr>
</table> </table>
</div><div class="memdoc"> </div><div class="memdoc">
<p>Use Print on Arduino </p>
<b>Initial value:</b><div class="fragment"><div class="line">=</div>
<div class="line"> <a class="code" href="_fat_file_8h.html#acd45286b7dfc5ba68be18c8c3a9d298d">FNAME_FLAG_LOST_CHARS</a> | <a class="code" href="_fat_file_8h.html#a63994c21f3b723a55247f063a1b01c9c">FNAME_FLAG_MIXED_CASE</a></div>
<div class="ttc" id="_fat_file_8h_html_a63994c21f3b723a55247f063a1b01c9c"><div class="ttname"><a href="_fat_file_8h.html#a63994c21f3b723a55247f063a1b01c9c">FNAME_FLAG_MIXED_CASE</a></div><div class="ttdeci">const uint8_t FNAME_FLAG_MIXED_CASE</div><div class="ttdef"><b>Definition:</b> FatFile.h:96</div></div>
<div class="ttc" id="_fat_file_8h_html_acd45286b7dfc5ba68be18c8c3a9d298d"><div class="ttname"><a href="_fat_file_8h.html#acd45286b7dfc5ba68be18c8c3a9d298d">FNAME_FLAG_LOST_CHARS</a></div><div class="ttdeci">const uint8_t FNAME_FLAG_LOST_CHARS</div><div class="ttdef"><b>Definition:</b> FatFile.h:94</div></div>
</div><!-- fragment --><p>LFN entries are required for file name. </p>


</div> </div>
</div> </div>
</div><!-- contents --> </div><!-- contents -->
<!-- start footer part --> <!-- start footer part -->
<hr class="footer"/><address class="footer"><small> <hr class="footer"/><address class="footer"><small>
Generated on Sat Nov 15 2014 10:05:35 for SdFat by &#160;<a href="http://www.doxygen.org/index.html">
Generated on Thu Dec 4 2014 08:53:20 for SdFat by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/> <img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.8 </a> 1.8.8
</small></address> </small></address>

+ 0
- 0
html/_fat_file_8h__incl.png View File

Before After
Width: 627  |  Height: 352  |  Size: 20KB Width: 437  |  Height: 352  |  Size: 19KB

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save