| @@ -1,8 +0,0 @@ | |||
| #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 | |||
| @@ -1,8 +1,22 @@ | |||
| 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 | |||
| file systems on SD/SDHC flash cards. | |||
| @@ -23,9 +37,9 @@ Read changes.txt if you have used previous releases of this library. | |||
| 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 | |||
| 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. | |||
| Updated 23 Jan 2016 | |||
| Updated 19 Aug 2016 | |||
| @@ -34,15 +34,17 @@ nonzero in SdFatConfig.h. | |||
| The %SdFat library supports Long %File Names or short 8.3 names. | |||
| Edit the SdFatConfig.h file to select short or long file names. | |||
| The main classes in %SdFat are SdFat, SdFatSoftSpi, SdFatLibSpi, | |||
| The main classes in %SdFat are SdFat, SdFatEX, SdFatSoftSpi, SdFatSoftSpiEX, | |||
| SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream, | |||
| 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(), | |||
| binary read(), binary write(), close(), remove(), and sync(). SdBaseFile | |||
| @@ -102,19 +104,22 @@ Long %File Names. Long %File names require extra flash but no extra RAM. | |||
| Opening Long %File Names can be slower than opening Short %File Names. | |||
| Data read and write performance is not changed by the type of %File Name. | |||
| Set SD_SPI_CONFIGURATION to enable various SPI options. The SdFatSoftSpi | |||
| and SdFatLibSpi classes can be enabled. SdFatLibSpi uses the standard | |||
| Arduino SPI library and SdFatSoftSpi uses software SPI. | |||
| 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. | |||
| Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. | |||
| 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 | |||
| Relative paths in SdFat are resolved in a manner similar to Windows. | |||
| @@ -352,7 +357,11 @@ getline - Example of getline from section 27.7.1.3 of the C++ standard. | |||
| 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. | |||
| @@ -362,13 +371,13 @@ QuickStart - A program to quickly test your SD card and SD shield/module. | |||
| 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). | |||
| @@ -380,10 +389,6 @@ SdInfo - Initialize an SD card and analyze its structure for trouble shooting. | |||
| StdioBench - Demo and test of stdio style stream. | |||
| StreamParseInt - Demo of the SD.h API and the File class parseInt() function. | |||
| ThreeCards - Demonstrate simultaneous use of SdFat, SdFatLibSpi, SdFatSoftSpi. | |||
| Timestamp - Sets file create, modify, and access timestamps. | |||
| TwoCards - Example using two SD cards. | |||
| @@ -97,8 +97,9 @@ void setup() { | |||
| cout << now << endl; | |||
| #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(); | |||
| } | |||
| @@ -4,6 +4,8 @@ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| const uint8_t chipSelect = SS; | |||
| SdFat sd; | |||
| SdFile file; | |||
| @@ -24,7 +26,7 @@ void setup() { | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| if (!sd.begin()) { | |||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
| Serial.println("begin failed"); | |||
| return; | |||
| } | |||
| @@ -5,7 +5,7 @@ | |||
| SdFat SD; | |||
| // 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; | |||
| //------------------------------------------------------------------------------ | |||
| @@ -36,9 +36,9 @@ void setup() { | |||
| 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(); | |||
| } | |||
| @@ -65,9 +65,9 @@ void setup() { | |||
| 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(); | |||
| } | |||
| @@ -47,9 +47,9 @@ void setup() { | |||
| } | |||
| 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(); | |||
| } | |||
| @@ -95,9 +95,9 @@ void setup() { | |||
| 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(); | |||
| } | |||
| @@ -22,9 +22,9 @@ void setup() { | |||
| while (!Serial) { | |||
| 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(); | |||
| } | |||
| @@ -256,12 +256,7 @@ ISR(TIMER1_COMPB_vect) { | |||
| } | |||
| //============================================================================== | |||
| // 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() { | |||
| @@ -625,8 +620,7 @@ void logData() { | |||
| // Create new file. | |||
| Serial.println(F("Creating new file")); | |||
| 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"); | |||
| } | |||
| // Get the address of the file on the SD. | |||
| @@ -783,8 +777,9 @@ void setup(void) { | |||
| Serial.print(F("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(); | |||
| fatalBlink(); | |||
| } | |||
| @@ -31,7 +31,9 @@ void setup() { | |||
| "You can use test files located in\r\n" | |||
| "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(); | |||
| } | |||
| Serial.print(F("FreeStack: ")); | |||
| @@ -9,65 +9,47 @@ | |||
| * | |||
| * 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 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. | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.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. | |||
| //============================================================================== | |||
| // 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 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; | |||
| //------------------------------------------------------------------------------ | |||
| // File definitions. | |||
| @@ -77,32 +59,34 @@ const int8_t ERROR_LED_PIN = -1; | |||
| // 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. 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" | |||
| #endif // FILE_BASE_NAME | |||
| //------------------------------------------------------------------------------ | |||
| // 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. | |||
| // | |||
| #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 | |||
| #error Too little SRAM | |||
| // | |||
| #elif RAMEND < 0X10FF | |||
| // Use total of two 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 1; | |||
| const uint8_t BUFFER_BLOCK_COUNT = 2; | |||
| // | |||
| #elif RAMEND < 0X20FF | |||
| // Use total of five 512 byte buffers. | |||
| // Use total of four 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 4; | |||
| // | |||
| #else // RAMEND | |||
| // Use total of 13 512 byte buffers. | |||
| // Use total of 12 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
| #endif // RAMEND | |||
| //============================================================================== | |||
| @@ -113,13 +97,13 @@ const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
| // 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; | |||
| char binName[13] = FILE_BASE_NAME "00.bin"; | |||
| // Number of data records in a block. | |||
| const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | |||
| @@ -132,33 +116,14 @@ struct block_t { | |||
| data_t data[DATA_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. | |||
| #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() { | |||
| while (true) { | |||
| SysCall::yield(); | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| delay(200); | |||
| @@ -167,7 +132,49 @@ void fatalBlink() { | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| //------------------------------------------------------------------------------ | |||
| // 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; | |||
| @@ -175,14 +182,17 @@ void binaryToCsv() { | |||
| uint32_t t0 = millis(); | |||
| uint32_t syncCluster = 0; | |||
| SdFile csvFile; | |||
| char csvName[13]; | |||
| char csvName[FILE_NAME_DIM]; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(); | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| binFile.rewind(); | |||
| Serial.println(); | |||
| Serial.print(F("FreeStack: ")); | |||
| Serial.println(FreeStack()); | |||
| // Create a new csvFile. | |||
| strcpy(csvName, binName); | |||
| strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||
| @@ -190,7 +200,7 @@ void binaryToCsv() { | |||
| if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | |||
| error("open csvFile failed"); | |||
| } | |||
| Serial.println(); | |||
| binFile.rewind(); | |||
| Serial.print(F("Writing: ")); | |||
| Serial.print(csvName); | |||
| Serial.println(F(" - type any character to stop")); | |||
| @@ -230,48 +240,55 @@ void binaryToCsv() { | |||
| Serial.print(0.001*(millis() - t0)); | |||
| 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 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)) { | |||
| 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; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -304,127 +321,105 @@ void dumpData() { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // log data | |||
| // max number of blocks to erase per erase call | |||
| uint32_t const ERASE_SIZE = 262144L; | |||
| void logData() { | |||
| uint32_t bgnBlock, endBlock; | |||
| // Allocate extra buffer space. | |||
| block_t block[BUFFER_BLOCK_COUNT]; | |||
| block_t* curBlock = 0; | |||
| Serial.println(); | |||
| // Find unused file name. | |||
| if (BASE_NAME_SIZE > 6) { | |||
| error("FILE_BASE_NAME too long"); | |||
| } | |||
| 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. | |||
| 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"); | |||
| } | |||
| // 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. | |||
| 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")); | |||
| // Wait for Serial Idle. | |||
| Serial.flush(); | |||
| delay(10); | |||
| 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 overrunTotal = 0; | |||
| uint32_t count = 0; | |||
| uint32_t maxDelta = 0; | |||
| uint32_t minDelta = 99999; | |||
| uint32_t maxLatency = 0; | |||
| 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; | |||
| if (Serial.available()) { | |||
| closeFile = true; | |||
| } | |||
| } | |||
| if (closeFile) { | |||
| if (curBlock != 0) { | |||
| // Put buffer in full queue. | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = queueNext(fullHead); | |||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
| curBlock = 0; | |||
| } | |||
| } 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->overrun = overrun; | |||
| overrun = 0; | |||
| @@ -438,18 +433,29 @@ void logData() { | |||
| } 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 { | |||
| 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) { | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = queueNext(fullHead); | |||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
| curBlock = 0; | |||
| } | |||
| if ((uint32_t)delta > maxDelta) maxDelta = delta; | |||
| if ((uint32_t)delta < minDelta) minDelta = delta; | |||
| } | |||
| } | |||
| } | |||
| if (fullHead == fullTail) { | |||
| // Exit loop if done. | |||
| if (closeFile) { | |||
| @@ -458,29 +464,18 @@ void logData() { | |||
| } else if (!sd.card()->isBusy()) { | |||
| // Get address of block to write. | |||
| block_t* pBlock = fullQueue[fullTail]; | |||
| fullTail = queueNext(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; | |||
| t1 = millis(); | |||
| if (usec > maxLatency) { | |||
| 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. | |||
| emptyQueue[emptyHead] = pBlock; | |||
| emptyHead = queueNext(emptyHead); | |||
| emptyStack[emptyTop++] = pBlock; | |||
| bn++; | |||
| if (bn == FILE_BLOCK_COUNT) { | |||
| // File full so stop | |||
| @@ -491,6 +486,12 @@ void logData() { | |||
| 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")); | |||
| @@ -498,25 +499,38 @@ void logData() { | |||
| 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(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) { | |||
| @@ -529,7 +543,6 @@ void setup(void) { | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| Serial.print(F("FreeStack: ")); | |||
| Serial.println(FreeStack()); | |||
| Serial.print(F("Records/block: ")); | |||
| @@ -537,11 +550,13 @@ void setup(void) { | |||
| if (sizeof(block_t) != 512) { | |||
| 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(); | |||
| } | |||
| userSetup(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop(void) { | |||
| @@ -551,14 +566,21 @@ void loop(void) { | |||
| } 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. | |||
| @@ -569,14 +591,21 @@ void loop(void) { | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, LOW); | |||
| } | |||
| if (c == 'c') { | |||
| 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")); | |||
| } | |||
| @@ -1,8 +0,0 @@ | |||
| #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 | |||
| @@ -0,0 +1,41 @@ | |||
| #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() { | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| #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 | |||
| @@ -9,84 +9,34 @@ | |||
| * | |||
| * 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 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. | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.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. | |||
| //============================================================================== | |||
| // 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 = 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. | |||
| // | |||
| @@ -94,8 +44,12 @@ const uint32_t LOG_INTERVAL_USEC = 10000; | |||
| const uint8_t SD_CS_PIN = SS; | |||
| // | |||
| // Digital pin to indicate an error, set to -1 if not used. | |||
| // The led blinks for fatal errors. The led goes on solid for SD write | |||
| // overrun errors and logging continues. | |||
| // 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; | |||
| //------------------------------------------------------------------------------ | |||
| // File definitions. | |||
| @@ -105,32 +59,34 @@ const int8_t ERROR_LED_PIN = -1; | |||
| // 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. 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" | |||
| #endif // FILE_BASE_NAME | |||
| //------------------------------------------------------------------------------ | |||
| // 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. | |||
| // | |||
| #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 | |||
| #error Too little SRAM | |||
| // | |||
| #elif RAMEND < 0X10FF | |||
| // Use total of two 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 1; | |||
| const uint8_t BUFFER_BLOCK_COUNT = 2; | |||
| // | |||
| #elif RAMEND < 0X20FF | |||
| // Use total of five 512 byte buffers. | |||
| // Use total of four 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 4; | |||
| // | |||
| #else // RAMEND | |||
| // Use total of 13 512 byte buffers. | |||
| // Use total of 12 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
| #endif // RAMEND | |||
| //============================================================================== | |||
| @@ -141,13 +97,13 @@ const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
| // 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; | |||
| char binName[13] = FILE_BASE_NAME "00.bin"; | |||
| // Number of data records in a block. | |||
| const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | |||
| @@ -160,33 +116,14 @@ struct block_t { | |||
| data_t data[DATA_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. | |||
| #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() { | |||
| while (true) { | |||
| SysCall::yield(); | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| delay(200); | |||
| @@ -195,7 +132,49 @@ void fatalBlink() { | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| //------------------------------------------------------------------------------ | |||
| // 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; | |||
| @@ -203,14 +182,17 @@ void binaryToCsv() { | |||
| uint32_t t0 = millis(); | |||
| uint32_t syncCluster = 0; | |||
| SdFile csvFile; | |||
| char csvName[13]; | |||
| char csvName[FILE_NAME_DIM]; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(); | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| binFile.rewind(); | |||
| Serial.println(); | |||
| Serial.print(F("FreeStack: ")); | |||
| Serial.println(FreeStack()); | |||
| // Create a new csvFile. | |||
| strcpy(csvName, binName); | |||
| strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||
| @@ -218,7 +200,7 @@ void binaryToCsv() { | |||
| if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | |||
| error("open csvFile failed"); | |||
| } | |||
| Serial.println(); | |||
| binFile.rewind(); | |||
| Serial.print(F("Writing: ")); | |||
| Serial.print(csvName); | |||
| Serial.println(F(" - type any character to stop")); | |||
| @@ -258,48 +240,55 @@ void binaryToCsv() { | |||
| Serial.print(0.001*(millis() - t0)); | |||
| 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 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)) { | |||
| 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; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -332,127 +321,105 @@ void dumpData() { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // log data | |||
| // max number of blocks to erase per erase call | |||
| uint32_t const ERASE_SIZE = 262144L; | |||
| void logData() { | |||
| uint32_t bgnBlock, endBlock; | |||
| // Allocate extra buffer space. | |||
| block_t block[BUFFER_BLOCK_COUNT]; | |||
| block_t* curBlock = 0; | |||
| Serial.println(); | |||
| // Find unused file name. | |||
| if (BASE_NAME_SIZE > 6) { | |||
| error("FILE_BASE_NAME too long"); | |||
| } | |||
| 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. | |||
| 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"); | |||
| } | |||
| // 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. | |||
| 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")); | |||
| // Wait for Serial Idle. | |||
| Serial.flush(); | |||
| delay(10); | |||
| 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 overrunTotal = 0; | |||
| uint32_t count = 0; | |||
| uint32_t maxDelta = 0; | |||
| uint32_t minDelta = 99999; | |||
| uint32_t maxLatency = 0; | |||
| 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; | |||
| if (Serial.available()) { | |||
| closeFile = true; | |||
| } | |||
| } | |||
| if (closeFile) { | |||
| if (curBlock != 0) { | |||
| // Put buffer in full queue. | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = queueNext(fullHead); | |||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
| curBlock = 0; | |||
| } | |||
| } 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->overrun = overrun; | |||
| overrun = 0; | |||
| @@ -466,18 +433,29 @@ void logData() { | |||
| } 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 { | |||
| 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) { | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = queueNext(fullHead); | |||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
| curBlock = 0; | |||
| } | |||
| if ((uint32_t)delta > maxDelta) maxDelta = delta; | |||
| if ((uint32_t)delta < minDelta) minDelta = delta; | |||
| } | |||
| } | |||
| } | |||
| if (fullHead == fullTail) { | |||
| // Exit loop if done. | |||
| if (closeFile) { | |||
| @@ -486,29 +464,18 @@ void logData() { | |||
| } else if (!sd.card()->isBusy()) { | |||
| // Get address of block to write. | |||
| block_t* pBlock = fullQueue[fullTail]; | |||
| fullTail = queueNext(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; | |||
| t1 = millis(); | |||
| if (usec > maxLatency) { | |||
| 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. | |||
| emptyQueue[emptyHead] = pBlock; | |||
| emptyHead = queueNext(emptyHead); | |||
| emptyStack[emptyTop++] = pBlock; | |||
| bn++; | |||
| if (bn == FILE_BLOCK_COUNT) { | |||
| // File full so stop | |||
| @@ -519,6 +486,12 @@ void logData() { | |||
| 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")); | |||
| @@ -526,25 +499,38 @@ void logData() { | |||
| 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(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) { | |||
| @@ -557,7 +543,6 @@ void setup(void) { | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| Serial.print(F("FreeStack: ")); | |||
| Serial.println(FreeStack()); | |||
| Serial.print(F("Records/block: ")); | |||
| @@ -565,47 +550,62 @@ void setup(void) { | |||
| if (sizeof(block_t) != 512) { | |||
| 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(); | |||
| } | |||
| setupADXL345(); | |||
| userSetup(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop(void) { | |||
| // discard any input | |||
| // Read any Serial data. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.read() >= 0); | |||
| } 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.read() >= 0); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, LOW); | |||
| } | |||
| if (c == 'c') { | |||
| 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")); | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| // Empty file with name LowLatencyLoggerADXL345.ino to make IDE happy. | |||
| @@ -0,0 +1,67 @@ | |||
| #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")); | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| #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 | |||
| @@ -0,0 +1,612 @@ | |||
| /** | |||
| * 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")); | |||
| } | |||
| } | |||
| @@ -0,0 +1,2 @@ | |||
| // Empty file with name LowLatencyLoggerMPU6050.ino to make IDE happy. | |||
| @@ -0,0 +1,51 @@ | |||
| // 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")); | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| #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 | |||
| @@ -25,9 +25,9 @@ void setup() { | |||
| 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(); | |||
| } | |||
| @@ -49,9 +49,9 @@ void loop() { | |||
| 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(); | |||
| } | |||
| @@ -8,10 +8,10 @@ | |||
| // to 10 to disable the Ethernet controller. | |||
| 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. | |||
| SdFat sd; | |||
| @@ -28,7 +28,7 @@ int chipSelect; | |||
| void cardOrSpeed() { | |||
| 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() { | |||
| @@ -98,7 +98,7 @@ void loop() { | |||
| pinMode(DISABLE_CHIP_SELECT, OUTPUT); | |||
| digitalWrite(DISABLE_CHIP_SELECT, HIGH); | |||
| } | |||
| if (!sd.begin(chipSelect, spiSpeed)) { | |||
| if (!sd.begin(chipSelect, SPI_SPEED)) { | |||
| if (sd.card()->errorCode()) { | |||
| cout << F( | |||
| "\nSD initialization failed.\n" | |||
| @@ -3,11 +3,7 @@ | |||
| * can be used for high speed data logging. | |||
| * | |||
| * 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 | |||
| * to the length that is used for a logging. It only takes | |||
| @@ -21,11 +17,15 @@ | |||
| // SD chip select pin | |||
| 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 | |||
| SdFat sd; | |||
| @@ -42,13 +42,6 @@ ArduinoOutStream cout(Serial); | |||
| // store error strings in flash to save RAM | |||
| #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) { | |||
| Serial.begin(9600); | |||
| @@ -71,9 +64,9 @@ void loop(void) { | |||
| 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(); | |||
| } | |||
| @@ -81,7 +74,7 @@ void loop(void) { | |||
| sd.remove("RawWrite.txt"); | |||
| // 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"); | |||
| } | |||
| // get the location of the file's blocks | |||
| @@ -104,28 +97,48 @@ void loop(void) { | |||
| 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 | |||
| if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) { | |||
| error("writeStart failed"); | |||
| } | |||
| // init stats | |||
| uint16_t overruns = 0; | |||
| delay(1000); | |||
| uint32_t dotCount = 0; | |||
| uint32_t maxQueuePrint = 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 | |||
| 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 | |||
| uint32_t n = b; | |||
| uint32_t n = b++; | |||
| for (int8_t d = 5; d >= 0; d--) { | |||
| pCache[d] = n || d == 5 ? n % 10 + '0' : ' '; | |||
| n /= 10; | |||
| @@ -136,45 +149,30 @@ void loop(void) { | |||
| error("writeData failed"); | |||
| } | |||
| tw = micros() - tw; | |||
| totalWriteTime += tw; | |||
| // check for max write time | |||
| if (tw > maxWriteTime) { | |||
| 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 | |||
| if (!sd.card()->writeStop()) { | |||
| 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("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 | |||
| file.close(); | |||
| Serial.println(); | |||
| @@ -1,111 +0,0 @@ | |||
| // 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() { | |||
| } | |||
| @@ -0,0 +1,120 @@ | |||
| /* | |||
| * 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() {} | |||
| @@ -7,7 +7,6 @@ | |||
| ** MOSI - pin 11 | |||
| ** MISO - pin 12 | |||
| ** CLK - pin 13 | |||
| ** CS - pin 4 | |||
| created Nov 2010 | |||
| by David A. Mellis | |||
| @@ -17,30 +16,24 @@ | |||
| This example code is in the public domain. | |||
| */ | |||
| #define SD_CS_PIN SS | |||
| #include <SPI.h> | |||
| //#include <SD.h> | |||
| #include "SdFat.h" | |||
| SdFat SD; | |||
| #define SD_CS_PIN SS | |||
| File myFile; | |||
| void setup() | |||
| { | |||
| void setup() { | |||
| // Open serial communications and wait for port to open: | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| ; // wait for serial port to connect. Needed for native USB port only | |||
| } | |||
| Serial.print("Initializing SD card..."); | |||
| // On the Ethernet Shield, CS is pin 4. It's set as an output by default. | |||
| // Note that even if it's not used as the CS pin, the hardware SS pin | |||
| // (10 on most Arduino boards, 53 on the Mega) must be left as an output | |||
| // or the SD library functions will not work. | |||
| pinMode(10, OUTPUT); | |||
| if (!SD.begin(SD_CS_PIN)) { | |||
| Serial.println("initialization failed!"); | |||
| @@ -81,8 +74,7 @@ void setup() | |||
| } | |||
| } | |||
| void loop() | |||
| { | |||
| void loop() { | |||
| // nothing happens after setup | |||
| } | |||
| @@ -1,77 +0,0 @@ | |||
| // 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 | |||
| } | |||
| @@ -0,0 +1,176 @@ | |||
| /* | |||
| * 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() {} | |||
| @@ -26,9 +26,9 @@ | |||
| // Adafruit SD shields and modules: pin 10 | |||
| 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 | |||
| ArduinoOutStream cout(Serial); | |||
| @@ -65,11 +65,9 @@ char noName[] = "NO NAME "; | |||
| char fat16str[] = "FAT16 "; | |||
| 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()) { | |||
| cout << F("SD error: ") << hex << int(card.errorCode()); | |||
| cout << ',' << int(card.errorData()) << dec << endl; | |||
| @@ -499,7 +497,7 @@ void setup() { | |||
| return; | |||
| } | |||
| if (!card.begin(chipSelect, spiSpeed)) { | |||
| if (!card.begin(chipSelect, SPI_SPEED)) { | |||
| cout << F( | |||
| "\nSD initialization failure!\n" | |||
| "Is the SD card inserted correctly?\n" | |||
| @@ -30,16 +30,7 @@ uint32_t cardSize; | |||
| uint32_t eraseSize; | |||
| //------------------------------------------------------------------------------ | |||
| // 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() { | |||
| cid_t cid; | |||
| @@ -97,17 +88,13 @@ uint8_t csdDmp() { | |||
| //------------------------------------------------------------------------------ | |||
| // print partition table | |||
| 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"); | |||
| return false; | |||
| } | |||
| 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) { | |||
| cout << F("\nNo MBR. Assuming Super Floppy format.\n"); | |||
| return true; | |||
| @@ -116,7 +103,7 @@ uint8_t partDmp() { | |||
| cout << F("\nSD Partition Table\n"); | |||
| cout << F("part,boot,type,start,length\n"); | |||
| for (uint8_t ip = 1; ip < 5; ip++) { | |||
| part_t *pt = &p->mbr.part[ip - 1]; | |||
| part_t *pt = &mbr.part[ip - 1]; | |||
| cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type); | |||
| cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl; | |||
| } | |||
| @@ -183,9 +170,9 @@ void loop() { | |||
| } | |||
| 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"); | |||
| return; | |||
| } | |||
| @@ -5,7 +5,7 @@ | |||
| // | |||
| #include <SPI.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. | |||
| const uint8_t SOFT_MISO_PIN = 12; | |||
| @@ -53,6 +53,6 @@ void setup() { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| 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 | |||
| @@ -39,7 +39,10 @@ void setup() { | |||
| SysCall::yield(); | |||
| } | |||
| 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(); | |||
| } | |||
| @@ -137,10 +140,11 @@ void setup() { | |||
| case 3: | |||
| for (uint16_t i = 0; i < 10000; i++) { | |||
| uint32_t n = i + 1000000000UL; | |||
| #if PRINT_FIELD | |||
| stdioFile.printField(i + 1000000000UL, '\n'); | |||
| stdioFile.printField(n, '\n'); | |||
| #else // PRINT_FIELD | |||
| stdioFile.println(i + 1000000000UL); | |||
| stdioFile.println(n); | |||
| #endif // PRINT_FIELD | |||
| } | |||
| break; | |||
| @@ -1,231 +0,0 @@ | |||
| /* | |||
| * 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 | |||
| @@ -83,9 +83,9 @@ void setup(void) { | |||
| while (!Serial.available()) { | |||
| 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(); | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| /* | |||
| * Warning This example requires extra RAM and may crash on Uno. | |||
| * Example use of two SD cards. | |||
| */ | |||
| #include <SPI.h> | |||
| @@ -49,9 +49,9 @@ void setup() { | |||
| while (!Serial.available()) { | |||
| 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(); | |||
| } | |||
| // Insure no TEST_FILE. | |||
| @@ -75,6 +75,7 @@ void setup() { | |||
| cout << F("Remove ") << TEST_FILE << endl << endl; | |||
| sd.remove(TEST_FILE); | |||
| printFreeSpace(); | |||
| cout << F("Done") << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -29,8 +29,12 @@ uint8_t buf[BUF_SIZE]; | |||
| // file system | |||
| 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. | |||
| // SdFatSoftSpi<6, 7, 5> sd; | |||
| @@ -98,9 +102,9 @@ void loop() { | |||
| 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(); | |||
| } | |||
| @@ -79,9 +79,9 @@ void setup() { | |||
| 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(); | |||
| } | |||
| @@ -5,7 +5,7 @@ | |||
| #include "SdFat.h" | |||
| // SD card chip select pin. | |||
| const uint8_t SD_CHIP_SELECT = SS; | |||
| const uint8_t chipSelect = SS; | |||
| //------------------------------------------------------------------------------ | |||
| // File system object. | |||
| @@ -40,9 +40,9 @@ void setup() { | |||
| cin.readline(); | |||
| 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(); | |||
| } | |||
| if (sd.exists("Folder1") | |||
| @@ -72,9 +72,9 @@ void setup(void) { | |||
| } | |||
| 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(); | |||
| } | |||
| @@ -66,9 +66,9 @@ void setup(void) { | |||
| 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(); | |||
| } | |||
| @@ -1,92 +1,130 @@ | |||
| /* | |||
| * 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; | |||
| } | |||
| // 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() { | |||
| @@ -94,27 +132,81 @@ void setup() { | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| yield(); | |||
| } | |||
| cout << F("Type any character to start\n"); | |||
| Serial.println("Type any character to start"); | |||
| 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() { | |||
| } | |||
| @@ -29,9 +29,9 @@ void setup() { | |||
| 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(); | |||
| } | |||
| @@ -20,7 +20,9 @@ void setup() { | |||
| if (c != '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(); | |||
| } | |||
| // Use wipe() for no dot progress indicator. | |||
| @@ -28,7 +30,9 @@ void setup() { | |||
| sd.errorHalt("Wipe failed."); | |||
| } | |||
| // 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."); | |||
| } | |||
| Serial.println("Done"); | |||
| @@ -1,5 +1,5 @@ | |||
| name=SdFat | |||
| version=2015.4.26 | |||
| version=2016.7.24 | |||
| author= | |||
| maintainer= | |||
| sentence=FAT16/FAT32 file system for SD cards. | |||
| @@ -27,7 +27,6 @@ | |||
| */ | |||
| #ifndef DigitalPin_h | |||
| #define DigitalPin_h | |||
| #include "SystemInclude.h" | |||
| #if defined(__AVR__) | |||
| #include <avr/io.h> | |||
| /** GpioPinMap type */ | |||
| @@ -1,9 +1,9 @@ | |||
| /* Arduino SdSpi Library | |||
| /* Arduino SdSpiAltDriver Library | |||
| * Copyright (C) 2016 by William Greiman | |||
| * | |||
| * 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 | |||
| * it under the terms of the GNU General Public License as published by | |||
| @@ -16,52 +16,40 @@ | |||
| * 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 | |||
| * along with the Arduino SdSpiAltDriver Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #if defined(ESP8266) | |||
| #include "SdSpi.h" | |||
| #include "SdSpiDriver.h" | |||
| //------------------------------------------------------------------------------ | |||
| /** Initialize the SPI bus. | |||
| * | |||
| * \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(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** 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. | |||
| SPI.endTransaction(); | |||
| #endif // ENABLE_SPI_TRANSACTIONS | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Receive a byte. | |||
| * | |||
| * \return The byte. | |||
| */ | |||
| uint8_t SdSpi::receive() { | |||
| uint8_t SdSpiAltDriver::receive() { | |||
| return SPI.transfer(0XFF); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -72,7 +60,7 @@ uint8_t SdSpi::receive() { | |||
| * | |||
| * \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. | |||
| SPI.transferBytes(0, buf, n); | |||
| return 0; | |||
| @@ -82,7 +70,7 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| * | |||
| * \param[in] b Byte to send | |||
| */ | |||
| void SdSpi::send(uint8_t b) { | |||
| void SdSpiAltDriver::send(uint8_t b) { | |||
| SPI.transfer(b); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -91,7 +79,7 @@ void SdSpi::send(uint8_t b) { | |||
| * \param[in] buf Buffer for data to be sent. | |||
| * \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. | |||
| while ((reinterpret_cast<uintptr_t>(buf) & 0X3) && n) { | |||
| SPI.transfer(*buf++); | |||
| @@ -1,7 +1,7 @@ | |||
| /* Arduino SdSpi Library | |||
| /* Arduino SdSpiAltDriver Library | |||
| * 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 | |||
| * it under the terms of the GNU General Public License as published by | |||
| @@ -14,10 +14,10 @@ | |||
| * 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 | |||
| * along with the Arduino SdSpiAltDriver Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include "SdSpi.h" | |||
| #include "SdSpiDriver.h" | |||
| #if defined(__SAM3X8E__) || defined(__SAM3X8H__) | |||
| /** Use SAM3X DMAC if nonzero */ | |||
| #define USE_SAM3X_DMAC 1 | |||
| @@ -57,25 +57,11 @@ static bool dmac_channel_transfer_done(uint32_t ul_num) { | |||
| 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 | |||
| pmc_enable_periph_clk(ID_DMAC); | |||
| dmac_disable(); | |||
| @@ -134,28 +120,26 @@ static void spiDmaTX(const uint8_t* src, uint16_t count) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // 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; | |||
| // disable SPI | |||
| // Save the divisor | |||
| uint32_t scbr = pSpi->SPI_CSR[SPI_CHIP_SEL] & 0XFF00; | |||
| // Disable SPI | |||
| pSpi->SPI_CR = SPI_CR_SPIDIS; | |||
| // reset SPI | |||
| pSpi->SPI_CR = SPI_CR_SWRST; | |||
| // no mode fault detection, set master mode | |||
| pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR; | |||
| // 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 | |||
| pSpi->SPI_CR |= SPI_CR_SPIEN; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::endTransaction() { | |||
| #if ENABLE_SPI_TRANSACTIONS | |||
| void SdSpiAltDriver::deactivate() { | |||
| SPI.endTransaction(); | |||
| #endif // ENABLE_SPI_TRANSACTIONS | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| static inline uint8_t spiTransfer(uint8_t b) { | |||
| @@ -168,12 +152,12 @@ static inline uint8_t spiTransfer(uint8_t b) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI receive a byte */ | |||
| uint8_t SdSpi::receive() { | |||
| uint8_t SdSpiAltDriver::receive() { | |||
| return spiTransfer(0XFF); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** 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; | |||
| int rtn = 0; | |||
| #if USE_SAM3X_DMAC | |||
| @@ -206,11 +190,11 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI send a byte */ | |||
| void SdSpi::send(uint8_t b) { | |||
| void SdSpiAltDriver::send(uint8_t 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; | |||
| #if USE_SAM3X_DMAC | |||
| spiDmaTX(buf, n); | |||
| @@ -1,7 +1,7 @@ | |||
| /* Arduino SdSpi Library | |||
| /* Arduino SdSpiAltDriver Library | |||
| * 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 | |||
| * it under the terms of the GNU General Public License as published by | |||
| @@ -14,72 +14,64 @@ | |||
| * 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 | |||
| * along with the Arduino SdSpiAltDriver Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #if defined(__STM32F1__) | |||
| #include "SdSpi.h" | |||
| #include "SdSpiDriver.h" | |||
| #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. | |||
| */ | |||
| void SdSpi::endTransaction() { | |||
| #if ENABLE_SPI_TRANSACTIONS | |||
| SPI.endTransaction(); | |||
| #endif // ENABLE_SPI_TRANSACTIONS | |||
| void SdSpiAltDriver::deactivate() { | |||
| pSpi[m_spiPort]->endTransaction(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Receive a 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. | |||
| @@ -89,14 +81,14 @@ uint8_t SdSpi::receive() { | |||
| * | |||
| * \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; | |||
| #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 | |||
| // SPI.read(buf, n); | |||
| // pSpi[m_spiPort]->read(buf, n); fails ?? use byte transfer | |||
| for (size_t i = 0; i < n; i++) { | |||
| buf[i] = SPI.transfer(0XFF); | |||
| buf[i] = pSpi[m_spiPort]->transfer(0XFF); | |||
| } | |||
| #endif // USE_STM32F1_DMAC | |||
| return rtn; | |||
| @@ -106,8 +98,8 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| * | |||
| * \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. | |||
| @@ -115,11 +107,15 @@ void SdSpi::send(uint8_t b) { | |||
| * \param[in] buf Buffer for data to be sent. | |||
| * \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 | |||
| SPI.dmaSend(const_cast<uint8*>(buf), n); | |||
| pSpi[m_spiPort]->dmaSend(const_cast<uint8*>(buf), n); | |||
| #else // #if USE_STM32F1_DMAC | |||
| SPI.write(buf, n); | |||
| pSpi[m_spiPort]->write(buf, n); | |||
| #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__) | |||
| @@ -1,7 +1,7 @@ | |||
| /* Arduino SdSpi Library | |||
| /* Arduino SdSpiAltDriver Library | |||
| * 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 | |||
| * it under the terms of the GNU General Public License as published by | |||
| @@ -14,14 +14,32 @@ | |||
| * 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 | |||
| * along with the Arduino SdSpiAltDriver Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include "SdSpi.h" | |||
| #include "SdSpiDriver.h" | |||
| #if defined(__arm__) && defined(CORE_TEENSY) | |||
| // SPI definitions | |||
| #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 | |||
| // use 16-bit frame if SPI_USE_8BIT_FRAME is zero | |||
| #define SPI_USE_8BIT_FRAME 0 | |||
| // Limit initial fifo to three entries to avoid fifo overrun | |||
| @@ -37,68 +55,8 @@ | |||
| #define SPI_PUSHR_CTAS(n) (((n) & 7) << 28) | |||
| #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 */ | |||
| uint8_t SdSpi::receive() { | |||
| uint8_t SdSpiAltDriver::receive() { | |||
| SPI0_MCR |= SPI_MCR_CLR_RXF; | |||
| SPI0_SR = SPI_SR_TCF; | |||
| SPI0_PUSHR = 0xFF; | |||
| @@ -107,7 +65,7 @@ uint8_t SdSpi::receive() { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** 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 | |||
| SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | |||
| #if SPI_USE_8BIT_FRAME | |||
| @@ -162,7 +120,7 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI send a byte */ | |||
| void SdSpi::send(uint8_t b) { | |||
| void SdSpiAltDriver::send(uint8_t b) { | |||
| SPI0_MCR |= SPI_MCR_CLR_RXF; | |||
| SPI0_SR = SPI_SR_TCF; | |||
| SPI0_PUSHR = b; | |||
| @@ -170,7 +128,7 @@ void SdSpi::send(uint8_t b) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** 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 | |||
| SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | |||
| #if SPI_USE_8BIT_FRAME | |||
| @@ -228,52 +186,12 @@ void SdSpi::send(const uint8_t* buf , size_t n) { | |||
| #else // 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. | |||
| * | |||
| * \return The byte. | |||
| */ | |||
| uint8_t SdSpi::receive() { | |||
| uint8_t SdSpiAltDriver::receive() { | |||
| return SPI.transfer(0XFF); | |||
| } | |||
| /** Receive multiple bytes. | |||
| @@ -283,7 +201,7 @@ uint8_t SdSpi::receive() { | |||
| * | |||
| * \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++) { | |||
| buf[i] = SPI.transfer(0XFF); | |||
| } | |||
| @@ -293,7 +211,7 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| * | |||
| * \param[in] b Byte to send | |||
| */ | |||
| void SdSpi::send(uint8_t b) { | |||
| void SdSpiAltDriver::send(uint8_t b) { | |||
| SPI.transfer(b); | |||
| } | |||
| /** Send multiple bytes. | |||
| @@ -301,16 +219,10 @@ void SdSpi::send(uint8_t b) { | |||
| * \param[in] buf Buffer for data to be sent. | |||
| * \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++) { | |||
| SPI.transfer(buf[i]); | |||
| } | |||
| } | |||
| #endif // KINETISK | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::endTransaction() { | |||
| #if ENABLE_SPI_TRANSACTIONS | |||
| SPI.endTransaction(); | |||
| #endif // ENABLE_SPI_TRANSACTIONS | |||
| } | |||
| #endif // defined(__arm__) && defined(CORE_TEENSY) | |||
| @@ -0,0 +1,89 @@ | |||
| /* 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; | |||
| } | |||
| @@ -0,0 +1,197 @@ | |||
| /* 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 | |||
| @@ -114,11 +114,7 @@ class PrintFile : public FatFile, public Print { | |||
| * \class File | |||
| * \brief Arduino SD.h style File API | |||
| */ | |||
| #if ARDUINO_FILE_USES_STREAM | |||
| class File : public FatFile, public Stream { | |||
| #else // ARDUINO_FILE_USES_STREAM | |||
| class File : public FatFile, public Print { | |||
| #endif // ARDUINO_FILE_USES_STREAM | |||
| public: | |||
| File() {} | |||
| /** Create a file object and open it in the current working directory. | |||
| @@ -25,7 +25,6 @@ | |||
| */ | |||
| #include "FatLibConfig.h" | |||
| #if ENABLE_ARDUINO_FEATURES | |||
| #include "SysCall.h" | |||
| #include "bufstream.h" | |||
| //============================================================================== | |||
| /** | |||
| @@ -51,7 +50,7 @@ class ArduinoInStream : public ibufstream { | |||
| uint32_t t; | |||
| m_line[0] = '\0'; | |||
| while (!m_hw->available()) { | |||
| SysCall::yield(); | |||
| yield(); | |||
| } | |||
| while (1) { | |||
| @@ -0,0 +1,56 @@ | |||
| #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 | |||
| @@ -22,46 +22,46 @@ | |||
| //------------------------------------------------------------------------------ | |||
| // use the gnu style oflag in open() | |||
| /** open() oflag for reading */ | |||
| uint8_t const O_READ = 0X01; | |||
| const uint8_t O_READ = 0X01; | |||
| /** open() oflag - same as O_IN */ | |||
| uint8_t const O_RDONLY = O_READ; | |||
| const uint8_t O_RDONLY = O_READ; | |||
| /** open() oflag for write */ | |||
| uint8_t const O_WRITE = 0X02; | |||
| const uint8_t O_WRITE = 0X02; | |||
| /** 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 */ | |||
| uint8_t const O_RDWR = (O_READ | O_WRITE); | |||
| const uint8_t O_RDWR = (O_READ | O_WRITE); | |||
| /** 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. */ | |||
| uint8_t const O_APPEND = 0X04; | |||
| const uint8_t O_APPEND = 0X04; | |||
| /** 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 */ | |||
| uint8_t const O_TRUNC = 0X10; | |||
| const uint8_t O_TRUNC = 0X10; | |||
| /** 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 */ | |||
| 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 */ | |||
| uint8_t const O_EXCL = 0X80; | |||
| const uint8_t O_EXCL = 0X80; | |||
| // FatFile class static and const definitions | |||
| // flags for ls() | |||
| /** 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 */ | |||
| uint8_t const LS_DATE = 2; | |||
| const uint8_t LS_DATE = 2; | |||
| /** 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 */ | |||
| uint8_t const LS_R = 8; | |||
| const uint8_t LS_R = 8; | |||
| // flags for timestamp | |||
| /** 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 */ | |||
| uint8_t const T_CREATE = 2; | |||
| const uint8_t T_CREATE = 2; | |||
| /** Set the file's write date and time */ | |||
| uint8_t const T_WRITE = 4; | |||
| const uint8_t T_WRITE = 4; | |||
| #endif // FatApiConstants_h | |||
| @@ -50,7 +50,7 @@ bool FatFile::addDirCluster() { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| block = m_vol->clusterStartBlock(m_curCluster); | |||
| block = m_vol->clusterFirstBlock(m_curCluster); | |||
| pc = m_vol->cacheFetchData(block, FatCache::CACHE_RESERVE_FOR_WRITE); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| @@ -113,8 +113,8 @@ bool FatFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { | |||
| DBG_FAIL_MACRO; | |||
| 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; | |||
| return true; | |||
| } | |||
| @@ -127,6 +127,7 @@ fail: | |||
| bool FatFile::createContiguous(FatFile* dirFile, | |||
| const char* path, uint32_t size) { | |||
| uint32_t count; | |||
| // don't allow zero length file | |||
| if (size == 0) { | |||
| DBG_FAIL_MACRO; | |||
| @@ -149,7 +150,6 @@ bool FatFile::createContiguous(FatFile* dirFile, | |||
| // insure sync() will update dir entry | |||
| m_flags |= F_FILE_DIR_DIRTY; | |||
| return sync(); | |||
| fail: | |||
| @@ -344,7 +344,7 @@ bool FatFile::mkdir(FatFile* parent, fname_t* fname) { | |||
| } | |||
| // 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); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| @@ -612,7 +612,7 @@ bool FatFile::openParent(FatFile* dirFile) { | |||
| if (dirFile->m_dirCluster == 0) { | |||
| 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); | |||
| if (!cb) { | |||
| DBG_FAIL_MACRO; | |||
| @@ -748,7 +748,7 @@ int FatFile::read(void* buf, size_t nbyte) { | |||
| } | |||
| } | |||
| } | |||
| block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster; | |||
| block = m_vol->clusterFirstBlock(m_curCluster) + blockOfCluster; | |||
| } | |||
| if (offset != 0 || toRead < 512 || block == m_vol->cacheBlockNumber()) { | |||
| // amount to be read from current block | |||
| @@ -941,7 +941,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) { | |||
| // update dot dot if directory | |||
| if (dirCluster) { | |||
| // 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); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| @@ -955,7 +955,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) { | |||
| goto fail; | |||
| } | |||
| // 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); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| @@ -1404,7 +1404,7 @@ int FatFile::write(const void* buf, size_t nbyte) { | |||
| } | |||
| } | |||
| // 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) { | |||
| // partial block - must use cache | |||
| @@ -217,7 +217,7 @@ class FatFile { | |||
| /** Create and open a new contiguous file of a specified size. | |||
| * | |||
| * \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. | |||
| * | |||
| * \return The value true is returned for success and | |||
| @@ -225,6 +225,17 @@ class FatFile { | |||
| */ | |||
| bool createContiguous(FatFile* dirFile, | |||
| 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. */ | |||
| uint32_t curCluster() const { | |||
| return m_curCluster; | |||
| @@ -801,6 +812,13 @@ class FatFile { | |||
| m_cwd = dir; | |||
| 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 | |||
| * to be written to the storage device. | |||
| * | |||
| @@ -956,9 +974,9 @@ class FatFile { | |||
| // bits defined in m_flags | |||
| // 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 | |||
| static uint8_t const F_FILE_DIR_DIRTY = 0X80; | |||
| static const uint8_t F_FILE_DIR_DIRTY = 0X80; | |||
| // global pointer to cwd dir | |||
| static FatFile* m_cwd; | |||
| @@ -234,7 +234,6 @@ bool FatFile::printModifyDateTime(print_t* pr) { | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| size_t FatFile::printFileSize(print_t* pr) { | |||
| char buf[11]; | |||
| @@ -21,6 +21,7 @@ | |||
| #define FatFileSystem_h | |||
| #include "FatVolume.h" | |||
| #include "FatFile.h" | |||
| #include "ArduinoStream.h" | |||
| #include "ArduinoFiles.h" | |||
| /** | |||
| * \file | |||
| @@ -35,11 +36,13 @@ class FatFileSystem : public FatVolume { | |||
| public: | |||
| /** | |||
| * Initialize an FatFileSystem object. | |||
| * \param[in] blockDev Device block driver. | |||
| * \param[in] part partition to initialize. | |||
| * \return The value true is returned for success and | |||
| * 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(); | |||
| return (part ? init(part) : init(1) || init(0)) | |||
| && vwd()->openRoot(this) && FatFile::setCwd(vwd()); | |||
| @@ -91,7 +94,7 @@ class FatFileSystem : public FatVolume { | |||
| * \return a File object. | |||
| */ | |||
| 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 | |||
| /** Change a volume's working directory to root | |||
| @@ -142,7 +145,6 @@ class FatFileSystem : public FatVolume { | |||
| if (!dir.isDir()) { | |||
| goto fail; | |||
| } | |||
| // *m_vwd = dir; | |||
| m_vwd = dir; | |||
| if (set_cwd) { | |||
| FatFile::setCwd(vwd()); | |||
| @@ -189,7 +191,7 @@ fail: | |||
| * | |||
| * 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); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| @@ -53,19 +53,6 @@ | |||
| #define USE_LONG_FILE_NAMES 1 | |||
| #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 | |||
| * for FAT table entries. Improves performance for large writes that | |||
| @@ -92,6 +79,15 @@ | |||
| #endif // RAMEND | |||
| #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. | |||
| * | |||
| @@ -133,7 +129,9 @@ | |||
| /** | |||
| * Enable Extra features for Arduino. | |||
| */ | |||
| // #define ENABLE_ARDUINO_FEATURES 0 ////////////////////////FIX THIS ///////////////// | |||
| #ifndef ENABLE_ARDUINO_FEATURES | |||
| #include <Arduino.h> | |||
| #if defined(ARDUINO) || defined(PLATFORM_ID) || defined(DOXYGEN) | |||
| #define ENABLE_ARDUINO_FEATURES 1 | |||
| #else // #if defined(ARDUINO) || defined(DOXYGEN) | |||
| @@ -29,11 +29,11 @@ | |||
| */ | |||
| //------------------------------------------------------------------------------ | |||
| /** 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 */ | |||
| uint8_t const BOOTSIG1 = 0XAA; | |||
| const uint8_t BOOTSIG1 = 0XAA; | |||
| /** 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 | |||
| @@ -525,9 +525,9 @@ struct fat32_boot { | |||
| typedef struct fat32_boot fat32_boot_t; | |||
| //------------------------------------------------------------------------------ | |||
| /** 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 */ | |||
| uint32_t const FSINFO_STRUCT_SIG = 0x61417272; | |||
| const uint32_t FSINFO_STRUCT_SIG = 0x61417272; | |||
| /** | |||
| * \struct fat32_fsinfo | |||
| * | |||
| @@ -566,19 +566,19 @@ typedef struct fat32_fsinfo fat32_fsinfo_t; | |||
| //------------------------------------------------------------------------------ | |||
| // End Of Chain values for FAT entries | |||
| /** 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. */ | |||
| uint16_t const FAT12EOC_MIN = 0XFF8; | |||
| const uint16_t FAT12EOC_MIN = 0XFF8; | |||
| /** 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. */ | |||
| uint16_t const FAT16EOC_MIN = 0XFFF8; | |||
| const uint16_t FAT16EOC_MIN = 0XFFF8; | |||
| /** 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. */ | |||
| uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; | |||
| const uint32_t FAT32EOC_MIN = 0X0FFFFFF8; | |||
| /** Mask a for FAT32 entry. Entries are 28 bits. */ | |||
| uint32_t const FAT32MASK = 0X0FFFFFFF; | |||
| const uint32_t FAT32MASK = 0X0FFFFFFF; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \struct directoryEntry | |||
| @@ -665,33 +665,33 @@ typedef struct directoryEntry dir_t; | |||
| // Definitions for directory entries | |||
| // | |||
| /** 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" */ | |||
| 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 */ | |||
| uint8_t const DIR_NAME_FREE = 0X00; | |||
| const uint8_t DIR_NAME_FREE = 0X00; | |||
| /** 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 */ | |||
| uint8_t const DIR_ATT_HIDDEN = 0X02; | |||
| const uint8_t DIR_ATT_HIDDEN = 0X02; | |||
| /** 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 */ | |||
| uint8_t const DIR_ATT_VOLUME_ID = 0X08; | |||
| const uint8_t DIR_ATT_VOLUME_ID = 0X08; | |||
| /** 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 */ | |||
| uint8_t const DIR_ATT_ARCHIVE = 0X20; | |||
| const uint8_t DIR_ATT_ARCHIVE = 0X20; | |||
| /** Test value for long name entry. Test is | |||
| (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 */ | |||
| uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; | |||
| const uint8_t DIR_ATT_LONG_NAME_MASK = 0X3F; | |||
| /** defined attribute bits */ | |||
| uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; | |||
| const uint8_t DIR_ATT_DEFINED_BITS = 0X3F; | |||
| /** Mask for file/subdirectory tests */ | |||
| uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); | |||
| const uint8_t DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); | |||
| /** Filename base-name is all lower case */ | |||
| const uint8_t DIR_NT_LC_BASE = 0X08; | |||
| @@ -818,9 +818,9 @@ static inline uint8_t FAT_SECOND(uint16_t fatTime) { | |||
| return 2*(fatTime & 0X1F); | |||
| } | |||
| /** 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 */ | |||
| 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 */ | |||
| const uint8_t LDIR_NAME1_DIM = 5; | |||
| @@ -39,6 +39,7 @@ cache_t* FatCache::read(uint32_t lbn, uint8_t option) { | |||
| return &m_block; | |||
| fail: | |||
| return 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -183,7 +184,7 @@ fail: | |||
| 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); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -196,7 +197,7 @@ int8_t FatVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||
| // error if reserved cluster of beyond FAT | |||
| DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | |||
| if (m_fatType == 32) { | |||
| if (fatType() == 32) { | |||
| lba = m_fatStartBlock + (cluster >> 7); | |||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | |||
| if (!pc) { | |||
| @@ -206,8 +207,7 @@ int8_t FatVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||
| next = pc->fat32[cluster & 0X7F] & FAT32MASK; | |||
| goto done; | |||
| } | |||
| if (m_fatType == 16) { | |||
| if (fatType() == 16) { | |||
| lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | |||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | |||
| if (!pc) { | |||
| @@ -217,7 +217,7 @@ int8_t FatVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||
| next = pc->fat16[cluster & 0XFF]; | |||
| goto done; | |||
| } | |||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||
| if (FAT12_SUPPORT && fatType() == 12) { | |||
| uint16_t index = cluster; | |||
| index += index >> 1; | |||
| lba = m_fatStartBlock + (index >> 9); | |||
| @@ -263,7 +263,7 @@ bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { | |||
| // error if reserved cluster of beyond FAT | |||
| DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | |||
| if (m_fatType == 32) { | |||
| if (fatType() == 32) { | |||
| lba = m_fatStartBlock + (cluster >> 7); | |||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | |||
| if (!pc) { | |||
| @@ -274,7 +274,7 @@ bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { | |||
| return true; | |||
| } | |||
| if (m_fatType == 16) { | |||
| if (fatType() == 16) { | |||
| lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | |||
| pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | |||
| if (!pc) { | |||
| @@ -285,7 +285,7 @@ bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { | |||
| return true; | |||
| } | |||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||
| if (FAT12_SUPPORT && fatType() == 12) { | |||
| uint16_t index = cluster; | |||
| index += index >> 1; | |||
| lba = m_fatStartBlock + (index >> 9); | |||
| @@ -367,7 +367,7 @@ int32_t FatVolume::freeClusterCount() { | |||
| uint32_t todo = m_lastCluster + 1; | |||
| uint16_t n; | |||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||
| if (FAT12_SUPPORT && fatType() == 12) { | |||
| for (unsigned i = 2; i < todo; i++) { | |||
| uint32_t c; | |||
| int8_t fg = fatGet(i, &c); | |||
| @@ -379,7 +379,7 @@ int32_t FatVolume::freeClusterCount() { | |||
| free++; | |||
| } | |||
| } | |||
| } else if (m_fatType == 16 || m_fatType == 32) { | |||
| } else if (fatType() == 16 || fatType() == 32) { | |||
| lba = m_fatStartBlock; | |||
| while (todo) { | |||
| cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ); | |||
| @@ -387,11 +387,11 @@ int32_t FatVolume::freeClusterCount() { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| n = m_fatType == 16 ? 256 : 128; | |||
| n = fatType() == 16 ? 256 : 128; | |||
| if (todo < n) { | |||
| n = todo; | |||
| } | |||
| if (m_fatType == 16) { | |||
| if (fatType() == 16) { | |||
| for (uint16_t i = 0; i < n; i++) { | |||
| if (pc->fat16[i] == 0) { | |||
| free++; | |||
| @@ -427,12 +427,10 @@ bool FatVolume::init(uint8_t part) { | |||
| uint8_t tmp; | |||
| m_fatType = 0; | |||
| m_allocSearchStart = 1; | |||
| m_cache.init(this); | |||
| #if USE_SEPARATE_FAT_CACHE | |||
| m_fatCache.init(this); | |||
| #endif // USE_SEPARATE_FAT_CACHE | |||
| // if part == 0 assume super floppy with FAT boot sector in block zero | |||
| // if part > 0 assume mbr volume with partition table | |||
| if (part) { | |||
| @@ -468,7 +466,6 @@ bool FatVolume::init(uint8_t part) { | |||
| } | |||
| m_blocksPerCluster = fbs->sectorsPerCluster; | |||
| m_clusterBlockMask = m_blocksPerCluster - 1; | |||
| // determine shift that is same as multiply by m_blocksPerCluster | |||
| m_clusterSizeShift = 0; | |||
| for (tmp = 1; m_blocksPerCluster != tmp; tmp <<= 1, m_clusterSizeShift++) { | |||
| @@ -477,7 +474,6 @@ bool FatVolume::init(uint8_t part) { | |||
| goto fail; | |||
| } | |||
| } | |||
| m_blocksPerFat = fbs->sectorsPerFat16 ? | |||
| fbs->sectorsPerFat16 : fbs->sectorsPerFat32; | |||
| @@ -503,7 +499,6 @@ bool FatVolume::init(uint8_t part) { | |||
| // Indicate unknown number of free clusters. | |||
| setFreeClusterCount(-1); | |||
| // FAT type is determined by cluster count | |||
| if (clusterCount < 4085) { | |||
| m_fatType = 12; | |||
| @@ -527,7 +522,7 @@ bool FatVolume::wipe(print_t* pr) { | |||
| cache_t* cache; | |||
| uint16_t count; | |||
| uint32_t lbn; | |||
| if (!m_fatType) { | |||
| if (!fatType()) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| @@ -538,8 +533,8 @@ bool FatVolume::wipe(print_t* pr) { | |||
| } | |||
| memset(cache->data, 0, 512); | |||
| // Zero root. | |||
| if (m_fatType == 32) { | |||
| lbn = clusterStartBlock(m_rootDirStart); | |||
| if (fatType() == 32) { | |||
| lbn = clusterFirstBlock(m_rootDirStart); | |||
| count = m_blocksPerCluster; | |||
| } else { | |||
| lbn = m_rootDirStart; | |||
| @@ -564,13 +559,13 @@ bool FatVolume::wipe(print_t* pr) { | |||
| } | |||
| } | |||
| // Reserve first two clusters. | |||
| if (m_fatType == 32) { | |||
| if (fatType() == 32) { | |||
| cache->fat32[0] = 0x0FFFFFF8; | |||
| cache->fat32[1] = 0x0FFFFFFF; | |||
| } else if (m_fatType == 16) { | |||
| } else if (fatType() == 16) { | |||
| cache->fat16[0] = 0XFFF8; | |||
| cache->fat16[1] = 0XFFFF; | |||
| } else if (FAT12_SUPPORT && m_fatType == 12) { | |||
| } else if (FAT12_SUPPORT && fatType() == 12) { | |||
| cache->fat32[0] = 0XFFFFF8; | |||
| } else { | |||
| DBG_FAIL_MACRO; | |||
| @@ -581,7 +576,7 @@ bool FatVolume::wipe(print_t* pr) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (m_fatType == 32) { | |||
| if (fatType() == 32) { | |||
| // Reserve root cluster. | |||
| if (!fatPutEOC(m_rootDirStart) || !cacheSync()) { | |||
| DBG_FAIL_MACRO; | |||
| @@ -24,18 +24,18 @@ | |||
| * \brief FatVolume class | |||
| */ | |||
| #include <stddef.h> | |||
| #include "SysCall.h" | |||
| #include "FatLibConfig.h" | |||
| #include "FatStructs.h" | |||
| #include "BlockDriver.h" | |||
| //------------------------------------------------------------------------------ | |||
| #ifndef DOXYGEN_SHOULD_SKIP_THIS | |||
| /** Macro for debug. */ | |||
| #define DEBUG_MODE 0 | |||
| #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_HALT_IF(b) if (b) {Serial.println(F(#b));\ | |||
| DBG_FAIL_MACRO; SysCall::halt();} | |||
| DBG_FAIL_MACRO; while (1);} | |||
| #else // DEBUG_MODE | |||
| #define DBG_FAIL_MACRO | |||
| #define DBG_PRINT_IF(b) | |||
| @@ -100,11 +100,11 @@ class FatCache { | |||
| /** Sync existing block but do not read new block. */ | |||
| static const uint8_t CACHE_OPTION_NO_READ = 4; | |||
| /** Cache block for read. */ | |||
| static uint8_t const CACHE_FOR_READ = 0; | |||
| static const uint8_t CACHE_FOR_READ = 0; | |||
| /** 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. */ | |||
| static uint8_t const CACHE_RESERVE_FOR_WRITE | |||
| static const uint8_t CACHE_RESERVE_FOR_WRITE | |||
| = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; | |||
| /** \return Cache block address. */ | |||
| cache_t* block() { | |||
| @@ -256,7 +256,9 @@ class FatVolume { | |||
| // Allow FatFile and FatCache access to FatVolume private functions. | |||
| friend class FatCache; | |||
| friend class FatFile; | |||
| friend class FatFileSystem; | |||
| //------------------------------------------------------------------------------ | |||
| BlockDriver* m_blockDev; // block device | |||
| uint8_t m_blocksPerCluster; // Cluster size in blocks. | |||
| uint8_t m_clusterBlockMask; // Mask to extract block of cluster. | |||
| uint8_t m_clusterSizeShift; // Cluster count to block count shift. | |||
| @@ -269,6 +271,24 @@ class FatVolume { | |||
| uint32_t m_lastCluster; // Last cluster number in FAT. | |||
| 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 | |||
| int32_t m_freeClusterCount; // Count of free clusters in volume. | |||
| void setFreeClusterCount(int32_t value) { | |||
| @@ -297,7 +317,7 @@ class FatVolume { | |||
| options | FatCache::CACHE_STATUS_MIRROR_FAT); | |||
| } | |||
| bool cacheSync() { | |||
| return m_cache.sync() && m_fatCache.sync(); | |||
| return m_cache.sync() && m_fatCache.sync() && syncBlocks(); | |||
| } | |||
| #else // | |||
| cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { | |||
| @@ -305,7 +325,7 @@ class FatVolume { | |||
| options | FatCache::CACHE_STATUS_MIRROR_FAT); | |||
| } | |||
| bool cacheSync() { | |||
| return m_cache.sync(); | |||
| return m_cache.sync() && syncBlocks(); | |||
| } | |||
| #endif // USE_SEPARATE_FAT_CACHE | |||
| cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { | |||
| @@ -332,7 +352,7 @@ class FatVolume { | |||
| uint8_t blockOfCluster(uint32_t position) const { | |||
| 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); | |||
| bool fatPut(uint32_t cluster, uint32_t value); | |||
| bool fatPutEOC(uint32_t cluster) { | |||
| @@ -342,13 +362,5 @@ class FatVolume { | |||
| bool isEOC(uint32_t cluster) const { | |||
| return cluster > m_lastCluster; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| // Virtual block I/O functions. | |||
| virtual bool readBlock(uint32_t block, uint8_t* dst) = 0; | |||
| virtual bool writeBlock(uint32_t block, const uint8_t* 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 | |||
| @@ -32,8 +32,10 @@ extern char __bss_end; | |||
| * \return The number of free bytes. | |||
| */ | |||
| 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 | |||
| static int FreeStack() { | |||
| @@ -42,7 +44,7 @@ static int FreeStack() { | |||
| #elif defined(__arm__) | |||
| extern "C" char* sbrk(int incr); | |||
| static int FreeStack() { | |||
| char top; | |||
| char top = 't'; | |||
| return &top - reinterpret_cast<char*>(sbrk(0)); | |||
| } | |||
| #else | |||
| @@ -17,11 +17,15 @@ | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include "SystemInclude.h" | |||
| #include "SysCall.h" | |||
| #if defined(UDR0) || defined(DOXYGEN) | |||
| #include "MinimumSerial.h" | |||
| 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) { | |||
| uint16_t baud_setting; | |||
| // don't worry, the compiler will squeeze out F_CPU != 16000000UL | |||
| @@ -43,6 +47,10 @@ void MinimumSerial::begin(uint32_t baud) { | |||
| UCSR0B |= (1 << TXEN0) | (1 << RXEN0); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void MinimumSerial::flush() { | |||
| while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int MinimumSerial::read() { | |||
| if (UCSR0A & (1 << RXC0)) { | |||
| return UDR0; | |||
| @@ -17,9 +17,13 @@ | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| /** | |||
| * \file | |||
| * \brief Minimal AVR Serial driver. | |||
| */ | |||
| #ifndef MinimumSerial_h | |||
| #define MinimumSerial_h | |||
| #include "SystemInclude.h" | |||
| #include "SysCall.h" | |||
| //============================================================================== | |||
| /** | |||
| * \class MinimumSerial | |||
| @@ -27,12 +31,20 @@ | |||
| */ | |||
| class MinimumSerial : public Print { | |||
| 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. | |||
| * Do not call this function if you use another serial library. | |||
| * \param[in] baud rate | |||
| */ | |||
| void begin(uint32_t baud); | |||
| /** Wait for write done. */ | |||
| void flush(); | |||
| /** | |||
| * Unbuffered read | |||
| * \return -1 if no character is available or an available character. | |||
| @@ -1,100 +0,0 @@ | |||
| /* 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) | |||
| @@ -1,5 +1,5 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * Copyright (C) 2016 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| @@ -23,16 +23,16 @@ | |||
| * \file | |||
| * \brief SdFat class | |||
| */ | |||
| #include "SysCall.h" | |||
| #include "BlockDriver.h" | |||
| #ifdef ARDUINO | |||
| #include "SdSpiCard/SdSpiCard.h" | |||
| #include "FatLib/FatLib.h" | |||
| #else // ARDUINO | |||
| #include "SdSpiCard.h" | |||
| #include "FatLib.h" | |||
| #endif // ARDUINO | |||
| //------------------------------------------------------------------------------ | |||
| /** SdFat version YYYYMMDD */ | |||
| #define SD_FAT_VERSION 20160430 | |||
| #define SD_FAT_VERSION 20160801 | |||
| //============================================================================== | |||
| /** | |||
| * \class SdBaseFile | |||
| @@ -51,6 +51,7 @@ class SdBaseFile : public FatFile { | |||
| */ | |||
| SdBaseFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {} | |||
| }; | |||
| //----------------------------------------------------------------------------- | |||
| #if ENABLE_ARDUINO_FEATURES | |||
| /** | |||
| * \class SdFile | |||
| @@ -70,25 +71,37 @@ class SdFile : public PrintFile { | |||
| SdFile(const char* path, uint8_t oflag) : PrintFile(path, oflag) {} | |||
| }; | |||
| #endif // #if ENABLE_ARDUINO_FEATURES | |||
| //----------------------------------------------------------------------------- | |||
| /** | |||
| * \class SdFatBase | |||
| * \class SdFileSystem | |||
| * \brief Virtual base class for %SdFat library. | |||
| */ | |||
| class SdFatBase : public FatFileSystem { | |||
| template<class SdDriverClass> | |||
| class SdFileSystem : public FatFileSystem { | |||
| public: | |||
| /** Initialize SD card and file system. | |||
| * \param[in] spi SPI object for the card. | |||
| * \param[in] csPin SD card chip select pin. | |||
| * \param[in] divisor SPI divisor. | |||
| * \param[in] spiSettings SPI speed, mode, and bit order. | |||
| * \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 */ | |||
| 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. */ | |||
| void errorHalt() { | |||
| @@ -98,7 +111,10 @@ class SdFatBase : public FatFileSystem { | |||
| * | |||
| * \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. | |||
| * | |||
| * \param[in] msg Message to print. | |||
| @@ -111,8 +127,10 @@ class SdFatBase : public FatFileSystem { | |||
| * \param[in] pr Print destination. | |||
| * \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 */ | |||
| void errorPrint() { | |||
| errorPrint(&Serial); | |||
| @@ -120,7 +138,15 @@ class SdFatBase : public FatFileSystem { | |||
| /** %Print any SD error code. | |||
| * \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. | |||
| * | |||
| * \param[in] msg Message to print. | |||
| @@ -133,14 +159,10 @@ class SdFatBase : public FatFileSystem { | |||
| * \param[in] pr Print destination. | |||
| * \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. */ | |||
| void initErrorHalt() { | |||
| @@ -150,42 +172,65 @@ class SdFatBase : public FatFileSystem { | |||
| * | |||
| * \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. | |||
| */ | |||
| void initErrorHalt(char const *msg) { | |||
| initErrorHalt(&Serial, msg); | |||
| } | |||
| /**Print message, error details, and halt after SdFatBase::init() fails. | |||
| /**Print message, error details, and halt after begin() fails. | |||
| * \param[in] pr Print device. | |||
| * \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() { | |||
| initErrorPrint(&Serial); | |||
| } | |||
| /** Print error details after SdFatBase::init() fails. | |||
| /** Print error details after begin() fails. | |||
| * | |||
| * \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. | |||
| */ | |||
| void initErrorPrint(char const *msg) { | |||
| initErrorPrint(&Serial, msg); | |||
| } | |||
| /**Print message and error details and halt after SdFatBase::init() fails. | |||
| /**Print message and error details and halt after begin() fails. | |||
| * | |||
| * \param[in] pr Print destination. | |||
| * \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) | |||
| /** %Print msg, any SD error code, and halt. | |||
| * | |||
| @@ -199,7 +244,11 @@ class SdFatBase : public FatFileSystem { | |||
| * \param[in] pr Print destination. | |||
| * \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. | |||
| * | |||
| * \param[in] msg Message to print. | |||
| @@ -212,149 +261,202 @@ class SdFatBase : public FatFileSystem { | |||
| * \param[in] pr Print destination. | |||
| * \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. | |||
| */ | |||
| void initErrorHalt(const __FlashStringHelper* 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] 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. | |||
| */ | |||
| void initErrorPrint(const __FlashStringHelper* 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] 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) | |||
| private: | |||
| /** \return The card error code */ | |||
| uint8_t cardErrorCode() { | |||
| return m_sdCard.errorCode(); | |||
| return m_card.errorCode(); | |||
| } | |||
| /** \return the card error data */ | |||
| 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 | |||
| * \brief Main file system class for %SdFat library. | |||
| */ | |||
| class SdFat : public SdFatBase { | |||
| class SdFat : public SdFileSystem<SdBlockDriver> { | |||
| public: | |||
| #if IMPLEMENT_SPI_INTERFACE_SELECTION | |||
| #if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) | |||
| 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. | |||
| * | |||
| * \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. | |||
| */ | |||
| 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] divisor SPI divisor. | |||
| * \param[in] settings SPI speed, mode, and bit order. | |||
| * \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: | |||
| 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: | |||
| /** 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] divisor SPI divisor. | |||
| * \param[in] spiSettings ignored for software SPI.. | |||
| * \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: | |||
| 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> | |||
| class SdFatSoftSpi : public SdFatBase { | |||
| class SdFatSoftSpiEX : public SdFileSystem<SdBlockDriverEX> { | |||
| public: | |||
| /** Initialize SD card and file system. | |||
| * | |||
| * \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. | |||
| */ | |||
| 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. | |||
| */ | |||
| 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: | |||
| SdSpiSoft<MisoPin, MosiPin, SckPin> m_spi; | |||
| SdFatSpiDriver m_spi; | |||
| }; | |||
| #endif /// SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) | |||
| #endif // SdFat_h | |||
| @@ -49,50 +49,28 @@ | |||
| #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 | |||
| @@ -104,19 +82,14 @@ uint8_t const SOFT_SPI_SCK_PIN = 13; | |||
| /** | |||
| * 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 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * 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. | |||
| * | |||
| @@ -162,16 +135,6 @@ uint8_t const SOFT_SPI_SCK_PIN = 13; | |||
| */ | |||
| #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 | |||
| * for FAT table entries. This improves performance for large writes | |||
| @@ -197,42 +160,25 @@ const uint8_t SPI_SCK_INIT_DIVISOR = 128; | |||
| /** | |||
| * 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(__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 | |||
| #else // SD_HAS_CUSTOM_SPI | |||
| // Use standard SPI library. | |||
| #define SD_HAS_CUSTOM_SPI 0 | |||
| #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 | |||
| @@ -1,41 +0,0 @@ | |||
| /* 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 | |||
| @@ -1,35 +0,0 @@ | |||
| /* 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 | |||
| @@ -33,157 +33,161 @@ | |||
| //------------------------------------------------------------------------------ | |||
| // SD card errors | |||
| /** 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*/ | |||
| 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) */ | |||
| 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) */ | |||
| 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) */ | |||
| 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) */ | |||
| uint8_t const SD_CARD_ERROR_CMD24 = 0X6; | |||
| const uint8_t SD_CARD_ERROR_CMD24 = 0X6; | |||
| /** 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) */ | |||
| uint8_t const SD_CARD_ERROR_CMD58 = 0X8; | |||
| const uint8_t SD_CARD_ERROR_CMD58 = 0X8; | |||
| /** 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 */ | |||
| uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; | |||
| const uint8_t SD_CARD_ERROR_ACMD41 = 0XA; | |||
| /** 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 */ | |||
| uint8_t const SD_CARD_ERROR_ERASE = 0XC; | |||
| const uint8_t SD_CARD_ERROR_ERASE = 0XC; | |||
| /** 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 */ | |||
| 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 */ | |||
| uint8_t const SD_CARD_ERROR_READ = 0XF; | |||
| const uint8_t SD_CARD_ERROR_READ = 0XF; | |||
| /** 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 */ | |||
| 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 */ | |||
| 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 */ | |||
| 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 */ | |||
| 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 */ | |||
| uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||
| const uint8_t SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||
| /** 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 */ | |||
| uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18; | |||
| const uint8_t SD_CARD_ERROR_SCK_RATE = 0X18; | |||
| /** 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) */ | |||
| uint8_t const SD_CARD_ERROR_CMD59 = 0X1A; | |||
| const uint8_t SD_CARD_ERROR_CMD59 = 0X1A; | |||
| /** invalid read CRC */ | |||
| uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B; | |||
| const uint8_t SD_CARD_ERROR_READ_CRC = 0X1B; | |||
| /** 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 | |||
| /** 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 */ | |||
| uint8_t const SD_CARD_TYPE_SD2 = 2; | |||
| const uint8_t SD_CARD_TYPE_SD2 = 2; | |||
| /** 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 | |||
| /** 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 */ | |||
| uint8_t const SPI_DIV3_SPEED = 3; | |||
| #define SPI_DIV3_SPEED SD_SCK_HZ(F_CPU/3) | |||
| /** 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 */ | |||
| uint8_t const SPI_DIV6_SPEED = 6; | |||
| #define SPI_DIV6_SPEED SD_SCK_HZ(F_CPU/6) | |||
| /** 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. */ | |||
| uint8_t const SPI_EIGHTH_SPEED = 16; | |||
| #define SPI_EIGHTH_SPEED SD_SCK_HZ(F_CPU/16) | |||
| /** 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 | |||
| /** init timeout ms */ | |||
| unsigned const SD_INIT_TIMEOUT = 2000; | |||
| const uint16_t SD_INIT_TIMEOUT = 2000; | |||
| /** erase timeout ms */ | |||
| unsigned const SD_ERASE_TIMEOUT = 10000; | |||
| const uint16_t SD_ERASE_TIMEOUT = 10000; | |||
| /** read timeout ms */ | |||
| unsigned const SD_READ_TIMEOUT = 300; | |||
| const uint16_t SD_READ_TIMEOUT = 300; | |||
| /** write time out ms */ | |||
| unsigned const SD_WRITE_TIMEOUT = 600; | |||
| const uint16_t SD_WRITE_TIMEOUT = 600; | |||
| //------------------------------------------------------------------------------ | |||
| // SD card commands | |||
| /** 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 */ | |||
| uint8_t const CMD6 = 0X06; | |||
| const uint8_t CMD6 = 0X06; | |||
| /** 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) */ | |||
| uint8_t const CMD9 = 0X09; | |||
| const uint8_t CMD9 = 0X09; | |||
| /** 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 */ | |||
| uint8_t const CMD12 = 0X0C; | |||
| const uint8_t CMD12 = 0X0C; | |||
| /** 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 */ | |||
| uint8_t const CMD17 = 0X11; | |||
| const uint8_t CMD17 = 0X11; | |||
| /** 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 */ | |||
| uint8_t const CMD24 = 0X18; | |||
| const uint8_t CMD24 = 0X18; | |||
| /** 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 */ | |||
| uint8_t const CMD32 = 0X20; | |||
| const uint8_t CMD32 = 0X20; | |||
| /** ERASE_WR_BLK_END - sets the address of the last block of the continuous | |||
| range to be erased*/ | |||
| uint8_t const CMD33 = 0X21; | |||
| const uint8_t CMD33 = 0X21; | |||
| /** ERASE - erase all previously selected blocks */ | |||
| uint8_t const CMD38 = 0X26; | |||
| const uint8_t CMD38 = 0X26; | |||
| /** 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 */ | |||
| uint8_t const CMD58 = 0X3A; | |||
| const uint8_t CMD58 = 0X3A; | |||
| /** 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 | |||
| pre-erased before writing */ | |||
| uint8_t const ACMD23 = 0X17; | |||
| const uint8_t ACMD23 = 0X17; | |||
| /** SD_SEND_OP_COMD - Sends host capacity support information and | |||
| activates the card's initialization process */ | |||
| uint8_t const ACMD41 = 0X29; | |||
| const uint8_t ACMD41 = 0X29; | |||
| //============================================================================== | |||
| /** 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 */ | |||
| uint8_t const R1_IDLE_STATE = 0X01; | |||
| const uint8_t R1_IDLE_STATE = 0X01; | |||
| /** 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*/ | |||
| uint8_t const DATA_START_BLOCK = 0XFE; | |||
| const uint8_t DATA_START_BLOCK = 0XFE; | |||
| /** 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*/ | |||
| uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC; | |||
| const uint8_t WRITE_MULTIPLE_TOKEN = 0XFC; | |||
| /** 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 */ | |||
| uint8_t const DATA_RES_ACCEPTED = 0X05; | |||
| const uint8_t DATA_RES_ACCEPTED = 0X05; | |||
| //============================================================================== | |||
| /** | |||
| * \class CID | |||
| @@ -1,411 +0,0 @@ | |||
| /* 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 | |||
| @@ -0,0 +1,75 @@ | |||
| /* 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 | |||
| @@ -18,7 +18,6 @@ | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include "SdSpiCard.h" | |||
| #include "SdSpi.h" | |||
| // debug trace macro | |||
| #define SD_TRACE(m, b) | |||
| // #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); | |||
| @@ -44,7 +43,7 @@ static uint8_t CRC7(const uint8_t* data, uint8_t n) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| #if USE_SD_CRC == 1 | |||
| // slower CRC-CCITT | |||
| // Shift based CRC-CCITT | |||
| // uses the x^16,x^12,x^5,x^1 polynomial. | |||
| static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { | |||
| uint16_t crc = 0; | |||
| @@ -59,7 +58,7 @@ static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { | |||
| } | |||
| #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. | |||
| #ifdef __AVR__ | |||
| static const uint16_t crctab[] PROGMEM = { | |||
| @@ -115,29 +114,24 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) { | |||
| //============================================================================== | |||
| // 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_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; | |||
| // 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. | |||
| spiUnselect(); | |||
| for (uint8_t i = 0; i < 10; i++) { | |||
| spiSend(0XFF); | |||
| } | |||
| spiEndTransaction(); | |||
| spiSelect(); | |||
| // command to go idle in SPI mode | |||
| 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); | |||
| goto fail; | |||
| } | |||
| @@ -161,7 +155,7 @@ bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
| type(SD_CARD_TYPE_SD2); | |||
| break; | |||
| } | |||
| if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||
| if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||
| error(SD_CARD_ERROR_CMD8); | |||
| goto fail; | |||
| } | |||
| @@ -171,7 +165,7 @@ bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
| while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { | |||
| // check for timeout | |||
| if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||
| if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||
| error(SD_CARD_ERROR_ACMD41); | |||
| goto fail; | |||
| } | |||
| @@ -190,43 +184,43 @@ bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
| spiReceive(); | |||
| } | |||
| } | |||
| chipSelectHigh(); | |||
| m_sckDivisor = sckDivisor; | |||
| spiStop(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // send command and return error code. Return zero for OK | |||
| uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { | |||
| // select card | |||
| if (!m_selected) { | |||
| chipSelectLow(); | |||
| if (!m_spiActive) { | |||
| spiStart(); | |||
| } | |||
| // wait if busy | |||
| waitNotBusy(SD_WRITE_TIMEOUT); | |||
| uint8_t *pa = reinterpret_cast<uint8_t *>(&arg); | |||
| #if USE_SD_CRC | |||
| // 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 | |||
| for (uint8_t k = 0; k < 6; k++) { | |||
| spiSend(d[k]); | |||
| } | |||
| spiSend(buf, 6); | |||
| #else // USE_SD_CRC | |||
| // send command | |||
| spiSend(cmd | 0x40); | |||
| // send argument | |||
| uint8_t *pa = reinterpret_cast<uint8_t *>(&arg); | |||
| for (int8_t i = 3; i >= 0; i--) { | |||
| spiSend(pa[i]); | |||
| } | |||
| @@ -268,35 +262,6 @@ uint32_t SdSpiCard::cardSize() { | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| 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) { | |||
| csd_t csd; | |||
| if (!readCSD(&csd)) { | |||
| @@ -326,11 +291,11 @@ bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | |||
| error(SD_CARD_ERROR_ERASE_TIMEOUT); | |||
| goto fail; | |||
| } | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -341,21 +306,33 @@ bool SdSpiCard::eraseSingleBlockEnable() { | |||
| //------------------------------------------------------------------------------ | |||
| bool SdSpiCard::isBusy() { | |||
| bool rtn = true; | |||
| bool selected = m_selected; | |||
| chipSelectLow(); | |||
| bool spiActive = m_spiActive; | |||
| if (!spiActive) { | |||
| spiStart(); | |||
| } | |||
| for (uint8_t i = 0; i < 8; i++) { | |||
| if (0XFF == spiReceive()) { | |||
| rtn = false; | |||
| break; | |||
| } | |||
| } | |||
| if (!selected) { | |||
| chipSelectHigh(); | |||
| if (!spiActive) { | |||
| spiStop(); | |||
| } | |||
| 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) { | |||
| SD_TRACE("RB", blockNumber); | |||
| // use address if not SDHC card | |||
| @@ -369,11 +346,11 @@ bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { | |||
| if (!readData(dst, 512)) { | |||
| goto fail; | |||
| } | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -390,15 +367,7 @@ bool SdSpiCard::readBlocks(uint32_t block, uint8_t* dst, size_t count) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| 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) { | |||
| @@ -406,9 +375,9 @@ bool SdSpiCard::readData(uint8_t* dst, size_t count) { | |||
| uint16_t crc; | |||
| #endif // USE_SD_CRC | |||
| // wait for start block token | |||
| unsigned t0 = millis(); | |||
| uint16_t t0 = curTimeMS(); | |||
| while ((m_status = spiReceive()) == 0XFF) { | |||
| if (((unsigned)millis() - t0) > SD_READ_TIMEOUT) { | |||
| if (isTimedOut(t0, SD_READ_TIMEOUT)) { | |||
| error(SD_CARD_ERROR_READ_TIMEOUT); | |||
| goto fail; | |||
| } | |||
| @@ -438,7 +407,7 @@ bool SdSpiCard::readData(uint8_t* dst, size_t count) { | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -452,11 +421,11 @@ bool SdSpiCard::readOCR(uint32_t* ocr) { | |||
| p[3 - i] = spiReceive(); | |||
| } | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -467,10 +436,14 @@ bool SdSpiCard::readRegister(uint8_t cmd, void* buf) { | |||
| error(SD_CARD_ERROR_READ_REG); | |||
| goto fail; | |||
| } | |||
| return readData(dst, 16); | |||
| if (!readData(dst, 16)) { | |||
| goto fail; | |||
| } | |||
| spiStop(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -483,67 +456,81 @@ bool SdSpiCard::readStart(uint32_t blockNumber) { | |||
| error(SD_CARD_ERROR_CMD18); | |||
| goto fail; | |||
| } | |||
| // spiStop(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| 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; | |||
| } | |||
| chipSelectHigh(); | |||
| if (!readData(status, 64)) { | |||
| goto fail; | |||
| } | |||
| spiStop(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| 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; | |||
| } | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| 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 | |||
| 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) { | |||
| if (((unsigned)millis() - t0) >= timeoutMillis) { | |||
| goto fail; | |||
| if (isTimedOut(t0, timeoutMS)) { | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| fail: | |||
| return false; | |||
| #endif // WDT_YIELD_TIME_MICROS | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | |||
| @@ -574,16 +561,16 @@ bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | |||
| } | |||
| #endif // CHECK_PROGRAMMING | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { | |||
| if (!writeStart(block, count)) { | |||
| if (!writeStart(block)) { | |||
| goto fail; | |||
| } | |||
| for (size_t b = 0; b < count; b++, src += 512) { | |||
| @@ -594,13 +581,11 @@ bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { | |||
| return writeStop(); | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdSpiCard::writeData(const uint8_t* src) { | |||
| bool selected = m_selected; | |||
| chipSelectLow(); | |||
| // wait for previous write to finish | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||
| error(SD_CARD_ERROR_WRITE_TIMEOUT); | |||
| @@ -609,13 +594,10 @@ bool SdSpiCard::writeData(const uint8_t* src) { | |||
| if (!writeData(WRITE_MULTIPLE_TOKEN, src)) { | |||
| goto fail; | |||
| } | |||
| if (!selected) { | |||
| chipSelectHigh(); | |||
| } | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -639,7 +621,23 @@ bool SdSpiCard::writeData(uint8_t token, const uint8_t* src) { | |||
| return true; | |||
| 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; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -661,24 +659,20 @@ bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) { | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdSpiCard::writeStop() { | |||
| chipSelectLow(); | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||
| goto fail; | |||
| } | |||
| spiSend(STOP_TRAN_TOKEN); | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||
| goto fail; | |||
| } | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return true; | |||
| fail: | |||
| error(SD_CARD_ERROR_STOP_TRAN); | |||
| chipSelectHigh(); | |||
| spiStop(); | |||
| return false; | |||
| } | |||
| @@ -23,10 +23,10 @@ | |||
| * \file | |||
| * \brief SdSpiCard class for V2 SD/SDHC cards | |||
| */ | |||
| #include "SystemInclude.h" | |||
| #include "SdFatConfig.h" | |||
| #include <stddef.h> | |||
| #include "SysCall.h" | |||
| #include "SdInfo.h" | |||
| #include "SdSpi.h" | |||
| #include "SdSpiDriver.h" | |||
| //============================================================================== | |||
| /** | |||
| * \class SdSpiCard | |||
| @@ -34,23 +34,13 @@ | |||
| */ | |||
| class SdSpiCard { | |||
| 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. */ | |||
| 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. | |||
| * \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. | |||
| */ | |||
| 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. | |||
| * | |||
| @@ -58,16 +48,6 @@ class SdSpiCard { | |||
| * or zero if an error occurs. | |||
| */ | |||
| 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. | |||
| * | |||
| * \param[in] firstBlock The address of the first block in the range. | |||
| @@ -178,40 +158,18 @@ class SdSpiCard { | |||
| * the value false is returned for failure. | |||
| */ | |||
| 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. | |||
| * | |||
| * \return The value true is returned for success and | |||
| * the value false is returned for failure. | |||
| */ | |||
| bool readStop(); | |||
| /** Return SCK divisor. | |||
| * | |||
| * \return Requested SCK divisor. | |||
| */ | |||
| uint8_t sckDivisor() { | |||
| return m_sckDivisor; | |||
| } | |||
| /** \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 0 - SD V1, 1 - SD V2, or 3 - SDHC. | |||
| */ | |||
| @@ -244,6 +202,18 @@ class SdSpiCard { | |||
| */ | |||
| bool writeData(const uint8_t* src); | |||
| /** 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] eraseCount The number of blocks to be pre-erased. | |||
| @@ -261,6 +231,10 @@ class SdSpiCard { | |||
| * the value false is returned for failure. | |||
| */ | |||
| bool writeStop(); | |||
| /** Set CS low and activate the card. */ | |||
| void spiStart(); | |||
| /** Set CS high and deactivate the card. */ | |||
| void spiStop(); | |||
| private: | |||
| // private functions | |||
| @@ -269,74 +243,48 @@ class SdSpiCard { | |||
| return cardCommand(cmd, 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 readRegister(uint8_t cmd, void* buf); | |||
| void type(uint8_t value) { | |||
| m_type = value; | |||
| } | |||
| bool waitNotBusy(uint16_t timeoutMillis); | |||
| bool waitNotBusy(uint16_t timeoutMS); | |||
| 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() { | |||
| return m_spi->receive(); | |||
| return m_spiDriver->receive(); | |||
| } | |||
| 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) { | |||
| m_spi->send(data); | |||
| m_spiDriver->send(data); | |||
| } | |||
| 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_sckDivisor; | |||
| SdSpiDriver *m_spiDriver; | |||
| bool m_spiActive; | |||
| uint8_t m_status; | |||
| uint8_t m_type; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| * \class Sd2Card | |||
| * \brief Raw access to SD and SDHC card using default SPI library. | |||
| */ | |||
| class Sd2Card : public SdSpiCard { | |||
| public: | |||
| /** Initialize the SD card. | |||
| * \param[in] chipSelectPin SD chip select pin. | |||
| * \param[in] sckDivisor SPI clock divisor. | |||
| * \return true for success else false. | |||
| */ | |||
| bool begin(uint8_t chipSelectPin = SS, uint8_t sckDivisor = 2) { | |||
| return SdSpiCard::begin(&m_spi, chipSelectPin, sckDivisor); | |||
| } | |||
| /** Initialize the SD card. Obsolete form. | |||
| * \param[in] chipSelectPin SD chip select pin. | |||
| * \param[in] sckDivisor SPI clock divisor. | |||
| * \return true for success else false. | |||
| */ | |||
| bool init(uint8_t sckDivisor = 2, uint8_t chipSelectPin = SS) { | |||
| return begin(chipSelectPin, sckDivisor); | |||
| } | |||
| private: | |||
| bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS, | |||
| uint8_t sckDivisor = SPI_FULL_SPEED) { | |||
| (void)spi; | |||
| (void)chipSelectPin; | |||
| (void)sckDivisor; | |||
| return false; | |||
| } | |||
| SpiDefault_t m_spi; | |||
| }; | |||
| #endif // SpiCard_h | |||
| @@ -1,110 +0,0 @@ | |||
| /* 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) | |||
| @@ -0,0 +1,359 @@ | |||
| /* 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 | |||
| @@ -31,10 +31,22 @@ | |||
| #else // defined(ARDUINO) | |||
| #error "Unknown system" | |||
| #endif // defined(ARDUINO) | |||
| //----------------------------------------------------------------------------- | |||
| #ifdef ESP8266 | |||
| // undefine F macro if ESP8266. | |||
| #undef F | |||
| #endif // ESP8266 | |||
| //----------------------------------------------------------------------------- | |||
| #ifndef F | |||
| /** Define macro for strings stored in flash. */ | |||
| #define F(str) (str) | |||
| #endif // F | |||
| //----------------------------------------------------------------------------- | |||
| /** \return the time in milliseconds. */ | |||
| inline uint16_t curTimeMS() { | |||
| return millis(); | |||
| } | |||
| //----------------------------------------------------------------------------- | |||
| /** | |||
| * \class SysCall | |||
| * \brief SysCall - Class to wrap system calls. | |||
| @@ -51,7 +63,12 @@ class SysCall { | |||
| 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() { | |||
| // Use the external Arduino yield() function. | |||
| ::yield(); | |||
| @@ -60,8 +77,7 @@ inline void SysCall::yield() { | |||
| inline void SysCall::yield() { | |||
| Particle.process(); | |||
| } | |||
| #else // defined(ARDUINO) | |||
| #else // ESP8266 | |||
| inline void SysCall::yield() {} | |||
| #endif // defined(ARDUINO) | |||
| #endif // ESP8266 | |||
| #endif // SysCall_h | |||
| @@ -1,29 +0,0 @@ | |||
| /* 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 | |||