#ifndef UserDataType_h | |||||
#define UserDataType_h | |||||
const uint8_t ACCEL_DIM = 3; | |||||
struct data_t { | |||||
unsigned long time; | |||||
int16_t accel[ACCEL_DIM]; | |||||
}; | |||||
#endif // UserDataType_h |
This is a beta version so there may be bugs and compatibility | |||||
problems. | |||||
### Warning: This version has major changes so it may be unstable. | |||||
Please report problems to the email address listed in the | |||||
"Bugs and Comments" section of the html documentation. | |||||
Recent versions of the Arduino IDE have bugs that may cause SdFat-beta to crash. | |||||
https://forum.arduino.cc/index.php?topic=419264.0 | |||||
SdFat-beta was tested using with Arduino AVR boards 1.6.11. | |||||
If you are using IDE 1.6.11 you must also install AVR boards 1.6.11, not | |||||
1.6.12 or 1.6.13. | |||||
If you are Arduino IDE 1.6.11, do Tools > Board > Boards Manager > Arduino AVR Boards > 1.6.11 > Install > Close. | |||||
The SPI divisor has been replaced by SPISettings. | |||||
There are two new classes, SdFatEX and SdFatSoftSpiEX. | |||||
Please read changes.txt and the html documentation for more information. | |||||
Please report problems as issues. | |||||
The Arduino SdFat library provides read/write access to FAT16/FAT32 | The Arduino SdFat library provides read/write access to FAT16/FAT32 | ||||
file systems on SD/SDHC flash cards. | file systems on SD/SDHC flash cards. | ||||
Please read the html documentation for this library. Start with | Please read the html documentation for this library. Start with | ||||
html/index.html and read the Main Page. Next go to the Classes tab and | html/index.html and read the Main Page. Next go to the Classes tab and | ||||
read the documentation for the classes SdFat, SdBaseFile, SdFile, File, | |||||
StdioStream, ifstream, ofstream, and others. | |||||
read the documentation for the classes SdFat, SdFatEX, SdBaseFile, | |||||
SdFile, File, StdioStream, ifstream, ofstream, and others. | |||||
Please continue by reading the html documentation. | Please continue by reading the html documentation. | ||||
Updated 23 Jan 2016 | |||||
Updated 19 Aug 2016 |
The %SdFat library supports Long %File Names or short 8.3 names. | The %SdFat library supports Long %File Names or short 8.3 names. | ||||
Edit the SdFatConfig.h file to select short or long file names. | Edit the SdFatConfig.h file to select short or long file names. | ||||
The main classes in %SdFat are SdFat, SdFatSoftSpi, SdFatLibSpi, | |||||
The main classes in %SdFat are SdFat, SdFatEX, SdFatSoftSpi, SdFatSoftSpiEX, | |||||
SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream, | SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream, | ||||
and \ref ofstream. | and \ref ofstream. | ||||
The SdFat, SdFatLibSpi, and SdFatSoftSpi classes maintain a FAT volume, | |||||
a current working directory, and simplifies initialization of other classes. | |||||
The SdFat class uses a fast custom hardware SPI implementation. The | |||||
SdFatLibSpi class uses the standard Arduino SPI library. The SdFatSoftSpi | |||||
class uses software SPI. | |||||
The SdFat, SdFatEX, SdFatSoftSpi and SdFatSoftSpiEX classes maintain a | |||||
FAT volume, a current working directory, and simplify initialization | |||||
of other classes. The SdFat and SdFatEX classes uses a fast custom hardware SPI | |||||
implementation. The SdFatSoftSpi and SdFatSoftSpiEX classes uses software SPI. | |||||
the SdFatEX and SdFatSoftSpiEX use extended multi-block I/O for enhanced | |||||
performance. These classes must have exclusive use of the SPI bus. | |||||
The SdBaseFile class provides basic file access functions such as open(), | The SdBaseFile class provides basic file access functions such as open(), | ||||
binary read(), binary write(), close(), remove(), and sync(). SdBaseFile | binary read(), binary write(), close(), remove(), and sync(). SdBaseFile | ||||
Opening Long %File Names can be slower than opening Short %File Names. | Opening Long %File Names can be slower than opening Short %File Names. | ||||
Data read and write performance is not changed by the type of %File Name. | Data read and write performance is not changed by the type of %File Name. | ||||
Set SD_SPI_CONFIGURATION to enable various SPI options. The SdFatSoftSpi | |||||
and SdFatLibSpi classes can be enabled. SdFatLibSpi uses the standard | |||||
Arduino SPI library and SdFatSoftSpi uses software SPI. | |||||
If the symbol ENABLE_EXTENDED_TRANSFER_CLASS is nonzero, the class SdFatEX | |||||
will be defined. If the symbol ENABLE_SOFTWARE_SPI_CLASS is also nonzero, | |||||
the class SdFatSoftSpiEX will be defined. | |||||
These classes used extended multi-block SD I/O for better performance. | |||||
the SPI bus may not be shared with other devices in this mode. | |||||
Set USE_STANDARD_SPI_LIBRARY and ENABLE_SOFTWARE_SPI_CLASS to | |||||
enable various SPI options. set USE_STANDARD_SPI_LIBRARY to use the standard | |||||
Arduino SPI library. set ENABLE_SOFTWARE_SPI_CLASS to enable the SdFatSoftSpi | |||||
class which uses software SPI. | |||||
To enable SD card CRC checking set USE_SD_CRC nonzero. | To enable SD card CRC checking set USE_SD_CRC nonzero. | ||||
Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. | Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. | ||||
FAT12 has not been well tested and requires additional flash. | FAT12 has not been well tested and requires additional flash. | ||||
Set ENABLE_SPI_TRANSACTIONS nonzero to enable the SPI transaction feature | |||||
of the standard Arduino SPI library. You must include SPI.h in your | |||||
programs when ENABLE_SPI_TRANSACTIONS is nonzero. | |||||
\section SDPath Paths and Working Directories | \section SDPath Paths and Working Directories | ||||
Relative paths in SdFat are resolved in a manner similar to Windows. | Relative paths in SdFat are resolved in a manner similar to Windows. | ||||
LongFileName - Example use of openNext, printName, and open by index. | LongFileName - Example use of openNext, printName, and open by index. | ||||
LowLatencyLogger - A modifiable data logger for higher data rates. | |||||
LowLatencyLogger - A data logger for higher data rates. ADC version. | |||||
LowLatencyLoggerADXL345 - A data logger for higher data rates. ADXL345 SPI. | |||||
LowLatencyLoggerMPU6050 - A data logger for higher data rates. MPU6050 I2C. | |||||
OpenNext - Open all files in the root dir and print their filename. | OpenNext - Open all files in the root dir and print their filename. | ||||
RawWrite - A test of raw write functions for contiguous files. | RawWrite - A test of raw write functions for contiguous files. | ||||
readCSV - Read a comma-separated value file using iostream extractors. | |||||
ReadCsv - Function to read a CSV text file one field at a time. | |||||
ReadCsvArray - Read a two dimensional array from a CSV file. | |||||
ReadCsvStream - Read a comma-separated value file using iostream extractors. | |||||
ReadCsvFields - Function to read a CSV text file one field at a time. | |||||
ReadCsvArray - Read a two dimensional array from a CSV file. | |||||
ReadWriteSdFat - SdFat version of Arduino SD ReadWrite example. | |||||
ReadWrite - Compatibility test of Arduino SD ReadWrite example. | |||||
rename - A demo of SdFat::rename(old, new) and SdFile::rename(dirFile, newPath). | rename - A demo of SdFat::rename(old, new) and SdFile::rename(dirFile, newPath). | ||||
StdioBench - Demo and test of stdio style stream. | StdioBench - Demo and test of stdio style stream. | ||||
StreamParseInt - Demo of the SD.h API and the File class parseInt() function. | |||||
ThreeCards - Demonstrate simultaneous use of SdFat, SdFatLibSpi, SdFatSoftSpi. | |||||
Timestamp - Sets file create, modify, and access timestamps. | Timestamp - Sets file create, modify, and access timestamps. | ||||
TwoCards - Example using two SD cards. | TwoCards - Example using two SD cards. |
cout << now << endl; | cout << now << endl; | ||||
#endif // USE_DS1307 | #endif // USE_DS1307 | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
#include <SPI.h> | #include <SPI.h> | ||||
#include "SdFat.h" | #include "SdFat.h" | ||||
const uint8_t chipSelect = SS; | |||||
SdFat sd; | SdFat sd; | ||||
SdFile file; | SdFile file; | ||||
while (!Serial.available()) { | while (!Serial.available()) { | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
if (!sd.begin()) { | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
Serial.println("begin failed"); | Serial.println("begin failed"); | ||||
return; | return; | ||||
} | } |
SdFat SD; | SdFat SD; | ||||
// SD card chip select pin - Modify the value of csPin for your SD module. | // SD card chip select pin - Modify the value of csPin for your SD module. | ||||
const uint8_t csPin = 10; | |||||
const uint8_t csPin = SS; | |||||
File file; | File file; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ |
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
} | } | ||||
delay(400); // catch Due reset problem | delay(400); // catch Due reset problem | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
while (!Serial) { | while (!Serial) { | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
// Error messages stored in flash. | // Error messages stored in flash. | ||||
#define error(msg) errorFlash(F(msg)) | |||||
//------------------------------------------------------------------------------ | |||||
void errorFlash(const __FlashStringHelper* msg) { | |||||
sd.errorPrint(msg); | |||||
fatalBlink(); | |||||
} | |||||
#define error(msg) {sd.errorPrint(F(msg));fatalBlink();} | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// | // | ||||
void fatalBlink() { | void fatalBlink() { | ||||
// Create new file. | // Create new file. | ||||
Serial.println(F("Creating new file")); | Serial.println(F("Creating new file")); | ||||
binFile.close(); | binFile.close(); | ||||
if (!binFile.createContiguous(sd.vwd(), | |||||
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
error("createContiguous failed"); | error("createContiguous failed"); | ||||
} | } | ||||
// Get the address of the file on the SD. | // Get the address of the file on the SD. | ||||
Serial.print(F("FreeStack: ")); | Serial.print(F("FreeStack: ")); | ||||
Serial.println(FreeStack()); | Serial.println(FreeStack()); | ||||
// initialize file system. | |||||
if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
sd.initErrorPrint(); | sd.initErrorPrint(); | ||||
fatalBlink(); | fatalBlink(); | ||||
} | } |
"You can use test files located in\r\n" | "You can use test files located in\r\n" | ||||
"SdFat/examples/LongFileName/testFiles")); | "SdFat/examples/LongFileName/testFiles")); | ||||
if (!sd.begin(SD_CS_PIN)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
Serial.print(F("FreeStack: ")); | Serial.print(F("FreeStack: ")); |
* | * | ||||
* If your SD card has a long write latency, it may be necessary to use | * If your SD card has a long write latency, it may be necessary to use | ||||
* slower sample rates. Using a Mega Arduino helps overcome latency | * slower sample rates. Using a Mega Arduino helps overcome latency | ||||
* problems since 13 512 byte buffers will be used. | |||||
* problems since 12 512 byte buffers will be used. | |||||
* | * | ||||
* Data is written to the file using a SD multiple block write command. | * Data is written to the file using a SD multiple block write command. | ||||
*/ | */ | ||||
#include <SPI.h> | #include <SPI.h> | ||||
#include "SdFat.h" | #include "SdFat.h" | ||||
#include "FreeStack.h" | #include "FreeStack.h" | ||||
#include "UserDataType.h" // Edit this include file to change data_t. | |||||
//------------------------------------------------------------------------------ | |||||
// Set useSharedSpi true for use of an SPI sensor. | |||||
const bool useSharedSpi = false; | |||||
// File start time in micros. | |||||
uint32_t startMicros; | |||||
//------------------------------------------------------------------------------ | |||||
// User data functions. Modify these functions for your data items. | |||||
// Acquire a data record. | |||||
void acquireData(data_t* data) { | |||||
data->time = micros(); | |||||
for (int i = 0; i < ADC_DIM; i++) { | |||||
data->adc[i] = analogRead(i); | |||||
} | |||||
} | |||||
// Print a data record. | |||||
void printData(Print* pr, data_t* data) { | |||||
pr->print(data->time - startMicros); | |||||
for (int i = 0; i < ADC_DIM; i++) { | |||||
pr->write(','); | |||||
pr->print(data->adc[i]); | |||||
} | |||||
pr->println(); | |||||
} | |||||
#include "UserTypes.h" | |||||
// Print data header. | |||||
void printHeader(Print* pr) { | |||||
pr->print(F("time")); | |||||
for (int i = 0; i < ADC_DIM; i++) { | |||||
pr->print(F(",adc")); | |||||
pr->print(i); | |||||
} | |||||
pr->println(); | |||||
} | |||||
#ifdef __AVR_ATmega328P__ | |||||
#include "MinimumSerial.h" | |||||
MinimumSerial MinSerial; | |||||
#define Serial MinSerial | |||||
#endif // __AVR_ATmega328P__ | |||||
//============================================================================== | //============================================================================== | ||||
// Start of configuration constants. | // Start of configuration constants. | ||||
//============================================================================== | //============================================================================== | ||||
// Abort run on an overrun. Data before the overrun will be saved. | |||||
#define ABORT_ON_OVERRUN 1 | |||||
//------------------------------------------------------------------------------ | |||||
//Interval between data records in microseconds. | //Interval between data records in microseconds. | ||||
const uint32_t LOG_INTERVAL_USEC = 2000; | const uint32_t LOG_INTERVAL_USEC = 2000; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// Set USE_SHARED_SPI non-zero for use of an SPI sensor. | |||||
// May not work for some cards. | |||||
#ifndef USE_SHARED_SPI | |||||
#define USE_SHARED_SPI 0 | |||||
#endif // USE_SHARED_SPI | |||||
//------------------------------------------------------------------------------ | |||||
// Pin definitions. | // Pin definitions. | ||||
// | // | ||||
// SD chip select pin. | // SD chip select pin. | ||||
const uint8_t SD_CS_PIN = SS; | const uint8_t SD_CS_PIN = SS; | ||||
// | // | ||||
// Digital pin to indicate an error, set to -1 if not used. | // Digital pin to indicate an error, set to -1 if not used. | ||||
// The led blinks for fatal errors. The led goes on solid for SD write | |||||
// overrun errors and logging continues. | |||||
// The led blinks for fatal errors. The led goes on solid for | |||||
// overrun errors and logging continues unless ABORT_ON_OVERRUN | |||||
// is non-zero. | |||||
#ifdef ERROR_LED_PIN | |||||
#undef ERROR_LED_PIN | #undef ERROR_LED_PIN | ||||
#endif // ERROR_LED_PIN | |||||
const int8_t ERROR_LED_PIN = -1; | const int8_t ERROR_LED_PIN = -1; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// File definitions. | // File definitions. | ||||
// This file is flash erased using special SD commands. The file will be | // This file is flash erased using special SD commands. The file will be | ||||
// truncated if logging is stopped early. | // truncated if logging is stopped early. | ||||
const uint32_t FILE_BLOCK_COUNT = 256000; | const uint32_t FILE_BLOCK_COUNT = 256000; | ||||
// log file base name. Must be six characters or less. | |||||
// | |||||
// log file base name if not defined in UserTypes.h | |||||
#ifndef FILE_BASE_NAME | |||||
#define FILE_BASE_NAME "data" | #define FILE_BASE_NAME "data" | ||||
#endif // FILE_BASE_NAME | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// Buffer definitions. | // Buffer definitions. | ||||
// | // | ||||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional | |||||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional | |||||
// buffers. | // buffers. | ||||
// | // | ||||
#ifndef RAMEND | #ifndef RAMEND | ||||
// Assume ARM. Use total of nine 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 8; | |||||
// Assume ARM. Use total of ten 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 10; | |||||
// | // | ||||
#elif RAMEND < 0X8FF | #elif RAMEND < 0X8FF | ||||
#error Too little SRAM | #error Too little SRAM | ||||
// | // | ||||
#elif RAMEND < 0X10FF | #elif RAMEND < 0X10FF | ||||
// Use total of two 512 byte buffers. | // Use total of two 512 byte buffers. | ||||
const uint8_t BUFFER_BLOCK_COUNT = 1; | |||||
const uint8_t BUFFER_BLOCK_COUNT = 2; | |||||
// | // | ||||
#elif RAMEND < 0X20FF | #elif RAMEND < 0X20FF | ||||
// Use total of five 512 byte buffers. | |||||
// Use total of four 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 4; | const uint8_t BUFFER_BLOCK_COUNT = 4; | ||||
// | // | ||||
#else // RAMEND | #else // RAMEND | ||||
// Use total of 13 512 byte buffers. | |||||
// Use total of 12 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 12; | const uint8_t BUFFER_BLOCK_COUNT = 12; | ||||
#endif // RAMEND | #endif // RAMEND | ||||
//============================================================================== | //============================================================================== | ||||
// Size of file base name. Must not be larger than six. | // Size of file base name. Must not be larger than six. | ||||
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | ||||
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7; | |||||
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin"; | |||||
SdFat sd; | SdFat sd; | ||||
SdBaseFile binFile; | SdBaseFile binFile; | ||||
char binName[13] = FILE_BASE_NAME "00.bin"; | |||||
// Number of data records in a block. | // Number of data records in a block. | ||||
const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | ||||
data_t data[DATA_DIM]; | data_t data[DATA_DIM]; | ||||
uint8_t fill[FILL_DIM]; | uint8_t fill[FILL_DIM]; | ||||
}; | }; | ||||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 2; | |||||
block_t* emptyQueue[QUEUE_DIM]; | |||||
uint8_t emptyHead; | |||||
uint8_t emptyTail; | |||||
block_t* fullQueue[QUEUE_DIM]; | |||||
uint8_t fullHead; | |||||
uint8_t fullTail; | |||||
// Advance queue index. | |||||
inline uint8_t queueNext(uint8_t ht) { | |||||
return ht < (QUEUE_DIM - 1) ? ht + 1 : 0; | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
// Error messages stored in flash. | // Error messages stored in flash. | ||||
#define error(msg) errorFlash(F(msg)) | |||||
//------------------------------------------------------------------------------ | |||||
void errorFlash(const __FlashStringHelper* msg) { | |||||
sd.errorPrint(msg); | |||||
fatalBlink(); | |||||
} | |||||
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();} | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// | // | ||||
void fatalBlink() { | void fatalBlink() { | ||||
while (true) { | while (true) { | ||||
SysCall::yield(); | |||||
if (ERROR_LED_PIN >= 0) { | if (ERROR_LED_PIN >= 0) { | ||||
digitalWrite(ERROR_LED_PIN, HIGH); | digitalWrite(ERROR_LED_PIN, HIGH); | ||||
delay(200); | delay(200); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
//============================================================================== | |||||
//------------------------------------------------------------------------------ | |||||
// read data file and check for overruns | |||||
void checkOverrun() { | |||||
bool headerPrinted = false; | |||||
block_t block; | |||||
uint32_t bn = 0; | |||||
if (!binFile.isOpen()) { | |||||
Serial.println(); | |||||
Serial.println(F("No current binary file")); | |||||
return; | |||||
} | |||||
binFile.rewind(); | |||||
Serial.println(); | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
Serial.println(F("Checking overrun errors - type any character to stop")); | |||||
while (binFile.read(&block, 512) == 512) { | |||||
if (block.count == 0) { | |||||
break; | |||||
} | |||||
if (block.overrun) { | |||||
if (!headerPrinted) { | |||||
Serial.println(); | |||||
Serial.println(F("Overruns:")); | |||||
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||||
headerPrinted = true; | |||||
} | |||||
Serial.print(bn); | |||||
Serial.print(','); | |||||
Serial.print(binFile.firstBlock() + bn); | |||||
Serial.print(','); | |||||
Serial.println(block.overrun); | |||||
} | |||||
bn++; | |||||
} | |||||
if (!headerPrinted) { | |||||
Serial.println(F("No errors found")); | |||||
} else { | |||||
Serial.println(F("Done")); | |||||
} | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
// Convert binary file to csv file. | // Convert binary file to csv file. | ||||
void binaryToCsv() { | void binaryToCsv() { | ||||
uint8_t lastPct = 0; | uint8_t lastPct = 0; | ||||
uint32_t t0 = millis(); | uint32_t t0 = millis(); | ||||
uint32_t syncCluster = 0; | uint32_t syncCluster = 0; | ||||
SdFile csvFile; | SdFile csvFile; | ||||
char csvName[13]; | |||||
char csvName[FILE_NAME_DIM]; | |||||
if (!binFile.isOpen()) { | if (!binFile.isOpen()) { | ||||
Serial.println(); | Serial.println(); | ||||
Serial.println(F("No current binary file")); | Serial.println(F("No current binary file")); | ||||
return; | return; | ||||
} | } | ||||
binFile.rewind(); | |||||
Serial.println(); | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
// Create a new csvFile. | // Create a new csvFile. | ||||
strcpy(csvName, binName); | strcpy(csvName, binName); | ||||
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | ||||
if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | ||||
error("open csvFile failed"); | error("open csvFile failed"); | ||||
} | } | ||||
Serial.println(); | |||||
binFile.rewind(); | |||||
Serial.print(F("Writing: ")); | Serial.print(F("Writing: ")); | ||||
Serial.print(csvName); | Serial.print(csvName); | ||||
Serial.println(F(" - type any character to stop")); | Serial.println(F(" - type any character to stop")); | ||||
Serial.print(0.001*(millis() - t0)); | Serial.print(0.001*(millis() - t0)); | ||||
Serial.println(F(" Seconds")); | Serial.println(F(" Seconds")); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | |||||
// read data file and check for overruns | |||||
void checkOverrun() { | |||||
bool headerPrinted = false; | |||||
block_t block; | |||||
//----------------------------------------------------------------------------- | |||||
void createBinFile() { | |||||
// max number of blocks to erase per erase call | |||||
const uint32_t ERASE_SIZE = 262144L; | |||||
uint32_t bgnBlock, endBlock; | uint32_t bgnBlock, endBlock; | ||||
uint32_t bn = 0; | |||||
if (!binFile.isOpen()) { | |||||
Serial.println(); | |||||
Serial.println(F("No current binary file")); | |||||
return; | |||||
Serial.println(); | |||||
while (sd.exists(binName)) { | |||||
if (binName[BASE_NAME_SIZE + 1] != '9') { | |||||
binName[BASE_NAME_SIZE + 1]++; | |||||
} else { | |||||
binName[BASE_NAME_SIZE + 1] = '0'; | |||||
if (binName[BASE_NAME_SIZE] == '9') { | |||||
error("Can't create file name"); | |||||
} | |||||
binName[BASE_NAME_SIZE]++; | |||||
} | |||||
} | |||||
// Delete old tmp file. | |||||
if (sd.exists(TMP_FILE_NAME)) { | |||||
Serial.println(F("Deleting tmp file")); | |||||
if (!sd.remove(TMP_FILE_NAME)) { | |||||
error("Can't remove tmp file"); | |||||
} | |||||
} | |||||
// Create new file. | |||||
Serial.println(F("Creating new file")); | |||||
binFile.close(); | |||||
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
error("createContiguous failed"); | |||||
} | } | ||||
// Get the address of the file on the SD. | |||||
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | ||||
error("contiguousRange failed"); | error("contiguousRange failed"); | ||||
} | } | ||||
binFile.rewind(); | |||||
Serial.println(); | |||||
Serial.println(F("Checking overrun errors - type any character to stop")); | |||||
while (binFile.read(&block, 512) == 512) { | |||||
if (block.count == 0) { | |||||
break; | |||||
// Flash erase all data in the file. | |||||
Serial.println(F("Erasing all data")); | |||||
uint32_t bgnErase = bgnBlock; | |||||
uint32_t endErase; | |||||
while (bgnErase < endBlock) { | |||||
endErase = bgnErase + ERASE_SIZE; | |||||
if (endErase > endBlock) { | |||||
endErase = endBlock; | |||||
} | } | ||||
if (block.overrun) { | |||||
if (!headerPrinted) { | |||||
Serial.println(); | |||||
Serial.println(F("Overruns:")); | |||||
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||||
headerPrinted = true; | |||||
} | |||||
Serial.print(bn); | |||||
Serial.print(','); | |||||
Serial.print(bgnBlock + bn); | |||||
Serial.print(','); | |||||
Serial.println(block.overrun); | |||||
if (!sd.card()->erase(bgnErase, endErase)) { | |||||
error("erase failed"); | |||||
} | } | ||||
bn++; | |||||
} | |||||
if (!headerPrinted) { | |||||
Serial.println(F("No errors found")); | |||||
} else { | |||||
Serial.println(F("Done")); | |||||
bgnErase = endErase + 1; | |||||
} | } | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// log data | // log data | ||||
// max number of blocks to erase per erase call | |||||
uint32_t const ERASE_SIZE = 262144L; | |||||
void logData() { | void logData() { | ||||
uint32_t bgnBlock, endBlock; | |||||
// Allocate extra buffer space. | |||||
block_t block[BUFFER_BLOCK_COUNT]; | |||||
block_t* curBlock = 0; | |||||
Serial.println(); | |||||
// Find unused file name. | |||||
if (BASE_NAME_SIZE > 6) { | |||||
error("FILE_BASE_NAME too long"); | |||||
} | |||||
while (sd.exists(binName)) { | |||||
if (binName[BASE_NAME_SIZE + 1] != '9') { | |||||
binName[BASE_NAME_SIZE + 1]++; | |||||
} else { | |||||
binName[BASE_NAME_SIZE + 1] = '0'; | |||||
if (binName[BASE_NAME_SIZE] == '9') { | |||||
error("Can't create file name"); | |||||
} | |||||
binName[BASE_NAME_SIZE]++; | |||||
createBinFile(); | |||||
recordBinFile(); | |||||
renameBinFile(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void openBinFile() { | |||||
char name[FILE_NAME_DIM]; | |||||
strcpy(name, binName); | |||||
Serial.println(F("\nEnter two digit version")); | |||||
Serial.write(name, BASE_NAME_SIZE); | |||||
for (int i = 0; i < 2; i++) { | |||||
while (!Serial.available()) { | |||||
SysCall::yield(); | |||||
} | } | ||||
} | |||||
// Delete old tmp file. | |||||
if (sd.exists(TMP_FILE_NAME)) { | |||||
Serial.println(F("Deleting tmp file")); | |||||
if (!sd.remove(TMP_FILE_NAME)) { | |||||
error("Can't remove tmp file"); | |||||
char c = Serial.read(); | |||||
Serial.write(c); | |||||
if (c < '0' || c > '9') { | |||||
Serial.println("\nInvalid digit"); | |||||
return; | |||||
} | } | ||||
name[BASE_NAME_SIZE + i] = c; | |||||
} | } | ||||
// Create new file. | |||||
Serial.println(F("Creating new file")); | |||||
binFile.close(); | |||||
if (!binFile.createContiguous(sd.vwd(), | |||||
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
error("createContiguous failed"); | |||||
Serial.println(&name[BASE_NAME_SIZE+2]); | |||||
if (!sd.exists(name)) { | |||||
Serial.println(F("File does not exist")); | |||||
return; | |||||
} | } | ||||
// Get the address of the file on the SD. | |||||
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||||
error("contiguousRange failed"); | |||||
binFile.close(); | |||||
strcpy(binName, name); | |||||
if (!binFile.open(binName, O_READ)) { | |||||
Serial.println(F("open failed")); | |||||
return; | |||||
} | } | ||||
Serial.println(F("File opened")); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void recordBinFile() { | |||||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||||
// Index of last queue location. | |||||
const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||||
// Allocate extra buffer space. | |||||
block_t block[BUFFER_BLOCK_COUNT - 1]; | |||||
block_t* curBlock = 0; | |||||
block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||||
uint8_t emptyTop; | |||||
uint8_t minTop; | |||||
block_t* fullQueue[QUEUE_DIM]; | |||||
uint8_t fullHead = 0; | |||||
uint8_t fullTail = 0; | |||||
// Use SdFat's internal buffer. | // Use SdFat's internal buffer. | ||||
uint8_t* cache = (uint8_t*)sd.vol()->cacheClear(); | |||||
if (cache == 0) { | |||||
emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||||
if (emptyStack[0] == 0) { | |||||
error("cacheClear failed"); | error("cacheClear failed"); | ||||
} | } | ||||
// Flash erase all data in the file. | |||||
Serial.println(F("Erasing all data")); | |||||
uint32_t bgnErase = bgnBlock; | |||||
uint32_t endErase; | |||||
while (bgnErase < endBlock) { | |||||
endErase = bgnErase + ERASE_SIZE; | |||||
if (endErase > endBlock) { | |||||
endErase = endBlock; | |||||
} | |||||
if (!sd.card()->erase(bgnErase, endErase)) { | |||||
error("erase failed"); | |||||
} | |||||
bgnErase = endErase + 1; | |||||
// Put rest of buffers on the empty stack. | |||||
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) { | |||||
emptyStack[i] = &block[i - 1]; | |||||
} | } | ||||
emptyTop = BUFFER_BLOCK_COUNT; | |||||
minTop = BUFFER_BLOCK_COUNT; | |||||
// Start a multiple block write. | // Start a multiple block write. | ||||
if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) { | |||||
error("writeBegin failed"); | |||||
} | |||||
// Set chip select high if other devices use SPI. | |||||
if (useSharedSpi) { | |||||
sd.card()->chipSelectHigh(); | |||||
} | |||||
// Initialize queues. | |||||
emptyHead = emptyTail = 0; | |||||
fullHead = fullTail = 0; | |||||
// Use SdFat buffer for one block. | |||||
emptyQueue[emptyHead] = (block_t*)cache; | |||||
emptyHead = queueNext(emptyHead); | |||||
// Put rest of buffers in the empty queue. | |||||
for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { | |||||
emptyQueue[emptyHead] = &block[i]; | |||||
emptyHead = queueNext(emptyHead); | |||||
if (!sd.card()->writeStart(binFile.firstBlock())) { | |||||
error("writeBStart failed"); | |||||
} | } | ||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
Serial.println(F("Logging - type any character to stop")); | Serial.println(F("Logging - type any character to stop")); | ||||
// Wait for Serial Idle. | |||||
Serial.flush(); | |||||
delay(10); | |||||
bool closeFile = false; | bool closeFile = false; | ||||
uint32_t bn = 0; | |||||
uint32_t t0 = millis(); | |||||
uint32_t t1 = t0; | |||||
uint32_t bn = 0; | |||||
uint32_t maxLatency = 0; | |||||
uint32_t overrun = 0; | uint32_t overrun = 0; | ||||
uint32_t overrunTotal = 0; | uint32_t overrunTotal = 0; | ||||
uint32_t count = 0; | |||||
uint32_t maxDelta = 0; | |||||
uint32_t minDelta = 99999; | |||||
uint32_t maxLatency = 0; | |||||
uint32_t logTime = micros(); | uint32_t logTime = micros(); | ||||
// Set time for first record of file. | |||||
startMicros = logTime + LOG_INTERVAL_USEC; | |||||
while (1) { | |||||
// Time for next data record. | |||||
while(1) { | |||||
// Time for next data record. | |||||
logTime += LOG_INTERVAL_USEC; | logTime += LOG_INTERVAL_USEC; | ||||
if (Serial.available()) { | if (Serial.available()) { | ||||
closeFile = true; | closeFile = true; | ||||
} | |||||
} | |||||
if (closeFile) { | if (closeFile) { | ||||
if (curBlock != 0) { | if (curBlock != 0) { | ||||
// Put buffer in full queue. | // Put buffer in full queue. | ||||
fullQueue[fullHead] = curBlock; | fullQueue[fullHead] = curBlock; | ||||
fullHead = queueNext(fullHead); | |||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
curBlock = 0; | curBlock = 0; | ||||
} | } | ||||
} else { | } else { | ||||
if (curBlock == 0 && emptyTail != emptyHead) { | |||||
curBlock = emptyQueue[emptyTail]; | |||||
emptyTail = queueNext(emptyTail); | |||||
if (curBlock == 0 && emptyTop != 0) { | |||||
curBlock = emptyStack[--emptyTop]; | |||||
if (emptyTop < minTop) { | |||||
minTop = emptyTop; | |||||
} | |||||
curBlock->count = 0; | curBlock->count = 0; | ||||
curBlock->overrun = overrun; | curBlock->overrun = overrun; | ||||
overrun = 0; | overrun = 0; | ||||
} while (delta < 0); | } while (delta < 0); | ||||
if (curBlock == 0) { | if (curBlock == 0) { | ||||
overrun++; | overrun++; | ||||
overrunTotal++; | |||||
if (ERROR_LED_PIN >= 0) { | |||||
digitalWrite(ERROR_LED_PIN, HIGH); | |||||
} | |||||
#if ABORT_ON_OVERRUN | |||||
Serial.println(F("Overrun abort")); | |||||
break; | |||||
#endif // ABORT_ON_OVERRUN | |||||
} else { | } else { | ||||
acquireData(&curBlock->data[curBlock->count++]); | |||||
#if USE_SHARED_SPI | |||||
sd.card()->spiStop(); | |||||
#endif // USE_SHARED_SPI | |||||
acquireData(&curBlock->data[curBlock->count++]); | |||||
#if USE_SHARED_SPI | |||||
sd.card()->spiStart(); | |||||
#endif // USE_SHARED_SPI | |||||
if (curBlock->count == DATA_DIM) { | if (curBlock->count == DATA_DIM) { | ||||
fullQueue[fullHead] = curBlock; | fullQueue[fullHead] = curBlock; | ||||
fullHead = queueNext(fullHead); | |||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
curBlock = 0; | curBlock = 0; | ||||
} | |||||
if ((uint32_t)delta > maxDelta) maxDelta = delta; | |||||
if ((uint32_t)delta < minDelta) minDelta = delta; | |||||
} | |||||
} | } | ||||
} | } | ||||
if (fullHead == fullTail) { | if (fullHead == fullTail) { | ||||
// Exit loop if done. | // Exit loop if done. | ||||
if (closeFile) { | if (closeFile) { | ||||
} else if (!sd.card()->isBusy()) { | } else if (!sd.card()->isBusy()) { | ||||
// Get address of block to write. | // Get address of block to write. | ||||
block_t* pBlock = fullQueue[fullTail]; | block_t* pBlock = fullQueue[fullTail]; | ||||
fullTail = queueNext(fullTail); | |||||
fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0; | |||||
// Write block to SD. | // Write block to SD. | ||||
uint32_t usec = micros(); | uint32_t usec = micros(); | ||||
if (!sd.card()->writeData((uint8_t*)pBlock)) { | if (!sd.card()->writeData((uint8_t*)pBlock)) { | ||||
error("write data failed"); | error("write data failed"); | ||||
} | } | ||||
usec = micros() - usec; | usec = micros() - usec; | ||||
t1 = millis(); | |||||
if (usec > maxLatency) { | if (usec > maxLatency) { | ||||
maxLatency = usec; | maxLatency = usec; | ||||
} | } | ||||
count += pBlock->count; | |||||
// Add overruns and possibly light LED. | |||||
if (pBlock->overrun) { | |||||
overrunTotal += pBlock->overrun; | |||||
if (ERROR_LED_PIN >= 0) { | |||||
digitalWrite(ERROR_LED_PIN, HIGH); | |||||
} | |||||
} | |||||
// Move block to empty queue. | // Move block to empty queue. | ||||
emptyQueue[emptyHead] = pBlock; | |||||
emptyHead = queueNext(emptyHead); | |||||
emptyStack[emptyTop++] = pBlock; | |||||
bn++; | bn++; | ||||
if (bn == FILE_BLOCK_COUNT) { | if (bn == FILE_BLOCK_COUNT) { | ||||
// File full so stop | // File full so stop | ||||
if (!sd.card()->writeStop()) { | if (!sd.card()->writeStop()) { | ||||
error("writeStop failed"); | error("writeStop failed"); | ||||
} | } | ||||
Serial.print(F("Min Free buffers: ")); | |||||
Serial.println(minTop); | |||||
Serial.print(F("Max block write usec: ")); | |||||
Serial.println(maxLatency); | |||||
Serial.print(F("Overruns: ")); | |||||
Serial.println(overrunTotal); | |||||
// Truncate file if recording stopped early. | // Truncate file if recording stopped early. | ||||
if (bn != FILE_BLOCK_COUNT) { | if (bn != FILE_BLOCK_COUNT) { | ||||
Serial.println(F("Truncating file")); | Serial.println(F("Truncating file")); | ||||
error("Can't truncate file"); | error("Can't truncate file"); | ||||
} | } | ||||
} | } | ||||
} | |||||
//----------------------------------------------------------------------------- | |||||
void renameBinFile() { | |||||
if (!binFile.rename(sd.vwd(), binName)) { | if (!binFile.rename(sd.vwd(), binName)) { | ||||
error("Can't rename file"); | error("Can't rename file"); | ||||
} | |||||
} | |||||
Serial.print(F("File renamed: ")); | Serial.print(F("File renamed: ")); | ||||
Serial.println(binName); | Serial.println(binName); | ||||
Serial.print(F("Max block write usec: ")); | |||||
Serial.println(maxLatency); | |||||
Serial.print(F("Record time sec: ")); | |||||
Serial.println(0.001*(t1 - t0), 3); | |||||
Serial.print(minDelta); | |||||
Serial.print(F(" <= jitter microseconds <= ")); | |||||
Serial.println(maxDelta); | |||||
Serial.print(F("Sample count: ")); | |||||
Serial.println(count); | |||||
Serial.print(F("Samples/sec: ")); | |||||
Serial.println((1000.0)*count/(t1-t0)); | |||||
Serial.print(F("Overruns: ")); | |||||
Serial.println(overrunTotal); | |||||
Serial.println(F("Done")); | |||||
Serial.print("File size: "); | |||||
Serial.print(binFile.fileSize()/512); | |||||
Serial.println(F(" blocks")); | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void testSensor() { | |||||
const uint32_t interval = 200000; | |||||
int32_t diff; | |||||
data_t data; | |||||
Serial.println(F("\nTesting - type any character to stop\n")); | |||||
// Wait for Serial Idle. | |||||
delay(1000); | |||||
printHeader(&Serial); | |||||
uint32_t m = micros(); | |||||
while (!Serial.available()) { | |||||
m += interval; | |||||
do { | |||||
diff = m - micros(); | |||||
} while (diff > 0); | |||||
acquireData(&data); | |||||
printData(&Serial, &data); | |||||
} | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void setup(void) { | void setup(void) { | ||||
while (!Serial) { | while (!Serial) { | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
Serial.print(F("FreeStack: ")); | Serial.print(F("FreeStack: ")); | ||||
Serial.println(FreeStack()); | Serial.println(FreeStack()); | ||||
Serial.print(F("Records/block: ")); | Serial.print(F("Records/block: ")); | ||||
if (sizeof(block_t) != 512) { | if (sizeof(block_t) != 512) { | ||||
error("Invalid block size"); | error("Invalid block size"); | ||||
} | } | ||||
// initialize file system. | |||||
if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||||
sd.initErrorPrint(); | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
sd.initErrorPrint(&Serial); | |||||
fatalBlink(); | fatalBlink(); | ||||
} | } | ||||
userSetup(); | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void loop(void) { | void loop(void) { | ||||
} while (Serial.available() && Serial.read() >= 0); | } while (Serial.available() && Serial.read() >= 0); | ||||
Serial.println(); | Serial.println(); | ||||
Serial.println(F("type:")); | Serial.println(F("type:")); | ||||
Serial.println(F("b - open existing bin file")); | |||||
Serial.println(F("c - convert file to csv")); | Serial.println(F("c - convert file to csv")); | ||||
Serial.println(F("d - dump data to Serial")); | Serial.println(F("d - dump data to Serial")); | ||||
Serial.println(F("e - overrun error details")); | Serial.println(F("e - overrun error details")); | ||||
Serial.println(F("l - list files")); | |||||
Serial.println(F("r - record data")); | Serial.println(F("r - record data")); | ||||
Serial.println(F("t - test without logging")); | |||||
while(!Serial.available()) { | while(!Serial.available()) { | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
#if WDT_YIELD_TIME_MICROS | |||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||||
SysCall::halt(); | |||||
#endif | |||||
char c = tolower(Serial.read()); | char c = tolower(Serial.read()); | ||||
// Discard extra Serial data. | // Discard extra Serial data. | ||||
if (ERROR_LED_PIN >= 0) { | if (ERROR_LED_PIN >= 0) { | ||||
digitalWrite(ERROR_LED_PIN, LOW); | digitalWrite(ERROR_LED_PIN, LOW); | ||||
} | } | ||||
if (c == 'c') { | |||||
if (c == 'b') { | |||||
openBinFile(); | |||||
} else if (c == 'c') { | |||||
binaryToCsv(); | binaryToCsv(); | ||||
} else if (c == 'd') { | } else if (c == 'd') { | ||||
dumpData(); | dumpData(); | ||||
} else if (c == 'e') { | } else if (c == 'e') { | ||||
checkOverrun(); | checkOverrun(); | ||||
} else if (c == 'l') { | |||||
Serial.println(F("\nls:")); | |||||
sd.ls(&Serial, LS_SIZE); | |||||
} else if (c == 'r') { | } else if (c == 'r') { | ||||
logData(); | logData(); | ||||
} else if (c == 't') { | |||||
testSensor(); | |||||
} else { | } else { | ||||
Serial.println(F("Invalid entry")); | Serial.println(F("Invalid entry")); | ||||
} | } |
#ifndef UserDataType_h | |||||
#define UserDataType_h | |||||
const uint8_t ADC_DIM = 4; | |||||
struct data_t { | |||||
unsigned long time; | |||||
unsigned short adc[ADC_DIM]; | |||||
}; | |||||
#endif // UserDataType_h |
#include "UserTypes.h" | |||||
// User data functions. Modify these functions for your data items. | |||||
// Start time for data | |||||
uint32_t startTime; | |||||
// Acquire a data record. | |||||
void acquireData(data_t* data) { | |||||
data->time = micros(); | |||||
for (int i = 0; i < ADC_DIM; i++) { | |||||
data->adc[i] = analogRead(i); | |||||
} | |||||
} | |||||
// Print a data record. | |||||
void printData(Print* pr, data_t* data) { | |||||
if (startTime == 0) { | |||||
startTime = data->time; | |||||
} | |||||
pr->print(data->time - startTime); | |||||
for (int i = 0; i < ADC_DIM; i++) { | |||||
pr->write(','); | |||||
pr->print(data->adc[i]); | |||||
} | |||||
pr->println(); | |||||
} | |||||
// Print data header. | |||||
void printHeader(Print* pr) { | |||||
startTime = 0; | |||||
pr->print(F("micros")); | |||||
for (int i = 0; i < ADC_DIM; i++) { | |||||
pr->print(F(",adc")); | |||||
pr->print(i); | |||||
} | |||||
pr->println(); | |||||
} | |||||
// Sensor setup | |||||
void userSetup() { | |||||
} |
#ifndef UserTypes_h | |||||
#define UserTypes_h | |||||
#include "Arduino.h" | |||||
// User data types. Modify for your data items. | |||||
#define FILE_BASE_NAME "adc4pin" | |||||
const uint8_t ADC_DIM = 4; | |||||
struct data_t { | |||||
unsigned long time; | |||||
unsigned short adc[ADC_DIM]; | |||||
}; | |||||
void acquireData(data_t* data); | |||||
void printData(Print* pr, data_t* data); | |||||
void printHeader(Print* pr); | |||||
void userSetup(); | |||||
#endif // UserTypes_h |
* | * | ||||
* If your SD card has a long write latency, it may be necessary to use | * If your SD card has a long write latency, it may be necessary to use | ||||
* slower sample rates. Using a Mega Arduino helps overcome latency | * slower sample rates. Using a Mega Arduino helps overcome latency | ||||
* problems since 13 512 byte buffers will be used. | |||||
* problems since 12 512 byte buffers will be used. | |||||
* | * | ||||
* Data is written to the file using a SD multiple block write command. | * Data is written to the file using a SD multiple block write command. | ||||
*/ | */ | ||||
#include <SPI.h> | #include <SPI.h> | ||||
#include "SdFat.h" | #include "SdFat.h" | ||||
#include "FreeStack.h" | #include "FreeStack.h" | ||||
//------------------------------------------------------------------------------ | |||||
// Set useSharedSpi true for use of an SPI sensor. | |||||
const bool useSharedSpi = true; | |||||
// File start time in micros. | |||||
uint32_t startMicros; | |||||
//------------------------------------------------------------------------------ | |||||
// User data functions. Modify these functions for your data items. | |||||
#include "UserDataType.h" // Edit this include file to change data_t. | |||||
const uint8_t ADXL345_CS = 9; | |||||
const uint8_t POWER_CTL = 0x2D; //Power Control Register | |||||
const uint8_t DATA_FORMAT = 0x31; | |||||
const uint8_t DATAX0 = 0x32; //X-Axis Data 0 | |||||
const uint8_t DATAX1 = 0x33; //X-Axis Data 1 | |||||
const uint8_t DATAY0 = 0x34; //Y-Axis Data 0 | |||||
const uint8_t DATAY1 = 0x35; //Y-Axis Data 1 | |||||
const uint8_t DATAZ0 = 0x36; //Z-Axis Data 0 | |||||
const uint8_t DATAZ1 = 0x37; //Z-Axis Data 1 | |||||
void writeADXL345Register(const uint8_t registerAddress, const uint8_t value) { | |||||
SPI.setDataMode(SPI_MODE3); | |||||
digitalWrite(ADXL345_CS, LOW); | |||||
SPI.transfer(registerAddress); | |||||
SPI.transfer(value); | |||||
digitalWrite(ADXL345_CS, HIGH); | |||||
} | |||||
void setupADXL345() { | |||||
SPI.begin(); | |||||
pinMode(ADXL345_CS, OUTPUT); | |||||
digitalWrite(ADXL345_CS, HIGH); | |||||
//Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register. | |||||
writeADXL345Register(DATA_FORMAT, 0x01); | |||||
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register. | |||||
writeADXL345Register(POWER_CTL, 0x08); //Measurement mode | |||||
} | |||||
// Acquire a data record. | |||||
void acquireData(data_t* data) { | |||||
data->time = micros(); | |||||
SPI.setDataMode(SPI_MODE3); | |||||
digitalWrite(ADXL345_CS, LOW); | |||||
// Read multiple bytes so or 0XC0 with address. | |||||
SPI.transfer(DATAX0 | 0XC0); | |||||
data->accel[0] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
data->accel[1] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
data->accel[2] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
digitalWrite(ADXL345_CS, HIGH); | |||||
} | |||||
// Print a data record. | |||||
void printData(Print* pr, data_t* data) { | |||||
pr->print(data->time - startMicros); | |||||
for (int i = 0; i < ACCEL_DIM; i++) { | |||||
pr->write(','); | |||||
pr->print(data->accel[i]); | |||||
} | |||||
pr->println(); | |||||
} | |||||
#include "UserTypes.h" | |||||
// Print data header. | |||||
void printHeader(Print* pr) { | |||||
pr->println(F("time,ax,ay,az")); | |||||
} | |||||
#ifdef __AVR_ATmega328P__ | |||||
#include "MinimumSerial.h" | |||||
MinimumSerial MinSerial; | |||||
#define Serial MinSerial | |||||
#endif // __AVR_ATmega328P__ | |||||
//============================================================================== | //============================================================================== | ||||
// Start of configuration constants. | // Start of configuration constants. | ||||
//============================================================================== | //============================================================================== | ||||
// Abort run on an overrun. Data before the overrun will be saved. | |||||
#define ABORT_ON_OVERRUN 1 | |||||
//------------------------------------------------------------------------------ | |||||
//Interval between data records in microseconds. | //Interval between data records in microseconds. | ||||
const uint32_t LOG_INTERVAL_USEC = 10000; | |||||
const uint32_t LOG_INTERVAL_USEC = 2000; | |||||
//------------------------------------------------------------------------------ | |||||
// Set USE_SHARED_SPI non-zero for use of an SPI sensor. | |||||
// May not work for some cards. | |||||
#ifndef USE_SHARED_SPI | |||||
#define USE_SHARED_SPI 0 | |||||
#endif // USE_SHARED_SPI | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// Pin definitions. | // Pin definitions. | ||||
// | // | ||||
const uint8_t SD_CS_PIN = SS; | const uint8_t SD_CS_PIN = SS; | ||||
// | // | ||||
// Digital pin to indicate an error, set to -1 if not used. | // Digital pin to indicate an error, set to -1 if not used. | ||||
// The led blinks for fatal errors. The led goes on solid for SD write | |||||
// overrun errors and logging continues. | |||||
// The led blinks for fatal errors. The led goes on solid for | |||||
// overrun errors and logging continues unless ABORT_ON_OVERRUN | |||||
// is non-zero. | |||||
#ifdef ERROR_LED_PIN | |||||
#undef ERROR_LED_PIN | |||||
#endif // ERROR_LED_PIN | |||||
const int8_t ERROR_LED_PIN = -1; | const int8_t ERROR_LED_PIN = -1; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// File definitions. | // File definitions. | ||||
// This file is flash erased using special SD commands. The file will be | // This file is flash erased using special SD commands. The file will be | ||||
// truncated if logging is stopped early. | // truncated if logging is stopped early. | ||||
const uint32_t FILE_BLOCK_COUNT = 256000; | const uint32_t FILE_BLOCK_COUNT = 256000; | ||||
// log file base name. Must be six characters or less. | |||||
// | |||||
// log file base name if not defined in UserTypes.h | |||||
#ifndef FILE_BASE_NAME | |||||
#define FILE_BASE_NAME "data" | #define FILE_BASE_NAME "data" | ||||
#endif // FILE_BASE_NAME | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// Buffer definitions. | // Buffer definitions. | ||||
// | // | ||||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional | |||||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional | |||||
// buffers. | // buffers. | ||||
// | // | ||||
#ifndef RAMEND | #ifndef RAMEND | ||||
// Assume ARM. Use total of nine 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 8; | |||||
// Assume ARM. Use total of ten 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 10; | |||||
// | // | ||||
#elif RAMEND < 0X8FF | #elif RAMEND < 0X8FF | ||||
#error Too little SRAM | #error Too little SRAM | ||||
// | // | ||||
#elif RAMEND < 0X10FF | #elif RAMEND < 0X10FF | ||||
// Use total of two 512 byte buffers. | // Use total of two 512 byte buffers. | ||||
const uint8_t BUFFER_BLOCK_COUNT = 1; | |||||
const uint8_t BUFFER_BLOCK_COUNT = 2; | |||||
// | // | ||||
#elif RAMEND < 0X20FF | #elif RAMEND < 0X20FF | ||||
// Use total of five 512 byte buffers. | |||||
// Use total of four 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 4; | const uint8_t BUFFER_BLOCK_COUNT = 4; | ||||
// | // | ||||
#else // RAMEND | #else // RAMEND | ||||
// Use total of 13 512 byte buffers. | |||||
// Use total of 12 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 12; | const uint8_t BUFFER_BLOCK_COUNT = 12; | ||||
#endif // RAMEND | #endif // RAMEND | ||||
//============================================================================== | //============================================================================== | ||||
// Size of file base name. Must not be larger than six. | // Size of file base name. Must not be larger than six. | ||||
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | ||||
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7; | |||||
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin"; | |||||
SdFat sd; | SdFat sd; | ||||
SdBaseFile binFile; | SdBaseFile binFile; | ||||
char binName[13] = FILE_BASE_NAME "00.bin"; | |||||
// Number of data records in a block. | // Number of data records in a block. | ||||
const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | ||||
data_t data[DATA_DIM]; | data_t data[DATA_DIM]; | ||||
uint8_t fill[FILL_DIM]; | uint8_t fill[FILL_DIM]; | ||||
}; | }; | ||||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 2; | |||||
block_t* emptyQueue[QUEUE_DIM]; | |||||
uint8_t emptyHead; | |||||
uint8_t emptyTail; | |||||
block_t* fullQueue[QUEUE_DIM]; | |||||
uint8_t fullHead; | |||||
uint8_t fullTail; | |||||
// Advance queue index. | |||||
inline uint8_t queueNext(uint8_t ht) { | |||||
return ht < (QUEUE_DIM - 1) ? ht + 1 : 0; | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
// Error messages stored in flash. | // Error messages stored in flash. | ||||
#define error(msg) errorFlash(F(msg)) | |||||
//------------------------------------------------------------------------------ | |||||
void errorFlash(const __FlashStringHelper* msg) { | |||||
sd.errorPrint(msg); | |||||
fatalBlink(); | |||||
} | |||||
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();} | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// | // | ||||
void fatalBlink() { | void fatalBlink() { | ||||
while (true) { | while (true) { | ||||
SysCall::yield(); | |||||
if (ERROR_LED_PIN >= 0) { | if (ERROR_LED_PIN >= 0) { | ||||
digitalWrite(ERROR_LED_PIN, HIGH); | digitalWrite(ERROR_LED_PIN, HIGH); | ||||
delay(200); | delay(200); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
//============================================================================== | |||||
//------------------------------------------------------------------------------ | |||||
// read data file and check for overruns | |||||
void checkOverrun() { | |||||
bool headerPrinted = false; | |||||
block_t block; | |||||
uint32_t bn = 0; | |||||
if (!binFile.isOpen()) { | |||||
Serial.println(); | |||||
Serial.println(F("No current binary file")); | |||||
return; | |||||
} | |||||
binFile.rewind(); | |||||
Serial.println(); | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
Serial.println(F("Checking overrun errors - type any character to stop")); | |||||
while (binFile.read(&block, 512) == 512) { | |||||
if (block.count == 0) { | |||||
break; | |||||
} | |||||
if (block.overrun) { | |||||
if (!headerPrinted) { | |||||
Serial.println(); | |||||
Serial.println(F("Overruns:")); | |||||
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||||
headerPrinted = true; | |||||
} | |||||
Serial.print(bn); | |||||
Serial.print(','); | |||||
Serial.print(binFile.firstBlock() + bn); | |||||
Serial.print(','); | |||||
Serial.println(block.overrun); | |||||
} | |||||
bn++; | |||||
} | |||||
if (!headerPrinted) { | |||||
Serial.println(F("No errors found")); | |||||
} else { | |||||
Serial.println(F("Done")); | |||||
} | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
// Convert binary file to csv file. | // Convert binary file to csv file. | ||||
void binaryToCsv() { | void binaryToCsv() { | ||||
uint8_t lastPct = 0; | uint8_t lastPct = 0; | ||||
uint32_t t0 = millis(); | uint32_t t0 = millis(); | ||||
uint32_t syncCluster = 0; | uint32_t syncCluster = 0; | ||||
SdFile csvFile; | SdFile csvFile; | ||||
char csvName[13]; | |||||
char csvName[FILE_NAME_DIM]; | |||||
if (!binFile.isOpen()) { | if (!binFile.isOpen()) { | ||||
Serial.println(); | Serial.println(); | ||||
Serial.println(F("No current binary file")); | Serial.println(F("No current binary file")); | ||||
return; | return; | ||||
} | } | ||||
binFile.rewind(); | |||||
Serial.println(); | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
// Create a new csvFile. | // Create a new csvFile. | ||||
strcpy(csvName, binName); | strcpy(csvName, binName); | ||||
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | ||||
if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | ||||
error("open csvFile failed"); | error("open csvFile failed"); | ||||
} | } | ||||
Serial.println(); | |||||
binFile.rewind(); | |||||
Serial.print(F("Writing: ")); | Serial.print(F("Writing: ")); | ||||
Serial.print(csvName); | Serial.print(csvName); | ||||
Serial.println(F(" - type any character to stop")); | Serial.println(F(" - type any character to stop")); | ||||
Serial.print(0.001*(millis() - t0)); | Serial.print(0.001*(millis() - t0)); | ||||
Serial.println(F(" Seconds")); | Serial.println(F(" Seconds")); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | |||||
// read data file and check for overruns | |||||
void checkOverrun() { | |||||
bool headerPrinted = false; | |||||
block_t block; | |||||
//----------------------------------------------------------------------------- | |||||
void createBinFile() { | |||||
// max number of blocks to erase per erase call | |||||
const uint32_t ERASE_SIZE = 262144L; | |||||
uint32_t bgnBlock, endBlock; | uint32_t bgnBlock, endBlock; | ||||
uint32_t bn = 0; | |||||
if (!binFile.isOpen()) { | |||||
Serial.println(); | |||||
Serial.println(F("No current binary file")); | |||||
return; | |||||
Serial.println(); | |||||
while (sd.exists(binName)) { | |||||
if (binName[BASE_NAME_SIZE + 1] != '9') { | |||||
binName[BASE_NAME_SIZE + 1]++; | |||||
} else { | |||||
binName[BASE_NAME_SIZE + 1] = '0'; | |||||
if (binName[BASE_NAME_SIZE] == '9') { | |||||
error("Can't create file name"); | |||||
} | |||||
binName[BASE_NAME_SIZE]++; | |||||
} | |||||
} | |||||
// Delete old tmp file. | |||||
if (sd.exists(TMP_FILE_NAME)) { | |||||
Serial.println(F("Deleting tmp file")); | |||||
if (!sd.remove(TMP_FILE_NAME)) { | |||||
error("Can't remove tmp file"); | |||||
} | |||||
} | } | ||||
// Create new file. | |||||
Serial.println(F("Creating new file")); | |||||
binFile.close(); | |||||
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
error("createContiguous failed"); | |||||
} | |||||
// Get the address of the file on the SD. | |||||
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | ||||
error("contiguousRange failed"); | error("contiguousRange failed"); | ||||
} | } | ||||
binFile.rewind(); | |||||
Serial.println(); | |||||
Serial.println(F("Checking overrun errors - type any character to stop")); | |||||
while (binFile.read(&block, 512) == 512) { | |||||
if (block.count == 0) { | |||||
break; | |||||
// Flash erase all data in the file. | |||||
Serial.println(F("Erasing all data")); | |||||
uint32_t bgnErase = bgnBlock; | |||||
uint32_t endErase; | |||||
while (bgnErase < endBlock) { | |||||
endErase = bgnErase + ERASE_SIZE; | |||||
if (endErase > endBlock) { | |||||
endErase = endBlock; | |||||
} | } | ||||
if (block.overrun) { | |||||
if (!headerPrinted) { | |||||
Serial.println(); | |||||
Serial.println(F("Overruns:")); | |||||
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||||
headerPrinted = true; | |||||
} | |||||
Serial.print(bn); | |||||
Serial.print(','); | |||||
Serial.print(bgnBlock + bn); | |||||
Serial.print(','); | |||||
Serial.println(block.overrun); | |||||
if (!sd.card()->erase(bgnErase, endErase)) { | |||||
error("erase failed"); | |||||
} | } | ||||
bn++; | |||||
} | |||||
if (!headerPrinted) { | |||||
Serial.println(F("No errors found")); | |||||
} else { | |||||
Serial.println(F("Done")); | |||||
bgnErase = endErase + 1; | |||||
} | } | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// log data | // log data | ||||
// max number of blocks to erase per erase call | |||||
uint32_t const ERASE_SIZE = 262144L; | |||||
void logData() { | void logData() { | ||||
uint32_t bgnBlock, endBlock; | |||||
// Allocate extra buffer space. | |||||
block_t block[BUFFER_BLOCK_COUNT]; | |||||
block_t* curBlock = 0; | |||||
Serial.println(); | |||||
// Find unused file name. | |||||
if (BASE_NAME_SIZE > 6) { | |||||
error("FILE_BASE_NAME too long"); | |||||
} | |||||
while (sd.exists(binName)) { | |||||
if (binName[BASE_NAME_SIZE + 1] != '9') { | |||||
binName[BASE_NAME_SIZE + 1]++; | |||||
} else { | |||||
binName[BASE_NAME_SIZE + 1] = '0'; | |||||
if (binName[BASE_NAME_SIZE] == '9') { | |||||
error("Can't create file name"); | |||||
} | |||||
binName[BASE_NAME_SIZE]++; | |||||
createBinFile(); | |||||
recordBinFile(); | |||||
renameBinFile(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void openBinFile() { | |||||
char name[FILE_NAME_DIM]; | |||||
strcpy(name, binName); | |||||
Serial.println(F("\nEnter two digit version")); | |||||
Serial.write(name, BASE_NAME_SIZE); | |||||
for (int i = 0; i < 2; i++) { | |||||
while (!Serial.available()) { | |||||
SysCall::yield(); | |||||
} | } | ||||
} | |||||
// Delete old tmp file. | |||||
if (sd.exists(TMP_FILE_NAME)) { | |||||
Serial.println(F("Deleting tmp file")); | |||||
if (!sd.remove(TMP_FILE_NAME)) { | |||||
error("Can't remove tmp file"); | |||||
char c = Serial.read(); | |||||
Serial.write(c); | |||||
if (c < '0' || c > '9') { | |||||
Serial.println("\nInvalid digit"); | |||||
return; | |||||
} | } | ||||
name[BASE_NAME_SIZE + i] = c; | |||||
} | } | ||||
// Create new file. | |||||
Serial.println(F("Creating new file")); | |||||
binFile.close(); | |||||
if (!binFile.createContiguous(sd.vwd(), | |||||
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
error("createContiguous failed"); | |||||
Serial.println(&name[BASE_NAME_SIZE+2]); | |||||
if (!sd.exists(name)) { | |||||
Serial.println(F("File does not exist")); | |||||
return; | |||||
} | } | ||||
// Get the address of the file on the SD. | |||||
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||||
error("contiguousRange failed"); | |||||
binFile.close(); | |||||
strcpy(binName, name); | |||||
if (!binFile.open(binName, O_READ)) { | |||||
Serial.println(F("open failed")); | |||||
return; | |||||
} | } | ||||
Serial.println(F("File opened")); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void recordBinFile() { | |||||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||||
// Index of last queue location. | |||||
const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||||
// Allocate extra buffer space. | |||||
block_t block[BUFFER_BLOCK_COUNT - 1]; | |||||
block_t* curBlock = 0; | |||||
block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||||
uint8_t emptyTop; | |||||
uint8_t minTop; | |||||
block_t* fullQueue[QUEUE_DIM]; | |||||
uint8_t fullHead = 0; | |||||
uint8_t fullTail = 0; | |||||
// Use SdFat's internal buffer. | // Use SdFat's internal buffer. | ||||
uint8_t* cache = (uint8_t*)sd.vol()->cacheClear(); | |||||
if (cache == 0) { | |||||
emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||||
if (emptyStack[0] == 0) { | |||||
error("cacheClear failed"); | error("cacheClear failed"); | ||||
} | } | ||||
// Flash erase all data in the file. | |||||
Serial.println(F("Erasing all data")); | |||||
uint32_t bgnErase = bgnBlock; | |||||
uint32_t endErase; | |||||
while (bgnErase < endBlock) { | |||||
endErase = bgnErase + ERASE_SIZE; | |||||
if (endErase > endBlock) { | |||||
endErase = endBlock; | |||||
} | |||||
if (!sd.card()->erase(bgnErase, endErase)) { | |||||
error("erase failed"); | |||||
} | |||||
bgnErase = endErase + 1; | |||||
// Put rest of buffers on the empty stack. | |||||
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) { | |||||
emptyStack[i] = &block[i - 1]; | |||||
} | } | ||||
emptyTop = BUFFER_BLOCK_COUNT; | |||||
minTop = BUFFER_BLOCK_COUNT; | |||||
// Start a multiple block write. | // Start a multiple block write. | ||||
if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) { | |||||
error("writeBegin failed"); | |||||
} | |||||
// Set chip select high if other devices use SPI. | |||||
if (useSharedSpi) { | |||||
sd.card()->chipSelectHigh(); | |||||
} | |||||
// Initialize queues. | |||||
emptyHead = emptyTail = 0; | |||||
fullHead = fullTail = 0; | |||||
// Use SdFat buffer for one block. | |||||
emptyQueue[emptyHead] = (block_t*)cache; | |||||
emptyHead = queueNext(emptyHead); | |||||
// Put rest of buffers in the empty queue. | |||||
for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { | |||||
emptyQueue[emptyHead] = &block[i]; | |||||
emptyHead = queueNext(emptyHead); | |||||
if (!sd.card()->writeStart(binFile.firstBlock())) { | |||||
error("writeBStart failed"); | |||||
} | } | ||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
Serial.println(F("Logging - type any character to stop")); | Serial.println(F("Logging - type any character to stop")); | ||||
// Wait for Serial Idle. | |||||
Serial.flush(); | |||||
delay(10); | |||||
bool closeFile = false; | bool closeFile = false; | ||||
uint32_t bn = 0; | |||||
uint32_t t0 = millis(); | |||||
uint32_t t1 = t0; | |||||
uint32_t bn = 0; | |||||
uint32_t maxLatency = 0; | |||||
uint32_t overrun = 0; | uint32_t overrun = 0; | ||||
uint32_t overrunTotal = 0; | uint32_t overrunTotal = 0; | ||||
uint32_t count = 0; | |||||
uint32_t maxDelta = 0; | |||||
uint32_t minDelta = 99999; | |||||
uint32_t maxLatency = 0; | |||||
uint32_t logTime = micros(); | uint32_t logTime = micros(); | ||||
// Set time for first record of file. | |||||
startMicros = logTime + LOG_INTERVAL_USEC; | |||||
while (1) { | |||||
// Time for next data record. | |||||
while(1) { | |||||
// Time for next data record. | |||||
logTime += LOG_INTERVAL_USEC; | logTime += LOG_INTERVAL_USEC; | ||||
if (Serial.available()) { | if (Serial.available()) { | ||||
closeFile = true; | closeFile = true; | ||||
} | |||||
} | |||||
if (closeFile) { | if (closeFile) { | ||||
if (curBlock != 0) { | if (curBlock != 0) { | ||||
// Put buffer in full queue. | // Put buffer in full queue. | ||||
fullQueue[fullHead] = curBlock; | fullQueue[fullHead] = curBlock; | ||||
fullHead = queueNext(fullHead); | |||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
curBlock = 0; | curBlock = 0; | ||||
} | } | ||||
} else { | } else { | ||||
if (curBlock == 0 && emptyTail != emptyHead) { | |||||
curBlock = emptyQueue[emptyTail]; | |||||
emptyTail = queueNext(emptyTail); | |||||
if (curBlock == 0 && emptyTop != 0) { | |||||
curBlock = emptyStack[--emptyTop]; | |||||
if (emptyTop < minTop) { | |||||
minTop = emptyTop; | |||||
} | |||||
curBlock->count = 0; | curBlock->count = 0; | ||||
curBlock->overrun = overrun; | curBlock->overrun = overrun; | ||||
overrun = 0; | overrun = 0; | ||||
} while (delta < 0); | } while (delta < 0); | ||||
if (curBlock == 0) { | if (curBlock == 0) { | ||||
overrun++; | overrun++; | ||||
overrunTotal++; | |||||
if (ERROR_LED_PIN >= 0) { | |||||
digitalWrite(ERROR_LED_PIN, HIGH); | |||||
} | |||||
#if ABORT_ON_OVERRUN | |||||
Serial.println(F("Overrun abort")); | |||||
break; | |||||
#endif // ABORT_ON_OVERRUN | |||||
} else { | } else { | ||||
acquireData(&curBlock->data[curBlock->count++]); | |||||
#if USE_SHARED_SPI | |||||
sd.card()->spiStop(); | |||||
#endif // USE_SHARED_SPI | |||||
acquireData(&curBlock->data[curBlock->count++]); | |||||
#if USE_SHARED_SPI | |||||
sd.card()->spiStart(); | |||||
#endif // USE_SHARED_SPI | |||||
if (curBlock->count == DATA_DIM) { | if (curBlock->count == DATA_DIM) { | ||||
fullQueue[fullHead] = curBlock; | fullQueue[fullHead] = curBlock; | ||||
fullHead = queueNext(fullHead); | |||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
curBlock = 0; | curBlock = 0; | ||||
} | |||||
if ((uint32_t)delta > maxDelta) maxDelta = delta; | |||||
if ((uint32_t)delta < minDelta) minDelta = delta; | |||||
} | |||||
} | } | ||||
} | } | ||||
if (fullHead == fullTail) { | if (fullHead == fullTail) { | ||||
// Exit loop if done. | // Exit loop if done. | ||||
if (closeFile) { | if (closeFile) { | ||||
} else if (!sd.card()->isBusy()) { | } else if (!sd.card()->isBusy()) { | ||||
// Get address of block to write. | // Get address of block to write. | ||||
block_t* pBlock = fullQueue[fullTail]; | block_t* pBlock = fullQueue[fullTail]; | ||||
fullTail = queueNext(fullTail); | |||||
fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0; | |||||
// Write block to SD. | // Write block to SD. | ||||
uint32_t usec = micros(); | uint32_t usec = micros(); | ||||
if (!sd.card()->writeData((uint8_t*)pBlock)) { | if (!sd.card()->writeData((uint8_t*)pBlock)) { | ||||
error("write data failed"); | error("write data failed"); | ||||
} | } | ||||
usec = micros() - usec; | usec = micros() - usec; | ||||
t1 = millis(); | |||||
if (usec > maxLatency) { | if (usec > maxLatency) { | ||||
maxLatency = usec; | maxLatency = usec; | ||||
} | } | ||||
count += pBlock->count; | |||||
// Add overruns and possibly light LED. | |||||
if (pBlock->overrun) { | |||||
overrunTotal += pBlock->overrun; | |||||
if (ERROR_LED_PIN >= 0) { | |||||
digitalWrite(ERROR_LED_PIN, HIGH); | |||||
} | |||||
} | |||||
// Move block to empty queue. | // Move block to empty queue. | ||||
emptyQueue[emptyHead] = pBlock; | |||||
emptyHead = queueNext(emptyHead); | |||||
emptyStack[emptyTop++] = pBlock; | |||||
bn++; | bn++; | ||||
if (bn == FILE_BLOCK_COUNT) { | if (bn == FILE_BLOCK_COUNT) { | ||||
// File full so stop | // File full so stop | ||||
if (!sd.card()->writeStop()) { | if (!sd.card()->writeStop()) { | ||||
error("writeStop failed"); | error("writeStop failed"); | ||||
} | } | ||||
Serial.print(F("Min Free buffers: ")); | |||||
Serial.println(minTop); | |||||
Serial.print(F("Max block write usec: ")); | |||||
Serial.println(maxLatency); | |||||
Serial.print(F("Overruns: ")); | |||||
Serial.println(overrunTotal); | |||||
// Truncate file if recording stopped early. | // Truncate file if recording stopped early. | ||||
if (bn != FILE_BLOCK_COUNT) { | if (bn != FILE_BLOCK_COUNT) { | ||||
Serial.println(F("Truncating file")); | Serial.println(F("Truncating file")); | ||||
error("Can't truncate file"); | error("Can't truncate file"); | ||||
} | } | ||||
} | } | ||||
} | |||||
//----------------------------------------------------------------------------- | |||||
void renameBinFile() { | |||||
if (!binFile.rename(sd.vwd(), binName)) { | if (!binFile.rename(sd.vwd(), binName)) { | ||||
error("Can't rename file"); | error("Can't rename file"); | ||||
} | |||||
} | |||||
Serial.print(F("File renamed: ")); | Serial.print(F("File renamed: ")); | ||||
Serial.println(binName); | Serial.println(binName); | ||||
Serial.print(F("Max block write usec: ")); | |||||
Serial.println(maxLatency); | |||||
Serial.print(F("Record time sec: ")); | |||||
Serial.println(0.001*(t1 - t0), 3); | |||||
Serial.print(minDelta); | |||||
Serial.print(F(" <= jitter microseconds <= ")); | |||||
Serial.println(maxDelta); | |||||
Serial.print(F("Sample count: ")); | |||||
Serial.println(count); | |||||
Serial.print(F("Samples/sec: ")); | |||||
Serial.println((1000.0)*count/(t1-t0)); | |||||
Serial.print(F("Overruns: ")); | |||||
Serial.println(overrunTotal); | |||||
Serial.println(F("Done")); | |||||
Serial.print("File size: "); | |||||
Serial.print(binFile.fileSize()/512); | |||||
Serial.println(F(" blocks")); | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void testSensor() { | |||||
const uint32_t interval = 200000; | |||||
int32_t diff; | |||||
data_t data; | |||||
Serial.println(F("\nTesting - type any character to stop\n")); | |||||
// Wait for Serial Idle. | |||||
delay(1000); | |||||
printHeader(&Serial); | |||||
uint32_t m = micros(); | |||||
while (!Serial.available()) { | |||||
m += interval; | |||||
do { | |||||
diff = m - micros(); | |||||
} while (diff > 0); | |||||
acquireData(&data); | |||||
printData(&Serial, &data); | |||||
} | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void setup(void) { | void setup(void) { | ||||
while (!Serial) { | while (!Serial) { | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
Serial.print(F("FreeStack: ")); | Serial.print(F("FreeStack: ")); | ||||
Serial.println(FreeStack()); | Serial.println(FreeStack()); | ||||
Serial.print(F("Records/block: ")); | Serial.print(F("Records/block: ")); | ||||
if (sizeof(block_t) != 512) { | if (sizeof(block_t) != 512) { | ||||
error("Invalid block size"); | error("Invalid block size"); | ||||
} | } | ||||
// initialize file system. | |||||
if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||||
sd.initErrorPrint(); | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
sd.initErrorPrint(&Serial); | |||||
fatalBlink(); | fatalBlink(); | ||||
} | } | ||||
setupADXL345(); | |||||
userSetup(); | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void loop(void) { | void loop(void) { | ||||
// discard any input | |||||
// Read any Serial data. | |||||
do { | do { | ||||
delay(10); | delay(10); | ||||
} while (Serial.read() >= 0); | |||||
} while (Serial.available() && Serial.read() >= 0); | |||||
Serial.println(); | Serial.println(); | ||||
Serial.println(F("type:")); | Serial.println(F("type:")); | ||||
Serial.println(F("b - open existing bin file")); | |||||
Serial.println(F("c - convert file to csv")); | Serial.println(F("c - convert file to csv")); | ||||
Serial.println(F("d - dump data to Serial")); | Serial.println(F("d - dump data to Serial")); | ||||
Serial.println(F("e - overrun error details")); | Serial.println(F("e - overrun error details")); | ||||
Serial.println(F("l - list files")); | |||||
Serial.println(F("r - record data")); | Serial.println(F("r - record data")); | ||||
Serial.println(F("t - test without logging")); | |||||
while(!Serial.available()) { | while(!Serial.available()) { | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
#if WDT_YIELD_TIME_MICROS | |||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||||
SysCall::halt(); | |||||
#endif | |||||
char c = tolower(Serial.read()); | char c = tolower(Serial.read()); | ||||
// Discard extra Serial data. | // Discard extra Serial data. | ||||
do { | do { | ||||
delay(10); | delay(10); | ||||
} while (Serial.read() >= 0); | |||||
} while (Serial.available() && Serial.read() >= 0); | |||||
if (ERROR_LED_PIN >= 0) { | if (ERROR_LED_PIN >= 0) { | ||||
digitalWrite(ERROR_LED_PIN, LOW); | digitalWrite(ERROR_LED_PIN, LOW); | ||||
} | } | ||||
if (c == 'c') { | |||||
if (c == 'b') { | |||||
openBinFile(); | |||||
} else if (c == 'c') { | |||||
binaryToCsv(); | binaryToCsv(); | ||||
} else if (c == 'd') { | } else if (c == 'd') { | ||||
dumpData(); | dumpData(); | ||||
} else if (c == 'e') { | } else if (c == 'e') { | ||||
checkOverrun(); | checkOverrun(); | ||||
} else if (c == 'l') { | |||||
Serial.println(F("\nls:")); | |||||
sd.ls(&Serial, LS_SIZE); | |||||
} else if (c == 'r') { | } else if (c == 'r') { | ||||
logData(); | logData(); | ||||
} else if (c == 't') { | |||||
testSensor(); | |||||
} else { | } else { | ||||
Serial.println(F("Invalid entry")); | Serial.println(F("Invalid entry")); | ||||
} | } |
// Empty file with name LowLatencyLoggerADXL345.ino to make IDE happy. |
#include "UserTypes.h" | |||||
// User data functions. Modify these functions for your data items. | |||||
// Start time for data | |||||
static uint32_t startMicros; | |||||
const uint8_t ADXL345_CS = 9; | |||||
const uint8_t POWER_CTL = 0x2D; //Power Control Register | |||||
const uint8_t DATA_FORMAT = 0x31; | |||||
const uint8_t DATAX0 = 0x32; //X-Axis Data 0 | |||||
const uint8_t DATAX1 = 0x33; //X-Axis Data 1 | |||||
const uint8_t DATAY0 = 0x34; //Y-Axis Data 0 | |||||
const uint8_t DATAY1 = 0x35; //Y-Axis Data 1 | |||||
const uint8_t DATAZ0 = 0x36; //Z-Axis Data 0 | |||||
const uint8_t DATAZ1 = 0x37; //Z-Axis Data 1 | |||||
void writeADXL345Register(const uint8_t registerAddress, const uint8_t value) { | |||||
digitalWrite(ADXL345_CS, LOW); | |||||
SPI.transfer(registerAddress); | |||||
SPI.transfer(value); | |||||
digitalWrite(ADXL345_CS, HIGH); | |||||
} | |||||
void userSetup() { | |||||
SPI.begin(); | |||||
pinMode(ADXL345_CS, OUTPUT); | |||||
digitalWrite(ADXL345_CS, HIGH); | |||||
//Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register. | |||||
writeADXL345Register(DATA_FORMAT, 0x01); | |||||
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register. | |||||
writeADXL345Register(POWER_CTL, 0x08); //Measurement mode | |||||
} | |||||
// Acquire a data record. | |||||
void acquireData(data_t* data) { | |||||
// Max SPI clock frequency is 5 MHz with CPOL = 1 and CPHA = 1. | |||||
SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE3)); | |||||
data->time = micros(); | |||||
digitalWrite(ADXL345_CS, LOW); | |||||
// Read multiple bytes so or 0XC0 with address. | |||||
SPI.transfer(DATAX0 | 0XC0); | |||||
data->accel[0] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
data->accel[1] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
data->accel[2] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||||
digitalWrite(ADXL345_CS, HIGH); | |||||
SPI.endTransaction(); | |||||
} | |||||
// Print a data record. | |||||
void printData(Print* pr, data_t* data) { | |||||
if (startMicros == 0) { | |||||
startMicros = data->time; | |||||
} | |||||
pr->print(data->time - startMicros); | |||||
for (int i = 0; i < ACCEL_DIM; i++) { | |||||
pr->write(','); | |||||
pr->print(data->accel[i]); | |||||
} | |||||
pr->println(); | |||||
} | |||||
// Print data header. | |||||
void printHeader(Print* pr) { | |||||
startMicros = 0; | |||||
pr->println(F("micros,ax,ay,az")); | |||||
} |
#ifndef UserTypes_h | |||||
#define UserTypes_h | |||||
#include "Arduino.h" | |||||
#include "SPI.h" | |||||
#define USE_SHARED_SPI 1 | |||||
#define FILE_BASE_NAME "ADXL4G" | |||||
// User data types. Modify for your data items. | |||||
const uint8_t ACCEL_DIM = 3; | |||||
struct data_t { | |||||
unsigned long time; | |||||
int16_t accel[ACCEL_DIM]; | |||||
}; | |||||
void acquireData(data_t* data); | |||||
void printData(Print* pr, data_t* data); | |||||
void printHeader(Print* pr); | |||||
void userSetup(); | |||||
#endif // UserTypes_h |
/** | |||||
* This program logs data to a binary file. Functions are included | |||||
* to convert the binary file to a csv text file. | |||||
* | |||||
* Samples are logged at regular intervals. The maximum logging rate | |||||
* depends on the quality of your SD card and the time required to | |||||
* read sensor data. This example has been tested at 500 Hz with | |||||
* good SD card on an Uno. 4000 HZ is possible on a Due. | |||||
* | |||||
* If your SD card has a long write latency, it may be necessary to use | |||||
* slower sample rates. Using a Mega Arduino helps overcome latency | |||||
* problems since 12 512 byte buffers will be used. | |||||
* | |||||
* Data is written to the file using a SD multiple block write command. | |||||
*/ | |||||
#include <SPI.h> | |||||
#include "SdFat.h" | |||||
#include "FreeStack.h" | |||||
#include "UserTypes.h" | |||||
#ifdef __AVR_ATmega328P__ | |||||
#include "MinimumSerial.h" | |||||
MinimumSerial MinSerial; | |||||
#define Serial MinSerial | |||||
#endif // __AVR_ATmega328P__ | |||||
//============================================================================== | |||||
// Start of configuration constants. | |||||
//============================================================================== | |||||
// Abort run on an overrun. Data before the overrun will be saved. | |||||
#define ABORT_ON_OVERRUN 1 | |||||
//------------------------------------------------------------------------------ | |||||
//Interval between data records in microseconds. | |||||
const uint32_t LOG_INTERVAL_USEC = 2000; | |||||
//------------------------------------------------------------------------------ | |||||
// Set USE_SHARED_SPI non-zero for use of an SPI sensor. | |||||
// May not work for some cards. | |||||
#ifndef USE_SHARED_SPI | |||||
#define USE_SHARED_SPI 0 | |||||
#endif // USE_SHARED_SPI | |||||
//------------------------------------------------------------------------------ | |||||
// Pin definitions. | |||||
// | |||||
// SD chip select pin. | |||||
const uint8_t SD_CS_PIN = SS; | |||||
// | |||||
// Digital pin to indicate an error, set to -1 if not used. | |||||
// The led blinks for fatal errors. The led goes on solid for | |||||
// overrun errors and logging continues unless ABORT_ON_OVERRUN | |||||
// is non-zero. | |||||
#ifdef ERROR_LED_PIN | |||||
#undef ERROR_LED_PIN | |||||
#endif // ERROR_LED_PIN | |||||
const int8_t ERROR_LED_PIN = 3; | |||||
//------------------------------------------------------------------------------ | |||||
// File definitions. | |||||
// | |||||
// Maximum file size in blocks. | |||||
// The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks. | |||||
// This file is flash erased using special SD commands. The file will be | |||||
// truncated if logging is stopped early. | |||||
const uint32_t FILE_BLOCK_COUNT = 256000; | |||||
// | |||||
// log file base name if not defined in UserTypes.h | |||||
#ifndef FILE_BASE_NAME | |||||
#define FILE_BASE_NAME "data" | |||||
#endif // FILE_BASE_NAME | |||||
//------------------------------------------------------------------------------ | |||||
// Buffer definitions. | |||||
// | |||||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional | |||||
// buffers. | |||||
// | |||||
#ifndef RAMEND | |||||
// Assume ARM. Use total of ten 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 10; | |||||
// | |||||
#elif RAMEND < 0X8FF | |||||
#error Too little SRAM | |||||
// | |||||
#elif RAMEND < 0X10FF | |||||
// Use total of two 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 2; | |||||
// | |||||
#elif RAMEND < 0X20FF | |||||
// Use total of four 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 4; | |||||
// | |||||
#else // RAMEND | |||||
// Use total of 12 512 byte buffers. | |||||
const uint8_t BUFFER_BLOCK_COUNT = 12; | |||||
#endif // RAMEND | |||||
//============================================================================== | |||||
// End of configuration constants. | |||||
//============================================================================== | |||||
// Temporary log file. Will be deleted if a reset or power failure occurs. | |||||
#define TMP_FILE_NAME "tmp_log.bin" | |||||
// Size of file base name. Must not be larger than six. | |||||
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | |||||
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7; | |||||
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin"; | |||||
SdFat sd; | |||||
SdBaseFile binFile; | |||||
// Number of data records in a block. | |||||
const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | |||||
//Compute fill so block size is 512 bytes. FILL_DIM may be zero. | |||||
const uint16_t FILL_DIM = 512 - 4 - DATA_DIM*sizeof(data_t); | |||||
struct block_t { | |||||
uint16_t count; | |||||
uint16_t overrun; | |||||
data_t data[DATA_DIM]; | |||||
uint8_t fill[FILL_DIM]; | |||||
}; | |||||
//============================================================================== | |||||
// Error messages stored in flash. | |||||
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();} | |||||
//------------------------------------------------------------------------------ | |||||
// | |||||
void fatalBlink() { | |||||
while (true) { | |||||
SysCall::yield(); | |||||
if (ERROR_LED_PIN >= 0) { | |||||
digitalWrite(ERROR_LED_PIN, HIGH); | |||||
delay(200); | |||||
digitalWrite(ERROR_LED_PIN, LOW); | |||||
delay(200); | |||||
} | |||||
} | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
// read data file and check for overruns | |||||
void checkOverrun() { | |||||
bool headerPrinted = false; | |||||
block_t block; | |||||
uint32_t bn = 0; | |||||
if (!binFile.isOpen()) { | |||||
Serial.println(); | |||||
Serial.println(F("No current binary file")); | |||||
return; | |||||
} | |||||
binFile.rewind(); | |||||
Serial.println(); | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
Serial.println(F("Checking overrun errors - type any character to stop")); | |||||
while (binFile.read(&block, 512) == 512) { | |||||
if (block.count == 0) { | |||||
break; | |||||
} | |||||
if (block.overrun) { | |||||
if (!headerPrinted) { | |||||
Serial.println(); | |||||
Serial.println(F("Overruns:")); | |||||
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||||
headerPrinted = true; | |||||
} | |||||
Serial.print(bn); | |||||
Serial.print(','); | |||||
Serial.print(binFile.firstBlock() + bn); | |||||
Serial.print(','); | |||||
Serial.println(block.overrun); | |||||
} | |||||
bn++; | |||||
} | |||||
if (!headerPrinted) { | |||||
Serial.println(F("No errors found")); | |||||
} else { | |||||
Serial.println(F("Done")); | |||||
} | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
// Convert binary file to csv file. | |||||
void binaryToCsv() { | |||||
uint8_t lastPct = 0; | |||||
block_t block; | |||||
uint32_t t0 = millis(); | |||||
uint32_t syncCluster = 0; | |||||
SdFile csvFile; | |||||
char csvName[FILE_NAME_DIM]; | |||||
if (!binFile.isOpen()) { | |||||
Serial.println(); | |||||
Serial.println(F("No current binary file")); | |||||
return; | |||||
} | |||||
Serial.println(); | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
// Create a new csvFile. | |||||
strcpy(csvName, binName); | |||||
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||||
if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | |||||
error("open csvFile failed"); | |||||
} | |||||
binFile.rewind(); | |||||
Serial.print(F("Writing: ")); | |||||
Serial.print(csvName); | |||||
Serial.println(F(" - type any character to stop")); | |||||
printHeader(&csvFile); | |||||
uint32_t tPct = millis(); | |||||
while (!Serial.available() && binFile.read(&block, 512) == 512) { | |||||
uint16_t i; | |||||
if (block.count == 0) { | |||||
break; | |||||
} | |||||
if (block.overrun) { | |||||
csvFile.print(F("OVERRUN,")); | |||||
csvFile.println(block.overrun); | |||||
} | |||||
for (i = 0; i < block.count; i++) { | |||||
printData(&csvFile, &block.data[i]); | |||||
} | |||||
if (csvFile.curCluster() != syncCluster) { | |||||
csvFile.sync(); | |||||
syncCluster = csvFile.curCluster(); | |||||
} | |||||
if ((millis() - tPct) > 1000) { | |||||
uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100); | |||||
if (pct != lastPct) { | |||||
tPct = millis(); | |||||
lastPct = pct; | |||||
Serial.print(pct, DEC); | |||||
Serial.println('%'); | |||||
} | |||||
} | |||||
if (Serial.available()) { | |||||
break; | |||||
} | |||||
} | |||||
csvFile.close(); | |||||
Serial.print(F("Done: ")); | |||||
Serial.print(0.001*(millis() - t0)); | |||||
Serial.println(F(" Seconds")); | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
void createBinFile() { | |||||
// max number of blocks to erase per erase call | |||||
const uint32_t ERASE_SIZE = 262144L; | |||||
uint32_t bgnBlock, endBlock; | |||||
Serial.println(); | |||||
while (sd.exists(binName)) { | |||||
if (binName[BASE_NAME_SIZE + 1] != '9') { | |||||
binName[BASE_NAME_SIZE + 1]++; | |||||
} else { | |||||
binName[BASE_NAME_SIZE + 1] = '0'; | |||||
if (binName[BASE_NAME_SIZE] == '9') { | |||||
error("Can't create file name"); | |||||
} | |||||
binName[BASE_NAME_SIZE]++; | |||||
} | |||||
} | |||||
// Delete old tmp file. | |||||
if (sd.exists(TMP_FILE_NAME)) { | |||||
Serial.println(F("Deleting tmp file")); | |||||
if (!sd.remove(TMP_FILE_NAME)) { | |||||
error("Can't remove tmp file"); | |||||
} | |||||
} | |||||
// Create new file. | |||||
Serial.println(F("Creating new file")); | |||||
binFile.close(); | |||||
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||||
error("createContiguous failed"); | |||||
} | |||||
// Get the address of the file on the SD. | |||||
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||||
error("contiguousRange failed"); | |||||
} | |||||
// Flash erase all data in the file. | |||||
Serial.println(F("Erasing all data")); | |||||
uint32_t bgnErase = bgnBlock; | |||||
uint32_t endErase; | |||||
while (bgnErase < endBlock) { | |||||
endErase = bgnErase + ERASE_SIZE; | |||||
if (endErase > endBlock) { | |||||
endErase = endBlock; | |||||
} | |||||
if (!sd.card()->erase(bgnErase, endErase)) { | |||||
error("erase failed"); | |||||
} | |||||
bgnErase = endErase + 1; | |||||
} | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
// dump data file to Serial | |||||
void dumpData() { | |||||
block_t block; | |||||
if (!binFile.isOpen()) { | |||||
Serial.println(); | |||||
Serial.println(F("No current binary file")); | |||||
return; | |||||
} | |||||
binFile.rewind(); | |||||
Serial.println(); | |||||
Serial.println(F("Type any character to stop")); | |||||
delay(1000); | |||||
printHeader(&Serial); | |||||
while (!Serial.available() && binFile.read(&block , 512) == 512) { | |||||
if (block.count == 0) { | |||||
break; | |||||
} | |||||
if (block.overrun) { | |||||
Serial.print(F("OVERRUN,")); | |||||
Serial.println(block.overrun); | |||||
} | |||||
for (uint16_t i = 0; i < block.count; i++) { | |||||
printData(&Serial, &block.data[i]); | |||||
} | |||||
} | |||||
Serial.println(F("Done")); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
// log data | |||||
void logData() { | |||||
createBinFile(); | |||||
recordBinFile(); | |||||
renameBinFile(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void openBinFile() { | |||||
char name[FILE_NAME_DIM]; | |||||
strcpy(name, binName); | |||||
Serial.println(F("\nEnter two digit version")); | |||||
Serial.write(name, BASE_NAME_SIZE); | |||||
for (int i = 0; i < 2; i++) { | |||||
while (!Serial.available()) { | |||||
SysCall::yield(); | |||||
} | |||||
char c = Serial.read(); | |||||
Serial.write(c); | |||||
if (c < '0' || c > '9') { | |||||
Serial.println("\nInvalid digit"); | |||||
return; | |||||
} | |||||
name[BASE_NAME_SIZE + i] = c; | |||||
} | |||||
Serial.println(&name[BASE_NAME_SIZE+2]); | |||||
if (!sd.exists(name)) { | |||||
Serial.println(F("File does not exist")); | |||||
return; | |||||
} | |||||
binFile.close(); | |||||
strcpy(binName, name); | |||||
if (!binFile.open(binName, O_READ)) { | |||||
Serial.println(F("open failed")); | |||||
return; | |||||
} | |||||
Serial.println(F("File opened")); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void recordBinFile() { | |||||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||||
// Index of last queue location. | |||||
const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||||
// Allocate extra buffer space. | |||||
block_t block[BUFFER_BLOCK_COUNT - 1]; | |||||
block_t* curBlock = 0; | |||||
block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||||
uint8_t emptyTop; | |||||
uint8_t minTop; | |||||
block_t* fullQueue[QUEUE_DIM]; | |||||
uint8_t fullHead = 0; | |||||
uint8_t fullTail = 0; | |||||
// Use SdFat's internal buffer. | |||||
emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||||
if (emptyStack[0] == 0) { | |||||
error("cacheClear failed"); | |||||
} | |||||
// Put rest of buffers on the empty stack. | |||||
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) { | |||||
emptyStack[i] = &block[i - 1]; | |||||
} | |||||
emptyTop = BUFFER_BLOCK_COUNT; | |||||
minTop = BUFFER_BLOCK_COUNT; | |||||
// Start a multiple block write. | |||||
if (!sd.card()->writeStart(binFile.firstBlock())) { | |||||
error("writeBStart failed"); | |||||
} | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
Serial.println(F("Logging - type any character to stop")); | |||||
bool closeFile = false; | |||||
uint32_t bn = 0; | |||||
uint32_t maxLatency = 0; | |||||
uint32_t overrun = 0; | |||||
uint32_t overrunTotal = 0; | |||||
uint32_t logTime = micros(); | |||||
while(1) { | |||||
// Time for next data record. | |||||
logTime += LOG_INTERVAL_USEC; | |||||
if (Serial.available()) { | |||||
closeFile = true; | |||||
} | |||||
if (closeFile) { | |||||
if (curBlock != 0) { | |||||
// Put buffer in full queue. | |||||
fullQueue[fullHead] = curBlock; | |||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
curBlock = 0; | |||||
} | |||||
} else { | |||||
if (curBlock == 0 && emptyTop != 0) { | |||||
curBlock = emptyStack[--emptyTop]; | |||||
if (emptyTop < minTop) { | |||||
minTop = emptyTop; | |||||
} | |||||
curBlock->count = 0; | |||||
curBlock->overrun = overrun; | |||||
overrun = 0; | |||||
} | |||||
if ((int32_t)(logTime - micros()) < 0) { | |||||
error("Rate too fast"); | |||||
} | |||||
int32_t delta; | |||||
do { | |||||
delta = micros() - logTime; | |||||
} while (delta < 0); | |||||
if (curBlock == 0) { | |||||
overrun++; | |||||
overrunTotal++; | |||||
if (ERROR_LED_PIN >= 0) { | |||||
digitalWrite(ERROR_LED_PIN, HIGH); | |||||
} | |||||
#if ABORT_ON_OVERRUN | |||||
Serial.println(F("Overrun abort")); | |||||
break; | |||||
#endif // ABORT_ON_OVERRUN | |||||
} else { | |||||
#if USE_SHARED_SPI | |||||
sd.card()->spiStop(); | |||||
#endif // USE_SHARED_SPI | |||||
acquireData(&curBlock->data[curBlock->count++]); | |||||
#if USE_SHARED_SPI | |||||
sd.card()->spiStart(); | |||||
#endif // USE_SHARED_SPI | |||||
if (curBlock->count == DATA_DIM) { | |||||
fullQueue[fullHead] = curBlock; | |||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||||
curBlock = 0; | |||||
} | |||||
} | |||||
} | |||||
if (fullHead == fullTail) { | |||||
// Exit loop if done. | |||||
if (closeFile) { | |||||
break; | |||||
} | |||||
} else if (!sd.card()->isBusy()) { | |||||
// Get address of block to write. | |||||
block_t* pBlock = fullQueue[fullTail]; | |||||
fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0; | |||||
// Write block to SD. | |||||
uint32_t usec = micros(); | |||||
if (!sd.card()->writeData((uint8_t*)pBlock)) { | |||||
error("write data failed"); | |||||
} | |||||
usec = micros() - usec; | |||||
if (usec > maxLatency) { | |||||
maxLatency = usec; | |||||
} | |||||
// Move block to empty queue. | |||||
emptyStack[emptyTop++] = pBlock; | |||||
bn++; | |||||
if (bn == FILE_BLOCK_COUNT) { | |||||
// File full so stop | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
if (!sd.card()->writeStop()) { | |||||
error("writeStop failed"); | |||||
} | |||||
Serial.print(F("Min Free buffers: ")); | |||||
Serial.println(minTop); | |||||
Serial.print(F("Max block write usec: ")); | |||||
Serial.println(maxLatency); | |||||
Serial.print(F("Overruns: ")); | |||||
Serial.println(overrunTotal); | |||||
// Truncate file if recording stopped early. | |||||
if (bn != FILE_BLOCK_COUNT) { | |||||
Serial.println(F("Truncating file")); | |||||
if (!binFile.truncate(512L * bn)) { | |||||
error("Can't truncate file"); | |||||
} | |||||
} | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
void renameBinFile() { | |||||
if (!binFile.rename(sd.vwd(), binName)) { | |||||
error("Can't rename file"); | |||||
} | |||||
Serial.print(F("File renamed: ")); | |||||
Serial.println(binName); | |||||
Serial.print("File size: "); | |||||
Serial.print(binFile.fileSize()/512); | |||||
Serial.println(F(" blocks")); | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void testSensor() { | |||||
const uint32_t interval = 200000; | |||||
int32_t diff; | |||||
data_t data; | |||||
Serial.println(F("\nTesting - type any character to stop\n")); | |||||
// Wait for Serial Idle. | |||||
delay(1000); | |||||
printHeader(&Serial); | |||||
uint32_t m = micros(); | |||||
while (!Serial.available()) { | |||||
m += interval; | |||||
do { | |||||
diff = m - micros(); | |||||
} while (diff > 0); | |||||
acquireData(&data); | |||||
printData(&Serial, &data); | |||||
} | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void setup(void) { | |||||
if (ERROR_LED_PIN >= 0) { | |||||
pinMode(ERROR_LED_PIN, OUTPUT); | |||||
} | |||||
Serial.begin(9600); | |||||
// Wait for USB Serial | |||||
while (!Serial) { | |||||
SysCall::yield(); | |||||
} | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
Serial.print(F("Records/block: ")); | |||||
Serial.println(DATA_DIM); | |||||
if (sizeof(block_t) != 512) { | |||||
error("Invalid block size"); | |||||
} | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
sd.initErrorPrint(&Serial); | |||||
fatalBlink(); | |||||
} | |||||
userSetup(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void loop(void) { | |||||
// Read any Serial data. | |||||
do { | |||||
delay(10); | |||||
} while (Serial.available() && Serial.read() >= 0); | |||||
Serial.println(); | |||||
Serial.println(F("type:")); | |||||
Serial.println(F("b - open existing bin file")); | |||||
Serial.println(F("c - convert file to csv")); | |||||
Serial.println(F("d - dump data to Serial")); | |||||
Serial.println(F("e - overrun error details")); | |||||
Serial.println(F("l - list files")); | |||||
Serial.println(F("r - record data")); | |||||
Serial.println(F("t - test without logging")); | |||||
while(!Serial.available()) { | |||||
SysCall::yield(); | |||||
} | |||||
#if WDT_YIELD_TIME_MICROS | |||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||||
SysCall::halt(); | |||||
#endif | |||||
char c = tolower(Serial.read()); | |||||
// Discard extra Serial data. | |||||
do { | |||||
delay(10); | |||||
} while (Serial.available() && Serial.read() >= 0); | |||||
if (ERROR_LED_PIN >= 0) { | |||||
digitalWrite(ERROR_LED_PIN, LOW); | |||||
} | |||||
if (c == 'b') { | |||||
openBinFile(); | |||||
} else if (c == 'c') { | |||||
binaryToCsv(); | |||||
} else if (c == 'd') { | |||||
dumpData(); | |||||
} else if (c == 'e') { | |||||
checkOverrun(); | |||||
} else if (c == 'l') { | |||||
Serial.println(F("\nls:")); | |||||
sd.ls(&Serial, LS_SIZE); | |||||
} else if (c == 'r') { | |||||
logData(); | |||||
} else if (c == 't') { | |||||
testSensor(); | |||||
} else { | |||||
Serial.println(F("Invalid entry")); | |||||
} | |||||
} |
// Empty file with name LowLatencyLoggerMPU6050.ino to make IDE happy. | |||||
// User data functions. Modify these functions for your data items. | |||||
#include "UserTypes.h" | |||||
#include "Wire.h" | |||||
#include "I2Cdev.h" | |||||
#include "MPU6050.h" | |||||
//------------------------------------------------------------------------------ | |||||
MPU6050 mpu; | |||||
static uint32_t startMicros; | |||||
// Acquire a data record. | |||||
void acquireData(data_t* data) { | |||||
data->time = micros(); | |||||
mpu.getMotion6(&data->ax, &data->ay, &data->az, | |||||
&data->gx, &data->gy, &data->gz); | |||||
} | |||||
// setup AVR I2C | |||||
void userSetup() { | |||||
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE | |||||
Wire.begin(); | |||||
Wire.setClock(400000); | |||||
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE | |||||
Fastwire::setup(400, true); | |||||
#endif | |||||
mpu.initialize(); | |||||
} | |||||
// Print a data record. | |||||
void printData(Print* pr, data_t* data) { | |||||
if (startMicros == 0) { | |||||
startMicros = data->time; | |||||
} | |||||
pr->print(data->time- startMicros); | |||||
pr->write(','); | |||||
pr->print(data->ax); | |||||
pr->write(','); | |||||
pr->print(data->ay); | |||||
pr->write(','); | |||||
pr->print(data->az); | |||||
pr->write(','); | |||||
pr->print(data->gx); | |||||
pr->write(','); | |||||
pr->print(data->gy); | |||||
pr->write(','); | |||||
pr->println(data->gz); | |||||
} | |||||
// Print data header. | |||||
void printHeader(Print* pr) { | |||||
startMicros = 0; | |||||
pr->println(F("micros,ax,ay,az,gx,gy,gz")); | |||||
} |
#ifndef UserTypes_h | |||||
#define UserTypes_h | |||||
#include "Arduino.h" | |||||
#define FILE_BASE_NAME "mpuraw" | |||||
struct data_t { | |||||
unsigned long time; | |||||
int16_t ax; | |||||
int16_t ay; | |||||
int16_t az; | |||||
int16_t gx; | |||||
int16_t gy; | |||||
int16_t gz; | |||||
}; | |||||
void acquireData(data_t* data); | |||||
void printData(Print* pr, data_t* data); | |||||
void printHeader(Print* pr); | |||||
void userSetup(); | |||||
#endif // UserTypes_h |
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
cout << F("FreeStack: ") << FreeStack() << endl; | cout << F("FreeStack: ") << FreeStack() << endl; | ||||
// initialize the SD card at SPI_FULL_SPEED for best performance. | |||||
// try SPI_HALF_SPEED if bus errors occur. | |||||
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
// to 10 to disable the Ethernet controller. | // to 10 to disable the Ethernet controller. | ||||
const int8_t DISABLE_CHIP_SELECT = -1; | const int8_t DISABLE_CHIP_SELECT = -1; | ||||
// | // | ||||
// Test with reduced SPI speed for breadboards. | |||||
// Change spiSpeed to SPI_FULL_SPEED for better performance | |||||
// Use SPI_QUARTER_SPEED for even slower SPI bus speed | |||||
const uint8_t spiSpeed = SPI_HALF_SPEED; | |||||
// Test with reduced SPI speed for breadboards. SD_SCK_MHZ(4) will select | |||||
// the highest speed supported by the board that is not over 4 MHz. | |||||
// Change SPI_SPEED to SD_SCK_MHZ(50) for best performance. | |||||
#define SPI_SPEED SD_SCK_MHZ(4) | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// File system object. | // File system object. | ||||
SdFat sd; | SdFat sd; | ||||
void cardOrSpeed() { | void cardOrSpeed() { | ||||
cout << F("Try another SD card or reduce the SPI bus speed.\n"); | cout << F("Try another SD card or reduce the SPI bus speed.\n"); | ||||
cout << F("Edit spiSpeed in this program to change it.\n"); | |||||
cout << F("Edit SPI_SPEED in this program to change it.\n"); | |||||
} | } | ||||
void reformatMsg() { | void reformatMsg() { | ||||
pinMode(DISABLE_CHIP_SELECT, OUTPUT); | pinMode(DISABLE_CHIP_SELECT, OUTPUT); | ||||
digitalWrite(DISABLE_CHIP_SELECT, HIGH); | digitalWrite(DISABLE_CHIP_SELECT, HIGH); | ||||
} | } | ||||
if (!sd.begin(chipSelect, spiSpeed)) { | |||||
if (!sd.begin(chipSelect, SPI_SPEED)) { | |||||
if (sd.card()->errorCode()) { | if (sd.card()->errorCode()) { | ||||
cout << F( | cout << F( | ||||
"\nSD initialization failed.\n" | "\nSD initialization failed.\n" |
* can be used for high speed data logging. | * can be used for high speed data logging. | ||||
* | * | ||||
* This program simulates logging from a source that produces | * This program simulates logging from a source that produces | ||||
* data at a constant rate of one block every MICROS_PER_BLOCK. | |||||
* | |||||
* If a high quality SanDisk card is used with this program | |||||
* no overruns occur and the maximum block write time is | |||||
* under 2000 micros. | |||||
* data at a constant rate of RATE_KB_PER_SEC. | |||||
* | * | ||||
* Note: Apps should create a very large file then truncates it | * Note: Apps should create a very large file then truncates it | ||||
* to the length that is used for a logging. It only takes | * to the length that is used for a logging. It only takes | ||||
// SD chip select pin | // SD chip select pin | ||||
const uint8_t chipSelect = SS; | const uint8_t chipSelect = SS; | ||||
// number of blocks in the contiguous file | |||||
const uint32_t BLOCK_COUNT = 10000UL; | |||||
const uint32_t RATE_KB_PER_SEC = 100; | |||||
const uint32_t TEST_TIME_SEC = 100; | |||||
// time to produce a block of data | |||||
const uint32_t MICROS_PER_BLOCK = 10000; | |||||
// Time between printing progress dots | |||||
const uint32_t DOT_TIME_MS = 5000UL; | |||||
// number of blocks in the contiguous file | |||||
const uint32_t BLOCK_COUNT = (1000*RATE_KB_PER_SEC*TEST_TIME_SEC + 511)/512; | |||||
// file system | // file system | ||||
SdFat sd; | SdFat sd; | ||||
// store error strings in flash to save RAM | // store error strings in flash to save RAM | ||||
#define error(s) sd.errorHalt(F(s)) | #define error(s) sd.errorHalt(F(s)) | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// log of first overruns | |||||
#define OVER_DIM 20 | |||||
struct { | |||||
uint32_t block; | |||||
uint32_t micros; | |||||
} over[OVER_DIM]; | |||||
//------------------------------------------------------------------------------ | |||||
void setup(void) { | void setup(void) { | ||||
Serial.begin(9600); | Serial.begin(9600); | ||||
cout << F("FreeStack: ") << FreeStack() << endl; | cout << F("FreeStack: ") << FreeStack() << endl; | ||||
// initialize the SD card at SPI_FULL_SPEED for best performance. | |||||
// try SPI_HALF_SPEED if bus errors occur. | |||||
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
sd.remove("RawWrite.txt"); | sd.remove("RawWrite.txt"); | ||||
// create a contiguous file | // create a contiguous file | ||||
if (!file.createContiguous(sd.vwd(), "RawWrite.txt", 512UL*BLOCK_COUNT)) { | |||||
if (!file.createContiguous("RawWrite.txt", 512UL*BLOCK_COUNT)) { | |||||
error("createContiguous failed"); | error("createContiguous failed"); | ||||
} | } | ||||
// get the location of the file's blocks | // get the location of the file's blocks | ||||
pCache[i + 63] = '\n'; | pCache[i + 63] = '\n'; | ||||
} | } | ||||
cout << F("Start raw write of ") << file.fileSize() << F(" bytes at\n"); | |||||
cout << 512000000UL/MICROS_PER_BLOCK << F(" bytes per second\n"); | |||||
cout << F("Please wait ") << (BLOCK_COUNT*MICROS_PER_BLOCK)/1000000UL; | |||||
cout << F(" seconds\n"); | |||||
cout << F("Start raw write of ") << file.fileSize()/1000UL << F(" KB\n"); | |||||
cout << F("Target rate: ") << RATE_KB_PER_SEC << F(" KB/sec\n"); | |||||
cout << F("Target time: ") << TEST_TIME_SEC << F(" seconds\n"); | |||||
// tell card to setup for multiple block write with pre-erase | // tell card to setup for multiple block write with pre-erase | ||||
if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) { | if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) { | ||||
error("writeStart failed"); | error("writeStart failed"); | ||||
} | } | ||||
// init stats | // init stats | ||||
uint16_t overruns = 0; | |||||
delay(1000); | |||||
uint32_t dotCount = 0; | |||||
uint32_t maxQueuePrint = 0; | |||||
uint32_t maxWriteTime = 0; | uint32_t maxWriteTime = 0; | ||||
uint32_t t = micros(); | |||||
uint32_t tNext = t; | |||||
uint32_t minWriteTime = 9999999; | |||||
uint32_t totalWriteTime = 0; | |||||
uint32_t maxQueueSize = 0; | |||||
uint32_t nWrite = 0; | |||||
uint32_t b = 0; | |||||
// write data | // write data | ||||
for (uint32_t b = 0; b < BLOCK_COUNT; b++) { | |||||
// write must be done by this time | |||||
tNext += MICROS_PER_BLOCK; | |||||
uint32_t startTime = millis(); | |||||
while (nWrite < BLOCK_COUNT) { | |||||
uint32_t nProduced = RATE_KB_PER_SEC*(millis() - startTime)/512UL; | |||||
uint32_t queueSize = nProduced - nWrite; | |||||
if (queueSize == 0) continue; | |||||
if (queueSize > maxQueueSize) { | |||||
maxQueueSize = queueSize; | |||||
} | |||||
if ((millis() - startTime - dotCount*DOT_TIME_MS) > DOT_TIME_MS) { | |||||
if (maxQueueSize != maxQueuePrint) { | |||||
cout << F("\nQ: ") << maxQueueSize << endl; | |||||
maxQueuePrint = maxQueueSize; | |||||
} else { | |||||
cout << "."; | |||||
if (++dotCount%10 == 0) { | |||||
cout << endl; | |||||
} | |||||
} | |||||
} | |||||
// put block number at start of first line in block | // put block number at start of first line in block | ||||
uint32_t n = b; | |||||
uint32_t n = b++; | |||||
for (int8_t d = 5; d >= 0; d--) { | for (int8_t d = 5; d >= 0; d--) { | ||||
pCache[d] = n || d == 5 ? n % 10 + '0' : ' '; | pCache[d] = n || d == 5 ? n % 10 + '0' : ' '; | ||||
n /= 10; | n /= 10; | ||||
error("writeData failed"); | error("writeData failed"); | ||||
} | } | ||||
tw = micros() - tw; | tw = micros() - tw; | ||||
totalWriteTime += tw; | |||||
// check for max write time | // check for max write time | ||||
if (tw > maxWriteTime) { | if (tw > maxWriteTime) { | ||||
maxWriteTime = tw; | maxWriteTime = tw; | ||||
} | } | ||||
// check for overrun | |||||
if (micros() > tNext) { | |||||
if (overruns < OVER_DIM) { | |||||
over[overruns].block = b; | |||||
over[overruns].micros = tw; | |||||
} | |||||
overruns++; | |||||
// advance time to reflect overrun | |||||
tNext = micros(); | |||||
} else { | |||||
// wait for time to write next block | |||||
while(micros() < tNext); | |||||
if (tw < minWriteTime) { | |||||
minWriteTime = tw; | |||||
} | } | ||||
nWrite++; | |||||
} | } | ||||
// total write time | |||||
t = micros() - t; | |||||
uint32_t endTime = millis(); | |||||
uint32_t avgWriteTime = totalWriteTime/BLOCK_COUNT; | |||||
// end multiple block write mode | // end multiple block write mode | ||||
if (!sd.card()->writeStop()) { | if (!sd.card()->writeStop()) { | ||||
error("writeStop failed"); | error("writeStop failed"); | ||||
} | } | ||||
cout << F("Done\n"); | |||||
cout << F("Elapsed time: ") << setprecision(3)<< 1.e-6*t; | |||||
cout << F("\nDone\n"); | |||||
cout << F("maxQueueSize: ") << maxQueueSize << endl; | |||||
cout << F("Elapsed time: ") << setprecision(3)<< 1.e-3*(endTime - startTime); | |||||
cout << F(" seconds\n"); | cout << F(" seconds\n"); | ||||
cout << F("Max write time: ") << maxWriteTime << F(" micros\n"); | |||||
cout << F("Overruns: ") << overruns << endl; | |||||
if (overruns) { | |||||
uint8_t n = overruns > OVER_DIM ? OVER_DIM : overruns; | |||||
cout << F("fileBlock,micros") << endl; | |||||
for (uint8_t i = 0; i < n; i++) { | |||||
cout << over[i].block << ',' << over[i].micros << endl; | |||||
} | |||||
} | |||||
cout << F("Min block write time: ") << minWriteTime << F(" micros\n"); | |||||
cout << F("Max block write time: ") << maxWriteTime << F(" micros\n"); | |||||
cout << F("Avg block write time: ") << avgWriteTime << F(" micros\n"); | |||||
// close file for next pass of loop | // close file for next pass of loop | ||||
file.close(); | file.close(); | ||||
Serial.println(); | Serial.println(); |
// Function to read a CSV text file one field at a time. | |||||
// | |||||
#include <SPI.h> | |||||
#include <SdFat.h> | |||||
#define CS_PIN SS | |||||
SdFat SD; | |||||
File file; | |||||
/* | |||||
* Read a file one field at a time. | |||||
* | |||||
* file - File to read. | |||||
* | |||||
* str - Character array for the field. | |||||
* | |||||
* size - Size of str array. | |||||
* | |||||
* delim - String containing field delimiters. | |||||
* | |||||
* return - length of field including terminating delimiter. | |||||
* | |||||
* Note, the last character of str will not be a delimiter if | |||||
* a read error occurs, the field is too long, or the file | |||||
* does not end with a delimiter. Consider this an error | |||||
* if not at end-of-file. | |||||
* | |||||
*/ | |||||
size_t readField(File* file, char* str, size_t size, const char* delim) { | |||||
char ch; | |||||
size_t n = 0; | |||||
while ((n + 1) < size && file->read(&ch, 1) == 1) { | |||||
// Delete CR. | |||||
if (ch == '\r') { | |||||
continue; | |||||
} | |||||
str[n++] = ch; | |||||
if (strchr(delim, ch)) { | |||||
break; | |||||
} | |||||
} | |||||
str[n] = '\0'; | |||||
return n; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
#define errorHalt(msg) {Serial.println(F(msg)); SysCall::halt();} | |||||
//------------------------------------------------------------------------------ | |||||
void setup() { | |||||
Serial.begin(9600); | |||||
// Wait for USB Serial | |||||
while (!Serial) { | |||||
SysCall::yield(); | |||||
} | |||||
Serial.println("Type any character to start"); | |||||
while (!Serial.available()) { | |||||
SysCall::yield(); | |||||
} | |||||
// Initialize the SD. | |||||
if (!SD.begin(CS_PIN)) errorHalt("begin failed"); | |||||
// Create or open the file. | |||||
file = SD.open("READTEST.TXT", FILE_WRITE); | |||||
if (!file) errorHalt("open failed"); | |||||
// Rewind file so test data is not appended. | |||||
file.rewind(); | |||||
// Write test data. | |||||
file.print(F( | |||||
"field_1_1,field_1_2,field_1_3\r\n" | |||||
"field_2_1,field_2_2,field_2_3\r\n" | |||||
"field_3_1,field_3_2\r\n" // missing a field | |||||
"field_4_1,field_4_2,field_4_3\r\n" | |||||
"field_5_1,field_5_2,field_5_3" // no delimiter | |||||
)); | |||||
// Rewind the file for read. | |||||
file.rewind(); | |||||
size_t n; // Length of returned field with delimiter. | |||||
char str[20]; // Must hold longest field with delimiter and zero byte. | |||||
// Read the file and print fields. | |||||
while (true) { | |||||
n = readField(&file, str, sizeof(str), ",\n"); | |||||
// done if Error or at EOF. | |||||
if (n == 0) break; | |||||
// Print the type of delimiter. | |||||
if (str[n-1] == ',' || str[n-1] == '\n') { | |||||
Serial.print(str[n-1] == ',' ? F("comma: ") : F("endl: ")); | |||||
// Remove the delimiter. | |||||
str[n-1] = 0; | |||||
} else { | |||||
// At eof, too long, or read error. Too long is error. | |||||
Serial.print(file.available() ? F("error: ") : F("eof: ")); | |||||
} | |||||
// Print the field. | |||||
Serial.println(str); | |||||
} | |||||
file.close(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void loop() { | |||||
} | |||||
/* | |||||
* This example reads a simple CSV, comma-separated values, file. | |||||
* Each line of the file has a label and three values, a long and two floats. | |||||
*/ | |||||
#include <SPI.h> | |||||
#include "SdFat.h" | |||||
// SD chip select pin | |||||
const uint8_t chipSelect = SS; | |||||
// file system object | |||||
SdFat sd; | |||||
// create Serial stream | |||||
ArduinoOutStream cout(Serial); | |||||
char fileName[] = "testfile.csv"; | |||||
//------------------------------------------------------------------------------ | |||||
// store error strings in flash to save RAM | |||||
#define error(s) sd.errorHalt(F(s)) | |||||
//------------------------------------------------------------------------------ | |||||
// read and print CSV test file | |||||
void readFile() { | |||||
long lg = 0; | |||||
float f1, f2; | |||||
char text[10]; | |||||
char c1, c2, c3; // space for commas. | |||||
// open input file | |||||
ifstream sdin(fileName); | |||||
// check for open error | |||||
if (!sdin.is_open()) { | |||||
error("open"); | |||||
} | |||||
// read until input fails | |||||
while (1) { | |||||
// Get text field. | |||||
sdin.get(text, sizeof(text), ','); | |||||
// Assume EOF if fail. | |||||
if (sdin.fail()) { | |||||
break; | |||||
} | |||||
// Get commas and numbers. | |||||
sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2; | |||||
// Skip CR/LF. | |||||
sdin.skipWhite(); | |||||
if (sdin.fail()) { | |||||
error("bad input"); | |||||
} | |||||
// error in line if not commas | |||||
if (c1 != ',' || c2 != ',' || c3 != ',') { | |||||
error("comma"); | |||||
} | |||||
// print in six character wide columns | |||||
cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl; | |||||
} | |||||
// Error in an input line if file is not at EOF. | |||||
if (!sdin.eof()) { | |||||
error("readFile"); | |||||
} | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
// write test file | |||||
void writeFile() { | |||||
// create or open and truncate output file | |||||
ofstream sdout(fileName); | |||||
// write file from string stored in flash | |||||
sdout << F( | |||||
"Line 1,1,2.3,4.5\n" | |||||
"Line 2,6,7.8,9.0\n" | |||||
"Line 3,9,8.7,6.5\n" | |||||
"Line 4,-4,-3.2,-1\n") << flush; | |||||
// check for any errors | |||||
if (!sdout) { | |||||
error("writeFile"); | |||||
} | |||||
sdout.close(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void setup() { | |||||
Serial.begin(9600); | |||||
// Wait for USB Serial | |||||
while (!Serial) { | |||||
SysCall::yield(); | |||||
} | |||||
cout << F("Type any character to start\n"); | |||||
while (!Serial.available()) { | |||||
SysCall::yield(); | |||||
} | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | |||||
} | |||||
// create test file | |||||
writeFile(); | |||||
cout << endl; | |||||
// read and print test | |||||
readFile(); | |||||
cout << "\nDone!" << endl; | |||||
} | |||||
void loop() {} |
** MOSI - pin 11 | ** MOSI - pin 11 | ||||
** MISO - pin 12 | ** MISO - pin 12 | ||||
** CLK - pin 13 | ** CLK - pin 13 | ||||
** CS - pin 4 | |||||
created Nov 2010 | created Nov 2010 | ||||
by David A. Mellis | by David A. Mellis | ||||
This example code is in the public domain. | This example code is in the public domain. | ||||
*/ | */ | ||||
#define SD_CS_PIN SS | |||||
#include <SPI.h> | #include <SPI.h> | ||||
//#include <SD.h> | //#include <SD.h> | ||||
#include "SdFat.h" | #include "SdFat.h" | ||||
SdFat SD; | SdFat SD; | ||||
#define SD_CS_PIN SS | |||||
File myFile; | File myFile; | ||||
void setup() | |||||
{ | |||||
void setup() { | |||||
// Open serial communications and wait for port to open: | // Open serial communications and wait for port to open: | ||||
Serial.begin(9600); | Serial.begin(9600); | ||||
// Wait for USB Serial | |||||
while (!Serial) { | while (!Serial) { | ||||
SysCall::yield(); | |||||
; // wait for serial port to connect. Needed for native USB port only | |||||
} | } | ||||
Serial.print("Initializing SD card..."); | Serial.print("Initializing SD card..."); | ||||
// On the Ethernet Shield, CS is pin 4. It's set as an output by default. | |||||
// Note that even if it's not used as the CS pin, the hardware SS pin | |||||
// (10 on most Arduino boards, 53 on the Mega) must be left as an output | |||||
// or the SD library functions will not work. | |||||
pinMode(10, OUTPUT); | |||||
if (!SD.begin(SD_CS_PIN)) { | if (!SD.begin(SD_CS_PIN)) { | ||||
Serial.println("initialization failed!"); | Serial.println("initialization failed!"); | ||||
} | } | ||||
} | } | ||||
void loop() | |||||
{ | |||||
void loop() { | |||||
// nothing happens after setup | // nothing happens after setup | ||||
} | } | ||||
// Ported to SdFat from the native Arduino SD library example by Bill Greiman | |||||
// On the Ethernet Shield, CS is pin 4. SdFat handles setting SS | |||||
const int chipSelect = 4; | |||||
/* | |||||
SD card read/write | |||||
This example shows how to read and write data to and from an SD card file | |||||
The circuit: | |||||
* SD card attached to SPI bus as follows: | |||||
** MOSI - pin 11 | |||||
** MISO - pin 12 | |||||
** CLK - pin 13 | |||||
** CS - pin 4 | |||||
created Nov 2010 | |||||
by David A. Mellis | |||||
updated 2 Dec 2010 | |||||
by Tom Igoe | |||||
modified by Bill Greiman 11 Apr 2011 | |||||
This example code is in the public domain. | |||||
*/ | |||||
#include <SPI.h> | |||||
#include "SdFat.h" | |||||
SdFat sd; | |||||
SdFile myFile; | |||||
void setup() { | |||||
Serial.begin(9600); | |||||
// Wait for USB Serial | |||||
while (!Serial) { | |||||
SysCall::yield(); | |||||
} | |||||
Serial.println("Type any character to start"); | |||||
while (!Serial.available()) { | |||||
SysCall::yield(); | |||||
} | |||||
// Initialize SdFat or print a detailed error message and halt | |||||
// Use half speed like the native library. | |||||
// change to SPI_FULL_SPEED for more performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
sd.initErrorHalt(); | |||||
} | |||||
// open the file for write at end like the Native SD library | |||||
if (!myFile.open("test.txt", O_RDWR | O_CREAT | O_AT_END)) { | |||||
sd.errorHalt("opening test.txt for write failed"); | |||||
} | |||||
// if the file opened okay, write to it: | |||||
Serial.print("Writing to test.txt..."); | |||||
myFile.println("testing 1, 2, 3."); | |||||
// close the file: | |||||
myFile.close(); | |||||
Serial.println("done."); | |||||
// re-open the file for reading: | |||||
if (!myFile.open("test.txt", O_READ)) { | |||||
sd.errorHalt("opening test.txt for read failed"); | |||||
} | |||||
Serial.println("test.txt:"); | |||||
// read from the file until there's nothing else in it: | |||||
int data; | |||||
while ((data = myFile.read()) >= 0) { | |||||
Serial.write(data); | |||||
} | |||||
// close the file: | |||||
myFile.close(); | |||||
} | |||||
void loop() { | |||||
// nothing happens after setup | |||||
} | |||||
/* | |||||
* Example use of two SPI ports on an STM32 board. | |||||
* Note SPI speed is limited to 18 MHz. | |||||
*/ | |||||
#include <SPI.h> | |||||
#include "SdFat.h" | |||||
#include "FreeStack.h" | |||||
// set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes | |||||
// Use first SPI port | |||||
SdFat sd1(1); | |||||
// SdFatEX sd1(1); | |||||
const uint8_t SD1_CS = PA4; // chip select for sd1 | |||||
// Use second SPI port | |||||
SdFat sd2(2); | |||||
// SdFatEX sd2(2); | |||||
const uint8_t SD2_CS = PB12; // chip select for sd2 | |||||
const uint8_t BUF_DIM = 100; | |||||
uint8_t buf[BUF_DIM]; | |||||
const uint32_t FILE_SIZE = 1000000; | |||||
const uint16_t NWRITE = FILE_SIZE/BUF_DIM; | |||||
//------------------------------------------------------------------------------ | |||||
// print error msg, any SD error codes, and halt. | |||||
// store messages in flash | |||||
#define errorExit(msg) errorHalt(F(msg)) | |||||
#define initError(msg) initErrorHalt(F(msg)) | |||||
//------------------------------------------------------------------------------ | |||||
void setup() { | |||||
Serial.begin(9600); | |||||
// Wait for USB Serial | |||||
while (!Serial) { | |||||
SysCall::yield(); | |||||
} | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
// fill buffer with known data | |||||
for (size_t i = 0; i < sizeof(buf); i++) { | |||||
buf[i] = i; | |||||
} | |||||
Serial.println(F("type any character to start")); | |||||
while (!Serial.available()) { | |||||
SysCall::yield(); | |||||
} | |||||
// initialize the first card | |||||
if (!sd1.begin(SD1_CS, SD_SCK_MHZ(18))) { | |||||
sd1.initError("sd1:"); | |||||
} | |||||
// create Dir1 on sd1 if it does not exist | |||||
if (!sd1.exists("/Dir1")) { | |||||
if (!sd1.mkdir("/Dir1")) { | |||||
sd1.errorExit("sd1.mkdir"); | |||||
} | |||||
} | |||||
// initialize the second card | |||||
if (!sd2.begin(SD2_CS, SD_SCK_MHZ(18))) { | |||||
sd2.initError("sd2:"); | |||||
} | |||||
// create Dir2 on sd2 if it does not exist | |||||
if (!sd2.exists("/Dir2")) { | |||||
if (!sd2.mkdir("/Dir2")) { | |||||
sd2.errorExit("sd2.mkdir"); | |||||
} | |||||
} | |||||
// list root directory on both cards | |||||
Serial.println(F("------sd1 root-------")); | |||||
sd1.ls(); | |||||
Serial.println(F("------sd2 root-------")); | |||||
sd2.ls(); | |||||
// make /Dir1 the default directory for sd1 | |||||
if (!sd1.chdir("/Dir1")) { | |||||
sd1.errorExit("sd1.chdir"); | |||||
} | |||||
// remove test.bin from /Dir1 directory of sd1 | |||||
if (sd1.exists("test.bin")) { | |||||
if (!sd1.remove("test.bin")) { | |||||
sd2.errorExit("remove test.bin"); | |||||
} | |||||
} | |||||
// make /Dir2 the default directory for sd2 | |||||
if (!sd2.chdir("/Dir2")) { | |||||
sd2.errorExit("sd2.chdir"); | |||||
} | |||||
// remove rename.bin from /Dir2 directory of sd2 | |||||
if (sd2.exists("rename.bin")) { | |||||
if (!sd2.remove("rename.bin")) { | |||||
sd2.errorExit("remove rename.bin"); | |||||
} | |||||
} | |||||
// list current directory on both cards | |||||
Serial.println(F("------sd1 Dir1-------")); | |||||
sd1.ls(); | |||||
Serial.println(F("------sd2 Dir2-------")); | |||||
sd2.ls(); | |||||
Serial.println(F("---------------------")); | |||||
// set the current working directory for open() to sd1 | |||||
sd1.chvol(); | |||||
// create or open /Dir1/test.bin and truncate it to zero length | |||||
SdFile file1; | |||||
if (!file1.open("test.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||||
sd1.errorExit("file1"); | |||||
} | |||||
Serial.println(F("Writing test.bin to sd1")); | |||||
// write data to /Dir1/test.bin on sd1 | |||||
for (uint16_t i = 0; i < NWRITE; i++) { | |||||
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { | |||||
sd1.errorExit("sd1.write"); | |||||
} | |||||
} | |||||
// set the current working directory for open() to sd2 | |||||
sd2.chvol(); | |||||
// create or open /Dir2/copy.bin and truncate it to zero length | |||||
SdFile file2; | |||||
if (!file2.open("copy.bin", O_WRITE | O_CREAT | O_TRUNC)) { | |||||
sd2.errorExit("file2"); | |||||
} | |||||
Serial.println(F("Copying test.bin to copy.bin")); | |||||
// copy file1 to file2 | |||||
file1.rewind(); | |||||
uint32_t t = millis(); | |||||
while (1) { | |||||
int n = file1.read(buf, sizeof(buf)); | |||||
if (n < 0) { | |||||
sd1.errorExit("read1"); | |||||
} | |||||
if (n == 0) { | |||||
break; | |||||
} | |||||
if ((int)file2.write(buf, n) != n) { | |||||
sd2.errorExit("write2"); | |||||
} | |||||
} | |||||
t = millis() - t; | |||||
Serial.print(F("File size: ")); | |||||
Serial.println(file2.fileSize()); | |||||
Serial.print(F("Copy time: ")); | |||||
Serial.print(t); | |||||
Serial.println(F(" millis")); | |||||
// close test.bin | |||||
file1.close(); | |||||
file2.close(); | |||||
// list current directory on both cards | |||||
Serial.println(F("------sd1 -------")); | |||||
sd1.ls("/", LS_R | LS_DATE | LS_SIZE); | |||||
Serial.println(F("------sd2 -------")); | |||||
sd2.ls("/", LS_R | LS_DATE | LS_SIZE); | |||||
Serial.println(F("---------------------")); | |||||
Serial.println(F("Renaming copy.bin")); | |||||
// rename the copy | |||||
if (!sd2.rename("copy.bin", "rename.bin")) { | |||||
sd2.errorExit("sd2.rename"); | |||||
} | |||||
// list current directory on both cards | |||||
Serial.println(F("------sd1 -------")); | |||||
sd1.ls("/", LS_R | LS_DATE | LS_SIZE); | |||||
Serial.println(F("------sd2 -------")); | |||||
sd2.ls("/", LS_R | LS_DATE | LS_SIZE); | |||||
Serial.println(F("---------------------")); | |||||
Serial.println(F("Done")); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void loop() {} |
// Adafruit SD shields and modules: pin 10 | // Adafruit SD shields and modules: pin 10 | ||||
const uint8_t chipSelect = SS; | const uint8_t chipSelect = SS; | ||||
// Change spiSpeed to SPI_FULL_SPEED for better performance | |||||
// Use SPI_QUARTER_SPEED for even slower SPI bus speed | |||||
const uint8_t spiSpeed = SPI_HALF_SPEED; | |||||
// Initialize at highest supported speed not over 50 MHz. | |||||
// Reduce max speed if errors occur. | |||||
#define SPI_SPEED SD_SCK_MHZ(50) | |||||
// Serial output stream | // Serial output stream | ||||
ArduinoOutStream cout(Serial); | ArduinoOutStream cout(Serial); | ||||
char fat16str[] = "FAT16 "; | char fat16str[] = "FAT16 "; | ||||
char fat32str[] = "FAT32 "; | char fat32str[] = "FAT32 "; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
#define sdError(msg) sdError_F(F(msg)) | |||||
void sdError_F(const __FlashStringHelper* str) { | |||||
cout << F("error: "); | |||||
cout << str << endl; | |||||
#define sdError(msg) {cout << F("error: ") << F(msg) << endl; sdErrorHalt();} | |||||
//------------------------------------------------------------------------------ | |||||
void sdErrorHalt() { | |||||
if (card.errorCode()) { | if (card.errorCode()) { | ||||
cout << F("SD error: ") << hex << int(card.errorCode()); | cout << F("SD error: ") << hex << int(card.errorCode()); | ||||
cout << ',' << int(card.errorData()) << dec << endl; | cout << ',' << int(card.errorData()) << dec << endl; | ||||
return; | return; | ||||
} | } | ||||
if (!card.begin(chipSelect, spiSpeed)) { | |||||
if (!card.begin(chipSelect, SPI_SPEED)) { | |||||
cout << F( | cout << F( | ||||
"\nSD initialization failure!\n" | "\nSD initialization failure!\n" | ||||
"Is the SD card inserted correctly?\n" | "Is the SD card inserted correctly?\n" |
uint32_t eraseSize; | uint32_t eraseSize; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// store error strings in flash | // store error strings in flash | ||||
#define sdErrorMsg(msg) sdErrorMsg_F(F(msg)); | |||||
void sdErrorMsg_F(const __FlashStringHelper* str) { | |||||
cout << str << endl; | |||||
if (sd.card()->errorCode()) { | |||||
cout << F("SD errorCode: "); | |||||
cout << hex << int(sd.card()->errorCode()) << endl; | |||||
cout << F("SD errorData: "); | |||||
cout << int(sd.card()->errorData()) << dec << endl; | |||||
} | |||||
} | |||||
#define sdErrorMsg(msg) sd.errorPrint(F(msg)); | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
uint8_t cidDmp() { | uint8_t cidDmp() { | ||||
cid_t cid; | cid_t cid; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// print partition table | // print partition table | ||||
uint8_t partDmp() { | uint8_t partDmp() { | ||||
cache_t *p = sd.vol()->cacheClear(); | |||||
if (!p) { | |||||
sdErrorMsg("cacheClear failed"); | |||||
return false; | |||||
} | |||||
if (!sd.card()->readBlock(0, p->data)) { | |||||
mbr_t mbr; | |||||
if (!sd.card()->readBlock(0, (uint8_t*)&mbr)) { | |||||
sdErrorMsg("read MBR failed"); | sdErrorMsg("read MBR failed"); | ||||
return false; | return false; | ||||
} | } | ||||
for (uint8_t ip = 1; ip < 5; ip++) { | for (uint8_t ip = 1; ip < 5; ip++) { | ||||
part_t *pt = &p->mbr.part[ip - 1]; | |||||
part_t *pt = &mbr.part[ip - 1]; | |||||
if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) { | if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) { | ||||
cout << F("\nNo MBR. Assuming Super Floppy format.\n"); | cout << F("\nNo MBR. Assuming Super Floppy format.\n"); | ||||
return true; | return true; | ||||
cout << F("\nSD Partition Table\n"); | cout << F("\nSD Partition Table\n"); | ||||
cout << F("part,boot,type,start,length\n"); | cout << F("part,boot,type,start,length\n"); | ||||
for (uint8_t ip = 1; ip < 5; ip++) { | for (uint8_t ip = 1; ip < 5; ip++) { | ||||
part_t *pt = &p->mbr.part[ip - 1]; | |||||
part_t *pt = &mbr.part[ip - 1]; | |||||
cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type); | cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type); | ||||
cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl; | cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl; | ||||
} | } | ||||
} | } | ||||
uint32_t t = millis(); | uint32_t t = millis(); | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.cardBegin(SD_CHIP_SELECT, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.cardBegin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) { | |||||
sdErrorMsg("\ncardBegin failed"); | sdErrorMsg("\ncardBegin failed"); | ||||
return; | return; | ||||
} | } |
// | // | ||||
#include <SPI.h> | #include <SPI.h> | ||||
#include "SdFat.h" | #include "SdFat.h" | ||||
#if SD_SPI_CONFIGURATION >= 3 // Must be set in SdFat/SdFatConfig.h | |||||
#if ENABLE_SOFTWARE_SPI_CLASS // Must be set in SdFat/SdFatConfig.h | |||||
// | // | ||||
// Pin numbers in templates must be constants. | // Pin numbers in templates must be constants. | ||||
const uint8_t SOFT_MISO_PIN = 12; | const uint8_t SOFT_MISO_PIN = 12; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void loop() {} | void loop() {} | ||||
#else // SD_SPI_CONFIGURATION >= 3 | |||||
#error SD_SPI_CONFIGURATION must be set to 3 in SdFat/SdFatConfig.h | |||||
#endif //SD_SPI_CONFIGURATION >= 3 | |||||
#else // ENABLE_SOFTWARE_SPI_CLASS | |||||
#error ENABLE_SOFTWARE_SPI_CLASS must be set non-zero in SdFat/SdFatConfig.h | |||||
#endif //ENABLE_SOFTWARE_SPI_CLASS |
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
Serial.println(F("Starting test")); | Serial.println(F("Starting test")); | ||||
if (!sd.begin(SD_CS_PIN)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||||
sd.errorHalt(); | sd.errorHalt(); | ||||
} | } | ||||
case 3: | case 3: | ||||
for (uint16_t i = 0; i < 10000; i++) { | for (uint16_t i = 0; i < 10000; i++) { | ||||
uint32_t n = i + 1000000000UL; | |||||
#if PRINT_FIELD | #if PRINT_FIELD | ||||
stdioFile.printField(i + 1000000000UL, '\n'); | |||||
stdioFile.printField(n, '\n'); | |||||
#else // PRINT_FIELD | #else // PRINT_FIELD | ||||
stdioFile.println(i + 1000000000UL); | |||||
stdioFile.println(n); | |||||
#endif // PRINT_FIELD | #endif // PRINT_FIELD | ||||
} | } | ||||
break; | break; |
/* | |||||
* Example use of three SD cards. | |||||
*/ | |||||
#include <SPI.h> | |||||
#include "SdFat.h" | |||||
#include "FreeStack.h" | |||||
#if SD_SPI_CONFIGURATION >= 3 // Must be set in SdFat/SdFatConfig.h | |||||
// SD1 is a microSD on hardware SPI pins 50-52 | |||||
// Using my fast custom SPI | |||||
SdFat sd1; | |||||
const uint8_t SD1_CS = 53; | |||||
// SD2 is a Catalex shield on hardware SPI pins 50-52 | |||||
// Using the standard Arduino SPI library | |||||
SdFatLibSpi sd2; | |||||
const uint8_t SD2_CS = 4; | |||||
// SD3 is a Adafruit data logging shield on pins 10-13 | |||||
// Using Software SPI | |||||
SdFatSoftSpi<12, 11, 13> sd3; | |||||
const uint8_t SD3_CS = 10; | |||||
const uint8_t BUF_DIM = 100; | |||||
uint8_t buf[BUF_DIM]; | |||||
const uint32_t FILE_SIZE = 1000000; | |||||
const uint16_t NWRITE = FILE_SIZE/BUF_DIM; | |||||
//------------------------------------------------------------------------------ | |||||
// print error msg, any SD error codes, and halt. | |||||
// store messages in flash | |||||
#define errorExit(msg) errorHalt(F(msg)) | |||||
#define initError(msg) initErrorHalt(F(msg)) | |||||
//------------------------------------------------------------------------------ | |||||
void list() { | |||||
// list current directory on all cards | |||||
Serial.println(F("------sd1-------")); | |||||
sd1.ls("/", LS_SIZE|LS_R); | |||||
Serial.println(F("------sd2-------")); | |||||
sd2.ls("/", LS_SIZE|LS_R); | |||||
Serial.println(F("------sd3-------")); | |||||
sd3.ls("/", LS_SIZE|LS_R); | |||||
Serial.println(F("---------------------")); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void setup() { | |||||
Serial.begin(9600); | |||||
// Wait for USB Serial | |||||
while (!Serial) { | |||||
SysCall::yield(); | |||||
} | |||||
Serial.print(F("FreeStack: ")); | |||||
Serial.println(FreeStack()); | |||||
// fill buffer with known data | |||||
for (size_t i = 0; i < sizeof(buf); i++) { | |||||
buf[i] = i; | |||||
} | |||||
Serial.println(F("type any character to start")); | |||||
while (!Serial.available()) { | |||||
SysCall::yield(); | |||||
} | |||||
// disable sd2 while initializing sd1 | |||||
pinMode(SD2_CS, OUTPUT); | |||||
digitalWrite(SD2_CS, HIGH); | |||||
// initialize the first card | |||||
if (!sd1.begin(SD1_CS)) { | |||||
sd1.initError("sd1:"); | |||||
} | |||||
// initialize the second card | |||||
if (!sd2.begin(SD2_CS)) { | |||||
sd2.initError("sd2:"); | |||||
} | |||||
// initialize the third card | |||||
if (!sd3.begin(SD3_CS)) { | |||||
sd3.initError("sd3:"); | |||||
} | |||||
Serial.println(F("Cards OK - creating directories")); | |||||
// create Dir1 on sd1 if it does not exist | |||||
if (!sd1.exists("/Dir1")) { | |||||
if (!sd1.mkdir("/Dir1")) { | |||||
sd1.errorExit("sd1.mkdir"); | |||||
} | |||||
} | |||||
// make /Dir1 the default directory for sd1 | |||||
if (!sd1.chdir("/Dir1")) { | |||||
sd1.errorExit("sd1.chdir"); | |||||
} | |||||
// create Dir2 on sd2 if it does not exist | |||||
if (!sd2.exists("/Dir2")) { | |||||
if (!sd2.mkdir("/Dir2")) { | |||||
sd2.errorExit("sd2.mkdir"); | |||||
} | |||||
} | |||||
// make /Dir2 the default directory for sd2 | |||||
if (!sd2.chdir("/Dir2")) { | |||||
sd2.errorExit("sd2.chdir"); | |||||
} | |||||
// create Dir3 on sd3 if it does not exist | |||||
if (!sd3.exists("/Dir3")) { | |||||
if (!sd3.mkdir("/Dir3")) { | |||||
sd2.errorExit("sd3.mkdir"); | |||||
} | |||||
} | |||||
// make /Dir3 the default directory for sd3 | |||||
if (!sd3.chdir("/Dir3")) { | |||||
sd3.errorExit("sd3.chdir"); | |||||
} | |||||
Serial.println(F("Directories created - removing old files")); | |||||
if (sd1.exists("TEST1.bin")) { | |||||
if (!sd1.remove("TEST1.bin")) { | |||||
sd1.errorExit("sd1.remove"); | |||||
} | |||||
} | |||||
if (sd2.exists("TEST2.bin")) { | |||||
if (!sd2.remove("TEST2.bin")) { | |||||
sd2.errorExit("sd2.remove"); | |||||
} | |||||
} | |||||
if (sd3.exists("TEST3.bin")) { | |||||
if (!sd3.remove("TEST3.bin")) { | |||||
sd2.errorExit("sd3.remove"); | |||||
} | |||||
} | |||||
Serial.println("Initial SD directories"); | |||||
list(); | |||||
// create or open /Dir1/TEST1.bin and truncate it to zero length | |||||
SdFile file1; | |||||
if (!file1.open(&sd1, "TEST1.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||||
sd1.errorExit("file1"); | |||||
} | |||||
Serial.println(F("Writing SD1:/Dir1/TEST1.bin")); | |||||
// write data to /Dir1/TEST1.bin on sd1 | |||||
for (uint16_t i = 0; i < NWRITE; i++) { | |||||
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { | |||||
sd1.errorExit("sd1.write"); | |||||
} | |||||
} | |||||
file1.sync(); | |||||
list(); | |||||
// create or open /Dir2/TEST2.bin and truncate it to zero length | |||||
SdFile file2; | |||||
if (!file2.open(&sd2, "TEST2.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||||
sd2.errorExit("file2"); | |||||
} | |||||
Serial.println(F("Copying SD1:/Dir1/TEST1.bin to SD2::/Dir2/TEST2.bin")); | |||||
// copy file1 to file2 | |||||
file1.rewind(); | |||||
uint32_t t = millis(); | |||||
while (1) { | |||||
int n = file1.read(buf, sizeof(buf)); | |||||
if (n < 0) { | |||||
sd1.errorExit("read1"); | |||||
} | |||||
if (n == 0) { | |||||
break; | |||||
} | |||||
if ((int)file2.write(buf, n) != n) { | |||||
sd2.errorExit("write3"); | |||||
} | |||||
} | |||||
t = millis() - t; | |||||
file2.sync(); | |||||
Serial.print(F("File size: ")); | |||||
Serial.println(file2.fileSize()); | |||||
Serial.print(F("Copy time: ")); | |||||
Serial.print(t); | |||||
Serial.println(F(" millis")); | |||||
list(); | |||||
// create or open /Dir3/TEST3.bin and truncate it to zero length | |||||
SdFile file3; | |||||
if (!file3.open(&sd3, "TEST3.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||||
sd3.errorExit("file3"); | |||||
} | |||||
file2.rewind(); | |||||
Serial.println(F("Copying SD2:/Dir2/TEST2.bin to SD3:/Dir3/TEST3.bin")); | |||||
while (1) { | |||||
int n = file2.read(buf, sizeof(buf)); | |||||
if (n == 0) { | |||||
break; | |||||
} | |||||
if (n != sizeof(buf)) { | |||||
sd2.errorExit("read2"); | |||||
} | |||||
if ((int)file3.write(buf, n) != n) { | |||||
sd3.errorExit("write2"); | |||||
} | |||||
} | |||||
file3.sync(); | |||||
list(); | |||||
// Verify content of file3 | |||||
file3.rewind(); | |||||
Serial.println(F("Verifying content of TEST3.bin")); | |||||
for (uint16_t i = 0; i < NWRITE; i++) { | |||||
if (file3.read(buf, sizeof(buf)) != sizeof(buf)) { | |||||
sd3.errorExit("sd3.read"); | |||||
} | |||||
for (size_t j = 0; j < sizeof(buf); j++) { | |||||
if (j != buf[j]) { | |||||
sd3.errorExit("Verify error"); | |||||
} | |||||
} | |||||
} | |||||
Serial.println(F("Done - Verify OK")); | |||||
file1.close(); | |||||
file2.close(); | |||||
file3.close(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void loop() {} | |||||
#else // SD_SPI_CONFIGURATION >= 3 | |||||
#error SD_SPI_CONFIGURATION must be set to 3 in SdFat/SdFatConfig.h | |||||
#endif //SD_SPI_CONFIGURATION >= 3 |
while (!Serial.available()) { | while (!Serial.available()) { | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
/* | /* | ||||
* Warning This example requires extra RAM and may crash on Uno. | |||||
* Example use of two SD cards. | * Example use of two SD cards. | ||||
*/ | */ | ||||
#include <SPI.h> | #include <SPI.h> |
while (!Serial.available()) { | while (!Serial.available()) { | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
// Insure no TEST_FILE. | // Insure no TEST_FILE. | ||||
cout << F("Remove ") << TEST_FILE << endl << endl; | cout << F("Remove ") << TEST_FILE << endl << endl; | ||||
sd.remove(TEST_FILE); | sd.remove(TEST_FILE); | ||||
printFreeSpace(); | printFreeSpace(); | ||||
cout << F("Done") << endl; | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void loop() {} | void loop() {} |
// file system | // file system | ||||
SdFat sd; | SdFat sd; | ||||
// Set SD_SPI_CONFIGURATION to three to test next two definitions. | |||||
// SdFatLibSpi sd; | |||||
// Set ENABLE_EXTENDED_TRANSFER_CLASS to use extended SD I/O. | |||||
// Requires dedicated use of the SPI bus. | |||||
// SdFatEX sd; | |||||
// Set ENABLE_SOFTWARE_SPI_CLASS to use software SPI. | |||||
// Args are misoPin, mosiPin, sckPin. | // Args are misoPin, mosiPin, sckPin. | ||||
// SdFatSoftSpi<6, 7, 5> sd; | // SdFatSoftSpi<6, 7, 5> sd; | ||||
cout << F("FreeStack: ") << FreeStack() << endl; | cout << F("FreeStack: ") << FreeStack() << endl; | ||||
// initialize the SD card at SPI_FULL_SPEED for best performance. | |||||
// try SPI_HALF_SPEED if bus errors occur. | |||||
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
// Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
#include "SdFat.h" | #include "SdFat.h" | ||||
// SD card chip select pin. | // SD card chip select pin. | ||||
const uint8_t SD_CHIP_SELECT = SS; | |||||
const uint8_t chipSelect = SS; | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// File system object. | // File system object. | ||||
cin.readline(); | cin.readline(); | ||||
cout << endl; | cout << endl; | ||||
// Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
if (sd.exists("Folder1") | if (sd.exists("Folder1") |
} | } | ||||
delay(400); // catch Due reset problem | delay(400); // catch Due reset problem | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
/* | |||||
* This example reads a simple CSV, comma-separated values, file. | |||||
* Each line of the file has a label and three values, a long and two floats. | |||||
*/ | |||||
#include <SPI.h> | |||||
#include "SdFat.h" | |||||
// SD chip select pin | |||||
const uint8_t chipSelect = SS; | |||||
// file system object | |||||
SdFat sd; | |||||
// Functions to read a CSV text file one field at a time. | |||||
// | |||||
#include <limits.h> | |||||
#include <SPI.h> | |||||
// create Serial stream | |||||
ArduinoOutStream cout(Serial); | |||||
// next line for SD.h | |||||
//#include <SD.h> | |||||
char fileName[] = "testfile.csv"; | |||||
//------------------------------------------------------------------------------ | |||||
// store error strings in flash to save RAM | |||||
#define error(s) sd.errorHalt(F(s)) | |||||
//------------------------------------------------------------------------------ | |||||
// read and print CSV test file | |||||
void readFile() { | |||||
long lg = 0; | |||||
float f1, f2; | |||||
char text[10]; | |||||
char c1, c2, c3; // space for commas. | |||||
// next two lines for SdFat | |||||
#include <SdFat.h> | |||||
SdFat SD; | |||||
// open input file | |||||
ifstream sdin(fileName); | |||||
#define CS_PIN SS | |||||
// check for open error | |||||
if (!sdin.is_open()) { | |||||
error("open"); | |||||
} | |||||
// example can use comma or semicolon | |||||
#define CSV_DELIM ',' | |||||
// read until input fails | |||||
while (1) { | |||||
// Get text field. | |||||
sdin.get(text, sizeof(text), ','); | |||||
File file; | |||||
// Assume EOF if fail. | |||||
if (sdin.fail()) { | |||||
/* | |||||
* Read a file one field at a time. | |||||
* | |||||
* file - File to read. | |||||
* | |||||
* str - Character array for the field. | |||||
* | |||||
* size - Size of str array. | |||||
* | |||||
* delim - csv delimiter. | |||||
* | |||||
* return - negative value for failure. | |||||
* delimiter, '\n' or zero(EOF) for success. | |||||
*/ | |||||
int csvReadText(File* file, char* str, size_t size, char delim) { | |||||
char ch; | |||||
int rtn; | |||||
size_t n = 0; | |||||
while (true) { | |||||
// check for EOF | |||||
if (!file->available()) { | |||||
rtn = 0; | |||||
break; | break; | ||||
} | } | ||||
// Get commas and numbers. | |||||
sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2; | |||||
// Skip CR/LF. | |||||
sdin.skipWhite(); | |||||
if (sdin.fail()) { | |||||
error("bad input"); | |||||
if (file->read(&ch, 1) != 1) { | |||||
// read error | |||||
rtn = -1; | |||||
break; | |||||
} | } | ||||
// error in line if not commas | |||||
if (c1 != ',' || c2 != ',' || c3 != ',') { | |||||
error("comma"); | |||||
// Delete CR. | |||||
if (ch == '\r') { | |||||
continue; | |||||
} | } | ||||
// print in six character wide columns | |||||
cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl; | |||||
} | |||||
// Error in an input line if file is not at EOF. | |||||
if (!sdin.eof()) { | |||||
error("readFile"); | |||||
if (ch == delim || ch == '\n') { | |||||
rtn = ch; | |||||
break; | |||||
} | |||||
if ((n+1) >= size) { | |||||
// string too long | |||||
rtn = -2; | |||||
n--; | |||||
break; | |||||
} | |||||
str[n++] = ch; | |||||
} | } | ||||
str[n] = '\0'; | |||||
return rtn; | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// write test file | |||||
void writeFile() { | |||||
// create or open and truncate output file | |||||
ofstream sdout(fileName); | |||||
// write file from string stored in flash | |||||
sdout << F( | |||||
"Line 1,1,2.3,4.5\n" | |||||
"Line 2,6,7.8,9.0\n" | |||||
"Line 3,9,8.7,6.5\n" | |||||
"Line 4,-4,-3.2,-1\n") << flush; | |||||
// check for any errors | |||||
if (!sdout) { | |||||
error("writeFile"); | |||||
} | |||||
sdout.close(); | |||||
int csvReadInt32(File* file, int32_t* num, char delim) { | |||||
char buf[20]; | |||||
char* ptr; | |||||
int rtn = csvReadText(file, buf, sizeof(buf), delim); | |||||
if (rtn < 0) return rtn; | |||||
*num = strtol(buf, &ptr, 10); | |||||
if (buf == ptr) return -3; | |||||
while(isspace(*ptr)) ptr++; | |||||
return *ptr == 0 ? rtn : -4; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
int csvReadInt16(File* file, int16_t* num, char delim) { | |||||
int32_t tmp; | |||||
int rtn = csvReadInt32(file, &tmp, delim); | |||||
if (rtn < 0) return rtn; | |||||
if (tmp < INT_MIN || tmp > INT_MAX) return -5; | |||||
*num = tmp; | |||||
return rtn; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
int csvReadUint32(File* file, uint32_t* num, char delim) { | |||||
char buf[20]; | |||||
char* ptr; | |||||
int rtn = csvReadText(file, buf, sizeof(buf), delim); | |||||
if (rtn < 0) return rtn; | |||||
*num = strtoul(buf, &ptr, 10); | |||||
if (buf == ptr) return -3; | |||||
while(isspace(*ptr)) ptr++; | |||||
return *ptr == 0 ? rtn : -4; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
int csvReadUint16(File* file, uint16_t* num, char delim) { | |||||
uint32_t tmp; | |||||
int rtn = csvReadUint32(file, &tmp, delim); | |||||
if (rtn < 0) return rtn; | |||||
if (tmp > UINT_MAX) return -5; | |||||
*num = tmp; | |||||
return rtn; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
int csvReadDouble(File* file, double* num, char delim) { | |||||
char buf[20]; | |||||
char* ptr; | |||||
int rtn = csvReadText(file, buf, sizeof(buf), delim); | |||||
if (rtn < 0) return rtn; | |||||
*num = strtod(buf, &ptr); | |||||
if (buf == ptr) return -3; | |||||
while(isspace(*ptr)) ptr++; | |||||
return *ptr == 0 ? rtn : -4; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
int csvReadFloat(File* file, float* num, char delim) { | |||||
double tmp; | |||||
int rtn = csvReadDouble(file, &tmp, delim); | |||||
if (rtn < 0)return rtn; | |||||
// could test for too large. | |||||
*num = tmp; | |||||
return rtn; | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void setup() { | void setup() { | ||||
// Wait for USB Serial | // Wait for USB Serial | ||||
while (!Serial) { | while (!Serial) { | ||||
SysCall::yield(); | |||||
yield(); | |||||
} | } | ||||
cout << F("Type any character to start\n"); | |||||
Serial.println("Type any character to start"); | |||||
while (!Serial.available()) { | while (!Serial.available()) { | ||||
SysCall::yield(); | |||||
yield(); | |||||
} | } | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
sd.initErrorHalt(); | |||||
// Initialize the SD. | |||||
if (!SD.begin(CS_PIN)) { | |||||
Serial.println("begin failed"); | |||||
return; | |||||
} | } | ||||
// create test file | |||||
writeFile(); | |||||
cout << endl; | |||||
// read and print test | |||||
readFile(); | |||||
cout << "\nDone!" << endl; | |||||
// Remove existing file. | |||||
SD.remove("READTEST.TXT"); | |||||
// Create the file. | |||||
file = SD.open("READTEST.TXT", FILE_WRITE); | |||||
if (!file) { | |||||
Serial.println("open failed"); | |||||
return; | |||||
} | |||||
// Write test data. | |||||
file.print(F( | |||||
#if CSV_DELIM == ',' | |||||
"36,23.20,20.70,57.60,79.50,01:08:14,23.06.16\r\n" | |||||
"37,23.21,20.71,57.61,79.51,02:08:14,23.07.16\r\n" | |||||
#elif CSV_DELIM == ';' | |||||
"36;23.20;20.70;57.60;79.50;01:08:14;23.06.16\r\n" | |||||
"37;23.21;20.71;57.61;79.51;02:08:14;23.07.16\r\n" | |||||
#else | |||||
#error "Bad CSV_DELIM" | |||||
#endif | |||||
)); | |||||
// Rewind the file for read. | |||||
file.seek(0); | |||||
// Read the file and print fields. | |||||
int16_t tcalc; | |||||
float t1, t2, h1, h2; | |||||
// Must be dim 9 to allow for zero byte. | |||||
char timeS[9], dateS[9]; | |||||
while (file.available()) { | |||||
if (csvReadInt16(&file, &tcalc, CSV_DELIM) != CSV_DELIM | |||||
|| csvReadFloat(&file, &t1, CSV_DELIM) != CSV_DELIM | |||||
|| csvReadFloat(&file, &t2, CSV_DELIM) != CSV_DELIM | |||||
|| csvReadFloat(&file, &h1, CSV_DELIM) != CSV_DELIM | |||||
|| csvReadFloat(&file, &h2, CSV_DELIM) != CSV_DELIM | |||||
|| csvReadText(&file, timeS, sizeof(timeS), CSV_DELIM) != CSV_DELIM | |||||
|| csvReadText(&file, dateS, sizeof(dateS), CSV_DELIM) != '\n') { | |||||
Serial.println("read error"); | |||||
int ch; | |||||
int nr = 0; | |||||
// print part of file after error. | |||||
while ((ch = file.read()) > 0 && nr++ < 100) { | |||||
Serial.write(ch); | |||||
} | |||||
break; | |||||
} | |||||
Serial.print(tcalc); | |||||
Serial.print(CSV_DELIM); | |||||
Serial.print(t1); | |||||
Serial.print(CSV_DELIM); | |||||
Serial.print(t2); | |||||
Serial.print(CSV_DELIM); | |||||
Serial.print(h1); | |||||
Serial.print(CSV_DELIM); | |||||
Serial.print(h2); | |||||
Serial.print(CSV_DELIM); | |||||
Serial.print(timeS); | |||||
Serial.print(CSV_DELIM); | |||||
Serial.println(dateS); | |||||
} | |||||
file.close(); | |||||
} | } | ||||
void loop() {} | |||||
//------------------------------------------------------------------------------ | |||||
void loop() { | |||||
} | |||||
SysCall::yield(); | SysCall::yield(); | ||||
} | } | ||||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||||
// breadboards. use SPI_FULL_SPEED for better performance. | |||||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
if (c != 'Y') { | if (c != 'Y') { | ||||
sd.errorHalt("Quitting, you did not type 'Y'."); | sd.errorHalt("Quitting, you did not type 'Y'."); | ||||
} | } | ||||
if (!sd.begin(chipSelect)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.initErrorHalt(); | sd.initErrorHalt(); | ||||
} | } | ||||
// Use wipe() for no dot progress indicator. | // Use wipe() for no dot progress indicator. | ||||
sd.errorHalt("Wipe failed."); | sd.errorHalt("Wipe failed."); | ||||
} | } | ||||
// Must reinitialize after wipe. | // Must reinitialize after wipe. | ||||
if (!sd.begin(chipSelect)) { | |||||
// Initialize at the highest speed supported by the board that is | |||||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||||
sd.errorHalt("Second init failed."); | sd.errorHalt("Second init failed."); | ||||
} | } | ||||
Serial.println("Done"); | Serial.println("Done"); |
name=SdFat | name=SdFat | ||||
version=2015.4.26 | |||||
version=2016.7.24 | |||||
author= | author= | ||||
maintainer= | maintainer= | ||||
sentence=FAT16/FAT32 file system for SD cards. | sentence=FAT16/FAT32 file system for SD cards. |
*/ | */ | ||||
#ifndef DigitalPin_h | #ifndef DigitalPin_h | ||||
#define DigitalPin_h | #define DigitalPin_h | ||||
#include "SystemInclude.h" | |||||
#if defined(__AVR__) | #if defined(__AVR__) | ||||
#include <avr/io.h> | #include <avr/io.h> | ||||
/** GpioPinMap type */ | /** GpioPinMap type */ |
/* Arduino SdSpi Library | |||||
/* Arduino SdSpiAltDriver Library | |||||
* Copyright (C) 2016 by William Greiman | * Copyright (C) 2016 by William Greiman | ||||
* | * | ||||
* STM32F1 code for Maple and Maple Mini support, 2015 by Victor Perez | * STM32F1 code for Maple and Maple Mini support, 2015 by Victor Perez | ||||
* | * | ||||
* This file is part of the Arduino SdSpi Library | |||||
* This file is part of the Arduino SdSpiAltDriver Library | |||||
* | * | ||||
* This Library is free software: you can redistribute it and/or modify | * This Library is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* GNU General Public License for more details. | * GNU General Public License for more details. | ||||
* | * | ||||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
* along with the Arduino SdSpi Library. If not, see | |||||
* along with the Arduino SdSpiAltDriver Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#if defined(ESP8266) | #if defined(ESP8266) | ||||
#include "SdSpi.h" | |||||
#include "SdSpiDriver.h" | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Initialize the SPI bus. | /** Initialize the SPI bus. | ||||
* | * | ||||
* \param[in] chipSelectPin SD card chip select pin. | * \param[in] chipSelectPin SD card chip select pin. | ||||
*/ | */ | ||||
void SdSpi::begin(uint8_t chipSelectPin) { | |||||
pinMode(chipSelectPin, OUTPUT); | |||||
digitalWrite(chipSelectPin, HIGH); | |||||
void SdSpiAltDriver::begin(uint8_t csPin) { | |||||
m_csPin = csPin; | |||||
pinMode(m_csPin, OUTPUT); | |||||
digitalWrite(m_csPin, HIGH); | |||||
SPI.begin(); | SPI.begin(); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Set SPI options for access to SD/SDHC cards. | /** Set SPI options for access to SD/SDHC cards. | ||||
* | * | ||||
* \param[in] divisor SCK clock divider relative to the max SPI clock. | |||||
*/ | */ | ||||
void SdSpi::beginTransaction(uint8_t divisor) { | |||||
const uint32_t F_SPI_MAX = 80000000; | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
// Note: ESP8266 beginTransaction does not protect for interrupts. | |||||
SPISettings settings(F_SPI_MAX/(divisor ? divisor : 1), MSBFIRST, SPI_MODE0); | |||||
SPI.beginTransaction(settings); | |||||
#else // ENABLE_SPI_TRANSACTIONS | |||||
// Note: ESP8266 beginTransaction is the same as following code. | |||||
SPI.setFrequency(F_SPI_MAX/(divisor ? divisor : 1)); | |||||
SPI.setBitOrder(MSBFIRST); | |||||
SPI.setDataMode(SPI_MODE0); | |||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
void SdSpiAltDriver::activate() { | |||||
SPI.beginTransaction(m_spiSettings); | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void SdSpi::endTransaction() { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
void SdSpiAltDriver::deactivate() { | |||||
// Note: endTransaction is an empty function on ESP8266. | // Note: endTransaction is an empty function on ESP8266. | ||||
SPI.endTransaction(); | SPI.endTransaction(); | ||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Receive a byte. | /** Receive a byte. | ||||
* | * | ||||
* \return The byte. | * \return The byte. | ||||
*/ | */ | ||||
uint8_t SdSpi::receive() { | |||||
uint8_t SdSpiAltDriver::receive() { | |||||
return SPI.transfer(0XFF); | return SPI.transfer(0XFF); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
* | * | ||||
* \return Zero for no error or nonzero error code. | * \return Zero for no error or nonzero error code. | ||||
*/ | */ | ||||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
// Works without 32-bit alignment of buf. | // Works without 32-bit alignment of buf. | ||||
SPI.transferBytes(0, buf, n); | SPI.transferBytes(0, buf, n); | ||||
return 0; | return 0; | ||||
* | * | ||||
* \param[in] b Byte to send | * \param[in] b Byte to send | ||||
*/ | */ | ||||
void SdSpi::send(uint8_t b) { | |||||
void SdSpiAltDriver::send(uint8_t b) { | |||||
SPI.transfer(b); | SPI.transfer(b); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
* \param[in] buf Buffer for data to be sent. | * \param[in] buf Buffer for data to be sent. | ||||
* \param[in] n Number of bytes to send. | * \param[in] n Number of bytes to send. | ||||
*/ | */ | ||||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
// Adjust to 32-bit alignment. | // Adjust to 32-bit alignment. | ||||
while ((reinterpret_cast<uintptr_t>(buf) & 0X3) && n) { | while ((reinterpret_cast<uintptr_t>(buf) & 0X3) && n) { | ||||
SPI.transfer(*buf++); | SPI.transfer(*buf++); |
/* Arduino SdSpi Library | |||||
/* Arduino SdSpiAltDriver Library | |||||
* Copyright (C) 2013 by William Greiman | * Copyright (C) 2013 by William Greiman | ||||
* | * | ||||
* This file is part of the Arduino SdSpi Library | |||||
* This file is part of the Arduino SdSpiAltDriver Library | |||||
* | * | ||||
* This Library is free software: you can redistribute it and/or modify | * This Library is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* GNU General Public License for more details. | * GNU General Public License for more details. | ||||
* | * | ||||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
* along with the Arduino SdSpi Library. If not, see | |||||
* along with the Arduino SdSpiAltDriver Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include "SdSpi.h" | |||||
#include "SdSpiDriver.h" | |||||
#if defined(__SAM3X8E__) || defined(__SAM3X8H__) | #if defined(__SAM3X8E__) || defined(__SAM3X8H__) | ||||
/** Use SAM3X DMAC if nonzero */ | /** Use SAM3X DMAC if nonzero */ | ||||
#define USE_SAM3X_DMAC 1 | #define USE_SAM3X_DMAC 1 | ||||
return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true; | return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void SdSpi::begin(uint8_t chipSelectPin) { | |||||
pinMode(chipSelectPin, OUTPUT); | |||||
digitalWrite(chipSelectPin, HIGH); | |||||
PIO_Configure( | |||||
g_APinDescription[PIN_SPI_MOSI].pPort, | |||||
g_APinDescription[PIN_SPI_MOSI].ulPinType, | |||||
g_APinDescription[PIN_SPI_MOSI].ulPin, | |||||
g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration); | |||||
PIO_Configure( | |||||
g_APinDescription[PIN_SPI_MISO].pPort, | |||||
g_APinDescription[PIN_SPI_MISO].ulPinType, | |||||
g_APinDescription[PIN_SPI_MISO].ulPin, | |||||
g_APinDescription[PIN_SPI_MISO].ulPinConfiguration); | |||||
PIO_Configure( | |||||
g_APinDescription[PIN_SPI_SCK].pPort, | |||||
g_APinDescription[PIN_SPI_SCK].ulPinType, | |||||
g_APinDescription[PIN_SPI_SCK].ulPin, | |||||
g_APinDescription[PIN_SPI_SCK].ulPinConfiguration); | |||||
pmc_enable_periph_clk(ID_SPI0); | |||||
void SdSpiAltDriver::begin(uint8_t csPin) { | |||||
m_csPin = csPin; | |||||
pinMode(m_csPin, OUTPUT); | |||||
digitalWrite(m_csPin, HIGH); | |||||
SPI.begin(); | |||||
#if USE_SAM3X_DMAC | #if USE_SAM3X_DMAC | ||||
pmc_enable_periph_clk(ID_DMAC); | pmc_enable_periph_clk(ID_DMAC); | ||||
dmac_disable(); | dmac_disable(); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// initialize SPI controller | // initialize SPI controller | ||||
void SdSpi::beginTransaction(uint8_t sckDivisor) { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
SPI.beginTransaction(SPISettings()); | |||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
uint8_t scbr = sckDivisor; | |||||
void SdSpiAltDriver::activate() { | |||||
SPI.beginTransaction(m_spiSettings); | |||||
Spi* pSpi = SPI0; | Spi* pSpi = SPI0; | ||||
// disable SPI | |||||
// Save the divisor | |||||
uint32_t scbr = pSpi->SPI_CSR[SPI_CHIP_SEL] & 0XFF00; | |||||
// Disable SPI | |||||
pSpi->SPI_CR = SPI_CR_SPIDIS; | pSpi->SPI_CR = SPI_CR_SPIDIS; | ||||
// reset SPI | // reset SPI | ||||
pSpi->SPI_CR = SPI_CR_SWRST; | pSpi->SPI_CR = SPI_CR_SWRST; | ||||
// no mode fault detection, set master mode | // no mode fault detection, set master mode | ||||
pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR; | pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR; | ||||
// mode 0, 8-bit, | // mode 0, 8-bit, | ||||
pSpi->SPI_CSR[SPI_CHIP_SEL] = SPI_CSR_SCBR(scbr) | SPI_CSR_NCPHA; | |||||
pSpi->SPI_CSR[SPI_CHIP_SEL] = scbr | SPI_CSR_CSAAT | SPI_CSR_NCPHA; | |||||
// enable SPI | // enable SPI | ||||
pSpi->SPI_CR |= SPI_CR_SPIEN; | pSpi->SPI_CR |= SPI_CR_SPIEN; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void SdSpi::endTransaction() { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
void SdSpiAltDriver::deactivate() { | |||||
SPI.endTransaction(); | SPI.endTransaction(); | ||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
static inline uint8_t spiTransfer(uint8_t b) { | static inline uint8_t spiTransfer(uint8_t b) { | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** SPI receive a byte */ | /** SPI receive a byte */ | ||||
uint8_t SdSpi::receive() { | |||||
uint8_t SdSpiAltDriver::receive() { | |||||
return spiTransfer(0XFF); | return spiTransfer(0XFF); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** SPI receive multiple bytes */ | /** SPI receive multiple bytes */ | ||||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
Spi* pSpi = SPI0; | Spi* pSpi = SPI0; | ||||
int rtn = 0; | int rtn = 0; | ||||
#if USE_SAM3X_DMAC | #if USE_SAM3X_DMAC | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** SPI send a byte */ | /** SPI send a byte */ | ||||
void SdSpi::send(uint8_t b) { | |||||
void SdSpiAltDriver::send(uint8_t b) { | |||||
spiTransfer(b); | spiTransfer(b); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
Spi* pSpi = SPI0; | Spi* pSpi = SPI0; | ||||
#if USE_SAM3X_DMAC | #if USE_SAM3X_DMAC | ||||
spiDmaTX(buf, n); | spiDmaTX(buf, n); |
/* Arduino SdSpi Library | |||||
/* Arduino SdSpiAltDriver Library | |||||
* Copyright (C) 2013 by William Greiman | * Copyright (C) 2013 by William Greiman | ||||
* | * | ||||
* This file is part of the Arduino SdSpi Library | |||||
* This file is part of the Arduino SdSpiAltDriver Library | |||||
* | * | ||||
* This Library is free software: you can redistribute it and/or modify | * This Library is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* GNU General Public License for more details. | * GNU General Public License for more details. | ||||
* | * | ||||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
* along with the Arduino SdSpi Library. If not, see | |||||
* along with the Arduino SdSpiAltDriver Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#if defined(__STM32F1__) | #if defined(__STM32F1__) | ||||
#include "SdSpi.h" | |||||
#include "SdSpiDriver.h" | |||||
#define USE_STM32F1_DMAC 1 | #define USE_STM32F1_DMAC 1 | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Initialize the SPI bus. | |||||
static SPIClass m_SPI1(1); | |||||
#if BOARD_NR_SPI > 1 | |||||
static SPIClass m_SPI2(2); | |||||
#endif // BOARD_NR_SPI > 1 | |||||
#if BOARD_NR_SPI > 2 | |||||
static SPIClass m_SPI2(3); | |||||
#endif // BOARD_NR_SPI > 2 | |||||
// | |||||
static SPIClass* pSpi[] = | |||||
#if BOARD_NR_SPI == 1 | |||||
{&m_SPI1}; | |||||
#elif BOARD_NR_SPI == 2 | |||||
{&m_SPI1, &m_SPI2}; | |||||
#elif BOARD_NR_SPI == 3 | |||||
{&m_SPI1, &m_SPI2, &m_SPI3}; | |||||
#else // BOARD_NR_SPI | |||||
#error "BOARD_NR_SPI too large" | |||||
#endif // BOARD_NR_SPI | |||||
//------------------------------------------------------------------------------ | |||||
/** Set SPI options for access to SD/SDHC cards. | |||||
* | * | ||||
* \param[in] chipSelectPin SD card chip select pin. | |||||
* \param[in] divisor SCK clock divider relative to the APB1 or APB2 clock. | |||||
*/ | */ | ||||
void SdSpi::begin(uint8_t chipSelectPin) { | |||||
pinMode(chipSelectPin, OUTPUT); | |||||
digitalWrite(chipSelectPin, HIGH); | |||||
SPI.begin(); | |||||
void SdSpiAltDriver::activate() { | |||||
pSpi[m_spiPort]->beginTransaction(m_spiSettings); | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Set SPI options for access to SD/SDHC cards. | |||||
/** Initialize the SPI bus. | |||||
* | * | ||||
* \param[in] divisor SCK clock divider relative to the APB1 or APB2 clock. | |||||
* \param[in] chipSelectPin SD card chip select pin. | |||||
*/ | */ | ||||
void SdSpi::beginTransaction(uint8_t divisor) { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
// Correct divisor will be set below. | |||||
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); | |||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
uint32_t br; // Baud rate control field in SPI_CR1. | |||||
if (divisor <= 2) { | |||||
br = SPI_CLOCK_DIV2; | |||||
} else if (divisor <= 4) { | |||||
br = SPI_CLOCK_DIV4; | |||||
} else if (divisor <= 8) { | |||||
br = SPI_CLOCK_DIV8; | |||||
} else if (divisor <= 16) { | |||||
br = SPI_CLOCK_DIV16; | |||||
} else if (divisor <= 32) { | |||||
br = SPI_CLOCK_DIV32; | |||||
} else if (divisor <= 64) { | |||||
br = SPI_CLOCK_DIV64; | |||||
} else if (divisor <= 128) { | |||||
br = SPI_CLOCK_DIV128; | |||||
} else { | |||||
br = SPI_CLOCK_DIV256; | |||||
} | |||||
SPI.setClockDivider(br); | |||||
#if !ENABLE_SPI_TRANSACTIONS | |||||
SPI.setBitOrder(MSBFIRST); | |||||
SPI.setDataMode(SPI_MODE0); | |||||
#endif // !ENABLE_SPI_TRANSACTIONS | |||||
void SdSpiAltDriver::begin(uint8_t csPin) { | |||||
m_csPin = csPin; | |||||
pinMode(m_csPin, OUTPUT); | |||||
digitalWrite(m_csPin, HIGH); | |||||
pSpi[m_spiPort]->begin(); | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | /** | ||||
* End SPI transaction. | * End SPI transaction. | ||||
*/ | */ | ||||
void SdSpi::endTransaction() { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
SPI.endTransaction(); | |||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
void SdSpiAltDriver::deactivate() { | |||||
pSpi[m_spiPort]->endTransaction(); | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Receive a byte. | /** Receive a byte. | ||||
* | * | ||||
* \return The byte. | * \return The byte. | ||||
*/ | */ | ||||
uint8_t SdSpi::receive() { | |||||
return SPI.transfer(0XFF); | |||||
uint8_t SdSpiAltDriver::receive() { | |||||
return pSpi[m_spiPort]->transfer(0XFF); | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Receive multiple bytes. | /** Receive multiple bytes. | ||||
* | * | ||||
* \return Zero for no error or nonzero error code. | * \return Zero for no error or nonzero error code. | ||||
*/ | */ | ||||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
int rtn = 0; | int rtn = 0; | ||||
#if USE_STM32F1_DMAC | #if USE_STM32F1_DMAC | ||||
rtn = SPI.dmaTransfer(0, const_cast<uint8*>(buf), n); | |||||
rtn = pSpi[m_spiPort]->dmaTransfer(0, const_cast<uint8*>(buf), n); | |||||
#else // USE_STM32F1_DMAC | #else // USE_STM32F1_DMAC | ||||
// SPI.read(buf, n); | |||||
// pSpi[m_spiPort]->read(buf, n); fails ?? use byte transfer | |||||
for (size_t i = 0; i < n; i++) { | for (size_t i = 0; i < n; i++) { | ||||
buf[i] = SPI.transfer(0XFF); | |||||
buf[i] = pSpi[m_spiPort]->transfer(0XFF); | |||||
} | } | ||||
#endif // USE_STM32F1_DMAC | #endif // USE_STM32F1_DMAC | ||||
return rtn; | return rtn; | ||||
* | * | ||||
* \param[in] b Byte to send | * \param[in] b Byte to send | ||||
*/ | */ | ||||
void SdSpi::send(uint8_t b) { | |||||
SPI.transfer(b); | |||||
void SdSpiAltDriver::send(uint8_t b) { | |||||
pSpi[m_spiPort]->transfer(b); | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Send multiple bytes. | /** Send multiple bytes. | ||||
* \param[in] buf Buffer for data to be sent. | * \param[in] buf Buffer for data to be sent. | ||||
* \param[in] n Number of bytes to send. | * \param[in] n Number of bytes to send. | ||||
*/ | */ | ||||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
#if USE_STM32F1_DMAC | #if USE_STM32F1_DMAC | ||||
SPI.dmaSend(const_cast<uint8*>(buf), n); | |||||
pSpi[m_spiPort]->dmaSend(const_cast<uint8*>(buf), n); | |||||
#else // #if USE_STM32F1_DMAC | #else // #if USE_STM32F1_DMAC | ||||
SPI.write(buf, n); | |||||
pSpi[m_spiPort]->write(buf, n); | |||||
#endif // USE_STM32F1_DMAC | #endif // USE_STM32F1_DMAC | ||||
} | } | ||||
#endif // USE_NATIVE_STM32F1_SPI | |||||
//----------------------------------------------------------------------------- | |||||
void SdSpiAltDriver::setPort(uint8_t portNumber) { | |||||
m_spiPort = portNumber < 1 || portNumber > BOARD_NR_SPI ? 0 : portNumber -1; | |||||
} | |||||
#endif // defined(__STM32F1__) |
/* Arduino SdSpi Library | |||||
/* Arduino SdSpiAltDriver Library | |||||
* Copyright (C) 2013 by William Greiman | * Copyright (C) 2013 by William Greiman | ||||
* | * | ||||
* This file is part of the Arduino SdSpi Library | |||||
* This file is part of the Arduino SdSpiAltDriver Library | |||||
* | * | ||||
* This Library is free software: you can redistribute it and/or modify | * This Library is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* GNU General Public License for more details. | * GNU General Public License for more details. | ||||
* | * | ||||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
* along with the Arduino SdSpi Library. If not, see | |||||
* along with the Arduino SdSpiAltDriver Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include "SdSpi.h" | |||||
#include "SdSpiDriver.h" | |||||
#if defined(__arm__) && defined(CORE_TEENSY) | #if defined(__arm__) && defined(CORE_TEENSY) | ||||
// SPI definitions | // SPI definitions | ||||
#include "kinetis.h" | #include "kinetis.h" | ||||
//------------------------------------------------------------------------------ | |||||
void SdSpiAltDriver::activate() { | |||||
SPI.beginTransaction(m_spiSettings); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdSpiAltDriver::begin(uint8_t chipSelectPin) { | |||||
m_csPin = chipSelectPin; | |||||
pinMode(m_csPin, OUTPUT); | |||||
digitalWrite(m_csPin, HIGH); | |||||
SPI.begin(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdSpiAltDriver::deactivate() { | |||||
SPI.endTransaction(); | |||||
} | |||||
//============================================================================== | |||||
#ifdef KINETISK | #ifdef KINETISK | ||||
// use 16-bit frame if SPI_USE_8BIT_FRAME is zero | // use 16-bit frame if SPI_USE_8BIT_FRAME is zero | ||||
#define SPI_USE_8BIT_FRAME 0 | #define SPI_USE_8BIT_FRAME 0 | ||||
// Limit initial fifo to three entries to avoid fifo overrun | // Limit initial fifo to three entries to avoid fifo overrun | ||||
#define SPI_PUSHR_CTAS(n) (((n) & 7) << 28) | #define SPI_PUSHR_CTAS(n) (((n) & 7) << 28) | ||||
#endif // SPI_PUSHR_CTAS | #endif // SPI_PUSHR_CTAS | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | |||||
* initialize SPI pins | |||||
*/ | |||||
void SdSpi::begin(uint8_t chipSelectPin) { | |||||
pinMode(chipSelectPin, OUTPUT); | |||||
digitalWrite(chipSelectPin, HIGH); | |||||
SIM_SCGC6 |= SIM_SCGC6_SPI0; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
/** | |||||
* Initialize hardware SPI | |||||
* | |||||
*/ | |||||
void SdSpi::beginTransaction(uint8_t sckDivisor) { | |||||
uint32_t ctar, ctar0, ctar1; | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
SPI.beginTransaction(SPISettings()); | |||||
#endif // #if ENABLE_SPI_TRANSACTIONS | |||||
if (sckDivisor <= 2) { | |||||
// 1/2 speed | |||||
ctar = SPI_CTAR_DBR | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); | |||||
} else if (sckDivisor <= 4) { | |||||
// 1/4 speed | |||||
ctar = SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); | |||||
} else if (sckDivisor <= 8) { | |||||
// 1/8 speed | |||||
ctar = SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); | |||||
} else if (sckDivisor <= 12) { | |||||
// 1/12 speed | |||||
ctar = SPI_CTAR_BR(2) | SPI_CTAR_CSSCK(2); | |||||
} else if (sckDivisor <= 16) { | |||||
// 1/16 speed | |||||
ctar = SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(3); | |||||
} else if (sckDivisor <= 32) { | |||||
// 1/32 speed | |||||
ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(4); | |||||
} else if (sckDivisor <= 64) { | |||||
// 1/64 speed | |||||
ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(5); | |||||
} else { | |||||
// 1/128 speed | |||||
ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(6); | |||||
} | |||||
// CTAR0 - 8 bit transfer | |||||
ctar0 = ctar | SPI_CTAR_FMSZ(7); | |||||
// CTAR1 - 16 bit transfer | |||||
ctar1 = ctar | SPI_CTAR_FMSZ(15); | |||||
if (SPI0_CTAR0 != ctar0 || SPI0_CTAR1 != ctar1) { | |||||
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); | |||||
SPI0_CTAR0 = ctar0; | |||||
SPI0_CTAR1 = ctar1; | |||||
} | |||||
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); | |||||
CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); | |||||
CORE_PIN12_CONFIG = PORT_PCR_MUX(2); | |||||
CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
/** SPI receive a byte */ | /** SPI receive a byte */ | ||||
uint8_t SdSpi::receive() { | |||||
uint8_t SdSpiAltDriver::receive() { | |||||
SPI0_MCR |= SPI_MCR_CLR_RXF; | SPI0_MCR |= SPI_MCR_CLR_RXF; | ||||
SPI0_SR = SPI_SR_TCF; | SPI0_SR = SPI_SR_TCF; | ||||
SPI0_PUSHR = 0xFF; | SPI0_PUSHR = 0xFF; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** SPI receive multiple bytes */ | /** SPI receive multiple bytes */ | ||||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
// clear any data in RX FIFO | // clear any data in RX FIFO | ||||
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | ||||
#if SPI_USE_8BIT_FRAME | #if SPI_USE_8BIT_FRAME | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** SPI send a byte */ | /** SPI send a byte */ | ||||
void SdSpi::send(uint8_t b) { | |||||
void SdSpiAltDriver::send(uint8_t b) { | |||||
SPI0_MCR |= SPI_MCR_CLR_RXF; | SPI0_MCR |= SPI_MCR_CLR_RXF; | ||||
SPI0_SR = SPI_SR_TCF; | SPI0_SR = SPI_SR_TCF; | ||||
SPI0_PUSHR = b; | SPI0_PUSHR = b; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** SPI send multiple bytes */ | /** SPI send multiple bytes */ | ||||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
// clear any data in RX FIFO | // clear any data in RX FIFO | ||||
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | ||||
#if SPI_USE_8BIT_FRAME | #if SPI_USE_8BIT_FRAME | ||||
#else // KINETISK | #else // KINETISK | ||||
//============================================================================== | //============================================================================== | ||||
// Use standard SPI library if not KINETISK | // Use standard SPI library if not KINETISK | ||||
/** | |||||
* Initialize SPI pins. | |||||
*/ | |||||
void SdSpi::begin(uint8_t chipSelectPin) { | |||||
pinMode(chipSelectPin, OUTPUT); | |||||
digitalWrite(chipSelectPin, HIGH); | |||||
SPI.begin(); | |||||
} | |||||
/** Set SPI options for access to SD/SDHC cards. | |||||
* | |||||
* \param[in] divisor SCK clock divider relative to the system clock. | |||||
*/ | |||||
void SdSpi::beginTransaction(uint8_t divisor) { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
SPI.beginTransaction(SPISettings()); | |||||
#else // #if ENABLE_SPI_TRANSACTIONS | |||||
SPI.setBitOrder(MSBFIRST); | |||||
SPI.setDataMode(SPI_MODE0); | |||||
#endif // #if ENABLE_SPI_TRANSACTIONS | |||||
#ifndef SPI_CLOCK_DIV128 | |||||
SPI.setClockDivider(divisor); | |||||
#else // SPI_CLOCK_DIV128 | |||||
int v; | |||||
if (divisor <= 2) { | |||||
v = SPI_CLOCK_DIV2; | |||||
} else if (divisor <= 4) { | |||||
v = SPI_CLOCK_DIV4; | |||||
} else if (divisor <= 8) { | |||||
v = SPI_CLOCK_DIV8; | |||||
} else if (divisor <= 16) { | |||||
v = SPI_CLOCK_DIV16; | |||||
} else if (divisor <= 32) { | |||||
v = SPI_CLOCK_DIV32; | |||||
} else if (divisor <= 64) { | |||||
v = SPI_CLOCK_DIV64; | |||||
} else { | |||||
v = SPI_CLOCK_DIV128; | |||||
} | |||||
SPI.setClockDivider(v); | |||||
#endif // SPI_CLOCK_DIV128 | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
/** Receive a byte. | /** Receive a byte. | ||||
* | * | ||||
* \return The byte. | * \return The byte. | ||||
*/ | */ | ||||
uint8_t SdSpi::receive() { | |||||
uint8_t SdSpiAltDriver::receive() { | |||||
return SPI.transfer(0XFF); | return SPI.transfer(0XFF); | ||||
} | } | ||||
/** Receive multiple bytes. | /** Receive multiple bytes. | ||||
* | * | ||||
* \return Zero for no error or nonzero error code. | * \return Zero for no error or nonzero error code. | ||||
*/ | */ | ||||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
for (size_t i = 0; i < n; i++) { | for (size_t i = 0; i < n; i++) { | ||||
buf[i] = SPI.transfer(0XFF); | buf[i] = SPI.transfer(0XFF); | ||||
} | } | ||||
* | * | ||||
* \param[in] b Byte to send | * \param[in] b Byte to send | ||||
*/ | */ | ||||
void SdSpi::send(uint8_t b) { | |||||
void SdSpiAltDriver::send(uint8_t b) { | |||||
SPI.transfer(b); | SPI.transfer(b); | ||||
} | } | ||||
/** Send multiple bytes. | /** Send multiple bytes. | ||||
* \param[in] buf Buffer for data to be sent. | * \param[in] buf Buffer for data to be sent. | ||||
* \param[in] n Number of bytes to send. | * \param[in] n Number of bytes to send. | ||||
*/ | */ | ||||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
for (size_t i = 0; i < n; i++) { | for (size_t i = 0; i < n; i++) { | ||||
SPI.transfer(buf[i]); | SPI.transfer(buf[i]); | ||||
} | } | ||||
} | } | ||||
#endif // KINETISK | #endif // KINETISK | ||||
//------------------------------------------------------------------------------ | |||||
void SdSpi::endTransaction() { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
SPI.endTransaction(); | |||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
} | |||||
#endif // defined(__arm__) && defined(CORE_TEENSY) | #endif // defined(__arm__) && defined(CORE_TEENSY) |
/* Arduino SdFat Library | |||||
* Copyright (C) 2016 by William Greiman | |||||
* | |||||
* This file is part of the Arduino SdFat Library | |||||
* | |||||
* This Library is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This Library is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with the Arduino SdFat Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "BlockDriver.h" | |||||
bool SdBlockDriverEX::readBlock(uint32_t block, uint8_t* dst) { | |||||
if (m_curState != READ_STATE || block != m_curBlock) { | |||||
if (!syncBlocks()) { | |||||
return false; | |||||
} | |||||
if (!SdSpiCard::readStart(block)) { | |||||
return false; | |||||
} | |||||
m_curBlock = block; | |||||
m_curState = READ_STATE; | |||||
} | |||||
if (!SdSpiCard::readData(dst)) { | |||||
return false; | |||||
} | |||||
m_curBlock++; | |||||
return true; | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
bool SdBlockDriverEX::readBlocks(uint32_t block, uint8_t* dst, size_t nb) { | |||||
for (size_t i = 0; i < nb; i++) { | |||||
if (!readBlock(block + i, dst + i*512UL)) { | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
bool SdBlockDriverEX::syncBlocks() { | |||||
if (m_curState == READ_STATE) { | |||||
m_curState = IDLE_STATE; | |||||
if (!SdSpiCard::readStop()) { | |||||
return false; | |||||
} | |||||
} else if (m_curState == WRITE_STATE) { | |||||
m_curState = IDLE_STATE; | |||||
if (!SdSpiCard::writeStop()) { | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
bool SdBlockDriverEX::writeBlock(uint32_t block, const uint8_t* src) { | |||||
if (m_curState != WRITE_STATE || m_curBlock != block) { | |||||
if (!syncBlocks()) { | |||||
return false; | |||||
} | |||||
if (!SdSpiCard::writeStart(block)) { | |||||
return false; | |||||
} | |||||
m_curBlock = block; | |||||
m_curState = WRITE_STATE; | |||||
} | |||||
if (!SdSpiCard::writeData(src)) { | |||||
return false; | |||||
} | |||||
m_curBlock++; | |||||
return true; | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
bool SdBlockDriverEX::writeBlocks(uint32_t block, | |||||
const uint8_t* src, size_t nb) { | |||||
for (size_t i = 0; i < nb; i++) { | |||||
if (!writeBlock(block + i, src + i*512UL)) { | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} |
/* Arduino SdFat Library | |||||
* Copyright (C) 2016 by William Greiman | |||||
* | |||||
* This file is part of the Arduino SdFat Library | |||||
* | |||||
* This Library is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This Library is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with the Arduino SdFat Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
/** | |||||
* \file | |||||
* \brief Define block driver. | |||||
*/ | |||||
#ifndef BlockDriver_h | |||||
#define BlockDriver_h | |||||
#ifdef ARDUINO | |||||
#include "SdSpiCard/SdSpiCard.h" | |||||
#else // ARDUINO | |||||
#include "SdSpiCard.h" | |||||
#endif // ARDUINO | |||||
#include "FatLib/BaseBlockDriver.h" | |||||
//----------------------------------------------------------------------------- | |||||
/** | |||||
* \class SdBlockDriver | |||||
* \brief Standard SD block driver. | |||||
*/ | |||||
#if ENABLE_EXTENDED_TRANSFER_CLASS | |||||
class SdBlockDriver : public BaseBlockDriver, public SdSpiCard { | |||||
#else // ENABLE_EXTENDED_TRANSFER_CLASS | |||||
class SdBlockDriver : public SdSpiCard { | |||||
#endif // ENABLE_EXTENDED_TRANSFER_CLASS | |||||
public: | |||||
/** Initialize the SD card | |||||
* | |||||
* \param[in] spi SPI driver. | |||||
* \param[in] csPin Card chip select pin number. | |||||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||||
spi->begin(csPin); | |||||
spi->setSpiSettings(SD_SCK_HZ(250000)); | |||||
if (!SdSpiCard::begin(spi)) { | |||||
return false; | |||||
} | |||||
spi->setSpiSettings(spiSettings); | |||||
return true; | |||||
} | |||||
/** | |||||
* Read a 512 byte block from an SD card. | |||||
* | |||||
* \param[in] block Logical block to be read. | |||||
* \param[out] dst Pointer to the location that will receive the data. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool readBlock(uint32_t block, uint8_t* dst) { | |||||
return SdSpiCard::readBlock(block, dst); | |||||
} | |||||
/** End multi-block transfer and go to idle state. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool syncBlocks() { | |||||
return true; | |||||
} | |||||
/** | |||||
* Writes a 512 byte block to an SD card. | |||||
* | |||||
* \param[in] block Logical block to be written. | |||||
* \param[in] src Pointer to the location of the data to be written. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool writeBlock(uint32_t block, const uint8_t* src) { | |||||
return SdSpiCard::writeBlock(block, src); | |||||
} | |||||
/** | |||||
* Read multiple 512 byte blocks from an SD card. | |||||
* | |||||
* \param[in] block Logical block to be read. | |||||
* \param[in] nb Number of blocks to be read. | |||||
* \param[out] dst Pointer to the location that will receive the data. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) { | |||||
return SdSpiCard::readBlocks(block, dst, nb); | |||||
} | |||||
/** | |||||
* Write multiple 512 byte blocks to an SD card. | |||||
* | |||||
* \param[in] block Logical block to be written. | |||||
* \param[in] nb Number of blocks to be written. | |||||
* \param[in] src Pointer to the location of the data to be written. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) { | |||||
return SdSpiCard::writeBlocks(block, src, nb); | |||||
} | |||||
}; | |||||
//----------------------------------------------------------------------------- | |||||
/** | |||||
* \class SdBlockDriverEX | |||||
* \brief Extended SD I/O block driver. | |||||
*/ | |||||
class SdBlockDriverEX : public SdSpiCard, public BaseBlockDriver { | |||||
public: | |||||
/** Initialize the SD card | |||||
* | |||||
* \param[in] spi SPI driver. | |||||
* \param[in] csPin Card chip select pin number. | |||||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||||
m_curState = IDLE_STATE; | |||||
spi->begin(csPin); | |||||
spi->setSpiSettings(SD_SCK_HZ(250000)); | |||||
if (!SdSpiCard::begin(spi)) { | |||||
return false; | |||||
} | |||||
spi->setSpiSettings(spiSettings); | |||||
return true; | |||||
} | |||||
/** | |||||
* Read a 512 byte block from an SD card. | |||||
* | |||||
* \param[in] block Logical block to be read. | |||||
* \param[out] dst Pointer to the location that will receive the data. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool readBlock(uint32_t block, uint8_t* dst); | |||||
/** End multi-block transfer and go to idle state. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool syncBlocks(); | |||||
/** | |||||
* Writes a 512 byte block to an SD card. | |||||
* | |||||
* \param[in] block Logical block to be written. | |||||
* \param[in] src Pointer to the location of the data to be written. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool writeBlock(uint32_t block, const uint8_t* src); | |||||
/** | |||||
* Read multiple 512 byte blocks from an SD card. | |||||
* | |||||
* \param[in] block Logical block to be read. | |||||
* \param[in] nb Number of blocks to be read. | |||||
* \param[out] dst Pointer to the location that will receive the data. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool readBlocks(uint32_t block, uint8_t* dst, size_t nb); | |||||
/** | |||||
* Write multiple 512 byte blocks to an SD card. | |||||
* | |||||
* \param[in] block Logical block to be written. | |||||
* \param[in] nb Number of blocks to be written. | |||||
* \param[in] src Pointer to the location of the data to be written. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb); | |||||
private: | |||||
static const uint32_t IDLE_STATE = 0; | |||||
static const uint32_t READ_STATE = 1; | |||||
static const uint32_t WRITE_STATE = 2; | |||||
uint32_t m_curBlock; | |||||
uint8_t m_curState; | |||||
}; | |||||
//----------------------------------------------------------------------------- | |||||
/** typedef for BlockDriver */ | |||||
#if ENABLE_EXTENDED_TRANSFER_CLASS | |||||
typedef BaseBlockDriver BlockDriver; | |||||
#else // ENABLE_EXTENDED_TRANSFER_CLASS | |||||
typedef SdBlockDriver BlockDriver; | |||||
#endif // ENABLE_EXTENDED_TRANSFER_CLASS | |||||
#endif // BlockDriver_h |
* \class File | * \class File | ||||
* \brief Arduino SD.h style File API | * \brief Arduino SD.h style File API | ||||
*/ | */ | ||||
#if ARDUINO_FILE_USES_STREAM | |||||
class File : public FatFile, public Stream { | class File : public FatFile, public Stream { | ||||
#else // ARDUINO_FILE_USES_STREAM | |||||
class File : public FatFile, public Print { | |||||
#endif // ARDUINO_FILE_USES_STREAM | |||||
public: | public: | ||||
File() {} | File() {} | ||||
/** Create a file object and open it in the current working directory. | /** Create a file object and open it in the current working directory. |
*/ | */ | ||||
#include "FatLibConfig.h" | #include "FatLibConfig.h" | ||||
#if ENABLE_ARDUINO_FEATURES | #if ENABLE_ARDUINO_FEATURES | ||||
#include "SysCall.h" | |||||
#include "bufstream.h" | #include "bufstream.h" | ||||
//============================================================================== | //============================================================================== | ||||
/** | /** | ||||
uint32_t t; | uint32_t t; | ||||
m_line[0] = '\0'; | m_line[0] = '\0'; | ||||
while (!m_hw->available()) { | while (!m_hw->available()) { | ||||
SysCall::yield(); | |||||
yield(); | |||||
} | } | ||||
while (1) { | while (1) { |
#ifndef BaseBlockDriver_h | |||||
#define BaseBlockDriver_h | |||||
#include "FatLibConfig.h" | |||||
/** | |||||
* \class BaseBlockDriver | |||||
* \brief Base block driver. | |||||
*/ | |||||
class BaseBlockDriver { | |||||
public: | |||||
/** | |||||
* Read a 512 byte block from an SD card. | |||||
* | |||||
* \param[in] block Logical block to be read. | |||||
* \param[out] dst Pointer to the location that will receive the data. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
virtual bool readBlock(uint32_t block, uint8_t* dst) = 0; | |||||
/** End multi-block transfer and go to idle state. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
virtual bool syncBlocks() = 0; | |||||
/** | |||||
* Writes a 512 byte block to an SD card. | |||||
* | |||||
* \param[in] block Logical block to be written. | |||||
* \param[in] src Pointer to the location of the data to be written. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
virtual bool writeBlock(uint32_t block, const uint8_t* src) = 0; | |||||
#if USE_MULTI_BLOCK_IO | |||||
/** | |||||
* Read multiple 512 byte blocks from an SD card. | |||||
* | |||||
* \param[in] block Logical block to be read. | |||||
* \param[in] nb Number of blocks to be read. | |||||
* \param[out] dst Pointer to the location that will receive the data. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0; | |||||
/** | |||||
* Write multiple 512 byte blocks to an SD card. | |||||
* | |||||
* \param[in] block Logical block to be written. | |||||
* \param[in] nb Number of blocks to be written. | |||||
* \param[in] src Pointer to the location of the data to be written. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0; | |||||
#endif // USE_MULTI_BLOCK_IO | |||||
}; | |||||
#endif // BaseBlockDriver_h |
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// use the gnu style oflag in open() | // use the gnu style oflag in open() | ||||
/** open() oflag for reading */ | /** open() oflag for reading */ | ||||
uint8_t const O_READ = 0X01; | |||||
const uint8_t O_READ = 0X01; | |||||
/** open() oflag - same as O_IN */ | /** open() oflag - same as O_IN */ | ||||
uint8_t const O_RDONLY = O_READ; | |||||
const uint8_t O_RDONLY = O_READ; | |||||
/** open() oflag for write */ | /** open() oflag for write */ | ||||
uint8_t const O_WRITE = 0X02; | |||||
const uint8_t O_WRITE = 0X02; | |||||
/** open() oflag - same as O_WRITE */ | /** open() oflag - same as O_WRITE */ | ||||
uint8_t const O_WRONLY = O_WRITE; | |||||
const uint8_t O_WRONLY = O_WRITE; | |||||
/** open() oflag for reading and writing */ | /** open() oflag for reading and writing */ | ||||
uint8_t const O_RDWR = (O_READ | O_WRITE); | |||||
const uint8_t O_RDWR = (O_READ | O_WRITE); | |||||
/** open() oflag mask for access modes */ | /** open() oflag mask for access modes */ | ||||
uint8_t const O_ACCMODE = (O_READ | O_WRITE); | |||||
const uint8_t O_ACCMODE = (O_READ | O_WRITE); | |||||
/** The file offset shall be set to the end of the file prior to each write. */ | /** The file offset shall be set to the end of the file prior to each write. */ | ||||
uint8_t const O_APPEND = 0X04; | |||||
const uint8_t O_APPEND = 0X04; | |||||
/** synchronous writes - call sync() after each write */ | /** synchronous writes - call sync() after each write */ | ||||
uint8_t const O_SYNC = 0X08; | |||||
const uint8_t O_SYNC = 0X08; | |||||
/** truncate the file to zero length */ | /** truncate the file to zero length */ | ||||
uint8_t const O_TRUNC = 0X10; | |||||
const uint8_t O_TRUNC = 0X10; | |||||
/** set the initial position at the end of the file */ | /** set the initial position at the end of the file */ | ||||
uint8_t const O_AT_END = 0X20; | |||||
const uint8_t O_AT_END = 0X20; | |||||
/** create the file if nonexistent */ | /** create the file if nonexistent */ | ||||
uint8_t const O_CREAT = 0X40; | |||||
const uint8_t O_CREAT = 0X40; | |||||
/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ | /** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ | ||||
uint8_t const O_EXCL = 0X80; | |||||
const uint8_t O_EXCL = 0X80; | |||||
// FatFile class static and const definitions | // FatFile class static and const definitions | ||||
// flags for ls() | // flags for ls() | ||||
/** ls() flag for list all files including hidden. */ | /** ls() flag for list all files including hidden. */ | ||||
uint8_t const LS_A = 1; | |||||
const uint8_t LS_A = 1; | |||||
/** ls() flag to print modify. date */ | /** ls() flag to print modify. date */ | ||||
uint8_t const LS_DATE = 2; | |||||
const uint8_t LS_DATE = 2; | |||||
/** ls() flag to print file size. */ | /** ls() flag to print file size. */ | ||||
uint8_t const LS_SIZE = 4; | |||||
const uint8_t LS_SIZE = 4; | |||||
/** ls() flag for recursive list of subdirectories */ | /** ls() flag for recursive list of subdirectories */ | ||||
uint8_t const LS_R = 8; | |||||
const uint8_t LS_R = 8; | |||||
// flags for timestamp | // flags for timestamp | ||||
/** set the file's last access date */ | /** set the file's last access date */ | ||||
uint8_t const T_ACCESS = 1; | |||||
const uint8_t T_ACCESS = 1; | |||||
/** set the file's creation date and time */ | /** set the file's creation date and time */ | ||||
uint8_t const T_CREATE = 2; | |||||
const uint8_t T_CREATE = 2; | |||||
/** Set the file's write date and time */ | /** Set the file's write date and time */ | ||||
uint8_t const T_WRITE = 4; | |||||
const uint8_t T_WRITE = 4; | |||||
#endif // FatApiConstants_h | #endif // FatApiConstants_h |
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
block = m_vol->clusterStartBlock(m_curCluster); | |||||
block = m_vol->clusterFirstBlock(m_curCluster); | |||||
pc = m_vol->cacheFetchData(block, FatCache::CACHE_RESERVE_FOR_WRITE); | pc = m_vol->cacheFetchData(block, FatCache::CACHE_RESERVE_FOR_WRITE); | ||||
if (!pc) { | if (!pc) { | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
*bgnBlock = m_vol->clusterStartBlock(m_firstCluster); | |||||
*endBlock = m_vol->clusterStartBlock(c) | |||||
*bgnBlock = m_vol->clusterFirstBlock(m_firstCluster); | |||||
*endBlock = m_vol->clusterFirstBlock(c) | |||||
+ m_vol->blocksPerCluster() - 1; | + m_vol->blocksPerCluster() - 1; | ||||
return true; | return true; | ||||
} | } | ||||
bool FatFile::createContiguous(FatFile* dirFile, | bool FatFile::createContiguous(FatFile* dirFile, | ||||
const char* path, uint32_t size) { | const char* path, uint32_t size) { | ||||
uint32_t count; | uint32_t count; | ||||
// don't allow zero length file | // don't allow zero length file | ||||
if (size == 0) { | if (size == 0) { | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
// insure sync() will update dir entry | // insure sync() will update dir entry | ||||
m_flags |= F_FILE_DIR_DIRTY; | m_flags |= F_FILE_DIR_DIRTY; | ||||
return sync(); | return sync(); | ||||
fail: | fail: | ||||
} | } | ||||
// cache block for '.' and '..' | // cache block for '.' and '..' | ||||
block = m_vol->clusterStartBlock(m_firstCluster); | |||||
block = m_vol->clusterFirstBlock(m_firstCluster); | |||||
pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); | pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); | ||||
if (!pc) { | if (!pc) { | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
if (dirFile->m_dirCluster == 0) { | if (dirFile->m_dirCluster == 0) { | ||||
return openRoot(dirFile->m_vol); | return openRoot(dirFile->m_vol); | ||||
} | } | ||||
lbn = dirFile->m_vol->clusterStartBlock(dirFile->m_dirCluster); | |||||
lbn = dirFile->m_vol->clusterFirstBlock(dirFile->m_dirCluster); | |||||
cb = dirFile->m_vol->cacheFetchData(lbn, FatCache::CACHE_FOR_READ); | cb = dirFile->m_vol->cacheFetchData(lbn, FatCache::CACHE_FOR_READ); | ||||
if (!cb) { | if (!cb) { | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster; | |||||
block = m_vol->clusterFirstBlock(m_curCluster) + blockOfCluster; | |||||
} | } | ||||
if (offset != 0 || toRead < 512 || block == m_vol->cacheBlockNumber()) { | if (offset != 0 || toRead < 512 || block == m_vol->cacheBlockNumber()) { | ||||
// amount to be read from current block | // amount to be read from current block | ||||
// update dot dot if directory | // update dot dot if directory | ||||
if (dirCluster) { | if (dirCluster) { | ||||
// get new dot dot | // get new dot dot | ||||
uint32_t block = m_vol->clusterStartBlock(dirCluster); | |||||
uint32_t block = m_vol->clusterFirstBlock(dirCluster); | |||||
pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ); | pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ); | ||||
if (!pc) { | if (!pc) { | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
// store new dot dot | // store new dot dot | ||||
block = m_vol->clusterStartBlock(m_firstCluster); | |||||
block = m_vol->clusterFirstBlock(m_firstCluster); | |||||
pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); | pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); | ||||
if (!pc) { | if (!pc) { | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
} | } | ||||
} | } | ||||
// block for data write | // block for data write | ||||
uint32_t block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster; | |||||
uint32_t block = m_vol->clusterFirstBlock(m_curCluster) + blockOfCluster; | |||||
if (blockOffset != 0 || nToWrite < 512) { | if (blockOffset != 0 || nToWrite < 512) { | ||||
// partial block - must use cache | // partial block - must use cache |
/** Create and open a new contiguous file of a specified size. | /** Create and open a new contiguous file of a specified size. | ||||
* | * | ||||
* \param[in] dirFile The directory where the file will be created. | * \param[in] dirFile The directory where the file will be created. | ||||
* \param[in] path A path with a valid DOS 8.3 file name. | |||||
* \param[in] path A path with a validfile name. | |||||
* \param[in] size The desired file size. | * \param[in] size The desired file size. | ||||
* | * | ||||
* \return The value true is returned for success and | * \return The value true is returned for success and | ||||
*/ | */ | ||||
bool createContiguous(FatFile* dirFile, | bool createContiguous(FatFile* dirFile, | ||||
const char* path, uint32_t size); | const char* path, uint32_t size); | ||||
/** Create and open a new contiguous file of a specified size. | |||||
* | |||||
* \param[in] path A path with a validfile name. | |||||
* \param[in] size The desired file size. | |||||
* | |||||
* \return The value true is returned for success and | |||||
* the value false, is returned for failure. | |||||
*/ | |||||
bool createContiguous(const char* path, uint32_t size) { | |||||
return createContiguous(m_cwd, path, size); | |||||
} | |||||
/** \return The current cluster number for a file or directory. */ | /** \return The current cluster number for a file or directory. */ | ||||
uint32_t curCluster() const { | uint32_t curCluster() const { | ||||
return m_curCluster; | return m_curCluster; | ||||
m_cwd = dir; | m_cwd = dir; | ||||
return true; | return true; | ||||
} | } | ||||
/** \return first block of file or zero for empty file. */ | |||||
uint32_t firstBlock() { | |||||
if (m_firstCluster) { | |||||
return m_vol->clusterFirstBlock(m_firstCluster); | |||||
} | |||||
return 0; | |||||
} | |||||
/** The sync() call causes all modified data and directory fields | /** The sync() call causes all modified data and directory fields | ||||
* to be written to the storage device. | * to be written to the storage device. | ||||
* | * | ||||
// bits defined in m_flags | // bits defined in m_flags | ||||
// should be 0X0F | // should be 0X0F | ||||
static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); | |||||
static const uint8_t F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); | |||||
// sync of directory entry required | // sync of directory entry required | ||||
static uint8_t const F_FILE_DIR_DIRTY = 0X80; | |||||
static const uint8_t F_FILE_DIR_DIRTY = 0X80; | |||||
// global pointer to cwd dir | // global pointer to cwd dir | ||||
static FatFile* m_cwd; | static FatFile* m_cwd; |
fail: | fail: | ||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
size_t FatFile::printFileSize(print_t* pr) { | size_t FatFile::printFileSize(print_t* pr) { | ||||
char buf[11]; | char buf[11]; |
#define FatFileSystem_h | #define FatFileSystem_h | ||||
#include "FatVolume.h" | #include "FatVolume.h" | ||||
#include "FatFile.h" | #include "FatFile.h" | ||||
#include "ArduinoStream.h" | |||||
#include "ArduinoFiles.h" | #include "ArduinoFiles.h" | ||||
/** | /** | ||||
* \file | * \file | ||||
public: | public: | ||||
/** | /** | ||||
* Initialize an FatFileSystem object. | * Initialize an FatFileSystem object. | ||||
* \param[in] blockDev Device block driver. | |||||
* \param[in] part partition to initialize. | * \param[in] part partition to initialize. | ||||
* \return The value true is returned for success and | * \return The value true is returned for success and | ||||
* the value false is returned for failure. | * the value false is returned for failure. | ||||
*/ | */ | ||||
bool begin(uint8_t part = 0) { | |||||
bool begin(BlockDriver* blockDev, uint8_t part = 0) { | |||||
m_blockDev = blockDev; | |||||
vwd()->close(); | vwd()->close(); | ||||
return (part ? init(part) : init(1) || init(0)) | return (part ? init(part) : init(1) || init(0)) | ||||
&& vwd()->openRoot(this) && FatFile::setCwd(vwd()); | && vwd()->openRoot(this) && FatFile::setCwd(vwd()); | ||||
* \return a File object. | * \return a File object. | ||||
*/ | */ | ||||
File open(const String &path, uint8_t mode = FILE_READ) { | File open(const String &path, uint8_t mode = FILE_READ) { | ||||
return open(path.c_str(), mode ); | |||||
return open(path.c_str(), mode ); | |||||
} | } | ||||
#endif // ENABLE_ARDUINO_FEATURES | #endif // ENABLE_ARDUINO_FEATURES | ||||
/** Change a volume's working directory to root | /** Change a volume's working directory to root | ||||
if (!dir.isDir()) { | if (!dir.isDir()) { | ||||
goto fail; | goto fail; | ||||
} | } | ||||
// *m_vwd = dir; | |||||
m_vwd = dir; | m_vwd = dir; | ||||
if (set_cwd) { | if (set_cwd) { | ||||
FatFile::setCwd(vwd()); | FatFile::setCwd(vwd()); | ||||
* | * | ||||
* LS_R - Recursive list of subdirectories. | * LS_R - Recursive list of subdirectories. | ||||
*/ | */ | ||||
void ls(print_t* pr, uint8_t flags) { | |||||
void ls(print_t* pr, uint8_t flags = 0) { | |||||
vwd()->ls(pr, flags); | vwd()->ls(pr, flags); | ||||
} | } | ||||
//---------------------------------------------------------------------------- | //---------------------------------------------------------------------------- |
#define USE_LONG_FILE_NAMES 1 | #define USE_LONG_FILE_NAMES 1 | ||||
#endif // USE_LONG_FILE_NAMES | #endif // USE_LONG_FILE_NAMES | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | |||||
* Set ARDUINO_FILE_USES_STREAM nonzero to use Stream as the base class | |||||
* for the Arduino File class. If ARDUINO_FILE_USES_STREAM is zero, Print | |||||
* will be used as the base class for the Arduino File class. | |||||
* | |||||
* You can save some flash if you do not use Stream input functions such as | |||||
* find(), findUntil(), readBytesUntil(), readString(), readStringUntil(), | |||||
* parseInt(), and parsefloat(). | |||||
*/ | |||||
#ifndef ARDUINO_FILE_USES_STREAM | |||||
#define ARDUINO_FILE_USES_STREAM 1 | |||||
#endif // ARDUINO_FILE_USES_STREAM | |||||
//------------------------------------------------------------------------------ | |||||
/** | /** | ||||
* Set USE_SEPARATE_FAT_CACHE non-zero to use a second 512 byte cache | * Set USE_SEPARATE_FAT_CACHE non-zero to use a second 512 byte cache | ||||
* for FAT table entries. Improves performance for large writes that | * for FAT table entries. Improves performance for large writes that | ||||
#endif // RAMEND | #endif // RAMEND | ||||
#endif // USE_MULTI_BLOCK_IO | #endif // USE_MULTI_BLOCK_IO | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | |||||
* Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters | |||||
* updated. This will increase the speed of the freeClusterCount() call | |||||
* after the first call. Extra flash will be required. | |||||
*/ | |||||
#ifndef MAINTAIN_FREE_CLUSTER_COUNT | |||||
#define MAINTAIN_FREE_CLUSTER_COUNT 0 | |||||
#endif // MAINTAIN_FREE_CLUSTER_COUNT | |||||
//------------------------------------------------------------------------------ | |||||
/** | /** | ||||
* Set DESTRUCTOR_CLOSES_FILE non-zero to close a file in its destructor. | * Set DESTRUCTOR_CLOSES_FILE non-zero to close a file in its destructor. | ||||
* | * | ||||
/** | /** | ||||
* Enable Extra features for Arduino. | * Enable Extra features for Arduino. | ||||
*/ | */ | ||||
// #define ENABLE_ARDUINO_FEATURES 0 ////////////////////////FIX THIS ///////////////// | |||||
#ifndef ENABLE_ARDUINO_FEATURES | #ifndef ENABLE_ARDUINO_FEATURES | ||||
#include <Arduino.h> | |||||
#if defined(ARDUINO) || defined(PLATFORM_ID) || defined(DOXYGEN) | #if defined(ARDUINO) || defined(PLATFORM_ID) || defined(DOXYGEN) | ||||
#define ENABLE_ARDUINO_FEATURES 1 | #define ENABLE_ARDUINO_FEATURES 1 | ||||
#else // #if defined(ARDUINO) || defined(DOXYGEN) | #else // #if defined(ARDUINO) || defined(DOXYGEN) |
*/ | */ | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Value for byte 510 of boot block or MBR */ | /** Value for byte 510 of boot block or MBR */ | ||||
uint8_t const BOOTSIG0 = 0X55; | |||||
const uint8_t BOOTSIG0 = 0X55; | |||||
/** Value for byte 511 of boot block or MBR */ | /** Value for byte 511 of boot block or MBR */ | ||||
uint8_t const BOOTSIG1 = 0XAA; | |||||
const uint8_t BOOTSIG1 = 0XAA; | |||||
/** Value for bootSignature field int FAT/FAT32 boot sector */ | /** Value for bootSignature field int FAT/FAT32 boot sector */ | ||||
uint8_t const EXTENDED_BOOT_SIG = 0X29; | |||||
const uint8_t EXTENDED_BOOT_SIG = 0X29; | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | /** | ||||
* \struct partitionTable | * \struct partitionTable | ||||
typedef struct fat32_boot fat32_boot_t; | typedef struct fat32_boot fat32_boot_t; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Lead signature for a FSINFO sector */ | /** Lead signature for a FSINFO sector */ | ||||
uint32_t const FSINFO_LEAD_SIG = 0x41615252; | |||||
const uint32_t FSINFO_LEAD_SIG = 0x41615252; | |||||
/** Struct signature for a FSINFO sector */ | /** Struct signature for a FSINFO sector */ | ||||
uint32_t const FSINFO_STRUCT_SIG = 0x61417272; | |||||
const uint32_t FSINFO_STRUCT_SIG = 0x61417272; | |||||
/** | /** | ||||
* \struct fat32_fsinfo | * \struct fat32_fsinfo | ||||
* | * | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// End Of Chain values for FAT entries | // End Of Chain values for FAT entries | ||||
/** FAT12 end of chain value used by Microsoft. */ | /** FAT12 end of chain value used by Microsoft. */ | ||||
uint16_t const FAT12EOC = 0XFFF; | |||||
const uint16_t FAT12EOC = 0XFFF; | |||||
/** Minimum value for FAT12 EOC. Use to test for EOC. */ | /** Minimum value for FAT12 EOC. Use to test for EOC. */ | ||||
uint16_t const FAT12EOC_MIN = 0XFF8; | |||||
const uint16_t FAT12EOC_MIN = 0XFF8; | |||||
/** FAT16 end of chain value used by Microsoft. */ | /** FAT16 end of chain value used by Microsoft. */ | ||||
uint16_t const FAT16EOC = 0XFFFF; | |||||
const uint16_t FAT16EOC = 0XFFFF; | |||||
/** Minimum value for FAT16 EOC. Use to test for EOC. */ | /** Minimum value for FAT16 EOC. Use to test for EOC. */ | ||||
uint16_t const FAT16EOC_MIN = 0XFFF8; | |||||
const uint16_t FAT16EOC_MIN = 0XFFF8; | |||||
/** FAT32 end of chain value used by Microsoft. */ | /** FAT32 end of chain value used by Microsoft. */ | ||||
uint32_t const FAT32EOC = 0X0FFFFFFF; | |||||
const uint32_t FAT32EOC = 0X0FFFFFFF; | |||||
/** Minimum value for FAT32 EOC. Use to test for EOC. */ | /** Minimum value for FAT32 EOC. Use to test for EOC. */ | ||||
uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; | |||||
const uint32_t FAT32EOC_MIN = 0X0FFFFFF8; | |||||
/** Mask a for FAT32 entry. Entries are 28 bits. */ | /** Mask a for FAT32 entry. Entries are 28 bits. */ | ||||
uint32_t const FAT32MASK = 0X0FFFFFFF; | |||||
const uint32_t FAT32MASK = 0X0FFFFFFF; | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | /** | ||||
* \struct directoryEntry | * \struct directoryEntry | ||||
// Definitions for directory entries | // Definitions for directory entries | ||||
// | // | ||||
/** escape for name[0] = 0XE5 */ | /** escape for name[0] = 0XE5 */ | ||||
uint8_t const DIR_NAME_0XE5 = 0X05; | |||||
const uint8_t DIR_NAME_0XE5 = 0X05; | |||||
/** name[0] value for entry that is free after being "deleted" */ | /** name[0] value for entry that is free after being "deleted" */ | ||||
uint8_t const DIR_NAME_DELETED = 0XE5; | |||||
const uint8_t DIR_NAME_DELETED = 0XE5; | |||||
/** name[0] value for entry that is free and no allocated entries follow */ | /** name[0] value for entry that is free and no allocated entries follow */ | ||||
uint8_t const DIR_NAME_FREE = 0X00; | |||||
const uint8_t DIR_NAME_FREE = 0X00; | |||||
/** file is read-only */ | /** file is read-only */ | ||||
uint8_t const DIR_ATT_READ_ONLY = 0X01; | |||||
const uint8_t DIR_ATT_READ_ONLY = 0X01; | |||||
/** File should e hidden in directory listings */ | /** File should e hidden in directory listings */ | ||||
uint8_t const DIR_ATT_HIDDEN = 0X02; | |||||
const uint8_t DIR_ATT_HIDDEN = 0X02; | |||||
/** Entry is for a system file */ | /** Entry is for a system file */ | ||||
uint8_t const DIR_ATT_SYSTEM = 0X04; | |||||
const uint8_t DIR_ATT_SYSTEM = 0X04; | |||||
/** Directory entry contains the volume label */ | /** Directory entry contains the volume label */ | ||||
uint8_t const DIR_ATT_VOLUME_ID = 0X08; | |||||
const uint8_t DIR_ATT_VOLUME_ID = 0X08; | |||||
/** Entry is for a directory */ | /** Entry is for a directory */ | ||||
uint8_t const DIR_ATT_DIRECTORY = 0X10; | |||||
const uint8_t DIR_ATT_DIRECTORY = 0X10; | |||||
/** Old DOS archive bit for backup support */ | /** Old DOS archive bit for backup support */ | ||||
uint8_t const DIR_ATT_ARCHIVE = 0X20; | |||||
const uint8_t DIR_ATT_ARCHIVE = 0X20; | |||||
/** Test value for long name entry. Test is | /** Test value for long name entry. Test is | ||||
(d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ | (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ | ||||
uint8_t const DIR_ATT_LONG_NAME = 0X0F; | |||||
const uint8_t DIR_ATT_LONG_NAME = 0X0F; | |||||
/** Test mask for long name entry */ | /** Test mask for long name entry */ | ||||
uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; | |||||
const uint8_t DIR_ATT_LONG_NAME_MASK = 0X3F; | |||||
/** defined attribute bits */ | /** defined attribute bits */ | ||||
uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; | |||||
const uint8_t DIR_ATT_DEFINED_BITS = 0X3F; | |||||
/** Mask for file/subdirectory tests */ | /** Mask for file/subdirectory tests */ | ||||
uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); | |||||
const uint8_t DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); | |||||
/** Filename base-name is all lower case */ | /** Filename base-name is all lower case */ | ||||
const uint8_t DIR_NT_LC_BASE = 0X08; | const uint8_t DIR_NT_LC_BASE = 0X08; | ||||
return 2*(fatTime & 0X1F); | return 2*(fatTime & 0X1F); | ||||
} | } | ||||
/** Default date for file timestamps is 1 Jan 2000 */ | /** Default date for file timestamps is 1 Jan 2000 */ | ||||
uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; | |||||
const uint16_t FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; | |||||
/** Default time for file timestamp is 1 am */ | /** Default time for file timestamp is 1 am */ | ||||
uint16_t const FAT_DEFAULT_TIME = (1 << 11); | |||||
const uint16_t FAT_DEFAULT_TIME = (1 << 11); | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** Dimension of first name field in long directory entry */ | /** Dimension of first name field in long directory entry */ | ||||
const uint8_t LDIR_NAME1_DIM = 5; | const uint8_t LDIR_NAME1_DIM = 5; |
return &m_block; | return &m_block; | ||||
fail: | fail: | ||||
return 0; | return 0; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
uint32_t FatVolume::clusterStartBlock(uint32_t cluster) const { | |||||
uint32_t FatVolume::clusterFirstBlock(uint32_t cluster) const { | |||||
return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); | return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// error if reserved cluster of beyond FAT | // error if reserved cluster of beyond FAT | ||||
DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | ||||
if (m_fatType == 32) { | |||||
if (fatType() == 32) { | |||||
lba = m_fatStartBlock + (cluster >> 7); | lba = m_fatStartBlock + (cluster >> 7); | ||||
pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | ||||
if (!pc) { | if (!pc) { | ||||
next = pc->fat32[cluster & 0X7F] & FAT32MASK; | next = pc->fat32[cluster & 0X7F] & FAT32MASK; | ||||
goto done; | goto done; | ||||
} | } | ||||
if (m_fatType == 16) { | |||||
if (fatType() == 16) { | |||||
lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | ||||
pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | ||||
if (!pc) { | if (!pc) { | ||||
next = pc->fat16[cluster & 0XFF]; | next = pc->fat16[cluster & 0XFF]; | ||||
goto done; | goto done; | ||||
} | } | ||||
if (FAT12_SUPPORT && m_fatType == 12) { | |||||
if (FAT12_SUPPORT && fatType() == 12) { | |||||
uint16_t index = cluster; | uint16_t index = cluster; | ||||
index += index >> 1; | index += index >> 1; | ||||
lba = m_fatStartBlock + (index >> 9); | lba = m_fatStartBlock + (index >> 9); | ||||
// error if reserved cluster of beyond FAT | // error if reserved cluster of beyond FAT | ||||
DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | ||||
if (m_fatType == 32) { | |||||
if (fatType() == 32) { | |||||
lba = m_fatStartBlock + (cluster >> 7); | lba = m_fatStartBlock + (cluster >> 7); | ||||
pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | ||||
if (!pc) { | if (!pc) { | ||||
return true; | return true; | ||||
} | } | ||||
if (m_fatType == 16) { | |||||
if (fatType() == 16) { | |||||
lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | ||||
pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | ||||
if (!pc) { | if (!pc) { | ||||
return true; | return true; | ||||
} | } | ||||
if (FAT12_SUPPORT && m_fatType == 12) { | |||||
if (FAT12_SUPPORT && fatType() == 12) { | |||||
uint16_t index = cluster; | uint16_t index = cluster; | ||||
index += index >> 1; | index += index >> 1; | ||||
lba = m_fatStartBlock + (index >> 9); | lba = m_fatStartBlock + (index >> 9); | ||||
uint32_t todo = m_lastCluster + 1; | uint32_t todo = m_lastCluster + 1; | ||||
uint16_t n; | uint16_t n; | ||||
if (FAT12_SUPPORT && m_fatType == 12) { | |||||
if (FAT12_SUPPORT && fatType() == 12) { | |||||
for (unsigned i = 2; i < todo; i++) { | for (unsigned i = 2; i < todo; i++) { | ||||
uint32_t c; | uint32_t c; | ||||
int8_t fg = fatGet(i, &c); | int8_t fg = fatGet(i, &c); | ||||
free++; | free++; | ||||
} | } | ||||
} | } | ||||
} else if (m_fatType == 16 || m_fatType == 32) { | |||||
} else if (fatType() == 16 || fatType() == 32) { | |||||
lba = m_fatStartBlock; | lba = m_fatStartBlock; | ||||
while (todo) { | while (todo) { | ||||
cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ); | cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ); | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
n = m_fatType == 16 ? 256 : 128; | |||||
n = fatType() == 16 ? 256 : 128; | |||||
if (todo < n) { | if (todo < n) { | ||||
n = todo; | n = todo; | ||||
} | } | ||||
if (m_fatType == 16) { | |||||
if (fatType() == 16) { | |||||
for (uint16_t i = 0; i < n; i++) { | for (uint16_t i = 0; i < n; i++) { | ||||
if (pc->fat16[i] == 0) { | if (pc->fat16[i] == 0) { | ||||
free++; | free++; | ||||
uint8_t tmp; | uint8_t tmp; | ||||
m_fatType = 0; | m_fatType = 0; | ||||
m_allocSearchStart = 1; | m_allocSearchStart = 1; | ||||
m_cache.init(this); | m_cache.init(this); | ||||
#if USE_SEPARATE_FAT_CACHE | #if USE_SEPARATE_FAT_CACHE | ||||
m_fatCache.init(this); | m_fatCache.init(this); | ||||
#endif // USE_SEPARATE_FAT_CACHE | #endif // USE_SEPARATE_FAT_CACHE | ||||
// if part == 0 assume super floppy with FAT boot sector in block zero | // if part == 0 assume super floppy with FAT boot sector in block zero | ||||
// if part > 0 assume mbr volume with partition table | // if part > 0 assume mbr volume with partition table | ||||
if (part) { | if (part) { | ||||
} | } | ||||
m_blocksPerCluster = fbs->sectorsPerCluster; | m_blocksPerCluster = fbs->sectorsPerCluster; | ||||
m_clusterBlockMask = m_blocksPerCluster - 1; | m_clusterBlockMask = m_blocksPerCluster - 1; | ||||
// determine shift that is same as multiply by m_blocksPerCluster | // determine shift that is same as multiply by m_blocksPerCluster | ||||
m_clusterSizeShift = 0; | m_clusterSizeShift = 0; | ||||
for (tmp = 1; m_blocksPerCluster != tmp; tmp <<= 1, m_clusterSizeShift++) { | for (tmp = 1; m_blocksPerCluster != tmp; tmp <<= 1, m_clusterSizeShift++) { | ||||
goto fail; | goto fail; | ||||
} | } | ||||
} | } | ||||
m_blocksPerFat = fbs->sectorsPerFat16 ? | m_blocksPerFat = fbs->sectorsPerFat16 ? | ||||
fbs->sectorsPerFat16 : fbs->sectorsPerFat32; | fbs->sectorsPerFat16 : fbs->sectorsPerFat32; | ||||
// Indicate unknown number of free clusters. | // Indicate unknown number of free clusters. | ||||
setFreeClusterCount(-1); | setFreeClusterCount(-1); | ||||
// FAT type is determined by cluster count | // FAT type is determined by cluster count | ||||
if (clusterCount < 4085) { | if (clusterCount < 4085) { | ||||
m_fatType = 12; | m_fatType = 12; | ||||
cache_t* cache; | cache_t* cache; | ||||
uint16_t count; | uint16_t count; | ||||
uint32_t lbn; | uint32_t lbn; | ||||
if (!m_fatType) { | |||||
if (!fatType()) { | |||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
} | } | ||||
memset(cache->data, 0, 512); | memset(cache->data, 0, 512); | ||||
// Zero root. | // Zero root. | ||||
if (m_fatType == 32) { | |||||
lbn = clusterStartBlock(m_rootDirStart); | |||||
if (fatType() == 32) { | |||||
lbn = clusterFirstBlock(m_rootDirStart); | |||||
count = m_blocksPerCluster; | count = m_blocksPerCluster; | ||||
} else { | } else { | ||||
lbn = m_rootDirStart; | lbn = m_rootDirStart; | ||||
} | } | ||||
} | } | ||||
// Reserve first two clusters. | // Reserve first two clusters. | ||||
if (m_fatType == 32) { | |||||
if (fatType() == 32) { | |||||
cache->fat32[0] = 0x0FFFFFF8; | cache->fat32[0] = 0x0FFFFFF8; | ||||
cache->fat32[1] = 0x0FFFFFFF; | cache->fat32[1] = 0x0FFFFFFF; | ||||
} else if (m_fatType == 16) { | |||||
} else if (fatType() == 16) { | |||||
cache->fat16[0] = 0XFFF8; | cache->fat16[0] = 0XFFF8; | ||||
cache->fat16[1] = 0XFFFF; | cache->fat16[1] = 0XFFFF; | ||||
} else if (FAT12_SUPPORT && m_fatType == 12) { | |||||
} else if (FAT12_SUPPORT && fatType() == 12) { | |||||
cache->fat32[0] = 0XFFFFF8; | cache->fat32[0] = 0XFFFFF8; | ||||
} else { | } else { | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
if (m_fatType == 32) { | |||||
if (fatType() == 32) { | |||||
// Reserve root cluster. | // Reserve root cluster. | ||||
if (!fatPutEOC(m_rootDirStart) || !cacheSync()) { | if (!fatPutEOC(m_rootDirStart) || !cacheSync()) { | ||||
DBG_FAIL_MACRO; | DBG_FAIL_MACRO; |
* \brief FatVolume class | * \brief FatVolume class | ||||
*/ | */ | ||||
#include <stddef.h> | #include <stddef.h> | ||||
#include "SysCall.h" | |||||
#include "FatLibConfig.h" | #include "FatLibConfig.h" | ||||
#include "FatStructs.h" | #include "FatStructs.h" | ||||
#include "BlockDriver.h" | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS | #ifndef DOXYGEN_SHOULD_SKIP_THIS | ||||
/** Macro for debug. */ | /** Macro for debug. */ | ||||
#define DEBUG_MODE 0 | #define DEBUG_MODE 0 | ||||
#if DEBUG_MODE | #if DEBUG_MODE | ||||
#define DBG_FAIL_MACRO Serial.print(F(__FILE__)); Serial.println(__LINE__) | |||||
#define DBG_FAIL_MACRO Serial.print(F(__FILE__)); Serial.println(__LINE__); | |||||
#define DBG_PRINT_IF(b) if (b) {Serial.println(F(#b)); DBG_FAIL_MACRO;} | #define DBG_PRINT_IF(b) if (b) {Serial.println(F(#b)); DBG_FAIL_MACRO;} | ||||
#define DBG_HALT_IF(b) if (b) {Serial.println(F(#b));\ | #define DBG_HALT_IF(b) if (b) {Serial.println(F(#b));\ | ||||
DBG_FAIL_MACRO; SysCall::halt();} | |||||
DBG_FAIL_MACRO; while (1);} | |||||
#else // DEBUG_MODE | #else // DEBUG_MODE | ||||
#define DBG_FAIL_MACRO | #define DBG_FAIL_MACRO | ||||
#define DBG_PRINT_IF(b) | #define DBG_PRINT_IF(b) | ||||
/** Sync existing block but do not read new block. */ | /** Sync existing block but do not read new block. */ | ||||
static const uint8_t CACHE_OPTION_NO_READ = 4; | static const uint8_t CACHE_OPTION_NO_READ = 4; | ||||
/** Cache block for read. */ | /** Cache block for read. */ | ||||
static uint8_t const CACHE_FOR_READ = 0; | |||||
static const uint8_t CACHE_FOR_READ = 0; | |||||
/** Cache block for write. */ | /** Cache block for write. */ | ||||
static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; | |||||
static const uint8_t CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; | |||||
/** Reserve cache block for write - do not read from block device. */ | /** Reserve cache block for write - do not read from block device. */ | ||||
static uint8_t const CACHE_RESERVE_FOR_WRITE | |||||
static const uint8_t CACHE_RESERVE_FOR_WRITE | |||||
= CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; | = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; | ||||
/** \return Cache block address. */ | /** \return Cache block address. */ | ||||
cache_t* block() { | cache_t* block() { | ||||
// Allow FatFile and FatCache access to FatVolume private functions. | // Allow FatFile and FatCache access to FatVolume private functions. | ||||
friend class FatCache; | friend class FatCache; | ||||
friend class FatFile; | friend class FatFile; | ||||
friend class FatFileSystem; | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
BlockDriver* m_blockDev; // block device | |||||
uint8_t m_blocksPerCluster; // Cluster size in blocks. | uint8_t m_blocksPerCluster; // Cluster size in blocks. | ||||
uint8_t m_clusterBlockMask; // Mask to extract block of cluster. | uint8_t m_clusterBlockMask; // Mask to extract block of cluster. | ||||
uint8_t m_clusterSizeShift; // Cluster count to block count shift. | uint8_t m_clusterSizeShift; // Cluster count to block count shift. | ||||
uint32_t m_lastCluster; // Last cluster number in FAT. | uint32_t m_lastCluster; // Last cluster number in FAT. | ||||
uint32_t m_rootDirStart; // Start block for FAT16, cluster for FAT32. | uint32_t m_rootDirStart; // Start block for FAT16, cluster for FAT32. | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// block I/O functions. | |||||
bool readBlock(uint32_t block, uint8_t* dst) { | |||||
return m_blockDev->readBlock(block, dst); | |||||
} | |||||
bool syncBlocks() { | |||||
return m_blockDev->syncBlocks(); | |||||
} | |||||
bool writeBlock(uint32_t block, const uint8_t* src) { | |||||
return m_blockDev->writeBlock(block, src); | |||||
} | |||||
#if USE_MULTI_BLOCK_IO | |||||
bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) { | |||||
return m_blockDev->readBlocks(block, dst, nb); | |||||
} | |||||
bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) { | |||||
return m_blockDev->writeBlocks(block, src, nb); | |||||
} | |||||
#endif // USE_MULTI_BLOCK_IO | |||||
#if MAINTAIN_FREE_CLUSTER_COUNT | #if MAINTAIN_FREE_CLUSTER_COUNT | ||||
int32_t m_freeClusterCount; // Count of free clusters in volume. | int32_t m_freeClusterCount; // Count of free clusters in volume. | ||||
void setFreeClusterCount(int32_t value) { | void setFreeClusterCount(int32_t value) { | ||||
options | FatCache::CACHE_STATUS_MIRROR_FAT); | options | FatCache::CACHE_STATUS_MIRROR_FAT); | ||||
} | } | ||||
bool cacheSync() { | bool cacheSync() { | ||||
return m_cache.sync() && m_fatCache.sync(); | |||||
return m_cache.sync() && m_fatCache.sync() && syncBlocks(); | |||||
} | } | ||||
#else // | #else // | ||||
cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { | cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { | ||||
options | FatCache::CACHE_STATUS_MIRROR_FAT); | options | FatCache::CACHE_STATUS_MIRROR_FAT); | ||||
} | } | ||||
bool cacheSync() { | bool cacheSync() { | ||||
return m_cache.sync(); | |||||
return m_cache.sync() && syncBlocks(); | |||||
} | } | ||||
#endif // USE_SEPARATE_FAT_CACHE | #endif // USE_SEPARATE_FAT_CACHE | ||||
cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { | cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { | ||||
uint8_t blockOfCluster(uint32_t position) const { | uint8_t blockOfCluster(uint32_t position) const { | ||||
return (position >> 9) & m_clusterBlockMask; | return (position >> 9) & m_clusterBlockMask; | ||||
} | } | ||||
uint32_t clusterStartBlock(uint32_t cluster) const; | |||||
uint32_t clusterFirstBlock(uint32_t cluster) const; | |||||
int8_t fatGet(uint32_t cluster, uint32_t* value); | int8_t fatGet(uint32_t cluster, uint32_t* value); | ||||
bool fatPut(uint32_t cluster, uint32_t value); | bool fatPut(uint32_t cluster, uint32_t value); | ||||
bool fatPutEOC(uint32_t cluster) { | bool fatPutEOC(uint32_t cluster) { | ||||
bool isEOC(uint32_t cluster) const { | bool isEOC(uint32_t cluster) const { | ||||
return cluster > m_lastCluster; | return cluster > m_lastCluster; | ||||
} | } | ||||
//---------------------------------------------------------------------------- | |||||
// Virtual block I/O functions. | |||||
virtual bool readBlock(uint32_t block, uint8_t* dst) = 0; | |||||
virtual bool writeBlock(uint32_t block, const uint8_t* src) = 0; | |||||
#if USE_MULTI_BLOCK_IO | |||||
virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0; | |||||
virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0; | |||||
#endif // USE_MULTI_BLOCK_IO | |||||
}; | }; | ||||
#endif // FatVolume | #endif // FatVolume |
* \return The number of free bytes. | * \return The number of free bytes. | ||||
*/ | */ | ||||
static int FreeStack() { | static int FreeStack() { | ||||
char top; | |||||
return __brkval ? &top - __brkval : &top - &__bss_end; | |||||
char* sp = reinterpret_cast<char*>(SP); | |||||
return __brkval ? sp - __brkval : sp - &__bss_end; | |||||
// char top; | |||||
// return __brkval ? &top - __brkval : &top - &__bss_end; | |||||
} | } | ||||
#elif defined(PLATFORM_ID) // Particle board | #elif defined(PLATFORM_ID) // Particle board | ||||
static int FreeStack() { | static int FreeStack() { | ||||
#elif defined(__arm__) | #elif defined(__arm__) | ||||
extern "C" char* sbrk(int incr); | extern "C" char* sbrk(int incr); | ||||
static int FreeStack() { | static int FreeStack() { | ||||
char top; | |||||
char top = 't'; | |||||
return &top - reinterpret_cast<char*>(sbrk(0)); | return &top - reinterpret_cast<char*>(sbrk(0)); | ||||
} | } | ||||
#else | #else |
* along with the Arduino SdFat Library. If not, see | * along with the Arduino SdFat Library. If not, see | ||||
* <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include "SystemInclude.h" | |||||
#include "SysCall.h" | |||||
#if defined(UDR0) || defined(DOXYGEN) | #if defined(UDR0) || defined(DOXYGEN) | ||||
#include "MinimumSerial.h" | #include "MinimumSerial.h" | ||||
const uint16_t MIN_2X_BAUD = F_CPU/(4*(2*0XFFF + 1)) + 1; | const uint16_t MIN_2X_BAUD = F_CPU/(4*(2*0XFFF + 1)) + 1; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
int MinimumSerial::available() { | |||||
return UCSR0A & (1 << RXC0) ? 1 : 0; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void MinimumSerial::begin(uint32_t baud) { | void MinimumSerial::begin(uint32_t baud) { | ||||
uint16_t baud_setting; | uint16_t baud_setting; | ||||
// don't worry, the compiler will squeeze out F_CPU != 16000000UL | // don't worry, the compiler will squeeze out F_CPU != 16000000UL | ||||
UCSR0B |= (1 << TXEN0) | (1 << RXEN0); | UCSR0B |= (1 << TXEN0) | (1 << RXEN0); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void MinimumSerial::flush() { | |||||
while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
int MinimumSerial::read() { | int MinimumSerial::read() { | ||||
if (UCSR0A & (1 << RXC0)) { | if (UCSR0A & (1 << RXC0)) { | ||||
return UDR0; | return UDR0; |
* along with the Arduino SdFat Library. If not, see | * along with the Arduino SdFat Library. If not, see | ||||
* <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
/** | |||||
* \file | |||||
* \brief Minimal AVR Serial driver. | |||||
*/ | |||||
#ifndef MinimumSerial_h | #ifndef MinimumSerial_h | ||||
#define MinimumSerial_h | #define MinimumSerial_h | ||||
#include "SystemInclude.h" | |||||
#include "SysCall.h" | |||||
//============================================================================== | //============================================================================== | ||||
/** | /** | ||||
* \class MinimumSerial | * \class MinimumSerial | ||||
*/ | */ | ||||
class MinimumSerial : public Print { | class MinimumSerial : public Print { | ||||
public: | public: | ||||
/** \return true for hardware serial */ | |||||
operator bool() { return true; } | |||||
/** | |||||
* \return one if data is available. | |||||
*/ | |||||
int available(); | |||||
/** | /** | ||||
* Set baud rate for serial port zero and enable in non interrupt mode. | * Set baud rate for serial port zero and enable in non interrupt mode. | ||||
* Do not call this function if you use another serial library. | * Do not call this function if you use another serial library. | ||||
* \param[in] baud rate | * \param[in] baud rate | ||||
*/ | */ | ||||
void begin(uint32_t baud); | void begin(uint32_t baud); | ||||
/** Wait for write done. */ | |||||
void flush(); | |||||
/** | /** | ||||
* Unbuffered read | * Unbuffered read | ||||
* \return -1 if no character is available or an available character. | * \return -1 if no character is available or an available character. |
/* Arduino SdFat Library | |||||
* Copyright (C) 2012 by William Greiman | |||||
* | |||||
* This file is part of the Arduino SdFat Library | |||||
* | |||||
* This Library is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This Library is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with the Arduino SdFat Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "SdFat.h" | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::errorHalt(Print* pr) { | |||||
errorPrint(pr); | |||||
SysCall::halt(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::errorHalt(Print* pr, char const* msg) { | |||||
errorPrint(pr, msg); | |||||
SysCall::halt(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::errorPrint(Print* pr) { | |||||
if (!cardErrorCode()) { | |||||
return; | |||||
} | |||||
pr->print(F("SD errorCode: 0X")); | |||||
pr->print(cardErrorCode(), HEX); | |||||
pr->print(F(",0X")); | |||||
pr->println(cardErrorData(), HEX); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::errorPrint(Print* pr, char const* msg) { | |||||
pr->print(F("error: ")); | |||||
pr->println(msg); | |||||
errorPrint(pr); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::initErrorHalt(Print* pr) { | |||||
initErrorPrint(pr); | |||||
SysCall::halt(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::initErrorHalt(Print* pr, char const *msg) { | |||||
pr->println(msg); | |||||
initErrorHalt(pr); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::initErrorPrint(Print* pr) { | |||||
if (cardErrorCode()) { | |||||
pr->println(F("Can't access SD card. Do not reformat.")); | |||||
if (cardErrorCode() == SD_CARD_ERROR_CMD0) { | |||||
pr->println(F("No card, wrong chip select pin, or SPI problem?")); | |||||
} | |||||
errorPrint(pr); | |||||
} else if (vol()->fatType() == 0) { | |||||
pr->println(F("Invalid format, reformat SD.")); | |||||
} else if (!vwd()->isOpen()) { | |||||
pr->println(F("Can't open root directory.")); | |||||
} else { | |||||
pr->println(F("No error found.")); | |||||
} | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::initErrorPrint(Print* pr, char const *msg) { | |||||
pr->println(msg); | |||||
initErrorPrint(pr); | |||||
} | |||||
#if defined(ARDUINO) || defined(DOXYGEN) | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::errorPrint(Print* pr, const __FlashStringHelper* msg) { | |||||
pr->print(F("error: ")); | |||||
pr->println(msg); | |||||
errorPrint(pr); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::errorHalt(Print* pr, const __FlashStringHelper* msg) { | |||||
errorPrint(pr, msg); | |||||
SysCall::halt(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::initErrorHalt(Print* pr, const __FlashStringHelper* msg) { | |||||
pr->println(msg); | |||||
initErrorHalt(pr); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdFatBase::initErrorPrint(Print* pr, const __FlashStringHelper* msg) { | |||||
pr->println(msg); | |||||
initErrorPrint(pr); | |||||
} | |||||
#endif // defined(ARDUINO) || defined(DOXYGEN) |
/* Arduino SdFat Library | /* Arduino SdFat Library | ||||
* Copyright (C) 2012 by William Greiman | |||||
* Copyright (C) 2016 by William Greiman | |||||
* | * | ||||
* This file is part of the Arduino SdFat Library | * This file is part of the Arduino SdFat Library | ||||
* | * | ||||
* \file | * \file | ||||
* \brief SdFat class | * \brief SdFat class | ||||
*/ | */ | ||||
#include "SysCall.h" | |||||
#include "BlockDriver.h" | |||||
#ifdef ARDUINO | #ifdef ARDUINO | ||||
#include "SdSpiCard/SdSpiCard.h" | |||||
#include "FatLib/FatLib.h" | #include "FatLib/FatLib.h" | ||||
#else // ARDUINO | #else // ARDUINO | ||||
#include "SdSpiCard.h" | |||||
#include "FatLib.h" | #include "FatLib.h" | ||||
#endif // ARDUINO | #endif // ARDUINO | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** SdFat version YYYYMMDD */ | /** SdFat version YYYYMMDD */ | ||||
#define SD_FAT_VERSION 20160430 | |||||
#define SD_FAT_VERSION 20160801 | |||||
//============================================================================== | //============================================================================== | ||||
/** | /** | ||||
* \class SdBaseFile | * \class SdBaseFile | ||||
*/ | */ | ||||
SdBaseFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {} | SdBaseFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {} | ||||
}; | }; | ||||
//----------------------------------------------------------------------------- | |||||
#if ENABLE_ARDUINO_FEATURES | #if ENABLE_ARDUINO_FEATURES | ||||
/** | /** | ||||
* \class SdFile | * \class SdFile | ||||
SdFile(const char* path, uint8_t oflag) : PrintFile(path, oflag) {} | SdFile(const char* path, uint8_t oflag) : PrintFile(path, oflag) {} | ||||
}; | }; | ||||
#endif // #if ENABLE_ARDUINO_FEATURES | #endif // #if ENABLE_ARDUINO_FEATURES | ||||
//----------------------------------------------------------------------------- | |||||
/** | /** | ||||
* \class SdFatBase | |||||
* \class SdFileSystem | |||||
* \brief Virtual base class for %SdFat library. | * \brief Virtual base class for %SdFat library. | ||||
*/ | */ | ||||
class SdFatBase : public FatFileSystem { | |||||
template<class SdDriverClass> | |||||
class SdFileSystem : public FatFileSystem { | |||||
public: | public: | ||||
/** Initialize SD card and file system. | /** Initialize SD card and file system. | ||||
* \param[in] spi SPI object for the card. | * \param[in] spi SPI object for the card. | ||||
* \param[in] csPin SD card chip select pin. | * \param[in] csPin SD card chip select pin. | ||||
* \param[in] divisor SPI divisor. | |||||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||||
* \return true for success else false. | * \return true for success else false. | ||||
*/ | */ | ||||
bool begin(SdSpiCard::m_spi_t* spi, uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
return m_sdCard.begin(spi, csPin, divisor) && | |||||
FatFileSystem::begin(); | |||||
bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||||
return m_card.begin(spi, csPin, spiSettings) && | |||||
FatFileSystem::begin(&m_card); | |||||
} | } | ||||
/** \return Pointer to SD card object */ | /** \return Pointer to SD card object */ | ||||
SdSpiCard *card() { | |||||
return &m_sdCard; | |||||
SdDriverClass *card() { | |||||
m_card.syncBlocks(); | |||||
return &m_card; | |||||
} | |||||
/** Initialize SD card for diagnostic use. | |||||
* \param[in] spi SPI object for the card. | |||||
* \param[in] csPin SD card chip select pin. | |||||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||||
* \return true for success else false. | |||||
*/ | |||||
bool cardBegin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||||
return m_card.begin(spi, csPin, spiSettings); | |||||
} | } | ||||
/** %Print any SD error code to Serial and halt. */ | /** %Print any SD error code to Serial and halt. */ | ||||
void errorHalt() { | void errorHalt() { | ||||
* | * | ||||
* \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
*/ | */ | ||||
void errorHalt(Print* pr); | |||||
void errorHalt(Print* pr) { | |||||
errorPrint(pr); | |||||
SysCall::halt(); | |||||
} | |||||
/** %Print msg, any SD error code and halt. | /** %Print msg, any SD error code and halt. | ||||
* | * | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
* \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void errorHalt(Print* pr, char const* msg); | |||||
void errorHalt(Print* pr, char const* msg) { | |||||
errorPrint(pr, msg); | |||||
SysCall::halt(); | |||||
} | |||||
/** %Print any SD error code to Serial */ | /** %Print any SD error code to Serial */ | ||||
void errorPrint() { | void errorPrint() { | ||||
errorPrint(&Serial); | errorPrint(&Serial); | ||||
/** %Print any SD error code. | /** %Print any SD error code. | ||||
* \param[in] pr Print device. | * \param[in] pr Print device. | ||||
*/ | */ | ||||
void errorPrint(Print* pr); | |||||
void errorPrint(Print* pr) { | |||||
if (!cardErrorCode()) { | |||||
return; | |||||
} | |||||
pr->print(F("SD errorCode: 0X")); | |||||
pr->print(cardErrorCode(), HEX); | |||||
pr->print(F(",0X")); | |||||
pr->println(cardErrorData(), HEX); | |||||
} | |||||
/** %Print msg, any SD error code. | /** %Print msg, any SD error code. | ||||
* | * | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
* \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void errorPrint(Print* pr, char const* msg); | |||||
/** Diagnostic call to initialize FatFileSystem - use for | |||||
* diagnostic purposes only. | |||||
* \return true for success else false. | |||||
*/ | |||||
bool fsBegin() { | |||||
return FatFileSystem::begin(); | |||||
void errorPrint(Print* pr, char const* msg) { | |||||
pr->print(F("error: ")); | |||||
pr->println(msg); | |||||
errorPrint(pr); | |||||
} | } | ||||
/** %Print any SD error code and halt. */ | /** %Print any SD error code and halt. */ | ||||
void initErrorHalt() { | void initErrorHalt() { | ||||
* | * | ||||
* \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
*/ | */ | ||||
void initErrorHalt(Print* pr); | |||||
/**Print message, error details, and halt after SdFat::init() fails. | |||||
void initErrorHalt(Print* pr) { | |||||
initErrorPrint(pr); | |||||
SysCall::halt(); | |||||
} | |||||
/**Print message, error details, and halt after begin() fails. | |||||
* | * | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void initErrorHalt(char const *msg) { | void initErrorHalt(char const *msg) { | ||||
initErrorHalt(&Serial, msg); | initErrorHalt(&Serial, msg); | ||||
} | } | ||||
/**Print message, error details, and halt after SdFatBase::init() fails. | |||||
/**Print message, error details, and halt after begin() fails. | |||||
* \param[in] pr Print device. | * \param[in] pr Print device. | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void initErrorHalt(Print* pr, char const *msg); | |||||
void initErrorHalt(Print* pr, char const *msg) { | |||||
pr->println(msg); | |||||
initErrorHalt(pr); | |||||
} | |||||
/** Print error details after SdFat::init() fails. */ | |||||
/** Print error details after begin() fails. */ | |||||
void initErrorPrint() { | void initErrorPrint() { | ||||
initErrorPrint(&Serial); | initErrorPrint(&Serial); | ||||
} | } | ||||
/** Print error details after SdFatBase::init() fails. | |||||
/** Print error details after begin() fails. | |||||
* | * | ||||
* \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
*/ | */ | ||||
void initErrorPrint(Print* pr); | |||||
/**Print message and error details and halt after SdFat::init() fails. | |||||
void initErrorPrint(Print* pr) { | |||||
if (cardErrorCode()) { | |||||
pr->println(F("Can't access SD card. Do not reformat.")); | |||||
if (cardErrorCode() == SD_CARD_ERROR_CMD0) { | |||||
pr->println(F("No card, wrong chip select pin, or SPI problem?")); | |||||
} | |||||
errorPrint(pr); | |||||
} else if (vol()->fatType() == 0) { | |||||
pr->println(F("Invalid format, reformat SD.")); | |||||
} else if (!vwd()->isOpen()) { | |||||
pr->println(F("Can't open root directory.")); | |||||
} else { | |||||
pr->println(F("No error found.")); | |||||
} | |||||
} | |||||
/**Print message and error details and halt after begin() fails. | |||||
* | * | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void initErrorPrint(char const *msg) { | void initErrorPrint(char const *msg) { | ||||
initErrorPrint(&Serial, msg); | initErrorPrint(&Serial, msg); | ||||
} | } | ||||
/**Print message and error details and halt after SdFatBase::init() fails. | |||||
/**Print message and error details and halt after begin() fails. | |||||
* | * | ||||
* \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void initErrorPrint(Print* pr, char const *msg); | |||||
void initErrorPrint(Print* pr, char const *msg) { | |||||
pr->println(msg); | |||||
initErrorPrint(pr); | |||||
} | |||||
#if defined(ARDUINO) || defined(DOXYGEN) | #if defined(ARDUINO) || defined(DOXYGEN) | ||||
/** %Print msg, any SD error code, and halt. | /** %Print msg, any SD error code, and halt. | ||||
* | * | ||||
* \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void errorHalt(Print* pr, const __FlashStringHelper* msg); | |||||
void errorHalt(Print* pr, const __FlashStringHelper* msg) { | |||||
errorPrint(pr, msg); | |||||
SysCall::halt(); | |||||
} | |||||
/** %Print msg, any SD error code. | /** %Print msg, any SD error code. | ||||
* | * | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
* \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void errorPrint(Print* pr, const __FlashStringHelper* msg); | |||||
/**Print message, error details, and halt after SdFat::init() fails. | |||||
void errorPrint(Print* pr, const __FlashStringHelper* msg) { | |||||
pr->print(F("error: ")); | |||||
pr->println(msg); | |||||
errorPrint(pr); | |||||
} | |||||
/**Print message, error details, and halt after begin() fails. | |||||
* | * | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void initErrorHalt(const __FlashStringHelper* msg) { | void initErrorHalt(const __FlashStringHelper* msg) { | ||||
initErrorHalt(&Serial, msg); | initErrorHalt(&Serial, msg); | ||||
} | } | ||||
/**Print message, error details, and halt after SdFatBase::init() fails. | |||||
/**Print message, error details, and halt after begin() fails. | |||||
* \param[in] pr Print device for message. | * \param[in] pr Print device for message. | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void initErrorHalt(Print* pr, const __FlashStringHelper* msg); | |||||
/**Print message and error details and halt after SdFat::init() fails. | |||||
void initErrorHalt(Print* pr, const __FlashStringHelper* msg) { | |||||
pr->println(msg); | |||||
initErrorHalt(pr); | |||||
} | |||||
/**Print message and error details and halt after begin() fails. | |||||
* | * | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void initErrorPrint(const __FlashStringHelper* msg) { | void initErrorPrint(const __FlashStringHelper* msg) { | ||||
initErrorPrint(&Serial, msg); | initErrorPrint(&Serial, msg); | ||||
} | } | ||||
/**Print message and error details and halt after SdFatBase::init() fails. | |||||
/**Print message and error details and halt after begin() fails. | |||||
* | * | ||||
* \param[in] pr Print destination. | * \param[in] pr Print destination. | ||||
* \param[in] msg Message to print. | * \param[in] msg Message to print. | ||||
*/ | */ | ||||
void initErrorPrint(Print* pr, const __FlashStringHelper* msg); | |||||
void initErrorPrint(Print* pr, const __FlashStringHelper* msg) { | |||||
pr->println(msg); | |||||
initErrorPrint(pr); | |||||
} | |||||
#endif // defined(ARDUINO) || defined(DOXYGEN) | #endif // defined(ARDUINO) || defined(DOXYGEN) | ||||
private: | |||||
/** \return The card error code */ | |||||
uint8_t cardErrorCode() { | uint8_t cardErrorCode() { | ||||
return m_sdCard.errorCode(); | |||||
return m_card.errorCode(); | |||||
} | } | ||||
/** \return the card error data */ | |||||
uint8_t cardErrorData() { | uint8_t cardErrorData() { | ||||
return m_sdCard.errorData(); | |||||
} | |||||
bool readBlock(uint32_t block, uint8_t* dst) { | |||||
return m_sdCard.readBlock(block, dst); | |||||
} | |||||
bool writeBlock(uint32_t block, const uint8_t* src) { | |||||
return m_sdCard.writeBlock(block, src); | |||||
} | |||||
bool readBlocks(uint32_t block, uint8_t* dst, size_t n) { | |||||
return m_sdCard.readBlocks(block, dst, n); | |||||
} | |||||
bool writeBlocks(uint32_t block, const uint8_t* src, size_t n) { | |||||
return m_sdCard.writeBlocks(block, src, n); | |||||
return m_card.errorData(); | |||||
} | } | ||||
SdSpiCard m_sdCard; | |||||
private: | |||||
SdDriverClass m_card; | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
/** | /** | ||||
* \class SdFat | * \class SdFat | ||||
* \brief Main file system class for %SdFat library. | * \brief Main file system class for %SdFat library. | ||||
*/ | */ | ||||
class SdFat : public SdFatBase { | |||||
class SdFat : public SdFileSystem<SdBlockDriver> { | |||||
public: | public: | ||||
#if IMPLEMENT_SPI_INTERFACE_SELECTION | |||||
#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) | |||||
SdFat() { | SdFat() { | ||||
m_spi.setSpiIf(0); | |||||
m_spi.setPort(0); | |||||
} | } | ||||
explicit SdFat(uint8_t spiIf) { | |||||
m_spi.setSpiIf(spiIf < SPI_INTERFACE_COUNT ? spiIf : 0); | |||||
/** Constructor with SPI port selection. | |||||
* \param[in] spiPort SPI port number. | |||||
*/ | |||||
explicit SdFat(uint8_t spiPort) { | |||||
m_spi.setPort(spiPort); | |||||
} | } | ||||
#endif // IMPLEMENT_SPI_INTERFACE_SELECTION | |||||
#endif // IMPLEMENT_SPI_PORT_SELECTION | |||||
/** Initialize SD card and file system. | /** Initialize SD card and file system. | ||||
* | * | ||||
* \param[in] csPin SD card chip select pin. | * \param[in] csPin SD card chip select pin. | ||||
* \param[in] divisor SPI divisor. | |||||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||||
* \return true for success else false. | * \return true for success else false. | ||||
*/ | |||||
bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
return SdFatBase::begin(&m_spi, csPin, divisor); | |||||
*/ | |||||
bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { | |||||
return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||||
} | } | ||||
/** Diagnostic call to initialize SD card - use for diagnostic purposes only. | |||||
/** Initialize SD card for diagnostic use only. | |||||
* | |||||
* \param[in] csPin SD card chip select pin. | * \param[in] csPin SD card chip select pin. | ||||
* \param[in] divisor SPI divisor. | |||||
* \param[in] settings SPI speed, mode, and bit order. | |||||
* \return true for success else false. | * \return true for success else false. | ||||
*/ | |||||
bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
return card()->begin(&m_spi, csPin, divisor); | |||||
*/ | |||||
bool cardBegin(uint8_t csPin = SS, SPISettings settings = SPI_FULL_SPEED) { | |||||
return SdFileSystem::cardBegin(&m_spi, csPin, settings); | |||||
} | |||||
/** Initialize file system for diagnostic use only. | |||||
* \return true for success else false. | |||||
*/ | |||||
bool fsBegin() { | |||||
return FatFileSystem::begin(card()); | |||||
} | } | ||||
private: | private: | ||||
SpiDefault_t m_spi; | |||||
SdFatSpiDriver m_spi; | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
#if SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) | |||||
#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
/** | /** | ||||
* \class SdFatLibSpi | |||||
* \brief SdFat class using the standard Arduino SPI library. | |||||
* \class SdFatSoftSpi | |||||
* \brief SdFat class using software SPI. | |||||
*/ | */ | ||||
class SdFatLibSpi: public SdFatBase { | |||||
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||||
class SdFatSoftSpi : public SdFileSystem<SdBlockDriver> { | |||||
public: | public: | ||||
/** Initialize SD card and file system. | /** Initialize SD card and file system. | ||||
* | |||||
* \param[in] csPin SD card chip select pin. | |||||
* \param[in] divisor SPI divisor. | |||||
* \return true for success else false. | |||||
*/ | |||||
bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
return SdFatBase::begin(&m_spi, csPin, divisor); | |||||
} | |||||
/** Diagnostic call to initialize SD card - use for diagnostic purposes only. | |||||
* | |||||
* \param[in] csPin SD card chip select pin. | * \param[in] csPin SD card chip select pin. | ||||
* \param[in] divisor SPI divisor. | |||||
* \param[in] spiSettings ignored for software SPI.. | |||||
* \return true for success else false. | * \return true for success else false. | ||||
*/ | */ | ||||
bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
return card()->begin(&m_spi, csPin, divisor); | |||||
bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { | |||||
return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||||
} | |||||
private: | |||||
SdSpiSoftDriver<MisoPin, MosiPin, SckPin> m_spi; | |||||
}; | |||||
#endif // #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
#if ENABLE_EXTENDED_TRANSFER_CLASS || defined(DOXYGEN) | |||||
//============================================================================== | |||||
/** | |||||
* \class SdFatEX | |||||
* \brief SdFat class with extended SD I/O. | |||||
*/ | |||||
class SdFatEX : public SdFileSystem<SdBlockDriverEX> { | |||||
public: | |||||
#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) | |||||
SdFatEX() { | |||||
m_spi.setPort(0); | |||||
} | |||||
/** Constructor with SPI port selection. | |||||
* \param[in] spiPort SPI port number. | |||||
*/ | |||||
explicit SdFatEX(uint8_t spiPort) { | |||||
m_spi.setPort(spiPort); | |||||
} | |||||
#endif // IMPLEMENT_SPI_PORT_SELECTION | |||||
/** Initialize SD card and file system. | |||||
* | |||||
* \param[in] csPin SD card chip select pin. | |||||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||||
* \return true for success else false. | |||||
*/ | |||||
bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { | |||||
return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||||
} | } | ||||
private: | private: | ||||
SdSpiLib m_spi; | |||||
SdFatSpiDriver m_spi; | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
/** | /** | ||||
* \class SdFatSoftSpi | |||||
* \brief SdFat class using software SPI. | |||||
* \class SdFatSoftSpiEX | |||||
* \brief SdFat class using software SPI and extended SD I/O. | |||||
*/ | */ | ||||
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | ||||
class SdFatSoftSpi : public SdFatBase { | |||||
class SdFatSoftSpiEX : public SdFileSystem<SdBlockDriverEX> { | |||||
public: | public: | ||||
/** Initialize SD card and file system. | /** Initialize SD card and file system. | ||||
* | * | ||||
* \param[in] csPin SD card chip select pin. | * \param[in] csPin SD card chip select pin. | ||||
* \param[in] divisor SPI divisor. | |||||
* \param[in] spiSettings ignored for software SPI. | |||||
* \return true for success else false. | * \return true for success else false. | ||||
*/ | */ | ||||
bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
return SdFatBase::begin(&m_spi, csPin, divisor); | |||||
bool begin(uint8_t csPin = SS, SPISettings spiSettings = 2) { | |||||
return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||||
} | } | ||||
/** Diagnostic call to initialize SD card - use for diagnostic purposes only. | |||||
* \param[in] csPin SD card chip select pin. | |||||
* \param[in] divisor SPI divisor. | |||||
private: | |||||
SdSpiSoftDriver<MisoPin, MosiPin, SckPin> m_spi; | |||||
}; | |||||
#endif // #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
#endif // ENABLE_EXTENDED_TRANSFER_CLASS || defined(DOXYGEN) | |||||
//============================================================================= | |||||
/** | |||||
* \class Sd2Card | |||||
* \brief Raw access to SD and SDHC card using default SPI library. | |||||
*/ | |||||
class Sd2Card : public SdSpiCard { | |||||
public: | |||||
/** Initialize the SD card. | |||||
* \param[in] chipSelectPin SD chip select pin. | |||||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||||
* \return true for success else false. | * \return true for success else false. | ||||
*/ | */ | ||||
bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||||
return card()->begin(&m_spi, csPin, divisor); | |||||
bool begin(uint8_t chipSelectPin = SS, | |||||
SPISettings spiSettings = SD_SCK_MHZ(50)) { | |||||
m_spi.begin(chipSelectPin); | |||||
m_spi.setSpiSettings(SD_SCK_HZ(250000)); | |||||
if (!SdSpiCard::begin(&m_spi)) { | |||||
return false; | |||||
} | |||||
m_spi.setSpiSettings(spiSettings); | |||||
return true; | |||||
} | } | ||||
private: | private: | ||||
SdSpiSoft<MisoPin, MosiPin, SckPin> m_spi; | |||||
SdFatSpiDriver m_spi; | |||||
}; | }; | ||||
#endif /// SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) | |||||
#endif // SdFat_h | #endif // SdFat_h |
#define USE_LONG_FILE_NAMES 1 | #define USE_LONG_FILE_NAMES 1 | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | /** | ||||
* Set ARDUINO_FILE_USES_STREAM nonzero to use Stream as the base class | |||||
* for the Arduino File class. If ARDUINO_FILE_USES_STREAM is zero, Print | |||||
* will be used as the base class for the Arduino File class. | |||||
* If the symbol ENABLE_EXTENDED_TRANSFER_CLASS is nonzero, the class SdFatEX | |||||
* will be defined. If the symbol ENABLE_SOFTWARE_SPI_CLASS is also nonzero, | |||||
* the class SdFatSoftSpiEX will be defined. | |||||
* | * | ||||
* You can save some flash if you do not use Stream input functions such as | |||||
* find(), findUntil(), readBytesUntil(), readString(), readStringUntil(), | |||||
* parseInt(), and parseFloat(). | |||||
* These classes used extended multi-block SD I/O for better performance. | |||||
* the SPI bus may not be shared with other devices in this mode. | |||||
*/ | */ | ||||
#define ARDUINO_FILE_USES_STREAM 1 | |||||
//------------------------------------------------------------------------------ | |||||
#define ENABLE_EXTENDED_TRANSFER_CLASS 0 | |||||
//----------------------------------------------------------------------------- | |||||
/** | /** | ||||
* The symbol SD_SPI_CONFIGURATION defines SPI access to the SD card. | |||||
* | |||||
* IF SD_SPI_CONFIGUTATION is define to be zero, only the SdFat class | |||||
* is define and SdFat uses a fast custom SPI implementation if avaiable. | |||||
* If SD_HAS_CUSTOM_SPI is zero, the standard SPI library is used. | |||||
* | |||||
* If SD_SPI_CONFIGURATION is define to be one, only the SdFat class is | |||||
* define and SdFat uses the standard Arduino SPI.h library. | |||||
* | |||||
* If SD_SPI_CONFIGURATION is define to be two, only the SdFat class is | |||||
* define and SdFat uses software SPI on the pins defined below. | |||||
* | |||||
* If SD_SPI_CONFIGURATION is define to be three, the three classes, SdFat, | |||||
* SdFatLibSpi, and SdFatSoftSpi are defined. SdFat uses the fast | |||||
* custom SPI implementation. SdFatLibSpi uses the standard Arduino SPI | |||||
* library. SdFatSoftSpi is a template class that uses Software SPI. The | |||||
* template parameters define the software SPI pins. See the ThreeCard | |||||
* example for simultaneous use of all three classes. | |||||
*/ | |||||
#define SD_SPI_CONFIGURATION 0 | |||||
//------------------------------------------------------------------------------ | |||||
* If the symbol USE_STANDARD_SPI_LIBRARY is nonzero, the classes SdFat and | |||||
* SdFatEX use the standard Arduino SPI.h library. If USE_STANDARD_SPI_LIBRARY | |||||
* is zero, an optimized custom SPI driver is used if it exists. | |||||
*/ | |||||
#define USE_STANDARD_SPI_LIBRARY 0 | |||||
//----------------------------------------------------------------------------- | |||||
/** | /** | ||||
* If SD_SPI_CONFIGURATION is defined to be two, these definitions | |||||
* will define the pins used for software SPI. | |||||
* | |||||
* The default definition allows Uno shields to be used on other boards. | |||||
* If the symbol ENABLE_SOFTWARE_SPI_CLASS is nonzero, the class SdFatSoftSpi | |||||
* will be defined. If ENABLE_EXTENDED_TRANSFER_CLASS is also nonzero, | |||||
* the class SdFatSoftSpiEX will be defined. | |||||
*/ | */ | ||||
/** Software SPI Master Out Slave In pin */ | |||||
uint8_t const SOFT_SPI_MOSI_PIN = 11; | |||||
/** Software SPI Master In Slave Out pin */ | |||||
uint8_t const SOFT_SPI_MISO_PIN = 12; | |||||
/** Software SPI Clock pin */ | |||||
uint8_t const SOFT_SPI_SCK_PIN = 13; | |||||
#define ENABLE_SOFTWARE_SPI_CLASS 0 | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | /** | ||||
* Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters | * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters | ||||
/** | /** | ||||
* To enable SD card CRC checking set USE_SD_CRC nonzero. | * To enable SD card CRC checking set USE_SD_CRC nonzero. | ||||
* | * | ||||
* Set USE_SD_CRC to 1 to use a smaller slower CRC-CCITT function. | |||||
* Set USE_SD_CRC to 1 to use a smaller CRC-CCITT function. This function | |||||
* is slower for AVR but may be fast for ARM and other processors. | |||||
* | * | ||||
* Set USE_SD_CRC to 2 to used a larger faster table driven CRC-CCITT function. | |||||
* Set USE_SD_CRC to 2 to used a larger table driven CRC-CCITT function. This | |||||
* function is faster for AVR but may be slower for ARM and other processors. | |||||
*/ | */ | ||||
#define USE_SD_CRC 0 | #define USE_SD_CRC 0 | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | |||||
* Set ENABLE_SPI_TRANSACTIONS nonzero to enable the SPI transaction feature | |||||
* of the standard Arduino SPI library. You must include SPI.h in your | |||||
* programs when ENABLE_SPI_TRANSACTIONS is nonzero. | |||||
*/ | |||||
#define ENABLE_SPI_TRANSACTIONS 0 | |||||
//------------------------------------------------------------------------------ | |||||
/** | /** | ||||
* Handle Watchdog Timer for WiFi modules. | * Handle Watchdog Timer for WiFi modules. | ||||
* | * | ||||
*/ | */ | ||||
#define ENDL_CALLS_FLUSH 0 | #define ENDL_CALLS_FLUSH 0 | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | |||||
* SPI SCK divisor for SD initialization commands. | |||||
* or greater | |||||
*/ | |||||
#ifdef __AVR__ | |||||
const uint8_t SPI_SCK_INIT_DIVISOR = 64; | |||||
#else | |||||
const uint8_t SPI_SCK_INIT_DIVISOR = 128; | |||||
#endif | |||||
//------------------------------------------------------------------------------ | |||||
/** | /** | ||||
* Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache | * Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache | ||||
* for FAT table entries. This improves performance for large writes | * for FAT table entries. This improves performance for large writes | ||||
/** | /** | ||||
* Determine the default SPI configuration. | * Determine the default SPI configuration. | ||||
*/ | */ | ||||
#if defined(__AVR__)\ | |||||
#if defined(__STM32F1__) | |||||
// has multiple SPI ports | |||||
#define SD_HAS_CUSTOM_SPI 2 | |||||
#elif defined(__AVR__)\ | |||||
|| defined(__SAM3X8E__) || defined(__SAM3X8H__)\ | || defined(__SAM3X8E__) || defined(__SAM3X8H__)\ | ||||
|| (defined(__arm__) && defined(CORE_TEENSY))\ | || (defined(__arm__) && defined(CORE_TEENSY))\ | ||||
|| defined(__STM32F1__)\ | |||||
|| defined(PLATFORM_ID)\ | |||||
|| defined(ESP8266)\ | |||||
|| defined(DOXYGEN) | |||||
// Use custom fast implementation. | |||||
|| defined(ESP8266) | |||||
#define SD_HAS_CUSTOM_SPI 1 | #define SD_HAS_CUSTOM_SPI 1 | ||||
#else // SD_HAS_CUSTOM_SPI | #else // SD_HAS_CUSTOM_SPI | ||||
// Use standard SPI library. | // Use standard SPI library. | ||||
#define SD_HAS_CUSTOM_SPI 0 | #define SD_HAS_CUSTOM_SPI 0 | ||||
#endif // SD_HAS_CUSTOM_SPI | #endif // SD_HAS_CUSTOM_SPI | ||||
//----------------------------------------------------------------------------- | |||||
/** | |||||
* Number of hardware interfaces. | |||||
*/ | |||||
#if defined(PLATFORM_ID) | |||||
#if Wiring_SPI1 && Wiring_SPI2 | |||||
#define SPI_INTERFACE_COUNT 3 | |||||
#elif Wiring_SPI1 | |||||
#define SPI_INTERFACE_COUNT 2 | |||||
#endif // Wiring_SPI1 && Wiring_SPI2 | |||||
#endif // defined(PLATFORM_ID) | |||||
// default is one | |||||
#ifndef SPI_INTERFACE_COUNT | |||||
#define SPI_INTERFACE_COUNT 1 | |||||
#endif // SPI_INTERFACE_COUNT | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
/** | /** | ||||
* Check if API to select HW SPI interface is needed. | |||||
* Check if API to select HW SPI port is needed. | |||||
*/ | */ | ||||
#if SPI_INTERFACE_COUNT > 1 && SD_HAS_CUSTOM_SPI\ | |||||
&& SD_SPI_CONFIGURATION != 1 && SD_SPI_CONFIGURATION != 2 | |||||
#define IMPLEMENT_SPI_INTERFACE_SELECTION 1 | |||||
#else // SPI_INTERFACE_COUNT > 1 | |||||
#define IMPLEMENT_SPI_INTERFACE_SELECTION 0 | |||||
#endif // SPI_INTERFACE_COUNT > 1 | |||||
#if (USE_STANDARD_SPI_LIBRARY || SD_HAS_CUSTOM_SPI < 2) | |||||
#define IMPLEMENT_SPI_PORT_SELECTION 0 | |||||
#else // USE_STANDARD_SPI_LIBRARY | |||||
#define IMPLEMENT_SPI_PORT_SELECTION 1 | |||||
#endif // USE_STANDARD_SPI_LIBRARY | |||||
#endif // SdFatConfig_h | #endif // SdFatConfig_h |
/* Arduino SdFat Library | |||||
* Copyright (C) 2015 by William Greiman | |||||
* | |||||
* This file is part of the Arduino SdFat Library | |||||
* | |||||
* This Library is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This Library is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with the Arduino SdFat Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include <stdlib.h> | |||||
#include "SdFat.h" | |||||
#include "SdFatUtil.h" | |||||
//------------------------------------------------------------------------------ | |||||
#ifdef __arm__ | |||||
extern "C" char* sbrk(int incr); | |||||
int SdFatUtil::FreeRam() { | |||||
char top; | |||||
return &top - reinterpret_cast<char*>(sbrk(0)); | |||||
} | |||||
#else // __arm__ | |||||
extern char *__brkval; | |||||
extern char __bss_end; | |||||
/** Amount of free RAM | |||||
* \return The number of free bytes. | |||||
*/ | |||||
int SdFatUtil::FreeRam() { | |||||
char top; | |||||
return __brkval ? &top - __brkval : &top - &__bss_end; | |||||
} | |||||
#endif // __arm | |||||
/* Arduino SdFat Library | |||||
* Copyright (C) 2012 by William Greiman | |||||
* | |||||
* This file is part of the Arduino SdFat Library | |||||
* | |||||
* This Library is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This Library is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with the Arduino SdFat Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef SdFatUtil_h | |||||
#define SdFatUtil_h | |||||
/** | |||||
* \file | |||||
* \brief Useful utility functions. | |||||
*/ | |||||
#include "SdFat.h" | |||||
namespace SdFatUtil { | |||||
/** Amount of free RAM | |||||
* \return The number of free bytes. | |||||
*/ | |||||
int FreeRam(); | |||||
} // namespace SdFatUtil | |||||
using namespace SdFatUtil; // NOLINT | |||||
#endif // #define SdFatUtil_h |
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// SD card errors | // SD card errors | ||||
/** timeout error for command CMD0 (initialize card in SPI mode) */ | /** timeout error for command CMD0 (initialize card in SPI mode) */ | ||||
uint8_t const SD_CARD_ERROR_CMD0 = 0X1; | |||||
const uint8_t SD_CARD_ERROR_CMD0 = 0X1; | |||||
/** CMD8 was not accepted - not a valid SD card*/ | /** CMD8 was not accepted - not a valid SD card*/ | ||||
uint8_t const SD_CARD_ERROR_CMD8 = 0X2; | |||||
const uint8_t SD_CARD_ERROR_CMD8 = 0X2; | |||||
/** card returned an error response for CMD12 (stop multiblock read) */ | /** card returned an error response for CMD12 (stop multiblock read) */ | ||||
uint8_t const SD_CARD_ERROR_CMD12 = 0X3; | |||||
const uint8_t SD_CARD_ERROR_CMD12 = 0X3; | |||||
/** card returned an error response for CMD17 (read block) */ | /** card returned an error response for CMD17 (read block) */ | ||||
uint8_t const SD_CARD_ERROR_CMD17 = 0X4; | |||||
const uint8_t SD_CARD_ERROR_CMD17 = 0X4; | |||||
/** card returned an error response for CMD18 (read multiple block) */ | /** card returned an error response for CMD18 (read multiple block) */ | ||||
uint8_t const SD_CARD_ERROR_CMD18 = 0X5; | |||||
const uint8_t SD_CARD_ERROR_CMD18 = 0X5; | |||||
/** card returned an error response for CMD24 (write block) */ | /** card returned an error response for CMD24 (write block) */ | ||||
uint8_t const SD_CARD_ERROR_CMD24 = 0X6; | |||||
const uint8_t SD_CARD_ERROR_CMD24 = 0X6; | |||||
/** WRITE_MULTIPLE_BLOCKS command failed */ | /** WRITE_MULTIPLE_BLOCKS command failed */ | ||||
uint8_t const SD_CARD_ERROR_CMD25 = 0X7; | |||||
const uint8_t SD_CARD_ERROR_CMD25 = 0X7; | |||||
/** card returned an error response for CMD58 (read OCR) */ | /** card returned an error response for CMD58 (read OCR) */ | ||||
uint8_t const SD_CARD_ERROR_CMD58 = 0X8; | |||||
const uint8_t SD_CARD_ERROR_CMD58 = 0X8; | |||||
/** SET_WR_BLK_ERASE_COUNT failed */ | /** SET_WR_BLK_ERASE_COUNT failed */ | ||||
uint8_t const SD_CARD_ERROR_ACMD23 = 0X9; | |||||
const uint8_t SD_CARD_ERROR_ACMD23 = 0X9; | |||||
/** ACMD41 initialization process timeout */ | /** ACMD41 initialization process timeout */ | ||||
uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; | |||||
const uint8_t SD_CARD_ERROR_ACMD41 = 0XA; | |||||
/** card returned a bad CSR version field */ | /** card returned a bad CSR version field */ | ||||
uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB; | |||||
const uint8_t SD_CARD_ERROR_BAD_CSD = 0XB; | |||||
/** erase block group command failed */ | /** erase block group command failed */ | ||||
uint8_t const SD_CARD_ERROR_ERASE = 0XC; | |||||
const uint8_t SD_CARD_ERROR_ERASE = 0XC; | |||||
/** card not capable of single block erase */ | /** card not capable of single block erase */ | ||||
uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; | |||||
const uint8_t SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; | |||||
/** Erase sequence timed out */ | /** Erase sequence timed out */ | ||||
uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; | |||||
const uint8_t SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; | |||||
/** card returned an error token instead of read data */ | /** card returned an error token instead of read data */ | ||||
uint8_t const SD_CARD_ERROR_READ = 0XF; | |||||
const uint8_t SD_CARD_ERROR_READ = 0XF; | |||||
/** read CID or CSD failed */ | /** read CID or CSD failed */ | ||||
uint8_t const SD_CARD_ERROR_READ_REG = 0X10; | |||||
const uint8_t SD_CARD_ERROR_READ_REG = 0X10; | |||||
/** timeout while waiting for start of read data */ | /** timeout while waiting for start of read data */ | ||||
uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11; | |||||
const uint8_t SD_CARD_ERROR_READ_TIMEOUT = 0X11; | |||||
/** card did not accept STOP_TRAN_TOKEN */ | /** card did not accept STOP_TRAN_TOKEN */ | ||||
uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12; | |||||
const uint8_t SD_CARD_ERROR_STOP_TRAN = 0X12; | |||||
/** card returned an error token as a response to a write operation */ | /** card returned an error token as a response to a write operation */ | ||||
uint8_t const SD_CARD_ERROR_WRITE = 0X13; | |||||
/** attempt to write protected block zero */ | |||||
uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X14; // REMOVE - not used | |||||
const uint8_t SD_CARD_ERROR_WRITE = 0X13; | |||||
/** card busy for command */ | |||||
const uint8_t SD_CARD_ERROR_CMD_BUSY = 0X14; | |||||
/** card did not go ready for a multiple block write */ | /** card did not go ready for a multiple block write */ | ||||
uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; // Not used | |||||
const uint8_t SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; // Not used | |||||
/** card returned an error to a CMD13 status check after a write */ | /** card returned an error to a CMD13 status check after a write */ | ||||
uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||||
const uint8_t SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||||
/** timeout occurred during write programming */ | /** timeout occurred during write programming */ | ||||
uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; | |||||
const uint8_t SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; | |||||
/** incorrect rate selected */ | /** incorrect rate selected */ | ||||
uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18; | |||||
const uint8_t SD_CARD_ERROR_SCK_RATE = 0X18; | |||||
/** init() not called */ | /** init() not called */ | ||||
uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; | |||||
const uint8_t SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; | |||||
/** card returned an error for CMD59 (CRC_ON_OFF) */ | /** card returned an error for CMD59 (CRC_ON_OFF) */ | ||||
uint8_t const SD_CARD_ERROR_CMD59 = 0X1A; | |||||
const uint8_t SD_CARD_ERROR_CMD59 = 0X1A; | |||||
/** invalid read CRC */ | /** invalid read CRC */ | ||||
uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B; | |||||
const uint8_t SD_CARD_ERROR_READ_CRC = 0X1B; | |||||
/** SPI DMA error */ | /** SPI DMA error */ | ||||
uint8_t const SD_CARD_ERROR_SPI_DMA = 0X1C; | |||||
/** CMD6 not accepted */ | |||||
uint8_t const SD_CARD_ERROR_CMD6 = 0X1D; | |||||
const uint8_t SD_CARD_ERROR_SPI_DMA = 0X1C; | |||||
/** ACMD13 not accepted */ | |||||
const uint8_t SD_CARD_ERROR_ACMD13 = 0X1D; | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// card types | // card types | ||||
/** Standard capacity V1 SD card */ | /** Standard capacity V1 SD card */ | ||||
uint8_t const SD_CARD_TYPE_SD1 = 1; | |||||
const uint8_t SD_CARD_TYPE_SD1 = 1; | |||||
/** Standard capacity V2 SD card */ | /** Standard capacity V2 SD card */ | ||||
uint8_t const SD_CARD_TYPE_SD2 = 2; | |||||
const uint8_t SD_CARD_TYPE_SD2 = 2; | |||||
/** High Capacity SD card */ | /** High Capacity SD card */ | ||||
uint8_t const SD_CARD_TYPE_SDHC = 3; | |||||
const uint8_t SD_CARD_TYPE_SDHC = 3; | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
#define SD_SCK_HZ(maxSpeed) SPISettings(maxSpeed, MSBFIRST, SPI_MODE0) | |||||
#define SD_SCK_MHZ(maxMhz) SPISettings(1000000UL*maxMhz, MSBFIRST, SPI_MODE0) | |||||
// SPI divisor constants | // SPI divisor constants | ||||
/** Set SCK to max rate of F_CPU/2. */ | /** Set SCK to max rate of F_CPU/2. */ | ||||
uint8_t const SPI_FULL_SPEED = 2; | |||||
#define SPI_FULL_SPEED SD_SCK_MHZ(50) | |||||
/** Set SCK rate to F_CPU/3 for Due */ | /** Set SCK rate to F_CPU/3 for Due */ | ||||
uint8_t const SPI_DIV3_SPEED = 3; | |||||
#define SPI_DIV3_SPEED SD_SCK_HZ(F_CPU/3) | |||||
/** Set SCK rate to F_CPU/4. */ | /** Set SCK rate to F_CPU/4. */ | ||||
uint8_t const SPI_HALF_SPEED = 4; | |||||
#define SPI_HALF_SPEED SD_SCK_HZ(F_CPU/4) | |||||
/** Set SCK rate to F_CPU/6 for Due */ | /** Set SCK rate to F_CPU/6 for Due */ | ||||
uint8_t const SPI_DIV6_SPEED = 6; | |||||
#define SPI_DIV6_SPEED SD_SCK_HZ(F_CPU/6) | |||||
/** Set SCK rate to F_CPU/8. */ | /** Set SCK rate to F_CPU/8. */ | ||||
uint8_t const SPI_QUARTER_SPEED = 8; | |||||
#define SPI_QUARTER_SPEED SD_SCK_HZ(F_CPU/8) | |||||
/** Set SCK rate to F_CPU/16. */ | /** Set SCK rate to F_CPU/16. */ | ||||
uint8_t const SPI_EIGHTH_SPEED = 16; | |||||
#define SPI_EIGHTH_SPEED SD_SCK_HZ(F_CPU/16) | |||||
/** Set SCK rate to F_CPU/32. */ | /** Set SCK rate to F_CPU/32. */ | ||||
uint8_t const SPI_SIXTEENTH_SPEED = 32; | |||||
#define SPI_SIXTEENTH_SPEED SD_SCK_HZ(F_CPU/32) | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// SD operation timeouts | // SD operation timeouts | ||||
/** init timeout ms */ | /** init timeout ms */ | ||||
unsigned const SD_INIT_TIMEOUT = 2000; | |||||
const uint16_t SD_INIT_TIMEOUT = 2000; | |||||
/** erase timeout ms */ | /** erase timeout ms */ | ||||
unsigned const SD_ERASE_TIMEOUT = 10000; | |||||
const uint16_t SD_ERASE_TIMEOUT = 10000; | |||||
/** read timeout ms */ | /** read timeout ms */ | ||||
unsigned const SD_READ_TIMEOUT = 300; | |||||
const uint16_t SD_READ_TIMEOUT = 300; | |||||
/** write time out ms */ | /** write time out ms */ | ||||
unsigned const SD_WRITE_TIMEOUT = 600; | |||||
const uint16_t SD_WRITE_TIMEOUT = 600; | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// SD card commands | // SD card commands | ||||
/** GO_IDLE_STATE - init card in spi mode if CS low */ | /** GO_IDLE_STATE - init card in spi mode if CS low */ | ||||
uint8_t const CMD0 = 0X00; | |||||
const uint8_t CMD0 = 0X00; | |||||
/** SWITCH_FUNC - Switch Function Command */ | /** SWITCH_FUNC - Switch Function Command */ | ||||
uint8_t const CMD6 = 0X06; | |||||
const uint8_t CMD6 = 0X06; | |||||
/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ | /** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ | ||||
uint8_t const CMD8 = 0X08; | |||||
const uint8_t CMD8 = 0X08; | |||||
/** SEND_CSD - read the Card Specific Data (CSD register) */ | /** SEND_CSD - read the Card Specific Data (CSD register) */ | ||||
uint8_t const CMD9 = 0X09; | |||||
const uint8_t CMD9 = 0X09; | |||||
/** SEND_CID - read the card identification information (CID register) */ | /** SEND_CID - read the card identification information (CID register) */ | ||||
uint8_t const CMD10 = 0X0A; | |||||
const uint8_t CMD10 = 0X0A; | |||||
/** STOP_TRANSMISSION - end multiple block read sequence */ | /** STOP_TRANSMISSION - end multiple block read sequence */ | ||||
uint8_t const CMD12 = 0X0C; | |||||
const uint8_t CMD12 = 0X0C; | |||||
/** SEND_STATUS - read the card status register */ | /** SEND_STATUS - read the card status register */ | ||||
uint8_t const CMD13 = 0X0D; | |||||
const uint8_t CMD13 = 0X0D; | |||||
/** READ_SINGLE_BLOCK - read a single data block from the card */ | /** READ_SINGLE_BLOCK - read a single data block from the card */ | ||||
uint8_t const CMD17 = 0X11; | |||||
const uint8_t CMD17 = 0X11; | |||||
/** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ | /** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ | ||||
uint8_t const CMD18 = 0X12; | |||||
const uint8_t CMD18 = 0X12; | |||||
/** WRITE_BLOCK - write a single data block to the card */ | /** WRITE_BLOCK - write a single data block to the card */ | ||||
uint8_t const CMD24 = 0X18; | |||||
const uint8_t CMD24 = 0X18; | |||||
/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ | /** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ | ||||
uint8_t const CMD25 = 0X19; | |||||
const uint8_t CMD25 = 0X19; | |||||
/** ERASE_WR_BLK_START - sets the address of the first block to be erased */ | /** ERASE_WR_BLK_START - sets the address of the first block to be erased */ | ||||
uint8_t const CMD32 = 0X20; | |||||
const uint8_t CMD32 = 0X20; | |||||
/** ERASE_WR_BLK_END - sets the address of the last block of the continuous | /** ERASE_WR_BLK_END - sets the address of the last block of the continuous | ||||
range to be erased*/ | range to be erased*/ | ||||
uint8_t const CMD33 = 0X21; | |||||
const uint8_t CMD33 = 0X21; | |||||
/** ERASE - erase all previously selected blocks */ | /** ERASE - erase all previously selected blocks */ | ||||
uint8_t const CMD38 = 0X26; | |||||
const uint8_t CMD38 = 0X26; | |||||
/** APP_CMD - escape for application specific command */ | /** APP_CMD - escape for application specific command */ | ||||
uint8_t const CMD55 = 0X37; | |||||
const uint8_t CMD55 = 0X37; | |||||
/** READ_OCR - read the OCR register of a card */ | /** READ_OCR - read the OCR register of a card */ | ||||
uint8_t const CMD58 = 0X3A; | |||||
const uint8_t CMD58 = 0X3A; | |||||
/** CRC_ON_OFF - enable or disable CRC checking */ | /** CRC_ON_OFF - enable or disable CRC checking */ | ||||
uint8_t const CMD59 = 0X3B; | |||||
const uint8_t CMD59 = 0X3B; | |||||
/** SD_STATUS - Send the SD Status. */ | |||||
const uint8_t ACMD13 = 0X0D; | |||||
/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be | /** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be | ||||
pre-erased before writing */ | pre-erased before writing */ | ||||
uint8_t const ACMD23 = 0X17; | |||||
const uint8_t ACMD23 = 0X17; | |||||
/** SD_SEND_OP_COMD - Sends host capacity support information and | /** SD_SEND_OP_COMD - Sends host capacity support information and | ||||
activates the card's initialization process */ | activates the card's initialization process */ | ||||
uint8_t const ACMD41 = 0X29; | |||||
const uint8_t ACMD41 = 0X29; | |||||
//============================================================================== | //============================================================================== | ||||
/** status for card in the ready state */ | /** status for card in the ready state */ | ||||
uint8_t const R1_READY_STATE = 0X00; | |||||
const uint8_t R1_READY_STATE = 0X00; | |||||
/** status for card in the idle state */ | /** status for card in the idle state */ | ||||
uint8_t const R1_IDLE_STATE = 0X01; | |||||
const uint8_t R1_IDLE_STATE = 0X01; | |||||
/** status bit for illegal command */ | /** status bit for illegal command */ | ||||
uint8_t const R1_ILLEGAL_COMMAND = 0X04; | |||||
const uint8_t R1_ILLEGAL_COMMAND = 0X04; | |||||
/** start data token for read or write single block*/ | /** start data token for read or write single block*/ | ||||
uint8_t const DATA_START_BLOCK = 0XFE; | |||||
const uint8_t DATA_START_BLOCK = 0XFE; | |||||
/** stop token for write multiple blocks*/ | /** stop token for write multiple blocks*/ | ||||
uint8_t const STOP_TRAN_TOKEN = 0XFD; | |||||
const uint8_t STOP_TRAN_TOKEN = 0XFD; | |||||
/** start data token for write multiple blocks*/ | /** start data token for write multiple blocks*/ | ||||
uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC; | |||||
const uint8_t WRITE_MULTIPLE_TOKEN = 0XFC; | |||||
/** mask for data response tokens after a write block operation */ | /** mask for data response tokens after a write block operation */ | ||||
uint8_t const DATA_RES_MASK = 0X1F; | |||||
const uint8_t DATA_RES_MASK = 0X1F; | |||||
/** write data accepted token */ | /** write data accepted token */ | ||||
uint8_t const DATA_RES_ACCEPTED = 0X05; | |||||
const uint8_t DATA_RES_ACCEPTED = 0X05; | |||||
//============================================================================== | //============================================================================== | ||||
/** | /** | ||||
* \class CID | * \class CID |
/* Arduino SdSpi Library | |||||
* Copyright (C) 2013 by William Greiman | |||||
* | |||||
* This file is part of the Arduino SdSpi Library | |||||
* | |||||
* This Library is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This Library is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with the Arduino SdSpi Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
/** | |||||
* \file | |||||
* \brief SdSpi class for V2 SD/SDHC cards | |||||
*/ | |||||
#ifndef SdSpi_h | |||||
#define SdSpi_h | |||||
#include "SystemInclude.h" | |||||
#include "SdFatConfig.h" | |||||
//------------------------------------------------------------------------------ | |||||
/** | |||||
* \class SdSpiBase | |||||
* \brief Virtual SPI class for access to SD and SDHC flash memory cards. | |||||
*/ | |||||
class SdSpiBase { | |||||
public: | |||||
/** Initialize the SPI bus. | |||||
* | |||||
* \param[in] chipSelectPin SD card chip select pin. | |||||
*/ | |||||
virtual void begin(uint8_t chipSelectPin) = 0; | |||||
/** Set SPI options for access to SD/SDHC cards. | |||||
* | |||||
* \param[in] divisor SCK clock divider relative to the system clock. | |||||
*/ | |||||
virtual void beginTransaction(uint8_t divisor); | |||||
/** | |||||
* End SPI transaction. | |||||
*/ | |||||
virtual void endTransaction(); | |||||
/** Receive a byte. | |||||
* | |||||
* \return The byte. | |||||
*/ | |||||
virtual uint8_t receive() = 0; | |||||
/** Receive multiple bytes. | |||||
* | |||||
* \param[out] buf Buffer to receive the data. | |||||
* \param[in] n Number of bytes to receive. | |||||
* | |||||
* \return Zero for no error or nonzero error code. | |||||
*/ | |||||
virtual uint8_t receive(uint8_t* buf, size_t n) = 0; | |||||
/** Send a byte. | |||||
* | |||||
* \param[in] data Byte to send | |||||
*/ | |||||
virtual void send(uint8_t data) = 0; | |||||
/** Send multiple bytes. | |||||
* | |||||
* \param[in] buf Buffer for data to be sent. | |||||
* \param[in] n Number of bytes to send. | |||||
*/ | |||||
virtual void send(const uint8_t* buf, size_t n) = 0; | |||||
}; | |||||
//------------------------------------------------------------------------------ | |||||
/** | |||||
* \class SdSpi | |||||
* \brief SPI class for access to SD and SDHC flash memory cards. | |||||
*/ | |||||
#if SD_SPI_CONFIGURATION >= 3 | |||||
class SdSpi : public SdSpiBase { | |||||
#else // SD_SPI_CONFIGURATION >= 3 | |||||
class SdSpi { | |||||
#endif // SD_SPI_CONFIGURATION >= 3 | |||||
public: | |||||
/** Initialize the SPI bus. | |||||
* | |||||
* \param[in] chipSelectPin SD card chip select pin. | |||||
*/ | |||||
void begin(uint8_t chipSelectPin); | |||||
/** Set SPI options for access to SD/SDHC cards. | |||||
* | |||||
* \param[in] divisor SCK clock divider relative to the system clock. | |||||
*/ | |||||
void beginTransaction(uint8_t divisor); | |||||
/** | |||||
* End SPI transaction | |||||
*/ | |||||
void endTransaction(); | |||||
/** Receive a byte. | |||||
* | |||||
* \return The byte. | |||||
*/ | |||||
uint8_t receive(); | |||||
/** Receive multiple bytes. | |||||
* | |||||
* \param[out] buf Buffer to receive the data. | |||||
* \param[in] n Number of bytes to receive. | |||||
* | |||||
* \return Zero for no error or nonzero error code. | |||||
*/ | |||||
uint8_t receive(uint8_t* buf, size_t n); | |||||
/** Send a byte. | |||||
* | |||||
* \param[in] data Byte to send | |||||
*/ | |||||
void send(uint8_t data); | |||||
/** Send multiple bytes. | |||||
* | |||||
* \param[in] buf Buffer for data to be sent. | |||||
* \param[in] n Number of bytes to send. | |||||
*/ | |||||
void send(const uint8_t* buf, size_t n); | |||||
/** \return true - uses SPI transactions */ | |||||
#if IMPLEMENT_SPI_INTERFACE_SELECTION | |||||
void setSpiIf(uint8_t spiIf) { | |||||
m_spiIf = spiIf; | |||||
} | |||||
private: | |||||
uint8_t m_spiIf; | |||||
#endif // IMPLEMENT_SPI_INTERFACE_SELECTION | |||||
}; | |||||
//------------------------------------------------------------------------------ | |||||
/** | |||||
* \class SdSpiLib | |||||
* \brief Arduino SPI library class for access to SD and SDHC flash | |||||
* memory cards. | |||||
*/ | |||||
#if SD_SPI_CONFIGURATION >= 3 | |||||
class SdSpiLib : public SdSpiBase { | |||||
#else // SD_SPI_CONFIGURATION >= 3 | |||||
class SdSpiLib { | |||||
#endif // SD_SPI_CONFIGURATION >= 3 | |||||
public: | |||||
/** Initialize the SPI bus. | |||||
* | |||||
* \param[in] chipSelectPin SD card chip select pin. | |||||
*/ | |||||
void begin(uint8_t chipSelectPin) { | |||||
pinMode(chipSelectPin, OUTPUT); | |||||
digitalWrite(chipSelectPin, HIGH); | |||||
SPI.begin(); | |||||
} | |||||
/** Set SPI options for access to SD/SDHC cards. | |||||
* | |||||
* \param[in] divisor SCK clock divider relative to the system clock. | |||||
*/ | |||||
void beginTransaction(uint8_t divisor) { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); | |||||
#else // ENABLE_SPI_TRANSACTIONS | |||||
SPI.setBitOrder(MSBFIRST); | |||||
SPI.setDataMode(SPI_MODE0); | |||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
#ifndef SPI_CLOCK_DIV128 | |||||
SPI.setClockDivider(divisor); | |||||
#else // SPI_CLOCK_DIV128 | |||||
int v; | |||||
if (divisor <= 2) { | |||||
v = SPI_CLOCK_DIV2; | |||||
} else if (divisor <= 4) { | |||||
v = SPI_CLOCK_DIV4; | |||||
} else if (divisor <= 8) { | |||||
v = SPI_CLOCK_DIV8; | |||||
} else if (divisor <= 16) { | |||||
v = SPI_CLOCK_DIV16; | |||||
} else if (divisor <= 32) { | |||||
v = SPI_CLOCK_DIV32; | |||||
} else if (divisor <= 64) { | |||||
v = SPI_CLOCK_DIV64; | |||||
} else { | |||||
v = SPI_CLOCK_DIV128; | |||||
} | |||||
SPI.setClockDivider(v); | |||||
#endif // SPI_CLOCK_DIV128 | |||||
} | |||||
/** | |||||
* End SPI transaction. | |||||
*/ | |||||
void endTransaction() { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
SPI.endTransaction(); | |||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
} | |||||
/** Receive a byte. | |||||
* | |||||
* \return The byte. | |||||
*/ | |||||
uint8_t receive() { | |||||
return SPI.transfer(0XFF); | |||||
} | |||||
/** Receive multiple bytes. | |||||
* | |||||
* \param[out] buf Buffer to receive the data. | |||||
* \param[in] n Number of bytes to receive. | |||||
* | |||||
* \return Zero for no error or nonzero error code. | |||||
*/ | |||||
uint8_t receive(uint8_t* buf, size_t n) { | |||||
for (size_t i = 0; i < n; i++) { | |||||
buf[i] = SPI.transfer(0XFF); | |||||
} | |||||
return 0; | |||||
} | |||||
/** Send a byte. | |||||
* | |||||
* \param[in] b Byte to send | |||||
*/ | |||||
void send(uint8_t b) { | |||||
SPI.transfer(b); | |||||
} | |||||
/** Send multiple bytes. | |||||
* | |||||
* \param[in] buf Buffer for data to be sent. | |||||
* \param[in] n Number of bytes to send. | |||||
*/ | |||||
void send(const uint8_t* buf , size_t n) { | |||||
for (size_t i = 0; i < n; i++) { | |||||
SPI.transfer(buf[i]); | |||||
} | |||||
} | |||||
}; | |||||
//------------------------------------------------------------------------------ | |||||
#if SD_SPI_CONFIGURATION > 1 || defined(DOXYGEN) | |||||
#ifdef ARDUINO | |||||
#include "SoftSPI.h" | |||||
#elif defined(PLATFORM_ID) // Only defined if a Particle device | |||||
#include "SoftSPIParticle.h" | |||||
#endif // ARDUINO | |||||
/** | |||||
* \class SdSpiSoft | |||||
* \brief Software SPI class for access to SD and SDHC flash memory cards. | |||||
*/ | |||||
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||||
class SdSpiSoft : public SdSpiBase { | |||||
public: | |||||
/** Initialize the SPI bus. | |||||
* | |||||
* \param[in] chipSelectPin SD card chip select pin. | |||||
*/ | |||||
void begin(uint8_t chipSelectPin) { | |||||
pinMode(chipSelectPin, OUTPUT); | |||||
digitalWrite(chipSelectPin, HIGH); | |||||
m_spi.begin(); | |||||
} | |||||
/** | |||||
* Initialize hardware SPI - dummy for soft SPI | |||||
* \param[in] divisor SCK divisor - ignored. | |||||
*/ | |||||
void beginTransaction(uint8_t divisor) { | |||||
(void)divisor; | |||||
} | |||||
/** | |||||
* End SPI transaction - dummy for soft SPI | |||||
*/ | |||||
void endTransaction() {} | |||||
/** Receive a byte. | |||||
* | |||||
* \return The byte. | |||||
*/ | |||||
uint8_t receive() { | |||||
return m_spi.receive(); | |||||
} | |||||
/** Receive multiple bytes. | |||||
* | |||||
* \param[out] buf Buffer to receive the data. | |||||
* \param[in] n Number of bytes to receive. | |||||
* | |||||
* \return Zero for no error or nonzero error code. | |||||
*/ | |||||
uint8_t receive(uint8_t* buf, size_t n) { | |||||
for (size_t i = 0; i < n; i++) { | |||||
buf[i] = receive(); | |||||
} | |||||
return 0; | |||||
} | |||||
/** Send a byte. | |||||
* | |||||
* \param[in] data Byte to send | |||||
*/ | |||||
void send(uint8_t data) { | |||||
m_spi.send(data); | |||||
} | |||||
/** Send multiple bytes. | |||||
* | |||||
* \param[in] buf Buffer for data to be sent. | |||||
* \param[in] n Number of bytes to send. | |||||
*/ | |||||
void send(const uint8_t* buf , size_t n) { | |||||
for (size_t i = 0; i < n; i++) { | |||||
send(buf[i]); | |||||
} | |||||
} | |||||
private: | |||||
SoftSPI<MisoPin, MosiPin, SckPin, 0> m_spi; | |||||
}; | |||||
#endif // SD_SPI_CONFIGURATION > 1 || defined(DOXYGEN) | |||||
//------------------------------------------------------------------------------ | |||||
#if SD_SPI_CONFIGURATION == 2 | |||||
/** Default is software SPI. */ | |||||
typedef SdSpiSoft<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN> | |||||
SpiDefault_t; | |||||
#elif SD_SPI_CONFIGURATION == 1 || !SD_HAS_CUSTOM_SPI | |||||
/** Default is Arduino library SPI. */ | |||||
typedef SdSpiLib SpiDefault_t; | |||||
#else // SpiDefault_t | |||||
/** Default is custom fast SPI. */ | |||||
typedef SdSpi SpiDefault_t; | |||||
#endif // SpiDefault_t | |||||
//------------------------------------------------------------------------------ | |||||
// Use of in-line for AVR to save flash. | |||||
#ifdef __AVR__ | |||||
//------------------------------------------------------------------------------ | |||||
inline void SdSpi::begin(uint8_t chipSelectPin) { | |||||
#ifdef __AVR_ATmega328P__ | |||||
// Save a few bytes for 328 CPU - gcc optimizes single bit '|' to sbi. | |||||
PORTB |= 1 << 2; // SS high | |||||
DDRB |= 1 << 2; // SS output mode | |||||
DDRB |= 1 << 3; // MOSI output mode | |||||
DDRB |= 1 << 5; // SCK output mode | |||||
#else // __AVR_ATmega328P__ | |||||
// set SS high - may be chip select for another SPI device | |||||
digitalWrite(SS, HIGH); | |||||
// SS must be in output mode even it is not chip select | |||||
pinMode(SS, OUTPUT); | |||||
pinMode(MOSI, OUTPUT); | |||||
pinMode(SCK, OUTPUT); | |||||
#endif // __AVR_ATmega328P__ | |||||
pinMode(chipSelectPin, OUTPUT); | |||||
digitalWrite(chipSelectPin, HIGH); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline void SdSpi::beginTransaction(uint8_t divisor) { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
SPI.beginTransaction(SPISettings()); | |||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
uint8_t b = 2; | |||||
uint8_t r = 0; | |||||
// See AVR processor documentation. | |||||
for (; divisor > b && r < 7; b <<= 1, r += r < 5 ? 1 : 2) {} | |||||
SPCR = (1 << SPE) | (1 << MSTR) | (r >> 1); | |||||
SPSR = r & 1 ? 0 : 1 << SPI2X; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline void SdSpi::endTransaction() { | |||||
#if ENABLE_SPI_TRANSACTIONS | |||||
SPI.endTransaction(); | |||||
#endif // ENABLE_SPI_TRANSACTIONS | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline uint8_t SdSpi::receive() { | |||||
SPDR = 0XFF; | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
return SPDR; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
if (n-- == 0) { | |||||
return 0; | |||||
} | |||||
SPDR = 0XFF; | |||||
for (size_t i = 0; i < n; i++) { | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
uint8_t b = SPDR; | |||||
SPDR = 0XFF; | |||||
buf[i] = b; | |||||
} | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
buf[n] = SPDR; | |||||
return 0; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline void SdSpi::send(uint8_t data) { | |||||
SPDR = data; | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
if (n == 0) { | |||||
return; | |||||
} | |||||
SPDR = buf[0]; | |||||
if (n > 1) { | |||||
uint8_t b = buf[1]; | |||||
size_t i = 2; | |||||
while (1) { | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
SPDR = b; | |||||
if (i == n) { | |||||
break; | |||||
} | |||||
b = buf[i++]; | |||||
} | |||||
} | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
} | |||||
#endif // __AVR__ | |||||
#endif // SdSpi_h |
/* Arduino SdSpiCard Library | |||||
* Copyright (C) 2016 by William Greiman | |||||
* | |||||
* This file is part of the Arduino SdSpiCard Library | |||||
* | |||||
* This Library is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This Library is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with the Arduino SdSpiCard Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef SdSpiBaseDriver_h | |||||
#define SdSpiBaseDriver_h | |||||
/** | |||||
* \class SdSpiBaseDriver | |||||
* \brief SPI base driver. | |||||
*/ | |||||
class SdSpiBaseDriver { | |||||
public: | |||||
/** Set SPI options for access to SD/SDHC cards. | |||||
* | |||||
*/ | |||||
virtual void activate() = 0; | |||||
/** Initialize the SPI bus. | |||||
* | |||||
* \param[in] chipSelectPin SD card chip select pin. | |||||
*/ | |||||
virtual void begin(uint8_t chipSelectPin) = 0; | |||||
/** | |||||
* End SPI transaction. | |||||
*/ | |||||
virtual void deactivate() = 0; | |||||
/** Receive a byte. | |||||
* | |||||
* \return The byte. | |||||
*/ | |||||
virtual uint8_t receive() = 0; | |||||
/** Receive multiple bytes. | |||||
* | |||||
* \param[out] buf Buffer to receive the data. | |||||
* \param[in] n Number of bytes to receive. | |||||
* | |||||
* \return Zero for no error or nonzero error code. | |||||
*/ | |||||
virtual uint8_t receive(uint8_t* buf, size_t n) = 0; | |||||
/** Send a byte. | |||||
* | |||||
* \param[in] data Byte to send | |||||
*/ | |||||
virtual void send(uint8_t data) = 0; | |||||
/** Send multiple bytes. | |||||
* | |||||
* \param[in] buf Buffer for data to be sent. | |||||
* \param[in] n Number of bytes to send. | |||||
*/ | |||||
virtual void send(const uint8_t* buf, size_t n) = 0; | |||||
/** Set CS low. */ | |||||
virtual void select() = 0; | |||||
/** Save SPI settings. | |||||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||||
*/ | |||||
virtual void setSpiSettings(SPISettings spiSettings) = 0; | |||||
/** Set CS high. */ | |||||
virtual void unselect() = 0; | |||||
}; | |||||
#endif // SdSpiBaseDriver_h |
* <http://www.gnu.org/licenses/>. | * <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include "SdSpiCard.h" | #include "SdSpiCard.h" | ||||
#include "SdSpi.h" | |||||
// debug trace macro | // debug trace macro | ||||
#define SD_TRACE(m, b) | #define SD_TRACE(m, b) | ||||
// #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); | // #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
#if USE_SD_CRC == 1 | #if USE_SD_CRC == 1 | ||||
// slower CRC-CCITT | |||||
// Shift based CRC-CCITT | |||||
// uses the x^16,x^12,x^5,x^1 polynomial. | // uses the x^16,x^12,x^5,x^1 polynomial. | ||||
static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { | static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { | ||||
uint16_t crc = 0; | uint16_t crc = 0; | ||||
} | } | ||||
#elif USE_SD_CRC > 1 // CRC_CCITT | #elif USE_SD_CRC > 1 // CRC_CCITT | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// faster CRC-CCITT | |||||
// Table based CRC-CCITT | |||||
// uses the x^16,x^12,x^5,x^1 polynomial. | // uses the x^16,x^12,x^5,x^1 polynomial. | ||||
#ifdef __AVR__ | #ifdef __AVR__ | ||||
static const uint16_t crctab[] PROGMEM = { | static const uint16_t crctab[] PROGMEM = { | ||||
//============================================================================== | //============================================================================== | ||||
// SdSpiCard member functions | // SdSpiCard member functions | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||||
bool SdSpiCard::begin(SdSpiDriver* spiDriver) { | |||||
m_spiActive = false; | |||||
m_errorCode = m_type = 0; | m_errorCode = m_type = 0; | ||||
m_spi = spi; | |||||
m_chipSelectPin = chipSelectPin; | |||||
// 16-bit init start time allows over a minute | |||||
unsigned t0 = (unsigned)millis(); | |||||
m_spiDriver = spiDriver; | |||||
uint16_t t0 = curTimeMS(); | |||||
uint32_t arg; | uint32_t arg; | ||||
// initialize SPI bus and chip select pin. | |||||
spiBegin(m_chipSelectPin); | |||||
// set SCK rate for initialization commands. | |||||
m_sckDivisor = SPI_SCK_INIT_DIVISOR; | |||||
spiBeginTransaction(m_sckDivisor); | |||||
spiStart(); | |||||
// must supply min of 74 clock cycles with CS high. | // must supply min of 74 clock cycles with CS high. | ||||
spiUnselect(); | |||||
for (uint8_t i = 0; i < 10; i++) { | for (uint8_t i = 0; i < 10; i++) { | ||||
spiSend(0XFF); | spiSend(0XFF); | ||||
} | } | ||||
spiEndTransaction(); | |||||
spiSelect(); | |||||
// command to go idle in SPI mode | // command to go idle in SPI mode | ||||
while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { | while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { | ||||
if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||||
if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||||
error(SD_CARD_ERROR_CMD0); | error(SD_CARD_ERROR_CMD0); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
type(SD_CARD_TYPE_SD2); | type(SD_CARD_TYPE_SD2); | ||||
break; | break; | ||||
} | } | ||||
if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||||
if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||||
error(SD_CARD_ERROR_CMD8); | error(SD_CARD_ERROR_CMD8); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { | while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { | ||||
// check for timeout | // check for timeout | ||||
if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||||
if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||||
error(SD_CARD_ERROR_ACMD41); | error(SD_CARD_ERROR_ACMD41); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
spiReceive(); | spiReceive(); | ||||
} | } | ||||
} | } | ||||
chipSelectHigh(); | |||||
m_sckDivisor = sckDivisor; | |||||
spiStop(); | |||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// send command and return error code. Return zero for OK | // send command and return error code. Return zero for OK | ||||
uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { | uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { | ||||
// select card | // select card | ||||
if (!m_selected) { | |||||
chipSelectLow(); | |||||
if (!m_spiActive) { | |||||
spiStart(); | |||||
} | } | ||||
// wait if busy | // wait if busy | ||||
waitNotBusy(SD_WRITE_TIMEOUT); | waitNotBusy(SD_WRITE_TIMEOUT); | ||||
uint8_t *pa = reinterpret_cast<uint8_t *>(&arg); | |||||
#if USE_SD_CRC | #if USE_SD_CRC | ||||
// form message | // form message | ||||
uint8_t buf[6]; | |||||
buf[0] = (uint8_t)0x40U | cmd; | |||||
buf[1] = (uint8_t)(arg >> 24U); | |||||
buf[2] = (uint8_t)(arg >> 16U); | |||||
buf[3] = (uint8_t)(arg >> 8U); | |||||
buf[4] = (uint8_t)arg; | |||||
uint8_t d[6] = {cmd , pa[3], pa[2], pa[1], pa[0]}; | |||||
d[0] |= 0X40; | |||||
// add crc | |||||
d[5] = CRC7(d, 5); | |||||
// add CRC | |||||
buf[5] = CRC7(buf, 5); | |||||
// send message | // send message | ||||
for (uint8_t k = 0; k < 6; k++) { | |||||
spiSend(d[k]); | |||||
} | |||||
spiSend(buf, 6); | |||||
#else // USE_SD_CRC | #else // USE_SD_CRC | ||||
// send command | // send command | ||||
spiSend(cmd | 0x40); | spiSend(cmd | 0x40); | ||||
// send argument | // send argument | ||||
uint8_t *pa = reinterpret_cast<uint8_t *>(&arg); | |||||
for (int8_t i = 3; i >= 0; i--) { | for (int8_t i = 3; i >= 0; i--) { | ||||
spiSend(pa[i]); | spiSend(pa[i]); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
void SdSpiCard::chipSelectHigh() { | |||||
if (!m_selected) { | |||||
SD_CS_DBG("chipSelectHigh error"); | |||||
return; | |||||
} | |||||
digitalWrite(m_chipSelectPin, HIGH); | |||||
// insure MISO goes high impedance | |||||
spiSend(0XFF); | |||||
spiEndTransaction(); | |||||
m_selected = false; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdSpiCard::chipSelectLow() { | |||||
#if WDT_YIELD_TIME_MICROS | |||||
static uint32_t last; | |||||
if ((micros() - last) > WDT_YIELD_TIME_MICROS) { | |||||
SysCall::yield(); | |||||
last = micros(); | |||||
} | |||||
#endif // WDT_YIELD_TIME_MICROS | |||||
if (m_selected) { | |||||
SD_CS_DBG("chipSelectLow error"); | |||||
return; | |||||
} | |||||
spiBeginTransaction(m_sckDivisor); | |||||
digitalWrite(m_chipSelectPin, LOW); | |||||
m_selected = true; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | ||||
csd_t csd; | csd_t csd; | ||||
if (!readCSD(&csd)) { | if (!readCSD(&csd)) { | ||||
error(SD_CARD_ERROR_ERASE_TIMEOUT); | error(SD_CARD_ERROR_ERASE_TIMEOUT); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
bool SdSpiCard::isBusy() { | bool SdSpiCard::isBusy() { | ||||
bool rtn = true; | bool rtn = true; | ||||
bool selected = m_selected; | |||||
chipSelectLow(); | |||||
bool spiActive = m_spiActive; | |||||
if (!spiActive) { | |||||
spiStart(); | |||||
} | |||||
for (uint8_t i = 0; i < 8; i++) { | for (uint8_t i = 0; i < 8; i++) { | ||||
if (0XFF == spiReceive()) { | if (0XFF == spiReceive()) { | ||||
rtn = false; | rtn = false; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (!selected) { | |||||
chipSelectHigh(); | |||||
if (!spiActive) { | |||||
spiStop(); | |||||
} | } | ||||
return rtn; | return rtn; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
bool SdSpiCard::isTimedOut(uint16_t startMS, uint16_t timeoutMS) { | |||||
#if WDT_YIELD_TIME_MICROS | |||||
static uint32_t last; | |||||
if ((micros() - last) > WDT_YIELD_TIME_MICROS) { | |||||
SysCall::yield(); | |||||
last = micros(); | |||||
} | |||||
#endif // WDT_YIELD_TIME_MICROS | |||||
return (curTimeMS() - startMS) > timeoutMS; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { | bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { | ||||
SD_TRACE("RB", blockNumber); | SD_TRACE("RB", blockNumber); | ||||
// use address if not SDHC card | // use address if not SDHC card | ||||
if (!readData(dst, 512)) { | if (!readData(dst, 512)) { | ||||
goto fail; | goto fail; | ||||
} | } | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
bool SdSpiCard::readData(uint8_t *dst) { | bool SdSpiCard::readData(uint8_t *dst) { | ||||
bool selected = m_selected; | |||||
chipSelectLow(); | |||||
if (!readData(dst, 512)) { | |||||
return false; | |||||
} | |||||
if (!selected) { | |||||
chipSelectHigh(); | |||||
} | |||||
return true; | |||||
return readData(dst, 512); | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
bool SdSpiCard::readData(uint8_t* dst, size_t count) { | bool SdSpiCard::readData(uint8_t* dst, size_t count) { | ||||
uint16_t crc; | uint16_t crc; | ||||
#endif // USE_SD_CRC | #endif // USE_SD_CRC | ||||
// wait for start block token | // wait for start block token | ||||
unsigned t0 = millis(); | |||||
uint16_t t0 = curTimeMS(); | |||||
while ((m_status = spiReceive()) == 0XFF) { | while ((m_status = spiReceive()) == 0XFF) { | ||||
if (((unsigned)millis() - t0) > SD_READ_TIMEOUT) { | |||||
if (isTimedOut(t0, SD_READ_TIMEOUT)) { | |||||
error(SD_CARD_ERROR_READ_TIMEOUT); | error(SD_CARD_ERROR_READ_TIMEOUT); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
p[3 - i] = spiReceive(); | p[3 - i] = spiReceive(); | ||||
} | } | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
error(SD_CARD_ERROR_READ_REG); | error(SD_CARD_ERROR_READ_REG); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
return readData(dst, 16); | |||||
if (!readData(dst, 16)) { | |||||
goto fail; | |||||
} | |||||
spiStop(); | |||||
return true; | |||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
error(SD_CARD_ERROR_CMD18); | error(SD_CARD_ERROR_CMD18); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
// spiStop(); | |||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | |||||
bool SdSpiCard::readStop() { | |||||
if (cardCommand(CMD12, 0)) { | |||||
error(SD_CARD_ERROR_CMD12); | |||||
//----------------------------------------------------------------------------- | |||||
bool SdSpiCard::readStatus(uint8_t* status) { | |||||
// retrun is R2 so read extra status byte. | |||||
if (cardAcmd(ACMD13, 0) || spiReceive()) { | |||||
error(SD_CARD_ERROR_ACMD13); | |||||
goto fail; | goto fail; | ||||
} | } | ||||
chipSelectHigh(); | |||||
if (!readData(status, 64)) { | |||||
goto fail; | |||||
} | |||||
spiStop(); | |||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | |||||
bool SdSpiCard::sendCmd6(uint32_t arg, uint8_t* status) { | |||||
if (cardCommand(CMD6, arg)) { | |||||
error(SD_CARD_ERROR_CMD6); | |||||
goto fail; | |||||
//----------------------------------------------------------------------------- | |||||
void SdSpiCard::spiStart() { | |||||
if (!m_spiActive) { | |||||
spiActivate(); | |||||
spiSelect(); | |||||
m_spiActive = true; | |||||
} | } | ||||
if (!readData(status, 64)) { | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
void SdSpiCard::spiStop() { | |||||
if (m_spiActive) { | |||||
spiUnselect(); | |||||
spiSend(0XFF); | |||||
spiDeactivate(); | |||||
m_spiActive = false; | |||||
} | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
bool SdSpiCard::readStop() { | |||||
if (cardCommand(CMD12, 0)) { | |||||
error(SD_CARD_ERROR_CMD12); | |||||
goto fail; | goto fail; | ||||
} | } | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
bool SdSpiCard::setHighSpeedMode(uint8_t divisor) { | |||||
uint8_t status[64]; | |||||
uint8_t saveDivisor = m_sckDivisor; | |||||
setSckDivisor(128); | |||||
if (!sendCmd6(0X00FFFFFF, status) || (2 & status[13]) == 0 || | |||||
!sendCmd6(0X80FFFFF1, status) || (status[16] & 0XF) != 1) { | |||||
setSckDivisor(saveDivisor); | |||||
return false; | |||||
} | |||||
setSckDivisor(divisor); | |||||
return true; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
// wait for card to go not busy | // wait for card to go not busy | ||||
bool SdSpiCard::waitNotBusy(uint16_t timeoutMillis) { | |||||
unsigned t0 = millis(); | |||||
bool SdSpiCard::waitNotBusy(uint16_t timeoutMS) { | |||||
uint16_t t0 = curTimeMS(); | |||||
#if WDT_YIELD_TIME_MICROS | |||||
// Call isTimedOut first to insure yield is called. | |||||
while (!isTimedOut(t0, timeoutMS)) { | |||||
if (spiReceive() == 0XFF) { | |||||
return true; | |||||
} | |||||
} | |||||
return false; | |||||
#else // WDT_YIELD_TIME_MICROS | |||||
// Check not busy first since yield is not called in isTimedOut. | |||||
while (spiReceive() != 0XFF) { | while (spiReceive() != 0XFF) { | ||||
if (((unsigned)millis() - t0) >= timeoutMillis) { | |||||
goto fail; | |||||
if (isTimedOut(t0, timeoutMS)) { | |||||
return false; | |||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
fail: | |||||
return false; | |||||
#endif // WDT_YIELD_TIME_MICROS | |||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | ||||
} | } | ||||
#endif // CHECK_PROGRAMMING | #endif // CHECK_PROGRAMMING | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { | bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { | ||||
if (!writeStart(block, count)) { | |||||
if (!writeStart(block)) { | |||||
goto fail; | goto fail; | ||||
} | } | ||||
for (size_t b = 0; b < count; b++, src += 512) { | for (size_t b = 0; b < count; b++, src += 512) { | ||||
return writeStop(); | return writeStop(); | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
bool SdSpiCard::writeData(const uint8_t* src) { | bool SdSpiCard::writeData(const uint8_t* src) { | ||||
bool selected = m_selected; | |||||
chipSelectLow(); | |||||
// wait for previous write to finish | // wait for previous write to finish | ||||
if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | ||||
error(SD_CARD_ERROR_WRITE_TIMEOUT); | error(SD_CARD_ERROR_WRITE_TIMEOUT); | ||||
if (!writeData(WRITE_MULTIPLE_TOKEN, src)) { | if (!writeData(WRITE_MULTIPLE_TOKEN, src)) { | ||||
goto fail; | goto fail; | ||||
} | } | ||||
if (!selected) { | |||||
chipSelectHigh(); | |||||
} | |||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
bool SdSpiCard::writeStart(uint32_t blockNumber) { | |||||
// use address if not SDHC card | |||||
if (type() != SD_CARD_TYPE_SDHC) { | |||||
blockNumber <<= 9; | |||||
} | |||||
if (cardCommand(CMD25, blockNumber)) { | |||||
error(SD_CARD_ERROR_CMD25); | |||||
goto fail; | |||||
} | |||||
return true; | |||||
fail: | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
return true; | return true; | ||||
fail: | fail: | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
bool SdSpiCard::writeStop() { | bool SdSpiCard::writeStop() { | ||||
chipSelectLow(); | |||||
if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | ||||
goto fail; | goto fail; | ||||
} | } | ||||
spiSend(STOP_TRAN_TOKEN); | spiSend(STOP_TRAN_TOKEN); | ||||
if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||||
goto fail; | |||||
} | |||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return true; | return true; | ||||
fail: | fail: | ||||
error(SD_CARD_ERROR_STOP_TRAN); | error(SD_CARD_ERROR_STOP_TRAN); | ||||
chipSelectHigh(); | |||||
spiStop(); | |||||
return false; | return false; | ||||
} | } |
* \file | * \file | ||||
* \brief SdSpiCard class for V2 SD/SDHC cards | * \brief SdSpiCard class for V2 SD/SDHC cards | ||||
*/ | */ | ||||
#include "SystemInclude.h" | |||||
#include "SdFatConfig.h" | |||||
#include <stddef.h> | |||||
#include "SysCall.h" | |||||
#include "SdInfo.h" | #include "SdInfo.h" | ||||
#include "SdSpi.h" | |||||
#include "SdSpiDriver.h" | |||||
//============================================================================== | //============================================================================== | ||||
/** | /** | ||||
* \class SdSpiCard | * \class SdSpiCard | ||||
*/ | */ | ||||
class SdSpiCard { | class SdSpiCard { | ||||
public: | public: | ||||
/** typedef for SPI class. */ | |||||
#if SD_SPI_CONFIGURATION < 3 | |||||
typedef SpiDefault_t m_spi_t; | |||||
#else // SD_SPI_CONFIGURATION < 3 | |||||
typedef SdSpiBase m_spi_t; | |||||
#endif // SD_SPI_CONFIGURATION < 3 | |||||
/** Construct an instance of SdSpiCard. */ | /** Construct an instance of SdSpiCard. */ | ||||
SdSpiCard() : m_selected(false), | |||||
m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} | |||||
SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} | |||||
/** Initialize the SD card. | /** Initialize the SD card. | ||||
* \param[in] spi SPI object. | |||||
* \param[in] chipSelectPin SD chip select pin. | |||||
* \param[in] sckDivisor SPI clock divisor. | |||||
* \param[in] spiDriver SPI driver for card. | |||||
* \return true for success else false. | * \return true for success else false. | ||||
*/ | */ | ||||
bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS, | |||||
uint8_t sckDivisor = SPI_FULL_SPEED); | |||||
bool begin(SdSpiDriver* spiDriver); | |||||
/** | /** | ||||
* Determine the size of an SD flash memory card. | * Determine the size of an SD flash memory card. | ||||
* | * | ||||
* or zero if an error occurs. | * or zero if an error occurs. | ||||
*/ | */ | ||||
uint32_t cardSize(); | uint32_t cardSize(); | ||||
/** Set the SD chip select pin high, send a dummy byte, and call SPI endTransaction. | |||||
* | |||||
* This function should only be called by programs doing raw I/O to the SD. | |||||
*/ | |||||
void chipSelectHigh(); | |||||
/** Set the SD chip select pin low and call SPI beginTransaction. | |||||
* | |||||
* This function should only be called by programs doing raw I/O to the SD. | |||||
*/ | |||||
void chipSelectLow(); | |||||
/** Erase a range of blocks. | /** Erase a range of blocks. | ||||
* | * | ||||
* \param[in] firstBlock The address of the first block in the range. | * \param[in] firstBlock The address of the first block in the range. | ||||
* the value false is returned for failure. | * the value false is returned for failure. | ||||
*/ | */ | ||||
bool readStart(uint32_t blockNumber); | bool readStart(uint32_t blockNumber); | ||||
/** Return the 64 byte card status | |||||
* \param[out] status location for 64 status bytes. | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool readStatus(uint8_t* status); | |||||
/** End a read multiple blocks sequence. | /** End a read multiple blocks sequence. | ||||
* | * | ||||
* \return The value true is returned for success and | * \return The value true is returned for success and | ||||
* the value false is returned for failure. | * the value false is returned for failure. | ||||
*/ | */ | ||||
bool readStop(); | bool readStop(); | ||||
/** Return SCK divisor. | |||||
* | |||||
* \return Requested SCK divisor. | |||||
*/ | |||||
uint8_t sckDivisor() { | |||||
return m_sckDivisor; | |||||
} | |||||
/** \return the SD chip select status, true if slected else false. */ | |||||
bool selected() {return m_selected;} | |||||
/** Send CMD6 - Switch Function Command | |||||
* | |||||
* param[in] arg 32-bit argument to CMD6. | |||||
* param[out] status - 64 byte status returned by CMD6. | |||||
* \return true if the command was accepted else false. | |||||
*/ | |||||
bool sendCmd6(uint32_t arg, uint8_t* status); | |||||
/** Set High Speed Bus Mode. | |||||
* | |||||
* param[in] divisor new value for SPI SCK divisor. | |||||
* \return true if successful else false. | |||||
*/ | |||||
bool setHighSpeedMode(uint8_t divisor); | |||||
/** Set SCK divisor. | |||||
* param[in] sckDivisor value for divisor. | |||||
*/ | |||||
void setSckDivisor(uint8_t sckDivisor) { | |||||
m_sckDivisor = sckDivisor; | |||||
} | |||||
/** Return the card type: SD V1, SD V2 or SDHC | /** Return the card type: SD V1, SD V2 or SDHC | ||||
* \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. | * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. | ||||
*/ | */ | ||||
*/ | */ | ||||
bool writeData(const uint8_t* src); | bool writeData(const uint8_t* src); | ||||
/** Start a write multiple blocks sequence. | /** Start a write multiple blocks sequence. | ||||
* | |||||
* \param[in] blockNumber Address of first block in sequence. | |||||
* | |||||
* \note This function is used with writeData() and writeStop() | |||||
* for optimized multiple block writes. | |||||
* | |||||
* \return The value true is returned for success and | |||||
* the value false is returned for failure. | |||||
*/ | |||||
bool writeStart(uint32_t blockNumber); | |||||
/** Start a write multiple blocks sequence with pre-erase. | |||||
* | * | ||||
* \param[in] blockNumber Address of first block in sequence. | * \param[in] blockNumber Address of first block in sequence. | ||||
* \param[in] eraseCount The number of blocks to be pre-erased. | * \param[in] eraseCount The number of blocks to be pre-erased. | ||||
* the value false is returned for failure. | * the value false is returned for failure. | ||||
*/ | */ | ||||
bool writeStop(); | bool writeStop(); | ||||
/** Set CS low and activate the card. */ | |||||
void spiStart(); | |||||
/** Set CS high and deactivate the card. */ | |||||
void spiStop(); | |||||
private: | private: | ||||
// private functions | // private functions | ||||
return cardCommand(cmd, arg); | return cardCommand(cmd, arg); | ||||
} | } | ||||
uint8_t cardCommand(uint8_t cmd, uint32_t arg); | uint8_t cardCommand(uint8_t cmd, uint32_t arg); | ||||
bool isTimedOut(uint16_t startMS, uint16_t timeoutMS); | |||||
bool readData(uint8_t* dst, size_t count); | bool readData(uint8_t* dst, size_t count); | ||||
bool readRegister(uint8_t cmd, void* buf); | bool readRegister(uint8_t cmd, void* buf); | ||||
void type(uint8_t value) { | void type(uint8_t value) { | ||||
m_type = value; | m_type = value; | ||||
} | } | ||||
bool waitNotBusy(uint16_t timeoutMillis); | |||||
bool waitNotBusy(uint16_t timeoutMS); | |||||
bool writeData(uint8_t token, const uint8_t* src); | bool writeData(uint8_t token, const uint8_t* src); | ||||
void spiBegin(uint8_t chipSelectPin) { | |||||
m_spi->begin(chipSelectPin); | |||||
} | |||||
void spiBeginTransaction(uint8_t spiDivisor) { | |||||
m_spi->beginTransaction(spiDivisor); | |||||
//--------------------------------------------------------------------------- | |||||
// functions defined in SdSpiDriver.h | |||||
void spiActivate() { | |||||
m_spiDriver->activate(); | |||||
} | } | ||||
void spiEndTransaction() { | |||||
m_spi->endTransaction(); | |||||
void spiDeactivate() { | |||||
m_spiDriver->deactivate(); | |||||
} | } | ||||
uint8_t spiReceive() { | uint8_t spiReceive() { | ||||
return m_spi->receive(); | |||||
return m_spiDriver->receive(); | |||||
} | } | ||||
uint8_t spiReceive(uint8_t* buf, size_t n) { | uint8_t spiReceive(uint8_t* buf, size_t n) { | ||||
return m_spi->receive(buf, n); | |||||
return m_spiDriver->receive(buf, n); | |||||
} | } | ||||
void spiSend(uint8_t data) { | void spiSend(uint8_t data) { | ||||
m_spi->send(data); | |||||
m_spiDriver->send(data); | |||||
} | } | ||||
void spiSend(const uint8_t* buf, size_t n) { | void spiSend(const uint8_t* buf, size_t n) { | ||||
m_spi->send(buf, n); | |||||
m_spiDriver->send(buf, n); | |||||
} | |||||
void spiSelect() { | |||||
m_spiDriver->select(); | |||||
} | |||||
void spiUnselect() { | |||||
m_spiDriver->unselect(); | |||||
} | } | ||||
m_spi_t* m_spi; | |||||
bool m_selected; | |||||
uint8_t m_chipSelectPin; | |||||
uint8_t m_errorCode; | uint8_t m_errorCode; | ||||
uint8_t m_sckDivisor; | |||||
SdSpiDriver *m_spiDriver; | |||||
bool m_spiActive; | |||||
uint8_t m_status; | uint8_t m_status; | ||||
uint8_t m_type; | uint8_t m_type; | ||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
/** | |||||
* \class Sd2Card | |||||
* \brief Raw access to SD and SDHC card using default SPI library. | |||||
*/ | |||||
class Sd2Card : public SdSpiCard { | |||||
public: | |||||
/** Initialize the SD card. | |||||
* \param[in] chipSelectPin SD chip select pin. | |||||
* \param[in] sckDivisor SPI clock divisor. | |||||
* \return true for success else false. | |||||
*/ | |||||
bool begin(uint8_t chipSelectPin = SS, uint8_t sckDivisor = 2) { | |||||
return SdSpiCard::begin(&m_spi, chipSelectPin, sckDivisor); | |||||
} | |||||
/** Initialize the SD card. Obsolete form. | |||||
* \param[in] chipSelectPin SD chip select pin. | |||||
* \param[in] sckDivisor SPI clock divisor. | |||||
* \return true for success else false. | |||||
*/ | |||||
bool init(uint8_t sckDivisor = 2, uint8_t chipSelectPin = SS) { | |||||
return begin(chipSelectPin, sckDivisor); | |||||
} | |||||
private: | |||||
bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS, | |||||
uint8_t sckDivisor = SPI_FULL_SPEED) { | |||||
(void)spi; | |||||
(void)chipSelectPin; | |||||
(void)sckDivisor; | |||||
return false; | |||||
} | |||||
SpiDefault_t m_spi; | |||||
}; | |||||
#endif // SpiCard_h | #endif // SpiCard_h |
/* Arduino SdFat Library | |||||
* Copyright (C) 2016 by William Greiman | |||||
* | |||||
* This file is part of the Arduino SdFat Library | |||||
* | |||||
* This Library is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This Library is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with the Arduino SdFat Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "SdSpi.h" | |||||
#if defined(PLATFORM_ID) | |||||
static uint32_t bugDelay = 0; // fix for SPI DMA bug. | |||||
static volatile bool SPI_DMA_TransferCompleted = false; | |||||
static SPIClass* const spiPtr[] = { | |||||
&SPI | |||||
#if Wiring_SPI1 | |||||
, &SPI1 | |||||
#if Wiring_SPI2 | |||||
, &SPI2 | |||||
#endif // Wiring_SPI2 | |||||
#endif // Wiring_SPI1 | |||||
}; | |||||
#if SPI_INTERFACE_COUNT == 1 | |||||
const uint8_t m_spiIf = 0; | |||||
#endif | |||||
//----------------------------------------------------------------------------- | |||||
void SD_SPI_DMA_TransferComplete_Callback(void) { | |||||
SPI_DMA_TransferCompleted = true; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdSpi::begin(uint8_t chipSelectPin) { | |||||
spiPtr[m_spiIf]->begin(chipSelectPin); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
void SdSpi::beginTransaction(uint8_t divisor) { | |||||
spiPtr[m_spiIf]->setBitOrder(MSBFIRST); | |||||
spiPtr[m_spiIf]->setDataMode(SPI_MODE0); | |||||
#ifndef SPI_CLOCK_DIV128 | |||||
spiPtr[m_spiIf]->setClockDivider(divisor); | |||||
#else // SPI_CLOCK_DIV128 | |||||
int v; | |||||
if (divisor <= 2) { | |||||
v = SPI_CLOCK_DIV2; | |||||
} else if (divisor <= 4) { | |||||
v = SPI_CLOCK_DIV4; | |||||
} else if (divisor <= 8) { | |||||
v = SPI_CLOCK_DIV8; | |||||
} else if (divisor <= 16) { | |||||
v = SPI_CLOCK_DIV16; | |||||
} else if (divisor <= 32) { | |||||
v = SPI_CLOCK_DIV32; | |||||
} else if (divisor <= 64) { | |||||
v = SPI_CLOCK_DIV64; | |||||
} else { | |||||
v = SPI_CLOCK_DIV128; | |||||
} | |||||
spiPtr[m_spiIf]->setClockDivider(v); | |||||
#endif // SPI_CLOCK_DIV128 | |||||
// delay for SPI transfer done callback too soon bug. | |||||
bugDelay = 24*divisor*(1 + m_spiIf)/60; | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
void SdSpi::endTransaction() { | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
/** SPI receive a byte */ | |||||
uint8_t SdSpi::receive() { | |||||
return spiPtr[m_spiIf]->transfer(0xFF); | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||||
SPI_DMA_TransferCompleted = false; | |||||
spiPtr[m_spiIf]->transfer(0, buf, n, SD_SPI_DMA_TransferComplete_Callback); | |||||
while (!SPI_DMA_TransferCompleted) {} | |||||
if (bugDelay) { | |||||
delayMicroseconds(bugDelay); | |||||
} | |||||
return 0; | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
/** SPI send a byte */ | |||||
void SdSpi::send(uint8_t b) { | |||||
spiPtr[m_spiIf]->transfer(b); | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||||
SPI_DMA_TransferCompleted = false; | |||||
spiPtr[m_spiIf]->transfer(const_cast<uint8_t*>(buf), 0, n, | |||||
SD_SPI_DMA_TransferComplete_Callback); | |||||
while (!SPI_DMA_TransferCompleted) {} | |||||
if (bugDelay) { | |||||
delayMicroseconds(bugDelay); | |||||
} | |||||
} | |||||
#endif // defined(PLATFORM_ID) |
/* SdFat Library | |||||
* Copyright (C) 2016 by William Greiman | |||||
* | |||||
* This file is part of the FatLib Library | |||||
* | |||||
* This Library is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This Library is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with the FatLib Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
/** | |||||
* \file | |||||
* \brief SpiDriver classes | |||||
*/ | |||||
#ifndef SdSpiDriver_h | |||||
#define SdSpiDriver_h | |||||
#include <Arduino.h> | |||||
#include "SPI.h" | |||||
#include "SdSpiCard/SdSpiBaseDriver.h" | |||||
#include "SdFatConfig.h" | |||||
//----------------------------------------------------------------------------- | |||||
/** | |||||
* \class SdSpiLibDriver | |||||
* \brief SdSpiLibDriver - use standard SPI library. | |||||
*/ | |||||
#if ENABLE_SOFTWARE_SPI_CLASS | |||||
class SdSpiLibDriver : public SdSpiBaseDriver { | |||||
#else // ENABLE_SOFTWARE_SPI_CLASS | |||||
class SdSpiLibDriver { | |||||
#endif // ENABLE_SOFTWARE_SPI_CLASS | |||||
public: | |||||
/** Activate SPI hardware. */ | |||||
void activate() { | |||||
SPI.beginTransaction(m_spiSettings); | |||||
} | |||||
/** Deactivate SPI hardware. */ | |||||
void deactivate() { | |||||
SPI.endTransaction(); | |||||
} | |||||
/** Initialize the SPI bus. | |||||
* | |||||
* \param[in] csPin SD card chip select pin. | |||||
*/ | |||||
void begin(uint8_t csPin) { | |||||
m_csPin = csPin; | |||||
digitalWrite(csPin, HIGH); | |||||
pinMode(csPin, OUTPUT); | |||||
SPI.begin(); | |||||
} | |||||
/** Receive a byte. | |||||
* | |||||
* \return The byte. | |||||
*/ | |||||
uint8_t receive() { | |||||
return SPI.transfer( 0XFF); | |||||
} | |||||
/** Receive multiple bytes. | |||||
* | |||||
* \param[out] buf Buffer to receive the data. | |||||
* \param[in] n Number of bytes to receive. | |||||
* | |||||
* \return Zero for no error or nonzero error code. | |||||
*/ | |||||
uint8_t receive(uint8_t* buf, size_t n) { | |||||
for (size_t i = 0; i < n; i++) { | |||||
buf[i] = SPI.transfer(0XFF); | |||||
} | |||||
return 0; | |||||
} | |||||
/** Send a byte. | |||||
* | |||||
* \param[in] data Byte to send | |||||
*/ | |||||
void send(uint8_t data) { | |||||
SPI.transfer(data); | |||||
} | |||||
/** Send multiple bytes. | |||||
* | |||||
* \param[in] buf Buffer for data to be sent. | |||||
* \param[in] n Number of bytes to send. | |||||
*/ | |||||
void send(const uint8_t* buf, size_t n) { | |||||
for (size_t i = 0; i < n; i++) { | |||||
SPI.transfer(buf[i]); | |||||
} | |||||
} | |||||
/** Set CS low. */ | |||||
void select() { | |||||
digitalWrite(m_csPin, LOW); | |||||
} | |||||
/** Save SPISettings. | |||||
* | |||||
* \param[in] spiSettings SPI speed, mode, and byte order. | |||||
*/ | |||||
void setSpiSettings(SPISettings spiSettings) { | |||||
m_spiSettings = spiSettings; | |||||
} | |||||
/** Set CS high. */ | |||||
void unselect() { | |||||
digitalWrite(m_csPin, HIGH); | |||||
} | |||||
private: | |||||
SPISettings m_spiSettings; | |||||
uint8_t m_csPin; | |||||
}; | |||||
//----------------------------------------------------------------------------- | |||||
/** | |||||
* \class SdSpiAltDriver | |||||
* \brief Optimized SPI class for access to SD and SDHC flash memory cards. | |||||
*/ | |||||
#if ENABLE_SOFTWARE_SPI_CLASS | |||||
class SdSpiAltDriver : public SdSpiBaseDriver { | |||||
#else // ENABLE_SOFTWARE_SPI_CLASS | |||||
class SdSpiAltDriver { | |||||
#endif // ENABLE_SOFTWARE_SPI_CLASS | |||||
public: | |||||
/** Activate SPI hardware. */ | |||||
void activate(); | |||||
/** Deactivate SPI hardware. */ | |||||
void deactivate(); | |||||
/** Initialize the SPI bus. | |||||
* | |||||
* \param[in] csPin SD card chip select pin. | |||||
*/ | |||||
void begin(uint8_t csPin); | |||||
/** Receive a byte. | |||||
* | |||||
* \return The byte. | |||||
*/ | |||||
uint8_t receive(); | |||||
/** Receive multiple bytes. | |||||
* | |||||
* \param[out] buf Buffer to receive the data. | |||||
* \param[in] n Number of bytes to receive. | |||||
* | |||||
* \return Zero for no error or nonzero error code. | |||||
*/ | |||||
uint8_t receive(uint8_t* buf, size_t n); | |||||
/** Send a byte. | |||||
* | |||||
* \param[in] data Byte to send | |||||
*/ | |||||
void send(uint8_t data); | |||||
/** Send multiple bytes. | |||||
* | |||||
* \param[in] buf Buffer for data to be sent. | |||||
* \param[in] n Number of bytes to send. | |||||
*/ | |||||
void send(const uint8_t* buf, size_t n); | |||||
/** Set CS low. */ | |||||
void select() { | |||||
digitalWrite(m_csPin, LOW); | |||||
} | |||||
/** Save SPISettings. | |||||
* | |||||
* \param[in] spiSettings SPI speed, mode, and byte order. | |||||
*/ | |||||
void setSpiSettings(SPISettings spiSettings) { | |||||
m_spiSettings = spiSettings; | |||||
} | |||||
/** Set CS high. */ | |||||
void unselect() { | |||||
digitalWrite(m_csPin, HIGH); | |||||
} | |||||
#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) | |||||
/** Set SPI port number. | |||||
* \param[in] portNumber Hardware SPI port number. | |||||
*/ | |||||
void setPort(uint8_t portNumber); | |||||
private: | |||||
uint8_t m_spiPort; | |||||
#else // IMPLEMENT_SPI_PORT_SELECTION | |||||
private: | |||||
#endif // IMPLEMENT_SPI_PORT_SELECTION | |||||
SPISettings m_spiSettings; | |||||
uint8_t m_csPin; | |||||
}; | |||||
//------------------------------------------------------------------------------ | |||||
#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
#ifdef ARDUINO | |||||
#include "AltSpiDrivers/SoftSPI.h" | |||||
#elif defined(PLATFORM_ID) // Only defined if a Particle device | |||||
#include "SoftSPIParticle.h" | |||||
#endif // ARDUINO | |||||
/** | |||||
* \class SdSpiSoftDriver | |||||
* \brief Software SPI class for access to SD and SDHC flash memory cards. | |||||
*/ | |||||
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||||
class SdSpiSoftDriver : public SdSpiBaseDriver { | |||||
public: | |||||
/** Dummy activate SPI hardware for software SPI */ | |||||
void activate() {} | |||||
/** Dummy deactivate SPI hardware for software SPI */ | |||||
void deactivate() {} | |||||
/** Initialize the SPI bus. | |||||
* | |||||
* \param[in] csPin SD card chip select pin. | |||||
*/ | |||||
void begin(uint8_t csPin) { | |||||
m_csPin = csPin; | |||||
pinMode(m_csPin, OUTPUT); | |||||
digitalWrite(m_csPin, HIGH); | |||||
m_spi.begin(); | |||||
} | |||||
/** Receive a byte. | |||||
* | |||||
* \return The byte. | |||||
*/ | |||||
uint8_t receive() { | |||||
return m_spi.receive(); | |||||
} | |||||
/** Receive multiple bytes. | |||||
* | |||||
* \param[out] buf Buffer to receive the data. | |||||
* \param[in] n Number of bytes to receive. | |||||
* | |||||
* \return Zero for no error or nonzero error code. | |||||
*/ | |||||
uint8_t receive(uint8_t* buf, size_t n) { | |||||
for (size_t i = 0; i < n; i++) { | |||||
buf[i] = receive(); | |||||
} | |||||
return 0; | |||||
} | |||||
/** Send a byte. | |||||
* | |||||
* \param[in] data Byte to send | |||||
*/ | |||||
void send(uint8_t data) { | |||||
m_spi.send(data); | |||||
} | |||||
/** Send multiple bytes. | |||||
* | |||||
* \param[in] buf Buffer for data to be sent. | |||||
* \param[in] n Number of bytes to send. | |||||
*/ | |||||
void send(const uint8_t* buf , size_t n) { | |||||
for (size_t i = 0; i < n; i++) { | |||||
send(buf[i]); | |||||
} | |||||
} | |||||
/** Set CS low. */ | |||||
void select() { | |||||
digitalWrite(m_csPin, LOW); | |||||
} | |||||
/** Save SPISettings. | |||||
* | |||||
* \param[in] spiSettings SPI speed, mode, and byte order. | |||||
*/ | |||||
void setSpiSettings(SPISettings spiSettings) { | |||||
(void)spiSettings; | |||||
} | |||||
/** Set CS high. */ | |||||
void unselect() { | |||||
digitalWrite(m_csPin, HIGH); | |||||
} | |||||
private: | |||||
uint8_t m_csPin; | |||||
SoftSPI<MisoPin, MosiPin, SckPin, 0> m_spi; | |||||
}; | |||||
#endif // ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||||
//----------------------------------------------------------------------------- | |||||
// Choose SPI driver for SdFat and SdFatEX classes. | |||||
#if USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI | |||||
/** SdFat uses Arduino library SPI. */ | |||||
typedef SdSpiLibDriver SdFatSpiDriver; | |||||
#else // USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI | |||||
/** SdFat uses custom fast SPI. */ | |||||
typedef SdSpiAltDriver SdFatSpiDriver; | |||||
#endif // USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI | |||||
/** typedef for for SdSpiCard class. */ | |||||
#if ENABLE_SOFTWARE_SPI_CLASS | |||||
// Need virtual driver. | |||||
typedef SdSpiBaseDriver SdSpiDriver; | |||||
#else // ENABLE_SOFTWARE_SPI_CLASS | |||||
// Don't need virtual driver. | |||||
typedef SdFatSpiDriver SdSpiDriver; | |||||
#endif // ENABLE_SOFTWARE_SPI_CLASS | |||||
//============================================================================= | |||||
// Use of in-line for AVR to save flash. | |||||
#ifdef __AVR__ | |||||
//------------------------------------------------------------------------------ | |||||
inline void SdSpiAltDriver::begin(uint8_t csPin) { | |||||
m_csPin = csPin; | |||||
pinMode(m_csPin, OUTPUT); | |||||
digitalWrite(m_csPin, HIGH); | |||||
SPI.begin(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline void SdSpiAltDriver::activate() { | |||||
SPI.beginTransaction(m_spiSettings); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline void SdSpiAltDriver::deactivate() { | |||||
SPI.endTransaction(); | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline uint8_t SdSpiAltDriver::receive() { | |||||
SPDR = 0XFF; | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
return SPDR; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||||
if (n-- == 0) { | |||||
return 0; | |||||
} | |||||
SPDR = 0XFF; | |||||
for (size_t i = 0; i < n; i++) { | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
uint8_t b = SPDR; | |||||
SPDR = 0XFF; | |||||
buf[i] = b; | |||||
} | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
buf[n] = SPDR; | |||||
return 0; | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline void SdSpiAltDriver::send(uint8_t data) { | |||||
SPDR = data; | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
} | |||||
//------------------------------------------------------------------------------ | |||||
inline void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||||
if (n == 0) { | |||||
return; | |||||
} | |||||
SPDR = buf[0]; | |||||
if (n > 1) { | |||||
uint8_t b = buf[1]; | |||||
size_t i = 2; | |||||
while (1) { | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
SPDR = b; | |||||
if (i == n) { | |||||
break; | |||||
} | |||||
b = buf[i++]; | |||||
} | |||||
} | |||||
while (!(SPSR & (1 << SPIF))) {} | |||||
} | |||||
#endif // __AVR__ | |||||
#endif // SdSpiDriver_h |
#else // defined(ARDUINO) | #else // defined(ARDUINO) | ||||
#error "Unknown system" | #error "Unknown system" | ||||
#endif // defined(ARDUINO) | #endif // defined(ARDUINO) | ||||
//----------------------------------------------------------------------------- | |||||
#ifdef ESP8266 | |||||
// undefine F macro if ESP8266. | |||||
#undef F | |||||
#endif // ESP8266 | |||||
//----------------------------------------------------------------------------- | |||||
#ifndef F | #ifndef F | ||||
/** Define macro for strings stored in flash. */ | /** Define macro for strings stored in flash. */ | ||||
#define F(str) (str) | #define F(str) (str) | ||||
#endif // F | #endif // F | ||||
//----------------------------------------------------------------------------- | |||||
/** \return the time in milliseconds. */ | |||||
inline uint16_t curTimeMS() { | |||||
return millis(); | |||||
} | |||||
//----------------------------------------------------------------------------- | |||||
/** | /** | ||||
* \class SysCall | * \class SysCall | ||||
* \brief SysCall - Class to wrap system calls. | * \brief SysCall - Class to wrap system calls. | ||||
static void yield(); | static void yield(); | ||||
}; | }; | ||||
#if defined(ARDUINO) | |||||
#if defined(ESP8266) | |||||
inline void SysCall::yield() { | |||||
// Avoid ESP8266 bug | |||||
delay(0); | |||||
} | |||||
#elif defined(ARDUINO) | |||||
inline void SysCall::yield() { | inline void SysCall::yield() { | ||||
// Use the external Arduino yield() function. | // Use the external Arduino yield() function. | ||||
::yield(); | ::yield(); | ||||
inline void SysCall::yield() { | inline void SysCall::yield() { | ||||
Particle.process(); | Particle.process(); | ||||
} | } | ||||
#else // defined(ARDUINO) | |||||
#else // ESP8266 | |||||
inline void SysCall::yield() {} | inline void SysCall::yield() {} | ||||
#endif // defined(ARDUINO) | |||||
#endif // ESP8266 | |||||
#endif // SysCall_h | #endif // SysCall_h |
/* Arduino SdFat Library | |||||
* Copyright (C) 2016 by William Greiman | |||||
* | |||||
* This file is part of the Arduino SdFat Library | |||||
* | |||||
* This Library is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This Library is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with the Arduino SdFat Library. If not, see | |||||
* <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef SystemInclude_h | |||||
#define SystemInclude_h | |||||
#if defined(ARDUINO) | |||||
#include "FatLib/SysCall.h" | |||||
#elif defined(PLATFORM_ID) // Only defined if a Particle device | |||||
#include "SysCall.h" | |||||
#else // System type | |||||
#error Unknown System. | |||||
#endif // System type | |||||
#endif // SystemInclude_h |