| @@ -1,62 +0,0 @@ | |||
| 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. | |||
| @@ -1,13 +0,0 @@ | |||
| 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. | |||
| @@ -1,21 +0,0 @@ | |||
| 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. | |||
| @@ -1,23 +0,0 @@ | |||
| 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 | |||
| @@ -44,7 +44,9 @@ void MinimumSerial::begin(uint32_t baud) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int MinimumSerial::read() { | |||
| if (UCSR0A & (1 << RXC0)) return UDR0; | |||
| if (UCSR0A & (1 << RXC0)) { | |||
| return UDR0; | |||
| } | |||
| return -1; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -36,14 +36,14 @@ class MinimumSerial : public Print { | |||
| /** | |||
| * Unbuffered read | |||
| * \return -1 if no character is available or an available character. | |||
| */ | |||
| */ | |||
| 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); | |||
| using Print::write; | |||
| }; | |||
| @@ -28,7 +28,7 @@ | |||
| #include "utility/FatLib.h" | |||
| //------------------------------------------------------------------------------ | |||
| /** SdFat version YYYYMMDD */ | |||
| #define SD_FAT_VERSION 20141115 | |||
| #define SD_FAT_VERSION 20141204 | |||
| //============================================================================== | |||
| /** | |||
| * \class SdFatBase | |||
| @@ -44,12 +44,16 @@ class SdFatBase : public FatFileSystem { | |||
| */ | |||
| bool begin(SdSpiCard::m_spi_t* spi, uint8_t csPin = SS, uint8_t divisor = 2) { | |||
| return m_sdCard.begin(spi, csPin, divisor) && | |||
| FatFileSystem::begin(&m_vwd); | |||
| FatFileSystem::begin(&m_vwd); | |||
| } | |||
| /** \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. */ | |||
| void errorHalt() {errorHalt(&Serial);} | |||
| void errorHalt() { | |||
| errorHalt(&Serial); | |||
| } | |||
| /** %Print any SD error code and halt. | |||
| * | |||
| * \param[in] pr Print destination. | |||
| @@ -59,7 +63,9 @@ class SdFatBase : public FatFileSystem { | |||
| * | |||
| * \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. | |||
| * | |||
| * \param[in] pr Print destination. | |||
| @@ -70,7 +76,9 @@ class SdFatBase : public FatFileSystem { | |||
| * | |||
| * \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. | |||
| * | |||
| * \param[in] pr Print destination. | |||
| @@ -78,7 +86,9 @@ class SdFatBase : public FatFileSystem { | |||
| */ | |||
| void errorHalt(Print* pr, const __FlashStringHelper* msg); | |||
| /** %Print any SD error code to Serial */ | |||
| void errorPrint() {errorPrint(&Serial);} | |||
| void errorPrint() { | |||
| errorPrint(&Serial); | |||
| } | |||
| /** %Print any SD error code. | |||
| * \param[in] pr Print device. | |||
| */ | |||
| @@ -87,7 +97,9 @@ class SdFatBase : public FatFileSystem { | |||
| * | |||
| * \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. | |||
| * | |||
| * \param[in] pr Print destination. | |||
| @@ -98,19 +110,26 @@ class SdFatBase : public FatFileSystem { | |||
| * | |||
| * \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. | |||
| * | |||
| * \param[in] pr Print destination. | |||
| * \param[in] msg Message to print. | |||
| */ | |||
| 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. | |||
| */ | |||
| bool fsBegin() {return FatFileSystem::begin(&m_vwd);} | |||
| bool fsBegin() { | |||
| return FatFileSystem::begin(&m_vwd); | |||
| } | |||
| /** %Print any SD error code and halt. */ | |||
| void initErrorHalt() {initErrorHalt(&Serial);} | |||
| void initErrorHalt() { | |||
| initErrorHalt(&Serial); | |||
| } | |||
| /** %Print error details and halt after begin fails. | |||
| * | |||
| * \param[in] pr Print destination. | |||
| @@ -120,22 +139,30 @@ class SdFatBase : public FatFileSystem { | |||
| * | |||
| * \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. | |||
| * \param[in] pr Print device. | |||
| * \param[in] msg Message to print. | |||
| */ | |||
| 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) { | |||
| 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); | |||
| /** Print error details after SdFat::init() fails. */ | |||
| void initErrorPrint() {initErrorPrint(&Serial);} | |||
| void initErrorPrint() { | |||
| initErrorPrint(&Serial); | |||
| } | |||
| /** Print error details after SdFatBase::init() fails. | |||
| * | |||
| * \param[in] pr Print destination. | |||
| @@ -145,7 +172,9 @@ class SdFatBase : public FatFileSystem { | |||
| * | |||
| * \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. | |||
| * | |||
| * \param[in] pr Print destination. | |||
| @@ -201,13 +230,19 @@ class SdFatBase : public FatFileSystem { | |||
| */ | |||
| File open(const char *path, uint8_t mode = FILE_READ); | |||
| /** \return a pointer to the volume working directory. */ | |||
| SdBaseFile* vwd() {return &m_vwd;} | |||
| SdBaseFile* vwd() { | |||
| return &m_vwd; | |||
| } | |||
| using FatFileSystem::ls; | |||
| 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) { | |||
| return m_sdCard.readBlock(block, dst); | |||
| } | |||
| @@ -239,7 +274,7 @@ class SdFat : public SdFatBase { | |||
| bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||
| 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] divisor SPI divisor. | |||
| * \return true for success else false. | |||
| @@ -251,23 +286,23 @@ class SdFat : public SdFatBase { | |||
| SpiDefault_t m_spi; | |||
| }; | |||
| //============================================================================== | |||
| #if USE_MULTIPLE_SPI_TYPES || defined(DOXYGEN) | |||
| #if SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) | |||
| /** | |||
| * \class SdFatLibSpi | |||
| * \brief SdFat class using the standard Arduino SPI library. | |||
| */ | |||
| class SdFatLibSpi: public SdFatBase { | |||
| 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) { | |||
| 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] divisor SPI divisor. | |||
| * \return true for success else false. | |||
| @@ -296,7 +331,7 @@ class SdFatSoftSpi : public SdFatBase { | |||
| bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||
| 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] divisor SPI divisor. | |||
| * \return true for success else false. | |||
| @@ -308,5 +343,5 @@ class SdFatSoftSpi : public SdFatBase { | |||
| private: | |||
| SdSpiSoft<MisoPin, MosiPin, SckPin> m_spi; | |||
| }; | |||
| #endif // USE_MULTIPLE_SPI_TYPES | |||
| #endif /// SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) | |||
| #endif // SdFat_h | |||
| @@ -35,7 +35,9 @@ void SdFatBase::errorHalt(Print* pr, const __FlashStringHelper* msg) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdFatBase::errorPrint(Print* pr) { | |||
| if (!cardErrorCode()) return; | |||
| if (!cardErrorCode()) { | |||
| return; | |||
| } | |||
| pr->print(F("SD errorCode: 0X")); | |||
| pr->print(cardErrorCode(), HEX); | |||
| pr->print(F(",0X")); | |||
| @@ -64,10 +66,6 @@ void SdFatBase::initErrorHalt(Print* pr, char const *msg) { | |||
| 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) { | |||
| pr->println(msg); | |||
| initErrorHalt(pr); | |||
| @@ -29,11 +29,58 @@ | |||
| #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. | |||
| @@ -47,78 +94,23 @@ | |||
| /** | |||
| * 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. | |||
| * programs 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. | |||
| * 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. | |||
| * | |||
| * | |||
| * 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. | |||
| */ | |||
| #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. | |||
| * FAT12 has not been well tested and requires additional flash. | |||
| @@ -154,7 +146,7 @@ uint8_t const SOFT_SPI_SCK_PIN = 13; | |||
| /** | |||
| * 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 | |||
| * with code written for older versions of SdFat. | |||
| * with code written for older versions of SdFat. | |||
| */ | |||
| #define SD_FILE_USES_STREAM 0 | |||
| //------------------------------------------------------------------------------ | |||
| @@ -170,8 +162,8 @@ const uint8_t SPI_SCK_INIT_DIVISOR = 128; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * 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__ | |||
| #define USE_SEPARATE_FAT_CACHE 1 | |||
| @@ -23,9 +23,6 @@ | |||
| //------------------------------------------------------------------------------ | |||
| #ifdef __arm__ | |||
| extern "C" char* sbrk(int incr); | |||
| /** Amount of free RAM | |||
| * \return The number of free bytes. | |||
| */ | |||
| int SdFatUtil::FreeRam() { | |||
| char top; | |||
| return &top - reinterpret_cast<char*>(sbrk(0)); | |||
| @@ -42,20 +39,12 @@ int SdFatUtil::FreeRam() { | |||
| } | |||
| #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) { | |||
| 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) { | |||
| print_P(pr, str); | |||
| pr->println(); | |||
| @@ -12,7 +12,7 @@ | |||
| * 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 Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| @@ -30,8 +30,21 @@ | |||
| #define PgmPrintln(x) SerialPrintln_P(PSTR(x)) | |||
| namespace SdFatUtil { | |||
| /** Amount of free RAM | |||
| * \return The number of free bytes. | |||
| */ | |||
| 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); | |||
| /** %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); | |||
| //---------------------------------------------------------------------------- | |||
| /** %Print a string in flash memory to Serial. | |||
| @@ -49,8 +62,6 @@ namespace SdFatUtil { | |||
| inline void SerialPrintln_P(PGM_P str) { | |||
| println_P(&Serial, str); | |||
| } | |||
| void SerialPrint_P(PGM_P str); | |||
| void SerialPrintln_P(PGM_P str); | |||
| } // namespace SdFatUtil | |||
| using namespace SdFatUtil; // NOLINT | |||
| #endif // #define SdFatUtil_h | |||
| @@ -1,13 +1,13 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * | |||
| * 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 | |||
| * (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 | |||
| @@ -31,8 +31,8 @@ cards are supported. | |||
| Experimental support for FAT12 can be enabled by setting FAT12_SUPPORT | |||
| 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, | |||
| SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream, | |||
| @@ -42,7 +42,7 @@ The SdFat, SdFatLibSpi, and SdFatSoftSpi classes maintain a FAT volume, | |||
| a current working directory, and simplifies initialization of other classes. | |||
| The SdFat class uses a fast custom hardware SPI implementation. The | |||
| 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(), | |||
| binary read(), binary write(), close(), remove(), and sync(). SdBaseFile | |||
| @@ -52,11 +52,11 @@ The SdFile class has all the SdBaseFile class functions plus the Arduino | |||
| Print class functions. | |||
| 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. | |||
| 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 | |||
| text files. | |||
| @@ -65,7 +65,7 @@ The \ref ifstream class implements C++ iostreams for reading 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. | |||
| There are many tutorials and much documentation about using C++ iostreams | |||
| @@ -85,7 +85,7 @@ developed to test %SdFat and illustrate its use. | |||
| \section Install Installation | |||
| 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. | |||
| @@ -94,31 +94,77 @@ http://arduino.cc/en/Guide/Libraries | |||
| \section SDconfig SdFat Configuration | |||
| 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. | |||
| Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. | |||
| 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 | |||
| 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 | |||
| 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 | |||
| @@ -139,7 +185,7 @@ limited RAM. | |||
| \section Hardware Hardware Configuration | |||
| %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. | |||
| The hardware interface to the SD card should not use a resistor based level | |||
| @@ -153,22 +199,78 @@ uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the | |||
| 74LCX245. | |||
| 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. | |||
| A feature to use software SPI is available. Software SPI is slower | |||
| than hardware SPI but allows any digital pins to be used. See | |||
| 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 | |||
| 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 | |||
| 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. | |||
| 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 | |||
| @@ -177,13 +279,15 @@ of letters and digits. The following special characters are also allowed: | |||
| $ % ' - _ @ ~ ` ! ( ) { } ^ # & | |||
| 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 | |||
| \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 | |||
| since 2048 bytes of I/O is required to update file and | |||
| @@ -202,8 +306,8 @@ SDFormatter which can be downloaded from: | |||
| 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. | |||
| SDFormatter aligns flash erase boundaries with file | |||
| @@ -214,7 +318,7 @@ very small cards as FAT12. Use the SdFat formatter to force FAT16 | |||
| formatting of small cards. | |||
| 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 | |||
| file systems become slower if many files have been created and deleted. | |||
| @@ -229,7 +333,7 @@ A number of examples are provided in the SdFat/examples folder. | |||
| See the html documentation for a list. | |||
| 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 | |||
| the example. | |||
| @@ -252,7 +356,7 @@ formating - Print a table with various formatting options. | |||
| 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. | |||
| @@ -260,7 +364,7 @@ OpenNext - Open all files in the root dir and print their filename. | |||
| 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. | |||
| @@ -270,10 +374,10 @@ ReadWriteSdFat - SdFat version of Arduino SD ReadWrite example. | |||
| 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. | |||
| SdInfo - Initialize an SD card and analyze its structure for trouble shooting. | |||
| StdioBench - Demo and test of stdio style stream. | |||
| @@ -281,8 +385,8 @@ StdioBench - Demo and test of stdio style stream. | |||
| StreamParseInt - Demo of the SD.h API and the File class parseInt() function. | |||
| ThreeCards - Demonstrate simultaneous use of SdFat, SdFatLibSpi, SdFatSoftSpi. | |||
| Timestamp - Sets file create, modify, and access timestamps. | |||
| TwoCards - Example using two SD cards. | |||
| */ | |||
| */ | |||
| @@ -43,7 +43,9 @@ class SdBaseFile : public FatFile { | |||
| * \param[in] path File location and name. | |||
| * \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::printFatDate; | |||
| using FatFile::printFatTime; | |||
| @@ -81,8 +83,8 @@ class SdBaseFile : public FatFile { | |||
| } | |||
| /** 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() { | |||
| return FatFile::printName(&Serial); | |||
| @@ -124,24 +126,32 @@ class SdFile : public SdBaseFile, public Print { | |||
| return n > INT_MAX ? INT_MAX : n; | |||
| } | |||
| /** 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 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. | |||
| * | |||
| * \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. | |||
| */ | |||
| int read() {return SdBaseFile::read();} | |||
| int read() { | |||
| return SdBaseFile::read(); | |||
| } | |||
| /** Write a byte to a file. Required by the Arduino Print class. | |||
| * \param[in] b the byte to be written. | |||
| * Use getWriteError to check for errors. | |||
| * \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. | |||
| * \param[in] str Pointer to the string. | |||
| * Use getWriteError to check for errors. | |||
| @@ -168,21 +178,6 @@ class SdFile : public SdBaseFile, public Print { | |||
| size_t write(const uint8_t *buf, size_t 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")); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| @@ -200,14 +195,18 @@ class File : public SdBaseFile, public Stream { | |||
| * bitwise-inclusive OR of open flags. see | |||
| * 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::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 | |||
| * or INT_MAX if more than INT_MAX bytes are available. | |||
| */ | |||
| @@ -216,24 +215,32 @@ class File : public SdBaseFile, public Stream { | |||
| return n > INT_MAX ? INT_MAX : n; | |||
| } | |||
| /** 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() { | |||
| m_name[0] = 0; | |||
| getFilename(m_name); | |||
| getSFN(m_name); | |||
| return m_name; | |||
| } | |||
| /** Return the next available byte without consuming it. | |||
| * | |||
| * \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. */ | |||
| uint32_t position() {return curPosition();} | |||
| uint32_t position() { | |||
| return curPosition(); | |||
| } | |||
| /** Opens the next file or folder in a directory. | |||
| * | |||
| * \param[in] mode open mode flags. | |||
| @@ -249,10 +256,14 @@ class File : public SdBaseFile, public Stream { | |||
| * \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. | |||
| */ | |||
| int read() {return SdBaseFile::read();} | |||
| int read() { | |||
| return SdBaseFile::read(); | |||
| } | |||
| /** Rewind a file if it is a directory */ | |||
| void rewindDirectory() { | |||
| if (isDir()) rewind(); | |||
| if (isDir()) { | |||
| rewind(); | |||
| } | |||
| } | |||
| /** | |||
| * Seek to a new position in the file, which must be between | |||
| @@ -261,15 +272,21 @@ class File : public SdBaseFile, public Stream { | |||
| * \param[in] pos the new file position. | |||
| * \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. */ | |||
| uint32_t size() {return fileSize();} | |||
| uint32_t size() { | |||
| return fileSize(); | |||
| } | |||
| /** Write a byte to a file. Required by the Arduino Print class. | |||
| * \param[in] b the byte to be written. | |||
| * Use getWriteError to check for errors. | |||
| * \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. | |||
| * | |||
| * \note Data is moved to the cache but may not be written to the | |||
| @@ -218,7 +218,7 @@ typedef struct CID { | |||
| unsigned char always1 : 1; | |||
| /** CRC7 checksum */ | |||
| unsigned char crc : 7; | |||
| }__attribute__((packed)) cid_t; | |||
| } __attribute__((packed)) cid_t; | |||
| //============================================================================== | |||
| /** | |||
| * \class CSDV1 | |||
| @@ -283,7 +283,7 @@ typedef struct CSDV1 { | |||
| // byte 15 | |||
| unsigned char always1 : 1; | |||
| unsigned char crc : 7; | |||
| }__attribute__((packed)) csd1_t; | |||
| } __attribute__((packed)) csd1_t; | |||
| //============================================================================== | |||
| /** | |||
| * \class CSDV2 | |||
| @@ -368,7 +368,7 @@ typedef struct CSDV2 { | |||
| unsigned char always1 : 1; | |||
| /** checksum */ | |||
| unsigned char crc : 7; | |||
| }__attribute__((packed)) csd2_t; | |||
| } __attribute__((packed)) csd2_t; | |||
| //============================================================================== | |||
| /** | |||
| * \class csd_t | |||
| @@ -17,40 +17,15 @@ | |||
| * along with the Arduino SdSpi Library. If not, see | |||
| * <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 | |||
| #define SdSpi_h | |||
| #include <Arduino.h> | |||
| #include "SdFatConfig.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 | |||
| @@ -83,11 +58,11 @@ class SdSpiBase { | |||
| * \param[in] data Byte to send | |||
| */ | |||
| 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; | |||
| /** \return true if hardware SPI else false */ | |||
| virtual bool useSpiTransactions() = 0; | |||
| @@ -97,11 +72,11 @@ class SdSpiBase { | |||
| * \class SdSpi | |||
| * \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 { | |||
| #else | |||
| #else // SD_SPI_CONFIGURATION >= 3 | |||
| class SdSpi { | |||
| #endif | |||
| #endif // SD_SPI_CONFIGURATION >= 3 | |||
| public: | |||
| /** Initialize the SPI bus */ | |||
| void begin(); | |||
| @@ -135,27 +110,30 @@ class SdSpi { | |||
| */ | |||
| void send(const uint8_t* buf, size_t n); | |||
| /** \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 | |||
| * \brief Arduino SPI library class for access to SD and SDHC flash | |||
| * 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 { | |||
| #else | |||
| #else // SD_SPI_CONFIGURATION >= 3 | |||
| class SdSpiLib { | |||
| #endif | |||
| #endif // SD_SPI_CONFIGURATION >= 3 | |||
| public: | |||
| /** | |||
| * Initialize SPI pins. | |||
| */ | |||
| void begin() {SPI.begin();} | |||
| void begin() { | |||
| SPI.begin(); | |||
| } | |||
| /** Set SPI options for access to SD/SDHC cards. | |||
| * | |||
| * \param[in] divisor SCK clock divider relative to the system clock. | |||
| @@ -163,25 +141,35 @@ class SdSpiLib { | |||
| void init(uint8_t divisor) { | |||
| SPI.setBitOrder(MSBFIRST); | |||
| SPI.setDataMode(SPI_MODE0); | |||
| #ifndef SPI_CLOCK_DIV128 | |||
| #ifndef SPI_CLOCK_DIV128 | |||
| SPI.setClockDivider(divisor); | |||
| #else // SPI_CLOCK_DIV128 | |||
| #else // SPI_CLOCK_DIV128 | |||
| 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); | |||
| #endif // SPI_CLOCK_DIV128 | |||
| #endif // SPI_CLOCK_DIV128 | |||
| } | |||
| /** Receive a byte. | |||
| * | |||
| * \return The byte. | |||
| */ | |||
| uint8_t receive() {return SPI.transfer(0XFF);} | |||
| uint8_t receive() { | |||
| return SPI.transfer(0XFF); | |||
| } | |||
| /** Receive multiple bytes. | |||
| * | |||
| * \param[out] buf Buffer to receive the data. | |||
| @@ -213,25 +201,25 @@ class SdSpiLib { | |||
| } | |||
| } | |||
| /** \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 | |||
| * \brief Software SPI class for access to SD and SDHC flash memory cards. | |||
| */ | |||
| template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||
| #if USE_MULTIPLE_SPI_TYPES | |||
| class SdSpiSoft : public SdSpiBase { | |||
| #else | |||
| class SdSpiSoft { | |||
| #endif | |||
| public: | |||
| /** | |||
| * initialize SPI pins | |||
| */ | |||
| void begin() {m_spi.begin();} | |||
| void begin() { | |||
| m_spi.begin(); | |||
| } | |||
| /** | |||
| * Initialize hardware SPI - dummy for soft SPI | |||
| * \param[in] divisor SCK divisor - ignored. | |||
| @@ -241,14 +229,16 @@ class SdSpiSoft { | |||
| * | |||
| * \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) { | |||
| for (size_t i = 0; i < n; i++) { | |||
| buf[i] = receive(); | |||
| @@ -259,7 +249,9 @@ class SdSpiSoft { | |||
| * | |||
| * \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. | |||
| * | |||
| * \param[in] buf Buffer for data to be sent. | |||
| @@ -271,22 +263,27 @@ class SdSpiSoft { | |||
| } | |||
| } | |||
| /** \return false - no SPI transactions */ | |||
| bool useSpiTransactions() {return false;} | |||
| bool useSpiTransactions() { | |||
| return false; | |||
| } | |||
| private: | |||
| 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. */ | |||
| typedef SdSpiLib SpiDefault_t; | |||
| #elif USE_SOFTWARE_SPI | |||
| #elif SD_SPI_CONFIGURATION == 2 | |||
| /** Default is software SPI. */ | |||
| 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. | |||
| #ifdef __AVR__ | |||
| @@ -327,7 +324,9 @@ inline uint8_t SdSpi::receive() { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| if (n-- == 0) return 0; | |||
| if (n-- == 0) { | |||
| return 0; | |||
| } | |||
| SPDR = 0XFF; | |||
| for (size_t i = 0; i < n; i++) { | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| @@ -346,7 +345,9 @@ inline void SdSpi::send(uint8_t data) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| inline void SdSpi::send(const uint8_t* buf , size_t n) { | |||
| if (n == 0) return; | |||
| if (n == 0) { | |||
| return; | |||
| } | |||
| SPDR = buf[0]; | |||
| if (n > 1) { | |||
| uint8_t b = buf[1]; | |||
| @@ -354,7 +355,9 @@ inline void SdSpi::send(const uint8_t* buf , size_t n) { | |||
| while (1) { | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| SPDR = b; | |||
| if (i == n) break; | |||
| if (i == n) { | |||
| break; | |||
| } | |||
| b = buf[i++]; | |||
| } | |||
| } | |||
| @@ -19,9 +19,9 @@ | |||
| */ | |||
| #include "SdSpiCard.h" | |||
| #include "SdSpi.h" | |||
| #if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) | |||
| #if ENABLE_SPI_TRANSACTION | |||
| #include <SPI.h> | |||
| #endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) | |||
| #endif // ENABLE_SPI_TRANSACTION | |||
| // debug trace macro | |||
| #define SD_TRACE(m, b) | |||
| // #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); | |||
| @@ -35,7 +35,9 @@ static uint8_t CRC7(const uint8_t* data, uint8_t n) { | |||
| uint8_t d = data[i]; | |||
| for (uint8_t j = 0; j < 8; j++) { | |||
| crc <<= 1; | |||
| if ((d & 0x80) ^ (crc & 0x80)) crc ^= 0x09; | |||
| if ((d & 0x80) ^ (crc & 0x80)) { | |||
| crc ^= 0x09; | |||
| } | |||
| d <<= 1; | |||
| } | |||
| } | |||
| @@ -131,7 +133,9 @@ bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
| spiInit(m_sckDivisor); | |||
| // 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 | |||
| while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { | |||
| @@ -152,7 +156,9 @@ bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
| type(SD_CARD_TYPE_SD1); | |||
| 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) { | |||
| type(SD_CARD_TYPE_SD2); | |||
| break; | |||
| @@ -178,15 +184,19 @@ bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
| error(SD_CARD_ERROR_CMD58); | |||
| 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. | |||
| for (uint8_t i = 0; i < 3; i++) spiReceive(); | |||
| for (uint8_t i = 0; i < 3; i++) { | |||
| spiReceive(); | |||
| } | |||
| } | |||
| chipSelectHigh(); | |||
| m_sckDivisor = sckDivisor; | |||
| return true; | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| @@ -209,20 +219,26 @@ uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { | |||
| d[5] = CRC7(d, 5); | |||
| // 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 | |||
| // send command | |||
| spiSend(cmd | 0x40); | |||
| // 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 | |||
| spiSend(cmd == CMD0 ? 0X95 : 0X87); | |||
| #endif // USE_SD_CRC | |||
| // skip stuff byte for stop read | |||
| if (cmd == CMD12) spiReceive(); | |||
| if (cmd == CMD12) { | |||
| spiReceive(); | |||
| } | |||
| // wait for response | |||
| for (uint8_t i = 0; ((m_status = spiReceive()) & 0X80) && i != 0XFF; i++) { | |||
| @@ -232,7 +248,9 @@ uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { | |||
| //------------------------------------------------------------------------------ | |||
| uint32_t SdSpiCard::cardSize() { | |||
| csd_t csd; | |||
| if (!readCSD(&csd)) return 0; | |||
| if (!readCSD(&csd)) { | |||
| return 0; | |||
| } | |||
| if (csd.v1.csd_ver == 0) { | |||
| uint8_t read_bl_len = csd.v1.read_bl_len; | |||
| uint16_t c_size = (csd.v1.c_size_high << 10) | |||
| @@ -262,13 +280,17 @@ void SdSpiCard::chipSelectHigh() { | |||
| // insure MISO goes high impedance | |||
| spiSend(0XFF); | |||
| #if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) | |||
| if (useSpiTransactions()) SPI.endTransaction(); | |||
| if (useSpiTransactions()) { | |||
| SPI.endTransaction(); | |||
| } | |||
| #endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpiCard::chipSelectLow() { | |||
| #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) | |||
| spiInit(m_sckDivisor); | |||
| digitalWrite(m_chipSelectPin, LOW); | |||
| @@ -276,7 +298,9 @@ void SdSpiCard::chipSelectLow() { | |||
| //------------------------------------------------------------------------------ | |||
| bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | |||
| csd_t csd; | |||
| if (!readCSD(&csd)) goto fail; | |||
| if (!readCSD(&csd)) { | |||
| goto fail; | |||
| } | |||
| // check for single block erase | |||
| if (!csd.v1.erase_blk_en) { | |||
| // erase size mask | |||
| @@ -292,10 +316,10 @@ bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | |||
| lastBlock <<= 9; | |||
| } | |||
| 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)) { | |||
| error(SD_CARD_ERROR_ERASE_TIMEOUT); | |||
| @@ -304,7 +328,7 @@ bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| @@ -319,7 +343,9 @@ bool SdSpiCard::isBusy() { | |||
| chipSelectLow(); | |||
| for (uint8_t i = 0; i < 8; i++) { | |||
| rtn = spiReceive() != 0XFF; | |||
| if (!rtn) break; | |||
| if (!rtn) { | |||
| break; | |||
| } | |||
| } | |||
| chipSelectHigh(); | |||
| return rtn; | |||
| @@ -328,22 +354,28 @@ bool SdSpiCard::isBusy() { | |||
| bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { | |||
| SD_TRACE("RB", blockNumber); | |||
| // 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)) { | |||
| error(SD_CARD_ERROR_CMD17); | |||
| goto fail; | |||
| } | |||
| return readData(dst, 512); | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| 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) { | |||
| if (!readData(dst)) return false; | |||
| if (!readData(dst)) { | |||
| return false; | |||
| } | |||
| } | |||
| return readStop(); | |||
| } | |||
| @@ -390,7 +422,7 @@ bool SdSpiCard::readData(uint8_t* dst, size_t count) { | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| @@ -401,12 +433,14 @@ bool SdSpiCard::readOCR(uint32_t* ocr) { | |||
| error(SD_CARD_ERROR_CMD58); | |||
| 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(); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| @@ -420,14 +454,16 @@ bool SdSpiCard::readRegister(uint8_t cmd, void* buf) { | |||
| } | |||
| return readData(dst, 16); | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdSpiCard::readStart(uint32_t 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)) { | |||
| error(SD_CARD_ERROR_CMD18); | |||
| goto fail; | |||
| @@ -435,7 +471,7 @@ bool SdSpiCard::readStart(uint32_t blockNumber) { | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| @@ -448,7 +484,7 @@ bool SdSpiCard::readStop() { | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| @@ -457,24 +493,30 @@ bool SdSpiCard::readStop() { | |||
| bool SdSpiCard::waitNotBusy(uint16_t timeoutMillis) { | |||
| uint16_t t0 = millis(); | |||
| while (spiReceive() != 0XFF) { | |||
| if (((uint16_t)millis() - t0) >= timeoutMillis) goto fail; | |||
| if (((uint16_t)millis() - t0) >= timeoutMillis) { | |||
| goto fail; | |||
| } | |||
| spiYield(); | |||
| } | |||
| return true; | |||
| fail: | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | |||
| SD_TRACE("WB", blockNumber); | |||
| // 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)) { | |||
| error(SD_CARD_ERROR_CMD24); | |||
| goto fail; | |||
| } | |||
| if (!writeData(DATA_START_BLOCK, src)) goto fail; | |||
| if (!writeData(DATA_START_BLOCK, src)) { | |||
| goto fail; | |||
| } | |||
| #define CHECK_PROGRAMMING 0 | |||
| #if CHECK_PROGRAMMING | |||
| @@ -493,15 +535,19 @@ bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| 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) { | |||
| if (!writeData(src)) return false; | |||
| if (!writeData(src)) { | |||
| return false; | |||
| } | |||
| } | |||
| return writeStop(); | |||
| } | |||
| @@ -509,12 +555,16 @@ bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { | |||
| bool SdSpiCard::writeData(const uint8_t* src) { | |||
| chipSelectLow(); | |||
| // 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(); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| error(SD_CARD_ERROR_WRITE_MULTIPLE); | |||
| chipSelectHigh(); | |||
| return false; | |||
| @@ -539,7 +589,7 @@ bool SdSpiCard::writeData(uint8_t token, const uint8_t* src) { | |||
| } | |||
| return true; | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| @@ -552,7 +602,9 @@ bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) { | |||
| goto fail; | |||
| } | |||
| // 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)) { | |||
| error(SD_CARD_ERROR_CMD25); | |||
| goto fail; | |||
| @@ -560,20 +612,24 @@ bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) { | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdSpiCard::writeStop() { | |||
| chipSelectLow(); | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||
| goto fail; | |||
| } | |||
| spiSend(STOP_TRAN_TOKEN); | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||
| goto fail; | |||
| } | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| error(SD_CARD_ERROR_STOP_TRAN); | |||
| chipSelectHigh(); | |||
| return false; | |||
| @@ -35,11 +35,11 @@ | |||
| class SdSpiCard { | |||
| public: | |||
| /** 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; | |||
| #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. */ | |||
| SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} | |||
| /** Initialize the SD card. | |||
| @@ -49,7 +49,7 @@ class SdSpiCard { | |||
| * \return true for success else false. | |||
| */ | |||
| 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. | |||
| * | |||
| @@ -67,8 +67,8 @@ class SdSpiCard { | |||
| * either 0 or 1, depends on the card vendor. The card must support | |||
| * 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); | |||
| /** Determine if card supports single block erase. | |||
| @@ -81,13 +81,19 @@ class SdSpiCard { | |||
| * Set SD 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. | |||
| */ | |||
| int errorCode() const {return m_errorCode;} | |||
| int errorCode() const { | |||
| return m_errorCode; | |||
| } | |||
| /** \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. | |||
| * | |||
| @@ -99,8 +105,8 @@ class SdSpiCard { | |||
| * | |||
| * \param[in] block Logical block to be read. | |||
| * \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); | |||
| /** | |||
| @@ -109,8 +115,8 @@ class SdSpiCard { | |||
| * \param[in] block Logical block to be read. | |||
| * \param[in] count Number of blocks to be read. | |||
| * \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); | |||
| /** | |||
| @@ -140,8 +146,8 @@ class SdSpiCard { | |||
| * | |||
| * \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); | |||
| /** Read OCR register. | |||
| @@ -157,32 +163,36 @@ class SdSpiCard { | |||
| * \note This function is used with readData() and readStop() for optimized | |||
| * 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); | |||
| /** 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(); | |||
| /** Return 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 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. | |||
| * | |||
| * \param[in] blockNumber Logical block 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); | |||
| /** | |||
| @@ -191,14 +201,14 @@ class SdSpiCard { | |||
| * \param[in] block Logical block 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. | |||
| * \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); | |||
| /** Write one data block in a multiple block write sequence | |||
| * \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); | |||
| /** Start a write multiple blocks sequence. | |||
| @@ -209,14 +219,14 @@ class SdSpiCard { | |||
| * \note This function is used with writeData() and writeStop() | |||
| * 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); | |||
| /** 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(); | |||
| @@ -232,17 +242,32 @@ class SdSpiCard { | |||
| void chipSelectHigh(); | |||
| void chipSelectLow(); | |||
| void spiYield(); | |||
| void type(uint8_t value) {m_type = value;} | |||
| void type(uint8_t value) { | |||
| m_type = value; | |||
| } | |||
| bool waitNotBusy(uint16_t timeoutMillis); | |||
| 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; | |||
| uint8_t m_chipSelectPin; | |||
| uint8_t m_errorCode; | |||
| @@ -261,7 +286,7 @@ class Sd2Card : public SdSpiCard { | |||
| * \param[in] chipSelectPin SD chip select pin. | |||
| * \param[in] sckDivisor SPI clock divisor. | |||
| * \return true for success else false. | |||
| */ | |||
| */ | |||
| bool begin(uint8_t chipSelectPin = SS, uint8_t sckDivisor = 2) { | |||
| return SdSpiCard::begin(&m_spi, chipSelectPin, sckDivisor); | |||
| } | |||
| @@ -269,13 +294,15 @@ class Sd2Card : public SdSpiCard { | |||
| * \param[in] chipSelectPin SD chip select pin. | |||
| * \param[in] sckDivisor SPI clock divisor. | |||
| * \return true for success else false. | |||
| */ | |||
| */ | |||
| bool init(uint8_t sckDivisor = 2, uint8_t chipSelectPin = SS) { | |||
| return begin(chipSelectPin, sckDivisor); | |||
| } | |||
| 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; | |||
| }; | |||
| #endif // SpiCard_h | |||
| @@ -59,20 +59,20 @@ static bool dmac_channel_transfer_done(uint32_t ul_num) { | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::begin() { | |||
| 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( | |||
| 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( | |||
| 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); | |||
| #if USE_SAM3X_DMAC | |||
| pmc_enable_periph_clk(ID_DMAC); | |||
| @@ -97,12 +97,12 @@ static void spiDmaRX(uint8_t* dst, uint16_t count) { | |||
| 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_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_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_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); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -119,11 +119,11 @@ static void spiDmaTX(const uint8_t* src, uint16_t count) { | |||
| 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_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_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_CFG_DST_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ALAP_CFG; | |||
| @@ -181,7 +181,9 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| break; | |||
| } | |||
| } | |||
| if (pSpi->SPI_SR & SPI_SR_OVRES) rtn |= 1; | |||
| if (pSpi->SPI_SR & SPI_SR_OVRES) { | |||
| rtn |= 1; | |||
| } | |||
| #else // USE_SAM3X_DMAC | |||
| for (size_t i = 0; i < n; i++) { | |||
| pSpi->SPI_TDR = 0XFF; | |||
| @@ -27,24 +27,26 @@ | |||
| //============================================================================== | |||
| /** | |||
| * \class SdVolume | |||
| * \brief SdVolume used in Quick start. Soon to be removed. | |||
| * \brief SdVolume Soon to be removed. | |||
| */ | |||
| class SdVolume : public FatVolume { | |||
| 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) { | |||
| m_sdCard = dev; | |||
| return FatVolume::init(part); | |||
| @@ -1,6 +1,6 @@ | |||
| // A simple data logger for the Arduino analog pins with optional DS1307 | |||
| // uses RTClib from https://github.com/adafruit/RTClib | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> // define FreeRam() | |||
| @@ -45,7 +45,7 @@ RTC_DS1307 RTC; // define the Real Time Clock object | |||
| //------------------------------------------------------------------------------ | |||
| // call back for file timestamps | |||
| 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 | |||
| *date = FAT_DATE(now.year(), now.month(), now.day()); | |||
| @@ -65,21 +65,24 @@ ostream& operator << (ostream& os, DateTime& dt) { | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| 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 | |||
| cout << pstr("Type any character to start\n"); | |||
| cout << F("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| while (Serial.read() >= 0) {} | |||
| #endif // WAIT_TO_START | |||
| #if USE_DS1307 | |||
| // connect to RTC | |||
| Wire.begin(); | |||
| if (!RTC.begin()) error("RTC failed"); | |||
| if (!RTC.begin()) { | |||
| error("RTC failed"); | |||
| } | |||
| // set date time callback function | |||
| SdFile::dateTimeCallback(dateTime); | |||
| @@ -88,34 +91,40 @@ void setup() { | |||
| #endif // USE_DS1307 | |||
| // 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 | |||
| char name[] = "LOGGER00.CSV"; | |||
| char name[] = "logger00.csv"; | |||
| for (uint8_t i = 0; i < 100; i++) { | |||
| name[6] = i/10 + '0'; | |||
| name[7] = i%10 + '0'; | |||
| if (sd.exists(name)) continue; | |||
| if (sd.exists(name)) { | |||
| continue; | |||
| } | |||
| logfile.open(name); | |||
| 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 | |||
| obufstream bout(buf, sizeof(buf)); | |||
| bout << pstr("millis"); | |||
| bout << F("millis"); | |||
| #if USE_DS1307 | |||
| bout << pstr(",date,time"); | |||
| bout << F(",date,time"); | |||
| #endif // USE_DS1307 | |||
| for (uint8_t i = 0; i < SENSOR_COUNT; i++) { | |||
| bout << pstr(",sens") << int(i); | |||
| bout << F(",sens") << int(i); | |||
| } | |||
| logfile << buf << endl; | |||
| @@ -157,17 +166,23 @@ void loop() { | |||
| logfile << buf << flush; | |||
| // check for error | |||
| if (!logfile) error("write data failed"); | |||
| if (!logfile) { | |||
| error("write data failed"); | |||
| } | |||
| #if ECHO_TO_SERIAL | |||
| cout << buf; | |||
| #endif // ECHO_TO_SERIAL | |||
| // 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(); | |||
| cout << pstr("Done!"); | |||
| cout << F("Done!"); | |||
| while (1); | |||
| } | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| /* | |||
| * 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() {} | |||
| @@ -6,7 +6,7 @@ ArduinoOutStream cout(Serial); | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(2000); | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * This sketch is a simple Print benchmark. | |||
| * This program is a simple Print benchmark. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SD.h> | |||
| // SD chip select pin | |||
| @@ -34,7 +34,7 @@ void loop() { | |||
| 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")); | |||
| while (Serial.read() <= 0) { | |||
| } | |||
| @@ -42,17 +42,21 @@ void loop() { | |||
| // 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")); | |||
| // do write 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) { | |||
| case 0: | |||
| Serial.println(F("Test of println(uint16_t)")); | |||
| @@ -83,8 +87,12 @@ void loop() { | |||
| error("write failed"); | |||
| } | |||
| 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; | |||
| } | |||
| file.flush(); | |||
| @@ -106,7 +114,7 @@ void loop() { | |||
| Serial.print(F(" usec, Avg Latency: ")); | |||
| Serial.print(totalLatency/N_PRINT); | |||
| Serial.println(F(" usec\n")); | |||
| SD.remove("BENCH.TXT"); | |||
| SD.remove("bench.txt"); | |||
| } | |||
| file.close(); | |||
| Serial.println(F("Done!\n")); | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 <SD.h> | |||
| @@ -1,9 +1,9 @@ | |||
| /* | |||
| * 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> | |||
| SdFat sd; | |||
| @@ -18,7 +18,7 @@ void setup() { | |||
| Serial.println("begin failed"); | |||
| 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"); | |||
| @@ -1,11 +1,11 @@ | |||
| /* | |||
| * 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> | |||
| // SD chip select pin | |||
| @@ -22,26 +22,30 @@ ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| // filename for this example | |||
| char name[] = "APPEND.TXT"; | |||
| char name[] = "append.txt"; | |||
| Serial.begin(9600); | |||
| 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) {} | |||
| delay(400); // Catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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++) { | |||
| // open stream for append | |||
| ofstream sdout(name, ios::out | ios::app); | |||
| if (!sdout) error("open failed"); | |||
| if (!sdout) { | |||
| error("open failed"); | |||
| } | |||
| // append 100 lines to the file | |||
| for (uint8_t j = 0; j < 100; j++) { | |||
| @@ -52,10 +56,14 @@ void setup() { | |||
| // close the stream | |||
| sdout.close(); | |||
| if (!sdout) error("append data failed"); | |||
| if (!sdout) { | |||
| error("append data failed"); | |||
| } | |||
| // output progress indicator | |||
| if (i % 25 == 0) cout << endl; | |||
| if (i % 25 == 0) { | |||
| cout << endl; | |||
| } | |||
| cout << '.'; | |||
| } | |||
| cout << endl << "Done" << endl; | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * 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> | |||
| // SD chip select pin | |||
| @@ -15,13 +15,15 @@ ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| void writeTestFile() { | |||
| // open the output file | |||
| ofstream sdout("AVG_TEST.TXT"); | |||
| ofstream sdout("AvgTest.txt"); | |||
| // write a series of float numbers | |||
| for (int16_t i = -1001; i < 2000; i += 13) { | |||
| sdout << 0.1 * i << endl; | |||
| } | |||
| if (!sdout) sd.errorHalt("sdout failed"); | |||
| if (!sdout) { | |||
| sd.errorHalt("sdout failed"); | |||
| } | |||
| sdout.close(); | |||
| } | |||
| @@ -32,10 +34,12 @@ void calcAverage() { | |||
| double sum = 0; // sum of input numbers | |||
| // open the input file | |||
| ifstream sdin("AVG_TEST.TXT"); | |||
| ifstream sdin("AvgTest.txt"); | |||
| // check for an open failure | |||
| if (!sdin) sd.errorHalt("sdin failed"); | |||
| if (!sdin) { | |||
| sd.errorHalt("sdin failed"); | |||
| } | |||
| // read and sum the numbers | |||
| while (sdin >> num) { | |||
| @@ -52,14 +56,16 @@ void setup() { | |||
| Serial.begin(9600); | |||
| 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) {} | |||
| delay(400); // Catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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 | |||
| writeTestFile(); | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| * 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. | |||
| */ | |||
| #include <SPI.h> | |||
| @@ -25,7 +25,7 @@ void error(char* s) { | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial){} // wait for Leonardo | |||
| while (!Serial) {} // wait for Leonardo | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| @@ -36,16 +36,17 @@ void loop() { | |||
| // discard any input | |||
| 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")); | |||
| while (Serial.read() <= 0) {} | |||
| 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. | |||
| 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) { | |||
| error("open failed"); | |||
| } | |||
| @@ -77,8 +78,12 @@ void loop() { | |||
| error("write failed"); | |||
| } | |||
| 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; | |||
| } | |||
| file.flush(); | |||
| @@ -108,8 +113,12 @@ void loop() { | |||
| error("read failed"); | |||
| } | |||
| 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; | |||
| if (buf[BUF_SIZE-1] != '\n') { | |||
| error("data check"); | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * Use of ibufsteam to parse a line and obufstream to format a line | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // create a serial output stream | |||
| @@ -14,7 +14,7 @@ void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(2000); | |||
| // initialize input string | |||
| ibufstream bin("123 456 789"); | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * Append a line to a file - demo of pathnames and streams | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| @@ -14,20 +14,22 @@ SdFat sd; | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| /* | |||
| * Append a line to LOGFILE.TXT | |||
| * Append a line to logfile.txt | |||
| */ | |||
| void logEvent(const char *msg) { | |||
| // create dir if needed | |||
| sd.mkdir("LOGS/2011/JAN"); | |||
| sd.mkdir("logs/2014/Jan"); | |||
| // 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 | |||
| sdlog << msg << endl; | |||
| // check for errors | |||
| if (!sdlog) sd.errorHalt("append failed"); | |||
| if (!sdlog) { | |||
| sd.errorHalt("append failed"); | |||
| } | |||
| sdlog.close(); | |||
| } | |||
| @@ -36,19 +38,21 @@ void setup() { | |||
| Serial.begin(9600); | |||
| 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) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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 | |||
| 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() {} | |||
| @@ -1,5 +1,5 @@ | |||
| // Demo of rewriting a line read by fgets | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // SD card chip select pin | |||
| @@ -18,18 +18,22 @@ void demoFgets() { | |||
| char line[25]; | |||
| int c; | |||
| uint32_t pos; | |||
| // open test file | |||
| SdFile rdfile("FGETS.TXT", O_RDWR); | |||
| SdFile rdfile("fgets.txt", O_RDWR); | |||
| // check for open error | |||
| if (!rdfile.isOpen()) error("demoFgets"); | |||
| if (!rdfile.isOpen()) { | |||
| error("demoFgets"); | |||
| } | |||
| // 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 | |||
| while (1) { | |||
| pos = rdfile.curPosition(); | |||
| @@ -37,56 +41,66 @@ void demoFgets() { | |||
| error("Line not found"); | |||
| } | |||
| // 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.rewind(); | |||
| // 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 | |||
| rdfile.close(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void makeTestFile() { | |||
| // 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 | |||
| if (!wrfile.isOpen()) error("MakeTestFile"); | |||
| if (!wrfile.isOpen()) { | |||
| error("MakeTestFile"); | |||
| } | |||
| // write test file | |||
| 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(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| 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) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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(); | |||
| demoFgets(); | |||
| cout << pstr("\nDone\n"); | |||
| cout << F("\nDone\n"); | |||
| } | |||
| void loop(void) {} | |||
| @@ -1,8 +1,8 @@ | |||
| /* | |||
| * Read the logfile created by the eventlog.pde example. | |||
| * Read the logfile created by the eventlog.ino example. | |||
| * Demo of pathnames and working directories | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| @@ -21,19 +21,25 @@ void setup() { | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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 | |||
| 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 | |||
| 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 | |||
| while ((c = file.get()) >= 0) cout << (char)c; | |||
| while ((c = file.get()) >= 0) { | |||
| cout << (char)c; | |||
| } | |||
| cout << "Done" << endl; | |||
| } | |||
| @@ -4,7 +4,7 @@ | |||
| * 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 | |||
| * may be in any order. | |||
| * | |||
| * | |||
| * Edit the configuration constants below to set the sample pins, sample rate, | |||
| * and other configuration values. | |||
| * | |||
| @@ -20,7 +20,7 @@ | |||
| * Data is written to the file using a SD multiple block write command. | |||
| */ | |||
| #ifdef __AVR__ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| #include "AnalogBinLogger.h" | |||
| @@ -50,7 +50,7 @@ const float SAMPLE_INTERVAL = 1.0/SAMPLE_RATE; | |||
| // | |||
| // 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 | |||
| // your sample interval. | |||
| // your sample interval. | |||
| // #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 5 // F_CPU/32 500 kHz on an Uno | |||
| @@ -72,7 +72,7 @@ uint8_t const ADC_REF = (1 << REFS0); // Vcc Reference. | |||
| const uint32_t FILE_BLOCK_COUNT = 256000; | |||
| // 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. | |||
| #define RECORD_EIGHT_BITS 0 | |||
| @@ -89,7 +89,7 @@ const uint8_t SD_CS_PIN = SS; | |||
| //------------------------------------------------------------------------------ | |||
| // 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 | |||
| //(BUFFER_BLOCK_COUNT + 1). | |||
| // | |||
| @@ -124,7 +124,7 @@ const uint8_t QUEUE_DIM = 32; // Must be a power of two! | |||
| // End of configuration constants. | |||
| //============================================================================== | |||
| // 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. | |||
| const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | |||
| @@ -145,7 +145,7 @@ SdFat sd; | |||
| SdBaseFile binFile; | |||
| char binName[13] = FILE_BASE_NAME "00.BIN"; | |||
| char binName[13] = FILE_BASE_NAME "00.bin"; | |||
| #if RECORD_EIGHT_BITS | |||
| const size_t SAMPLES_PER_BLOCK = DATA_DIM8/PIN_COUNT; | |||
| @@ -164,7 +164,9 @@ volatile uint8_t fullHead; // volatile insures non-interrupt code sees changes. | |||
| uint8_t fullTail; | |||
| // 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 | |||
| @@ -193,14 +195,16 @@ ISR(ADC_vect) { | |||
| #if RECORD_EIGHT_BITS | |||
| uint8_t d = ADCH; | |||
| #else // RECORD_EIGHT_BITS | |||
| // This will access ADCL first. | |||
| // This will access ADCL first. | |||
| uint16_t d = ADC; | |||
| #endif // RECORD_EIGHT_BITS | |||
| if (isrBufNeeded && emptyHead == emptyTail) { | |||
| // no buffers - count overrun | |||
| if (isrOver < 0XFFFF) isrOver++; | |||
| if (isrOver < 0XFFFF) { | |||
| isrOver++; | |||
| } | |||
| // Avoid missed timer error. | |||
| timerFlag = false; | |||
| return; | |||
| @@ -210,30 +214,32 @@ ISR(ADC_vect) { | |||
| ADMUX = adcmux[adcindex]; | |||
| ADCSRB = adcsrb[adcindex]; | |||
| ADCSRA = adcsra[adcindex]; | |||
| if (adcindex == 0) timerFlag = false; | |||
| if (adcindex == 0) { | |||
| timerFlag = false; | |||
| } | |||
| adcindex = adcindex < (PIN_COUNT - 1) ? adcindex + 1 : 0; | |||
| } else { | |||
| timerFlag = false; | |||
| } | |||
| // Check for buffer needed. | |||
| if (isrBufNeeded) { | |||
| if (isrBufNeeded) { | |||
| // Remove buffer from empty queue. | |||
| isrBuf = emptyQueue[emptyTail]; | |||
| emptyTail = queueNext(emptyTail); | |||
| isrBuf->count = 0; | |||
| isrBuf->overrun = isrOver; | |||
| isrBufNeeded = false; | |||
| isrBufNeeded = false; | |||
| } | |||
| // Store ADC data. | |||
| isrBuf->data[isrBuf->count++] = d; | |||
| // Check for buffer full. | |||
| 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. | |||
| fullQueue[tmp] = (block_t*)isrBuf; | |||
| fullHead = queueNext(tmp); | |||
| // Set buffer needed and clear overruns. | |||
| isrBufNeeded = true; | |||
| isrOver = 0; | |||
| @@ -243,7 +249,9 @@ ISR(ADC_vect) { | |||
| // timer1 interrupt to clear OCF1B | |||
| ISR(TIMER1_COMPB_vect) { | |||
| // Make sure ADC ISR responded to timer event. | |||
| if (timerFlag) timerError = true; | |||
| if (timerFlag) { | |||
| timerError = true; | |||
| } | |||
| timerFlag = true; | |||
| } | |||
| //============================================================================== | |||
| @@ -273,7 +281,7 @@ void fatalBlink() { | |||
| //------------------------------------------------------------------------------ | |||
| // initialize ADC and timer1 | |||
| 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. | |||
| if (ADC_REF & ~((1 << REFS0) | (1 << REFS1))) { | |||
| @@ -287,10 +295,12 @@ void adcInit(metadata_t* meta) { | |||
| #else // ADC_PRESCALER | |||
| // Allow extra cpu cycles to change ADC settings if more than one pin. | |||
| 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--) { | |||
| if (adcCycles >= (MIN_ADC_CYCLES << adps)) break; | |||
| if (adcCycles >= (MIN_ADC_CYCLES << adps)) { | |||
| break; | |||
| } | |||
| } | |||
| #endif // ADC_PRESCALER | |||
| meta->adcFrequency = F_CPU >> adps; | |||
| @@ -309,20 +319,26 @@ void adcInit(metadata_t* meta) { | |||
| } | |||
| meta->pinCount = PIN_COUNT; | |||
| meta->recordEightBits = RECORD_EIGHT_BITS; | |||
| for (int i = 0; i < PIN_COUNT; 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; | |||
| // 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; | |||
| 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. | |||
| adcsrb[i] = i == 0 ? (1 << ADTS2) | (1 << ADTS0) : 0; | |||
| #ifdef MUX5 | |||
| if (pin > 7) adcsrb[i] |= (1 << MUX5); | |||
| if (pin > 7) { | |||
| adcsrb[i] |= (1 << MUX5); | |||
| } | |||
| #endif // MUX5 | |||
| adcsra[i] = (1 << ADEN) | (1 << ADIE) | adps; | |||
| adcsra[i] |= i == 0 ? 1 << ADATE : 1 << ADSC; | |||
| @@ -360,10 +376,10 @@ void adcInit(metadata_t* meta) { | |||
| ICR1 = ticks - 1; | |||
| // compare for ADC start | |||
| OCR1B = 0; | |||
| // multiply by prescaler | |||
| ticks <<= tshift; | |||
| // Sample interval in CPU clock ticks. | |||
| meta->sampleInterval = ticks; | |||
| meta->cpuFrequency = F_CPU; | |||
| @@ -373,15 +389,15 @@ void adcInit(metadata_t* meta) { | |||
| Serial.print(' '); | |||
| Serial.print(meta->pinNumber[i], DEC); | |||
| } | |||
| Serial.println(); | |||
| Serial.println(); | |||
| Serial.print(F("ADC bits: ")); | |||
| Serial.println(meta->recordEightBits ? 8 : 10); | |||
| Serial.print(F("ADC clock kHz: ")); | |||
| Serial.println(meta->adcFrequency/1000); | |||
| Serial.print(F("Sample Rate: ")); | |||
| Serial.println(sampleRate); | |||
| Serial.println(sampleRate); | |||
| Serial.print(F("Sample interval usec: ")); | |||
| Serial.println(1000000.0/sampleRate, 4); | |||
| Serial.println(1000000.0/sampleRate, 4); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // enable ADC and timer1 interrupts | |||
| @@ -393,7 +409,7 @@ void adcStart() { | |||
| // Clear any pending interrupt. | |||
| ADCSRA |= 1 << ADIF; | |||
| // Setup for first pin. | |||
| ADMUX = adcmux[0]; | |||
| ADCSRB = adcsrb[0]; | |||
| @@ -412,7 +428,7 @@ void adcStop() { | |||
| ADCSRA = 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Convert binary file to CSV file. | |||
| // Convert binary file to csv file. | |||
| void binaryToCsv() { | |||
| uint8_t lastPct = 0; | |||
| block_t buf; | |||
| @@ -420,19 +436,21 @@ void binaryToCsv() { | |||
| uint32_t t0 = millis(); | |||
| char csvName[13]; | |||
| StdioStream csvStream; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| 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_P(&csvName[BASE_NAME_SIZE + 3], PSTR("CSV")); | |||
| strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||
| if (!csvStream.fopen(csvName, "w")) { | |||
| error("open csvStream failed"); | |||
| error("open csvStream failed"); | |||
| } | |||
| Serial.println(); | |||
| Serial.print(F("Writing: ")); | |||
| @@ -444,23 +462,29 @@ void binaryToCsv() { | |||
| csvStream.print(intervalMicros, 4); | |||
| csvStream.println(F(",usec")); | |||
| for (uint8_t i = 0; i < pm->pinCount; i++) { | |||
| if (i) csvStream.putc(','); | |||
| if (i) { | |||
| csvStream.putc(','); | |||
| } | |||
| csvStream.print(F("pin")); | |||
| csvStream.print(pm->pinNumber[i]); | |||
| } | |||
| csvStream.println(); | |||
| csvStream.println(); | |||
| uint32_t tPct = millis(); | |||
| while (!Serial.available() && binFile.read(&buf, 512) == 512) { | |||
| uint16_t i; | |||
| if (buf.count == 0) break; | |||
| if (buf.count == 0) { | |||
| break; | |||
| } | |||
| if (buf.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 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(); | |||
| } | |||
| @@ -473,9 +497,11 @@ void binaryToCsv() { | |||
| Serial.println('%'); | |||
| } | |||
| } | |||
| if (Serial.available()) break; | |||
| if (Serial.available()) { | |||
| break; | |||
| } | |||
| } | |||
| csvStream.fclose(); | |||
| csvStream.fclose(); | |||
| Serial.print(F("Done: ")); | |||
| Serial.print(0.001*(millis() - t0)); | |||
| Serial.println(F(" Seconds")); | |||
| @@ -487,7 +513,7 @@ void checkOverrun() { | |||
| block_t buf; | |||
| uint32_t bgnBlock, endBlock; | |||
| uint32_t bn = 0; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| @@ -503,7 +529,9 @@ void checkOverrun() { | |||
| } | |||
| bn++; | |||
| while (binFile.read(&buf, 512) == 512) { | |||
| if (buf.count == 0) break; | |||
| if (buf.count == 0) { | |||
| break; | |||
| } | |||
| if (buf.overrun) { | |||
| if (!headerPrinted) { | |||
| Serial.println(); | |||
| @@ -541,7 +569,9 @@ void dumpData() { | |||
| Serial.println(F("Type any character to stop")); | |||
| delay(1000); | |||
| while (!Serial.available() && binFile.read(&buf , 512) == 512) { | |||
| if (buf.count == 0) break; | |||
| if (buf.count == 0) { | |||
| break; | |||
| } | |||
| if (buf.overrun) { | |||
| Serial.print(F("OVERRUN,")); | |||
| Serial.println(buf.overrun); | |||
| @@ -563,15 +593,15 @@ void dumpData() { | |||
| uint32_t const ERASE_SIZE = 262144L; | |||
| void logData() { | |||
| uint32_t bgnBlock, endBlock; | |||
| // Allocate extra buffer space. | |||
| block_t block[BUFFER_BLOCK_COUNT]; | |||
| Serial.println(); | |||
| // Initialize ADC and timer1. | |||
| adcInit((metadata_t*) &block[0]); | |||
| // Find unused file name. | |||
| if (BASE_NAME_SIZE > 6) { | |||
| error("FILE_BASE_NAME too long"); | |||
| @@ -598,7 +628,7 @@ void logData() { | |||
| Serial.println(F("Creating new file")); | |||
| binFile.close(); | |||
| if (!binFile.createContiguous(sd.vwd(), | |||
| TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
| TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
| error("createContiguous failed"); | |||
| } | |||
| // Get the address of the file on the SD. | |||
| @@ -607,15 +637,19 @@ void logData() { | |||
| } | |||
| // Use SdFat's internal buffer. | |||
| 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. | |||
| Serial.println(F("Erasing all data")); | |||
| uint32_t bgnErase = bgnBlock; | |||
| uint32_t endErase; | |||
| while (bgnErase < endBlock) { | |||
| endErase = bgnErase + ERASE_SIZE; | |||
| if (endErase > endBlock) endErase = endBlock; | |||
| if (endErase > endBlock) { | |||
| endErase = endBlock; | |||
| } | |||
| if (!sd.card()->erase(bgnErase, endErase)) { | |||
| error("erase failed"); | |||
| } | |||
| @@ -628,15 +662,15 @@ void logData() { | |||
| // Write metadata. | |||
| if (!sd.card()->writeData((uint8_t*)&block[0])) { | |||
| error("Write metadata failed"); | |||
| } | |||
| } | |||
| // Initialize queues. | |||
| emptyHead = emptyTail = 0; | |||
| fullHead = fullTail = 0; | |||
| // Use SdFat buffer for one block. | |||
| emptyQueue[emptyHead] = (block_t*)cache; | |||
| emptyHead = queueNext(emptyHead); | |||
| // Put rest of buffers in the empty queue. | |||
| for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { | |||
| emptyQueue[emptyHead] = &block[i]; | |||
| @@ -661,7 +695,7 @@ void logData() { | |||
| if (fullHead != fullTail) { | |||
| // Get address of block to write. | |||
| block_t* pBlock = fullQueue[fullTail]; | |||
| // Write block to SD. | |||
| uint32_t usec = micros(); | |||
| if (!sd.card()->writeData((uint8_t*)pBlock)) { | |||
| @@ -669,10 +703,12 @@ void logData() { | |||
| } | |||
| usec = micros() - usec; | |||
| t1 = millis(); | |||
| if (usec > maxLatency) maxLatency = usec; | |||
| if (usec > maxLatency) { | |||
| maxLatency = usec; | |||
| } | |||
| count += pBlock->count; | |||
| // Add overruns and possibly light LED. | |||
| // Add overruns and possibly light LED. | |||
| if (pBlock->overrun) { | |||
| overruns += pBlock->overrun; | |||
| if (ERROR_LED_PIN >= 0) { | |||
| @@ -704,22 +740,24 @@ void logData() { | |||
| fullHead = queueNext(fullHead); | |||
| isrBuf = 0; | |||
| } | |||
| if (fullHead == fullTail) break; | |||
| if (fullHead == fullTail) { | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| if (!sd.card()->writeStop()) { | |||
| error("writeStop failed"); | |||
| } | |||
| // Truncate file if recording stopped early. | |||
| if (bn != FILE_BLOCK_COUNT) { | |||
| if (bn != FILE_BLOCK_COUNT) { | |||
| Serial.println(F("Truncating file")); | |||
| if (!binFile.truncate(512L * bn)) { | |||
| error("Can't truncate file"); | |||
| } | |||
| } | |||
| if (!binFile.rename(sd.vwd(), binName)) { | |||
| error("Can't rename file"); | |||
| } | |||
| error("Can't rename file"); | |||
| } | |||
| Serial.print(F("File renamed: ")); | |||
| Serial.println(binName); | |||
| Serial.print(F("Max block write usec: ")); | |||
| @@ -740,10 +778,10 @@ void setup(void) { | |||
| pinMode(ERROR_LED_PIN, OUTPUT); | |||
| } | |||
| Serial.begin(9600); | |||
| // Read the first sample pin to init the ADC. | |||
| analogRead(PIN_LIST[0]); | |||
| Serial.print(F("FreeRam: ")); | |||
| Serial.println(FreeRam()); | |||
| @@ -759,8 +797,8 @@ void loop(void) { | |||
| while (Serial.read() >= 0) {} | |||
| Serial.println(); | |||
| 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("r - record ADC data")); | |||
| @@ -773,12 +811,12 @@ void loop(void) { | |||
| do { | |||
| delay(10); | |||
| } while (Serial.read() >= 0); | |||
| if (c == 'c') { | |||
| binaryToCsv(); | |||
| } else if (c == 'd') { | |||
| dumpData(); | |||
| } else if (c == 'e') { | |||
| } else if (c == 'e') { | |||
| checkOverrun(); | |||
| } else if (c == 'r') { | |||
| logData(); | |||
| @@ -1,5 +1,5 @@ | |||
| // 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. | |||
| #include<SPI.h> | |||
| #include <SdFat.h> | |||
| @@ -10,6 +10,7 @@ const uint8_t SD_CS_PIN = SS; | |||
| SdFat sd; | |||
| SdFile file; | |||
| SdFile dirFile; | |||
| // Number of files found. | |||
| uint16_t n = 0; | |||
| @@ -21,37 +22,39 @@ const uint16_t nMax = 10; | |||
| uint16_t dirIndex[nMax]; | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| const size_t NAME_DIM = 50; | |||
| char name[NAME_DIM]; | |||
| dir_t dir; | |||
| Serial.begin(9600); | |||
| while (!Serial) {} | |||
| delay(1000); | |||
| // Print the location of some test files. | |||
| 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")); | |||
| if (!sd.begin(SD_CS_PIN)) sd.initErrorHalt(); | |||
| if (!sd.begin(SD_CS_PIN)) { | |||
| sd.initErrorHalt(); | |||
| } | |||
| Serial.print(F("Free RAM: ")); | |||
| Serial.println(FreeRam()); | |||
| 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. | |||
| if (!file.isSubDir() && !file.isHidden()) { | |||
| // Save dirIndex of file in directory. | |||
| dirIndex[n] = file.dirIndex(); | |||
| // Print the file number and name. | |||
| Serial.print(n++); | |||
| Serial.write(' '); | |||
| Serial.println(name); | |||
| file.printName(&Serial); | |||
| Serial.println(); | |||
| } | |||
| file.close(); | |||
| } | |||
| @@ -59,29 +62,33 @@ void setup() { | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| int c; | |||
| // Discard any Serial input. | |||
| while (Serial.read() > 0) {} | |||
| Serial.print(F("\r\nEnter File Number: ")); | |||
| while ((c = Serial.read()) < 0) {}; | |||
| if (!isdigit(c) || (c -= '0') >= n) { | |||
| Serial.println(F("Invald number")); | |||
| return; | |||
| } | |||
| Serial.println(c); | |||
| if (!file.open(sd.vwd(), dirIndex[c], O_READ)) { | |||
| if (!file.open(&dirFile, dirIndex[c], O_READ)) { | |||
| sd.errorHalt(F("open")); | |||
| } | |||
| Serial.println(); | |||
| char last; | |||
| // Copy up to 500 characters to Serial. | |||
| for (int i = 0; i < 500 && (c = file.read()) > 0; i++) { | |||
| Serial.write(last = (char)c); | |||
| } | |||
| // Add new line if missing from last line. | |||
| if (last != '\n') Serial.println(); | |||
| if (last != '\n') { | |||
| Serial.println(); | |||
| } | |||
| file.close(); | |||
| Serial.flush(); | |||
| delay(100); | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| /** | |||
| * 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 | |||
| * depends on the quality of your SD card and the time required to | |||
| @@ -13,7 +13,7 @@ | |||
| * | |||
| * Data is written to the file using a SD multiple block write command. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| //------------------------------------------------------------------------------ | |||
| @@ -32,7 +32,7 @@ void acquireData(data_t* data) { | |||
| void printData(Print* pr, data_t* data) { | |||
| pr->print(data->time); | |||
| for (int i = 0; i < ADC_DIM; i++) { | |||
| pr->write(','); | |||
| pr->write(','); | |||
| pr->print(data->adc[i]); | |||
| } | |||
| pr->println(); | |||
| @@ -61,7 +61,7 @@ const uint8_t SD_CS_PIN = SS; | |||
| // 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 | |||
| // overrun errors and logging continues. | |||
| const int8_t ERROR_LED_PIN = 3; | |||
| const int8_t ERROR_LED_PIN = -1; | |||
| //------------------------------------------------------------------------------ | |||
| // File definitions. | |||
| // | |||
| @@ -72,11 +72,11 @@ const int8_t ERROR_LED_PIN = 3; | |||
| const uint32_t FILE_BLOCK_COUNT = 256000; | |||
| // log file base name. Must be six characters or less. | |||
| #define FILE_BASE_NAME "DATA" | |||
| #define FILE_BASE_NAME "data" | |||
| //------------------------------------------------------------------------------ | |||
| // 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. | |||
| // | |||
| #ifndef RAMEND | |||
| @@ -102,7 +102,7 @@ const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
| // End of configuration constants. | |||
| //============================================================================== | |||
| // 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. | |||
| const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | |||
| @@ -111,7 +111,7 @@ SdFat sd; | |||
| 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. | |||
| const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | |||
| @@ -137,7 +137,9 @@ uint8_t fullHead; | |||
| uint8_t fullTail; | |||
| // 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. | |||
| #define error(msg) errorFlash(F(msg)) | |||
| @@ -159,7 +161,7 @@ void fatalBlink() { | |||
| } | |||
| } | |||
| //============================================================================== | |||
| // Convert binary file to CSV file. | |||
| // Convert binary file to csv file. | |||
| void binaryToCsv() { | |||
| uint8_t lastPct = 0; | |||
| block_t block; | |||
| @@ -167,7 +169,7 @@ void binaryToCsv() { | |||
| uint32_t syncCluster = 0; | |||
| SdFile csvFile; | |||
| char csvName[13]; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(); | |||
| Serial.println(F("No current binary file")); | |||
| @@ -176,10 +178,10 @@ void binaryToCsv() { | |||
| binFile.rewind(); | |||
| // Create a new csvFile. | |||
| 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)) { | |||
| error("open csvFile failed"); | |||
| error("open csvFile failed"); | |||
| } | |||
| Serial.println(); | |||
| Serial.print(F("Writing: ")); | |||
| @@ -189,7 +191,9 @@ void binaryToCsv() { | |||
| uint32_t tPct = millis(); | |||
| while (!Serial.available() && binFile.read(&block, 512) == 512) { | |||
| uint16_t i; | |||
| if (block.count == 0) break; | |||
| if (block.count == 0) { | |||
| break; | |||
| } | |||
| if (block.overrun) { | |||
| csvFile.print(F("OVERRUN,")); | |||
| csvFile.println(block.overrun); | |||
| @@ -210,7 +214,9 @@ void binaryToCsv() { | |||
| Serial.println('%'); | |||
| } | |||
| } | |||
| if (Serial.available()) break; | |||
| if (Serial.available()) { | |||
| break; | |||
| } | |||
| } | |||
| csvFile.close(); | |||
| Serial.print(F("Done: ")); | |||
| @@ -224,7 +230,7 @@ void checkOverrun() { | |||
| block_t block; | |||
| uint32_t bgnBlock, endBlock; | |||
| uint32_t bn = 0; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(); | |||
| Serial.println(F("No current binary file")); | |||
| @@ -237,7 +243,9 @@ void checkOverrun() { | |||
| Serial.println(); | |||
| Serial.println(F("Checking overrun errors - type any character to stop")); | |||
| while (binFile.read(&block, 512) == 512) { | |||
| if (block.count == 0) break; | |||
| if (block.count == 0) { | |||
| break; | |||
| } | |||
| if (block.overrun) { | |||
| if (!headerPrinted) { | |||
| Serial.println(); | |||
| @@ -274,7 +282,9 @@ void dumpData() { | |||
| delay(1000); | |||
| printHeader(&Serial); | |||
| while (!Serial.available() && binFile.read(&block , 512) == 512) { | |||
| if (block.count == 0) break; | |||
| if (block.count == 0) { | |||
| break; | |||
| } | |||
| if (block.overrun) { | |||
| Serial.print(F("OVERRUN,")); | |||
| Serial.println(block.overrun); | |||
| @@ -291,12 +301,12 @@ void dumpData() { | |||
| uint32_t const ERASE_SIZE = 262144L; | |||
| void logData() { | |||
| uint32_t bgnBlock, endBlock; | |||
| // Allocate extra buffer space. | |||
| block_t block[BUFFER_BLOCK_COUNT]; | |||
| block_t* curBlock = 0; | |||
| Serial.println(); | |||
| // Find unused file name. | |||
| if (BASE_NAME_SIZE > 6) { | |||
| error("FILE_BASE_NAME too long"); | |||
| @@ -323,7 +333,7 @@ void logData() { | |||
| Serial.println(F("Creating new file")); | |||
| binFile.close(); | |||
| if (!binFile.createContiguous(sd.vwd(), | |||
| TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
| TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
| error("createContiguous failed"); | |||
| } | |||
| // Get the address of the file on the SD. | |||
| @@ -332,15 +342,19 @@ void logData() { | |||
| } | |||
| // Use SdFat's internal buffer. | |||
| 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. | |||
| Serial.println(F("Erasing all data")); | |||
| uint32_t bgnErase = bgnBlock; | |||
| uint32_t endErase; | |||
| while (bgnErase < endBlock) { | |||
| endErase = bgnErase + ERASE_SIZE; | |||
| if (endErase > endBlock) endErase = endBlock; | |||
| if (endErase > endBlock) { | |||
| endErase = endBlock; | |||
| } | |||
| if (!sd.card()->erase(bgnErase, endErase)) { | |||
| error("erase failed"); | |||
| } | |||
| @@ -353,11 +367,11 @@ void logData() { | |||
| // Initialize queues. | |||
| emptyHead = emptyTail = 0; | |||
| fullHead = fullTail = 0; | |||
| // Use SdFat buffer for one block. | |||
| emptyQueue[emptyHead] = (block_t*)cache; | |||
| emptyHead = queueNext(emptyHead); | |||
| // Put rest of buffers in the empty queue. | |||
| for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { | |||
| emptyQueue[emptyHead] = &block[i]; | |||
| @@ -382,15 +396,17 @@ void logData() { | |||
| while (1) { | |||
| // Time for next data record. | |||
| logTime += LOG_INTERVAL_USEC; | |||
| if (Serial.available()) closeFile = true; | |||
| if (Serial.available()) { | |||
| closeFile = true; | |||
| } | |||
| if (closeFile) { | |||
| if (curBlock != 0 && curBlock->count >= 0) { | |||
| if (curBlock != 0 && curBlock->count >= 0) { | |||
| // Put buffer in full queue. | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = queueNext(fullHead); | |||
| curBlock = 0; | |||
| } | |||
| } | |||
| } else { | |||
| if (curBlock == 0 && emptyTail != emptyHead) { | |||
| curBlock = emptyQueue[emptyTail]; | |||
| @@ -402,26 +418,30 @@ void logData() { | |||
| do { | |||
| diff = logTime - micros(); | |||
| } while(diff > 0); | |||
| if (diff < -10) error("LOG_INTERVAL_USEC too small"); | |||
| if (diff < -10) { | |||
| error("LOG_INTERVAL_USEC too small"); | |||
| } | |||
| if (curBlock == 0) { | |||
| overrun++; | |||
| } else { | |||
| acquireData(&curBlock->data[curBlock->count++]); | |||
| if (curBlock->count == DATA_DIM) { | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = queueNext(fullHead); | |||
| fullHead = queueNext(fullHead); | |||
| curBlock = 0; | |||
| } | |||
| } | |||
| } | |||
| if (fullHead == fullTail) { | |||
| // Exit loop if done. | |||
| if (closeFile) break; | |||
| if (closeFile) { | |||
| break; | |||
| } | |||
| } else if (!sd.card()->isBusy()) { | |||
| // Get address of block to write. | |||
| block_t* pBlock = fullQueue[fullTail]; | |||
| fullTail = queueNext(fullTail); | |||
| fullTail = queueNext(fullTail); | |||
| // Write block to SD. | |||
| uint32_t usec = micros(); | |||
| if (!sd.card()->writeData((uint8_t*)pBlock)) { | |||
| @@ -429,10 +449,12 @@ void logData() { | |||
| } | |||
| usec = micros() - usec; | |||
| t1 = millis(); | |||
| if (usec > maxLatency) maxLatency = usec; | |||
| if (usec > maxLatency) { | |||
| maxLatency = usec; | |||
| } | |||
| count += pBlock->count; | |||
| // Add overruns and possibly light LED. | |||
| // Add overruns and possibly light LED. | |||
| if (pBlock->overrun) { | |||
| overrunTotal += pBlock->overrun; | |||
| if (ERROR_LED_PIN >= 0) { | |||
| @@ -453,15 +475,15 @@ void logData() { | |||
| error("writeStop failed"); | |||
| } | |||
| // Truncate file if recording stopped early. | |||
| if (bn != FILE_BLOCK_COUNT) { | |||
| if (bn != FILE_BLOCK_COUNT) { | |||
| Serial.println(F("Truncating file")); | |||
| if (!binFile.truncate(512L * bn)) { | |||
| error("Can't truncate file"); | |||
| } | |||
| } | |||
| if (!binFile.rename(sd.vwd(), binName)) { | |||
| error("Can't rename file"); | |||
| } | |||
| error("Can't rename file"); | |||
| } | |||
| Serial.print(F("File renamed: ")); | |||
| Serial.println(binName); | |||
| Serial.print(F("Max block write usec: ")); | |||
| @@ -483,12 +505,14 @@ void setup(void) { | |||
| } | |||
| Serial.begin(9600); | |||
| while (!Serial) {} | |||
| Serial.print(F("FreeRam: ")); | |||
| Serial.println(FreeRam()); | |||
| Serial.print(F("Records/block: ")); | |||
| 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. | |||
| if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||
| sd.initErrorPrint(); | |||
| @@ -501,19 +525,19 @@ void loop(void) { | |||
| while (Serial.read() >= 0) {} | |||
| Serial.println(); | |||
| 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("r - record data")); | |||
| while(!Serial.available()) {} | |||
| char c = tolower(Serial.read()); | |||
| // Discard extra Serial data. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.read() >= 0); | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, LOW); | |||
| } | |||
| @@ -521,7 +545,7 @@ void loop(void) { | |||
| binaryToCsv(); | |||
| } else if (c == 'd') { | |||
| dumpData(); | |||
| } else if (c == 'e') { | |||
| } else if (c == 'e') { | |||
| checkOverrun(); | |||
| } else if (c == 'r') { | |||
| logData(); | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * Print size, modify date/time, and name for all files in root. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| @@ -16,17 +16,20 @@ void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(1000); | |||
| Serial.println(); | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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 | |||
| while (file.openNext(sd.vwd(), O_READ)) { | |||
| file.printFileSize(&Serial); | |||
| Serial.write(' '); | |||
| file.printModifyDateTime(&Serial); | |||
| Serial.write(' '); | |||
| Serial.write(' '); | |||
| file.printName(&Serial); | |||
| if (file.isDir()) { | |||
| // Indicate a directory. | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * This sketch is a simple Print benchmark. | |||
| * This program is a simple Print benchmark. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| @@ -38,24 +38,26 @@ void loop() { | |||
| while (Serial.read() >= 0) { | |||
| } | |||
| // 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) { | |||
| } | |||
| 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. | |||
| // 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 | |||
| for (int test = 0; test < 6; test++) { | |||
| char fileName[13] = "BENCH0.TXT"; | |||
| char fileName[13] = "bench0.txt"; | |||
| fileName[5] = '0' + test; | |||
| // open or create file - truncate existing file. | |||
| if (!file.open(fileName, O_CREAT | O_TRUNC | O_RDWR)) { | |||
| @@ -66,29 +68,29 @@ void loop() { | |||
| totalLatency = 0; | |||
| switch(test) { | |||
| case 0: | |||
| cout << pstr("Test of println(uint16_t)\n"); | |||
| cout << F("Test of println(uint16_t)\n"); | |||
| break; | |||
| case 1: | |||
| cout << pstr("Test of printField(uint16_t, char)\n"); | |||
| cout << F("Test of printField(uint16_t, char)\n"); | |||
| break; | |||
| case 2: | |||
| cout << pstr("Test of println(uint32_t)\n"); | |||
| cout << F("Test of println(uint32_t)\n"); | |||
| break; | |||
| case 3: | |||
| cout << pstr("Test of printField(uint32_t, char)\n"); | |||
| break; | |||
| cout << F("Test of printField(uint32_t, char)\n"); | |||
| break; | |||
| case 4: | |||
| cout << pstr("Test of println(float)\n"); | |||
| break; | |||
| cout << F("Test of println(float)\n"); | |||
| break; | |||
| case 5: | |||
| cout << pstr("Test of printField(float, char)\n"); | |||
| break; | |||
| cout << F("Test of printField(float, char)\n"); | |||
| break; | |||
| } | |||
| uint32_t t = millis(); | |||
| for (uint16_t i = 0; i < N_PRINT; i++) { | |||
| uint32_t m = micros(); | |||
| @@ -104,16 +106,16 @@ void loop() { | |||
| case 2: | |||
| file.println(12345678UL + i); | |||
| break; | |||
| break; | |||
| case 3: | |||
| file.printField(12345678UL + i, '\n'); | |||
| break; | |||
| break; | |||
| case 4: | |||
| file.println((float)0.01*i); | |||
| break; | |||
| case 5: | |||
| file.printField((float)0.01*i, '\n'); | |||
| break; | |||
| @@ -122,20 +124,24 @@ void loop() { | |||
| error("write failed"); | |||
| } | |||
| 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; | |||
| } | |||
| file.close(); | |||
| t = millis() - t; | |||
| 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"); | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| // Quick hardware test. | |||
| // | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // | |||
| // Set DISABLE_CHIP_SELECT to disable a second SPI device. | |||
| @@ -27,39 +27,39 @@ ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf)); | |||
| int chipSelect; | |||
| 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() { | |||
| 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() { | |||
| Serial.begin(9600); | |||
| 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) { | |||
| 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; | |||
| @@ -67,89 +67,91 @@ void loop() { | |||
| // read any existing Serial data | |||
| while (Serial.read() >= 0) {} | |||
| if (!firstTry) cout << pstr("\nRestarting\n"); | |||
| if (!firstTry) { | |||
| cout << F("\nRestarting\n"); | |||
| } | |||
| firstTry = false; | |||
| cout << pstr("\nEnter the chip select pin number: "); | |||
| cout << F("\nEnter the chip select pin number: "); | |||
| while (!Serial.available()) {} | |||
| delay(400); // catch Due restart problem | |||
| delay(400); // catch Due restart problem | |||
| cin.readline(); | |||
| if (cin >> chipSelect) { | |||
| cout << chipSelect << endl; | |||
| } else { | |||
| cout << pstr("\nInvalid pin number\n"); | |||
| cout << F("\nInvalid pin number\n"); | |||
| return; | |||
| } | |||
| 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 { | |||
| cout << pstr("\nDisabling SPI device on pin "); | |||
| cout << F("\nDisabling SPI device on pin "); | |||
| cout << int(DISABLE_CHIP_SELECT) << endl; | |||
| pinMode(DISABLE_CHIP_SELECT, OUTPUT); | |||
| digitalWrite(DISABLE_CHIP_SELECT, HIGH); | |||
| } | |||
| if (!sd.begin(chipSelect, spiSpeed)) { | |||
| 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 << pstr(", errorData: ") << int(sd.card()->errorData()); | |||
| cout << F(", errorData: ") << int(sd.card()->errorData()); | |||
| cout << dec << noshowbase << endl; | |||
| return; | |||
| } | |||
| cout << pstr("\nCard successfully initialized.\n"); | |||
| cout << F("\nCard successfully initialized.\n"); | |||
| 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(); | |||
| return; | |||
| } | |||
| } | |||
| if (!sd.vwd()->isOpen()) { | |||
| cout << pstr("Can't open root directory.\n"); | |||
| cout << F("Can't open root directory.\n"); | |||
| reformatMsg(); | |||
| return; | |||
| } | |||
| cout << pstr("Can't determine error type\n"); | |||
| } | |||
| cout << F("Can't determine error type\n"); | |||
| return; | |||
| } | |||
| cout << pstr("\nCard successfully initialized.\n"); | |||
| cout << F("\nCard successfully initialized.\n"); | |||
| cout << endl; | |||
| uint32_t size = sd.card()->cardSize(); | |||
| if (size == 0) { | |||
| cout << pstr("Can't determine the card size.\n"); | |||
| cout << F("Can't determine the card size.\n"); | |||
| cardOrSpeed(); | |||
| return; | |||
| } | |||
| 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 << 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 << 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); | |||
| 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(); | |||
| return; | |||
| } | |||
| // read any existing Serial data | |||
| 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) {} | |||
| } | |||
| @@ -1,11 +1,11 @@ | |||
| /* | |||
| * 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. | |||
| * | |||
| * 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 | |||
| * under 2000 micros. | |||
| * | |||
| @@ -14,7 +14,7 @@ | |||
| * a few seconds to erase a 500 MB file since the card only | |||
| * marks the blocks as erased; no data transfer is required. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| @@ -57,21 +57,23 @@ void setup(void) { | |||
| void loop(void) { | |||
| while (Serial.read() >= 0) {} | |||
| // 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) {} | |||
| 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. | |||
| // 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 | |||
| sd.remove("RAW.TXT"); | |||
| sd.remove("RawWrite.txt"); | |||
| // 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"); | |||
| } | |||
| // get the location of the file's blocks | |||
| @@ -94,10 +96,10 @@ void loop(void) { | |||
| 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 | |||
| if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) { | |||
| @@ -116,13 +118,15 @@ void loop(void) { | |||
| // put block number at start of first line in block | |||
| 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' : ' '; | |||
| n /= 10; | |||
| } | |||
| // write a 512 byte block | |||
| uint32_t tw = micros(); | |||
| if (!sd.card()->writeData(pCache)) error("writeData failed"); | |||
| if (!sd.card()->writeData(pCache)) { | |||
| error("writeData failed"); | |||
| } | |||
| tw = micros() - tw; | |||
| // check for max write time | |||
| @@ -138,8 +142,7 @@ void loop(void) { | |||
| overruns++; | |||
| // advance time to reflect overrun | |||
| tNext = micros(); | |||
| } | |||
| else { | |||
| } else { | |||
| // wait for time to write next block | |||
| while(micros() < tNext); | |||
| } | |||
| @@ -148,16 +151,18 @@ void loop(void) { | |||
| t = micros() - t; | |||
| // 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) { | |||
| 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++) { | |||
| cout << over[i].block << ',' << over[i].micros << endl; | |||
| } | |||
| @@ -0,0 +1,88 @@ | |||
| /* | |||
| 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 | |||
| } | |||
| @@ -3,24 +3,24 @@ | |||
| const int chipSelect = 4; | |||
| /* | |||
| 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: | |||
| * 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 | |||
| updated 2 Dec 2010 | |||
| by Tom Igoe | |||
| modified by Bill Greiman 11 Apr 2011 | |||
| This example code is in the public domain. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| SdFat sd; | |||
| SdFile myFile; | |||
| @@ -31,11 +31,13 @@ void setup() { | |||
| Serial.println("Type any character to start"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| // Initialize SdFat or print a detailed error message and halt | |||
| // Use half speed like the native library. | |||
| // 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 | |||
| if (!myFile.open("test.txt", O_RDWR | O_CREAT | O_AT_END)) { | |||
| @@ -57,7 +59,9 @@ void setup() { | |||
| // read from the file until there's nothing else in it: | |||
| int data; | |||
| while ((data = myFile.read()) >= 0) Serial.write(data); | |||
| while ((data = myFile.read()) >= 0) { | |||
| Serial.write(data); | |||
| } | |||
| // close the file: | |||
| myFile.close(); | |||
| } | |||
| @@ -1,19 +1,19 @@ | |||
| /* | |||
| * This sketch will format an SD or SDHC card. | |||
| * This program will format an SD or SDHC card. | |||
| * Warning all data will be deleted! | |||
| * | |||
| * 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: | |||
| * | |||
| * http://www.sdcard.org/consumers/formatter/ | |||
| * | |||
| * For smaller cards this sketch uses FAT16 | |||
| * For smaller cards this program uses FAT16 | |||
| * and SDFormatter uses FAT12. | |||
| */ | |||
| // Print extra info for debug if DEBUG_PRINT is nonzero | |||
| #define DEBUG_PRINT 0 | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| #if DEBUG_PRINT | |||
| #include <SdFatUtil.h> | |||
| @@ -65,13 +65,13 @@ char noName[] = "NO NAME "; | |||
| char fat16str[] = "FAT16 "; | |||
| 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()) { | |||
| cout << pstr("SD error: ") << hex << int(card.errorCode()); | |||
| cout << F("SD error: ") << hex << int(card.errorCode()); | |||
| cout << ',' << int(card.errorData()) << dec << endl; | |||
| } | |||
| while (1); | |||
| @@ -79,19 +79,19 @@ void sdError_P(const char* str) { | |||
| //------------------------------------------------------------------------------ | |||
| #if DEBUG_PRINT | |||
| 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 << 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; | |||
| } | |||
| #endif // DEBUG_PRINT | |||
| @@ -122,7 +122,7 @@ void initSizes() { | |||
| sectorsPerCluster = 128; | |||
| } | |||
| cout << pstr("Blocks/Cluster: ") << int(sectorsPerCluster) << endl; | |||
| cout << F("Blocks/Cluster: ") << int(sectorsPerCluster) << endl; | |||
| // set fake disk geometry | |||
| sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63; | |||
| @@ -161,7 +161,9 @@ void clearFatDir(uint32_t bgn, uint32_t count) { | |||
| sdError("Clear FAT/DIR writeStart failed"); | |||
| } | |||
| for (uint32_t i = 0; i < count; i++) { | |||
| if ((i & 0XFF) == 0) cout << '.'; | |||
| if ((i & 0XFF) == 0) { | |||
| cout << '.'; | |||
| } | |||
| if (!card.writeData(cache.data)) { | |||
| sdError("Clear FAT/DIR writeData failed"); | |||
| } | |||
| @@ -193,7 +195,9 @@ void writeMbr() { | |||
| part_t* p = cache.mbr.part; | |||
| p->boot = 0; | |||
| uint16_t c = lbnToCylinder(relSector); | |||
| if (c > 1023) sdError("MBR CHS"); | |||
| if (c > 1023) { | |||
| sdError("MBR CHS"); | |||
| } | |||
| p->beginCylinderHigh = c >> 8; | |||
| p->beginCylinderLow = c & 0XFF; | |||
| p->beginHead = lbnToHead(relSector); | |||
| @@ -215,7 +219,9 @@ void writeMbr() { | |||
| } | |||
| p->firstSector = relSector; | |||
| 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 | |||
| @@ -230,12 +236,16 @@ void makeFat16() { | |||
| nc = (cardSizeBlocks - dataStart)/sectorsPerCluster; | |||
| fatSize = (nc + 2 + 255)/256; | |||
| uint32_t r = BU16 + 1 + 2 * fatSize + 32; | |||
| if (dataStart < r) continue; | |||
| if (dataStart < r) { | |||
| continue; | |||
| } | |||
| relSector = dataStart - r + BU16; | |||
| break; | |||
| } | |||
| // 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; | |||
| fatStart = relSector + reservedSectors; | |||
| partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32; | |||
| @@ -283,7 +293,7 @@ void makeFat16() { | |||
| cache.fat16[1] = 0XFFFF; | |||
| // write first block of FAT and backup for reserved clusters | |||
| if (!writeCache(fatStart) | |||
| || !writeCache(fatStart + fatSize)) { | |||
| || !writeCache(fatStart + fatSize)) { | |||
| sdError("FAT16 reserve failed"); | |||
| } | |||
| } | |||
| @@ -296,10 +306,14 @@ void makeFat32() { | |||
| nc = (cardSizeBlocks - dataStart)/sectorsPerCluster; | |||
| fatSize = (nc + 2 + 127)/128; | |||
| uint32_t r = relSector + 9 + 2 * fatSize; | |||
| if (dataStart >= r) break; | |||
| if (dataStart >= r) { | |||
| break; | |||
| } | |||
| } | |||
| // 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; | |||
| fatStart = relSector + reservedSectors; | |||
| partSize = nc * sectorsPerCluster + dataStart - relSector; | |||
| @@ -342,13 +356,13 @@ void makeFat32() { | |||
| memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType)); | |||
| // write partition boot sector and backup | |||
| if (!writeCache(relSector) | |||
| || !writeCache(relSector + 6)) { | |||
| || !writeCache(relSector + 6)) { | |||
| sdError("FAT32 write PBS failed"); | |||
| } | |||
| clearCache(true); | |||
| // write extra boot area and backup | |||
| if (!writeCache(relSector + 2) | |||
| || !writeCache(relSector + 8)) { | |||
| || !writeCache(relSector + 8)) { | |||
| sdError("FAT32 PBS ext failed"); | |||
| } | |||
| fat32_fsinfo_t* pf = &cache.fsinfo; | |||
| @@ -358,7 +372,7 @@ void makeFat32() { | |||
| pf->nextFree = 0XFFFFFFFF; | |||
| // write FSINFO sector and backup | |||
| if (!writeCache(relSector + 1) | |||
| || !writeCache(relSector + 7)) { | |||
| || !writeCache(relSector + 7)) { | |||
| sdError("FAT32 FSINFO failed"); | |||
| } | |||
| clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster); | |||
| @@ -368,7 +382,7 @@ void makeFat32() { | |||
| cache.fat32[2] = 0x0FFFFFFF; | |||
| // write first block of FAT and backup for reserved clusters | |||
| if (!writeCache(fatStart) | |||
| || !writeCache(fatStart + fatSize)) { | |||
| || !writeCache(fatStart + fatSize)) { | |||
| sdError("FAT32 reserve failed"); | |||
| } | |||
| } | |||
| @@ -376,43 +390,51 @@ void makeFat32() { | |||
| // flash erase all data | |||
| uint32_t const ERASE_SIZE = 262144L; | |||
| void eraseCard() { | |||
| cout << endl << pstr("Erasing\n"); | |||
| cout << endl << F("Erasing\n"); | |||
| uint32_t firstBlock = 0; | |||
| uint32_t lastBlock; | |||
| uint16_t n = 0; | |||
| do { | |||
| 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 << '.'; | |||
| if ((n++)%32 == 31) cout << endl; | |||
| if ((n++)%32 == 31) { | |||
| cout << endl; | |||
| } | |||
| firstBlock += ERASE_SIZE; | |||
| } while (firstBlock < cardSizeBlocks); | |||
| 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 << 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 << pstr("Erase done\n"); | |||
| cout << F("Erase done\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void formatCard() { | |||
| cout << endl; | |||
| cout << pstr("Formatting\n"); | |||
| cout << F("Formatting\n"); | |||
| initSizes(); | |||
| if (card.type() != SD_CARD_TYPE_SDHC) { | |||
| cout << pstr("FAT16\n"); | |||
| cout << F("FAT16\n"); | |||
| makeFat16(); | |||
| } else { | |||
| cout << pstr("FAT32\n"); | |||
| cout << F("FAT32\n"); | |||
| makeFat32(); | |||
| } | |||
| #if DEBUG_PRINT | |||
| debugPrint(); | |||
| #endif // DEBUG_PRINT | |||
| cout << pstr("Format done\n"); | |||
| cout << F("Format done\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| @@ -420,61 +442,63 @@ void setup() { | |||
| Serial.begin(9600); | |||
| 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()) {} | |||
| delay(400); // catch Due restart problem | |||
| c = Serial.read(); | |||
| cout << c << endl; | |||
| if (c != 'Y') { | |||
| cout << pstr("Quiting, you did not enter 'Y'.\n"); | |||
| cout << F("Quiting, you did not enter 'Y'.\n"); | |||
| return; | |||
| } | |||
| // read any existing Serial data | |||
| 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()) {} | |||
| c = Serial.read(); | |||
| cout << c << endl; | |||
| if (!strchr("EFQ", c)) { | |||
| cout << pstr("Quiting, invalid option entered.") << endl; | |||
| cout << F("Quiting, invalid option entered.") << endl; | |||
| return; | |||
| } | |||
| 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"); | |||
| } | |||
| cardSizeBlocks = card.cardSize(); | |||
| if (cardSizeBlocks == 0) sdError("cardSize"); | |||
| if (cardSizeBlocks == 0) { | |||
| sdError("cardSize"); | |||
| } | |||
| 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') { | |||
| eraseCard(); | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| * 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 <SdFat.h> | |||
| @@ -30,13 +30,13 @@ uint32_t cardSize; | |||
| uint32_t eraseSize; | |||
| //------------------------------------------------------------------------------ | |||
| // 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()) { | |||
| cout << pstr("SD errorCode: "); | |||
| cout << F("SD errorCode: "); | |||
| cout << hex << int(sd.card()->errorCode()) << endl; | |||
| cout << pstr("SD errorData: "); | |||
| cout << F("SD errorData: "); | |||
| cout << int(sd.card()->errorData()) << dec << endl; | |||
| } | |||
| } | |||
| @@ -47,17 +47,17 @@ uint8_t cidDmp() { | |||
| sdErrorMsg("readCID failed"); | |||
| return false; | |||
| } | |||
| cout << pstr("\nManufacturer ID: "); | |||
| cout << F("\nManufacturer ID: "); | |||
| 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++) { | |||
| cout << cid.pnm[i]; | |||
| } | |||
| cout << pstr("\nVersion: "); | |||
| cout << F("\nVersion: "); | |||
| 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 << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl; | |||
| cout << endl; | |||
| @@ -78,19 +78,19 @@ uint8_t csdDmp() { | |||
| eraseSingleBlock = csd.v2.erase_blk_en; | |||
| eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low; | |||
| } else { | |||
| cout << pstr("csd version error\n"); | |||
| cout << F("csd version error\n"); | |||
| return false; | |||
| } | |||
| 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) { | |||
| cout << pstr("true\n"); | |||
| cout << F("true\n"); | |||
| } else { | |||
| cout << pstr("false\n"); | |||
| cout << F("false\n"); | |||
| } | |||
| return true; | |||
| } | |||
| @@ -103,18 +103,18 @@ uint8_t partDmp() { | |||
| return false; | |||
| } | |||
| 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++) { | |||
| part_t *pt = &p->mbr.part[ip - 1]; | |||
| 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; | |||
| } | |||
| } | |||
| 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++) { | |||
| part_t *pt = &p->mbr.part[ip - 1]; | |||
| cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type); | |||
| @@ -124,22 +124,22 @@ uint8_t partDmp() { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| 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(); | |||
| cout << volFree << endl; | |||
| 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) { | |||
| 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"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -151,19 +151,19 @@ void setup() { | |||
| cout << uppercase << showbase << endl; | |||
| // 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) { | |||
| 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 { | |||
| cout << pstr("\nDisabling SPI device on pin "); | |||
| cout << F("\nDisabling SPI device on pin "); | |||
| cout << int(DISABLE_CHIP_SELECT) << endl; | |||
| pinMode(DISABLE_CHIP_SELECT, OUTPUT); | |||
| 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() { | |||
| @@ -171,10 +171,10 @@ void loop() { | |||
| while (Serial.read() >= 0) {} | |||
| // 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) {} | |||
| delay(400); // catch Due reset problem | |||
| uint32_t t = millis(); | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| @@ -183,43 +183,49 @@ void loop() { | |||
| return; | |||
| } | |||
| t = millis() - t; | |||
| cardSize = sd.card()->cardSize(); | |||
| if (cardSize == 0) { | |||
| sdErrorMsg("cardSize failed"); | |||
| 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()) { | |||
| 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; | |||
| if (!sd.card()->readOCR(&ocr)) { | |||
| 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()) { | |||
| sdErrorMsg("\nFile System initialization failed.\n"); | |||
| return; | |||
| @@ -5,7 +5,7 @@ | |||
| // | |||
| #include <SPI.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. | |||
| const uint8_t SOFT_MISO_PIN = 12; | |||
| @@ -23,24 +23,26 @@ SdFile file; | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // Wait for Leonardo | |||
| while (!Serial) {} // Wait for Leonardo | |||
| Serial.println("Type any character to start"); | |||
| 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")); | |||
| } | |||
| file.println(F("This line was printed using software SPI.")); | |||
| file.close(); | |||
| Serial.println(F("Done.")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| 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 | |||
| @@ -1,5 +1,5 @@ | |||
| // Benchmark comparing SdFile and StdioStream. | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // Define PRINT_FIELD nonzero to use printField. | |||
| @@ -17,10 +17,11 @@ StdioStream stdioFile; | |||
| float f[100]; | |||
| 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", | |||
| "float nnn.ffff, 10000 times"}; | |||
| "float nnn.ffff, 10000 times" | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| uint32_t m; | |||
| @@ -28,25 +29,27 @@ void setup() { | |||
| uint32_t stdioSize; | |||
| uint32_t printTime; | |||
| uint32_t stdioTime; | |||
| Serial.begin(9600); | |||
| while (!Serial) {} | |||
| Serial.println(F("Type any character to start")); | |||
| while (!Serial.available()); | |||
| 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++) { | |||
| f[i] = 123.0 + 0.1234*i; | |||
| } | |||
| } | |||
| for (uint8_t dataType = 0; dataType < 5; dataType++) { | |||
| for (uint8_t fileType = 0; fileType < 2; 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"); | |||
| return; | |||
| return; | |||
| } | |||
| printTime = millis(); | |||
| switch (dataType) { | |||
| @@ -55,121 +58,122 @@ void setup() { | |||
| for (uint8_t j = 0; j < 255; j++) { | |||
| printFile.println(j); | |||
| } | |||
| } | |||
| } | |||
| break; | |||
| case 1: | |||
| for (uint16_t i = 0; i < 20000; i++) { | |||
| printFile.println(i); | |||
| } | |||
| break; | |||
| case 2: | |||
| for (uint32_t i = 0; i < 20000; i++) { | |||
| printFile.println(i); | |||
| } | |||
| break; | |||
| case 3: | |||
| for (uint16_t i = 0; i < 10000; i++) { | |||
| printFile.println(i + 1000000000UL); | |||
| } | |||
| break; | |||
| case 4: | |||
| for (int j = 0; j < 100; j++) { | |||
| for (uint8_t i = 0; i < 100; i++) { | |||
| printFile.println(f[i], 4); | |||
| } | |||
| } | |||
| break; | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| printFile.sync(); | |||
| printFile.sync(); | |||
| printTime = millis() - printTime; | |||
| printFile.rewind(); | |||
| printSize = printFile.fileSize(); | |||
| printSize = printFile.fileSize(); | |||
| } else { | |||
| if (!stdioFile.fopen("STREAM.TXT", "w+")) { | |||
| if (!stdioFile.fopen("stream.txt", "w+")) { | |||
| Serial.println("fopen fail"); | |||
| return; | |||
| } | |||
| stdioTime = millis(); | |||
| switch (dataType) { | |||
| switch (dataType) { | |||
| case 0: | |||
| for (uint16_t i =0; i < 100; i++) { | |||
| for (uint8_t j = 0; j < 255; j++) { | |||
| #if PRINT_FIELD | |||
| #if PRINT_FIELD | |||
| stdioFile.printField(j, '\n'); | |||
| #else // PRINT_FIELD | |||
| #else // PRINT_FIELD | |||
| stdioFile.println(j); | |||
| #endif // PRINT_FIELD | |||
| #endif // PRINT_FIELD | |||
| } | |||
| } | |||
| } | |||
| break; | |||
| case 1: | |||
| for (uint16_t i = 0; i < 20000; i++) { | |||
| #if PRINT_FIELD | |||
| #if PRINT_FIELD | |||
| stdioFile.printField(i, '\n'); | |||
| #else // PRINT_FIELD | |||
| #else // PRINT_FIELD | |||
| stdioFile.println(i); | |||
| #endif // PRINT_FIELD | |||
| #endif // PRINT_FIELD | |||
| } | |||
| break; | |||
| case 2: | |||
| for (uint32_t i = 0; i < 20000; i++) { | |||
| #if PRINT_FIELD | |||
| #if PRINT_FIELD | |||
| stdioFile.printField(i, '\n'); | |||
| #else // PRINT_FIELD | |||
| #else // PRINT_FIELD | |||
| stdioFile.println(i); | |||
| #endif // PRINT_FIELD | |||
| #endif // PRINT_FIELD | |||
| } | |||
| break; | |||
| case 3: | |||
| for (uint16_t i = 0; i < 10000; i++) { | |||
| #if PRINT_FIELD | |||
| #if PRINT_FIELD | |||
| stdioFile.printField(i + 1000000000UL, '\n'); | |||
| #else // PRINT_FIELD | |||
| #else // PRINT_FIELD | |||
| stdioFile.println(i + 1000000000UL); | |||
| #endif // PRINT_FIELD | |||
| #endif // PRINT_FIELD | |||
| } | |||
| break; | |||
| case 4: | |||
| for (int j = 0; j < 100; j++) { | |||
| for (uint8_t i = 0; i < 100; i++) { | |||
| #if PRINT_FIELD | |||
| #if PRINT_FIELD | |||
| 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: | |||
| break; | |||
| } | |||
| stdioFile.fflush(); | |||
| stdioTime = millis() - stdioTime; | |||
| stdioSize = stdioFile.ftell(); | |||
| stdioSize = stdioFile.ftell(); | |||
| if (STDIO_LIST_COUNT) { | |||
| size_t len; | |||
| stdioFile.rewind(); | |||
| for (int i = 0; i < STDIO_LIST_COUNT; i++) { | |||
| stdioFile.fgets(buf, sizeof(buf), &len); | |||
| Serial.print(len);Serial.print(','); | |||
| Serial.print(len); | |||
| Serial.print(','); | |||
| Serial.print(buf); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| Serial.println(label[dataType]); | |||
| Serial.println(label[dataType]); | |||
| if (VERIFY_CONTENT && printSize == stdioSize) { | |||
| printFile.rewind(); | |||
| stdioFile.rewind(); | |||
| @@ -187,7 +191,7 @@ void setup() { | |||
| Serial.print(printSize); | |||
| Serial.print(" != "); | |||
| } | |||
| Serial.println(stdioSize); | |||
| Serial.println(stdioSize); | |||
| Serial.print("print millis: "); | |||
| Serial.println(printTime); | |||
| Serial.print("stdio millis: "); | |||
| @@ -195,8 +199,8 @@ void setup() { | |||
| Serial.print("ratio: "); | |||
| Serial.println((float)printTime/(float)stdioTime); | |||
| Serial.println(); | |||
| printFile.close(); | |||
| stdioFile.fclose(); | |||
| printFile.close(); | |||
| stdioFile.fclose(); | |||
| } | |||
| Serial.println("Done"); | |||
| } | |||
| @@ -15,26 +15,26 @@ void setup() { | |||
| while(!Serial) {} | |||
| Serial.println(F("Type any character to start")); | |||
| while (!Serial.available()) {} | |||
| // Initialize the SD. | |||
| if (!SD.begin(csPin)) { | |||
| Serial.println(F("begin error")); | |||
| return; | |||
| } | |||
| // 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. | |||
| file.println("12345"); | |||
| // Rewind the file and read the number with parseInt(). | |||
| file.seek(0); | |||
| int i = file.parseInt(); | |||
| Serial.print(F("parseInt: ")); | |||
| Serial.println(i); | |||
| Serial.println(i); | |||
| file.close(); | |||
| } | |||
| @@ -4,13 +4,13 @@ | |||
| #include <SPI.h> | |||
| #include <SdFat.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 | |||
| // Using my fast custom SPI | |||
| SdFat sd1; | |||
| const uint8_t SD1_CS = 53; | |||
| // SD2 is a Catalex shield on hardware SPI pins 50-52 | |||
| // Using the standard Arduino SPI library | |||
| SdFatLibSpi sd2; | |||
| @@ -33,14 +33,14 @@ const uint16_t NWRITE = FILE_SIZE/BUF_DIM; | |||
| #define initError(msg) initErrorHalt(F(msg)) | |||
| //------------------------------------------------------------------------------ | |||
| void list() { | |||
| // list current directory on both cards | |||
| // list current directory on all cards | |||
| Serial.println(F("------sd1-------")); | |||
| sd1.ls("/", LS_SIZE|LS_R); | |||
| Serial.println(F("------sd2-------")); | |||
| sd2.ls("/", LS_SIZE|LS_R); | |||
| Serial.println(F("------sd3-------")); | |||
| sd3.ls("/", LS_SIZE|LS_R); | |||
| Serial.println(F("---------------------")); | |||
| sd3.ls("/", LS_SIZE|LS_R); | |||
| Serial.println(F("---------------------")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| @@ -49,71 +49,97 @@ void setup() { | |||
| Serial.print(F("FreeRam: ")); | |||
| Serial.println(FreeRam()); | |||
| // 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")); | |||
| while (Serial.read() <= 0) {} | |||
| // disable sd2 while initializing sd1 | |||
| pinMode(SD2_CS, OUTPUT); | |||
| digitalWrite(SD2_CS, HIGH); | |||
| // initialize the first card | |||
| if (!sd1.begin(SD1_CS)) sd1.initError("sd1:"); | |||
| if (!sd1.begin(SD1_CS)) { | |||
| sd1.initError("sd1:"); | |||
| } | |||
| // initialize the second card | |||
| if (!sd2.begin(SD2_CS)) sd2.initError("sd2:"); | |||
| if (!sd2.begin(SD2_CS)) { | |||
| sd2.initError("sd2:"); | |||
| } | |||
| // 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")); | |||
| // 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")); | |||
| 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"); | |||
| 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; | |||
| 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"); | |||
| } | |||
| 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++) { | |||
| if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
| sd1.errorExit("sd1.write"); | |||
| @@ -121,24 +147,30 @@ void setup() { | |||
| } | |||
| file1.sync(); | |||
| 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; | |||
| 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"); | |||
| } | |||
| 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 | |||
| file1.rewind(); | |||
| uint32_t t = millis(); | |||
| while (1) { | |||
| 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; | |||
| file2.sync(); | |||
| @@ -148,32 +180,40 @@ void setup() { | |||
| Serial.print(t); | |||
| Serial.println(F(" millis")); | |||
| 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; | |||
| 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"); | |||
| } | |||
| 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) { | |||
| 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(); | |||
| list(); | |||
| // Verify content of file3 | |||
| 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++) { | |||
| if (file3.read(buf, sizeof(buf)) != sizeof(buf)) { | |||
| sd3.errorExit("sd3.read"); | |||
| } | |||
| 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")); | |||
| @@ -183,6 +223,6 @@ void setup() { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| 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 | |||
| @@ -1,8 +1,8 @@ | |||
| /* | |||
| * This sketch tests the dateTimeCallback() function | |||
| * This program tests the dateTimeCallback() function | |||
| * and the timestamp() function. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| SdFat sd; | |||
| @@ -52,21 +52,23 @@ void dateTime(uint16_t* date, uint16_t* time) { | |||
| */ | |||
| void printTimestamps(SdFile& f) { | |||
| 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); | |||
| cout << ' '; | |||
| f.printFatTime(d.creationTime); | |||
| cout << endl; | |||
| cout << pstr("Modify: "); | |||
| cout << F("Modify: "); | |||
| f.printFatDate(d.lastWriteDate); | |||
| cout <<' '; | |||
| f.printFatTime(d.lastWriteTime); | |||
| cout << endl; | |||
| cout << pstr("Access: "); | |||
| cout << F("Access: "); | |||
| f.printFatDate(d.lastAccessDate); | |||
| cout << endl; | |||
| } | |||
| @@ -75,24 +77,26 @@ void setup(void) { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| cout << pstr("Type any character to start\n"); | |||
| cout << F("Type any character to start\n"); | |||
| while (!Serial.available()); | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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 | |||
| 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 | |||
| 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); | |||
| // close file | |||
| @@ -112,8 +116,8 @@ void setup(void) { | |||
| SdFile::dateTimeCallback(dateTime); | |||
| // 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"); | |||
| printTimestamps(file); | |||
| @@ -130,7 +134,7 @@ void setup(void) { | |||
| // force dir update | |||
| file.sync(); | |||
| cout << pstr("\nTimes after write\n"); | |||
| cout << F("\nTimes after write\n"); | |||
| printTimestamps(file); | |||
| // close file | |||
| @@ -144,8 +148,8 @@ void setup(void) { | |||
| SdFile::dateTimeCallbackCancel(); | |||
| // 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 | |||
| if (!file.timestamp(T_CREATE, 2014, 11, 10, 1, 2, 3)) { | |||
| @@ -159,11 +163,11 @@ void setup(void) { | |||
| if (!file.timestamp(T_ACCESS, 2014, 11, 12, 7, 8, 9)) { | |||
| error("set access time failed"); | |||
| } | |||
| cout << pstr("\nTimes after timestamp() calls\n"); | |||
| cout << F("\nTimes after timestamp() calls\n"); | |||
| printTimestamps(file); | |||
| file.close(); | |||
| cout << pstr("\nDone\n"); | |||
| cout << F("\nDone\n"); | |||
| } | |||
| void loop(void){} | |||
| void loop(void) {} | |||
| @@ -9,7 +9,7 @@ SdFat sd1; | |||
| const uint8_t SD1_CS = 10; // chip select for sd1 | |||
| 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; | |||
| uint8_t buf[BUF_DIM]; | |||
| @@ -28,10 +28,12 @@ void setup() { | |||
| Serial.print(F("FreeRam: ")); | |||
| Serial.println(FreeRam()); | |||
| // 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")); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| @@ -39,22 +41,26 @@ void setup() { | |||
| // disable sd2 while initializing sd1 | |||
| pinMode(SD2_CS, OUTPUT); | |||
| digitalWrite(SD2_CS, HIGH); | |||
| // initialize the first card | |||
| if (!sd1.begin(SD1_CS)) { | |||
| 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 | |||
| if (!sd2.begin(SD2_CS)) { | |||
| 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 | |||
| Serial.println(F("------sd1 root-------")); | |||
| @@ -62,36 +68,40 @@ void setup() { | |||
| Serial.println(F("------sd2 root-------")); | |||
| 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 | |||
| Serial.println(F("------sd1 DIR1-------")); | |||
| Serial.println(F("------sd1 Dir1-------")); | |||
| sd1.ls(); | |||
| Serial.println(F("------sd2 DIR2-------")); | |||
| Serial.println(F("------sd2 Dir2-------")); | |||
| sd2.ls(); | |||
| 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 | |||
| 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; | |||
| 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"); | |||
| } | |||
| 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++) { | |||
| if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
| sd1.errorExit("sd1.write"); | |||
| @@ -99,23 +109,29 @@ void setup() { | |||
| } | |||
| // set the current working directory for open() to sd2 | |||
| 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; | |||
| 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"); | |||
| } | |||
| Serial.println(F("Copying TEST.BIN to COPY.BIN")); | |||
| Serial.println(F("Copying test.bin to copy.bin")); | |||
| // copy file1 to file2 | |||
| file1.rewind(); | |||
| uint32_t t = millis(); | |||
| while (1) { | |||
| 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; | |||
| Serial.print(F("File size: ")); | |||
| @@ -123,15 +139,26 @@ void setup() { | |||
| Serial.print(F("Copy time: ")); | |||
| Serial.print(t); | |||
| Serial.println(F(" millis")); | |||
| // close TEST.BIN | |||
| // close test.bin | |||
| 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 | |||
| file2.close(); | |||
| if (!sd2.rename("COPY.BIN", "RENAME.BIN")) { | |||
| if (!sd2.rename("copy.bin", "rename.bin")) { | |||
| 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")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * 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 <SdFatUtil.h> | |||
| @@ -44,17 +44,17 @@ void cidDmp() { | |||
| if (!sd.card()->readCID(&cid)) { | |||
| error("readCID failed"); | |||
| } | |||
| cout << pstr("\nManufacturer ID: "); | |||
| cout << F("\nManufacturer ID: "); | |||
| 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++) { | |||
| cout << cid.pnm[i]; | |||
| } | |||
| cout << pstr("\nVersion: "); | |||
| cout << F("\nVersion: "); | |||
| 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 << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl; | |||
| cout << endl; | |||
| @@ -62,9 +62,10 @@ void cidDmp() { | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| 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 | |||
| cout << uppercase << showbase << endl; | |||
| } | |||
| @@ -79,25 +80,27 @@ void loop() { | |||
| // discard any input | |||
| 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) {} | |||
| 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. | |||
| // 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(); | |||
| // 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"); | |||
| } | |||
| @@ -108,15 +111,15 @@ void loop() { | |||
| buf[BUF_SIZE-2] = '\r'; | |||
| 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 | |||
| 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++) { | |||
| file.truncate(0); | |||
| maxLatency = 0; | |||
| @@ -129,21 +132,25 @@ void loop() { | |||
| error("write failed"); | |||
| } | |||
| 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; | |||
| } | |||
| file.sync(); | |||
| t = millis() - t; | |||
| s = file.fileSize(); | |||
| cout << s/t <<',' << maxLatency << ',' << minLatency; | |||
| cout << s/t <<',' << maxLatency << ',' << minLatency; | |||
| 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 | |||
| for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) { | |||
| file.rewind(); | |||
| @@ -158,17 +165,21 @@ void loop() { | |||
| error("read failed"); | |||
| } | |||
| 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; | |||
| if (buf[BUF_SIZE-1] != '\n') { | |||
| error("data check"); | |||
| } | |||
| } | |||
| t = millis() - t; | |||
| cout << s/t <<',' << maxLatency << ',' << minLatency; | |||
| cout << s/t <<',' << maxLatency << ',' << minLatency; | |||
| cout << ',' << totalLatency/n << endl; | |||
| } | |||
| cout << endl << pstr("Done") << endl; | |||
| cout << endl << F("Done") << endl; | |||
| file.close(); | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * Demo of ArduinoInStream and ArduinoOutStream | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // create serial output stream | |||
| @@ -1,20 +1,20 @@ | |||
| /* | |||
| * Simple data logger. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // SD chip select pin. Be sure to disable any other SPI devices such as Enet. | |||
| 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 | |||
| // 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. | |||
| const uint32_t SAMPLE_INTERVAL_MS = 200; | |||
| // Log file base name. Must be six characters or less. | |||
| #define FILE_BASE_NAME "DATA" | |||
| #define FILE_BASE_NAME "Data" | |||
| //------------------------------------------------------------------------------ | |||
| // File system object. | |||
| SdFat sd; | |||
| @@ -43,14 +43,14 @@ void writeHeader() { | |||
| // Log a data record. | |||
| void logData() { | |||
| uint16_t data[ANALOG_COUNT]; | |||
| // Read all channels to avoid SD write latency between readings. | |||
| for (uint8_t i = 0; i < ANALOG_COUNT; i++) { | |||
| data[i] = analogRead(i); | |||
| } | |||
| // Write data to file. Start with log time in micros. | |||
| file.print(logTime); | |||
| // Write ADC data to CSV record. | |||
| for (uint8_t i = 0; i < ANALOG_COUNT; i++) { | |||
| file.write(','); | |||
| @@ -64,19 +64,21 @@ void logData() { | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| 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); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(1000); | |||
| Serial.println(F("Type any character to start")); | |||
| while (!Serial.available()) {} | |||
| // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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. | |||
| if (BASE_NAME_SIZE > 6) { | |||
| error("FILE_BASE_NAME too long"); | |||
| @@ -91,18 +93,20 @@ void setup() { | |||
| 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 { | |||
| delay(10); | |||
| } while (Serial.read() >= 0); | |||
| Serial.print(F("Logging to: ")); | |||
| Serial.println(fileName); | |||
| Serial.println(F("Type any character to stop")); | |||
| // Write data header. | |||
| writeHeader(); | |||
| // Start on a multiple of the sample interval. | |||
| logTime = micros()/(1000UL*SAMPLE_INTERVAL_MS) + 1; | |||
| logTime *= 1000UL*SAMPLE_INTERVAL_MS; | |||
| @@ -111,21 +115,25 @@ void setup() { | |||
| void loop() { | |||
| // Time for next record. | |||
| logTime += 1000UL*SAMPLE_INTERVAL_MS; | |||
| // Wait for log time. | |||
| int32_t diff; | |||
| do { | |||
| diff = micros() - logTime; | |||
| } 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(); | |||
| // 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()) { | |||
| // Close file and stop. | |||
| file.close(); | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * Example use of chdir(), ls(), mkdir(), and rmdir(). | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // SD card chip select pin. | |||
| const uint8_t SD_CHIP_SELECT = SS; | |||
| @@ -31,22 +31,24 @@ void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(1000); | |||
| cout << pstr("Type any character to start\n"); | |||
| cout << F("Type any character to start\n"); | |||
| // Wait for input line and discard. | |||
| cin.readline(); | |||
| // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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. | |||
| 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) { | |||
| 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 { | |||
| cout << pstr("Type: 'WIPE' to delete all SD files.\n"); | |||
| cout << F("Type: 'WIPE' to delete all SD files.\n"); | |||
| char buf[10]; | |||
| cin.readline(); | |||
| cin.get(buf, sizeof(buf)); | |||
| @@ -54,51 +56,63 @@ void setup() { | |||
| error("Invalid WIPE input"); | |||
| } | |||
| 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. | |||
| 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(); | |||
| 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(); | |||
| 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); | |||
| // 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. | |||
| 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); | |||
| // 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. | |||
| @@ -1,5 +1,5 @@ | |||
| // Demo of fgets function to read lines from a file. | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| @@ -16,16 +16,18 @@ void demoFgets() { | |||
| char line[25]; | |||
| int n; | |||
| // open test file | |||
| SdFile rdfile("FGETS.TXT", O_READ); | |||
| SdFile rdfile("fgets.txt", O_READ); | |||
| // 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 | |||
| while ((n = rdfile.fgets(line, sizeof(line))) > 0) { | |||
| if (line[n - 1] == '\n') { | |||
| @@ -38,19 +40,21 @@ void demoFgets() { | |||
| //------------------------------------------------------------------------------ | |||
| void makeTestFile() { | |||
| // 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 | |||
| if (!wrfile.isOpen()) error("MakeTestFile"); | |||
| if (!wrfile.isOpen()) { | |||
| error("MakeTestFile"); | |||
| } | |||
| // write test file | |||
| 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(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -58,18 +62,20 @@ void setup(void) { | |||
| Serial.begin(9600); | |||
| 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) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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(); | |||
| demoFgets(); | |||
| cout << pstr("\nDone\n"); | |||
| cout << F("\nDone\n"); | |||
| } | |||
| void loop(void) {} | |||
| @@ -15,7 +15,7 @@ void example(void) { | |||
| for (int row = 1; row <= max; row++) { | |||
| 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; | |||
| @@ -25,7 +25,9 @@ void example(void) { | |||
| // shows how to set and restore the fill character | |||
| void showDate(int m, int d, int y) { | |||
| // convert two digit year | |||
| if (y < 100) y += 2000; | |||
| if (y < 100) { | |||
| y += 2000; | |||
| } | |||
| // set new fill to '0' save old fill character | |||
| char old = cout.fill('0'); | |||
| @@ -39,10 +41,10 @@ void showDate(int m, int d, int y) { | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(2000); | |||
| cout << endl << "default formatting" << endl; | |||
| example(); | |||
| @@ -6,7 +6,7 @@ | |||
| * Note: This example is meant to demonstrate subtleties the standard and | |||
| * may not the best way to read a file. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| @@ -19,14 +19,14 @@ SdFat sd; | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| void makeTestFile() { | |||
| ofstream sdout("GETLINE.TXT"); | |||
| ofstream sdout("getline.txt"); | |||
| // 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(); | |||
| } | |||
| @@ -34,7 +34,7 @@ void makeTestFile() { | |||
| void testGetline() { | |||
| const int line_buffer_size = 18; | |||
| char buffer[line_buffer_size]; | |||
| ifstream sdin("GETLINE.TXT"); | |||
| ifstream sdin("getline.txt"); | |||
| int line_number = 0; | |||
| while (sdin.getline(buffer, line_buffer_size, '\n') || sdin.gcount()) { | |||
| @@ -57,13 +57,15 @@ void setup(void) { | |||
| while (!Serial) {} // wait for Leonardo | |||
| // 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) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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 | |||
| makeTestFile(); | |||
| @@ -1,8 +1,8 @@ | |||
| /* | |||
| * 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> | |||
| // SD chip select pin | |||
| @@ -14,7 +14,7 @@ SdFat sd; | |||
| // create Serial stream | |||
| ArduinoOutStream cout(Serial); | |||
| char fileName[] = "TESTFILE.CSV"; | |||
| char fileName[] = "testfile.csv"; | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt(F(s)) | |||
| @@ -25,37 +25,47 @@ void readFile() { | |||
| float f1, f2; | |||
| char text[10]; | |||
| char c1, c2, c3; // space for commas. | |||
| // open input file | |||
| ifstream sdin(fileName); | |||
| // check for open error | |||
| if (!sdin.is_open()) error("open"); | |||
| if (!sdin.is_open()) { | |||
| error("open"); | |||
| } | |||
| // read until input fails | |||
| while (1) { | |||
| // Get text field. | |||
| sdin.get(text, sizeof(text), ','); | |||
| // Assume EOF if fail. | |||
| if (sdin.fail()) break; | |||
| if (sdin.fail()) { | |||
| break; | |||
| } | |||
| // Get commas and numbers. | |||
| sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2; | |||
| // Skip CR/LF. | |||
| sdin.skipWhite(); | |||
| if (sdin.fail()) error("bad input"); | |||
| if (sdin.fail()) { | |||
| error("bad input"); | |||
| } | |||
| // error in line if not commas | |||
| if (c1 != ',' || c2 != ',' || c3 != ',') error("comma"); | |||
| if (c1 != ',' || c2 != ',' || c3 != ',') { | |||
| error("comma"); | |||
| } | |||
| // print in six character wide columns | |||
| cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl; | |||
| } | |||
| // Error in an input line if file is not at EOF. | |||
| if (!sdin.eof()) error("readFile"); | |||
| if (!sdin.eof()) { | |||
| error("readFile"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // write test file | |||
| @@ -63,39 +73,43 @@ void writeFile() { | |||
| // create or open and truncate output file | |||
| ofstream sdout(fileName); | |||
| // 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 | |||
| if (!sdout) error("writeFile"); | |||
| if (!sdout) { | |||
| error("writeFile"); | |||
| } | |||
| sdout.close(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| 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) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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 | |||
| writeFile(); | |||
| cout << endl; | |||
| // read and print test | |||
| readFile(); | |||
| readFile(); | |||
| cout << "\nDone!" << endl; | |||
| } | |||
| void loop() {} | |||
| @@ -1,8 +1,8 @@ | |||
| /* | |||
| * This sketch demonstrates use of SdFile::rename() | |||
| * This program demonstrates use of SdFile::rename() | |||
| * and SdFat::rename(). | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| @@ -21,58 +21,82 @@ void setup() { | |||
| Serial.begin(9600); | |||
| 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) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // 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 | |||
| 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. | |||
| 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 | |||
| cout << pstr("------") << endl; | |||
| cout << F("------") << endl; | |||
| 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 | |||
| cout << pstr("------") << endl; | |||
| cout << F("------") << endl; | |||
| 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) | |||
| 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 | |||
| 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 | |||
| cout << pstr("------") << endl; | |||
| cout << F("------") << endl; | |||
| sd.ls(LS_R); | |||
| cout << pstr("Done") << endl; | |||
| cout << F("Done") << endl; | |||
| } | |||
| void loop() {} | |||
| @@ -54,7 +54,9 @@ class ArduinoInStream : public ibufstream { | |||
| while (1) { | |||
| t = millis(); | |||
| while (!m_hw->available()) { | |||
| if ((millis() - t) > 10) goto done; | |||
| if ((millis() - t) > 10) { | |||
| goto done; | |||
| } | |||
| } | |||
| if (i >= (m_size - 1)) { | |||
| setstate(failbit); | |||
| @@ -63,7 +65,7 @@ class ArduinoInStream : public ibufstream { | |||
| m_line[i++] = m_hw->read(); | |||
| m_line[i] = '\0'; | |||
| } | |||
| done: | |||
| done: | |||
| init(m_line); | |||
| } | |||
| @@ -73,12 +75,16 @@ class ArduinoInStream : public ibufstream { | |||
| * \param[in] way | |||
| * \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: | |||
| char *m_line; | |||
| @@ -105,14 +111,26 @@ class ArduinoOutStream : public ostream { | |||
| * \param[in] c | |||
| */ | |||
| void putch(char c) { | |||
| if (c == '\n') m_pr->write('\r'); | |||
| if (c == '\n') { | |||
| m_pr->write('\r'); | |||
| } | |||
| 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 | |||
| private: | |||
| ArduinoOutStream() {} | |||
| @@ -81,7 +81,9 @@ inline void fastDigitalToggle(uint8_t 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__ | |||
| #include <avr/io.h> | |||
| #include <util/atomic.h> | |||
| @@ -449,7 +451,7 @@ static const uint8_t digitalPinCount = sizeof(pinMap)/sizeof(pin_map_t); | |||
| //============================================================================== | |||
| /** generate bad pin number error */ | |||
| 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 | |||
| * @param[in] pin Number of pin to be checked. | |||
| @@ -457,7 +459,7 @@ void badPinNumber(void) | |||
| static inline __attribute__((always_inline)) | |||
| void badPinCheck(uint8_t pin) { | |||
| if (!__builtin_constant_p(pin) || pin >= digitalPinCount) { | |||
| badPinNumber(); | |||
| badPinNumber(); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -502,13 +504,13 @@ bool fastDigitalRead(uint8_t pin) { | |||
| static inline __attribute__((always_inline)) | |||
| void fastDigitalToggle(uint8_t 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 | |||
| @@ -594,7 +596,7 @@ class DigitalPin { | |||
| //---------------------------------------------------------------------------- | |||
| /** set pin configuration | |||
| * @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. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| @@ -606,13 +608,17 @@ class DigitalPin { | |||
| * Set pin level high if output mode or enable 20K pull-up if input mode. | |||
| */ | |||
| 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. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| void low() {write(false);} | |||
| void low() { | |||
| write(false); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** | |||
| * Set pin mode | |||
| @@ -48,12 +48,14 @@ uint8_t const O_EXCL = 0X80; | |||
| // FatFile class static and const definitions | |||
| // 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 */ | |||
| uint8_t const LS_R = 4; | |||
| uint8_t const LS_R = 8; | |||
| // flags for timestamp | |||
| /** set the file's last access date */ | |||
| @@ -23,7 +23,7 @@ | |||
| * \file | |||
| * \brief FatFile class | |||
| */ | |||
| #include <ctype.h> | |||
| // #include <ctype.h> | |||
| #include <string.h> | |||
| #include <stddef.h> | |||
| #include <limits.h> | |||
| @@ -33,25 +33,6 @@ | |||
| #include "FatVolume.h" | |||
| 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. | |||
| #ifdef __AVR__ | |||
| #include <avr/pgmspace.h> | |||
| @@ -89,6 +70,37 @@ struct FatPos_t { | |||
| uint32_t cluster; | |||
| 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 | |||
| @@ -96,15 +108,8 @@ struct FatPos_t { | |||
| */ | |||
| class FatFile { | |||
| 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. */ | |||
| 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. | |||
| * | |||
| * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |||
| @@ -114,19 +119,33 @@ class FatFile { | |||
| */ | |||
| FatFile(const char* path, uint8_t oflag) { | |||
| m_attr = FILE_ATTR_CLOSED; | |||
| m_writeError = false; | |||
| m_error = 0; | |||
| open(path, oflag); | |||
| } | |||
| #if DESTRUCTOR_CLOSES_FILE | |||
| ~FatFile() {if(isOpen()) close();} | |||
| ~FatFile() { | |||
| if (isOpen()) { | |||
| close(); | |||
| } | |||
| } | |||
| #endif // DESTRUCTOR_CLOSES_FILE | |||
| /** \return value of writeError */ | |||
| bool getWriteError() {return m_writeError;} | |||
| bool getWriteError() { | |||
| return m_error & WRITE_ERROR; | |||
| } | |||
| /** 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 | |||
| * \param[out] pos struct to receive position | |||
| */ | |||
| @@ -135,15 +154,17 @@ class FatFile { | |||
| * \param[out] pos struct with value for new position | |||
| */ | |||
| 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 | |||
| * 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(); | |||
| /** Check for contiguous file and return its raw block range. | |||
| @@ -151,10 +172,8 @@ class FatFile { | |||
| * \param[out] bgnBlock the first 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); | |||
| /** Create and open a new contiguous file of a specified size. | |||
| @@ -166,22 +185,23 @@ class FatFile { | |||
| * \param[in] path A path with a valid DOS 8.3 file name. | |||
| * \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, | |||
| const char* path, uint32_t size); | |||
| const char* path, uint32_t size); | |||
| /** \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. */ | |||
| uint32_t curPosition() const {return m_curPosition;} | |||
| uint32_t curPosition() const { | |||
| return m_curPosition; | |||
| } | |||
| /** \return Current working directory */ | |||
| static FatFile* cwd() {return m_cwd;} | |||
| static FatFile* cwd() { | |||
| return m_cwd; | |||
| } | |||
| /** Set the date/time callback function | |||
| * | |||
| * \param[in] dateTime The user's call back function. The callback | |||
| @@ -214,20 +234,23 @@ class FatFile { | |||
| m_dateTime = dateTime; | |||
| } | |||
| /** Cancel the date/time callback function. */ | |||
| static void dateTimeCallbackCancel() {m_dateTime = 0;} | |||
| static void dateTimeCallbackCancel() { | |||
| m_dateTime = 0; | |||
| } | |||
| /** Return a 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); | |||
| /** | |||
| * \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 | |||
| * \a name in standard 8.3 short name format. | |||
| * | |||
| @@ -236,6 +259,16 @@ class FatFile { | |||
| * \return length of the 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 | |||
| * | |||
| * \param[in] path Path of the file to be tested for. | |||
| @@ -272,38 +305,100 @@ class FatFile { | |||
| * 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); | |||
| /** \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. */ | |||
| 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. */ | |||
| 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. */ | |||
| bool isFile() const {return !isDir();} | |||
| bool isFile() const { | |||
| return m_attr & FILE_ATTR_FILE; | |||
| } | |||
| /** \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. */ | |||
| bool isOpen() const {return m_attr & FILE_ATTR_IS_OPEN;} | |||
| bool isOpen() const { | |||
| return m_attr; | |||
| } | |||
| /** \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. */ | |||
| 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. */ | |||
| 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. */ | |||
| 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. | |||
| * | |||
| * \param[in] pr Print stream for list. | |||
| @@ -329,10 +424,8 @@ class FatFile { | |||
| * | |||
| * \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); | |||
| /** Open a file in the volume working directory of a FatFileSystem. | |||
| @@ -344,8 +437,8 @@ class FatFile { | |||
| * \param[in] oflag bitwise-inclusive OR of open mode flags. | |||
| * 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); | |||
| /** Open a file by index. | |||
| @@ -405,11 +498,8 @@ class FatFile { | |||
| * \note Directory files must be opened read only. Write and truncation is | |||
| * 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); | |||
| /** Open a file in the current working directory. | |||
| @@ -419,8 +509,8 @@ class FatFile { | |||
| * \param[in] oflag bitwise-inclusive OR of open mode flags. | |||
| * 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) { | |||
| return open(m_cwd, path, oflag); | |||
| @@ -436,34 +526,12 @@ class FatFile { | |||
| * \return true for success or false for failure. | |||
| */ | |||
| 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. | |||
| * | |||
| * \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); | |||
| /** Return the next available byte without consuming it. | |||
| @@ -475,8 +543,8 @@ class FatFile { | |||
| * | |||
| * \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); | |||
| /** %Print a directory date field. | |||
| @@ -530,26 +598,34 @@ class FatFile { | |||
| * | |||
| * \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); | |||
| /** Print a file's name | |||
| * | |||
| * \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); | |||
| /** Print a file's size. | |||
| * | |||
| * \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); | |||
| /** 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. | |||
| * | |||
| * \return For success read returns the next byte in the file as an int. | |||
| @@ -592,10 +668,8 @@ class FatFile { | |||
| * 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". | |||
| * | |||
| * \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(); | |||
| /** Remove a file. | |||
| @@ -609,24 +683,21 @@ class FatFile { | |||
| * 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". | |||
| * | |||
| * \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); | |||
| /** Set the file's current position to zero. */ | |||
| void rewind() {seekSet(0);} | |||
| void rewind() { | |||
| seekSet(0); | |||
| } | |||
| /** Rename a file or subdirectory. | |||
| * | |||
| * \param[in] dirFile Directory for the new path. | |||
| * \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); | |||
| /** Remove a directory file. | |||
| @@ -639,10 +710,8 @@ class FatFile { | |||
| * 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". | |||
| * | |||
| * \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(); | |||
| /** Recursively delete a directory and all contained files. | |||
| @@ -657,8 +726,8 @@ class FatFile { | |||
| * \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(). | |||
| * | |||
| * \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(); | |||
| /** Set the files position to current position + \a pos. See seekSet(). | |||
| @@ -669,16 +738,19 @@ class FatFile { | |||
| return seekSet(m_curPosition + offset); | |||
| } | |||
| /** 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. | |||
| * \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. | |||
| * | |||
| * \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); | |||
| /** Set the current working directory. | |||
| @@ -688,17 +760,17 @@ class FatFile { | |||
| * \return true for success else false. | |||
| */ | |||
| static bool setCwd(FatFile* dir) { | |||
| if (!dir->isDir()) return false; | |||
| if (!dir->isDir()) { | |||
| return false; | |||
| } | |||
| m_cwd = dir; | |||
| return true; | |||
| } | |||
| /** The sync() call causes all modified data and directory fields | |||
| * 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(); | |||
| /** Copy a file's timestamps | |||
| @@ -709,8 +781,8 @@ class FatFile { | |||
| * Modify and access timestamps may be overwritten if a date time callback | |||
| * 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); | |||
| /** Set a file's timestamps in its directory entry. | |||
| @@ -743,36 +815,40 @@ class FatFile { | |||
| * Modify and access timestamps may be overwritten if a date time callback | |||
| * 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, | |||
| 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() | |||
| * if possible. | |||
| * | |||
| * \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 | |||
| * will be maintained if it is less than or equal to \a length otherwise | |||
| * it will be set to end of 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); | |||
| /** \return FatVolume that contains this file. */ | |||
| FatVolume* volume() const {return m_vol;} | |||
| FatVolume* volume() const { | |||
| return m_vol; | |||
| } | |||
| /** Write a single byte. | |||
| * \param[in] b The byte to be written. | |||
| * \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. | |||
| * | |||
| * \note Data is moved to the cache but may not be written to the | |||
| @@ -800,7 +876,7 @@ class FatFile { | |||
| /** Entry is for a system file. */ | |||
| static const uint8_t FILE_ATTR_SYSTEM = DIR_ATT_SYSTEM; | |||
| /** 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 */ | |||
| static const uint8_t FILE_ATTR_SUBDIR = DIR_ATT_DIRECTORY; | |||
| /** A FAT12 or FAT16 root directory */ | |||
| @@ -816,19 +892,23 @@ class FatFile { | |||
| DIR_ATT_SYSTEM | DIR_ATT_DIRECTORY; | |||
| /** experimental don't use */ | |||
| bool openParent(FatFile* dir); | |||
| // private functions | |||
| bool addCluster(); | |||
| bool addDirCluster(); | |||
| 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); | |||
| dir_t* readDirCache(); | |||
| dir_t* readDirCache(bool skipReadOk = false); | |||
| bool setDirSize(); | |||
| // bits defined in m_flags | |||
| @@ -842,15 +922,18 @@ class FatFile { | |||
| // data time callback function | |||
| static void (*m_dateTime)(uint16_t* date, uint16_t* time); | |||
| // 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_error; // Error 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 | |||
| FatVolume* m_vol; // volume where file is located | |||
| uint32_t m_dirCluster; | |||
| uint32_t m_curCluster; // cluster for current file position | |||
| uint32_t m_curPosition; // current file position | |||
| 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_firstCluster; // first cluster of file | |||
| }; | |||
| @@ -17,286 +17,667 @@ | |||
| * along with the FatLib Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef DOXYGEN_SHOULD_SKIP_THIS | |||
| #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; | |||
| 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; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| 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 freeFound = 0; | |||
| uint8_t ord; | |||
| uint8_t chksum; | |||
| uint16_t freeIndex; | |||
| uint16_t curIndex; | |||
| 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; | |||
| 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) { | |||
| 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) { | |||
| 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 { | |||
| 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 '..' | |||
| 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); | |||
| 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; | |||
| } | |||
| 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; | |||
| } | |||
| } | |||
| } 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 { | |||
| 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; | |||
| fail: | |||
| *bgnIndex = foundFree ? freeIndex : curIndex; | |||
| fail: | |||
| 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; | |||
| 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; | |||
| 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; | |||
| 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; | |||
| 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; | |||
| 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 | |||
| @@ -42,70 +42,36 @@ static void printU32(print_t* pr, uint32_t v) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void FatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) { | |||
| if (!isDir()) return; | |||
| FatFile file; | |||
| 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) { | |||
| dir_t dir; | |||
| if (!dirEntry(&dir)) { | |||
| @@ -117,7 +83,7 @@ bool FatFile::printCreateDateTime(print_t* pr) { | |||
| printFatTime(pr, dir.creationTime); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -215,27 +181,18 @@ bool FatFile::printModifyDateTime(print_t* pr) { | |||
| printFatTime(pr, dir.lastWriteTime); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| 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) { | |||
| char buf[11]; | |||
| char *ptr = buf + sizeof(buf); | |||
| *--ptr = 0; | |||
| ptr = fmtDec(fileSize(), ptr); | |||
| while (ptr > buf) *--ptr = ' '; | |||
| while (ptr > buf) { | |||
| *--ptr = ' '; | |||
| } | |||
| return pr->write(buf); | |||
| } | |||
| @@ -0,0 +1,273 @@ | |||
| /* 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 | |||
| @@ -27,7 +27,7 @@ | |||
| */ | |||
| //------------------------------------------------------------------------------ | |||
| /** FatFileSystem version YYYYMMDD */ | |||
| #define FAT_LIB_VERSION 20141115 | |||
| #define FAT_LIB_VERSION 20141201 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \class FatFileSystem | |||
| @@ -38,8 +38,8 @@ class FatFileSystem : protected FatVolume { | |||
| /** | |||
| * Initialize an FatFileSystem object. | |||
| * \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) { | |||
| m_vwd = d; | |||
| @@ -57,8 +57,8 @@ class FatFileSystem : protected FatVolume { | |||
| * \param[in] set_cwd Set the current working directory to this volume's | |||
| * 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) { | |||
| vwd()->close(); | |||
| @@ -81,20 +81,28 @@ class FatFileSystem : protected FatVolume { | |||
| * \param[in] set_cwd Set the current working directory to this volume's | |||
| * 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) { | |||
| 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; | |||
| if (set_cwd) FatFile::setCwd(vwd()); | |||
| if (set_cwd) { | |||
| FatFile::setCwd(vwd()); | |||
| } | |||
| return true; | |||
| fail: | |||
| fail: | |||
| return false; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| @@ -164,8 +172,8 @@ class FatFileSystem : protected FatVolume { | |||
| * | |||
| * \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) { | |||
| FatFile sub; | |||
| @@ -176,8 +184,8 @@ class FatFileSystem : protected FatVolume { | |||
| * | |||
| * \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) { | |||
| return FatFile::remove(vwd(), path); | |||
| @@ -195,12 +203,14 @@ class FatFileSystem : protected FatVolume { | |||
| * moved and file system corruption could occur if the file is accessed by | |||
| * 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) { | |||
| 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); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| @@ -210,12 +220,14 @@ class FatFileSystem : protected FatVolume { | |||
| * | |||
| * 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) { | |||
| FatFile sub; | |||
| if (!sub.open(vwd(), path, O_READ)) return false; | |||
| if (!sub.open(vwd(), path, O_READ)) { | |||
| return false; | |||
| } | |||
| return sub.rmdir(); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| @@ -226,20 +238,33 @@ class FatFileSystem : protected FatVolume { | |||
| * \param[in] path A path with a valid 8.3 DOS name 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) { | |||
| 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 a pointer to the FatVolume object. */ | |||
| FatVolume* vol() {return this;} | |||
| FatVolume* vol() { | |||
| return this; | |||
| } | |||
| /** \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: | |||
| FatFile* m_vwd; | |||
| @@ -23,21 +23,48 @@ | |||
| */ | |||
| #ifndef FatLibConfig_h | |||
| #define FatLibConfig_h | |||
| /** Use SdFatConfig.h if nonzero */ | |||
| #define USE_SDFAT_CONFIG 1 | |||
| #if USE_SDFAT_CONFIG | |||
| #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 | |||
| #include <stdint.h> | |||
| #ifdef __AVR__ | |||
| #include <avr/io.h> | |||
| #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 | |||
| * for FAT table entries. Improves performance for large writes that | |||
| * are not a multiple of 512 bytes. | |||
| */ | |||
| #ifdef __arm__ | |||
| #define USE_SEPARATE_FAT_CACHE 1 | |||
| #else // __arm__ | |||
| @@ -43,56 +43,56 @@ uint8_t const EXTENDED_BOOT_SIG = 0X29; | |||
| * The MBR partition table has four entries. | |||
| */ | |||
| 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** High bits cylinder for first block in partition. */ | |||
| /** High bits cylinder for first block in partition. */ | |||
| 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** High bits of end cylinder */ | |||
| /** High bits of end cylinder */ | |||
| 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; | |||
| /** Logical block address of the first block in the partition. */ | |||
| /** Logical block address of the first block in the partition. */ | |||
| uint32_t firstSector; | |||
| /** Length of the partition, in blocks. */ | |||
| /** Length of the partition, in blocks. */ | |||
| uint32_t totalSectors; | |||
| }__attribute__((packed)); | |||
| } __attribute__((packed)); | |||
| /** Type name for partitionTable */ | |||
| typedef struct partitionTable part_t; | |||
| //------------------------------------------------------------------------------ | |||
| @@ -104,19 +104,19 @@ typedef struct partitionTable part_t; | |||
| * The first block of a storage device that is formatted with a MBR. | |||
| */ | |||
| struct masterBootRecord { | |||
| /** Code Area for master boot program. */ | |||
| /** Code Area for master boot program. */ | |||
| 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; | |||
| /** Usually zero but may be more boot code. */ | |||
| /** Usually zero but may be more boot code. */ | |||
| uint16_t usuallyZero; | |||
| /** Partition tables. */ | |||
| /** Partition tables. */ | |||
| part_t part[4]; | |||
| /** First MBR signature byte. Must be 0X55 */ | |||
| /** First MBR signature byte. Must be 0X55 */ | |||
| uint8_t mbrSig0; | |||
| /** Second MBR signature byte. Must be 0XAA */ | |||
| /** Second MBR signature byte. Must be 0XAA */ | |||
| uint8_t mbrSig1; | |||
| }__attribute__((packed)); | |||
| } __attribute__((packed)); | |||
| /** Type name for masterBootRecord */ | |||
| typedef struct masterBootRecord mbr_t; | |||
| //------------------------------------------------------------------------------ | |||
| @@ -127,124 +127,124 @@ typedef struct masterBootRecord mbr_t; | |||
| * | |||
| */ | |||
| 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]; | |||
| /** | |||
| * 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]; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** Sectors per track for interrupt 0x13. Not used otherwise. */ | |||
| /** Sectors per track for interrupt 0x13. Not used otherwise. */ | |||
| uint16_t sectorsPerTrack; | |||
| /** Number of heads for interrupt 0x13. Not used otherwise. */ | |||
| /** Number of heads for interrupt 0x13. Not used otherwise. */ | |||
| 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** used by Windows NT - should be zero for FAT */ | |||
| /** used by Windows NT - should be zero for FAT */ | |||
| uint8_t reserved1; | |||
| /** 0X29 if next three fields are valid */ | |||
| /** 0X29 if next three fields are valid */ | |||
| 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; | |||
| /** | |||
| * 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]; | |||
| /** | |||
| * 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]; | |||
| /** X86 boot code */ | |||
| /** X86 boot code */ | |||
| uint8_t bootCode[448]; | |||
| /** must be 0X55 */ | |||
| /** must be 0X55 */ | |||
| uint8_t bootSectorSig0; | |||
| /** must be 0XAA */ | |||
| /** must be 0XAA */ | |||
| uint8_t bootSectorSig1; | |||
| }__attribute__((packed)); | |||
| } __attribute__((packed)); | |||
| /** Type name for FAT Boot Sector */ | |||
| typedef struct fat_boot fat_boot_t; | |||
| //------------------------------------------------------------------------------ | |||
| @@ -255,150 +255,150 @@ typedef struct fat_boot fat_boot_t; | |||
| * | |||
| */ | |||
| 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]; | |||
| /** | |||
| * 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]; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * For FAT32 volumes, this field must be 0. | |||
| */ | |||
| /** | |||
| * For FAT32 volumes, this field must be 0. | |||
| */ | |||
| 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; | |||
| /** | |||
| * 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; | |||
| /** Sectors per track for interrupt 0x13. Not used otherwise. */ | |||
| /** Sectors per track for interrupt 0x13. Not used otherwise. */ | |||
| uint16_t sectorsPerTrack; | |||
| /** Number of heads for interrupt 0x13. Not used otherwise. */ | |||
| /** Number of heads for interrupt 0x13. Not used otherwise. */ | |||
| 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; | |||
| /** | |||
| * Contains the total number of sectors in the FAT32 volume. | |||
| */ | |||
| /** | |||
| * Contains the total number of sectors in the FAT32 volume. | |||
| */ | |||
| 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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]; | |||
| /** | |||
| * 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; | |||
| /** used by Windows NT - should be zero for FAT */ | |||
| /** used by Windows NT - should be zero for FAT */ | |||
| uint8_t reserved1; | |||
| /** 0X29 if next three fields are valid */ | |||
| /** 0X29 if next three fields are valid */ | |||
| 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; | |||
| /** | |||
| * 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]; | |||
| /** | |||
| * A text field with a value of FAT32. | |||
| */ | |||
| /** | |||
| * A text field with a value of FAT32. | |||
| */ | |||
| char fileSystemType[8]; | |||
| /** X86 boot code */ | |||
| /** X86 boot code */ | |||
| uint8_t bootCode[420]; | |||
| /** must be 0X55 */ | |||
| /** must be 0X55 */ | |||
| uint8_t bootSectorSig0; | |||
| /** must be 0XAA */ | |||
| /** must be 0XAA */ | |||
| uint8_t bootSectorSig1; | |||
| }__attribute__((packed)); | |||
| } __attribute__((packed)); | |||
| /** Type name for FAT32 Boot Sector */ | |||
| typedef struct fat32_boot fat32_boot_t; | |||
| //------------------------------------------------------------------------------ | |||
| @@ -413,32 +413,32 @@ uint32_t const FSINFO_STRUCT_SIG = 0x61417272; | |||
| * | |||
| */ | |||
| struct fat32_fsinfo { | |||
| /** must be 0X52, 0X52, 0X61, 0X41 */ | |||
| /** must be 0X52, 0X52, 0X61, 0X41 */ | |||
| uint32_t leadSignature; | |||
| /** must be zero */ | |||
| /** must be zero */ | |||
| uint8_t reserved1[480]; | |||
| /** must be 0X72, 0X72, 0X41, 0X61 */ | |||
| /** must be 0X72, 0X72, 0X41, 0X61 */ | |||
| 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; | |||
| /** | |||
| * 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; | |||
| /** must be zero */ | |||
| /** must be zero */ | |||
| uint8_t reserved2[12]; | |||
| /** must be 0X00, 0X00, 0X55, 0XAA */ | |||
| /** must be 0X00, 0X00, 0X55, 0XAA */ | |||
| uint8_t tailSignature[4]; | |||
| }__attribute__((packed)); | |||
| } __attribute__((packed)); | |||
| /** Type name for FAT32 FSINFO Sector */ | |||
| typedef struct fat32_fsinfo fat32_fsinfo_t; | |||
| //------------------------------------------------------------------------------ | |||
| @@ -463,80 +463,80 @@ uint32_t const FAT32MASK = 0X0FFFFFFF; | |||
| * \brief FAT short directory entry | |||
| * | |||
| * 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 | |||
| * 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): | |||
| * | |||
| * 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). | |||
| * | |||
| * | |||
| * 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. | |||
| * | |||
| * 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). | |||
| * | |||
| * | |||
| * Bits 11-15: Hours, valid value range 0-23 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). | |||
| * | |||
| * | |||
| * The valid time range is from Midnight 00:00:00 to 23:59:58. | |||
| */ | |||
| 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]; | |||
| /** 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; | |||
| /** | |||
| * 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; | |||
| /** | |||
| * 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; | |||
| /** Time file was created. */ | |||
| /** Time file was created. */ | |||
| uint16_t creationTime; | |||
| /** Date file was created. */ | |||
| /** Date file was created. */ | |||
| 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; | |||
| /** | |||
| * 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; | |||
| /** Time of last write. File creation is considered a write. */ | |||
| /** Time of last write. File creation is considered a write. */ | |||
| 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; | |||
| /** Low word of this entry's first cluster number. */ | |||
| /** Low word of this entry's first cluster number. */ | |||
| 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; | |||
| }__attribute__((packed)); | |||
| } __attribute__((packed)); | |||
| /** Type name for directoryEntry */ | |||
| typedef struct directoryEntry dir_t; | |||
| //------------------------------------------------------------------------------ | |||
| @@ -571,6 +571,12 @@ uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; | |||
| /** Mask for file/subdirectory tests */ | |||
| 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 | |||
| * \param[in] dir Pointer to a directory entry. | |||
| * | |||
| @@ -593,9 +599,9 @@ static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { | |||
| * \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) { | |||
| 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. | |||
| * | |||
| * \return true if the entry is hidden else false. | |||
| @@ -611,7 +617,7 @@ static inline uint8_t DIR_IS_HIDDEN(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; | |||
| } | |||
| /** Directory entry is system type | |||
| /** Directory entry is system type | |||
| * \param[in] dir Pointer to a directory entry. | |||
| * | |||
| * \return true if the entry is system else false. | |||
| @@ -705,39 +711,39 @@ const uint8_t LDIR_NAME3_DIM = 2; | |||
| * \brief FAT long directory entry | |||
| */ | |||
| 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; | |||
| /** 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]; | |||
| /** Attributes - must be ATTR_LONG_NAME */ | |||
| /** Attributes - must be ATTR_LONG_NAME */ | |||
| 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; | |||
| /** | |||
| * 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; | |||
| /** 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]; | |||
| /** 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; | |||
| /** 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]; | |||
| }__attribute__((packed)); | |||
| } __attribute__((packed)); | |||
| /** Type name for longDirectoryEntry */ | |||
| typedef struct longDirectoryEntry ldir_t; | |||
| /** | |||
| @@ -746,17 +752,4 @@ typedef struct longDirectoryEntry ldir_t; | |||
| * begin with an entry having this mask. | |||
| */ | |||
| 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 | |||
| @@ -17,6 +17,7 @@ | |||
| * along with the FatLib Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <string.h> | |||
| #include "FatVolume.h" | |||
| //------------------------------------------------------------------------------ | |||
| cache_t* FatCache::read(uint32_t lbn, uint8_t option) { | |||
| @@ -37,7 +38,7 @@ cache_t* FatCache::read(uint32_t lbn, uint8_t option) { | |||
| m_status |= option & CACHE_STATUS_MASK; | |||
| return &m_block; | |||
| fail: | |||
| fail: | |||
| return 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -59,7 +60,7 @@ bool FatCache::sync() { | |||
| } | |||
| return true; | |||
| fail: | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -69,14 +70,19 @@ bool FatVolume::allocateCluster(uint32_t current, uint32_t* next) { | |||
| while (1) { | |||
| find++; | |||
| // 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; | |||
| if (!fatGet(find, &f)) { | |||
| int8_t fg = fatGet(find, &f); | |||
| if (fg < 0) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (f == 0) break; | |||
| if (fg && f == 0) { | |||
| break; | |||
| } | |||
| if (find == start) { | |||
| // Can't find space checked all clusters. | |||
| DBG_FAIL_MACRO; | |||
| @@ -101,7 +107,7 @@ bool FatVolume::allocateCluster(uint32_t current, uint32_t* next) { | |||
| *next = find; | |||
| return true; | |||
| fail: | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -124,16 +130,19 @@ bool FatVolume::allocContiguous(uint32_t count, uint32_t* firstCluster) { | |||
| bgnCluster = endCluster = 2; | |||
| } | |||
| uint32_t f; | |||
| if (!fatGet(endCluster, &f)) { | |||
| int8_t fg = fatGet(endCluster, &f); | |||
| if (fg < 0) { | |||
| DBG_FAIL_MACRO; | |||
| 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 | |||
| bgnCluster = endCluster + 1; | |||
| // don't update search start if unallocated clusters before endCluster. | |||
| if (bgnCluster != endCluster) { | |||
| setStart = false; | |||
| } | |||
| } else if ((endCluster - bgnCluster + 1) == count) { | |||
| // done - found space | |||
| break; | |||
| @@ -146,7 +155,9 @@ bool FatVolume::allocContiguous(uint32_t count, uint32_t* firstCluster) { | |||
| endCluster++; | |||
| } | |||
| // remember possible next free cluster | |||
| if (setStart) m_allocSearchStart = endCluster + 1; | |||
| if (setStart) { | |||
| m_allocSearchStart = endCluster + 1; | |||
| } | |||
| // mark end of chain | |||
| if (!fatPutEOC(endCluster)) { | |||
| @@ -165,7 +176,7 @@ bool FatVolume::allocContiguous(uint32_t count, uint32_t* firstCluster) { | |||
| *firstCluster = bgnCluster; | |||
| return true; | |||
| fail: | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -173,16 +184,14 @@ uint32_t FatVolume::clusterStartBlock(uint32_t cluster) const { | |||
| 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 next; | |||
| cache_t* pc; | |||
| // 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) { | |||
| lba = m_fatStartBlock + (cluster >> 7); | |||
| @@ -191,8 +200,8 @@ bool FatVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| *value = pc->fat32[cluster & 0X7F] & FAT32MASK; | |||
| return true; | |||
| next = pc->fat32[cluster & 0X7F] & FAT32MASK; | |||
| goto done; | |||
| } | |||
| if (m_fatType == 16) { | |||
| @@ -202,8 +211,8 @@ bool FatVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| *value = pc->fat16[cluster & 0XFF]; | |||
| return true; | |||
| next = pc->fat16[cluster & 0XFF]; | |||
| goto done; | |||
| } | |||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||
| uint16_t index = cluster; | |||
| @@ -226,15 +235,21 @@ bool FatVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||
| index = 0; | |||
| } | |||
| tmp |= pc->data[index] << 8; | |||
| *value = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; | |||
| return true; | |||
| next = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; | |||
| goto done; | |||
| } else { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| done: | |||
| if (isEOC(next)) { | |||
| return 0; | |||
| } | |||
| *value = next; | |||
| return 1; | |||
| fail: | |||
| return false; | |||
| fail: | |||
| return -1; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Store a FAT entry | |||
| @@ -243,13 +258,10 @@ bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { | |||
| cache_t* pc; | |||
| // 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) { | |||
| lba = m_fatStartBlock + (cluster >> 7); | |||
| lba = m_fatStartBlock + (cluster >> 7); | |||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| @@ -307,16 +319,17 @@ bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { | |||
| goto fail; | |||
| } | |||
| fail: | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // free a cluster chain | |||
| bool FatVolume::freeChain(uint32_t cluster) { | |||
| uint32_t next; | |||
| int8_t fg; | |||
| do { | |||
| if (!fatGet(cluster, &next)) { | |||
| fg = fatGet(cluster, &next); | |||
| if (fg < 0) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| @@ -325,13 +338,15 @@ bool FatVolume::freeChain(uint32_t cluster) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (cluster < m_allocSearchStart) m_allocSearchStart = cluster; | |||
| if (cluster < m_allocSearchStart) { | |||
| m_allocSearchStart = cluster; | |||
| } | |||
| cluster = next; | |||
| } while (!isEOC(cluster)); | |||
| } while (fg); | |||
| return true; | |||
| fail: | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -344,11 +359,14 @@ int32_t FatVolume::freeClusterCount() { | |||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||
| for (unsigned i = 2; i < todo; i++) { | |||
| uint32_t c; | |||
| if (!fatGet(i, &c)) { | |||
| int8_t fg = fatGet(i, &c); | |||
| if (fg < 0) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (c == 0) free++; | |||
| if (fg && c == 0) { | |||
| free++; | |||
| } | |||
| } | |||
| } else if (m_fatType == 16 || m_fatType == 32) { | |||
| lba = m_fatStartBlock; | |||
| @@ -359,14 +377,20 @@ int32_t FatVolume::freeClusterCount() { | |||
| goto fail; | |||
| } | |||
| n = m_fatType == 16 ? 256 : 128; | |||
| if (todo < n) n = todo; | |||
| if (todo < n) { | |||
| n = todo; | |||
| } | |||
| if (m_fatType == 16) { | |||
| for (uint16_t i = 0; i < n; i++) { | |||
| if (pc->fat16[i] == 0) free++; | |||
| if (pc->fat16[i] == 0) { | |||
| free++; | |||
| } | |||
| } | |||
| } else { | |||
| for (uint16_t i = 0; i < n; i++) { | |||
| if (pc->fat32[i] == 0) free++; | |||
| if (pc->fat32[i] == 0) { | |||
| free++; | |||
| } | |||
| } | |||
| } | |||
| todo -= n; | |||
| @@ -378,7 +402,7 @@ int32_t FatVolume::freeClusterCount() { | |||
| } | |||
| return free; | |||
| fail: | |||
| fail: | |||
| return -1; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -424,11 +448,11 @@ bool FatVolume::init(uint8_t part) { | |||
| } | |||
| fbs = &(pc->fbs32); | |||
| 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_clusterBlockMask = m_blocksPerCluster - 1; | |||
| @@ -443,7 +467,7 @@ bool FatVolume::init(uint8_t part) { | |||
| } | |||
| m_blocksPerFat = fbs->sectorsPerFat16 ? | |||
| fbs->sectorsPerFat16 : fbs->sectorsPerFat32; | |||
| fbs->sectorsPerFat16 : fbs->sectorsPerFat32; | |||
| m_fatStartBlock = volumeStartBlock + fbs->reservedSectorCount; | |||
| @@ -457,13 +481,14 @@ bool FatVolume::init(uint8_t part) { | |||
| // total blocks for FAT16 or FAT32 | |||
| totalBlocks = fbs->totalSectors16 ? | |||
| fbs->totalSectors16 : fbs->totalSectors32; | |||
| fbs->totalSectors16 : fbs->totalSectors32; | |||
| // total data blocks | |||
| clusterCount = totalBlocks - (m_dataStartBlock - volumeStartBlock); | |||
| // divide by cluster size to get cluster count | |||
| clusterCount >>= m_clusterSizeShift; | |||
| m_lastCluster = clusterCount + 1; | |||
| // FAT type is determined by cluster count | |||
| if (clusterCount < 4085) { | |||
| m_fatType = 12; | |||
| @@ -479,6 +504,83 @@ bool FatVolume::init(uint8_t part) { | |||
| } | |||
| 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; | |||
| } | |||
| @@ -27,9 +27,40 @@ | |||
| #include "FatLibConfig.h" | |||
| #include "FatStructs.h" | |||
| //------------------------------------------------------------------------------ | |||
| #ifndef DOXYGEN_SHOULD_SKIP_THIS | |||
| /** 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. | |||
| class FatVolume; | |||
| @@ -38,21 +69,21 @@ class FatVolume; | |||
| * \brief Cache for an raw data block. | |||
| */ | |||
| union cache_t { | |||
| /** Used to access cached file data blocks. */ | |||
| /** Used to access cached file data blocks. */ | |||
| uint8_t data[512]; | |||
| /** Used to access cached FAT16 entries. */ | |||
| /** Used to access cached FAT16 entries. */ | |||
| uint16_t fat16[256]; | |||
| /** Used to access cached FAT32 entries. */ | |||
| /** Used to access cached FAT32 entries. */ | |||
| uint32_t fat32[128]; | |||
| /** Used to access cached directory entries. */ | |||
| /** Used to access cached directory entries. */ | |||
| dir_t dir[16]; | |||
| /** Used to access a cached Master Boot Record. */ | |||
| /** Used to access a cached Master Boot Record. */ | |||
| 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; | |||
| /** Used to access to a cached FAT32 boot sector. */ | |||
| /** Used to access to a cached FAT32 boot sector. */ | |||
| 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; | |||
| }; | |||
| //============================================================================== | |||
| @@ -68,7 +99,7 @@ class FatCache { | |||
| static const uint8_t CACHE_STATUS_MIRROR_FAT = 2; | |||
| /** Cache block status bits */ | |||
| 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. */ | |||
| static const uint8_t CACHE_OPTION_NO_READ = 4; | |||
| /** Cache block for read. */ | |||
| @@ -77,11 +108,15 @@ class FatCache { | |||
| static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; | |||
| /** Reserve cache block for write - do not read from block device. */ | |||
| 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. */ | |||
| cache_t* block() {return &m_block;} | |||
| cache_t* block() { | |||
| return &m_block; | |||
| } | |||
| /** Set current block dirty. */ | |||
| void dirty() {m_status |= CACHE_STATUS_DIRTY;} | |||
| void dirty() { | |||
| m_status |= CACHE_STATUS_DIRTY; | |||
| } | |||
| /** Initialize the cache. | |||
| * \param[in] vol FatVolume that owns this FatCache. | |||
| */ | |||
| @@ -95,7 +130,9 @@ class FatCache { | |||
| m_lbn = 0XFFFFFFFF; | |||
| } | |||
| /** \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. | |||
| * \param[in] lbn Block to read. | |||
| * \param[in] option mode for cached block. | |||
| @@ -124,29 +161,47 @@ class FatVolume { | |||
| FatVolume() : m_fatType(0) {} | |||
| /** \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. */ | |||
| 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. | |||
| * \return A pointer to the cache buffer or zero if an error occurs. | |||
| */ | |||
| cache_t* cacheClear() { | |||
| if (!cacheSync()) return 0; | |||
| if (!cacheSync()) { | |||
| return 0; | |||
| } | |||
| m_cache.invalidate(); | |||
| return m_cache.block(); | |||
| } | |||
| /** \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. */ | |||
| 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. */ | |||
| uint32_t dataStartBlock() const {return m_dataStartBlock;} | |||
| uint32_t dataStartBlock() const { | |||
| return m_dataStartBlock; | |||
| } | |||
| /** \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. */ | |||
| 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. */ | |||
| uint8_t fatType() const {return m_fatType;} | |||
| uint8_t fatType() const { | |||
| return m_fatType; | |||
| } | |||
| /** Volume free space in clusters. | |||
| * | |||
| * \return Count of free clusters for success or -1 if an error occurs. | |||
| @@ -155,12 +210,12 @@ class FatVolume { | |||
| /** Initialize a FAT volume. Try partition one first then try super | |||
| * 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. | |||
| * \param[in] part The partition to be used. Legal values for \a part are | |||
| @@ -168,24 +223,33 @@ class FatVolume { | |||
| * a MBR, Master Boot Record, or zero if the device is formatted as | |||
| * 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); | |||
| /** \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 | |||
| 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 | |||
| * | |||
| * \param[in] n cluster number. | |||
| * \param[out] v value of entry | |||
| * \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: | |||
| // Allow FatFile and FatCache access to FatVolume private functions. | |||
| @@ -212,13 +276,17 @@ class FatVolume { | |||
| return m_fatCache.read(blockNumber, | |||
| 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 // | |||
| cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { | |||
| return cacheFetchData(blockNumber, | |||
| options | FatCache::CACHE_STATUS_MIRROR_FAT); | |||
| } | |||
| bool cacheSync() {return m_cache.sync();} | |||
| bool cacheSync() { | |||
| return m_cache.sync(); | |||
| } | |||
| #endif // USE_SEPARATE_FAT_CACHE | |||
| cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { | |||
| return m_cache.read(blockNumber, options); | |||
| @@ -226,17 +294,26 @@ class FatVolume { | |||
| void cacheInvalidate() { | |||
| 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 allocContiguous(uint32_t count, uint32_t* firstCluster); | |||
| uint8_t blockOfCluster(uint32_t position) const { | |||
| return (position >> 9) & m_clusterBlockMask;} | |||
| return (position >> 9) & m_clusterBlockMask; | |||
| } | |||
| 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 fatPutEOC(uint32_t cluster) { | |||
| return fatPut(cluster, 0x0FFFFFFF); | |||
| @@ -248,10 +325,10 @@ class FatVolume { | |||
| //---------------------------------------------------------------------------- | |||
| // Virtual block I/O functions. | |||
| 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 | |||
| 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 // FatVolume | |||
| @@ -170,9 +170,13 @@ float scale10(float v, int8_t n) { | |||
| n &= 63; | |||
| for (uint8_t i = 0; n; n >>= 1, i++) { | |||
| #ifdef __AVR__ | |||
| if (n & 1) v *= pgm_read_float(&s[i]); | |||
| if (n & 1) { | |||
| v *= pgm_read_float(&s[i]); | |||
| } | |||
| #else // __AVR__ | |||
| if (n & 1) v *= s[i]; | |||
| if (n & 1) { | |||
| v *= s[i]; | |||
| } | |||
| #endif // __AVR__ | |||
| } | |||
| return v; | |||
| @@ -229,7 +233,9 @@ char* fmtDec(uint32_t n, char* p) { | |||
| //------------------------------------------------------------------------------ | |||
| char* fmtFloat(float value, char* p, uint8_t prec) { | |||
| char sign = value < 0 ? '-' : 0; | |||
| if (sign) value = -value; | |||
| if (sign) { | |||
| value = -value; | |||
| } | |||
| if (isnan(value)) { | |||
| *--p = 'n'; | |||
| @@ -249,7 +255,9 @@ char* fmtFloat(float value, char* p, uint8_t prec) { | |||
| *--p = 'o'; | |||
| return p; | |||
| } | |||
| if (prec > 9) prec = 9; | |||
| if (prec > 9) { | |||
| prec = 9; | |||
| } | |||
| value += scale10(0.5, -prec); | |||
| uint32_t whole = value; | |||
| @@ -257,11 +265,15 @@ char* fmtFloat(float value, char* p, uint8_t prec) { | |||
| char* tmp = p - prec; | |||
| uint32_t fraction = scale10(value - whole, prec); | |||
| p = fmtDec(fraction, p); | |||
| while (p > tmp) *--p = '0'; | |||
| while (p > tmp) { | |||
| *--p = '0'; | |||
| } | |||
| *--p = '.'; | |||
| } | |||
| p = fmtDec(whole, p); | |||
| if (sign) *--p = sign; | |||
| if (sign) { | |||
| *--p = sign; | |||
| } | |||
| return p; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -274,7 +286,9 @@ char* fmtFloat(float value, char* p, uint8_t prec) { | |||
| */ | |||
| char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) { | |||
| bool neg = value < 0; | |||
| if (neg) value = -value; | |||
| if (neg) { | |||
| value = -value; | |||
| } | |||
| // check for nan inf ovf | |||
| if (isnan(value)) { | |||
| @@ -295,7 +309,9 @@ char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) { | |||
| *--ptr = 'o'; | |||
| return ptr; | |||
| } | |||
| if (prec > 9) prec = 9; | |||
| if (prec > 9) { | |||
| prec = 9; | |||
| } | |||
| float round = scale10(0.5, -prec); | |||
| if (expChar) { | |||
| int8_t exp = 0; | |||
| @@ -315,10 +331,14 @@ char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) { | |||
| exp++; | |||
| } | |||
| expNeg = exp < 0; | |||
| if (expNeg) exp = -exp; | |||
| if (expNeg) { | |||
| exp = -exp; | |||
| } | |||
| } | |||
| ptr = fmtDec((uint16_t)exp, ptr); | |||
| if (exp < 10) *--ptr = '0'; | |||
| if (exp < 10) { | |||
| *--ptr = '0'; | |||
| } | |||
| *--ptr = expNeg ? '-' : '+'; | |||
| *--ptr = expChar; | |||
| } else { | |||
| @@ -330,11 +350,15 @@ char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) { | |||
| char* tmp = ptr - prec; | |||
| uint32_t fraction = scale10(value - whole, prec); | |||
| ptr = fmtDec(fraction, ptr); | |||
| while (ptr > tmp) *--ptr = '0'; | |||
| while (ptr > tmp) { | |||
| *--ptr = '0'; | |||
| } | |||
| *--ptr = '.'; | |||
| } | |||
| ptr = fmtDec(whole, ptr); | |||
| if (neg) *--ptr = '-'; | |||
| if (neg) { | |||
| *--ptr = '-'; | |||
| } | |||
| return ptr; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -359,31 +383,43 @@ float scanFloat(const char* str, char** ptr) { | |||
| float v; | |||
| 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 == '-'; | |||
| if (c == '-' || c == '+') c = *str++; | |||
| if (c == '-' || c == '+') { | |||
| c = *str++; | |||
| } | |||
| // Skip leading zeros | |||
| while (c == '0') { | |||
| c = *str++; | |||
| digit = true; | |||
| } | |||
| for (;;) { | |||
| if (isdigit(c)) { | |||
| if (isDigit(c)) { | |||
| digit = true; | |||
| if (nd < 9) { | |||
| fract = 10*fract + c - '0'; | |||
| nd++; | |||
| if (dot) fracExp--; | |||
| if (dot) { | |||
| fracExp--; | |||
| } | |||
| } else { | |||
| if (!dot) fracExp++; | |||
| if (!dot) { | |||
| fracExp++; | |||
| } | |||
| } | |||
| } else if (c == '.') { | |||
| if (dot) goto fail; | |||
| if (dot) { | |||
| goto fail; | |||
| } | |||
| dot = true; | |||
| } else { | |||
| if (!digit) goto fail; | |||
| if (!digit) { | |||
| goto fail; | |||
| } | |||
| break; | |||
| } | |||
| successPtr = str; | |||
| @@ -396,19 +432,23 @@ float scanFloat(const char* str, char** ptr) { | |||
| if (c == '-' || c == '+') { | |||
| 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'; | |||
| successPtr = str; | |||
| c = *str++; | |||
| } | |||
| 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); | |||
| return neg ? -v: v; | |||
| fail: | |||
| fail: | |||
| return 0; | |||
| } | |||
| @@ -19,7 +19,13 @@ | |||
| */ | |||
| #ifndef 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 <stdint.h> | |||
| char* fmtDec(uint16_t n, char* p); | |||
| @@ -18,7 +18,7 @@ | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| /** | |||
| * @file | |||
| * @file | |||
| * @brief Software SPI. | |||
| * | |||
| * @defgroup softSPI Software SPI | |||
| @@ -110,9 +110,13 @@ class SoftSPI { | |||
| private: | |||
| //---------------------------------------------------------------------------- | |||
| 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)) | |||
| bool MODE_CPOL(uint8_t mode) {return (mode & 2) != 0;} | |||
| bool MODE_CPOL(uint8_t mode) { | |||
| return (mode & 2) != 0; | |||
| } | |||
| inline __attribute__((always_inline)) | |||
| void receiveBit(uint8_t bit, uint8_t* data) { | |||
| if (MODE_CPHA(Mode)) { | |||
| @@ -121,8 +125,10 @@ class SoftSPI { | |||
| nop; | |||
| nop; | |||
| 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)) { | |||
| fastDigitalWrite(SckPin, MODE_CPOL(Mode)); | |||
| } | |||
| @@ -135,7 +141,7 @@ class SoftSPI { | |||
| } | |||
| fastDigitalWrite(MosiPin, data & (1 << bit)); | |||
| fastDigitalWrite(SckPin, | |||
| MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); | |||
| MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); | |||
| nop; | |||
| nop; | |||
| if (!MODE_CPHA(Mode)) { | |||
| @@ -150,8 +156,10 @@ class SoftSPI { | |||
| } | |||
| fastDigitalWrite(MosiPin, txData & (1 << bit)); | |||
| 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)) { | |||
| fastDigitalWrite(SckPin, MODE_CPOL(Mode)); | |||
| } | |||
| @@ -26,9 +26,13 @@ int StdioStream::fclose() { | |||
| return EOF; | |||
| } | |||
| 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_w = 0; | |||
| m_flags = 0; | |||
| @@ -37,7 +41,9 @@ int StdioStream::fclose() { | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::fflush() { | |||
| 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; | |||
| } | |||
| @@ -45,16 +51,22 @@ int StdioStream::fflush() { | |||
| char* StdioStream::fgets(char* str, size_t num, size_t* len) { | |||
| char* s = str; | |||
| size_t n; | |||
| if (num-- == 0) return 0; | |||
| if (num-- == 0) { | |||
| return 0; | |||
| } | |||
| while (num) { | |||
| if ((n = m_r) == 0) { | |||
| if (!fillBuf()) { | |||
| if (s == str) return 0; | |||
| if (s == str) { | |||
| return 0; | |||
| } | |||
| break; | |||
| } | |||
| 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)); | |||
| if (end != 0) { | |||
| n = ++end - m_p; | |||
| @@ -71,7 +83,9 @@ char* StdioStream::fgets(char* str, size_t num, size_t* len) { | |||
| num -= n; | |||
| } | |||
| *s = 0; | |||
| if (len) *len = s - str; | |||
| if (len) { | |||
| *len = s - str; | |||
| } | |||
| return str; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -114,14 +128,18 @@ bool StdioStream::fopen(const char* filename, const char* mode) { | |||
| 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_w = 0; | |||
| m_p = m_buf; | |||
| return true; | |||
| fail: | |||
| fail: | |||
| m_flags = 0; | |||
| return false; | |||
| } | |||
| @@ -134,7 +152,9 @@ int StdioStream::fputs(const char* str) { | |||
| int StdioStream::fputs_P(PGM_P str) { | |||
| PGM_P bgn = 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; | |||
| } | |||
| @@ -142,7 +162,9 @@ int StdioStream::fputs_P(PGM_P str) { | |||
| size_t StdioStream::fread(void* ptr, size_t size, size_t count) { | |||
| uint8_t* dst = reinterpret_cast<uint8_t*>(ptr); | |||
| size_t total = size*count; | |||
| if (total == 0) return 0; | |||
| if (total == 0) { | |||
| return 0; | |||
| } | |||
| size_t need = total; | |||
| while (need > m_r) { | |||
| memcpy(dst, m_p, m_r); | |||
| @@ -197,14 +219,16 @@ int StdioStream::fseek(int32_t offset, int origin) { | |||
| m_p = m_buf; | |||
| return 0; | |||
| fail: | |||
| fail: | |||
| return EOF; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int32_t StdioStream::ftell() { | |||
| uint32_t pos = FatFile::curPosition(); | |||
| if (m_flags & F_SRD) { | |||
| if (m_r > pos) return -1L; | |||
| if (m_r > pos) { | |||
| return -1L; | |||
| } | |||
| pos -= m_r; | |||
| } else if (m_flags & F_SWR) { | |||
| pos += m_p - m_buf; | |||
| @@ -217,7 +241,9 @@ size_t StdioStream::fwrite(const void* ptr, size_t size, size_t count) { | |||
| #if 0 //////////////////////////////////////////////////////////////////////////////////// | |||
| const uint8_t* src = static_cast<const uint8_t*>(ptr); | |||
| size_t total = count*size; | |||
| if (total == 0) return 0; | |||
| if (total == 0) { | |||
| return 0; | |||
| } | |||
| size_t todo = total; | |||
| while (todo > m_w) { | |||
| @@ -236,7 +262,7 @@ size_t StdioStream::fwrite(const void* ptr, size_t size, size_t count) { | |||
| #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); | |||
| size_t todo = count; | |||
| @@ -245,7 +271,9 @@ size_t StdioStream::fwrite(const void* ptr, size_t size, size_t count) { | |||
| m_p += m_w; | |||
| src += m_w; | |||
| todo -= m_w; | |||
| if (!flushBuf()) return EOF; | |||
| if (!flushBuf()) { | |||
| return EOF; | |||
| } | |||
| } | |||
| memcpy(m_p, src, todo); | |||
| m_p += todo; | |||
| @@ -257,7 +285,9 @@ size_t StdioStream::print(const __FlashStringHelper *str) { | |||
| const char *p = (const char PROGMEM *)str; | |||
| uint8_t c; | |||
| while ((c = pgm_read_byte(p))) { | |||
| if (putc(c) < 0) return 0; | |||
| if (putc(c) < 0) { | |||
| return 0; | |||
| } | |||
| p++; | |||
| } | |||
| return p - (const char PROGMEM *)str; | |||
| @@ -281,56 +311,76 @@ int StdioStream::printDec(float value, uint8_t prec) { | |||
| } | |||
| // check for NaN INF OVF | |||
| if (isnan(value)) { | |||
| if (fputs_P(PSTR("nan")) < 0) return -1; | |||
| if (fputs_P(PSTR("nan")) < 0) { | |||
| return -1; | |||
| } | |||
| rtn += 3; | |||
| } else if (isinf(value)) { | |||
| if (fputs_P(PSTR("inf")) < 0) return -1; | |||
| if (fputs_P(PSTR("inf")) < 0) { | |||
| return -1; | |||
| } | |||
| rtn += 3; | |||
| } else if (value > 4294967040.0) { | |||
| if (fputs_P(PSTR("ovf")) < 0) return -1;; | |||
| if (fputs_P(PSTR("ovf")) < 0) { | |||
| return -1; | |||
| } | |||
| rtn += 3; | |||
| } else { | |||
| if (sign) { | |||
| if (putc(sign) < 0) return -1; | |||
| if (putc(sign) < 0) { | |||
| return -1; | |||
| } | |||
| 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); | |||
| uint32_t whole = value; | |||
| int np; | |||
| if ((np = printDec(whole)) < 0) return -1; | |||
| if ((np = printDec(whole)) < 0) { | |||
| return -1; | |||
| } | |||
| rtn += np; | |||
| if (prec) { | |||
| if (putc('.') < 0) return -1; | |||
| if (putc('.') < 0) { | |||
| return -1; | |||
| } | |||
| char* str = fmtSpace(prec); | |||
| if (!str) return -1; | |||
| if (!str) { | |||
| return -1; | |||
| } | |||
| char* tmp = str - prec; | |||
| // uint32_t fraction = s*(value - whole); | |||
| // uint32_t fraction = s*(value - whole); | |||
| uint32_t fraction = scale10(value - whole, prec); | |||
| ptr = fmtDec(fraction, str); | |||
| while (ptr > tmp) *--ptr = '0'; | |||
| while (ptr > tmp) { | |||
| *--ptr = '0'; | |||
| } | |||
| rtn += prec + 1; | |||
| } | |||
| } | |||
| return rtn; | |||
| #endif | |||
| #endif | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::printDec(signed char n) { | |||
| uint8_t s = 0; | |||
| if (n < 0) { | |||
| if (fputc('-') < 0) return -1; | |||
| if (fputc('-') < 0) { | |||
| return -1; | |||
| } | |||
| n = -n; | |||
| s = 1; | |||
| } | |||
| @@ -341,11 +391,15 @@ int StdioStream::printDec(int16_t n) { | |||
| int s; | |||
| uint8_t rtn = 0; | |||
| if (n < 0) { | |||
| if (fputc('-') < 0) return -1; | |||
| if (fputc('-') < 0) { | |||
| return -1; | |||
| } | |||
| n = -n; | |||
| rtn++; | |||
| } | |||
| if ((s = printDec((uint16_t)n)) < 0) return s; | |||
| if ((s = printDec((uint16_t)n)) < 0) { | |||
| return s; | |||
| } | |||
| return rtn; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -364,7 +418,9 @@ int StdioStream::printDec(uint16_t n) { | |||
| len = n < 1000 ? 3 : n < 10000 ? 4 : 5; | |||
| } | |||
| char* str = fmtSpace(len); | |||
| if (!str) return -1; | |||
| if (!str) { | |||
| return -1; | |||
| } | |||
| fmtDec(n, str); | |||
| return len; | |||
| #endif | |||
| @@ -373,7 +429,9 @@ int StdioStream::printDec(uint16_t n) { | |||
| int StdioStream::printDec(int32_t n) { | |||
| uint8_t s = 0; | |||
| if (n < 0) { | |||
| if (fputc('-') < 0) return -1; | |||
| if (fputc('-') < 0) { | |||
| return -1; | |||
| } | |||
| n = -n; | |||
| s = 1; | |||
| } | |||
| @@ -399,7 +457,9 @@ int StdioStream::printDec(uint32_t n) { | |||
| } | |||
| char* str = fmtSpace(len); | |||
| if (!str) return -1; | |||
| if (!str) { | |||
| return -1; | |||
| } | |||
| fmtDec(n, str); | |||
| return len; | |||
| #endif | |||
| @@ -419,7 +479,9 @@ int StdioStream::printHex(uint32_t n) { | |||
| len = n < 0X100000 ? 5 : n < 0X1000000 ? 6 : n < 0X10000000 ? 7 : 8; | |||
| } | |||
| char* str = fmtSpace(len); | |||
| if (!str) return -1; | |||
| if (!str) { | |||
| return -1; | |||
| } | |||
| do { | |||
| uint8_t h = n & 0XF; | |||
| @@ -432,7 +494,9 @@ int StdioStream::printHex(uint32_t n) { | |||
| //------------------------------------------------------------------------------ | |||
| bool StdioStream::rewind() { | |||
| if (m_flags & F_SWR) { | |||
| if (!flushBuf()) return false; | |||
| if (!flushBuf()) { | |||
| return false; | |||
| } | |||
| } | |||
| FatFile::seekSet(0); | |||
| m_r = 0; | |||
| @@ -441,11 +505,17 @@ bool StdioStream::rewind() { | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::ungetc(int c) { | |||
| // error if EOF. | |||
| if (c == EOF) return EOF; | |||
| if (c == EOF) { | |||
| return EOF; | |||
| } | |||
| // error if not reading. | |||
| if ((m_flags & F_SRD) == 0) return EOF; | |||
| if ((m_flags & F_SRD) == 0) { | |||
| return EOF; | |||
| } | |||
| // error if no space. | |||
| if (m_p == m_buf) return EOF; | |||
| if (m_p == m_buf) { | |||
| return EOF; | |||
| } | |||
| m_r++; | |||
| m_flags &= ~F_EOF; | |||
| return *--m_p = (uint8_t)c; | |||
| @@ -463,7 +533,8 @@ int StdioStream::fillGet() { | |||
| //------------------------------------------------------------------------------ | |||
| // private | |||
| 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)) { | |||
| m_flags |= F_ERR; | |||
| return false; | |||
| @@ -490,7 +561,8 @@ bool StdioStream::fillBuf() { | |||
| //------------------------------------------------------------------------------ | |||
| // private | |||
| 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)) { | |||
| m_flags |= F_ERR; | |||
| return false; | |||
| @@ -505,13 +577,17 @@ bool StdioStream::flushBuf() { | |||
| uint8_t n = m_p - m_buf; | |||
| m_p = 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; | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::flushPut(uint8_t c) { | |||
| if (!flushBuf()) return EOF; | |||
| if (!flushBuf()) { | |||
| return EOF; | |||
| } | |||
| m_w--; | |||
| return *m_p++ = c; | |||
| } | |||
| @@ -522,7 +598,9 @@ char* StdioStream::fmtSpace(uint8_t len) { | |||
| return 0; | |||
| } | |||
| } | |||
| if (len > m_w) return 0; | |||
| if (len > m_w) { | |||
| return 0; | |||
| } | |||
| m_p += len; | |||
| m_w -= len; | |||
| return reinterpret_cast<char*>(m_p); | |||
| @@ -106,9 +106,9 @@ const uint8_t UNGETC_BUF_SIZE = 2; | |||
| */ | |||
| class StdioStream : private FatFile { | |||
| public: | |||
| /** Constructor | |||
| * | |||
| */ | |||
| /** Constructor | |||
| * | |||
| */ | |||
| StdioStream() { | |||
| m_w = m_r = 0; | |||
| m_p = m_buf; | |||
| @@ -116,7 +116,9 @@ class StdioStream : private FatFile { | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** 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. | |||
| * | |||
| @@ -134,12 +136,16 @@ class StdioStream : private FatFile { | |||
| /** Test the stream's end-of-file indicator. | |||
| * \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. | |||
| * \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. | |||
| * | |||
| @@ -160,7 +166,9 @@ class StdioStream : private FatFile { | |||
| * set and the fgetc function returns EOF. Otherwise, the fgetc function | |||
| * 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. | |||
| * | |||
| @@ -256,7 +264,9 @@ class StdioStream : private FatFile { | |||
| * has written. Otherwise, it returns EOF and sets the error indicator for | |||
| * 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. | |||
| * | |||
| @@ -348,7 +358,9 @@ class StdioStream : private FatFile { | |||
| * returns the next character from the input stream. | |||
| */ | |||
| 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. | |||
| * | |||
| @@ -362,7 +374,9 @@ class StdioStream : private FatFile { | |||
| * the stream. | |||
| */ | |||
| 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. | |||
| * | |||
| @@ -371,7 +385,9 @@ class StdioStream : private FatFile { | |||
| inline __attribute__((always_inline)) | |||
| int putCRLF() { | |||
| if (m_w < 2) { | |||
| if (!flushBuf()) return -1; | |||
| if (!flushBuf()) { | |||
| return -1; | |||
| } | |||
| } | |||
| *m_p++ = '\r'; | |||
| *m_p++ = '\n'; | |||
| @@ -554,32 +570,32 @@ class StdioStream : private FatFile { | |||
| */ | |||
| 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) { | |||
| 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 rtn = printDec(value, prec); | |||
| 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> | |||
| int printField(T value, char term) { | |||
| int rtn = printDec(value); | |||
| @@ -39,7 +39,7 @@ class ibufstream : public istream { | |||
| * Warning: The string will not be copied so must stay in scope. | |||
| */ | |||
| explicit ibufstream(const char* str) { | |||
| init(str); | |||
| init(str); | |||
| } | |||
| /** Initialize an ibufstream | |||
| * \param[in] str pointer to string to be parsed | |||
| @@ -55,14 +55,18 @@ class ibufstream : public istream { | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| 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); | |||
| return -1; | |||
| } | |||
| void getpos(FatPos_t *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) { | |||
| if (pos < m_len) { | |||
| m_pos = pos; | |||
| @@ -109,9 +113,13 @@ class obufstream : public ostream { | |||
| m_in = 0; | |||
| } | |||
| /** \return a pointer to the buffer */ | |||
| char* buf() {return m_buf;} | |||
| char* buf() { | |||
| return m_buf; | |||
| } | |||
| /** \return the length of the formatted string */ | |||
| size_t length() {return m_in;} | |||
| size_t length() { | |||
| return m_in; | |||
| } | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| @@ -124,16 +132,24 @@ class obufstream : public ostream { | |||
| m_buf[m_in]= '\0'; | |||
| } | |||
| 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) { | |||
| if (pos > m_in) return false; | |||
| if (pos > m_in) { | |||
| return false; | |||
| } | |||
| m_in = pos; | |||
| m_buf[m_in] = '\0'; | |||
| return true; | |||
| } | |||
| bool sync() {return true;} | |||
| bool sync() { | |||
| return true; | |||
| } | |||
| pos_type tellpos() { | |||
| return m_in; | |||
| @@ -19,7 +19,7 @@ | |||
| */ | |||
| #include "fstream.h" | |||
| //============================================================================== | |||
| /// @cond SHOW_PROTECTED | |||
| /// @cond SHOW_PROTECTED | |||
| int16_t FatStreamBase::getch() { | |||
| uint8_t c; | |||
| int8_t s = read(&c, 1); | |||
| @@ -31,53 +31,63 @@ int16_t FatStreamBase::getch() { | |||
| } | |||
| return -1; | |||
| } | |||
| if (c != '\r' || (getmode() & ios::binary)) return c; | |||
| if (c != '\r' || (getmode() & ios::binary)) { | |||
| return c; | |||
| } | |||
| 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'; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void FatStreamBase::open(const char* path, ios::openmode mode) { | |||
| uint8_t flags; | |||
| uint8_t flags; | |||
| 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); | |||
| clear(); | |||
| return; | |||
| fail: | |||
| fail: | |||
| FatFile::close(); | |||
| setstate(failbit); | |||
| return; | |||
| @@ -88,7 +98,9 @@ void FatStreamBase::putch(char c) { | |||
| write('\r'); | |||
| } | |||
| write(c); | |||
| if (getWriteError()) setstate(badbit); | |||
| if (getWriteError()) { | |||
| setstate(badbit); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void FatStreamBase::putstr(const char* str) { | |||
| @@ -96,15 +108,21 @@ void FatStreamBase::putstr(const char* str) { | |||
| while (1) { | |||
| char c = str[n]; | |||
| 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'); | |||
| str += n; | |||
| n = 0; | |||
| } | |||
| n++; | |||
| } | |||
| if (getWriteError()) setstate(badbit); | |||
| if (getWriteError()) { | |||
| setstate(badbit); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Internal do not use | |||
| @@ -114,20 +132,20 @@ void FatStreamBase::putstr(const char* str) { | |||
| bool FatStreamBase::seekoff(off_type off, seekdir way) { | |||
| pos_type pos; | |||
| 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); | |||
| } | |||
| @@ -40,11 +40,15 @@ class FatStreamBase : protected FatFile, virtual public ios { | |||
| /** Internal do not use | |||
| * \return mode | |||
| */ | |||
| ios::openmode getmode() {return m_mode;} | |||
| ios::openmode getmode() { | |||
| return m_mode; | |||
| } | |||
| /** Internal do not use | |||
| * \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 seekpos(pos_type pos); | |||
| int write(const void* buf, size_t n); | |||
| @@ -83,7 +87,9 @@ class fstream : public iostream, FatStreamBase { | |||
| /** Close a file and force cached data and directory information | |||
| * to be written to the storage device. | |||
| */ | |||
| void close() {FatFile::close();} | |||
| void close() { | |||
| FatFile::close(); | |||
| } | |||
| /** Open a fstream | |||
| * \param[in] path file to open | |||
| * \param[in] mode open mode | |||
| @@ -110,36 +116,54 @@ class fstream : public iostream, FatStreamBase { | |||
| FatStreamBase::open(path, mode); | |||
| } | |||
| /** \return True if stream is open else false. */ | |||
| bool is_open () {return FatFile::isOpen();} | |||
| bool is_open() { | |||
| return FatFile::isOpen(); | |||
| } | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| /** Internal - do not use | |||
| * \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 | |||
| * \param[in] c | |||
| */ | |||
| void putch(char c) {FatStreamBase::putch(c);} | |||
| void putch(char c) { | |||
| FatStreamBase::putch(c); | |||
| } | |||
| /** Internal - do not use | |||
| * \param[in] str | |||
| */ | |||
| void putstr(const char *str) {FatStreamBase::putstr(str);} | |||
| void putstr(const char *str) { | |||
| FatStreamBase::putstr(str); | |||
| } | |||
| /** Internal - do not use | |||
| * \param[in] pos | |||
| */ | |||
| bool seekoff(off_type off, seekdir 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 | |||
| }; | |||
| //============================================================================== | |||
| @@ -164,9 +188,13 @@ class ifstream : public istream, FatStreamBase { | |||
| /** Close a file and force cached data and directory information | |||
| * to be written to the storage device. | |||
| */ | |||
| void close() {FatFile::close();} | |||
| void close() { | |||
| FatFile::close(); | |||
| } | |||
| /** \return True if stream is open else false. */ | |||
| bool is_open() {return FatFile::isOpen();} | |||
| bool is_open() { | |||
| return FatFile::isOpen(); | |||
| } | |||
| /** Open an ifstream | |||
| * \param[in] path file to open | |||
| * \param[in] mode open mode | |||
| @@ -182,20 +210,30 @@ class ifstream : public istream, FatStreamBase { | |||
| /** Internal - do not use | |||
| * \return | |||
| */ | |||
| int16_t getch() {return FatStreamBase::getch();} | |||
| int16_t getch() { | |||
| return FatStreamBase::getch(); | |||
| } | |||
| /** Internal - do not use | |||
| * \param[out] pos | |||
| */ | |||
| void getpos(FatPos_t* pos) {FatFile::getpos(pos);} | |||
| void getpos(FatPos_t* pos) { | |||
| FatFile::getpos(pos); | |||
| } | |||
| /** Internal - do not use | |||
| * \param[in] pos | |||
| */ | |||
| bool seekoff(off_type off, seekdir 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 | |||
| }; | |||
| //============================================================================== | |||
| @@ -226,7 +264,9 @@ class ofstream : public ostream, FatStreamBase { | |||
| /** Close a file and force cached data and directory information | |||
| * to be written to the storage device. | |||
| */ | |||
| void close() {FatFile::close();} | |||
| void close() { | |||
| FatFile::close(); | |||
| } | |||
| /** Open an ofstream | |||
| * \param[in] path file to open | |||
| * \param[in] mode open mode | |||
| @@ -237,7 +277,9 @@ class ofstream : public ostream, FatStreamBase { | |||
| FatStreamBase::open(path, mode | out); | |||
| } | |||
| /** \return True if stream is open else false. */ | |||
| bool is_open() {return FatFile::isOpen();} | |||
| bool is_open() { | |||
| return FatFile::isOpen(); | |||
| } | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| @@ -245,18 +287,28 @@ class ofstream : public ostream, FatStreamBase { | |||
| * Internal do not use | |||
| * \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) { | |||
| 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 | |||
| * \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 | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| @@ -21,7 +21,7 @@ | |||
| #define ios_h | |||
| #include "FatFile.h" | |||
| /** | |||
| * \file | |||
| * \file | |||
| * \brief \ref ios_base and \ref ios classes | |||
| */ | |||
| //============================================================================== | |||
| @@ -116,7 +116,9 @@ class ios_base { | |||
| ios_base() : m_fill(' '), m_fmtflags(dec | right | skipws) | |||
| , m_precision(2), m_width(0) {} | |||
| /** \return fill character */ | |||
| char fill() {return m_fill;} | |||
| char fill() { | |||
| return m_fill; | |||
| } | |||
| /** Set fill character | |||
| * \param[in] c new fill character | |||
| * \return old fill character | |||
| @@ -127,7 +129,9 @@ class ios_base { | |||
| return r; | |||
| } | |||
| /** \return format flags */ | |||
| fmtflags flags() const {return m_fmtflags;} | |||
| fmtflags flags() const { | |||
| return m_fmtflags; | |||
| } | |||
| /** set format flags | |||
| * \param[in] fl new flag | |||
| * \return old flags | |||
| @@ -138,7 +142,9 @@ class ios_base { | |||
| return tmp; | |||
| } | |||
| /** \return precision */ | |||
| int precision() const {return m_precision;} | |||
| int precision() const { | |||
| return m_precision; | |||
| } | |||
| /** set precision | |||
| * \param[in] n new precision | |||
| * \return old precision | |||
| @@ -176,7 +182,9 @@ class ios_base { | |||
| m_fmtflags &= ~fl; | |||
| } | |||
| /** \return width */ | |||
| unsigned width() {return m_width;} | |||
| unsigned width() { | |||
| return m_width; | |||
| } | |||
| /** set width | |||
| * \param[in] n new width | |||
| * \return old width | |||
| @@ -360,11 +368,17 @@ class ios : public ios_base { | |||
| return !fail() ? reinterpret_cast<const void*>(this) : 0; | |||
| } | |||
| /** \return true if fail() else false. */ | |||
| bool operator!() const {return fail();} | |||
| bool operator!() const { | |||
| return fail(); | |||
| } | |||
| /** \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. */ | |||
| 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. | |||
| * | |||
| * Warning: An empty file returns false before the first read. | |||
| @@ -372,21 +386,31 @@ class ios : public ios_base { | |||
| * Moral: eof() is only useful in combination with fail(), to find out | |||
| * 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. */ | |||
| 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. */ | |||
| bool bad() const {return m_iostate & badbit;} | |||
| bool bad() const { | |||
| return m_iostate & badbit; | |||
| } | |||
| /** Clear iostate bits. | |||
| * | |||
| * \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. | |||
| * | |||
| * \param[in] state Bitts to set. | |||
| **/ | |||
| void setstate(iostate state) {m_iostate |= state;} | |||
| void setstate(iostate state) { | |||
| m_iostate |= state; | |||
| } | |||
| private: | |||
| iostate m_iostate; | |||
| @@ -17,7 +17,7 @@ | |||
| * along with the FatLib Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <ctype.h> | |||
| // #include <ctype.h> | |||
| #include <float.h> | |||
| #include "istream.h" | |||
| //------------------------------------------------------------------------------ | |||
| @@ -35,7 +35,9 @@ int istream::get() { | |||
| //------------------------------------------------------------------------------ | |||
| istream& istream::get(char& c) { | |||
| int tmp = get(); | |||
| if (tmp >= 0) c = tmp; | |||
| if (tmp >= 0) { | |||
| c = tmp; | |||
| } | |||
| return *this; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -54,8 +56,12 @@ istream& istream::get(char *str, streamsize n, char delim) { | |||
| } | |||
| 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; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -75,7 +81,9 @@ void istream::getBool(bool *b) { | |||
| while (1) { | |||
| falseOk = falseOk && c == pgm_read_byte(falsePtr + i); | |||
| trueOk = trueOk && c == pgm_read_byte(truePtr + i); | |||
| if (trueOk == false && falseOk == false) break; | |||
| if (trueOk == false && falseOk == false) { | |||
| break; | |||
| } | |||
| i++; | |||
| if (trueOk && i == true_len) { | |||
| *b = true; | |||
| @@ -128,19 +136,27 @@ bool istream::getDouble(double* value) { | |||
| got_digit = true; | |||
| if (frac < uint32_max/10) { | |||
| frac = frac * 10 + (c - '0'); | |||
| if (got_dot) fracExp--; | |||
| if (got_dot) { | |||
| fracExp--; | |||
| } | |||
| } else { | |||
| if (!got_dot) fracExp++; | |||
| if (!got_dot) { | |||
| fracExp++; | |||
| } | |||
| } | |||
| } else if (!got_dot && c == '.') { | |||
| got_dot = true; | |||
| } else { | |||
| break; | |||
| } | |||
| if (fracExp < -EXP_LIMIT || fracExp > EXP_LIMIT) goto fail; | |||
| if (fracExp < -EXP_LIMIT || fracExp > EXP_LIMIT) { | |||
| goto fail; | |||
| } | |||
| c = getch(&endPos); | |||
| } | |||
| if (!got_digit) goto fail; | |||
| if (!got_digit) { | |||
| goto fail; | |||
| } | |||
| if (c == 'e' || c == 'E') { | |||
| c = getch(); | |||
| expNeg = c == '-'; | |||
| @@ -148,7 +164,9 @@ bool istream::getDouble(double* value) { | |||
| c = getch(); | |||
| } | |||
| while (isdigit(c)) { | |||
| if (exp > EXP_LIMIT) goto fail; | |||
| if (exp > EXP_LIMIT) { | |||
| goto fail; | |||
| } | |||
| exp = exp * 10 + (c - '0'); | |||
| c = getch(&endPos); | |||
| } | |||
| @@ -156,17 +174,23 @@ bool istream::getDouble(double* value) { | |||
| v = static_cast<double>(frac); | |||
| exp = expNeg ? fracExp - exp : fracExp + exp; | |||
| expNeg = exp < 0; | |||
| if (expNeg) exp = -exp; | |||
| if (expNeg) { | |||
| exp = -exp; | |||
| } | |||
| pow10 = 10.0; | |||
| while (exp) { | |||
| if (exp & 1) { | |||
| if (expNeg) { | |||
| // check for underflow | |||
| if (v < FLT_MIN * pow10 && frac != 0) goto fail; | |||
| if (v < FLT_MIN * pow10 && frac != 0) { | |||
| goto fail; | |||
| } | |||
| v /= pow10; | |||
| } else { | |||
| // check for overflow | |||
| if (v > FLT_MAX / pow10) goto fail; | |||
| if (v > FLT_MAX / pow10) { | |||
| goto fail; | |||
| } | |||
| v *= pow10; | |||
| } | |||
| } | |||
| @@ -177,7 +201,7 @@ bool istream::getDouble(double* value) { | |||
| *value = neg ? -v : v; | |||
| return true; | |||
| fail: | |||
| fail: | |||
| // error restore position to last good place | |||
| setpos(&endPos); | |||
| setstate(failbit); | |||
| @@ -189,7 +213,9 @@ istream& istream::getline(char *str, streamsize n, char delim) { | |||
| FatPos_t pos; | |||
| int c; | |||
| m_gcount = 0; | |||
| if (n > 0) str[0] = '\0'; | |||
| if (n > 0) { | |||
| str[0] = '\0'; | |||
| } | |||
| while (1) { | |||
| c = getch(&pos); | |||
| if (c < 0) { | |||
| @@ -207,7 +233,9 @@ istream& istream::getline(char *str, streamsize n, char delim) { | |||
| str[m_gcount++] = c; | |||
| str[m_gcount] = '\0'; | |||
| } | |||
| if (m_gcount == 0) setstate(failbit); | |||
| if (m_gcount == 0) { | |||
| setstate(failbit); | |||
| } | |||
| return *this; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -295,7 +323,9 @@ void istream::getStr(char *str) { | |||
| } | |||
| } | |||
| str[i] = '\0'; | |||
| if (i == 0) setstate(failbit); | |||
| if (i == 0) { | |||
| setstate(failbit); | |||
| } | |||
| width(0); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -308,7 +338,9 @@ istream& istream::ignore(streamsize n, int delim) { | |||
| break; | |||
| } | |||
| m_gcount++; | |||
| if (c == delim) break; | |||
| if (c == delim) { | |||
| break; | |||
| } | |||
| } | |||
| return *this; | |||
| } | |||
| @@ -320,7 +352,9 @@ int istream::peek() { | |||
| getpos(&pos); | |||
| c = getch(); | |||
| if (c < 0) { | |||
| if (!bad()) setstate(eofbit); | |||
| if (!bad()) { | |||
| setstate(eofbit); | |||
| } | |||
| } else { | |||
| setpos(&pos); | |||
| } | |||
| @@ -55,124 +55,124 @@ class istream : public virtual ios { | |||
| pf(*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) { | |||
| getStr(str); | |||
| 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) { | |||
| getChar(&ch); | |||
| 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) { | |||
| getStr(reinterpret_cast<char*>(str)); | |||
| 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) { | |||
| getChar(reinterpret_cast<char*>(&ch)); | |||
| 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) { | |||
| getStr(reinterpret_cast<char*>(str)); | |||
| 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) { | |||
| getChar(reinterpret_cast<char*>(&ch)); | |||
| 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) { | |||
| getBool(&arg); | |||
| 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 | |||
| getNumber(&arg); | |||
| 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 | |||
| getNumber(&arg); | |||
| 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) { | |||
| getNumber(&arg); | |||
| 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) { | |||
| getNumber(&arg); | |||
| 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 | |||
| getNumber(&arg); | |||
| 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 | |||
| getNumber(&arg); | |||
| return *this; | |||
| } | |||
| /** | |||
| /** | |||
| * Extract a value of type double. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| @@ -181,11 +181,11 @@ class istream : public virtual ios { | |||
| getDouble(&arg); | |||
| 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) { | |||
| double v; | |||
| getDouble(&v); | |||
| @@ -207,13 +207,15 @@ class istream : public virtual ios { | |||
| * \return The number of characters extracted by the last unformatted | |||
| * input function. | |||
| */ | |||
| streamsize gcount() const {return m_gcount;} | |||
| streamsize gcount() const { | |||
| return m_gcount; | |||
| } | |||
| /** | |||
| * Extract a character if one is available. | |||
| * | |||
| * \return The character or -1 if a failure occurs. A failure is indicated | |||
| * by the stream state. | |||
| */ | |||
| */ | |||
| int get(); | |||
| /** | |||
| * Extract a character if one is available. | |||
| @@ -221,10 +223,10 @@ class istream : public virtual ios { | |||
| * \param[out] ch location to receive the extracted character. | |||
| * | |||
| * \return always returns *this. A failure is indicated by the stream state. | |||
| */ | |||
| */ | |||
| istream& get(char& ch); | |||
| /** | |||
| * Extract characters. | |||
| * Extract characters. | |||
| * | |||
| * \param[out] str Location to receive extracted characters. | |||
| * \param[in] n Size of str. | |||
| @@ -236,7 +238,7 @@ class istream : public virtual ios { | |||
| * failbit is set. If end-of-file occurs the eofbit is set. | |||
| * | |||
| * \return always returns *this. A failure is indicated by the stream state. | |||
| */ | |||
| */ | |||
| istream& get(char *str, streamsize n, char delim = '\n'); | |||
| /** | |||
| * Extract characters | |||
| @@ -269,28 +271,32 @@ class istream : public virtual ios { | |||
| * | |||
| * \return *this | |||
| * | |||
| */ | |||
| */ | |||
| istream& ignore(streamsize n = 1, int delim= -1); | |||
| /** | |||
| * Return the next available character without consuming it. | |||
| * | |||
| * \return The character if the stream state is good else -1; | |||
| * | |||
| */ | |||
| */ | |||
| int peek(); | |||
| // istream& read(char *str, streamsize count); | |||
| // streamsize readsome(char *str, streamsize count); | |||
| /** | |||
| * \return the stream position | |||
| */ | |||
| pos_type tellg() {return tellpos();} | |||
| pos_type tellg() { | |||
| return tellpos(); | |||
| } | |||
| /** | |||
| * Set the stream position | |||
| * \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. | |||
| */ | |||
| istream& seekg(pos_type pos) { | |||
| if (!seekpos(pos)) setstate(failbit); | |||
| if (!seekpos(pos)) { | |||
| setstate(failbit); | |||
| } | |||
| return *this; | |||
| } | |||
| /** | |||
| @@ -302,17 +308,19 @@ class istream : public virtual ios { | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& seekg(off_type off, seekdir way) { | |||
| if (!seekoff(off, way)) setstate(failbit); | |||
| if (!seekoff(off, way)) { | |||
| setstate(failbit); | |||
| } | |||
| return *this; | |||
| } | |||
| void skipWhite(); | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| /** | |||
| * Internal - do not use | |||
| * \return | |||
| */ | |||
| /** | |||
| * Internal - do not use | |||
| * \return | |||
| */ | |||
| virtual int16_t getch() = 0; | |||
| /** | |||
| * Internal - do not use | |||
| @@ -24,7 +24,9 @@ | |||
| #endif | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::do_fill(unsigned len) { | |||
| for (; len < width(); len++) putch(fill()); | |||
| for (; len < width(); len++) { | |||
| putch(fill()); | |||
| } | |||
| width(0); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -86,13 +88,17 @@ void ostream::putDouble(double n) { | |||
| return; | |||
| } | |||
| // 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; | |||
| uint32_t intPart = n; | |||
| double fractionPart = n - intPart; | |||
| // format intPart and decimal point | |||
| if (nd || (flags() & showpoint)) *--str = '.'; | |||
| if (nd || (flags() & showpoint)) { | |||
| *--str = '.'; | |||
| } | |||
| str = fmtNum(intPart, str, 10); | |||
| // calculate length for fill | |||
| @@ -102,12 +108,16 @@ void ostream::putDouble(double n) { | |||
| // extract adjust field | |||
| fmtflags adj = flags() & adjustfield; | |||
| if (adj == internal) { | |||
| if (sign) putch(sign); | |||
| if (sign) { | |||
| putch(sign); | |||
| } | |||
| do_fill(len); | |||
| } else { | |||
| // do fill for internal or right | |||
| fill_not_left(len); | |||
| if (sign) *--str = sign; | |||
| if (sign) { | |||
| *--str = sign; | |||
| } | |||
| } | |||
| putstr(str); | |||
| // output fraction | |||
| @@ -123,7 +133,9 @@ void ostream::putDouble(double n) { | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::putNum(int32_t n) { | |||
| bool neg = n < 0 && flagsToBase() == 10; | |||
| if (neg) n = -n; | |||
| if (neg) { | |||
| n = -n; | |||
| } | |||
| putNum(n, neg); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -150,7 +162,9 @@ void ostream::putNum(uint32_t n, bool neg) { | |||
| uint8_t len = end - str; | |||
| fmtflags adj = flags() & adjustfield; | |||
| if (adj == internal) { | |||
| while (str < num) putch(*str++); | |||
| while (str < num) { | |||
| putch(*str++); | |||
| } | |||
| } | |||
| if (adj != left) { | |||
| do_fill(len); | |||
| @@ -230,20 +230,26 @@ class ostream : public virtual ios { | |||
| * \return A reference to the ostream object. | |||
| */ | |||
| ostream& flush() { | |||
| if (!sync()) setstate(badbit); | |||
| if (!sync()) { | |||
| setstate(badbit); | |||
| } | |||
| return *this; | |||
| } | |||
| /** | |||
| * \return the stream position | |||
| */ | |||
| pos_type tellp() {return tellpos();} | |||
| pos_type tellp() { | |||
| return tellpos(); | |||
| } | |||
| /** | |||
| * Set the stream position | |||
| * \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. | |||
| */ | |||
| ostream& seekp(pos_type pos) { | |||
| if (!seekpos(pos)) setstate(failbit); | |||
| if (!seekpos(pos)) { | |||
| setstate(failbit); | |||
| } | |||
| return *this; | |||
| } | |||
| /** | |||
| @@ -255,7 +261,9 @@ class ostream : public virtual ios { | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| ostream& seekp(off_type off, seekdir way) { | |||
| if (!seekoff(off, way)) setstate(failbit); | |||
| if (!seekoff(off, way)) { | |||
| setstate(failbit); | |||
| } | |||
| return *this; | |||
| } | |||
| @@ -36,7 +36,7 @@ void testBegin() { | |||
| while (Serial.read() <= 0) {} | |||
| delay(200); // Catch Due reset problem | |||
| print_P(testOut, PSTR("FreeRam: ")); | |||
| testOut->print(F("FreeRam: ")); | |||
| testOut->println(FreeRam()); | |||
| testOut->println(); | |||
| failCount = 0; | |||
| @@ -45,21 +45,21 @@ void testBegin() { | |||
| //------------------------------------------------------------------------------ | |||
| void testEnd() { | |||
| 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()); | |||
| print_P(testOut, PSTR("Test count: ")); | |||
| testOut->print(F("Test count: ")); | |||
| testOut->println(testCount); | |||
| print_P(testOut, PSTR("Fail count: ")); | |||
| testOut->print(F("Fail count: ")); | |||
| testOut->println(failCount); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| static void testResult(bool b, uint8_t n) { | |||
| while (n++ < 60) testOut->write(' '); | |||
| if (b) { | |||
| println_P(testOut, PSTR("..ok")); | |||
| testOut->println(F("..ok")); | |||
| } else { | |||
| println_P(testOut, PSTR("FAIL")); | |||
| testOut->println(F("FAIL")); | |||
| failCount++; | |||
| } | |||
| testCount++; | |||
| @@ -69,14 +69,14 @@ void testVerify_P(char* result, PGM_P expect) { | |||
| testOut->write('"'); | |||
| testOut->print(result); | |||
| testOut->print("\",\""); | |||
| print_P(testOut, expect); | |||
| testOut->print((const __FlashStringHelper*)expect); | |||
| testOut->write('"'); | |||
| uint8_t n = strlen(result) + strlenPGM(expect) + 5; | |||
| testResult(!strcmp_P(result, expect), n); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void testVerify_P(bool b, PGM_P msg) { | |||
| print_P(testOut, msg); | |||
| testOut->print((const __FlashStringHelper*)msg); | |||
| uint8_t n = strlenPGM(msg); | |||
| testResult(b, n); | |||
| } | |||
| @@ -25,10 +25,10 @@ SdFat sd; | |||
| * create enough files to force a cluster to be allocated to dir. | |||
| */ | |||
| void dirAllocTest(SdBaseFile* dir) { | |||
| char buf[13], name[13]; | |||
| char buf[32], name[32]; | |||
| SdFile file; | |||
| uint16_t n; | |||
| uint32_t size = dir->fileSize(); | |||
| uint32_t size = dir->dirSize(); | |||
| // create files and write name to file | |||
| for (n = 0; ; n++){ | |||
| @@ -61,7 +61,7 @@ void dirAllocTest(SdBaseFile* dir) { | |||
| Serial.println(t2 - t1); | |||
| // directory size will change when a cluster is added | |||
| if (dir->fileSize() != size) break; | |||
| if (dir->curPosition() > size) break; | |||
| } | |||
| // read files and check content | |||
| @@ -76,7 +76,7 @@ void dirAllocTest(SdBaseFile* dir) { | |||
| // open end time and read start time | |||
| 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"); | |||
| // read end time | |||
| @@ -112,6 +112,7 @@ void setup() { | |||
| // try SPI_HALF_SPEED if bus errors occur. | |||
| if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) sd.initErrorHalt(); | |||
| uint32_t m = millis(); | |||
| // write files to root if FAT32 | |||
| if (sd.vol()->fatType() == 32) { | |||
| Serial.println(F("Writing files to root")); | |||
| @@ -129,8 +130,9 @@ void setup() { | |||
| if (!sub2.mkdir(&sub1, "SUB2")) error("mkdir SUB2 failed"); | |||
| Serial.println(F("Writing files to SUB2")); | |||
| dirAllocTest(&sub2); | |||
| Serial.println(F("Done")); | |||
| m = millis() - m; | |||
| Serial.print(F("Done millis: ")); | |||
| Serial.println(m); | |||
| } | |||
| void loop() { } | |||
| void loop() { } | |||
| @@ -21,7 +21,7 @@ SdFat sd; | |||
| * remove all files in dir. | |||
| */ | |||
| void deleteFiles(SdBaseFile* dir) { | |||
| char name[13]; | |||
| char name[32]; | |||
| SdFile file; | |||
| // open and delete files | |||
| @@ -0,0 +1,35 @@ | |||
| // 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() {} | |||
| @@ -0,0 +1,235 @@ | |||
| #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() {} | |||
| @@ -0,0 +1,219 @@ | |||
| #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() {} | |||
| @@ -1,15 +0,0 @@ | |||
| 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. | |||
| @@ -1,44 +0,0 @@ | |||
| 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(); | |||
| @@ -1,3 +1,7 @@ | |||
| 04 Dec 2014 | |||
| Added support for Long File Names. | |||
| 14 Nov 2014 | |||
| Replaced the core SdFat code with FatLib, a generic FAT12/FAT16/FAT32 | |||
| @@ -100,7 +100,7 @@ Include dependency graph for ArduinoStream.h:</div> | |||
| <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> | |||
| <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><table class="memberdecls"> | |||
| <tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a> | |||
| @@ -117,7 +117,7 @@ Classes</h2></td></tr> | |||
| </div></div><!-- contents --> | |||
| <!-- start footer part --> | |||
| <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"/> | |||
| </a> 1.8.8 | |||
| </small></address> | |||
| @@ -151,7 +151,7 @@ Variables</h2></td></tr> | |||
| </div></div><!-- contents --> | |||
| <!-- start footer part --> | |||
| <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"/> | |||
| </a> 1.8.8 | |||
| </small></address> | |||
| @@ -87,7 +87,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); | |||
| <div class="summary"> | |||
| <a href="#nested-classes">Classes</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="title">FatFile.h File Reference</div> </div> | |||
| </div><!--header--> | |||
| @@ -95,21 +95,19 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); | |||
| <p><a class="el" href="class_fat_file.html" title="Basic file class. ">FatFile</a> class. | |||
| <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 <limits.h></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 "<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 <Arduino.h></code><br /> | |||
| </div><div class="textblock"><div class="dynheader"> | |||
| Include dependency graph for FatFile.h:</div> | |||
| <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> | |||
| <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 class="textblock"><div class="dynheader"> | |||
| This graph shows which files directly or indirectly include this file:</div> | |||
| @@ -127,9 +125,14 @@ Classes</h2></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="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"> | |||
| <tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="define-members"></a> | |||
| 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="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> | |||
| @@ -141,14 +144,39 @@ Macros</h2></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> | |||
| </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> | |||
| <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><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> | |||
| <div class="memitem"> | |||
| <div class="memproto"> | |||
| @@ -226,24 +254,80 @@ Typedefs</h2></td></tr> | |||
| </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="memproto"> | |||
| <table class="memname"> | |||
| <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> | |||
| </table> | |||
| </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><!-- contents --> | |||
| <!-- start footer part --> | |||
| <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"/> | |||
| </a> 1.8.8 | |||
| </small></address> | |||