| #ifndef UserDataType_h | |||||
| #define UserDataType_h | |||||
| const uint8_t ACCEL_DIM = 3; | |||||
| struct data_t { | |||||
| unsigned long time; | |||||
| int16_t accel[ACCEL_DIM]; | |||||
| }; | |||||
| #endif // UserDataType_h |
| This is a beta version so there may be bugs and compatibility | |||||
| problems. | |||||
| ### Warning: This version has major changes so it may be unstable. | |||||
| Please report problems to the email address listed in the | |||||
| "Bugs and Comments" section of the html documentation. | |||||
| Recent versions of the Arduino IDE have bugs that may cause SdFat-beta to crash. | |||||
| https://forum.arduino.cc/index.php?topic=419264.0 | |||||
| SdFat-beta was tested using with Arduino AVR boards 1.6.11. | |||||
| If you are using IDE 1.6.11 you must also install AVR boards 1.6.11, not | |||||
| 1.6.12 or 1.6.13. | |||||
| If you are Arduino IDE 1.6.11, do Tools > Board > Boards Manager > Arduino AVR Boards > 1.6.11 > Install > Close. | |||||
| The SPI divisor has been replaced by SPISettings. | |||||
| There are two new classes, SdFatEX and SdFatSoftSpiEX. | |||||
| Please read changes.txt and the html documentation for more information. | |||||
| Please report problems as issues. | |||||
| The Arduino SdFat library provides read/write access to FAT16/FAT32 | The Arduino SdFat library provides read/write access to FAT16/FAT32 | ||||
| file systems on SD/SDHC flash cards. | file systems on SD/SDHC flash cards. | ||||
| Please read the html documentation for this library. Start with | Please read the html documentation for this library. Start with | ||||
| html/index.html and read the Main Page. Next go to the Classes tab and | html/index.html and read the Main Page. Next go to the Classes tab and | ||||
| read the documentation for the classes SdFat, SdBaseFile, SdFile, File, | |||||
| StdioStream, ifstream, ofstream, and others. | |||||
| read the documentation for the classes SdFat, SdFatEX, SdBaseFile, | |||||
| SdFile, File, StdioStream, ifstream, ofstream, and others. | |||||
| Please continue by reading the html documentation. | Please continue by reading the html documentation. | ||||
| Updated 23 Jan 2016 | |||||
| Updated 19 Aug 2016 |
| The %SdFat library supports Long %File Names or short 8.3 names. | The %SdFat library supports Long %File Names or short 8.3 names. | ||||
| Edit the SdFatConfig.h file to select short or long file names. | Edit the SdFatConfig.h file to select short or long file names. | ||||
| The main classes in %SdFat are SdFat, SdFatSoftSpi, SdFatLibSpi, | |||||
| The main classes in %SdFat are SdFat, SdFatEX, SdFatSoftSpi, SdFatSoftSpiEX, | |||||
| SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream, | SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream, | ||||
| and \ref ofstream. | and \ref ofstream. | ||||
| 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 SdFat, SdFatEX, SdFatSoftSpi and SdFatSoftSpiEX classes maintain a | |||||
| FAT volume, a current working directory, and simplify initialization | |||||
| of other classes. The SdFat and SdFatEX classes uses a fast custom hardware SPI | |||||
| implementation. The SdFatSoftSpi and SdFatSoftSpiEX classes uses software SPI. | |||||
| the SdFatEX and SdFatSoftSpiEX use extended multi-block I/O for enhanced | |||||
| performance. These classes must have exclusive use of the SPI bus. | |||||
| 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 | ||||
| Opening Long %File Names can be slower than opening Short %File Names. | Opening Long %File Names can be slower than opening Short %File Names. | ||||
| Data read and write performance is not changed by the type of %File Name. | Data read and write performance is not changed by the type of %File Name. | ||||
| Set SD_SPI_CONFIGURATION to enable various SPI options. The SdFatSoftSpi | |||||
| and SdFatLibSpi classes can be enabled. SdFatLibSpi uses the standard | |||||
| Arduino SPI library and SdFatSoftSpi uses software SPI. | |||||
| If the symbol ENABLE_EXTENDED_TRANSFER_CLASS is nonzero, the class SdFatEX | |||||
| will be defined. If the symbol ENABLE_SOFTWARE_SPI_CLASS is also nonzero, | |||||
| the class SdFatSoftSpiEX will be defined. | |||||
| These classes used extended multi-block SD I/O for better performance. | |||||
| the SPI bus may not be shared with other devices in this mode. | |||||
| Set USE_STANDARD_SPI_LIBRARY and ENABLE_SOFTWARE_SPI_CLASS to | |||||
| enable various SPI options. set USE_STANDARD_SPI_LIBRARY to use the standard | |||||
| Arduino SPI library. set ENABLE_SOFTWARE_SPI_CLASS to enable the SdFatSoftSpi | |||||
| class which uses software SPI. | |||||
| To enable SD card CRC checking set USE_SD_CRC nonzero. | To enable SD card CRC checking set USE_SD_CRC nonzero. | ||||
| Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. | Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. | ||||
| FAT12 has not been well tested and requires additional flash. | FAT12 has not been well tested and requires additional flash. | ||||
| Set ENABLE_SPI_TRANSACTIONS nonzero to enable the SPI transaction feature | |||||
| of the standard Arduino SPI library. You must include SPI.h in your | |||||
| programs when ENABLE_SPI_TRANSACTIONS is nonzero. | |||||
| \section SDPath Paths and Working Directories | \section SDPath Paths and Working Directories | ||||
| Relative paths in SdFat are resolved in a manner similar to Windows. | Relative paths in SdFat are resolved in a manner similar to Windows. | ||||
| LongFileName - Example use of openNext, printName, and open by index. | LongFileName - Example use of openNext, printName, and open by index. | ||||
| LowLatencyLogger - A modifiable data logger for higher data rates. | |||||
| LowLatencyLogger - A data logger for higher data rates. ADC version. | |||||
| LowLatencyLoggerADXL345 - A data logger for higher data rates. ADXL345 SPI. | |||||
| LowLatencyLoggerMPU6050 - A data logger for higher data rates. MPU6050 I2C. | |||||
| OpenNext - Open all files in the root dir and print their filename. | OpenNext - Open all files in the root dir and print their filename. | ||||
| RawWrite - A test of raw write functions for contiguous files. | RawWrite - A test of raw write functions for contiguous files. | ||||
| readCSV - Read a comma-separated value file using iostream extractors. | |||||
| ReadCsv - Function to read a CSV text file one field at a time. | |||||
| ReadCsvArray - Read a two dimensional array from a CSV file. | |||||
| ReadCsvStream - Read a comma-separated value file using iostream extractors. | |||||
| ReadCsvFields - Function to read a CSV text file one field at a time. | |||||
| ReadCsvArray - Read a two dimensional array from a CSV file. | |||||
| ReadWriteSdFat - SdFat version of Arduino SD ReadWrite example. | |||||
| ReadWrite - Compatibility test of Arduino SD ReadWrite example. | |||||
| rename - A demo of SdFat::rename(old, new) and SdFile::rename(dirFile, newPath). | rename - A demo of SdFat::rename(old, new) and SdFile::rename(dirFile, newPath). | ||||
| StdioBench - Demo and test of stdio style stream. | StdioBench - Demo and test of stdio style stream. | ||||
| StreamParseInt - Demo of the SD.h API and the File class parseInt() function. | |||||
| 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. |
| cout << now << endl; | cout << now << endl; | ||||
| #endif // USE_DS1307 | #endif // USE_DS1307 | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| #include <SPI.h> | #include <SPI.h> | ||||
| #include "SdFat.h" | #include "SdFat.h" | ||||
| const uint8_t chipSelect = SS; | |||||
| SdFat sd; | SdFat sd; | ||||
| SdFile file; | SdFile file; | ||||
| while (!Serial.available()) { | while (!Serial.available()) { | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| if (!sd.begin()) { | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| Serial.println("begin failed"); | Serial.println("begin failed"); | ||||
| return; | return; | ||||
| } | } |
| SdFat SD; | 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 = SS; | |||||
| File file; | File file; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ |
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| } | } | ||||
| delay(400); // catch Due reset problem | delay(400); // catch Due reset problem | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| while (!Serial) { | while (!Serial) { | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| // Error messages stored in flash. | // Error messages stored in flash. | ||||
| #define error(msg) errorFlash(F(msg)) | |||||
| //------------------------------------------------------------------------------ | |||||
| void errorFlash(const __FlashStringHelper* msg) { | |||||
| sd.errorPrint(msg); | |||||
| fatalBlink(); | |||||
| } | |||||
| #define error(msg) {sd.errorPrint(F(msg));fatalBlink();} | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // | // | ||||
| void fatalBlink() { | void fatalBlink() { | ||||
| // Create new file. | // Create new file. | ||||
| Serial.println(F("Creating new file")); | Serial.println(F("Creating new file")); | ||||
| binFile.close(); | binFile.close(); | ||||
| if (!binFile.createContiguous(sd.vwd(), | |||||
| TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
| if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
| error("createContiguous failed"); | error("createContiguous failed"); | ||||
| } | } | ||||
| // Get the address of the file on the SD. | // Get the address of the file on the SD. | ||||
| Serial.print(F("FreeStack: ")); | Serial.print(F("FreeStack: ")); | ||||
| Serial.println(FreeStack()); | Serial.println(FreeStack()); | ||||
| // initialize file system. | |||||
| if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorPrint(); | sd.initErrorPrint(); | ||||
| fatalBlink(); | fatalBlink(); | ||||
| } | } |
| "You can use test files located in\r\n" | "You can use test files located in\r\n" | ||||
| "SdFat/examples/LongFileName/testFiles")); | "SdFat/examples/LongFileName/testFiles")); | ||||
| if (!sd.begin(SD_CS_PIN)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| Serial.print(F("FreeStack: ")); | Serial.print(F("FreeStack: ")); |
| * | * | ||||
| * If your SD card has a long write latency, it may be necessary to use | * If your SD card has a long write latency, it may be necessary to use | ||||
| * slower sample rates. Using a Mega Arduino helps overcome latency | * slower sample rates. Using a Mega Arduino helps overcome latency | ||||
| * problems since 13 512 byte buffers will be used. | |||||
| * problems since 12 512 byte buffers will be used. | |||||
| * | * | ||||
| * Data is written to the file using a SD multiple block write command. | * Data is written to the file using a SD multiple block write command. | ||||
| */ | */ | ||||
| #include <SPI.h> | #include <SPI.h> | ||||
| #include "SdFat.h" | #include "SdFat.h" | ||||
| #include "FreeStack.h" | #include "FreeStack.h" | ||||
| #include "UserDataType.h" // Edit this include file to change data_t. | |||||
| //------------------------------------------------------------------------------ | |||||
| // Set useSharedSpi true for use of an SPI sensor. | |||||
| const bool useSharedSpi = false; | |||||
| // File start time in micros. | |||||
| uint32_t startMicros; | |||||
| //------------------------------------------------------------------------------ | |||||
| // User data functions. Modify these functions for your data items. | |||||
| // Acquire a data record. | |||||
| void acquireData(data_t* data) { | |||||
| data->time = micros(); | |||||
| for (int i = 0; i < ADC_DIM; i++) { | |||||
| data->adc[i] = analogRead(i); | |||||
| } | |||||
| } | |||||
| // Print a data record. | |||||
| void printData(Print* pr, data_t* data) { | |||||
| pr->print(data->time - startMicros); | |||||
| for (int i = 0; i < ADC_DIM; i++) { | |||||
| pr->write(','); | |||||
| pr->print(data->adc[i]); | |||||
| } | |||||
| pr->println(); | |||||
| } | |||||
| #include "UserTypes.h" | |||||
| // Print data header. | |||||
| void printHeader(Print* pr) { | |||||
| pr->print(F("time")); | |||||
| for (int i = 0; i < ADC_DIM; i++) { | |||||
| pr->print(F(",adc")); | |||||
| pr->print(i); | |||||
| } | |||||
| pr->println(); | |||||
| } | |||||
| #ifdef __AVR_ATmega328P__ | |||||
| #include "MinimumSerial.h" | |||||
| MinimumSerial MinSerial; | |||||
| #define Serial MinSerial | |||||
| #endif // __AVR_ATmega328P__ | |||||
| //============================================================================== | //============================================================================== | ||||
| // Start of configuration constants. | // Start of configuration constants. | ||||
| //============================================================================== | //============================================================================== | ||||
| // Abort run on an overrun. Data before the overrun will be saved. | |||||
| #define ABORT_ON_OVERRUN 1 | |||||
| //------------------------------------------------------------------------------ | |||||
| //Interval between data records in microseconds. | //Interval between data records in microseconds. | ||||
| const uint32_t LOG_INTERVAL_USEC = 2000; | const uint32_t LOG_INTERVAL_USEC = 2000; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // Set USE_SHARED_SPI non-zero for use of an SPI sensor. | |||||
| // May not work for some cards. | |||||
| #ifndef USE_SHARED_SPI | |||||
| #define USE_SHARED_SPI 0 | |||||
| #endif // USE_SHARED_SPI | |||||
| //------------------------------------------------------------------------------ | |||||
| // Pin definitions. | // Pin definitions. | ||||
| // | // | ||||
| // SD chip select pin. | // SD chip select pin. | ||||
| const uint8_t SD_CS_PIN = SS; | const uint8_t SD_CS_PIN = SS; | ||||
| // | // | ||||
| // Digital pin to indicate an error, set to -1 if not used. | // Digital pin to indicate an error, set to -1 if not used. | ||||
| // The led blinks for fatal errors. The led goes on solid for SD write | |||||
| // overrun errors and logging continues. | |||||
| // The led blinks for fatal errors. The led goes on solid for | |||||
| // overrun errors and logging continues unless ABORT_ON_OVERRUN | |||||
| // is non-zero. | |||||
| #ifdef ERROR_LED_PIN | |||||
| #undef ERROR_LED_PIN | #undef ERROR_LED_PIN | ||||
| #endif // ERROR_LED_PIN | |||||
| const int8_t ERROR_LED_PIN = -1; | const int8_t ERROR_LED_PIN = -1; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // File definitions. | // File definitions. | ||||
| // This file is flash erased using special SD commands. The file will be | // This file is flash erased using special SD commands. The file will be | ||||
| // truncated if logging is stopped early. | // truncated if logging is stopped early. | ||||
| const uint32_t FILE_BLOCK_COUNT = 256000; | const uint32_t FILE_BLOCK_COUNT = 256000; | ||||
| // log file base name. Must be six characters or less. | |||||
| // | |||||
| // log file base name if not defined in UserTypes.h | |||||
| #ifndef FILE_BASE_NAME | |||||
| #define FILE_BASE_NAME "data" | #define FILE_BASE_NAME "data" | ||||
| #endif // FILE_BASE_NAME | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // Buffer definitions. | // Buffer definitions. | ||||
| // | // | ||||
| // The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional | |||||
| // The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional | |||||
| // buffers. | // buffers. | ||||
| // | // | ||||
| #ifndef RAMEND | #ifndef RAMEND | ||||
| // Assume ARM. Use total of nine 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 8; | |||||
| // Assume ARM. Use total of ten 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 10; | |||||
| // | // | ||||
| #elif RAMEND < 0X8FF | #elif RAMEND < 0X8FF | ||||
| #error Too little SRAM | #error Too little SRAM | ||||
| // | // | ||||
| #elif RAMEND < 0X10FF | #elif RAMEND < 0X10FF | ||||
| // Use total of two 512 byte buffers. | // Use total of two 512 byte buffers. | ||||
| const uint8_t BUFFER_BLOCK_COUNT = 1; | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 2; | |||||
| // | // | ||||
| #elif RAMEND < 0X20FF | #elif RAMEND < 0X20FF | ||||
| // Use total of five 512 byte buffers. | |||||
| // Use total of four 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 4; | const uint8_t BUFFER_BLOCK_COUNT = 4; | ||||
| // | // | ||||
| #else // RAMEND | #else // RAMEND | ||||
| // Use total of 13 512 byte buffers. | |||||
| // Use total of 12 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 12; | const uint8_t BUFFER_BLOCK_COUNT = 12; | ||||
| #endif // RAMEND | #endif // RAMEND | ||||
| //============================================================================== | //============================================================================== | ||||
| // Size of file base name. Must not be larger than six. | // Size of file base name. Must not be larger than six. | ||||
| const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | ||||
| const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7; | |||||
| char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin"; | |||||
| SdFat sd; | SdFat sd; | ||||
| SdBaseFile binFile; | SdBaseFile binFile; | ||||
| char binName[13] = FILE_BASE_NAME "00.bin"; | |||||
| // Number of data records in a block. | // Number of data records in a block. | ||||
| const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | ||||
| data_t data[DATA_DIM]; | data_t data[DATA_DIM]; | ||||
| uint8_t fill[FILL_DIM]; | uint8_t fill[FILL_DIM]; | ||||
| }; | }; | ||||
| const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 2; | |||||
| block_t* emptyQueue[QUEUE_DIM]; | |||||
| uint8_t emptyHead; | |||||
| uint8_t emptyTail; | |||||
| block_t* fullQueue[QUEUE_DIM]; | |||||
| uint8_t fullHead; | |||||
| uint8_t fullTail; | |||||
| // Advance queue index. | |||||
| inline uint8_t queueNext(uint8_t ht) { | |||||
| return ht < (QUEUE_DIM - 1) ? ht + 1 : 0; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| // Error messages stored in flash. | // Error messages stored in flash. | ||||
| #define error(msg) errorFlash(F(msg)) | |||||
| //------------------------------------------------------------------------------ | |||||
| void errorFlash(const __FlashStringHelper* msg) { | |||||
| sd.errorPrint(msg); | |||||
| fatalBlink(); | |||||
| } | |||||
| #define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();} | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // | // | ||||
| void fatalBlink() { | void fatalBlink() { | ||||
| while (true) { | while (true) { | ||||
| SysCall::yield(); | |||||
| if (ERROR_LED_PIN >= 0) { | if (ERROR_LED_PIN >= 0) { | ||||
| digitalWrite(ERROR_LED_PIN, HIGH); | digitalWrite(ERROR_LED_PIN, HIGH); | ||||
| delay(200); | delay(200); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| //============================================================================== | |||||
| //------------------------------------------------------------------------------ | |||||
| // read data file and check for overruns | |||||
| void checkOverrun() { | |||||
| bool headerPrinted = false; | |||||
| block_t block; | |||||
| uint32_t bn = 0; | |||||
| if (!binFile.isOpen()) { | |||||
| Serial.println(); | |||||
| Serial.println(F("No current binary file")); | |||||
| return; | |||||
| } | |||||
| binFile.rewind(); | |||||
| Serial.println(); | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| Serial.println(F("Checking overrun errors - type any character to stop")); | |||||
| while (binFile.read(&block, 512) == 512) { | |||||
| if (block.count == 0) { | |||||
| break; | |||||
| } | |||||
| if (block.overrun) { | |||||
| if (!headerPrinted) { | |||||
| Serial.println(); | |||||
| Serial.println(F("Overruns:")); | |||||
| Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||||
| headerPrinted = true; | |||||
| } | |||||
| Serial.print(bn); | |||||
| Serial.print(','); | |||||
| Serial.print(binFile.firstBlock() + bn); | |||||
| Serial.print(','); | |||||
| Serial.println(block.overrun); | |||||
| } | |||||
| bn++; | |||||
| } | |||||
| if (!headerPrinted) { | |||||
| Serial.println(F("No errors found")); | |||||
| } else { | |||||
| Serial.println(F("Done")); | |||||
| } | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| // Convert binary file to csv file. | // Convert binary file to csv file. | ||||
| void binaryToCsv() { | void binaryToCsv() { | ||||
| uint8_t lastPct = 0; | uint8_t lastPct = 0; | ||||
| uint32_t t0 = millis(); | uint32_t t0 = millis(); | ||||
| uint32_t syncCluster = 0; | uint32_t syncCluster = 0; | ||||
| SdFile csvFile; | SdFile csvFile; | ||||
| char csvName[13]; | |||||
| char csvName[FILE_NAME_DIM]; | |||||
| if (!binFile.isOpen()) { | if (!binFile.isOpen()) { | ||||
| Serial.println(); | Serial.println(); | ||||
| Serial.println(F("No current binary file")); | Serial.println(F("No current binary file")); | ||||
| return; | return; | ||||
| } | } | ||||
| binFile.rewind(); | |||||
| Serial.println(); | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| // Create a new csvFile. | // Create a new csvFile. | ||||
| strcpy(csvName, binName); | strcpy(csvName, binName); | ||||
| strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | ||||
| if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | ||||
| error("open csvFile failed"); | error("open csvFile failed"); | ||||
| } | } | ||||
| Serial.println(); | |||||
| binFile.rewind(); | |||||
| Serial.print(F("Writing: ")); | Serial.print(F("Writing: ")); | ||||
| Serial.print(csvName); | Serial.print(csvName); | ||||
| Serial.println(F(" - type any character to stop")); | Serial.println(F(" - type any character to stop")); | ||||
| Serial.print(0.001*(millis() - t0)); | Serial.print(0.001*(millis() - t0)); | ||||
| Serial.println(F(" Seconds")); | Serial.println(F(" Seconds")); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | |||||
| // read data file and check for overruns | |||||
| void checkOverrun() { | |||||
| bool headerPrinted = false; | |||||
| block_t block; | |||||
| //----------------------------------------------------------------------------- | |||||
| void createBinFile() { | |||||
| // max number of blocks to erase per erase call | |||||
| const uint32_t ERASE_SIZE = 262144L; | |||||
| uint32_t bgnBlock, endBlock; | uint32_t bgnBlock, endBlock; | ||||
| uint32_t bn = 0; | |||||
| if (!binFile.isOpen()) { | |||||
| Serial.println(); | |||||
| Serial.println(F("No current binary file")); | |||||
| return; | |||||
| Serial.println(); | |||||
| while (sd.exists(binName)) { | |||||
| if (binName[BASE_NAME_SIZE + 1] != '9') { | |||||
| binName[BASE_NAME_SIZE + 1]++; | |||||
| } else { | |||||
| binName[BASE_NAME_SIZE + 1] = '0'; | |||||
| if (binName[BASE_NAME_SIZE] == '9') { | |||||
| error("Can't create file name"); | |||||
| } | |||||
| binName[BASE_NAME_SIZE]++; | |||||
| } | |||||
| } | |||||
| // Delete old tmp file. | |||||
| if (sd.exists(TMP_FILE_NAME)) { | |||||
| Serial.println(F("Deleting tmp file")); | |||||
| if (!sd.remove(TMP_FILE_NAME)) { | |||||
| error("Can't remove tmp file"); | |||||
| } | |||||
| } | |||||
| // Create new file. | |||||
| Serial.println(F("Creating new file")); | |||||
| binFile.close(); | |||||
| if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
| error("createContiguous failed"); | |||||
| } | } | ||||
| // Get the address of the file on the SD. | |||||
| if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | ||||
| error("contiguousRange failed"); | error("contiguousRange failed"); | ||||
| } | } | ||||
| binFile.rewind(); | |||||
| Serial.println(); | |||||
| Serial.println(F("Checking overrun errors - type any character to stop")); | |||||
| while (binFile.read(&block, 512) == 512) { | |||||
| if (block.count == 0) { | |||||
| break; | |||||
| // Flash erase all data in the file. | |||||
| Serial.println(F("Erasing all data")); | |||||
| uint32_t bgnErase = bgnBlock; | |||||
| uint32_t endErase; | |||||
| while (bgnErase < endBlock) { | |||||
| endErase = bgnErase + ERASE_SIZE; | |||||
| if (endErase > endBlock) { | |||||
| endErase = endBlock; | |||||
| } | } | ||||
| if (block.overrun) { | |||||
| if (!headerPrinted) { | |||||
| Serial.println(); | |||||
| Serial.println(F("Overruns:")); | |||||
| Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||||
| headerPrinted = true; | |||||
| } | |||||
| Serial.print(bn); | |||||
| Serial.print(','); | |||||
| Serial.print(bgnBlock + bn); | |||||
| Serial.print(','); | |||||
| Serial.println(block.overrun); | |||||
| if (!sd.card()->erase(bgnErase, endErase)) { | |||||
| error("erase failed"); | |||||
| } | } | ||||
| bn++; | |||||
| } | |||||
| if (!headerPrinted) { | |||||
| Serial.println(F("No errors found")); | |||||
| } else { | |||||
| Serial.println(F("Done")); | |||||
| bgnErase = endErase + 1; | |||||
| } | } | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // log data | // log data | ||||
| // max number of blocks to erase per erase call | |||||
| uint32_t const ERASE_SIZE = 262144L; | |||||
| void logData() { | void logData() { | ||||
| uint32_t bgnBlock, endBlock; | |||||
| // Allocate extra buffer space. | |||||
| block_t block[BUFFER_BLOCK_COUNT]; | |||||
| block_t* curBlock = 0; | |||||
| Serial.println(); | |||||
| // Find unused file name. | |||||
| if (BASE_NAME_SIZE > 6) { | |||||
| error("FILE_BASE_NAME too long"); | |||||
| } | |||||
| while (sd.exists(binName)) { | |||||
| if (binName[BASE_NAME_SIZE + 1] != '9') { | |||||
| binName[BASE_NAME_SIZE + 1]++; | |||||
| } else { | |||||
| binName[BASE_NAME_SIZE + 1] = '0'; | |||||
| if (binName[BASE_NAME_SIZE] == '9') { | |||||
| error("Can't create file name"); | |||||
| } | |||||
| binName[BASE_NAME_SIZE]++; | |||||
| createBinFile(); | |||||
| recordBinFile(); | |||||
| renameBinFile(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void openBinFile() { | |||||
| char name[FILE_NAME_DIM]; | |||||
| strcpy(name, binName); | |||||
| Serial.println(F("\nEnter two digit version")); | |||||
| Serial.write(name, BASE_NAME_SIZE); | |||||
| for (int i = 0; i < 2; i++) { | |||||
| while (!Serial.available()) { | |||||
| SysCall::yield(); | |||||
| } | } | ||||
| } | |||||
| // Delete old tmp file. | |||||
| if (sd.exists(TMP_FILE_NAME)) { | |||||
| Serial.println(F("Deleting tmp file")); | |||||
| if (!sd.remove(TMP_FILE_NAME)) { | |||||
| error("Can't remove tmp file"); | |||||
| char c = Serial.read(); | |||||
| Serial.write(c); | |||||
| if (c < '0' || c > '9') { | |||||
| Serial.println("\nInvalid digit"); | |||||
| return; | |||||
| } | } | ||||
| name[BASE_NAME_SIZE + i] = c; | |||||
| } | } | ||||
| // Create new file. | |||||
| Serial.println(F("Creating new file")); | |||||
| binFile.close(); | |||||
| if (!binFile.createContiguous(sd.vwd(), | |||||
| TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
| error("createContiguous failed"); | |||||
| Serial.println(&name[BASE_NAME_SIZE+2]); | |||||
| if (!sd.exists(name)) { | |||||
| Serial.println(F("File does not exist")); | |||||
| return; | |||||
| } | } | ||||
| // Get the address of the file on the SD. | |||||
| if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||||
| error("contiguousRange failed"); | |||||
| binFile.close(); | |||||
| strcpy(binName, name); | |||||
| if (!binFile.open(binName, O_READ)) { | |||||
| Serial.println(F("open failed")); | |||||
| return; | |||||
| } | } | ||||
| Serial.println(F("File opened")); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void recordBinFile() { | |||||
| const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||||
| // Index of last queue location. | |||||
| const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||||
| // Allocate extra buffer space. | |||||
| block_t block[BUFFER_BLOCK_COUNT - 1]; | |||||
| block_t* curBlock = 0; | |||||
| block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||||
| uint8_t emptyTop; | |||||
| uint8_t minTop; | |||||
| block_t* fullQueue[QUEUE_DIM]; | |||||
| uint8_t fullHead = 0; | |||||
| uint8_t fullTail = 0; | |||||
| // Use SdFat's internal buffer. | // Use SdFat's internal buffer. | ||||
| uint8_t* cache = (uint8_t*)sd.vol()->cacheClear(); | |||||
| if (cache == 0) { | |||||
| emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||||
| if (emptyStack[0] == 0) { | |||||
| error("cacheClear failed"); | error("cacheClear failed"); | ||||
| } | } | ||||
| // Flash erase all data in the file. | |||||
| Serial.println(F("Erasing all data")); | |||||
| uint32_t bgnErase = bgnBlock; | |||||
| uint32_t endErase; | |||||
| while (bgnErase < endBlock) { | |||||
| endErase = bgnErase + ERASE_SIZE; | |||||
| if (endErase > endBlock) { | |||||
| endErase = endBlock; | |||||
| } | |||||
| if (!sd.card()->erase(bgnErase, endErase)) { | |||||
| error("erase failed"); | |||||
| } | |||||
| bgnErase = endErase + 1; | |||||
| // Put rest of buffers on the empty stack. | |||||
| for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) { | |||||
| emptyStack[i] = &block[i - 1]; | |||||
| } | } | ||||
| emptyTop = BUFFER_BLOCK_COUNT; | |||||
| minTop = BUFFER_BLOCK_COUNT; | |||||
| // Start a multiple block write. | // Start a multiple block write. | ||||
| if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) { | |||||
| error("writeBegin failed"); | |||||
| } | |||||
| // Set chip select high if other devices use SPI. | |||||
| if (useSharedSpi) { | |||||
| sd.card()->chipSelectHigh(); | |||||
| } | |||||
| // Initialize queues. | |||||
| emptyHead = emptyTail = 0; | |||||
| fullHead = fullTail = 0; | |||||
| // Use SdFat buffer for one block. | |||||
| emptyQueue[emptyHead] = (block_t*)cache; | |||||
| emptyHead = queueNext(emptyHead); | |||||
| // Put rest of buffers in the empty queue. | |||||
| for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { | |||||
| emptyQueue[emptyHead] = &block[i]; | |||||
| emptyHead = queueNext(emptyHead); | |||||
| if (!sd.card()->writeStart(binFile.firstBlock())) { | |||||
| error("writeBStart failed"); | |||||
| } | } | ||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| Serial.println(F("Logging - type any character to stop")); | Serial.println(F("Logging - type any character to stop")); | ||||
| // Wait for Serial Idle. | |||||
| Serial.flush(); | |||||
| delay(10); | |||||
| bool closeFile = false; | bool closeFile = false; | ||||
| uint32_t bn = 0; | |||||
| uint32_t t0 = millis(); | |||||
| uint32_t t1 = t0; | |||||
| uint32_t bn = 0; | |||||
| uint32_t maxLatency = 0; | |||||
| uint32_t overrun = 0; | uint32_t overrun = 0; | ||||
| uint32_t overrunTotal = 0; | uint32_t overrunTotal = 0; | ||||
| uint32_t count = 0; | |||||
| uint32_t maxDelta = 0; | |||||
| uint32_t minDelta = 99999; | |||||
| uint32_t maxLatency = 0; | |||||
| uint32_t logTime = micros(); | uint32_t logTime = micros(); | ||||
| // Set time for first record of file. | |||||
| startMicros = logTime + LOG_INTERVAL_USEC; | |||||
| while (1) { | |||||
| // Time for next data record. | |||||
| while(1) { | |||||
| // Time for next data record. | |||||
| logTime += LOG_INTERVAL_USEC; | logTime += LOG_INTERVAL_USEC; | ||||
| if (Serial.available()) { | if (Serial.available()) { | ||||
| closeFile = true; | closeFile = true; | ||||
| } | |||||
| } | |||||
| if (closeFile) { | if (closeFile) { | ||||
| if (curBlock != 0) { | if (curBlock != 0) { | ||||
| // Put buffer in full queue. | // Put buffer in full queue. | ||||
| fullQueue[fullHead] = curBlock; | fullQueue[fullHead] = curBlock; | ||||
| fullHead = queueNext(fullHead); | |||||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
| curBlock = 0; | curBlock = 0; | ||||
| } | } | ||||
| } else { | } else { | ||||
| if (curBlock == 0 && emptyTail != emptyHead) { | |||||
| curBlock = emptyQueue[emptyTail]; | |||||
| emptyTail = queueNext(emptyTail); | |||||
| if (curBlock == 0 && emptyTop != 0) { | |||||
| curBlock = emptyStack[--emptyTop]; | |||||
| if (emptyTop < minTop) { | |||||
| minTop = emptyTop; | |||||
| } | |||||
| curBlock->count = 0; | curBlock->count = 0; | ||||
| curBlock->overrun = overrun; | curBlock->overrun = overrun; | ||||
| overrun = 0; | overrun = 0; | ||||
| } while (delta < 0); | } while (delta < 0); | ||||
| if (curBlock == 0) { | if (curBlock == 0) { | ||||
| overrun++; | overrun++; | ||||
| overrunTotal++; | |||||
| if (ERROR_LED_PIN >= 0) { | |||||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||||
| } | |||||
| #if ABORT_ON_OVERRUN | |||||
| Serial.println(F("Overrun abort")); | |||||
| break; | |||||
| #endif // ABORT_ON_OVERRUN | |||||
| } else { | } else { | ||||
| acquireData(&curBlock->data[curBlock->count++]); | |||||
| #if USE_SHARED_SPI | |||||
| sd.card()->spiStop(); | |||||
| #endif // USE_SHARED_SPI | |||||
| acquireData(&curBlock->data[curBlock->count++]); | |||||
| #if USE_SHARED_SPI | |||||
| sd.card()->spiStart(); | |||||
| #endif // USE_SHARED_SPI | |||||
| if (curBlock->count == DATA_DIM) { | if (curBlock->count == DATA_DIM) { | ||||
| fullQueue[fullHead] = curBlock; | fullQueue[fullHead] = curBlock; | ||||
| fullHead = queueNext(fullHead); | |||||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
| curBlock = 0; | curBlock = 0; | ||||
| } | |||||
| if ((uint32_t)delta > maxDelta) maxDelta = delta; | |||||
| if ((uint32_t)delta < minDelta) minDelta = delta; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| if (fullHead == fullTail) { | if (fullHead == fullTail) { | ||||
| // Exit loop if done. | // Exit loop if done. | ||||
| if (closeFile) { | if (closeFile) { | ||||
| } else if (!sd.card()->isBusy()) { | } else if (!sd.card()->isBusy()) { | ||||
| // Get address of block to write. | // Get address of block to write. | ||||
| block_t* pBlock = fullQueue[fullTail]; | block_t* pBlock = fullQueue[fullTail]; | ||||
| fullTail = queueNext(fullTail); | |||||
| fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0; | |||||
| // Write block to SD. | // Write block to SD. | ||||
| uint32_t usec = micros(); | uint32_t usec = micros(); | ||||
| if (!sd.card()->writeData((uint8_t*)pBlock)) { | if (!sd.card()->writeData((uint8_t*)pBlock)) { | ||||
| error("write data failed"); | error("write data failed"); | ||||
| } | } | ||||
| usec = micros() - usec; | usec = micros() - usec; | ||||
| t1 = millis(); | |||||
| if (usec > maxLatency) { | if (usec > maxLatency) { | ||||
| maxLatency = usec; | maxLatency = usec; | ||||
| } | } | ||||
| count += pBlock->count; | |||||
| // Add overruns and possibly light LED. | |||||
| if (pBlock->overrun) { | |||||
| overrunTotal += pBlock->overrun; | |||||
| if (ERROR_LED_PIN >= 0) { | |||||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||||
| } | |||||
| } | |||||
| // Move block to empty queue. | // Move block to empty queue. | ||||
| emptyQueue[emptyHead] = pBlock; | |||||
| emptyHead = queueNext(emptyHead); | |||||
| emptyStack[emptyTop++] = pBlock; | |||||
| bn++; | bn++; | ||||
| if (bn == FILE_BLOCK_COUNT) { | if (bn == FILE_BLOCK_COUNT) { | ||||
| // File full so stop | // File full so stop | ||||
| if (!sd.card()->writeStop()) { | if (!sd.card()->writeStop()) { | ||||
| error("writeStop failed"); | error("writeStop failed"); | ||||
| } | } | ||||
| Serial.print(F("Min Free buffers: ")); | |||||
| Serial.println(minTop); | |||||
| Serial.print(F("Max block write usec: ")); | |||||
| Serial.println(maxLatency); | |||||
| Serial.print(F("Overruns: ")); | |||||
| Serial.println(overrunTotal); | |||||
| // Truncate file if recording stopped early. | // Truncate file if recording stopped early. | ||||
| if (bn != FILE_BLOCK_COUNT) { | if (bn != FILE_BLOCK_COUNT) { | ||||
| Serial.println(F("Truncating file")); | Serial.println(F("Truncating file")); | ||||
| error("Can't truncate file"); | error("Can't truncate file"); | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| void renameBinFile() { | |||||
| if (!binFile.rename(sd.vwd(), binName)) { | if (!binFile.rename(sd.vwd(), binName)) { | ||||
| error("Can't rename file"); | error("Can't rename file"); | ||||
| } | |||||
| } | |||||
| Serial.print(F("File renamed: ")); | Serial.print(F("File renamed: ")); | ||||
| Serial.println(binName); | Serial.println(binName); | ||||
| Serial.print(F("Max block write usec: ")); | |||||
| Serial.println(maxLatency); | |||||
| Serial.print(F("Record time sec: ")); | |||||
| Serial.println(0.001*(t1 - t0), 3); | |||||
| Serial.print(minDelta); | |||||
| Serial.print(F(" <= jitter microseconds <= ")); | |||||
| Serial.println(maxDelta); | |||||
| Serial.print(F("Sample count: ")); | |||||
| Serial.println(count); | |||||
| Serial.print(F("Samples/sec: ")); | |||||
| Serial.println((1000.0)*count/(t1-t0)); | |||||
| Serial.print(F("Overruns: ")); | |||||
| Serial.println(overrunTotal); | |||||
| Serial.println(F("Done")); | |||||
| Serial.print("File size: "); | |||||
| Serial.print(binFile.fileSize()/512); | |||||
| Serial.println(F(" blocks")); | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void testSensor() { | |||||
| const uint32_t interval = 200000; | |||||
| int32_t diff; | |||||
| data_t data; | |||||
| Serial.println(F("\nTesting - type any character to stop\n")); | |||||
| // Wait for Serial Idle. | |||||
| delay(1000); | |||||
| printHeader(&Serial); | |||||
| uint32_t m = micros(); | |||||
| while (!Serial.available()) { | |||||
| m += interval; | |||||
| do { | |||||
| diff = m - micros(); | |||||
| } while (diff > 0); | |||||
| acquireData(&data); | |||||
| printData(&Serial, &data); | |||||
| } | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void setup(void) { | void setup(void) { | ||||
| while (!Serial) { | while (!Serial) { | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| Serial.print(F("FreeStack: ")); | Serial.print(F("FreeStack: ")); | ||||
| Serial.println(FreeStack()); | Serial.println(FreeStack()); | ||||
| Serial.print(F("Records/block: ")); | Serial.print(F("Records/block: ")); | ||||
| if (sizeof(block_t) != 512) { | if (sizeof(block_t) != 512) { | ||||
| error("Invalid block size"); | error("Invalid block size"); | ||||
| } | } | ||||
| // initialize file system. | |||||
| if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||||
| sd.initErrorPrint(); | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorPrint(&Serial); | |||||
| fatalBlink(); | fatalBlink(); | ||||
| } | } | ||||
| userSetup(); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void loop(void) { | void loop(void) { | ||||
| } while (Serial.available() && Serial.read() >= 0); | } while (Serial.available() && Serial.read() >= 0); | ||||
| Serial.println(); | Serial.println(); | ||||
| Serial.println(F("type:")); | Serial.println(F("type:")); | ||||
| Serial.println(F("b - open existing bin file")); | |||||
| Serial.println(F("c - convert file to csv")); | Serial.println(F("c - convert file to csv")); | ||||
| Serial.println(F("d - dump data to Serial")); | Serial.println(F("d - dump data to Serial")); | ||||
| Serial.println(F("e - overrun error details")); | Serial.println(F("e - overrun error details")); | ||||
| Serial.println(F("l - list files")); | |||||
| Serial.println(F("r - record data")); | Serial.println(F("r - record data")); | ||||
| Serial.println(F("t - test without logging")); | |||||
| while(!Serial.available()) { | while(!Serial.available()) { | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| #if WDT_YIELD_TIME_MICROS | |||||
| Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||||
| SysCall::halt(); | |||||
| #endif | |||||
| char c = tolower(Serial.read()); | char c = tolower(Serial.read()); | ||||
| // Discard extra Serial data. | // Discard extra Serial data. | ||||
| if (ERROR_LED_PIN >= 0) { | if (ERROR_LED_PIN >= 0) { | ||||
| digitalWrite(ERROR_LED_PIN, LOW); | digitalWrite(ERROR_LED_PIN, LOW); | ||||
| } | } | ||||
| if (c == 'c') { | |||||
| if (c == 'b') { | |||||
| openBinFile(); | |||||
| } else if (c == 'c') { | |||||
| binaryToCsv(); | binaryToCsv(); | ||||
| } else if (c == 'd') { | } else if (c == 'd') { | ||||
| dumpData(); | dumpData(); | ||||
| } else if (c == 'e') { | } else if (c == 'e') { | ||||
| checkOverrun(); | checkOverrun(); | ||||
| } else if (c == 'l') { | |||||
| Serial.println(F("\nls:")); | |||||
| sd.ls(&Serial, LS_SIZE); | |||||
| } else if (c == 'r') { | } else if (c == 'r') { | ||||
| logData(); | logData(); | ||||
| } else if (c == 't') { | |||||
| testSensor(); | |||||
| } else { | } else { | ||||
| Serial.println(F("Invalid entry")); | Serial.println(F("Invalid entry")); | ||||
| } | } |
| #ifndef UserDataType_h | |||||
| #define UserDataType_h | |||||
| const uint8_t ADC_DIM = 4; | |||||
| struct data_t { | |||||
| unsigned long time; | |||||
| unsigned short adc[ADC_DIM]; | |||||
| }; | |||||
| #endif // UserDataType_h |
| #include "UserTypes.h" | |||||
| // User data functions. Modify these functions for your data items. | |||||
| // Start time for data | |||||
| uint32_t startTime; | |||||
| // Acquire a data record. | |||||
| void acquireData(data_t* data) { | |||||
| data->time = micros(); | |||||
| for (int i = 0; i < ADC_DIM; i++) { | |||||
| data->adc[i] = analogRead(i); | |||||
| } | |||||
| } | |||||
| // Print a data record. | |||||
| void printData(Print* pr, data_t* data) { | |||||
| if (startTime == 0) { | |||||
| startTime = data->time; | |||||
| } | |||||
| pr->print(data->time - startTime); | |||||
| for (int i = 0; i < ADC_DIM; i++) { | |||||
| pr->write(','); | |||||
| pr->print(data->adc[i]); | |||||
| } | |||||
| pr->println(); | |||||
| } | |||||
| // Print data header. | |||||
| void printHeader(Print* pr) { | |||||
| startTime = 0; | |||||
| pr->print(F("micros")); | |||||
| for (int i = 0; i < ADC_DIM; i++) { | |||||
| pr->print(F(",adc")); | |||||
| pr->print(i); | |||||
| } | |||||
| pr->println(); | |||||
| } | |||||
| // Sensor setup | |||||
| void userSetup() { | |||||
| } |
| #ifndef UserTypes_h | |||||
| #define UserTypes_h | |||||
| #include "Arduino.h" | |||||
| // User data types. Modify for your data items. | |||||
| #define FILE_BASE_NAME "adc4pin" | |||||
| const uint8_t ADC_DIM = 4; | |||||
| struct data_t { | |||||
| unsigned long time; | |||||
| unsigned short adc[ADC_DIM]; | |||||
| }; | |||||
| void acquireData(data_t* data); | |||||
| void printData(Print* pr, data_t* data); | |||||
| void printHeader(Print* pr); | |||||
| void userSetup(); | |||||
| #endif // UserTypes_h |
| * | * | ||||
| * If your SD card has a long write latency, it may be necessary to use | * If your SD card has a long write latency, it may be necessary to use | ||||
| * slower sample rates. Using a Mega Arduino helps overcome latency | * slower sample rates. Using a Mega Arduino helps overcome latency | ||||
| * problems since 13 512 byte buffers will be used. | |||||
| * problems since 12 512 byte buffers will be used. | |||||
| * | * | ||||
| * Data is written to the file using a SD multiple block write command. | * Data is written to the file using a SD multiple block write command. | ||||
| */ | */ | ||||
| #include <SPI.h> | #include <SPI.h> | ||||
| #include "SdFat.h" | #include "SdFat.h" | ||||
| #include "FreeStack.h" | #include "FreeStack.h" | ||||
| //------------------------------------------------------------------------------ | |||||
| // Set useSharedSpi true for use of an SPI sensor. | |||||
| const bool useSharedSpi = true; | |||||
| // File start time in micros. | |||||
| uint32_t startMicros; | |||||
| //------------------------------------------------------------------------------ | |||||
| // User data functions. Modify these functions for your data items. | |||||
| #include "UserDataType.h" // Edit this include file to change data_t. | |||||
| const uint8_t ADXL345_CS = 9; | |||||
| const uint8_t POWER_CTL = 0x2D; //Power Control Register | |||||
| const uint8_t DATA_FORMAT = 0x31; | |||||
| const uint8_t DATAX0 = 0x32; //X-Axis Data 0 | |||||
| const uint8_t DATAX1 = 0x33; //X-Axis Data 1 | |||||
| const uint8_t DATAY0 = 0x34; //Y-Axis Data 0 | |||||
| const uint8_t DATAY1 = 0x35; //Y-Axis Data 1 | |||||
| const uint8_t DATAZ0 = 0x36; //Z-Axis Data 0 | |||||
| const uint8_t DATAZ1 = 0x37; //Z-Axis Data 1 | |||||
| void writeADXL345Register(const uint8_t registerAddress, const uint8_t value) { | |||||
| SPI.setDataMode(SPI_MODE3); | |||||
| digitalWrite(ADXL345_CS, LOW); | |||||
| SPI.transfer(registerAddress); | |||||
| SPI.transfer(value); | |||||
| digitalWrite(ADXL345_CS, HIGH); | |||||
| } | |||||
| void setupADXL345() { | |||||
| SPI.begin(); | |||||
| pinMode(ADXL345_CS, OUTPUT); | |||||
| digitalWrite(ADXL345_CS, HIGH); | |||||
| //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register. | |||||
| writeADXL345Register(DATA_FORMAT, 0x01); | |||||
| //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register. | |||||
| writeADXL345Register(POWER_CTL, 0x08); //Measurement mode | |||||
| } | |||||
| // Acquire a data record. | |||||
| void acquireData(data_t* data) { | |||||
| data->time = micros(); | |||||
| SPI.setDataMode(SPI_MODE3); | |||||
| digitalWrite(ADXL345_CS, LOW); | |||||
| // Read multiple bytes so or 0XC0 with address. | |||||
| SPI.transfer(DATAX0 | 0XC0); | |||||
| data->accel[0] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
| data->accel[1] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
| data->accel[2] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
| digitalWrite(ADXL345_CS, HIGH); | |||||
| } | |||||
| // Print a data record. | |||||
| void printData(Print* pr, data_t* data) { | |||||
| pr->print(data->time - startMicros); | |||||
| for (int i = 0; i < ACCEL_DIM; i++) { | |||||
| pr->write(','); | |||||
| pr->print(data->accel[i]); | |||||
| } | |||||
| pr->println(); | |||||
| } | |||||
| #include "UserTypes.h" | |||||
| // Print data header. | |||||
| void printHeader(Print* pr) { | |||||
| pr->println(F("time,ax,ay,az")); | |||||
| } | |||||
| #ifdef __AVR_ATmega328P__ | |||||
| #include "MinimumSerial.h" | |||||
| MinimumSerial MinSerial; | |||||
| #define Serial MinSerial | |||||
| #endif // __AVR_ATmega328P__ | |||||
| //============================================================================== | //============================================================================== | ||||
| // Start of configuration constants. | // Start of configuration constants. | ||||
| //============================================================================== | //============================================================================== | ||||
| // Abort run on an overrun. Data before the overrun will be saved. | |||||
| #define ABORT_ON_OVERRUN 1 | |||||
| //------------------------------------------------------------------------------ | |||||
| //Interval between data records in microseconds. | //Interval between data records in microseconds. | ||||
| const uint32_t LOG_INTERVAL_USEC = 10000; | |||||
| const uint32_t LOG_INTERVAL_USEC = 2000; | |||||
| //------------------------------------------------------------------------------ | |||||
| // Set USE_SHARED_SPI non-zero for use of an SPI sensor. | |||||
| // May not work for some cards. | |||||
| #ifndef USE_SHARED_SPI | |||||
| #define USE_SHARED_SPI 0 | |||||
| #endif // USE_SHARED_SPI | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // Pin definitions. | // Pin definitions. | ||||
| // | // | ||||
| const uint8_t SD_CS_PIN = SS; | const uint8_t SD_CS_PIN = SS; | ||||
| // | // | ||||
| // Digital pin to indicate an error, set to -1 if not used. | // Digital pin to indicate an error, set to -1 if not used. | ||||
| // The led blinks for fatal errors. The led goes on solid for SD write | |||||
| // overrun errors and logging continues. | |||||
| // The led blinks for fatal errors. The led goes on solid for | |||||
| // overrun errors and logging continues unless ABORT_ON_OVERRUN | |||||
| // is non-zero. | |||||
| #ifdef ERROR_LED_PIN | |||||
| #undef ERROR_LED_PIN | |||||
| #endif // ERROR_LED_PIN | |||||
| const int8_t ERROR_LED_PIN = -1; | const int8_t ERROR_LED_PIN = -1; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // File definitions. | // File definitions. | ||||
| // This file is flash erased using special SD commands. The file will be | // This file is flash erased using special SD commands. The file will be | ||||
| // truncated if logging is stopped early. | // truncated if logging is stopped early. | ||||
| const uint32_t FILE_BLOCK_COUNT = 256000; | const uint32_t FILE_BLOCK_COUNT = 256000; | ||||
| // log file base name. Must be six characters or less. | |||||
| // | |||||
| // log file base name if not defined in UserTypes.h | |||||
| #ifndef FILE_BASE_NAME | |||||
| #define FILE_BASE_NAME "data" | #define FILE_BASE_NAME "data" | ||||
| #endif // FILE_BASE_NAME | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // Buffer definitions. | // Buffer definitions. | ||||
| // | // | ||||
| // The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional | |||||
| // The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional | |||||
| // buffers. | // buffers. | ||||
| // | // | ||||
| #ifndef RAMEND | #ifndef RAMEND | ||||
| // Assume ARM. Use total of nine 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 8; | |||||
| // Assume ARM. Use total of ten 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 10; | |||||
| // | // | ||||
| #elif RAMEND < 0X8FF | #elif RAMEND < 0X8FF | ||||
| #error Too little SRAM | #error Too little SRAM | ||||
| // | // | ||||
| #elif RAMEND < 0X10FF | #elif RAMEND < 0X10FF | ||||
| // Use total of two 512 byte buffers. | // Use total of two 512 byte buffers. | ||||
| const uint8_t BUFFER_BLOCK_COUNT = 1; | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 2; | |||||
| // | // | ||||
| #elif RAMEND < 0X20FF | #elif RAMEND < 0X20FF | ||||
| // Use total of five 512 byte buffers. | |||||
| // Use total of four 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 4; | const uint8_t BUFFER_BLOCK_COUNT = 4; | ||||
| // | // | ||||
| #else // RAMEND | #else // RAMEND | ||||
| // Use total of 13 512 byte buffers. | |||||
| // Use total of 12 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 12; | const uint8_t BUFFER_BLOCK_COUNT = 12; | ||||
| #endif // RAMEND | #endif // RAMEND | ||||
| //============================================================================== | //============================================================================== | ||||
| // Size of file base name. Must not be larger than six. | // Size of file base name. Must not be larger than six. | ||||
| const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | ||||
| const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7; | |||||
| char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin"; | |||||
| SdFat sd; | SdFat sd; | ||||
| SdBaseFile binFile; | SdBaseFile binFile; | ||||
| char binName[13] = FILE_BASE_NAME "00.bin"; | |||||
| // Number of data records in a block. | // Number of data records in a block. | ||||
| const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | ||||
| data_t data[DATA_DIM]; | data_t data[DATA_DIM]; | ||||
| uint8_t fill[FILL_DIM]; | uint8_t fill[FILL_DIM]; | ||||
| }; | }; | ||||
| const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 2; | |||||
| block_t* emptyQueue[QUEUE_DIM]; | |||||
| uint8_t emptyHead; | |||||
| uint8_t emptyTail; | |||||
| block_t* fullQueue[QUEUE_DIM]; | |||||
| uint8_t fullHead; | |||||
| uint8_t fullTail; | |||||
| // Advance queue index. | |||||
| inline uint8_t queueNext(uint8_t ht) { | |||||
| return ht < (QUEUE_DIM - 1) ? ht + 1 : 0; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| // Error messages stored in flash. | // Error messages stored in flash. | ||||
| #define error(msg) errorFlash(F(msg)) | |||||
| //------------------------------------------------------------------------------ | |||||
| void errorFlash(const __FlashStringHelper* msg) { | |||||
| sd.errorPrint(msg); | |||||
| fatalBlink(); | |||||
| } | |||||
| #define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();} | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // | // | ||||
| void fatalBlink() { | void fatalBlink() { | ||||
| while (true) { | while (true) { | ||||
| SysCall::yield(); | |||||
| if (ERROR_LED_PIN >= 0) { | if (ERROR_LED_PIN >= 0) { | ||||
| digitalWrite(ERROR_LED_PIN, HIGH); | digitalWrite(ERROR_LED_PIN, HIGH); | ||||
| delay(200); | delay(200); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| //============================================================================== | |||||
| //------------------------------------------------------------------------------ | |||||
| // read data file and check for overruns | |||||
| void checkOverrun() { | |||||
| bool headerPrinted = false; | |||||
| block_t block; | |||||
| uint32_t bn = 0; | |||||
| if (!binFile.isOpen()) { | |||||
| Serial.println(); | |||||
| Serial.println(F("No current binary file")); | |||||
| return; | |||||
| } | |||||
| binFile.rewind(); | |||||
| Serial.println(); | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| Serial.println(F("Checking overrun errors - type any character to stop")); | |||||
| while (binFile.read(&block, 512) == 512) { | |||||
| if (block.count == 0) { | |||||
| break; | |||||
| } | |||||
| if (block.overrun) { | |||||
| if (!headerPrinted) { | |||||
| Serial.println(); | |||||
| Serial.println(F("Overruns:")); | |||||
| Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||||
| headerPrinted = true; | |||||
| } | |||||
| Serial.print(bn); | |||||
| Serial.print(','); | |||||
| Serial.print(binFile.firstBlock() + bn); | |||||
| Serial.print(','); | |||||
| Serial.println(block.overrun); | |||||
| } | |||||
| bn++; | |||||
| } | |||||
| if (!headerPrinted) { | |||||
| Serial.println(F("No errors found")); | |||||
| } else { | |||||
| Serial.println(F("Done")); | |||||
| } | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| // Convert binary file to csv file. | // Convert binary file to csv file. | ||||
| void binaryToCsv() { | void binaryToCsv() { | ||||
| uint8_t lastPct = 0; | uint8_t lastPct = 0; | ||||
| uint32_t t0 = millis(); | uint32_t t0 = millis(); | ||||
| uint32_t syncCluster = 0; | uint32_t syncCluster = 0; | ||||
| SdFile csvFile; | SdFile csvFile; | ||||
| char csvName[13]; | |||||
| char csvName[FILE_NAME_DIM]; | |||||
| if (!binFile.isOpen()) { | if (!binFile.isOpen()) { | ||||
| Serial.println(); | Serial.println(); | ||||
| Serial.println(F("No current binary file")); | Serial.println(F("No current binary file")); | ||||
| return; | return; | ||||
| } | } | ||||
| binFile.rewind(); | |||||
| Serial.println(); | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| // Create a new csvFile. | // Create a new csvFile. | ||||
| strcpy(csvName, binName); | strcpy(csvName, binName); | ||||
| strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | ||||
| if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | ||||
| error("open csvFile failed"); | error("open csvFile failed"); | ||||
| } | } | ||||
| Serial.println(); | |||||
| binFile.rewind(); | |||||
| Serial.print(F("Writing: ")); | Serial.print(F("Writing: ")); | ||||
| Serial.print(csvName); | Serial.print(csvName); | ||||
| Serial.println(F(" - type any character to stop")); | Serial.println(F(" - type any character to stop")); | ||||
| Serial.print(0.001*(millis() - t0)); | Serial.print(0.001*(millis() - t0)); | ||||
| Serial.println(F(" Seconds")); | Serial.println(F(" Seconds")); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | |||||
| // read data file and check for overruns | |||||
| void checkOverrun() { | |||||
| bool headerPrinted = false; | |||||
| block_t block; | |||||
| //----------------------------------------------------------------------------- | |||||
| void createBinFile() { | |||||
| // max number of blocks to erase per erase call | |||||
| const uint32_t ERASE_SIZE = 262144L; | |||||
| uint32_t bgnBlock, endBlock; | uint32_t bgnBlock, endBlock; | ||||
| uint32_t bn = 0; | |||||
| if (!binFile.isOpen()) { | |||||
| Serial.println(); | |||||
| Serial.println(F("No current binary file")); | |||||
| return; | |||||
| Serial.println(); | |||||
| while (sd.exists(binName)) { | |||||
| if (binName[BASE_NAME_SIZE + 1] != '9') { | |||||
| binName[BASE_NAME_SIZE + 1]++; | |||||
| } else { | |||||
| binName[BASE_NAME_SIZE + 1] = '0'; | |||||
| if (binName[BASE_NAME_SIZE] == '9') { | |||||
| error("Can't create file name"); | |||||
| } | |||||
| binName[BASE_NAME_SIZE]++; | |||||
| } | |||||
| } | |||||
| // Delete old tmp file. | |||||
| if (sd.exists(TMP_FILE_NAME)) { | |||||
| Serial.println(F("Deleting tmp file")); | |||||
| if (!sd.remove(TMP_FILE_NAME)) { | |||||
| error("Can't remove tmp file"); | |||||
| } | |||||
| } | } | ||||
| // Create new file. | |||||
| Serial.println(F("Creating new file")); | |||||
| binFile.close(); | |||||
| if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
| error("createContiguous failed"); | |||||
| } | |||||
| // Get the address of the file on the SD. | |||||
| if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | ||||
| error("contiguousRange failed"); | error("contiguousRange failed"); | ||||
| } | } | ||||
| binFile.rewind(); | |||||
| Serial.println(); | |||||
| Serial.println(F("Checking overrun errors - type any character to stop")); | |||||
| while (binFile.read(&block, 512) == 512) { | |||||
| if (block.count == 0) { | |||||
| break; | |||||
| // Flash erase all data in the file. | |||||
| Serial.println(F("Erasing all data")); | |||||
| uint32_t bgnErase = bgnBlock; | |||||
| uint32_t endErase; | |||||
| while (bgnErase < endBlock) { | |||||
| endErase = bgnErase + ERASE_SIZE; | |||||
| if (endErase > endBlock) { | |||||
| endErase = endBlock; | |||||
| } | } | ||||
| if (block.overrun) { | |||||
| if (!headerPrinted) { | |||||
| Serial.println(); | |||||
| Serial.println(F("Overruns:")); | |||||
| Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||||
| headerPrinted = true; | |||||
| } | |||||
| Serial.print(bn); | |||||
| Serial.print(','); | |||||
| Serial.print(bgnBlock + bn); | |||||
| Serial.print(','); | |||||
| Serial.println(block.overrun); | |||||
| if (!sd.card()->erase(bgnErase, endErase)) { | |||||
| error("erase failed"); | |||||
| } | } | ||||
| bn++; | |||||
| } | |||||
| if (!headerPrinted) { | |||||
| Serial.println(F("No errors found")); | |||||
| } else { | |||||
| Serial.println(F("Done")); | |||||
| bgnErase = endErase + 1; | |||||
| } | } | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // log data | // log data | ||||
| // max number of blocks to erase per erase call | |||||
| uint32_t const ERASE_SIZE = 262144L; | |||||
| void logData() { | void logData() { | ||||
| uint32_t bgnBlock, endBlock; | |||||
| // Allocate extra buffer space. | |||||
| block_t block[BUFFER_BLOCK_COUNT]; | |||||
| block_t* curBlock = 0; | |||||
| Serial.println(); | |||||
| // Find unused file name. | |||||
| if (BASE_NAME_SIZE > 6) { | |||||
| error("FILE_BASE_NAME too long"); | |||||
| } | |||||
| while (sd.exists(binName)) { | |||||
| if (binName[BASE_NAME_SIZE + 1] != '9') { | |||||
| binName[BASE_NAME_SIZE + 1]++; | |||||
| } else { | |||||
| binName[BASE_NAME_SIZE + 1] = '0'; | |||||
| if (binName[BASE_NAME_SIZE] == '9') { | |||||
| error("Can't create file name"); | |||||
| } | |||||
| binName[BASE_NAME_SIZE]++; | |||||
| createBinFile(); | |||||
| recordBinFile(); | |||||
| renameBinFile(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void openBinFile() { | |||||
| char name[FILE_NAME_DIM]; | |||||
| strcpy(name, binName); | |||||
| Serial.println(F("\nEnter two digit version")); | |||||
| Serial.write(name, BASE_NAME_SIZE); | |||||
| for (int i = 0; i < 2; i++) { | |||||
| while (!Serial.available()) { | |||||
| SysCall::yield(); | |||||
| } | } | ||||
| } | |||||
| // Delete old tmp file. | |||||
| if (sd.exists(TMP_FILE_NAME)) { | |||||
| Serial.println(F("Deleting tmp file")); | |||||
| if (!sd.remove(TMP_FILE_NAME)) { | |||||
| error("Can't remove tmp file"); | |||||
| char c = Serial.read(); | |||||
| Serial.write(c); | |||||
| if (c < '0' || c > '9') { | |||||
| Serial.println("\nInvalid digit"); | |||||
| return; | |||||
| } | } | ||||
| name[BASE_NAME_SIZE + i] = c; | |||||
| } | } | ||||
| // Create new file. | |||||
| Serial.println(F("Creating new file")); | |||||
| binFile.close(); | |||||
| if (!binFile.createContiguous(sd.vwd(), | |||||
| TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
| error("createContiguous failed"); | |||||
| Serial.println(&name[BASE_NAME_SIZE+2]); | |||||
| if (!sd.exists(name)) { | |||||
| Serial.println(F("File does not exist")); | |||||
| return; | |||||
| } | } | ||||
| // Get the address of the file on the SD. | |||||
| if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||||
| error("contiguousRange failed"); | |||||
| binFile.close(); | |||||
| strcpy(binName, name); | |||||
| if (!binFile.open(binName, O_READ)) { | |||||
| Serial.println(F("open failed")); | |||||
| return; | |||||
| } | } | ||||
| Serial.println(F("File opened")); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void recordBinFile() { | |||||
| const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||||
| // Index of last queue location. | |||||
| const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||||
| // Allocate extra buffer space. | |||||
| block_t block[BUFFER_BLOCK_COUNT - 1]; | |||||
| block_t* curBlock = 0; | |||||
| block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||||
| uint8_t emptyTop; | |||||
| uint8_t minTop; | |||||
| block_t* fullQueue[QUEUE_DIM]; | |||||
| uint8_t fullHead = 0; | |||||
| uint8_t fullTail = 0; | |||||
| // Use SdFat's internal buffer. | // Use SdFat's internal buffer. | ||||
| uint8_t* cache = (uint8_t*)sd.vol()->cacheClear(); | |||||
| if (cache == 0) { | |||||
| emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||||
| if (emptyStack[0] == 0) { | |||||
| error("cacheClear failed"); | error("cacheClear failed"); | ||||
| } | } | ||||
| // Flash erase all data in the file. | |||||
| Serial.println(F("Erasing all data")); | |||||
| uint32_t bgnErase = bgnBlock; | |||||
| uint32_t endErase; | |||||
| while (bgnErase < endBlock) { | |||||
| endErase = bgnErase + ERASE_SIZE; | |||||
| if (endErase > endBlock) { | |||||
| endErase = endBlock; | |||||
| } | |||||
| if (!sd.card()->erase(bgnErase, endErase)) { | |||||
| error("erase failed"); | |||||
| } | |||||
| bgnErase = endErase + 1; | |||||
| // Put rest of buffers on the empty stack. | |||||
| for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) { | |||||
| emptyStack[i] = &block[i - 1]; | |||||
| } | } | ||||
| emptyTop = BUFFER_BLOCK_COUNT; | |||||
| minTop = BUFFER_BLOCK_COUNT; | |||||
| // Start a multiple block write. | // Start a multiple block write. | ||||
| if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) { | |||||
| error("writeBegin failed"); | |||||
| } | |||||
| // Set chip select high if other devices use SPI. | |||||
| if (useSharedSpi) { | |||||
| sd.card()->chipSelectHigh(); | |||||
| } | |||||
| // Initialize queues. | |||||
| emptyHead = emptyTail = 0; | |||||
| fullHead = fullTail = 0; | |||||
| // Use SdFat buffer for one block. | |||||
| emptyQueue[emptyHead] = (block_t*)cache; | |||||
| emptyHead = queueNext(emptyHead); | |||||
| // Put rest of buffers in the empty queue. | |||||
| for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { | |||||
| emptyQueue[emptyHead] = &block[i]; | |||||
| emptyHead = queueNext(emptyHead); | |||||
| if (!sd.card()->writeStart(binFile.firstBlock())) { | |||||
| error("writeBStart failed"); | |||||
| } | } | ||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| Serial.println(F("Logging - type any character to stop")); | Serial.println(F("Logging - type any character to stop")); | ||||
| // Wait for Serial Idle. | |||||
| Serial.flush(); | |||||
| delay(10); | |||||
| bool closeFile = false; | bool closeFile = false; | ||||
| uint32_t bn = 0; | |||||
| uint32_t t0 = millis(); | |||||
| uint32_t t1 = t0; | |||||
| uint32_t bn = 0; | |||||
| uint32_t maxLatency = 0; | |||||
| uint32_t overrun = 0; | uint32_t overrun = 0; | ||||
| uint32_t overrunTotal = 0; | uint32_t overrunTotal = 0; | ||||
| uint32_t count = 0; | |||||
| uint32_t maxDelta = 0; | |||||
| uint32_t minDelta = 99999; | |||||
| uint32_t maxLatency = 0; | |||||
| uint32_t logTime = micros(); | uint32_t logTime = micros(); | ||||
| // Set time for first record of file. | |||||
| startMicros = logTime + LOG_INTERVAL_USEC; | |||||
| while (1) { | |||||
| // Time for next data record. | |||||
| while(1) { | |||||
| // Time for next data record. | |||||
| logTime += LOG_INTERVAL_USEC; | logTime += LOG_INTERVAL_USEC; | ||||
| if (Serial.available()) { | if (Serial.available()) { | ||||
| closeFile = true; | closeFile = true; | ||||
| } | |||||
| } | |||||
| if (closeFile) { | if (closeFile) { | ||||
| if (curBlock != 0) { | if (curBlock != 0) { | ||||
| // Put buffer in full queue. | // Put buffer in full queue. | ||||
| fullQueue[fullHead] = curBlock; | fullQueue[fullHead] = curBlock; | ||||
| fullHead = queueNext(fullHead); | |||||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
| curBlock = 0; | curBlock = 0; | ||||
| } | } | ||||
| } else { | } else { | ||||
| if (curBlock == 0 && emptyTail != emptyHead) { | |||||
| curBlock = emptyQueue[emptyTail]; | |||||
| emptyTail = queueNext(emptyTail); | |||||
| if (curBlock == 0 && emptyTop != 0) { | |||||
| curBlock = emptyStack[--emptyTop]; | |||||
| if (emptyTop < minTop) { | |||||
| minTop = emptyTop; | |||||
| } | |||||
| curBlock->count = 0; | curBlock->count = 0; | ||||
| curBlock->overrun = overrun; | curBlock->overrun = overrun; | ||||
| overrun = 0; | overrun = 0; | ||||
| } while (delta < 0); | } while (delta < 0); | ||||
| if (curBlock == 0) { | if (curBlock == 0) { | ||||
| overrun++; | overrun++; | ||||
| overrunTotal++; | |||||
| if (ERROR_LED_PIN >= 0) { | |||||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||||
| } | |||||
| #if ABORT_ON_OVERRUN | |||||
| Serial.println(F("Overrun abort")); | |||||
| break; | |||||
| #endif // ABORT_ON_OVERRUN | |||||
| } else { | } else { | ||||
| acquireData(&curBlock->data[curBlock->count++]); | |||||
| #if USE_SHARED_SPI | |||||
| sd.card()->spiStop(); | |||||
| #endif // USE_SHARED_SPI | |||||
| acquireData(&curBlock->data[curBlock->count++]); | |||||
| #if USE_SHARED_SPI | |||||
| sd.card()->spiStart(); | |||||
| #endif // USE_SHARED_SPI | |||||
| if (curBlock->count == DATA_DIM) { | if (curBlock->count == DATA_DIM) { | ||||
| fullQueue[fullHead] = curBlock; | fullQueue[fullHead] = curBlock; | ||||
| fullHead = queueNext(fullHead); | |||||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
| curBlock = 0; | curBlock = 0; | ||||
| } | |||||
| if ((uint32_t)delta > maxDelta) maxDelta = delta; | |||||
| if ((uint32_t)delta < minDelta) minDelta = delta; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| if (fullHead == fullTail) { | if (fullHead == fullTail) { | ||||
| // Exit loop if done. | // Exit loop if done. | ||||
| if (closeFile) { | if (closeFile) { | ||||
| } else if (!sd.card()->isBusy()) { | } else if (!sd.card()->isBusy()) { | ||||
| // Get address of block to write. | // Get address of block to write. | ||||
| block_t* pBlock = fullQueue[fullTail]; | block_t* pBlock = fullQueue[fullTail]; | ||||
| fullTail = queueNext(fullTail); | |||||
| fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0; | |||||
| // Write block to SD. | // Write block to SD. | ||||
| uint32_t usec = micros(); | uint32_t usec = micros(); | ||||
| if (!sd.card()->writeData((uint8_t*)pBlock)) { | if (!sd.card()->writeData((uint8_t*)pBlock)) { | ||||
| error("write data failed"); | error("write data failed"); | ||||
| } | } | ||||
| usec = micros() - usec; | usec = micros() - usec; | ||||
| t1 = millis(); | |||||
| if (usec > maxLatency) { | if (usec > maxLatency) { | ||||
| maxLatency = usec; | maxLatency = usec; | ||||
| } | } | ||||
| count += pBlock->count; | |||||
| // Add overruns and possibly light LED. | |||||
| if (pBlock->overrun) { | |||||
| overrunTotal += pBlock->overrun; | |||||
| if (ERROR_LED_PIN >= 0) { | |||||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||||
| } | |||||
| } | |||||
| // Move block to empty queue. | // Move block to empty queue. | ||||
| emptyQueue[emptyHead] = pBlock; | |||||
| emptyHead = queueNext(emptyHead); | |||||
| emptyStack[emptyTop++] = pBlock; | |||||
| bn++; | bn++; | ||||
| if (bn == FILE_BLOCK_COUNT) { | if (bn == FILE_BLOCK_COUNT) { | ||||
| // File full so stop | // File full so stop | ||||
| if (!sd.card()->writeStop()) { | if (!sd.card()->writeStop()) { | ||||
| error("writeStop failed"); | error("writeStop failed"); | ||||
| } | } | ||||
| Serial.print(F("Min Free buffers: ")); | |||||
| Serial.println(minTop); | |||||
| Serial.print(F("Max block write usec: ")); | |||||
| Serial.println(maxLatency); | |||||
| Serial.print(F("Overruns: ")); | |||||
| Serial.println(overrunTotal); | |||||
| // Truncate file if recording stopped early. | // Truncate file if recording stopped early. | ||||
| if (bn != FILE_BLOCK_COUNT) { | if (bn != FILE_BLOCK_COUNT) { | ||||
| Serial.println(F("Truncating file")); | Serial.println(F("Truncating file")); | ||||
| error("Can't truncate file"); | error("Can't truncate file"); | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| void renameBinFile() { | |||||
| if (!binFile.rename(sd.vwd(), binName)) { | if (!binFile.rename(sd.vwd(), binName)) { | ||||
| error("Can't rename file"); | error("Can't rename file"); | ||||
| } | |||||
| } | |||||
| Serial.print(F("File renamed: ")); | Serial.print(F("File renamed: ")); | ||||
| Serial.println(binName); | Serial.println(binName); | ||||
| Serial.print(F("Max block write usec: ")); | |||||
| Serial.println(maxLatency); | |||||
| Serial.print(F("Record time sec: ")); | |||||
| Serial.println(0.001*(t1 - t0), 3); | |||||
| Serial.print(minDelta); | |||||
| Serial.print(F(" <= jitter microseconds <= ")); | |||||
| Serial.println(maxDelta); | |||||
| Serial.print(F("Sample count: ")); | |||||
| Serial.println(count); | |||||
| Serial.print(F("Samples/sec: ")); | |||||
| Serial.println((1000.0)*count/(t1-t0)); | |||||
| Serial.print(F("Overruns: ")); | |||||
| Serial.println(overrunTotal); | |||||
| Serial.println(F("Done")); | |||||
| Serial.print("File size: "); | |||||
| Serial.print(binFile.fileSize()/512); | |||||
| Serial.println(F(" blocks")); | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void testSensor() { | |||||
| const uint32_t interval = 200000; | |||||
| int32_t diff; | |||||
| data_t data; | |||||
| Serial.println(F("\nTesting - type any character to stop\n")); | |||||
| // Wait for Serial Idle. | |||||
| delay(1000); | |||||
| printHeader(&Serial); | |||||
| uint32_t m = micros(); | |||||
| while (!Serial.available()) { | |||||
| m += interval; | |||||
| do { | |||||
| diff = m - micros(); | |||||
| } while (diff > 0); | |||||
| acquireData(&data); | |||||
| printData(&Serial, &data); | |||||
| } | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void setup(void) { | void setup(void) { | ||||
| while (!Serial) { | while (!Serial) { | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| Serial.print(F("FreeStack: ")); | Serial.print(F("FreeStack: ")); | ||||
| Serial.println(FreeStack()); | Serial.println(FreeStack()); | ||||
| Serial.print(F("Records/block: ")); | Serial.print(F("Records/block: ")); | ||||
| if (sizeof(block_t) != 512) { | if (sizeof(block_t) != 512) { | ||||
| error("Invalid block size"); | error("Invalid block size"); | ||||
| } | } | ||||
| // initialize file system. | |||||
| if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||||
| sd.initErrorPrint(); | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorPrint(&Serial); | |||||
| fatalBlink(); | fatalBlink(); | ||||
| } | } | ||||
| setupADXL345(); | |||||
| userSetup(); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void loop(void) { | void loop(void) { | ||||
| // discard any input | |||||
| // Read any Serial data. | |||||
| do { | do { | ||||
| delay(10); | delay(10); | ||||
| } while (Serial.read() >= 0); | |||||
| } while (Serial.available() && Serial.read() >= 0); | |||||
| Serial.println(); | Serial.println(); | ||||
| Serial.println(F("type:")); | Serial.println(F("type:")); | ||||
| Serial.println(F("b - open existing bin file")); | |||||
| Serial.println(F("c - convert file to csv")); | Serial.println(F("c - convert file to csv")); | ||||
| Serial.println(F("d - dump data to Serial")); | Serial.println(F("d - dump data to Serial")); | ||||
| Serial.println(F("e - overrun error details")); | Serial.println(F("e - overrun error details")); | ||||
| Serial.println(F("l - list files")); | |||||
| Serial.println(F("r - record data")); | Serial.println(F("r - record data")); | ||||
| Serial.println(F("t - test without logging")); | |||||
| while(!Serial.available()) { | while(!Serial.available()) { | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| #if WDT_YIELD_TIME_MICROS | |||||
| Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||||
| SysCall::halt(); | |||||
| #endif | |||||
| char c = tolower(Serial.read()); | char c = tolower(Serial.read()); | ||||
| // Discard extra Serial data. | // Discard extra Serial data. | ||||
| do { | do { | ||||
| delay(10); | delay(10); | ||||
| } while (Serial.read() >= 0); | |||||
| } while (Serial.available() && Serial.read() >= 0); | |||||
| if (ERROR_LED_PIN >= 0) { | if (ERROR_LED_PIN >= 0) { | ||||
| digitalWrite(ERROR_LED_PIN, LOW); | digitalWrite(ERROR_LED_PIN, LOW); | ||||
| } | } | ||||
| if (c == 'c') { | |||||
| if (c == 'b') { | |||||
| openBinFile(); | |||||
| } else if (c == 'c') { | |||||
| binaryToCsv(); | binaryToCsv(); | ||||
| } else if (c == 'd') { | } else if (c == 'd') { | ||||
| dumpData(); | dumpData(); | ||||
| } else if (c == 'e') { | } else if (c == 'e') { | ||||
| checkOverrun(); | checkOverrun(); | ||||
| } else if (c == 'l') { | |||||
| Serial.println(F("\nls:")); | |||||
| sd.ls(&Serial, LS_SIZE); | |||||
| } else if (c == 'r') { | } else if (c == 'r') { | ||||
| logData(); | logData(); | ||||
| } else if (c == 't') { | |||||
| testSensor(); | |||||
| } else { | } else { | ||||
| Serial.println(F("Invalid entry")); | Serial.println(F("Invalid entry")); | ||||
| } | } |
| // Empty file with name LowLatencyLoggerADXL345.ino to make IDE happy. |
| #include "UserTypes.h" | |||||
| // User data functions. Modify these functions for your data items. | |||||
| // Start time for data | |||||
| static uint32_t startMicros; | |||||
| const uint8_t ADXL345_CS = 9; | |||||
| const uint8_t POWER_CTL = 0x2D; //Power Control Register | |||||
| const uint8_t DATA_FORMAT = 0x31; | |||||
| const uint8_t DATAX0 = 0x32; //X-Axis Data 0 | |||||
| const uint8_t DATAX1 = 0x33; //X-Axis Data 1 | |||||
| const uint8_t DATAY0 = 0x34; //Y-Axis Data 0 | |||||
| const uint8_t DATAY1 = 0x35; //Y-Axis Data 1 | |||||
| const uint8_t DATAZ0 = 0x36; //Z-Axis Data 0 | |||||
| const uint8_t DATAZ1 = 0x37; //Z-Axis Data 1 | |||||
| void writeADXL345Register(const uint8_t registerAddress, const uint8_t value) { | |||||
| digitalWrite(ADXL345_CS, LOW); | |||||
| SPI.transfer(registerAddress); | |||||
| SPI.transfer(value); | |||||
| digitalWrite(ADXL345_CS, HIGH); | |||||
| } | |||||
| void userSetup() { | |||||
| SPI.begin(); | |||||
| pinMode(ADXL345_CS, OUTPUT); | |||||
| digitalWrite(ADXL345_CS, HIGH); | |||||
| //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register. | |||||
| writeADXL345Register(DATA_FORMAT, 0x01); | |||||
| //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register. | |||||
| writeADXL345Register(POWER_CTL, 0x08); //Measurement mode | |||||
| } | |||||
| // Acquire a data record. | |||||
| void acquireData(data_t* data) { | |||||
| // Max SPI clock frequency is 5 MHz with CPOL = 1 and CPHA = 1. | |||||
| SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE3)); | |||||
| data->time = micros(); | |||||
| digitalWrite(ADXL345_CS, LOW); | |||||
| // Read multiple bytes so or 0XC0 with address. | |||||
| SPI.transfer(DATAX0 | 0XC0); | |||||
| data->accel[0] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
| data->accel[1] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
| data->accel[2] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
| digitalWrite(ADXL345_CS, HIGH); | |||||
| SPI.endTransaction(); | |||||
| } | |||||
| // Print a data record. | |||||
| void printData(Print* pr, data_t* data) { | |||||
| if (startMicros == 0) { | |||||
| startMicros = data->time; | |||||
| } | |||||
| pr->print(data->time - startMicros); | |||||
| for (int i = 0; i < ACCEL_DIM; i++) { | |||||
| pr->write(','); | |||||
| pr->print(data->accel[i]); | |||||
| } | |||||
| pr->println(); | |||||
| } | |||||
| // Print data header. | |||||
| void printHeader(Print* pr) { | |||||
| startMicros = 0; | |||||
| pr->println(F("micros,ax,ay,az")); | |||||
| } |
| #ifndef UserTypes_h | |||||
| #define UserTypes_h | |||||
| #include "Arduino.h" | |||||
| #include "SPI.h" | |||||
| #define USE_SHARED_SPI 1 | |||||
| #define FILE_BASE_NAME "ADXL4G" | |||||
| // User data types. Modify for your data items. | |||||
| const uint8_t ACCEL_DIM = 3; | |||||
| struct data_t { | |||||
| unsigned long time; | |||||
| int16_t accel[ACCEL_DIM]; | |||||
| }; | |||||
| void acquireData(data_t* data); | |||||
| void printData(Print* pr, data_t* data); | |||||
| void printHeader(Print* pr); | |||||
| void userSetup(); | |||||
| #endif // UserTypes_h |
| /** | |||||
| * This program logs data to a binary file. Functions are included | |||||
| * to convert the binary file to a csv text file. | |||||
| * | |||||
| * Samples are logged at regular intervals. The maximum logging rate | |||||
| * depends on the quality of your SD card and the time required to | |||||
| * read sensor data. This example has been tested at 500 Hz with | |||||
| * good SD card on an Uno. 4000 HZ is possible on a Due. | |||||
| * | |||||
| * If your SD card has a long write latency, it may be necessary to use | |||||
| * slower sample rates. Using a Mega Arduino helps overcome latency | |||||
| * problems since 12 512 byte buffers will be used. | |||||
| * | |||||
| * Data is written to the file using a SD multiple block write command. | |||||
| */ | |||||
| #include <SPI.h> | |||||
| #include "SdFat.h" | |||||
| #include "FreeStack.h" | |||||
| #include "UserTypes.h" | |||||
| #ifdef __AVR_ATmega328P__ | |||||
| #include "MinimumSerial.h" | |||||
| MinimumSerial MinSerial; | |||||
| #define Serial MinSerial | |||||
| #endif // __AVR_ATmega328P__ | |||||
| //============================================================================== | |||||
| // Start of configuration constants. | |||||
| //============================================================================== | |||||
| // Abort run on an overrun. Data before the overrun will be saved. | |||||
| #define ABORT_ON_OVERRUN 1 | |||||
| //------------------------------------------------------------------------------ | |||||
| //Interval between data records in microseconds. | |||||
| const uint32_t LOG_INTERVAL_USEC = 2000; | |||||
| //------------------------------------------------------------------------------ | |||||
| // Set USE_SHARED_SPI non-zero for use of an SPI sensor. | |||||
| // May not work for some cards. | |||||
| #ifndef USE_SHARED_SPI | |||||
| #define USE_SHARED_SPI 0 | |||||
| #endif // USE_SHARED_SPI | |||||
| //------------------------------------------------------------------------------ | |||||
| // Pin definitions. | |||||
| // | |||||
| // SD chip select pin. | |||||
| const uint8_t SD_CS_PIN = SS; | |||||
| // | |||||
| // Digital pin to indicate an error, set to -1 if not used. | |||||
| // The led blinks for fatal errors. The led goes on solid for | |||||
| // overrun errors and logging continues unless ABORT_ON_OVERRUN | |||||
| // is non-zero. | |||||
| #ifdef ERROR_LED_PIN | |||||
| #undef ERROR_LED_PIN | |||||
| #endif // ERROR_LED_PIN | |||||
| const int8_t ERROR_LED_PIN = 3; | |||||
| //------------------------------------------------------------------------------ | |||||
| // File definitions. | |||||
| // | |||||
| // Maximum file size in blocks. | |||||
| // The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks. | |||||
| // This file is flash erased using special SD commands. The file will be | |||||
| // truncated if logging is stopped early. | |||||
| const uint32_t FILE_BLOCK_COUNT = 256000; | |||||
| // | |||||
| // log file base name if not defined in UserTypes.h | |||||
| #ifndef FILE_BASE_NAME | |||||
| #define FILE_BASE_NAME "data" | |||||
| #endif // FILE_BASE_NAME | |||||
| //------------------------------------------------------------------------------ | |||||
| // Buffer definitions. | |||||
| // | |||||
| // The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional | |||||
| // buffers. | |||||
| // | |||||
| #ifndef RAMEND | |||||
| // Assume ARM. Use total of ten 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 10; | |||||
| // | |||||
| #elif RAMEND < 0X8FF | |||||
| #error Too little SRAM | |||||
| // | |||||
| #elif RAMEND < 0X10FF | |||||
| // Use total of two 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 2; | |||||
| // | |||||
| #elif RAMEND < 0X20FF | |||||
| // Use total of four 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 4; | |||||
| // | |||||
| #else // RAMEND | |||||
| // Use total of 12 512 byte buffers. | |||||
| const uint8_t BUFFER_BLOCK_COUNT = 12; | |||||
| #endif // RAMEND | |||||
| //============================================================================== | |||||
| // End of configuration constants. | |||||
| //============================================================================== | |||||
| // Temporary log file. Will be deleted if a reset or power failure occurs. | |||||
| #define TMP_FILE_NAME "tmp_log.bin" | |||||
| // Size of file base name. Must not be larger than six. | |||||
| const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | |||||
| const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7; | |||||
| char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin"; | |||||
| SdFat sd; | |||||
| SdBaseFile binFile; | |||||
| // Number of data records in a block. | |||||
| const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | |||||
| //Compute fill so block size is 512 bytes. FILL_DIM may be zero. | |||||
| const uint16_t FILL_DIM = 512 - 4 - DATA_DIM*sizeof(data_t); | |||||
| struct block_t { | |||||
| uint16_t count; | |||||
| uint16_t overrun; | |||||
| data_t data[DATA_DIM]; | |||||
| uint8_t fill[FILL_DIM]; | |||||
| }; | |||||
| //============================================================================== | |||||
| // Error messages stored in flash. | |||||
| #define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();} | |||||
| //------------------------------------------------------------------------------ | |||||
| // | |||||
| void fatalBlink() { | |||||
| while (true) { | |||||
| SysCall::yield(); | |||||
| if (ERROR_LED_PIN >= 0) { | |||||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||||
| delay(200); | |||||
| digitalWrite(ERROR_LED_PIN, LOW); | |||||
| delay(200); | |||||
| } | |||||
| } | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| // read data file and check for overruns | |||||
| void checkOverrun() { | |||||
| bool headerPrinted = false; | |||||
| block_t block; | |||||
| uint32_t bn = 0; | |||||
| if (!binFile.isOpen()) { | |||||
| Serial.println(); | |||||
| Serial.println(F("No current binary file")); | |||||
| return; | |||||
| } | |||||
| binFile.rewind(); | |||||
| Serial.println(); | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| Serial.println(F("Checking overrun errors - type any character to stop")); | |||||
| while (binFile.read(&block, 512) == 512) { | |||||
| if (block.count == 0) { | |||||
| break; | |||||
| } | |||||
| if (block.overrun) { | |||||
| if (!headerPrinted) { | |||||
| Serial.println(); | |||||
| Serial.println(F("Overruns:")); | |||||
| Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||||
| headerPrinted = true; | |||||
| } | |||||
| Serial.print(bn); | |||||
| Serial.print(','); | |||||
| Serial.print(binFile.firstBlock() + bn); | |||||
| Serial.print(','); | |||||
| Serial.println(block.overrun); | |||||
| } | |||||
| bn++; | |||||
| } | |||||
| if (!headerPrinted) { | |||||
| Serial.println(F("No errors found")); | |||||
| } else { | |||||
| Serial.println(F("Done")); | |||||
| } | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| // Convert binary file to csv file. | |||||
| void binaryToCsv() { | |||||
| uint8_t lastPct = 0; | |||||
| block_t block; | |||||
| uint32_t t0 = millis(); | |||||
| uint32_t syncCluster = 0; | |||||
| SdFile csvFile; | |||||
| char csvName[FILE_NAME_DIM]; | |||||
| if (!binFile.isOpen()) { | |||||
| Serial.println(); | |||||
| Serial.println(F("No current binary file")); | |||||
| return; | |||||
| } | |||||
| Serial.println(); | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| // Create a new csvFile. | |||||
| strcpy(csvName, binName); | |||||
| strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||||
| if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | |||||
| error("open csvFile failed"); | |||||
| } | |||||
| binFile.rewind(); | |||||
| Serial.print(F("Writing: ")); | |||||
| Serial.print(csvName); | |||||
| Serial.println(F(" - type any character to stop")); | |||||
| printHeader(&csvFile); | |||||
| uint32_t tPct = millis(); | |||||
| while (!Serial.available() && binFile.read(&block, 512) == 512) { | |||||
| uint16_t i; | |||||
| if (block.count == 0) { | |||||
| break; | |||||
| } | |||||
| if (block.overrun) { | |||||
| csvFile.print(F("OVERRUN,")); | |||||
| csvFile.println(block.overrun); | |||||
| } | |||||
| for (i = 0; i < block.count; i++) { | |||||
| printData(&csvFile, &block.data[i]); | |||||
| } | |||||
| if (csvFile.curCluster() != syncCluster) { | |||||
| csvFile.sync(); | |||||
| syncCluster = csvFile.curCluster(); | |||||
| } | |||||
| if ((millis() - tPct) > 1000) { | |||||
| uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100); | |||||
| if (pct != lastPct) { | |||||
| tPct = millis(); | |||||
| lastPct = pct; | |||||
| Serial.print(pct, DEC); | |||||
| Serial.println('%'); | |||||
| } | |||||
| } | |||||
| if (Serial.available()) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| csvFile.close(); | |||||
| Serial.print(F("Done: ")); | |||||
| Serial.print(0.001*(millis() - t0)); | |||||
| Serial.println(F(" Seconds")); | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| void createBinFile() { | |||||
| // max number of blocks to erase per erase call | |||||
| const uint32_t ERASE_SIZE = 262144L; | |||||
| uint32_t bgnBlock, endBlock; | |||||
| Serial.println(); | |||||
| while (sd.exists(binName)) { | |||||
| if (binName[BASE_NAME_SIZE + 1] != '9') { | |||||
| binName[BASE_NAME_SIZE + 1]++; | |||||
| } else { | |||||
| binName[BASE_NAME_SIZE + 1] = '0'; | |||||
| if (binName[BASE_NAME_SIZE] == '9') { | |||||
| error("Can't create file name"); | |||||
| } | |||||
| binName[BASE_NAME_SIZE]++; | |||||
| } | |||||
| } | |||||
| // Delete old tmp file. | |||||
| if (sd.exists(TMP_FILE_NAME)) { | |||||
| Serial.println(F("Deleting tmp file")); | |||||
| if (!sd.remove(TMP_FILE_NAME)) { | |||||
| error("Can't remove tmp file"); | |||||
| } | |||||
| } | |||||
| // Create new file. | |||||
| Serial.println(F("Creating new file")); | |||||
| binFile.close(); | |||||
| if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
| error("createContiguous failed"); | |||||
| } | |||||
| // Get the address of the file on the SD. | |||||
| if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||||
| error("contiguousRange failed"); | |||||
| } | |||||
| // Flash erase all data in the file. | |||||
| Serial.println(F("Erasing all data")); | |||||
| uint32_t bgnErase = bgnBlock; | |||||
| uint32_t endErase; | |||||
| while (bgnErase < endBlock) { | |||||
| endErase = bgnErase + ERASE_SIZE; | |||||
| if (endErase > endBlock) { | |||||
| endErase = endBlock; | |||||
| } | |||||
| if (!sd.card()->erase(bgnErase, endErase)) { | |||||
| error("erase failed"); | |||||
| } | |||||
| bgnErase = endErase + 1; | |||||
| } | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| // dump data file to Serial | |||||
| void dumpData() { | |||||
| block_t block; | |||||
| if (!binFile.isOpen()) { | |||||
| Serial.println(); | |||||
| Serial.println(F("No current binary file")); | |||||
| return; | |||||
| } | |||||
| binFile.rewind(); | |||||
| Serial.println(); | |||||
| Serial.println(F("Type any character to stop")); | |||||
| delay(1000); | |||||
| printHeader(&Serial); | |||||
| while (!Serial.available() && binFile.read(&block , 512) == 512) { | |||||
| if (block.count == 0) { | |||||
| break; | |||||
| } | |||||
| if (block.overrun) { | |||||
| Serial.print(F("OVERRUN,")); | |||||
| Serial.println(block.overrun); | |||||
| } | |||||
| for (uint16_t i = 0; i < block.count; i++) { | |||||
| printData(&Serial, &block.data[i]); | |||||
| } | |||||
| } | |||||
| Serial.println(F("Done")); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| // log data | |||||
| void logData() { | |||||
| createBinFile(); | |||||
| recordBinFile(); | |||||
| renameBinFile(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void openBinFile() { | |||||
| char name[FILE_NAME_DIM]; | |||||
| strcpy(name, binName); | |||||
| Serial.println(F("\nEnter two digit version")); | |||||
| Serial.write(name, BASE_NAME_SIZE); | |||||
| for (int i = 0; i < 2; i++) { | |||||
| while (!Serial.available()) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| char c = Serial.read(); | |||||
| Serial.write(c); | |||||
| if (c < '0' || c > '9') { | |||||
| Serial.println("\nInvalid digit"); | |||||
| return; | |||||
| } | |||||
| name[BASE_NAME_SIZE + i] = c; | |||||
| } | |||||
| Serial.println(&name[BASE_NAME_SIZE+2]); | |||||
| if (!sd.exists(name)) { | |||||
| Serial.println(F("File does not exist")); | |||||
| return; | |||||
| } | |||||
| binFile.close(); | |||||
| strcpy(binName, name); | |||||
| if (!binFile.open(binName, O_READ)) { | |||||
| Serial.println(F("open failed")); | |||||
| return; | |||||
| } | |||||
| Serial.println(F("File opened")); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void recordBinFile() { | |||||
| const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||||
| // Index of last queue location. | |||||
| const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||||
| // Allocate extra buffer space. | |||||
| block_t block[BUFFER_BLOCK_COUNT - 1]; | |||||
| block_t* curBlock = 0; | |||||
| block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||||
| uint8_t emptyTop; | |||||
| uint8_t minTop; | |||||
| block_t* fullQueue[QUEUE_DIM]; | |||||
| uint8_t fullHead = 0; | |||||
| uint8_t fullTail = 0; | |||||
| // Use SdFat's internal buffer. | |||||
| emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||||
| if (emptyStack[0] == 0) { | |||||
| error("cacheClear failed"); | |||||
| } | |||||
| // Put rest of buffers on the empty stack. | |||||
| for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) { | |||||
| emptyStack[i] = &block[i - 1]; | |||||
| } | |||||
| emptyTop = BUFFER_BLOCK_COUNT; | |||||
| minTop = BUFFER_BLOCK_COUNT; | |||||
| // Start a multiple block write. | |||||
| if (!sd.card()->writeStart(binFile.firstBlock())) { | |||||
| error("writeBStart failed"); | |||||
| } | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| Serial.println(F("Logging - type any character to stop")); | |||||
| bool closeFile = false; | |||||
| uint32_t bn = 0; | |||||
| uint32_t maxLatency = 0; | |||||
| uint32_t overrun = 0; | |||||
| uint32_t overrunTotal = 0; | |||||
| uint32_t logTime = micros(); | |||||
| while(1) { | |||||
| // Time for next data record. | |||||
| logTime += LOG_INTERVAL_USEC; | |||||
| if (Serial.available()) { | |||||
| closeFile = true; | |||||
| } | |||||
| if (closeFile) { | |||||
| if (curBlock != 0) { | |||||
| // Put buffer in full queue. | |||||
| fullQueue[fullHead] = curBlock; | |||||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
| curBlock = 0; | |||||
| } | |||||
| } else { | |||||
| if (curBlock == 0 && emptyTop != 0) { | |||||
| curBlock = emptyStack[--emptyTop]; | |||||
| if (emptyTop < minTop) { | |||||
| minTop = emptyTop; | |||||
| } | |||||
| curBlock->count = 0; | |||||
| curBlock->overrun = overrun; | |||||
| overrun = 0; | |||||
| } | |||||
| if ((int32_t)(logTime - micros()) < 0) { | |||||
| error("Rate too fast"); | |||||
| } | |||||
| int32_t delta; | |||||
| do { | |||||
| delta = micros() - logTime; | |||||
| } while (delta < 0); | |||||
| if (curBlock == 0) { | |||||
| overrun++; | |||||
| overrunTotal++; | |||||
| if (ERROR_LED_PIN >= 0) { | |||||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||||
| } | |||||
| #if ABORT_ON_OVERRUN | |||||
| Serial.println(F("Overrun abort")); | |||||
| break; | |||||
| #endif // ABORT_ON_OVERRUN | |||||
| } else { | |||||
| #if USE_SHARED_SPI | |||||
| sd.card()->spiStop(); | |||||
| #endif // USE_SHARED_SPI | |||||
| acquireData(&curBlock->data[curBlock->count++]); | |||||
| #if USE_SHARED_SPI | |||||
| sd.card()->spiStart(); | |||||
| #endif // USE_SHARED_SPI | |||||
| if (curBlock->count == DATA_DIM) { | |||||
| fullQueue[fullHead] = curBlock; | |||||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
| curBlock = 0; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (fullHead == fullTail) { | |||||
| // Exit loop if done. | |||||
| if (closeFile) { | |||||
| break; | |||||
| } | |||||
| } else if (!sd.card()->isBusy()) { | |||||
| // Get address of block to write. | |||||
| block_t* pBlock = fullQueue[fullTail]; | |||||
| fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0; | |||||
| // Write block to SD. | |||||
| uint32_t usec = micros(); | |||||
| if (!sd.card()->writeData((uint8_t*)pBlock)) { | |||||
| error("write data failed"); | |||||
| } | |||||
| usec = micros() - usec; | |||||
| if (usec > maxLatency) { | |||||
| maxLatency = usec; | |||||
| } | |||||
| // Move block to empty queue. | |||||
| emptyStack[emptyTop++] = pBlock; | |||||
| bn++; | |||||
| if (bn == FILE_BLOCK_COUNT) { | |||||
| // File full so stop | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!sd.card()->writeStop()) { | |||||
| error("writeStop failed"); | |||||
| } | |||||
| Serial.print(F("Min Free buffers: ")); | |||||
| Serial.println(minTop); | |||||
| Serial.print(F("Max block write usec: ")); | |||||
| Serial.println(maxLatency); | |||||
| Serial.print(F("Overruns: ")); | |||||
| Serial.println(overrunTotal); | |||||
| // Truncate file if recording stopped early. | |||||
| if (bn != FILE_BLOCK_COUNT) { | |||||
| Serial.println(F("Truncating file")); | |||||
| if (!binFile.truncate(512L * bn)) { | |||||
| error("Can't truncate file"); | |||||
| } | |||||
| } | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| void renameBinFile() { | |||||
| if (!binFile.rename(sd.vwd(), binName)) { | |||||
| error("Can't rename file"); | |||||
| } | |||||
| Serial.print(F("File renamed: ")); | |||||
| Serial.println(binName); | |||||
| Serial.print("File size: "); | |||||
| Serial.print(binFile.fileSize()/512); | |||||
| Serial.println(F(" blocks")); | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void testSensor() { | |||||
| const uint32_t interval = 200000; | |||||
| int32_t diff; | |||||
| data_t data; | |||||
| Serial.println(F("\nTesting - type any character to stop\n")); | |||||
| // Wait for Serial Idle. | |||||
| delay(1000); | |||||
| printHeader(&Serial); | |||||
| uint32_t m = micros(); | |||||
| while (!Serial.available()) { | |||||
| m += interval; | |||||
| do { | |||||
| diff = m - micros(); | |||||
| } while (diff > 0); | |||||
| acquireData(&data); | |||||
| printData(&Serial, &data); | |||||
| } | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void setup(void) { | |||||
| if (ERROR_LED_PIN >= 0) { | |||||
| pinMode(ERROR_LED_PIN, OUTPUT); | |||||
| } | |||||
| Serial.begin(9600); | |||||
| // Wait for USB Serial | |||||
| while (!Serial) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| Serial.print(F("Records/block: ")); | |||||
| Serial.println(DATA_DIM); | |||||
| if (sizeof(block_t) != 512) { | |||||
| error("Invalid block size"); | |||||
| } | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorPrint(&Serial); | |||||
| fatalBlink(); | |||||
| } | |||||
| userSetup(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void loop(void) { | |||||
| // Read any Serial data. | |||||
| do { | |||||
| delay(10); | |||||
| } while (Serial.available() && Serial.read() >= 0); | |||||
| Serial.println(); | |||||
| Serial.println(F("type:")); | |||||
| Serial.println(F("b - open existing bin file")); | |||||
| Serial.println(F("c - convert file to csv")); | |||||
| Serial.println(F("d - dump data to Serial")); | |||||
| Serial.println(F("e - overrun error details")); | |||||
| Serial.println(F("l - list files")); | |||||
| Serial.println(F("r - record data")); | |||||
| Serial.println(F("t - test without logging")); | |||||
| while(!Serial.available()) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| #if WDT_YIELD_TIME_MICROS | |||||
| Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||||
| SysCall::halt(); | |||||
| #endif | |||||
| char c = tolower(Serial.read()); | |||||
| // Discard extra Serial data. | |||||
| do { | |||||
| delay(10); | |||||
| } while (Serial.available() && Serial.read() >= 0); | |||||
| if (ERROR_LED_PIN >= 0) { | |||||
| digitalWrite(ERROR_LED_PIN, LOW); | |||||
| } | |||||
| if (c == 'b') { | |||||
| openBinFile(); | |||||
| } else if (c == 'c') { | |||||
| binaryToCsv(); | |||||
| } else if (c == 'd') { | |||||
| dumpData(); | |||||
| } else if (c == 'e') { | |||||
| checkOverrun(); | |||||
| } else if (c == 'l') { | |||||
| Serial.println(F("\nls:")); | |||||
| sd.ls(&Serial, LS_SIZE); | |||||
| } else if (c == 'r') { | |||||
| logData(); | |||||
| } else if (c == 't') { | |||||
| testSensor(); | |||||
| } else { | |||||
| Serial.println(F("Invalid entry")); | |||||
| } | |||||
| } |
| // Empty file with name LowLatencyLoggerMPU6050.ino to make IDE happy. | |||||
| // User data functions. Modify these functions for your data items. | |||||
| #include "UserTypes.h" | |||||
| #include "Wire.h" | |||||
| #include "I2Cdev.h" | |||||
| #include "MPU6050.h" | |||||
| //------------------------------------------------------------------------------ | |||||
| MPU6050 mpu; | |||||
| static uint32_t startMicros; | |||||
| // Acquire a data record. | |||||
| void acquireData(data_t* data) { | |||||
| data->time = micros(); | |||||
| mpu.getMotion6(&data->ax, &data->ay, &data->az, | |||||
| &data->gx, &data->gy, &data->gz); | |||||
| } | |||||
| // setup AVR I2C | |||||
| void userSetup() { | |||||
| #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE | |||||
| Wire.begin(); | |||||
| Wire.setClock(400000); | |||||
| #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE | |||||
| Fastwire::setup(400, true); | |||||
| #endif | |||||
| mpu.initialize(); | |||||
| } | |||||
| // Print a data record. | |||||
| void printData(Print* pr, data_t* data) { | |||||
| if (startMicros == 0) { | |||||
| startMicros = data->time; | |||||
| } | |||||
| pr->print(data->time- startMicros); | |||||
| pr->write(','); | |||||
| pr->print(data->ax); | |||||
| pr->write(','); | |||||
| pr->print(data->ay); | |||||
| pr->write(','); | |||||
| pr->print(data->az); | |||||
| pr->write(','); | |||||
| pr->print(data->gx); | |||||
| pr->write(','); | |||||
| pr->print(data->gy); | |||||
| pr->write(','); | |||||
| pr->println(data->gz); | |||||
| } | |||||
| // Print data header. | |||||
| void printHeader(Print* pr) { | |||||
| startMicros = 0; | |||||
| pr->println(F("micros,ax,ay,az,gx,gy,gz")); | |||||
| } |
| #ifndef UserTypes_h | |||||
| #define UserTypes_h | |||||
| #include "Arduino.h" | |||||
| #define FILE_BASE_NAME "mpuraw" | |||||
| struct data_t { | |||||
| unsigned long time; | |||||
| int16_t ax; | |||||
| int16_t ay; | |||||
| int16_t az; | |||||
| int16_t gx; | |||||
| int16_t gy; | |||||
| int16_t gz; | |||||
| }; | |||||
| void acquireData(data_t* data); | |||||
| void printData(Print* pr, data_t* data); | |||||
| void printHeader(Print* pr); | |||||
| void userSetup(); | |||||
| #endif // UserTypes_h |
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| cout << F("FreeStack: ") << FreeStack() << endl; | cout << F("FreeStack: ") << FreeStack() << endl; | ||||
| // initialize the SD card at SPI_FULL_SPEED for best performance. | |||||
| // try SPI_HALF_SPEED if bus errors occur. | |||||
| if (!sd.begin(chipSelect, SPI_FULL_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| // to 10 to disable the Ethernet controller. | // to 10 to disable the Ethernet controller. | ||||
| const int8_t DISABLE_CHIP_SELECT = -1; | const int8_t DISABLE_CHIP_SELECT = -1; | ||||
| // | // | ||||
| // Test with reduced SPI speed for breadboards. | |||||
| // Change spiSpeed to SPI_FULL_SPEED for better performance | |||||
| // Use SPI_QUARTER_SPEED for even slower SPI bus speed | |||||
| const uint8_t spiSpeed = SPI_HALF_SPEED; | |||||
| // Test with reduced SPI speed for breadboards. SD_SCK_MHZ(4) will select | |||||
| // the highest speed supported by the board that is not over 4 MHz. | |||||
| // Change SPI_SPEED to SD_SCK_MHZ(50) for best performance. | |||||
| #define SPI_SPEED SD_SCK_MHZ(4) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // File system object. | // File system object. | ||||
| SdFat sd; | SdFat sd; | ||||
| void cardOrSpeed() { | void cardOrSpeed() { | ||||
| cout << F("Try another SD card or reduce the SPI bus speed.\n"); | cout << F("Try another SD card or reduce the SPI bus speed.\n"); | ||||
| cout << F("Edit spiSpeed in this program to change it.\n"); | |||||
| cout << F("Edit SPI_SPEED in this program to change it.\n"); | |||||
| } | } | ||||
| void reformatMsg() { | void reformatMsg() { | ||||
| pinMode(DISABLE_CHIP_SELECT, OUTPUT); | pinMode(DISABLE_CHIP_SELECT, OUTPUT); | ||||
| digitalWrite(DISABLE_CHIP_SELECT, HIGH); | digitalWrite(DISABLE_CHIP_SELECT, HIGH); | ||||
| } | } | ||||
| if (!sd.begin(chipSelect, spiSpeed)) { | |||||
| if (!sd.begin(chipSelect, SPI_SPEED)) { | |||||
| if (sd.card()->errorCode()) { | if (sd.card()->errorCode()) { | ||||
| cout << F( | cout << F( | ||||
| "\nSD initialization failed.\n" | "\nSD initialization failed.\n" |
| * can be used for high speed data logging. | * can be used for high speed data logging. | ||||
| * | * | ||||
| * This program simulates logging from a source that produces | * This program simulates logging from a source that produces | ||||
| * data at a constant rate of one block every MICROS_PER_BLOCK. | |||||
| * | |||||
| * If a high quality SanDisk card is used with this program | |||||
| * no overruns occur and the maximum block write time is | |||||
| * under 2000 micros. | |||||
| * data at a constant rate of RATE_KB_PER_SEC. | |||||
| * | * | ||||
| * Note: Apps should create a very large file then truncates it | * Note: Apps should create a very large file then truncates it | ||||
| * to the length that is used for a logging. It only takes | * to the length that is used for a logging. It only takes | ||||
| // SD chip select pin | // SD chip select pin | ||||
| const uint8_t chipSelect = SS; | const uint8_t chipSelect = SS; | ||||
| // number of blocks in the contiguous file | |||||
| const uint32_t BLOCK_COUNT = 10000UL; | |||||
| const uint32_t RATE_KB_PER_SEC = 100; | |||||
| const uint32_t TEST_TIME_SEC = 100; | |||||
| // time to produce a block of data | |||||
| const uint32_t MICROS_PER_BLOCK = 10000; | |||||
| // Time between printing progress dots | |||||
| const uint32_t DOT_TIME_MS = 5000UL; | |||||
| // number of blocks in the contiguous file | |||||
| const uint32_t BLOCK_COUNT = (1000*RATE_KB_PER_SEC*TEST_TIME_SEC + 511)/512; | |||||
| // file system | // file system | ||||
| SdFat sd; | SdFat sd; | ||||
| // store error strings in flash to save RAM | // store error strings in flash to save RAM | ||||
| #define error(s) sd.errorHalt(F(s)) | #define error(s) sd.errorHalt(F(s)) | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // log of first overruns | |||||
| #define OVER_DIM 20 | |||||
| struct { | |||||
| uint32_t block; | |||||
| uint32_t micros; | |||||
| } over[OVER_DIM]; | |||||
| //------------------------------------------------------------------------------ | |||||
| void setup(void) { | void setup(void) { | ||||
| Serial.begin(9600); | Serial.begin(9600); | ||||
| cout << F("FreeStack: ") << FreeStack() << endl; | cout << F("FreeStack: ") << FreeStack() << endl; | ||||
| // initialize the SD card at SPI_FULL_SPEED for best performance. | |||||
| // try SPI_HALF_SPEED if bus errors occur. | |||||
| if (!sd.begin(chipSelect, SPI_FULL_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| sd.remove("RawWrite.txt"); | sd.remove("RawWrite.txt"); | ||||
| // create a contiguous file | // create a contiguous file | ||||
| if (!file.createContiguous(sd.vwd(), "RawWrite.txt", 512UL*BLOCK_COUNT)) { | |||||
| if (!file.createContiguous("RawWrite.txt", 512UL*BLOCK_COUNT)) { | |||||
| error("createContiguous failed"); | error("createContiguous failed"); | ||||
| } | } | ||||
| // get the location of the file's blocks | // get the location of the file's blocks | ||||
| pCache[i + 63] = '\n'; | pCache[i + 63] = '\n'; | ||||
| } | } | ||||
| cout << F("Start raw write of ") << file.fileSize() << F(" bytes at\n"); | |||||
| cout << 512000000UL/MICROS_PER_BLOCK << F(" bytes per second\n"); | |||||
| cout << F("Please wait ") << (BLOCK_COUNT*MICROS_PER_BLOCK)/1000000UL; | |||||
| cout << F(" seconds\n"); | |||||
| cout << F("Start raw write of ") << file.fileSize()/1000UL << F(" KB\n"); | |||||
| cout << F("Target rate: ") << RATE_KB_PER_SEC << F(" KB/sec\n"); | |||||
| cout << F("Target time: ") << TEST_TIME_SEC << F(" seconds\n"); | |||||
| // tell card to setup for multiple block write with pre-erase | // tell card to setup for multiple block write with pre-erase | ||||
| if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) { | if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) { | ||||
| error("writeStart failed"); | error("writeStart failed"); | ||||
| } | } | ||||
| // init stats | // init stats | ||||
| uint16_t overruns = 0; | |||||
| delay(1000); | |||||
| uint32_t dotCount = 0; | |||||
| uint32_t maxQueuePrint = 0; | |||||
| uint32_t maxWriteTime = 0; | uint32_t maxWriteTime = 0; | ||||
| uint32_t t = micros(); | |||||
| uint32_t tNext = t; | |||||
| uint32_t minWriteTime = 9999999; | |||||
| uint32_t totalWriteTime = 0; | |||||
| uint32_t maxQueueSize = 0; | |||||
| uint32_t nWrite = 0; | |||||
| uint32_t b = 0; | |||||
| // write data | // write data | ||||
| for (uint32_t b = 0; b < BLOCK_COUNT; b++) { | |||||
| // write must be done by this time | |||||
| tNext += MICROS_PER_BLOCK; | |||||
| uint32_t startTime = millis(); | |||||
| while (nWrite < BLOCK_COUNT) { | |||||
| uint32_t nProduced = RATE_KB_PER_SEC*(millis() - startTime)/512UL; | |||||
| uint32_t queueSize = nProduced - nWrite; | |||||
| if (queueSize == 0) continue; | |||||
| if (queueSize > maxQueueSize) { | |||||
| maxQueueSize = queueSize; | |||||
| } | |||||
| if ((millis() - startTime - dotCount*DOT_TIME_MS) > DOT_TIME_MS) { | |||||
| if (maxQueueSize != maxQueuePrint) { | |||||
| cout << F("\nQ: ") << maxQueueSize << endl; | |||||
| maxQueuePrint = maxQueueSize; | |||||
| } else { | |||||
| cout << "."; | |||||
| if (++dotCount%10 == 0) { | |||||
| cout << endl; | |||||
| } | |||||
| } | |||||
| } | |||||
| // put block number at start of first line in block | // put block number at start of first line in block | ||||
| uint32_t n = b; | |||||
| uint32_t n = b++; | |||||
| for (int8_t d = 5; d >= 0; d--) { | for (int8_t d = 5; d >= 0; d--) { | ||||
| pCache[d] = n || d == 5 ? n % 10 + '0' : ' '; | pCache[d] = n || d == 5 ? n % 10 + '0' : ' '; | ||||
| n /= 10; | n /= 10; | ||||
| error("writeData failed"); | error("writeData failed"); | ||||
| } | } | ||||
| tw = micros() - tw; | tw = micros() - tw; | ||||
| totalWriteTime += tw; | |||||
| // check for max write time | // check for max write time | ||||
| if (tw > maxWriteTime) { | if (tw > maxWriteTime) { | ||||
| maxWriteTime = tw; | maxWriteTime = tw; | ||||
| } | } | ||||
| // check for overrun | |||||
| if (micros() > tNext) { | |||||
| if (overruns < OVER_DIM) { | |||||
| over[overruns].block = b; | |||||
| over[overruns].micros = tw; | |||||
| } | |||||
| overruns++; | |||||
| // advance time to reflect overrun | |||||
| tNext = micros(); | |||||
| } else { | |||||
| // wait for time to write next block | |||||
| while(micros() < tNext); | |||||
| if (tw < minWriteTime) { | |||||
| minWriteTime = tw; | |||||
| } | } | ||||
| nWrite++; | |||||
| } | } | ||||
| // total write time | |||||
| t = micros() - t; | |||||
| uint32_t endTime = millis(); | |||||
| uint32_t avgWriteTime = totalWriteTime/BLOCK_COUNT; | |||||
| // end multiple block write mode | // end multiple block write mode | ||||
| if (!sd.card()->writeStop()) { | if (!sd.card()->writeStop()) { | ||||
| error("writeStop failed"); | error("writeStop failed"); | ||||
| } | } | ||||
| cout << F("Done\n"); | |||||
| cout << F("Elapsed time: ") << setprecision(3)<< 1.e-6*t; | |||||
| cout << F("\nDone\n"); | |||||
| cout << F("maxQueueSize: ") << maxQueueSize << endl; | |||||
| cout << F("Elapsed time: ") << setprecision(3)<< 1.e-3*(endTime - startTime); | |||||
| cout << F(" seconds\n"); | cout << F(" seconds\n"); | ||||
| cout << F("Max write time: ") << maxWriteTime << F(" micros\n"); | |||||
| cout << F("Overruns: ") << overruns << endl; | |||||
| if (overruns) { | |||||
| uint8_t n = overruns > OVER_DIM ? OVER_DIM : overruns; | |||||
| cout << F("fileBlock,micros") << endl; | |||||
| for (uint8_t i = 0; i < n; i++) { | |||||
| cout << over[i].block << ',' << over[i].micros << endl; | |||||
| } | |||||
| } | |||||
| cout << F("Min block write time: ") << minWriteTime << F(" micros\n"); | |||||
| cout << F("Max block write time: ") << maxWriteTime << F(" micros\n"); | |||||
| cout << F("Avg block write time: ") << avgWriteTime << F(" micros\n"); | |||||
| // close file for next pass of loop | // close file for next pass of loop | ||||
| file.close(); | file.close(); | ||||
| Serial.println(); | Serial.println(); |
| // Function to read a CSV text file one field at a time. | |||||
| // | |||||
| #include <SPI.h> | |||||
| #include <SdFat.h> | |||||
| #define CS_PIN SS | |||||
| SdFat SD; | |||||
| File file; | |||||
| /* | |||||
| * Read a file one field at a time. | |||||
| * | |||||
| * file - File to read. | |||||
| * | |||||
| * str - Character array for the field. | |||||
| * | |||||
| * size - Size of str array. | |||||
| * | |||||
| * delim - String containing field delimiters. | |||||
| * | |||||
| * return - length of field including terminating delimiter. | |||||
| * | |||||
| * Note, the last character of str will not be a delimiter if | |||||
| * a read error occurs, the field is too long, or the file | |||||
| * does not end with a delimiter. Consider this an error | |||||
| * if not at end-of-file. | |||||
| * | |||||
| */ | |||||
| size_t readField(File* file, char* str, size_t size, const char* delim) { | |||||
| char ch; | |||||
| size_t n = 0; | |||||
| while ((n + 1) < size && file->read(&ch, 1) == 1) { | |||||
| // Delete CR. | |||||
| if (ch == '\r') { | |||||
| continue; | |||||
| } | |||||
| str[n++] = ch; | |||||
| if (strchr(delim, ch)) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| str[n] = '\0'; | |||||
| return n; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| #define errorHalt(msg) {Serial.println(F(msg)); SysCall::halt();} | |||||
| //------------------------------------------------------------------------------ | |||||
| void setup() { | |||||
| Serial.begin(9600); | |||||
| // Wait for USB Serial | |||||
| while (!Serial) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| Serial.println("Type any character to start"); | |||||
| while (!Serial.available()) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| // Initialize the SD. | |||||
| if (!SD.begin(CS_PIN)) errorHalt("begin failed"); | |||||
| // Create or open the file. | |||||
| file = SD.open("READTEST.TXT", FILE_WRITE); | |||||
| if (!file) errorHalt("open failed"); | |||||
| // Rewind file so test data is not appended. | |||||
| file.rewind(); | |||||
| // Write test data. | |||||
| file.print(F( | |||||
| "field_1_1,field_1_2,field_1_3\r\n" | |||||
| "field_2_1,field_2_2,field_2_3\r\n" | |||||
| "field_3_1,field_3_2\r\n" // missing a field | |||||
| "field_4_1,field_4_2,field_4_3\r\n" | |||||
| "field_5_1,field_5_2,field_5_3" // no delimiter | |||||
| )); | |||||
| // Rewind the file for read. | |||||
| file.rewind(); | |||||
| size_t n; // Length of returned field with delimiter. | |||||
| char str[20]; // Must hold longest field with delimiter and zero byte. | |||||
| // Read the file and print fields. | |||||
| while (true) { | |||||
| n = readField(&file, str, sizeof(str), ",\n"); | |||||
| // done if Error or at EOF. | |||||
| if (n == 0) break; | |||||
| // Print the type of delimiter. | |||||
| if (str[n-1] == ',' || str[n-1] == '\n') { | |||||
| Serial.print(str[n-1] == ',' ? F("comma: ") : F("endl: ")); | |||||
| // Remove the delimiter. | |||||
| str[n-1] = 0; | |||||
| } else { | |||||
| // At eof, too long, or read error. Too long is error. | |||||
| Serial.print(file.available() ? F("error: ") : F("eof: ")); | |||||
| } | |||||
| // Print the field. | |||||
| Serial.println(str); | |||||
| } | |||||
| file.close(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void loop() { | |||||
| } | |||||
| /* | |||||
| * This example reads a simple CSV, comma-separated values, file. | |||||
| * Each line of the file has a label and three values, a long and two floats. | |||||
| */ | |||||
| #include <SPI.h> | |||||
| #include "SdFat.h" | |||||
| // SD chip select pin | |||||
| const uint8_t chipSelect = SS; | |||||
| // file system object | |||||
| SdFat sd; | |||||
| // create Serial stream | |||||
| ArduinoOutStream cout(Serial); | |||||
| char fileName[] = "testfile.csv"; | |||||
| //------------------------------------------------------------------------------ | |||||
| // store error strings in flash to save RAM | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | |||||
| // read and print CSV test file | |||||
| void readFile() { | |||||
| long lg = 0; | |||||
| float f1, f2; | |||||
| char text[10]; | |||||
| char c1, c2, c3; // space for commas. | |||||
| // open input file | |||||
| ifstream sdin(fileName); | |||||
| // check for open error | |||||
| if (!sdin.is_open()) { | |||||
| error("open"); | |||||
| } | |||||
| // read until input fails | |||||
| while (1) { | |||||
| // Get text field. | |||||
| sdin.get(text, sizeof(text), ','); | |||||
| // Assume EOF if fail. | |||||
| if (sdin.fail()) { | |||||
| break; | |||||
| } | |||||
| // Get commas and numbers. | |||||
| sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2; | |||||
| // Skip CR/LF. | |||||
| sdin.skipWhite(); | |||||
| if (sdin.fail()) { | |||||
| error("bad input"); | |||||
| } | |||||
| // error in line if not commas | |||||
| if (c1 != ',' || c2 != ',' || c3 != ',') { | |||||
| error("comma"); | |||||
| } | |||||
| // print in six character wide columns | |||||
| cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl; | |||||
| } | |||||
| // Error in an input line if file is not at EOF. | |||||
| if (!sdin.eof()) { | |||||
| error("readFile"); | |||||
| } | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| // write test file | |||||
| void writeFile() { | |||||
| // create or open and truncate output file | |||||
| ofstream sdout(fileName); | |||||
| // write file from string stored in flash | |||||
| sdout << F( | |||||
| "Line 1,1,2.3,4.5\n" | |||||
| "Line 2,6,7.8,9.0\n" | |||||
| "Line 3,9,8.7,6.5\n" | |||||
| "Line 4,-4,-3.2,-1\n") << flush; | |||||
| // check for any errors | |||||
| if (!sdout) { | |||||
| error("writeFile"); | |||||
| } | |||||
| sdout.close(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void setup() { | |||||
| Serial.begin(9600); | |||||
| // Wait for USB Serial | |||||
| while (!Serial) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| cout << F("Type any character to start\n"); | |||||
| while (!Serial.available()) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | |||||
| } | |||||
| // create test file | |||||
| writeFile(); | |||||
| cout << endl; | |||||
| // read and print test | |||||
| readFile(); | |||||
| cout << "\nDone!" << endl; | |||||
| } | |||||
| void loop() {} |
| ** MOSI - pin 11 | ** MOSI - pin 11 | ||||
| ** MISO - pin 12 | ** MISO - pin 12 | ||||
| ** CLK - pin 13 | ** CLK - pin 13 | ||||
| ** CS - pin 4 | |||||
| created Nov 2010 | created Nov 2010 | ||||
| by David A. Mellis | by David A. Mellis | ||||
| This example code is in the public domain. | This example code is in the public domain. | ||||
| */ | */ | ||||
| #define SD_CS_PIN SS | |||||
| #include <SPI.h> | #include <SPI.h> | ||||
| //#include <SD.h> | //#include <SD.h> | ||||
| #include "SdFat.h" | #include "SdFat.h" | ||||
| SdFat SD; | SdFat SD; | ||||
| #define SD_CS_PIN SS | |||||
| File myFile; | File myFile; | ||||
| void setup() | |||||
| { | |||||
| void setup() { | |||||
| // Open serial communications and wait for port to open: | // Open serial communications and wait for port to open: | ||||
| Serial.begin(9600); | Serial.begin(9600); | ||||
| // Wait for USB Serial | |||||
| while (!Serial) { | while (!Serial) { | ||||
| SysCall::yield(); | |||||
| ; // wait for serial port to connect. Needed for native USB port only | |||||
| } | } | ||||
| Serial.print("Initializing SD card..."); | Serial.print("Initializing SD card..."); | ||||
| // On the Ethernet Shield, CS is pin 4. It's set as an output by default. | |||||
| // Note that even if it's not used as the CS pin, the hardware SS pin | |||||
| // (10 on most Arduino boards, 53 on the Mega) must be left as an output | |||||
| // or the SD library functions will not work. | |||||
| pinMode(10, OUTPUT); | |||||
| if (!SD.begin(SD_CS_PIN)) { | if (!SD.begin(SD_CS_PIN)) { | ||||
| Serial.println("initialization failed!"); | Serial.println("initialization failed!"); | ||||
| } | } | ||||
| } | } | ||||
| void loop() | |||||
| { | |||||
| void loop() { | |||||
| // nothing happens after setup | // nothing happens after setup | ||||
| } | } | ||||
| // Ported to SdFat from the native Arduino SD library example by Bill Greiman | |||||
| // On the Ethernet Shield, CS is pin 4. SdFat handles setting SS | |||||
| const int chipSelect = 4; | |||||
| /* | |||||
| SD card read/write | |||||
| This example shows how to read and write data to and from an SD card file | |||||
| The circuit: | |||||
| * SD card attached to SPI bus as follows: | |||||
| ** MOSI - pin 11 | |||||
| ** MISO - pin 12 | |||||
| ** CLK - pin 13 | |||||
| ** CS - pin 4 | |||||
| created Nov 2010 | |||||
| by David A. Mellis | |||||
| updated 2 Dec 2010 | |||||
| by Tom Igoe | |||||
| modified by Bill Greiman 11 Apr 2011 | |||||
| This example code is in the public domain. | |||||
| */ | |||||
| #include <SPI.h> | |||||
| #include "SdFat.h" | |||||
| SdFat sd; | |||||
| SdFile myFile; | |||||
| void setup() { | |||||
| Serial.begin(9600); | |||||
| // Wait for USB Serial | |||||
| while (!Serial) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| Serial.println("Type any character to start"); | |||||
| while (!Serial.available()) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| // Initialize SdFat or print a detailed error message and halt | |||||
| // Use half speed like the native library. | |||||
| // change to SPI_FULL_SPEED for more performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| sd.initErrorHalt(); | |||||
| } | |||||
| // open the file for write at end like the Native SD library | |||||
| if (!myFile.open("test.txt", O_RDWR | O_CREAT | O_AT_END)) { | |||||
| sd.errorHalt("opening test.txt for write failed"); | |||||
| } | |||||
| // if the file opened okay, write to it: | |||||
| Serial.print("Writing to test.txt..."); | |||||
| myFile.println("testing 1, 2, 3."); | |||||
| // close the file: | |||||
| myFile.close(); | |||||
| Serial.println("done."); | |||||
| // re-open the file for reading: | |||||
| if (!myFile.open("test.txt", O_READ)) { | |||||
| sd.errorHalt("opening test.txt for read failed"); | |||||
| } | |||||
| Serial.println("test.txt:"); | |||||
| // read from the file until there's nothing else in it: | |||||
| int data; | |||||
| while ((data = myFile.read()) >= 0) { | |||||
| Serial.write(data); | |||||
| } | |||||
| // close the file: | |||||
| myFile.close(); | |||||
| } | |||||
| void loop() { | |||||
| // nothing happens after setup | |||||
| } | |||||
| /* | |||||
| * Example use of two SPI ports on an STM32 board. | |||||
| * Note SPI speed is limited to 18 MHz. | |||||
| */ | |||||
| #include <SPI.h> | |||||
| #include "SdFat.h" | |||||
| #include "FreeStack.h" | |||||
| // set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes | |||||
| // Use first SPI port | |||||
| SdFat sd1(1); | |||||
| // SdFatEX sd1(1); | |||||
| const uint8_t SD1_CS = PA4; // chip select for sd1 | |||||
| // Use second SPI port | |||||
| SdFat sd2(2); | |||||
| // SdFatEX sd2(2); | |||||
| const uint8_t SD2_CS = PB12; // chip select for sd2 | |||||
| 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 setup() { | |||||
| Serial.begin(9600); | |||||
| // Wait for USB Serial | |||||
| while (!Serial) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| // fill buffer with known data | |||||
| for (size_t i = 0; i < sizeof(buf); i++) { | |||||
| buf[i] = i; | |||||
| } | |||||
| Serial.println(F("type any character to start")); | |||||
| while (!Serial.available()) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| // initialize the first card | |||||
| if (!sd1.begin(SD1_CS, SD_SCK_MHZ(18))) { | |||||
| sd1.initError("sd1:"); | |||||
| } | |||||
| // create Dir1 on sd1 if it does not exist | |||||
| if (!sd1.exists("/Dir1")) { | |||||
| if (!sd1.mkdir("/Dir1")) { | |||||
| sd1.errorExit("sd1.mkdir"); | |||||
| } | |||||
| } | |||||
| // initialize the second card | |||||
| if (!sd2.begin(SD2_CS, SD_SCK_MHZ(18))) { | |||||
| sd2.initError("sd2:"); | |||||
| } | |||||
| // create Dir2 on sd2 if it does not exist | |||||
| if (!sd2.exists("/Dir2")) { | |||||
| if (!sd2.mkdir("/Dir2")) { | |||||
| sd2.errorExit("sd2.mkdir"); | |||||
| } | |||||
| } | |||||
| // list root directory on both cards | |||||
| Serial.println(F("------sd1 root-------")); | |||||
| sd1.ls(); | |||||
| Serial.println(F("------sd2 root-------")); | |||||
| sd2.ls(); | |||||
| // make /Dir1 the default directory for sd1 | |||||
| if (!sd1.chdir("/Dir1")) { | |||||
| sd1.errorExit("sd1.chdir"); | |||||
| } | |||||
| // remove test.bin from /Dir1 directory of sd1 | |||||
| if (sd1.exists("test.bin")) { | |||||
| if (!sd1.remove("test.bin")) { | |||||
| sd2.errorExit("remove test.bin"); | |||||
| } | |||||
| } | |||||
| // make /Dir2 the default directory for sd2 | |||||
| if (!sd2.chdir("/Dir2")) { | |||||
| sd2.errorExit("sd2.chdir"); | |||||
| } | |||||
| // remove rename.bin from /Dir2 directory of sd2 | |||||
| if (sd2.exists("rename.bin")) { | |||||
| if (!sd2.remove("rename.bin")) { | |||||
| sd2.errorExit("remove rename.bin"); | |||||
| } | |||||
| } | |||||
| // list current directory on both cards | |||||
| Serial.println(F("------sd1 Dir1-------")); | |||||
| sd1.ls(); | |||||
| Serial.println(F("------sd2 Dir2-------")); | |||||
| sd2.ls(); | |||||
| Serial.println(F("---------------------")); | |||||
| // set the current working directory for open() to sd1 | |||||
| sd1.chvol(); | |||||
| // create or open /Dir1/test.bin and truncate it to zero length | |||||
| SdFile file1; | |||||
| if (!file1.open("test.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||||
| sd1.errorExit("file1"); | |||||
| } | |||||
| Serial.println(F("Writing test.bin to sd1")); | |||||
| // write data to /Dir1/test.bin on sd1 | |||||
| for (uint16_t i = 0; i < NWRITE; i++) { | |||||
| if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { | |||||
| sd1.errorExit("sd1.write"); | |||||
| } | |||||
| } | |||||
| // set the current working directory for open() to sd2 | |||||
| sd2.chvol(); | |||||
| // create or open /Dir2/copy.bin and truncate it to zero length | |||||
| SdFile file2; | |||||
| if (!file2.open("copy.bin", O_WRITE | O_CREAT | O_TRUNC)) { | |||||
| sd2.errorExit("file2"); | |||||
| } | |||||
| Serial.println(F("Copying test.bin to copy.bin")); | |||||
| // copy file1 to file2 | |||||
| file1.rewind(); | |||||
| uint32_t t = millis(); | |||||
| while (1) { | |||||
| int n = file1.read(buf, sizeof(buf)); | |||||
| if (n < 0) { | |||||
| sd1.errorExit("read1"); | |||||
| } | |||||
| if (n == 0) { | |||||
| break; | |||||
| } | |||||
| if ((int)file2.write(buf, n) != n) { | |||||
| sd2.errorExit("write2"); | |||||
| } | |||||
| } | |||||
| t = millis() - t; | |||||
| Serial.print(F("File size: ")); | |||||
| Serial.println(file2.fileSize()); | |||||
| Serial.print(F("Copy time: ")); | |||||
| Serial.print(t); | |||||
| Serial.println(F(" millis")); | |||||
| // close test.bin | |||||
| file1.close(); | |||||
| file2.close(); | |||||
| // list current directory on both cards | |||||
| Serial.println(F("------sd1 -------")); | |||||
| sd1.ls("/", LS_R | LS_DATE | LS_SIZE); | |||||
| Serial.println(F("------sd2 -------")); | |||||
| sd2.ls("/", LS_R | LS_DATE | LS_SIZE); | |||||
| Serial.println(F("---------------------")); | |||||
| Serial.println(F("Renaming copy.bin")); | |||||
| // rename the copy | |||||
| if (!sd2.rename("copy.bin", "rename.bin")) { | |||||
| sd2.errorExit("sd2.rename"); | |||||
| } | |||||
| // list current directory on both cards | |||||
| Serial.println(F("------sd1 -------")); | |||||
| sd1.ls("/", LS_R | LS_DATE | LS_SIZE); | |||||
| Serial.println(F("------sd2 -------")); | |||||
| sd2.ls("/", LS_R | LS_DATE | LS_SIZE); | |||||
| Serial.println(F("---------------------")); | |||||
| Serial.println(F("Done")); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void loop() {} |
| // Adafruit SD shields and modules: pin 10 | // Adafruit SD shields and modules: pin 10 | ||||
| const uint8_t chipSelect = SS; | const uint8_t chipSelect = SS; | ||||
| // Change spiSpeed to SPI_FULL_SPEED for better performance | |||||
| // Use SPI_QUARTER_SPEED for even slower SPI bus speed | |||||
| const uint8_t spiSpeed = SPI_HALF_SPEED; | |||||
| // Initialize at highest supported speed not over 50 MHz. | |||||
| // Reduce max speed if errors occur. | |||||
| #define SPI_SPEED SD_SCK_MHZ(50) | |||||
| // Serial output stream | // Serial output stream | ||||
| ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
| char fat16str[] = "FAT16 "; | char fat16str[] = "FAT16 "; | ||||
| char fat32str[] = "FAT32 "; | char fat32str[] = "FAT32 "; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| #define sdError(msg) sdError_F(F(msg)) | |||||
| void sdError_F(const __FlashStringHelper* str) { | |||||
| cout << F("error: "); | |||||
| cout << str << endl; | |||||
| #define sdError(msg) {cout << F("error: ") << F(msg) << endl; sdErrorHalt();} | |||||
| //------------------------------------------------------------------------------ | |||||
| void sdErrorHalt() { | |||||
| if (card.errorCode()) { | if (card.errorCode()) { | ||||
| cout << F("SD error: ") << hex << int(card.errorCode()); | cout << F("SD error: ") << hex << int(card.errorCode()); | ||||
| cout << ',' << int(card.errorData()) << dec << endl; | cout << ',' << int(card.errorData()) << dec << endl; | ||||
| return; | return; | ||||
| } | } | ||||
| if (!card.begin(chipSelect, spiSpeed)) { | |||||
| if (!card.begin(chipSelect, SPI_SPEED)) { | |||||
| cout << F( | cout << F( | ||||
| "\nSD initialization failure!\n" | "\nSD initialization failure!\n" | ||||
| "Is the SD card inserted correctly?\n" | "Is the SD card inserted correctly?\n" |
| uint32_t eraseSize; | uint32_t eraseSize; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // store error strings in flash | // store error strings in flash | ||||
| #define sdErrorMsg(msg) sdErrorMsg_F(F(msg)); | |||||
| void sdErrorMsg_F(const __FlashStringHelper* str) { | |||||
| cout << str << endl; | |||||
| if (sd.card()->errorCode()) { | |||||
| cout << F("SD errorCode: "); | |||||
| cout << hex << int(sd.card()->errorCode()) << endl; | |||||
| cout << F("SD errorData: "); | |||||
| cout << int(sd.card()->errorData()) << dec << endl; | |||||
| } | |||||
| } | |||||
| #define sdErrorMsg(msg) sd.errorPrint(F(msg)); | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| uint8_t cidDmp() { | uint8_t cidDmp() { | ||||
| cid_t cid; | cid_t cid; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // print partition table | // print partition table | ||||
| uint8_t partDmp() { | uint8_t partDmp() { | ||||
| cache_t *p = sd.vol()->cacheClear(); | |||||
| if (!p) { | |||||
| sdErrorMsg("cacheClear failed"); | |||||
| return false; | |||||
| } | |||||
| if (!sd.card()->readBlock(0, p->data)) { | |||||
| mbr_t mbr; | |||||
| if (!sd.card()->readBlock(0, (uint8_t*)&mbr)) { | |||||
| sdErrorMsg("read MBR failed"); | sdErrorMsg("read MBR failed"); | ||||
| return false; | return false; | ||||
| } | } | ||||
| for (uint8_t ip = 1; ip < 5; ip++) { | for (uint8_t ip = 1; ip < 5; ip++) { | ||||
| part_t *pt = &p->mbr.part[ip - 1]; | |||||
| part_t *pt = &mbr.part[ip - 1]; | |||||
| if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) { | if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) { | ||||
| cout << F("\nNo MBR. Assuming Super Floppy format.\n"); | cout << F("\nNo MBR. Assuming Super Floppy format.\n"); | ||||
| return true; | return true; | ||||
| cout << F("\nSD Partition Table\n"); | cout << F("\nSD Partition Table\n"); | ||||
| cout << F("part,boot,type,start,length\n"); | cout << F("part,boot,type,start,length\n"); | ||||
| for (uint8_t ip = 1; ip < 5; ip++) { | for (uint8_t ip = 1; ip < 5; ip++) { | ||||
| part_t *pt = &p->mbr.part[ip - 1]; | |||||
| part_t *pt = &mbr.part[ip - 1]; | |||||
| cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type); | cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type); | ||||
| cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl; | cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl; | ||||
| } | } | ||||
| } | } | ||||
| uint32_t t = millis(); | uint32_t t = millis(); | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.cardBegin(SD_CHIP_SELECT, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.cardBegin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) { | |||||
| sdErrorMsg("\ncardBegin failed"); | sdErrorMsg("\ncardBegin failed"); | ||||
| return; | return; | ||||
| } | } |
| // | // | ||||
| #include <SPI.h> | #include <SPI.h> | ||||
| #include "SdFat.h" | #include "SdFat.h" | ||||
| #if SD_SPI_CONFIGURATION >= 3 // Must be set in SdFat/SdFatConfig.h | |||||
| #if ENABLE_SOFTWARE_SPI_CLASS // Must be set in SdFat/SdFatConfig.h | |||||
| // | // | ||||
| // Pin numbers in templates must be constants. | // Pin numbers in templates must be constants. | ||||
| const uint8_t SOFT_MISO_PIN = 12; | const uint8_t SOFT_MISO_PIN = 12; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void loop() {} | void loop() {} | ||||
| #else // SD_SPI_CONFIGURATION >= 3 | |||||
| #error SD_SPI_CONFIGURATION must be set to 3 in SdFat/SdFatConfig.h | |||||
| #endif //SD_SPI_CONFIGURATION >= 3 | |||||
| #else // ENABLE_SOFTWARE_SPI_CLASS | |||||
| #error ENABLE_SOFTWARE_SPI_CLASS must be set non-zero in SdFat/SdFatConfig.h | |||||
| #endif //ENABLE_SOFTWARE_SPI_CLASS |
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| Serial.println(F("Starting test")); | Serial.println(F("Starting test")); | ||||
| if (!sd.begin(SD_CS_PIN)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
| sd.errorHalt(); | sd.errorHalt(); | ||||
| } | } | ||||
| case 3: | case 3: | ||||
| for (uint16_t i = 0; i < 10000; i++) { | for (uint16_t i = 0; i < 10000; i++) { | ||||
| uint32_t n = i + 1000000000UL; | |||||
| #if PRINT_FIELD | #if PRINT_FIELD | ||||
| stdioFile.printField(i + 1000000000UL, '\n'); | |||||
| stdioFile.printField(n, '\n'); | |||||
| #else // PRINT_FIELD | #else // PRINT_FIELD | ||||
| stdioFile.println(i + 1000000000UL); | |||||
| stdioFile.println(n); | |||||
| #endif // PRINT_FIELD | #endif // PRINT_FIELD | ||||
| } | } | ||||
| break; | break; |
| /* | |||||
| * Example use of three SD cards. | |||||
| */ | |||||
| #include <SPI.h> | |||||
| #include "SdFat.h" | |||||
| #include "FreeStack.h" | |||||
| #if SD_SPI_CONFIGURATION >= 3 // Must be set in SdFat/SdFatConfig.h | |||||
| // SD1 is a microSD on hardware SPI pins 50-52 | |||||
| // Using my fast custom SPI | |||||
| SdFat sd1; | |||||
| const uint8_t SD1_CS = 53; | |||||
| // SD2 is a Catalex shield on hardware SPI pins 50-52 | |||||
| // Using the standard Arduino SPI library | |||||
| SdFatLibSpi sd2; | |||||
| 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 all cards | |||||
| Serial.println(F("------sd1-------")); | |||||
| sd1.ls("/", LS_SIZE|LS_R); | |||||
| Serial.println(F("------sd2-------")); | |||||
| sd2.ls("/", LS_SIZE|LS_R); | |||||
| Serial.println(F("------sd3-------")); | |||||
| sd3.ls("/", LS_SIZE|LS_R); | |||||
| Serial.println(F("---------------------")); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void setup() { | |||||
| Serial.begin(9600); | |||||
| // Wait for USB Serial | |||||
| while (!Serial) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| Serial.print(F("FreeStack: ")); | |||||
| Serial.println(FreeStack()); | |||||
| // fill buffer with known data | |||||
| for (size_t i = 0; i < sizeof(buf); i++) { | |||||
| buf[i] = i; | |||||
| } | |||||
| Serial.println(F("type any character to start")); | |||||
| while (!Serial.available()) { | |||||
| SysCall::yield(); | |||||
| } | |||||
| // 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/TEST1.bin on sd1 | |||||
| for (uint16_t 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 ((int)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 ((int)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 (uint16_t i = 0; i < NWRITE; i++) { | |||||
| if (file3.read(buf, sizeof(buf)) != sizeof(buf)) { | |||||
| sd3.errorExit("sd3.read"); | |||||
| } | |||||
| for (size_t 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 // SD_SPI_CONFIGURATION >= 3 | |||||
| #error SD_SPI_CONFIGURATION must be set to 3 in SdFat/SdFatConfig.h | |||||
| #endif //SD_SPI_CONFIGURATION >= 3 |
| while (!Serial.available()) { | while (!Serial.available()) { | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| /* | /* | ||||
| * Warning This example requires extra RAM and may crash on Uno. | |||||
| * Example use of two SD cards. | * Example use of two SD cards. | ||||
| */ | */ | ||||
| #include <SPI.h> | #include <SPI.h> |
| while (!Serial.available()) { | while (!Serial.available()) { | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| // Insure no TEST_FILE. | // Insure no TEST_FILE. | ||||
| cout << F("Remove ") << TEST_FILE << endl << endl; | cout << F("Remove ") << TEST_FILE << endl << endl; | ||||
| sd.remove(TEST_FILE); | sd.remove(TEST_FILE); | ||||
| printFreeSpace(); | printFreeSpace(); | ||||
| cout << F("Done") << endl; | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void loop() {} | void loop() {} |
| // file system | // file system | ||||
| SdFat sd; | SdFat sd; | ||||
| // Set SD_SPI_CONFIGURATION to three to test next two definitions. | |||||
| // SdFatLibSpi sd; | |||||
| // Set ENABLE_EXTENDED_TRANSFER_CLASS to use extended SD I/O. | |||||
| // Requires dedicated use of the SPI bus. | |||||
| // SdFatEX sd; | |||||
| // Set ENABLE_SOFTWARE_SPI_CLASS to use software SPI. | |||||
| // Args are misoPin, mosiPin, sckPin. | // Args are misoPin, mosiPin, sckPin. | ||||
| // SdFatSoftSpi<6, 7, 5> sd; | // SdFatSoftSpi<6, 7, 5> sd; | ||||
| cout << F("FreeStack: ") << FreeStack() << endl; | cout << F("FreeStack: ") << FreeStack() << endl; | ||||
| // initialize the SD card at SPI_FULL_SPEED for best performance. | |||||
| // try SPI_HALF_SPEED if bus errors occur. | |||||
| if (!sd.begin(chipSelect, SPI_FULL_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| #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 chipSelect = SS; | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // File system object. | // File system object. | ||||
| cin.readline(); | cin.readline(); | ||||
| cout << endl; | cout << endl; | ||||
| // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| if (sd.exists("Folder1") | if (sd.exists("Folder1") |
| } | } | ||||
| delay(400); // catch Due reset problem | delay(400); // catch Due reset problem | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| /* | |||||
| * This example reads a simple CSV, comma-separated values, file. | |||||
| * Each line of the file has a label and three values, a long and two floats. | |||||
| */ | |||||
| #include <SPI.h> | |||||
| #include "SdFat.h" | |||||
| // SD chip select pin | |||||
| const uint8_t chipSelect = SS; | |||||
| // file system object | |||||
| SdFat sd; | |||||
| // Functions to read a CSV text file one field at a time. | |||||
| // | |||||
| #include <limits.h> | |||||
| #include <SPI.h> | |||||
| // create Serial stream | |||||
| ArduinoOutStream cout(Serial); | |||||
| // next line for SD.h | |||||
| //#include <SD.h> | |||||
| char fileName[] = "testfile.csv"; | |||||
| //------------------------------------------------------------------------------ | |||||
| // store error strings in flash to save RAM | |||||
| #define error(s) sd.errorHalt(F(s)) | |||||
| //------------------------------------------------------------------------------ | |||||
| // read and print CSV test file | |||||
| void readFile() { | |||||
| long lg = 0; | |||||
| float f1, f2; | |||||
| char text[10]; | |||||
| char c1, c2, c3; // space for commas. | |||||
| // next two lines for SdFat | |||||
| #include <SdFat.h> | |||||
| SdFat SD; | |||||
| // open input file | |||||
| ifstream sdin(fileName); | |||||
| #define CS_PIN SS | |||||
| // check for open error | |||||
| if (!sdin.is_open()) { | |||||
| error("open"); | |||||
| } | |||||
| // example can use comma or semicolon | |||||
| #define CSV_DELIM ',' | |||||
| // read until input fails | |||||
| while (1) { | |||||
| // Get text field. | |||||
| sdin.get(text, sizeof(text), ','); | |||||
| File file; | |||||
| // Assume EOF if fail. | |||||
| if (sdin.fail()) { | |||||
| /* | |||||
| * Read a file one field at a time. | |||||
| * | |||||
| * file - File to read. | |||||
| * | |||||
| * str - Character array for the field. | |||||
| * | |||||
| * size - Size of str array. | |||||
| * | |||||
| * delim - csv delimiter. | |||||
| * | |||||
| * return - negative value for failure. | |||||
| * delimiter, '\n' or zero(EOF) for success. | |||||
| */ | |||||
| int csvReadText(File* file, char* str, size_t size, char delim) { | |||||
| char ch; | |||||
| int rtn; | |||||
| size_t n = 0; | |||||
| while (true) { | |||||
| // check for EOF | |||||
| if (!file->available()) { | |||||
| rtn = 0; | |||||
| break; | break; | ||||
| } | } | ||||
| // Get commas and numbers. | |||||
| sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2; | |||||
| // Skip CR/LF. | |||||
| sdin.skipWhite(); | |||||
| if (sdin.fail()) { | |||||
| error("bad input"); | |||||
| if (file->read(&ch, 1) != 1) { | |||||
| // read error | |||||
| rtn = -1; | |||||
| break; | |||||
| } | } | ||||
| // error in line if not commas | |||||
| if (c1 != ',' || c2 != ',' || c3 != ',') { | |||||
| error("comma"); | |||||
| // Delete CR. | |||||
| if (ch == '\r') { | |||||
| continue; | |||||
| } | } | ||||
| // print in six character wide columns | |||||
| cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl; | |||||
| } | |||||
| // Error in an input line if file is not at EOF. | |||||
| if (!sdin.eof()) { | |||||
| error("readFile"); | |||||
| if (ch == delim || ch == '\n') { | |||||
| rtn = ch; | |||||
| break; | |||||
| } | |||||
| if ((n+1) >= size) { | |||||
| // string too long | |||||
| rtn = -2; | |||||
| n--; | |||||
| break; | |||||
| } | |||||
| str[n++] = ch; | |||||
| } | } | ||||
| str[n] = '\0'; | |||||
| return rtn; | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // write test file | |||||
| void writeFile() { | |||||
| // create or open and truncate output file | |||||
| ofstream sdout(fileName); | |||||
| // write file from string stored in flash | |||||
| sdout << F( | |||||
| "Line 1,1,2.3,4.5\n" | |||||
| "Line 2,6,7.8,9.0\n" | |||||
| "Line 3,9,8.7,6.5\n" | |||||
| "Line 4,-4,-3.2,-1\n") << flush; | |||||
| // check for any errors | |||||
| if (!sdout) { | |||||
| error("writeFile"); | |||||
| } | |||||
| sdout.close(); | |||||
| int csvReadInt32(File* file, int32_t* num, char delim) { | |||||
| char buf[20]; | |||||
| char* ptr; | |||||
| int rtn = csvReadText(file, buf, sizeof(buf), delim); | |||||
| if (rtn < 0) return rtn; | |||||
| *num = strtol(buf, &ptr, 10); | |||||
| if (buf == ptr) return -3; | |||||
| while(isspace(*ptr)) ptr++; | |||||
| return *ptr == 0 ? rtn : -4; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int csvReadInt16(File* file, int16_t* num, char delim) { | |||||
| int32_t tmp; | |||||
| int rtn = csvReadInt32(file, &tmp, delim); | |||||
| if (rtn < 0) return rtn; | |||||
| if (tmp < INT_MIN || tmp > INT_MAX) return -5; | |||||
| *num = tmp; | |||||
| return rtn; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int csvReadUint32(File* file, uint32_t* num, char delim) { | |||||
| char buf[20]; | |||||
| char* ptr; | |||||
| int rtn = csvReadText(file, buf, sizeof(buf), delim); | |||||
| if (rtn < 0) return rtn; | |||||
| *num = strtoul(buf, &ptr, 10); | |||||
| if (buf == ptr) return -3; | |||||
| while(isspace(*ptr)) ptr++; | |||||
| return *ptr == 0 ? rtn : -4; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int csvReadUint16(File* file, uint16_t* num, char delim) { | |||||
| uint32_t tmp; | |||||
| int rtn = csvReadUint32(file, &tmp, delim); | |||||
| if (rtn < 0) return rtn; | |||||
| if (tmp > UINT_MAX) return -5; | |||||
| *num = tmp; | |||||
| return rtn; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int csvReadDouble(File* file, double* num, char delim) { | |||||
| char buf[20]; | |||||
| char* ptr; | |||||
| int rtn = csvReadText(file, buf, sizeof(buf), delim); | |||||
| if (rtn < 0) return rtn; | |||||
| *num = strtod(buf, &ptr); | |||||
| if (buf == ptr) return -3; | |||||
| while(isspace(*ptr)) ptr++; | |||||
| return *ptr == 0 ? rtn : -4; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int csvReadFloat(File* file, float* num, char delim) { | |||||
| double tmp; | |||||
| int rtn = csvReadDouble(file, &tmp, delim); | |||||
| if (rtn < 0)return rtn; | |||||
| // could test for too large. | |||||
| *num = tmp; | |||||
| return rtn; | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void setup() { | void setup() { | ||||
| // Wait for USB Serial | // Wait for USB Serial | ||||
| while (!Serial) { | while (!Serial) { | ||||
| SysCall::yield(); | |||||
| yield(); | |||||
| } | } | ||||
| cout << F("Type any character to start\n"); | |||||
| Serial.println("Type any character to start"); | |||||
| while (!Serial.available()) { | while (!Serial.available()) { | ||||
| SysCall::yield(); | |||||
| yield(); | |||||
| } | } | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| sd.initErrorHalt(); | |||||
| // Initialize the SD. | |||||
| if (!SD.begin(CS_PIN)) { | |||||
| Serial.println("begin failed"); | |||||
| return; | |||||
| } | } | ||||
| // create test file | |||||
| writeFile(); | |||||
| cout << endl; | |||||
| // read and print test | |||||
| readFile(); | |||||
| cout << "\nDone!" << endl; | |||||
| // Remove existing file. | |||||
| SD.remove("READTEST.TXT"); | |||||
| // Create the file. | |||||
| file = SD.open("READTEST.TXT", FILE_WRITE); | |||||
| if (!file) { | |||||
| Serial.println("open failed"); | |||||
| return; | |||||
| } | |||||
| // Write test data. | |||||
| file.print(F( | |||||
| #if CSV_DELIM == ',' | |||||
| "36,23.20,20.70,57.60,79.50,01:08:14,23.06.16\r\n" | |||||
| "37,23.21,20.71,57.61,79.51,02:08:14,23.07.16\r\n" | |||||
| #elif CSV_DELIM == ';' | |||||
| "36;23.20;20.70;57.60;79.50;01:08:14;23.06.16\r\n" | |||||
| "37;23.21;20.71;57.61;79.51;02:08:14;23.07.16\r\n" | |||||
| #else | |||||
| #error "Bad CSV_DELIM" | |||||
| #endif | |||||
| )); | |||||
| // Rewind the file for read. | |||||
| file.seek(0); | |||||
| // Read the file and print fields. | |||||
| int16_t tcalc; | |||||
| float t1, t2, h1, h2; | |||||
| // Must be dim 9 to allow for zero byte. | |||||
| char timeS[9], dateS[9]; | |||||
| while (file.available()) { | |||||
| if (csvReadInt16(&file, &tcalc, CSV_DELIM) != CSV_DELIM | |||||
| || csvReadFloat(&file, &t1, CSV_DELIM) != CSV_DELIM | |||||
| || csvReadFloat(&file, &t2, CSV_DELIM) != CSV_DELIM | |||||
| || csvReadFloat(&file, &h1, CSV_DELIM) != CSV_DELIM | |||||
| || csvReadFloat(&file, &h2, CSV_DELIM) != CSV_DELIM | |||||
| || csvReadText(&file, timeS, sizeof(timeS), CSV_DELIM) != CSV_DELIM | |||||
| || csvReadText(&file, dateS, sizeof(dateS), CSV_DELIM) != '\n') { | |||||
| Serial.println("read error"); | |||||
| int ch; | |||||
| int nr = 0; | |||||
| // print part of file after error. | |||||
| while ((ch = file.read()) > 0 && nr++ < 100) { | |||||
| Serial.write(ch); | |||||
| } | |||||
| break; | |||||
| } | |||||
| Serial.print(tcalc); | |||||
| Serial.print(CSV_DELIM); | |||||
| Serial.print(t1); | |||||
| Serial.print(CSV_DELIM); | |||||
| Serial.print(t2); | |||||
| Serial.print(CSV_DELIM); | |||||
| Serial.print(h1); | |||||
| Serial.print(CSV_DELIM); | |||||
| Serial.print(h2); | |||||
| Serial.print(CSV_DELIM); | |||||
| Serial.print(timeS); | |||||
| Serial.print(CSV_DELIM); | |||||
| Serial.println(dateS); | |||||
| } | |||||
| file.close(); | |||||
| } | } | ||||
| void loop() {} | |||||
| //------------------------------------------------------------------------------ | |||||
| void loop() { | |||||
| } | |||||
| SysCall::yield(); | SysCall::yield(); | ||||
| } | } | ||||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| if (c != 'Y') { | if (c != 'Y') { | ||||
| sd.errorHalt("Quitting, you did not type 'Y'."); | sd.errorHalt("Quitting, you did not type 'Y'."); | ||||
| } | } | ||||
| if (!sd.begin(chipSelect)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
| } | } | ||||
| // Use wipe() for no dot progress indicator. | // Use wipe() for no dot progress indicator. | ||||
| sd.errorHalt("Wipe failed."); | sd.errorHalt("Wipe failed."); | ||||
| } | } | ||||
| // Must reinitialize after wipe. | // Must reinitialize after wipe. | ||||
| if (!sd.begin(chipSelect)) { | |||||
| // Initialize at the highest speed supported by the board that is | |||||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
| sd.errorHalt("Second init failed."); | sd.errorHalt("Second init failed."); | ||||
| } | } | ||||
| Serial.println("Done"); | Serial.println("Done"); |
| name=SdFat | name=SdFat | ||||
| version=2015.4.26 | |||||
| version=2016.7.24 | |||||
| author= | author= | ||||
| maintainer= | maintainer= | ||||
| sentence=FAT16/FAT32 file system for SD cards. | sentence=FAT16/FAT32 file system for SD cards. |
| */ | */ | ||||
| #ifndef DigitalPin_h | #ifndef DigitalPin_h | ||||
| #define DigitalPin_h | #define DigitalPin_h | ||||
| #include "SystemInclude.h" | |||||
| #if defined(__AVR__) | #if defined(__AVR__) | ||||
| #include <avr/io.h> | #include <avr/io.h> | ||||
| /** GpioPinMap type */ | /** GpioPinMap type */ |
| /* Arduino SdSpi Library | |||||
| /* Arduino SdSpiAltDriver Library | |||||
| * Copyright (C) 2016 by William Greiman | * Copyright (C) 2016 by William Greiman | ||||
| * | * | ||||
| * STM32F1 code for Maple and Maple Mini support, 2015 by Victor Perez | * STM32F1 code for Maple and Maple Mini support, 2015 by Victor Perez | ||||
| * | * | ||||
| * This file is part of the Arduino SdSpi Library | |||||
| * This file is part of the Arduino SdSpiAltDriver 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 SdSpi Library. If not, see | |||||
| * along with the Arduino SdSpiAltDriver Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #if defined(ESP8266) | #if defined(ESP8266) | ||||
| #include "SdSpi.h" | |||||
| #include "SdSpiDriver.h" | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Initialize the SPI bus. | /** Initialize the SPI bus. | ||||
| * | * | ||||
| * \param[in] chipSelectPin SD card chip select pin. | * \param[in] chipSelectPin SD card chip select pin. | ||||
| */ | */ | ||||
| void SdSpi::begin(uint8_t chipSelectPin) { | |||||
| pinMode(chipSelectPin, OUTPUT); | |||||
| digitalWrite(chipSelectPin, HIGH); | |||||
| void SdSpiAltDriver::begin(uint8_t csPin) { | |||||
| m_csPin = csPin; | |||||
| pinMode(m_csPin, OUTPUT); | |||||
| digitalWrite(m_csPin, HIGH); | |||||
| SPI.begin(); | SPI.begin(); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Set SPI options for access to SD/SDHC cards. | /** Set SPI options for access to SD/SDHC cards. | ||||
| * | * | ||||
| * \param[in] divisor SCK clock divider relative to the max SPI clock. | |||||
| */ | */ | ||||
| void SdSpi::beginTransaction(uint8_t divisor) { | |||||
| const uint32_t F_SPI_MAX = 80000000; | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| // Note: ESP8266 beginTransaction does not protect for interrupts. | |||||
| SPISettings settings(F_SPI_MAX/(divisor ? divisor : 1), MSBFIRST, SPI_MODE0); | |||||
| SPI.beginTransaction(settings); | |||||
| #else // ENABLE_SPI_TRANSACTIONS | |||||
| // Note: ESP8266 beginTransaction is the same as following code. | |||||
| SPI.setFrequency(F_SPI_MAX/(divisor ? divisor : 1)); | |||||
| SPI.setBitOrder(MSBFIRST); | |||||
| SPI.setDataMode(SPI_MODE0); | |||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| void SdSpiAltDriver::activate() { | |||||
| SPI.beginTransaction(m_spiSettings); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void SdSpi::endTransaction() { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| void SdSpiAltDriver::deactivate() { | |||||
| // Note: endTransaction is an empty function on ESP8266. | // Note: endTransaction is an empty function on ESP8266. | ||||
| SPI.endTransaction(); | SPI.endTransaction(); | ||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Receive a byte. | /** Receive a byte. | ||||
| * | * | ||||
| * \return The byte. | * \return The byte. | ||||
| */ | */ | ||||
| uint8_t SdSpi::receive() { | |||||
| uint8_t SdSpiAltDriver::receive() { | |||||
| return SPI.transfer(0XFF); | return SPI.transfer(0XFF); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| * | * | ||||
| * \return Zero for no error or nonzero error code. | * \return Zero for no error or nonzero error code. | ||||
| */ | */ | ||||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
| uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
| // Works without 32-bit alignment of buf. | // Works without 32-bit alignment of buf. | ||||
| SPI.transferBytes(0, buf, n); | SPI.transferBytes(0, buf, n); | ||||
| return 0; | return 0; | ||||
| * | * | ||||
| * \param[in] b Byte to send | * \param[in] b Byte to send | ||||
| */ | */ | ||||
| void SdSpi::send(uint8_t b) { | |||||
| void SdSpiAltDriver::send(uint8_t b) { | |||||
| SPI.transfer(b); | SPI.transfer(b); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| * \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 SdSpi::send(const uint8_t* buf , size_t n) { | |||||
| void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
| // Adjust to 32-bit alignment. | // Adjust to 32-bit alignment. | ||||
| while ((reinterpret_cast<uintptr_t>(buf) & 0X3) && n) { | while ((reinterpret_cast<uintptr_t>(buf) & 0X3) && n) { | ||||
| SPI.transfer(*buf++); | SPI.transfer(*buf++); |
| /* Arduino SdSpi Library | |||||
| /* Arduino SdSpiAltDriver Library | |||||
| * Copyright (C) 2013 by William Greiman | * Copyright (C) 2013 by William Greiman | ||||
| * | * | ||||
| * This file is part of the Arduino SdSpi Library | |||||
| * This file is part of the Arduino SdSpiAltDriver 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 SdSpi Library. If not, see | |||||
| * along with the Arduino SdSpiAltDriver Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include "SdSpi.h" | |||||
| #include "SdSpiDriver.h" | |||||
| #if defined(__SAM3X8E__) || defined(__SAM3X8H__) | #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 | ||||
| return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true; | return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void SdSpi::begin(uint8_t chipSelectPin) { | |||||
| pinMode(chipSelectPin, OUTPUT); | |||||
| digitalWrite(chipSelectPin, HIGH); | |||||
| PIO_Configure( | |||||
| g_APinDescription[PIN_SPI_MOSI].pPort, | |||||
| g_APinDescription[PIN_SPI_MOSI].ulPinType, | |||||
| g_APinDescription[PIN_SPI_MOSI].ulPin, | |||||
| g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration); | |||||
| PIO_Configure( | |||||
| g_APinDescription[PIN_SPI_MISO].pPort, | |||||
| g_APinDescription[PIN_SPI_MISO].ulPinType, | |||||
| g_APinDescription[PIN_SPI_MISO].ulPin, | |||||
| g_APinDescription[PIN_SPI_MISO].ulPinConfiguration); | |||||
| PIO_Configure( | |||||
| g_APinDescription[PIN_SPI_SCK].pPort, | |||||
| g_APinDescription[PIN_SPI_SCK].ulPinType, | |||||
| g_APinDescription[PIN_SPI_SCK].ulPin, | |||||
| g_APinDescription[PIN_SPI_SCK].ulPinConfiguration); | |||||
| pmc_enable_periph_clk(ID_SPI0); | |||||
| void SdSpiAltDriver::begin(uint8_t csPin) { | |||||
| m_csPin = csPin; | |||||
| pinMode(m_csPin, OUTPUT); | |||||
| digitalWrite(m_csPin, HIGH); | |||||
| SPI.begin(); | |||||
| #if USE_SAM3X_DMAC | #if USE_SAM3X_DMAC | ||||
| pmc_enable_periph_clk(ID_DMAC); | pmc_enable_periph_clk(ID_DMAC); | ||||
| dmac_disable(); | dmac_disable(); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // initialize SPI controller | // initialize SPI controller | ||||
| void SdSpi::beginTransaction(uint8_t sckDivisor) { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| SPI.beginTransaction(SPISettings()); | |||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| uint8_t scbr = sckDivisor; | |||||
| void SdSpiAltDriver::activate() { | |||||
| SPI.beginTransaction(m_spiSettings); | |||||
| Spi* pSpi = SPI0; | Spi* pSpi = SPI0; | ||||
| // disable SPI | |||||
| // Save the divisor | |||||
| uint32_t scbr = pSpi->SPI_CSR[SPI_CHIP_SEL] & 0XFF00; | |||||
| // Disable SPI | |||||
| pSpi->SPI_CR = SPI_CR_SPIDIS; | pSpi->SPI_CR = SPI_CR_SPIDIS; | ||||
| // reset SPI | // reset SPI | ||||
| pSpi->SPI_CR = SPI_CR_SWRST; | pSpi->SPI_CR = SPI_CR_SWRST; | ||||
| // no mode fault detection, set master mode | // no mode fault detection, set master mode | ||||
| pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR; | pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR; | ||||
| // mode 0, 8-bit, | // mode 0, 8-bit, | ||||
| pSpi->SPI_CSR[SPI_CHIP_SEL] = SPI_CSR_SCBR(scbr) | SPI_CSR_NCPHA; | |||||
| pSpi->SPI_CSR[SPI_CHIP_SEL] = scbr | SPI_CSR_CSAAT | SPI_CSR_NCPHA; | |||||
| // enable SPI | // enable SPI | ||||
| pSpi->SPI_CR |= SPI_CR_SPIEN; | pSpi->SPI_CR |= SPI_CR_SPIEN; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void SdSpi::endTransaction() { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| void SdSpiAltDriver::deactivate() { | |||||
| SPI.endTransaction(); | SPI.endTransaction(); | ||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| static inline uint8_t spiTransfer(uint8_t b) { | static inline uint8_t spiTransfer(uint8_t b) { | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** SPI receive a byte */ | /** SPI receive a byte */ | ||||
| uint8_t SdSpi::receive() { | |||||
| uint8_t SdSpiAltDriver::receive() { | |||||
| return spiTransfer(0XFF); | return spiTransfer(0XFF); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** SPI receive multiple bytes */ | /** SPI receive multiple bytes */ | ||||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
| uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
| Spi* pSpi = SPI0; | Spi* pSpi = SPI0; | ||||
| int rtn = 0; | int rtn = 0; | ||||
| #if USE_SAM3X_DMAC | #if USE_SAM3X_DMAC | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** SPI send a byte */ | /** SPI send a byte */ | ||||
| void SdSpi::send(uint8_t b) { | |||||
| void SdSpiAltDriver::send(uint8_t b) { | |||||
| spiTransfer(b); | spiTransfer(b); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
| void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
| Spi* pSpi = SPI0; | Spi* pSpi = SPI0; | ||||
| #if USE_SAM3X_DMAC | #if USE_SAM3X_DMAC | ||||
| spiDmaTX(buf, n); | spiDmaTX(buf, n); |
| /* Arduino SdSpi Library | |||||
| /* Arduino SdSpiAltDriver Library | |||||
| * Copyright (C) 2013 by William Greiman | * Copyright (C) 2013 by William Greiman | ||||
| * | * | ||||
| * This file is part of the Arduino SdSpi Library | |||||
| * This file is part of the Arduino SdSpiAltDriver 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 SdSpi Library. If not, see | |||||
| * along with the Arduino SdSpiAltDriver Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #if defined(__STM32F1__) | #if defined(__STM32F1__) | ||||
| #include "SdSpi.h" | |||||
| #include "SdSpiDriver.h" | |||||
| #define USE_STM32F1_DMAC 1 | #define USE_STM32F1_DMAC 1 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Initialize the SPI bus. | |||||
| static SPIClass m_SPI1(1); | |||||
| #if BOARD_NR_SPI > 1 | |||||
| static SPIClass m_SPI2(2); | |||||
| #endif // BOARD_NR_SPI > 1 | |||||
| #if BOARD_NR_SPI > 2 | |||||
| static SPIClass m_SPI2(3); | |||||
| #endif // BOARD_NR_SPI > 2 | |||||
| // | |||||
| static SPIClass* pSpi[] = | |||||
| #if BOARD_NR_SPI == 1 | |||||
| {&m_SPI1}; | |||||
| #elif BOARD_NR_SPI == 2 | |||||
| {&m_SPI1, &m_SPI2}; | |||||
| #elif BOARD_NR_SPI == 3 | |||||
| {&m_SPI1, &m_SPI2, &m_SPI3}; | |||||
| #else // BOARD_NR_SPI | |||||
| #error "BOARD_NR_SPI too large" | |||||
| #endif // BOARD_NR_SPI | |||||
| //------------------------------------------------------------------------------ | |||||
| /** Set SPI options for access to SD/SDHC cards. | |||||
| * | * | ||||
| * \param[in] chipSelectPin SD card chip select pin. | |||||
| * \param[in] divisor SCK clock divider relative to the APB1 or APB2 clock. | |||||
| */ | */ | ||||
| void SdSpi::begin(uint8_t chipSelectPin) { | |||||
| pinMode(chipSelectPin, OUTPUT); | |||||
| digitalWrite(chipSelectPin, HIGH); | |||||
| SPI.begin(); | |||||
| void SdSpiAltDriver::activate() { | |||||
| pSpi[m_spiPort]->beginTransaction(m_spiSettings); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Set SPI options for access to SD/SDHC cards. | |||||
| /** Initialize the SPI bus. | |||||
| * | * | ||||
| * \param[in] divisor SCK clock divider relative to the APB1 or APB2 clock. | |||||
| * \param[in] chipSelectPin SD card chip select pin. | |||||
| */ | */ | ||||
| void SdSpi::beginTransaction(uint8_t divisor) { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| // Correct divisor will be set below. | |||||
| SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); | |||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| uint32_t br; // Baud rate control field in SPI_CR1. | |||||
| if (divisor <= 2) { | |||||
| br = SPI_CLOCK_DIV2; | |||||
| } else if (divisor <= 4) { | |||||
| br = SPI_CLOCK_DIV4; | |||||
| } else if (divisor <= 8) { | |||||
| br = SPI_CLOCK_DIV8; | |||||
| } else if (divisor <= 16) { | |||||
| br = SPI_CLOCK_DIV16; | |||||
| } else if (divisor <= 32) { | |||||
| br = SPI_CLOCK_DIV32; | |||||
| } else if (divisor <= 64) { | |||||
| br = SPI_CLOCK_DIV64; | |||||
| } else if (divisor <= 128) { | |||||
| br = SPI_CLOCK_DIV128; | |||||
| } else { | |||||
| br = SPI_CLOCK_DIV256; | |||||
| } | |||||
| SPI.setClockDivider(br); | |||||
| #if !ENABLE_SPI_TRANSACTIONS | |||||
| SPI.setBitOrder(MSBFIRST); | |||||
| SPI.setDataMode(SPI_MODE0); | |||||
| #endif // !ENABLE_SPI_TRANSACTIONS | |||||
| void SdSpiAltDriver::begin(uint8_t csPin) { | |||||
| m_csPin = csPin; | |||||
| pinMode(m_csPin, OUTPUT); | |||||
| digitalWrite(m_csPin, HIGH); | |||||
| pSpi[m_spiPort]->begin(); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * End SPI transaction. | * End SPI transaction. | ||||
| */ | */ | ||||
| void SdSpi::endTransaction() { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| SPI.endTransaction(); | |||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| void SdSpiAltDriver::deactivate() { | |||||
| pSpi[m_spiPort]->endTransaction(); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Receive a byte. | /** Receive a byte. | ||||
| * | * | ||||
| * \return The byte. | * \return The byte. | ||||
| */ | */ | ||||
| uint8_t SdSpi::receive() { | |||||
| return SPI.transfer(0XFF); | |||||
| uint8_t SdSpiAltDriver::receive() { | |||||
| return pSpi[m_spiPort]->transfer(0XFF); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Receive multiple bytes. | /** Receive multiple bytes. | ||||
| * | * | ||||
| * \return Zero for no error or nonzero error code. | * \return Zero for no error or nonzero error code. | ||||
| */ | */ | ||||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
| uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
| int rtn = 0; | int rtn = 0; | ||||
| #if USE_STM32F1_DMAC | #if USE_STM32F1_DMAC | ||||
| rtn = SPI.dmaTransfer(0, const_cast<uint8*>(buf), n); | |||||
| rtn = pSpi[m_spiPort]->dmaTransfer(0, const_cast<uint8*>(buf), n); | |||||
| #else // USE_STM32F1_DMAC | #else // USE_STM32F1_DMAC | ||||
| // SPI.read(buf, n); | |||||
| // pSpi[m_spiPort]->read(buf, n); fails ?? use byte transfer | |||||
| for (size_t i = 0; i < n; i++) { | for (size_t i = 0; i < n; i++) { | ||||
| buf[i] = SPI.transfer(0XFF); | |||||
| buf[i] = pSpi[m_spiPort]->transfer(0XFF); | |||||
| } | } | ||||
| #endif // USE_STM32F1_DMAC | #endif // USE_STM32F1_DMAC | ||||
| return rtn; | return rtn; | ||||
| * | * | ||||
| * \param[in] b Byte to send | * \param[in] b Byte to send | ||||
| */ | */ | ||||
| void SdSpi::send(uint8_t b) { | |||||
| SPI.transfer(b); | |||||
| void SdSpiAltDriver::send(uint8_t b) { | |||||
| pSpi[m_spiPort]->transfer(b); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** 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 SdSpi::send(const uint8_t* buf , size_t n) { | |||||
| void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
| #if USE_STM32F1_DMAC | #if USE_STM32F1_DMAC | ||||
| SPI.dmaSend(const_cast<uint8*>(buf), n); | |||||
| pSpi[m_spiPort]->dmaSend(const_cast<uint8*>(buf), n); | |||||
| #else // #if USE_STM32F1_DMAC | #else // #if USE_STM32F1_DMAC | ||||
| SPI.write(buf, n); | |||||
| pSpi[m_spiPort]->write(buf, n); | |||||
| #endif // USE_STM32F1_DMAC | #endif // USE_STM32F1_DMAC | ||||
| } | } | ||||
| #endif // USE_NATIVE_STM32F1_SPI | |||||
| //----------------------------------------------------------------------------- | |||||
| void SdSpiAltDriver::setPort(uint8_t portNumber) { | |||||
| m_spiPort = portNumber < 1 || portNumber > BOARD_NR_SPI ? 0 : portNumber -1; | |||||
| } | |||||
| #endif // defined(__STM32F1__) |
| /* Arduino SdSpi Library | |||||
| /* Arduino SdSpiAltDriver Library | |||||
| * Copyright (C) 2013 by William Greiman | * Copyright (C) 2013 by William Greiman | ||||
| * | * | ||||
| * This file is part of the Arduino SdSpi Library | |||||
| * This file is part of the Arduino SdSpiAltDriver 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 SdSpi Library. If not, see | |||||
| * along with the Arduino SdSpiAltDriver Library. If not, see | |||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include "SdSpi.h" | |||||
| #include "SdSpiDriver.h" | |||||
| #if defined(__arm__) && defined(CORE_TEENSY) | #if defined(__arm__) && defined(CORE_TEENSY) | ||||
| // SPI definitions | // SPI definitions | ||||
| #include "kinetis.h" | #include "kinetis.h" | ||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpiAltDriver::activate() { | |||||
| SPI.beginTransaction(m_spiSettings); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpiAltDriver::begin(uint8_t chipSelectPin) { | |||||
| m_csPin = chipSelectPin; | |||||
| pinMode(m_csPin, OUTPUT); | |||||
| digitalWrite(m_csPin, HIGH); | |||||
| SPI.begin(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpiAltDriver::deactivate() { | |||||
| SPI.endTransaction(); | |||||
| } | |||||
| //============================================================================== | |||||
| #ifdef KINETISK | #ifdef KINETISK | ||||
| // 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 | ||||
| #define SPI_PUSHR_CTAS(n) (((n) & 7) << 28) | #define SPI_PUSHR_CTAS(n) (((n) & 7) << 28) | ||||
| #endif // SPI_PUSHR_CTAS | #endif // SPI_PUSHR_CTAS | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * initialize SPI pins | |||||
| */ | |||||
| void SdSpi::begin(uint8_t chipSelectPin) { | |||||
| pinMode(chipSelectPin, OUTPUT); | |||||
| digitalWrite(chipSelectPin, HIGH); | |||||
| SIM_SCGC6 |= SIM_SCGC6_SPI0; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * Initialize hardware SPI | |||||
| * | |||||
| */ | |||||
| void SdSpi::beginTransaction(uint8_t sckDivisor) { | |||||
| uint32_t ctar, ctar0, ctar1; | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| SPI.beginTransaction(SPISettings()); | |||||
| #endif // #if ENABLE_SPI_TRANSACTIONS | |||||
| if (sckDivisor <= 2) { | |||||
| // 1/2 speed | |||||
| ctar = SPI_CTAR_DBR | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); | |||||
| } else if (sckDivisor <= 4) { | |||||
| // 1/4 speed | |||||
| ctar = SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); | |||||
| } else if (sckDivisor <= 8) { | |||||
| // 1/8 speed | |||||
| ctar = SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); | |||||
| } else if (sckDivisor <= 12) { | |||||
| // 1/12 speed | |||||
| ctar = SPI_CTAR_BR(2) | SPI_CTAR_CSSCK(2); | |||||
| } else if (sckDivisor <= 16) { | |||||
| // 1/16 speed | |||||
| ctar = SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(3); | |||||
| } else if (sckDivisor <= 32) { | |||||
| // 1/32 speed | |||||
| ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(4); | |||||
| } else if (sckDivisor <= 64) { | |||||
| // 1/64 speed | |||||
| ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(5); | |||||
| } else { | |||||
| // 1/128 speed | |||||
| ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(6); | |||||
| } | |||||
| // CTAR0 - 8 bit transfer | |||||
| ctar0 = ctar | SPI_CTAR_FMSZ(7); | |||||
| // CTAR1 - 16 bit transfer | |||||
| ctar1 = ctar | SPI_CTAR_FMSZ(15); | |||||
| if (SPI0_CTAR0 != ctar0 || SPI0_CTAR1 != ctar1) { | |||||
| SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); | |||||
| SPI0_CTAR0 = ctar0; | |||||
| SPI0_CTAR1 = ctar1; | |||||
| } | |||||
| SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); | |||||
| CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); | |||||
| CORE_PIN12_CONFIG = PORT_PCR_MUX(2); | |||||
| CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| /** SPI receive a byte */ | /** SPI receive a byte */ | ||||
| uint8_t SdSpi::receive() { | |||||
| uint8_t SdSpiAltDriver::receive() { | |||||
| SPI0_MCR |= SPI_MCR_CLR_RXF; | SPI0_MCR |= SPI_MCR_CLR_RXF; | ||||
| SPI0_SR = SPI_SR_TCF; | SPI0_SR = SPI_SR_TCF; | ||||
| SPI0_PUSHR = 0xFF; | SPI0_PUSHR = 0xFF; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** SPI receive multiple bytes */ | /** SPI receive multiple bytes */ | ||||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
| uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
| // clear any data in RX FIFO | // clear any data in RX FIFO | ||||
| SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | ||||
| #if SPI_USE_8BIT_FRAME | #if SPI_USE_8BIT_FRAME | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** SPI send a byte */ | /** SPI send a byte */ | ||||
| void SdSpi::send(uint8_t b) { | |||||
| void SdSpiAltDriver::send(uint8_t b) { | |||||
| SPI0_MCR |= SPI_MCR_CLR_RXF; | SPI0_MCR |= SPI_MCR_CLR_RXF; | ||||
| SPI0_SR = SPI_SR_TCF; | SPI0_SR = SPI_SR_TCF; | ||||
| SPI0_PUSHR = b; | SPI0_PUSHR = b; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** SPI send multiple bytes */ | /** SPI send multiple bytes */ | ||||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
| void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
| // clear any data in RX FIFO | // clear any data in RX FIFO | ||||
| SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | ||||
| #if SPI_USE_8BIT_FRAME | #if SPI_USE_8BIT_FRAME | ||||
| #else // KINETISK | #else // KINETISK | ||||
| //============================================================================== | //============================================================================== | ||||
| // Use standard SPI library if not KINETISK | // Use standard SPI library if not KINETISK | ||||
| /** | |||||
| * Initialize SPI pins. | |||||
| */ | |||||
| void SdSpi::begin(uint8_t chipSelectPin) { | |||||
| pinMode(chipSelectPin, OUTPUT); | |||||
| digitalWrite(chipSelectPin, HIGH); | |||||
| SPI.begin(); | |||||
| } | |||||
| /** Set SPI options for access to SD/SDHC cards. | |||||
| * | |||||
| * \param[in] divisor SCK clock divider relative to the system clock. | |||||
| */ | |||||
| void SdSpi::beginTransaction(uint8_t divisor) { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| SPI.beginTransaction(SPISettings()); | |||||
| #else // #if ENABLE_SPI_TRANSACTIONS | |||||
| SPI.setBitOrder(MSBFIRST); | |||||
| SPI.setDataMode(SPI_MODE0); | |||||
| #endif // #if ENABLE_SPI_TRANSACTIONS | |||||
| #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. | /** Receive a byte. | ||||
| * | * | ||||
| * \return The byte. | * \return The byte. | ||||
| */ | */ | ||||
| uint8_t SdSpi::receive() { | |||||
| uint8_t SdSpiAltDriver::receive() { | |||||
| return SPI.transfer(0XFF); | return SPI.transfer(0XFF); | ||||
| } | } | ||||
| /** Receive multiple bytes. | /** Receive multiple bytes. | ||||
| * | * | ||||
| * \return Zero for no error or nonzero error code. | * \return Zero for no error or nonzero error code. | ||||
| */ | */ | ||||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
| uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
| for (size_t i = 0; i < n; i++) { | for (size_t i = 0; i < n; i++) { | ||||
| buf[i] = SPI.transfer(0XFF); | buf[i] = SPI.transfer(0XFF); | ||||
| } | } | ||||
| * | * | ||||
| * \param[in] b Byte to send | * \param[in] b Byte to send | ||||
| */ | */ | ||||
| void SdSpi::send(uint8_t b) { | |||||
| void SdSpiAltDriver::send(uint8_t b) { | |||||
| SPI.transfer(b); | SPI.transfer(b); | ||||
| } | } | ||||
| /** 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 SdSpi::send(const uint8_t* buf , size_t n) { | |||||
| void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
| for (size_t i = 0; i < n; i++) { | for (size_t i = 0; i < n; i++) { | ||||
| SPI.transfer(buf[i]); | SPI.transfer(buf[i]); | ||||
| } | } | ||||
| } | } | ||||
| #endif // KINETISK | #endif // KINETISK | ||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpi::endTransaction() { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| SPI.endTransaction(); | |||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| } | |||||
| #endif // defined(__arm__) && defined(CORE_TEENSY) | #endif // defined(__arm__) && defined(CORE_TEENSY) |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2016 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 "BlockDriver.h" | |||||
| bool SdBlockDriverEX::readBlock(uint32_t block, uint8_t* dst) { | |||||
| if (m_curState != READ_STATE || block != m_curBlock) { | |||||
| if (!syncBlocks()) { | |||||
| return false; | |||||
| } | |||||
| if (!SdSpiCard::readStart(block)) { | |||||
| return false; | |||||
| } | |||||
| m_curBlock = block; | |||||
| m_curState = READ_STATE; | |||||
| } | |||||
| if (!SdSpiCard::readData(dst)) { | |||||
| return false; | |||||
| } | |||||
| m_curBlock++; | |||||
| return true; | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| bool SdBlockDriverEX::readBlocks(uint32_t block, uint8_t* dst, size_t nb) { | |||||
| for (size_t i = 0; i < nb; i++) { | |||||
| if (!readBlock(block + i, dst + i*512UL)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| bool SdBlockDriverEX::syncBlocks() { | |||||
| if (m_curState == READ_STATE) { | |||||
| m_curState = IDLE_STATE; | |||||
| if (!SdSpiCard::readStop()) { | |||||
| return false; | |||||
| } | |||||
| } else if (m_curState == WRITE_STATE) { | |||||
| m_curState = IDLE_STATE; | |||||
| if (!SdSpiCard::writeStop()) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| bool SdBlockDriverEX::writeBlock(uint32_t block, const uint8_t* src) { | |||||
| if (m_curState != WRITE_STATE || m_curBlock != block) { | |||||
| if (!syncBlocks()) { | |||||
| return false; | |||||
| } | |||||
| if (!SdSpiCard::writeStart(block)) { | |||||
| return false; | |||||
| } | |||||
| m_curBlock = block; | |||||
| m_curState = WRITE_STATE; | |||||
| } | |||||
| if (!SdSpiCard::writeData(src)) { | |||||
| return false; | |||||
| } | |||||
| m_curBlock++; | |||||
| return true; | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| bool SdBlockDriverEX::writeBlocks(uint32_t block, | |||||
| const uint8_t* src, size_t nb) { | |||||
| for (size_t i = 0; i < nb; i++) { | |||||
| if (!writeBlock(block + i, src + i*512UL)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2016 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/>. | |||||
| */ | |||||
| /** | |||||
| * \file | |||||
| * \brief Define block driver. | |||||
| */ | |||||
| #ifndef BlockDriver_h | |||||
| #define BlockDriver_h | |||||
| #ifdef ARDUINO | |||||
| #include "SdSpiCard/SdSpiCard.h" | |||||
| #else // ARDUINO | |||||
| #include "SdSpiCard.h" | |||||
| #endif // ARDUINO | |||||
| #include "FatLib/BaseBlockDriver.h" | |||||
| //----------------------------------------------------------------------------- | |||||
| /** | |||||
| * \class SdBlockDriver | |||||
| * \brief Standard SD block driver. | |||||
| */ | |||||
| #if ENABLE_EXTENDED_TRANSFER_CLASS | |||||
| class SdBlockDriver : public BaseBlockDriver, public SdSpiCard { | |||||
| #else // ENABLE_EXTENDED_TRANSFER_CLASS | |||||
| class SdBlockDriver : public SdSpiCard { | |||||
| #endif // ENABLE_EXTENDED_TRANSFER_CLASS | |||||
| public: | |||||
| /** Initialize the SD card | |||||
| * | |||||
| * \param[in] spi SPI driver. | |||||
| * \param[in] csPin Card chip select pin number. | |||||
| * \param[in] spiSettings SPI speed, mode, and bit order. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||||
| spi->begin(csPin); | |||||
| spi->setSpiSettings(SD_SCK_HZ(250000)); | |||||
| if (!SdSpiCard::begin(spi)) { | |||||
| return false; | |||||
| } | |||||
| spi->setSpiSettings(spiSettings); | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * 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 true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool readBlock(uint32_t block, uint8_t* dst) { | |||||
| return SdSpiCard::readBlock(block, dst); | |||||
| } | |||||
| /** End multi-block transfer and go to idle state. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool syncBlocks() { | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * Writes a 512 byte block to an SD card. | |||||
| * | |||||
| * \param[in] block Logical block to be written. | |||||
| * \param[in] src Pointer to the location of the data to be written. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool writeBlock(uint32_t block, const uint8_t* src) { | |||||
| return SdSpiCard::writeBlock(block, src); | |||||
| } | |||||
| /** | |||||
| * Read multiple 512 byte blocks from an SD card. | |||||
| * | |||||
| * \param[in] block Logical block to be read. | |||||
| * \param[in] nb Number of blocks to be read. | |||||
| * \param[out] dst Pointer to the location that will receive the data. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) { | |||||
| return SdSpiCard::readBlocks(block, dst, nb); | |||||
| } | |||||
| /** | |||||
| * Write multiple 512 byte blocks to an SD card. | |||||
| * | |||||
| * \param[in] block Logical block to be written. | |||||
| * \param[in] nb Number of blocks to be written. | |||||
| * \param[in] src Pointer to the location of the data to be written. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) { | |||||
| return SdSpiCard::writeBlocks(block, src, nb); | |||||
| } | |||||
| }; | |||||
| //----------------------------------------------------------------------------- | |||||
| /** | |||||
| * \class SdBlockDriverEX | |||||
| * \brief Extended SD I/O block driver. | |||||
| */ | |||||
| class SdBlockDriverEX : public SdSpiCard, public BaseBlockDriver { | |||||
| public: | |||||
| /** Initialize the SD card | |||||
| * | |||||
| * \param[in] spi SPI driver. | |||||
| * \param[in] csPin Card chip select pin number. | |||||
| * \param[in] spiSettings SPI speed, mode, and bit order. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||||
| m_curState = IDLE_STATE; | |||||
| spi->begin(csPin); | |||||
| spi->setSpiSettings(SD_SCK_HZ(250000)); | |||||
| if (!SdSpiCard::begin(spi)) { | |||||
| return false; | |||||
| } | |||||
| spi->setSpiSettings(spiSettings); | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * 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 true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool readBlock(uint32_t block, uint8_t* dst); | |||||
| /** End multi-block transfer and go to idle state. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool syncBlocks(); | |||||
| /** | |||||
| * Writes a 512 byte block to an SD card. | |||||
| * | |||||
| * \param[in] block Logical block to be written. | |||||
| * \param[in] src Pointer to the location of the data to be written. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool writeBlock(uint32_t block, const uint8_t* src); | |||||
| /** | |||||
| * Read multiple 512 byte blocks from an SD card. | |||||
| * | |||||
| * \param[in] block Logical block to be read. | |||||
| * \param[in] nb Number of blocks to be read. | |||||
| * \param[out] dst Pointer to the location that will receive the data. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool readBlocks(uint32_t block, uint8_t* dst, size_t nb); | |||||
| /** | |||||
| * Write multiple 512 byte blocks to an SD card. | |||||
| * | |||||
| * \param[in] block Logical block to be written. | |||||
| * \param[in] nb Number of blocks to be written. | |||||
| * \param[in] src Pointer to the location of the data to be written. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb); | |||||
| private: | |||||
| static const uint32_t IDLE_STATE = 0; | |||||
| static const uint32_t READ_STATE = 1; | |||||
| static const uint32_t WRITE_STATE = 2; | |||||
| uint32_t m_curBlock; | |||||
| uint8_t m_curState; | |||||
| }; | |||||
| //----------------------------------------------------------------------------- | |||||
| /** typedef for BlockDriver */ | |||||
| #if ENABLE_EXTENDED_TRANSFER_CLASS | |||||
| typedef BaseBlockDriver BlockDriver; | |||||
| #else // ENABLE_EXTENDED_TRANSFER_CLASS | |||||
| typedef SdBlockDriver BlockDriver; | |||||
| #endif // ENABLE_EXTENDED_TRANSFER_CLASS | |||||
| #endif // BlockDriver_h |
| * \class File | * \class File | ||||
| * \brief Arduino SD.h style File API | * \brief Arduino SD.h style File API | ||||
| */ | */ | ||||
| #if ARDUINO_FILE_USES_STREAM | |||||
| class File : public FatFile, public Stream { | class File : public FatFile, public Stream { | ||||
| #else // ARDUINO_FILE_USES_STREAM | |||||
| class File : public FatFile, public Print { | |||||
| #endif // ARDUINO_FILE_USES_STREAM | |||||
| public: | public: | ||||
| File() {} | File() {} | ||||
| /** Create a file object and open it in the current working directory. | /** Create a file object and open it in the current working directory. |
| */ | */ | ||||
| #include "FatLibConfig.h" | #include "FatLibConfig.h" | ||||
| #if ENABLE_ARDUINO_FEATURES | #if ENABLE_ARDUINO_FEATURES | ||||
| #include "SysCall.h" | |||||
| #include "bufstream.h" | #include "bufstream.h" | ||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| uint32_t t; | uint32_t t; | ||||
| m_line[0] = '\0'; | m_line[0] = '\0'; | ||||
| while (!m_hw->available()) { | while (!m_hw->available()) { | ||||
| SysCall::yield(); | |||||
| yield(); | |||||
| } | } | ||||
| while (1) { | while (1) { |
| #ifndef BaseBlockDriver_h | |||||
| #define BaseBlockDriver_h | |||||
| #include "FatLibConfig.h" | |||||
| /** | |||||
| * \class BaseBlockDriver | |||||
| * \brief Base block driver. | |||||
| */ | |||||
| class BaseBlockDriver { | |||||
| public: | |||||
| /** | |||||
| * 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 true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| virtual bool readBlock(uint32_t block, uint8_t* dst) = 0; | |||||
| /** End multi-block transfer and go to idle state. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| virtual bool syncBlocks() = 0; | |||||
| /** | |||||
| * Writes a 512 byte block to an SD card. | |||||
| * | |||||
| * \param[in] block Logical block to be written. | |||||
| * \param[in] src Pointer to the location of the data to be written. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| virtual bool writeBlock(uint32_t block, const uint8_t* src) = 0; | |||||
| #if USE_MULTI_BLOCK_IO | |||||
| /** | |||||
| * Read multiple 512 byte blocks from an SD card. | |||||
| * | |||||
| * \param[in] block Logical block to be read. | |||||
| * \param[in] nb Number of blocks to be read. | |||||
| * \param[out] dst Pointer to the location that will receive the data. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0; | |||||
| /** | |||||
| * Write multiple 512 byte blocks to an SD card. | |||||
| * | |||||
| * \param[in] block Logical block to be written. | |||||
| * \param[in] nb Number of blocks to be written. | |||||
| * \param[in] src Pointer to the location of the data to be written. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0; | |||||
| #endif // USE_MULTI_BLOCK_IO | |||||
| }; | |||||
| #endif // BaseBlockDriver_h |
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // use the gnu style oflag in open() | // use the gnu style oflag in open() | ||||
| /** open() oflag for reading */ | /** open() oflag for reading */ | ||||
| uint8_t const O_READ = 0X01; | |||||
| const uint8_t O_READ = 0X01; | |||||
| /** open() oflag - same as O_IN */ | /** open() oflag - same as O_IN */ | ||||
| uint8_t const O_RDONLY = O_READ; | |||||
| const uint8_t O_RDONLY = O_READ; | |||||
| /** open() oflag for write */ | /** open() oflag for write */ | ||||
| uint8_t const O_WRITE = 0X02; | |||||
| const uint8_t O_WRITE = 0X02; | |||||
| /** open() oflag - same as O_WRITE */ | /** open() oflag - same as O_WRITE */ | ||||
| uint8_t const O_WRONLY = O_WRITE; | |||||
| const uint8_t O_WRONLY = O_WRITE; | |||||
| /** open() oflag for reading and writing */ | /** open() oflag for reading and writing */ | ||||
| uint8_t const O_RDWR = (O_READ | O_WRITE); | |||||
| const uint8_t O_RDWR = (O_READ | O_WRITE); | |||||
| /** open() oflag mask for access modes */ | /** open() oflag mask for access modes */ | ||||
| uint8_t const O_ACCMODE = (O_READ | O_WRITE); | |||||
| const uint8_t O_ACCMODE = (O_READ | O_WRITE); | |||||
| /** The file offset shall be set to the end of the file prior to each write. */ | /** The file offset shall be set to the end of the file prior to each write. */ | ||||
| uint8_t const O_APPEND = 0X04; | |||||
| const uint8_t O_APPEND = 0X04; | |||||
| /** synchronous writes - call sync() after each write */ | /** synchronous writes - call sync() after each write */ | ||||
| uint8_t const O_SYNC = 0X08; | |||||
| const uint8_t O_SYNC = 0X08; | |||||
| /** truncate the file to zero length */ | /** truncate the file to zero length */ | ||||
| uint8_t const O_TRUNC = 0X10; | |||||
| const uint8_t O_TRUNC = 0X10; | |||||
| /** set the initial position at the end of the file */ | /** set the initial position at the end of the file */ | ||||
| uint8_t const O_AT_END = 0X20; | |||||
| const uint8_t O_AT_END = 0X20; | |||||
| /** create the file if nonexistent */ | /** create the file if nonexistent */ | ||||
| uint8_t const O_CREAT = 0X40; | |||||
| const uint8_t O_CREAT = 0X40; | |||||
| /** 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; | |||||
| const uint8_t O_EXCL = 0X80; | |||||
| // FatFile class static and const definitions | // FatFile class static and const definitions | ||||
| // flags for ls() | // flags for ls() | ||||
| /** ls() flag for list all files including hidden. */ | /** ls() flag for list all files including hidden. */ | ||||
| uint8_t const LS_A = 1; | |||||
| const uint8_t LS_A = 1; | |||||
| /** ls() flag to print modify. date */ | /** ls() flag to print modify. date */ | ||||
| uint8_t const LS_DATE = 2; | |||||
| const uint8_t LS_DATE = 2; | |||||
| /** ls() flag to print file size. */ | /** ls() flag to print file size. */ | ||||
| uint8_t const LS_SIZE = 4; | |||||
| const uint8_t LS_SIZE = 4; | |||||
| /** ls() flag for recursive list of subdirectories */ | /** ls() flag for recursive list of subdirectories */ | ||||
| uint8_t const LS_R = 8; | |||||
| const uint8_t LS_R = 8; | |||||
| // flags for timestamp | // flags for timestamp | ||||
| /** set the file's last access date */ | /** set the file's last access date */ | ||||
| uint8_t const T_ACCESS = 1; | |||||
| const uint8_t T_ACCESS = 1; | |||||
| /** set the file's creation date and time */ | /** set the file's creation date and time */ | ||||
| uint8_t const T_CREATE = 2; | |||||
| const uint8_t T_CREATE = 2; | |||||
| /** Set the file's write date and time */ | /** Set the file's write date and time */ | ||||
| uint8_t const T_WRITE = 4; | |||||
| const uint8_t T_WRITE = 4; | |||||
| #endif // FatApiConstants_h | #endif // FatApiConstants_h |
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| block = m_vol->clusterStartBlock(m_curCluster); | |||||
| block = m_vol->clusterFirstBlock(m_curCluster); | |||||
| pc = m_vol->cacheFetchData(block, FatCache::CACHE_RESERVE_FOR_WRITE); | pc = m_vol->cacheFetchData(block, FatCache::CACHE_RESERVE_FOR_WRITE); | ||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| *bgnBlock = m_vol->clusterStartBlock(m_firstCluster); | |||||
| *endBlock = m_vol->clusterStartBlock(c) | |||||
| *bgnBlock = m_vol->clusterFirstBlock(m_firstCluster); | |||||
| *endBlock = m_vol->clusterFirstBlock(c) | |||||
| + m_vol->blocksPerCluster() - 1; | + m_vol->blocksPerCluster() - 1; | ||||
| return true; | return true; | ||||
| } | } | ||||
| bool FatFile::createContiguous(FatFile* dirFile, | bool FatFile::createContiguous(FatFile* dirFile, | ||||
| const char* path, uint32_t size) { | const char* path, uint32_t size) { | ||||
| uint32_t count; | uint32_t count; | ||||
| // don't allow zero length file | // don't allow zero length file | ||||
| if (size == 0) { | if (size == 0) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| // insure sync() will update dir entry | // insure sync() will update dir entry | ||||
| m_flags |= F_FILE_DIR_DIRTY; | m_flags |= F_FILE_DIR_DIRTY; | ||||
| return sync(); | return sync(); | ||||
| fail: | fail: | ||||
| } | } | ||||
| // cache block for '.' and '..' | // cache block for '.' and '..' | ||||
| block = m_vol->clusterStartBlock(m_firstCluster); | |||||
| block = m_vol->clusterFirstBlock(m_firstCluster); | |||||
| pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); | pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); | ||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| if (dirFile->m_dirCluster == 0) { | if (dirFile->m_dirCluster == 0) { | ||||
| return openRoot(dirFile->m_vol); | return openRoot(dirFile->m_vol); | ||||
| } | } | ||||
| lbn = dirFile->m_vol->clusterStartBlock(dirFile->m_dirCluster); | |||||
| lbn = dirFile->m_vol->clusterFirstBlock(dirFile->m_dirCluster); | |||||
| cb = dirFile->m_vol->cacheFetchData(lbn, FatCache::CACHE_FOR_READ); | cb = dirFile->m_vol->cacheFetchData(lbn, FatCache::CACHE_FOR_READ); | ||||
| if (!cb) { | if (!cb) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster; | |||||
| block = m_vol->clusterFirstBlock(m_curCluster) + blockOfCluster; | |||||
| } | } | ||||
| if (offset != 0 || toRead < 512 || block == m_vol->cacheBlockNumber()) { | if (offset != 0 || toRead < 512 || block == m_vol->cacheBlockNumber()) { | ||||
| // amount to be read from current block | // amount to be read from current block | ||||
| // update dot dot if directory | // update dot dot if directory | ||||
| if (dirCluster) { | if (dirCluster) { | ||||
| // get new dot dot | // get new dot dot | ||||
| uint32_t block = m_vol->clusterStartBlock(dirCluster); | |||||
| uint32_t block = m_vol->clusterFirstBlock(dirCluster); | |||||
| pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ); | pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ); | ||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| // store new dot dot | // store new dot dot | ||||
| block = m_vol->clusterStartBlock(m_firstCluster); | |||||
| block = m_vol->clusterFirstBlock(m_firstCluster); | |||||
| pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); | pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); | ||||
| if (!pc) { | if (!pc) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| } | } | ||||
| } | } | ||||
| // block for data write | // block for data write | ||||
| uint32_t block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster; | |||||
| uint32_t block = m_vol->clusterFirstBlock(m_curCluster) + blockOfCluster; | |||||
| if (blockOffset != 0 || nToWrite < 512) { | if (blockOffset != 0 || nToWrite < 512) { | ||||
| // partial block - must use cache | // partial block - must use cache |
| /** Create and open a new contiguous file of a specified size. | /** Create and open a new contiguous file of a specified size. | ||||
| * | * | ||||
| * \param[in] dirFile The directory where the file will be created. | * \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] path A path with a validfile name. | |||||
| * \param[in] size The desired file size. | * \param[in] size The desired file size. | ||||
| * | * | ||||
| * \return The value true is returned for success and | * \return The value true is returned for success and | ||||
| */ | */ | ||||
| bool createContiguous(FatFile* dirFile, | bool createContiguous(FatFile* dirFile, | ||||
| const char* path, uint32_t size); | const char* path, uint32_t size); | ||||
| /** Create and open a new contiguous file of a specified size. | |||||
| * | |||||
| * \param[in] path A path with a validfile name. | |||||
| * \param[in] size The desired file size. | |||||
| * | |||||
| * \return The value true is returned for success and | |||||
| * the value false, is returned for failure. | |||||
| */ | |||||
| bool createContiguous(const char* path, uint32_t size) { | |||||
| return createContiguous(m_cwd, path, size); | |||||
| } | |||||
| /** \return The current cluster number for a file or directory. */ | /** \return The current cluster number for a file or directory. */ | ||||
| uint32_t curCluster() const { | uint32_t curCluster() const { | ||||
| return m_curCluster; | return m_curCluster; | ||||
| m_cwd = dir; | m_cwd = dir; | ||||
| return true; | return true; | ||||
| } | } | ||||
| /** \return first block of file or zero for empty file. */ | |||||
| uint32_t firstBlock() { | |||||
| if (m_firstCluster) { | |||||
| return m_vol->clusterFirstBlock(m_firstCluster); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| /** The sync() call causes all modified data and directory fields | /** The sync() call causes all modified data and directory fields | ||||
| * to be written to the storage device. | * to be written to the storage device. | ||||
| * | * | ||||
| // bits defined in m_flags | // bits defined in m_flags | ||||
| // should be 0X0F | // should be 0X0F | ||||
| static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); | |||||
| static const uint8_t F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); | |||||
| // sync of directory entry required | // sync of directory entry required | ||||
| static uint8_t const F_FILE_DIR_DIRTY = 0X80; | |||||
| static const uint8_t F_FILE_DIR_DIRTY = 0X80; | |||||
| // global pointer to cwd dir | // global pointer to cwd dir | ||||
| static FatFile* m_cwd; | static FatFile* m_cwd; |
| fail: | fail: | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| size_t FatFile::printFileSize(print_t* pr) { | size_t FatFile::printFileSize(print_t* pr) { | ||||
| char buf[11]; | char buf[11]; |
| #define FatFileSystem_h | #define FatFileSystem_h | ||||
| #include "FatVolume.h" | #include "FatVolume.h" | ||||
| #include "FatFile.h" | #include "FatFile.h" | ||||
| #include "ArduinoStream.h" | |||||
| #include "ArduinoFiles.h" | #include "ArduinoFiles.h" | ||||
| /** | /** | ||||
| * \file | * \file | ||||
| public: | public: | ||||
| /** | /** | ||||
| * Initialize an FatFileSystem object. | * Initialize an FatFileSystem object. | ||||
| * \param[in] blockDev Device block driver. | |||||
| * \param[in] part partition to initialize. | * \param[in] part partition to initialize. | ||||
| * \return The value true is returned for success and | * \return The value true is returned for success and | ||||
| * the value false is returned for failure. | * the value false is returned for failure. | ||||
| */ | */ | ||||
| bool begin(uint8_t part = 0) { | |||||
| bool begin(BlockDriver* blockDev, uint8_t part = 0) { | |||||
| m_blockDev = blockDev; | |||||
| vwd()->close(); | vwd()->close(); | ||||
| return (part ? init(part) : init(1) || init(0)) | return (part ? init(part) : init(1) || init(0)) | ||||
| && vwd()->openRoot(this) && FatFile::setCwd(vwd()); | && vwd()->openRoot(this) && FatFile::setCwd(vwd()); | ||||
| * \return a File object. | * \return a File object. | ||||
| */ | */ | ||||
| File open(const String &path, uint8_t mode = FILE_READ) { | File open(const String &path, uint8_t mode = FILE_READ) { | ||||
| return open(path.c_str(), mode ); | |||||
| return open(path.c_str(), mode ); | |||||
| } | } | ||||
| #endif // ENABLE_ARDUINO_FEATURES | #endif // ENABLE_ARDUINO_FEATURES | ||||
| /** Change a volume's working directory to root | /** Change a volume's working directory to root | ||||
| if (!dir.isDir()) { | if (!dir.isDir()) { | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| // *m_vwd = dir; | |||||
| m_vwd = dir; | m_vwd = dir; | ||||
| if (set_cwd) { | if (set_cwd) { | ||||
| FatFile::setCwd(vwd()); | FatFile::setCwd(vwd()); | ||||
| * | * | ||||
| * LS_R - Recursive list of subdirectories. | * LS_R - Recursive list of subdirectories. | ||||
| */ | */ | ||||
| void ls(print_t* pr, uint8_t flags) { | |||||
| void ls(print_t* pr, uint8_t flags = 0) { | |||||
| vwd()->ls(pr, flags); | vwd()->ls(pr, flags); | ||||
| } | } | ||||
| //---------------------------------------------------------------------------- | //---------------------------------------------------------------------------- |
| #define USE_LONG_FILE_NAMES 1 | #define USE_LONG_FILE_NAMES 1 | ||||
| #endif // USE_LONG_FILE_NAMES | #endif // USE_LONG_FILE_NAMES | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Set ARDUINO_FILE_USES_STREAM nonzero to use Stream as the base class | |||||
| * for the Arduino File class. If ARDUINO_FILE_USES_STREAM is zero, Print | |||||
| * will be used as the base class for the Arduino File class. | |||||
| * | |||||
| * You can save some flash if you do not use Stream input functions such as | |||||
| * find(), findUntil(), readBytesUntil(), readString(), readStringUntil(), | |||||
| * parseInt(), and parsefloat(). | |||||
| */ | |||||
| #ifndef ARDUINO_FILE_USES_STREAM | |||||
| #define ARDUINO_FILE_USES_STREAM 1 | |||||
| #endif // ARDUINO_FILE_USES_STREAM | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | /** | ||||
| * Set USE_SEPARATE_FAT_CACHE non-zero to use a second 512 byte cache | * Set USE_SEPARATE_FAT_CACHE non-zero to use a second 512 byte cache | ||||
| * for FAT table entries. Improves performance for large writes that | * for FAT table entries. Improves performance for large writes that | ||||
| #endif // RAMEND | #endif // RAMEND | ||||
| #endif // USE_MULTI_BLOCK_IO | #endif // USE_MULTI_BLOCK_IO | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters | |||||
| * updated. This will increase the speed of the freeClusterCount() call | |||||
| * after the first call. Extra flash will be required. | |||||
| */ | |||||
| #ifndef MAINTAIN_FREE_CLUSTER_COUNT | |||||
| #define MAINTAIN_FREE_CLUSTER_COUNT 0 | |||||
| #endif // MAINTAIN_FREE_CLUSTER_COUNT | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | /** | ||||
| * Set DESTRUCTOR_CLOSES_FILE non-zero to close a file in its destructor. | * Set DESTRUCTOR_CLOSES_FILE non-zero to close a file in its destructor. | ||||
| * | * | ||||
| /** | /** | ||||
| * Enable Extra features for Arduino. | * Enable Extra features for Arduino. | ||||
| */ | */ | ||||
| // #define ENABLE_ARDUINO_FEATURES 0 ////////////////////////FIX THIS ///////////////// | |||||
| #ifndef ENABLE_ARDUINO_FEATURES | #ifndef ENABLE_ARDUINO_FEATURES | ||||
| #include <Arduino.h> | |||||
| #if defined(ARDUINO) || defined(PLATFORM_ID) || defined(DOXYGEN) | #if defined(ARDUINO) || defined(PLATFORM_ID) || defined(DOXYGEN) | ||||
| #define ENABLE_ARDUINO_FEATURES 1 | #define ENABLE_ARDUINO_FEATURES 1 | ||||
| #else // #if defined(ARDUINO) || defined(DOXYGEN) | #else // #if defined(ARDUINO) || defined(DOXYGEN) |
| */ | */ | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Value for byte 510 of boot block or MBR */ | /** Value for byte 510 of boot block or MBR */ | ||||
| uint8_t const BOOTSIG0 = 0X55; | |||||
| const uint8_t BOOTSIG0 = 0X55; | |||||
| /** Value for byte 511 of boot block or MBR */ | /** Value for byte 511 of boot block or MBR */ | ||||
| uint8_t const BOOTSIG1 = 0XAA; | |||||
| const uint8_t BOOTSIG1 = 0XAA; | |||||
| /** Value for bootSignature field int FAT/FAT32 boot sector */ | /** Value for bootSignature field int FAT/FAT32 boot sector */ | ||||
| uint8_t const EXTENDED_BOOT_SIG = 0X29; | |||||
| const uint8_t EXTENDED_BOOT_SIG = 0X29; | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * \struct partitionTable | * \struct partitionTable | ||||
| typedef struct fat32_boot fat32_boot_t; | typedef struct fat32_boot fat32_boot_t; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Lead signature for a FSINFO sector */ | /** Lead signature for a FSINFO sector */ | ||||
| uint32_t const FSINFO_LEAD_SIG = 0x41615252; | |||||
| const uint32_t FSINFO_LEAD_SIG = 0x41615252; | |||||
| /** Struct signature for a FSINFO sector */ | /** Struct signature for a FSINFO sector */ | ||||
| uint32_t const FSINFO_STRUCT_SIG = 0x61417272; | |||||
| const uint32_t FSINFO_STRUCT_SIG = 0x61417272; | |||||
| /** | /** | ||||
| * \struct fat32_fsinfo | * \struct fat32_fsinfo | ||||
| * | * | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // End Of Chain values for FAT entries | // End Of Chain values for FAT entries | ||||
| /** FAT12 end of chain value used by Microsoft. */ | /** FAT12 end of chain value used by Microsoft. */ | ||||
| uint16_t const FAT12EOC = 0XFFF; | |||||
| const uint16_t FAT12EOC = 0XFFF; | |||||
| /** Minimum value for FAT12 EOC. Use to test for EOC. */ | /** Minimum value for FAT12 EOC. Use to test for EOC. */ | ||||
| uint16_t const FAT12EOC_MIN = 0XFF8; | |||||
| const uint16_t FAT12EOC_MIN = 0XFF8; | |||||
| /** FAT16 end of chain value used by Microsoft. */ | /** FAT16 end of chain value used by Microsoft. */ | ||||
| uint16_t const FAT16EOC = 0XFFFF; | |||||
| const uint16_t FAT16EOC = 0XFFFF; | |||||
| /** Minimum value for FAT16 EOC. Use to test for EOC. */ | /** Minimum value for FAT16 EOC. Use to test for EOC. */ | ||||
| uint16_t const FAT16EOC_MIN = 0XFFF8; | |||||
| const uint16_t FAT16EOC_MIN = 0XFFF8; | |||||
| /** FAT32 end of chain value used by Microsoft. */ | /** FAT32 end of chain value used by Microsoft. */ | ||||
| uint32_t const FAT32EOC = 0X0FFFFFFF; | |||||
| const uint32_t FAT32EOC = 0X0FFFFFFF; | |||||
| /** Minimum value for FAT32 EOC. Use to test for EOC. */ | /** Minimum value for FAT32 EOC. Use to test for EOC. */ | ||||
| uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; | |||||
| const uint32_t FAT32EOC_MIN = 0X0FFFFFF8; | |||||
| /** Mask a for FAT32 entry. Entries are 28 bits. */ | /** Mask a for FAT32 entry. Entries are 28 bits. */ | ||||
| uint32_t const FAT32MASK = 0X0FFFFFFF; | |||||
| const uint32_t FAT32MASK = 0X0FFFFFFF; | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * \struct directoryEntry | * \struct directoryEntry | ||||
| // Definitions for directory entries | // Definitions for directory entries | ||||
| // | // | ||||
| /** escape for name[0] = 0XE5 */ | /** escape for name[0] = 0XE5 */ | ||||
| uint8_t const DIR_NAME_0XE5 = 0X05; | |||||
| const uint8_t 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_DELETED = 0XE5; | |||||
| const uint8_t DIR_NAME_DELETED = 0XE5; | |||||
| /** name[0] value for entry that is free and no allocated entries follow */ | /** name[0] value for entry that is free and no allocated entries follow */ | ||||
| uint8_t const DIR_NAME_FREE = 0X00; | |||||
| const uint8_t DIR_NAME_FREE = 0X00; | |||||
| /** file is read-only */ | /** file is read-only */ | ||||
| uint8_t const DIR_ATT_READ_ONLY = 0X01; | |||||
| const uint8_t DIR_ATT_READ_ONLY = 0X01; | |||||
| /** File should e hidden in directory listings */ | /** File should e hidden in directory listings */ | ||||
| uint8_t const DIR_ATT_HIDDEN = 0X02; | |||||
| const uint8_t DIR_ATT_HIDDEN = 0X02; | |||||
| /** Entry is for a system file */ | /** Entry is for a system file */ | ||||
| uint8_t const DIR_ATT_SYSTEM = 0X04; | |||||
| const uint8_t DIR_ATT_SYSTEM = 0X04; | |||||
| /** Directory entry contains the volume label */ | /** Directory entry contains the volume label */ | ||||
| uint8_t const DIR_ATT_VOLUME_ID = 0X08; | |||||
| const uint8_t DIR_ATT_VOLUME_ID = 0X08; | |||||
| /** Entry is for a directory */ | /** Entry is for a directory */ | ||||
| uint8_t const DIR_ATT_DIRECTORY = 0X10; | |||||
| const uint8_t DIR_ATT_DIRECTORY = 0X10; | |||||
| /** Old DOS archive bit for backup support */ | /** Old DOS archive bit for backup support */ | ||||
| uint8_t const DIR_ATT_ARCHIVE = 0X20; | |||||
| const uint8_t DIR_ATT_ARCHIVE = 0X20; | |||||
| /** Test value for long name entry. Test is | /** Test value for long name entry. Test is | ||||
| (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ | (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ | ||||
| uint8_t const DIR_ATT_LONG_NAME = 0X0F; | |||||
| const uint8_t DIR_ATT_LONG_NAME = 0X0F; | |||||
| /** Test mask for long name entry */ | /** Test mask for long name entry */ | ||||
| uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; | |||||
| const uint8_t DIR_ATT_LONG_NAME_MASK = 0X3F; | |||||
| /** defined attribute bits */ | /** defined attribute bits */ | ||||
| uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; | |||||
| const uint8_t DIR_ATT_DEFINED_BITS = 0X3F; | |||||
| /** Mask for file/subdirectory tests */ | /** Mask for file/subdirectory tests */ | ||||
| uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); | |||||
| const uint8_t DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); | |||||
| /** Filename base-name is all lower case */ | /** Filename base-name is all lower case */ | ||||
| const uint8_t DIR_NT_LC_BASE = 0X08; | const uint8_t DIR_NT_LC_BASE = 0X08; | ||||
| return 2*(fatTime & 0X1F); | return 2*(fatTime & 0X1F); | ||||
| } | } | ||||
| /** Default date for file timestamps is 1 Jan 2000 */ | /** Default date for file timestamps is 1 Jan 2000 */ | ||||
| uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; | |||||
| const uint16_t 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); | |||||
| const uint16_t FAT_DEFAULT_TIME = (1 << 11); | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** Dimension of first name field in long directory entry */ | /** Dimension of first name field in long directory entry */ | ||||
| const uint8_t LDIR_NAME1_DIM = 5; | const uint8_t LDIR_NAME1_DIM = 5; |
| return &m_block; | return &m_block; | ||||
| fail: | fail: | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| uint32_t FatVolume::clusterStartBlock(uint32_t cluster) const { | |||||
| uint32_t FatVolume::clusterFirstBlock(uint32_t cluster) const { | |||||
| return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); | return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // error if reserved cluster of beyond FAT | // error if reserved cluster of beyond FAT | ||||
| DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | ||||
| if (m_fatType == 32) { | |||||
| if (fatType() == 32) { | |||||
| lba = m_fatStartBlock + (cluster >> 7); | lba = m_fatStartBlock + (cluster >> 7); | ||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | ||||
| if (!pc) { | if (!pc) { | ||||
| next = pc->fat32[cluster & 0X7F] & FAT32MASK; | next = pc->fat32[cluster & 0X7F] & FAT32MASK; | ||||
| goto done; | goto done; | ||||
| } | } | ||||
| if (m_fatType == 16) { | |||||
| if (fatType() == 16) { | |||||
| lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | ||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | ||||
| if (!pc) { | if (!pc) { | ||||
| next = pc->fat16[cluster & 0XFF]; | next = pc->fat16[cluster & 0XFF]; | ||||
| goto done; | goto done; | ||||
| } | } | ||||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||||
| if (FAT12_SUPPORT && 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); | ||||
| // error if reserved cluster of beyond FAT | // error if reserved cluster of beyond FAT | ||||
| DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | ||||
| if (m_fatType == 32) { | |||||
| if (fatType() == 32) { | |||||
| lba = m_fatStartBlock + (cluster >> 7); | lba = m_fatStartBlock + (cluster >> 7); | ||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | ||||
| if (!pc) { | if (!pc) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (m_fatType == 16) { | |||||
| if (fatType() == 16) { | |||||
| lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | ||||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | ||||
| if (!pc) { | if (!pc) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||||
| if (FAT12_SUPPORT && 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); | ||||
| uint32_t todo = m_lastCluster + 1; | uint32_t todo = m_lastCluster + 1; | ||||
| uint16_t n; | uint16_t n; | ||||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||||
| if (FAT12_SUPPORT && fatType() == 12) { | |||||
| for (unsigned i = 2; i < todo; i++) { | for (unsigned i = 2; i < todo; i++) { | ||||
| uint32_t c; | uint32_t c; | ||||
| int8_t fg = fatGet(i, &c); | int8_t fg = fatGet(i, &c); | ||||
| free++; | free++; | ||||
| } | } | ||||
| } | } | ||||
| } else if (m_fatType == 16 || m_fatType == 32) { | |||||
| } else if (fatType() == 16 || fatType() == 32) { | |||||
| lba = m_fatStartBlock; | lba = m_fatStartBlock; | ||||
| while (todo) { | while (todo) { | ||||
| cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ); | cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ); | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| n = m_fatType == 16 ? 256 : 128; | |||||
| n = fatType() == 16 ? 256 : 128; | |||||
| if (todo < n) { | if (todo < n) { | ||||
| n = todo; | n = todo; | ||||
| } | } | ||||
| if (m_fatType == 16) { | |||||
| if (fatType() == 16) { | |||||
| for (uint16_t i = 0; i < n; i++) { | for (uint16_t i = 0; i < n; i++) { | ||||
| if (pc->fat16[i] == 0) { | if (pc->fat16[i] == 0) { | ||||
| free++; | free++; | ||||
| uint8_t tmp; | uint8_t tmp; | ||||
| m_fatType = 0; | m_fatType = 0; | ||||
| m_allocSearchStart = 1; | m_allocSearchStart = 1; | ||||
| m_cache.init(this); | m_cache.init(this); | ||||
| #if USE_SEPARATE_FAT_CACHE | #if USE_SEPARATE_FAT_CACHE | ||||
| m_fatCache.init(this); | 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) { | ||||
| } | } | ||||
| 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; tmp <<= 1, m_clusterSizeShift++) { | for (tmp = 1; m_blocksPerCluster != tmp; tmp <<= 1, m_clusterSizeShift++) { | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| } | } | ||||
| m_blocksPerFat = fbs->sectorsPerFat16 ? | m_blocksPerFat = fbs->sectorsPerFat16 ? | ||||
| fbs->sectorsPerFat16 : fbs->sectorsPerFat32; | fbs->sectorsPerFat16 : fbs->sectorsPerFat32; | ||||
| // Indicate unknown number of free clusters. | // Indicate unknown number of free clusters. | ||||
| setFreeClusterCount(-1); | setFreeClusterCount(-1); | ||||
| // FAT type is determined by cluster count | // FAT type is determined by cluster count | ||||
| if (clusterCount < 4085) { | if (clusterCount < 4085) { | ||||
| m_fatType = 12; | m_fatType = 12; | ||||
| cache_t* cache; | cache_t* cache; | ||||
| uint16_t count; | uint16_t count; | ||||
| uint32_t lbn; | uint32_t lbn; | ||||
| if (!m_fatType) { | |||||
| if (!fatType()) { | |||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| } | } | ||||
| memset(cache->data, 0, 512); | memset(cache->data, 0, 512); | ||||
| // Zero root. | // Zero root. | ||||
| if (m_fatType == 32) { | |||||
| lbn = clusterStartBlock(m_rootDirStart); | |||||
| if (fatType() == 32) { | |||||
| lbn = clusterFirstBlock(m_rootDirStart); | |||||
| count = m_blocksPerCluster; | count = m_blocksPerCluster; | ||||
| } else { | } else { | ||||
| lbn = m_rootDirStart; | lbn = m_rootDirStart; | ||||
| } | } | ||||
| } | } | ||||
| // Reserve first two clusters. | // Reserve first two clusters. | ||||
| if (m_fatType == 32) { | |||||
| if (fatType() == 32) { | |||||
| cache->fat32[0] = 0x0FFFFFF8; | cache->fat32[0] = 0x0FFFFFF8; | ||||
| cache->fat32[1] = 0x0FFFFFFF; | cache->fat32[1] = 0x0FFFFFFF; | ||||
| } else if (m_fatType == 16) { | |||||
| } else if (fatType() == 16) { | |||||
| cache->fat16[0] = 0XFFF8; | cache->fat16[0] = 0XFFF8; | ||||
| cache->fat16[1] = 0XFFFF; | cache->fat16[1] = 0XFFFF; | ||||
| } else if (FAT12_SUPPORT && m_fatType == 12) { | |||||
| } else if (FAT12_SUPPORT && fatType() == 12) { | |||||
| cache->fat32[0] = 0XFFFFF8; | cache->fat32[0] = 0XFFFFF8; | ||||
| } else { | } else { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| if (m_fatType == 32) { | |||||
| if (fatType() == 32) { | |||||
| // Reserve root cluster. | // Reserve root cluster. | ||||
| if (!fatPutEOC(m_rootDirStart) || !cacheSync()) { | if (!fatPutEOC(m_rootDirStart) || !cacheSync()) { | ||||
| DBG_FAIL_MACRO; | DBG_FAIL_MACRO; |
| * \brief FatVolume class | * \brief FatVolume class | ||||
| */ | */ | ||||
| #include <stddef.h> | #include <stddef.h> | ||||
| #include "SysCall.h" | |||||
| #include "FatLibConfig.h" | #include "FatLibConfig.h" | ||||
| #include "FatStructs.h" | #include "FatStructs.h" | ||||
| #include "BlockDriver.h" | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| #ifndef DOXYGEN_SHOULD_SKIP_THIS | #ifndef DOXYGEN_SHOULD_SKIP_THIS | ||||
| /** Macro for debug. */ | /** Macro for debug. */ | ||||
| #define DEBUG_MODE 0 | #define DEBUG_MODE 0 | ||||
| #if DEBUG_MODE | #if DEBUG_MODE | ||||
| #define DBG_FAIL_MACRO Serial.print(F(__FILE__)); Serial.println(__LINE__) | |||||
| #define DBG_FAIL_MACRO Serial.print(F(__FILE__)); Serial.println(__LINE__); | |||||
| #define DBG_PRINT_IF(b) if (b) {Serial.println(F(#b)); DBG_FAIL_MACRO;} | #define DBG_PRINT_IF(b) if (b) {Serial.println(F(#b)); DBG_FAIL_MACRO;} | ||||
| #define DBG_HALT_IF(b) if (b) {Serial.println(F(#b));\ | #define DBG_HALT_IF(b) if (b) {Serial.println(F(#b));\ | ||||
| DBG_FAIL_MACRO; SysCall::halt();} | |||||
| DBG_FAIL_MACRO; while (1);} | |||||
| #else // DEBUG_MODE | #else // DEBUG_MODE | ||||
| #define DBG_FAIL_MACRO | #define DBG_FAIL_MACRO | ||||
| #define DBG_PRINT_IF(b) | #define DBG_PRINT_IF(b) | ||||
| /** Sync existing block but do not read new block. */ | /** Sync existing block but do not read new block. */ | ||||
| static const uint8_t CACHE_OPTION_NO_READ = 4; | static const uint8_t CACHE_OPTION_NO_READ = 4; | ||||
| /** Cache block for read. */ | /** Cache block for read. */ | ||||
| static uint8_t const CACHE_FOR_READ = 0; | |||||
| static const uint8_t CACHE_FOR_READ = 0; | |||||
| /** Cache block for write. */ | /** Cache block for write. */ | ||||
| static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; | |||||
| static const uint8_t CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; | |||||
| /** Reserve cache block for write - do not read from block device. */ | /** Reserve cache block for write - do not read from block device. */ | ||||
| static uint8_t const CACHE_RESERVE_FOR_WRITE | |||||
| static const uint8_t CACHE_RESERVE_FOR_WRITE | |||||
| = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; | = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; | ||||
| /** \return Cache block address. */ | /** \return Cache block address. */ | ||||
| cache_t* block() { | cache_t* block() { | ||||
| // Allow FatFile and FatCache access to FatVolume private functions. | // Allow FatFile and FatCache access to FatVolume private functions. | ||||
| friend class FatCache; | friend class FatCache; | ||||
| friend class FatFile; | friend class FatFile; | ||||
| friend class FatFileSystem; | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| BlockDriver* m_blockDev; // block device | |||||
| uint8_t m_blocksPerCluster; // Cluster size in blocks. | uint8_t m_blocksPerCluster; // Cluster size in blocks. | ||||
| uint8_t m_clusterBlockMask; // Mask to extract block of cluster. | uint8_t m_clusterBlockMask; // Mask to extract block of cluster. | ||||
| uint8_t m_clusterSizeShift; // Cluster count to block count shift. | uint8_t m_clusterSizeShift; // Cluster count to block count shift. | ||||
| uint32_t m_lastCluster; // Last cluster number in FAT. | uint32_t m_lastCluster; // Last cluster number in FAT. | ||||
| uint32_t m_rootDirStart; // Start block for FAT16, cluster for FAT32. | uint32_t m_rootDirStart; // Start block for FAT16, cluster for FAT32. | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // block I/O functions. | |||||
| bool readBlock(uint32_t block, uint8_t* dst) { | |||||
| return m_blockDev->readBlock(block, dst); | |||||
| } | |||||
| bool syncBlocks() { | |||||
| return m_blockDev->syncBlocks(); | |||||
| } | |||||
| bool writeBlock(uint32_t block, const uint8_t* src) { | |||||
| return m_blockDev->writeBlock(block, src); | |||||
| } | |||||
| #if USE_MULTI_BLOCK_IO | |||||
| bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) { | |||||
| return m_blockDev->readBlocks(block, dst, nb); | |||||
| } | |||||
| bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) { | |||||
| return m_blockDev->writeBlocks(block, src, nb); | |||||
| } | |||||
| #endif // USE_MULTI_BLOCK_IO | |||||
| #if MAINTAIN_FREE_CLUSTER_COUNT | #if MAINTAIN_FREE_CLUSTER_COUNT | ||||
| int32_t m_freeClusterCount; // Count of free clusters in volume. | int32_t m_freeClusterCount; // Count of free clusters in volume. | ||||
| void setFreeClusterCount(int32_t value) { | void setFreeClusterCount(int32_t value) { | ||||
| options | FatCache::CACHE_STATUS_MIRROR_FAT); | options | FatCache::CACHE_STATUS_MIRROR_FAT); | ||||
| } | } | ||||
| bool cacheSync() { | bool cacheSync() { | ||||
| return m_cache.sync() && m_fatCache.sync(); | |||||
| return m_cache.sync() && m_fatCache.sync() && syncBlocks(); | |||||
| } | } | ||||
| #else // | #else // | ||||
| cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { | cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { | ||||
| options | FatCache::CACHE_STATUS_MIRROR_FAT); | options | FatCache::CACHE_STATUS_MIRROR_FAT); | ||||
| } | } | ||||
| bool cacheSync() { | bool cacheSync() { | ||||
| return m_cache.sync(); | |||||
| return m_cache.sync() && syncBlocks(); | |||||
| } | } | ||||
| #endif // USE_SEPARATE_FAT_CACHE | #endif // USE_SEPARATE_FAT_CACHE | ||||
| cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { | cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { | ||||
| uint8_t blockOfCluster(uint32_t position) const { | uint8_t blockOfCluster(uint32_t position) const { | ||||
| return (position >> 9) & m_clusterBlockMask; | return (position >> 9) & m_clusterBlockMask; | ||||
| } | } | ||||
| uint32_t clusterStartBlock(uint32_t cluster) const; | |||||
| uint32_t clusterFirstBlock(uint32_t cluster) const; | |||||
| int8_t fatGet(uint32_t cluster, uint32_t* value); | int8_t fatGet(uint32_t cluster, uint32_t* value); | ||||
| bool fatPut(uint32_t cluster, uint32_t value); | bool fatPut(uint32_t cluster, uint32_t value); | ||||
| bool fatPutEOC(uint32_t cluster) { | bool fatPutEOC(uint32_t cluster) { | ||||
| bool isEOC(uint32_t cluster) const { | bool isEOC(uint32_t cluster) const { | ||||
| return cluster > m_lastCluster; | 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* src) = 0; | |||||
| #if USE_MULTI_BLOCK_IO | |||||
| virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0; | |||||
| virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0; | |||||
| #endif // USE_MULTI_BLOCK_IO | |||||
| }; | }; | ||||
| #endif // FatVolume | #endif // FatVolume |
| * \return The number of free bytes. | * \return The number of free bytes. | ||||
| */ | */ | ||||
| static int FreeStack() { | static int FreeStack() { | ||||
| char top; | |||||
| return __brkval ? &top - __brkval : &top - &__bss_end; | |||||
| char* sp = reinterpret_cast<char*>(SP); | |||||
| return __brkval ? sp - __brkval : sp - &__bss_end; | |||||
| // char top; | |||||
| // return __brkval ? &top - __brkval : &top - &__bss_end; | |||||
| } | } | ||||
| #elif defined(PLATFORM_ID) // Particle board | #elif defined(PLATFORM_ID) // Particle board | ||||
| static int FreeStack() { | static int FreeStack() { | ||||
| #elif defined(__arm__) | #elif defined(__arm__) | ||||
| extern "C" char* sbrk(int incr); | extern "C" char* sbrk(int incr); | ||||
| static int FreeStack() { | static int FreeStack() { | ||||
| char top; | |||||
| char top = 't'; | |||||
| return &top - reinterpret_cast<char*>(sbrk(0)); | return &top - reinterpret_cast<char*>(sbrk(0)); | ||||
| } | } | ||||
| #else | #else |
| * along with the Arduino SdFat Library. If not, see | * along with the Arduino SdFat Library. If not, see | ||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include "SystemInclude.h" | |||||
| #include "SysCall.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; | const uint16_t MIN_2X_BAUD = F_CPU/(4*(2*0XFFF + 1)) + 1; | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| int MinimumSerial::available() { | |||||
| return UCSR0A & (1 << RXC0) ? 1 : 0; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| 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 | ||||
| UCSR0B |= (1 << TXEN0) | (1 << RXEN0); | UCSR0B |= (1 << TXEN0) | (1 << RXEN0); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void MinimumSerial::flush() { | |||||
| while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| int MinimumSerial::read() { | int MinimumSerial::read() { | ||||
| if (UCSR0A & (1 << RXC0)) { | if (UCSR0A & (1 << RXC0)) { | ||||
| return UDR0; | return UDR0; |
| * along with the Arduino SdFat Library. If not, see | * along with the Arduino SdFat Library. If not, see | ||||
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| /** | |||||
| * \file | |||||
| * \brief Minimal AVR Serial driver. | |||||
| */ | |||||
| #ifndef MinimumSerial_h | #ifndef MinimumSerial_h | ||||
| #define MinimumSerial_h | #define MinimumSerial_h | ||||
| #include "SystemInclude.h" | |||||
| #include "SysCall.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class MinimumSerial | * \class MinimumSerial | ||||
| */ | */ | ||||
| class MinimumSerial : public Print { | class MinimumSerial : public Print { | ||||
| public: | public: | ||||
| /** \return true for hardware serial */ | |||||
| operator bool() { return true; } | |||||
| /** | |||||
| * \return one if data is available. | |||||
| */ | |||||
| int available(); | |||||
| /** | /** | ||||
| * Set baud rate for serial port zero and enable in non interrupt mode. | * Set baud rate for serial port zero and enable in non interrupt mode. | ||||
| * Do not call this function if you use another serial library. | * Do not call this function if you use another serial library. | ||||
| * \param[in] baud rate | * \param[in] baud rate | ||||
| */ | */ | ||||
| void begin(uint32_t baud); | void begin(uint32_t baud); | ||||
| /** Wait for write done. */ | |||||
| void flush(); | |||||
| /** | /** | ||||
| * Unbuffered read | * Unbuffered read | ||||
| * \return -1 if no character is available or an available character. | * \return -1 if no character is available or an available character. |
| /* 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); | |||||
| SysCall::halt(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::errorHalt(Print* pr, char const* msg) { | |||||
| errorPrint(pr, msg); | |||||
| SysCall::halt(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| 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::initErrorHalt(Print* pr) { | |||||
| initErrorPrint(pr); | |||||
| SysCall::halt(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::initErrorHalt(Print* pr, char const *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); | |||||
| } | |||||
| #if defined(ARDUINO) || defined(DOXYGEN) | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::errorPrint(Print* pr, const __FlashStringHelper* msg) { | |||||
| pr->print(F("error: ")); | |||||
| pr->println(msg); | |||||
| errorPrint(pr); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::errorHalt(Print* pr, const __FlashStringHelper* msg) { | |||||
| errorPrint(pr, msg); | |||||
| SysCall::halt(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::initErrorHalt(Print* pr, const __FlashStringHelper* msg) { | |||||
| pr->println(msg); | |||||
| initErrorHalt(pr); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdFatBase::initErrorPrint(Print* pr, const __FlashStringHelper* msg) { | |||||
| pr->println(msg); | |||||
| initErrorPrint(pr); | |||||
| } | |||||
| #endif // defined(ARDUINO) || defined(DOXYGEN) |
| /* Arduino SdFat Library | /* Arduino SdFat Library | ||||
| * Copyright (C) 2012 by William Greiman | |||||
| * Copyright (C) 2016 by William Greiman | |||||
| * | * | ||||
| * This file is part of the Arduino SdFat Library | * This file is part of the Arduino SdFat Library | ||||
| * | * | ||||
| * \file | * \file | ||||
| * \brief SdFat class | * \brief SdFat class | ||||
| */ | */ | ||||
| #include "SysCall.h" | |||||
| #include "BlockDriver.h" | |||||
| #ifdef ARDUINO | #ifdef ARDUINO | ||||
| #include "SdSpiCard/SdSpiCard.h" | |||||
| #include "FatLib/FatLib.h" | #include "FatLib/FatLib.h" | ||||
| #else // ARDUINO | #else // ARDUINO | ||||
| #include "SdSpiCard.h" | |||||
| #include "FatLib.h" | #include "FatLib.h" | ||||
| #endif // ARDUINO | #endif // ARDUINO | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** SdFat version YYYYMMDD */ | /** SdFat version YYYYMMDD */ | ||||
| #define SD_FAT_VERSION 20160430 | |||||
| #define SD_FAT_VERSION 20160801 | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class SdBaseFile | * \class SdBaseFile | ||||
| */ | */ | ||||
| SdBaseFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {} | SdBaseFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {} | ||||
| }; | }; | ||||
| //----------------------------------------------------------------------------- | |||||
| #if ENABLE_ARDUINO_FEATURES | #if ENABLE_ARDUINO_FEATURES | ||||
| /** | /** | ||||
| * \class SdFile | * \class SdFile | ||||
| SdFile(const char* path, uint8_t oflag) : PrintFile(path, oflag) {} | SdFile(const char* path, uint8_t oflag) : PrintFile(path, oflag) {} | ||||
| }; | }; | ||||
| #endif // #if ENABLE_ARDUINO_FEATURES | #endif // #if ENABLE_ARDUINO_FEATURES | ||||
| //----------------------------------------------------------------------------- | |||||
| /** | /** | ||||
| * \class SdFatBase | |||||
| * \class SdFileSystem | |||||
| * \brief Virtual base class for %SdFat library. | * \brief Virtual base class for %SdFat library. | ||||
| */ | */ | ||||
| class SdFatBase : public FatFileSystem { | |||||
| template<class SdDriverClass> | |||||
| class SdFileSystem : public FatFileSystem { | |||||
| public: | public: | ||||
| /** Initialize SD card and file system. | /** Initialize SD card and file system. | ||||
| * \param[in] spi SPI object for the card. | * \param[in] spi SPI object for the card. | ||||
| * \param[in] csPin SD card chip select pin. | * \param[in] csPin SD card chip select pin. | ||||
| * \param[in] divisor SPI divisor. | |||||
| * \param[in] spiSettings SPI speed, mode, and bit order. | |||||
| * \return true for success else false. | * \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(); | |||||
| bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||||
| return m_card.begin(spi, csPin, spiSettings) && | |||||
| FatFileSystem::begin(&m_card); | |||||
| } | } | ||||
| /** \return Pointer to SD card object */ | /** \return Pointer to SD card object */ | ||||
| SdSpiCard *card() { | |||||
| return &m_sdCard; | |||||
| SdDriverClass *card() { | |||||
| m_card.syncBlocks(); | |||||
| return &m_card; | |||||
| } | |||||
| /** Initialize SD card for diagnostic use. | |||||
| * \param[in] spi SPI object for the card. | |||||
| * \param[in] csPin SD card chip select pin. | |||||
| * \param[in] spiSettings SPI speed, mode, and bit order. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool cardBegin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||||
| return m_card.begin(spi, csPin, spiSettings); | |||||
| } | } | ||||
| /** %Print any SD error code to Serial and halt. */ | /** %Print any SD error code to Serial and halt. */ | ||||
| void errorHalt() { | void errorHalt() { | ||||
| * | * | ||||
| * \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
| */ | */ | ||||
| void errorHalt(Print* pr); | |||||
| void errorHalt(Print* pr) { | |||||
| errorPrint(pr); | |||||
| SysCall::halt(); | |||||
| } | |||||
| /** %Print msg, any SD error code and halt. | /** %Print msg, any SD error code and halt. | ||||
| * | * | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| * \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void errorHalt(Print* pr, char const* msg); | |||||
| void errorHalt(Print* pr, char const* msg) { | |||||
| errorPrint(pr, msg); | |||||
| SysCall::halt(); | |||||
| } | |||||
| /** %Print any SD error code to Serial */ | /** %Print any SD error code to Serial */ | ||||
| void errorPrint() { | void errorPrint() { | ||||
| errorPrint(&Serial); | errorPrint(&Serial); | ||||
| /** %Print any SD error code. | /** %Print any SD error code. | ||||
| * \param[in] pr Print device. | * \param[in] pr Print device. | ||||
| */ | */ | ||||
| void errorPrint(Print* pr); | |||||
| void errorPrint(Print* pr) { | |||||
| if (!cardErrorCode()) { | |||||
| return; | |||||
| } | |||||
| pr->print(F("SD errorCode: 0X")); | |||||
| pr->print(cardErrorCode(), HEX); | |||||
| pr->print(F(",0X")); | |||||
| pr->println(cardErrorData(), HEX); | |||||
| } | |||||
| /** %Print msg, any SD error code. | /** %Print msg, any SD error code. | ||||
| * | * | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| * \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void errorPrint(Print* pr, char const* msg); | |||||
| /** Diagnostic call to initialize FatFileSystem - use for | |||||
| * diagnostic purposes only. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool fsBegin() { | |||||
| return FatFileSystem::begin(); | |||||
| void errorPrint(Print* pr, char const* msg) { | |||||
| pr->print(F("error: ")); | |||||
| pr->println(msg); | |||||
| errorPrint(pr); | |||||
| } | } | ||||
| /** %Print any SD error code and halt. */ | /** %Print any SD error code and halt. */ | ||||
| void initErrorHalt() { | void initErrorHalt() { | ||||
| * | * | ||||
| * \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
| */ | */ | ||||
| void initErrorHalt(Print* pr); | |||||
| /**Print message, error details, and halt after SdFat::init() fails. | |||||
| void initErrorHalt(Print* pr) { | |||||
| initErrorPrint(pr); | |||||
| SysCall::halt(); | |||||
| } | |||||
| /**Print message, error details, and halt after begin() fails. | |||||
| * | * | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void initErrorHalt(char const *msg) { | void initErrorHalt(char const *msg) { | ||||
| initErrorHalt(&Serial, msg); | initErrorHalt(&Serial, msg); | ||||
| } | } | ||||
| /**Print message, error details, and halt after SdFatBase::init() fails. | |||||
| /**Print message, error details, and halt after begin() fails. | |||||
| * \param[in] pr Print device. | * \param[in] pr Print device. | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void initErrorHalt(Print* pr, char const *msg); | |||||
| void initErrorHalt(Print* pr, char const *msg) { | |||||
| pr->println(msg); | |||||
| initErrorHalt(pr); | |||||
| } | |||||
| /** Print error details after SdFat::init() fails. */ | |||||
| /** Print error details after begin() fails. */ | |||||
| void initErrorPrint() { | void initErrorPrint() { | ||||
| initErrorPrint(&Serial); | initErrorPrint(&Serial); | ||||
| } | } | ||||
| /** Print error details after SdFatBase::init() fails. | |||||
| /** Print error details after begin() fails. | |||||
| * | * | ||||
| * \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
| */ | */ | ||||
| void initErrorPrint(Print* pr); | |||||
| /**Print message and error details and halt after SdFat::init() fails. | |||||
| void 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.")); | |||||
| } | |||||
| } | |||||
| /**Print message and error details and halt after begin() fails. | |||||
| * | * | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void initErrorPrint(char const *msg) { | void initErrorPrint(char const *msg) { | ||||
| initErrorPrint(&Serial, msg); | initErrorPrint(&Serial, msg); | ||||
| } | } | ||||
| /**Print message and error details and halt after SdFatBase::init() fails. | |||||
| /**Print message and error details and halt after begin() fails. | |||||
| * | * | ||||
| * \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void initErrorPrint(Print* pr, char const *msg); | |||||
| void initErrorPrint(Print* pr, char const *msg) { | |||||
| pr->println(msg); | |||||
| initErrorPrint(pr); | |||||
| } | |||||
| #if defined(ARDUINO) || defined(DOXYGEN) | #if defined(ARDUINO) || defined(DOXYGEN) | ||||
| /** %Print msg, any SD error code, and halt. | /** %Print msg, any SD error code, and halt. | ||||
| * | * | ||||
| * \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void errorHalt(Print* pr, const __FlashStringHelper* msg); | |||||
| void errorHalt(Print* pr, const __FlashStringHelper* msg) { | |||||
| errorPrint(pr, msg); | |||||
| SysCall::halt(); | |||||
| } | |||||
| /** %Print msg, any SD error code. | /** %Print msg, any SD error code. | ||||
| * | * | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| * \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void errorPrint(Print* pr, const __FlashStringHelper* msg); | |||||
| /**Print message, error details, and halt after SdFat::init() fails. | |||||
| void errorPrint(Print* pr, const __FlashStringHelper* msg) { | |||||
| pr->print(F("error: ")); | |||||
| pr->println(msg); | |||||
| errorPrint(pr); | |||||
| } | |||||
| /**Print message, error details, and halt after begin() fails. | |||||
| * | * | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void initErrorHalt(const __FlashStringHelper* msg) { | void initErrorHalt(const __FlashStringHelper* msg) { | ||||
| initErrorHalt(&Serial, msg); | initErrorHalt(&Serial, msg); | ||||
| } | } | ||||
| /**Print message, error details, and halt after SdFatBase::init() fails. | |||||
| /**Print message, error details, and halt after begin() fails. | |||||
| * \param[in] pr Print device for message. | * \param[in] pr Print device for message. | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void initErrorHalt(Print* pr, const __FlashStringHelper* msg); | |||||
| /**Print message and error details and halt after SdFat::init() fails. | |||||
| void initErrorHalt(Print* pr, const __FlashStringHelper* msg) { | |||||
| pr->println(msg); | |||||
| initErrorHalt(pr); | |||||
| } | |||||
| /**Print message and error details and halt after begin() fails. | |||||
| * | * | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void initErrorPrint(const __FlashStringHelper* msg) { | void initErrorPrint(const __FlashStringHelper* msg) { | ||||
| initErrorPrint(&Serial, msg); | initErrorPrint(&Serial, msg); | ||||
| } | } | ||||
| /**Print message and error details and halt after SdFatBase::init() fails. | |||||
| /**Print message and error details and halt after begin() fails. | |||||
| * | * | ||||
| * \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
| * \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
| */ | */ | ||||
| void initErrorPrint(Print* pr, const __FlashStringHelper* msg); | |||||
| void initErrorPrint(Print* pr, const __FlashStringHelper* msg) { | |||||
| pr->println(msg); | |||||
| initErrorPrint(pr); | |||||
| } | |||||
| #endif // defined(ARDUINO) || defined(DOXYGEN) | #endif // defined(ARDUINO) || defined(DOXYGEN) | ||||
| private: | |||||
| /** \return The card error code */ | |||||
| uint8_t cardErrorCode() { | uint8_t cardErrorCode() { | ||||
| return m_sdCard.errorCode(); | |||||
| return m_card.errorCode(); | |||||
| } | } | ||||
| /** \return the card error data */ | |||||
| uint8_t cardErrorData() { | 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); | |||||
| return m_card.errorData(); | |||||
| } | } | ||||
| SdSpiCard m_sdCard; | |||||
| private: | |||||
| SdDriverClass m_card; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class SdFat | * \class SdFat | ||||
| * \brief Main file system class for %SdFat library. | * \brief Main file system class for %SdFat library. | ||||
| */ | */ | ||||
| class SdFat : public SdFatBase { | |||||
| class SdFat : public SdFileSystem<SdBlockDriver> { | |||||
| public: | public: | ||||
| #if IMPLEMENT_SPI_INTERFACE_SELECTION | |||||
| #if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) | |||||
| SdFat() { | SdFat() { | ||||
| m_spi.setSpiIf(0); | |||||
| m_spi.setPort(0); | |||||
| } | } | ||||
| explicit SdFat(uint8_t spiIf) { | |||||
| m_spi.setSpiIf(spiIf < SPI_INTERFACE_COUNT ? spiIf : 0); | |||||
| /** Constructor with SPI port selection. | |||||
| * \param[in] spiPort SPI port number. | |||||
| */ | |||||
| explicit SdFat(uint8_t spiPort) { | |||||
| m_spi.setPort(spiPort); | |||||
| } | } | ||||
| #endif // IMPLEMENT_SPI_INTERFACE_SELECTION | |||||
| #endif // IMPLEMENT_SPI_PORT_SELECTION | |||||
| /** Initialize SD card and file system. | /** Initialize SD card and file system. | ||||
| * | * | ||||
| * \param[in] csPin SD card chip select pin. | * \param[in] csPin SD card chip select pin. | ||||
| * \param[in] divisor SPI divisor. | |||||
| * \param[in] spiSettings SPI speed, mode, and bit order. | |||||
| * \return true for success else false. | * \return true for success else false. | ||||
| */ | |||||
| bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return SdFatBase::begin(&m_spi, csPin, divisor); | |||||
| */ | |||||
| bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { | |||||
| return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||||
| } | } | ||||
| /** Diagnostic call to initialize SD card - use for diagnostic purposes only. | |||||
| /** Initialize SD card for diagnostic use only. | |||||
| * | |||||
| * \param[in] csPin SD card chip select pin. | * \param[in] csPin SD card chip select pin. | ||||
| * \param[in] divisor SPI divisor. | |||||
| * \param[in] settings SPI speed, mode, and bit order. | |||||
| * \return true for success else false. | * \return true for success else false. | ||||
| */ | |||||
| bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return card()->begin(&m_spi, csPin, divisor); | |||||
| */ | |||||
| bool cardBegin(uint8_t csPin = SS, SPISettings settings = SPI_FULL_SPEED) { | |||||
| return SdFileSystem::cardBegin(&m_spi, csPin, settings); | |||||
| } | |||||
| /** Initialize file system for diagnostic use only. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool fsBegin() { | |||||
| return FatFileSystem::begin(card()); | |||||
| } | } | ||||
| private: | private: | ||||
| SpiDefault_t m_spi; | |||||
| SdFatSpiDriver m_spi; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| #if SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) | |||||
| #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
| /** | /** | ||||
| * \class SdFatLibSpi | |||||
| * \brief SdFat class using the standard Arduino SPI library. | |||||
| * \class SdFatSoftSpi | |||||
| * \brief SdFat class using software SPI. | |||||
| */ | */ | ||||
| class SdFatLibSpi: public SdFatBase { | |||||
| template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||||
| class SdFatSoftSpi : public SdFileSystem<SdBlockDriver> { | |||||
| public: | public: | ||||
| /** Initialize SD card and file system. | /** 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); | |||||
| } | |||||
| /** Diagnostic call to initialize SD card - use for diagnostic purposes only. | |||||
| * | |||||
| * \param[in] csPin SD card chip select pin. | * \param[in] csPin SD card chip select pin. | ||||
| * \param[in] divisor SPI divisor. | |||||
| * \param[in] spiSettings ignored for software SPI.. | |||||
| * \return true for success else false. | * \return true for success else false. | ||||
| */ | */ | ||||
| bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return card()->begin(&m_spi, csPin, divisor); | |||||
| bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { | |||||
| return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||||
| } | |||||
| private: | |||||
| SdSpiSoftDriver<MisoPin, MosiPin, SckPin> m_spi; | |||||
| }; | |||||
| #endif // #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
| #if ENABLE_EXTENDED_TRANSFER_CLASS || defined(DOXYGEN) | |||||
| //============================================================================== | |||||
| /** | |||||
| * \class SdFatEX | |||||
| * \brief SdFat class with extended SD I/O. | |||||
| */ | |||||
| class SdFatEX : public SdFileSystem<SdBlockDriverEX> { | |||||
| public: | |||||
| #if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) | |||||
| SdFatEX() { | |||||
| m_spi.setPort(0); | |||||
| } | |||||
| /** Constructor with SPI port selection. | |||||
| * \param[in] spiPort SPI port number. | |||||
| */ | |||||
| explicit SdFatEX(uint8_t spiPort) { | |||||
| m_spi.setPort(spiPort); | |||||
| } | |||||
| #endif // IMPLEMENT_SPI_PORT_SELECTION | |||||
| /** Initialize SD card and file system. | |||||
| * | |||||
| * \param[in] csPin SD card chip select pin. | |||||
| * \param[in] spiSettings SPI speed, mode, and bit order. | |||||
| * \return true for success else false. | |||||
| */ | |||||
| bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { | |||||
| return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||||
| } | } | ||||
| private: | private: | ||||
| SdSpiLib m_spi; | |||||
| SdFatSpiDriver m_spi; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
| /** | /** | ||||
| * \class SdFatSoftSpi | |||||
| * \brief SdFat class using software SPI. | |||||
| * \class SdFatSoftSpiEX | |||||
| * \brief SdFat class using software SPI and extended SD I/O. | |||||
| */ | */ | ||||
| template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | ||||
| class SdFatSoftSpi : public SdFatBase { | |||||
| class SdFatSoftSpiEX : public SdFileSystem<SdBlockDriverEX> { | |||||
| public: | public: | ||||
| /** Initialize SD card and file system. | /** Initialize SD card and file system. | ||||
| * | * | ||||
| * \param[in] csPin SD card chip select pin. | * \param[in] csPin SD card chip select pin. | ||||
| * \param[in] divisor SPI divisor. | |||||
| * \param[in] spiSettings ignored for software SPI. | |||||
| * \return true for success else false. | * \return true for success else false. | ||||
| */ | */ | ||||
| bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return SdFatBase::begin(&m_spi, csPin, divisor); | |||||
| bool begin(uint8_t csPin = SS, SPISettings spiSettings = 2) { | |||||
| return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||||
| } | } | ||||
| /** Diagnostic call to initialize SD card - use for diagnostic purposes only. | |||||
| * \param[in] csPin SD card chip select pin. | |||||
| * \param[in] divisor SPI divisor. | |||||
| private: | |||||
| SdSpiSoftDriver<MisoPin, MosiPin, SckPin> m_spi; | |||||
| }; | |||||
| #endif // #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
| #endif // ENABLE_EXTENDED_TRANSFER_CLASS || defined(DOXYGEN) | |||||
| //============================================================================= | |||||
| /** | |||||
| * \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] spiSettings SPI speed, mode, and bit order. | |||||
| * \return true for success else false. | * \return true for success else false. | ||||
| */ | */ | ||||
| bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
| return card()->begin(&m_spi, csPin, divisor); | |||||
| bool begin(uint8_t chipSelectPin = SS, | |||||
| SPISettings spiSettings = SD_SCK_MHZ(50)) { | |||||
| m_spi.begin(chipSelectPin); | |||||
| m_spi.setSpiSettings(SD_SCK_HZ(250000)); | |||||
| if (!SdSpiCard::begin(&m_spi)) { | |||||
| return false; | |||||
| } | |||||
| m_spi.setSpiSettings(spiSettings); | |||||
| return true; | |||||
| } | } | ||||
| private: | private: | ||||
| SdSpiSoft<MisoPin, MosiPin, SckPin> m_spi; | |||||
| SdFatSpiDriver m_spi; | |||||
| }; | }; | ||||
| #endif /// SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) | |||||
| #endif // SdFat_h | #endif // SdFat_h |
| #define USE_LONG_FILE_NAMES 1 | #define USE_LONG_FILE_NAMES 1 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * Set ARDUINO_FILE_USES_STREAM nonzero to use Stream as the base class | |||||
| * for the Arduino File class. If ARDUINO_FILE_USES_STREAM is zero, Print | |||||
| * will be used as the base class for the Arduino File class. | |||||
| * If the symbol ENABLE_EXTENDED_TRANSFER_CLASS is nonzero, the class SdFatEX | |||||
| * will be defined. If the symbol ENABLE_SOFTWARE_SPI_CLASS is also nonzero, | |||||
| * the class SdFatSoftSpiEX will be defined. | |||||
| * | * | ||||
| * You can save some flash if you do not use Stream input functions such as | |||||
| * find(), findUntil(), readBytesUntil(), readString(), readStringUntil(), | |||||
| * parseInt(), and parseFloat(). | |||||
| * These classes used extended multi-block SD I/O for better performance. | |||||
| * the SPI bus may not be shared with other devices in this mode. | |||||
| */ | */ | ||||
| #define ARDUINO_FILE_USES_STREAM 1 | |||||
| //------------------------------------------------------------------------------ | |||||
| #define ENABLE_EXTENDED_TRANSFER_CLASS 0 | |||||
| //----------------------------------------------------------------------------- | |||||
| /** | /** | ||||
| * The symbol SD_SPI_CONFIGURATION defines SPI access to the SD card. | |||||
| * | |||||
| * IF SD_SPI_CONFIGUTATION is define to be zero, only the SdFat class | |||||
| * is define and SdFat uses a fast custom SPI implementation if avaiable. | |||||
| * If SD_HAS_CUSTOM_SPI is zero, the standard SPI library is used. | |||||
| * | |||||
| * If SD_SPI_CONFIGURATION is define to be one, only the SdFat class is | |||||
| * define and SdFat uses the standard Arduino SPI.h library. | |||||
| * | |||||
| * If SD_SPI_CONFIGURATION is define to be two, only the SdFat class is | |||||
| * define and SdFat uses software SPI on the pins defined below. | |||||
| * | |||||
| * If SD_SPI_CONFIGURATION is define to be three, the three classes, SdFat, | |||||
| * SdFatLibSpi, and SdFatSoftSpi are defined. SdFat uses the fast | |||||
| * custom SPI implementation. SdFatLibSpi uses the standard Arduino SPI | |||||
| * library. SdFatSoftSpi is a template class that uses Software SPI. The | |||||
| * template parameters define the software SPI pins. See the ThreeCard | |||||
| * example for simultaneous use of all three classes. | |||||
| */ | |||||
| #define SD_SPI_CONFIGURATION 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| * If the symbol USE_STANDARD_SPI_LIBRARY is nonzero, the classes SdFat and | |||||
| * SdFatEX use the standard Arduino SPI.h library. If USE_STANDARD_SPI_LIBRARY | |||||
| * is zero, an optimized custom SPI driver is used if it exists. | |||||
| */ | |||||
| #define USE_STANDARD_SPI_LIBRARY 0 | |||||
| //----------------------------------------------------------------------------- | |||||
| /** | /** | ||||
| * If SD_SPI_CONFIGURATION is defined to be two, these definitions | |||||
| * will define the pins used for software SPI. | |||||
| * | |||||
| * The default definition allows Uno shields to be used on other boards. | |||||
| * If the symbol ENABLE_SOFTWARE_SPI_CLASS is nonzero, the class SdFatSoftSpi | |||||
| * will be defined. If ENABLE_EXTENDED_TRANSFER_CLASS is also nonzero, | |||||
| * the class SdFatSoftSpiEX will be defined. | |||||
| */ | */ | ||||
| /** Software SPI Master Out Slave In pin */ | |||||
| uint8_t const SOFT_SPI_MOSI_PIN = 11; | |||||
| /** Software SPI Master In Slave Out pin */ | |||||
| uint8_t const SOFT_SPI_MISO_PIN = 12; | |||||
| /** Software SPI Clock pin */ | |||||
| uint8_t const SOFT_SPI_SCK_PIN = 13; | |||||
| #define ENABLE_SOFTWARE_SPI_CLASS 0 | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters | * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters | ||||
| /** | /** | ||||
| * To enable SD card CRC checking set USE_SD_CRC nonzero. | * To enable SD card CRC checking set USE_SD_CRC nonzero. | ||||
| * | * | ||||
| * Set USE_SD_CRC to 1 to use a smaller slower CRC-CCITT function. | |||||
| * Set USE_SD_CRC to 1 to use a smaller CRC-CCITT function. This function | |||||
| * is slower for AVR but may be fast for ARM and other processors. | |||||
| * | * | ||||
| * Set USE_SD_CRC to 2 to used a larger faster table driven CRC-CCITT function. | |||||
| * Set USE_SD_CRC to 2 to used a larger table driven CRC-CCITT function. This | |||||
| * function is faster for AVR but may be slower for ARM and other processors. | |||||
| */ | */ | ||||
| #define USE_SD_CRC 0 | #define USE_SD_CRC 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * Set ENABLE_SPI_TRANSACTIONS nonzero to enable the SPI transaction feature | |||||
| * of the standard Arduino SPI library. You must include SPI.h in your | |||||
| * programs when ENABLE_SPI_TRANSACTIONS is nonzero. | |||||
| */ | |||||
| #define ENABLE_SPI_TRANSACTIONS 0 | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | /** | ||||
| * Handle Watchdog Timer for WiFi modules. | * Handle Watchdog Timer for WiFi modules. | ||||
| * | * | ||||
| */ | */ | ||||
| #define ENDL_CALLS_FLUSH 0 | #define ENDL_CALLS_FLUSH 0 | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | |||||
| * SPI SCK divisor for SD initialization commands. | |||||
| * or greater | |||||
| */ | |||||
| #ifdef __AVR__ | |||||
| const uint8_t SPI_SCK_INIT_DIVISOR = 64; | |||||
| #else | |||||
| const uint8_t SPI_SCK_INIT_DIVISOR = 128; | |||||
| #endif | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | /** | ||||
| * Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache | * Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache | ||||
| * for FAT table entries. This improves performance for large writes | * for FAT table entries. This improves performance for large writes | ||||
| /** | /** | ||||
| * Determine the default SPI configuration. | * Determine the default SPI configuration. | ||||
| */ | */ | ||||
| #if defined(__AVR__)\ | |||||
| #if defined(__STM32F1__) | |||||
| // has multiple SPI ports | |||||
| #define SD_HAS_CUSTOM_SPI 2 | |||||
| #elif defined(__AVR__)\ | |||||
| || defined(__SAM3X8E__) || defined(__SAM3X8H__)\ | || defined(__SAM3X8E__) || defined(__SAM3X8H__)\ | ||||
| || (defined(__arm__) && defined(CORE_TEENSY))\ | || (defined(__arm__) && defined(CORE_TEENSY))\ | ||||
| || defined(__STM32F1__)\ | |||||
| || defined(PLATFORM_ID)\ | |||||
| || defined(ESP8266)\ | |||||
| || defined(DOXYGEN) | |||||
| // Use custom fast implementation. | |||||
| || defined(ESP8266) | |||||
| #define SD_HAS_CUSTOM_SPI 1 | #define SD_HAS_CUSTOM_SPI 1 | ||||
| #else // SD_HAS_CUSTOM_SPI | #else // SD_HAS_CUSTOM_SPI | ||||
| // Use standard SPI library. | // Use standard SPI library. | ||||
| #define SD_HAS_CUSTOM_SPI 0 | #define SD_HAS_CUSTOM_SPI 0 | ||||
| #endif // SD_HAS_CUSTOM_SPI | #endif // SD_HAS_CUSTOM_SPI | ||||
| //----------------------------------------------------------------------------- | |||||
| /** | |||||
| * Number of hardware interfaces. | |||||
| */ | |||||
| #if defined(PLATFORM_ID) | |||||
| #if Wiring_SPI1 && Wiring_SPI2 | |||||
| #define SPI_INTERFACE_COUNT 3 | |||||
| #elif Wiring_SPI1 | |||||
| #define SPI_INTERFACE_COUNT 2 | |||||
| #endif // Wiring_SPI1 && Wiring_SPI2 | |||||
| #endif // defined(PLATFORM_ID) | |||||
| // default is one | |||||
| #ifndef SPI_INTERFACE_COUNT | |||||
| #define SPI_INTERFACE_COUNT 1 | |||||
| #endif // SPI_INTERFACE_COUNT | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| /** | /** | ||||
| * Check if API to select HW SPI interface is needed. | |||||
| * Check if API to select HW SPI port is needed. | |||||
| */ | */ | ||||
| #if SPI_INTERFACE_COUNT > 1 && SD_HAS_CUSTOM_SPI\ | |||||
| && SD_SPI_CONFIGURATION != 1 && SD_SPI_CONFIGURATION != 2 | |||||
| #define IMPLEMENT_SPI_INTERFACE_SELECTION 1 | |||||
| #else // SPI_INTERFACE_COUNT > 1 | |||||
| #define IMPLEMENT_SPI_INTERFACE_SELECTION 0 | |||||
| #endif // SPI_INTERFACE_COUNT > 1 | |||||
| #if (USE_STANDARD_SPI_LIBRARY || SD_HAS_CUSTOM_SPI < 2) | |||||
| #define IMPLEMENT_SPI_PORT_SELECTION 0 | |||||
| #else // USE_STANDARD_SPI_LIBRARY | |||||
| #define IMPLEMENT_SPI_PORT_SELECTION 1 | |||||
| #endif // USE_STANDARD_SPI_LIBRARY | |||||
| #endif // SdFatConfig_h | #endif // SdFatConfig_h |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2015 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 <stdlib.h> | |||||
| #include "SdFat.h" | |||||
| #include "SdFatUtil.h" | |||||
| //------------------------------------------------------------------------------ | |||||
| #ifdef __arm__ | |||||
| extern "C" char* sbrk(int incr); | |||||
| int SdFatUtil::FreeRam() { | |||||
| char top; | |||||
| return &top - reinterpret_cast<char*>(sbrk(0)); | |||||
| } | |||||
| #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; | |||||
| } | |||||
| #endif // __arm | |||||
| /* 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 SdFatUtil_h | |||||
| #define SdFatUtil_h | |||||
| /** | |||||
| * \file | |||||
| * \brief Useful utility functions. | |||||
| */ | |||||
| #include "SdFat.h" | |||||
| namespace SdFatUtil { | |||||
| /** Amount of free RAM | |||||
| * \return The number of free bytes. | |||||
| */ | |||||
| int FreeRam(); | |||||
| } // namespace SdFatUtil | |||||
| using namespace SdFatUtil; // NOLINT | |||||
| #endif // #define SdFatUtil_h |
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // SD card errors | // SD card errors | ||||
| /** timeout error for command CMD0 (initialize card in SPI mode) */ | /** timeout error for command CMD0 (initialize card in SPI mode) */ | ||||
| uint8_t const SD_CARD_ERROR_CMD0 = 0X1; | |||||
| const uint8_t SD_CARD_ERROR_CMD0 = 0X1; | |||||
| /** CMD8 was not accepted - not a valid SD card*/ | /** CMD8 was not accepted - not a valid SD card*/ | ||||
| uint8_t const SD_CARD_ERROR_CMD8 = 0X2; | |||||
| const uint8_t SD_CARD_ERROR_CMD8 = 0X2; | |||||
| /** card returned an error response for CMD12 (stop multiblock read) */ | /** card returned an error response for CMD12 (stop multiblock read) */ | ||||
| uint8_t const SD_CARD_ERROR_CMD12 = 0X3; | |||||
| const uint8_t SD_CARD_ERROR_CMD12 = 0X3; | |||||
| /** card returned an error response for CMD17 (read block) */ | /** card returned an error response for CMD17 (read block) */ | ||||
| uint8_t const SD_CARD_ERROR_CMD17 = 0X4; | |||||
| const uint8_t SD_CARD_ERROR_CMD17 = 0X4; | |||||
| /** card returned an error response for CMD18 (read multiple block) */ | /** card returned an error response for CMD18 (read multiple block) */ | ||||
| uint8_t const SD_CARD_ERROR_CMD18 = 0X5; | |||||
| const uint8_t SD_CARD_ERROR_CMD18 = 0X5; | |||||
| /** card returned an error response for CMD24 (write block) */ | /** card returned an error response for CMD24 (write block) */ | ||||
| uint8_t const SD_CARD_ERROR_CMD24 = 0X6; | |||||
| const uint8_t SD_CARD_ERROR_CMD24 = 0X6; | |||||
| /** WRITE_MULTIPLE_BLOCKS command failed */ | /** WRITE_MULTIPLE_BLOCKS command failed */ | ||||
| uint8_t const SD_CARD_ERROR_CMD25 = 0X7; | |||||
| const uint8_t SD_CARD_ERROR_CMD25 = 0X7; | |||||
| /** card returned an error response for CMD58 (read OCR) */ | /** card returned an error response for CMD58 (read OCR) */ | ||||
| uint8_t const SD_CARD_ERROR_CMD58 = 0X8; | |||||
| const uint8_t SD_CARD_ERROR_CMD58 = 0X8; | |||||
| /** SET_WR_BLK_ERASE_COUNT failed */ | /** SET_WR_BLK_ERASE_COUNT failed */ | ||||
| uint8_t const SD_CARD_ERROR_ACMD23 = 0X9; | |||||
| const uint8_t SD_CARD_ERROR_ACMD23 = 0X9; | |||||
| /** ACMD41 initialization process timeout */ | /** ACMD41 initialization process timeout */ | ||||
| uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; | |||||
| const uint8_t SD_CARD_ERROR_ACMD41 = 0XA; | |||||
| /** card returned a bad CSR version field */ | /** card returned a bad CSR version field */ | ||||
| uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB; | |||||
| const uint8_t SD_CARD_ERROR_BAD_CSD = 0XB; | |||||
| /** erase block group command failed */ | /** erase block group command failed */ | ||||
| uint8_t const SD_CARD_ERROR_ERASE = 0XC; | |||||
| const uint8_t SD_CARD_ERROR_ERASE = 0XC; | |||||
| /** card not capable of single block erase */ | /** card not capable of single block erase */ | ||||
| uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; | |||||
| const uint8_t SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; | |||||
| /** Erase sequence timed out */ | /** Erase sequence timed out */ | ||||
| uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; | |||||
| const uint8_t SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; | |||||
| /** card returned an error token instead of read data */ | /** card returned an error token instead of read data */ | ||||
| uint8_t const SD_CARD_ERROR_READ = 0XF; | |||||
| const uint8_t SD_CARD_ERROR_READ = 0XF; | |||||
| /** read CID or CSD failed */ | /** read CID or CSD failed */ | ||||
| uint8_t const SD_CARD_ERROR_READ_REG = 0X10; | |||||
| const uint8_t SD_CARD_ERROR_READ_REG = 0X10; | |||||
| /** timeout while waiting for start of read data */ | /** timeout while waiting for start of read data */ | ||||
| uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11; | |||||
| const uint8_t SD_CARD_ERROR_READ_TIMEOUT = 0X11; | |||||
| /** card did not accept STOP_TRAN_TOKEN */ | /** card did not accept STOP_TRAN_TOKEN */ | ||||
| uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12; | |||||
| const uint8_t SD_CARD_ERROR_STOP_TRAN = 0X12; | |||||
| /** card returned an error token as a response to a write operation */ | /** 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 | |||||
| const uint8_t SD_CARD_ERROR_WRITE = 0X13; | |||||
| /** card busy for command */ | |||||
| const uint8_t SD_CARD_ERROR_CMD_BUSY = 0X14; | |||||
| /** card did not go ready for a multiple block write */ | /** card did not go ready for a multiple block write */ | ||||
| uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; // Not used | |||||
| const uint8_t SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; // Not used | |||||
| /** card returned an error to a CMD13 status check after a write */ | /** card returned an error to a CMD13 status check after a write */ | ||||
| uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||||
| const uint8_t SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||||
| /** timeout occurred during write programming */ | /** timeout occurred during write programming */ | ||||
| uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; | |||||
| const uint8_t SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; | |||||
| /** incorrect rate selected */ | /** incorrect rate selected */ | ||||
| uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18; | |||||
| const uint8_t SD_CARD_ERROR_SCK_RATE = 0X18; | |||||
| /** init() not called */ | /** init() not called */ | ||||
| uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; | |||||
| const uint8_t SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; | |||||
| /** card returned an error for CMD59 (CRC_ON_OFF) */ | /** card returned an error for CMD59 (CRC_ON_OFF) */ | ||||
| uint8_t const SD_CARD_ERROR_CMD59 = 0X1A; | |||||
| const uint8_t SD_CARD_ERROR_CMD59 = 0X1A; | |||||
| /** invalid read CRC */ | /** invalid read CRC */ | ||||
| uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B; | |||||
| const uint8_t SD_CARD_ERROR_READ_CRC = 0X1B; | |||||
| /** SPI DMA error */ | /** SPI DMA error */ | ||||
| uint8_t const SD_CARD_ERROR_SPI_DMA = 0X1C; | |||||
| /** CMD6 not accepted */ | |||||
| uint8_t const SD_CARD_ERROR_CMD6 = 0X1D; | |||||
| const uint8_t SD_CARD_ERROR_SPI_DMA = 0X1C; | |||||
| /** ACMD13 not accepted */ | |||||
| const uint8_t SD_CARD_ERROR_ACMD13 = 0X1D; | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // card types | // card types | ||||
| /** Standard capacity V1 SD card */ | /** Standard capacity V1 SD card */ | ||||
| uint8_t const SD_CARD_TYPE_SD1 = 1; | |||||
| const uint8_t SD_CARD_TYPE_SD1 = 1; | |||||
| /** Standard capacity V2 SD card */ | /** Standard capacity V2 SD card */ | ||||
| uint8_t const SD_CARD_TYPE_SD2 = 2; | |||||
| const uint8_t SD_CARD_TYPE_SD2 = 2; | |||||
| /** High Capacity SD card */ | /** High Capacity SD card */ | ||||
| uint8_t const SD_CARD_TYPE_SDHC = 3; | |||||
| const uint8_t SD_CARD_TYPE_SDHC = 3; | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| #define SD_SCK_HZ(maxSpeed) SPISettings(maxSpeed, MSBFIRST, SPI_MODE0) | |||||
| #define SD_SCK_MHZ(maxMhz) SPISettings(1000000UL*maxMhz, MSBFIRST, SPI_MODE0) | |||||
| // 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; | |||||
| #define SPI_FULL_SPEED SD_SCK_MHZ(50) | |||||
| /** Set SCK rate to F_CPU/3 for Due */ | /** Set SCK rate to F_CPU/3 for Due */ | ||||
| uint8_t const SPI_DIV3_SPEED = 3; | |||||
| #define SPI_DIV3_SPEED SD_SCK_HZ(F_CPU/3) | |||||
| /** Set SCK rate to F_CPU/4. */ | /** Set SCK rate to F_CPU/4. */ | ||||
| uint8_t const SPI_HALF_SPEED = 4; | |||||
| #define SPI_HALF_SPEED SD_SCK_HZ(F_CPU/4) | |||||
| /** Set SCK rate to F_CPU/6 for Due */ | /** Set SCK rate to F_CPU/6 for Due */ | ||||
| uint8_t const SPI_DIV6_SPEED = 6; | |||||
| #define SPI_DIV6_SPEED SD_SCK_HZ(F_CPU/6) | |||||
| /** Set SCK rate to F_CPU/8. */ | /** Set SCK rate to F_CPU/8. */ | ||||
| uint8_t const SPI_QUARTER_SPEED = 8; | |||||
| #define SPI_QUARTER_SPEED SD_SCK_HZ(F_CPU/8) | |||||
| /** Set SCK rate to F_CPU/16. */ | /** Set SCK rate to F_CPU/16. */ | ||||
| uint8_t const SPI_EIGHTH_SPEED = 16; | |||||
| #define SPI_EIGHTH_SPEED SD_SCK_HZ(F_CPU/16) | |||||
| /** Set SCK rate to F_CPU/32. */ | /** Set SCK rate to F_CPU/32. */ | ||||
| uint8_t const SPI_SIXTEENTH_SPEED = 32; | |||||
| #define SPI_SIXTEENTH_SPEED SD_SCK_HZ(F_CPU/32) | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // SD operation timeouts | // SD operation timeouts | ||||
| /** init timeout ms */ | /** init timeout ms */ | ||||
| unsigned const SD_INIT_TIMEOUT = 2000; | |||||
| const uint16_t SD_INIT_TIMEOUT = 2000; | |||||
| /** erase timeout ms */ | /** erase timeout ms */ | ||||
| unsigned const SD_ERASE_TIMEOUT = 10000; | |||||
| const uint16_t SD_ERASE_TIMEOUT = 10000; | |||||
| /** read timeout ms */ | /** read timeout ms */ | ||||
| unsigned const SD_READ_TIMEOUT = 300; | |||||
| const uint16_t SD_READ_TIMEOUT = 300; | |||||
| /** write time out ms */ | /** write time out ms */ | ||||
| unsigned const SD_WRITE_TIMEOUT = 600; | |||||
| const uint16_t SD_WRITE_TIMEOUT = 600; | |||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // SD card commands | // SD card commands | ||||
| /** GO_IDLE_STATE - init card in spi mode if CS low */ | /** GO_IDLE_STATE - init card in spi mode if CS low */ | ||||
| uint8_t const CMD0 = 0X00; | |||||
| const uint8_t CMD0 = 0X00; | |||||
| /** SWITCH_FUNC - Switch Function Command */ | /** SWITCH_FUNC - Switch Function Command */ | ||||
| uint8_t const CMD6 = 0X06; | |||||
| const uint8_t CMD6 = 0X06; | |||||
| /** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ | /** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ | ||||
| uint8_t const CMD8 = 0X08; | |||||
| const uint8_t CMD8 = 0X08; | |||||
| /** SEND_CSD - read the Card Specific Data (CSD register) */ | /** SEND_CSD - read the Card Specific Data (CSD register) */ | ||||
| uint8_t const CMD9 = 0X09; | |||||
| const uint8_t CMD9 = 0X09; | |||||
| /** SEND_CID - read the card identification information (CID register) */ | /** SEND_CID - read the card identification information (CID register) */ | ||||
| uint8_t const CMD10 = 0X0A; | |||||
| const uint8_t CMD10 = 0X0A; | |||||
| /** STOP_TRANSMISSION - end multiple block read sequence */ | /** STOP_TRANSMISSION - end multiple block read sequence */ | ||||
| uint8_t const CMD12 = 0X0C; | |||||
| const uint8_t CMD12 = 0X0C; | |||||
| /** SEND_STATUS - read the card status register */ | /** SEND_STATUS - read the card status register */ | ||||
| uint8_t const CMD13 = 0X0D; | |||||
| const uint8_t CMD13 = 0X0D; | |||||
| /** READ_SINGLE_BLOCK - read a single data block from the card */ | /** READ_SINGLE_BLOCK - read a single data block from the card */ | ||||
| uint8_t const CMD17 = 0X11; | |||||
| const uint8_t CMD17 = 0X11; | |||||
| /** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ | /** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ | ||||
| uint8_t const CMD18 = 0X12; | |||||
| const uint8_t CMD18 = 0X12; | |||||
| /** WRITE_BLOCK - write a single data block to the card */ | /** WRITE_BLOCK - write a single data block to the card */ | ||||
| uint8_t const CMD24 = 0X18; | |||||
| const uint8_t CMD24 = 0X18; | |||||
| /** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ | /** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ | ||||
| uint8_t const CMD25 = 0X19; | |||||
| const uint8_t CMD25 = 0X19; | |||||
| /** ERASE_WR_BLK_START - sets the address of the first block to be erased */ | /** ERASE_WR_BLK_START - sets the address of the first block to be erased */ | ||||
| uint8_t const CMD32 = 0X20; | |||||
| const uint8_t CMD32 = 0X20; | |||||
| /** ERASE_WR_BLK_END - sets the address of the last block of the continuous | /** ERASE_WR_BLK_END - sets the address of the last block of the continuous | ||||
| range to be erased*/ | range to be erased*/ | ||||
| uint8_t const CMD33 = 0X21; | |||||
| const uint8_t CMD33 = 0X21; | |||||
| /** ERASE - erase all previously selected blocks */ | /** ERASE - erase all previously selected blocks */ | ||||
| uint8_t const CMD38 = 0X26; | |||||
| const uint8_t CMD38 = 0X26; | |||||
| /** APP_CMD - escape for application specific command */ | /** APP_CMD - escape for application specific command */ | ||||
| uint8_t const CMD55 = 0X37; | |||||
| const uint8_t CMD55 = 0X37; | |||||
| /** READ_OCR - read the OCR register of a card */ | /** READ_OCR - read the OCR register of a card */ | ||||
| uint8_t const CMD58 = 0X3A; | |||||
| const uint8_t CMD58 = 0X3A; | |||||
| /** CRC_ON_OFF - enable or disable CRC checking */ | /** CRC_ON_OFF - enable or disable CRC checking */ | ||||
| uint8_t const CMD59 = 0X3B; | |||||
| const uint8_t CMD59 = 0X3B; | |||||
| /** SD_STATUS - Send the SD Status. */ | |||||
| const uint8_t ACMD13 = 0X0D; | |||||
| /** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be | /** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be | ||||
| pre-erased before writing */ | pre-erased before writing */ | ||||
| uint8_t const ACMD23 = 0X17; | |||||
| const uint8_t ACMD23 = 0X17; | |||||
| /** 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; | |||||
| const uint8_t ACMD41 = 0X29; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** status for card in the ready state */ | /** status for card in the ready state */ | ||||
| uint8_t const R1_READY_STATE = 0X00; | |||||
| const uint8_t R1_READY_STATE = 0X00; | |||||
| /** status for card in the idle state */ | /** status for card in the idle state */ | ||||
| uint8_t const R1_IDLE_STATE = 0X01; | |||||
| const uint8_t R1_IDLE_STATE = 0X01; | |||||
| /** status bit for illegal command */ | /** status bit for illegal command */ | ||||
| uint8_t const R1_ILLEGAL_COMMAND = 0X04; | |||||
| const uint8_t R1_ILLEGAL_COMMAND = 0X04; | |||||
| /** start data token for read or write single block*/ | /** start data token for read or write single block*/ | ||||
| uint8_t const DATA_START_BLOCK = 0XFE; | |||||
| const uint8_t DATA_START_BLOCK = 0XFE; | |||||
| /** stop token for write multiple blocks*/ | /** stop token for write multiple blocks*/ | ||||
| uint8_t const STOP_TRAN_TOKEN = 0XFD; | |||||
| const uint8_t STOP_TRAN_TOKEN = 0XFD; | |||||
| /** start data token for write multiple blocks*/ | /** start data token for write multiple blocks*/ | ||||
| uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC; | |||||
| const uint8_t WRITE_MULTIPLE_TOKEN = 0XFC; | |||||
| /** mask for data response tokens after a write block operation */ | /** mask for data response tokens after a write block operation */ | ||||
| uint8_t const DATA_RES_MASK = 0X1F; | |||||
| const uint8_t DATA_RES_MASK = 0X1F; | |||||
| /** write data accepted token */ | /** write data accepted token */ | ||||
| uint8_t const DATA_RES_ACCEPTED = 0X05; | |||||
| const uint8_t DATA_RES_ACCEPTED = 0X05; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class CID | * \class CID |
| /* 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/>. | |||||
| */ | |||||
| /** | |||||
| * \file | |||||
| * \brief SdSpi class for V2 SD/SDHC cards | |||||
| */ | |||||
| #ifndef SdSpi_h | |||||
| #define SdSpi_h | |||||
| #include "SystemInclude.h" | |||||
| #include "SdFatConfig.h" | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * \class SdSpiBase | |||||
| * \brief Virtual SPI class for access to SD and SDHC flash memory cards. | |||||
| */ | |||||
| class SdSpiBase { | |||||
| public: | |||||
| /** Initialize the SPI bus. | |||||
| * | |||||
| * \param[in] chipSelectPin SD card chip select pin. | |||||
| */ | |||||
| virtual void begin(uint8_t chipSelectPin) = 0; | |||||
| /** Set SPI options for access to SD/SDHC cards. | |||||
| * | |||||
| * \param[in] divisor SCK clock divider relative to the system clock. | |||||
| */ | |||||
| virtual void beginTransaction(uint8_t divisor); | |||||
| /** | |||||
| * End SPI transaction. | |||||
| */ | |||||
| virtual void endTransaction(); | |||||
| /** 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; | |||||
| }; | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * \class SdSpi | |||||
| * \brief SPI class for access to SD and SDHC flash memory cards. | |||||
| */ | |||||
| #if SD_SPI_CONFIGURATION >= 3 | |||||
| class SdSpi : public SdSpiBase { | |||||
| #else // SD_SPI_CONFIGURATION >= 3 | |||||
| class SdSpi { | |||||
| #endif // SD_SPI_CONFIGURATION >= 3 | |||||
| public: | |||||
| /** Initialize the SPI bus. | |||||
| * | |||||
| * \param[in] chipSelectPin SD card chip select pin. | |||||
| */ | |||||
| void begin(uint8_t chipSelectPin); | |||||
| /** Set SPI options for access to SD/SDHC cards. | |||||
| * | |||||
| * \param[in] divisor SCK clock divider relative to the system clock. | |||||
| */ | |||||
| void beginTransaction(uint8_t divisor); | |||||
| /** | |||||
| * End SPI transaction | |||||
| */ | |||||
| void endTransaction(); | |||||
| /** Receive a byte. | |||||
| * | |||||
| * \return The byte. | |||||
| */ | |||||
| uint8_t 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); | |||||
| /** Send a byte. | |||||
| * | |||||
| * \param[in] data Byte to send | |||||
| */ | |||||
| void send(uint8_t 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); | |||||
| /** \return true - uses SPI transactions */ | |||||
| #if IMPLEMENT_SPI_INTERFACE_SELECTION | |||||
| void setSpiIf(uint8_t spiIf) { | |||||
| m_spiIf = spiIf; | |||||
| } | |||||
| private: | |||||
| uint8_t m_spiIf; | |||||
| #endif // IMPLEMENT_SPI_INTERFACE_SELECTION | |||||
| }; | |||||
| //------------------------------------------------------------------------------ | |||||
| /** | |||||
| * \class SdSpiLib | |||||
| * \brief Arduino SPI library class for access to SD and SDHC flash | |||||
| * memory cards. | |||||
| */ | |||||
| #if SD_SPI_CONFIGURATION >= 3 | |||||
| class SdSpiLib : public SdSpiBase { | |||||
| #else // SD_SPI_CONFIGURATION >= 3 | |||||
| class SdSpiLib { | |||||
| #endif // SD_SPI_CONFIGURATION >= 3 | |||||
| public: | |||||
| /** Initialize the SPI bus. | |||||
| * | |||||
| * \param[in] chipSelectPin SD card chip select pin. | |||||
| */ | |||||
| void begin(uint8_t chipSelectPin) { | |||||
| pinMode(chipSelectPin, OUTPUT); | |||||
| digitalWrite(chipSelectPin, HIGH); | |||||
| SPI.begin(); | |||||
| } | |||||
| /** Set SPI options for access to SD/SDHC cards. | |||||
| * | |||||
| * \param[in] divisor SCK clock divider relative to the system clock. | |||||
| */ | |||||
| void beginTransaction(uint8_t divisor) { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); | |||||
| #else // ENABLE_SPI_TRANSACTIONS | |||||
| SPI.setBitOrder(MSBFIRST); | |||||
| SPI.setDataMode(SPI_MODE0); | |||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| #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 | |||||
| } | |||||
| /** | |||||
| * End SPI transaction. | |||||
| */ | |||||
| void endTransaction() { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| SPI.endTransaction(); | |||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| } | |||||
| /** 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]); | |||||
| } | |||||
| } | |||||
| }; | |||||
| //------------------------------------------------------------------------------ | |||||
| #if SD_SPI_CONFIGURATION > 1 || defined(DOXYGEN) | |||||
| #ifdef ARDUINO | |||||
| #include "SoftSPI.h" | |||||
| #elif defined(PLATFORM_ID) // Only defined if a Particle device | |||||
| #include "SoftSPIParticle.h" | |||||
| #endif // ARDUINO | |||||
| /** | |||||
| * \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> | |||||
| class SdSpiSoft : public SdSpiBase { | |||||
| public: | |||||
| /** Initialize the SPI bus. | |||||
| * | |||||
| * \param[in] chipSelectPin SD card chip select pin. | |||||
| */ | |||||
| void begin(uint8_t chipSelectPin) { | |||||
| pinMode(chipSelectPin, OUTPUT); | |||||
| digitalWrite(chipSelectPin, HIGH); | |||||
| m_spi.begin(); | |||||
| } | |||||
| /** | |||||
| * Initialize hardware SPI - dummy for soft SPI | |||||
| * \param[in] divisor SCK divisor - ignored. | |||||
| */ | |||||
| void beginTransaction(uint8_t divisor) { | |||||
| (void)divisor; | |||||
| } | |||||
| /** | |||||
| * End SPI transaction - dummy for soft SPI | |||||
| */ | |||||
| void endTransaction() {} | |||||
| /** 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]); | |||||
| } | |||||
| } | |||||
| private: | |||||
| SoftSPI<MisoPin, MosiPin, SckPin, 0> m_spi; | |||||
| }; | |||||
| #endif // SD_SPI_CONFIGURATION > 1 || defined(DOXYGEN) | |||||
| //------------------------------------------------------------------------------ | |||||
| #if SD_SPI_CONFIGURATION == 2 | |||||
| /** Default is software SPI. */ | |||||
| typedef SdSpiSoft<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN> | |||||
| SpiDefault_t; | |||||
| #elif SD_SPI_CONFIGURATION == 1 || !SD_HAS_CUSTOM_SPI | |||||
| /** Default is Arduino library SPI. */ | |||||
| typedef SdSpiLib SpiDefault_t; | |||||
| #else // SpiDefault_t | |||||
| /** Default is custom fast SPI. */ | |||||
| typedef SdSpi SpiDefault_t; | |||||
| #endif // SpiDefault_t | |||||
| //------------------------------------------------------------------------------ | |||||
| // Use of in-line for AVR to save flash. | |||||
| #ifdef __AVR__ | |||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpi::begin(uint8_t chipSelectPin) { | |||||
| #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__ | |||||
| pinMode(chipSelectPin, OUTPUT); | |||||
| digitalWrite(chipSelectPin, HIGH); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpi::beginTransaction(uint8_t divisor) { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| SPI.beginTransaction(SPISettings()); | |||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| 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 void SdSpi::endTransaction() { | |||||
| #if ENABLE_SPI_TRANSACTIONS | |||||
| SPI.endTransaction(); | |||||
| #endif // ENABLE_SPI_TRANSACTIONS | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline uint8_t SdSpi::receive() { | |||||
| SPDR = 0XFF; | |||||
| while (!(SPSR & (1 << SPIF))) {} | |||||
| return SPDR; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline 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; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpi::send(uint8_t data) { | |||||
| SPDR = data; | |||||
| while (!(SPSR & (1 << SPIF))) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline 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 // __AVR__ | |||||
| #endif // SdSpi_h |
| /* Arduino SdSpiCard Library | |||||
| * Copyright (C) 2016 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 SdSpiBaseDriver_h | |||||
| #define SdSpiBaseDriver_h | |||||
| /** | |||||
| * \class SdSpiBaseDriver | |||||
| * \brief SPI base driver. | |||||
| */ | |||||
| class SdSpiBaseDriver { | |||||
| public: | |||||
| /** Set SPI options for access to SD/SDHC cards. | |||||
| * | |||||
| */ | |||||
| virtual void activate() = 0; | |||||
| /** Initialize the SPI bus. | |||||
| * | |||||
| * \param[in] chipSelectPin SD card chip select pin. | |||||
| */ | |||||
| virtual void begin(uint8_t chipSelectPin) = 0; | |||||
| /** | |||||
| * End SPI transaction. | |||||
| */ | |||||
| virtual void deactivate() = 0; | |||||
| /** 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; | |||||
| /** Set CS low. */ | |||||
| virtual void select() = 0; | |||||
| /** Save SPI settings. | |||||
| * \param[in] spiSettings SPI speed, mode, and bit order. | |||||
| */ | |||||
| virtual void setSpiSettings(SPISettings spiSettings) = 0; | |||||
| /** Set CS high. */ | |||||
| virtual void unselect() = 0; | |||||
| }; | |||||
| #endif // SdSpiBaseDriver_h |
| * <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
| */ | */ | ||||
| #include "SdSpiCard.h" | #include "SdSpiCard.h" | ||||
| #include "SdSpi.h" | |||||
| // 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); | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| #if USE_SD_CRC == 1 | #if USE_SD_CRC == 1 | ||||
| // slower CRC-CCITT | |||||
| // Shift based CRC-CCITT | |||||
| // uses the x^16,x^12,x^5,x^1 polynomial. | // uses the x^16,x^12,x^5,x^1 polynomial. | ||||
| static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { | static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { | ||||
| uint16_t crc = 0; | uint16_t crc = 0; | ||||
| } | } | ||||
| #elif USE_SD_CRC > 1 // CRC_CCITT | #elif USE_SD_CRC > 1 // CRC_CCITT | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // faster CRC-CCITT | |||||
| // Table based CRC-CCITT | |||||
| // uses the x^16,x^12,x^5,x^1 polynomial. | // uses the x^16,x^12,x^5,x^1 polynomial. | ||||
| #ifdef __AVR__ | #ifdef __AVR__ | ||||
| static const uint16_t crctab[] PROGMEM = { | static const uint16_t crctab[] PROGMEM = { | ||||
| //============================================================================== | //============================================================================== | ||||
| // SdSpiCard member functions | // SdSpiCard member functions | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||||
| bool SdSpiCard::begin(SdSpiDriver* spiDriver) { | |||||
| m_spiActive = false; | |||||
| m_errorCode = m_type = 0; | m_errorCode = m_type = 0; | ||||
| m_spi = spi; | |||||
| m_chipSelectPin = chipSelectPin; | |||||
| // 16-bit init start time allows over a minute | |||||
| unsigned t0 = (unsigned)millis(); | |||||
| m_spiDriver = spiDriver; | |||||
| uint16_t t0 = curTimeMS(); | |||||
| uint32_t arg; | uint32_t arg; | ||||
| // initialize SPI bus and chip select pin. | |||||
| spiBegin(m_chipSelectPin); | |||||
| // set SCK rate for initialization commands. | |||||
| m_sckDivisor = SPI_SCK_INIT_DIVISOR; | |||||
| spiBeginTransaction(m_sckDivisor); | |||||
| spiStart(); | |||||
| // must supply min of 74 clock cycles with CS high. | // must supply min of 74 clock cycles with CS high. | ||||
| spiUnselect(); | |||||
| for (uint8_t i = 0; i < 10; i++) { | for (uint8_t i = 0; i < 10; i++) { | ||||
| spiSend(0XFF); | spiSend(0XFF); | ||||
| } | } | ||||
| spiEndTransaction(); | |||||
| spiSelect(); | |||||
| // 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) { | ||||
| if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||||
| if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||||
| error(SD_CARD_ERROR_CMD0); | error(SD_CARD_ERROR_CMD0); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| type(SD_CARD_TYPE_SD2); | type(SD_CARD_TYPE_SD2); | ||||
| break; | break; | ||||
| } | } | ||||
| if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||||
| if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||||
| error(SD_CARD_ERROR_CMD8); | error(SD_CARD_ERROR_CMD8); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { | while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { | ||||
| // check for timeout | // check for timeout | ||||
| if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||||
| if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||||
| error(SD_CARD_ERROR_ACMD41); | error(SD_CARD_ERROR_ACMD41); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| spiReceive(); | spiReceive(); | ||||
| } | } | ||||
| } | } | ||||
| chipSelectHigh(); | |||||
| m_sckDivisor = sckDivisor; | |||||
| spiStop(); | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| // send command and return error code. Return zero for OK | // send command and return error code. Return zero for OK | ||||
| uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { | uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { | ||||
| // select card | // select card | ||||
| if (!m_selected) { | |||||
| chipSelectLow(); | |||||
| if (!m_spiActive) { | |||||
| spiStart(); | |||||
| } | } | ||||
| // wait if busy | // wait if busy | ||||
| waitNotBusy(SD_WRITE_TIMEOUT); | waitNotBusy(SD_WRITE_TIMEOUT); | ||||
| uint8_t *pa = reinterpret_cast<uint8_t *>(&arg); | |||||
| #if USE_SD_CRC | #if USE_SD_CRC | ||||
| // form message | // form message | ||||
| uint8_t buf[6]; | |||||
| buf[0] = (uint8_t)0x40U | cmd; | |||||
| buf[1] = (uint8_t)(arg >> 24U); | |||||
| buf[2] = (uint8_t)(arg >> 16U); | |||||
| buf[3] = (uint8_t)(arg >> 8U); | |||||
| buf[4] = (uint8_t)arg; | |||||
| uint8_t d[6] = {cmd , pa[3], pa[2], pa[1], pa[0]}; | |||||
| d[0] |= 0X40; | |||||
| // add crc | |||||
| d[5] = CRC7(d, 5); | |||||
| // add CRC | |||||
| buf[5] = CRC7(buf, 5); | |||||
| // send message | // send message | ||||
| for (uint8_t k = 0; k < 6; k++) { | |||||
| spiSend(d[k]); | |||||
| } | |||||
| spiSend(buf, 6); | |||||
| #else // USE_SD_CRC | #else // USE_SD_CRC | ||||
| // send command | // send command | ||||
| spiSend(cmd | 0x40); | spiSend(cmd | 0x40); | ||||
| // send argument | // send argument | ||||
| uint8_t *pa = reinterpret_cast<uint8_t *>(&arg); | |||||
| for (int8_t i = 3; i >= 0; i--) { | for (int8_t i = 3; i >= 0; i--) { | ||||
| spiSend(pa[i]); | spiSend(pa[i]); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| void SdSpiCard::chipSelectHigh() { | |||||
| if (!m_selected) { | |||||
| SD_CS_DBG("chipSelectHigh error"); | |||||
| return; | |||||
| } | |||||
| digitalWrite(m_chipSelectPin, HIGH); | |||||
| // insure MISO goes high impedance | |||||
| spiSend(0XFF); | |||||
| spiEndTransaction(); | |||||
| m_selected = false; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpiCard::chipSelectLow() { | |||||
| #if WDT_YIELD_TIME_MICROS | |||||
| static uint32_t last; | |||||
| if ((micros() - last) > WDT_YIELD_TIME_MICROS) { | |||||
| SysCall::yield(); | |||||
| last = micros(); | |||||
| } | |||||
| #endif // WDT_YIELD_TIME_MICROS | |||||
| if (m_selected) { | |||||
| SD_CS_DBG("chipSelectLow error"); | |||||
| return; | |||||
| } | |||||
| spiBeginTransaction(m_sckDivisor); | |||||
| digitalWrite(m_chipSelectPin, LOW); | |||||
| m_selected = true; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | ||||
| csd_t csd; | csd_t csd; | ||||
| if (!readCSD(&csd)) { | if (!readCSD(&csd)) { | ||||
| error(SD_CARD_ERROR_ERASE_TIMEOUT); | error(SD_CARD_ERROR_ERASE_TIMEOUT); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdSpiCard::isBusy() { | bool SdSpiCard::isBusy() { | ||||
| bool rtn = true; | bool rtn = true; | ||||
| bool selected = m_selected; | |||||
| chipSelectLow(); | |||||
| bool spiActive = m_spiActive; | |||||
| if (!spiActive) { | |||||
| spiStart(); | |||||
| } | |||||
| for (uint8_t i = 0; i < 8; i++) { | for (uint8_t i = 0; i < 8; i++) { | ||||
| if (0XFF == spiReceive()) { | if (0XFF == spiReceive()) { | ||||
| rtn = false; | rtn = false; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (!selected) { | |||||
| chipSelectHigh(); | |||||
| if (!spiActive) { | |||||
| spiStop(); | |||||
| } | } | ||||
| return rtn; | return rtn; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdSpiCard::isTimedOut(uint16_t startMS, uint16_t timeoutMS) { | |||||
| #if WDT_YIELD_TIME_MICROS | |||||
| static uint32_t last; | |||||
| if ((micros() - last) > WDT_YIELD_TIME_MICROS) { | |||||
| SysCall::yield(); | |||||
| last = micros(); | |||||
| } | |||||
| #endif // WDT_YIELD_TIME_MICROS | |||||
| return (curTimeMS() - startMS) > timeoutMS; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { | bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { | ||||
| SD_TRACE("RB", blockNumber); | SD_TRACE("RB", blockNumber); | ||||
| // use address if not SDHC card | // use address if not SDHC card | ||||
| if (!readData(dst, 512)) { | if (!readData(dst, 512)) { | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdSpiCard::readData(uint8_t *dst) { | bool SdSpiCard::readData(uint8_t *dst) { | ||||
| bool selected = m_selected; | |||||
| chipSelectLow(); | |||||
| if (!readData(dst, 512)) { | |||||
| return false; | |||||
| } | |||||
| if (!selected) { | |||||
| chipSelectHigh(); | |||||
| } | |||||
| return true; | |||||
| return readData(dst, 512); | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdSpiCard::readData(uint8_t* dst, size_t count) { | bool SdSpiCard::readData(uint8_t* dst, size_t count) { | ||||
| uint16_t crc; | uint16_t crc; | ||||
| #endif // USE_SD_CRC | #endif // USE_SD_CRC | ||||
| // wait for start block token | // wait for start block token | ||||
| unsigned t0 = millis(); | |||||
| uint16_t t0 = curTimeMS(); | |||||
| while ((m_status = spiReceive()) == 0XFF) { | while ((m_status = spiReceive()) == 0XFF) { | ||||
| if (((unsigned)millis() - t0) > SD_READ_TIMEOUT) { | |||||
| if (isTimedOut(t0, SD_READ_TIMEOUT)) { | |||||
| error(SD_CARD_ERROR_READ_TIMEOUT); | error(SD_CARD_ERROR_READ_TIMEOUT); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| p[3 - i] = spiReceive(); | p[3 - i] = spiReceive(); | ||||
| } | } | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| error(SD_CARD_ERROR_READ_REG); | error(SD_CARD_ERROR_READ_REG); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| return readData(dst, 16); | |||||
| if (!readData(dst, 16)) { | |||||
| goto fail; | |||||
| } | |||||
| spiStop(); | |||||
| return true; | |||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| error(SD_CARD_ERROR_CMD18); | error(SD_CARD_ERROR_CMD18); | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| // spiStop(); | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | |||||
| bool SdSpiCard::readStop() { | |||||
| if (cardCommand(CMD12, 0)) { | |||||
| error(SD_CARD_ERROR_CMD12); | |||||
| //----------------------------------------------------------------------------- | |||||
| bool SdSpiCard::readStatus(uint8_t* status) { | |||||
| // retrun is R2 so read extra status byte. | |||||
| if (cardAcmd(ACMD13, 0) || spiReceive()) { | |||||
| error(SD_CARD_ERROR_ACMD13); | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| chipSelectHigh(); | |||||
| if (!readData(status, 64)) { | |||||
| goto fail; | |||||
| } | |||||
| spiStop(); | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | |||||
| bool SdSpiCard::sendCmd6(uint32_t arg, uint8_t* status) { | |||||
| if (cardCommand(CMD6, arg)) { | |||||
| error(SD_CARD_ERROR_CMD6); | |||||
| goto fail; | |||||
| //----------------------------------------------------------------------------- | |||||
| void SdSpiCard::spiStart() { | |||||
| if (!m_spiActive) { | |||||
| spiActivate(); | |||||
| spiSelect(); | |||||
| m_spiActive = true; | |||||
| } | } | ||||
| if (!readData(status, 64)) { | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| void SdSpiCard::spiStop() { | |||||
| if (m_spiActive) { | |||||
| spiUnselect(); | |||||
| spiSend(0XFF); | |||||
| spiDeactivate(); | |||||
| m_spiActive = false; | |||||
| } | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool SdSpiCard::readStop() { | |||||
| if (cardCommand(CMD12, 0)) { | |||||
| error(SD_CARD_ERROR_CMD12); | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdSpiCard::setHighSpeedMode(uint8_t divisor) { | |||||
| uint8_t status[64]; | |||||
| uint8_t saveDivisor = m_sckDivisor; | |||||
| setSckDivisor(128); | |||||
| if (!sendCmd6(0X00FFFFFF, status) || (2 & status[13]) == 0 || | |||||
| !sendCmd6(0X80FFFFF1, status) || (status[16] & 0XF) != 1) { | |||||
| setSckDivisor(saveDivisor); | |||||
| return false; | |||||
| } | |||||
| setSckDivisor(divisor); | |||||
| return true; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| // wait for card to go not busy | // wait for card to go not busy | ||||
| bool SdSpiCard::waitNotBusy(uint16_t timeoutMillis) { | |||||
| unsigned t0 = millis(); | |||||
| bool SdSpiCard::waitNotBusy(uint16_t timeoutMS) { | |||||
| uint16_t t0 = curTimeMS(); | |||||
| #if WDT_YIELD_TIME_MICROS | |||||
| // Call isTimedOut first to insure yield is called. | |||||
| while (!isTimedOut(t0, timeoutMS)) { | |||||
| if (spiReceive() == 0XFF) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| #else // WDT_YIELD_TIME_MICROS | |||||
| // Check not busy first since yield is not called in isTimedOut. | |||||
| while (spiReceive() != 0XFF) { | while (spiReceive() != 0XFF) { | ||||
| if (((unsigned)millis() - t0) >= timeoutMillis) { | |||||
| goto fail; | |||||
| if (isTimedOut(t0, timeoutMS)) { | |||||
| return false; | |||||
| } | } | ||||
| } | } | ||||
| return true; | return true; | ||||
| fail: | |||||
| return false; | |||||
| #endif // WDT_YIELD_TIME_MICROS | |||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | ||||
| } | } | ||||
| #endif // CHECK_PROGRAMMING | #endif // CHECK_PROGRAMMING | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { | bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { | ||||
| if (!writeStart(block, count)) { | |||||
| if (!writeStart(block)) { | |||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| for (size_t b = 0; b < count; b++, src += 512) { | for (size_t b = 0; b < count; b++, src += 512) { | ||||
| return writeStop(); | return writeStop(); | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdSpiCard::writeData(const uint8_t* src) { | bool SdSpiCard::writeData(const uint8_t* src) { | ||||
| bool selected = m_selected; | |||||
| chipSelectLow(); | |||||
| // wait for previous write to finish | // wait for previous write to finish | ||||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | ||||
| error(SD_CARD_ERROR_WRITE_TIMEOUT); | error(SD_CARD_ERROR_WRITE_TIMEOUT); | ||||
| if (!writeData(WRITE_MULTIPLE_TOKEN, src)) { | if (!writeData(WRITE_MULTIPLE_TOKEN, src)) { | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| if (!selected) { | |||||
| chipSelectHigh(); | |||||
| } | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| bool SdSpiCard::writeStart(uint32_t blockNumber) { | |||||
| // use address if not SDHC card | |||||
| if (type() != SD_CARD_TYPE_SDHC) { | |||||
| blockNumber <<= 9; | |||||
| } | |||||
| if (cardCommand(CMD25, blockNumber)) { | |||||
| error(SD_CARD_ERROR_CMD25); | |||||
| goto fail; | |||||
| } | |||||
| return true; | |||||
| fail: | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
| bool SdSpiCard::writeStop() { | bool SdSpiCard::writeStop() { | ||||
| chipSelectLow(); | |||||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | ||||
| goto fail; | goto fail; | ||||
| } | } | ||||
| spiSend(STOP_TRAN_TOKEN); | spiSend(STOP_TRAN_TOKEN); | ||||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||||
| goto fail; | |||||
| } | |||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return true; | return true; | ||||
| fail: | fail: | ||||
| error(SD_CARD_ERROR_STOP_TRAN); | error(SD_CARD_ERROR_STOP_TRAN); | ||||
| chipSelectHigh(); | |||||
| spiStop(); | |||||
| return false; | return false; | ||||
| } | } |
| * \file | * \file | ||||
| * \brief SdSpiCard class for V2 SD/SDHC cards | * \brief SdSpiCard class for V2 SD/SDHC cards | ||||
| */ | */ | ||||
| #include "SystemInclude.h" | |||||
| #include "SdFatConfig.h" | |||||
| #include <stddef.h> | |||||
| #include "SysCall.h" | |||||
| #include "SdInfo.h" | #include "SdInfo.h" | ||||
| #include "SdSpi.h" | |||||
| #include "SdSpiDriver.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| * \class SdSpiCard | * \class SdSpiCard | ||||
| */ | */ | ||||
| class SdSpiCard { | class SdSpiCard { | ||||
| public: | public: | ||||
| /** typedef for SPI class. */ | |||||
| #if SD_SPI_CONFIGURATION < 3 | |||||
| typedef SpiDefault_t m_spi_t; | |||||
| #else // SD_SPI_CONFIGURATION < 3 | |||||
| typedef SdSpiBase m_spi_t; | |||||
| #endif // SD_SPI_CONFIGURATION < 3 | |||||
| /** Construct an instance of SdSpiCard. */ | /** Construct an instance of SdSpiCard. */ | ||||
| SdSpiCard() : m_selected(false), | |||||
| m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} | |||||
| SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} | |||||
| /** Initialize the SD card. | /** Initialize the SD card. | ||||
| * \param[in] spi SPI object. | |||||
| * \param[in] chipSelectPin SD chip select pin. | |||||
| * \param[in] sckDivisor SPI clock divisor. | |||||
| * \param[in] spiDriver SPI driver for card. | |||||
| * \return true for success else false. | * \return true for success else false. | ||||
| */ | */ | ||||
| bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS, | |||||
| uint8_t sckDivisor = SPI_FULL_SPEED); | |||||
| bool begin(SdSpiDriver* spiDriver); | |||||
| /** | /** | ||||
| * Determine the size of an SD flash memory card. | * Determine the size of an SD flash memory card. | ||||
| * | * | ||||
| * or zero if an error occurs. | * or zero if an error occurs. | ||||
| */ | */ | ||||
| uint32_t cardSize(); | uint32_t cardSize(); | ||||
| /** Set the SD chip select pin high, send a dummy byte, and call SPI endTransaction. | |||||
| * | |||||
| * This function should only be called by programs doing raw I/O to the SD. | |||||
| */ | |||||
| void chipSelectHigh(); | |||||
| /** Set the SD chip select pin low and call SPI beginTransaction. | |||||
| * | |||||
| * This function should only be called by programs doing raw I/O to the SD. | |||||
| */ | |||||
| void chipSelectLow(); | |||||
| /** Erase a range of blocks. | /** Erase a range of blocks. | ||||
| * | * | ||||
| * \param[in] firstBlock The address of the first block in the range. | * \param[in] firstBlock The address of the first block in the range. | ||||
| * the value false is returned for failure. | * the value false is returned for failure. | ||||
| */ | */ | ||||
| bool readStart(uint32_t blockNumber); | bool readStart(uint32_t blockNumber); | ||||
| /** Return the 64 byte card status | |||||
| * \param[out] status location for 64 status bytes. | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool readStatus(uint8_t* status); | |||||
| /** End a read multiple blocks sequence. | /** End a read multiple blocks sequence. | ||||
| * | * | ||||
| * \return The value true is returned for success and | * \return The value true is returned for success and | ||||
| * the value false is returned for failure. | * the value false is returned for failure. | ||||
| */ | */ | ||||
| bool readStop(); | bool readStop(); | ||||
| /** Return SCK divisor. | |||||
| * | |||||
| * \return Requested SCK divisor. | |||||
| */ | |||||
| uint8_t sckDivisor() { | |||||
| return m_sckDivisor; | |||||
| } | |||||
| /** \return the SD chip select status, true if slected else false. */ | |||||
| bool selected() {return m_selected;} | |||||
| /** Send CMD6 - Switch Function Command | |||||
| * | |||||
| * param[in] arg 32-bit argument to CMD6. | |||||
| * param[out] status - 64 byte status returned by CMD6. | |||||
| * \return true if the command was accepted else false. | |||||
| */ | |||||
| bool sendCmd6(uint32_t arg, uint8_t* status); | |||||
| /** Set High Speed Bus Mode. | |||||
| * | |||||
| * param[in] divisor new value for SPI SCK divisor. | |||||
| * \return true if successful else false. | |||||
| */ | |||||
| bool setHighSpeedMode(uint8_t divisor); | |||||
| /** Set SCK divisor. | |||||
| * param[in] sckDivisor value for divisor. | |||||
| */ | |||||
| void setSckDivisor(uint8_t sckDivisor) { | |||||
| m_sckDivisor = sckDivisor; | |||||
| } | |||||
| /** Return the card type: SD V1, SD V2 or SDHC | /** Return the card type: SD V1, SD V2 or SDHC | ||||
| * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. | * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. | ||||
| */ | */ | ||||
| */ | */ | ||||
| bool writeData(const uint8_t* src); | bool writeData(const uint8_t* src); | ||||
| /** Start a write multiple blocks sequence. | /** Start a write multiple blocks sequence. | ||||
| * | |||||
| * \param[in] blockNumber Address of first block in sequence. | |||||
| * | |||||
| * \note This function is used with writeData() and writeStop() | |||||
| * for optimized multiple block writes. | |||||
| * | |||||
| * \return The value true is returned for success and | |||||
| * the value false is returned for failure. | |||||
| */ | |||||
| bool writeStart(uint32_t blockNumber); | |||||
| /** Start a write multiple blocks sequence with pre-erase. | |||||
| * | * | ||||
| * \param[in] blockNumber Address of first block in sequence. | * \param[in] blockNumber Address of first block in sequence. | ||||
| * \param[in] eraseCount The number of blocks to be pre-erased. | * \param[in] eraseCount The number of blocks to be pre-erased. | ||||
| * the value false is returned for failure. | * the value false is returned for failure. | ||||
| */ | */ | ||||
| bool writeStop(); | bool writeStop(); | ||||
| /** Set CS low and activate the card. */ | |||||
| void spiStart(); | |||||
| /** Set CS high and deactivate the card. */ | |||||
| void spiStop(); | |||||
| private: | private: | ||||
| // private functions | // private functions | ||||
| return cardCommand(cmd, arg); | return cardCommand(cmd, arg); | ||||
| } | } | ||||
| uint8_t cardCommand(uint8_t cmd, uint32_t arg); | uint8_t cardCommand(uint8_t cmd, uint32_t arg); | ||||
| bool isTimedOut(uint16_t startMS, uint16_t timeoutMS); | |||||
| bool readData(uint8_t* dst, size_t count); | bool readData(uint8_t* dst, size_t count); | ||||
| bool readRegister(uint8_t cmd, void* buf); | bool readRegister(uint8_t cmd, void* buf); | ||||
| void type(uint8_t value) { | void type(uint8_t value) { | ||||
| m_type = value; | m_type = value; | ||||
| } | } | ||||
| bool waitNotBusy(uint16_t timeoutMillis); | |||||
| bool waitNotBusy(uint16_t timeoutMS); | |||||
| bool writeData(uint8_t token, const uint8_t* src); | bool writeData(uint8_t token, const uint8_t* src); | ||||
| void spiBegin(uint8_t chipSelectPin) { | |||||
| m_spi->begin(chipSelectPin); | |||||
| } | |||||
| void spiBeginTransaction(uint8_t spiDivisor) { | |||||
| m_spi->beginTransaction(spiDivisor); | |||||
| //--------------------------------------------------------------------------- | |||||
| // functions defined in SdSpiDriver.h | |||||
| void spiActivate() { | |||||
| m_spiDriver->activate(); | |||||
| } | } | ||||
| void spiEndTransaction() { | |||||
| m_spi->endTransaction(); | |||||
| void spiDeactivate() { | |||||
| m_spiDriver->deactivate(); | |||||
| } | } | ||||
| uint8_t spiReceive() { | uint8_t spiReceive() { | ||||
| return m_spi->receive(); | |||||
| return m_spiDriver->receive(); | |||||
| } | } | ||||
| uint8_t spiReceive(uint8_t* buf, size_t n) { | uint8_t spiReceive(uint8_t* buf, size_t n) { | ||||
| return m_spi->receive(buf, n); | |||||
| return m_spiDriver->receive(buf, n); | |||||
| } | } | ||||
| void spiSend(uint8_t data) { | void spiSend(uint8_t data) { | ||||
| m_spi->send(data); | |||||
| m_spiDriver->send(data); | |||||
| } | } | ||||
| void spiSend(const uint8_t* buf, size_t n) { | void spiSend(const uint8_t* buf, size_t n) { | ||||
| m_spi->send(buf, n); | |||||
| m_spiDriver->send(buf, n); | |||||
| } | |||||
| void spiSelect() { | |||||
| m_spiDriver->select(); | |||||
| } | |||||
| void spiUnselect() { | |||||
| m_spiDriver->unselect(); | |||||
| } | } | ||||
| m_spi_t* m_spi; | |||||
| bool m_selected; | |||||
| uint8_t m_chipSelectPin; | |||||
| uint8_t m_errorCode; | uint8_t m_errorCode; | ||||
| uint8_t m_sckDivisor; | |||||
| SdSpiDriver *m_spiDriver; | |||||
| bool m_spiActive; | |||||
| uint8_t m_status; | uint8_t m_status; | ||||
| uint8_t m_type; | 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) { | |||||
| (void)spi; | |||||
| (void)chipSelectPin; | |||||
| (void)sckDivisor; | |||||
| return false; | |||||
| } | |||||
| SpiDefault_t m_spi; | |||||
| }; | |||||
| #endif // SpiCard_h | #endif // SpiCard_h |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2016 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 "SdSpi.h" | |||||
| #if defined(PLATFORM_ID) | |||||
| static uint32_t bugDelay = 0; // fix for SPI DMA bug. | |||||
| static volatile bool SPI_DMA_TransferCompleted = false; | |||||
| static SPIClass* const spiPtr[] = { | |||||
| &SPI | |||||
| #if Wiring_SPI1 | |||||
| , &SPI1 | |||||
| #if Wiring_SPI2 | |||||
| , &SPI2 | |||||
| #endif // Wiring_SPI2 | |||||
| #endif // Wiring_SPI1 | |||||
| }; | |||||
| #if SPI_INTERFACE_COUNT == 1 | |||||
| const uint8_t m_spiIf = 0; | |||||
| #endif | |||||
| //----------------------------------------------------------------------------- | |||||
| void SD_SPI_DMA_TransferComplete_Callback(void) { | |||||
| SPI_DMA_TransferCompleted = true; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpi::begin(uint8_t chipSelectPin) { | |||||
| spiPtr[m_spiIf]->begin(chipSelectPin); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| void SdSpi::beginTransaction(uint8_t divisor) { | |||||
| spiPtr[m_spiIf]->setBitOrder(MSBFIRST); | |||||
| spiPtr[m_spiIf]->setDataMode(SPI_MODE0); | |||||
| #ifndef SPI_CLOCK_DIV128 | |||||
| spiPtr[m_spiIf]->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; | |||||
| } | |||||
| spiPtr[m_spiIf]->setClockDivider(v); | |||||
| #endif // SPI_CLOCK_DIV128 | |||||
| // delay for SPI transfer done callback too soon bug. | |||||
| bugDelay = 24*divisor*(1 + m_spiIf)/60; | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| void SdSpi::endTransaction() { | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| /** SPI receive a byte */ | |||||
| uint8_t SdSpi::receive() { | |||||
| return spiPtr[m_spiIf]->transfer(0xFF); | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
| SPI_DMA_TransferCompleted = false; | |||||
| spiPtr[m_spiIf]->transfer(0, buf, n, SD_SPI_DMA_TransferComplete_Callback); | |||||
| while (!SPI_DMA_TransferCompleted) {} | |||||
| if (bugDelay) { | |||||
| delayMicroseconds(bugDelay); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| /** SPI send a byte */ | |||||
| void SdSpi::send(uint8_t b) { | |||||
| spiPtr[m_spiIf]->transfer(b); | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
| SPI_DMA_TransferCompleted = false; | |||||
| spiPtr[m_spiIf]->transfer(const_cast<uint8_t*>(buf), 0, n, | |||||
| SD_SPI_DMA_TransferComplete_Callback); | |||||
| while (!SPI_DMA_TransferCompleted) {} | |||||
| if (bugDelay) { | |||||
| delayMicroseconds(bugDelay); | |||||
| } | |||||
| } | |||||
| #endif // defined(PLATFORM_ID) |
| /* SdFat Library | |||||
| * Copyright (C) 2016 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 SpiDriver classes | |||||
| */ | |||||
| #ifndef SdSpiDriver_h | |||||
| #define SdSpiDriver_h | |||||
| #include <Arduino.h> | |||||
| #include "SPI.h" | |||||
| #include "SdSpiCard/SdSpiBaseDriver.h" | |||||
| #include "SdFatConfig.h" | |||||
| //----------------------------------------------------------------------------- | |||||
| /** | |||||
| * \class SdSpiLibDriver | |||||
| * \brief SdSpiLibDriver - use standard SPI library. | |||||
| */ | |||||
| #if ENABLE_SOFTWARE_SPI_CLASS | |||||
| class SdSpiLibDriver : public SdSpiBaseDriver { | |||||
| #else // ENABLE_SOFTWARE_SPI_CLASS | |||||
| class SdSpiLibDriver { | |||||
| #endif // ENABLE_SOFTWARE_SPI_CLASS | |||||
| public: | |||||
| /** Activate SPI hardware. */ | |||||
| void activate() { | |||||
| SPI.beginTransaction(m_spiSettings); | |||||
| } | |||||
| /** Deactivate SPI hardware. */ | |||||
| void deactivate() { | |||||
| SPI.endTransaction(); | |||||
| } | |||||
| /** Initialize the SPI bus. | |||||
| * | |||||
| * \param[in] csPin SD card chip select pin. | |||||
| */ | |||||
| void begin(uint8_t csPin) { | |||||
| m_csPin = csPin; | |||||
| digitalWrite(csPin, HIGH); | |||||
| pinMode(csPin, OUTPUT); | |||||
| SPI.begin(); | |||||
| } | |||||
| /** 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] data Byte to send | |||||
| */ | |||||
| void send(uint8_t data) { | |||||
| SPI.transfer(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++) { | |||||
| SPI.transfer(buf[i]); | |||||
| } | |||||
| } | |||||
| /** Set CS low. */ | |||||
| void select() { | |||||
| digitalWrite(m_csPin, LOW); | |||||
| } | |||||
| /** Save SPISettings. | |||||
| * | |||||
| * \param[in] spiSettings SPI speed, mode, and byte order. | |||||
| */ | |||||
| void setSpiSettings(SPISettings spiSettings) { | |||||
| m_spiSettings = spiSettings; | |||||
| } | |||||
| /** Set CS high. */ | |||||
| void unselect() { | |||||
| digitalWrite(m_csPin, HIGH); | |||||
| } | |||||
| private: | |||||
| SPISettings m_spiSettings; | |||||
| uint8_t m_csPin; | |||||
| }; | |||||
| //----------------------------------------------------------------------------- | |||||
| /** | |||||
| * \class SdSpiAltDriver | |||||
| * \brief Optimized SPI class for access to SD and SDHC flash memory cards. | |||||
| */ | |||||
| #if ENABLE_SOFTWARE_SPI_CLASS | |||||
| class SdSpiAltDriver : public SdSpiBaseDriver { | |||||
| #else // ENABLE_SOFTWARE_SPI_CLASS | |||||
| class SdSpiAltDriver { | |||||
| #endif // ENABLE_SOFTWARE_SPI_CLASS | |||||
| public: | |||||
| /** Activate SPI hardware. */ | |||||
| void activate(); | |||||
| /** Deactivate SPI hardware. */ | |||||
| void deactivate(); | |||||
| /** Initialize the SPI bus. | |||||
| * | |||||
| * \param[in] csPin SD card chip select pin. | |||||
| */ | |||||
| void begin(uint8_t csPin); | |||||
| /** Receive a byte. | |||||
| * | |||||
| * \return The byte. | |||||
| */ | |||||
| uint8_t 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); | |||||
| /** Send a byte. | |||||
| * | |||||
| * \param[in] data Byte to send | |||||
| */ | |||||
| void send(uint8_t 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); | |||||
| /** Set CS low. */ | |||||
| void select() { | |||||
| digitalWrite(m_csPin, LOW); | |||||
| } | |||||
| /** Save SPISettings. | |||||
| * | |||||
| * \param[in] spiSettings SPI speed, mode, and byte order. | |||||
| */ | |||||
| void setSpiSettings(SPISettings spiSettings) { | |||||
| m_spiSettings = spiSettings; | |||||
| } | |||||
| /** Set CS high. */ | |||||
| void unselect() { | |||||
| digitalWrite(m_csPin, HIGH); | |||||
| } | |||||
| #if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) | |||||
| /** Set SPI port number. | |||||
| * \param[in] portNumber Hardware SPI port number. | |||||
| */ | |||||
| void setPort(uint8_t portNumber); | |||||
| private: | |||||
| uint8_t m_spiPort; | |||||
| #else // IMPLEMENT_SPI_PORT_SELECTION | |||||
| private: | |||||
| #endif // IMPLEMENT_SPI_PORT_SELECTION | |||||
| SPISettings m_spiSettings; | |||||
| uint8_t m_csPin; | |||||
| }; | |||||
| //------------------------------------------------------------------------------ | |||||
| #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
| #ifdef ARDUINO | |||||
| #include "AltSpiDrivers/SoftSPI.h" | |||||
| #elif defined(PLATFORM_ID) // Only defined if a Particle device | |||||
| #include "SoftSPIParticle.h" | |||||
| #endif // ARDUINO | |||||
| /** | |||||
| * \class SdSpiSoftDriver | |||||
| * \brief Software SPI class for access to SD and SDHC flash memory cards. | |||||
| */ | |||||
| template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||||
| class SdSpiSoftDriver : public SdSpiBaseDriver { | |||||
| public: | |||||
| /** Dummy activate SPI hardware for software SPI */ | |||||
| void activate() {} | |||||
| /** Dummy deactivate SPI hardware for software SPI */ | |||||
| void deactivate() {} | |||||
| /** Initialize the SPI bus. | |||||
| * | |||||
| * \param[in] csPin SD card chip select pin. | |||||
| */ | |||||
| void begin(uint8_t csPin) { | |||||
| m_csPin = csPin; | |||||
| pinMode(m_csPin, OUTPUT); | |||||
| digitalWrite(m_csPin, HIGH); | |||||
| m_spi.begin(); | |||||
| } | |||||
| /** 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]); | |||||
| } | |||||
| } | |||||
| /** Set CS low. */ | |||||
| void select() { | |||||
| digitalWrite(m_csPin, LOW); | |||||
| } | |||||
| /** Save SPISettings. | |||||
| * | |||||
| * \param[in] spiSettings SPI speed, mode, and byte order. | |||||
| */ | |||||
| void setSpiSettings(SPISettings spiSettings) { | |||||
| (void)spiSettings; | |||||
| } | |||||
| /** Set CS high. */ | |||||
| void unselect() { | |||||
| digitalWrite(m_csPin, HIGH); | |||||
| } | |||||
| private: | |||||
| uint8_t m_csPin; | |||||
| SoftSPI<MisoPin, MosiPin, SckPin, 0> m_spi; | |||||
| }; | |||||
| #endif // ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
| //----------------------------------------------------------------------------- | |||||
| // Choose SPI driver for SdFat and SdFatEX classes. | |||||
| #if USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI | |||||
| /** SdFat uses Arduino library SPI. */ | |||||
| typedef SdSpiLibDriver SdFatSpiDriver; | |||||
| #else // USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI | |||||
| /** SdFat uses custom fast SPI. */ | |||||
| typedef SdSpiAltDriver SdFatSpiDriver; | |||||
| #endif // USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI | |||||
| /** typedef for for SdSpiCard class. */ | |||||
| #if ENABLE_SOFTWARE_SPI_CLASS | |||||
| // Need virtual driver. | |||||
| typedef SdSpiBaseDriver SdSpiDriver; | |||||
| #else // ENABLE_SOFTWARE_SPI_CLASS | |||||
| // Don't need virtual driver. | |||||
| typedef SdFatSpiDriver SdSpiDriver; | |||||
| #endif // ENABLE_SOFTWARE_SPI_CLASS | |||||
| //============================================================================= | |||||
| // Use of in-line for AVR to save flash. | |||||
| #ifdef __AVR__ | |||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpiAltDriver::begin(uint8_t csPin) { | |||||
| m_csPin = csPin; | |||||
| pinMode(m_csPin, OUTPUT); | |||||
| digitalWrite(m_csPin, HIGH); | |||||
| SPI.begin(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpiAltDriver::activate() { | |||||
| SPI.beginTransaction(m_spiSettings); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpiAltDriver::deactivate() { | |||||
| SPI.endTransaction(); | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline uint8_t SdSpiAltDriver::receive() { | |||||
| SPDR = 0XFF; | |||||
| while (!(SPSR & (1 << SPIF))) {} | |||||
| return SPDR; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline uint8_t SdSpiAltDriver::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; | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpiAltDriver::send(uint8_t data) { | |||||
| SPDR = data; | |||||
| while (!(SPSR & (1 << SPIF))) {} | |||||
| } | |||||
| //------------------------------------------------------------------------------ | |||||
| inline void SdSpiAltDriver::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 // __AVR__ | |||||
| #endif // SdSpiDriver_h |
| #else // defined(ARDUINO) | #else // defined(ARDUINO) | ||||
| #error "Unknown system" | #error "Unknown system" | ||||
| #endif // defined(ARDUINO) | #endif // defined(ARDUINO) | ||||
| //----------------------------------------------------------------------------- | |||||
| #ifdef ESP8266 | |||||
| // undefine F macro if ESP8266. | |||||
| #undef F | |||||
| #endif // ESP8266 | |||||
| //----------------------------------------------------------------------------- | |||||
| #ifndef F | #ifndef F | ||||
| /** Define macro for strings stored in flash. */ | /** Define macro for strings stored in flash. */ | ||||
| #define F(str) (str) | #define F(str) (str) | ||||
| #endif // F | #endif // F | ||||
| //----------------------------------------------------------------------------- | |||||
| /** \return the time in milliseconds. */ | |||||
| inline uint16_t curTimeMS() { | |||||
| return millis(); | |||||
| } | |||||
| //----------------------------------------------------------------------------- | |||||
| /** | /** | ||||
| * \class SysCall | * \class SysCall | ||||
| * \brief SysCall - Class to wrap system calls. | * \brief SysCall - Class to wrap system calls. | ||||
| static void yield(); | static void yield(); | ||||
| }; | }; | ||||
| #if defined(ARDUINO) | |||||
| #if defined(ESP8266) | |||||
| inline void SysCall::yield() { | |||||
| // Avoid ESP8266 bug | |||||
| delay(0); | |||||
| } | |||||
| #elif defined(ARDUINO) | |||||
| inline void SysCall::yield() { | inline void SysCall::yield() { | ||||
| // Use the external Arduino yield() function. | // Use the external Arduino yield() function. | ||||
| ::yield(); | ::yield(); | ||||
| inline void SysCall::yield() { | inline void SysCall::yield() { | ||||
| Particle.process(); | Particle.process(); | ||||
| } | } | ||||
| #else // defined(ARDUINO) | |||||
| #else // ESP8266 | |||||
| inline void SysCall::yield() {} | inline void SysCall::yield() {} | ||||
| #endif // defined(ARDUINO) | |||||
| #endif // ESP8266 | |||||
| #endif // SysCall_h | #endif // SysCall_h |
| /* Arduino SdFat Library | |||||
| * Copyright (C) 2016 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 SystemInclude_h | |||||
| #define SystemInclude_h | |||||
| #if defined(ARDUINO) | |||||
| #include "FatLib/SysCall.h" | |||||
| #elif defined(PLATFORM_ID) // Only defined if a Particle device | |||||
| #include "SysCall.h" | |||||
| #else // System type | |||||
| #error Unknown System. | |||||
| #endif // System type | |||||
| #endif // SystemInclude_h |