@@ -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> |