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. |
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. |
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. |
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 |
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
int MinimumSerial::read() { | int MinimumSerial::read() { | ||||
if (UCSR0A & (1 << RXC0)) return UDR0; | |||||
if (UCSR0A & (1 << RXC0)) { | |||||
return UDR0; | |||||
} | |||||
return -1; | return -1; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ |
/** | /** | ||||
* 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; | ||||
}; | }; |
#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 |
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
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); |
#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 |
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
#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(); |
* 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 |
/* 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. | ||||
*/ | |||||
*/ |
* \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 |
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 |
* 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++]; | ||||
} | } | ||||
} | } |
*/ | */ | ||||
#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; |
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 |
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
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; |
//============================================================================== | //============================================================================== | ||||
/** | /** | ||||
* \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); |
// 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); | ||||
} | |||||
} |
/* | |||||
* 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() {} |
void setup() { | void setup() { | ||||
Serial.begin(9600); | Serial.begin(9600); | ||||
while (!Serial) {} // wait for Leonardo | while (!Serial) {} // wait for Leonardo | ||||
delay(2000); | delay(2000); | ||||
/* | /* | ||||
* 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")); |
/* | /* | ||||
* 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> |
/* | /* | ||||
* 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"); | ||||
/* | /* | ||||
* 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; |
/* | /* | ||||
* 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(); |
/* | /* | ||||
* 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"); |
/* | /* | ||||
* 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"); | ||||
/* | /* | ||||
* 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() {} |
// 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) {} |
/* | /* | ||||
* 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; | ||||
} | } |
* 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(); |
// 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); | |||||
} | } |
/** | /** | ||||
* 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(); |
/* | /* | ||||
* 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. |
/* | /* | ||||
* 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"); | |||||
} | } |
// 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) {} | ||||
} | } |
/* | /* | ||||
* 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; | ||||
} | } |
/* | |||||
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 | |||||
} | |||||
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(); | ||||
} | } |
/* | /* | ||||
* 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(); |
/* | /* | ||||
* 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; |
// | // | ||||
#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 |
// 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"); | ||||
} | } |
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(); | ||||
} | } | ||||
#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 |
/* | /* | ||||
* 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) {} |
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")); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ |
/* | /* | ||||
* 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(); | ||||
} | } |
/* | /* | ||||
* 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 |
/* | /* | ||||
* 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(); |
/* | /* | ||||
* 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. |
// 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) {} |
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(); | ||||
* 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(); |
/* | /* | ||||
* 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() {} |
/* | /* | ||||
* 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() {} |
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() {} |
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 |
// 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 */ |
* \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 | ||||
}; | }; |
* 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 |
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
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); | ||||
} | } |
/* 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 |
*/ | */ | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** 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; |
*/ | */ | ||||
#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__ |
* 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 |
* 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; | ||||
} | } |
#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 |
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; | ||||
} | } | ||||
*/ | */ | ||||
#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); |
* <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)); | ||||
} | } |
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); |
*/ | */ | ||||
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); |
* 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; |
*/ | */ | ||||
#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); | ||||
} | } |
/** 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 | ||||
}; | }; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ |
#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; |
* 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); | ||||
} | } |
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 |
#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); |
* \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; | ||||
} | } | ||||
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); | ||||
} | } |
* 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() { } |
* 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 |
// 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() {} |
#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() {} |
#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() {} |
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. |
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(); |
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 |
<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  <a href="http://www.doxygen.org/index.html"> | |||||
Generated on Thu Dec 4 2014 08:53:20 for SdFat by  <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> |
</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  <a href="http://www.doxygen.org/index.html"> | |||||
Generated on Thu Dec 4 2014 08:53:20 for SdFat by  <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> |
<div class="summary"> | <div class="summary"> | ||||
<a href="#nested-classes">Classes</a> | | <a href="#nested-classes">Classes</a> | | ||||
<a href="#define-members">Macros</a> | | <a href="#define-members">Macros</a> | | ||||
<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 <ctype.h></code><br /> | |||||
<code>#include <string.h></code><br /> | |||||
<div class="textblock"><code>#include <string.h></code><br /> | |||||
<code>#include <stddef.h></code><br /> | <code>#include <stddef.h></code><br /> | ||||
<code>#include <limits.h></code><br /> | <code>#include <limits.h></code><br /> | ||||
<code>#include "<a class="el" href="_fat_lib_config_8h.html">FatLibConfig.h</a>"</code><br /> | <code>#include "<a class="el" href="_fat_lib_config_8h.html">FatLibConfig.h</a>"</code><br /> | ||||
<code>#include "FatApiConstants.h"</code><br /> | <code>#include "FatApiConstants.h"</code><br /> | ||||
<code>#include "<a class="el" href="_fat_structs_8h.html">FatStructs.h</a>"</code><br /> | <code>#include "<a class="el" href="_fat_structs_8h.html">FatStructs.h</a>"</code><br /> | ||||
<code>#include "<a class="el" href="_fat_volume_8h.html">FatVolume.h</a>"</code><br /> | <code>#include "<a class="el" href="_fat_volume_8h.html">FatVolume.h</a>"</code><br /> | ||||
<code>#include <Arduino.h></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  </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  </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"> </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"> </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"> </td></tr> | <tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr> | ||||
<tr class="memitem:"><td class="memItemLeft" align="right" valign="top">struct  </td><td class="memItemRight" valign="bottom"><a class="el" href="structfname__t.html">fname_t</a></td></tr> | |||||
<tr class="memdesc:"><td class="mdescLeft"> </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"> </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 </td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a9f85580ad6f1dfc86fff09a58ff0a1c0">isDirSeparator</a>(c)   ((c) == '/')</td></tr> | |||||
<tr class="separator:a9f85580ad6f1dfc86fff09a58ff0a1c0"><td class="memSeparator" colspan="2"> </td></tr> | |||||
<tr class="memitem:a963f816fc88a5d8479c285ed4c630229"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a963f816fc88a5d8479c285ed4c630229">PGM_P</a>   const char*</td></tr> | <tr class="memitem:a963f816fc88a5d8479c285ed4c630229"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a963f816fc88a5d8479c285ed4c630229">PGM_P</a>   const char*</td></tr> | ||||
<tr class="separator:a963f816fc88a5d8479c285ed4c630229"><td class="memSeparator" colspan="2"> </td></tr> | <tr class="separator:a963f816fc88a5d8479c285ed4c630229"><td class="memSeparator" colspan="2"> </td></tr> | ||||
<tr class="memitem:a48c60b057902adf805797f183286728d"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a48c60b057902adf805797f183286728d">pgm_read_byte</a>(addr)   (*(const unsigned char*)(addr))</td></tr> | <tr class="memitem:a48c60b057902adf805797f183286728d"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a48c60b057902adf805797f183286728d">pgm_read_byte</a>(addr)   (*(const unsigned char*)(addr))</td></tr> | ||||
<tr class="memitem:a9c00057fd19e916cc1aa0a5949336beb"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a9c00057fd19e916cc1aa0a5949336beb">PSTR</a>(x)   (x)</td></tr> | <tr class="memitem:a9c00057fd19e916cc1aa0a5949336beb"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="_fat_file_8h.html#a9c00057fd19e916cc1aa0a5949336beb">PSTR</a>(x)   (x)</td></tr> | ||||
<tr class="separator:a9c00057fd19e916cc1aa0a5949336beb"><td class="memSeparator" colspan="2"> </td></tr> | <tr class="separator:a9c00057fd19e916cc1aa0a5949336beb"><td class="memSeparator" colspan="2"> </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 </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"> </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 </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"> </td></tr> | |||||
<tr class="memitem:a135b7572768b09661aa38afaceec7296"><td class="memItemLeft" align="right" valign="top">const uint8_t </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"> </td></tr> | |||||
<tr class="memitem:acd45286b7dfc5ba68be18c8c3a9d298d"><td class="memItemLeft" align="right" valign="top">const uint8_t </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"> </td></tr> | |||||
<tr class="memitem:a63994c21f3b723a55247f063a1b01c9c"><td class="memItemLeft" align="right" valign="top">const uint8_t </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"> </td></tr> | |||||
<tr class="memitem:a1a041207a19d2fd9a1e2739343ccb29b"><td class="memItemLeft" align="right" valign="top">const uint8_t </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"> </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"> </td> | |||||
<td class="paramname">c</td><td>)</td> | |||||
<td>   ((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  <a href="http://www.doxygen.org/index.html"> | |||||
Generated on Thu Dec 4 2014 08:53:20 for SdFat by  <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> |