| */ | */ | ||||
| #include <Arduino.h> | #include <Arduino.h> | ||||
| #if defined(UDR0) || defined(DOXYGEN) | #if defined(UDR0) || defined(DOXYGEN) | ||||
| #include <MinimumSerial.h> | |||||
| #include "MinimumSerial.h" | |||||
| const uint16_t MIN_2X_BAUD = F_CPU/(4*(2*0XFFF + 1)) + 1; | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Set baud rate for serial port zero and enable in non interrupt mode. | |||||
| * Do not call this function if you use another serial library. | |||||
| * \param[in] baud rate | |||||
| */ | |||||
| void MinimumSerial::begin(uint32_t baud) { | void MinimumSerial::begin(uint32_t baud) { | ||||
| uint16_t baud_setting; | uint16_t baud_setting; | ||||
| // don't worry, the compiler will squeeze out F_CPU != 16000000UL | // don't worry, the compiler will squeeze out F_CPU != 16000000UL | ||||
| if (F_CPU != 16000000UL || baud != 57600) { | |||||
| if ((F_CPU != 16000000UL || baud != 57600) && baud > MIN_2X_BAUD) { | |||||
| // Double the USART Transmission Speed | // Double the USART Transmission Speed | ||||
| UCSR0A = 1 << U2X0; | UCSR0A = 1 << U2X0; | ||||
| baud_setting = (F_CPU / 4 / baud - 1) / 2; | baud_setting = (F_CPU / 4 / baud - 1) / 2; | ||||
| UCSR0B |= (1 << TXEN0) | (1 << RXEN0); | UCSR0B |= (1 << TXEN0) | (1 << RXEN0); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Unbuffered read | |||||
| * \return -1 if no character is available or an available character. | |||||
| */ | |||||
| int MinimumSerial::read() { | int MinimumSerial::read() { | ||||
| if (UCSR0A & (1 << RXC0)) return UDR0; | if (UCSR0A & (1 << RXC0)) return UDR0; | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Unbuffered write | |||||
| * | |||||
| * \param[in] b byte to write. | |||||
| * \return 1 | |||||
| */ | |||||
| size_t MinimumSerial::write(uint8_t b) { | size_t MinimumSerial::write(uint8_t b) { | ||||
| while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} | while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} | ||||
| UDR0 = b; | UDR0 = b; | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| MinimumSerial MiniSerial; | |||||
| #endif // defined(UDR0) || defined(DOXYGEN) | #endif // defined(UDR0) || defined(DOXYGEN) |
| */ | */ | ||||
| #ifndef MinimumSerial_h | #ifndef MinimumSerial_h | ||||
| #define MinimumSerial_h | #define MinimumSerial_h | ||||
| #include <Arduino.h> | |||||
| //============================================================================== | |||||
| /** | /** | ||||
| * \class MinimumSerial | * \class MinimumSerial | ||||
| * \brief mini serial class for the %SdFat library. | * \brief mini serial class for the %SdFat library. | ||||
| */ | */ | ||||
| class MinimumSerial : public Print { | class MinimumSerial : public Print { | ||||
| public: | public: | ||||
| /** | |||||
| * Set baud rate for serial port zero and enable in non interrupt mode. | |||||
| * Do not call this function if you use another serial library. | |||||
| * \param[in] baud rate | |||||
| */ | |||||
| void begin(uint32_t baud); | void begin(uint32_t baud); | ||||
| /** | |||||
| * Unbuffered read | |||||
| * \return -1 if no character is available or an available character. | |||||
| */ | |||||
| int read(); | int read(); | ||||
| /** | |||||
| * Unbuffered write | |||||
| * | |||||
| * \param[in] b byte to write. | |||||
| * \return 1 | |||||
| */ | |||||
| size_t write(uint8_t b); | size_t write(uint8_t b); | ||||
| using Print::write; | using Print::write; | ||||
| }; | }; | ||||
| #ifdef UDR0 | |||||
| extern MinimumSerial MiniSerial; | |||||
| #endif // UDR0 | |||||
| #endif // MinimumSerial_h | #endif // MinimumSerial_h |
| /* Arduino Sd2Card Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| * | |||||
| * This file is part of the Arduino Sd2Card 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 Arduino Sd2Card Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #ifndef SpiCard_h | |||||
| #define SpiCard_h | |||||
| /** | |||||
| * \file | |||||
| * \brief Sd2Card class for V2 SD/SDHC cards | |||||
| */ | |||||
| #include <Arduino.h> | |||||
| #include <SdFatConfig.h> | |||||
| #include <SdInfo.h> | |||||
| #include <SdSpi.h> | |||||
| //------------------------------------------------------------------------------ | |||||
| // SD card errors | |||||
| /** timeout error for command CMD0 (initialize card in SPI mode) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD0 = 0X1; | |||||
| /** CMD8 was not accepted - not a valid SD card*/ | |||||
| uint8_t const SD_CARD_ERROR_CMD8 = 0X2; | |||||
| /** card returned an error response for CMD12 (stop multiblock read) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD12 = 0X3; | |||||
| /** card returned an error response for CMD17 (read block) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD17 = 0X4; | |||||
| /** card returned an error response for CMD18 (read multiple block) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD18 = 0X5; | |||||
| /** card returned an error response for CMD24 (write block) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD24 = 0X6; | |||||
| /** WRITE_MULTIPLE_BLOCKS command failed */ | |||||
| uint8_t const SD_CARD_ERROR_CMD25 = 0X7; | |||||
| /** card returned an error response for CMD58 (read OCR) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD58 = 0X8; | |||||
| /** SET_WR_BLK_ERASE_COUNT failed */ | |||||
| uint8_t const SD_CARD_ERROR_ACMD23 = 0X9; | |||||
| /** ACMD41 initialization process timeout */ | |||||
| uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; | |||||
| /** card returned a bad CSR version field */ | |||||
| uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB; | |||||
| /** erase block group command failed */ | |||||
| uint8_t const SD_CARD_ERROR_ERASE = 0XC; | |||||
| /** card not capable of single block erase */ | |||||
| uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; | |||||
| /** Erase sequence timed out */ | |||||
| uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; | |||||
| /** card returned an error token instead of read data */ | |||||
| uint8_t const SD_CARD_ERROR_READ = 0XF; | |||||
| /** read CID or CSD failed */ | |||||
| uint8_t const SD_CARD_ERROR_READ_REG = 0X10; | |||||
| /** timeout while waiting for start of read data */ | |||||
| uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11; | |||||
| /** card did not accept STOP_TRAN_TOKEN */ | |||||
| uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12; | |||||
| /** card returned an error token as a response to a write operation */ | |||||
| uint8_t const SD_CARD_ERROR_WRITE = 0X13; | |||||
| /** attempt to write protected block zero */ | |||||
| uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X14; // REMOVE - not used | |||||
| /** card did not go ready for a multiple block write */ | |||||
| uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; | |||||
| /** card returned an error to a CMD13 status check after a write */ | |||||
| uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||||
| /** timeout occurred during write programming */ | |||||
| uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; | |||||
| /** incorrect rate selected */ | |||||
| uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18; | |||||
| /** init() not called */ | |||||
| uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; | |||||
| /** card returned an error for CMD59 (CRC_ON_OFF) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD59 = 0X1A; | |||||
| /** invalid read CRC */ | |||||
| uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B; | |||||
| /** SPI DMA error */ | |||||
| uint8_t const SD_CARD_ERROR_SPI_DMA = 0X1C; | |||||
| //------------------------------------------------------------------------------ | |||||
| // card types | |||||
| /** Standard capacity V1 SD card */ | |||||
| uint8_t const SD_CARD_TYPE_SD1 = 1; | |||||
| /** Standard capacity V2 SD card */ | |||||
| uint8_t const SD_CARD_TYPE_SD2 = 2; | |||||
| /** High Capacity SD card */ | |||||
| uint8_t const SD_CARD_TYPE_SDHC = 3; | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * \class Sd2Card | |||||
| * \brief Raw access to SD and SDHC flash memory cards. | |||||
| */ | |||||
| class Sd2Card { | |||||
| public: | |||||
| /** Construct an instance of Sd2Card. */ | |||||
| Sd2Card() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} | |||||
| bool begin(uint8_t chipSelectPin = SD_CHIP_SELECT_PIN, | |||||
| uint8_t sckDivisor = SPI_FULL_SPEED); | |||||
| uint32_t cardSize(); | |||||
| bool erase(uint32_t firstBlock, uint32_t lastBlock); | |||||
| bool eraseSingleBlockEnable(); | |||||
| /** | |||||
| * Set SD error code. | |||||
| * \param[in] code value for error code. | |||||
| */ | |||||
| void error(uint8_t code) {m_errorCode = code;} | |||||
| /** | |||||
| * \return error code for last error. See Sd2Card.h for a list of error codes. | |||||
| */ | |||||
| int errorCode() const {return m_errorCode;} | |||||
| /** \return error data for last error. */ | |||||
| int errorData() const {return m_status;} | |||||
| /** | |||||
| * Initialize an SD flash memory card. | |||||
| * | |||||
| * \param[in] chipSelectPin SD chip select pin number. | |||||
| * \param[in] sckDivisor SPI SCK clock rate divisor. | |||||
| * | |||||
| * \return The value one, true, is returned for success and | |||||
| * the value zero, false, is returned for failure. The reason for failure | |||||
| * can be determined by calling errorCode() and errorData(). | |||||
| */ | |||||
| bool init(uint8_t sckDivisor = SPI_FULL_SPEED, | |||||
| uint8_t chipSelectPin = SD_CHIP_SELECT_PIN) { | |||||
| return begin(chipSelectPin, sckDivisor); | |||||
| } | |||||
| bool isBusy(); | |||||
| bool readBlock(uint32_t block, uint8_t* dst); | |||||
| /** | |||||
| * Read a card's CID register. The CID contains card identification | |||||
| * information such as Manufacturer ID, Product name, Product serial | |||||
| * number and Manufacturing date. | |||||
| * | |||||
| * \param[out] cid pointer to area for returned data. | |||||
| * | |||||
| * \return true for success or false for failure. | |||||
| */ | |||||
| bool readCID(cid_t* cid) { | |||||
| return readRegister(CMD10, cid); | |||||
| } | |||||
| /** | |||||
| * Read a card's CSD register. The CSD contains Card-Specific Data that | |||||
| * provides information regarding access to the card's contents. | |||||
| * | |||||
| * \param[out] csd pointer to area for returned data. | |||||
| * | |||||
| * \return true for success or false for failure. | |||||
| */ | |||||
| bool readCSD(csd_t* csd) { | |||||
| return readRegister(CMD9, csd); | |||||
| } | |||||
| bool readData(uint8_t *dst); | |||||
| bool readOCR(uint32_t* ocr); | |||||
| bool readStart(uint32_t blockNumber); | |||||
| bool readStop(); | |||||
| /** Return SCK divisor. | |||||
| * | |||||
| * \return Requested SCK divisor. | |||||
| */ | |||||
| 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;} | |||||
| bool writeBlock(uint32_t blockNumber, const uint8_t* src); | |||||
| bool writeData(const uint8_t* src); | |||||
| bool writeStart(uint32_t blockNumber, uint32_t eraseCount); | |||||
| bool writeStop(); | |||||
| private: | |||||
| //---------------------------------------------------------------------------- | |||||
| // private functions | |||||
| uint8_t cardAcmd(uint8_t cmd, uint32_t arg) { | |||||
| cardCommand(CMD55, 0); | |||||
| return cardCommand(cmd, arg); | |||||
| } | |||||
| uint8_t cardCommand(uint8_t cmd, uint32_t arg); | |||||
| bool readData(uint8_t* dst, size_t count); | |||||
| bool readRegister(uint8_t cmd, void* buf); | |||||
| void chipSelectHigh(); | |||||
| void chipSelectLow(); | |||||
| void spiYield(); | |||||
| void type(uint8_t value) {m_type = value;} | |||||
| bool waitNotBusy(uint16_t timeoutMillis); | |||||
| bool writeData(uint8_t token, const uint8_t* src); | |||||
| // private data | |||||
| static SdSpi m_spi; | |||||
| uint8_t m_chipSelectPin; | |||||
| uint8_t m_errorCode; | |||||
| uint8_t m_sckDivisor; | |||||
| uint8_t m_status; | |||||
| uint8_t m_type; | |||||
| }; | |||||
| #endif // SpiCard_h |
| /* 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 | |||||
| * 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 Arduino SdFat Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #ifndef SdBaseFile_h | |||||
| #define SdBaseFile_h | |||||
| /** | |||||
| * \file | |||||
| * \brief SdBaseFile class | |||||
| */ | |||||
| #ifdef __AVR__ | |||||
| #include <avr/pgmspace.h> | |||||
| #else // __AVR__ | |||||
| #ifndef PGM_P | |||||
| /** pointer to flash for ARM */ | |||||
| #define PGM_P const char* | |||||
| #endif // PGM_P | |||||
| #ifndef PSTR | |||||
| /** store literal string in flash for ARM */ | |||||
| #define PSTR(x) (x) | |||||
| #endif // PSTR | |||||
| #ifndef pgm_read_byte | |||||
| /** read 8-bits from flash for ARM */ | |||||
| #define pgm_read_byte(addr) (*(const unsigned char*)(addr)) | |||||
| #endif // pgm_read_byte | |||||
| #ifndef pgm_read_word | |||||
| /** read 16-bits from flash for ARM */ | |||||
| #define pgm_read_word(addr) (*(const uint16_t*)(addr)) | |||||
| #endif // pgm_read_word | |||||
| #ifndef PROGMEM | |||||
| /** store in flash for ARM */ | |||||
| #define PROGMEM const | |||||
| #endif // PROGMEM | |||||
| #endif // __AVR__ | |||||
| #include <Arduino.h> | |||||
| #include <SdFatConfig.h> | |||||
| #include <SdVolume.h> | |||||
| #include <utility/FatApiConstants.h> | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * \struct FatPos_t | |||||
| * \brief internal type for istream | |||||
| * do not use in user apps | |||||
| */ | |||||
| struct FatPos_t { | |||||
| /** stream position */ | |||||
| uint32_t position; | |||||
| /** cluster for position */ | |||||
| uint32_t cluster; | |||||
| FatPos_t() : position(0), cluster(0) {} | |||||
| }; | |||||
| // values for m_type | |||||
| /** This file has not been opened. */ | |||||
| uint8_t const FAT_FILE_TYPE_CLOSED = 0; | |||||
| /** A normal file */ | |||||
| uint8_t const FAT_FILE_TYPE_NORMAL = 1; | |||||
| /** A FAT12 or FAT16 root directory */ | |||||
| uint8_t const FAT_FILE_TYPE_ROOT_FIXED = 2; | |||||
| /** A FAT32 root directory */ | |||||
| uint8_t const FAT_FILE_TYPE_ROOT32 = 3; | |||||
| /** A subdirectory file*/ | |||||
| uint8_t const FAT_FILE_TYPE_SUBDIR = 4; | |||||
| /** Test value for directory type */ | |||||
| uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT_FIXED; | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * \class SdBaseFile | |||||
| * \brief Base class for SdFile with Print and C++ streams. | |||||
| */ | |||||
| class SdBaseFile { | |||||
| public: | |||||
| /** Create an instance. */ | |||||
| SdBaseFile() : writeError(false), m_type(FAT_FILE_TYPE_CLOSED) {} | |||||
| SdBaseFile(const char* path, uint8_t oflag); | |||||
| #if DESTRUCTOR_CLOSES_FILE | |||||
| ~SdBaseFile() {if(isOpen()) close();} | |||||
| #endif // DESTRUCTOR_CLOSES_FILE | |||||
| /** | |||||
| * writeError is set to true if an error occurs during a write(). | |||||
| * Set writeError to false before calling print() and/or write() and check | |||||
| * for true after calls to print() and/or write(). | |||||
| */ | |||||
| bool writeError; | |||||
| /** \return value of writeError */ | |||||
| bool getWriteError() {return writeError;} | |||||
| /** Set writeError to zero */ | |||||
| void clearWriteError() {writeError = 0;} | |||||
| //---------------------------------------------------------------------------- | |||||
| // helpers for stream classes | |||||
| /** get position for streams | |||||
| * \param[out] pos struct to receive position | |||||
| */ | |||||
| void getpos(FatPos_t* pos); | |||||
| /** set position for streams | |||||
| * \param[out] pos struct with value for new position | |||||
| */ | |||||
| void setpos(FatPos_t* pos); | |||||
| //---------------------------------------------------------------------------- | |||||
| /** \return number of bytes available from yhe current position to EOF */ | |||||
| uint32_t available() {return fileSize() - curPosition();} | |||||
| bool close(); | |||||
| bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); | |||||
| bool createContiguous(SdBaseFile* dirFile, | |||||
| const char* path, uint32_t size); | |||||
| /** \return The current cluster number for a file or directory. */ | |||||
| uint32_t curCluster() const {return m_curCluster;} | |||||
| /** \return The current position for a file or directory. */ | |||||
| uint32_t curPosition() const {return m_curPosition;} | |||||
| /** \return Current working directory */ | |||||
| static SdBaseFile* cwd() {return m_cwd;} | |||||
| /** Set the date/time callback function | |||||
| * | |||||
| * \param[in] dateTime The user's call back function. The callback | |||||
| * function is of the form: | |||||
| * | |||||
| * \code | |||||
| * void dateTime(uint16_t* date, uint16_t* time) { | |||||
| * uint16_t year; | |||||
| * uint8_t month, day, hour, minute, second; | |||||
| * | |||||
| * // User gets date and time from GPS or real-time clock here | |||||
| * | |||||
| * // return date using FAT_DATE macro to format fields | |||||
| * *date = FAT_DATE(year, month, day); | |||||
| * | |||||
| * // return time using FAT_TIME macro to format fields | |||||
| * *time = FAT_TIME(hour, minute, second); | |||||
| * } | |||||
| * \endcode | |||||
| * | |||||
| * Sets the function that is called when a file is created or when | |||||
| * a file's directory entry is modified by sync(). All timestamps, | |||||
| * access, creation, and modify, are set when a file is created. | |||||
| * sync() maintains the last access date and last modify date/time. | |||||
| * | |||||
| * See the timestamp() function. | |||||
| */ | |||||
| static void dateTimeCallback( | |||||
| void (*dateTime)(uint16_t* date, uint16_t* time)) { | |||||
| m_dateTime = dateTime; | |||||
| } | |||||
| /** Cancel the date/time callback function. */ | |||||
| static void dateTimeCallbackCancel() {m_dateTime = 0;} | |||||
| bool dirEntry(dir_t* dir); | |||||
| static void dirName(const dir_t& dir, char* name); | |||||
| bool exists(const char* name); | |||||
| 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 first cluster number for a file or directory. */ | |||||
| uint32_t firstCluster() const {return m_firstCluster;} | |||||
| bool getFilename(char* name); | |||||
| /** \return True if this is a directory else false. */ | |||||
| bool isDir() const {return m_type >= FAT_FILE_TYPE_MIN_DIR;} | |||||
| /** \return True if this is a normal file else false. */ | |||||
| bool isFile() const {return m_type == FAT_FILE_TYPE_NORMAL;} | |||||
| /** \return True if this is an open file/directory else false. */ | |||||
| bool isOpen() const {return m_type != FAT_FILE_TYPE_CLOSED;} | |||||
| /** \return True if this is a subdirectory else false. */ | |||||
| bool isSubDir() const {return m_type == FAT_FILE_TYPE_SUBDIR;} | |||||
| /** \return True if this is the root directory. */ | |||||
| bool isRoot() const { | |||||
| return m_type == FAT_FILE_TYPE_ROOT_FIXED || m_type == FAT_FILE_TYPE_ROOT32; | |||||
| } | |||||
| void ls(Print* pr, uint8_t flags = 0, uint8_t indent = 0); | |||||
| void ls(uint8_t flags = 0); | |||||
| bool mkdir(SdBaseFile* dir, const char* path, bool pFlag = true); | |||||
| // alias for backward compactability | |||||
| bool makeDir(SdBaseFile* dir, const char* path) { | |||||
| return mkdir(dir, path, false); | |||||
| } | |||||
| bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag); | |||||
| bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag); | |||||
| bool open(const char* path, uint8_t oflag = O_READ); | |||||
| bool openNext(SdBaseFile* dirFile, uint8_t oflag); | |||||
| bool openRoot(SdVolume* vol); | |||||
| int peek(); | |||||
| bool printCreateDateTime(Print* pr); | |||||
| static void printFatDate(uint16_t fatDate); | |||||
| static void printFatDate(Print* pr, uint16_t fatDate); | |||||
| static void printFatTime(uint16_t fatTime); | |||||
| static void printFatTime(Print* pr, uint16_t fatTime); | |||||
| int printField(float value, char term, uint8_t prec = 2); | |||||
| int printField(int16_t value, char term); | |||||
| int printField(uint16_t value, char term); | |||||
| int printField(int32_t value, char term); | |||||
| int printField(uint32_t value, char term); | |||||
| bool printModifyDateTime(Print* pr); | |||||
| size_t printName(); | |||||
| size_t printName(Print* pr); | |||||
| size_t printFileSize(Print* pr); | |||||
| int16_t read(); | |||||
| int read(void* buf, size_t nbyte); | |||||
| int8_t readDir(dir_t* dir); | |||||
| static bool remove(SdBaseFile* dirFile, const char* path); | |||||
| bool remove(); | |||||
| /** Set the file's current position to zero. */ | |||||
| void rewind() {seekSet(0);} | |||||
| bool rename(SdBaseFile* dirFile, const char* newPath); | |||||
| bool rmdir(); | |||||
| // for backward compatibility | |||||
| bool rmDir() {return rmdir();} | |||||
| bool rmRfStar(); | |||||
| /** Set the files position to current position + \a pos. See seekSet(). | |||||
| * \param[in] offset The new position in bytes from the current position. | |||||
| * \return true for success or false for failure. | |||||
| */ | |||||
| bool seekCur(int32_t offset) { | |||||
| return seekSet(m_curPosition + offset); | |||||
| } | |||||
| /** Set the files position to end-of-file + \a offset. See seekSet(). | |||||
| * \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 seekSet(uint32_t pos); | |||||
| bool sync(); | |||||
| bool timestamp(SdBaseFile* file); | |||||
| bool timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, | |||||
| uint8_t hour, uint8_t minute, uint8_t second); | |||||
| /** Type of file. You should use isFile() or isDir() instead of type() | |||||
| * if possible. | |||||
| * | |||||
| * \return The file or directory type. | |||||
| */ | |||||
| uint8_t type() const {return m_type;} | |||||
| bool truncate(uint32_t size); | |||||
| /** \return SdVolume that contains this file. */ | |||||
| SdVolume* volume() const {return m_vol;} | |||||
| int write(const void* buf, size_t nbyte); | |||||
| //------------------------------------------------------------------------------ | |||||
| private: | |||||
| // allow SdFat to set m_cwd | |||||
| friend class SdFat; | |||||
| /** experimental don't use */ | |||||
| bool openParent(SdBaseFile* dir); | |||||
| // private functions | |||||
| bool addCluster(); | |||||
| cache_t* addDirCluster(); | |||||
| dir_t* cacheDirEntry(uint8_t action); | |||||
| int8_t lsPrintNext(Print *pr, uint8_t flags, uint8_t indent); | |||||
| static bool make83Name(const char* str, uint8_t* name, const char** ptr); | |||||
| bool mkdir(SdBaseFile* parent, const uint8_t dname[11]); | |||||
| bool open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag); | |||||
| bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags); | |||||
| dir_t* readDirCache(); | |||||
| static void setCwd(SdBaseFile* cwd) {m_cwd = cwd;} | |||||
| bool setDirSize(); | |||||
| // bits defined in m_flags | |||||
| // should be 0X0F | |||||
| static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); | |||||
| // sync of directory entry required | |||||
| static uint8_t const F_FILE_DIR_DIRTY = 0X80; | |||||
| // global pointer to cwd dir | |||||
| static SdBaseFile* m_cwd; | |||||
| // data time callback function | |||||
| static void (*m_dateTime)(uint16_t* date, uint16_t* time); | |||||
| // private data | |||||
| uint8_t m_flags; // See above for definition of m_flags bits | |||||
| uint8_t m_type; // type of file see above for values | |||||
| uint8_t m_dirIndex; // index of directory entry in dirBlock | |||||
| SdVolume* m_vol; // volume where file is located | |||||
| uint32_t m_curCluster; // cluster for current file position | |||||
| uint32_t m_curPosition; // current file position in bytes from beginning | |||||
| uint32_t m_dirBlock; // block for this files directory entry | |||||
| uint32_t m_fileSize; // file size in bytes | |||||
| uint32_t m_firstCluster; // first cluster of file | |||||
| }; | |||||
| #endif // SdBaseFile_h |
| /* 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 | |||||
| * 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 Arduino SdFat Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #include <SdFat.h> | |||||
| #include <utility/FmtNumber.h> | |||||
| //------------------------------------------------------------------------------ | |||||
| /** List directory contents to stdOut. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| */ | |||||
| void SdBaseFile::ls(uint8_t flags) { | |||||
| ls(SdFat::stdOut(), flags, 0); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** List directory contents. | |||||
| * | |||||
| * \param[in] pr Print stream for list. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| * | |||||
| * \param[in] indent Amount of space before file name. Used for recursive | |||||
| * list to indicate subdirectory level. | |||||
| */ | |||||
| void SdBaseFile::ls(Print* pr, uint8_t flags, uint8_t indent) { | |||||
| if (!isDir()) { | |||||
| pr->println(F("bad dir")); | |||||
| return; | |||||
| } | |||||
| rewind(); | |||||
| int8_t status; | |||||
| while ((status = lsPrintNext(pr, flags, indent))) { | |||||
| if (status > 1 && (flags & LS_R)) { | |||||
| uint16_t index = curPosition()/32 - 1; | |||||
| SdBaseFile s; | |||||
| if (s.open(this, index, O_READ)) s.ls(pr, flags, indent + 2); | |||||
| seekSet(32 * (index + 1)); | |||||
| } | |||||
| } | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| // saves 32 bytes on stack for ls recursion | |||||
| // return 0 - EOF, 1 - normal file, or 2 - directory | |||||
| int8_t SdBaseFile::lsPrintNext(Print *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(' '); | |||||
| pr->print(dir.fileSize); | |||||
| } | |||||
| pr->println(); | |||||
| return DIR_IS_FILE(&dir) ? 1 : 2; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| // print uint8_t with width 2 | |||||
| static void print2u(Print* pr, uint8_t v) { | |||||
| if (v < 10) pr->write('0'); | |||||
| pr->print(v, DEC); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Print a file's creation date and time | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool SdBaseFile::printCreateDateTime(Print* pr) { | |||||
| dir_t dir; | |||||
| if (!dirEntry(&dir)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| printFatDate(pr, dir.creationDate); | |||||
| pr->write(' '); | |||||
| printFatTime(pr, dir.creationTime); | |||||
| return true; | |||||
| fail: | |||||
| return false; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print a directory date field to stdOut. | |||||
| * | |||||
| * Format is yyyy-mm-dd. | |||||
| * | |||||
| * \param[in] fatDate The date field from a directory entry. | |||||
| */ | |||||
| void SdBaseFile::printFatDate(uint16_t fatDate) { | |||||
| printFatDate(SdFat::stdOut(), fatDate); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print a directory date field. | |||||
| * | |||||
| * Format is yyyy-mm-dd. | |||||
| * | |||||
| * \param[in] pr Print stream for output. | |||||
| * \param[in] fatDate The date field from a directory entry. | |||||
| */ | |||||
| void SdBaseFile::printFatDate(Print* pr, uint16_t fatDate) { | |||||
| pr->print(FAT_YEAR(fatDate)); | |||||
| pr->write('-'); | |||||
| print2u(pr, FAT_MONTH(fatDate)); | |||||
| pr->write('-'); | |||||
| print2u(pr, FAT_DAY(fatDate)); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print a directory time field to stdOut. | |||||
| * | |||||
| * Format is hh:mm:ss. | |||||
| * | |||||
| * \param[in] fatTime The time field from a directory entry. | |||||
| */ | |||||
| void SdBaseFile::printFatTime(uint16_t fatTime) { | |||||
| printFatTime(SdFat::stdOut(), fatTime); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print a directory time field. | |||||
| * | |||||
| * Format is hh:mm:ss. | |||||
| * | |||||
| * \param[in] pr Print stream for output. | |||||
| * \param[in] fatTime The time field from a directory entry. | |||||
| */ | |||||
| void SdBaseFile::printFatTime(Print* pr, uint16_t fatTime) { | |||||
| print2u(pr, FAT_HOUR(fatTime)); | |||||
| pr->write(':'); | |||||
| print2u(pr, FAT_MINUTE(fatTime)); | |||||
| pr->write(':'); | |||||
| print2u(pr, FAT_SECOND(fatTime)); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Template for SdBaseFile::printField() */ | |||||
| template <typename Type> | |||||
| static int printFieldT(SdBaseFile* file, char sign, Type value, char term) { | |||||
| char buf[3*sizeof(Type) + 3]; | |||||
| char* str = &buf[sizeof(buf)]; | |||||
| if (term) { | |||||
| *--str = term; | |||||
| if (term == '\n') { | |||||
| *--str = '\r'; | |||||
| } | |||||
| } | |||||
| #ifdef OLD_FMT | |||||
| do { | |||||
| Type m = value; | |||||
| value /= 10; | |||||
| *--str = '0' + m - 10*value; | |||||
| } while (value); | |||||
| #else // OLD_FMT | |||||
| str = fmtDec(value, str); | |||||
| #endif // OLD_FMT | |||||
| if (sign) { | |||||
| *--str = sign; | |||||
| } | |||||
| return file->write(str, &buf[sizeof(buf)] - str); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Print a number followed by a field terminator. | |||||
| * \param[in] value The number to be printed. | |||||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||||
| * \param[in] prec Number of digits after decimal point. | |||||
| * \return The number of bytes written or -1 if an error occurs. | |||||
| */ | |||||
| int SdBaseFile::printField(float value, char term, uint8_t prec) { | |||||
| char buf[24]; | |||||
| char* str = &buf[sizeof(buf)]; | |||||
| if (term) { | |||||
| *--str = term; | |||||
| if (term == '\n') { | |||||
| *--str = '\r'; | |||||
| } | |||||
| } | |||||
| str = fmtFloat(value, str, prec); | |||||
| return write(str, buf + sizeof(buf) - str); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Print a number followed by a field terminator. | |||||
| * \param[in] value The number to be printed. | |||||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||||
| * \return The number of bytes written or -1 if an error occurs. | |||||
| */ | |||||
| int SdBaseFile::printField(uint16_t value, char term) { | |||||
| return printFieldT(this, 0, value, term); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Print a number followed by a field terminator. | |||||
| * \param[in] value The number to be printed. | |||||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||||
| * \return The number of bytes written or -1 if an error occurs. | |||||
| */ | |||||
| int SdBaseFile::printField(int16_t value, char term) { | |||||
| char sign = 0; | |||||
| if (value < 0) { | |||||
| sign = '-'; | |||||
| value = -value; | |||||
| } | |||||
| return printFieldT(this, sign, (uint16_t)value, term); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Print a number followed by a field terminator. | |||||
| * \param[in] value The number to be printed. | |||||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||||
| * \return The number of bytes written or -1 if an error occurs. | |||||
| */ | |||||
| int SdBaseFile::printField(uint32_t value, char term) { | |||||
| return printFieldT(this, 0, value, term); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Print a number followed by a field terminator. | |||||
| * \param[in] value The number to be printed. | |||||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||||
| * \return The number of bytes written or -1 if an error occurs. | |||||
| */ | |||||
| int SdBaseFile::printField(int32_t value, char term) { | |||||
| char sign = 0; | |||||
| if (value < 0) { | |||||
| sign = '-'; | |||||
| value = -value; | |||||
| } | |||||
| return printFieldT(this, sign, (uint32_t)value, term); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Print a file's modify date and time | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool SdBaseFile::printModifyDateTime(Print* pr) { | |||||
| dir_t dir; | |||||
| if (!dirEntry(&dir)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| printFatDate(pr, dir.lastWriteDate); | |||||
| pr->write(' '); | |||||
| printFatTime(pr, dir.lastWriteTime); | |||||
| return true; | |||||
| fail: | |||||
| return false; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** 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. | |||||
| */ | |||||
| size_t SdBaseFile::printName(Print* pr) { | |||||
| char name[13]; | |||||
| if (!getFilename(name)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| return pr->print(name); | |||||
| fail: | |||||
| return 0; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Print a file's name to stdOut | |||||
| * | |||||
| * \return The value one, true, is returned for success and | |||||
| * the value zero, false, is returned for failure. | |||||
| */ | |||||
| size_t SdBaseFile::printName() { | |||||
| return printName(SdFat::stdOut()); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** 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. | |||||
| */ | |||||
| size_t SdBaseFile::printFileSize(Print* pr) { | |||||
| char buf[10]; | |||||
| char *ptr = fmtDec(fileSize(), buf + sizeof(buf)); | |||||
| while (ptr > buf) *--ptr = ' '; | |||||
| return pr->write(reinterpret_cast<uint8_t *>(buf), sizeof(buf)); | |||||
| } |
| /* 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 | |||||
| * 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 Arduino SdFat Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #include <SdFat.h> | |||||
| //------------------------------------------------------------------------------ | |||||
| #if USE_SERIAL_FOR_STD_OUT || !defined(UDR0) | |||||
| Print* SdFat::m_stdOut = &Serial; | |||||
| #else // USE_SERIAL_FOR_STD_OUT | |||||
| #include <MinimumSerial.h> | |||||
| Print* SdFat::m_stdOut = &MiniSerial; | |||||
| #endif // USE_SERIAL_FOR_STD_OUT | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Initialize an SdFat object. | |||||
| * | |||||
| * Initializes the SD card, SD volume, and root directory. | |||||
| * | |||||
| * \param[in] chipSelectPin SD chip select pin. See Sd2Card::init(). | |||||
| * \param[in] sckDivisor value for SPI SCK divisor. See Sd2Card::init(). | |||||
| * | |||||
| * \return The value one, true, is returned for success and | |||||
| * the value zero, false, is returned for failure. | |||||
| */ | |||||
| bool SdFat::begin(uint8_t chipSelectPin, uint8_t sckDivisor) { | |||||
| return m_card.begin(chipSelectPin, sckDivisor) | |||||
| && m_vol.init(&m_card) && chdir(1); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Change a volume's working directory to root | |||||
| * | |||||
| * Changes the volume's working directory to the SD's root directory. | |||||
| * Optionally set the current working directory to the volume's | |||||
| * working directory. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool SdFat::chdir(bool set_cwd) { | |||||
| if (set_cwd) SdBaseFile::setCwd(&m_vwd); | |||||
| if (m_vwd.isOpen()) m_vwd.close(); | |||||
| return m_vwd.openRoot(&m_vol); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Change a volume's working directory | |||||
| * | |||||
| * Changes the volume working directory to the \a path subdirectory. | |||||
| * Optionally set the current working directory to the volume's | |||||
| * working directory. | |||||
| * | |||||
| * Example: If the volume's working directory is "/DIR", chdir("SUB") | |||||
| * will change the volume's working directory from "/DIR" to "/DIR/SUB". | |||||
| * | |||||
| * If path is "/", the volume's working directory will be changed to the | |||||
| * root directory | |||||
| * | |||||
| * \param[in] path The name of the subdirectory. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool SdFat::chdir(const char *path, bool set_cwd) { | |||||
| SdBaseFile dir; | |||||
| dir.open(&m_vwd, path, O_READ); | |||||
| // Check for correctly open directory. | |||||
| if (!dir.isDir()) goto fail; | |||||
| m_vwd = dir; | |||||
| if (set_cwd) SdBaseFile::setCwd(&m_vwd); | |||||
| return true; | |||||
| fail: | |||||
| return false; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Set the current working directory to a volume's working directory. | |||||
| * | |||||
| * This is useful with multiple SD cards. | |||||
| * | |||||
| * The current working directory is changed to this volume's working directory. | |||||
| * | |||||
| * This is like the Windows/DOS \<drive letter>: command. | |||||
| */ | |||||
| void SdFat::chvol() { | |||||
| SdBaseFile::setCwd(&m_vwd); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Test for the existence of a file. | |||||
| * | |||||
| * \param[in] name Name of the file to be tested for. | |||||
| * | |||||
| * \return true if the file exists else false. | |||||
| */ | |||||
| bool SdFat::exists(const char* name) { | |||||
| return m_vwd.exists(name); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** List the directory contents of the volume working directory to stdOut. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| */ | |||||
| void SdFat::ls(uint8_t flags) { | |||||
| m_vwd.ls(m_stdOut, flags); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** List the directory contents of the volume working directory to stdOut. | |||||
| * | |||||
| * \param[in] path directory to list. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| */ | |||||
| void SdFat::ls(const char* path, uint8_t flags) { | |||||
| ls(m_stdOut, path, flags); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** List the directory contents of the volume working directory. | |||||
| * | |||||
| * \param[in] pr Print stream for the list. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| */ | |||||
| void SdFat::ls(Print* pr, uint8_t flags) { | |||||
| m_vwd.ls(pr, flags); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** List the directory contents of the volume working directory to stdOut. | |||||
| * | |||||
| * \param[in] pr Print stream for the list. | |||||
| * | |||||
| * \param[in] path directory to list. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| */ | |||||
| void SdFat::ls(Print* pr, const char* path, uint8_t flags) { | |||||
| SdBaseFile dir(path, O_READ); | |||||
| dir.ls(pr, flags); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Make a subdirectory in the volume working directory. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool SdFat::mkdir(const char* path, bool pFlag) { | |||||
| SdBaseFile sub; | |||||
| return sub.mkdir(&m_vwd, path, pFlag); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Remove a file from the volume working directory. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool SdFat::remove(const char* path) { | |||||
| return SdBaseFile::remove(&m_vwd, path); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Rename a file or subdirectory. | |||||
| * | |||||
| * \param[in] oldPath Path name to the file or subdirectory to be renamed. | |||||
| * | |||||
| * \param[in] newPath New path name of the file or subdirectory. | |||||
| * | |||||
| * The \a newPath object must not exist before the rename call. | |||||
| * | |||||
| * The file to be renamed must not be open. The directory entry may be | |||||
| * 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. | |||||
| */ | |||||
| bool SdFat::rename(const char *oldPath, const char *newPath) { | |||||
| SdBaseFile file; | |||||
| if (!file.open(oldPath, O_READ)) return false; | |||||
| return file.rename(&m_vwd, newPath); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Remove a subdirectory from the volume's working directory. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. | |||||
| * | |||||
| * 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. | |||||
| */ | |||||
| bool SdFat::rmdir(const char* path) { | |||||
| SdBaseFile sub; | |||||
| if (!sub.open(path, O_READ)) return false; | |||||
| return sub.rmdir(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** 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] 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. | |||||
| */ | |||||
| bool SdFat::truncate(const char* path, uint32_t length) { | |||||
| SdBaseFile file; | |||||
| if (!file.open(path, O_WRITE)) return false; | |||||
| return file.truncate(length); | |||||
| } |
| * \file | * \file | ||||
| * \brief SdFat class | * \brief SdFat class | ||||
| */ | */ | ||||
| //------------------------------------------------------------------------------ | |||||
| /** Macro for debug. */ | |||||
| #define DBG_FAIL_MACRO // Serial.print(__FILE__);Serial.println(__LINE__) | |||||
| #include "SdSpiCard.h" | |||||
| #include "SdFile.h" | |||||
| #include "utility/FatLib.h" | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** SdFat version YYYYMMDD */ | /** SdFat version YYYYMMDD */ | ||||
| #define SD_FAT_VERSION 20141025 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** error if old IDE */ | |||||
| #if !defined(ARDUINO) || ARDUINO < 100 | |||||
| #error Arduino IDE must be 1.0 or greater | |||||
| #endif // ARDUINO < 100 | |||||
| //------------------------------------------------------------------------------ | |||||
| #include <SdFile.h> | |||||
| #include <SdStream.h> | |||||
| #include <StdioStream.h> | |||||
| #include <ArduinoStream.h> | |||||
| #include <MinimumSerial.h> | |||||
| //------------------------------------------------------------------------------ | |||||
| #define SD_FAT_VERSION 20141115 | |||||
| //============================================================================== | |||||
| /** | /** | ||||
| * \class SdFat | |||||
| * \brief Integration class for the %SdFat library. | |||||
| * \class SdFatBase | |||||
| * \brief Virtual base class for %SdFat library. | |||||
| */ | */ | ||||
| class SdFat { | |||||
| class SdFatBase : public FatFileSystem { | |||||
| public: | public: | ||||
| SdFat() {} | |||||
| /** \return a pointer to the Sd2Card object. */ | |||||
| Sd2Card* card() {return &m_card;} | |||||
| bool chdir(bool set_cwd = false); | |||||
| bool chdir(const char* path, bool set_cwd = false); | |||||
| void chvol(); | |||||
| void errorHalt(); | |||||
| void errorHalt(char const *msg); | |||||
| void errorPrint(); | |||||
| void errorPrint(char const *msg); | |||||
| bool exists(const char* name); | |||||
| bool begin(uint8_t chipSelectPin = SD_CHIP_SELECT_PIN, | |||||
| uint8_t sckDivisor = SPI_FULL_SPEED); | |||||
| void initErrorHalt(); | |||||
| void initErrorHalt(char const *msg); | |||||
| void initErrorPrint(); | |||||
| void initErrorPrint(char const *msg); | |||||
| void ls(uint8_t flags = 0); | |||||
| void ls(const char* path, uint8_t flags = 0); | |||||
| void ls(Print* pr, uint8_t flags = 0); | |||||
| void ls(Print* pr, const char* path, uint8_t flags = 0); | |||||
| bool mkdir(const char* path, bool pFlag = true); | |||||
| bool remove(const char* path); | |||||
| bool rename(const char *oldPath, const char *newPath); | |||||
| bool rmdir(const char* path); | |||||
| bool truncate(const char* path, uint32_t length); | |||||
| /** \return a pointer to the SdVolume object. */ | |||||
| SdVolume* vol() {return &m_vol;} | |||||
| /** \return a pointer to the volume working directory. */ | |||||
| SdBaseFile* vwd() {return &m_vwd;} | |||||
| //---------------------------------------------------------------------------- | |||||
| void errorHalt_P(PGM_P msg); | |||||
| void errorPrint_P(PGM_P msg); | |||||
| void initErrorHalt_P(PGM_P msg); | |||||
| void initErrorPrint_P(PGM_P msg); | |||||
| //---------------------------------------------------------------------------- | |||||
| /** | |||||
| * Set stdOut Print stream for messages. | |||||
| * \param[in] stream The new Print stream. | |||||
| */ | |||||
| static void setStdOut(Print* stream) {m_stdOut = stream;} | |||||
| /** \return Print stream for messages. */ | |||||
| static Print* stdOut() {return m_stdOut;} | |||||
| //---------------------------------------------------------------------------- | |||||
| /** open a file | |||||
| /** Initialize SD card and file system. | |||||
| * \param[in] spi SPI object for the card. | |||||
| * \param[in] csPin SD card chip select pin. | |||||
| * \param[in] divisor SPI divisor. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| 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); | |||||
| } | |||||
| /** \return Pointer to SD card object */ | |||||
| SdSpiCard *card() {return &m_sdCard;} | |||||
| /** %Print any SD error code to Serial and halt. */ | |||||
| void errorHalt() {errorHalt(&Serial);} | |||||
| /** %Print any SD error code and halt. | |||||
| * | |||||
| * \param[in] pr Print destination. | |||||
| */ | |||||
| void errorHalt(Print* pr); | |||||
| /** %Print msg, any SD error code and halt. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void errorHalt(char const* msg) {errorHalt(&Serial, msg);} | |||||
| /** %Print msg, any SD error code, and halt. | |||||
| * | |||||
| * \param[in] pr Print destination. | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void errorHalt(Print* pr, char const* msg); | |||||
| /** %Print msg, any SD error code, and halt. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void errorHalt(const __FlashStringHelper* msg) {errorHalt(&Serial, msg);} | |||||
| /** %Print msg, any SD error code, and halt. | |||||
| * | |||||
| * \param[in] pr Print destination. | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void errorHalt(Print* pr, const __FlashStringHelper* msg); | |||||
| /** %Print any SD error code to Serial */ | |||||
| void errorPrint() {errorPrint(&Serial);} | |||||
| /** %Print any SD error code. | |||||
| * \param[in] pr Print device. | |||||
| */ | |||||
| void errorPrint(Print* pr); | |||||
| /** %Print msg, any SD error code. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void errorPrint(const char* 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, char const* msg); | |||||
| /** %Print msg, any SD error code. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| 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. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool fsBegin() {return FatFileSystem::begin(&m_vwd);} | |||||
| /** %Print any SD error code and halt. */ | |||||
| void initErrorHalt() {initErrorHalt(&Serial);} | |||||
| /** %Print error details and halt after begin fails. | |||||
| * | |||||
| * \param[in] pr Print destination. | |||||
| */ | |||||
| void initErrorHalt(Print* pr); | |||||
| /**Print message, error details, and halt after SdFat::init() fails. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| 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. | |||||
| */ | |||||
| void initErrorHalt(const __FlashStringHelper* msg) { | |||||
| initErrorHalt(&Serial, msg); | |||||
| } | |||||
| void initErrorHalt(Print* pr, const __FlashStringHelper* msg); | |||||
| /** Print error details after SdFat::init() fails. */ | |||||
| void initErrorPrint() {initErrorPrint(&Serial);} | |||||
| /** Print error details after SdFatBase::init() fails. | |||||
| * | |||||
| * \param[in] pr Print destination. | |||||
| */ | |||||
| void initErrorPrint(Print* pr); | |||||
| /**Print message and error details and halt after SdFat::init() fails. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void initErrorPrint(char const *msg) {initErrorPrint(&Serial, msg);} | |||||
| /**Print message and error details and halt after SdFatBase::init() fails. | |||||
| * | |||||
| * \param[in] pr Print destination. | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void initErrorPrint(Print* pr, char const *msg); | |||||
| /**Print message and error details and halt after SdFat::init() fails. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void initErrorPrint(const __FlashStringHelper* msg) { | |||||
| initErrorPrint(&Serial, msg); | |||||
| } | |||||
| /**Print message and error details and halt after SdFatBase::init() fails. | |||||
| * | |||||
| * \param[in] pr Print destination. | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void initErrorPrint(Print* pr, const __FlashStringHelper* msg); | |||||
| /** List the directory contents of the volume working directory to Serial. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| */ | |||||
| void ls(uint8_t flags = 0) { | |||||
| ls(&Serial, flags); | |||||
| } | |||||
| /** List the directory contents of a directory to Serial. | |||||
| * | |||||
| * \param[in] path directory to list. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| */ | |||||
| void ls(const char* path, uint8_t flags = 0) { | |||||
| ls(&Serial, path, flags); | |||||
| } | |||||
| /** open a file | |||||
| * | * | ||||
| * \param[in] path location of file to be opened. | * \param[in] path location of file to be opened. | ||||
| * \param[in] mode open mode flags. | * \param[in] mode open mode flags. | ||||
| * \return a File object. | * \return a File object. | ||||
| */ | |||||
| File open(const char *path, uint8_t mode = FILE_READ) { | |||||
| File tmpFile; | |||||
| tmpFile.open(&m_vwd, path, mode); | |||||
| return tmpFile; | |||||
| } | |||||
| */ | |||||
| File open(const char *path, uint8_t mode = FILE_READ); | |||||
| /** \return a pointer to the volume working directory. */ | |||||
| SdBaseFile* vwd() {return &m_vwd;} | |||||
| using FatFileSystem::ls; | |||||
| private: | private: | ||||
| Sd2Card m_card; | |||||
| SdVolume m_vol; | |||||
| 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); | |||||
| } | |||||
| bool writeBlock(uint32_t block, const uint8_t* src) { | |||||
| return m_sdCard.writeBlock(block, src); | |||||
| } | |||||
| bool readBlocks(uint32_t block, uint8_t* dst, size_t n) { | |||||
| return m_sdCard.readBlocks(block, dst, n); | |||||
| } | |||||
| bool writeBlocks(uint32_t block, const uint8_t* src, size_t n) { | |||||
| return m_sdCard.writeBlocks(block, src, n); | |||||
| } | |||||
| SdBaseFile m_vwd; | SdBaseFile m_vwd; | ||||
| static Print* m_stdOut; | |||||
| SdSpiCard m_sdCard; | |||||
| }; | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class SdFat | |||||
| * \brief Main file system class for %SdFat library. | |||||
| */ | |||||
| class SdFat : 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. | |||||
| */ | |||||
| bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return SdFatBase::begin(&m_spi, csPin, divisor); | |||||
| } | |||||
| /** Initialize SD card - use for diagnostic purposes. | |||||
| * \param[in] csPin SD card chip select pin. | |||||
| * \param[in] divisor SPI divisor. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return card()->begin(&m_spi, csPin, divisor); | |||||
| } | |||||
| private: | |||||
| SpiDefault_t m_spi; | |||||
| }; | |||||
| //============================================================================== | |||||
| #if USE_MULTIPLE_SPI_TYPES || 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. | |||||
| */ | |||||
| bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return SdFatBase::begin(&m_spi, csPin, divisor); | |||||
| } | |||||
| /** Initialize SD card - use for diagnostic purposes. | |||||
| * \param[in] csPin SD card chip select pin. | |||||
| * \param[in] divisor SPI divisor. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return card()->begin(&m_spi, csPin, divisor); | |||||
| } | |||||
| private: | |||||
| SdSpiLib m_spi; | |||||
| }; | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class SdFatSoftSpi | |||||
| * \brief SdFat class using software SPI. | |||||
| */ | |||||
| template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||||
| class SdFatSoftSpi : 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. | |||||
| */ | |||||
| bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return SdFatBase::begin(&m_spi, csPin, divisor); | |||||
| } | |||||
| /** Initialize SD card - use for diagnostic purposes. | |||||
| * \param[in] csPin SD card chip select pin. | |||||
| * \param[in] divisor SPI divisor. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return card()->begin(&m_spi, csPin, divisor); | |||||
| } | |||||
| private: | |||||
| SdSpiSoft<MisoPin, MosiPin, SckPin> m_spi; | |||||
| }; | }; | ||||
| #endif // USE_MULTIPLE_SPI_TYPES | |||||
| #endif // SdFat_h | #endif // SdFat_h |
| /* 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 | |||||
| * 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 Arduino SdFat Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #include "SdFat.h" | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::errorHalt(Print* pr) { | |||||
| errorPrint(pr); | |||||
| while (1) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::errorHalt(Print* pr, char const* msg) { | |||||
| errorPrint(pr, msg); | |||||
| while (1) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::errorHalt(Print* pr, const __FlashStringHelper* msg) { | |||||
| errorPrint(pr, msg); | |||||
| while (1) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::errorPrint(Print* pr) { | |||||
| if (!cardErrorCode()) return; | |||||
| pr->print(F("SD errorCode: 0X")); | |||||
| pr->print(cardErrorCode(), HEX); | |||||
| pr->print(F(",0X")); | |||||
| pr->println(cardErrorData(), HEX); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::errorPrint(Print* pr, char const* msg) { | |||||
| pr->print(F("error: ")); | |||||
| pr->println(msg); | |||||
| errorPrint(pr); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::errorPrint(Print* pr, const __FlashStringHelper* msg) { | |||||
| pr->print(F("error: ")); | |||||
| pr->println(msg); | |||||
| errorPrint(pr); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::initErrorHalt(Print* pr) { | |||||
| initErrorPrint(pr); | |||||
| while (1) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::initErrorHalt(Print* pr, char const *msg) { | |||||
| pr->println(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); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::initErrorPrint(Print* pr) { | |||||
| if (cardErrorCode()) { | |||||
| pr->println(F("Can't access SD card. Do not reformat.")); | |||||
| if (cardErrorCode() == SD_CARD_ERROR_CMD0) { | |||||
| pr->println(F("No card, wrong chip select pin, or SPI problem?")); | |||||
| } | |||||
| errorPrint(pr); | |||||
| } else if (vol()->fatType() == 0) { | |||||
| pr->println(F("Invalid format, reformat SD.")); | |||||
| } else if (!vwd()->isOpen()) { | |||||
| pr->println(F("Can't open root directory.")); | |||||
| } else { | |||||
| pr->println(F("No error found.")); | |||||
| } | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::initErrorPrint(Print* pr, char const *msg) { | |||||
| pr->println(msg); | |||||
| initErrorPrint(pr); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::initErrorPrint(Print* pr, const __FlashStringHelper* msg) { | |||||
| pr->println(msg); | |||||
| initErrorPrint(pr); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| File SdFatBase::open(const char *path, uint8_t mode) { | |||||
| File tmpFile; | |||||
| tmpFile.open(&m_vwd, path, mode); | |||||
| return tmpFile; | |||||
| } |
| #endif // __AVR__ | #endif // __AVR__ | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * 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. | |||||
| * 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 SD_FILE_USES_STREAM 0 | |||||
| #define USE_MULTIPLE_SPI_TYPES 0 | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * To enable SD card CRC checking set USE_SD_CRC nonzero. | * To enable SD card CRC checking set USE_SD_CRC nonzero. | ||||
| */ | */ | ||||
| #define USE_SD_CRC 0 | #define USE_SD_CRC 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * To use multiple SD cards set USE_MULTIPLE_CARDS nonzero. | |||||
| * | |||||
| * Using multiple cards costs about 200 bytes of flash. | |||||
| * | |||||
| * Each card requires about 550 bytes of SRAM so use of a Mega is recommended. | |||||
| */ | |||||
| #define USE_MULTIPLE_CARDS 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor. | |||||
| * | |||||
| * Causes use of lots of heap in ARM. | |||||
| */ | |||||
| #define DESTRUCTOR_CLOSES_FILE 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * For AVR | |||||
| * | |||||
| * Set USE_SERIAL_FOR_STD_OUT nonzero to use Serial (the HardwareSerial class) | |||||
| * for error messages and output from print functions like ls(). | |||||
| * | |||||
| * If USE_SERIAL_FOR_STD_OUT is zero, a small non-interrupt driven class | |||||
| * is used to output messages to serial port zero. This allows an alternate | |||||
| * Serial library like SerialPort to be used with SdFat. | |||||
| * | |||||
| * You can redirect stdOut with SdFat::setStdOut(Print* stream) and | |||||
| * get the current stream with SdFat::stdOut(). | |||||
| */ | |||||
| #define USE_SERIAL_FOR_STD_OUT 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Set FAT12_SUPPORT nonzero to enable use if FAT12 volumes. | |||||
| * FAT12 has not been well tested and requires additional flash. | |||||
| */ | |||||
| #define FAT12_SUPPORT 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | /** | ||||
| * Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature | * Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature | ||||
| * of the standard Arduino SPI library. You must include SPI.h in your | * of the standard Arduino SPI library. You must include SPI.h in your | ||||
| #define ENABLE_SPI_YIELD 0 | #define ENABLE_SPI_YIELD 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * Set USE_ARDUINO_SPI_LIBRARY nonzero to force use of Arduino Standard | |||||
| * SPI library. This will override native and software SPI for all boards. | |||||
| * 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 | #define USE_ARDUINO_SPI_LIBRARY 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * Set AVR_SOFT_SPI nonzero to use software SPI on all AVR Arduinos. | |||||
| * 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 | #define AVR_SOFT_SPI 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * Set DUE_SOFT_SPI nonzero to use software SPI on Due Arduinos. | |||||
| * 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 | #define DUE_SOFT_SPI 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * Set LEONARDO_SOFT_SPI nonzero to use software SPI on Leonardo Arduinos. | |||||
| * 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 | * LEONARDO_SOFT_SPI allows an unmodified 328 Shield to be used | ||||
| * on Leonardo Arduinos. | * on Leonardo Arduinos. | ||||
| */ | */ | ||||
| #define LEONARDO_SOFT_SPI 0 | #define LEONARDO_SOFT_SPI 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * Set MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos. | |||||
| * 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 | * MEGA_SOFT_SPI allows an unmodified 328 Shield to be used | ||||
| * on Mega Arduinos. | |||||
| * on Mega Arduinos. Set the soft SPI pins below. | |||||
| */ | */ | ||||
| #define MEGA_SOFT_SPI 0 | #define MEGA_SOFT_SPI 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * Set TEENSY3_SOFT_SPI nonzero to use software SPI on Teensy 3.x boards. | |||||
| * 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 TEENSY3_SOFT_SPI 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| * boards. | * boards. | ||||
| */ | */ | ||||
| // define software SPI pins | // define software SPI pins | ||||
| /** Default Software SPI chip select pin */ | |||||
| uint8_t const SOFT_SPI_CS_PIN = 10; | |||||
| /** Software SPI Master Out Slave In pin */ | /** Software SPI Master Out Slave In pin */ | ||||
| uint8_t const SOFT_SPI_MOSI_PIN = 11; | uint8_t const SOFT_SPI_MOSI_PIN = 11; | ||||
| /** Software SPI Master In Slave Out pin */ | /** Software SPI Master In Slave Out pin */ | ||||
| /** Software SPI Clock pin */ | /** Software SPI Clock pin */ | ||||
| uint8_t const SOFT_SPI_SCK_PIN = 13; | 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. | |||||
| */ | |||||
| #define FAT12_SUPPORT 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor. | |||||
| * | |||||
| * Causes use of lots of heap in ARM. | |||||
| */ | |||||
| #define DESTRUCTOR_CLOSES_FILE 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | /** | ||||
| * Call flush for endl if ENDL_CALLS_FLUSH is nonzero | * Call flush for endl if ENDL_CALLS_FLUSH is nonzero | ||||
| * | * | ||||
| */ | */ | ||||
| #define ENDL_CALLS_FLUSH 0 | #define ENDL_CALLS_FLUSH 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * 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. | |||||
| */ | |||||
| #define SD_FILE_USES_STREAM 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | /** | ||||
| * SPI SCK divisor for SD initialization commands. | * SPI SCK divisor for SD initialization commands. | ||||
| * or greater | * or greater | ||||
| * Don't use mult-block read/write on small AVR boards. | * Don't use mult-block read/write on small AVR boards. | ||||
| */ | */ | ||||
| #if defined(RAMEND) && RAMEND < 3000 | #if defined(RAMEND) && RAMEND < 3000 | ||||
| #define USE_MULTI_BLOCK_SD_IO 0 | |||||
| #define USE_MULTI_BLOCK_IO 0 | |||||
| #else // RAMEND | #else // RAMEND | ||||
| #define USE_MULTI_BLOCK_SD_IO 1 | |||||
| #define USE_MULTI_BLOCK_IO 1 | |||||
| #endif // RAMEND | #endif // RAMEND | ||||
| #endif // SdFatConfig_h | #endif // SdFatConfig_h |
| /* 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 | |||||
| * 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 Arduino SdFat Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #include <SdFat.h> | |||||
| #ifndef PSTR | |||||
| #define PSTR(x) x | |||||
| #define PGM_P const char* | |||||
| #endif | |||||
| //------------------------------------------------------------------------------ | |||||
| static void pstrPrint(PGM_P str) { | |||||
| for (uint8_t c; (c = pgm_read_byte(str)); str++) SdFat::stdOut()->write(c); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| static void pstrPrintln(PGM_P str) { | |||||
| pstrPrint(str); | |||||
| SdFat::stdOut()->println(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print any SD error code and halt. */ | |||||
| void SdFat::errorHalt() { | |||||
| errorPrint(); | |||||
| while (1) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print msg, any SD error code, and halt. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void SdFat::errorHalt(char const* msg) { | |||||
| errorPrint(msg); | |||||
| while (1) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print msg, any SD error code, and halt. | |||||
| * | |||||
| * \param[in] msg Message in program space (flash memory) to print. | |||||
| */ | |||||
| void SdFat::errorHalt_P(PGM_P msg) { | |||||
| errorPrint_P(msg); | |||||
| while (1) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print any SD error code. */ | |||||
| void SdFat::errorPrint() { | |||||
| if (!m_card.errorCode()) return; | |||||
| pstrPrint(PSTR("SD errorCode: 0X")); | |||||
| m_stdOut->print(m_card.errorCode(), HEX); | |||||
| pstrPrint(PSTR(",0X")); | |||||
| m_stdOut->println(m_card.errorData(), HEX); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print msg, any SD error code. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void SdFat::errorPrint(char const* msg) { | |||||
| pstrPrint(PSTR("error: ")); | |||||
| m_stdOut->println(msg); | |||||
| errorPrint(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print msg, any SD error code. | |||||
| * | |||||
| * \param[in] msg Message in program space (flash memory) to print. | |||||
| */ | |||||
| void SdFat::errorPrint_P(PGM_P msg) { | |||||
| pstrPrint(PSTR("error: ")); | |||||
| pstrPrintln(msg); | |||||
| errorPrint(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print error details and halt after SdFat::init() fails. */ | |||||
| void SdFat::initErrorHalt() { | |||||
| initErrorPrint(); | |||||
| while (1) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /**Print message, error details, and halt after SdFat::init() fails. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void SdFat::initErrorHalt(char const *msg) { | |||||
| m_stdOut->println(msg); | |||||
| initErrorHalt(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /**Print message, error details, and halt after SdFat::init() fails. | |||||
| * | |||||
| * \param[in] msg Message in program space (flash memory) to print. | |||||
| */ | |||||
| void SdFat::initErrorHalt_P(PGM_P msg) { | |||||
| pstrPrintln(msg); | |||||
| initErrorHalt(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Print error details after SdFat::init() fails. */ | |||||
| void SdFat::initErrorPrint() { | |||||
| if (m_card.errorCode()) { | |||||
| pstrPrintln(PSTR("Can't access SD card. Do not reformat.")); | |||||
| if (m_card.errorCode() == SD_CARD_ERROR_CMD0) { | |||||
| pstrPrintln(PSTR("No card, wrong chip select pin, or SPI problem?")); | |||||
| } | |||||
| errorPrint(); | |||||
| } else if (m_vol.fatType() == 0) { | |||||
| pstrPrintln(PSTR("Invalid format, reformat SD.")); | |||||
| } else if (!m_vwd.isOpen()) { | |||||
| pstrPrintln(PSTR("Can't open root directory.")); | |||||
| } else { | |||||
| pstrPrintln(PSTR("No error found.")); | |||||
| } | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /**Print message and error details and halt after SdFat::init() fails. | |||||
| * | |||||
| * \param[in] msg Message to print. | |||||
| */ | |||||
| void SdFat::initErrorPrint(char const *msg) { | |||||
| m_stdOut->println(msg); | |||||
| initErrorPrint(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /**Print message and error details after SdFat::init() fails. | |||||
| * | |||||
| * \param[in] msg Message in program space (flash memory) to print. | |||||
| */ | |||||
| void SdFat::initErrorPrint_P(PGM_P msg) { | |||||
| pstrPrintln(msg); | |||||
| initErrorHalt(); | |||||
| } |
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <SdFat.h> | |||||
| #include <SdFatUtil.h> | |||||
| #include "SdFat.h" | |||||
| #include "SdFatUtil.h" | |||||
| //------------------------------------------------------------------------------ | |||||
| #ifdef __arm__ | #ifdef __arm__ | ||||
| // should use uinstd.h to define sbrk but Due causes a conflict | |||||
| extern "C" char* sbrk(int incr); | extern "C" char* sbrk(int incr); | ||||
| #else // __ARM__ | |||||
| extern char *__brkval; | |||||
| extern char __bss_end; | |||||
| #endif // __arm__ | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Amount of free RAM | /** Amount of free RAM | ||||
| * \return The number of free bytes. | * \return The number of free bytes. | ||||
| */ | */ | ||||
| int SdFatUtil::FreeRam() { | int SdFatUtil::FreeRam() { | ||||
| char top; | char top; | ||||
| #ifdef __arm__ | |||||
| return &top - reinterpret_cast<char*>(sbrk(0)); | return &top - reinterpret_cast<char*>(sbrk(0)); | ||||
| } | |||||
| #else // __arm__ | #else // __arm__ | ||||
| extern char *__brkval; | |||||
| extern char __bss_end; | |||||
| /** Amount of free RAM | |||||
| * \return The number of free bytes. | |||||
| */ | |||||
| int SdFatUtil::FreeRam() { | |||||
| char top; | |||||
| return __brkval ? &top - __brkval : &top - &__bss_end; | return __brkval ? &top - __brkval : &top - &__bss_end; | ||||
| #endif // __arm__ | |||||
| } | } | ||||
| #endif // __arm | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** %Print a string in flash memory. | /** %Print a string in flash memory. | ||||
| * | * | ||||
| print_P(pr, str); | print_P(pr, str); | ||||
| pr->println(); | pr->println(); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print a string in flash memory to Serial. | |||||
| * | |||||
| * \param[in] str Pointer to string stored in flash memory. | |||||
| */ | |||||
| void SdFatUtil::SerialPrint_P(PGM_P str) { | |||||
| print_P(SdFat::stdOut(), str); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** %Print a string in flash memory to Serial followed by a CR/LF. | |||||
| * | |||||
| * \param[in] str Pointer to string stored in flash memory. | |||||
| */ | |||||
| void SdFatUtil::SerialPrintln_P(PGM_P str) { | |||||
| println_P(SdFat::stdOut(), str); | |||||
| } |
| * \file | * \file | ||||
| * \brief Useful utility functions. | * \brief Useful utility functions. | ||||
| */ | */ | ||||
| #include <SdFat.h> | |||||
| #include "SdFat.h" | |||||
| /** Store and print a string in flash memory.*/ | /** Store and print a string in flash memory.*/ | ||||
| #define PgmPrint(x) SerialPrint_P(PSTR(x)) | #define PgmPrint(x) SerialPrint_P(PSTR(x)) | ||||
| /** Store and print a string in flash memory followed by a CR/LF.*/ | /** Store and print a string in flash memory followed by a CR/LF.*/ | ||||
| int FreeRam(); | int FreeRam(); | ||||
| void print_P(Print* pr, PGM_P str); | void print_P(Print* pr, PGM_P str); | ||||
| void println_P(Print* pr, PGM_P str); | void println_P(Print* pr, PGM_P str); | ||||
| //---------------------------------------------------------------------------- | |||||
| /** %Print a string in flash memory to Serial. | |||||
| * | |||||
| * \param[in] str Pointer to string stored in flash memory. | |||||
| */ | |||||
| inline void SerialPrint_P(PGM_P str) { | |||||
| print_P(&Serial, str); | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| /** %Print a string in flash memory to Serial followed by a CR/LF. | |||||
| * | |||||
| * \param[in] str Pointer to string stored in flash memory. | |||||
| */ | |||||
| inline void SerialPrintln_P(PGM_P str) { | |||||
| println_P(&Serial, str); | |||||
| } | |||||
| void SerialPrint_P(PGM_P str); | void SerialPrint_P(PGM_P str); | ||||
| void SerialPrintln_P(PGM_P str); | void SerialPrintln_P(PGM_P str); | ||||
| } | |||||
| } // namespace SdFatUtil | |||||
| using namespace SdFatUtil; // NOLINT | using namespace SdFatUtil; // NOLINT | ||||
| #endif // #define SdFatUtil_h | #endif // #define SdFatUtil_h |
| Experimental support for FAT12 can be enabled by setting FAT12_SUPPORT | Experimental support for FAT12 can be enabled by setting FAT12_SUPPORT | ||||
| nonzero in SdFatConfig.h. | nonzero in SdFatConfig.h. | ||||
| The %SdFat library only supports short 8.3 names. | |||||
| 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 main classes in %SdFat are SdFat, SdBaseFile, SdFile, File, StdioStream, | |||||
| \ref fstream, \ref ifstream, and \ref ofstream. | |||||
| The main classes in %SdFat are SdFat, SdFatSoftSpi, SdFatLibSpi, | |||||
| SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream, | |||||
| and \ref ofstream. | |||||
| The SdFat class maintains a FAT volume, a current working directory, | |||||
| and simplifies initialization of other classes. | |||||
| 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. | |||||
| The SdBaseFile class provides basic file access functions such as open(), | The SdBaseFile class provides basic file access functions such as open(), | ||||
| binary read(), binary write(), close(), remove(), and sync(). SdBaseFile | binary read(), binary write(), close(), remove(), and sync(). SdBaseFile | ||||
| The \ref ofstream class implements C++ iostreams for writing text files. | The \ref ofstream class implements C++ iostreams for writing text files. | ||||
| The classes \ref ifstream, \ref ofstream, \ref istream, and \ref ostream | |||||
| follow the C++ \ref iostream standard when possible. | |||||
| There are many tutorials and much documentation about using C++ iostreams | |||||
| on the web. | |||||
| http://www.cplusplus.com/ is a good C++ site for learning iostreams. | |||||
| The classes \ref ibufstream and \ref obufstream format and parse character | The classes \ref ibufstream and \ref obufstream format and parse character | ||||
| strings in memory buffers. | strings in memory buffers. | ||||
| Several configuration options may be changed by editing the SdFatConfig.h | Several configuration options may be changed by editing the SdFatConfig.h | ||||
| file in the SdFat folder. | file in the SdFat folder. | ||||
| Set SD_FILE_USES_STREAM nonzero to use Stream instead of Print for SdFile. | |||||
| Using Stream will use more flash. | |||||
| 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. | |||||
| To enable SD card CRC checking set USE_SD_CRC nonzero. | To enable SD card CRC checking set USE_SD_CRC nonzero. | ||||
| To use multiple SD cards set USE_MULTIPLE_CARDS nonzero. | |||||
| Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. | Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. | ||||
| FAT12 has not been well tested and requires additional flash. | FAT12 has not been well tested and requires additional flash. | ||||
| Set USE_ARDUINO_SPI_LIBRARY nonzero to force use of Arduino Standard | |||||
| SPI library. This will override native and software SPI for all boards. | |||||
| 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 for selected boards by setting the symbols | |||||
| AVR_SOFT_SPI, DUE_SOFT_SPI, LEONARDO_SOFT_SPI, MEGA_SOFT_SPI, | |||||
| and TEENSY3_SOFT_SPI. | |||||
| Use of software SPI can be enabled in the SdFat class for selected boards | |||||
| by setting the symbols AVR_SOFT_SPI, DUE_SOFT_SPI, LEONARDO_SOFT_SPI, | |||||
| MEGA_SOFT_SPI, and TEENSY3_SOFT_SPI. | |||||
| Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature | Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature | ||||
| of the standard Arduino SPI library. You must include SPI.h in your | of the standard Arduino SPI library. You must include SPI.h in your | ||||
| setting the SPI bus frequency to 4 MHz. This can be done by using | setting the SPI bus frequency to 4 MHz. This can be done by using | ||||
| card.init(SPI_HALF_SPEED) to initialize the SD card. | card.init(SPI_HALF_SPEED) to initialize the SD card. | ||||
| A feature to use software SPI is available. Software SPI is slower | |||||
| 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 | \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. | ||||
| getline - Example of getline from section 27.7.1.3 of the C++ standard. | getline - Example of getline from section 27.7.1.3 of the C++ standard. | ||||
| LongFileName - Example use of openNextLFN and open by index. | |||||
| LowLatencyLogger - A modifiable data logger for higher data rates. | LowLatencyLogger - A modifiable data logger for higher data rates. | ||||
| OpenNext - Open all files in the root dir and print their filename. | OpenNext - Open all files in the root dir and print their filename. | ||||
| SdFormatter - This sketch will format an SD or SDHC card. | SdFormatter - This sketch 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. | SdInfo - Initialize an SD card and analyze its structure for trouble shooting. | ||||
| StdioBench - Demo and test of stdio style stream. | StdioBench - Demo and test of stdio style stream. | ||||
| StreamParseInt - Simple demo of parseInt() Stream member function. | |||||
| StressTest - Create and write files until the SD is full. | |||||
| 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. | Timestamp - Sets file create, modify, and access timestamps. | ||||
| TwoCards - Example using two SD cards. | TwoCards - Example using two SD cards. |
| /* 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 | |||||
| * 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 Arduino SdFat Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #include <SdFile.h> | |||||
| /** Create a file object and open it in the current working directory. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |||||
| * | |||||
| * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive | |||||
| * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t). | |||||
| */ | |||||
| SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) { | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Write data to an open file. | |||||
| * | |||||
| * \note Data is moved to the cache but may not be written to the | |||||
| * storage device until sync() is called. | |||||
| * | |||||
| * \param[in] buf Pointer to the location of the data to be written. | |||||
| * | |||||
| * \param[in] nbyte Number of bytes to write. | |||||
| * | |||||
| * \return For success write() returns the number of bytes written, always | |||||
| * \a nbyte. If an error occurs, write() returns -1. Possible errors | |||||
| * include write() is called before a file has been opened, write is called | |||||
| * for a read-only file, device is full, a corrupt file system or an I/O error. | |||||
| * | |||||
| */ | |||||
| int SdFile::write(const void* buf, size_t nbyte) { | |||||
| return SdBaseFile::write(buf, nbyte); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** 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 SdFile::write(uint8_t b) { | |||||
| return SdBaseFile::write(&b, 1) == 1 ? 1 : 0; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** 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. | |||||
| * \return count of characters written for success or -1 for failure. | |||||
| */ | |||||
| int SdFile::write(const char* str) { | |||||
| return SdBaseFile::write(str, strlen(str)); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Write a PROGMEM string to a file. | |||||
| * \param[in] str Pointer to the PROGMEM string. | |||||
| * Use getWriteError to check for errors. | |||||
| */ | |||||
| void SdFile::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 SdFile::writeln_P(PGM_P str) { | |||||
| write_P(str); | |||||
| write_P(PSTR("\r\n")); | |||||
| } |
| #ifndef SdFile_h | #ifndef SdFile_h | ||||
| #define SdFile_h | #define SdFile_h | ||||
| #include <limits.h> | #include <limits.h> | ||||
| #include <SdBaseFile.h> | |||||
| #include "utility/FatLib.h" | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Arduino SD.h style flag for open for read. */ | |||||
| #define FILE_READ O_READ | |||||
| /** Arduino SD.h style flag for open at EOF for read/write with create. */ | |||||
| #define FILE_WRITE (O_RDWR | O_CREAT | O_AT_END) | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class SdBaseFile | |||||
| * \brief SdBaseFile base for SdFile and File. | |||||
| */ | |||||
| class SdBaseFile : public FatFile { | |||||
| public: | |||||
| SdBaseFile() {} | |||||
| /** Constructor with file open. | |||||
| * | |||||
| * \param[in] path File location and name. | |||||
| * \param[in] oflag File open mode. | |||||
| */ | |||||
| SdBaseFile(const char* path, uint8_t oflag) {open(path, oflag);} | |||||
| using FatFile::ls; | |||||
| using FatFile::printFatDate; | |||||
| using FatFile::printFatTime; | |||||
| using FatFile::printName; | |||||
| /** List directory contents. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| */ | |||||
| void ls(uint8_t flags = 0) { | |||||
| ls(&Serial, flags); | |||||
| } | |||||
| /** %Print a directory date field. | |||||
| * | |||||
| * Format is yyyy-mm-dd. | |||||
| * | |||||
| * \param[in] fatDate The date field from a directory entry. | |||||
| */ | |||||
| static void printFatDate(uint16_t fatDate) { | |||||
| printFatDate(&Serial, fatDate); | |||||
| } | |||||
| /** %Print a directory time field. | |||||
| * | |||||
| * Format is hh:mm:ss. | |||||
| * | |||||
| * \param[in] fatTime The time field from a directory entry. | |||||
| */ | |||||
| static void printFatTime(uint16_t fatTime) { | |||||
| printFatTime(&Serial, fatTime); | |||||
| } | |||||
| /** Print a file's name. | |||||
| * | |||||
| * \return The value one, true, is returned for success and | |||||
| * the value zero, false, is returned for failure. | |||||
| */ | |||||
| size_t printName() { | |||||
| return FatFile::printName(&Serial); | |||||
| } | |||||
| }; | |||||
| //============================================================================== | |||||
| /** | /** | ||||
| * \class SdFile | * \class SdFile | ||||
| * \brief SdBaseFile with Arduino Stream. | |||||
| * \brief SdFile SdBaseFile with Print. | |||||
| */ | */ | ||||
| #if SD_FILE_USES_STREAM | #if SD_FILE_USES_STREAM | ||||
| class SdFile : public SdBaseFile, public Stream { | class SdFile : public SdBaseFile, public Stream { | ||||
| #endif // SD_FILE_USES_STREAM | #endif // SD_FILE_USES_STREAM | ||||
| public: | public: | ||||
| SdFile() {} | SdFile() {} | ||||
| SdFile(const char* name, uint8_t oflag); | |||||
| /** Create a file object and open it in the current working directory. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |||||
| * | |||||
| * \param[in] oflag Values for \a oflag are constructed by a | |||||
| * bitwise-inclusive OR of open flags. see | |||||
| * SdBaseFile::open(SdBaseFile*, const char*, uint8_t). | |||||
| */ | |||||
| SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) {} | |||||
| #if DESTRUCTOR_CLOSES_FILE | #if DESTRUCTOR_CLOSES_FILE | ||||
| ~SdFile() {} | ~SdFile() {} | ||||
| #endif // DESTRUCTOR_CLOSES_FILE | #endif // DESTRUCTOR_CLOSES_FILE | ||||
| using SdBaseFile::clearWriteError; | |||||
| using SdBaseFile::getWriteError; | |||||
| using SdBaseFile::read; | |||||
| using SdBaseFile::write; | |||||
| /** \return number of bytes available from the current position to EOF | /** \return number of bytes available from the current position to EOF | ||||
| * or INT_MAX if more than INT_MAX bytes are available. | * or INT_MAX if more than INT_MAX bytes are available. | ||||
| */ | */ | ||||
| /** Return the next available byte without consuming it. | /** Return the next available byte without consuming it. | ||||
| * | * | ||||
| * \return The byte if no error and not at eof else -1; | * \return The byte if no error and not at eof else -1; | ||||
| */ | |||||
| */ | |||||
| int peek() {return SdBaseFile::peek();} | int peek() {return SdBaseFile::peek();} | ||||
| /** Read the next byte from a file. | /** Read the next byte from a file. | ||||
| * | * | ||||
| * \return For success return the next byte in the file as an int. | * \return For success return the next byte in the file as an int. | ||||
| * If an error occurs or end of file is reached return -1. | * If an error occurs or end of file is reached return -1. | ||||
| */ | |||||
| */ | |||||
| int read() {return SdBaseFile::read();} | int read() {return SdBaseFile::read();} | ||||
| /** Read data from a file starting at the current position. | |||||
| * | |||||
| * \param[out] buf Pointer to the location that will receive the data. | |||||
| * | |||||
| * \param[in] nbyte Maximum number of bytes to read. | |||||
| * | |||||
| * \return For success read() returns the number of bytes read. | |||||
| * A value less than \a nbyte, including zero, will be returned | |||||
| * if end of file is reached. | |||||
| * If an error occurs, read() returns -1. Possible errors include | |||||
| * read() called before a file has been opened, corrupt file system | |||||
| * or an I/O error occurred. | |||||
| */ | |||||
| int read(void* buf, size_t nbyte) {return SdBaseFile::read(buf, nbyte);} | |||||
| /** \return value of writeError */ | |||||
| bool getWriteError() {return SdBaseFile::getWriteError();} | |||||
| /** Set writeError to zero */ | |||||
| void clearWriteError() {SdBaseFile::clearWriteError();} | |||||
| size_t write(uint8_t b); | |||||
| int write(const char* str); | |||||
| /** Write data to an open file. | |||||
| * | |||||
| * \note Data is moved to the cache but may not be written to the | |||||
| * storage device until sync() is called. | |||||
| * | |||||
| * \param[in] buf Pointer to the location of the data to be written. | |||||
| * | |||||
| * \param[in] nbyte Number of bytes to write. | |||||
| * | |||||
| * \return For success write() returns the number of bytes written, always | |||||
| * \a nbyte. If an error occurs, write() returns -1. Possible errors | |||||
| * include write() is called before a file has been opened, write is called | |||||
| * for a read-only file, device is full, a corrupt file system or an | |||||
| * I/O error. | |||||
| */ | |||||
| int write(const void* buf, size_t nbyte); | |||||
| /** 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);} | |||||
| /** 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. | |||||
| * \return count of characters written for success or -1 for failure. | |||||
| */ | |||||
| int write(const char* str) { | |||||
| return SdBaseFile::write(str, strlen(str)); | |||||
| } | |||||
| /** Write data to an open file. Form required by Print. | /** Write data to an open file. Form required by Print. | ||||
| * | * | ||||
| * \note Data is moved to the cache but may not be written to the | * \note Data is moved to the cache but may not be written to the | ||||
| * \return For success write() returns the number of bytes written, always | * \return For success write() returns the number of bytes written, always | ||||
| * \a nbyte. If an error occurs, write() returns -1. Possible errors | * \a nbyte. If an error occurs, write() returns -1. Possible errors | ||||
| * include write() is called before a file has been opened, write is called | * include write() is called before a file has been opened, write is called | ||||
| * for a read-only file, device is full, a corrupt file system or an | |||||
| * for a read-only file, device is full, a corrupt file system or an | |||||
| * I/O error. | * I/O error. | ||||
| */ | |||||
| */ | |||||
| size_t write(const uint8_t *buf, size_t size) { | size_t write(const uint8_t *buf, size_t size) { | ||||
| return SdBaseFile::write(buf, size);} | |||||
| void write_P(PGM_P str); | |||||
| void writeln_P(PGM_P str); | |||||
| 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")); | |||||
| } | |||||
| }; | }; | ||||
| //------------------------------------------------------------------------------ | |||||
| /** Arduino SD.h style flag for open for read. */ | |||||
| #define FILE_READ O_READ | |||||
| /** Arduino SD.h style flag for open at EOF for read/write with create. */ | |||||
| #define FILE_WRITE (O_RDWR | O_CREAT | O_AT_END) | |||||
| //============================================================================== | |||||
| /** | /** | ||||
| * \class File | * \class File | ||||
| * \brief Arduino SD.h style File API | * \brief Arduino SD.h style File API | ||||
| */ | */ | ||||
| class File : public SdBaseFile, public Stream { | class File : public SdBaseFile, public Stream { | ||||
| public: | public: | ||||
| File() {} | |||||
| /** Create a file object and open it in the current working directory. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |||||
| * | |||||
| * \param[in] oflag Values for \a oflag are constructed by a | |||||
| * bitwise-inclusive OR of open flags. see | |||||
| * SdBaseFile::open(SdBaseFile*, const char*, uint8_t). | |||||
| */ | |||||
| File(const char* path, uint8_t oflag) {open(path, oflag);} | |||||
| using SdBaseFile::clearWriteError; | |||||
| using SdBaseFile::getWriteError; | |||||
| /** The parenthesis operator. | /** The parenthesis operator. | ||||
| * | * | ||||
| * \return true if a file is open. | * \return true if a file is open. | ||||
| uint32_t n = SdBaseFile::available(); | uint32_t n = SdBaseFile::available(); | ||||
| return n > INT_MAX ? INT_MAX : n; | return n > INT_MAX ? INT_MAX : n; | ||||
| } | } | ||||
| /** Set writeError to zero */ | |||||
| void clearWriteError() {SdBaseFile::clearWriteError();} | |||||
| /** Ensure that any bytes written to the file are saved to the SD card. */ | /** Ensure that any bytes written to the file are saved to the SD card. */ | ||||
| void flush() {sync();} | |||||
| /** \return value of writeError */ | |||||
| bool getWriteError() {return SdBaseFile::getWriteError();} | |||||
| void flush() {SdBaseFile::sync();} | |||||
| /** This function reports if the current file is a directory or not. | /** This function reports if the current file is a directory or not. | ||||
| * \return true if the file is a directory. | * \return true if the file is a directory. | ||||
| */ | |||||
| */ | |||||
| bool isDirectory() {return isDir();} | bool isDirectory() {return isDir();} | ||||
| /** \return a pointer to the file's name. */ | /** \return a pointer to the file's name. */ | ||||
| char* name() { | char* name() { | ||||
| /** Return the next available byte without consuming it. | /** Return the next available byte without consuming it. | ||||
| * | * | ||||
| * \return The byte if no error and not at eof else -1; | * \return The byte if no error and not at eof else -1; | ||||
| */ | |||||
| */ | |||||
| int peek() {return SdBaseFile::peek();} | int peek() {return SdBaseFile::peek();} | ||||
| /** \return the current file position. */ | /** \return the current file position. */ | ||||
| uint32_t position() {return curPosition();} | uint32_t position() {return curPosition();} | ||||
| * | * | ||||
| * \return For success return the next byte in the file as an int. | * \return For success return the next byte in the file as an int. | ||||
| * If an error occurs or end of file is reached return -1. | * If an error occurs or end of file is reached return -1. | ||||
| */ | |||||
| */ | |||||
| int read() {return SdBaseFile::read();} | int read() {return SdBaseFile::read();} | ||||
| /** Read data from a file starting at the current position. | |||||
| * | |||||
| * \param[out] buf Pointer to the location that will receive the data. | |||||
| * | |||||
| * \param[in] nbyte Maximum number of bytes to read. | |||||
| * | |||||
| * \return For success read() returns the number of bytes read. | |||||
| * A value less than \a nbyte, including zero, will be returned | |||||
| * if end of file is reached. | |||||
| * If an error occurs, read() returns -1. Possible errors include | |||||
| * read() called before a file has been opened, corrupt file system | |||||
| * or an I/O error occurred. | |||||
| */ | |||||
| int read(void* buf, size_t nbyte) {return SdBaseFile::read(buf, nbyte);} | |||||
| /** Rewind a file if it is a directory */ | /** Rewind a file if it is a directory */ | ||||
| void rewindDirectory() { | void rewindDirectory() { | ||||
| if (isDir()) rewind(); | if (isDir()) rewind(); | ||||
| } | } | ||||
| /** | |||||
| /** | |||||
| * Seek to a new position in the file, which must be between | * Seek to a new position in the file, which must be between | ||||
| * 0 and the size of the file (inclusive). | * 0 and the size of the file (inclusive). | ||||
| * | * | ||||
| * Use getWriteError to check for errors. | * Use getWriteError to check for errors. | ||||
| * \return 1 for success and 0 for failure. | * \return 1 for success and 0 for failure. | ||||
| */ | */ | ||||
| size_t write(uint8_t b) { | |||||
| return SdBaseFile::write(&b, 1) == 1 ? 1 : 0; | |||||
| } | |||||
| /** Write data to an open file. | |||||
| * | |||||
| * \note Data is moved to the cache but may not be written to the | |||||
| * storage device until sync() is called. | |||||
| * | |||||
| * \param[in] buf Pointer to the location of the data to be written. | |||||
| * | |||||
| * \param[in] nbyte Number of bytes to write. | |||||
| * | |||||
| * \return For success write() returns the number of bytes written, always | |||||
| * \a nbyte. If an error occurs, write() returns -1. Possible errors | |||||
| * include write() is called before a file has been opened, write is called | |||||
| * for a read-only file, device is full, a corrupt file system or an | |||||
| * I/O error. | |||||
| */ | |||||
| int write(const void* buf, size_t nbyte); | |||||
| size_t write(uint8_t b) {return SdBaseFile::write(b);} | |||||
| /** Write data to an open file. Form required by Print. | /** Write data to an open file. Form required by Print. | ||||
| * | * | ||||
| * \note Data is moved to the cache but may not be written to the | * \note Data is moved to the cache but may not be written to the | ||||
| * \return For success write() returns the number of bytes written, always | * \return For success write() returns the number of bytes written, always | ||||
| * \a nbyte. If an error occurs, write() returns -1. Possible errors | * \a nbyte. If an error occurs, write() returns -1. Possible errors | ||||
| * include write() is called before a file has been opened, write is called | * include write() is called before a file has been opened, write is called | ||||
| * for a read-only file, device is full, a corrupt file system or an | |||||
| * for a read-only file, device is full, a corrupt file system or an | |||||
| * I/O error. | * I/O error. | ||||
| */ | |||||
| */ | |||||
| size_t write(const uint8_t *buf, size_t size) { | size_t write(const uint8_t *buf, size_t size) { | ||||
| return SdBaseFile::write(buf, size); | return SdBaseFile::write(buf, size); | ||||
| } | } |
| /* Arduino Sd2Card Library | |||||
| /* Arduino SdSpiCard Library | |||||
| * Copyright (C) 2012 by William Greiman | * Copyright (C) 2012 by William Greiman | ||||
| * | * | ||||
| * This file is part of the Arduino Sd2Card Library | |||||
| * This file is part of the Arduino SdSpiCard Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino Sd2Card Library. If not, see | |||||
| * along with the Arduino SdSpiCard Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #ifndef SdInfo_h | #ifndef SdInfo_h | ||||
| // | // | ||||
| // http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs | // http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // SD card errors | |||||
| /** timeout error for command CMD0 (initialize card in SPI mode) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD0 = 0X1; | |||||
| /** CMD8 was not accepted - not a valid SD card*/ | |||||
| uint8_t const SD_CARD_ERROR_CMD8 = 0X2; | |||||
| /** card returned an error response for CMD12 (stop multiblock read) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD12 = 0X3; | |||||
| /** card returned an error response for CMD17 (read block) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD17 = 0X4; | |||||
| /** card returned an error response for CMD18 (read multiple block) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD18 = 0X5; | |||||
| /** card returned an error response for CMD24 (write block) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD24 = 0X6; | |||||
| /** WRITE_MULTIPLE_BLOCKS command failed */ | |||||
| uint8_t const SD_CARD_ERROR_CMD25 = 0X7; | |||||
| /** card returned an error response for CMD58 (read OCR) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD58 = 0X8; | |||||
| /** SET_WR_BLK_ERASE_COUNT failed */ | |||||
| uint8_t const SD_CARD_ERROR_ACMD23 = 0X9; | |||||
| /** ACMD41 initialization process timeout */ | |||||
| uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; | |||||
| /** card returned a bad CSR version field */ | |||||
| uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB; | |||||
| /** erase block group command failed */ | |||||
| uint8_t const SD_CARD_ERROR_ERASE = 0XC; | |||||
| /** card not capable of single block erase */ | |||||
| uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; | |||||
| /** Erase sequence timed out */ | |||||
| uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; | |||||
| /** card returned an error token instead of read data */ | |||||
| uint8_t const SD_CARD_ERROR_READ = 0XF; | |||||
| /** read CID or CSD failed */ | |||||
| uint8_t const SD_CARD_ERROR_READ_REG = 0X10; | |||||
| /** timeout while waiting for start of read data */ | |||||
| uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11; | |||||
| /** card did not accept STOP_TRAN_TOKEN */ | |||||
| uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12; | |||||
| /** card returned an error token as a response to a write operation */ | |||||
| uint8_t const SD_CARD_ERROR_WRITE = 0X13; | |||||
| /** attempt to write protected block zero */ | |||||
| uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X14; // REMOVE - not used | |||||
| /** card did not go ready for a multiple block write */ | |||||
| uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; | |||||
| /** card returned an error to a CMD13 status check after a write */ | |||||
| uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||||
| /** timeout occurred during write programming */ | |||||
| uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; | |||||
| /** incorrect rate selected */ | |||||
| uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18; | |||||
| /** init() not called */ | |||||
| uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; | |||||
| /** card returned an error for CMD59 (CRC_ON_OFF) */ | |||||
| uint8_t const SD_CARD_ERROR_CMD59 = 0X1A; | |||||
| /** invalid read CRC */ | |||||
| uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B; | |||||
| /** SPI DMA error */ | |||||
| uint8_t const SD_CARD_ERROR_SPI_DMA = 0X1C; | |||||
| //------------------------------------------------------------------------------ | |||||
| // card types | |||||
| /** Standard capacity V1 SD card */ | |||||
| uint8_t const SD_CARD_TYPE_SD1 = 1; | |||||
| /** Standard capacity V2 SD card */ | |||||
| uint8_t const SD_CARD_TYPE_SD2 = 2; | |||||
| /** High Capacity SD card */ | |||||
| uint8_t const SD_CARD_TYPE_SDHC = 3; | |||||
| //------------------------------------------------------------------------------ | |||||
| // SPI divisor constants | // SPI divisor constants | ||||
| /** Set SCK to max rate of F_CPU/2. */ | /** Set SCK to max rate of F_CPU/2. */ | ||||
| uint8_t const SPI_FULL_SPEED = 2; | uint8_t const SPI_FULL_SPEED = 2; | ||||
| /** SD_SEND_OP_COMD - Sends host capacity support information and | /** SD_SEND_OP_COMD - Sends host capacity support information and | ||||
| activates the card's initialization process */ | activates the card's initialization process */ | ||||
| uint8_t const ACMD41 = 0X29; | uint8_t const ACMD41 = 0X29; | ||||
| //------------------------------------------------------------------------------ | |||||
| //============================================================================== | |||||
| /** status for card in the ready state */ | /** status for card in the ready state */ | ||||
| uint8_t const R1_READY_STATE = 0X00; | uint8_t const R1_READY_STATE = 0X00; | ||||
| /** status for card in the idle state */ | /** status for card in the idle state */ | ||||
| uint8_t const DATA_RES_MASK = 0X1F; | uint8_t const DATA_RES_MASK = 0X1F; | ||||
| /** write data accepted token */ | /** write data accepted token */ | ||||
| uint8_t const DATA_RES_ACCEPTED = 0X05; | uint8_t const DATA_RES_ACCEPTED = 0X05; | ||||
| //------------------------------------------------------------------------------ | |||||
| /** Card IDentification (CID) register */ | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class CID | |||||
| * \brief Card IDentification (CID) register. | |||||
| */ | |||||
| typedef struct CID { | typedef struct CID { | ||||
| // byte 0 | // byte 0 | ||||
| /** Manufacturer ID */ | /** Manufacturer ID */ | ||||
| /** CRC7 checksum */ | /** CRC7 checksum */ | ||||
| unsigned char crc : 7; | unsigned char crc : 7; | ||||
| }__attribute__((packed)) cid_t; | }__attribute__((packed)) cid_t; | ||||
| //------------------------------------------------------------------------------ | |||||
| /** CSD for version 1.00 cards */ | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class CSDV1 | |||||
| * \brief CSD register for version 1.00 cards . | |||||
| */ | |||||
| typedef struct CSDV1 { | typedef struct CSDV1 { | ||||
| // byte 0 | // byte 0 | ||||
| unsigned char reserved1 : 6; | unsigned char reserved1 : 6; | ||||
| unsigned char always1 : 1; | unsigned char always1 : 1; | ||||
| unsigned char crc : 7; | unsigned char crc : 7; | ||||
| }__attribute__((packed)) csd1_t; | }__attribute__((packed)) csd1_t; | ||||
| //------------------------------------------------------------------------------ | |||||
| /** CSD for version 2.00 cards */ | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class CSDV2 | |||||
| * \brief CSD register for version 2.00 cards. | |||||
| */ | |||||
| typedef struct CSDV2 { | typedef struct CSDV2 { | ||||
| // byte 0 | // byte 0 | ||||
| unsigned char reserved1 : 6; | unsigned char reserved1 : 6; | ||||
| /** checksum */ | /** checksum */ | ||||
| unsigned char crc : 7; | unsigned char crc : 7; | ||||
| }__attribute__((packed)) csd2_t; | }__attribute__((packed)) csd2_t; | ||||
| //------------------------------------------------------------------------------ | |||||
| /** union of old and new style CSD register */ | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class csd_t | |||||
| * \brief Union of old and new style CSD register. | |||||
| */ | |||||
| union csd_t { | union csd_t { | ||||
| csd1_t v1; | csd1_t v1; | ||||
| csd2_t v2; | csd2_t v2; |
| #ifndef SdSpi_h | #ifndef SdSpi_h | ||||
| #define SdSpi_h | #define SdSpi_h | ||||
| #include <Arduino.h> | #include <Arduino.h> | ||||
| #include <SdFatConfig.h> | |||||
| #include "SdFatConfig.h" | |||||
| #include "utility/SoftSPI.h" | |||||
| #if !USE_ARDUINO_SPI_LIBRARY | #if !USE_ARDUINO_SPI_LIBRARY | ||||
| // AVR Arduinos | // AVR Arduinos | ||||
| #ifdef __AVR__ | #ifdef __AVR__ | ||||
| #elif LEONARDO_SOFT_SPI && defined(__AVR_ATmega32U4__) && !defined(CORE_TEENSY) | #elif LEONARDO_SOFT_SPI && defined(__AVR_ATmega32U4__) && !defined(CORE_TEENSY) | ||||
| #define USE_SOFTWARE_SPI 1 | #define USE_SOFTWARE_SPI 1 | ||||
| #elif MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)\ | #elif MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)\ | ||||
| ||defined(__AVR_ATmega2560__)) | |||||
| || defined(__AVR_ATmega2560__)) | |||||
| #define USE_SOFTWARE_SPI 1 | #define USE_SOFTWARE_SPI 1 | ||||
| #else // USE_SOFTWARE_SPI | |||||
| #define USE_NATIVE_AVR_SPI 1 | |||||
| #endif // USE_SOFTWARE_SPI | #endif // USE_SOFTWARE_SPI | ||||
| #endif // __AVR__ | #endif // __AVR__ | ||||
| // Due | // Due | ||||
| #if defined(__arm__) && !defined(CORE_TEENSY) | |||||
| #if DUE_SOFT_SPI | |||||
| #if DUE_SOFT_SPI && defined(__arm__) && !defined(CORE_TEENSY) | |||||
| #define USE_SOFTWARE_SPI 1 | #define USE_SOFTWARE_SPI 1 | ||||
| #else // DUE_SOFT_SPI | |||||
| /** Nonzero - use native SAM3X SPI */ | |||||
| #define USE_NATIVE_SAM3X_SPI 1 | |||||
| #endif // DUE_SOFT_SPI | |||||
| #endif // defined(__arm__) && !defined(CORE_TEENSY) | |||||
| // Teensy 3.0 | |||||
| #if defined(__arm__) && defined(CORE_TEENSY) | |||||
| #if TEENSY3_SOFT_SPI | |||||
| #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 | #define USE_SOFTWARE_SPI 1 | ||||
| #else // TEENSY3_SOFT_SPI | |||||
| /** Nonzero - use native MK20DX128 SPI */ | |||||
| #define USE_NATIVE_TEENSY3_SPI 1 | |||||
| #endif // TEENSY3_SOFT_SPI | |||||
| #endif // defined(__arm__) && defined(CORE_TEENSY) | |||||
| #endif // TEENSY3_SOFT_SPI && defined(__arm__) && defined(CORE_TEENSY) | |||||
| #endif // !USE_ARDUINO_SPI_LIBRARY | #endif // !USE_ARDUINO_SPI_LIBRARY | ||||
| #ifndef USE_SOFTWARE_SPI | #ifndef USE_SOFTWARE_SPI | ||||
| /** Default is no software SPI */ | |||||
| #define USE_SOFTWARE_SPI 0 | #define USE_SOFTWARE_SPI 0 | ||||
| #endif // USE_SOFTWARE_SPI | #endif // USE_SOFTWARE_SPI | ||||
| #ifndef USE_NATIVE_AVR_SPI | |||||
| #define USE_NATIVE_AVR_SPI 0 | |||||
| #endif | |||||
| #ifndef USE_NATIVE_SAM3X_SPI | |||||
| #define USE_NATIVE_SAM3X_SPI 0 | |||||
| #endif // USE_NATIVE_SAM3X_SPI | |||||
| #ifndef USE_NATIVE_TEENSY3_SPI | |||||
| #define USE_NATIVE_TEENSY3_SPI 0 | |||||
| #endif // USE_NATIVE_TEENSY3_SPI | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // define default chip select pin | |||||
| // | |||||
| #if !USE_SOFTWARE_SPI | |||||
| /** The default chip select pin for the SD card is SS. */ | |||||
| uint8_t const SD_CHIP_SELECT_PIN = SS; | |||||
| #else // USE_AVR_SOFTWARE_SPI | |||||
| /** SPI chip select pin for software SPI. */ | |||||
| uint8_t const SD_CHIP_SELECT_PIN = SOFT_SPI_CS_PIN; | |||||
| #endif // USE_AVR_SOFTWARE_SPI | |||||
| /** | |||||
| * \class SdSpiBase | |||||
| * \brief Virtual SPI class for access to SD and SDHC flash memory cards. | |||||
| */ | |||||
| class SdSpiBase { | |||||
| public: | |||||
| /** Initialize the SPI bus */ | |||||
| virtual void begin() = 0; | |||||
| /** Set SPI options for access to SD/SDHC cards. | |||||
| * | |||||
| * \param[in] divisor SCK clock divider relative to the system clock. | |||||
| */ | |||||
| virtual void init(uint8_t divisor); | |||||
| /** Receive a byte. | |||||
| * | |||||
| * \return The byte. | |||||
| */ | |||||
| virtual uint8_t receive() = 0; | |||||
| /** 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. | |||||
| */ | |||||
| virtual uint8_t receive(uint8_t* buf, size_t n) = 0; | |||||
| /** Send a byte. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| virtual void send(const uint8_t* buf, size_t n) = 0; | |||||
| /** \return true if hardware SPI else false */ | |||||
| virtual bool useSpiTransactions() = 0; | |||||
| }; | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * \class SdSpi | * \class SdSpi | ||||
| * \brief SPI class for access to SD and SDHC flash memory cards. | * \brief SPI class for access to SD and SDHC flash memory cards. | ||||
| */ | */ | ||||
| #if USE_MULTIPLE_SPI_TYPES | |||||
| class SdSpi : public SdSpiBase { | |||||
| #else | |||||
| class SdSpi { | class SdSpi { | ||||
| #endif | |||||
| public: | public: | ||||
| /** Initialize the SPI bus */ | /** Initialize the SPI bus */ | ||||
| void begin(); | void begin(); | ||||
| /** Set SPI options for access to SD/SDHC cards. | /** Set SPI options for access to SD/SDHC cards. | ||||
| * | |||||
| * \param[in] spiDivisor SCK clock divider relative to the system clock. | |||||
| * | |||||
| * \param[in] divisor SCK clock divider relative to the system clock. | |||||
| */ | */ | ||||
| void init(uint8_t spiDivisor); | |||||
| /** Receive a byte. | |||||
| void init(uint8_t divisor); | |||||
| /** Receive a byte. | |||||
| * | * | ||||
| * \return The byte. | * \return The byte. | ||||
| */ | */ | ||||
| uint8_t receive(); | uint8_t receive(); | ||||
| /** Receive multiple bytes. | /** Receive multiple bytes. | ||||
| * | * | ||||
| * \param[out] buf Buffer to receive the data. | |||||
| * \param[out] buf Buffer to receive the data. | |||||
| * \param[in] n Number of bytes to receive. | * \param[in] n Number of bytes to receive. | ||||
| * | * | ||||
| * \return Zero for no error or nonzero error code. | * \return Zero for no error or nonzero error code. | ||||
| */ | |||||
| */ | |||||
| uint8_t receive(uint8_t* buf, size_t n); | uint8_t receive(uint8_t* buf, size_t n); | ||||
| /** Send a byte. | /** Send a byte. | ||||
| * | * | ||||
| * \param[in] data Byte to send | * \param[in] data Byte to send | ||||
| */ | */ | ||||
| void send(uint8_t data); | void send(uint8_t data); | ||||
| /** Send multiple bytes. | |||||
| /** Send multiple bytes. | |||||
| * | * | ||||
| * \param[in] buf Buffer for data to be sent. | |||||
| * \param[in] buf Buffer for data to be sent. | |||||
| * \param[in] n Number of bytes to send. | * \param[in] n Number of bytes to send. | ||||
| */ | |||||
| */ | |||||
| void send(const uint8_t* buf, size_t n); | void send(const uint8_t* buf, size_t n); | ||||
| /** \return true - uses SPI transactions */ | |||||
| 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 | |||||
| class SdSpiLib : public SdSpiBase { | |||||
| #else | |||||
| class SdSpiLib { | |||||
| #endif | |||||
| public: | |||||
| /** | |||||
| * Initialize SPI pins. | |||||
| */ | |||||
| void begin() {SPI.begin();} | |||||
| /** Set SPI options for access to SD/SDHC cards. | |||||
| * | |||||
| * \param[in] divisor SCK clock divider relative to the system clock. | |||||
| */ | |||||
| void init(uint8_t divisor) { | |||||
| SPI.setBitOrder(MSBFIRST); | |||||
| SPI.setDataMode(SPI_MODE0); | |||||
| #ifndef SPI_CLOCK_DIV128 | |||||
| SPI.setClockDivider(divisor); | |||||
| #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; | |||||
| SPI.setClockDivider(v); | |||||
| #endif // SPI_CLOCK_DIV128 | |||||
| } | |||||
| /** Receive a byte. | |||||
| * | |||||
| * \return The byte. | |||||
| */ | |||||
| uint8_t receive() {return SPI.transfer(0XFF);} | |||||
| /** 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] = SPI.transfer(0XFF); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| /** Send a byte. | |||||
| * | |||||
| * \param[in] b Byte to send | |||||
| */ | |||||
| void send(uint8_t b) { | |||||
| SPI.transfer(b); | |||||
| } | |||||
| /** Send multiple bytes. | |||||
| * | |||||
| * \param[in] buf Buffer for data to be sent. | |||||
| * \param[in] n Number of bytes to send. | |||||
| */ | |||||
| void send(const uint8_t* buf , size_t n) { | |||||
| for (size_t i = 0; i < n; i++) { | |||||
| SPI.transfer(buf[i]); | |||||
| } | |||||
| } | |||||
| /** \return true - uses SPI transactions */ | |||||
| bool useSpiTransactions() {return true;} | |||||
| }; | |||||
| #endif // USE_MULTIPLE_SPI_TYPES || USE_ARDUINO_SPI_LIBRARY | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * \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();} | |||||
| /** | |||||
| * Initialize hardware SPI - dummy for soft SPI | |||||
| * \param[in] divisor SCK divisor - ignored. | |||||
| */ | |||||
| void init(uint8_t divisor) {} | |||||
| /** Receive a byte. | |||||
| * | |||||
| * \return The byte. | |||||
| */ | |||||
| uint8_t receive() {return m_spi.receive();} | |||||
| /** Receive multiple bytes. | |||||
| * | |||||
| * \param[out] buf Buffer to receive the data. | |||||
| * \param[in] n Number of bytes to receive. | |||||
| * | |||||
| * \return Zero for no error or nonzero error code. | |||||
| */ | |||||
| uint8_t receive(uint8_t* buf, size_t n) { | |||||
| for (size_t i = 0; i < n; i++) { | |||||
| buf[i] = receive(); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| /** Send a byte. | |||||
| * | |||||
| * \param[in] data Byte to send | |||||
| */ | |||||
| void send(uint8_t data) {m_spi.send(data);} | |||||
| /** Send multiple bytes. | |||||
| * | |||||
| * \param[in] buf Buffer for data to be sent. | |||||
| * \param[in] n Number of bytes to send. | |||||
| */ | |||||
| void send(const uint8_t* buf , size_t n) { | |||||
| for (size_t i = 0; i < n; i++) { | |||||
| send(buf[i]); | |||||
| } | |||||
| } | |||||
| /** \return false - no SPI transactions */ | |||||
| bool useSpiTransactions() {return false;} | |||||
| private: | |||||
| SoftSPI<MisoPin, MosiPin, SckPin, 0> m_spi; | |||||
| }; | }; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // Use of inline for AVR results in up to 10% better write performance. | |||||
| // Inline also save a little flash memory. | |||||
| /** inline avr native functions if nonzero. */ | |||||
| #define USE_AVR_NATIVE_SPI_INLINE 1 | |||||
| #if USE_NATIVE_AVR_SPI && USE_AVR_NATIVE_SPI_INLINE | |||||
| #if USE_ARDUINO_SPI_LIBRARY | |||||
| /** Default is Arduino library SPI. */ | |||||
| typedef SdSpiLib SpiDefault_t; | |||||
| #elif USE_SOFTWARE_SPI | |||||
| /** 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 | |||||
| //------------------------------------------------------------------------------ | |||||
| // Use of in-line for AVR to save flash. | |||||
| #ifdef __AVR__ | |||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpi::begin() { | |||||
| #ifdef __AVR_ATmega328P__ | |||||
| // Save a few bytes for 328 CPU - gcc optimizes single bit '|' to sbi. | |||||
| PORTB |= 1 << 2; // SS high | |||||
| DDRB |= 1 << 2; // SS output mode | |||||
| DDRB |= 1 << 3; // MOSI output mode | |||||
| DDRB |= 1 << 5; // SCK output mode | |||||
| #else // __AVR_ATmega328P__ | |||||
| // set SS high - may be chip select for another SPI device | |||||
| digitalWrite(SS, HIGH); | |||||
| // SS must be in output mode even it is not chip select | |||||
| pinMode(SS, OUTPUT); | |||||
| pinMode(MOSI, OUTPUT); | |||||
| pinMode(SCK, OUTPUT); | |||||
| #endif // __AVR_ATmega328P__ | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpi::init(uint8_t divisor) { | |||||
| uint8_t b = 2; | |||||
| uint8_t r = 0; | |||||
| // See AVR processor documentation. | |||||
| for (; divisor > b && r < 7; b <<= 1, r += r < 5 ? 1 : 2) {} | |||||
| SPCR = (1 << SPE) | (1 << MSTR) | (r >> 1); | |||||
| SPSR = r & 1 ? 0 : 1 << SPI2X; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline uint8_t SdSpi::receive() { | inline uint8_t SdSpi::receive() { | ||||
| SPDR = 0XFF; | SPDR = 0XFF; | ||||
| while (!(SPSR & (1 << SPIF))) {} | while (!(SPSR & (1 << SPIF))) {} | ||||
| return SPDR; | return SPDR; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | |||||
| inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | ||||
| if (n-- == 0) return 0; | if (n-- == 0) return 0; | ||||
| SPDR = 0XFF; | SPDR = 0XFF; | ||||
| buf[n] = SPDR; | buf[n] = SPDR; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpi::send(uint8_t data) { | inline void SdSpi::send(uint8_t data) { | ||||
| SPDR = data; | SPDR = data; | ||||
| while (!(SPSR & (1 << SPIF))) {} | while (!(SPSR & (1 << SPIF))) {} | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpi::send(const uint8_t* buf , size_t n) { | inline void SdSpi::send(const uint8_t* buf , size_t n) { | ||||
| if (n == 0) return; | if (n == 0) return; | ||||
| SPDR = buf[0]; | SPDR = buf[0]; | ||||
| } | } | ||||
| while (!(SPSR & (1 << SPIF))) {} | while (!(SPSR & (1 << SPIF))) {} | ||||
| } | } | ||||
| #endif // USE_NATIVE_AVR_SPI && USE_AVR_NATIVE_SPI_INLINE | |||||
| #endif // __AVR__ | |||||
| #endif // SdSpi_h | #endif // SdSpi_h | ||||
| /* Arduino SdSpi Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | |||||
| * This file is part of the Arduino SdSpi 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 Arduino SdSpi Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #include <SdSpi.h> | |||||
| #if USE_NATIVE_AVR_SPI | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpi::begin() { | |||||
| // set SS high - may be chip select for another SPI device | |||||
| digitalWrite(SS, HIGH); | |||||
| // SS must be in output mode even it is not chip select | |||||
| pinMode(SS, OUTPUT); | |||||
| pinMode(MISO, INPUT); | |||||
| pinMode(MOSI, OUTPUT); | |||||
| pinMode(SCK, OUTPUT); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpi::init(uint8_t sckDivisor) { | |||||
| uint8_t r = 0; | |||||
| for (uint8_t b = 2; sckDivisor > b && r < 6; b <<= 1, r++) {} | |||||
| // See avr processor documentation | |||||
| SPCR = (1 << SPE) | (1 << MSTR) | (r >> 1); | |||||
| SPSR = r & 1 || r == 6 ? 0 : 1 << SPI2X; | |||||
| } | |||||
| #if !USE_AVR_NATIVE_SPI_INLINE | |||||
| //------------------------------------------------------------------------------ | |||||
| uint8_t SdSpi::receive() { | |||||
| SPDR = 0XFF; | |||||
| while (!(SPSR & (1 << SPIF))) {} | |||||
| return SPDR; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
| if (n-- == 0) return 0; | |||||
| SPDR = 0XFF; | |||||
| for (size_t i = 0; i < n; i++) { | |||||
| while (!(SPSR & (1 << SPIF))) {} | |||||
| uint8_t b = SPDR; | |||||
| SPDR = 0XFF; | |||||
| buf[i] = b; | |||||
| } | |||||
| while (!(SPSR & (1 << SPIF))) {} | |||||
| buf[n] = SPDR; | |||||
| return 0; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpi::send(uint8_t data) { | |||||
| SPDR = data; | |||||
| while (!(SPSR & (1 << SPIF))) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
| if (n == 0) return; | |||||
| SPDR = buf[0]; | |||||
| if (n > 1) { | |||||
| uint8_t b = buf[1]; | |||||
| size_t i = 2; | |||||
| while (1) { | |||||
| while (!(SPSR & (1 << SPIF))) {} | |||||
| SPDR = b; | |||||
| if (i == n) break; | |||||
| b = buf[i++]; | |||||
| } | |||||
| } | |||||
| while (!(SPSR & (1 << SPIF))) {} | |||||
| } | |||||
| #endif // !USE_AVR_NATIVE_SPI_INLINE | |||||
| #endif // USE_NATIVE_AVR_SPI | |||||
| /* Arduino SdSpi Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | |||||
| * This file is part of the Arduino SdSpi 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 Arduino SdSpi Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #include <SdSpi.h> | |||||
| #if USE_ARDUINO_SPI_LIBRARY | |||||
| #include <SPI.h> | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpi::begin() { | |||||
| SPI.begin(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpi::init(uint8_t sckDivisor) { | |||||
| SPI.setBitOrder(MSBFIRST); | |||||
| SPI.setDataMode(SPI_MODE0); | |||||
| #ifndef SPI_CLOCK_DIV128 | |||||
| SPI.setClockDivider(sckDivisor); | |||||
| #else // SPI_CLOCK_DIV128 | |||||
| int v; | |||||
| if (sckDivisor <= 2) v = SPI_CLOCK_DIV2; | |||||
| else if (sckDivisor <= 4) v = SPI_CLOCK_DIV4; | |||||
| else if (sckDivisor <= 8) v = SPI_CLOCK_DIV8; | |||||
| else if (sckDivisor <= 16) v = SPI_CLOCK_DIV16; | |||||
| else if (sckDivisor <= 32) v = SPI_CLOCK_DIV32; | |||||
| else if (sckDivisor <= 64) v = SPI_CLOCK_DIV64; | |||||
| else v = SPI_CLOCK_DIV128; | |||||
| SPI.setClockDivider(v); | |||||
| #endif // SPI_CLOCK_DIV128 | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** SPI receive a byte */ | |||||
| uint8_t SdSpi::receive() { | |||||
| return SPI.transfer(0XFF); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** SPI receive multiple bytes */ | |||||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
| for (size_t i = 0; i < n; i++) { | |||||
| buf[i] = SPI.transfer(0XFF); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** SPI send a byte */ | |||||
| void SdSpi::send(uint8_t b) { | |||||
| SPI.transfer(b); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** SPI send multiple bytes */ | |||||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
| for (size_t i = 0; i < n; i++) { | |||||
| SPI.transfer(buf[i]); | |||||
| } | |||||
| } | |||||
| #endif // USE_ARDUINO_SPI_LIBRARY |
| /* Arduino Sd2Card Library | |||||
| /* Arduino SdSpiCard Library | |||||
| * Copyright (C) 2012 by William Greiman | * Copyright (C) 2012 by William Greiman | ||||
| * | * | ||||
| * This file is part of the Arduino Sd2Card Library | |||||
| * This file is part of the Arduino SdSpiCard Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino Sd2Card Library. If not, see | |||||
| * along with the Arduino SdSpiCard Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include <Sd2Card.h> | |||||
| #include <SdSpi.h> | |||||
| #if !USE_SOFTWARE_SPI && ENABLE_SPI_TRANSACTION | |||||
| #include "SdSpiCard.h" | |||||
| #include "SdSpi.h" | |||||
| #if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) | |||||
| #include <SPI.h> | #include <SPI.h> | ||||
| #endif // !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||||
| #endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) | |||||
| // debug trace macro | // debug trace macro | ||||
| #define SD_TRACE(m, b) | #define SD_TRACE(m, b) | ||||
| // #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); | // #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); | ||||
| //------------------------------------------------------------------------------ | |||||
| SdSpi Sd2Card::m_spi; | |||||
| //============================================================================== | //============================================================================== | ||||
| #if USE_SD_CRC | #if USE_SD_CRC | ||||
| // CRC functions | // CRC functions | ||||
| #endif // CRC_CCITT | #endif // CRC_CCITT | ||||
| #endif // USE_SD_CRC | #endif // USE_SD_CRC | ||||
| //============================================================================== | //============================================================================== | ||||
| // Sd2Card member functions | |||||
| // SdSpiCard member functions | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Initialize an SD flash memory card. | |||||
| * | |||||
| * \param[in] chipSelectPin SD chip select pin number. | |||||
| * \param[in] sckDivisor SPI SCK clock rate divisor. | |||||
| * | |||||
| * \return The value one, true, is returned for success and | |||||
| * the value zero, false, is returned for failure. The reason for failure | |||||
| * can be determined by calling errorCode() and errorData(). | |||||
| */ | |||||
| bool Sd2Card::begin(uint8_t chipSelectPin, uint8_t sckDivisor) { | |||||
| bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||||
| m_errorCode = m_type = 0; | m_errorCode = m_type = 0; | ||||
| m_spi = spi; | |||||
| m_chipSelectPin = chipSelectPin; | m_chipSelectPin = chipSelectPin; | ||||
| // 16-bit init start time allows over a minute | // 16-bit init start time allows over a minute | ||||
| uint16_t t0 = (uint16_t)millis(); | uint16_t t0 = (uint16_t)millis(); | ||||
| pinMode(m_chipSelectPin, OUTPUT); | pinMode(m_chipSelectPin, OUTPUT); | ||||
| digitalWrite(m_chipSelectPin, HIGH); | digitalWrite(m_chipSelectPin, HIGH); | ||||
| m_spi.begin(); | |||||
| spiBegin(); | |||||
| // set SCK rate for initialization commands | // set SCK rate for initialization commands | ||||
| m_sckDivisor = SPI_SCK_INIT_DIVISOR; | m_sckDivisor = SPI_SCK_INIT_DIVISOR; | ||||
| m_spi.init(m_sckDivisor); | |||||
| spiInit(m_sckDivisor); | |||||
| // must supply min of 74 clock cycles with CS high. | // must supply min of 74 clock cycles with CS high. | ||||
| for (uint8_t i = 0; i < 10; i++) m_spi.send(0XFF); | |||||
| for (uint8_t i = 0; i < 10; i++) spiSend(0XFF); | |||||
| // command to go idle in SPI mode | // command to go idle in SPI mode | ||||
| while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { | while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { | ||||
| type(SD_CARD_TYPE_SD1); | type(SD_CARD_TYPE_SD1); | ||||
| break; | break; | ||||
| } | } | ||||
| for (uint8_t i = 0; i < 4; i++) m_status = m_spi.receive(); | |||||
| for (uint8_t i = 0; i < 4; i++) m_status = spiReceive(); | |||||
| if (m_status == 0XAA) { | if (m_status == 0XAA) { | ||||
| type(SD_CARD_TYPE_SD2); | type(SD_CARD_TYPE_SD2); | ||||
| break; | break; | ||||
| error(SD_CARD_ERROR_CMD58); | error(SD_CARD_ERROR_CMD58); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| if ((m_spi.receive() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC); | |||||
| if ((spiReceive() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC); | |||||
| // Discard rest of ocr - contains allowed voltage range. | // Discard rest of ocr - contains allowed voltage range. | ||||
| for (uint8_t i = 0; i < 3; i++) m_spi.receive(); | |||||
| for (uint8_t i = 0; i < 3; i++) spiReceive(); | |||||
| } | } | ||||
| chipSelectHigh(); | chipSelectHigh(); | ||||
| m_sckDivisor = sckDivisor; | m_sckDivisor = sckDivisor; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // send command and return error code. Return zero for OK | // send command and return error code. Return zero for OK | ||||
| uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { | |||||
| uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { | |||||
| // select card | // select card | ||||
| chipSelectLow(); | chipSelectLow(); | ||||
| d[5] = CRC7(d, 5); | d[5] = CRC7(d, 5); | ||||
| // send message | // send message | ||||
| for (uint8_t k = 0; k < 6; k++) m_spi.send(d[k]); | |||||
| for (uint8_t k = 0; k < 6; k++) spiSend(d[k]); | |||||
| #else // USE_SD_CRC | #else // USE_SD_CRC | ||||
| // send command | // send command | ||||
| m_spi.send(cmd | 0x40); | |||||
| spiSend(cmd | 0x40); | |||||
| // send argument | // send argument | ||||
| for (int8_t i = 3; i >= 0; i--) m_spi.send(pa[i]); | |||||
| for (int8_t i = 3; i >= 0; i--) spiSend(pa[i]); | |||||
| // send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA | // send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA | ||||
| m_spi.send(cmd == CMD0 ? 0X95 : 0X87); | |||||
| spiSend(cmd == CMD0 ? 0X95 : 0X87); | |||||
| #endif // USE_SD_CRC | #endif // USE_SD_CRC | ||||
| // skip stuff byte for stop read | // skip stuff byte for stop read | ||||
| if (cmd == CMD12) m_spi.receive(); | |||||
| if (cmd == CMD12) spiReceive(); | |||||
| // wait for response | // wait for response | ||||
| for (uint8_t i = 0; ((m_status = m_spi.receive()) & 0X80) && i != 0XFF; i++) { | |||||
| for (uint8_t i = 0; ((m_status = spiReceive()) & 0X80) && i != 0XFF; i++) { | |||||
| } | } | ||||
| return m_status; | return m_status; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Determine the size of an SD flash memory card. | |||||
| * | |||||
| * \return The number of 512 byte data blocks in the card | |||||
| * or zero if an error occurs. | |||||
| */ | |||||
| uint32_t Sd2Card::cardSize() { | |||||
| uint32_t SdSpiCard::cardSize() { | |||||
| csd_t csd; | csd_t csd; | ||||
| if (!readCSD(&csd)) return 0; | if (!readCSD(&csd)) return 0; | ||||
| if (csd.v1.csd_ver == 0) { | if (csd.v1.csd_ver == 0) { | ||||
| } | } | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void Sd2Card::spiYield() { | |||||
| #if ENABLE_SPI_YIELD && !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||||
| void SdSpiCard::spiYield() { | |||||
| #if ENABLE_SPI_TRANSACTION && ENABLE_SPI_YIELD && defined(SPI_HAS_TRANSACTION) | |||||
| chipSelectHigh(); | chipSelectHigh(); | ||||
| chipSelectLow(); | chipSelectLow(); | ||||
| #endif // ENABLE_SPI_YIELD && !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||||
| #endif // ENABLE_SPI_TRANSACTION && ENABLE_SPI_YIELD && SPI_HAS_TRANSACTION | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void Sd2Card::chipSelectHigh() { | |||||
| void SdSpiCard::chipSelectHigh() { | |||||
| digitalWrite(m_chipSelectPin, HIGH); | digitalWrite(m_chipSelectPin, HIGH); | ||||
| // insure MISO goes high impedance | // insure MISO goes high impedance | ||||
| m_spi.send(0XFF); | |||||
| #if !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||||
| SPI.endTransaction(); | |||||
| #endif // !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||||
| spiSend(0XFF); | |||||
| #if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) | |||||
| if (useSpiTransactions()) SPI.endTransaction(); | |||||
| #endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void Sd2Card::chipSelectLow() { | |||||
| #if !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||||
| SPI.beginTransaction(SPISettings()); | |||||
| #endif // !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||||
| m_spi.init(m_sckDivisor); | |||||
| void SdSpiCard::chipSelectLow() { | |||||
| #if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) | |||||
| if (useSpiTransactions()) SPI.beginTransaction(SPISettings()); | |||||
| #endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) | |||||
| spiInit(m_sckDivisor); | |||||
| digitalWrite(m_chipSelectPin, LOW); | digitalWrite(m_chipSelectPin, LOW); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Erase a range of blocks. | |||||
| * | |||||
| * \param[in] firstBlock The address of the first block in the range. | |||||
| * \param[in] lastBlock The address of the last block in the range. | |||||
| * | |||||
| * \note This function requests the SD card to do a flash erase for a | |||||
| * range of blocks. The data on the card after an erase operation is | |||||
| * 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. | |||||
| */ | |||||
| bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { | |||||
| bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | |||||
| csd_t csd; | csd_t csd; | ||||
| if (!readCSD(&csd)) goto fail; | if (!readCSD(&csd)) goto fail; | ||||
| // check for single block erase | // check for single block erase | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Determine if card supports single block erase. | |||||
| * | |||||
| * \return The value one, true, is returned if single block erase is supported. | |||||
| * The value zero, false, is returned if single block erase is not supported. | |||||
| */ | |||||
| bool Sd2Card::eraseSingleBlockEnable() { | |||||
| bool SdSpiCard::eraseSingleBlockEnable() { | |||||
| csd_t csd; | csd_t csd; | ||||
| return readCSD(&csd) ? csd.v1.erase_blk_en : false; | return readCSD(&csd) ? csd.v1.erase_blk_en : false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Check for busy. MISO low indicates the card is busy. | |||||
| * | |||||
| * \return true if busy else false. | |||||
| */ | |||||
| bool Sd2Card::isBusy() { | |||||
| bool SdSpiCard::isBusy() { | |||||
| bool rtn; | bool rtn; | ||||
| chipSelectLow(); | chipSelectLow(); | ||||
| for (uint8_t i = 0; i < 8; i++) { | for (uint8_t i = 0; i < 8; i++) { | ||||
| rtn = m_spi.receive() != 0XFF; | |||||
| rtn = spiReceive() != 0XFF; | |||||
| if (!rtn) break; | if (!rtn) break; | ||||
| } | } | ||||
| chipSelectHigh(); | chipSelectHigh(); | ||||
| return rtn; | return rtn; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Read a 512 byte block from an SD card. | |||||
| * | |||||
| * \param[in] blockNumber 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. | |||||
| */ | |||||
| bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) { | |||||
| bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { | |||||
| SD_TRACE("RB", blockNumber); | SD_TRACE("RB", blockNumber); | ||||
| // use address if not SDHC card | // use address if not SDHC card | ||||
| if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9; | if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9; | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Read one data block in a multiple block read sequence | |||||
| * | |||||
| * \param[in] 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. | |||||
| */ | |||||
| bool Sd2Card::readData(uint8_t *dst) { | |||||
| bool SdSpiCard::readBlocks(uint32_t block, uint8_t* dst, size_t count) { | |||||
| if (!readStart(block)) return false; | |||||
| for (uint16_t b = 0; b < count; b++, dst += 512) { | |||||
| if (!readData(dst)) return false; | |||||
| } | |||||
| return readStop(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool SdSpiCard::readData(uint8_t *dst) { | |||||
| chipSelectLow(); | chipSelectLow(); | ||||
| return readData(dst, 512); | return readData(dst, 512); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool Sd2Card::readData(uint8_t* dst, size_t count) { | |||||
| bool SdSpiCard::readData(uint8_t* dst, size_t count) { | |||||
| #if USE_SD_CRC | #if USE_SD_CRC | ||||
| uint16_t crc; | uint16_t crc; | ||||
| #endif // USE_SD_CRC | #endif // USE_SD_CRC | ||||
| // wait for start block token | // wait for start block token | ||||
| uint16_t t0 = millis(); | uint16_t t0 = millis(); | ||||
| while ((m_status = m_spi.receive()) == 0XFF) { | |||||
| while ((m_status = spiReceive()) == 0XFF) { | |||||
| if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) { | if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) { | ||||
| error(SD_CARD_ERROR_READ_TIMEOUT); | error(SD_CARD_ERROR_READ_TIMEOUT); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| spiYield(); | |||||
| } | } | ||||
| if (m_status != DATA_START_BLOCK) { | if (m_status != DATA_START_BLOCK) { | ||||
| error(SD_CARD_ERROR_READ); | error(SD_CARD_ERROR_READ); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| // transfer data | // transfer data | ||||
| if ((m_status = m_spi.receive(dst, count))) { | |||||
| if ((m_status = spiReceive(dst, count))) { | |||||
| error(SD_CARD_ERROR_SPI_DMA); | error(SD_CARD_ERROR_SPI_DMA); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| #if USE_SD_CRC | #if USE_SD_CRC | ||||
| // get crc | // get crc | ||||
| crc = (m_spi.receive() << 8) | m_spi.receive(); | |||||
| crc = (spiReceive() << 8) | spiReceive(); | |||||
| if (crc != CRC_CCITT(dst, count)) { | if (crc != CRC_CCITT(dst, count)) { | ||||
| error(SD_CARD_ERROR_READ_CRC); | error(SD_CARD_ERROR_READ_CRC); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| #else | #else | ||||
| // discard crc | // discard crc | ||||
| m_spi.receive(); | |||||
| m_spi.receive(); | |||||
| spiReceive(); | |||||
| spiReceive(); | |||||
| #endif // USE_SD_CRC | #endif // USE_SD_CRC | ||||
| chipSelectHigh(); | chipSelectHigh(); | ||||
| return true; | return true; | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Read OCR register. | |||||
| * | |||||
| * \param[out] ocr Value of OCR register. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool Sd2Card::readOCR(uint32_t* ocr) { | |||||
| bool SdSpiCard::readOCR(uint32_t* ocr) { | |||||
| uint8_t *p = reinterpret_cast<uint8_t*>(ocr); | uint8_t *p = reinterpret_cast<uint8_t*>(ocr); | ||||
| if (cardCommand(CMD58, 0)) { | if (cardCommand(CMD58, 0)) { | ||||
| error(SD_CARD_ERROR_CMD58); | error(SD_CARD_ERROR_CMD58); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| for (uint8_t i = 0; i < 4; i++) p[3-i] = m_spi.receive(); | |||||
| for (uint8_t i = 0; i < 4; i++) p[3-i] = spiReceive(); | |||||
| chipSelectHigh(); | chipSelectHigh(); | ||||
| return true; | return true; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** read CID or CSR register */ | /** read CID or CSR register */ | ||||
| bool Sd2Card::readRegister(uint8_t cmd, void* buf) { | |||||
| bool SdSpiCard::readRegister(uint8_t cmd, void* buf) { | |||||
| uint8_t* dst = reinterpret_cast<uint8_t*>(buf); | uint8_t* dst = reinterpret_cast<uint8_t*>(buf); | ||||
| if (cardCommand(cmd, 0)) { | if (cardCommand(cmd, 0)) { | ||||
| error(SD_CARD_ERROR_READ_REG); | error(SD_CARD_ERROR_READ_REG); | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Start a read multiple blocks sequence. | |||||
| * | |||||
| * \param[in] blockNumber Address of first block in sequence. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool Sd2Card::readStart(uint32_t blockNumber) { | |||||
| bool SdSpiCard::readStart(uint32_t blockNumber) { | |||||
| SD_TRACE("RS", blockNumber); | SD_TRACE("RS", blockNumber); | ||||
| if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9; | if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9; | ||||
| if (cardCommand(CMD18, blockNumber)) { | if (cardCommand(CMD18, blockNumber)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** End a read multiple blocks sequence. | |||||
| * | |||||
| * \return The value one, true, is returned for success and | |||||
| * the value zero, false, is returned for failure. | |||||
| */ | |||||
| bool Sd2Card::readStop() { | |||||
| bool SdSpiCard::readStop() { | |||||
| if (cardCommand(CMD12, 0)) { | if (cardCommand(CMD12, 0)) { | ||||
| error(SD_CARD_ERROR_CMD12); | error(SD_CARD_ERROR_CMD12); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // wait for card to go not busy | // wait for card to go not busy | ||||
| bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) { | |||||
| bool SdSpiCard::waitNotBusy(uint16_t timeoutMillis) { | |||||
| uint16_t t0 = millis(); | uint16_t t0 = millis(); | ||||
| while (m_spi.receive() != 0XFF) { | |||||
| while (spiReceive() != 0XFF) { | |||||
| if (((uint16_t)millis() - t0) >= timeoutMillis) goto fail; | if (((uint16_t)millis() - t0) >= timeoutMillis) goto fail; | ||||
| spiYield(); | spiYield(); | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * 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. | |||||
| */ | |||||
| bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) { | |||||
| bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | |||||
| SD_TRACE("WB", blockNumber); | SD_TRACE("WB", blockNumber); | ||||
| // use address if not SDHC card | // use address if not SDHC card | ||||
| if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; | if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| // response is r2 so get and check two bytes for nonzero | // response is r2 so get and check two bytes for nonzero | ||||
| if (cardCommand(CMD13, 0) || m_spi.receive()) { | |||||
| if (cardCommand(CMD13, 0) || spiReceive()) { | |||||
| error(SD_CARD_ERROR_WRITE_PROGRAMMING); | error(SD_CARD_ERROR_WRITE_PROGRAMMING); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** 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. | |||||
| */ | |||||
| bool Sd2Card::writeData(const uint8_t* src) { | |||||
| bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { | |||||
| if (!writeStart(block, count)) return false; | |||||
| for (size_t b = 0; b < count; b++, src += 512) { | |||||
| if (!writeData(src)) return false; | |||||
| } | |||||
| return writeStop(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool SdSpiCard::writeData(const uint8_t* src) { | |||||
| chipSelectLow(); | chipSelectLow(); | ||||
| // wait for previous write to finish | // wait for previous write to finish | ||||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // send one block of data for write block or write multiple blocks | // send one block of data for write block or write multiple blocks | ||||
| bool Sd2Card::writeData(uint8_t token, const uint8_t* src) { | |||||
| bool SdSpiCard::writeData(uint8_t token, const uint8_t* src) { | |||||
| #if USE_SD_CRC | #if USE_SD_CRC | ||||
| uint16_t crc = CRC_CCITT(src, 512); | uint16_t crc = CRC_CCITT(src, 512); | ||||
| #else // USE_SD_CRC | #else // USE_SD_CRC | ||||
| uint16_t crc = 0XFFFF; | uint16_t crc = 0XFFFF; | ||||
| #endif // USE_SD_CRC | #endif // USE_SD_CRC | ||||
| m_spi.send(token); | |||||
| m_spi.send(src, 512); | |||||
| m_spi.send(crc >> 8); | |||||
| m_spi.send(crc & 0XFF); | |||||
| spiSend(token); | |||||
| spiSend(src, 512); | |||||
| spiSend(crc >> 8); | |||||
| spiSend(crc & 0XFF); | |||||
| m_status = m_spi.receive(); | |||||
| m_status = spiReceive(); | |||||
| if ((m_status & DATA_RES_MASK) != DATA_RES_ACCEPTED) { | if ((m_status & DATA_RES_MASK) != DATA_RES_ACCEPTED) { | ||||
| error(SD_CARD_ERROR_WRITE); | error(SD_CARD_ERROR_WRITE); | ||||
| goto fail; | goto fail; | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Start a write multiple blocks sequence. | |||||
| * | |||||
| * \param[in] blockNumber Address of first block in sequence. | |||||
| * \param[in] eraseCount The number of blocks to be pre-erased. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) { | |||||
| bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) { | |||||
| SD_TRACE("WS", blockNumber); | SD_TRACE("WS", blockNumber); | ||||
| // send pre-erase count | // send pre-erase count | ||||
| if (cardAcmd(ACMD23, eraseCount)) { | if (cardAcmd(ACMD23, eraseCount)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** End a write multiple blocks sequence. | |||||
| * | |||||
| * \return The value one, true, is returned for success and | |||||
| * the value zero, false, is returned for failure. | |||||
| */ | |||||
| bool Sd2Card::writeStop() { | |||||
| bool SdSpiCard::writeStop() { | |||||
| chipSelectLow(); | chipSelectLow(); | ||||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | ||||
| m_spi.send(STOP_TRAN_TOKEN); | |||||
| spiSend(STOP_TRAN_TOKEN); | |||||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | ||||
| chipSelectHigh(); | chipSelectHigh(); | ||||
| return true; | return true; |
| /* Arduino SdSpiCard Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| * | |||||
| * This file is part of the Arduino SdSpiCard 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 Arduino SdSpiCard Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #ifndef SpiCard_h | |||||
| #define SpiCard_h | |||||
| /** | |||||
| * \file | |||||
| * \brief SdSpiCard class for V2 SD/SDHC cards | |||||
| */ | |||||
| #include <Arduino.h> | |||||
| #include <SdFatConfig.h> | |||||
| #include <SdInfo.h> | |||||
| #include <SdSpi.h> | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class SdSpiCard | |||||
| * \brief Raw access to SD and SDHC flash memory cards via SPI protocol. | |||||
| */ | |||||
| class SdSpiCard { | |||||
| public: | |||||
| /** typedef for SPI class. */ | |||||
| #if USE_MULTIPLE_SPI_TYPES | |||||
| typedef SdSpiBase m_spi_t; | |||||
| #else // USE_MULTIPLE_SPI_TYPES | |||||
| typedef SpiDefault_t m_spi_t; | |||||
| #endif // USE_MULTIPLE_SPI_TYPES | |||||
| /** Construct an instance of SdSpiCard. */ | |||||
| SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} | |||||
| /** Initialize the SD card. | |||||
| * \param[in] spi SPI object. | |||||
| * \param[in] chipSelectPin SD chip select pin. | |||||
| * \param[in] sckDivisor SPI clock divisor. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS, | |||||
| uint8_t sckDivisor = SPI_FULL_SPEED); | |||||
| /** | |||||
| * Determine the size of an SD flash memory card. | |||||
| * | |||||
| * \return The number of 512 byte data blocks in the card | |||||
| * or zero if an error occurs. | |||||
| */ | |||||
| uint32_t cardSize(); | |||||
| /** Erase a range of blocks. | |||||
| * | |||||
| * \param[in] firstBlock The address of the first block in the range. | |||||
| * \param[in] lastBlock The address of the last block in the range. | |||||
| * | |||||
| * \note This function requests the SD card to do a flash erase for a | |||||
| * range of blocks. The data on the card after an erase operation is | |||||
| * 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. | |||||
| */ | |||||
| bool erase(uint32_t firstBlock, uint32_t lastBlock); | |||||
| /** Determine if card supports single block erase. | |||||
| * | |||||
| * \return true is returned if single block erase is supported. | |||||
| * false is returned if single block erase is not supported. | |||||
| */ | |||||
| bool eraseSingleBlockEnable(); | |||||
| /** | |||||
| * Set SD error code. | |||||
| * \param[in] code value for error 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;} | |||||
| /** \return error data for last error. */ | |||||
| int errorData() const {return m_status;} | |||||
| /** | |||||
| * Check for busy. MISO low indicates the card is busy. | |||||
| * | |||||
| * \return true if busy else false. | |||||
| */ | |||||
| bool isBusy(); | |||||
| /** | |||||
| * Read a 512 byte block from an SD card. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool readBlock(uint32_t block, uint8_t* dst); | |||||
| /** | |||||
| * Read multiple 512 byte blocks from an SD card. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool readBlocks(uint32_t block, uint8_t* dst, size_t count); | |||||
| /** | |||||
| * Read a card's CID register. The CID contains card identification | |||||
| * information such as Manufacturer ID, Product name, Product serial | |||||
| * number and Manufacturing date. | |||||
| * | |||||
| * \param[out] cid pointer to area for returned data. | |||||
| * | |||||
| * \return true for success or false for failure. | |||||
| */ | |||||
| bool readCID(cid_t* cid) { | |||||
| return readRegister(CMD10, cid); | |||||
| } | |||||
| /** | |||||
| * Read a card's CSD register. The CSD contains Card-Specific Data that | |||||
| * provides information regarding access to the card's contents. | |||||
| * | |||||
| * \param[out] csd pointer to area for returned data. | |||||
| * | |||||
| * \return true for success or false for failure. | |||||
| */ | |||||
| bool readCSD(csd_t* csd) { | |||||
| return readRegister(CMD9, csd); | |||||
| } | |||||
| /** Read one data block in a multiple block read sequence | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool readData(uint8_t *dst); | |||||
| /** Read OCR register. | |||||
| * | |||||
| * \param[out] ocr Value of OCR register. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool readOCR(uint32_t* ocr); | |||||
| /** Start a read multiple blocks sequence. | |||||
| * | |||||
| * \param[in] blockNumber Address of first block in sequence. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| 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. | |||||
| */ | |||||
| bool readStop(); | |||||
| /** Return SCK divisor. | |||||
| * | |||||
| * \return Requested SCK divisor. | |||||
| */ | |||||
| 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;} | |||||
| /** | |||||
| * 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. | |||||
| */ | |||||
| bool writeBlock(uint32_t blockNumber, const uint8_t* src); | |||||
| /** | |||||
| * Write multiple 512 byte blocks to an SD card. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| 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. | |||||
| */ | |||||
| bool writeData(const uint8_t* src); | |||||
| /** Start a write multiple blocks sequence. | |||||
| * | |||||
| * \param[in] blockNumber Address of first block in sequence. | |||||
| * \param[in] eraseCount The number of blocks to be pre-erased. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| 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. | |||||
| */ | |||||
| bool writeStop(); | |||||
| private: | |||||
| // private functions | |||||
| uint8_t cardAcmd(uint8_t cmd, uint32_t arg) { | |||||
| cardCommand(CMD55, 0); | |||||
| return cardCommand(cmd, arg); | |||||
| } | |||||
| uint8_t cardCommand(uint8_t cmd, uint32_t arg); | |||||
| bool readData(uint8_t* dst, size_t count); | |||||
| bool readRegister(uint8_t cmd, void* buf); | |||||
| void chipSelectHigh(); | |||||
| void chipSelectLow(); | |||||
| void spiYield(); | |||||
| 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();} | |||||
| m_spi_t* m_spi; | |||||
| uint8_t m_chipSelectPin; | |||||
| uint8_t m_errorCode; | |||||
| uint8_t m_sckDivisor; | |||||
| uint8_t m_status; | |||||
| uint8_t m_type; | |||||
| }; | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class Sd2Card | |||||
| * \brief Raw access to SD and SDHC card using default SPI library. | |||||
| */ | |||||
| class Sd2Card : public SdSpiCard { | |||||
| public: | |||||
| /** Initialize the SD card. | |||||
| * \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); | |||||
| } | |||||
| /** Initialize the SD card. Obsolete form. | |||||
| * \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;} | |||||
| SpiDefault_t m_spi; | |||||
| }; | |||||
| #endif // SpiCard_h |
| * along with the Arduino SdSpi Library. If not, see | * along with the Arduino SdSpi Library. If not, see | ||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include <SdSpi.h> | |||||
| #if USE_NATIVE_SAM3X_SPI | |||||
| #include "SdSpi.h" | |||||
| #if defined(__SAM3X8E__) || defined(__SAM3X8H__) | |||||
| /** Use SAM3X DMAC if nonzero */ | /** Use SAM3X DMAC if nonzero */ | ||||
| #define USE_SAM3X_DMAC 1 | #define USE_SAM3X_DMAC 1 | ||||
| /** Use extra Bus Matrix arbitration fix if nonzero */ | /** Use extra Bus Matrix arbitration fix if nonzero */ | ||||
| // leave RDR empty | // leave RDR empty | ||||
| uint8_t b = pSpi->SPI_RDR; | uint8_t b = pSpi->SPI_RDR; | ||||
| } | } | ||||
| #endif // USE_NATIVE_SAM3X_SPI | |||||
| #endif // defined(__SAM3X8E__) || defined(__SAM3X8H__) |
| /* Arduino SdSpi Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | |||||
| * This file is part of the Arduino SdSpi 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 Arduino SdSpi Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #include <SdSpi.h> | |||||
| #if USE_SOFTWARE_SPI | |||||
| #include <SoftSPI.h> | |||||
| static | |||||
| SoftSPI<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN, 0> softSpiBus; | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * initialize SPI pins | |||||
| */ | |||||
| void SdSpi::begin() { | |||||
| softSpiBus.begin(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Initialize hardware SPI - dummy for soft SPI | |||||
| */ | |||||
| void SdSpi::init(uint8_t sckDivisor) {} | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Soft SPI receive byte */ | |||||
| uint8_t SdSpi::receive() { | |||||
| return softSpiBus.receive(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Soft SPI read data */ | |||||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
| for (size_t i = 0; i < n; i++) { | |||||
| buf[i] = receive(); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Soft SPI send byte */ | |||||
| void SdSpi::send(uint8_t data) { | |||||
| softSpiBus.send(data); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
| for (size_t i = 0; i < n; i++) { | |||||
| send(buf[i]); | |||||
| } | |||||
| } | |||||
| #endif // USE_SOFTWARE_SPI |
| * along with the Arduino SdSpi Library. If not, see | * along with the Arduino SdSpi Library. If not, see | ||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include <SdSpi.h> | |||||
| #if USE_NATIVE_TEENSY3_SPI | |||||
| #include "SdSpi.h" | |||||
| #if defined(__arm__) && defined(CORE_TEENSY) | |||||
| // Teensy 3.0 functions | // Teensy 3.0 functions | ||||
| #include <mk20dx128.h> | |||||
| #include "mk20dx128.h" | |||||
| // use 16-bit frame if SPI_USE_8BIT_FRAME is zero | // use 16-bit frame if SPI_USE_8BIT_FRAME is zero | ||||
| #define SPI_USE_8BIT_FRAME 0 | #define SPI_USE_8BIT_FRAME 0 | ||||
| // Limit initial fifo to three entries to avoid fifo overrun | // Limit initial fifo to three entries to avoid fifo overrun | ||||
| } | } | ||||
| #endif // SPI_USE_8BIT_FRAME | #endif // SPI_USE_8BIT_FRAME | ||||
| } | } | ||||
| #endif // USE_NATIVE_TEENSY3_SPI | |||||
| #endif // defined(__arm__) && defined(CORE_TEENSY) |
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #ifndef SdVolume_h | #ifndef SdVolume_h | ||||
| #include "SdSpiCard.h" | |||||
| #include "utility/FatLib.h" | |||||
| #define SdVolume_h | #define SdVolume_h | ||||
| /** | |||||
| * \file | |||||
| * \brief SdVolume class | |||||
| */ | |||||
| #include <SdFatConfig.h> | |||||
| #include <Sd2Card.h> | |||||
| #include <utility/FatStructs.h> | |||||
| #ifndef USE_SD_VOLUME | |||||
| #error SdVolume is deperacated. Remove this line to continue using this class. | |||||
| #endif // USE_SD_VOLUME | |||||
| //============================================================================== | //============================================================================== | ||||
| // SdVolume class | |||||
| /** | |||||
| * \brief Cache for an SD data block | |||||
| */ | |||||
| union cache_t { | |||||
| /** Used to access cached file data blocks. */ | |||||
| uint8_t data[512]; | |||||
| /** Used to access cached FAT16 entries. */ | |||||
| uint16_t fat16[256]; | |||||
| /** Used to access cached FAT32 entries. */ | |||||
| uint32_t fat32[128]; | |||||
| /** Used to access cached directory entries. */ | |||||
| dir_t dir[16]; | |||||
| /** Used to access a cached Master Boot Record. */ | |||||
| mbr_t mbr; | |||||
| /** Used to access to a cached FAT boot sector. */ | |||||
| fat_boot_t fbs; | |||||
| /** Used to access to a cached FAT32 boot sector. */ | |||||
| fat32_boot_t fbs32; | |||||
| /** Used to access to a cached FAT32 FSINFO sector. */ | |||||
| fat32_fsinfo_t fsinfo; | |||||
| }; | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | /** | ||||
| * \class SdVolume | * \class SdVolume | ||||
| * \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. | |||||
| * \brief SdVolume used in Quick start. Soon to be removed. | |||||
| */ | */ | ||||
| class SdVolume { | |||||
| class SdVolume : public FatVolume { | |||||
| public: | public: | ||||
| /** Create an instance of SdVolume */ | |||||
| SdVolume() : m_fatType(0) {} | |||||
| /** Clear the cache and returns a pointer to the cache. Used by the WaveRP | |||||
| * recorder to do raw write to the SD card. Not for normal apps. | |||||
| * \return A pointer to the cache buffer or zero if an error occurs. | |||||
| */ | |||||
| cache_t* cacheClear() { | |||||
| if (!cacheSync()) return 0; | |||||
| m_cacheBlockNumber = 0XFFFFFFFF; | |||||
| return &m_cacheBuffer; | |||||
| } | |||||
| /** Initialize a FAT volume. Try partition one first then try super | |||||
| /** Initialize a FAT volume. Try partition one first then try super | |||||
| * floppy format. | * floppy format. | ||||
| * | * | ||||
| * \param[in] dev The Sd2Card where the volume is located. | * \param[in] dev The Sd2Card where the volume is located. | ||||
| * | * | ||||
| * \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. | |||||
| */ | |||||
| bool init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0);} | |||||
| bool init(Sd2Card* dev, uint8_t part); | |||||
| // inline functions that return volume info | |||||
| /** \return The volume's cluster size in blocks. */ | |||||
| uint8_t blocksPerCluster() const {return m_blocksPerCluster;} | |||||
| /** \return The number of blocks in one FAT. */ | |||||
| uint32_t blocksPerFat() const {return m_blocksPerFat;} | |||||
| /** \return The total number of clusters in the volume. */ | |||||
| uint32_t clusterCount() const {return m_clusterCount;} | |||||
| /** \return The shift count required to multiply by blocksPerCluster. */ | |||||
| uint8_t clusterSizeShift() const {return m_clusterSizeShift;} | |||||
| /** \return The logical block number for the start of file data. */ | |||||
| uint32_t dataStartBlock() const {return clusterStartBlock(2);} | |||||
| /** \return The number of FAT structures on the volume. */ | |||||
| uint8_t fatCount() const {return m_fatCount;} | |||||
| /** \return The logical block number for the start of the first FAT. */ | |||||
| 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;} | |||||
| int32_t freeClusterCount(); | |||||
| /** \return The number of entries in the root directory for FAT16 volumes. */ | |||||
| uint32_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;} | |||||
| /** Sd2Card object for this volume | |||||
| * \return pointer to Sd2Card object. | |||||
| * \return true for success else false. | |||||
| */ | */ | ||||
| Sd2Card* sdCard() {return m_sdCard;} | |||||
| /** Debug access to FAT table | |||||
| bool init(Sd2Card* dev) {return init(dev, 1) ? true : init(dev, 0);} | |||||
| /** Initialize a FAT volume. | |||||
| * | * | ||||
| * \param[in] n cluster number. | |||||
| * \param[out] v value of entry | |||||
| * \return true for success or false for failure | |||||
| * \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 dbgFat(uint32_t n, uint32_t* v) {return fatGet(n, v);} | |||||
| //------------------------------------------------------------------------------ | |||||
| private: | |||||
| // Allow SdBaseFile access to SdVolume private data. | |||||
| friend class SdBaseFile; | |||||
| //------------------------------------------------------------------------------ | |||||
| uint32_t m_allocSearchStart; // Start cluster for alloc search. | |||||
| uint8_t m_blocksPerCluster; // Cluster size in blocks. | |||||
| uint8_t m_clusterBlockMask; // Mask to extract block of cluster. | |||||
| uint32_t m_clusterCount; // Clusters in one FAT. | |||||
| uint8_t m_clusterSizeShift; // Cluster count to block count shift. | |||||
| uint32_t m_dataStartBlock; // First data block number. | |||||
| uint32_t m_fatStartBlock; // Start block for first FAT. | |||||
| uint8_t m_fatType; // Volume type (12, 16, OR 32). | |||||
| uint16_t m_rootDirEntryCount; // Number of entries in FAT16 root dir. | |||||
| uint32_t m_rootDirStart; // Start block for FAT16, cluster for FAT32. | |||||
| //------------------------------------------------------------------------------ | |||||
| // block caches | |||||
| // use of static functions save a bit of flash - maybe not worth complexity | |||||
| // | |||||
| static const uint8_t CACHE_STATUS_DIRTY = 1; | |||||
| static const uint8_t CACHE_STATUS_FAT_BLOCK = 2; | |||||
| static const uint8_t CACHE_STATUS_MASK | |||||
| = CACHE_STATUS_DIRTY | CACHE_STATUS_FAT_BLOCK; | |||||
| static const uint8_t CACHE_OPTION_NO_READ = 4; | |||||
| // value for option argument in cacheFetch to indicate read from cache | |||||
| static uint8_t const CACHE_FOR_READ = 0; | |||||
| // value for option argument in cacheFetch to indicate write to cache | |||||
| static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; | |||||
| // reserve cache block with no read | |||||
| static uint8_t const CACHE_RESERVE_FOR_WRITE | |||||
| = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; | |||||
| #if USE_MULTIPLE_CARDS | |||||
| uint8_t m_fatCount; // number of FATs on volume | |||||
| uint32_t m_blocksPerFat; // FAT size in blocks | |||||
| cache_t m_cacheBuffer; // 512 byte cache for device blocks | |||||
| uint32_t m_cacheBlockNumber; // Logical number of block in the cache | |||||
| Sd2Card* m_sdCard; // Sd2Card object for cache | |||||
| uint8_t m_cacheStatus; // status of cache block | |||||
| #if USE_SEPARATE_FAT_CACHE | |||||
| cache_t m_cacheFatBuffer; // 512 byte cache for FAT | |||||
| uint32_t m_cacheFatBlockNumber; // current Fat block number | |||||
| uint8_t m_cacheFatStatus; // status of cache Fatblock | |||||
| #endif // USE_SEPARATE_FAT_CACHE | |||||
| #else // USE_MULTIPLE_CARDS | |||||
| static uint8_t m_fatCount; // number of FATs on volume | |||||
| static uint32_t m_blocksPerFat; // FAT size in blocks | |||||
| static cache_t m_cacheBuffer; // 512 byte cache for device blocks | |||||
| static uint32_t m_cacheBlockNumber; // Logical number of block in the cache | |||||
| static uint8_t m_cacheStatus; // status of cache block | |||||
| #if USE_SEPARATE_FAT_CACHE | |||||
| static cache_t m_cacheFatBuffer; // 512 byte cache for FAT | |||||
| static uint32_t m_cacheFatBlockNumber; // current Fat block number | |||||
| static uint8_t m_cacheFatStatus; // status of cache Fatblock | |||||
| #endif // USE_SEPARATE_FAT_CACHE | |||||
| static Sd2Card* m_sdCard; // Sd2Card object for cache | |||||
| #endif // USE_MULTIPLE_CARDS | |||||
| bool init(Sd2Card* dev, uint8_t part) { | |||||
| m_sdCard = dev; | |||||
| return FatVolume::init(part); | |||||
| } | |||||
| cache_t *cacheAddress() {return &m_cacheBuffer;} | |||||
| uint32_t cacheBlockNumber() {return m_cacheBlockNumber;} | |||||
| #if USE_MULTIPLE_CARDS | |||||
| cache_t* cacheFetch(uint32_t blockNumber, uint8_t options); | |||||
| cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options); | |||||
| cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options); | |||||
| void cacheInvalidate(); | |||||
| bool cacheSync(); | |||||
| bool cacheWriteData(); | |||||
| bool cacheWriteFat(); | |||||
| #else // USE_MULTIPLE_CARDS | |||||
| static cache_t* cacheFetch(uint32_t blockNumber, uint8_t options); | |||||
| static cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options); | |||||
| static cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options); | |||||
| static void cacheInvalidate(); | |||||
| static bool cacheSync(); | |||||
| static bool cacheWriteData(); | |||||
| static bool cacheWriteFat(); | |||||
| #endif // USE_MULTIPLE_CARDS | |||||
| //------------------------------------------------------------------------------ | |||||
| bool allocContiguous(uint32_t count, uint32_t* curCluster); | |||||
| uint8_t blockOfCluster(uint32_t position) const { | |||||
| return (position >> 9) & m_clusterBlockMask;} | |||||
| uint32_t clusterStartBlock(uint32_t cluster) const; | |||||
| bool fatGet(uint32_t cluster, uint32_t* value); | |||||
| bool fatPut(uint32_t cluster, uint32_t value); | |||||
| bool fatPutEOC(uint32_t cluster) { | |||||
| return fatPut(cluster, 0x0FFFFFFF); | |||||
| private: | |||||
| // friend class FatFile; | |||||
| bool readBlock(uint32_t block, uint8_t* dst) { | |||||
| return m_sdCard->readBlock(block, dst); | |||||
| } | } | ||||
| bool freeChain(uint32_t cluster); | |||||
| bool isEOC(uint32_t cluster) const { | |||||
| if (FAT12_SUPPORT && m_fatType == 12) return cluster >= FAT12EOC_MIN; | |||||
| if (m_fatType == 16) return cluster >= FAT16EOC_MIN; | |||||
| return cluster >= FAT32EOC_MIN; | |||||
| bool writeBlock(uint32_t block, const uint8_t* src) { | |||||
| return m_sdCard->writeBlock(block, src); | |||||
| } | } | ||||
| bool readBlock(uint32_t block, uint8_t* dst) { | |||||
| return m_sdCard->readBlock(block, dst);} | |||||
| bool writeBlock(uint32_t block, const uint8_t* dst) { | |||||
| return m_sdCard->writeBlock(block, dst); | |||||
| bool readBlocks(uint32_t block, uint8_t* dst, size_t n) { | |||||
| return m_sdCard->readBlocks(block, dst, n); | |||||
| } | |||||
| bool writeBlocks(uint32_t block, const uint8_t* src, size_t n) { | |||||
| return m_sdCard->writeBlocks(block, src, n); | |||||
| } | } | ||||
| Sd2Card* m_sdCard; // Sd2Card object for cache | |||||
| }; | }; | ||||
| #endif // SdVolume | |||||
| #endif // SdVolume_h |
| // A simple data logger for the Arduino analog pins with optional DS1307 | // A simple data logger for the Arduino analog pins with optional DS1307 | ||||
| // uses RTClib from https://github.com/adafruit/RTClib | // uses RTClib from https://github.com/adafruit/RTClib | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| #include <SdFatUtil.h> // define FreeRam() | #include <SdFatUtil.h> // define FreeRam() | ||||
| #endif // SENSOR_COUNT | #endif // SENSOR_COUNT | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // store error strings in flash to save RAM | // store error strings in flash to save RAM | ||||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| #if USE_DS1307 | #if USE_DS1307 | ||||
| // use RTClib from Adafruit | // use RTClib from Adafruit |
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // create a serial output stream | // create a serial output stream |
| // | // | ||||
| // This is useful for debug and saves RAM | // This is useful for debug and saves RAM | ||||
| // Will not work on Due, Leonardo, or Teensy | // Will not work on Due, Leonardo, or Teensy | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| #include <SdFatUtil.h> | #include <SdFatUtil.h> | ||||
| #ifndef UDR0 | |||||
| #error no AVR serial port0 | |||||
| #endif | |||||
| #ifdef UDR0 // Must be AVR with serial port zero. | |||||
| #include <MinimumSerial.h> | |||||
| MinimumSerial MiniSerial; | |||||
| void setup() { | void setup() { | ||||
| MiniSerial.begin(9600); | MiniSerial.begin(9600); | ||||
| MiniSerial.println(FreeRam()); | MiniSerial.println(FreeRam()); | ||||
| } | |||||
| void loop() { | |||||
| int c; | |||||
| MiniSerial.println(F("Type any Character")); | MiniSerial.println(F("Type any Character")); | ||||
| while(MiniSerial.read() < 0) {} | |||||
| MiniSerial.println(F("Done")); | |||||
| while ((c = MiniSerial.read()) < 0) {} | |||||
| MiniSerial.print(F("Read: ")); | |||||
| MiniSerial.println((char)c); | |||||
| while (MiniSerial.read() >= 0) {} | |||||
| } | } | ||||
| void loop() {} | |||||
| #else // UDR0 | |||||
| #error no AVR serial port 0 | |||||
| #endif // UDR0 |
| /* | /* | ||||
| * This sketch is a simple Print benchmark. | * This sketch is a simple Print benchmark. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SD.h> | #include <SD.h> | ||||
| #include <SPI.h> | |||||
| // SD chip select pin | // SD chip select pin | ||||
| const uint8_t chipSelect = SS; | const uint8_t chipSelect = SS; | ||||
| break; | break; | ||||
| } | } | ||||
| // if (file.writeError) { | |||||
| // error("write failed"); | |||||
| // } | |||||
| if (file.getWriteError()) { | |||||
| error("write failed"); | |||||
| } | |||||
| m = micros() - m; | m = micros() - m; | ||||
| if (maxLatency < m) maxLatency = m; | if (maxLatency < m) maxLatency = m; | ||||
| if (minLatency > m) minLatency = m; | if (minLatency > m) minLatency = m; |
| * See SD_Size.pde for Arduino SD sketch. | * See SD_Size.pde for Arduino SD sketch. | ||||
| * | * | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| SdFat sd; | SdFat sd; |
| * The sketch will append 100 line each time it opens the file. | * The sketch will append 100 line each time it opens the file. | ||||
| * The sketch will open and close the file 100 times. | * The sketch will open and close the file 100 times. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD chip select pin | // SD chip select pin | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| // store error strings in flash to save RAM | // store error strings in flash to save RAM | ||||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void setup() { | void setup() { | ||||
| // filename for this example | // filename for this example |
| /* | /* | ||||
| * 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 <SdFat.h> | #include <SdFat.h> | ||||
| // SD chip select pin | // SD chip select pin |
| #include <SPI.h> | #include <SPI.h> | ||||
| #include <SD.h> | #include <SD.h> | ||||
| // SD chip select pin | // SD chip select pin | ||||
| const uint8_t chipSelect = SS; | const uint8_t chipSelect = SS; | ||||
| uint8_t buf[BUF_SIZE]; | uint8_t buf[BUF_SIZE]; | ||||
| // test file | // test file | ||||
| File file; | File file; | ||||
| /* | /* | ||||
| * Use of ibufsteam to parse a line and obufstream to format a line | * Use of ibufsteam to parse a line and obufstream to format a line | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // create a serial output stream | // create a serial output stream |
| /* | /* | ||||
| * Append a line to a file - demo of pathnames and streams | * Append a line to a file - demo of pathnames and streams | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD chip select pin | // SD chip select pin |
| // Demo of rewriting a line read by fgets | // Demo of rewriting a line read by fgets | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD card chip select pin | // SD card chip select pin | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // store error strings in flash memory | // store error strings in flash memory | ||||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void demoFgets() { | void demoFgets() { | ||||
| char line[25]; | char line[25]; | ||||
| if (!wrfile.isOpen()) error("MakeTestFile"); | if (!wrfile.isOpen()) error("MakeTestFile"); | ||||
| // write test file | // write test file | ||||
| wrfile.write_P(PSTR( | |||||
| wrfile.print(F( | |||||
| "Line A\r\n" | "Line A\r\n" | ||||
| "Line B\r\n" | "Line B\r\n" | ||||
| "Line C\r\n" | "Line C\r\n" |
| * Read the logfile created by the eventlog.pde example. | * Read the logfile created by the eventlog.pde example. | ||||
| * Demo of pathnames and working directories | * Demo of pathnames and working directories | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD chip select pin | // SD chip select pin |
| SD_Size - Determine flash used by SD.h example. | SD_Size - Determine flash used by SD.h example. | ||||
| SdFatSize - Determine flash used by SdFat. | SdFatSize - Determine flash used by SdFat. | ||||
| TestMkdirRmdir - Test mkdir, rmdir, and directory management. |
| * | * | ||||
| * Data is written to the file using a SD multiple block write command. | * Data is written to the file using a SD multiple block write command. | ||||
| */ | */ | ||||
| #ifdef __AVR__ | |||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| #include <SdFatUtil.h> | #include <SdFatUtil.h> | ||||
| #include <StdioStream.h> | |||||
| #include "AnalogBinLogger.h" | #include "AnalogBinLogger.h" | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // Analog pin number list for a sample. Pins may be in any order and pin | // Analog pin number list for a sample. Pins may be in any order and pin | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| // Error messages stored in flash. | // Error messages stored in flash. | ||||
| #define error(msg) error_P(PSTR(msg)) | |||||
| #define error(msg) errorFlash(F(msg)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void error_P(const char* msg) { | |||||
| sd.errorPrint_P(msg); | |||||
| void errorFlash(const __FlashStringHelper* msg) { | |||||
| sd.errorPrint(msg); | |||||
| fatalBlink(); | fatalBlink(); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| Serial.println(F("Invalid entry")); | Serial.println(F("Invalid entry")); | ||||
| } | } | ||||
| } | } | ||||
| #else // __AVR__ | |||||
| #error This program is only for AVR. | |||||
| #endif // __AVR__ |
| // Example use of openNextLFN and open by index. | |||||
| // You can use test files located in | |||||
| // SdFat/examples/LongFileName/testFiles. | |||||
| #include<SPI.h> | |||||
| #include <SdFat.h> | |||||
| #include <SdFatUtil.h> | |||||
| // SD card chip select pin. | |||||
| const uint8_t SD_CS_PIN = SS; | |||||
| SdFat sd; | |||||
| SdFile file; | |||||
| // Number of files found. | |||||
| uint16_t n = 0; | |||||
| // Max of ten files since files are selected with a single digit. | |||||
| const uint16_t nMax = 10; | |||||
| // Position of file's directory entry. | |||||
| uint16_t dirIndex[nMax]; | |||||
| //------------------------------------------------------------------------------ | |||||
| void setup() { | |||||
| const size_t NAME_DIM = 50; | |||||
| char name[NAME_DIM]; | |||||
| dir_t dir; | |||||
| Serial.begin(9600); | |||||
| while (!Serial) {} | |||||
| // Print the location of some test files. | |||||
| Serial.println(F("\r\n" | |||||
| "You can use test files located in\r\n" | |||||
| "SdFat/examples/LongFileName/testFiles")); | |||||
| 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) { | |||||
| // 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.close(); | |||||
| } | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| 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)) { | |||||
| 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(); | |||||
| file.close(); | |||||
| } |
| This is "A long name can be 255 characters.txt" | |||||
| This file has a typical Long File Name. | |||||
| The maximum length of a Long File Name is 255 characters. |
| LFN,NAME.TXT is not 8.3 since it has a comma. |
| MIXCASE.txt does not have a Long File Name. | |||||
| Starting with NT, file names of this form, | |||||
| have the basename and extension character case | |||||
| encoded in two bits of the 8.3 directory entry. |
| Not_8_3.txt has a Long File Name | |||||
| since the basename is mixed case. |
| OK%83.TXT is a valid 8.3 name. |
| STD_8_3.TXT - a vanilla 8.3 name. |
| With Blank.txt | |||||
| Just another example of a Long File Name. |
| "With Two.dots.txt" | |||||
| Lots of reasons this is a Long File Name. |
| lower.txt does not have a Long File Name. | |||||
| Starting with NT, file names of this form, | |||||
| have the basename and extension character case | |||||
| encoded in two bits of the 8.3 directory entry. |
| mixed.TXT does not have a Long File Name. | |||||
| Starting with NT, file names of this form, | |||||
| have the basename and extension character case | |||||
| encoded in two bits of the 8.3 directory entry. |
| * | * | ||||
| * Data is written to the file using a SD multiple block write command. | * Data is written to the file using a SD multiple block write command. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| #include <SdFatUtil.h> | #include <SdFatUtil.h> | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| inline uint8_t queueNext(uint8_t ht) {return ht < (QUEUE_DIM - 1) ? ht + 1 : 0;} | inline uint8_t queueNext(uint8_t ht) {return ht < (QUEUE_DIM - 1) ? ht + 1 : 0;} | ||||
| //============================================================================== | //============================================================================== | ||||
| // Error messages stored in flash. | // Error messages stored in flash. | ||||
| #define error(msg) error_P(PSTR(msg)) | |||||
| #define error(msg) errorFlash(F(msg)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void error_P(const char* msg) { | |||||
| sd.errorPrint_P(msg); | |||||
| void errorFlash(const __FlashStringHelper* msg) { | |||||
| sd.errorPrint(msg); | |||||
| fatalBlink(); | fatalBlink(); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| pinMode(ERROR_LED_PIN, OUTPUT); | pinMode(ERROR_LED_PIN, OUTPUT); | ||||
| } | } | ||||
| Serial.begin(9600); | Serial.begin(9600); | ||||
| while (!Serial) {} | |||||
| Serial.print(F("FreeRam: ")); | Serial.print(F("FreeRam: ")); | ||||
| Serial.println(FreeRam()); | Serial.println(FreeRam()); |
| /* | /* | ||||
| * Print size, modify date/time, and name for all files in root. | * Print size, modify date/time, and name for all files in root. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD chip select pin | // SD chip select pin |
| /* | /* | ||||
| * This sketch is a simple Print benchmark. | * This sketch is a simple Print benchmark. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| #include <SdFatUtil.h> | #include <SdFatUtil.h> | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // store error strings in flash to save RAM | // store error strings in flash to save RAM | ||||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void setup() { | void setup() { | ||||
| Serial.begin(9600); | Serial.begin(9600); | ||||
| file.printField((float)0.01*i, '\n'); | file.printField((float)0.01*i, '\n'); | ||||
| break; | break; | ||||
| } | } | ||||
| if (file.writeError) { | |||||
| if (file.getWriteError()) { | |||||
| error("write failed"); | error("write failed"); | ||||
| } | } | ||||
| m = micros() - m; | m = micros() - m; |
| // Quick hardware test. | // Quick hardware test. | ||||
| // | // | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // | // | ||||
| // Set DISABLE_CHIP_SELECT to disable a second SPI device. | // Set DISABLE_CHIP_SELECT to disable a second SPI device. | ||||
| // Use SPI_QUARTER_SPEED for even slower SPI bus speed | // Use SPI_QUARTER_SPEED for even slower SPI bus speed | ||||
| const uint8_t spiSpeed = SPI_HALF_SPEED; | const uint8_t spiSpeed = SPI_HALF_SPEED; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // Normally the SdFat class is used in applications in place | |||||
| // of Sd2Card, SdVolume, and SdFile for root. | |||||
| // These internal classes are used here to diagnose problems. | |||||
| Sd2Card card; | |||||
| SdVolume volume; | |||||
| SdFile root; | |||||
| // File system object. | |||||
| SdFat sd; | |||||
| // Serial streams | // Serial streams | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| void setup() { | void setup() { | ||||
| Serial.begin(9600); | Serial.begin(9600); | ||||
| while (!Serial) {} // Wait for Leonardo. | while (!Serial) {} // Wait for Leonardo. | ||||
| delay(1000); // Delay for Due. | |||||
| cout << pstr("\nSPI pins:\n"); | cout << pstr("\nSPI pins:\n"); | ||||
| cout << pstr("MISO: ") << int(MISO) << endl; | |||||
| cout << pstr("MOSI: ") << int(MOSI) << endl; | cout << pstr("MOSI: ") << int(MOSI) << endl; | ||||
| cout << pstr("MISO: ") << int(MISO) << endl; | |||||
| cout << pstr("SCK: ") << int(SCK) << endl; | cout << pstr("SCK: ") << int(SCK) << endl; | ||||
| cout << pstr("SS: ") << int(SS) << endl; | |||||
| if (DISABLE_CHIP_SELECT < 0) { | if (DISABLE_CHIP_SELECT < 0) { | ||||
| cout << pstr( | cout << pstr( | ||||
| firstTry = false; | firstTry = false; | ||||
| cout << pstr("\nEnter the chip select pin number: "); | cout << pstr("\nEnter the chip select pin number: "); | ||||
| while (!Serial.available()) {} | |||||
| delay(400); // catch Due restart problem | |||||
| cin.readline(); | cin.readline(); | ||||
| if (cin >> chipSelect) { | if (cin >> chipSelect) { | ||||
| cout << chipSelect << endl; | cout << chipSelect << endl; | ||||
| pinMode(DISABLE_CHIP_SELECT, OUTPUT); | pinMode(DISABLE_CHIP_SELECT, OUTPUT); | ||||
| digitalWrite(DISABLE_CHIP_SELECT, HIGH); | digitalWrite(DISABLE_CHIP_SELECT, HIGH); | ||||
| } | } | ||||
| if (!card.init(spiSpeed, chipSelect)) { | |||||
| 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("errorCode: ") << hex << showbase << int(card.errorCode()); | |||||
| cout << pstr(", errorData: ") << int(card.errorData()); | |||||
| cout << dec << noshowbase << endl; | |||||
| 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 << int(sd.card()->errorCode()); | |||||
| cout << pstr(", errorData: ") << int(sd.card()->errorData()); | |||||
| cout << dec << noshowbase << endl; | |||||
| return; | |||||
| } | |||||
| cout << pstr("\nCard successfully initialized.\n"); | |||||
| if (sd.vol()->fatType() == 0) { | |||||
| cout << pstr("Can't find a valid FAT16/FAT32 partition.\n"); | |||||
| reformatMsg(); | |||||
| return; | |||||
| } | |||||
| if (!sd.vwd()->isOpen()) { | |||||
| cout << pstr("Can't open root directory.\n"); | |||||
| reformatMsg(); | |||||
| return; | |||||
| } | |||||
| cout << pstr("Can't determine error type\n"); | |||||
| return; | return; | ||||
| } | } | ||||
| cout << pstr("\nCard successfully initialized.\n"); | cout << pstr("\nCard successfully initialized.\n"); | ||||
| cout << endl; | cout << endl; | ||||
| uint32_t size = card.cardSize(); | |||||
| uint32_t size = sd.card()->cardSize(); | |||||
| if (size == 0) { | if (size == 0) { | ||||
| cout << pstr("Can't determine the card size.\n"); | cout << pstr("Can't determine the card size.\n"); | ||||
| cardOrSpeed(); | cardOrSpeed(); | ||||
| cout << pstr("Card size: ") << sizeMB; | cout << pstr("Card size: ") << sizeMB; | ||||
| cout << pstr(" MB (MB = 1,000,000 bytes)\n"); | cout << pstr(" MB (MB = 1,000,000 bytes)\n"); | ||||
| cout << endl; | cout << endl; | ||||
| if (!volume.init(&card)) { | |||||
| if (card.errorCode()) { | |||||
| cout << pstr("Can't read the card.\n"); | |||||
| cardOrSpeed(); | |||||
| } else { | |||||
| cout << pstr("Can't find a valid FAT16/FAT32 partition.\n"); | |||||
| reformatMsg(); | |||||
| } | |||||
| return; | |||||
| } | |||||
| cout << pstr("Volume is FAT") << int(volume.fatType()); | |||||
| cout << pstr(", Cluster size (bytes): ") << 512L * volume.blocksPerCluster(); | |||||
| cout << pstr("Volume is FAT") << int(sd.vol()->fatType()); | |||||
| cout << pstr(", Cluster size (bytes): ") << 512L * sd.vol()->blocksPerCluster(); | |||||
| cout << endl << endl; | cout << endl << endl; | ||||
| root.close(); | |||||
| if (!root.openRoot(&volume)) { | |||||
| cout << pstr("Can't open root directory.\n"); | |||||
| reformatMsg(); | |||||
| return; | |||||
| } | |||||
| cout << pstr("Files found (name date time size):\n"); | cout << pstr("Files found (name date time size):\n"); | ||||
| root.ls(LS_R | LS_DATE | LS_SIZE); | |||||
| sd.ls(LS_R | LS_DATE | LS_SIZE); | |||||
| if ((sizeMB > 1100 && volume.blocksPerCluster() < 64) | |||||
| || (sizeMB < 2200 && volume.fatType() == 32)) { | |||||
| 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("\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("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"); | cout << pstr("Only cards larger than 2 GB should be formatted FAT32.\n"); |
| /* | /* | ||||
| * This sketch illustrates raw write functions in SdFat that | * This sketch illustrates raw write functions in SdFat that | ||||
| * can be used for high speed data logging. These functions | |||||
| * are used in the WaveRP library to record audio with the | |||||
| * Adafruit Wave Shield using the built-in Arduino ADC. | |||||
| * | |||||
| * The WaveRP library captures data from the ADC in an ISR | |||||
| * that is driven driven by timer one. Data is collected in | |||||
| * two 512 byte buffers and written to the SD card. | |||||
| * can be used for high speed data logging. | |||||
| * | * | ||||
| * This sketch simulates logging from a source that produces | * This sketch simulates logging from a source that produces | ||||
| * data at a constant rate of one block every MICROS_PER_BLOCK. | * data at a constant rate of one block every MICROS_PER_BLOCK. | ||||
| * no overruns occur and the maximum block write time is | * no overruns occur and the maximum block write time is | ||||
| * under 2000 micros. | * under 2000 micros. | ||||
| * | * | ||||
| * Note: WaveRP creates a very large file then truncates it | |||||
| * to the length that is used for a recording. It only takes | |||||
| * Note: Apps should create a very large file then truncates it | |||||
| * to the length that is used for a logging. It only takes | |||||
| * a few seconds to erase a 500 MB file since the card only | * a few seconds to erase a 500 MB file since the card only | ||||
| * marks the blocks as erased; no data transfer is required. | * marks the blocks as erased; no data transfer is required. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| #include <SdFatUtil.h> | #include <SdFatUtil.h> | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // store error strings in flash to save RAM | // store error strings in flash to save RAM | ||||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // log of first overruns | // log of first overruns | ||||
| #define OVER_DIM 20 | #define OVER_DIM 20 |
| This example code is in the public domain. | This example code is in the public domain. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| SdFat sd; | SdFat sd; | ||||
| SdFile myFile; | SdFile myFile; |
| */ | */ | ||||
| // Print extra info for debug if DEBUG_PRINT is nonzero | // Print extra info for debug if DEBUG_PRINT is nonzero | ||||
| #define DEBUG_PRINT 0 | #define DEBUG_PRINT 0 | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| #if DEBUG_PRINT | #if DEBUG_PRINT | ||||
| #include <SdFatUtil.h> | #include <SdFatUtil.h> | ||||
| return; | return; | ||||
| } | } | ||||
| if (!card.init(spiSpeed, chipSelect)) { | |||||
| if (!card.begin(chipSelect, spiSpeed)) { | |||||
| cout << pstr( | cout << pstr( | ||||
| "\nSD initialization failure!\n" | "\nSD initialization failure!\n" | ||||
| "Is the SD card inserted correctly?\n" | "Is the SD card inserted correctly?\n" | ||||
| "Is chip select correct at the top of this sketch?\n"); | "Is chip select correct at the top of this sketch?\n"); | ||||
| sdError("card.init failed"); | |||||
| sdError("card.begin failed"); | |||||
| } | } | ||||
| cardSizeBlocks = card.cardSize(); | cardSizeBlocks = card.cardSize(); | ||||
| if (cardSizeBlocks == 0) sdError("cardSize"); | if (cardSizeBlocks == 0) sdError("cardSize"); |
| /* | /* | ||||
| * This sketch attempts to initialize an SD card and analyze its structure. | * This sketch attempts to initialize an SD card and analyze its structure. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| /* | /* | ||||
| * SD chip select pin. Common values are: | * SD chip select pin. Common values are: | ||||
| * Adafruit SD shields and modules, pin 10. | * Adafruit SD shields and modules, pin 10. | ||||
| * Default SD chip select is the SPI SS pin. | * Default SD chip select is the SPI SS pin. | ||||
| */ | */ | ||||
| const uint8_t SdChipSelect = SS; | |||||
| Sd2Card card; | |||||
| SdVolume vol; | |||||
| const uint8_t SD_CHIP_SELECT = SS; | |||||
| /* | |||||
| * Set DISABLE_CHIP_SELECT to disable a second SPI device. | |||||
| * For example, with the Ethernet shield, set DISABLE_CHIP_SELECT | |||||
| * to 10 to disable the Ethernet controller. | |||||
| */ | |||||
| const int8_t DISABLE_CHIP_SELECT = -1; | |||||
| SdFat sd; | |||||
| // serial output steam | // serial output steam | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| #define sdErrorMsg(msg) sdErrorMsg_P(PSTR(msg)); | #define sdErrorMsg(msg) sdErrorMsg_P(PSTR(msg)); | ||||
| void sdErrorMsg_P(const char* str) { | void sdErrorMsg_P(const char* str) { | ||||
| cout << pgm(str) << endl; | cout << pgm(str) << endl; | ||||
| if (card.errorCode()) { | |||||
| if (sd.card()->errorCode()) { | |||||
| cout << pstr("SD errorCode: "); | cout << pstr("SD errorCode: "); | ||||
| cout << hex << int(card.errorCode()) << endl; | |||||
| cout << hex << int(sd.card()->errorCode()) << endl; | |||||
| cout << pstr("SD errorData: "); | cout << pstr("SD errorData: "); | ||||
| cout << int(card.errorData()) << dec << endl; | |||||
| cout << int(sd.card()->errorData()) << dec << endl; | |||||
| } | } | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| uint8_t cidDmp() { | uint8_t cidDmp() { | ||||
| cid_t cid; | cid_t cid; | ||||
| if (!card.readCID(&cid)) { | |||||
| if (!sd.card()->readCID(&cid)) { | |||||
| sdErrorMsg("readCID failed"); | sdErrorMsg("readCID failed"); | ||||
| return false; | return false; | ||||
| } | } | ||||
| uint8_t csdDmp() { | uint8_t csdDmp() { | ||||
| csd_t csd; | csd_t csd; | ||||
| uint8_t eraseSingleBlock; | uint8_t eraseSingleBlock; | ||||
| if (!card.readCSD(&csd)) { | |||||
| if (!sd.card()->readCSD(&csd)) { | |||||
| sdErrorMsg("readCSD failed"); | sdErrorMsg("readCSD failed"); | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // print partition table | // print partition table | ||||
| uint8_t partDmp() { | uint8_t partDmp() { | ||||
| cache_t *p = vol.cacheClear(); | |||||
| cache_t *p = sd.vol()->cacheClear(); | |||||
| if (!p) { | if (!p) { | ||||
| sdErrorMsg("cacheClear failed"); | sdErrorMsg("cacheClear failed"); | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (!card.readBlock(0, p->data)) { | |||||
| if (!sd.card()->readBlock(0, p->data)) { | |||||
| sdErrorMsg("read MBR failed"); | sdErrorMsg("read MBR failed"); | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void volDmp() { | void volDmp() { | ||||
| cout << pstr("\nVolume is FAT") << int(vol.fatType()) << endl; | |||||
| cout << pstr("blocksPerCluster: ") << int(vol.blocksPerCluster()) << endl; | |||||
| cout << pstr("clusterCount: ") << vol.clusterCount() << endl; | |||||
| uint32_t volFree = vol.freeClusterCount(); | |||||
| cout << pstr("freeClusters: ") << volFree << endl; | |||||
| float fs = 0.000512*volFree*vol.blocksPerCluster(); | |||||
| 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: "); | |||||
| 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("freeSpace: ") << fs << pstr(" MB (MB = 1,000,000 bytes)\n"); | ||||
| cout << pstr("fatStartBlock: ") << vol.fatStartBlock() << endl; | |||||
| cout << pstr("fatCount: ") << int(vol.fatCount()) << endl; | |||||
| cout << pstr("blocksPerFat: ") << vol.blocksPerFat() << endl; | |||||
| cout << pstr("rootDirStart: ") << vol.rootDirStart() << endl; | |||||
| cout << pstr("dataStartBlock: ") << vol.dataStartBlock() << endl; | |||||
| if (vol.dataStartBlock() % eraseSize) { | |||||
| 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; | |||||
| if (sd.vol()->dataStartBlock() % eraseSize) { | |||||
| cout << pstr("Data area is not aligned on flash erase boundaries!\n"); | cout << pstr("Data area is not aligned on flash erase boundaries!\n"); | ||||
| cout << pstr("Download and use formatter from www.sdcard.org/consumer!\n"); | |||||
| cout << pstr("Download and use formatter from www.sdsd.card()->org/consumer!\n"); | |||||
| } | } | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // pstr stores strings in flash to save RAM | // pstr stores strings in flash to save RAM | ||||
| cout << pstr("SdFat version: ") << SD_FAT_VERSION << endl; | cout << pstr("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"); | |||||
| } else { | |||||
| cout << pstr("\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"); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void loop() { | void loop() { | ||||
| uint32_t t = millis(); | uint32_t t = millis(); | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | ||||
| // breadboards. use SPI_FULL_SPEED for better performance. | // breadboards. use SPI_FULL_SPEED for better performance. | ||||
| if (!card.init(SPI_HALF_SPEED, SdChipSelect)) { | |||||
| sdErrorMsg("\ncard.init failed"); | |||||
| if (!sd.cardBegin(SD_CHIP_SELECT, SPI_HALF_SPEED)) { | |||||
| sdErrorMsg("\ncardBegin failed"); | |||||
| return; | return; | ||||
| } | } | ||||
| t = millis() - t; | t = millis() - t; | ||||
| cardSize = card.cardSize(); | |||||
| cardSize = sd.card()->cardSize(); | |||||
| if (cardSize == 0) { | if (cardSize == 0) { | ||||
| sdErrorMsg("cardSize failed"); | sdErrorMsg("cardSize failed"); | ||||
| return; | return; | ||||
| } | } | ||||
| cout << pstr("\ninit time: ") << t << " ms" << endl; | cout << pstr("\ninit time: ") << t << " ms" << endl; | ||||
| cout << pstr("\nCard type: "); | cout << pstr("\nCard type: "); | ||||
| switch (card.type()) { | |||||
| switch (sd.card()->type()) { | |||||
| case SD_CARD_TYPE_SD1: | case SD_CARD_TYPE_SD1: | ||||
| cout << pstr("SD1\n"); | cout << pstr("SD1\n"); | ||||
| break; | break; | ||||
| if (!cidDmp()) return; | if (!cidDmp()) return; | ||||
| if (!csdDmp()) return; | if (!csdDmp()) return; | ||||
| uint32_t ocr; | uint32_t ocr; | ||||
| if (!card.readOCR(&ocr)) { | |||||
| if (!sd.card()->readOCR(&ocr)) { | |||||
| sdErrorMsg("\nreadOCR failed"); | sdErrorMsg("\nreadOCR failed"); | ||||
| return; | return; | ||||
| } | } | ||||
| cout << pstr("OCR: ") << hex << ocr << dec << endl; | cout << pstr("OCR: ") << hex << ocr << dec << endl; | ||||
| if (!partDmp()) return; | if (!partDmp()) return; | ||||
| if (!vol.init(&card)) { | |||||
| sdErrorMsg("\nvol.init failed"); | |||||
| if (!sd.fsBegin()) { | |||||
| sdErrorMsg("\nFile System initialization failed.\n"); | |||||
| return; | return; | ||||
| } | } | ||||
| volDmp(); | volDmp(); |
| // An example of the SdFatSoftSpi template class. | |||||
| // This example is for an Adafruit Data Logging Shield on a Mega. | |||||
| // Software SPI is required on Mega since this shield connects to pins 10-13. | |||||
| // This example will also run on an Uno and other boards using software SPI. | |||||
| // | |||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | |||||
| #if USE_MULTIPLE_SPI_TYPES // Must be nonzero in SdFat/SdFatConfig.h | |||||
| // | |||||
| // Pin numbers in templates must be constants. | |||||
| const uint8_t SOFT_MISO_PIN = 12; | |||||
| const uint8_t SOFT_MOSI_PIN = 11; | |||||
| const uint8_t SOFT_SCK_PIN = 13; | |||||
| // | |||||
| // Chip select may be constant or RAM variable. | |||||
| const uint8_t SD_CHIP_SELECT_PIN = 10; | |||||
| // SdFat software SPI template | |||||
| SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd; | |||||
| // Test file. | |||||
| SdFile file; | |||||
| void setup() { | |||||
| Serial.begin(9600); | |||||
| 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)) { | |||||
| 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 |
| // Benchmark comparing SdFile and StdioStream. | // Benchmark comparing SdFile and StdioStream. | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // Define PRINT_FIELD nonzero to use printField. | // Define PRINT_FIELD nonzero to use printField. | ||||
| uint32_t stdioTime; | uint32_t stdioTime; | ||||
| Serial.begin(9600); | Serial.begin(9600); | ||||
| while (!Serial) {} | |||||
| Serial.println(F("Type any character to start")); | Serial.println(F("Type any character to start")); | ||||
| while (!Serial.available()); | while (!Serial.available()); | ||||
| Serial.println(F("Starting test")); | Serial.println(F("Starting test")); | ||||
| if (!sd.begin(SD_CS_PIN)) sd.errorHalt(); | if (!sd.begin(SD_CS_PIN)) sd.errorHalt(); | ||||
| for (uint8_t i = 0; i < 100; i++) { | for (uint8_t i = 0; i < 100; i++) { | ||||
| f[i] = 123.0 + 0.12345*i; | |||||
| f[i] = 123.0 + 0.1234*i; | |||||
| } | } | ||||
| for (uint8_t dataType = 0; dataType < 5; dataType++) { | for (uint8_t dataType = 0; dataType < 5; dataType++) { |
| // Simple demo of the Stream parsInt() member function. | // Simple demo of the Stream parsInt() member function. | ||||
| #include <SPI.h> | |||||
| // The next two lines replace #include <SD.h>. | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| SdFat SD; | |||||
| // SD card chip select pin - Modify the value of csPin for your SD module. | // SD card chip select pin - Modify the value of csPin for your SD module. | ||||
| const uint8_t csPin = 10; | const uint8_t csPin = 10; | ||||
| SdFat SD; | |||||
| File file; | File file; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void setup() { | void setup() { | ||||
| Serial.begin(9600); | Serial.begin(9600); | ||||
| // Wait for USB Serial. | |||||
| while(!Serial) {} | |||||
| Serial.println(F("Type any character to start")); | |||||
| while (!Serial.available()) {} | |||||
| // Initialize the SD. | // Initialize the SD. | ||||
| if (!SD.begin(csPin)) { | if (!SD.begin(csPin)) { | ||||
| Serial.println(F("begin error")); | Serial.println(F("begin error")); | ||||
| return; | return; | ||||
| } | } | ||||
| // Create and open the file. Use flag to truncate an existing file. | // Create and open the file. Use flag to truncate an existing file. | ||||
| if (!file.open("stream.txt", O_RDWR|O_CREAT|O_TRUNC)) { | |||||
| Serial.println(F("open error")); | |||||
| return; | |||||
| file = SD.open("stream.txt", O_RDWR|O_CREAT|O_TRUNC); | |||||
| if (!file) { | |||||
| Serial.println(F("open error")); | |||||
| return; | |||||
| } | } | ||||
| // Write a test number to the file. | // Write a test number to the file. | ||||
| file.println("12345"); | file.println("12345"); | ||||
| // Rewind the file and read the number with parseInt(). | // Rewind the file and read the number with parseInt(). | ||||
| file.rewind(); | |||||
| file.seek(0); | |||||
| int i = file.parseInt(); | int i = file.parseInt(); | ||||
| Serial.print(F("parseInt: ")); | Serial.print(F("parseInt: ")); | ||||
| Serial.println(i); | Serial.println(i); | ||||
| file.close(); | file.close(); | ||||
| } | } | ||||
| void loop() {} | |||||
| void loop() {} |
| /* | |||||
| * Example use of three SD cards. | |||||
| */ | |||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | |||||
| #include <SdFatUtil.h> | |||||
| #if USE_MULTIPLE_SPI_TYPES // Must be nonzero 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; | |||||
| const uint8_t SD2_CS = 4; | |||||
| // SD3 is a Adafruit data logging shield on pins 10-13 | |||||
| // Using Software SPI | |||||
| SdFatSoftSpi<12, 11, 13> sd3; | |||||
| const uint8_t SD3_CS = 10; | |||||
| const uint8_t BUF_DIM = 100; | |||||
| uint8_t buf[BUF_DIM]; | |||||
| const uint32_t FILE_SIZE = 1000000; | |||||
| const uint16_t NWRITE = FILE_SIZE/BUF_DIM; | |||||
| //------------------------------------------------------------------------------ | |||||
| // print error msg, any SD error codes, and halt. | |||||
| // store messages in flash | |||||
| #define errorExit(msg) errorHalt(F(msg)) | |||||
| #define initError(msg) initErrorHalt(F(msg)) | |||||
| //------------------------------------------------------------------------------ | |||||
| void list() { | |||||
| // list current directory on both 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("---------------------")); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void setup() { | |||||
| Serial.begin(9600); | |||||
| while (!Serial) {} // wait for Leonardo | |||||
| Serial.print(F("FreeRam: ")); | |||||
| Serial.println(FreeRam()); | |||||
| // fill buffer with known data | |||||
| 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:"); | |||||
| // initialize the second card | |||||
| if (!sd2.begin(SD2_CS)) sd2.initError("sd2:"); | |||||
| // initialize the third card | |||||
| 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"); | |||||
| 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"); | |||||
| } | |||||
| Serial.println("Initial SD directories"); | |||||
| list(); | |||||
| // 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)) { | |||||
| sd1.errorExit("file1"); | |||||
| } | |||||
| Serial.println(F("Writing SD1:/DIR1/TEST1.BIN")); | |||||
| // 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"); | |||||
| } | |||||
| } | |||||
| file1.sync(); | |||||
| list(); | |||||
| // 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)) { | |||||
| sd2.errorExit("file2"); | |||||
| } | |||||
| 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"); | |||||
| } | |||||
| t = millis() - t; | |||||
| file2.sync(); | |||||
| Serial.print(F("File size: ")); | |||||
| Serial.println(file2.fileSize()); | |||||
| Serial.print(F("Copy time: ")); | |||||
| Serial.print(t); | |||||
| Serial.println(F(" millis")); | |||||
| list(); | |||||
| // 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)) { | |||||
| sd3.errorExit("file3"); | |||||
| } | |||||
| file2.rewind(); | |||||
| 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"); | |||||
| } | |||||
| file3.sync(); | |||||
| list(); | |||||
| // Verify content of file3 | |||||
| file3.rewind(); | |||||
| 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"); | |||||
| } | |||||
| } | |||||
| Serial.println(F("Done - Verify OK")); | |||||
| file1.close(); | |||||
| file2.close(); | |||||
| file3.close(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| 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 |
| * This sketch tests the dateTimeCallback() function | * This sketch tests the dateTimeCallback() function | ||||
| * and the timestamp() function. | * and the timestamp() function. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| SdFat sd; | SdFat sd; | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // store error strings in flash to save RAM | // store error strings in flash to save RAM | ||||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /* | /* | ||||
| * date/time values for debug | * date/time values for debug | ||||
| * normally supplied by a real-time clock or GPS | * normally supplied by a real-time clock or GPS | ||||
| */ | */ | ||||
| // date 1-Oct-09 | |||||
| uint16_t year = 2009; | |||||
| // date 1-Oct-14 | |||||
| uint16_t year = 2014; | |||||
| uint8_t month = 10; | uint8_t month = 10; | ||||
| uint8_t day = 1; | uint8_t day = 1; | ||||
| error("open STAMP.TXT failed"); | error("open STAMP.TXT failed"); | ||||
| } | } | ||||
| // set creation date time | // set creation date time | ||||
| if (!file.timestamp(T_CREATE, 2009, 11, 10, 1, 2, 3)) { | |||||
| if (!file.timestamp(T_CREATE, 2014, 11, 10, 1, 2, 3)) { | |||||
| error("set create time failed"); | error("set create time failed"); | ||||
| } | } | ||||
| // set write/modification date time | // set write/modification date time | ||||
| if (!file.timestamp(T_WRITE, 2009, 11, 11, 4, 5, 6)) { | |||||
| if (!file.timestamp(T_WRITE, 2014, 11, 11, 4, 5, 6)) { | |||||
| error("set write time failed"); | error("set write time failed"); | ||||
| } | } | ||||
| // set access date | // set access date | ||||
| if (!file.timestamp(T_ACCESS, 2009, 11, 12, 7, 8, 9)) { | |||||
| if (!file.timestamp(T_ACCESS, 2014, 11, 12, 7, 8, 9)) { | |||||
| error("set access time failed"); | error("set access time failed"); | ||||
| } | } | ||||
| cout << pstr("\nTimes after timestamp() calls\n"); | cout << pstr("\nTimes after timestamp() calls\n"); |
| /* | /* | ||||
| * Example use of two SD cards. | * Example use of two SD cards. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| #include <SdFatUtil.h> | #include <SdFatUtil.h> | ||||
| #if !USE_MULTIPLE_CARDS | |||||
| #error You must set USE_MULTIPLE_CARDS nonzero in SdFatConfig.h | |||||
| #endif | |||||
| SdFat sd1; | SdFat sd1; | ||||
| const uint8_t SD1_CS = 10; // chip select for sd1 | const uint8_t SD1_CS = 10; // chip select for sd1 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // print error msg, any SD error codes, and halt. | // print error msg, any SD error codes, and halt. | ||||
| // store messages in flash | // store messages in flash | ||||
| #define errorExit(msg) errorHalt_P(PSTR(msg)) | |||||
| #define initError(msg) initErrorHalt_P(PSTR(msg)) | |||||
| #define errorExit(msg) errorHalt(F(msg)) | |||||
| #define initError(msg) initErrorHalt(F(msg)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void setup() { | void setup() { | ||||
| Serial.begin(9600); | Serial.begin(9600); | ||||
| while (!Serial) {} // wait for Leonardo | while (!Serial) {} // wait for Leonardo | ||||
| PgmPrint("FreeRam: "); | |||||
| Serial.print(F("FreeRam: ")); | |||||
| Serial.println(FreeRam()); | Serial.println(FreeRam()); | ||||
| // fill buffer with known data | // fill buffer with known data | ||||
| for (int i = 0; i < sizeof(buf); i++) buf[i] = i; | for (int i = 0; i < sizeof(buf); i++) buf[i] = i; | ||||
| PgmPrintln("type any character to start"); | |||||
| Serial.println(F("type any character to start")); | |||||
| while (Serial.read() <= 0) {} | while (Serial.read() <= 0) {} | ||||
| delay(400); // catch Due reset problem | delay(400); // catch Due reset problem | ||||
| if (!sd2.mkdir("/DIR2")) sd2.errorExit("sd2.mkdir"); | if (!sd2.mkdir("/DIR2")) sd2.errorExit("sd2.mkdir"); | ||||
| } | } | ||||
| // list root directory on both cards | // list root directory on both cards | ||||
| PgmPrintln("------sd1 root-------"); | |||||
| Serial.println(F("------sd1 root-------")); | |||||
| sd1.ls(); | sd1.ls(); | ||||
| PgmPrintln("------sd2 root-------"); | |||||
| Serial.println(F("------sd2 root-------")); | |||||
| sd2.ls(); | sd2.ls(); | ||||
| // make /DIR1 the default directory for sd1 | // make /DIR1 the default directory for sd1 | ||||
| if (!sd2.chdir("/DIR2")) sd2.errorExit("sd2.chdir"); | if (!sd2.chdir("/DIR2")) sd2.errorExit("sd2.chdir"); | ||||
| // list current directory on both cards | // list current directory on both cards | ||||
| PgmPrintln("------sd1 DIR1-------"); | |||||
| Serial.println(F("------sd1 DIR1-------")); | |||||
| sd1.ls(); | sd1.ls(); | ||||
| PgmPrintln("------sd2 DIR2-------"); | |||||
| Serial.println(F("------sd2 DIR2-------")); | |||||
| sd2.ls(); | sd2.ls(); | ||||
| PgmPrintln("---------------------"); | |||||
| Serial.println(F("---------------------")); | |||||
| // remove RENAME.BIN from /DIR2 directory of sd2 | // remove RENAME.BIN from /DIR2 directory of sd2 | ||||
| if (sd2.exists("RENAME.BIN")) { | if (sd2.exists("RENAME.BIN")) { | ||||
| if (!file1.open("TEST.BIN", O_RDWR | O_CREAT | O_TRUNC)) { | if (!file1.open("TEST.BIN", O_RDWR | O_CREAT | O_TRUNC)) { | ||||
| sd1.errorExit("file1"); | sd1.errorExit("file1"); | ||||
| } | } | ||||
| PgmPrintln("Writing TEST.BIN to sd1"); | |||||
| Serial.println(F("Writing TEST.BIN to sd1")); | |||||
| // write data to /DIR1/TEST.BIN on sd1 | // write data to /DIR1/TEST.BIN on sd1 | ||||
| for (int i = 0; i < NWRITE; i++) { | for (int i = 0; i < NWRITE; i++) { | ||||
| if (!file2.open("COPY.BIN", O_WRITE | O_CREAT | O_TRUNC)) { | if (!file2.open("COPY.BIN", O_WRITE | O_CREAT | O_TRUNC)) { | ||||
| sd2.errorExit("file2"); | sd2.errorExit("file2"); | ||||
| } | } | ||||
| PgmPrintln("Copying TEST.BIN to COPY.BIN"); | |||||
| Serial.println(F("Copying TEST.BIN to COPY.BIN")); | |||||
| // copy file1 to file2 | // copy file1 to file2 | ||||
| file1.rewind(); | file1.rewind(); | ||||
| if (file2.write(buf, n) != n) sd2.errorExit("write2"); | if (file2.write(buf, n) != n) sd2.errorExit("write2"); | ||||
| } | } | ||||
| t = millis() - t; | t = millis() - t; | ||||
| PgmPrint("File size: "); | |||||
| Serial.print(F("File size: ")); | |||||
| Serial.println(file2.fileSize()); | Serial.println(file2.fileSize()); | ||||
| PgmPrint("Copy time: "); | |||||
| Serial.print(F("Copy time: ")); | |||||
| Serial.print(t); | Serial.print(t); | ||||
| PgmPrintln(" millis"); | |||||
| Serial.println(F(" millis")); | |||||
| // close TEST.BIN | // close TEST.BIN | ||||
| file1.close(); | file1.close(); | ||||
| if (!sd2.rename("COPY.BIN", "RENAME.BIN")) { | if (!sd2.rename("COPY.BIN", "RENAME.BIN")) { | ||||
| sd2.errorExit("sd2.rename"); | sd2.errorExit("sd2.rename"); | ||||
| } | } | ||||
| PgmPrintln("Done"); | |||||
| Serial.println(F("Done")); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void loop() {} | void loop() {} |
| /* | /* | ||||
| * This sketch is a simple binary write/read benchmark. | * This sketch is a simple binary write/read benchmark. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| #include <SdFatUtil.h> | #include <SdFatUtil.h> | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // store error strings in flash to save RAM | // store error strings in flash to save RAM | ||||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void cidDmp() { | void cidDmp() { | ||||
| cid_t cid; | cid_t cid; |
| /* | /* | ||||
| * Demo of ArduinoInStream and ArduinoOutStream | * Demo of ArduinoInStream and ArduinoOutStream | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // create serial output stream | // create serial output stream |
| /* | /* | ||||
| * Simple data logger. | * Simple data logger. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD chip select pin. Be sure to disable any other SPI devices such as Enet. | // SD chip select pin. Be sure to disable any other SPI devices such as Enet. | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| // Error messages stored in flash. | // Error messages stored in flash. | ||||
| #define error(msg) error_P(PSTR(msg)) | |||||
| //------------------------------------------------------------------------------ | |||||
| void error_P(const char* msg) { | |||||
| sd.errorHalt_P(msg); | |||||
| } | |||||
| #define error(msg) sd.errorHalt(F(msg)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void setup() { | void setup() { | ||||
| const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; |
| /* | /* | ||||
| * Example use of chdir(), ls(), mkdir(), and rmdir(). | * Example use of chdir(), ls(), mkdir(), and rmdir(). | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD card chip select pin. | // SD card chip select pin. | ||||
| const uint8_t SD_CHIP_SELECT = SS; | const uint8_t SD_CHIP_SELECT = SS; | ||||
| ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf)); | ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf)); | ||||
| //============================================================================== | //============================================================================== | ||||
| // Error messages stored in flash. | // Error messages stored in flash. | ||||
| #define error(msg) error_P(PSTR(msg)) | |||||
| //------------------------------------------------------------------------------ | |||||
| void error_P(const char* msg) { | |||||
| sd.errorHalt_P(msg); | |||||
| } | |||||
| #define error(msg) sd.errorHalt(F(msg)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void setup() { | void setup() { | ||||
| Serial.begin(9600); | Serial.begin(9600); |
| // Demo of fgets function to read lines from a file. | // Demo of fgets function to read lines from a file. | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD chip select pin | // SD chip select pin | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // store error strings in flash memory | // store error strings in flash memory | ||||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void demoFgets() { | void demoFgets() { | ||||
| char line[25]; | char line[25]; | ||||
| if (!wrfile.isOpen()) error("MakeTestFile"); | if (!wrfile.isOpen()) error("MakeTestFile"); | ||||
| // write test file | // write test file | ||||
| wrfile.write_P(PSTR( | |||||
| wrfile.print(F( | |||||
| "Line with CRLF\r\n" | "Line with CRLF\r\n" | ||||
| "Line with only LF\n" | "Line with only LF\n" | ||||
| "Long line that will require an extra read\n" | "Long line that will require an extra read\n" |
| * Print a table with various formatting options | * Print a table with various formatting options | ||||
| * Format dates | * Format dates | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // create Serial stream | // create Serial stream |
| * Note: This example is meant to demonstrate subtleties the standard and | * Note: This example is meant to demonstrate subtleties the standard and | ||||
| * may not the best way to read a file. | * may not the best way to read a file. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD chip select pin | // SD chip select pin |
| * This example reads a simple CSV, comma-separated values, file. | * This example reads a simple CSV, comma-separated values, file. | ||||
| * Each line of the file has three values, a long and two floats. | * Each line of the file has three values, a long and two floats. | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD chip select pin | // SD chip select pin | ||||
| char fileName[] = "TESTFILE.CSV"; | char fileName[] = "TESTFILE.CSV"; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // store error strings in flash to save RAM | // store error strings in flash to save RAM | ||||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // read and print CSV test file | // read and print CSV test file | ||||
| void readFile() { | void readFile() { |
| * This sketch demonstrates use of SdFile::rename() | * This sketch demonstrates use of SdFile::rename() | ||||
| * and SdFat::rename(). | * and SdFat::rename(). | ||||
| */ | */ | ||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | #include <SdFat.h> | ||||
| // SD chip select pin | // SD chip select pin | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // store error strings in flash to save RAM | // store error strings in flash to save RAM | ||||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void setup() { | void setup() { | ||||
| Serial.begin(9600); | Serial.begin(9600); |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| /* FatLib Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | |||||
| * This file is part of the FatLib Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino SdFat Library. If not, see | |||||
| * along with the FatLib Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #ifndef ArduinoStream_h | #ifndef ArduinoStream_h | ||||
| * \file | * \file | ||||
| * \brief ArduinoInStream and ArduinoOutStream classes | * \brief ArduinoInStream and ArduinoOutStream classes | ||||
| */ | */ | ||||
| #include <bufstream.h> | |||||
| #if defined(ARDUINO) || defined(DOXYGEN) | |||||
| #include <Arduino.h> | |||||
| #include "bufstream.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class ArduinoInStream | * \class ArduinoInStream | ||||
| ArduinoOutStream() {} | ArduinoOutStream() {} | ||||
| Print* m_pr; | Print* m_pr; | ||||
| }; | }; | ||||
| #endif // ARDUINO | |||||
| #endif // ArduinoStream_h | #endif // ArduinoStream_h |
| */ | */ | ||||
| static inline __attribute__((always_inline)) | static inline __attribute__((always_inline)) | ||||
| void fastDigitalWrite(uint8_t pin, bool value) { | void fastDigitalWrite(uint8_t pin, bool value) { | ||||
| if (value) { | |||||
| *portSetRegister(pin) = 1; | |||||
| } else { | |||||
| *portClearRegister(pin) = 1; | |||||
| } | |||||
| if (value) { | |||||
| *portSetRegister(pin) = 1; | |||||
| } else { | |||||
| *portClearRegister(pin) = 1; | |||||
| } | |||||
| } | } | ||||
| #else // CORE_TEENSY | #else // CORE_TEENSY | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| * @return value read | * @return value read | ||||
| */ | */ | ||||
| static inline __attribute__((always_inline)) | static inline __attribute__((always_inline)) | ||||
| bool fastDigitalRead(uint8_t pin){ | |||||
| bool fastDigitalRead(uint8_t pin) { | |||||
| return g_APinDescription[pin].pPort->PIO_PDSR & g_APinDescription[pin].ulPin; | return g_APinDescription[pin].pPort->PIO_PDSR & g_APinDescription[pin].ulPin; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| * @param[in] level value to write | * @param[in] level value to write | ||||
| */ | */ | ||||
| static inline __attribute__((always_inline)) | static inline __attribute__((always_inline)) | ||||
| void fastDigitalWrite(uint8_t pin, bool value){ | |||||
| if(value) { | |||||
| void fastDigitalWrite(uint8_t pin, bool value) { | |||||
| if (value) { | |||||
| g_APinDescription[pin].pPort->PIO_SODR = g_APinDescription[pin].ulPin; | g_APinDescription[pin].pPort->PIO_SODR = g_APinDescription[pin].ulPin; | ||||
| } else { | } else { | ||||
| g_APinDescription[pin].pPort->PIO_CODR = g_APinDescription[pin].ulPin; | g_APinDescription[pin].pPort->PIO_CODR = g_APinDescription[pin].ulPin; | ||||
| #endif // CORE_TEENSY | #endif // CORE_TEENSY | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| inline void fastDigitalToggle(uint8_t pin) { | inline void fastDigitalToggle(uint8_t pin) { | ||||
| fastDigitalWrite(pin, !fastDigitalRead(pin)); | |||||
| } | |||||
| fastDigitalWrite(pin, !fastDigitalRead(pin)); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| inline void fastPinMode(uint8_t pin, bool mode) {pinMode(pin, mode);} | inline void fastPinMode(uint8_t pin, bool mode) {pinMode(pin, mode);} | ||||
| #else // __arm__ | #else // __arm__ | ||||
| ||defined(__AVR_ATmega168P__)\ | ||defined(__AVR_ATmega168P__)\ | ||||
| ||defined(__AVR_ATmega328P__) | ||defined(__AVR_ATmega328P__) | ||||
| // 168 and 328 Arduinos | // 168 and 328 Arduinos | ||||
| const static pin_map_t pinMap[] = { | |||||
| static const pin_map_t pinMap[] = { | |||||
| {&DDRD, &PIND, &PORTD, 0}, // D0 0 | {&DDRD, &PIND, &PORTD, 0}, // D0 0 | ||||
| {&DDRD, &PIND, &PORTD, 1}, // D1 1 | {&DDRD, &PIND, &PORTD, 1}, // D1 1 | ||||
| {&DDRD, &PIND, &PORTD, 2}, // D2 2 | {&DDRD, &PIND, &PORTD, 2}, // D2 2 | ||||
| static inline __attribute__((always_inline)) | static inline __attribute__((always_inline)) | ||||
| void fastBitWriteSafe(volatile uint8_t* address, uint8_t bit, bool level) { | void fastBitWriteSafe(volatile uint8_t* address, uint8_t bit, bool level) { | ||||
| uint8_t oldSREG; | uint8_t oldSREG; | ||||
| if (address > (uint8_t*)0X5F) { | |||||
| if (address > reinterpret_cast<uint8_t*>(0X5F)) { | |||||
| oldSREG = SREG; | oldSREG = SREG; | ||||
| cli(); | cli(); | ||||
| } | } | ||||
| } else { | } else { | ||||
| *address &= ~(1 << bit); | *address &= ~(1 << bit); | ||||
| } | } | ||||
| if (address > (uint8_t*)0X5F) { | |||||
| if (address > reinterpret_cast<uint8_t*>(0X5F)) { | |||||
| SREG = oldSREG; | SREG = oldSREG; | ||||
| } | } | ||||
| } | } | ||||
| * @param[in] pin Arduino pin number | * @param[in] pin Arduino pin number | ||||
| * | * | ||||
| * If the pin is in output mode toggle the pin level. | * If the pin is in output mode toggle the pin level. | ||||
| * If the pin is in input mode toggle the state of the 20K pullup. | |||||
| * If the pin is in input mode toggle the state of the 20K pull-up. | |||||
| */ | */ | ||||
| static inline __attribute__((always_inline)) | static inline __attribute__((always_inline)) | ||||
| void fastDigitalToggle(uint8_t pin) { | void fastDigitalToggle(uint8_t pin) { | ||||
| badPinCheck(pin); | badPinCheck(pin); | ||||
| if (pinMap[pin].pin > (uint8_t*)0X5F) { | |||||
| if (pinMap[pin].pin > reinterpret_cast<uint8_t*>(0X5F)) { | |||||
| // must write bit to high address port | // must write bit to high address port | ||||
| *pinMap[pin].pin = 1 << pinMap[pin].bit; | *pinMap[pin].pin = 1 << pinMap[pin].bit; | ||||
| } else { | } else { | ||||
| * @param[in] pin Arduino pin number | * @param[in] pin Arduino pin number | ||||
| * @param[in] mode if true set output mode else input mode | * @param[in] mode if true set output mode else input mode | ||||
| * | * | ||||
| * fastPinMode does not enable or disable the 20K pullup for input mode. | |||||
| * fastPinMode does not enable or disable the 20K pull-up for input mode. | |||||
| */ | */ | ||||
| static inline __attribute__((always_inline)) | static inline __attribute__((always_inline)) | ||||
| void fastPinMode(uint8_t pin, bool mode) { | void fastPinMode(uint8_t pin, bool mode) { | ||||
| * @param[in] pin Arduino pin number | * @param[in] pin Arduino pin number | ||||
| * @param[in] mode If true set output mode else input mode | * @param[in] mode If true set output mode else input mode | ||||
| * @param[in] level If mode is output, set level high/low. | * @param[in] level If mode is output, set level high/low. | ||||
| * If mode is input, enable or disable the pin's 20K pullup. | |||||
| * If mode is input, enable or disable the pin's 20K pull-up. | |||||
| */ | */ | ||||
| static inline __attribute__((always_inline)) | static inline __attribute__((always_inline)) | ||||
| void fastPinConfig(uint8_t pin, bool mode, bool level) { | void fastPinConfig(uint8_t pin, bool mode, bool level) { | ||||
| /** Constructor | /** Constructor | ||||
| * @param[in] mode If true set output mode else input mode | * @param[in] mode If true set output mode else input mode | ||||
| * @param[in] level If mode is output, set level high/low. | * @param[in] level If mode is output, set level high/low. | ||||
| * If mode is input, enable or disable the pin's 20K pullup. | |||||
| * If mode is input, enable or disable the pin's 20K pull-up. | |||||
| */ | */ | ||||
| DigitalPin(bool mode, bool level) { | DigitalPin(bool mode, bool level) { | ||||
| config(mode, level); | config(mode, level); | ||||
| /** Parenthesis operator | /** Parenthesis operator | ||||
| * @return Pin's level | * @return Pin's level | ||||
| */ | */ | ||||
| inline operator bool () const __attribute__((always_inline)) { | |||||
| inline operator bool() const __attribute__((always_inline)) { | |||||
| return read(); | return read(); | ||||
| } | } | ||||
| //---------------------------------------------------------------------------- | //---------------------------------------------------------------------------- | ||||
| /** set pin configuration | /** set pin configuration | ||||
| * @param[in] mode If true set output mode else input mode | * @param[in] mode If true set output mode else input mode | ||||
| * @param[in] level If mode is output, set level high/low. | |||||
| * If mode is input, enable or disable the pin's 20K pullup. | |||||
| * @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)) | inline __attribute__((always_inline)) | ||||
| void config(bool mode, bool level) { | void config(bool mode, bool level) { | ||||
| } | } | ||||
| //---------------------------------------------------------------------------- | //---------------------------------------------------------------------------- | ||||
| /** | /** | ||||
| * Set pin level high if output mode or enable 20K pullup if input mode. | |||||
| * Set pin level high if output mode or enable 20K pull-up if input mode. | |||||
| */ | */ | ||||
| inline __attribute__((always_inline)) | inline __attribute__((always_inline)) | ||||
| void high() {write(true);} | void high() {write(true);} | ||||
| //---------------------------------------------------------------------------- | //---------------------------------------------------------------------------- | ||||
| /** | /** | ||||
| * Set pin level low if output mode or disable 20K pullup if input mode. | |||||
| * Set pin level low if output mode or disable 20K pull-up if input mode. | |||||
| */ | */ | ||||
| inline __attribute__((always_inline)) | inline __attribute__((always_inline)) | ||||
| void low() {write(false);} | void low() {write(false);} | ||||
| * Set pin mode | * Set pin mode | ||||
| * @param[in] pinMode if true set output mode else input mode. | * @param[in] pinMode if true set output mode else input mode. | ||||
| * | * | ||||
| * mode() does not enable or disable the 20K pullup for input mode. | |||||
| * mode() does not enable or disable the 20K pull-up for input mode. | |||||
| */ | */ | ||||
| inline __attribute__((always_inline)) | inline __attribute__((always_inline)) | ||||
| void mode(bool pinMode) { | void mode(bool pinMode) { | ||||
| /** toggle a pin | /** toggle a pin | ||||
| * | * | ||||
| * If the pin is in output mode toggle the pin's level. | * If the pin is in output mode toggle the pin's level. | ||||
| * If the pin is in input mode toggle the state of the 20K pullup. | |||||
| * If the pin is in input mode toggle the state of the 20K pull-up. | |||||
| */ | */ | ||||
| inline __attribute__((always_inline)) | inline __attribute__((always_inline)) | ||||
| void toggle() { | void toggle() { | ||||
| } | } | ||||
| }; | }; | ||||
| #endif // DigitalPin_h | #endif // DigitalPin_h | ||||
| /** @} */ | |||||
| /** @} */ |
| /** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ | /** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ | ||||
| uint8_t const O_EXCL = 0X80; | uint8_t const O_EXCL = 0X80; | ||||
| // SdBaseFile class static and const definitions | |||||
| // FatFile class static and const definitions | |||||
| // flags for ls() | // flags for ls() | ||||
| /** ls() flag to print modify date */ | /** ls() flag to print modify date */ | ||||
| uint8_t const LS_DATE = 1; | uint8_t const LS_DATE = 1; |
| /* 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/>. | |||||
| */ | |||||
| #ifndef FatFile_h | |||||
| #define FatFile_h | |||||
| /** | |||||
| * \file | |||||
| * \brief FatFile class | |||||
| */ | |||||
| #include <ctype.h> | |||||
| #include <string.h> | |||||
| #include <stddef.h> | |||||
| #include <limits.h> | |||||
| #include "FatLibConfig.h" | |||||
| #include "FatApiConstants.h" | |||||
| #include "FatStructs.h" | |||||
| #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> | |||||
| #else // __AVR__ | |||||
| #ifndef PGM_P | |||||
| /** pointer to flash for ARM */ | |||||
| #define PGM_P const char* | |||||
| #endif // PGM_P | |||||
| #ifndef PSTR | |||||
| /** store literal string in flash for ARM */ | |||||
| #define PSTR(x) (x) | |||||
| #endif // PSTR | |||||
| #ifndef pgm_read_byte | |||||
| /** read 8-bits from flash for ARM */ | |||||
| #define pgm_read_byte(addr) (*(const unsigned char*)(addr)) | |||||
| #endif // pgm_read_byte | |||||
| #ifndef pgm_read_word | |||||
| /** read 16-bits from flash for ARM */ | |||||
| #define pgm_read_word(addr) (*(const uint16_t*)(addr)) | |||||
| #endif // pgm_read_word | |||||
| #ifndef PROGMEM | |||||
| /** store in flash for ARM */ | |||||
| #define PROGMEM const | |||||
| #endif // PROGMEM | |||||
| #endif // __AVR__ | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * \struct FatPos_t | |||||
| * \brief Internal type for file position - do not use in user apps. | |||||
| */ | |||||
| struct FatPos_t { | |||||
| /** stream position */ | |||||
| uint32_t position; | |||||
| /** cluster for position */ | |||||
| uint32_t cluster; | |||||
| FatPos_t() : position(0), cluster(0) {} | |||||
| }; | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class FatFile | |||||
| * \brief Basic file class. | |||||
| */ | |||||
| 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) {} | |||||
| /** Create a file object and open it in the current working directory. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |||||
| * | |||||
| * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive | |||||
| * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t). | |||||
| */ | |||||
| FatFile(const char* path, uint8_t oflag) { | |||||
| m_attr = FILE_ATTR_CLOSED; | |||||
| m_writeError = false; | |||||
| open(path, oflag); | |||||
| } | |||||
| #if DESTRUCTOR_CLOSES_FILE | |||||
| ~FatFile() {if(isOpen()) close();} | |||||
| #endif // DESTRUCTOR_CLOSES_FILE | |||||
| /** \return value of writeError */ | |||||
| bool getWriteError() {return m_writeError;} | |||||
| /** Set writeError to zero */ | |||||
| void clearWriteError() {m_writeError = 0;} | |||||
| //---------------------------------------------------------------------------- | |||||
| // helpers for stream classes | |||||
| /** get position for streams | |||||
| * \param[out] pos struct to receive position | |||||
| */ | |||||
| void getpos(FatPos_t* pos); | |||||
| /** set position for streams | |||||
| * \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();} | |||||
| /** 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. | |||||
| */ | |||||
| bool close(); | |||||
| /** Check for contiguous file and return its raw block range. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); | |||||
| /** Create and open a new contiguous file of a specified size. | |||||
| * | |||||
| * \note This function only supports short DOS 8.3 names. | |||||
| * See open() for more information. | |||||
| * | |||||
| * \param[in] dirFile The directory where the file will be created. | |||||
| * \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. | |||||
| * | |||||
| */ | |||||
| bool createContiguous(FatFile* dirFile, | |||||
| const char* path, uint32_t size); | |||||
| /** \return The current cluster number for a file or directory. */ | |||||
| uint32_t curCluster() const {return m_curCluster;} | |||||
| /** \return The current position for a file or directory. */ | |||||
| uint32_t curPosition() const {return m_curPosition;} | |||||
| /** \return Current working directory */ | |||||
| static FatFile* cwd() {return m_cwd;} | |||||
| /** Set the date/time callback function | |||||
| * | |||||
| * \param[in] dateTime The user's call back function. The callback | |||||
| * function is of the form: | |||||
| * | |||||
| * \code | |||||
| * void dateTime(uint16_t* date, uint16_t* time) { | |||||
| * uint16_t year; | |||||
| * uint8_t month, day, hour, minute, second; | |||||
| * | |||||
| * // User gets date and time from GPS or real-time clock here | |||||
| * | |||||
| * // return date using FAT_DATE macro to format fields | |||||
| * *date = FAT_DATE(year, month, day); | |||||
| * | |||||
| * // return time using FAT_TIME macro to format fields | |||||
| * *time = FAT_TIME(hour, minute, second); | |||||
| * } | |||||
| * \endcode | |||||
| * | |||||
| * Sets the function that is called when a file is created or when | |||||
| * a file's directory entry is modified by sync(). All timestamps, | |||||
| * access, creation, and modify, are set when a file is created. | |||||
| * sync() maintains the last access date and last modify date/time. | |||||
| * | |||||
| * See the timestamp() function. | |||||
| */ | |||||
| static void dateTimeCallback( | |||||
| void (*dateTime)(uint16_t* date, uint16_t* time)) { | |||||
| m_dateTime = dateTime; | |||||
| } | |||||
| /** Cancel the date/time callback function. */ | |||||
| 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. | |||||
| */ | |||||
| bool dirEntry(dir_t* dir); | |||||
| /** | |||||
| * \return The index of this file in it's directory. | |||||
| */ | |||||
| 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. | |||||
| * | |||||
| * \param[in] dir The directory structure containing the name. | |||||
| * \param[out] name A 13 byte char array for the formatted name. | |||||
| * \return length of the name. | |||||
| */ | |||||
| static uint8_t dirName(const dir_t* dir, char* name); | |||||
| /** Test for the existence of a file in a directory | |||||
| * | |||||
| * \param[in] path Path of the file to be tested for. | |||||
| * | |||||
| * The calling instance must be an open directory file. | |||||
| * | |||||
| * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory | |||||
| * dirFile. | |||||
| * | |||||
| * \return true if the file exists else false. | |||||
| */ | |||||
| bool exists(const char* path) { | |||||
| FatFile file; | |||||
| return file.open(this, path, O_READ); | |||||
| } | |||||
| /** | |||||
| * Get a string from a file. | |||||
| * | |||||
| * fgets() reads bytes from a file into the array pointed to by \a str, until | |||||
| * \a num - 1 bytes are read, or a delimiter is read and transferred to \a str, | |||||
| * or end-of-file is encountered. The string is then terminated | |||||
| * with a null byte. | |||||
| * | |||||
| * fgets() deletes CR, '\\r', from the string. This insures only a '\\n' | |||||
| * terminates the string for Windows text files which use CRLF for newline. | |||||
| * | |||||
| * \param[out] str Pointer to the array where the string is stored. | |||||
| * \param[in] num Maximum number of characters to be read | |||||
| * (including the final null byte). Usually the length | |||||
| * of the array \a str is used. | |||||
| * \param[in] delim Optional set of delimiters. The default is "\n". | |||||
| * | |||||
| * \return For success fgets() returns the length of the string in \a str. | |||||
| * If no data is read, fgets() returns zero for EOF or -1 if an error occurred. | |||||
| */ | |||||
| 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 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. | |||||
| * | |||||
| * \return The value one, true, is returned for success and | |||||
| * the value zero, false, is returned for failure. | |||||
| */ | |||||
| bool getFilename(char* name); | |||||
| /** \return True if this is a directory else false. */ | |||||
| bool isDir() const {return m_attr & FILE_ATTR_DIR;} | |||||
| /** \return True if this is a normal file else false. */ | |||||
| bool isFile() const {return !isDir();} | |||||
| /** \return True if this is a hidden file else false. */ | |||||
| bool isHidden() const {return m_attr & FILE_ATTR_HIDDEN;} | |||||
| /** \return True if this is an open file/directory else false. */ | |||||
| bool isOpen() const {return m_attr & FILE_ATTR_IS_OPEN;} | |||||
| /** \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;} | |||||
| /** \return True if this is the FAT12 of FAT16 root directory. */ | |||||
| bool isRootFixed() const {return m_attr & FILE_ATTR_ROOT_FIXED;} | |||||
| /** \return True if this is a subdirectory else false. */ | |||||
| 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;} | |||||
| /** List directory contents. | |||||
| * | |||||
| * \param[in] pr Print stream for list. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| * | |||||
| * \param[in] indent Amount of space before file name. Used for recursive | |||||
| * list to indicate subdirectory level. | |||||
| */ | |||||
| void ls(print_t* pr, uint8_t flags = 0, uint8_t indent = 0); | |||||
| /** Make a new directory. | |||||
| * | |||||
| * \param[in] dir An open FatFile instance for the directory that will | |||||
| * contain the new directory. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for the new directory. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool mkdir(FatFile* dir, const char* path, bool pFlag = true); | |||||
| /** Open a file in the volume working directory of a FatFileSystem. | |||||
| * | |||||
| * \param[in] fs File System where the file is located. | |||||
| * | |||||
| * \param[in] path with a valid 8.3 DOS name for a file to be opened. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool open(FatFileSystem* fs, const char* path, uint8_t oflag); | |||||
| /** Open a file by index. | |||||
| * | |||||
| * \param[in] dirFile An open FatFile instance for the directory. | |||||
| * | |||||
| * \param[in] index The \a index of the directory entry for the file to be | |||||
| * opened. The value for \a index is (directory file position)/32. | |||||
| * | |||||
| * \param[in] oflag bitwise-inclusive OR of open mode flags. | |||||
| * See see FatFile::open(FatFile*, const char*, uint8_t). | |||||
| * | |||||
| * See open() by path for definition of flags. | |||||
| * \return true for success or false for failure. | |||||
| */ | |||||
| bool open(FatFile* dirFile, uint16_t index, uint8_t oflag); | |||||
| /** Open a file or directory by name. | |||||
| * | |||||
| * \param[in] dirFile An open FatFile instance for the directory containing | |||||
| * the file to be opened. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |||||
| * | |||||
| * \param[in] oflag Values for \a oflag are constructed by a | |||||
| * bitwise-inclusive OR of flags from the following list | |||||
| * | |||||
| * O_READ - Open for reading. | |||||
| * | |||||
| * O_RDONLY - Same as O_READ. | |||||
| * | |||||
| * O_WRITE - Open for writing. | |||||
| * | |||||
| * O_WRONLY - Same as O_WRITE. | |||||
| * | |||||
| * O_RDWR - Open for reading and writing. | |||||
| * | |||||
| * O_APPEND - If set, the file offset shall be set to the end of the | |||||
| * file prior to each write. | |||||
| * | |||||
| * O_AT_END - Set the initial position at the end of the file. | |||||
| * | |||||
| * O_CREAT - If the file exists, this flag has no effect except as noted | |||||
| * under O_EXCL below. Otherwise, the file shall be created | |||||
| * | |||||
| * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists. | |||||
| * | |||||
| * O_SYNC - Call sync() after each write. This flag should not be used with | |||||
| * write(uint8_t) or any functions do character at a time writes since sync() | |||||
| * will be called after each byte. | |||||
| * | |||||
| * O_TRUNC - If the file exists and is a regular file, and the file is | |||||
| * successfully opened and is not read only, its length shall be truncated to 0. | |||||
| * | |||||
| * WARNING: A given file must not be opened by more than one FatFile object | |||||
| * or file corruption may occur. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool open(FatFile* dirFile, const char* path, uint8_t oflag); | |||||
| /** Open a file in the current working directory. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |||||
| * | |||||
| * \param[in] 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. | |||||
| */ | |||||
| bool open(const char* path, uint8_t oflag = O_READ) { | |||||
| return open(m_cwd, path, oflag); | |||||
| } | |||||
| /** Open the next file or subdirectory in a directory. | |||||
| * | |||||
| * \param[in] dirFile An open FatFile instance for the directory | |||||
| * containing the file to be opened. | |||||
| * | |||||
| * \param[in] oflag bitwise-inclusive OR of open mode flags. | |||||
| * See see FatFile::open(FatFile*, const char*, uint8_t). | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool openRoot(FatVolume* vol); | |||||
| /** Return the next available byte without consuming it. | |||||
| * | |||||
| * \return The byte if no error and not at eof else -1; | |||||
| */ | |||||
| int peek(); | |||||
| /** Print a file's creation date and time | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool printCreateDateTime(print_t* pr); | |||||
| /** %Print a directory date field. | |||||
| * | |||||
| * Format is yyyy-mm-dd. | |||||
| * | |||||
| * \param[in] pr Print stream for output. | |||||
| * \param[in] fatDate The date field from a directory entry. | |||||
| */ | |||||
| static void printFatDate(print_t* pr, uint16_t fatDate); | |||||
| /** %Print a directory time field. | |||||
| * | |||||
| * Format is hh:mm:ss. | |||||
| * | |||||
| * \param[in] pr Print stream for output. | |||||
| * \param[in] fatTime The time field from a directory entry. | |||||
| */ | |||||
| static void printFatTime(print_t* pr, uint16_t fatTime); | |||||
| /** Print a number followed by a field terminator. | |||||
| * \param[in] value The number to be printed. | |||||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||||
| * \param[in] prec Number of digits after decimal point. | |||||
| * \return The number of bytes written or -1 if an error occurs. | |||||
| */ | |||||
| int printField(float value, char term, uint8_t prec = 2); | |||||
| /** Print a number followed by a field terminator. | |||||
| * \param[in] value The number to be printed. | |||||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||||
| * \return The number of bytes written or -1 if an error occurs. | |||||
| */ | |||||
| int printField(int16_t value, char term); | |||||
| /** Print a number followed by a field terminator. | |||||
| * \param[in] value The number to be printed. | |||||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||||
| * \return The number of bytes written or -1 if an error occurs. | |||||
| */ | |||||
| int printField(uint16_t value, char term); | |||||
| /** Print a number followed by a field terminator. | |||||
| * \param[in] value The number to be printed. | |||||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||||
| * \return The number of bytes written or -1 if an error occurs. | |||||
| */ | |||||
| int printField(int32_t value, char term); | |||||
| /** Print a number followed by a field terminator. | |||||
| * \param[in] value The number to be printed. | |||||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||||
| * \return The number of bytes written or -1 if an error occurs. | |||||
| */ | |||||
| int printField(uint32_t value, char term); | |||||
| /** Print a file's modify date and time | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| 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. | |||||
| */ | |||||
| 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. | |||||
| */ | |||||
| size_t printFileSize(print_t* pr); | |||||
| /** Read the next byte from a file. | |||||
| * | |||||
| * \return For success read returns the next byte in the file as an int. | |||||
| * If an error occurs or end of file is reached -1 is returned. | |||||
| */ | |||||
| int read() { | |||||
| uint8_t b; | |||||
| return read(&b, 1) == 1 ? b : -1; | |||||
| } | |||||
| /** Read data from a file starting at the current position. | |||||
| * | |||||
| * \param[out] buf Pointer to the location that will receive the data. | |||||
| * | |||||
| * \param[in] nbyte Maximum number of bytes to read. | |||||
| * | |||||
| * \return For success read() returns the number of bytes read. | |||||
| * A value less than \a nbyte, including zero, will be returned | |||||
| * if end of file is reached. | |||||
| * If an error occurs, read() returns -1. Possible errors include | |||||
| * read() called before a file has been opened, corrupt file system | |||||
| * or an I/O error occurred. | |||||
| */ | |||||
| int read(void* buf, size_t nbyte); | |||||
| /** Read the next directory entry from a directory file. | |||||
| * | |||||
| * \param[out] dir The dir_t struct that will receive the data. | |||||
| * | |||||
| * \return For success readDir() returns the number of bytes read. | |||||
| * A value of zero will be returned if end of file is reached. | |||||
| * If an error occurs, readDir() returns -1. Possible errors include | |||||
| * readDir() called before a directory has been opened, this is not | |||||
| * a directory file or an I/O error occurred. | |||||
| */ | |||||
| int8_t readDir(dir_t* dir); | |||||
| /** Remove a file. | |||||
| * | |||||
| * The directory entry and all data for the file are deleted. | |||||
| * | |||||
| * \note This function should not be used to delete the 8.3 version of a | |||||
| * 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. | |||||
| */ | |||||
| bool remove(); | |||||
| /** Remove a file. | |||||
| * | |||||
| * The directory entry and all data for the file are deleted. | |||||
| * | |||||
| * \param[in] dirFile The directory that contains the file. | |||||
| * \param[in] path Path for the file to be removed. | |||||
| * | |||||
| * \note This function should not be used to delete the 8.3 version of a | |||||
| * 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. | |||||
| */ | |||||
| static bool remove(FatFile* dirFile, const char* path); | |||||
| /** Set the file's current position to zero. */ | |||||
| 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. | |||||
| */ | |||||
| bool rename(FatFile* dirFile, const char* newPath); | |||||
| /** Remove a directory file. | |||||
| * | |||||
| * The directory file will be removed only if it is empty and is not the | |||||
| * root directory. rmdir() follows DOS and Windows and ignores the | |||||
| * read-only attribute for the directory. | |||||
| * | |||||
| * \note This function should not be used to delete the 8.3 version of a | |||||
| * 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. | |||||
| */ | |||||
| bool rmdir(); | |||||
| /** Recursively delete a directory and all contained files. | |||||
| * | |||||
| * This is like the Unix/Linux 'rm -rf *' if called with the root directory | |||||
| * hence the name. | |||||
| * | |||||
| * Warning - This will remove all contents of the directory including | |||||
| * subdirectories. The directory will then be removed if it is not root. | |||||
| * The read-only attribute for files will be ignored. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool rmRfStar(); | |||||
| /** Set the files position to current position + \a pos. See seekSet(). | |||||
| * \param[in] offset The new position in bytes from the current position. | |||||
| * \return true for success or false for failure. | |||||
| */ | |||||
| bool seekCur(int32_t offset) { | |||||
| return seekSet(m_curPosition + offset); | |||||
| } | |||||
| /** Set the files position to end-of-file + \a offset. See seekSet(). | |||||
| * \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);} | |||||
| /** 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. | |||||
| */ | |||||
| bool seekSet(uint32_t pos); | |||||
| /** Set the current working directory. | |||||
| * | |||||
| * \param[in] dir New current working directory. | |||||
| * | |||||
| * \return true for success else false. | |||||
| */ | |||||
| static bool setCwd(FatFile* dir) { | |||||
| 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. | |||||
| */ | |||||
| bool sync(); | |||||
| /** Copy a file's timestamps | |||||
| * | |||||
| * \param[in] file File to copy timestamps from. | |||||
| * | |||||
| * \note | |||||
| * 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. | |||||
| */ | |||||
| bool timestamp(FatFile* file); | |||||
| /** Set a file's timestamps in its directory entry. | |||||
| * | |||||
| * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive | |||||
| * OR of flags from the following list | |||||
| * | |||||
| * T_ACCESS - Set the file's last access date. | |||||
| * | |||||
| * T_CREATE - Set the file's creation date and time. | |||||
| * | |||||
| * T_WRITE - Set the file's last write/modification date and time. | |||||
| * | |||||
| * \param[in] year Valid range 1980 - 2107 inclusive. | |||||
| * | |||||
| * \param[in] month Valid range 1 - 12 inclusive. | |||||
| * | |||||
| * \param[in] day Valid range 1 - 31 inclusive. | |||||
| * | |||||
| * \param[in] hour Valid range 0 - 23 inclusive. | |||||
| * | |||||
| * \param[in] minute Valid range 0 - 59 inclusive. | |||||
| * | |||||
| * \param[in] second Valid range 0 - 59 inclusive | |||||
| * | |||||
| * \note It is possible to set an invalid date since there is no check for | |||||
| * the number of days in a month. | |||||
| * | |||||
| * \note | |||||
| * 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. | |||||
| */ | |||||
| bool timestamp(uint8_t flags, uint16_t year, uint8_t month, uint8_t day, | |||||
| 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;} | |||||
| /** 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. | |||||
| */ | |||||
| bool truncate(uint32_t length); | |||||
| /** \return FatVolume that contains this file. */ | |||||
| 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);} | |||||
| /** Write data to an open file. | |||||
| * | |||||
| * \note Data is moved to the cache but may not be written to the | |||||
| * storage device until sync() is called. | |||||
| * | |||||
| * \param[in] buf Pointer to the location of the data to be written. | |||||
| * | |||||
| * \param[in] nbyte Number of bytes to write. | |||||
| * | |||||
| * \return For success write() returns the number of bytes written, always | |||||
| * \a nbyte. If an error occurs, write() returns -1. Possible errors | |||||
| * include write() is called before a file has been opened, write is called | |||||
| * for a read-only file, device is full, a corrupt file system or an I/O error. | |||||
| * | |||||
| */ | |||||
| int write(const void* buf, size_t nbyte); | |||||
| //------------------------------------------------------------------------------ | |||||
| private: | |||||
| /** This file has not been opened. */ | |||||
| static const uint8_t FILE_ATTR_CLOSED = 0; | |||||
| /** File is read-only. */ | |||||
| static const uint8_t FILE_ATTR_READ_ONLY = DIR_ATT_READ_ONLY; | |||||
| /** File should be hidden in directory listings. */ | |||||
| static const uint8_t FILE_ATTR_HIDDEN = DIR_ATT_HIDDEN; | |||||
| /** 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; | |||||
| /** Entry is for a subdirectory */ | |||||
| static const uint8_t FILE_ATTR_SUBDIR = DIR_ATT_DIRECTORY; | |||||
| /** A FAT12 or FAT16 root directory */ | |||||
| static const uint8_t FILE_ATTR_ROOT_FIXED = 0X20; | |||||
| /** A FAT32 root directory */ | |||||
| static const uint8_t FILE_ATTR_ROOT32 = 0X40; | |||||
| /** Entry is for root. */ | |||||
| static const uint8_t FILE_ATTR_ROOT = FILE_ATTR_ROOT_FIXED | FILE_ATTR_ROOT32; | |||||
| /** Directory type bits */ | |||||
| static const uint8_t FILE_ATTR_DIR = FILE_ATTR_SUBDIR | FILE_ATTR_ROOT; | |||||
| /** Attributes to copy from directory entry */ | |||||
| static const uint8_t FILE_ATTR_COPY = DIR_ATT_READ_ONLY | DIR_ATT_HIDDEN | | |||||
| 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); | |||||
| bool readLBN(uint32_t* lbn); | |||||
| dir_t* readDirCache(); | |||||
| bool setDirSize(); | |||||
| // bits defined in m_flags | |||||
| // should be 0X0F | |||||
| static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); | |||||
| // sync of directory entry required | |||||
| static uint8_t const F_FILE_DIR_DIRTY = 0X80; | |||||
| // global pointer to cwd dir | |||||
| static FatFile* m_cwd; | |||||
| // 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 | |||||
| uint8_t m_attr; // File attributes | |||||
| uint8_t m_flags; // See above for definition of m_flags bits | |||||
| uint16_t m_dirIndex; // index of directory entry in dir file | |||||
| FatVolume* m_vol; // volume where file is located | |||||
| 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 | |||||
| }; | |||||
| #endif // FatFile_h |
| /* 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/>. | |||||
| */ | |||||
| #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()) { | |||||
| 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++; | |||||
| } | |||||
| // 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; | |||||
| } | |||||
| } | |||||
| } | |||||
| fail: | |||||
| *index = free; | |||||
| return false; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool FatFile::createLfn(FatFile* dirFile, | |||||
| uint16_t bgnIndex, char* name, uint8_t oflag) { | |||||
| return false; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| 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; | |||||
| uint8_t freeNeed; | |||||
| dir_t* dir; | |||||
| size_t len; | |||||
| bool is83; | |||||
| bool foundFree = false; | |||||
| const char* ptr; | |||||
| uint8_t name83[11]; | |||||
| if (!isDir()) { | |||||
| 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(); | |||||
| while (1) { | |||||
| curIndex = m_curPosition/32; | |||||
| if (m_curPosition == m_fileSize) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| // read entry into cache | |||||
| dir = readDirCache(); | |||||
| if (!dir) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| #if 1 | |||||
| if (dir->name[0] == DIR_NAME_DELETED) { | |||||
| if (!foundFree) { | |||||
| if (freeIndex == 0XFFFF) { | |||||
| freeIndex = curIndex; | |||||
| freeCount = 0; | |||||
| } | |||||
| if (++freeCount == freeNeed) { | |||||
| foundFree = true; | |||||
| } | |||||
| } | |||||
| continue; | |||||
| } else { | |||||
| if (!foundFree) freeIndex = 0XFFFF; | |||||
| } | |||||
| #endif | |||||
| if (dir->name[0] == DIR_NAME_FREE) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| // skip empty slot or '.' or '..' | |||||
| if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { | |||||
| haveLong = false; | |||||
| continue; | |||||
| } | |||||
| 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; | |||||
| 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; | |||||
| } | |||||
| fill = false; | |||||
| } | |||||
| if (u > 255 || toupper(u) != toupper(name[nOff + i])) { | |||||
| haveLong = false; | |||||
| 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 (haveLong) goto done; | |||||
| if (is83 && !memcmp(dir->name, name83, sizeof(name83))) { | |||||
| goto done; | |||||
| } | |||||
| } else { | |||||
| haveLong = false; | |||||
| } | |||||
| } | |||||
| done: | |||||
| *bgnIndex = haveLong ? lfnBgn : curIndex; | |||||
| *endIndex = curIndex; | |||||
| return true; | |||||
| fail: | |||||
| *bgnIndex = foundFree ? freeIndex : curIndex; | |||||
| 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; | |||||
| dir_t* dir; | |||||
| uint16_t index; | |||||
| int rtn; | |||||
| // Check for valid directory and file is not open. | |||||
| if (!dirFile->isDir() || isOpen() || size < 13) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| 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) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| // done if last entry | |||||
| if (dir->name[0] == DIR_NAME_FREE) { | |||||
| DBG_FAIL_MACRO; | |||||
| return 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)) { | |||||
| 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; | |||||
| } | |||||
| 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; | |||||
| } | |||||
| } 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 (!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); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!openCachedEntry(dirFile, index, oflag)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| return rtn; | |||||
| } else { | |||||
| haveLong = false; | |||||
| } | |||||
| } | |||||
| done: | |||||
| return 0; | |||||
| fail: | |||||
| return -1; | |||||
| } | |||||
| #endif // DOXYGEN_SHOULD_SKIP_THIS |
| /* 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 <math.h> | |||||
| #include "FatFile.h" | |||||
| #include "FmtNumber.h" | |||||
| //------------------------------------------------------------------------------ | |||||
| // print uint8_t with width 2 | |||||
| static void print2u(print_t* pr, uint8_t v) { | |||||
| char c0 = '?'; | |||||
| char c1 = '?'; | |||||
| if (v < 100) { | |||||
| c1 = v/10; | |||||
| c0 = v - 10*c1 + '0'; | |||||
| c1 += '0'; | |||||
| } | |||||
| pr->write(c1); | |||||
| pr->write(c0); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| static void printU32(print_t* pr, uint32_t v) { | |||||
| char buf[11]; | |||||
| char* ptr = buf + sizeof(buf); | |||||
| *--ptr = 0; | |||||
| pr->write(fmtDec(v, ptr)); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void FatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) { | |||||
| if (!isDir()) return; | |||||
| 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)); | |||||
| } | |||||
| } | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| // 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)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| printFatDate(pr, dir.creationDate); | |||||
| pr->write(' '); | |||||
| printFatTime(pr, dir.creationTime); | |||||
| return true; | |||||
| fail: | |||||
| return false; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void FatFile::printFatDate(print_t* pr, uint16_t fatDate) { | |||||
| printU32(pr, FAT_YEAR(fatDate)); | |||||
| pr->write('-'); | |||||
| print2u(pr, FAT_MONTH(fatDate)); | |||||
| pr->write('-'); | |||||
| print2u(pr, FAT_DAY(fatDate)); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void FatFile::printFatTime(print_t* pr, uint16_t fatTime) { | |||||
| print2u(pr, FAT_HOUR(fatTime)); | |||||
| pr->write(':'); | |||||
| print2u(pr, FAT_MINUTE(fatTime)); | |||||
| pr->write(':'); | |||||
| print2u(pr, FAT_SECOND(fatTime)); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Template for FatFile::printField() */ | |||||
| template <typename Type> | |||||
| static int printFieldT(FatFile* file, char sign, Type value, char term) { | |||||
| char buf[3*sizeof(Type) + 3]; | |||||
| char* str = &buf[sizeof(buf)]; | |||||
| if (term) { | |||||
| *--str = term; | |||||
| if (term == '\n') { | |||||
| *--str = '\r'; | |||||
| } | |||||
| } | |||||
| #ifdef OLD_FMT | |||||
| do { | |||||
| Type m = value; | |||||
| value /= 10; | |||||
| *--str = '0' + m - 10*value; | |||||
| } while (value); | |||||
| #else // OLD_FMT | |||||
| str = fmtDec(value, str); | |||||
| #endif // OLD_FMT | |||||
| if (sign) { | |||||
| *--str = sign; | |||||
| } | |||||
| return file->write(str, &buf[sizeof(buf)] - str); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int FatFile::printField(float value, char term, uint8_t prec) { | |||||
| char buf[24]; | |||||
| char* str = &buf[sizeof(buf)]; | |||||
| if (term) { | |||||
| *--str = term; | |||||
| if (term == '\n') { | |||||
| *--str = '\r'; | |||||
| } | |||||
| } | |||||
| str = fmtFloat(value, str, prec); | |||||
| return write(str, buf + sizeof(buf) - str); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int FatFile::printField(uint16_t value, char term) { | |||||
| return printFieldT(this, 0, value, term); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int FatFile::printField(int16_t value, char term) { | |||||
| char sign = 0; | |||||
| if (value < 0) { | |||||
| sign = '-'; | |||||
| value = -value; | |||||
| } | |||||
| return printFieldT(this, sign, (uint16_t)value, term); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int FatFile::printField(uint32_t value, char term) { | |||||
| return printFieldT(this, 0, value, term); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int FatFile::printField(int32_t value, char term) { | |||||
| char sign = 0; | |||||
| if (value < 0) { | |||||
| sign = '-'; | |||||
| value = -value; | |||||
| } | |||||
| return printFieldT(this, sign, (uint32_t)value, term); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool FatFile::printModifyDateTime(print_t* pr) { | |||||
| dir_t dir; | |||||
| if (!dirEntry(&dir)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| printFatDate(pr, dir.lastWriteDate); | |||||
| pr->write(' '); | |||||
| printFatTime(pr, dir.lastWriteTime); | |||||
| return true; | |||||
| 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 = ' '; | |||||
| return pr->write(buf); | |||||
| } |
| /* FatLib Library | |||||
| * Copyright (C) 2013 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/>. | |||||
| */ | |||||
| #ifndef FatFileSystem_h | |||||
| #define FatFileSystem_h | |||||
| #include "FatVolume.h" | |||||
| #include "FatFile.h" | |||||
| /** | |||||
| * \file | |||||
| * \brief FatFileSystem class | |||||
| */ | |||||
| //------------------------------------------------------------------------------ | |||||
| /** FatFileSystem version YYYYMMDD */ | |||||
| #define FAT_LIB_VERSION 20141115 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * \class FatFileSystem | |||||
| * \brief Integration class for the FatLib library. | |||||
| */ | |||||
| class FatFileSystem : protected FatVolume { | |||||
| protected: | |||||
| /** | |||||
| * 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. | |||||
| */ | |||||
| bool begin(FatFile* d) { | |||||
| m_vwd = d; | |||||
| m_vwd->close(); | |||||
| return init() && vwd()->openRoot(this) && FatFile::setCwd(vwd()); | |||||
| } | |||||
| public: | |||||
| /** Change a volume's working directory to root | |||||
| * | |||||
| * Changes the volume's working directory to the SD's root directory. | |||||
| * Optionally set the current working directory to the volume's | |||||
| * working directory. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool chdir(bool set_cwd = false) { | |||||
| vwd()->close(); | |||||
| return vwd()->openRoot(this) && (set_cwd ? FatFile::setCwd(vwd()) : true); | |||||
| } | |||||
| /** Change a volume's working directory | |||||
| * | |||||
| * Changes the volume working directory to the \a path subdirectory. | |||||
| * Optionally set the current working directory to the volume's | |||||
| * working directory. | |||||
| * | |||||
| * Example: If the volume's working directory is "/DIR", chdir("SUB") | |||||
| * will change the volume's working directory from "/DIR" to "/DIR/SUB". | |||||
| * | |||||
| * If path is "/", the volume's working directory will be changed to the | |||||
| * root directory | |||||
| * | |||||
| * \param[in] path The name of the subdirectory. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| //---------------------------------------------------------------------------- | |||||
| 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; | |||||
| *m_vwd = dir; | |||||
| if (set_cwd) FatFile::setCwd(vwd()); | |||||
| return true; | |||||
| fail: | |||||
| return false; | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| /** Set the current working directory to a volume's working directory. | |||||
| * | |||||
| * This is useful with multiple SD cards. | |||||
| * | |||||
| * The current working directory is changed to this | |||||
| * volume's working directory. | |||||
| * | |||||
| * This is like the Windows/DOS \<drive letter>: command. | |||||
| */ | |||||
| void chvol() { | |||||
| FatFile::setCwd(vwd()); | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| /** | |||||
| * Test for the existence of a file. | |||||
| * | |||||
| * \param[in] path Path of the file to be tested for. | |||||
| * | |||||
| * \return true if the file exists else false. | |||||
| */ | |||||
| bool exists(const char* path) { | |||||
| return vwd()->exists(path); | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| /** List the directory contents of the volume working directory. | |||||
| * | |||||
| * \param[in] pr Print stream for list. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| */ | |||||
| void ls(print_t* pr, uint8_t flags) { | |||||
| vwd()->ls(pr, flags); | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| /** List the directory contents of a directory. | |||||
| * | |||||
| * \param[in] pr Print stream for list. | |||||
| * | |||||
| * \param[in] path directory to list. | |||||
| * | |||||
| * \param[in] flags The inclusive OR of | |||||
| * | |||||
| * LS_DATE - %Print file modification date | |||||
| * | |||||
| * LS_SIZE - %Print file size. | |||||
| * | |||||
| * LS_R - Recursive list of subdirectories. | |||||
| */ | |||||
| void ls(print_t* pr, const char* path, uint8_t flags) { | |||||
| FatFile dir; | |||||
| dir.open(vwd(), path, O_READ); | |||||
| dir.ls(pr, flags); | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| /** Make a subdirectory in the volume working directory. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool mkdir(const char* path, bool pFlag = true) { | |||||
| FatFile sub; | |||||
| return sub.mkdir(vwd(), path, pFlag); | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| /** Remove a file from the volume working directory. | |||||
| * | |||||
| * \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. | |||||
| */ | |||||
| bool remove(const char* path) { | |||||
| return FatFile::remove(vwd(), path); | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| /** Rename a file or subdirectory. | |||||
| * | |||||
| * \param[in] oldPath Path name to the file or subdirectory to be renamed. | |||||
| * | |||||
| * \param[in] newPath New path name of the file or subdirectory. | |||||
| * | |||||
| * The \a newPath object must not exist before the rename call. | |||||
| * | |||||
| * The file to be renamed must not be open. The directory entry may be | |||||
| * 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. | |||||
| */ | |||||
| bool rename(const char *oldPath, const char *newPath) { | |||||
| FatFile file; | |||||
| if (!file.open(vwd(), oldPath, O_READ)) return false; | |||||
| return file.rename(vwd(), newPath); | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| /** Remove a subdirectory from the volume's working directory. | |||||
| * | |||||
| * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. | |||||
| * | |||||
| * 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. | |||||
| */ | |||||
| bool rmdir(const char* path) { | |||||
| FatFile sub; | |||||
| if (!sub.open(vwd(), path, O_READ)) return false; | |||||
| return sub.rmdir(); | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| /** 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] 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. | |||||
| */ | |||||
| bool truncate(const char* path, uint32_t length) { | |||||
| FatFile file; | |||||
| if (!file.open(vwd(), path, O_WRITE)) return false; | |||||
| return file.truncate(length); | |||||
| } | |||||
| /** \return a pointer to the FatVolume object. */ | |||||
| FatVolume* vol() {return this;} | |||||
| /** \return a pointer to the volume working directory. */ | |||||
| FatFile* vwd() {return m_vwd;} | |||||
| private: | |||||
| FatFile* m_vwd; | |||||
| }; | |||||
| #endif // FatFileSystem_h |
| /* FatLib Library | |||||
| * Copyright (C) 2013 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/>. | |||||
| */ | |||||
| #ifndef FatLib_h | |||||
| #define FatLib_h | |||||
| #include "ArduinoStream.h" | |||||
| #include "FatFileSystem.h" | |||||
| #include "FatLibConfig.h" | |||||
| #include "FatVolume.h" | |||||
| #include "FatFile.h" | |||||
| #include "StdioStream.h" | |||||
| #include "fstream.h" | |||||
| #endif // FatLib_h |
| /* FatLib Library | |||||
| * Copyright (C) 2013 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/>. | |||||
| */ | |||||
| /** | |||||
| * \file | |||||
| * \brief configuration definitions | |||||
| */ | |||||
| #ifndef FatLibConfig_h | |||||
| #define FatLibConfig_h | |||||
| /** Use SdFatConfig.h if nonzero */ | |||||
| #define USE_SDFAT_CONFIG 1 | |||||
| #if USE_SDFAT_CONFIG | |||||
| #include "../SdFatConfig.h" | |||||
| #else // USE_SDFAT_CONFIG | |||||
| #include <stdint.h> | |||||
| #ifdef __AVR__ | |||||
| #include <avr/io.h> | |||||
| #endif // __AVR__ | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * 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__ | |||||
| #define USE_SEPARATE_FAT_CACHE 0 | |||||
| #endif // __arm__ | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Set USE_MULTI_BLOCK_IO non-zero to use multi-block SD read/write. | |||||
| * | |||||
| * Don't use mult-block read/write on small AVR boards. | |||||
| */ | |||||
| #if defined(RAMEND) && RAMEND < 3000 | |||||
| #define USE_MULTI_BLOCK_IO 0 | |||||
| #else | |||||
| #define USE_MULTI_BLOCK_IO 1 | |||||
| #endif | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Set DESTRUCTOR_CLOSES_FILE non-zero to close a file in its destructor. | |||||
| * | |||||
| * Causes use of lots of heap in ARM. | |||||
| */ | |||||
| #define DESTRUCTOR_CLOSES_FILE 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Call flush for endl if ENDL_CALLS_FLUSH is non-zero | |||||
| * | |||||
| * The standard for iostreams is to call flush. This is very costly for | |||||
| * SdFat. Each call to flush causes 2048 bytes of I/O to the SD. | |||||
| * | |||||
| * SdFat has a single 512 byte buffer for I/O so it must write the current | |||||
| * data block to the SD, read the directory block from the SD, update the | |||||
| * directory entry, write the directory block to the SD and read the data | |||||
| * block back into the buffer. | |||||
| * | |||||
| * The SD flash memory controller is not designed for this many rewrites | |||||
| * so performance may be reduced by more than a factor of 100. | |||||
| * | |||||
| * If ENDL_CALLS_FLUSH is zero, you must call flush and/or close to force | |||||
| * all data to be written to the SD. | |||||
| */ | |||||
| #define ENDL_CALLS_FLUSH 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Allow FAT12 volumes if FAT12_SUPPORT is non-zero. | |||||
| * FAT12 has not been well tested. | |||||
| */ | |||||
| #define FAT12_SUPPORT 0 | |||||
| #endif // USE_SDFAT_CONFIG | |||||
| #endif // FatLibConfig_h |
| /** | /** | ||||
| * This dates back to the old MS-DOS 1.x media determination and is | * 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 | * no longer usually used for anything. 0xF8 is the standard value | ||||
| * for fixed (nonremovable) media. For removable media, 0xF0 is | |||||
| * for fixed (non-removable) media. For removable media, 0xF0 is | |||||
| * frequently used. Legal values are 0xF0 or 0xF8-0xFF. | * frequently used. Legal values are 0xF0 or 0xF8-0xFF. | ||||
| */ | */ | ||||
| uint8_t mediaType; | uint8_t mediaType; | ||||
| /** 32-bit unsigned holding this file's size in bytes. */ | /** 32-bit unsigned holding this file's size in bytes. */ | ||||
| uint32_t fileSize; | uint32_t fileSize; | ||||
| }__attribute__((packed)); | }__attribute__((packed)); | ||||
| /** Type name for directoryEntry */ | |||||
| typedef struct directoryEntry dir_t; | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // Definitions for directory entries | // Definitions for directory entries | ||||
| // | // | ||||
| /** Type name for directoryEntry */ | |||||
| typedef struct directoryEntry dir_t; | |||||
| /** escape for name[0] = 0XE5 */ | /** escape for name[0] = 0XE5 */ | ||||
| uint8_t const DIR_NAME_0XE5 = 0X05; | uint8_t const DIR_NAME_0XE5 = 0X05; | ||||
| /** name[0] value for entry that is free after being "deleted" */ | /** name[0] value for entry that is free after being "deleted" */ | ||||
| uint8_t const DIR_NAME_FREE = 0X00; | uint8_t const DIR_NAME_FREE = 0X00; | ||||
| /** file is read-only */ | /** file is read-only */ | ||||
| uint8_t const DIR_ATT_READ_ONLY = 0X01; | uint8_t const DIR_ATT_READ_ONLY = 0X01; | ||||
| /** File should hidden in directory listings */ | |||||
| /** File should e hidden in directory listings */ | |||||
| uint8_t const DIR_ATT_HIDDEN = 0X02; | uint8_t const DIR_ATT_HIDDEN = 0X02; | ||||
| /** Entry is for a system file */ | /** Entry is for a system file */ | ||||
| uint8_t const DIR_ATT_SYSTEM = 0X04; | uint8_t const DIR_ATT_SYSTEM = 0X04; | ||||
| uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; | uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; | ||||
| /** defined attribute bits */ | /** defined attribute bits */ | ||||
| uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; | 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); | |||||
| /** Directory entry is for a file | |||||
| * \param[in] dir Pointer to a directory entry. | |||||
| * | |||||
| * \return true if the entry is for a normal file else false. | |||||
| */ | |||||
| static inline uint8_t DIR_IS_FILE(const dir_t* dir) { | |||||
| return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0; | |||||
| } | |||||
| /** Directory entry is for a file or subdirectory | |||||
| * \param[in] dir Pointer to a directory entry. | |||||
| * | |||||
| * \return true if the entry is for a normal file or subdirectory else false. | |||||
| */ | |||||
| static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { | |||||
| return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; | |||||
| } | |||||
| /** Directory entry is part of a long name | /** Directory entry is part of a long name | ||||
| * \param[in] dir Pointer to a directory entry. | * \param[in] dir Pointer to a directory entry. | ||||
| * | * | ||||
| static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { | static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { | ||||
| return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME; | return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME; | ||||
| } | } | ||||
| /** Mask for file/subdirectory tests */ | |||||
| uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); | |||||
| /** Directory entry is for a file | |||||
| /** Directory entry is hidden | |||||
| * \param[in] dir Pointer to a directory entry. | * \param[in] dir Pointer to a directory entry. | ||||
| * | * | ||||
| * \return true if the entry is for a normal file else false. | |||||
| * \return true if the entry is hidden else false. | |||||
| */ | */ | ||||
| static inline uint8_t DIR_IS_FILE(const dir_t* dir) { | |||||
| return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0; | |||||
| static inline uint8_t DIR_IS_HIDDEN(const dir_t* dir) { | |||||
| return dir->attributes & DIR_ATT_HIDDEN; | |||||
| } | } | ||||
| /** Directory entry is for a subdirectory | /** Directory entry is for a subdirectory | ||||
| * \param[in] dir Pointer to a directory entry. | * \param[in] dir Pointer to a directory entry. | ||||
| static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) { | static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) { | ||||
| return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; | return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; | ||||
| } | } | ||||
| /** Directory entry is for a file or subdirectory | |||||
| /** Directory entry is system type | |||||
| * \param[in] dir Pointer to a directory entry. | * \param[in] dir Pointer to a directory entry. | ||||
| * | * | ||||
| * \return true if the entry is for a normal file or subdirectory else false. | |||||
| * \return true if the entry is system else false. | |||||
| */ | */ | ||||
| static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { | |||||
| return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; | |||||
| static inline uint8_t DIR_IS_SYSTEM(const dir_t* dir) { | |||||
| return dir->attributes & DIR_ATT_SYSTEM; | |||||
| } | } | ||||
| /** date field for FAT directory entry | /** date field for FAT directory entry | ||||
| * \param[in] year [1980,2107] | * \param[in] year [1980,2107] | ||||
| uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; | uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; | ||||
| /** Default time for file timestamp is 1 am */ | /** Default time for file timestamp is 1 am */ | ||||
| uint16_t const FAT_DEFAULT_TIME = (1 << 11); | uint16_t const FAT_DEFAULT_TIME = (1 << 11); | ||||
| //------------------------------------------------------------------------------ | |||||
| /** Dimension of first name field in long directory entry */ | |||||
| const uint8_t LDIR_NAME1_DIM = 5; | |||||
| /** Dimension of first name field in long directory entry */ | |||||
| const uint8_t LDIR_NAME2_DIM = 6; | |||||
| /** Dimension of first name field in long directory entry */ | |||||
| const uint8_t LDIR_NAME3_DIM = 2; | |||||
| /** | |||||
| * \struct longDirectoryEntry | |||||
| * \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. | |||||
| */ | |||||
| uint8_t ord; | |||||
| /** Characters 1-5 of the long-name sub-component in this entry. */ | |||||
| uint16_t name1[LDIR_NAME1_DIM]; | |||||
| /** 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. | |||||
| */ | |||||
| uint8_t type; | |||||
| /** | |||||
| * 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. */ | |||||
| uint16_t name2[LDIR_NAME2_DIM]; | |||||
| /** 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. */ | |||||
| uint16_t name3[LDIR_NAME3_DIM]; | |||||
| }__attribute__((packed)); | |||||
| /** Type name for longDirectoryEntry */ | |||||
| typedef struct longDirectoryEntry ldir_t; | |||||
| /** | |||||
| * Ord mast that 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. | |||||
| */ | |||||
| const uint8_t LDIR_ORD_LAST_LONG_ENTRY = 0X40; | |||||
| /** | |||||
| * Fetch a 16-bit long file name character. | |||||
| * | |||||
| * \param[in] ldir Pointer to long file name directory entry. | |||||
| * \param[in] i Index of character to return; | |||||
| * \return The 16-bit field. | |||||
| */ | |||||
| inline uint16_t lfnChar(ldir_t *ldir, uint8_t i) { | |||||
| return i < LDIR_NAME1_DIM ? ldir->name1[i] : | |||||
| i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM) ? | |||||
| ldir->name2[i - LDIR_NAME1_DIM] : | |||||
| ldir->name3[i - (LDIR_NAME1_DIM + LDIR_NAME2_DIM)]; | |||||
| } | |||||
| #endif // FatStructs_h | #endif // FatStructs_h |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| /* FatLib Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | |||||
| * This file is part of the FatLib Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino SdFat Library. If not, see | |||||
| * along with the FatLib Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include <SdFat.h> | |||||
| #include "FatVolume.h" | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| #if !USE_MULTIPLE_CARDS | |||||
| // raw block cache | |||||
| uint8_t SdVolume::m_fatCount; // number of FATs on volume | |||||
| uint32_t SdVolume::m_blocksPerFat; // FAT size in blocks | |||||
| cache_t SdVolume::m_cacheBuffer; // 512 byte cache for Sd2Card | |||||
| uint32_t SdVolume::m_cacheBlockNumber; // current block number | |||||
| uint8_t SdVolume::m_cacheStatus; // status of cache block | |||||
| #if USE_SEPARATE_FAT_CACHE | |||||
| cache_t SdVolume::m_cacheFatBuffer; // 512 byte cache for FAT | |||||
| uint32_t SdVolume::m_cacheFatBlockNumber; // current Fat block number | |||||
| uint8_t SdVolume::m_cacheFatStatus; // status of cache Fatblock | |||||
| #endif // USE_SEPARATE_FAT_CACHE | |||||
| Sd2Card* SdVolume::m_sdCard; // pointer to SD card object | |||||
| #endif // USE_MULTIPLE_CARDS | |||||
| //------------------------------------------------------------------------------ | |||||
| // find a contiguous group of clusters | |||||
| bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { | |||||
| // start of group | |||||
| uint32_t bgnCluster; | |||||
| // end of group | |||||
| uint32_t endCluster; | |||||
| // last cluster of FAT | |||||
| uint32_t fatEnd = m_clusterCount + 1; | |||||
| // flag to save place to start next search | |||||
| bool setStart; | |||||
| // set search start cluster | |||||
| if (*curCluster) { | |||||
| // try to make file contiguous | |||||
| bgnCluster = *curCluster + 1; | |||||
| // don't save new start location | |||||
| setStart = false; | |||||
| } else { | |||||
| // start at likely place for free cluster | |||||
| bgnCluster = m_allocSearchStart; | |||||
| // save next search start if no holes. | |||||
| setStart = true; | |||||
| } | |||||
| // end of group | |||||
| endCluster = bgnCluster; | |||||
| // search the FAT for free clusters | |||||
| for (uint32_t n = 0;; n++, endCluster++) { | |||||
| // can't find space checked all clusters | |||||
| if (n >= m_clusterCount) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| // past end - start from beginning of FAT | |||||
| if (endCluster > fatEnd) { | |||||
| bgnCluster = endCluster = 2; | |||||
| } | |||||
| uint32_t f; | |||||
| if (!fatGet(endCluster, &f)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| if (f != 0) { | |||||
| // don't update search start if unallocated clusters before endCluster. | |||||
| if (bgnCluster != endCluster) setStart = false; | |||||
| // cluster in use try next cluster as bgnCluster | |||||
| bgnCluster = endCluster + 1; | |||||
| } else if ((endCluster - bgnCluster + 1) == count) { | |||||
| // done - found space | |||||
| break; | |||||
| } | |||||
| } | |||||
| // remember possible next free cluster | |||||
| if (setStart) m_allocSearchStart = endCluster + 1; | |||||
| // mark end of chain | |||||
| if (!fatPutEOC(endCluster)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| // link clusters | |||||
| while (endCluster > bgnCluster) { | |||||
| if (!fatPut(endCluster - 1, endCluster)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| endCluster--; | |||||
| } | |||||
| if (*curCluster != 0) { | |||||
| // connect chains | |||||
| if (!fatPut(*curCluster, bgnCluster)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| } | |||||
| // return first cluster number to caller | |||||
| *curCluster = bgnCluster; | |||||
| return true; | |||||
| fail: | |||||
| return false; | |||||
| } | |||||
| //============================================================================== | |||||
| // cache functions | |||||
| #if USE_SEPARATE_FAT_CACHE | |||||
| //------------------------------------------------------------------------------ | |||||
| cache_t* SdVolume::cacheFetch(uint32_t blockNumber, uint8_t options) { | |||||
| return cacheFetchData(blockNumber, options); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| cache_t* SdVolume::cacheFetchData(uint32_t blockNumber, uint8_t options) { | |||||
| if (m_cacheBlockNumber != blockNumber) { | |||||
| if (!cacheWriteData()) { | |||||
| cache_t* FatCache::read(uint32_t lbn, uint8_t option) { | |||||
| if (m_lbn != lbn) { | |||||
| if (!sync()) { | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| if (!(options & CACHE_OPTION_NO_READ)) { | |||||
| if (!m_sdCard->readBlock(blockNumber, m_cacheBuffer.data)) { | |||||
| if (!(option & CACHE_OPTION_NO_READ)) { | |||||
| if (!m_vol->readBlock(lbn, m_block.data)) { | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| } | } | ||||
| m_cacheStatus = 0; | |||||
| m_cacheBlockNumber = blockNumber; | |||||
| m_status = 0; | |||||
| m_lbn = lbn; | |||||
| } | } | ||||
| m_cacheStatus |= options & CACHE_STATUS_MASK; | |||||
| return &m_cacheBuffer; | |||||
| m_status |= option & CACHE_STATUS_MASK; | |||||
| return &m_block; | |||||
| fail: | fail: | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| cache_t* SdVolume::cacheFetchFat(uint32_t blockNumber, uint8_t options) { | |||||
| if (m_cacheFatBlockNumber != blockNumber) { | |||||
| if (!cacheWriteFat()) { | |||||
| bool FatCache::sync() { | |||||
| if (m_status & CACHE_STATUS_DIRTY) { | |||||
| if (!m_vol->writeBlock(m_lbn, m_block.data)) { | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| if (!(options & CACHE_OPTION_NO_READ)) { | |||||
| if (!m_sdCard->readBlock(blockNumber, m_cacheFatBuffer.data)) { | |||||
| // mirror second FAT | |||||
| if (m_status & CACHE_STATUS_MIRROR_FAT) { | |||||
| uint32_t lbn = m_lbn + m_vol->blocksPerFat(); | |||||
| if (!m_vol->writeBlock(lbn, m_block.data)) { | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| } | } | ||||
| m_cacheFatStatus = 0; | |||||
| m_cacheFatBlockNumber = blockNumber; | |||||
| m_status &= ~CACHE_STATUS_DIRTY; | |||||
| } | } | ||||
| m_cacheFatStatus |= options & CACHE_STATUS_MASK; | |||||
| return &m_cacheFatBuffer; | |||||
| return true; | |||||
| fail: | fail: | ||||
| return 0; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool SdVolume::cacheSync() { | |||||
| return cacheWriteData() && cacheWriteFat(); | |||||
| return false; | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdVolume::cacheWriteData() { | |||||
| if (m_cacheStatus & CACHE_STATUS_DIRTY) { | |||||
| if (!m_sdCard->writeBlock(m_cacheBlockNumber, m_cacheBuffer.data)) { | |||||
| bool FatVolume::allocateCluster(uint32_t current, uint32_t* next) { | |||||
| uint32_t find = current ? current : m_allocSearchStart; | |||||
| uint32_t start = find; | |||||
| while (1) { | |||||
| find++; | |||||
| // If at end of FAT go to beginning of FAT. | |||||
| if (find > m_lastCluster) find = 2; | |||||
| uint32_t f; | |||||
| if (!fatGet(find, &f)) { | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| m_cacheStatus &= ~CACHE_STATUS_DIRTY; | |||||
| } | |||||
| return true; | |||||
| fail: | |||||
| return false; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool SdVolume::cacheWriteFat() { | |||||
| if (m_cacheFatStatus & CACHE_STATUS_DIRTY) { | |||||
| if (!m_sdCard->writeBlock(m_cacheFatBlockNumber, m_cacheFatBuffer.data)) { | |||||
| if (f == 0) break; | |||||
| if (find == start) { | |||||
| // Can't find space checked all clusters. | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| // mirror second FAT | |||||
| if (m_fatCount > 1) { | |||||
| uint32_t lbn = m_cacheFatBlockNumber + m_blocksPerFat; | |||||
| if (!m_sdCard->writeBlock(lbn, m_cacheFatBuffer.data)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| } | |||||
| // mark end of chain | |||||
| if (!fatPutEOC(find)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| if (current) { | |||||
| // link clusters | |||||
| if (!fatPut(current, find)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | } | ||||
| m_cacheFatStatus &= ~CACHE_STATUS_DIRTY; | |||||
| } else { | |||||
| // Remember place for search start. | |||||
| m_allocSearchStart = find; | |||||
| } | } | ||||
| *next = find; | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| return false; | return false; | ||||
| } | } | ||||
| #else // USE_SEPARATE_FAT_CACHE | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| cache_t* SdVolume::cacheFetch(uint32_t blockNumber, uint8_t options) { | |||||
| if (m_cacheBlockNumber != blockNumber) { | |||||
| if (!cacheSync()) { | |||||
| // find a contiguous group of clusters | |||||
| bool FatVolume::allocContiguous(uint32_t count, uint32_t* firstCluster) { | |||||
| // flag to save place to start next search | |||||
| bool setStart = true; | |||||
| // start of group | |||||
| uint32_t bgnCluster; | |||||
| // end of group | |||||
| uint32_t endCluster; | |||||
| // Start at cluster after last allocated cluster. | |||||
| uint32_t startCluster = m_allocSearchStart; | |||||
| endCluster = bgnCluster = startCluster + 1; | |||||
| // search the FAT for free clusters | |||||
| while (1) { | |||||
| // If past end - start from beginning of FAT. | |||||
| if (endCluster > m_lastCluster) { | |||||
| bgnCluster = endCluster = 2; | |||||
| } | |||||
| uint32_t f; | |||||
| if (!fatGet(endCluster, &f)) { | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| if (!(options & CACHE_OPTION_NO_READ)) { | |||||
| if (!m_sdCard->readBlock(blockNumber, m_cacheBuffer.data)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| if (f != 0) { | |||||
| // don't update search start if unallocated clusters before endCluster. | |||||
| if (bgnCluster != endCluster) setStart = false; | |||||
| // cluster in use try next cluster as bgnCluster | |||||
| bgnCluster = endCluster + 1; | |||||
| } else if ((endCluster - bgnCluster + 1) == count) { | |||||
| // done - found space | |||||
| break; | |||||
| } | } | ||||
| m_cacheStatus = 0; | |||||
| m_cacheBlockNumber = blockNumber; | |||||
| // Can't find space if all clusters checked. | |||||
| if (startCluster == endCluster) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| endCluster++; | |||||
| } | } | ||||
| m_cacheStatus |= options & CACHE_STATUS_MASK; | |||||
| return &m_cacheBuffer; | |||||
| // remember possible next free cluster | |||||
| if (setStart) m_allocSearchStart = endCluster + 1; | |||||
| fail: | |||||
| return 0; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| cache_t* SdVolume::cacheFetchFat(uint32_t blockNumber, uint8_t options) { | |||||
| return cacheFetch(blockNumber, options | CACHE_STATUS_FAT_BLOCK); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool SdVolume::cacheSync() { | |||||
| if (m_cacheStatus & CACHE_STATUS_DIRTY) { | |||||
| if (!m_sdCard->writeBlock(m_cacheBlockNumber, m_cacheBuffer.data)) { | |||||
| // mark end of chain | |||||
| if (!fatPutEOC(endCluster)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| // link clusters | |||||
| while (endCluster > bgnCluster) { | |||||
| if (!fatPut(endCluster - 1, endCluster)) { | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| // mirror second FAT | |||||
| if ((m_cacheStatus & CACHE_STATUS_FAT_BLOCK) && m_fatCount > 1) { | |||||
| uint32_t lbn = m_cacheBlockNumber + m_blocksPerFat; | |||||
| if (!m_sdCard->writeBlock(lbn, m_cacheBuffer.data)) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| } | |||||
| m_cacheStatus &= ~CACHE_STATUS_DIRTY; | |||||
| endCluster--; | |||||
| } | } | ||||
| // return first cluster number to caller | |||||
| *firstCluster = bgnCluster; | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdVolume::cacheWriteData() { | |||||
| return cacheSync(); | |||||
| } | |||||
| #endif // USE_SEPARATE_FAT_CACHE | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdVolume::cacheInvalidate() { | |||||
| m_cacheBlockNumber = 0XFFFFFFFF; | |||||
| m_cacheStatus = 0; | |||||
| } | |||||
| //============================================================================== | |||||
| //------------------------------------------------------------------------------ | |||||
| uint32_t SdVolume::clusterStartBlock(uint32_t cluster) const { | |||||
| uint32_t FatVolume::clusterStartBlock(uint32_t cluster) const { | |||||
| return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); | return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // Fetch a FAT entry | // Fetch a FAT entry | ||||
| bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||||
| bool FatVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||||
| uint32_t lba; | uint32_t lba; | ||||
| cache_t* pc; | cache_t* pc; | ||||
| // error if reserved cluster of beyond FAT | // error if reserved cluster of beyond FAT | ||||
| if (cluster < 2 || cluster > (m_clusterCount + 1)) { | |||||
| if (cluster < 2 || cluster > m_lastCluster) { | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| if (m_fatType == 32) { | |||||
| lba = m_fatStartBlock + (cluster >> 7); | |||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | |||||
| if (!pc) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| *value = pc->fat32[cluster & 0X7F] & FAT32MASK; | |||||
| return true; | |||||
| } | |||||
| if (m_fatType == 16) { | |||||
| lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | |||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | |||||
| if (!pc) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| *value = pc->fat16[cluster & 0XFF]; | |||||
| return true; | |||||
| } | |||||
| if (FAT12_SUPPORT && m_fatType == 12) { | if (FAT12_SUPPORT && m_fatType == 12) { | ||||
| uint16_t index = cluster; | uint16_t index = cluster; | ||||
| index += index >> 1; | index += index >> 1; | ||||
| lba = m_fatStartBlock + (index >> 9); | lba = m_fatStartBlock + (index >> 9); | ||||
| pc = cacheFetchFat(lba, CACHE_FOR_READ); | |||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | |||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| uint16_t tmp = pc->data[index]; | uint16_t tmp = pc->data[index]; | ||||
| index++; | index++; | ||||
| if (index == 512) { | if (index == 512) { | ||||
| pc = cacheFetchFat(lba + 1, CACHE_FOR_READ); | |||||
| pc = cacheFetchFat(lba + 1, FatCache::CACHE_FOR_READ); | |||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| tmp |= pc->data[index] << 8; | tmp |= pc->data[index] << 8; | ||||
| *value = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; | *value = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; | ||||
| return true; | return true; | ||||
| } | |||||
| if (m_fatType == 16) { | |||||
| lba = m_fatStartBlock + (cluster >> 8); | |||||
| } else if (m_fatType == 32) { | |||||
| lba = m_fatStartBlock + (cluster >> 7); | |||||
| } else { | } else { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| pc = cacheFetchFat(lba, CACHE_FOR_READ); | |||||
| if (!pc) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| if (m_fatType == 16) { | |||||
| *value = pc->fat16[cluster & 0XFF]; | |||||
| } else { | |||||
| *value = pc->fat32[cluster & 0X7F] & FAT32MASK; | |||||
| } | |||||
| return true; | |||||
| fail: | fail: | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // Store a FAT entry | // Store a FAT entry | ||||
| bool SdVolume::fatPut(uint32_t cluster, uint32_t value) { | |||||
| bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { | |||||
| uint32_t lba; | uint32_t lba; | ||||
| cache_t* pc; | cache_t* pc; | ||||
| // error if reserved cluster of beyond FAT | // error if reserved cluster of beyond FAT | ||||
| if (cluster < 2 || cluster > (m_clusterCount + 1)) { | |||||
| if (cluster < 2 || cluster > m_lastCluster) { | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| if (m_fatType == 32) { | |||||
| lba = m_fatStartBlock + (cluster >> 7); | |||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | |||||
| if (!pc) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| pc->fat32[cluster & 0X7F] = value; | |||||
| return true; | |||||
| } | |||||
| if (m_fatType == 16) { | |||||
| lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | |||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | |||||
| if (!pc) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| pc->fat16[cluster & 0XFF] = value; | |||||
| return true; | |||||
| } | |||||
| if (FAT12_SUPPORT && m_fatType == 12) { | if (FAT12_SUPPORT && m_fatType == 12) { | ||||
| uint16_t index = cluster; | uint16_t index = cluster; | ||||
| index += index >> 1; | index += index >> 1; | ||||
| lba = m_fatStartBlock + (index >> 9); | lba = m_fatStartBlock + (index >> 9); | ||||
| pc = cacheFetchFat(lba, CACHE_FOR_WRITE); | |||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | |||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| if (index == 512) { | if (index == 512) { | ||||
| lba++; | lba++; | ||||
| index = 0; | index = 0; | ||||
| pc = cacheFetchFat(lba, CACHE_FOR_WRITE); | |||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | |||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| pc->data[index] = tmp; | pc->data[index] = tmp; | ||||
| return true; | return true; | ||||
| } | |||||
| if (m_fatType == 16) { | |||||
| lba = m_fatStartBlock + (cluster >> 8); | |||||
| } else if (m_fatType == 32) { | |||||
| lba = m_fatStartBlock + (cluster >> 7); | |||||
| } else { | } else { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| pc = cacheFetchFat(lba, CACHE_FOR_WRITE); | |||||
| if (!pc) { | |||||
| DBG_FAIL_MACRO; | |||||
| goto fail; | |||||
| } | |||||
| // store entry | |||||
| if (m_fatType == 16) { | |||||
| pc->fat16[cluster & 0XFF] = value; | |||||
| } else { | |||||
| pc->fat32[cluster & 0X7F] = value; | |||||
| } | |||||
| return true; | |||||
| fail: | fail: | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // free a cluster chain | // free a cluster chain | ||||
| bool SdVolume::freeChain(uint32_t cluster) { | |||||
| bool FatVolume::freeChain(uint32_t cluster) { | |||||
| uint32_t next; | uint32_t next; | ||||
| do { | do { | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Volume free space in clusters. | |||||
| * | |||||
| * \return Count of free clusters for success or -1 if an error occurs. | |||||
| */ | |||||
| int32_t SdVolume::freeClusterCount() { | |||||
| int32_t FatVolume::freeClusterCount() { | |||||
| uint32_t free = 0; | uint32_t free = 0; | ||||
| uint32_t lba; | uint32_t lba; | ||||
| uint32_t todo = m_clusterCount + 2; | |||||
| uint32_t todo = m_lastCluster + 1; | |||||
| uint16_t n; | uint16_t n; | ||||
| if (FAT12_SUPPORT && m_fatType == 12) { | if (FAT12_SUPPORT && m_fatType == 12) { | ||||
| } else if (m_fatType == 16 || m_fatType == 32) { | } else if (m_fatType == 16 || m_fatType == 32) { | ||||
| lba = m_fatStartBlock; | lba = m_fatStartBlock; | ||||
| while (todo) { | while (todo) { | ||||
| cache_t* pc = cacheFetchFat(lba++, CACHE_FOR_READ); | |||||
| cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ); | |||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Initialize a FAT volume. | |||||
| * | |||||
| * \param[in] dev The SD card where the volume is located. | |||||
| * | |||||
| * \param[in] part The partition to be used. Legal values for \a part are | |||||
| * 1-4 to use the corresponding partition on a device formatted with | |||||
| * 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. | |||||
| */ | |||||
| bool SdVolume::init(Sd2Card* dev, uint8_t part) { | |||||
| uint8_t tmp; | |||||
| bool FatVolume::init(uint8_t part) { | |||||
| uint32_t clusterCount; | |||||
| uint32_t totalBlocks; | uint32_t totalBlocks; | ||||
| uint32_t volumeStartBlock = 0; | uint32_t volumeStartBlock = 0; | ||||
| fat32_boot_t* fbs; | fat32_boot_t* fbs; | ||||
| cache_t* pc; | cache_t* pc; | ||||
| m_sdCard = dev; | |||||
| uint8_t tmp; | |||||
| m_fatType = 0; | m_fatType = 0; | ||||
| m_allocSearchStart = 2; | |||||
| m_cacheStatus = 0; // cacheSync() will write block if true | |||||
| m_cacheBlockNumber = 0XFFFFFFFF; | |||||
| m_allocSearchStart = 1; | |||||
| m_cache.init(this); | |||||
| #if USE_SEPARATE_FAT_CACHE | #if USE_SEPARATE_FAT_CACHE | ||||
| m_cacheFatStatus = 0; // cacheSync() will write block if true | |||||
| m_cacheFatBlockNumber = 0XFFFFFFFF; | |||||
| m_fatCache.init(this); | |||||
| #endif // USE_SEPARATE_FAT_CACHE | #endif // USE_SEPARATE_FAT_CACHE | ||||
| // if part == 0 assume super floppy with FAT boot sector in block zero | // if part == 0 assume super floppy with FAT boot sector in block zero | ||||
| // if part > 0 assume mbr volume with partition table | // if part > 0 assume mbr volume with partition table | ||||
| if (part) { | if (part) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| pc = cacheFetch(volumeStartBlock, CACHE_FOR_READ); | |||||
| pc = cacheFetchData(volumeStartBlock, FatCache::CACHE_FOR_READ); | |||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| volumeStartBlock = p->firstSector; | volumeStartBlock = p->firstSector; | ||||
| } | } | ||||
| pc = cacheFetch(volumeStartBlock, CACHE_FOR_READ); | |||||
| pc = cacheFetchData(volumeStartBlock, FatCache::CACHE_FOR_READ); | |||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| fbs = &(pc->fbs32); | fbs = &(pc->fbs32); | ||||
| if (fbs->bytesPerSector != 512 || | if (fbs->bytesPerSector != 512 || | ||||
| fbs->fatCount == 0 || | |||||
| fbs->fatCount != 2 || | |||||
| fbs->reservedSectorCount == 0) { | fbs->reservedSectorCount == 0) { | ||||
| // not valid FAT volume | // not valid FAT volume | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| m_fatCount = fbs->fatCount; | |||||
| m_blocksPerCluster = fbs->sectorsPerCluster; | m_blocksPerCluster = fbs->sectorsPerCluster; | ||||
| m_clusterBlockMask = m_blocksPerCluster - 1; | m_clusterBlockMask = m_blocksPerCluster - 1; | ||||
| // determine shift that is same as multiply by m_blocksPerCluster | // determine shift that is same as multiply by m_blocksPerCluster | ||||
| m_clusterSizeShift = 0; | m_clusterSizeShift = 0; | ||||
| for (tmp = 1; m_blocksPerCluster != tmp; m_clusterSizeShift++) { | |||||
| tmp <<= 1; | |||||
| for (tmp = 1; m_blocksPerCluster != tmp; tmp <<= 1, m_clusterSizeShift++) { | |||||
| if (tmp == 0) { | if (tmp == 0) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| m_rootDirEntryCount = fbs->rootDirEntryCount; | m_rootDirEntryCount = fbs->rootDirEntryCount; | ||||
| // directory start for FAT16 dataStart for FAT32 | // directory start for FAT16 dataStart for FAT32 | ||||
| m_rootDirStart = m_fatStartBlock + fbs->fatCount * m_blocksPerFat; | |||||
| m_rootDirStart = m_fatStartBlock + 2 * m_blocksPerFat; | |||||
| // data start for FAT16 and FAT32 | // data start for FAT16 and FAT32 | ||||
| m_dataStartBlock = m_rootDirStart + ((32 * fbs->rootDirEntryCount + 511)/512); | m_dataStartBlock = m_rootDirStart + ((32 * fbs->rootDirEntryCount + 511)/512); | ||||
| totalBlocks = fbs->totalSectors16 ? | totalBlocks = fbs->totalSectors16 ? | ||||
| fbs->totalSectors16 : fbs->totalSectors32; | fbs->totalSectors16 : fbs->totalSectors32; | ||||
| // total data blocks | // total data blocks | ||||
| m_clusterCount = totalBlocks - (m_dataStartBlock - volumeStartBlock); | |||||
| clusterCount = totalBlocks - (m_dataStartBlock - volumeStartBlock); | |||||
| // divide by cluster size to get cluster count | // divide by cluster size to get cluster count | ||||
| m_clusterCount >>= m_clusterSizeShift; | |||||
| clusterCount >>= m_clusterSizeShift; | |||||
| m_lastCluster = clusterCount + 1; | |||||
| // FAT type is determined by cluster count | // FAT type is determined by cluster count | ||||
| if (m_clusterCount < 4085) { | |||||
| if (clusterCount < 4085) { | |||||
| m_fatType = 12; | m_fatType = 12; | ||||
| if (!FAT12_SUPPORT) { | if (!FAT12_SUPPORT) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| } else if (m_clusterCount < 65525) { | |||||
| } else if (clusterCount < 65525) { | |||||
| m_fatType = 16; | m_fatType = 16; | ||||
| } else { | } else { | ||||
| m_rootDirStart = fbs->fat32RootCluster; | m_rootDirStart = fbs->fat32RootCluster; |
| /* FatLib Library | |||||
| * Copyright (C) 2013 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/>. | |||||
| */ | |||||
| #ifndef FatVolume_h | |||||
| #define FatVolume_h | |||||
| /** | |||||
| * \file | |||||
| * \brief FatVolume class | |||||
| */ | |||||
| #include <stddef.h> | |||||
| #include "FatLibConfig.h" | |||||
| #include "FatStructs.h" | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Macro for debug. */ | |||||
| // #include <Arduino.h> | |||||
| #define DBG_FAIL_MACRO // Serial.print(__FILE__);Serial.println(__LINE__) | |||||
| //------------------------------------------------------------------------------ | |||||
| // Forward declaration of FatVolume. | |||||
| class FatVolume; | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * \brief Cache for an raw data block. | |||||
| */ | |||||
| union cache_t { | |||||
| /** Used to access cached file data blocks. */ | |||||
| uint8_t data[512]; | |||||
| /** Used to access cached FAT16 entries. */ | |||||
| uint16_t fat16[256]; | |||||
| /** Used to access cached FAT32 entries. */ | |||||
| uint32_t fat32[128]; | |||||
| /** Used to access cached directory entries. */ | |||||
| dir_t dir[16]; | |||||
| /** Used to access a cached Master Boot Record. */ | |||||
| mbr_t mbr; | |||||
| /** Used to access to a cached FAT boot sector. */ | |||||
| fat_boot_t fbs; | |||||
| /** Used to access to a cached FAT32 boot sector. */ | |||||
| fat32_boot_t fbs32; | |||||
| /** Used to access to a cached FAT32 FSINFO sector. */ | |||||
| fat32_fsinfo_t fsinfo; | |||||
| }; | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class FatCache | |||||
| * \brief Block cache. | |||||
| */ | |||||
| class FatCache { | |||||
| public: | |||||
| /** Cached block is dirty */ | |||||
| static const uint8_t CACHE_STATUS_DIRTY = 1; | |||||
| /** Cashed block is FAT entry and must be mirrored in second FAT. */ | |||||
| 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; | |||||
| /** Sync existing block but do not read new block. */ | |||||
| static const uint8_t CACHE_OPTION_NO_READ = 4; | |||||
| /** Cache block for read. */ | |||||
| static uint8_t const CACHE_FOR_READ = 0; | |||||
| /** Cache block for write. */ | |||||
| 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; | |||||
| /** \return Cache block address. */ | |||||
| cache_t* block() {return &m_block;} | |||||
| /** Set current block dirty. */ | |||||
| void dirty() {m_status |= CACHE_STATUS_DIRTY;} | |||||
| /** Initialize the cache. | |||||
| * \param[in] vol FatVolume that owns this FatCache. | |||||
| */ | |||||
| void init(FatVolume *vol) { | |||||
| m_vol = vol; | |||||
| invalidate(); | |||||
| } | |||||
| /** Invalidate current cache block. */ | |||||
| void invalidate() { | |||||
| m_status = 0; | |||||
| m_lbn = 0XFFFFFFFF; | |||||
| } | |||||
| /** \return Logical block number for cached block. */ | |||||
| 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. | |||||
| * \return Address of cached block. */ | |||||
| cache_t* read(uint32_t lbn, uint8_t option); | |||||
| /** Write current block if dirty. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool sync(); | |||||
| private: | |||||
| uint8_t m_status; | |||||
| FatVolume* m_vol; | |||||
| uint32_t m_lbn; | |||||
| cache_t m_block; | |||||
| }; | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class FatVolume | |||||
| * \brief Access FAT16 and FAT32 volumes on raw file devices. | |||||
| */ | |||||
| class FatVolume { | |||||
| public: | |||||
| /** Create an instance of FatVolume | |||||
| */ | |||||
| FatVolume() : m_fatType(0) {} | |||||
| /** \return The volume's cluster size in blocks. */ | |||||
| uint8_t blocksPerCluster() const {return m_blocksPerCluster;} | |||||
| /** \return The number of blocks in one FAT. */ | |||||
| 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; | |||||
| m_cache.invalidate(); | |||||
| return m_cache.block(); | |||||
| } | |||||
| /** \return The total number of clusters in the volume. */ | |||||
| uint32_t clusterCount() const {return m_lastCluster - 1;} | |||||
| /** \return The shift count required to multiply by blocksPerCluster. */ | |||||
| 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;} | |||||
| /** \return The number of File Allocation Tables. */ | |||||
| uint8_t fatCount() {return 2;} | |||||
| /** \return The logical block number for the start of the first FAT. */ | |||||
| 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;} | |||||
| /** Volume free space in clusters. | |||||
| * | |||||
| * \return Count of free clusters for success or -1 if an error occurs. | |||||
| */ | |||||
| int32_t freeClusterCount(); | |||||
| /** 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. | |||||
| */ | |||||
| 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 | |||||
| * 1-4 to use the corresponding partition on a device formatted with | |||||
| * 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. | |||||
| */ | |||||
| bool init(uint8_t part); | |||||
| /** \return The number of entries in the root directory for FAT16 volumes. */ | |||||
| uint32_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;} | |||||
| /** 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);} | |||||
| //------------------------------------------------------------------------------ | |||||
| private: | |||||
| // Allow FatFile and FatCache access to FatVolume private functions. | |||||
| friend class FatCache; | |||||
| friend class FatFile; | |||||
| //------------------------------------------------------------------------------ | |||||
| uint8_t m_blocksPerCluster; // Cluster size in blocks. | |||||
| uint8_t m_clusterBlockMask; // Mask to extract block of cluster. | |||||
| uint8_t m_clusterSizeShift; // Cluster count to block count shift. | |||||
| uint8_t m_fatType; // Volume type (12, 16, OR 32). | |||||
| uint16_t m_rootDirEntryCount; // Number of entries in FAT16 root dir. | |||||
| uint32_t m_allocSearchStart; // Start cluster for alloc search. | |||||
| uint32_t m_blocksPerFat; // FAT size in blocks | |||||
| uint32_t m_dataStartBlock; // First data block number. | |||||
| uint32_t m_fatStartBlock; // Start block for first FAT. | |||||
| uint32_t m_lastCluster; // Last cluster number in FAT. | |||||
| uint32_t m_rootDirStart; // Start block for FAT16, cluster for FAT32. | |||||
| //------------------------------------------------------------------------------ | |||||
| // block caches | |||||
| FatCache m_cache; | |||||
| #if USE_SEPARATE_FAT_CACHE | |||||
| FatCache m_fatCache; | |||||
| cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { | |||||
| return m_fatCache.read(blockNumber, | |||||
| options | FatCache::CACHE_STATUS_MIRROR_FAT); | |||||
| } | |||||
| 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();} | |||||
| #endif // USE_SEPARATE_FAT_CACHE | |||||
| cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { | |||||
| return m_cache.read(blockNumber, options); | |||||
| } | |||||
| 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 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;} | |||||
| uint32_t clusterStartBlock(uint32_t cluster) const; | |||||
| bool fatGet(uint32_t cluster, uint32_t* value); | |||||
| bool fatPut(uint32_t cluster, uint32_t value); | |||||
| bool fatPutEOC(uint32_t cluster) { | |||||
| return fatPut(cluster, 0x0FFFFFFF); | |||||
| } | |||||
| bool freeChain(uint32_t cluster); | |||||
| bool isEOC(uint32_t cluster) const { | |||||
| return cluster > m_lastCluster; | |||||
| } | |||||
| //---------------------------------------------------------------------------- | |||||
| // 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; | |||||
| #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; | |||||
| #endif // USE_MULTI_BLOCK_IO | |||||
| }; | |||||
| #endif // FatVolume |
| * along with the FatLib Library. If not, see | * along with the FatLib Library. If not, see | ||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include <avr/pgmspace.h> | |||||
| #include <FmtNumber.h> | |||||
| #include "FmtNumber.h" | |||||
| // Use Stimmer div/mod 10 on avr | // Use Stimmer div/mod 10 on avr | ||||
| #ifdef __AVR__ | #ifdef __AVR__ | ||||
| #include <avr/pgmspace.h> | |||||
| #define USE_STIMMER | #define USE_STIMMER | ||||
| #endif // __AVR__ | #endif // __AVR__ | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| } | } | ||||
| */ | */ | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| #ifndef DOXYGEN_SHOULD_SKIP_THIS | |||||
| #ifdef __AVR__ | |||||
| static const float m[] PROGMEM = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32}; | static const float m[] PROGMEM = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32}; | ||||
| static const float p[] PROGMEM = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32}; | static const float p[] PROGMEM = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32}; | ||||
| #else // __AVR__ | |||||
| static const float m[] = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32}; | |||||
| static const float p[] = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32}; | |||||
| #endif // __AVR__ | |||||
| #endif // DOXYGEN_SHOULD_SKIP_THIS | |||||
| // scale float v by power of ten. return v*10^n | // scale float v by power of ten. return v*10^n | ||||
| float scale10(float v, int8_t n) { | float scale10(float v, int8_t n) { | ||||
| const float *s; | const float *s; | ||||
| } | } | ||||
| n &= 63; | n &= 63; | ||||
| for (uint8_t i = 0; n; n >>= 1, i++) { | 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]; | |||||
| #endif // __AVR__ | |||||
| } | } | ||||
| return v; | return v; | ||||
| } | } |
| */ | */ | ||||
| #ifndef FmtNumber_h | #ifndef FmtNumber_h | ||||
| #define FmtNumber_h | #define FmtNumber_h | ||||
| #include <Arduino.h> | |||||
| #include <ctype.h> | |||||
| #include <math.h> | |||||
| #include <stdint.h> | |||||
| char* fmtDec(uint16_t n, char* p); | char* fmtDec(uint16_t n, char* p); | ||||
| char* fmtDec(uint32_t n, char* p); | char* fmtDec(uint32_t n, char* p); | ||||
| char* fmtFloat(float value, char* p, uint8_t prec); | char* fmtFloat(float value, char* p, uint8_t prec); |
| #ifndef SoftSPI_h | #ifndef SoftSPI_h | ||||
| #define SoftSPI_h | #define SoftSPI_h | ||||
| #include <DigitalPin.h> | |||||
| #include "DigitalPin.h" | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Nop for timing. */ | /** Nop for timing. */ | ||||
| #define nop asm volatile ("nop\n\t") | #define nop asm volatile ("nop\n\t") |
| * along with the Arduino RamDisk Library. If not, see | * along with the Arduino RamDisk Library. If not, see | ||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include <Arduino.h> | |||||
| #include <StdioStream.h> | |||||
| #include <utility/FmtNumber.h> | |||||
| #include "StdioStream.h" | |||||
| #include "FmtNumber.h" | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| int StdioStream::fclose() { | int StdioStream::fclose() { | ||||
| int rtn = 0; | int rtn = 0; | ||||
| if (m_flags & F_SWR) { | if (m_flags & F_SWR) { | ||||
| if (!flushBuf()) rtn = EOF; | if (!flushBuf()) rtn = EOF; | ||||
| } | } | ||||
| if (!SdBaseFile::close()) rtn = EOF; | |||||
| if (!FatFile::close()) rtn = EOF; | |||||
| m_r = 0; | m_r = 0; | ||||
| m_w = 0; | m_w = 0; | ||||
| m_flags = 0; | m_flags = 0; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| int StdioStream::fflush() { | int StdioStream::fflush() { | ||||
| if ((m_flags & (F_SWR | F_SRW)) && !(m_flags & F_SRD)) { | if ((m_flags & (F_SWR | F_SRW)) && !(m_flags & F_SRD)) { | ||||
| if (flushBuf() && SdBaseFile::sync()) return 0; | |||||
| if (flushBuf() && FatFile::sync()) return 0; | |||||
| } | } | ||||
| return EOF; | return EOF; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| char* StdioStream::fgets(char* str, int num, size_t* len) { | |||||
| char* StdioStream::fgets(char* str, size_t num, size_t* len) { | |||||
| char* s = str; | char* s = str; | ||||
| size_t n; | size_t n; | ||||
| if (num-- <= 0) return 0; | |||||
| if (num-- == 0) return 0; | |||||
| while (num) { | while (num) { | ||||
| if ((n = m_r) == 0) { | if ((n = m_r) == 0) { | ||||
| if (!fillBuf()) { | if (!fillBuf()) { | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool StdioStream::fopen(const char* filename, const char* mode) { | bool StdioStream::fopen(const char* filename, const char* mode) { | ||||
| uint8_t oflags; | |||||
| uint8_t oflag; | |||||
| switch (*mode++) { | switch (*mode++) { | ||||
| case 'a': | case 'a': | ||||
| m_flags = F_SWR; | m_flags = F_SWR; | ||||
| oflags = O_WRITE | O_CREAT | O_APPEND | O_AT_END; | |||||
| oflag = O_WRITE | O_CREAT | O_APPEND | O_AT_END; | |||||
| break; | break; | ||||
| case 'r': | case 'r': | ||||
| m_flags = F_SRD; | m_flags = F_SRD; | ||||
| oflags = O_READ; | |||||
| oflag = O_READ; | |||||
| break; | break; | ||||
| case 'w': | case 'w': | ||||
| m_flags = F_SWR; | m_flags = F_SWR; | ||||
| oflags = O_WRITE | O_CREAT | O_TRUNC; | |||||
| oflag = O_WRITE | O_CREAT | O_TRUNC; | |||||
| break; | break; | ||||
| default: | default: | ||||
| switch (*mode++) { | switch (*mode++) { | ||||
| case '+': | case '+': | ||||
| m_flags |= F_SRW; | m_flags |= F_SRW; | ||||
| oflags |= O_RDWR; | |||||
| oflag |= O_RDWR; | |||||
| break; | break; | ||||
| case 'b': | case 'b': | ||||
| break; | break; | ||||
| case 'x': | case 'x': | ||||
| oflags |= O_EXCL; | |||||
| oflag |= O_EXCL; | |||||
| break; | break; | ||||
| default: | default: | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| } | } | ||||
| if ((oflags & O_EXCL) && !(oflags & O_WRITE)) goto fail; | |||||
| if (!SdBaseFile::open(filename, oflags)) goto fail; | |||||
| if ((oflag & O_EXCL) && !(oflag & O_WRITE)) goto fail; | |||||
| if (!FatFile::open(filename, oflag)) goto fail; | |||||
| m_r = 0; | m_r = 0; | ||||
| m_w = 0; | m_w = 0; | ||||
| m_p = m_buf; | m_p = m_buf; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| pos += offset; | pos += offset; | ||||
| if (!SdBaseFile::seekCur(pos)) { | |||||
| if (!FatFile::seekCur(pos)) { | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| break; | break; | ||||
| case SEEK_SET: | case SEEK_SET: | ||||
| if (!SdBaseFile::seekSet(offset)) { | |||||
| if (!FatFile::seekSet(offset)) { | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| break; | break; | ||||
| case SEEK_END: | case SEEK_END: | ||||
| if (!SdBaseFile::seekEnd(offset)) { | |||||
| if (!FatFile::seekEnd(offset)) { | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| int32_t StdioStream::ftell() { | int32_t StdioStream::ftell() { | ||||
| uint32_t pos = SdBaseFile::curPosition(); | |||||
| uint32_t pos = FatFile::curPosition(); | |||||
| if (m_flags & F_SRD) { | if (m_flags & F_SRD) { | ||||
| if (m_r > pos) return -1L; | if (m_r > pos) return -1L; | ||||
| pos -= m_r; | pos -= m_r; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| size_t StdioStream::print(const __FlashStringHelper *str) { | size_t StdioStream::print(const __FlashStringHelper *str) { | ||||
| const char PROGMEM *p = (const char PROGMEM *)str; | |||||
| const char *p = (const char PROGMEM *)str; | |||||
| uint8_t c; | uint8_t c; | ||||
| while (c = pgm_read_byte(p)) { | |||||
| while ((c = pgm_read_byte(p))) { | |||||
| if (putc(c) < 0) return 0; | if (putc(c) < 0) return 0; | ||||
| p++; | p++; | ||||
| } | } | ||||
| n = -n; | n = -n; | ||||
| s = 1; | s = 1; | ||||
| } | } | ||||
| printDec((unsigned char)n); | |||||
| return printDec((unsigned char)n); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| int StdioStream::printDec(int16_t n) { | int StdioStream::printDec(int16_t n) { | ||||
| if (m_flags & F_SWR) { | if (m_flags & F_SWR) { | ||||
| if (!flushBuf()) return false; | if (!flushBuf()) return false; | ||||
| } | } | ||||
| SdBaseFile::seekSet(0); | |||||
| FatFile::seekSet(0); | |||||
| m_r = 0; | m_r = 0; | ||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| m_p = m_buf + UNGETC_BUF_SIZE; | m_p = m_buf + UNGETC_BUF_SIZE; | ||||
| int nr = SdBaseFile::read(m_p, sizeof(m_buf) - UNGETC_BUF_SIZE); | |||||
| int nr = FatFile::read(m_p, sizeof(m_buf) - UNGETC_BUF_SIZE); | |||||
| if (nr <= 0) { | if (nr <= 0) { | ||||
| m_flags |= nr < 0 ? F_ERR : F_EOF; | m_flags |= nr < 0 ? F_ERR : F_EOF; | ||||
| m_r = 0; | m_r = 0; | ||||
| uint8_t n = m_p - m_buf; | uint8_t n = m_p - m_buf; | ||||
| m_p = m_buf; | m_p = m_buf; | ||||
| m_w = sizeof(m_buf); | m_w = sizeof(m_buf); | ||||
| if (SdBaseFile::write(m_buf, n) == n) return true; | |||||
| if (FatFile::write(m_buf, n) == n) return true; | |||||
| m_flags |= F_ERR; | m_flags |= F_ERR; | ||||
| return false; | return false; | ||||
| } | } |
| * \brief StdioStream class | * \brief StdioStream class | ||||
| */ | */ | ||||
| #include <limits.h> | #include <limits.h> | ||||
| #include <Arduino.h> | |||||
| #include <SdFat.h> | |||||
| #include <SdBaseFile.h> | |||||
| #include "FatFile.h" | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Total size of stream buffer. The entire buffer is used for output. | /** Total size of stream buffer. The entire buffer is used for output. | ||||
| * During input UNGETC_BUF_SIZE of this space is reserved for ungetc. | * During input UNGETC_BUF_SIZE of this space is reserved for ungetc. | ||||
| * | * | ||||
| * StdioStream does not support subdirectories or long file names. | * StdioStream does not support subdirectories or long file names. | ||||
| */ | */ | ||||
| class StdioStream : private SdBaseFile { | |||||
| class StdioStream : private FatFile { | |||||
| public: | public: | ||||
| /** Constructor | /** Constructor | ||||
| * | * | ||||
| * occurs during the operation, the array contents are indeterminate | * occurs during the operation, the array contents are indeterminate | ||||
| * and a null pointer is returned. | * and a null pointer is returned. | ||||
| */ | */ | ||||
| char* fgets(char* str, int num, size_t* len = 0); | |||||
| char* fgets(char* str, size_t num, size_t* len = 0); | |||||
| //---------------------------------------------------------------------------- | //---------------------------------------------------------------------------- | ||||
| /** Open a stream. | /** Open a stream. | ||||
| * | * | ||||
| * function or to a file positioning function (fseek, or rewind), and | * function or to a file positioning function (fseek, or rewind), and | ||||
| * input shall not be directly followed by output without an intervening | * input shall not be directly followed by output without an intervening | ||||
| * call to a file positioning function, unless the input operation | * call to a file positioning function, unless the input operation | ||||
| * encounters endof-file. | |||||
| * encounters end-of-file. | |||||
| * | * | ||||
| * \return true for success or false for failure. | * \return true for success or false for failure. | ||||
| */ | */ | ||||
| * \param[in] count the number of elements to be written. | * \param[in] count the number of elements to be written. | ||||
| * | * | ||||
| * \return number of elements successfully written. if this number is | * \return number of elements successfully written. if this number is | ||||
| * less than count, an error has occured. If size or count is zero, | |||||
| * less than count, an error has occurred. If size or count is zero, | |||||
| * fwrite returns zero. | * fwrite returns zero. | ||||
| */ | */ | ||||
| size_t fwrite(const void * ptr, size_t size, size_t count); | size_t fwrite(const void * ptr, size_t size, size_t count); | ||||
| //---------------------------------------------------------------------------- | //---------------------------------------------------------------------------- | ||||
| /** Get a byte from the stream. | /** Get a byte from the stream. | ||||
| * | * | ||||
| * getc and fgetc are equivalent but getc is inline so it is faster but | |||||
| * getc and fgetc are equivalent but getc is in-line so it is faster but | |||||
| * require more flash memory. | * require more flash memory. | ||||
| * | * | ||||
| * \return If the end-of-file indicator for the stream is set, or if the | * \return If the end-of-file indicator for the stream is set, or if the | ||||
| //---------------------------------------------------------------------------- | //---------------------------------------------------------------------------- | ||||
| /** Write a byte to a stream. | /** Write a byte to a stream. | ||||
| * | * | ||||
| * putc and fputc are equivalent but putc is inline so it is faster but | |||||
| * putc and fputc are equivalent but putc is in-line so it is faster but | |||||
| * require more flash memory. | * require more flash memory. | ||||
| * | * | ||||
| * \param[in] c the byte to be written (converted to an unsigned char). | * \param[in] c the byte to be written (converted to an unsigned char). | ||||
| * | * | ||||
| * \param[in] c the byte (converted to an unsigned char) to be pushed back. | * \param[in] c the byte (converted to an unsigned char) to be pushed back. | ||||
| * | * | ||||
| * One character of pushback is guaranteed. If the ungetc function is | |||||
| * One character of push-back is guaranteed. If the ungetc function is | |||||
| * called too many times without an intervening read or file positioning | * called too many times without an intervening read or file positioning | ||||
| * operation on that stream, the operation may fail. | * operation on that stream, the operation may fail. | ||||
| * | * |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| /* FatLib Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | |||||
| * This file is part of the FatLib Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino SdFat Library. If not, see | |||||
| * along with the FatLib Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #ifndef bufstream_h | #ifndef bufstream_h | ||||
| * \file | * \file | ||||
| * \brief \ref ibufstream and \ref obufstream classes | * \brief \ref ibufstream and \ref obufstream classes | ||||
| */ | */ | ||||
| #include <iostream.h> | |||||
| #include <string.h> | |||||
| #include "iostream.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class ibufstream | * \class ibufstream |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| /* FatLib Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | |||||
| * This file is part of the FatLib Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino SdFat Library. If not, see | |||||
| * along with the FatLib Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include <SdFat.h> | |||||
| #include "fstream.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| /// @cond SHOW_PROTECTED | /// @cond SHOW_PROTECTED | ||||
| int16_t SdStreamBase::getch() { | |||||
| int16_t FatStreamBase::getch() { | |||||
| uint8_t c; | uint8_t c; | ||||
| int8_t s = read(&c, 1); | int8_t s = read(&c, 1); | ||||
| if (s != 1) { | if (s != 1) { | ||||
| return '\r'; | return '\r'; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void SdStreamBase::open(const char* path, ios::openmode mode) { | |||||
| void FatStreamBase::open(const char* path, ios::openmode mode) { | |||||
| uint8_t flags; | uint8_t flags; | ||||
| switch (mode & (app | in | out | trunc)) { | switch (mode & (app | in | out | trunc)) { | ||||
| case app | in: | case app | in: | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| if (mode & ios::ate) flags |= O_AT_END; | if (mode & ios::ate) flags |= O_AT_END; | ||||
| if (!SdBaseFile::open(path, flags)) goto fail; | |||||
| if (!FatFile::open(path, flags)) goto fail; | |||||
| setmode(mode); | setmode(mode); | ||||
| clear(); | clear(); | ||||
| return; | return; | ||||
| fail: | fail: | ||||
| SdBaseFile::close(); | |||||
| FatFile::close(); | |||||
| setstate(failbit); | setstate(failbit); | ||||
| return; | return; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void SdStreamBase::putch(char c) { | |||||
| void FatStreamBase::putch(char c) { | |||||
| if (c == '\n' && !(getmode() & ios::binary)) { | if (c == '\n' && !(getmode() & ios::binary)) { | ||||
| write('\r'); | write('\r'); | ||||
| } | } | ||||
| write(c); | write(c); | ||||
| if (writeError) setstate(badbit); | |||||
| if (getWriteError()) setstate(badbit); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void SdStreamBase::putstr(const char* str) { | |||||
| void FatStreamBase::putstr(const char* str) { | |||||
| size_t n = 0; | size_t n = 0; | ||||
| while (1) { | while (1) { | ||||
| char c = str[n]; | char c = str[n]; | ||||
| } | } | ||||
| n++; | n++; | ||||
| } | } | ||||
| if (writeError) setstate(badbit); | |||||
| if (getWriteError()) setstate(badbit); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Internal do not use | /** Internal do not use | ||||
| * \param[in] off | * \param[in] off | ||||
| * \param[in] way | * \param[in] way | ||||
| */ | */ | ||||
| bool SdStreamBase::seekoff(off_type off, seekdir way) { | |||||
| bool FatStreamBase::seekoff(off_type off, seekdir way) { | |||||
| pos_type pos; | pos_type pos; | ||||
| switch (way) { | switch (way) { | ||||
| case beg: | case beg: | ||||
| /** Internal do not use | /** Internal do not use | ||||
| * \param[in] pos | * \param[in] pos | ||||
| */ | */ | ||||
| bool SdStreamBase::seekpos(pos_type pos) { | |||||
| bool FatStreamBase::seekpos(pos_type pos) { | |||||
| return seekSet(pos); | return seekSet(pos); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| int SdStreamBase::write(const void* buf, size_t n) { | |||||
| return SdBaseFile::write(buf, n); | |||||
| int FatStreamBase::write(const void* buf, size_t n) { | |||||
| return FatFile::write(buf, n); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void SdStreamBase::write(char c) { | |||||
| void FatStreamBase::write(char c) { | |||||
| write(&c, 1); | write(&c, 1); | ||||
| } | } | ||||
| /// @endcond | /// @endcond |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| /* FatLib Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | |||||
| * This file is part of the FatLib Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino SdFat Library. If not, see | |||||
| * along with the FatLib Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #ifndef SdStream_h | |||||
| #define SdStream_h | |||||
| #ifndef fstream_h | |||||
| #define fstream_h | |||||
| /** | /** | ||||
| * \file | * \file | ||||
| * \brief \ref fstream, \ref ifstream, and \ref ofstream classes | * \brief \ref fstream, \ref ifstream, and \ref ofstream classes | ||||
| */ | */ | ||||
| #include <SdBaseFile.h> | |||||
| #include <iostream.h> | |||||
| #include "FatFile.h" | |||||
| #include "iostream.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class SdStreamBase | |||||
| * \brief Base class for SD streams | |||||
| * \class FatStreamBase | |||||
| * \brief Base class for C++ style streams | |||||
| */ | */ | ||||
| class SdStreamBase : protected SdBaseFile, virtual public ios { | |||||
| class FatStreamBase : protected FatFile, virtual public ios { | |||||
| protected: | protected: | ||||
| /// @cond SHOW_PROTECTED | /// @cond SHOW_PROTECTED | ||||
| int16_t getch(); | int16_t getch(); | ||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class fstream | * \class fstream | ||||
| * \brief SD file input/output stream. | |||||
| * \brief file input/output stream. | |||||
| */ | */ | ||||
| class fstream : public iostream, SdStreamBase { | |||||
| class fstream : public iostream, FatStreamBase { | |||||
| public: | public: | ||||
| using iostream::peek; | using iostream::peek; | ||||
| fstream() {} | fstream() {} | ||||
| */ | */ | ||||
| void clear(iostate state = goodbit) { | void clear(iostate state = goodbit) { | ||||
| ios::clear(state); | ios::clear(state); | ||||
| SdBaseFile::writeError = false; | |||||
| FatFile::clearWriteError(); | |||||
| } | } | ||||
| /** Close a file and force cached data and directory information | /** Close a file and force cached data and directory information | ||||
| * to be written to the storage device. | * to be written to the storage device. | ||||
| */ | */ | ||||
| void close() {SdBaseFile::close();} | |||||
| void close() {FatFile::close();} | |||||
| /** Open a fstream | /** Open a fstream | ||||
| * \param[in] path file to open | * \param[in] path file to open | ||||
| * \param[in] mode open mode | * \param[in] mode open mode | ||||
| * create text file for update, writing at end of file. | * create text file for update, writing at end of file. | ||||
| */ | */ | ||||
| void open(const char* path, openmode mode = in | out) { | void open(const char* path, openmode mode = in | out) { | ||||
| SdStreamBase::open(path, mode); | |||||
| FatStreamBase::open(path, mode); | |||||
| } | } | ||||
| /** \return True if stream is open else false. */ | /** \return True if stream is open else false. */ | ||||
| bool is_open () {return SdBaseFile::isOpen();} | |||||
| bool is_open () {return FatFile::isOpen();} | |||||
| protected: | protected: | ||||
| /// @cond SHOW_PROTECTED | /// @cond SHOW_PROTECTED | ||||
| /** Internal - do not use | /** Internal - do not use | ||||
| * \return | * \return | ||||
| */ | */ | ||||
| int16_t getch() {return SdStreamBase::getch();} | |||||
| int16_t getch() {return FatStreamBase::getch();} | |||||
| /** Internal - do not use | /** Internal - do not use | ||||
| * \param[out] pos | * \param[out] pos | ||||
| */ | */ | ||||
| void getpos(FatPos_t* pos) {SdBaseFile::getpos(pos);} | |||||
| void getpos(FatPos_t* pos) {FatFile::getpos(pos);} | |||||
| /** Internal - do not use | /** Internal - do not use | ||||
| * \param[in] c | * \param[in] c | ||||
| */ | */ | ||||
| void putch(char c) {SdStreamBase::putch(c);} | |||||
| void putch(char c) {FatStreamBase::putch(c);} | |||||
| /** Internal - do not use | /** Internal - do not use | ||||
| * \param[in] str | * \param[in] str | ||||
| */ | */ | ||||
| void putstr(const char *str) {SdStreamBase::putstr(str);} | |||||
| void putstr(const char *str) {FatStreamBase::putstr(str);} | |||||
| /** Internal - do not use | /** Internal - do not use | ||||
| * \param[in] pos | * \param[in] pos | ||||
| */ | */ | ||||
| bool seekoff(off_type off, seekdir way) { | bool seekoff(off_type off, seekdir way) { | ||||
| return SdStreamBase::seekoff(off, way); | |||||
| return FatStreamBase::seekoff(off, way); | |||||
| } | } | ||||
| bool seekpos(pos_type pos) {return SdStreamBase::seekpos(pos);} | |||||
| void setpos(FatPos_t* pos) {SdBaseFile::setpos(pos);} | |||||
| bool sync() {return SdStreamBase::sync();} | |||||
| pos_type tellpos() {return SdStreamBase::curPosition();} | |||||
| bool seekpos(pos_type pos) {return FatStreamBase::seekpos(pos);} | |||||
| void setpos(FatPos_t* pos) {FatFile::setpos(pos);} | |||||
| bool sync() {return FatStreamBase::sync();} | |||||
| pos_type tellpos() {return FatStreamBase::curPosition();} | |||||
| /// @endcond | /// @endcond | ||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class ifstream | * \class ifstream | ||||
| * \brief SD file input stream. | |||||
| * \brief file input stream. | |||||
| */ | */ | ||||
| class ifstream : public istream, SdStreamBase { | |||||
| class ifstream : public istream, FatStreamBase { | |||||
| public: | public: | ||||
| using istream::peek; | using istream::peek; | ||||
| ifstream() {} | ifstream() {} | ||||
| /** Close a file and force cached data and directory information | /** Close a file and force cached data and directory information | ||||
| * to be written to the storage device. | * to be written to the storage device. | ||||
| */ | */ | ||||
| void close() {SdBaseFile::close();} | |||||
| void close() {FatFile::close();} | |||||
| /** \return True if stream is open else false. */ | /** \return True if stream is open else false. */ | ||||
| bool is_open() {return SdBaseFile::isOpen();} | |||||
| bool is_open() {return FatFile::isOpen();} | |||||
| /** Open an ifstream | /** Open an ifstream | ||||
| * \param[in] path file to open | * \param[in] path file to open | ||||
| * \param[in] mode open mode | * \param[in] mode open mode | ||||
| * \a mode See fstream::open() for valid modes. | * \a mode See fstream::open() for valid modes. | ||||
| */ | */ | ||||
| void open(const char* path, openmode mode = in) { | void open(const char* path, openmode mode = in) { | ||||
| SdStreamBase::open(path, mode | in); | |||||
| FatStreamBase::open(path, mode | in); | |||||
| } | } | ||||
| protected: | protected: | ||||
| /** Internal - do not use | /** Internal - do not use | ||||
| * \return | * \return | ||||
| */ | */ | ||||
| int16_t getch() {return SdStreamBase::getch();} | |||||
| int16_t getch() {return FatStreamBase::getch();} | |||||
| /** Internal - do not use | /** Internal - do not use | ||||
| * \param[out] pos | * \param[out] pos | ||||
| */ | */ | ||||
| void getpos(FatPos_t* pos) {SdBaseFile::getpos(pos);} | |||||
| void getpos(FatPos_t* pos) {FatFile::getpos(pos);} | |||||
| /** Internal - do not use | /** Internal - do not use | ||||
| * \param[in] pos | * \param[in] pos | ||||
| */ | */ | ||||
| bool seekoff(off_type off, seekdir way) { | bool seekoff(off_type off, seekdir way) { | ||||
| return SdStreamBase::seekoff(off, way); | |||||
| return FatStreamBase::seekoff(off, way); | |||||
| } | } | ||||
| bool seekpos(pos_type pos) {return SdStreamBase::seekpos(pos);} | |||||
| void setpos(FatPos_t* pos) {SdBaseFile::setpos(pos);} | |||||
| pos_type tellpos() {return SdStreamBase::curPosition();} | |||||
| bool seekpos(pos_type pos) {return FatStreamBase::seekpos(pos);} | |||||
| void setpos(FatPos_t* pos) {FatFile::setpos(pos);} | |||||
| pos_type tellpos() {return FatStreamBase::curPosition();} | |||||
| /// @endcond | /// @endcond | ||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class ofstream | * \class ofstream | ||||
| * \brief SD card output stream. | |||||
| * \brief file output stream. | |||||
| */ | */ | ||||
| class ofstream : public ostream, SdStreamBase { | |||||
| class ofstream : public ostream, FatStreamBase { | |||||
| public: | public: | ||||
| ofstream() {} | ofstream() {} | ||||
| /** Constructor with open | /** Constructor with open | ||||
| */ | */ | ||||
| void clear(iostate state = goodbit) { | void clear(iostate state = goodbit) { | ||||
| ios::clear(state); | ios::clear(state); | ||||
| SdBaseFile::writeError = false; | |||||
| FatFile::clearWriteError(); | |||||
| } | } | ||||
| /** Close a file and force cached data and directory information | /** Close a file and force cached data and directory information | ||||
| * to be written to the storage device. | * to be written to the storage device. | ||||
| */ | */ | ||||
| void close() {SdBaseFile::close();} | |||||
| void close() {FatFile::close();} | |||||
| /** Open an ofstream | /** Open an ofstream | ||||
| * \param[in] path file to open | * \param[in] path file to open | ||||
| * \param[in] mode open mode | * \param[in] mode open mode | ||||
| * \a mode See fstream::open() for valid modes. | * \a mode See fstream::open() for valid modes. | ||||
| */ | */ | ||||
| void open(const char* path, openmode mode = out) { | void open(const char* path, openmode mode = out) { | ||||
| SdStreamBase::open(path, mode | out); | |||||
| FatStreamBase::open(path, mode | out); | |||||
| } | } | ||||
| /** \return True if stream is open else false. */ | /** \return True if stream is open else false. */ | ||||
| bool is_open() {return SdBaseFile::isOpen();} | |||||
| bool is_open() {return FatFile::isOpen();} | |||||
| protected: | protected: | ||||
| /// @cond SHOW_PROTECTED | /// @cond SHOW_PROTECTED | ||||
| * Internal do not use | * Internal do not use | ||||
| * \param[in] c | * \param[in] c | ||||
| */ | */ | ||||
| void putch(char c) {SdStreamBase::putch(c);} | |||||
| void putstr(const char* str) {SdStreamBase::putstr(str);} | |||||
| void putch(char c) {FatStreamBase::putch(c);} | |||||
| void putstr(const char* str) {FatStreamBase::putstr(str);} | |||||
| bool seekoff(off_type off, seekdir way) { | bool seekoff(off_type off, seekdir way) { | ||||
| return SdStreamBase::seekoff(off, way); | |||||
| return FatStreamBase::seekoff(off, way); | |||||
| } | } | ||||
| bool seekpos(pos_type pos) {return SdStreamBase::seekpos(pos);} | |||||
| bool seekpos(pos_type pos) {return FatStreamBase::seekpos(pos);} | |||||
| /** | /** | ||||
| * Internal do not use | * Internal do not use | ||||
| * \param[in] b | * \param[in] b | ||||
| */ | */ | ||||
| bool sync() {return SdStreamBase::sync();} | |||||
| pos_type tellpos() {return SdStreamBase::curPosition();} | |||||
| bool sync() {return FatStreamBase::sync();} | |||||
| pos_type tellpos() {return FatStreamBase::curPosition();} | |||||
| /// @endcond | /// @endcond | ||||
| }; | }; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| #endif // SdStream_h | |||||
| #endif // fstream_h |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| /* FatLib Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | |||||
| * This file is part of the FatLib Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino SdFat Library. If not, see | |||||
| * along with the FatLib Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #ifndef ios_h | #ifndef ios_h | ||||
| #define ios_h | #define ios_h | ||||
| #include <SdBaseFile.h> | |||||
| #include "FatFile.h" | |||||
| /** | /** | ||||
| * \file | * \file | ||||
| * \brief \ref ios_base and \ref ios classes | * \brief \ref ios_base and \ref ios classes |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| /* FatLib Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | |||||
| * This file is part of the FatLib Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino SdFat Library. If not, see | |||||
| * along with the FatLib Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #ifndef iostream_h | #ifndef iostream_h | ||||
| * \file | * \file | ||||
| * \brief \ref iostream class | * \brief \ref iostream class | ||||
| */ | */ | ||||
| #include <istream.h> | |||||
| #include <ostream.h> | |||||
| #include "istream.h" | |||||
| #include "ostream.h" | |||||
| /** Skip white space | /** Skip white space | ||||
| * \param[in] is the Stream | * \param[in] is the Stream | ||||
| * \return The stream | * \return The stream |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| /* FatLib Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | |||||
| * This file is part of the FatLib Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino SdFat Library. If not, see | |||||
| * along with the FatLib Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include <ctype.h> | |||||
| #include <float.h> | #include <float.h> | ||||
| #include <istream.h> | |||||
| #include "istream.h" | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * 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 istream::get() { | int istream::get() { | ||||
| int c; | int c; | ||||
| m_gcount = 0; | m_gcount = 0; | ||||
| return c; | return c; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Extract a character if one is available. | |||||
| * | |||||
| * \param[out] c location to receive the extracted character. | |||||
| * | |||||
| * \return always returns *this. A failure is indicated by the stream state. | |||||
| */ | |||||
| istream& istream::get(char& c) { | istream& istream::get(char& c) { | ||||
| int tmp = get(); | int tmp = get(); | ||||
| if (tmp >= 0) c = tmp; | if (tmp >= 0) c = tmp; | ||||
| return *this; | return *this; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Extract characters. | |||||
| * | |||||
| * \param[out] str Location to receive extracted characters. | |||||
| * \param[in] n Size of str. | |||||
| * \param[in] delim Delimiter | |||||
| * | |||||
| * Characters are extracted until extraction fails, n is less than 1, | |||||
| * n-1 characters are extracted, or the next character equals | |||||
| * \a delim (delim is not extracted). If no characters are extracted | |||||
| * 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& istream::get(char *str, streamsize n, char delim) { | istream& istream::get(char *str, streamsize n, char delim) { | ||||
| int c; | int c; | ||||
| FatPos_t pos; | FatPos_t pos; | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Extract characters | |||||
| * | |||||
| * \param[out] str Location to receive extracted characters. | |||||
| * \param[in] n Size of str. | |||||
| * \param[in] delim Delimiter | |||||
| * | |||||
| * Characters are extracted until extraction fails, | |||||
| * the next character equals \a delim (delim is extracted), or n-1 | |||||
| * characters are extracted. | |||||
| * | |||||
| * The failbit is set if no characters are extracted or n-1 characters | |||||
| * are extracted. If end-of-file occurs the eofbit is set. | |||||
| * | |||||
| * \return always returns *this. A failure is indicated by the stream state. | |||||
| */ | |||||
| istream& istream::getline(char *str, streamsize n, char delim) { | istream& istream::getline(char *str, streamsize n, char delim) { | ||||
| FatPos_t pos; | FatPos_t pos; | ||||
| int c; | int c; | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * | |||||
| */ | |||||
| void istream::getStr(char *str) { | void istream::getStr(char *str) { | ||||
| FatPos_t pos; | FatPos_t pos; | ||||
| uint16_t i = 0; | uint16_t i = 0; | ||||
| width(0); | width(0); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Extract characters and discard them. | |||||
| * | |||||
| * \param[in] n maximum number of characters to ignore. | |||||
| * \param[in] delim Delimiter. | |||||
| * | |||||
| * Characters are extracted until extraction fails, \a n characters | |||||
| * are extracted, or the next input character equals \a delim | |||||
| * (the delimiter is extracted). If end-of-file occurs the eofbit is set. | |||||
| * | |||||
| * Failures are indicated by the state of the stream. | |||||
| * | |||||
| * \return *this | |||||
| * | |||||
| */ | |||||
| istream& istream::ignore(streamsize n, int delim) { | istream& istream::ignore(streamsize n, int delim) { | ||||
| int c; | int c; | ||||
| m_gcount = 0; | m_gcount = 0; | ||||
| return *this; | return *this; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Return the next available character without consuming it. | |||||
| * | |||||
| * \return The character if the stream state is good else -1; | |||||
| * | |||||
| */ | |||||
| int istream::peek() { | int istream::peek() { | ||||
| int16_t c; | int16_t c; | ||||
| FatPos_t pos; | FatPos_t pos; |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| /* FatLib Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | |||||
| * This file is part of the FatLib Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino SdFat Library. If not, see | |||||
| * along with the FatLib Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #ifndef istream_h | #ifndef istream_h | ||||
| * \file | * \file | ||||
| * \brief \ref istream class | * \brief \ref istream class | ||||
| */ | */ | ||||
| #include <ios.h> | |||||
| #include "ios.h" | |||||
| /** | /** | ||||
| * \class istream | * \class istream | ||||
| * input function. | * 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(); | int get(); | ||||
| /** | |||||
| * Extract a character if one is available. | |||||
| * | |||||
| * \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); | istream& get(char& ch); | ||||
| /** | |||||
| * Extract characters. | |||||
| * | |||||
| * \param[out] str Location to receive extracted characters. | |||||
| * \param[in] n Size of str. | |||||
| * \param[in] delim Delimiter | |||||
| * | |||||
| * Characters are extracted until extraction fails, n is less than 1, | |||||
| * n-1 characters are extracted, or the next character equals | |||||
| * \a delim (delim is not extracted). If no characters are extracted | |||||
| * 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'); | istream& get(char *str, streamsize n, char delim = '\n'); | ||||
| istream& getline(char *str, streamsize count, char delim = '\n'); | |||||
| /** | |||||
| * Extract characters | |||||
| * | |||||
| * \param[out] str Location to receive extracted characters. | |||||
| * \param[in] n Size of str. | |||||
| * \param[in] delim Delimiter | |||||
| * | |||||
| * Characters are extracted until extraction fails, | |||||
| * the next character equals \a delim (delim is extracted), or n-1 | |||||
| * characters are extracted. | |||||
| * | |||||
| * The failbit is set if no characters are extracted or n-1 characters | |||||
| * are extracted. If end-of-file occurs the eofbit is set. | |||||
| * | |||||
| * \return always returns *this. A failure is indicated by the stream state. | |||||
| */ | |||||
| istream& getline(char *str, streamsize n, char delim = '\n'); | |||||
| /** | |||||
| * Extract characters and discard them. | |||||
| * | |||||
| * \param[in] n maximum number of characters to ignore. | |||||
| * \param[in] delim Delimiter. | |||||
| * | |||||
| * Characters are extracted until extraction fails, \a n characters | |||||
| * are extracted, or the next input character equals \a delim | |||||
| * (the delimiter is extracted). If end-of-file occurs the eofbit is set. | |||||
| * | |||||
| * Failures are indicated by the state of the stream. | |||||
| * | |||||
| * \return *this | |||||
| * | |||||
| */ | |||||
| istream& ignore(streamsize n = 1, int delim= -1); | istream& ignore(streamsize n = 1, int delim= -1); | ||||
| /** | |||||
| * Return the next available character without consuming it. | |||||
| * | |||||
| * \return The character if the stream state is good else -1; | |||||
| * | |||||
| */ | |||||
| int peek(); | int peek(); | ||||
| // istream& read(char *str, streamsize count); | // istream& read(char *str, streamsize count); | ||||
| // streamsize readsome(char *str, streamsize count); | // streamsize readsome(char *str, streamsize count); |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2012 by William Greiman | |||||
| /* FatLib Library | |||||
| * Copyright (C) 2013 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | |||||
| * This file is part of the FatLib Library | |||||
| * | * | ||||
| * This Library is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with the Arduino SdFat Library. If not, see | |||||
| * along with the FatLib Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include <ostream.h> | |||||
| #include <string.h> | |||||
| #include "ostream.h" | |||||
| #ifndef PSTR | #ifndef PSTR | ||||
| #define PSTR(x) x | #define PSTR(x) x | ||||
| #endif | #endif | ||||
| putPgm(PSTR("BIG FLT")); | putPgm(PSTR("BIG FLT")); | ||||
| return; | return; | ||||
| } | } | ||||
| // round up and separate in and fraction parts | |||||
| // round up and separate int and fraction parts | |||||
| for (uint8_t i = 0; i < nd; ++i) round *= 0.1; | for (uint8_t i = 0; i < nd; ++i) round *= 0.1; | ||||
| n += round; | n += round; | ||||
| uint32_t intPart = n; | uint32_t intPart = n; |