@@ -1,8 +0,0 @@ | |||
#ifndef UserDataType_h | |||
#define UserDataType_h | |||
const uint8_t ACCEL_DIM = 3; | |||
struct data_t { | |||
unsigned long time; | |||
int16_t accel[ACCEL_DIM]; | |||
}; | |||
#endif // UserDataType_h |
@@ -1,8 +1,22 @@ | |||
This is a beta version so there may be bugs and compatibility | |||
problems. | |||
### Warning: This version has major changes so it may be unstable. | |||
Please report problems to the email address listed in the | |||
"Bugs and Comments" section of the html documentation. | |||
Recent versions of the Arduino IDE have bugs that may cause SdFat-beta to crash. | |||
https://forum.arduino.cc/index.php?topic=419264.0 | |||
SdFat-beta was tested using with Arduino AVR boards 1.6.11. | |||
If you are using IDE 1.6.11 you must also install AVR boards 1.6.11, not | |||
1.6.12 or 1.6.13. | |||
If you are Arduino IDE 1.6.11, do Tools > Board > Boards Manager > Arduino AVR Boards > 1.6.11 > Install > Close. | |||
The SPI divisor has been replaced by SPISettings. | |||
There are two new classes, SdFatEX and SdFatSoftSpiEX. | |||
Please read changes.txt and the html documentation for more information. | |||
Please report problems as issues. | |||
The Arduino SdFat library provides read/write access to FAT16/FAT32 | |||
file systems on SD/SDHC flash cards. | |||
@@ -23,9 +37,9 @@ Read changes.txt if you have used previous releases of this library. | |||
Please read the html documentation for this library. Start with | |||
html/index.html and read the Main Page. Next go to the Classes tab and | |||
read the documentation for the classes SdFat, SdBaseFile, SdFile, File, | |||
StdioStream, ifstream, ofstream, and others. | |||
read the documentation for the classes SdFat, SdFatEX, SdBaseFile, | |||
SdFile, File, StdioStream, ifstream, ofstream, and others. | |||
Please continue by reading the html documentation. | |||
Updated 23 Jan 2016 | |||
Updated 19 Aug 2016 |
@@ -34,15 +34,17 @@ nonzero in SdFatConfig.h. | |||
The %SdFat library supports Long %File Names or short 8.3 names. | |||
Edit the SdFatConfig.h file to select short or long file names. | |||
The main classes in %SdFat are SdFat, SdFatSoftSpi, SdFatLibSpi, | |||
The main classes in %SdFat are SdFat, SdFatEX, SdFatSoftSpi, SdFatSoftSpiEX, | |||
SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream, | |||
and \ref ofstream. | |||
The SdFat, SdFatLibSpi, and SdFatSoftSpi classes maintain a FAT volume, | |||
a current working directory, and simplifies initialization of other classes. | |||
The SdFat class uses a fast custom hardware SPI implementation. The | |||
SdFatLibSpi class uses the standard Arduino SPI library. The SdFatSoftSpi | |||
class uses software SPI. | |||
The SdFat, SdFatEX, SdFatSoftSpi and SdFatSoftSpiEX classes maintain a | |||
FAT volume, a current working directory, and simplify initialization | |||
of other classes. The SdFat and SdFatEX classes uses a fast custom hardware SPI | |||
implementation. The SdFatSoftSpi and SdFatSoftSpiEX classes uses software SPI. | |||
the SdFatEX and SdFatSoftSpiEX use extended multi-block I/O for enhanced | |||
performance. These classes must have exclusive use of the SPI bus. | |||
The SdBaseFile class provides basic file access functions such as open(), | |||
binary read(), binary write(), close(), remove(), and sync(). SdBaseFile | |||
@@ -102,19 +104,22 @@ Long %File Names. Long %File names require extra flash but no extra RAM. | |||
Opening Long %File Names can be slower than opening Short %File Names. | |||
Data read and write performance is not changed by the type of %File Name. | |||
Set SD_SPI_CONFIGURATION to enable various SPI options. The SdFatSoftSpi | |||
and SdFatLibSpi classes can be enabled. SdFatLibSpi uses the standard | |||
Arduino SPI library and SdFatSoftSpi uses software SPI. | |||
If the symbol ENABLE_EXTENDED_TRANSFER_CLASS is nonzero, the class SdFatEX | |||
will be defined. If the symbol ENABLE_SOFTWARE_SPI_CLASS is also nonzero, | |||
the class SdFatSoftSpiEX will be defined. | |||
These classes used extended multi-block SD I/O for better performance. | |||
the SPI bus may not be shared with other devices in this mode. | |||
Set USE_STANDARD_SPI_LIBRARY and ENABLE_SOFTWARE_SPI_CLASS to | |||
enable various SPI options. set USE_STANDARD_SPI_LIBRARY to use the standard | |||
Arduino SPI library. set ENABLE_SOFTWARE_SPI_CLASS to enable the SdFatSoftSpi | |||
class which uses software SPI. | |||
To enable SD card CRC checking set USE_SD_CRC nonzero. | |||
Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. | |||
FAT12 has not been well tested and requires additional flash. | |||
Set ENABLE_SPI_TRANSACTIONS nonzero to enable the SPI transaction feature | |||
of the standard Arduino SPI library. You must include SPI.h in your | |||
programs when ENABLE_SPI_TRANSACTIONS is nonzero. | |||
\section SDPath Paths and Working Directories | |||
Relative paths in SdFat are resolved in a manner similar to Windows. | |||
@@ -352,7 +357,11 @@ getline - Example of getline from section 27.7.1.3 of the C++ standard. | |||
LongFileName - Example use of openNext, printName, and open by index. | |||
LowLatencyLogger - A modifiable data logger for higher data rates. | |||
LowLatencyLogger - A data logger for higher data rates. ADC version. | |||
LowLatencyLoggerADXL345 - A data logger for higher data rates. ADXL345 SPI. | |||
LowLatencyLoggerMPU6050 - A data logger for higher data rates. MPU6050 I2C. | |||
OpenNext - Open all files in the root dir and print their filename. | |||
@@ -362,13 +371,13 @@ QuickStart - A program to quickly test your SD card and SD shield/module. | |||
RawWrite - A test of raw write functions for contiguous files. | |||
readCSV - Read a comma-separated value file using iostream extractors. | |||
ReadCsv - Function to read a CSV text file one field at a time. | |||
ReadCsvArray - Read a two dimensional array from a CSV file. | |||
ReadCsvStream - Read a comma-separated value file using iostream extractors. | |||
ReadCsvFields - Function to read a CSV text file one field at a time. | |||
ReadCsvArray - Read a two dimensional array from a CSV file. | |||
ReadWriteSdFat - SdFat version of Arduino SD ReadWrite example. | |||
ReadWrite - Compatibility test of Arduino SD ReadWrite example. | |||
rename - A demo of SdFat::rename(old, new) and SdFile::rename(dirFile, newPath). | |||
@@ -380,10 +389,6 @@ SdInfo - Initialize an SD card and analyze its structure for trouble shooting. | |||
StdioBench - Demo and test of stdio style stream. | |||
StreamParseInt - Demo of the SD.h API and the File class parseInt() function. | |||
ThreeCards - Demonstrate simultaneous use of SdFat, SdFatLibSpi, SdFatSoftSpi. | |||
Timestamp - Sets file create, modify, and access timestamps. | |||
TwoCards - Example using two SD cards. |
@@ -97,8 +97,9 @@ void setup() { | |||
cout << now << endl; | |||
#endif // USE_DS1307 | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -4,6 +4,8 @@ | |||
#include <SPI.h> | |||
#include "SdFat.h" | |||
const uint8_t chipSelect = SS; | |||
SdFat sd; | |||
SdFile file; | |||
@@ -24,7 +26,7 @@ void setup() { | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
if (!sd.begin()) { | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
Serial.println("begin failed"); | |||
return; | |||
} |
@@ -5,7 +5,7 @@ | |||
SdFat SD; | |||
// SD card chip select pin - Modify the value of csPin for your SD module. | |||
const uint8_t csPin = 10; | |||
const uint8_t csPin = SS; | |||
File file; | |||
//------------------------------------------------------------------------------ |
@@ -36,9 +36,9 @@ void setup() { | |||
SysCall::yield(); | |||
} | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -65,9 +65,9 @@ void setup() { | |||
SysCall::yield(); | |||
} | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -47,9 +47,9 @@ void setup() { | |||
} | |||
delay(400); // catch Due reset problem | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -95,9 +95,9 @@ void setup() { | |||
SysCall::yield(); | |||
} | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -22,9 +22,9 @@ void setup() { | |||
while (!Serial) { | |||
SysCall::yield(); | |||
} | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -256,12 +256,7 @@ ISR(TIMER1_COMPB_vect) { | |||
} | |||
//============================================================================== | |||
// Error messages stored in flash. | |||
#define error(msg) errorFlash(F(msg)) | |||
//------------------------------------------------------------------------------ | |||
void errorFlash(const __FlashStringHelper* msg) { | |||
sd.errorPrint(msg); | |||
fatalBlink(); | |||
} | |||
#define error(msg) {sd.errorPrint(F(msg));fatalBlink();} | |||
//------------------------------------------------------------------------------ | |||
// | |||
void fatalBlink() { | |||
@@ -625,8 +620,7 @@ void logData() { | |||
// Create new file. | |||
Serial.println(F("Creating new file")); | |||
binFile.close(); | |||
if (!binFile.createContiguous(sd.vwd(), | |||
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
error("createContiguous failed"); | |||
} | |||
// Get the address of the file on the SD. | |||
@@ -783,8 +777,9 @@ void setup(void) { | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
// initialize file system. | |||
if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||
sd.initErrorPrint(); | |||
fatalBlink(); | |||
} |
@@ -31,7 +31,9 @@ void setup() { | |||
"You can use test files located in\r\n" | |||
"SdFat/examples/LongFileName/testFiles")); | |||
if (!sd.begin(SD_CS_PIN)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
Serial.print(F("FreeStack: ")); |
@@ -9,65 +9,47 @@ | |||
* | |||
* If your SD card has a long write latency, it may be necessary to use | |||
* slower sample rates. Using a Mega Arduino helps overcome latency | |||
* problems since 13 512 byte buffers will be used. | |||
* problems since 12 512 byte buffers will be used. | |||
* | |||
* Data is written to the file using a SD multiple block write command. | |||
*/ | |||
#include <SPI.h> | |||
#include "SdFat.h" | |||
#include "FreeStack.h" | |||
#include "UserDataType.h" // Edit this include file to change data_t. | |||
//------------------------------------------------------------------------------ | |||
// Set useSharedSpi true for use of an SPI sensor. | |||
const bool useSharedSpi = false; | |||
// File start time in micros. | |||
uint32_t startMicros; | |||
//------------------------------------------------------------------------------ | |||
// User data functions. Modify these functions for your data items. | |||
// Acquire a data record. | |||
void acquireData(data_t* data) { | |||
data->time = micros(); | |||
for (int i = 0; i < ADC_DIM; i++) { | |||
data->adc[i] = analogRead(i); | |||
} | |||
} | |||
// Print a data record. | |||
void printData(Print* pr, data_t* data) { | |||
pr->print(data->time - startMicros); | |||
for (int i = 0; i < ADC_DIM; i++) { | |||
pr->write(','); | |||
pr->print(data->adc[i]); | |||
} | |||
pr->println(); | |||
} | |||
#include "UserTypes.h" | |||
// Print data header. | |||
void printHeader(Print* pr) { | |||
pr->print(F("time")); | |||
for (int i = 0; i < ADC_DIM; i++) { | |||
pr->print(F(",adc")); | |||
pr->print(i); | |||
} | |||
pr->println(); | |||
} | |||
#ifdef __AVR_ATmega328P__ | |||
#include "MinimumSerial.h" | |||
MinimumSerial MinSerial; | |||
#define Serial MinSerial | |||
#endif // __AVR_ATmega328P__ | |||
//============================================================================== | |||
// Start of configuration constants. | |||
//============================================================================== | |||
// Abort run on an overrun. Data before the overrun will be saved. | |||
#define ABORT_ON_OVERRUN 1 | |||
//------------------------------------------------------------------------------ | |||
//Interval between data records in microseconds. | |||
const uint32_t LOG_INTERVAL_USEC = 2000; | |||
//------------------------------------------------------------------------------ | |||
// Set USE_SHARED_SPI non-zero for use of an SPI sensor. | |||
// May not work for some cards. | |||
#ifndef USE_SHARED_SPI | |||
#define USE_SHARED_SPI 0 | |||
#endif // USE_SHARED_SPI | |||
//------------------------------------------------------------------------------ | |||
// Pin definitions. | |||
// | |||
// SD chip select pin. | |||
const uint8_t SD_CS_PIN = SS; | |||
// | |||
// Digital pin to indicate an error, set to -1 if not used. | |||
// The led blinks for fatal errors. The led goes on solid for SD write | |||
// overrun errors and logging continues. | |||
// The led blinks for fatal errors. The led goes on solid for | |||
// overrun errors and logging continues unless ABORT_ON_OVERRUN | |||
// is non-zero. | |||
#ifdef ERROR_LED_PIN | |||
#undef ERROR_LED_PIN | |||
#endif // ERROR_LED_PIN | |||
const int8_t ERROR_LED_PIN = -1; | |||
//------------------------------------------------------------------------------ | |||
// File definitions. | |||
@@ -77,32 +59,34 @@ const int8_t ERROR_LED_PIN = -1; | |||
// This file is flash erased using special SD commands. The file will be | |||
// truncated if logging is stopped early. | |||
const uint32_t FILE_BLOCK_COUNT = 256000; | |||
// log file base name. Must be six characters or less. | |||
// | |||
// log file base name if not defined in UserTypes.h | |||
#ifndef FILE_BASE_NAME | |||
#define FILE_BASE_NAME "data" | |||
#endif // FILE_BASE_NAME | |||
//------------------------------------------------------------------------------ | |||
// Buffer definitions. | |||
// | |||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional | |||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional | |||
// buffers. | |||
// | |||
#ifndef RAMEND | |||
// Assume ARM. Use total of nine 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 8; | |||
// Assume ARM. Use total of ten 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 10; | |||
// | |||
#elif RAMEND < 0X8FF | |||
#error Too little SRAM | |||
// | |||
#elif RAMEND < 0X10FF | |||
// Use total of two 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 1; | |||
const uint8_t BUFFER_BLOCK_COUNT = 2; | |||
// | |||
#elif RAMEND < 0X20FF | |||
// Use total of five 512 byte buffers. | |||
// Use total of four 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 4; | |||
// | |||
#else // RAMEND | |||
// Use total of 13 512 byte buffers. | |||
// Use total of 12 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
#endif // RAMEND | |||
//============================================================================== | |||
@@ -113,13 +97,13 @@ const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
// Size of file base name. Must not be larger than six. | |||
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | |||
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7; | |||
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin"; | |||
SdFat sd; | |||
SdBaseFile binFile; | |||
char binName[13] = FILE_BASE_NAME "00.bin"; | |||
// Number of data records in a block. | |||
const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | |||
@@ -132,33 +116,14 @@ struct block_t { | |||
data_t data[DATA_DIM]; | |||
uint8_t fill[FILL_DIM]; | |||
}; | |||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 2; | |||
block_t* emptyQueue[QUEUE_DIM]; | |||
uint8_t emptyHead; | |||
uint8_t emptyTail; | |||
block_t* fullQueue[QUEUE_DIM]; | |||
uint8_t fullHead; | |||
uint8_t fullTail; | |||
// Advance queue index. | |||
inline uint8_t queueNext(uint8_t ht) { | |||
return ht < (QUEUE_DIM - 1) ? ht + 1 : 0; | |||
} | |||
//============================================================================== | |||
// Error messages stored in flash. | |||
#define error(msg) errorFlash(F(msg)) | |||
//------------------------------------------------------------------------------ | |||
void errorFlash(const __FlashStringHelper* msg) { | |||
sd.errorPrint(msg); | |||
fatalBlink(); | |||
} | |||
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();} | |||
//------------------------------------------------------------------------------ | |||
// | |||
void fatalBlink() { | |||
while (true) { | |||
SysCall::yield(); | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, HIGH); | |||
delay(200); | |||
@@ -167,7 +132,49 @@ void fatalBlink() { | |||
} | |||
} | |||
} | |||
//============================================================================== | |||
//------------------------------------------------------------------------------ | |||
// read data file and check for overruns | |||
void checkOverrun() { | |||
bool headerPrinted = false; | |||
block_t block; | |||
uint32_t bn = 0; | |||
if (!binFile.isOpen()) { | |||
Serial.println(); | |||
Serial.println(F("No current binary file")); | |||
return; | |||
} | |||
binFile.rewind(); | |||
Serial.println(); | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
Serial.println(F("Checking overrun errors - type any character to stop")); | |||
while (binFile.read(&block, 512) == 512) { | |||
if (block.count == 0) { | |||
break; | |||
} | |||
if (block.overrun) { | |||
if (!headerPrinted) { | |||
Serial.println(); | |||
Serial.println(F("Overruns:")); | |||
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||
headerPrinted = true; | |||
} | |||
Serial.print(bn); | |||
Serial.print(','); | |||
Serial.print(binFile.firstBlock() + bn); | |||
Serial.print(','); | |||
Serial.println(block.overrun); | |||
} | |||
bn++; | |||
} | |||
if (!headerPrinted) { | |||
Serial.println(F("No errors found")); | |||
} else { | |||
Serial.println(F("Done")); | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
// Convert binary file to csv file. | |||
void binaryToCsv() { | |||
uint8_t lastPct = 0; | |||
@@ -175,14 +182,17 @@ void binaryToCsv() { | |||
uint32_t t0 = millis(); | |||
uint32_t syncCluster = 0; | |||
SdFile csvFile; | |||
char csvName[13]; | |||
char csvName[FILE_NAME_DIM]; | |||
if (!binFile.isOpen()) { | |||
Serial.println(); | |||
Serial.println(F("No current binary file")); | |||
return; | |||
} | |||
binFile.rewind(); | |||
Serial.println(); | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
// Create a new csvFile. | |||
strcpy(csvName, binName); | |||
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||
@@ -190,7 +200,7 @@ void binaryToCsv() { | |||
if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | |||
error("open csvFile failed"); | |||
} | |||
Serial.println(); | |||
binFile.rewind(); | |||
Serial.print(F("Writing: ")); | |||
Serial.print(csvName); | |||
Serial.println(F(" - type any character to stop")); | |||
@@ -230,48 +240,55 @@ void binaryToCsv() { | |||
Serial.print(0.001*(millis() - t0)); | |||
Serial.println(F(" Seconds")); | |||
} | |||
//------------------------------------------------------------------------------ | |||
// read data file and check for overruns | |||
void checkOverrun() { | |||
bool headerPrinted = false; | |||
block_t block; | |||
//----------------------------------------------------------------------------- | |||
void createBinFile() { | |||
// max number of blocks to erase per erase call | |||
const uint32_t ERASE_SIZE = 262144L; | |||
uint32_t bgnBlock, endBlock; | |||
uint32_t bn = 0; | |||
if (!binFile.isOpen()) { | |||
Serial.println(); | |||
Serial.println(F("No current binary file")); | |||
return; | |||
Serial.println(); | |||
while (sd.exists(binName)) { | |||
if (binName[BASE_NAME_SIZE + 1] != '9') { | |||
binName[BASE_NAME_SIZE + 1]++; | |||
} else { | |||
binName[BASE_NAME_SIZE + 1] = '0'; | |||
if (binName[BASE_NAME_SIZE] == '9') { | |||
error("Can't create file name"); | |||
} | |||
binName[BASE_NAME_SIZE]++; | |||
} | |||
} | |||
// Delete old tmp file. | |||
if (sd.exists(TMP_FILE_NAME)) { | |||
Serial.println(F("Deleting tmp file")); | |||
if (!sd.remove(TMP_FILE_NAME)) { | |||
error("Can't remove tmp file"); | |||
} | |||
} | |||
// Create new file. | |||
Serial.println(F("Creating new file")); | |||
binFile.close(); | |||
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
error("createContiguous failed"); | |||
} | |||
// Get the address of the file on the SD. | |||
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||
error("contiguousRange failed"); | |||
} | |||
binFile.rewind(); | |||
Serial.println(); | |||
Serial.println(F("Checking overrun errors - type any character to stop")); | |||
while (binFile.read(&block, 512) == 512) { | |||
if (block.count == 0) { | |||
break; | |||
// Flash erase all data in the file. | |||
Serial.println(F("Erasing all data")); | |||
uint32_t bgnErase = bgnBlock; | |||
uint32_t endErase; | |||
while (bgnErase < endBlock) { | |||
endErase = bgnErase + ERASE_SIZE; | |||
if (endErase > endBlock) { | |||
endErase = endBlock; | |||
} | |||
if (block.overrun) { | |||
if (!headerPrinted) { | |||
Serial.println(); | |||
Serial.println(F("Overruns:")); | |||
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||
headerPrinted = true; | |||
} | |||
Serial.print(bn); | |||
Serial.print(','); | |||
Serial.print(bgnBlock + bn); | |||
Serial.print(','); | |||
Serial.println(block.overrun); | |||
if (!sd.card()->erase(bgnErase, endErase)) { | |||
error("erase failed"); | |||
} | |||
bn++; | |||
} | |||
if (!headerPrinted) { | |||
Serial.println(F("No errors found")); | |||
} else { | |||
Serial.println(F("Done")); | |||
bgnErase = endErase + 1; | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -304,127 +321,105 @@ void dumpData() { | |||
} | |||
//------------------------------------------------------------------------------ | |||
// log data | |||
// max number of blocks to erase per erase call | |||
uint32_t const ERASE_SIZE = 262144L; | |||
void logData() { | |||
uint32_t bgnBlock, endBlock; | |||
// Allocate extra buffer space. | |||
block_t block[BUFFER_BLOCK_COUNT]; | |||
block_t* curBlock = 0; | |||
Serial.println(); | |||
// Find unused file name. | |||
if (BASE_NAME_SIZE > 6) { | |||
error("FILE_BASE_NAME too long"); | |||
} | |||
while (sd.exists(binName)) { | |||
if (binName[BASE_NAME_SIZE + 1] != '9') { | |||
binName[BASE_NAME_SIZE + 1]++; | |||
} else { | |||
binName[BASE_NAME_SIZE + 1] = '0'; | |||
if (binName[BASE_NAME_SIZE] == '9') { | |||
error("Can't create file name"); | |||
} | |||
binName[BASE_NAME_SIZE]++; | |||
createBinFile(); | |||
recordBinFile(); | |||
renameBinFile(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void openBinFile() { | |||
char name[FILE_NAME_DIM]; | |||
strcpy(name, binName); | |||
Serial.println(F("\nEnter two digit version")); | |||
Serial.write(name, BASE_NAME_SIZE); | |||
for (int i = 0; i < 2; i++) { | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
} | |||
// Delete old tmp file. | |||
if (sd.exists(TMP_FILE_NAME)) { | |||
Serial.println(F("Deleting tmp file")); | |||
if (!sd.remove(TMP_FILE_NAME)) { | |||
error("Can't remove tmp file"); | |||
char c = Serial.read(); | |||
Serial.write(c); | |||
if (c < '0' || c > '9') { | |||
Serial.println("\nInvalid digit"); | |||
return; | |||
} | |||
name[BASE_NAME_SIZE + i] = c; | |||
} | |||
// Create new file. | |||
Serial.println(F("Creating new file")); | |||
binFile.close(); | |||
if (!binFile.createContiguous(sd.vwd(), | |||
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
error("createContiguous failed"); | |||
Serial.println(&name[BASE_NAME_SIZE+2]); | |||
if (!sd.exists(name)) { | |||
Serial.println(F("File does not exist")); | |||
return; | |||
} | |||
// Get the address of the file on the SD. | |||
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||
error("contiguousRange failed"); | |||
binFile.close(); | |||
strcpy(binName, name); | |||
if (!binFile.open(binName, O_READ)) { | |||
Serial.println(F("open failed")); | |||
return; | |||
} | |||
Serial.println(F("File opened")); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void recordBinFile() { | |||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||
// Index of last queue location. | |||
const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||
// Allocate extra buffer space. | |||
block_t block[BUFFER_BLOCK_COUNT - 1]; | |||
block_t* curBlock = 0; | |||
block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||
uint8_t emptyTop; | |||
uint8_t minTop; | |||
block_t* fullQueue[QUEUE_DIM]; | |||
uint8_t fullHead = 0; | |||
uint8_t fullTail = 0; | |||
// Use SdFat's internal buffer. | |||
uint8_t* cache = (uint8_t*)sd.vol()->cacheClear(); | |||
if (cache == 0) { | |||
emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||
if (emptyStack[0] == 0) { | |||
error("cacheClear failed"); | |||
} | |||
// Flash erase all data in the file. | |||
Serial.println(F("Erasing all data")); | |||
uint32_t bgnErase = bgnBlock; | |||
uint32_t endErase; | |||
while (bgnErase < endBlock) { | |||
endErase = bgnErase + ERASE_SIZE; | |||
if (endErase > endBlock) { | |||
endErase = endBlock; | |||
} | |||
if (!sd.card()->erase(bgnErase, endErase)) { | |||
error("erase failed"); | |||
} | |||
bgnErase = endErase + 1; | |||
// Put rest of buffers on the empty stack. | |||
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) { | |||
emptyStack[i] = &block[i - 1]; | |||
} | |||
emptyTop = BUFFER_BLOCK_COUNT; | |||
minTop = BUFFER_BLOCK_COUNT; | |||
// Start a multiple block write. | |||
if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) { | |||
error("writeBegin failed"); | |||
} | |||
// Set chip select high if other devices use SPI. | |||
if (useSharedSpi) { | |||
sd.card()->chipSelectHigh(); | |||
} | |||
// Initialize queues. | |||
emptyHead = emptyTail = 0; | |||
fullHead = fullTail = 0; | |||
// Use SdFat buffer for one block. | |||
emptyQueue[emptyHead] = (block_t*)cache; | |||
emptyHead = queueNext(emptyHead); | |||
// Put rest of buffers in the empty queue. | |||
for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { | |||
emptyQueue[emptyHead] = &block[i]; | |||
emptyHead = queueNext(emptyHead); | |||
if (!sd.card()->writeStart(binFile.firstBlock())) { | |||
error("writeBStart failed"); | |||
} | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
Serial.println(F("Logging - type any character to stop")); | |||
// Wait for Serial Idle. | |||
Serial.flush(); | |||
delay(10); | |||
bool closeFile = false; | |||
uint32_t bn = 0; | |||
uint32_t t0 = millis(); | |||
uint32_t t1 = t0; | |||
uint32_t bn = 0; | |||
uint32_t maxLatency = 0; | |||
uint32_t overrun = 0; | |||
uint32_t overrunTotal = 0; | |||
uint32_t count = 0; | |||
uint32_t maxDelta = 0; | |||
uint32_t minDelta = 99999; | |||
uint32_t maxLatency = 0; | |||
uint32_t logTime = micros(); | |||
// Set time for first record of file. | |||
startMicros = logTime + LOG_INTERVAL_USEC; | |||
while (1) { | |||
// Time for next data record. | |||
while(1) { | |||
// Time for next data record. | |||
logTime += LOG_INTERVAL_USEC; | |||
if (Serial.available()) { | |||
closeFile = true; | |||
} | |||
} | |||
if (closeFile) { | |||
if (curBlock != 0) { | |||
// Put buffer in full queue. | |||
fullQueue[fullHead] = curBlock; | |||
fullHead = queueNext(fullHead); | |||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
curBlock = 0; | |||
} | |||
} else { | |||
if (curBlock == 0 && emptyTail != emptyHead) { | |||
curBlock = emptyQueue[emptyTail]; | |||
emptyTail = queueNext(emptyTail); | |||
if (curBlock == 0 && emptyTop != 0) { | |||
curBlock = emptyStack[--emptyTop]; | |||
if (emptyTop < minTop) { | |||
minTop = emptyTop; | |||
} | |||
curBlock->count = 0; | |||
curBlock->overrun = overrun; | |||
overrun = 0; | |||
@@ -438,18 +433,29 @@ void logData() { | |||
} while (delta < 0); | |||
if (curBlock == 0) { | |||
overrun++; | |||
overrunTotal++; | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, HIGH); | |||
} | |||
#if ABORT_ON_OVERRUN | |||
Serial.println(F("Overrun abort")); | |||
break; | |||
#endif // ABORT_ON_OVERRUN | |||
} else { | |||
acquireData(&curBlock->data[curBlock->count++]); | |||
#if USE_SHARED_SPI | |||
sd.card()->spiStop(); | |||
#endif // USE_SHARED_SPI | |||
acquireData(&curBlock->data[curBlock->count++]); | |||
#if USE_SHARED_SPI | |||
sd.card()->spiStart(); | |||
#endif // USE_SHARED_SPI | |||
if (curBlock->count == DATA_DIM) { | |||
fullQueue[fullHead] = curBlock; | |||
fullHead = queueNext(fullHead); | |||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
curBlock = 0; | |||
} | |||
if ((uint32_t)delta > maxDelta) maxDelta = delta; | |||
if ((uint32_t)delta < minDelta) minDelta = delta; | |||
} | |||
} | |||
} | |||
if (fullHead == fullTail) { | |||
// Exit loop if done. | |||
if (closeFile) { | |||
@@ -458,29 +464,18 @@ void logData() { | |||
} else if (!sd.card()->isBusy()) { | |||
// Get address of block to write. | |||
block_t* pBlock = fullQueue[fullTail]; | |||
fullTail = queueNext(fullTail); | |||
fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0; | |||
// Write block to SD. | |||
uint32_t usec = micros(); | |||
if (!sd.card()->writeData((uint8_t*)pBlock)) { | |||
error("write data failed"); | |||
} | |||
usec = micros() - usec; | |||
t1 = millis(); | |||
if (usec > maxLatency) { | |||
maxLatency = usec; | |||
} | |||
count += pBlock->count; | |||
// Add overruns and possibly light LED. | |||
if (pBlock->overrun) { | |||
overrunTotal += pBlock->overrun; | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, HIGH); | |||
} | |||
} | |||
// Move block to empty queue. | |||
emptyQueue[emptyHead] = pBlock; | |||
emptyHead = queueNext(emptyHead); | |||
emptyStack[emptyTop++] = pBlock; | |||
bn++; | |||
if (bn == FILE_BLOCK_COUNT) { | |||
// File full so stop | |||
@@ -491,6 +486,12 @@ void logData() { | |||
if (!sd.card()->writeStop()) { | |||
error("writeStop failed"); | |||
} | |||
Serial.print(F("Min Free buffers: ")); | |||
Serial.println(minTop); | |||
Serial.print(F("Max block write usec: ")); | |||
Serial.println(maxLatency); | |||
Serial.print(F("Overruns: ")); | |||
Serial.println(overrunTotal); | |||
// Truncate file if recording stopped early. | |||
if (bn != FILE_BLOCK_COUNT) { | |||
Serial.println(F("Truncating file")); | |||
@@ -498,25 +499,38 @@ void logData() { | |||
error("Can't truncate file"); | |||
} | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
void renameBinFile() { | |||
if (!binFile.rename(sd.vwd(), binName)) { | |||
error("Can't rename file"); | |||
} | |||
} | |||
Serial.print(F("File renamed: ")); | |||
Serial.println(binName); | |||
Serial.print(F("Max block write usec: ")); | |||
Serial.println(maxLatency); | |||
Serial.print(F("Record time sec: ")); | |||
Serial.println(0.001*(t1 - t0), 3); | |||
Serial.print(minDelta); | |||
Serial.print(F(" <= jitter microseconds <= ")); | |||
Serial.println(maxDelta); | |||
Serial.print(F("Sample count: ")); | |||
Serial.println(count); | |||
Serial.print(F("Samples/sec: ")); | |||
Serial.println((1000.0)*count/(t1-t0)); | |||
Serial.print(F("Overruns: ")); | |||
Serial.println(overrunTotal); | |||
Serial.println(F("Done")); | |||
Serial.print("File size: "); | |||
Serial.print(binFile.fileSize()/512); | |||
Serial.println(F(" blocks")); | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void testSensor() { | |||
const uint32_t interval = 200000; | |||
int32_t diff; | |||
data_t data; | |||
Serial.println(F("\nTesting - type any character to stop\n")); | |||
// Wait for Serial Idle. | |||
delay(1000); | |||
printHeader(&Serial); | |||
uint32_t m = micros(); | |||
while (!Serial.available()) { | |||
m += interval; | |||
do { | |||
diff = m - micros(); | |||
} while (diff > 0); | |||
acquireData(&data); | |||
printData(&Serial, &data); | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
void setup(void) { | |||
@@ -529,7 +543,6 @@ void setup(void) { | |||
while (!Serial) { | |||
SysCall::yield(); | |||
} | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
Serial.print(F("Records/block: ")); | |||
@@ -537,11 +550,13 @@ void setup(void) { | |||
if (sizeof(block_t) != 512) { | |||
error("Invalid block size"); | |||
} | |||
// initialize file system. | |||
if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||
sd.initErrorPrint(); | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||
sd.initErrorPrint(&Serial); | |||
fatalBlink(); | |||
} | |||
userSetup(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void loop(void) { | |||
@@ -551,14 +566,21 @@ void loop(void) { | |||
} while (Serial.available() && Serial.read() >= 0); | |||
Serial.println(); | |||
Serial.println(F("type:")); | |||
Serial.println(F("b - open existing bin file")); | |||
Serial.println(F("c - convert file to csv")); | |||
Serial.println(F("d - dump data to Serial")); | |||
Serial.println(F("e - overrun error details")); | |||
Serial.println(F("l - list files")); | |||
Serial.println(F("r - record data")); | |||
Serial.println(F("t - test without logging")); | |||
while(!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
#if WDT_YIELD_TIME_MICROS | |||
Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||
SysCall::halt(); | |||
#endif | |||
char c = tolower(Serial.read()); | |||
// Discard extra Serial data. | |||
@@ -569,14 +591,21 @@ void loop(void) { | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, LOW); | |||
} | |||
if (c == 'c') { | |||
if (c == 'b') { | |||
openBinFile(); | |||
} else if (c == 'c') { | |||
binaryToCsv(); | |||
} else if (c == 'd') { | |||
dumpData(); | |||
} else if (c == 'e') { | |||
checkOverrun(); | |||
} else if (c == 'l') { | |||
Serial.println(F("\nls:")); | |||
sd.ls(&Serial, LS_SIZE); | |||
} else if (c == 'r') { | |||
logData(); | |||
} else if (c == 't') { | |||
testSensor(); | |||
} else { | |||
Serial.println(F("Invalid entry")); | |||
} |
@@ -1,8 +0,0 @@ | |||
#ifndef UserDataType_h | |||
#define UserDataType_h | |||
const uint8_t ADC_DIM = 4; | |||
struct data_t { | |||
unsigned long time; | |||
unsigned short adc[ADC_DIM]; | |||
}; | |||
#endif // UserDataType_h |
@@ -0,0 +1,41 @@ | |||
#include "UserTypes.h" | |||
// User data functions. Modify these functions for your data items. | |||
// Start time for data | |||
uint32_t startTime; | |||
// Acquire a data record. | |||
void acquireData(data_t* data) { | |||
data->time = micros(); | |||
for (int i = 0; i < ADC_DIM; i++) { | |||
data->adc[i] = analogRead(i); | |||
} | |||
} | |||
// Print a data record. | |||
void printData(Print* pr, data_t* data) { | |||
if (startTime == 0) { | |||
startTime = data->time; | |||
} | |||
pr->print(data->time - startTime); | |||
for (int i = 0; i < ADC_DIM; i++) { | |||
pr->write(','); | |||
pr->print(data->adc[i]); | |||
} | |||
pr->println(); | |||
} | |||
// Print data header. | |||
void printHeader(Print* pr) { | |||
startTime = 0; | |||
pr->print(F("micros")); | |||
for (int i = 0; i < ADC_DIM; i++) { | |||
pr->print(F(",adc")); | |||
pr->print(i); | |||
} | |||
pr->println(); | |||
} | |||
// Sensor setup | |||
void userSetup() { | |||
} |
@@ -0,0 +1,15 @@ | |||
#ifndef UserTypes_h | |||
#define UserTypes_h | |||
#include "Arduino.h" | |||
// User data types. Modify for your data items. | |||
#define FILE_BASE_NAME "adc4pin" | |||
const uint8_t ADC_DIM = 4; | |||
struct data_t { | |||
unsigned long time; | |||
unsigned short adc[ADC_DIM]; | |||
}; | |||
void acquireData(data_t* data); | |||
void printData(Print* pr, data_t* data); | |||
void printHeader(Print* pr); | |||
void userSetup(); | |||
#endif // UserTypes_h |
@@ -9,84 +9,34 @@ | |||
* | |||
* If your SD card has a long write latency, it may be necessary to use | |||
* slower sample rates. Using a Mega Arduino helps overcome latency | |||
* problems since 13 512 byte buffers will be used. | |||
* problems since 12 512 byte buffers will be used. | |||
* | |||
* Data is written to the file using a SD multiple block write command. | |||
*/ | |||
#include <SPI.h> | |||
#include "SdFat.h" | |||
#include "FreeStack.h" | |||
//------------------------------------------------------------------------------ | |||
// Set useSharedSpi true for use of an SPI sensor. | |||
const bool useSharedSpi = true; | |||
// File start time in micros. | |||
uint32_t startMicros; | |||
//------------------------------------------------------------------------------ | |||
// User data functions. Modify these functions for your data items. | |||
#include "UserDataType.h" // Edit this include file to change data_t. | |||
const uint8_t ADXL345_CS = 9; | |||
const uint8_t POWER_CTL = 0x2D; //Power Control Register | |||
const uint8_t DATA_FORMAT = 0x31; | |||
const uint8_t DATAX0 = 0x32; //X-Axis Data 0 | |||
const uint8_t DATAX1 = 0x33; //X-Axis Data 1 | |||
const uint8_t DATAY0 = 0x34; //Y-Axis Data 0 | |||
const uint8_t DATAY1 = 0x35; //Y-Axis Data 1 | |||
const uint8_t DATAZ0 = 0x36; //Z-Axis Data 0 | |||
const uint8_t DATAZ1 = 0x37; //Z-Axis Data 1 | |||
void writeADXL345Register(const uint8_t registerAddress, const uint8_t value) { | |||
SPI.setDataMode(SPI_MODE3); | |||
digitalWrite(ADXL345_CS, LOW); | |||
SPI.transfer(registerAddress); | |||
SPI.transfer(value); | |||
digitalWrite(ADXL345_CS, HIGH); | |||
} | |||
void setupADXL345() { | |||
SPI.begin(); | |||
pinMode(ADXL345_CS, OUTPUT); | |||
digitalWrite(ADXL345_CS, HIGH); | |||
//Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register. | |||
writeADXL345Register(DATA_FORMAT, 0x01); | |||
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register. | |||
writeADXL345Register(POWER_CTL, 0x08); //Measurement mode | |||
} | |||
// Acquire a data record. | |||
void acquireData(data_t* data) { | |||
data->time = micros(); | |||
SPI.setDataMode(SPI_MODE3); | |||
digitalWrite(ADXL345_CS, LOW); | |||
// Read multiple bytes so or 0XC0 with address. | |||
SPI.transfer(DATAX0 | 0XC0); | |||
data->accel[0] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||
data->accel[1] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||
data->accel[2] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||
digitalWrite(ADXL345_CS, HIGH); | |||
} | |||
// Print a data record. | |||
void printData(Print* pr, data_t* data) { | |||
pr->print(data->time - startMicros); | |||
for (int i = 0; i < ACCEL_DIM; i++) { | |||
pr->write(','); | |||
pr->print(data->accel[i]); | |||
} | |||
pr->println(); | |||
} | |||
#include "UserTypes.h" | |||
// Print data header. | |||
void printHeader(Print* pr) { | |||
pr->println(F("time,ax,ay,az")); | |||
} | |||
#ifdef __AVR_ATmega328P__ | |||
#include "MinimumSerial.h" | |||
MinimumSerial MinSerial; | |||
#define Serial MinSerial | |||
#endif // __AVR_ATmega328P__ | |||
//============================================================================== | |||
// Start of configuration constants. | |||
//============================================================================== | |||
// Abort run on an overrun. Data before the overrun will be saved. | |||
#define ABORT_ON_OVERRUN 1 | |||
//------------------------------------------------------------------------------ | |||
//Interval between data records in microseconds. | |||
const uint32_t LOG_INTERVAL_USEC = 10000; | |||
const uint32_t LOG_INTERVAL_USEC = 2000; | |||
//------------------------------------------------------------------------------ | |||
// Set USE_SHARED_SPI non-zero for use of an SPI sensor. | |||
// May not work for some cards. | |||
#ifndef USE_SHARED_SPI | |||
#define USE_SHARED_SPI 0 | |||
#endif // USE_SHARED_SPI | |||
//------------------------------------------------------------------------------ | |||
// Pin definitions. | |||
// | |||
@@ -94,8 +44,12 @@ const uint32_t LOG_INTERVAL_USEC = 10000; | |||
const uint8_t SD_CS_PIN = SS; | |||
// | |||
// Digital pin to indicate an error, set to -1 if not used. | |||
// The led blinks for fatal errors. The led goes on solid for SD write | |||
// overrun errors and logging continues. | |||
// The led blinks for fatal errors. The led goes on solid for | |||
// overrun errors and logging continues unless ABORT_ON_OVERRUN | |||
// is non-zero. | |||
#ifdef ERROR_LED_PIN | |||
#undef ERROR_LED_PIN | |||
#endif // ERROR_LED_PIN | |||
const int8_t ERROR_LED_PIN = -1; | |||
//------------------------------------------------------------------------------ | |||
// File definitions. | |||
@@ -105,32 +59,34 @@ const int8_t ERROR_LED_PIN = -1; | |||
// This file is flash erased using special SD commands. The file will be | |||
// truncated if logging is stopped early. | |||
const uint32_t FILE_BLOCK_COUNT = 256000; | |||
// log file base name. Must be six characters or less. | |||
// | |||
// log file base name if not defined in UserTypes.h | |||
#ifndef FILE_BASE_NAME | |||
#define FILE_BASE_NAME "data" | |||
#endif // FILE_BASE_NAME | |||
//------------------------------------------------------------------------------ | |||
// Buffer definitions. | |||
// | |||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional | |||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional | |||
// buffers. | |||
// | |||
#ifndef RAMEND | |||
// Assume ARM. Use total of nine 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 8; | |||
// Assume ARM. Use total of ten 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 10; | |||
// | |||
#elif RAMEND < 0X8FF | |||
#error Too little SRAM | |||
// | |||
#elif RAMEND < 0X10FF | |||
// Use total of two 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 1; | |||
const uint8_t BUFFER_BLOCK_COUNT = 2; | |||
// | |||
#elif RAMEND < 0X20FF | |||
// Use total of five 512 byte buffers. | |||
// Use total of four 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 4; | |||
// | |||
#else // RAMEND | |||
// Use total of 13 512 byte buffers. | |||
// Use total of 12 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
#endif // RAMEND | |||
//============================================================================== | |||
@@ -141,13 +97,13 @@ const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
// Size of file base name. Must not be larger than six. | |||
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | |||
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7; | |||
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin"; | |||
SdFat sd; | |||
SdBaseFile binFile; | |||
char binName[13] = FILE_BASE_NAME "00.bin"; | |||
// Number of data records in a block. | |||
const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | |||
@@ -160,33 +116,14 @@ struct block_t { | |||
data_t data[DATA_DIM]; | |||
uint8_t fill[FILL_DIM]; | |||
}; | |||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 2; | |||
block_t* emptyQueue[QUEUE_DIM]; | |||
uint8_t emptyHead; | |||
uint8_t emptyTail; | |||
block_t* fullQueue[QUEUE_DIM]; | |||
uint8_t fullHead; | |||
uint8_t fullTail; | |||
// Advance queue index. | |||
inline uint8_t queueNext(uint8_t ht) { | |||
return ht < (QUEUE_DIM - 1) ? ht + 1 : 0; | |||
} | |||
//============================================================================== | |||
// Error messages stored in flash. | |||
#define error(msg) errorFlash(F(msg)) | |||
//------------------------------------------------------------------------------ | |||
void errorFlash(const __FlashStringHelper* msg) { | |||
sd.errorPrint(msg); | |||
fatalBlink(); | |||
} | |||
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();} | |||
//------------------------------------------------------------------------------ | |||
// | |||
void fatalBlink() { | |||
while (true) { | |||
SysCall::yield(); | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, HIGH); | |||
delay(200); | |||
@@ -195,7 +132,49 @@ void fatalBlink() { | |||
} | |||
} | |||
} | |||
//============================================================================== | |||
//------------------------------------------------------------------------------ | |||
// read data file and check for overruns | |||
void checkOverrun() { | |||
bool headerPrinted = false; | |||
block_t block; | |||
uint32_t bn = 0; | |||
if (!binFile.isOpen()) { | |||
Serial.println(); | |||
Serial.println(F("No current binary file")); | |||
return; | |||
} | |||
binFile.rewind(); | |||
Serial.println(); | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
Serial.println(F("Checking overrun errors - type any character to stop")); | |||
while (binFile.read(&block, 512) == 512) { | |||
if (block.count == 0) { | |||
break; | |||
} | |||
if (block.overrun) { | |||
if (!headerPrinted) { | |||
Serial.println(); | |||
Serial.println(F("Overruns:")); | |||
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||
headerPrinted = true; | |||
} | |||
Serial.print(bn); | |||
Serial.print(','); | |||
Serial.print(binFile.firstBlock() + bn); | |||
Serial.print(','); | |||
Serial.println(block.overrun); | |||
} | |||
bn++; | |||
} | |||
if (!headerPrinted) { | |||
Serial.println(F("No errors found")); | |||
} else { | |||
Serial.println(F("Done")); | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
// Convert binary file to csv file. | |||
void binaryToCsv() { | |||
uint8_t lastPct = 0; | |||
@@ -203,14 +182,17 @@ void binaryToCsv() { | |||
uint32_t t0 = millis(); | |||
uint32_t syncCluster = 0; | |||
SdFile csvFile; | |||
char csvName[13]; | |||
char csvName[FILE_NAME_DIM]; | |||
if (!binFile.isOpen()) { | |||
Serial.println(); | |||
Serial.println(F("No current binary file")); | |||
return; | |||
} | |||
binFile.rewind(); | |||
Serial.println(); | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
// Create a new csvFile. | |||
strcpy(csvName, binName); | |||
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||
@@ -218,7 +200,7 @@ void binaryToCsv() { | |||
if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | |||
error("open csvFile failed"); | |||
} | |||
Serial.println(); | |||
binFile.rewind(); | |||
Serial.print(F("Writing: ")); | |||
Serial.print(csvName); | |||
Serial.println(F(" - type any character to stop")); | |||
@@ -258,48 +240,55 @@ void binaryToCsv() { | |||
Serial.print(0.001*(millis() - t0)); | |||
Serial.println(F(" Seconds")); | |||
} | |||
//------------------------------------------------------------------------------ | |||
// read data file and check for overruns | |||
void checkOverrun() { | |||
bool headerPrinted = false; | |||
block_t block; | |||
//----------------------------------------------------------------------------- | |||
void createBinFile() { | |||
// max number of blocks to erase per erase call | |||
const uint32_t ERASE_SIZE = 262144L; | |||
uint32_t bgnBlock, endBlock; | |||
uint32_t bn = 0; | |||
if (!binFile.isOpen()) { | |||
Serial.println(); | |||
Serial.println(F("No current binary file")); | |||
return; | |||
Serial.println(); | |||
while (sd.exists(binName)) { | |||
if (binName[BASE_NAME_SIZE + 1] != '9') { | |||
binName[BASE_NAME_SIZE + 1]++; | |||
} else { | |||
binName[BASE_NAME_SIZE + 1] = '0'; | |||
if (binName[BASE_NAME_SIZE] == '9') { | |||
error("Can't create file name"); | |||
} | |||
binName[BASE_NAME_SIZE]++; | |||
} | |||
} | |||
// Delete old tmp file. | |||
if (sd.exists(TMP_FILE_NAME)) { | |||
Serial.println(F("Deleting tmp file")); | |||
if (!sd.remove(TMP_FILE_NAME)) { | |||
error("Can't remove tmp file"); | |||
} | |||
} | |||
// Create new file. | |||
Serial.println(F("Creating new file")); | |||
binFile.close(); | |||
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
error("createContiguous failed"); | |||
} | |||
// Get the address of the file on the SD. | |||
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||
error("contiguousRange failed"); | |||
} | |||
binFile.rewind(); | |||
Serial.println(); | |||
Serial.println(F("Checking overrun errors - type any character to stop")); | |||
while (binFile.read(&block, 512) == 512) { | |||
if (block.count == 0) { | |||
break; | |||
// Flash erase all data in the file. | |||
Serial.println(F("Erasing all data")); | |||
uint32_t bgnErase = bgnBlock; | |||
uint32_t endErase; | |||
while (bgnErase < endBlock) { | |||
endErase = bgnErase + ERASE_SIZE; | |||
if (endErase > endBlock) { | |||
endErase = endBlock; | |||
} | |||
if (block.overrun) { | |||
if (!headerPrinted) { | |||
Serial.println(); | |||
Serial.println(F("Overruns:")); | |||
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||
headerPrinted = true; | |||
} | |||
Serial.print(bn); | |||
Serial.print(','); | |||
Serial.print(bgnBlock + bn); | |||
Serial.print(','); | |||
Serial.println(block.overrun); | |||
if (!sd.card()->erase(bgnErase, endErase)) { | |||
error("erase failed"); | |||
} | |||
bn++; | |||
} | |||
if (!headerPrinted) { | |||
Serial.println(F("No errors found")); | |||
} else { | |||
Serial.println(F("Done")); | |||
bgnErase = endErase + 1; | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -332,127 +321,105 @@ void dumpData() { | |||
} | |||
//------------------------------------------------------------------------------ | |||
// log data | |||
// max number of blocks to erase per erase call | |||
uint32_t const ERASE_SIZE = 262144L; | |||
void logData() { | |||
uint32_t bgnBlock, endBlock; | |||
// Allocate extra buffer space. | |||
block_t block[BUFFER_BLOCK_COUNT]; | |||
block_t* curBlock = 0; | |||
Serial.println(); | |||
// Find unused file name. | |||
if (BASE_NAME_SIZE > 6) { | |||
error("FILE_BASE_NAME too long"); | |||
} | |||
while (sd.exists(binName)) { | |||
if (binName[BASE_NAME_SIZE + 1] != '9') { | |||
binName[BASE_NAME_SIZE + 1]++; | |||
} else { | |||
binName[BASE_NAME_SIZE + 1] = '0'; | |||
if (binName[BASE_NAME_SIZE] == '9') { | |||
error("Can't create file name"); | |||
} | |||
binName[BASE_NAME_SIZE]++; | |||
createBinFile(); | |||
recordBinFile(); | |||
renameBinFile(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void openBinFile() { | |||
char name[FILE_NAME_DIM]; | |||
strcpy(name, binName); | |||
Serial.println(F("\nEnter two digit version")); | |||
Serial.write(name, BASE_NAME_SIZE); | |||
for (int i = 0; i < 2; i++) { | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
} | |||
// Delete old tmp file. | |||
if (sd.exists(TMP_FILE_NAME)) { | |||
Serial.println(F("Deleting tmp file")); | |||
if (!sd.remove(TMP_FILE_NAME)) { | |||
error("Can't remove tmp file"); | |||
char c = Serial.read(); | |||
Serial.write(c); | |||
if (c < '0' || c > '9') { | |||
Serial.println("\nInvalid digit"); | |||
return; | |||
} | |||
name[BASE_NAME_SIZE + i] = c; | |||
} | |||
// Create new file. | |||
Serial.println(F("Creating new file")); | |||
binFile.close(); | |||
if (!binFile.createContiguous(sd.vwd(), | |||
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
error("createContiguous failed"); | |||
Serial.println(&name[BASE_NAME_SIZE+2]); | |||
if (!sd.exists(name)) { | |||
Serial.println(F("File does not exist")); | |||
return; | |||
} | |||
// Get the address of the file on the SD. | |||
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||
error("contiguousRange failed"); | |||
binFile.close(); | |||
strcpy(binName, name); | |||
if (!binFile.open(binName, O_READ)) { | |||
Serial.println(F("open failed")); | |||
return; | |||
} | |||
Serial.println(F("File opened")); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void recordBinFile() { | |||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||
// Index of last queue location. | |||
const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||
// Allocate extra buffer space. | |||
block_t block[BUFFER_BLOCK_COUNT - 1]; | |||
block_t* curBlock = 0; | |||
block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||
uint8_t emptyTop; | |||
uint8_t minTop; | |||
block_t* fullQueue[QUEUE_DIM]; | |||
uint8_t fullHead = 0; | |||
uint8_t fullTail = 0; | |||
// Use SdFat's internal buffer. | |||
uint8_t* cache = (uint8_t*)sd.vol()->cacheClear(); | |||
if (cache == 0) { | |||
emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||
if (emptyStack[0] == 0) { | |||
error("cacheClear failed"); | |||
} | |||
// Flash erase all data in the file. | |||
Serial.println(F("Erasing all data")); | |||
uint32_t bgnErase = bgnBlock; | |||
uint32_t endErase; | |||
while (bgnErase < endBlock) { | |||
endErase = bgnErase + ERASE_SIZE; | |||
if (endErase > endBlock) { | |||
endErase = endBlock; | |||
} | |||
if (!sd.card()->erase(bgnErase, endErase)) { | |||
error("erase failed"); | |||
} | |||
bgnErase = endErase + 1; | |||
// Put rest of buffers on the empty stack. | |||
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) { | |||
emptyStack[i] = &block[i - 1]; | |||
} | |||
emptyTop = BUFFER_BLOCK_COUNT; | |||
minTop = BUFFER_BLOCK_COUNT; | |||
// Start a multiple block write. | |||
if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) { | |||
error("writeBegin failed"); | |||
} | |||
// Set chip select high if other devices use SPI. | |||
if (useSharedSpi) { | |||
sd.card()->chipSelectHigh(); | |||
} | |||
// Initialize queues. | |||
emptyHead = emptyTail = 0; | |||
fullHead = fullTail = 0; | |||
// Use SdFat buffer for one block. | |||
emptyQueue[emptyHead] = (block_t*)cache; | |||
emptyHead = queueNext(emptyHead); | |||
// Put rest of buffers in the empty queue. | |||
for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { | |||
emptyQueue[emptyHead] = &block[i]; | |||
emptyHead = queueNext(emptyHead); | |||
if (!sd.card()->writeStart(binFile.firstBlock())) { | |||
error("writeBStart failed"); | |||
} | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
Serial.println(F("Logging - type any character to stop")); | |||
// Wait for Serial Idle. | |||
Serial.flush(); | |||
delay(10); | |||
bool closeFile = false; | |||
uint32_t bn = 0; | |||
uint32_t t0 = millis(); | |||
uint32_t t1 = t0; | |||
uint32_t bn = 0; | |||
uint32_t maxLatency = 0; | |||
uint32_t overrun = 0; | |||
uint32_t overrunTotal = 0; | |||
uint32_t count = 0; | |||
uint32_t maxDelta = 0; | |||
uint32_t minDelta = 99999; | |||
uint32_t maxLatency = 0; | |||
uint32_t logTime = micros(); | |||
// Set time for first record of file. | |||
startMicros = logTime + LOG_INTERVAL_USEC; | |||
while (1) { | |||
// Time for next data record. | |||
while(1) { | |||
// Time for next data record. | |||
logTime += LOG_INTERVAL_USEC; | |||
if (Serial.available()) { | |||
closeFile = true; | |||
} | |||
} | |||
if (closeFile) { | |||
if (curBlock != 0) { | |||
// Put buffer in full queue. | |||
fullQueue[fullHead] = curBlock; | |||
fullHead = queueNext(fullHead); | |||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
curBlock = 0; | |||
} | |||
} else { | |||
if (curBlock == 0 && emptyTail != emptyHead) { | |||
curBlock = emptyQueue[emptyTail]; | |||
emptyTail = queueNext(emptyTail); | |||
if (curBlock == 0 && emptyTop != 0) { | |||
curBlock = emptyStack[--emptyTop]; | |||
if (emptyTop < minTop) { | |||
minTop = emptyTop; | |||
} | |||
curBlock->count = 0; | |||
curBlock->overrun = overrun; | |||
overrun = 0; | |||
@@ -466,18 +433,29 @@ void logData() { | |||
} while (delta < 0); | |||
if (curBlock == 0) { | |||
overrun++; | |||
overrunTotal++; | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, HIGH); | |||
} | |||
#if ABORT_ON_OVERRUN | |||
Serial.println(F("Overrun abort")); | |||
break; | |||
#endif // ABORT_ON_OVERRUN | |||
} else { | |||
acquireData(&curBlock->data[curBlock->count++]); | |||
#if USE_SHARED_SPI | |||
sd.card()->spiStop(); | |||
#endif // USE_SHARED_SPI | |||
acquireData(&curBlock->data[curBlock->count++]); | |||
#if USE_SHARED_SPI | |||
sd.card()->spiStart(); | |||
#endif // USE_SHARED_SPI | |||
if (curBlock->count == DATA_DIM) { | |||
fullQueue[fullHead] = curBlock; | |||
fullHead = queueNext(fullHead); | |||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
curBlock = 0; | |||
} | |||
if ((uint32_t)delta > maxDelta) maxDelta = delta; | |||
if ((uint32_t)delta < minDelta) minDelta = delta; | |||
} | |||
} | |||
} | |||
if (fullHead == fullTail) { | |||
// Exit loop if done. | |||
if (closeFile) { | |||
@@ -486,29 +464,18 @@ void logData() { | |||
} else if (!sd.card()->isBusy()) { | |||
// Get address of block to write. | |||
block_t* pBlock = fullQueue[fullTail]; | |||
fullTail = queueNext(fullTail); | |||
fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0; | |||
// Write block to SD. | |||
uint32_t usec = micros(); | |||
if (!sd.card()->writeData((uint8_t*)pBlock)) { | |||
error("write data failed"); | |||
} | |||
usec = micros() - usec; | |||
t1 = millis(); | |||
if (usec > maxLatency) { | |||
maxLatency = usec; | |||
} | |||
count += pBlock->count; | |||
// Add overruns and possibly light LED. | |||
if (pBlock->overrun) { | |||
overrunTotal += pBlock->overrun; | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, HIGH); | |||
} | |||
} | |||
// Move block to empty queue. | |||
emptyQueue[emptyHead] = pBlock; | |||
emptyHead = queueNext(emptyHead); | |||
emptyStack[emptyTop++] = pBlock; | |||
bn++; | |||
if (bn == FILE_BLOCK_COUNT) { | |||
// File full so stop | |||
@@ -519,6 +486,12 @@ void logData() { | |||
if (!sd.card()->writeStop()) { | |||
error("writeStop failed"); | |||
} | |||
Serial.print(F("Min Free buffers: ")); | |||
Serial.println(minTop); | |||
Serial.print(F("Max block write usec: ")); | |||
Serial.println(maxLatency); | |||
Serial.print(F("Overruns: ")); | |||
Serial.println(overrunTotal); | |||
// Truncate file if recording stopped early. | |||
if (bn != FILE_BLOCK_COUNT) { | |||
Serial.println(F("Truncating file")); | |||
@@ -526,25 +499,38 @@ void logData() { | |||
error("Can't truncate file"); | |||
} | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
void renameBinFile() { | |||
if (!binFile.rename(sd.vwd(), binName)) { | |||
error("Can't rename file"); | |||
} | |||
} | |||
Serial.print(F("File renamed: ")); | |||
Serial.println(binName); | |||
Serial.print(F("Max block write usec: ")); | |||
Serial.println(maxLatency); | |||
Serial.print(F("Record time sec: ")); | |||
Serial.println(0.001*(t1 - t0), 3); | |||
Serial.print(minDelta); | |||
Serial.print(F(" <= jitter microseconds <= ")); | |||
Serial.println(maxDelta); | |||
Serial.print(F("Sample count: ")); | |||
Serial.println(count); | |||
Serial.print(F("Samples/sec: ")); | |||
Serial.println((1000.0)*count/(t1-t0)); | |||
Serial.print(F("Overruns: ")); | |||
Serial.println(overrunTotal); | |||
Serial.println(F("Done")); | |||
Serial.print("File size: "); | |||
Serial.print(binFile.fileSize()/512); | |||
Serial.println(F(" blocks")); | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void testSensor() { | |||
const uint32_t interval = 200000; | |||
int32_t diff; | |||
data_t data; | |||
Serial.println(F("\nTesting - type any character to stop\n")); | |||
// Wait for Serial Idle. | |||
delay(1000); | |||
printHeader(&Serial); | |||
uint32_t m = micros(); | |||
while (!Serial.available()) { | |||
m += interval; | |||
do { | |||
diff = m - micros(); | |||
} while (diff > 0); | |||
acquireData(&data); | |||
printData(&Serial, &data); | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
void setup(void) { | |||
@@ -557,7 +543,6 @@ void setup(void) { | |||
while (!Serial) { | |||
SysCall::yield(); | |||
} | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
Serial.print(F("Records/block: ")); | |||
@@ -565,47 +550,62 @@ void setup(void) { | |||
if (sizeof(block_t) != 512) { | |||
error("Invalid block size"); | |||
} | |||
// initialize file system. | |||
if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||
sd.initErrorPrint(); | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||
sd.initErrorPrint(&Serial); | |||
fatalBlink(); | |||
} | |||
setupADXL345(); | |||
userSetup(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void loop(void) { | |||
// discard any input | |||
// Read any Serial data. | |||
do { | |||
delay(10); | |||
} while (Serial.read() >= 0); | |||
} while (Serial.available() && Serial.read() >= 0); | |||
Serial.println(); | |||
Serial.println(F("type:")); | |||
Serial.println(F("b - open existing bin file")); | |||
Serial.println(F("c - convert file to csv")); | |||
Serial.println(F("d - dump data to Serial")); | |||
Serial.println(F("e - overrun error details")); | |||
Serial.println(F("l - list files")); | |||
Serial.println(F("r - record data")); | |||
Serial.println(F("t - test without logging")); | |||
while(!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
#if WDT_YIELD_TIME_MICROS | |||
Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||
SysCall::halt(); | |||
#endif | |||
char c = tolower(Serial.read()); | |||
// Discard extra Serial data. | |||
do { | |||
delay(10); | |||
} while (Serial.read() >= 0); | |||
} while (Serial.available() && Serial.read() >= 0); | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, LOW); | |||
} | |||
if (c == 'c') { | |||
if (c == 'b') { | |||
openBinFile(); | |||
} else if (c == 'c') { | |||
binaryToCsv(); | |||
} else if (c == 'd') { | |||
dumpData(); | |||
} else if (c == 'e') { | |||
checkOverrun(); | |||
} else if (c == 'l') { | |||
Serial.println(F("\nls:")); | |||
sd.ls(&Serial, LS_SIZE); | |||
} else if (c == 'r') { | |||
logData(); | |||
} else if (c == 't') { | |||
testSensor(); | |||
} else { | |||
Serial.println(F("Invalid entry")); | |||
} |
@@ -0,0 +1 @@ | |||
// Empty file with name LowLatencyLoggerADXL345.ino to make IDE happy. |
@@ -0,0 +1,67 @@ | |||
#include "UserTypes.h" | |||
// User data functions. Modify these functions for your data items. | |||
// Start time for data | |||
static uint32_t startMicros; | |||
const uint8_t ADXL345_CS = 9; | |||
const uint8_t POWER_CTL = 0x2D; //Power Control Register | |||
const uint8_t DATA_FORMAT = 0x31; | |||
const uint8_t DATAX0 = 0x32; //X-Axis Data 0 | |||
const uint8_t DATAX1 = 0x33; //X-Axis Data 1 | |||
const uint8_t DATAY0 = 0x34; //Y-Axis Data 0 | |||
const uint8_t DATAY1 = 0x35; //Y-Axis Data 1 | |||
const uint8_t DATAZ0 = 0x36; //Z-Axis Data 0 | |||
const uint8_t DATAZ1 = 0x37; //Z-Axis Data 1 | |||
void writeADXL345Register(const uint8_t registerAddress, const uint8_t value) { | |||
digitalWrite(ADXL345_CS, LOW); | |||
SPI.transfer(registerAddress); | |||
SPI.transfer(value); | |||
digitalWrite(ADXL345_CS, HIGH); | |||
} | |||
void userSetup() { | |||
SPI.begin(); | |||
pinMode(ADXL345_CS, OUTPUT); | |||
digitalWrite(ADXL345_CS, HIGH); | |||
//Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register. | |||
writeADXL345Register(DATA_FORMAT, 0x01); | |||
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register. | |||
writeADXL345Register(POWER_CTL, 0x08); //Measurement mode | |||
} | |||
// Acquire a data record. | |||
void acquireData(data_t* data) { | |||
// Max SPI clock frequency is 5 MHz with CPOL = 1 and CPHA = 1. | |||
SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE3)); | |||
data->time = micros(); | |||
digitalWrite(ADXL345_CS, LOW); | |||
// Read multiple bytes so or 0XC0 with address. | |||
SPI.transfer(DATAX0 | 0XC0); | |||
data->accel[0] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||
data->accel[1] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||
data->accel[2] = SPI.transfer(0) | (SPI.transfer(0) << 8); | |||
digitalWrite(ADXL345_CS, HIGH); | |||
SPI.endTransaction(); | |||
} | |||
// Print a data record. | |||
void printData(Print* pr, data_t* data) { | |||
if (startMicros == 0) { | |||
startMicros = data->time; | |||
} | |||
pr->print(data->time - startMicros); | |||
for (int i = 0; i < ACCEL_DIM; i++) { | |||
pr->write(','); | |||
pr->print(data->accel[i]); | |||
} | |||
pr->println(); | |||
} | |||
// Print data header. | |||
void printHeader(Print* pr) { | |||
startMicros = 0; | |||
pr->println(F("micros,ax,ay,az")); | |||
} |
@@ -0,0 +1,17 @@ | |||
#ifndef UserTypes_h | |||
#define UserTypes_h | |||
#include "Arduino.h" | |||
#include "SPI.h" | |||
#define USE_SHARED_SPI 1 | |||
#define FILE_BASE_NAME "ADXL4G" | |||
// User data types. Modify for your data items. | |||
const uint8_t ACCEL_DIM = 3; | |||
struct data_t { | |||
unsigned long time; | |||
int16_t accel[ACCEL_DIM]; | |||
}; | |||
void acquireData(data_t* data); | |||
void printData(Print* pr, data_t* data); | |||
void printHeader(Print* pr); | |||
void userSetup(); | |||
#endif // UserTypes_h |
@@ -0,0 +1,612 @@ | |||
/** | |||
* This program logs data to a binary file. Functions are included | |||
* to convert the binary file to a csv text file. | |||
* | |||
* Samples are logged at regular intervals. The maximum logging rate | |||
* depends on the quality of your SD card and the time required to | |||
* read sensor data. This example has been tested at 500 Hz with | |||
* good SD card on an Uno. 4000 HZ is possible on a Due. | |||
* | |||
* If your SD card has a long write latency, it may be necessary to use | |||
* slower sample rates. Using a Mega Arduino helps overcome latency | |||
* problems since 12 512 byte buffers will be used. | |||
* | |||
* Data is written to the file using a SD multiple block write command. | |||
*/ | |||
#include <SPI.h> | |||
#include "SdFat.h" | |||
#include "FreeStack.h" | |||
#include "UserTypes.h" | |||
#ifdef __AVR_ATmega328P__ | |||
#include "MinimumSerial.h" | |||
MinimumSerial MinSerial; | |||
#define Serial MinSerial | |||
#endif // __AVR_ATmega328P__ | |||
//============================================================================== | |||
// Start of configuration constants. | |||
//============================================================================== | |||
// Abort run on an overrun. Data before the overrun will be saved. | |||
#define ABORT_ON_OVERRUN 1 | |||
//------------------------------------------------------------------------------ | |||
//Interval between data records in microseconds. | |||
const uint32_t LOG_INTERVAL_USEC = 2000; | |||
//------------------------------------------------------------------------------ | |||
// Set USE_SHARED_SPI non-zero for use of an SPI sensor. | |||
// May not work for some cards. | |||
#ifndef USE_SHARED_SPI | |||
#define USE_SHARED_SPI 0 | |||
#endif // USE_SHARED_SPI | |||
//------------------------------------------------------------------------------ | |||
// Pin definitions. | |||
// | |||
// SD chip select pin. | |||
const uint8_t SD_CS_PIN = SS; | |||
// | |||
// Digital pin to indicate an error, set to -1 if not used. | |||
// The led blinks for fatal errors. The led goes on solid for | |||
// overrun errors and logging continues unless ABORT_ON_OVERRUN | |||
// is non-zero. | |||
#ifdef ERROR_LED_PIN | |||
#undef ERROR_LED_PIN | |||
#endif // ERROR_LED_PIN | |||
const int8_t ERROR_LED_PIN = 3; | |||
//------------------------------------------------------------------------------ | |||
// File definitions. | |||
// | |||
// Maximum file size in blocks. | |||
// The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks. | |||
// This file is flash erased using special SD commands. The file will be | |||
// truncated if logging is stopped early. | |||
const uint32_t FILE_BLOCK_COUNT = 256000; | |||
// | |||
// log file base name if not defined in UserTypes.h | |||
#ifndef FILE_BASE_NAME | |||
#define FILE_BASE_NAME "data" | |||
#endif // FILE_BASE_NAME | |||
//------------------------------------------------------------------------------ | |||
// Buffer definitions. | |||
// | |||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional | |||
// buffers. | |||
// | |||
#ifndef RAMEND | |||
// Assume ARM. Use total of ten 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 10; | |||
// | |||
#elif RAMEND < 0X8FF | |||
#error Too little SRAM | |||
// | |||
#elif RAMEND < 0X10FF | |||
// Use total of two 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 2; | |||
// | |||
#elif RAMEND < 0X20FF | |||
// Use total of four 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 4; | |||
// | |||
#else // RAMEND | |||
// Use total of 12 512 byte buffers. | |||
const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
#endif // RAMEND | |||
//============================================================================== | |||
// End of configuration constants. | |||
//============================================================================== | |||
// Temporary log file. Will be deleted if a reset or power failure occurs. | |||
#define TMP_FILE_NAME "tmp_log.bin" | |||
// Size of file base name. Must not be larger than six. | |||
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | |||
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7; | |||
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin"; | |||
SdFat sd; | |||
SdBaseFile binFile; | |||
// Number of data records in a block. | |||
const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | |||
//Compute fill so block size is 512 bytes. FILL_DIM may be zero. | |||
const uint16_t FILL_DIM = 512 - 4 - DATA_DIM*sizeof(data_t); | |||
struct block_t { | |||
uint16_t count; | |||
uint16_t overrun; | |||
data_t data[DATA_DIM]; | |||
uint8_t fill[FILL_DIM]; | |||
}; | |||
//============================================================================== | |||
// Error messages stored in flash. | |||
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();} | |||
//------------------------------------------------------------------------------ | |||
// | |||
void fatalBlink() { | |||
while (true) { | |||
SysCall::yield(); | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, HIGH); | |||
delay(200); | |||
digitalWrite(ERROR_LED_PIN, LOW); | |||
delay(200); | |||
} | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
// read data file and check for overruns | |||
void checkOverrun() { | |||
bool headerPrinted = false; | |||
block_t block; | |||
uint32_t bn = 0; | |||
if (!binFile.isOpen()) { | |||
Serial.println(); | |||
Serial.println(F("No current binary file")); | |||
return; | |||
} | |||
binFile.rewind(); | |||
Serial.println(); | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
Serial.println(F("Checking overrun errors - type any character to stop")); | |||
while (binFile.read(&block, 512) == 512) { | |||
if (block.count == 0) { | |||
break; | |||
} | |||
if (block.overrun) { | |||
if (!headerPrinted) { | |||
Serial.println(); | |||
Serial.println(F("Overruns:")); | |||
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||
headerPrinted = true; | |||
} | |||
Serial.print(bn); | |||
Serial.print(','); | |||
Serial.print(binFile.firstBlock() + bn); | |||
Serial.print(','); | |||
Serial.println(block.overrun); | |||
} | |||
bn++; | |||
} | |||
if (!headerPrinted) { | |||
Serial.println(F("No errors found")); | |||
} else { | |||
Serial.println(F("Done")); | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
// Convert binary file to csv file. | |||
void binaryToCsv() { | |||
uint8_t lastPct = 0; | |||
block_t block; | |||
uint32_t t0 = millis(); | |||
uint32_t syncCluster = 0; | |||
SdFile csvFile; | |||
char csvName[FILE_NAME_DIM]; | |||
if (!binFile.isOpen()) { | |||
Serial.println(); | |||
Serial.println(F("No current binary file")); | |||
return; | |||
} | |||
Serial.println(); | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
// Create a new csvFile. | |||
strcpy(csvName, binName); | |||
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||
if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | |||
error("open csvFile failed"); | |||
} | |||
binFile.rewind(); | |||
Serial.print(F("Writing: ")); | |||
Serial.print(csvName); | |||
Serial.println(F(" - type any character to stop")); | |||
printHeader(&csvFile); | |||
uint32_t tPct = millis(); | |||
while (!Serial.available() && binFile.read(&block, 512) == 512) { | |||
uint16_t i; | |||
if (block.count == 0) { | |||
break; | |||
} | |||
if (block.overrun) { | |||
csvFile.print(F("OVERRUN,")); | |||
csvFile.println(block.overrun); | |||
} | |||
for (i = 0; i < block.count; i++) { | |||
printData(&csvFile, &block.data[i]); | |||
} | |||
if (csvFile.curCluster() != syncCluster) { | |||
csvFile.sync(); | |||
syncCluster = csvFile.curCluster(); | |||
} | |||
if ((millis() - tPct) > 1000) { | |||
uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100); | |||
if (pct != lastPct) { | |||
tPct = millis(); | |||
lastPct = pct; | |||
Serial.print(pct, DEC); | |||
Serial.println('%'); | |||
} | |||
} | |||
if (Serial.available()) { | |||
break; | |||
} | |||
} | |||
csvFile.close(); | |||
Serial.print(F("Done: ")); | |||
Serial.print(0.001*(millis() - t0)); | |||
Serial.println(F(" Seconds")); | |||
} | |||
//----------------------------------------------------------------------------- | |||
void createBinFile() { | |||
// max number of blocks to erase per erase call | |||
const uint32_t ERASE_SIZE = 262144L; | |||
uint32_t bgnBlock, endBlock; | |||
Serial.println(); | |||
while (sd.exists(binName)) { | |||
if (binName[BASE_NAME_SIZE + 1] != '9') { | |||
binName[BASE_NAME_SIZE + 1]++; | |||
} else { | |||
binName[BASE_NAME_SIZE + 1] = '0'; | |||
if (binName[BASE_NAME_SIZE] == '9') { | |||
error("Can't create file name"); | |||
} | |||
binName[BASE_NAME_SIZE]++; | |||
} | |||
} | |||
// Delete old tmp file. | |||
if (sd.exists(TMP_FILE_NAME)) { | |||
Serial.println(F("Deleting tmp file")); | |||
if (!sd.remove(TMP_FILE_NAME)) { | |||
error("Can't remove tmp file"); | |||
} | |||
} | |||
// Create new file. | |||
Serial.println(F("Creating new file")); | |||
binFile.close(); | |||
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
error("createContiguous failed"); | |||
} | |||
// Get the address of the file on the SD. | |||
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||
error("contiguousRange failed"); | |||
} | |||
// Flash erase all data in the file. | |||
Serial.println(F("Erasing all data")); | |||
uint32_t bgnErase = bgnBlock; | |||
uint32_t endErase; | |||
while (bgnErase < endBlock) { | |||
endErase = bgnErase + ERASE_SIZE; | |||
if (endErase > endBlock) { | |||
endErase = endBlock; | |||
} | |||
if (!sd.card()->erase(bgnErase, endErase)) { | |||
error("erase failed"); | |||
} | |||
bgnErase = endErase + 1; | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
// dump data file to Serial | |||
void dumpData() { | |||
block_t block; | |||
if (!binFile.isOpen()) { | |||
Serial.println(); | |||
Serial.println(F("No current binary file")); | |||
return; | |||
} | |||
binFile.rewind(); | |||
Serial.println(); | |||
Serial.println(F("Type any character to stop")); | |||
delay(1000); | |||
printHeader(&Serial); | |||
while (!Serial.available() && binFile.read(&block , 512) == 512) { | |||
if (block.count == 0) { | |||
break; | |||
} | |||
if (block.overrun) { | |||
Serial.print(F("OVERRUN,")); | |||
Serial.println(block.overrun); | |||
} | |||
for (uint16_t i = 0; i < block.count; i++) { | |||
printData(&Serial, &block.data[i]); | |||
} | |||
} | |||
Serial.println(F("Done")); | |||
} | |||
//------------------------------------------------------------------------------ | |||
// log data | |||
void logData() { | |||
createBinFile(); | |||
recordBinFile(); | |||
renameBinFile(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void openBinFile() { | |||
char name[FILE_NAME_DIM]; | |||
strcpy(name, binName); | |||
Serial.println(F("\nEnter two digit version")); | |||
Serial.write(name, BASE_NAME_SIZE); | |||
for (int i = 0; i < 2; i++) { | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
char c = Serial.read(); | |||
Serial.write(c); | |||
if (c < '0' || c > '9') { | |||
Serial.println("\nInvalid digit"); | |||
return; | |||
} | |||
name[BASE_NAME_SIZE + i] = c; | |||
} | |||
Serial.println(&name[BASE_NAME_SIZE+2]); | |||
if (!sd.exists(name)) { | |||
Serial.println(F("File does not exist")); | |||
return; | |||
} | |||
binFile.close(); | |||
strcpy(binName, name); | |||
if (!binFile.open(binName, O_READ)) { | |||
Serial.println(F("open failed")); | |||
return; | |||
} | |||
Serial.println(F("File opened")); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void recordBinFile() { | |||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||
// Index of last queue location. | |||
const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||
// Allocate extra buffer space. | |||
block_t block[BUFFER_BLOCK_COUNT - 1]; | |||
block_t* curBlock = 0; | |||
block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||
uint8_t emptyTop; | |||
uint8_t minTop; | |||
block_t* fullQueue[QUEUE_DIM]; | |||
uint8_t fullHead = 0; | |||
uint8_t fullTail = 0; | |||
// Use SdFat's internal buffer. | |||
emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||
if (emptyStack[0] == 0) { | |||
error("cacheClear failed"); | |||
} | |||
// Put rest of buffers on the empty stack. | |||
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) { | |||
emptyStack[i] = &block[i - 1]; | |||
} | |||
emptyTop = BUFFER_BLOCK_COUNT; | |||
minTop = BUFFER_BLOCK_COUNT; | |||
// Start a multiple block write. | |||
if (!sd.card()->writeStart(binFile.firstBlock())) { | |||
error("writeBStart failed"); | |||
} | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
Serial.println(F("Logging - type any character to stop")); | |||
bool closeFile = false; | |||
uint32_t bn = 0; | |||
uint32_t maxLatency = 0; | |||
uint32_t overrun = 0; | |||
uint32_t overrunTotal = 0; | |||
uint32_t logTime = micros(); | |||
while(1) { | |||
// Time for next data record. | |||
logTime += LOG_INTERVAL_USEC; | |||
if (Serial.available()) { | |||
closeFile = true; | |||
} | |||
if (closeFile) { | |||
if (curBlock != 0) { | |||
// Put buffer in full queue. | |||
fullQueue[fullHead] = curBlock; | |||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
curBlock = 0; | |||
} | |||
} else { | |||
if (curBlock == 0 && emptyTop != 0) { | |||
curBlock = emptyStack[--emptyTop]; | |||
if (emptyTop < minTop) { | |||
minTop = emptyTop; | |||
} | |||
curBlock->count = 0; | |||
curBlock->overrun = overrun; | |||
overrun = 0; | |||
} | |||
if ((int32_t)(logTime - micros()) < 0) { | |||
error("Rate too fast"); | |||
} | |||
int32_t delta; | |||
do { | |||
delta = micros() - logTime; | |||
} while (delta < 0); | |||
if (curBlock == 0) { | |||
overrun++; | |||
overrunTotal++; | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, HIGH); | |||
} | |||
#if ABORT_ON_OVERRUN | |||
Serial.println(F("Overrun abort")); | |||
break; | |||
#endif // ABORT_ON_OVERRUN | |||
} else { | |||
#if USE_SHARED_SPI | |||
sd.card()->spiStop(); | |||
#endif // USE_SHARED_SPI | |||
acquireData(&curBlock->data[curBlock->count++]); | |||
#if USE_SHARED_SPI | |||
sd.card()->spiStart(); | |||
#endif // USE_SHARED_SPI | |||
if (curBlock->count == DATA_DIM) { | |||
fullQueue[fullHead] = curBlock; | |||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
curBlock = 0; | |||
} | |||
} | |||
} | |||
if (fullHead == fullTail) { | |||
// Exit loop if done. | |||
if (closeFile) { | |||
break; | |||
} | |||
} else if (!sd.card()->isBusy()) { | |||
// Get address of block to write. | |||
block_t* pBlock = fullQueue[fullTail]; | |||
fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0; | |||
// Write block to SD. | |||
uint32_t usec = micros(); | |||
if (!sd.card()->writeData((uint8_t*)pBlock)) { | |||
error("write data failed"); | |||
} | |||
usec = micros() - usec; | |||
if (usec > maxLatency) { | |||
maxLatency = usec; | |||
} | |||
// Move block to empty queue. | |||
emptyStack[emptyTop++] = pBlock; | |||
bn++; | |||
if (bn == FILE_BLOCK_COUNT) { | |||
// File full so stop | |||
break; | |||
} | |||
} | |||
} | |||
if (!sd.card()->writeStop()) { | |||
error("writeStop failed"); | |||
} | |||
Serial.print(F("Min Free buffers: ")); | |||
Serial.println(minTop); | |||
Serial.print(F("Max block write usec: ")); | |||
Serial.println(maxLatency); | |||
Serial.print(F("Overruns: ")); | |||
Serial.println(overrunTotal); | |||
// Truncate file if recording stopped early. | |||
if (bn != FILE_BLOCK_COUNT) { | |||
Serial.println(F("Truncating file")); | |||
if (!binFile.truncate(512L * bn)) { | |||
error("Can't truncate file"); | |||
} | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
void renameBinFile() { | |||
if (!binFile.rename(sd.vwd(), binName)) { | |||
error("Can't rename file"); | |||
} | |||
Serial.print(F("File renamed: ")); | |||
Serial.println(binName); | |||
Serial.print("File size: "); | |||
Serial.print(binFile.fileSize()/512); | |||
Serial.println(F(" blocks")); | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void testSensor() { | |||
const uint32_t interval = 200000; | |||
int32_t diff; | |||
data_t data; | |||
Serial.println(F("\nTesting - type any character to stop\n")); | |||
// Wait for Serial Idle. | |||
delay(1000); | |||
printHeader(&Serial); | |||
uint32_t m = micros(); | |||
while (!Serial.available()) { | |||
m += interval; | |||
do { | |||
diff = m - micros(); | |||
} while (diff > 0); | |||
acquireData(&data); | |||
printData(&Serial, &data); | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
void setup(void) { | |||
if (ERROR_LED_PIN >= 0) { | |||
pinMode(ERROR_LED_PIN, OUTPUT); | |||
} | |||
Serial.begin(9600); | |||
// Wait for USB Serial | |||
while (!Serial) { | |||
SysCall::yield(); | |||
} | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
Serial.print(F("Records/block: ")); | |||
Serial.println(DATA_DIM); | |||
if (sizeof(block_t) != 512) { | |||
error("Invalid block size"); | |||
} | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||
sd.initErrorPrint(&Serial); | |||
fatalBlink(); | |||
} | |||
userSetup(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void loop(void) { | |||
// Read any Serial data. | |||
do { | |||
delay(10); | |||
} while (Serial.available() && Serial.read() >= 0); | |||
Serial.println(); | |||
Serial.println(F("type:")); | |||
Serial.println(F("b - open existing bin file")); | |||
Serial.println(F("c - convert file to csv")); | |||
Serial.println(F("d - dump data to Serial")); | |||
Serial.println(F("e - overrun error details")); | |||
Serial.println(F("l - list files")); | |||
Serial.println(F("r - record data")); | |||
Serial.println(F("t - test without logging")); | |||
while(!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
#if WDT_YIELD_TIME_MICROS | |||
Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||
SysCall::halt(); | |||
#endif | |||
char c = tolower(Serial.read()); | |||
// Discard extra Serial data. | |||
do { | |||
delay(10); | |||
} while (Serial.available() && Serial.read() >= 0); | |||
if (ERROR_LED_PIN >= 0) { | |||
digitalWrite(ERROR_LED_PIN, LOW); | |||
} | |||
if (c == 'b') { | |||
openBinFile(); | |||
} else if (c == 'c') { | |||
binaryToCsv(); | |||
} else if (c == 'd') { | |||
dumpData(); | |||
} else if (c == 'e') { | |||
checkOverrun(); | |||
} else if (c == 'l') { | |||
Serial.println(F("\nls:")); | |||
sd.ls(&Serial, LS_SIZE); | |||
} else if (c == 'r') { | |||
logData(); | |||
} else if (c == 't') { | |||
testSensor(); | |||
} else { | |||
Serial.println(F("Invalid entry")); | |||
} | |||
} |
@@ -0,0 +1,2 @@ | |||
// Empty file with name LowLatencyLoggerMPU6050.ino to make IDE happy. | |||
@@ -0,0 +1,51 @@ | |||
// User data functions. Modify these functions for your data items. | |||
#include "UserTypes.h" | |||
#include "Wire.h" | |||
#include "I2Cdev.h" | |||
#include "MPU6050.h" | |||
//------------------------------------------------------------------------------ | |||
MPU6050 mpu; | |||
static uint32_t startMicros; | |||
// Acquire a data record. | |||
void acquireData(data_t* data) { | |||
data->time = micros(); | |||
mpu.getMotion6(&data->ax, &data->ay, &data->az, | |||
&data->gx, &data->gy, &data->gz); | |||
} | |||
// setup AVR I2C | |||
void userSetup() { | |||
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE | |||
Wire.begin(); | |||
Wire.setClock(400000); | |||
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE | |||
Fastwire::setup(400, true); | |||
#endif | |||
mpu.initialize(); | |||
} | |||
// Print a data record. | |||
void printData(Print* pr, data_t* data) { | |||
if (startMicros == 0) { | |||
startMicros = data->time; | |||
} | |||
pr->print(data->time- startMicros); | |||
pr->write(','); | |||
pr->print(data->ax); | |||
pr->write(','); | |||
pr->print(data->ay); | |||
pr->write(','); | |||
pr->print(data->az); | |||
pr->write(','); | |||
pr->print(data->gx); | |||
pr->write(','); | |||
pr->print(data->gy); | |||
pr->write(','); | |||
pr->println(data->gz); | |||
} | |||
// Print data header. | |||
void printHeader(Print* pr) { | |||
startMicros = 0; | |||
pr->println(F("micros,ax,ay,az,gx,gy,gz")); | |||
} |
@@ -0,0 +1,18 @@ | |||
#ifndef UserTypes_h | |||
#define UserTypes_h | |||
#include "Arduino.h" | |||
#define FILE_BASE_NAME "mpuraw" | |||
struct data_t { | |||
unsigned long time; | |||
int16_t ax; | |||
int16_t ay; | |||
int16_t az; | |||
int16_t gx; | |||
int16_t gy; | |||
int16_t gz; | |||
}; | |||
void acquireData(data_t* data); | |||
void printData(Print* pr, data_t* data); | |||
void printHeader(Print* pr); | |||
void userSetup(); | |||
#endif // UserTypes_h |
@@ -25,9 +25,9 @@ void setup() { | |||
SysCall::yield(); | |||
} | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -49,9 +49,9 @@ void loop() { | |||
cout << F("FreeStack: ") << FreeStack() << endl; | |||
// initialize the SD card at SPI_FULL_SPEED for best performance. | |||
// try SPI_HALF_SPEED if bus errors occur. | |||
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -8,10 +8,10 @@ | |||
// to 10 to disable the Ethernet controller. | |||
const int8_t DISABLE_CHIP_SELECT = -1; | |||
// | |||
// Test with reduced SPI speed for breadboards. | |||
// Change spiSpeed to SPI_FULL_SPEED for better performance | |||
// Use SPI_QUARTER_SPEED for even slower SPI bus speed | |||
const uint8_t spiSpeed = SPI_HALF_SPEED; | |||
// Test with reduced SPI speed for breadboards. SD_SCK_MHZ(4) will select | |||
// the highest speed supported by the board that is not over 4 MHz. | |||
// Change SPI_SPEED to SD_SCK_MHZ(50) for best performance. | |||
#define SPI_SPEED SD_SCK_MHZ(4) | |||
//------------------------------------------------------------------------------ | |||
// File system object. | |||
SdFat sd; | |||
@@ -28,7 +28,7 @@ int chipSelect; | |||
void cardOrSpeed() { | |||
cout << F("Try another SD card or reduce the SPI bus speed.\n"); | |||
cout << F("Edit spiSpeed in this program to change it.\n"); | |||
cout << F("Edit SPI_SPEED in this program to change it.\n"); | |||
} | |||
void reformatMsg() { | |||
@@ -98,7 +98,7 @@ void loop() { | |||
pinMode(DISABLE_CHIP_SELECT, OUTPUT); | |||
digitalWrite(DISABLE_CHIP_SELECT, HIGH); | |||
} | |||
if (!sd.begin(chipSelect, spiSpeed)) { | |||
if (!sd.begin(chipSelect, SPI_SPEED)) { | |||
if (sd.card()->errorCode()) { | |||
cout << F( | |||
"\nSD initialization failed.\n" |
@@ -3,11 +3,7 @@ | |||
* can be used for high speed data logging. | |||
* | |||
* This program simulates logging from a source that produces | |||
* data at a constant rate of one block every MICROS_PER_BLOCK. | |||
* | |||
* If a high quality SanDisk card is used with this program | |||
* no overruns occur and the maximum block write time is | |||
* under 2000 micros. | |||
* data at a constant rate of RATE_KB_PER_SEC. | |||
* | |||
* Note: Apps should create a very large file then truncates it | |||
* to the length that is used for a logging. It only takes | |||
@@ -21,11 +17,15 @@ | |||
// SD chip select pin | |||
const uint8_t chipSelect = SS; | |||
// number of blocks in the contiguous file | |||
const uint32_t BLOCK_COUNT = 10000UL; | |||
const uint32_t RATE_KB_PER_SEC = 100; | |||
const uint32_t TEST_TIME_SEC = 100; | |||
// time to produce a block of data | |||
const uint32_t MICROS_PER_BLOCK = 10000; | |||
// Time between printing progress dots | |||
const uint32_t DOT_TIME_MS = 5000UL; | |||
// number of blocks in the contiguous file | |||
const uint32_t BLOCK_COUNT = (1000*RATE_KB_PER_SEC*TEST_TIME_SEC + 511)/512; | |||
// file system | |||
SdFat sd; | |||
@@ -42,13 +42,6 @@ ArduinoOutStream cout(Serial); | |||
// store error strings in flash to save RAM | |||
#define error(s) sd.errorHalt(F(s)) | |||
//------------------------------------------------------------------------------ | |||
// log of first overruns | |||
#define OVER_DIM 20 | |||
struct { | |||
uint32_t block; | |||
uint32_t micros; | |||
} over[OVER_DIM]; | |||
//------------------------------------------------------------------------------ | |||
void setup(void) { | |||
Serial.begin(9600); | |||
@@ -71,9 +64,9 @@ void loop(void) { | |||
cout << F("FreeStack: ") << FreeStack() << endl; | |||
// initialize the SD card at SPI_FULL_SPEED for best performance. | |||
// try SPI_HALF_SPEED if bus errors occur. | |||
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -81,7 +74,7 @@ void loop(void) { | |||
sd.remove("RawWrite.txt"); | |||
// create a contiguous file | |||
if (!file.createContiguous(sd.vwd(), "RawWrite.txt", 512UL*BLOCK_COUNT)) { | |||
if (!file.createContiguous("RawWrite.txt", 512UL*BLOCK_COUNT)) { | |||
error("createContiguous failed"); | |||
} | |||
// get the location of the file's blocks | |||
@@ -104,28 +97,48 @@ void loop(void) { | |||
pCache[i + 63] = '\n'; | |||
} | |||
cout << F("Start raw write of ") << file.fileSize() << F(" bytes at\n"); | |||
cout << 512000000UL/MICROS_PER_BLOCK << F(" bytes per second\n"); | |||
cout << F("Please wait ") << (BLOCK_COUNT*MICROS_PER_BLOCK)/1000000UL; | |||
cout << F(" seconds\n"); | |||
cout << F("Start raw write of ") << file.fileSize()/1000UL << F(" KB\n"); | |||
cout << F("Target rate: ") << RATE_KB_PER_SEC << F(" KB/sec\n"); | |||
cout << F("Target time: ") << TEST_TIME_SEC << F(" seconds\n"); | |||
// tell card to setup for multiple block write with pre-erase | |||
if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) { | |||
error("writeStart failed"); | |||
} | |||
// init stats | |||
uint16_t overruns = 0; | |||
delay(1000); | |||
uint32_t dotCount = 0; | |||
uint32_t maxQueuePrint = 0; | |||
uint32_t maxWriteTime = 0; | |||
uint32_t t = micros(); | |||
uint32_t tNext = t; | |||
uint32_t minWriteTime = 9999999; | |||
uint32_t totalWriteTime = 0; | |||
uint32_t maxQueueSize = 0; | |||
uint32_t nWrite = 0; | |||
uint32_t b = 0; | |||
// write data | |||
for (uint32_t b = 0; b < BLOCK_COUNT; b++) { | |||
// write must be done by this time | |||
tNext += MICROS_PER_BLOCK; | |||
uint32_t startTime = millis(); | |||
while (nWrite < BLOCK_COUNT) { | |||
uint32_t nProduced = RATE_KB_PER_SEC*(millis() - startTime)/512UL; | |||
uint32_t queueSize = nProduced - nWrite; | |||
if (queueSize == 0) continue; | |||
if (queueSize > maxQueueSize) { | |||
maxQueueSize = queueSize; | |||
} | |||
if ((millis() - startTime - dotCount*DOT_TIME_MS) > DOT_TIME_MS) { | |||
if (maxQueueSize != maxQueuePrint) { | |||
cout << F("\nQ: ") << maxQueueSize << endl; | |||
maxQueuePrint = maxQueueSize; | |||
} else { | |||
cout << "."; | |||
if (++dotCount%10 == 0) { | |||
cout << endl; | |||
} | |||
} | |||
} | |||
// put block number at start of first line in block | |||
uint32_t n = b; | |||
uint32_t n = b++; | |||
for (int8_t d = 5; d >= 0; d--) { | |||
pCache[d] = n || d == 5 ? n % 10 + '0' : ' '; | |||
n /= 10; | |||
@@ -136,45 +149,30 @@ void loop(void) { | |||
error("writeData failed"); | |||
} | |||
tw = micros() - tw; | |||
totalWriteTime += tw; | |||
// check for max write time | |||
if (tw > maxWriteTime) { | |||
maxWriteTime = tw; | |||
} | |||
// check for overrun | |||
if (micros() > tNext) { | |||
if (overruns < OVER_DIM) { | |||
over[overruns].block = b; | |||
over[overruns].micros = tw; | |||
} | |||
overruns++; | |||
// advance time to reflect overrun | |||
tNext = micros(); | |||
} else { | |||
// wait for time to write next block | |||
while(micros() < tNext); | |||
if (tw < minWriteTime) { | |||
minWriteTime = tw; | |||
} | |||
nWrite++; | |||
} | |||
// total write time | |||
t = micros() - t; | |||
uint32_t endTime = millis(); | |||
uint32_t avgWriteTime = totalWriteTime/BLOCK_COUNT; | |||
// end multiple block write mode | |||
if (!sd.card()->writeStop()) { | |||
error("writeStop failed"); | |||
} | |||
cout << F("Done\n"); | |||
cout << F("Elapsed time: ") << setprecision(3)<< 1.e-6*t; | |||
cout << F("\nDone\n"); | |||
cout << F("maxQueueSize: ") << maxQueueSize << endl; | |||
cout << F("Elapsed time: ") << setprecision(3)<< 1.e-3*(endTime - startTime); | |||
cout << F(" seconds\n"); | |||
cout << F("Max write time: ") << maxWriteTime << F(" micros\n"); | |||
cout << F("Overruns: ") << overruns << endl; | |||
if (overruns) { | |||
uint8_t n = overruns > OVER_DIM ? OVER_DIM : overruns; | |||
cout << F("fileBlock,micros") << endl; | |||
for (uint8_t i = 0; i < n; i++) { | |||
cout << over[i].block << ',' << over[i].micros << endl; | |||
} | |||
} | |||
cout << F("Min block write time: ") << minWriteTime << F(" micros\n"); | |||
cout << F("Max block write time: ") << maxWriteTime << F(" micros\n"); | |||
cout << F("Avg block write time: ") << avgWriteTime << F(" micros\n"); | |||
// close file for next pass of loop | |||
file.close(); | |||
Serial.println(); |
@@ -1,111 +0,0 @@ | |||
// Function to read a CSV text file one field at a time. | |||
// | |||
#include <SPI.h> | |||
#include <SdFat.h> | |||
#define CS_PIN SS | |||
SdFat SD; | |||
File file; | |||
/* | |||
* Read a file one field at a time. | |||
* | |||
* file - File to read. | |||
* | |||
* str - Character array for the field. | |||
* | |||
* size - Size of str array. | |||
* | |||
* delim - String containing field delimiters. | |||
* | |||
* return - length of field including terminating delimiter. | |||
* | |||
* Note, the last character of str will not be a delimiter if | |||
* a read error occurs, the field is too long, or the file | |||
* does not end with a delimiter. Consider this an error | |||
* if not at end-of-file. | |||
* | |||
*/ | |||
size_t readField(File* file, char* str, size_t size, const char* delim) { | |||
char ch; | |||
size_t n = 0; | |||
while ((n + 1) < size && file->read(&ch, 1) == 1) { | |||
// Delete CR. | |||
if (ch == '\r') { | |||
continue; | |||
} | |||
str[n++] = ch; | |||
if (strchr(delim, ch)) { | |||
break; | |||
} | |||
} | |||
str[n] = '\0'; | |||
return n; | |||
} | |||
//------------------------------------------------------------------------------ | |||
#define errorHalt(msg) {Serial.println(F(msg)); SysCall::halt();} | |||
//------------------------------------------------------------------------------ | |||
void setup() { | |||
Serial.begin(9600); | |||
// Wait for USB Serial | |||
while (!Serial) { | |||
SysCall::yield(); | |||
} | |||
Serial.println("Type any character to start"); | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
// Initialize the SD. | |||
if (!SD.begin(CS_PIN)) errorHalt("begin failed"); | |||
// Create or open the file. | |||
file = SD.open("READTEST.TXT", FILE_WRITE); | |||
if (!file) errorHalt("open failed"); | |||
// Rewind file so test data is not appended. | |||
file.rewind(); | |||
// Write test data. | |||
file.print(F( | |||
"field_1_1,field_1_2,field_1_3\r\n" | |||
"field_2_1,field_2_2,field_2_3\r\n" | |||
"field_3_1,field_3_2\r\n" // missing a field | |||
"field_4_1,field_4_2,field_4_3\r\n" | |||
"field_5_1,field_5_2,field_5_3" // no delimiter | |||
)); | |||
// Rewind the file for read. | |||
file.rewind(); | |||
size_t n; // Length of returned field with delimiter. | |||
char str[20]; // Must hold longest field with delimiter and zero byte. | |||
// Read the file and print fields. | |||
while (true) { | |||
n = readField(&file, str, sizeof(str), ",\n"); | |||
// done if Error or at EOF. | |||
if (n == 0) break; | |||
// Print the type of delimiter. | |||
if (str[n-1] == ',' || str[n-1] == '\n') { | |||
Serial.print(str[n-1] == ',' ? F("comma: ") : F("endl: ")); | |||
// Remove the delimiter. | |||
str[n-1] = 0; | |||
} else { | |||
// At eof, too long, or read error. Too long is error. | |||
Serial.print(file.available() ? F("error: ") : F("eof: ")); | |||
} | |||
// Print the field. | |||
Serial.println(str); | |||
} | |||
file.close(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void loop() { | |||
} | |||
@@ -0,0 +1,120 @@ | |||
/* | |||
* This example reads a simple CSV, comma-separated values, file. | |||
* Each line of the file has a label and three values, a long and two floats. | |||
*/ | |||
#include <SPI.h> | |||
#include "SdFat.h" | |||
// SD chip select pin | |||
const uint8_t chipSelect = SS; | |||
// file system object | |||
SdFat sd; | |||
// create Serial stream | |||
ArduinoOutStream cout(Serial); | |||
char fileName[] = "testfile.csv"; | |||
//------------------------------------------------------------------------------ | |||
// store error strings in flash to save RAM | |||
#define error(s) sd.errorHalt(F(s)) | |||
//------------------------------------------------------------------------------ | |||
// read and print CSV test file | |||
void readFile() { | |||
long lg = 0; | |||
float f1, f2; | |||
char text[10]; | |||
char c1, c2, c3; // space for commas. | |||
// open input file | |||
ifstream sdin(fileName); | |||
// check for open error | |||
if (!sdin.is_open()) { | |||
error("open"); | |||
} | |||
// read until input fails | |||
while (1) { | |||
// Get text field. | |||
sdin.get(text, sizeof(text), ','); | |||
// Assume EOF if fail. | |||
if (sdin.fail()) { | |||
break; | |||
} | |||
// Get commas and numbers. | |||
sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2; | |||
// Skip CR/LF. | |||
sdin.skipWhite(); | |||
if (sdin.fail()) { | |||
error("bad input"); | |||
} | |||
// error in line if not commas | |||
if (c1 != ',' || c2 != ',' || c3 != ',') { | |||
error("comma"); | |||
} | |||
// print in six character wide columns | |||
cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl; | |||
} | |||
// Error in an input line if file is not at EOF. | |||
if (!sdin.eof()) { | |||
error("readFile"); | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
// write test file | |||
void writeFile() { | |||
// create or open and truncate output file | |||
ofstream sdout(fileName); | |||
// write file from string stored in flash | |||
sdout << F( | |||
"Line 1,1,2.3,4.5\n" | |||
"Line 2,6,7.8,9.0\n" | |||
"Line 3,9,8.7,6.5\n" | |||
"Line 4,-4,-3.2,-1\n") << flush; | |||
// check for any errors | |||
if (!sdout) { | |||
error("writeFile"); | |||
} | |||
sdout.close(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void setup() { | |||
Serial.begin(9600); | |||
// Wait for USB Serial | |||
while (!Serial) { | |||
SysCall::yield(); | |||
} | |||
cout << F("Type any character to start\n"); | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
// create test file | |||
writeFile(); | |||
cout << endl; | |||
// read and print test | |||
readFile(); | |||
cout << "\nDone!" << endl; | |||
} | |||
void loop() {} |
@@ -7,7 +7,6 @@ | |||
** MOSI - pin 11 | |||
** MISO - pin 12 | |||
** CLK - pin 13 | |||
** CS - pin 4 | |||
created Nov 2010 | |||
by David A. Mellis | |||
@@ -17,30 +16,24 @@ | |||
This example code is in the public domain. | |||
*/ | |||
#define SD_CS_PIN SS | |||
#include <SPI.h> | |||
//#include <SD.h> | |||
#include "SdFat.h" | |||
SdFat SD; | |||
#define SD_CS_PIN SS | |||
File myFile; | |||
void setup() | |||
{ | |||
void setup() { | |||
// Open serial communications and wait for port to open: | |||
Serial.begin(9600); | |||
// Wait for USB Serial | |||
while (!Serial) { | |||
SysCall::yield(); | |||
; // wait for serial port to connect. Needed for native USB port only | |||
} | |||
Serial.print("Initializing SD card..."); | |||
// On the Ethernet Shield, CS is pin 4. It's set as an output by default. | |||
// Note that even if it's not used as the CS pin, the hardware SS pin | |||
// (10 on most Arduino boards, 53 on the Mega) must be left as an output | |||
// or the SD library functions will not work. | |||
pinMode(10, OUTPUT); | |||
if (!SD.begin(SD_CS_PIN)) { | |||
Serial.println("initialization failed!"); | |||
@@ -81,8 +74,7 @@ void setup() | |||
} | |||
} | |||
void loop() | |||
{ | |||
void loop() { | |||
// nothing happens after setup | |||
} | |||
@@ -1,77 +0,0 @@ | |||
// Ported to SdFat from the native Arduino SD library example by Bill Greiman | |||
// On the Ethernet Shield, CS is pin 4. SdFat handles setting SS | |||
const int chipSelect = 4; | |||
/* | |||
SD card read/write | |||
This example shows how to read and write data to and from an SD card file | |||
The circuit: | |||
* SD card attached to SPI bus as follows: | |||
** MOSI - pin 11 | |||
** MISO - pin 12 | |||
** CLK - pin 13 | |||
** CS - pin 4 | |||
created Nov 2010 | |||
by David A. Mellis | |||
updated 2 Dec 2010 | |||
by Tom Igoe | |||
modified by Bill Greiman 11 Apr 2011 | |||
This example code is in the public domain. | |||
*/ | |||
#include <SPI.h> | |||
#include "SdFat.h" | |||
SdFat sd; | |||
SdFile myFile; | |||
void setup() { | |||
Serial.begin(9600); | |||
// Wait for USB Serial | |||
while (!Serial) { | |||
SysCall::yield(); | |||
} | |||
Serial.println("Type any character to start"); | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
// Initialize SdFat or print a detailed error message and halt | |||
// Use half speed like the native library. | |||
// change to SPI_FULL_SPEED for more performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
sd.initErrorHalt(); | |||
} | |||
// open the file for write at end like the Native SD library | |||
if (!myFile.open("test.txt", O_RDWR | O_CREAT | O_AT_END)) { | |||
sd.errorHalt("opening test.txt for write failed"); | |||
} | |||
// if the file opened okay, write to it: | |||
Serial.print("Writing to test.txt..."); | |||
myFile.println("testing 1, 2, 3."); | |||
// close the file: | |||
myFile.close(); | |||
Serial.println("done."); | |||
// re-open the file for reading: | |||
if (!myFile.open("test.txt", O_READ)) { | |||
sd.errorHalt("opening test.txt for read failed"); | |||
} | |||
Serial.println("test.txt:"); | |||
// read from the file until there's nothing else in it: | |||
int data; | |||
while ((data = myFile.read()) >= 0) { | |||
Serial.write(data); | |||
} | |||
// close the file: | |||
myFile.close(); | |||
} | |||
void loop() { | |||
// nothing happens after setup | |||
} | |||
@@ -0,0 +1,176 @@ | |||
/* | |||
* Example use of two SPI ports on an STM32 board. | |||
* Note SPI speed is limited to 18 MHz. | |||
*/ | |||
#include <SPI.h> | |||
#include "SdFat.h" | |||
#include "FreeStack.h" | |||
// set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes | |||
// Use first SPI port | |||
SdFat sd1(1); | |||
// SdFatEX sd1(1); | |||
const uint8_t SD1_CS = PA4; // chip select for sd1 | |||
// Use second SPI port | |||
SdFat sd2(2); | |||
// SdFatEX sd2(2); | |||
const uint8_t SD2_CS = PB12; // chip select for sd2 | |||
const uint8_t BUF_DIM = 100; | |||
uint8_t buf[BUF_DIM]; | |||
const uint32_t FILE_SIZE = 1000000; | |||
const uint16_t NWRITE = FILE_SIZE/BUF_DIM; | |||
//------------------------------------------------------------------------------ | |||
// print error msg, any SD error codes, and halt. | |||
// store messages in flash | |||
#define errorExit(msg) errorHalt(F(msg)) | |||
#define initError(msg) initErrorHalt(F(msg)) | |||
//------------------------------------------------------------------------------ | |||
void setup() { | |||
Serial.begin(9600); | |||
// Wait for USB Serial | |||
while (!Serial) { | |||
SysCall::yield(); | |||
} | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
// fill buffer with known data | |||
for (size_t i = 0; i < sizeof(buf); i++) { | |||
buf[i] = i; | |||
} | |||
Serial.println(F("type any character to start")); | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
// initialize the first card | |||
if (!sd1.begin(SD1_CS, SD_SCK_MHZ(18))) { | |||
sd1.initError("sd1:"); | |||
} | |||
// create Dir1 on sd1 if it does not exist | |||
if (!sd1.exists("/Dir1")) { | |||
if (!sd1.mkdir("/Dir1")) { | |||
sd1.errorExit("sd1.mkdir"); | |||
} | |||
} | |||
// initialize the second card | |||
if (!sd2.begin(SD2_CS, SD_SCK_MHZ(18))) { | |||
sd2.initError("sd2:"); | |||
} | |||
// create Dir2 on sd2 if it does not exist | |||
if (!sd2.exists("/Dir2")) { | |||
if (!sd2.mkdir("/Dir2")) { | |||
sd2.errorExit("sd2.mkdir"); | |||
} | |||
} | |||
// list root directory on both cards | |||
Serial.println(F("------sd1 root-------")); | |||
sd1.ls(); | |||
Serial.println(F("------sd2 root-------")); | |||
sd2.ls(); | |||
// make /Dir1 the default directory for sd1 | |||
if (!sd1.chdir("/Dir1")) { | |||
sd1.errorExit("sd1.chdir"); | |||
} | |||
// remove test.bin from /Dir1 directory of sd1 | |||
if (sd1.exists("test.bin")) { | |||
if (!sd1.remove("test.bin")) { | |||
sd2.errorExit("remove test.bin"); | |||
} | |||
} | |||
// make /Dir2 the default directory for sd2 | |||
if (!sd2.chdir("/Dir2")) { | |||
sd2.errorExit("sd2.chdir"); | |||
} | |||
// remove rename.bin from /Dir2 directory of sd2 | |||
if (sd2.exists("rename.bin")) { | |||
if (!sd2.remove("rename.bin")) { | |||
sd2.errorExit("remove rename.bin"); | |||
} | |||
} | |||
// list current directory on both cards | |||
Serial.println(F("------sd1 Dir1-------")); | |||
sd1.ls(); | |||
Serial.println(F("------sd2 Dir2-------")); | |||
sd2.ls(); | |||
Serial.println(F("---------------------")); | |||
// set the current working directory for open() to sd1 | |||
sd1.chvol(); | |||
// create or open /Dir1/test.bin and truncate it to zero length | |||
SdFile file1; | |||
if (!file1.open("test.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||
sd1.errorExit("file1"); | |||
} | |||
Serial.println(F("Writing test.bin to sd1")); | |||
// write data to /Dir1/test.bin on sd1 | |||
for (uint16_t i = 0; i < NWRITE; i++) { | |||
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
sd1.errorExit("sd1.write"); | |||
} | |||
} | |||
// set the current working directory for open() to sd2 | |||
sd2.chvol(); | |||
// create or open /Dir2/copy.bin and truncate it to zero length | |||
SdFile file2; | |||
if (!file2.open("copy.bin", O_WRITE | O_CREAT | O_TRUNC)) { | |||
sd2.errorExit("file2"); | |||
} | |||
Serial.println(F("Copying test.bin to copy.bin")); | |||
// copy file1 to file2 | |||
file1.rewind(); | |||
uint32_t t = millis(); | |||
while (1) { | |||
int n = file1.read(buf, sizeof(buf)); | |||
if (n < 0) { | |||
sd1.errorExit("read1"); | |||
} | |||
if (n == 0) { | |||
break; | |||
} | |||
if ((int)file2.write(buf, n) != n) { | |||
sd2.errorExit("write2"); | |||
} | |||
} | |||
t = millis() - t; | |||
Serial.print(F("File size: ")); | |||
Serial.println(file2.fileSize()); | |||
Serial.print(F("Copy time: ")); | |||
Serial.print(t); | |||
Serial.println(F(" millis")); | |||
// close test.bin | |||
file1.close(); | |||
file2.close(); | |||
// list current directory on both cards | |||
Serial.println(F("------sd1 -------")); | |||
sd1.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
Serial.println(F("------sd2 -------")); | |||
sd2.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
Serial.println(F("---------------------")); | |||
Serial.println(F("Renaming copy.bin")); | |||
// rename the copy | |||
if (!sd2.rename("copy.bin", "rename.bin")) { | |||
sd2.errorExit("sd2.rename"); | |||
} | |||
// list current directory on both cards | |||
Serial.println(F("------sd1 -------")); | |||
sd1.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
Serial.println(F("------sd2 -------")); | |||
sd2.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
Serial.println(F("---------------------")); | |||
Serial.println(F("Done")); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void loop() {} |
@@ -26,9 +26,9 @@ | |||
// Adafruit SD shields and modules: pin 10 | |||
const uint8_t chipSelect = SS; | |||
// Change spiSpeed to SPI_FULL_SPEED for better performance | |||
// Use SPI_QUARTER_SPEED for even slower SPI bus speed | |||
const uint8_t spiSpeed = SPI_HALF_SPEED; | |||
// Initialize at highest supported speed not over 50 MHz. | |||
// Reduce max speed if errors occur. | |||
#define SPI_SPEED SD_SCK_MHZ(50) | |||
// Serial output stream | |||
ArduinoOutStream cout(Serial); | |||
@@ -65,11 +65,9 @@ char noName[] = "NO NAME "; | |||
char fat16str[] = "FAT16 "; | |||
char fat32str[] = "FAT32 "; | |||
//------------------------------------------------------------------------------ | |||
#define sdError(msg) sdError_F(F(msg)) | |||
void sdError_F(const __FlashStringHelper* str) { | |||
cout << F("error: "); | |||
cout << str << endl; | |||
#define sdError(msg) {cout << F("error: ") << F(msg) << endl; sdErrorHalt();} | |||
//------------------------------------------------------------------------------ | |||
void sdErrorHalt() { | |||
if (card.errorCode()) { | |||
cout << F("SD error: ") << hex << int(card.errorCode()); | |||
cout << ',' << int(card.errorData()) << dec << endl; | |||
@@ -499,7 +497,7 @@ void setup() { | |||
return; | |||
} | |||
if (!card.begin(chipSelect, spiSpeed)) { | |||
if (!card.begin(chipSelect, SPI_SPEED)) { | |||
cout << F( | |||
"\nSD initialization failure!\n" | |||
"Is the SD card inserted correctly?\n" |
@@ -30,16 +30,7 @@ uint32_t cardSize; | |||
uint32_t eraseSize; | |||
//------------------------------------------------------------------------------ | |||
// store error strings in flash | |||
#define sdErrorMsg(msg) sdErrorMsg_F(F(msg)); | |||
void sdErrorMsg_F(const __FlashStringHelper* str) { | |||
cout << str << endl; | |||
if (sd.card()->errorCode()) { | |||
cout << F("SD errorCode: "); | |||
cout << hex << int(sd.card()->errorCode()) << endl; | |||
cout << F("SD errorData: "); | |||
cout << int(sd.card()->errorData()) << dec << endl; | |||
} | |||
} | |||
#define sdErrorMsg(msg) sd.errorPrint(F(msg)); | |||
//------------------------------------------------------------------------------ | |||
uint8_t cidDmp() { | |||
cid_t cid; | |||
@@ -97,17 +88,13 @@ uint8_t csdDmp() { | |||
//------------------------------------------------------------------------------ | |||
// print partition table | |||
uint8_t partDmp() { | |||
cache_t *p = sd.vol()->cacheClear(); | |||
if (!p) { | |||
sdErrorMsg("cacheClear failed"); | |||
return false; | |||
} | |||
if (!sd.card()->readBlock(0, p->data)) { | |||
mbr_t mbr; | |||
if (!sd.card()->readBlock(0, (uint8_t*)&mbr)) { | |||
sdErrorMsg("read MBR failed"); | |||
return false; | |||
} | |||
for (uint8_t ip = 1; ip < 5; ip++) { | |||
part_t *pt = &p->mbr.part[ip - 1]; | |||
part_t *pt = &mbr.part[ip - 1]; | |||
if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) { | |||
cout << F("\nNo MBR. Assuming Super Floppy format.\n"); | |||
return true; | |||
@@ -116,7 +103,7 @@ uint8_t partDmp() { | |||
cout << F("\nSD Partition Table\n"); | |||
cout << F("part,boot,type,start,length\n"); | |||
for (uint8_t ip = 1; ip < 5; ip++) { | |||
part_t *pt = &p->mbr.part[ip - 1]; | |||
part_t *pt = &mbr.part[ip - 1]; | |||
cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type); | |||
cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl; | |||
} | |||
@@ -183,9 +170,9 @@ void loop() { | |||
} | |||
uint32_t t = millis(); | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.cardBegin(SD_CHIP_SELECT, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.cardBegin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) { | |||
sdErrorMsg("\ncardBegin failed"); | |||
return; | |||
} |
@@ -5,7 +5,7 @@ | |||
// | |||
#include <SPI.h> | |||
#include "SdFat.h" | |||
#if SD_SPI_CONFIGURATION >= 3 // Must be set in SdFat/SdFatConfig.h | |||
#if ENABLE_SOFTWARE_SPI_CLASS // Must be set in SdFat/SdFatConfig.h | |||
// | |||
// Pin numbers in templates must be constants. | |||
const uint8_t SOFT_MISO_PIN = 12; | |||
@@ -53,6 +53,6 @@ void setup() { | |||
} | |||
//------------------------------------------------------------------------------ | |||
void loop() {} | |||
#else // SD_SPI_CONFIGURATION >= 3 | |||
#error SD_SPI_CONFIGURATION must be set to 3 in SdFat/SdFatConfig.h | |||
#endif //SD_SPI_CONFIGURATION >= 3 | |||
#else // ENABLE_SOFTWARE_SPI_CLASS | |||
#error ENABLE_SOFTWARE_SPI_CLASS must be set non-zero in SdFat/SdFatConfig.h | |||
#endif //ENABLE_SOFTWARE_SPI_CLASS |
@@ -39,7 +39,10 @@ void setup() { | |||
SysCall::yield(); | |||
} | |||
Serial.println(F("Starting test")); | |||
if (!sd.begin(SD_CS_PIN)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||
sd.errorHalt(); | |||
} | |||
@@ -137,10 +140,11 @@ void setup() { | |||
case 3: | |||
for (uint16_t i = 0; i < 10000; i++) { | |||
uint32_t n = i + 1000000000UL; | |||
#if PRINT_FIELD | |||
stdioFile.printField(i + 1000000000UL, '\n'); | |||
stdioFile.printField(n, '\n'); | |||
#else // PRINT_FIELD | |||
stdioFile.println(i + 1000000000UL); | |||
stdioFile.println(n); | |||
#endif // PRINT_FIELD | |||
} | |||
break; |
@@ -1,231 +0,0 @@ | |||
/* | |||
* Example use of three SD cards. | |||
*/ | |||
#include <SPI.h> | |||
#include "SdFat.h" | |||
#include "FreeStack.h" | |||
#if SD_SPI_CONFIGURATION >= 3 // Must be set in SdFat/SdFatConfig.h | |||
// SD1 is a microSD on hardware SPI pins 50-52 | |||
// Using my fast custom SPI | |||
SdFat sd1; | |||
const uint8_t SD1_CS = 53; | |||
// SD2 is a Catalex shield on hardware SPI pins 50-52 | |||
// Using the standard Arduino SPI library | |||
SdFatLibSpi sd2; | |||
const uint8_t SD2_CS = 4; | |||
// SD3 is a Adafruit data logging shield on pins 10-13 | |||
// Using Software SPI | |||
SdFatSoftSpi<12, 11, 13> sd3; | |||
const uint8_t SD3_CS = 10; | |||
const uint8_t BUF_DIM = 100; | |||
uint8_t buf[BUF_DIM]; | |||
const uint32_t FILE_SIZE = 1000000; | |||
const uint16_t NWRITE = FILE_SIZE/BUF_DIM; | |||
//------------------------------------------------------------------------------ | |||
// print error msg, any SD error codes, and halt. | |||
// store messages in flash | |||
#define errorExit(msg) errorHalt(F(msg)) | |||
#define initError(msg) initErrorHalt(F(msg)) | |||
//------------------------------------------------------------------------------ | |||
void list() { | |||
// list current directory on all cards | |||
Serial.println(F("------sd1-------")); | |||
sd1.ls("/", LS_SIZE|LS_R); | |||
Serial.println(F("------sd2-------")); | |||
sd2.ls("/", LS_SIZE|LS_R); | |||
Serial.println(F("------sd3-------")); | |||
sd3.ls("/", LS_SIZE|LS_R); | |||
Serial.println(F("---------------------")); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void setup() { | |||
Serial.begin(9600); | |||
// Wait for USB Serial | |||
while (!Serial) { | |||
SysCall::yield(); | |||
} | |||
Serial.print(F("FreeStack: ")); | |||
Serial.println(FreeStack()); | |||
// fill buffer with known data | |||
for (size_t i = 0; i < sizeof(buf); i++) { | |||
buf[i] = i; | |||
} | |||
Serial.println(F("type any character to start")); | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
// disable sd2 while initializing sd1 | |||
pinMode(SD2_CS, OUTPUT); | |||
digitalWrite(SD2_CS, HIGH); | |||
// initialize the first card | |||
if (!sd1.begin(SD1_CS)) { | |||
sd1.initError("sd1:"); | |||
} | |||
// initialize the second card | |||
if (!sd2.begin(SD2_CS)) { | |||
sd2.initError("sd2:"); | |||
} | |||
// initialize the third card | |||
if (!sd3.begin(SD3_CS)) { | |||
sd3.initError("sd3:"); | |||
} | |||
Serial.println(F("Cards OK - creating directories")); | |||
// create Dir1 on sd1 if it does not exist | |||
if (!sd1.exists("/Dir1")) { | |||
if (!sd1.mkdir("/Dir1")) { | |||
sd1.errorExit("sd1.mkdir"); | |||
} | |||
} | |||
// make /Dir1 the default directory for sd1 | |||
if (!sd1.chdir("/Dir1")) { | |||
sd1.errorExit("sd1.chdir"); | |||
} | |||
// create Dir2 on sd2 if it does not exist | |||
if (!sd2.exists("/Dir2")) { | |||
if (!sd2.mkdir("/Dir2")) { | |||
sd2.errorExit("sd2.mkdir"); | |||
} | |||
} | |||
// make /Dir2 the default directory for sd2 | |||
if (!sd2.chdir("/Dir2")) { | |||
sd2.errorExit("sd2.chdir"); | |||
} | |||
// create Dir3 on sd3 if it does not exist | |||
if (!sd3.exists("/Dir3")) { | |||
if (!sd3.mkdir("/Dir3")) { | |||
sd2.errorExit("sd3.mkdir"); | |||
} | |||
} | |||
// make /Dir3 the default directory for sd3 | |||
if (!sd3.chdir("/Dir3")) { | |||
sd3.errorExit("sd3.chdir"); | |||
} | |||
Serial.println(F("Directories created - removing old files")); | |||
if (sd1.exists("TEST1.bin")) { | |||
if (!sd1.remove("TEST1.bin")) { | |||
sd1.errorExit("sd1.remove"); | |||
} | |||
} | |||
if (sd2.exists("TEST2.bin")) { | |||
if (!sd2.remove("TEST2.bin")) { | |||
sd2.errorExit("sd2.remove"); | |||
} | |||
} | |||
if (sd3.exists("TEST3.bin")) { | |||
if (!sd3.remove("TEST3.bin")) { | |||
sd2.errorExit("sd3.remove"); | |||
} | |||
} | |||
Serial.println("Initial SD directories"); | |||
list(); | |||
// create or open /Dir1/TEST1.bin and truncate it to zero length | |||
SdFile file1; | |||
if (!file1.open(&sd1, "TEST1.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||
sd1.errorExit("file1"); | |||
} | |||
Serial.println(F("Writing SD1:/Dir1/TEST1.bin")); | |||
// write data to /Dir1/TEST1.bin on sd1 | |||
for (uint16_t i = 0; i < NWRITE; i++) { | |||
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
sd1.errorExit("sd1.write"); | |||
} | |||
} | |||
file1.sync(); | |||
list(); | |||
// create or open /Dir2/TEST2.bin and truncate it to zero length | |||
SdFile file2; | |||
if (!file2.open(&sd2, "TEST2.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||
sd2.errorExit("file2"); | |||
} | |||
Serial.println(F("Copying SD1:/Dir1/TEST1.bin to SD2::/Dir2/TEST2.bin")); | |||
// copy file1 to file2 | |||
file1.rewind(); | |||
uint32_t t = millis(); | |||
while (1) { | |||
int n = file1.read(buf, sizeof(buf)); | |||
if (n < 0) { | |||
sd1.errorExit("read1"); | |||
} | |||
if (n == 0) { | |||
break; | |||
} | |||
if ((int)file2.write(buf, n) != n) { | |||
sd2.errorExit("write3"); | |||
} | |||
} | |||
t = millis() - t; | |||
file2.sync(); | |||
Serial.print(F("File size: ")); | |||
Serial.println(file2.fileSize()); | |||
Serial.print(F("Copy time: ")); | |||
Serial.print(t); | |||
Serial.println(F(" millis")); | |||
list(); | |||
// create or open /Dir3/TEST3.bin and truncate it to zero length | |||
SdFile file3; | |||
if (!file3.open(&sd3, "TEST3.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||
sd3.errorExit("file3"); | |||
} | |||
file2.rewind(); | |||
Serial.println(F("Copying SD2:/Dir2/TEST2.bin to SD3:/Dir3/TEST3.bin")); | |||
while (1) { | |||
int n = file2.read(buf, sizeof(buf)); | |||
if (n == 0) { | |||
break; | |||
} | |||
if (n != sizeof(buf)) { | |||
sd2.errorExit("read2"); | |||
} | |||
if ((int)file3.write(buf, n) != n) { | |||
sd3.errorExit("write2"); | |||
} | |||
} | |||
file3.sync(); | |||
list(); | |||
// Verify content of file3 | |||
file3.rewind(); | |||
Serial.println(F("Verifying content of TEST3.bin")); | |||
for (uint16_t i = 0; i < NWRITE; i++) { | |||
if (file3.read(buf, sizeof(buf)) != sizeof(buf)) { | |||
sd3.errorExit("sd3.read"); | |||
} | |||
for (size_t j = 0; j < sizeof(buf); j++) { | |||
if (j != buf[j]) { | |||
sd3.errorExit("Verify error"); | |||
} | |||
} | |||
} | |||
Serial.println(F("Done - Verify OK")); | |||
file1.close(); | |||
file2.close(); | |||
file3.close(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void loop() {} | |||
#else // SD_SPI_CONFIGURATION >= 3 | |||
#error SD_SPI_CONFIGURATION must be set to 3 in SdFat/SdFatConfig.h | |||
#endif //SD_SPI_CONFIGURATION >= 3 |
@@ -83,9 +83,9 @@ void setup(void) { | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -1,4 +1,5 @@ | |||
/* | |||
* Warning This example requires extra RAM and may crash on Uno. | |||
* Example use of two SD cards. | |||
*/ | |||
#include <SPI.h> |
@@ -49,9 +49,9 @@ void setup() { | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
} | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
// Insure no TEST_FILE. | |||
@@ -75,6 +75,7 @@ void setup() { | |||
cout << F("Remove ") << TEST_FILE << endl << endl; | |||
sd.remove(TEST_FILE); | |||
printFreeSpace(); | |||
cout << F("Done") << endl; | |||
} | |||
//------------------------------------------------------------------------------ | |||
void loop() {} |
@@ -29,8 +29,12 @@ uint8_t buf[BUF_SIZE]; | |||
// file system | |||
SdFat sd; | |||
// Set SD_SPI_CONFIGURATION to three to test next two definitions. | |||
// SdFatLibSpi sd; | |||
// Set ENABLE_EXTENDED_TRANSFER_CLASS to use extended SD I/O. | |||
// Requires dedicated use of the SPI bus. | |||
// SdFatEX sd; | |||
// Set ENABLE_SOFTWARE_SPI_CLASS to use software SPI. | |||
// Args are misoPin, mosiPin, sckPin. | |||
// SdFatSoftSpi<6, 7, 5> sd; | |||
@@ -98,9 +102,9 @@ void loop() { | |||
cout << F("FreeStack: ") << FreeStack() << endl; | |||
// initialize the SD card at SPI_FULL_SPEED for best performance. | |||
// try SPI_HALF_SPEED if bus errors occur. | |||
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -79,9 +79,9 @@ void setup() { | |||
SysCall::yield(); | |||
} | |||
// Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -5,7 +5,7 @@ | |||
#include "SdFat.h" | |||
// SD card chip select pin. | |||
const uint8_t SD_CHIP_SELECT = SS; | |||
const uint8_t chipSelect = SS; | |||
//------------------------------------------------------------------------------ | |||
// File system object. | |||
@@ -40,9 +40,9 @@ void setup() { | |||
cin.readline(); | |||
cout << endl; | |||
// Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
if (sd.exists("Folder1") |
@@ -72,9 +72,9 @@ void setup(void) { | |||
} | |||
delay(400); // catch Due reset problem | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -66,9 +66,9 @@ void setup(void) { | |||
SysCall::yield(); | |||
} | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -1,92 +1,130 @@ | |||
/* | |||
* This example reads a simple CSV, comma-separated values, file. | |||
* Each line of the file has a label and three values, a long and two floats. | |||
*/ | |||
#include <SPI.h> | |||
#include "SdFat.h" | |||
// SD chip select pin | |||
const uint8_t chipSelect = SS; | |||
// file system object | |||
SdFat sd; | |||
// Functions to read a CSV text file one field at a time. | |||
// | |||
#include <limits.h> | |||
#include <SPI.h> | |||
// create Serial stream | |||
ArduinoOutStream cout(Serial); | |||
// next line for SD.h | |||
//#include <SD.h> | |||
char fileName[] = "testfile.csv"; | |||
//------------------------------------------------------------------------------ | |||
// store error strings in flash to save RAM | |||
#define error(s) sd.errorHalt(F(s)) | |||
//------------------------------------------------------------------------------ | |||
// read and print CSV test file | |||
void readFile() { | |||
long lg = 0; | |||
float f1, f2; | |||
char text[10]; | |||
char c1, c2, c3; // space for commas. | |||
// next two lines for SdFat | |||
#include <SdFat.h> | |||
SdFat SD; | |||
// open input file | |||
ifstream sdin(fileName); | |||
#define CS_PIN SS | |||
// check for open error | |||
if (!sdin.is_open()) { | |||
error("open"); | |||
} | |||
// example can use comma or semicolon | |||
#define CSV_DELIM ',' | |||
// read until input fails | |||
while (1) { | |||
// Get text field. | |||
sdin.get(text, sizeof(text), ','); | |||
File file; | |||
// Assume EOF if fail. | |||
if (sdin.fail()) { | |||
/* | |||
* Read a file one field at a time. | |||
* | |||
* file - File to read. | |||
* | |||
* str - Character array for the field. | |||
* | |||
* size - Size of str array. | |||
* | |||
* delim - csv delimiter. | |||
* | |||
* return - negative value for failure. | |||
* delimiter, '\n' or zero(EOF) for success. | |||
*/ | |||
int csvReadText(File* file, char* str, size_t size, char delim) { | |||
char ch; | |||
int rtn; | |||
size_t n = 0; | |||
while (true) { | |||
// check for EOF | |||
if (!file->available()) { | |||
rtn = 0; | |||
break; | |||
} | |||
// Get commas and numbers. | |||
sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2; | |||
// Skip CR/LF. | |||
sdin.skipWhite(); | |||
if (sdin.fail()) { | |||
error("bad input"); | |||
if (file->read(&ch, 1) != 1) { | |||
// read error | |||
rtn = -1; | |||
break; | |||
} | |||
// error in line if not commas | |||
if (c1 != ',' || c2 != ',' || c3 != ',') { | |||
error("comma"); | |||
// Delete CR. | |||
if (ch == '\r') { | |||
continue; | |||
} | |||
// print in six character wide columns | |||
cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl; | |||
} | |||
// Error in an input line if file is not at EOF. | |||
if (!sdin.eof()) { | |||
error("readFile"); | |||
if (ch == delim || ch == '\n') { | |||
rtn = ch; | |||
break; | |||
} | |||
if ((n+1) >= size) { | |||
// string too long | |||
rtn = -2; | |||
n--; | |||
break; | |||
} | |||
str[n++] = ch; | |||
} | |||
str[n] = '\0'; | |||
return rtn; | |||
} | |||
//------------------------------------------------------------------------------ | |||
// write test file | |||
void writeFile() { | |||
// create or open and truncate output file | |||
ofstream sdout(fileName); | |||
// write file from string stored in flash | |||
sdout << F( | |||
"Line 1,1,2.3,4.5\n" | |||
"Line 2,6,7.8,9.0\n" | |||
"Line 3,9,8.7,6.5\n" | |||
"Line 4,-4,-3.2,-1\n") << flush; | |||
// check for any errors | |||
if (!sdout) { | |||
error("writeFile"); | |||
} | |||
sdout.close(); | |||
int csvReadInt32(File* file, int32_t* num, char delim) { | |||
char buf[20]; | |||
char* ptr; | |||
int rtn = csvReadText(file, buf, sizeof(buf), delim); | |||
if (rtn < 0) return rtn; | |||
*num = strtol(buf, &ptr, 10); | |||
if (buf == ptr) return -3; | |||
while(isspace(*ptr)) ptr++; | |||
return *ptr == 0 ? rtn : -4; | |||
} | |||
//------------------------------------------------------------------------------ | |||
int csvReadInt16(File* file, int16_t* num, char delim) { | |||
int32_t tmp; | |||
int rtn = csvReadInt32(file, &tmp, delim); | |||
if (rtn < 0) return rtn; | |||
if (tmp < INT_MIN || tmp > INT_MAX) return -5; | |||
*num = tmp; | |||
return rtn; | |||
} | |||
//------------------------------------------------------------------------------ | |||
int csvReadUint32(File* file, uint32_t* num, char delim) { | |||
char buf[20]; | |||
char* ptr; | |||
int rtn = csvReadText(file, buf, sizeof(buf), delim); | |||
if (rtn < 0) return rtn; | |||
*num = strtoul(buf, &ptr, 10); | |||
if (buf == ptr) return -3; | |||
while(isspace(*ptr)) ptr++; | |||
return *ptr == 0 ? rtn : -4; | |||
} | |||
//------------------------------------------------------------------------------ | |||
int csvReadUint16(File* file, uint16_t* num, char delim) { | |||
uint32_t tmp; | |||
int rtn = csvReadUint32(file, &tmp, delim); | |||
if (rtn < 0) return rtn; | |||
if (tmp > UINT_MAX) return -5; | |||
*num = tmp; | |||
return rtn; | |||
} | |||
//------------------------------------------------------------------------------ | |||
int csvReadDouble(File* file, double* num, char delim) { | |||
char buf[20]; | |||
char* ptr; | |||
int rtn = csvReadText(file, buf, sizeof(buf), delim); | |||
if (rtn < 0) return rtn; | |||
*num = strtod(buf, &ptr); | |||
if (buf == ptr) return -3; | |||
while(isspace(*ptr)) ptr++; | |||
return *ptr == 0 ? rtn : -4; | |||
} | |||
//------------------------------------------------------------------------------ | |||
int csvReadFloat(File* file, float* num, char delim) { | |||
double tmp; | |||
int rtn = csvReadDouble(file, &tmp, delim); | |||
if (rtn < 0)return rtn; | |||
// could test for too large. | |||
*num = tmp; | |||
return rtn; | |||
} | |||
//------------------------------------------------------------------------------ | |||
void setup() { | |||
@@ -94,27 +132,81 @@ void setup() { | |||
// Wait for USB Serial | |||
while (!Serial) { | |||
SysCall::yield(); | |||
yield(); | |||
} | |||
cout << F("Type any character to start\n"); | |||
Serial.println("Type any character to start"); | |||
while (!Serial.available()) { | |||
SysCall::yield(); | |||
yield(); | |||
} | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
sd.initErrorHalt(); | |||
// Initialize the SD. | |||
if (!SD.begin(CS_PIN)) { | |||
Serial.println("begin failed"); | |||
return; | |||
} | |||
// create test file | |||
writeFile(); | |||
cout << endl; | |||
// read and print test | |||
readFile(); | |||
cout << "\nDone!" << endl; | |||
// Remove existing file. | |||
SD.remove("READTEST.TXT"); | |||
// Create the file. | |||
file = SD.open("READTEST.TXT", FILE_WRITE); | |||
if (!file) { | |||
Serial.println("open failed"); | |||
return; | |||
} | |||
// Write test data. | |||
file.print(F( | |||
#if CSV_DELIM == ',' | |||
"36,23.20,20.70,57.60,79.50,01:08:14,23.06.16\r\n" | |||
"37,23.21,20.71,57.61,79.51,02:08:14,23.07.16\r\n" | |||
#elif CSV_DELIM == ';' | |||
"36;23.20;20.70;57.60;79.50;01:08:14;23.06.16\r\n" | |||
"37;23.21;20.71;57.61;79.51;02:08:14;23.07.16\r\n" | |||
#else | |||
#error "Bad CSV_DELIM" | |||
#endif | |||
)); | |||
// Rewind the file for read. | |||
file.seek(0); | |||
// Read the file and print fields. | |||
int16_t tcalc; | |||
float t1, t2, h1, h2; | |||
// Must be dim 9 to allow for zero byte. | |||
char timeS[9], dateS[9]; | |||
while (file.available()) { | |||
if (csvReadInt16(&file, &tcalc, CSV_DELIM) != CSV_DELIM | |||
|| csvReadFloat(&file, &t1, CSV_DELIM) != CSV_DELIM | |||
|| csvReadFloat(&file, &t2, CSV_DELIM) != CSV_DELIM | |||
|| csvReadFloat(&file, &h1, CSV_DELIM) != CSV_DELIM | |||
|| csvReadFloat(&file, &h2, CSV_DELIM) != CSV_DELIM | |||
|| csvReadText(&file, timeS, sizeof(timeS), CSV_DELIM) != CSV_DELIM | |||
|| csvReadText(&file, dateS, sizeof(dateS), CSV_DELIM) != '\n') { | |||
Serial.println("read error"); | |||
int ch; | |||
int nr = 0; | |||
// print part of file after error. | |||
while ((ch = file.read()) > 0 && nr++ < 100) { | |||
Serial.write(ch); | |||
} | |||
break; | |||
} | |||
Serial.print(tcalc); | |||
Serial.print(CSV_DELIM); | |||
Serial.print(t1); | |||
Serial.print(CSV_DELIM); | |||
Serial.print(t2); | |||
Serial.print(CSV_DELIM); | |||
Serial.print(h1); | |||
Serial.print(CSV_DELIM); | |||
Serial.print(h2); | |||
Serial.print(CSV_DELIM); | |||
Serial.print(timeS); | |||
Serial.print(CSV_DELIM); | |||
Serial.println(dateS); | |||
} | |||
file.close(); | |||
} | |||
void loop() {} | |||
//------------------------------------------------------------------------------ | |||
void loop() { | |||
} | |||
@@ -29,9 +29,9 @@ void setup() { | |||
SysCall::yield(); | |||
} | |||
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
// breadboards. use SPI_FULL_SPEED for better performance. | |||
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
@@ -20,7 +20,9 @@ void setup() { | |||
if (c != 'Y') { | |||
sd.errorHalt("Quitting, you did not type 'Y'."); | |||
} | |||
if (!sd.begin(chipSelect)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.initErrorHalt(); | |||
} | |||
// Use wipe() for no dot progress indicator. | |||
@@ -28,7 +30,9 @@ void setup() { | |||
sd.errorHalt("Wipe failed."); | |||
} | |||
// Must reinitialize after wipe. | |||
if (!sd.begin(chipSelect)) { | |||
// Initialize at the highest speed supported by the board that is | |||
// not over 50 MHz. Try a lower speed if SPI errors occur. | |||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
sd.errorHalt("Second init failed."); | |||
} | |||
Serial.println("Done"); |
@@ -1,5 +1,5 @@ | |||
name=SdFat | |||
version=2015.4.26 | |||
version=2016.7.24 | |||
author= | |||
maintainer= | |||
sentence=FAT16/FAT32 file system for SD cards. |
@@ -27,7 +27,6 @@ | |||
*/ | |||
#ifndef DigitalPin_h | |||
#define DigitalPin_h | |||
#include "SystemInclude.h" | |||
#if defined(__AVR__) | |||
#include <avr/io.h> | |||
/** GpioPinMap type */ |
@@ -1,9 +1,9 @@ | |||
/* Arduino SdSpi Library | |||
/* Arduino SdSpiAltDriver Library | |||
* Copyright (C) 2016 by William Greiman | |||
* | |||
* STM32F1 code for Maple and Maple Mini support, 2015 by Victor Perez | |||
* | |||
* This file is part of the Arduino SdSpi Library | |||
* This file is part of the Arduino SdSpiAltDriver Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -16,52 +16,40 @@ | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdSpi Library. If not, see | |||
* along with the Arduino SdSpiAltDriver Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#if defined(ESP8266) | |||
#include "SdSpi.h" | |||
#include "SdSpiDriver.h" | |||
//------------------------------------------------------------------------------ | |||
/** Initialize the SPI bus. | |||
* | |||
* \param[in] chipSelectPin SD card chip select pin. | |||
*/ | |||
void SdSpi::begin(uint8_t chipSelectPin) { | |||
pinMode(chipSelectPin, OUTPUT); | |||
digitalWrite(chipSelectPin, HIGH); | |||
void SdSpiAltDriver::begin(uint8_t csPin) { | |||
m_csPin = csPin; | |||
pinMode(m_csPin, OUTPUT); | |||
digitalWrite(m_csPin, HIGH); | |||
SPI.begin(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** Set SPI options for access to SD/SDHC cards. | |||
* | |||
* \param[in] divisor SCK clock divider relative to the max SPI clock. | |||
*/ | |||
void SdSpi::beginTransaction(uint8_t divisor) { | |||
const uint32_t F_SPI_MAX = 80000000; | |||
#if ENABLE_SPI_TRANSACTIONS | |||
// Note: ESP8266 beginTransaction does not protect for interrupts. | |||
SPISettings settings(F_SPI_MAX/(divisor ? divisor : 1), MSBFIRST, SPI_MODE0); | |||
SPI.beginTransaction(settings); | |||
#else // ENABLE_SPI_TRANSACTIONS | |||
// Note: ESP8266 beginTransaction is the same as following code. | |||
SPI.setFrequency(F_SPI_MAX/(divisor ? divisor : 1)); | |||
SPI.setBitOrder(MSBFIRST); | |||
SPI.setDataMode(SPI_MODE0); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
void SdSpiAltDriver::activate() { | |||
SPI.beginTransaction(m_spiSettings); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdSpi::endTransaction() { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
void SdSpiAltDriver::deactivate() { | |||
// Note: endTransaction is an empty function on ESP8266. | |||
SPI.endTransaction(); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
uint8_t SdSpi::receive() { | |||
uint8_t SdSpiAltDriver::receive() { | |||
return SPI.transfer(0XFF); | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -72,7 +60,7 @@ uint8_t SdSpi::receive() { | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||
// Works without 32-bit alignment of buf. | |||
SPI.transferBytes(0, buf, n); | |||
return 0; | |||
@@ -82,7 +70,7 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
* | |||
* \param[in] b Byte to send | |||
*/ | |||
void SdSpi::send(uint8_t b) { | |||
void SdSpiAltDriver::send(uint8_t b) { | |||
SPI.transfer(b); | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -91,7 +79,7 @@ void SdSpi::send(uint8_t b) { | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||
void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||
// Adjust to 32-bit alignment. | |||
while ((reinterpret_cast<uintptr_t>(buf) & 0X3) && n) { | |||
SPI.transfer(*buf++); |
@@ -1,7 +1,7 @@ | |||
/* Arduino SdSpi Library | |||
/* Arduino SdSpiAltDriver Library | |||
* Copyright (C) 2013 by William Greiman | |||
* | |||
* This file is part of the Arduino SdSpi Library | |||
* This file is part of the Arduino SdSpiAltDriver Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -14,10 +14,10 @@ | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdSpi Library. If not, see | |||
* along with the Arduino SdSpiAltDriver Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "SdSpi.h" | |||
#include "SdSpiDriver.h" | |||
#if defined(__SAM3X8E__) || defined(__SAM3X8H__) | |||
/** Use SAM3X DMAC if nonzero */ | |||
#define USE_SAM3X_DMAC 1 | |||
@@ -57,25 +57,11 @@ static bool dmac_channel_transfer_done(uint32_t ul_num) { | |||
return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true; | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdSpi::begin(uint8_t chipSelectPin) { | |||
pinMode(chipSelectPin, OUTPUT); | |||
digitalWrite(chipSelectPin, HIGH); | |||
PIO_Configure( | |||
g_APinDescription[PIN_SPI_MOSI].pPort, | |||
g_APinDescription[PIN_SPI_MOSI].ulPinType, | |||
g_APinDescription[PIN_SPI_MOSI].ulPin, | |||
g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration); | |||
PIO_Configure( | |||
g_APinDescription[PIN_SPI_MISO].pPort, | |||
g_APinDescription[PIN_SPI_MISO].ulPinType, | |||
g_APinDescription[PIN_SPI_MISO].ulPin, | |||
g_APinDescription[PIN_SPI_MISO].ulPinConfiguration); | |||
PIO_Configure( | |||
g_APinDescription[PIN_SPI_SCK].pPort, | |||
g_APinDescription[PIN_SPI_SCK].ulPinType, | |||
g_APinDescription[PIN_SPI_SCK].ulPin, | |||
g_APinDescription[PIN_SPI_SCK].ulPinConfiguration); | |||
pmc_enable_periph_clk(ID_SPI0); | |||
void SdSpiAltDriver::begin(uint8_t csPin) { | |||
m_csPin = csPin; | |||
pinMode(m_csPin, OUTPUT); | |||
digitalWrite(m_csPin, HIGH); | |||
SPI.begin(); | |||
#if USE_SAM3X_DMAC | |||
pmc_enable_periph_clk(ID_DMAC); | |||
dmac_disable(); | |||
@@ -134,28 +120,26 @@ static void spiDmaTX(const uint8_t* src, uint16_t count) { | |||
} | |||
//------------------------------------------------------------------------------ | |||
// initialize SPI controller | |||
void SdSpi::beginTransaction(uint8_t sckDivisor) { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
SPI.beginTransaction(SPISettings()); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
uint8_t scbr = sckDivisor; | |||
void SdSpiAltDriver::activate() { | |||
SPI.beginTransaction(m_spiSettings); | |||
Spi* pSpi = SPI0; | |||
// disable SPI | |||
// Save the divisor | |||
uint32_t scbr = pSpi->SPI_CSR[SPI_CHIP_SEL] & 0XFF00; | |||
// Disable SPI | |||
pSpi->SPI_CR = SPI_CR_SPIDIS; | |||
// reset SPI | |||
pSpi->SPI_CR = SPI_CR_SWRST; | |||
// no mode fault detection, set master mode | |||
pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR; | |||
// mode 0, 8-bit, | |||
pSpi->SPI_CSR[SPI_CHIP_SEL] = SPI_CSR_SCBR(scbr) | SPI_CSR_NCPHA; | |||
pSpi->SPI_CSR[SPI_CHIP_SEL] = scbr | SPI_CSR_CSAAT | SPI_CSR_NCPHA; | |||
// enable SPI | |||
pSpi->SPI_CR |= SPI_CR_SPIEN; | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdSpi::endTransaction() { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
void SdSpiAltDriver::deactivate() { | |||
SPI.endTransaction(); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
} | |||
//------------------------------------------------------------------------------ | |||
static inline uint8_t spiTransfer(uint8_t b) { | |||
@@ -168,12 +152,12 @@ static inline uint8_t spiTransfer(uint8_t b) { | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** SPI receive a byte */ | |||
uint8_t SdSpi::receive() { | |||
uint8_t SdSpiAltDriver::receive() { | |||
return spiTransfer(0XFF); | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** SPI receive multiple bytes */ | |||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||
Spi* pSpi = SPI0; | |||
int rtn = 0; | |||
#if USE_SAM3X_DMAC | |||
@@ -206,11 +190,11 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** SPI send a byte */ | |||
void SdSpi::send(uint8_t b) { | |||
void SdSpiAltDriver::send(uint8_t b) { | |||
spiTransfer(b); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||
void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||
Spi* pSpi = SPI0; | |||
#if USE_SAM3X_DMAC | |||
spiDmaTX(buf, n); |
@@ -1,7 +1,7 @@ | |||
/* Arduino SdSpi Library | |||
/* Arduino SdSpiAltDriver Library | |||
* Copyright (C) 2013 by William Greiman | |||
* | |||
* This file is part of the Arduino SdSpi Library | |||
* This file is part of the Arduino SdSpiAltDriver Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -14,72 +14,64 @@ | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdSpi Library. If not, see | |||
* along with the Arduino SdSpiAltDriver Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#if defined(__STM32F1__) | |||
#include "SdSpi.h" | |||
#include "SdSpiDriver.h" | |||
#define USE_STM32F1_DMAC 1 | |||
//------------------------------------------------------------------------------ | |||
/** Initialize the SPI bus. | |||
static SPIClass m_SPI1(1); | |||
#if BOARD_NR_SPI > 1 | |||
static SPIClass m_SPI2(2); | |||
#endif // BOARD_NR_SPI > 1 | |||
#if BOARD_NR_SPI > 2 | |||
static SPIClass m_SPI2(3); | |||
#endif // BOARD_NR_SPI > 2 | |||
// | |||
static SPIClass* pSpi[] = | |||
#if BOARD_NR_SPI == 1 | |||
{&m_SPI1}; | |||
#elif BOARD_NR_SPI == 2 | |||
{&m_SPI1, &m_SPI2}; | |||
#elif BOARD_NR_SPI == 3 | |||
{&m_SPI1, &m_SPI2, &m_SPI3}; | |||
#else // BOARD_NR_SPI | |||
#error "BOARD_NR_SPI too large" | |||
#endif // BOARD_NR_SPI | |||
//------------------------------------------------------------------------------ | |||
/** Set SPI options for access to SD/SDHC cards. | |||
* | |||
* \param[in] chipSelectPin SD card chip select pin. | |||
* \param[in] divisor SCK clock divider relative to the APB1 or APB2 clock. | |||
*/ | |||
void SdSpi::begin(uint8_t chipSelectPin) { | |||
pinMode(chipSelectPin, OUTPUT); | |||
digitalWrite(chipSelectPin, HIGH); | |||
SPI.begin(); | |||
void SdSpiAltDriver::activate() { | |||
pSpi[m_spiPort]->beginTransaction(m_spiSettings); | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** Set SPI options for access to SD/SDHC cards. | |||
/** Initialize the SPI bus. | |||
* | |||
* \param[in] divisor SCK clock divider relative to the APB1 or APB2 clock. | |||
* \param[in] chipSelectPin SD card chip select pin. | |||
*/ | |||
void SdSpi::beginTransaction(uint8_t divisor) { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
// Correct divisor will be set below. | |||
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
uint32_t br; // Baud rate control field in SPI_CR1. | |||
if (divisor <= 2) { | |||
br = SPI_CLOCK_DIV2; | |||
} else if (divisor <= 4) { | |||
br = SPI_CLOCK_DIV4; | |||
} else if (divisor <= 8) { | |||
br = SPI_CLOCK_DIV8; | |||
} else if (divisor <= 16) { | |||
br = SPI_CLOCK_DIV16; | |||
} else if (divisor <= 32) { | |||
br = SPI_CLOCK_DIV32; | |||
} else if (divisor <= 64) { | |||
br = SPI_CLOCK_DIV64; | |||
} else if (divisor <= 128) { | |||
br = SPI_CLOCK_DIV128; | |||
} else { | |||
br = SPI_CLOCK_DIV256; | |||
} | |||
SPI.setClockDivider(br); | |||
#if !ENABLE_SPI_TRANSACTIONS | |||
SPI.setBitOrder(MSBFIRST); | |||
SPI.setDataMode(SPI_MODE0); | |||
#endif // !ENABLE_SPI_TRANSACTIONS | |||
void SdSpiAltDriver::begin(uint8_t csPin) { | |||
m_csPin = csPin; | |||
pinMode(m_csPin, OUTPUT); | |||
digitalWrite(m_csPin, HIGH); | |||
pSpi[m_spiPort]->begin(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* End SPI transaction. | |||
*/ | |||
void SdSpi::endTransaction() { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
SPI.endTransaction(); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
void SdSpiAltDriver::deactivate() { | |||
pSpi[m_spiPort]->endTransaction(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
uint8_t SdSpi::receive() { | |||
return SPI.transfer(0XFF); | |||
uint8_t SdSpiAltDriver::receive() { | |||
return pSpi[m_spiPort]->transfer(0XFF); | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** Receive multiple bytes. | |||
@@ -89,14 +81,14 @@ uint8_t SdSpi::receive() { | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||
int rtn = 0; | |||
#if USE_STM32F1_DMAC | |||
rtn = SPI.dmaTransfer(0, const_cast<uint8*>(buf), n); | |||
rtn = pSpi[m_spiPort]->dmaTransfer(0, const_cast<uint8*>(buf), n); | |||
#else // USE_STM32F1_DMAC | |||
// SPI.read(buf, n); | |||
// pSpi[m_spiPort]->read(buf, n); fails ?? use byte transfer | |||
for (size_t i = 0; i < n; i++) { | |||
buf[i] = SPI.transfer(0XFF); | |||
buf[i] = pSpi[m_spiPort]->transfer(0XFF); | |||
} | |||
#endif // USE_STM32F1_DMAC | |||
return rtn; | |||
@@ -106,8 +98,8 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
* | |||
* \param[in] b Byte to send | |||
*/ | |||
void SdSpi::send(uint8_t b) { | |||
SPI.transfer(b); | |||
void SdSpiAltDriver::send(uint8_t b) { | |||
pSpi[m_spiPort]->transfer(b); | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** Send multiple bytes. | |||
@@ -115,11 +107,15 @@ void SdSpi::send(uint8_t b) { | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||
void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||
#if USE_STM32F1_DMAC | |||
SPI.dmaSend(const_cast<uint8*>(buf), n); | |||
pSpi[m_spiPort]->dmaSend(const_cast<uint8*>(buf), n); | |||
#else // #if USE_STM32F1_DMAC | |||
SPI.write(buf, n); | |||
pSpi[m_spiPort]->write(buf, n); | |||
#endif // USE_STM32F1_DMAC | |||
} | |||
#endif // USE_NATIVE_STM32F1_SPI | |||
//----------------------------------------------------------------------------- | |||
void SdSpiAltDriver::setPort(uint8_t portNumber) { | |||
m_spiPort = portNumber < 1 || portNumber > BOARD_NR_SPI ? 0 : portNumber -1; | |||
} | |||
#endif // defined(__STM32F1__) |
@@ -1,7 +1,7 @@ | |||
/* Arduino SdSpi Library | |||
/* Arduino SdSpiAltDriver Library | |||
* Copyright (C) 2013 by William Greiman | |||
* | |||
* This file is part of the Arduino SdSpi Library | |||
* This file is part of the Arduino SdSpiAltDriver Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -14,14 +14,32 @@ | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdSpi Library. If not, see | |||
* along with the Arduino SdSpiAltDriver Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "SdSpi.h" | |||
#include "SdSpiDriver.h" | |||
#if defined(__arm__) && defined(CORE_TEENSY) | |||
// SPI definitions | |||
#include "kinetis.h" | |||
//------------------------------------------------------------------------------ | |||
void SdSpiAltDriver::activate() { | |||
SPI.beginTransaction(m_spiSettings); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdSpiAltDriver::begin(uint8_t chipSelectPin) { | |||
m_csPin = chipSelectPin; | |||
pinMode(m_csPin, OUTPUT); | |||
digitalWrite(m_csPin, HIGH); | |||
SPI.begin(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdSpiAltDriver::deactivate() { | |||
SPI.endTransaction(); | |||
} | |||
//============================================================================== | |||
#ifdef KINETISK | |||
// use 16-bit frame if SPI_USE_8BIT_FRAME is zero | |||
#define SPI_USE_8BIT_FRAME 0 | |||
// Limit initial fifo to three entries to avoid fifo overrun | |||
@@ -37,68 +55,8 @@ | |||
#define SPI_PUSHR_CTAS(n) (((n) & 7) << 28) | |||
#endif // SPI_PUSHR_CTAS | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* initialize SPI pins | |||
*/ | |||
void SdSpi::begin(uint8_t chipSelectPin) { | |||
pinMode(chipSelectPin, OUTPUT); | |||
digitalWrite(chipSelectPin, HIGH); | |||
SIM_SCGC6 |= SIM_SCGC6_SPI0; | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Initialize hardware SPI | |||
* | |||
*/ | |||
void SdSpi::beginTransaction(uint8_t sckDivisor) { | |||
uint32_t ctar, ctar0, ctar1; | |||
#if ENABLE_SPI_TRANSACTIONS | |||
SPI.beginTransaction(SPISettings()); | |||
#endif // #if ENABLE_SPI_TRANSACTIONS | |||
if (sckDivisor <= 2) { | |||
// 1/2 speed | |||
ctar = SPI_CTAR_DBR | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); | |||
} else if (sckDivisor <= 4) { | |||
// 1/4 speed | |||
ctar = SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); | |||
} else if (sckDivisor <= 8) { | |||
// 1/8 speed | |||
ctar = SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); | |||
} else if (sckDivisor <= 12) { | |||
// 1/12 speed | |||
ctar = SPI_CTAR_BR(2) | SPI_CTAR_CSSCK(2); | |||
} else if (sckDivisor <= 16) { | |||
// 1/16 speed | |||
ctar = SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(3); | |||
} else if (sckDivisor <= 32) { | |||
// 1/32 speed | |||
ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(4); | |||
} else if (sckDivisor <= 64) { | |||
// 1/64 speed | |||
ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(5); | |||
} else { | |||
// 1/128 speed | |||
ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(6); | |||
} | |||
// CTAR0 - 8 bit transfer | |||
ctar0 = ctar | SPI_CTAR_FMSZ(7); | |||
// CTAR1 - 16 bit transfer | |||
ctar1 = ctar | SPI_CTAR_FMSZ(15); | |||
if (SPI0_CTAR0 != ctar0 || SPI0_CTAR1 != ctar1) { | |||
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); | |||
SPI0_CTAR0 = ctar0; | |||
SPI0_CTAR1 = ctar1; | |||
} | |||
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); | |||
CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); | |||
CORE_PIN12_CONFIG = PORT_PCR_MUX(2); | |||
CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** SPI receive a byte */ | |||
uint8_t SdSpi::receive() { | |||
uint8_t SdSpiAltDriver::receive() { | |||
SPI0_MCR |= SPI_MCR_CLR_RXF; | |||
SPI0_SR = SPI_SR_TCF; | |||
SPI0_PUSHR = 0xFF; | |||
@@ -107,7 +65,7 @@ uint8_t SdSpi::receive() { | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** SPI receive multiple bytes */ | |||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||
// clear any data in RX FIFO | |||
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | |||
#if SPI_USE_8BIT_FRAME | |||
@@ -162,7 +120,7 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** SPI send a byte */ | |||
void SdSpi::send(uint8_t b) { | |||
void SdSpiAltDriver::send(uint8_t b) { | |||
SPI0_MCR |= SPI_MCR_CLR_RXF; | |||
SPI0_SR = SPI_SR_TCF; | |||
SPI0_PUSHR = b; | |||
@@ -170,7 +128,7 @@ void SdSpi::send(uint8_t b) { | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** SPI send multiple bytes */ | |||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||
void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||
// clear any data in RX FIFO | |||
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | |||
#if SPI_USE_8BIT_FRAME | |||
@@ -228,52 +186,12 @@ void SdSpi::send(const uint8_t* buf , size_t n) { | |||
#else // KINETISK | |||
//============================================================================== | |||
// Use standard SPI library if not KINETISK | |||
/** | |||
* Initialize SPI pins. | |||
*/ | |||
void SdSpi::begin(uint8_t chipSelectPin) { | |||
pinMode(chipSelectPin, OUTPUT); | |||
digitalWrite(chipSelectPin, HIGH); | |||
SPI.begin(); | |||
} | |||
/** Set SPI options for access to SD/SDHC cards. | |||
* | |||
* \param[in] divisor SCK clock divider relative to the system clock. | |||
*/ | |||
void SdSpi::beginTransaction(uint8_t divisor) { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
SPI.beginTransaction(SPISettings()); | |||
#else // #if ENABLE_SPI_TRANSACTIONS | |||
SPI.setBitOrder(MSBFIRST); | |||
SPI.setDataMode(SPI_MODE0); | |||
#endif // #if ENABLE_SPI_TRANSACTIONS | |||
#ifndef SPI_CLOCK_DIV128 | |||
SPI.setClockDivider(divisor); | |||
#else // SPI_CLOCK_DIV128 | |||
int v; | |||
if (divisor <= 2) { | |||
v = SPI_CLOCK_DIV2; | |||
} else if (divisor <= 4) { | |||
v = SPI_CLOCK_DIV4; | |||
} else if (divisor <= 8) { | |||
v = SPI_CLOCK_DIV8; | |||
} else if (divisor <= 16) { | |||
v = SPI_CLOCK_DIV16; | |||
} else if (divisor <= 32) { | |||
v = SPI_CLOCK_DIV32; | |||
} else if (divisor <= 64) { | |||
v = SPI_CLOCK_DIV64; | |||
} else { | |||
v = SPI_CLOCK_DIV128; | |||
} | |||
SPI.setClockDivider(v); | |||
#endif // SPI_CLOCK_DIV128 | |||
} | |||
//------------------------------------------------------------------------------ | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
uint8_t SdSpi::receive() { | |||
uint8_t SdSpiAltDriver::receive() { | |||
return SPI.transfer(0XFF); | |||
} | |||
/** Receive multiple bytes. | |||
@@ -283,7 +201,7 @@ uint8_t SdSpi::receive() { | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||
for (size_t i = 0; i < n; i++) { | |||
buf[i] = SPI.transfer(0XFF); | |||
} | |||
@@ -293,7 +211,7 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
* | |||
* \param[in] b Byte to send | |||
*/ | |||
void SdSpi::send(uint8_t b) { | |||
void SdSpiAltDriver::send(uint8_t b) { | |||
SPI.transfer(b); | |||
} | |||
/** Send multiple bytes. | |||
@@ -301,16 +219,10 @@ void SdSpi::send(uint8_t b) { | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||
void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||
for (size_t i = 0; i < n; i++) { | |||
SPI.transfer(buf[i]); | |||
} | |||
} | |||
#endif // KINETISK | |||
//------------------------------------------------------------------------------ | |||
void SdSpi::endTransaction() { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
SPI.endTransaction(); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
} | |||
#endif // defined(__arm__) && defined(CORE_TEENSY) |
@@ -0,0 +1,89 @@ | |||
/* Arduino SdFat Library | |||
* Copyright (C) 2016 by William Greiman | |||
* | |||
* This file is part of the Arduino SdFat Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This Library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdFat Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "BlockDriver.h" | |||
bool SdBlockDriverEX::readBlock(uint32_t block, uint8_t* dst) { | |||
if (m_curState != READ_STATE || block != m_curBlock) { | |||
if (!syncBlocks()) { | |||
return false; | |||
} | |||
if (!SdSpiCard::readStart(block)) { | |||
return false; | |||
} | |||
m_curBlock = block; | |||
m_curState = READ_STATE; | |||
} | |||
if (!SdSpiCard::readData(dst)) { | |||
return false; | |||
} | |||
m_curBlock++; | |||
return true; | |||
} | |||
//----------------------------------------------------------------------------- | |||
bool SdBlockDriverEX::readBlocks(uint32_t block, uint8_t* dst, size_t nb) { | |||
for (size_t i = 0; i < nb; i++) { | |||
if (!readBlock(block + i, dst + i*512UL)) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
//----------------------------------------------------------------------------- | |||
bool SdBlockDriverEX::syncBlocks() { | |||
if (m_curState == READ_STATE) { | |||
m_curState = IDLE_STATE; | |||
if (!SdSpiCard::readStop()) { | |||
return false; | |||
} | |||
} else if (m_curState == WRITE_STATE) { | |||
m_curState = IDLE_STATE; | |||
if (!SdSpiCard::writeStop()) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
//----------------------------------------------------------------------------- | |||
bool SdBlockDriverEX::writeBlock(uint32_t block, const uint8_t* src) { | |||
if (m_curState != WRITE_STATE || m_curBlock != block) { | |||
if (!syncBlocks()) { | |||
return false; | |||
} | |||
if (!SdSpiCard::writeStart(block)) { | |||
return false; | |||
} | |||
m_curBlock = block; | |||
m_curState = WRITE_STATE; | |||
} | |||
if (!SdSpiCard::writeData(src)) { | |||
return false; | |||
} | |||
m_curBlock++; | |||
return true; | |||
} | |||
//----------------------------------------------------------------------------- | |||
bool SdBlockDriverEX::writeBlocks(uint32_t block, | |||
const uint8_t* src, size_t nb) { | |||
for (size_t i = 0; i < nb; i++) { | |||
if (!writeBlock(block + i, src + i*512UL)) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} |
@@ -0,0 +1,197 @@ | |||
/* Arduino SdFat Library | |||
* Copyright (C) 2016 by William Greiman | |||
* | |||
* This file is part of the Arduino SdFat Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This Library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdFat Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
/** | |||
* \file | |||
* \brief Define block driver. | |||
*/ | |||
#ifndef BlockDriver_h | |||
#define BlockDriver_h | |||
#ifdef ARDUINO | |||
#include "SdSpiCard/SdSpiCard.h" | |||
#else // ARDUINO | |||
#include "SdSpiCard.h" | |||
#endif // ARDUINO | |||
#include "FatLib/BaseBlockDriver.h" | |||
//----------------------------------------------------------------------------- | |||
/** | |||
* \class SdBlockDriver | |||
* \brief Standard SD block driver. | |||
*/ | |||
#if ENABLE_EXTENDED_TRANSFER_CLASS | |||
class SdBlockDriver : public BaseBlockDriver, public SdSpiCard { | |||
#else // ENABLE_EXTENDED_TRANSFER_CLASS | |||
class SdBlockDriver : public SdSpiCard { | |||
#endif // ENABLE_EXTENDED_TRANSFER_CLASS | |||
public: | |||
/** Initialize the SD card | |||
* | |||
* \param[in] spi SPI driver. | |||
* \param[in] csPin Card chip select pin number. | |||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||
spi->begin(csPin); | |||
spi->setSpiSettings(SD_SCK_HZ(250000)); | |||
if (!SdSpiCard::begin(spi)) { | |||
return false; | |||
} | |||
spi->setSpiSettings(spiSettings); | |||
return true; | |||
} | |||
/** | |||
* Read a 512 byte block from an SD card. | |||
* | |||
* \param[in] block Logical block to be read. | |||
* \param[out] dst Pointer to the location that will receive the data. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool readBlock(uint32_t block, uint8_t* dst) { | |||
return SdSpiCard::readBlock(block, dst); | |||
} | |||
/** End multi-block transfer and go to idle state. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool syncBlocks() { | |||
return true; | |||
} | |||
/** | |||
* Writes a 512 byte block to an SD card. | |||
* | |||
* \param[in] block Logical block to be written. | |||
* \param[in] src Pointer to the location of the data to be written. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool writeBlock(uint32_t block, const uint8_t* src) { | |||
return SdSpiCard::writeBlock(block, src); | |||
} | |||
/** | |||
* Read multiple 512 byte blocks from an SD card. | |||
* | |||
* \param[in] block Logical block to be read. | |||
* \param[in] nb Number of blocks to be read. | |||
* \param[out] dst Pointer to the location that will receive the data. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) { | |||
return SdSpiCard::readBlocks(block, dst, nb); | |||
} | |||
/** | |||
* Write multiple 512 byte blocks to an SD card. | |||
* | |||
* \param[in] block Logical block to be written. | |||
* \param[in] nb Number of blocks to be written. | |||
* \param[in] src Pointer to the location of the data to be written. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) { | |||
return SdSpiCard::writeBlocks(block, src, nb); | |||
} | |||
}; | |||
//----------------------------------------------------------------------------- | |||
/** | |||
* \class SdBlockDriverEX | |||
* \brief Extended SD I/O block driver. | |||
*/ | |||
class SdBlockDriverEX : public SdSpiCard, public BaseBlockDriver { | |||
public: | |||
/** Initialize the SD card | |||
* | |||
* \param[in] spi SPI driver. | |||
* \param[in] csPin Card chip select pin number. | |||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||
m_curState = IDLE_STATE; | |||
spi->begin(csPin); | |||
spi->setSpiSettings(SD_SCK_HZ(250000)); | |||
if (!SdSpiCard::begin(spi)) { | |||
return false; | |||
} | |||
spi->setSpiSettings(spiSettings); | |||
return true; | |||
} | |||
/** | |||
* Read a 512 byte block from an SD card. | |||
* | |||
* \param[in] block Logical block to be read. | |||
* \param[out] dst Pointer to the location that will receive the data. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool readBlock(uint32_t block, uint8_t* dst); | |||
/** End multi-block transfer and go to idle state. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool syncBlocks(); | |||
/** | |||
* Writes a 512 byte block to an SD card. | |||
* | |||
* \param[in] block Logical block to be written. | |||
* \param[in] src Pointer to the location of the data to be written. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool writeBlock(uint32_t block, const uint8_t* src); | |||
/** | |||
* Read multiple 512 byte blocks from an SD card. | |||
* | |||
* \param[in] block Logical block to be read. | |||
* \param[in] nb Number of blocks to be read. | |||
* \param[out] dst Pointer to the location that will receive the data. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool readBlocks(uint32_t block, uint8_t* dst, size_t nb); | |||
/** | |||
* Write multiple 512 byte blocks to an SD card. | |||
* | |||
* \param[in] block Logical block to be written. | |||
* \param[in] nb Number of blocks to be written. | |||
* \param[in] src Pointer to the location of the data to be written. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb); | |||
private: | |||
static const uint32_t IDLE_STATE = 0; | |||
static const uint32_t READ_STATE = 1; | |||
static const uint32_t WRITE_STATE = 2; | |||
uint32_t m_curBlock; | |||
uint8_t m_curState; | |||
}; | |||
//----------------------------------------------------------------------------- | |||
/** typedef for BlockDriver */ | |||
#if ENABLE_EXTENDED_TRANSFER_CLASS | |||
typedef BaseBlockDriver BlockDriver; | |||
#else // ENABLE_EXTENDED_TRANSFER_CLASS | |||
typedef SdBlockDriver BlockDriver; | |||
#endif // ENABLE_EXTENDED_TRANSFER_CLASS | |||
#endif // BlockDriver_h |
@@ -114,11 +114,7 @@ class PrintFile : public FatFile, public Print { | |||
* \class File | |||
* \brief Arduino SD.h style File API | |||
*/ | |||
#if ARDUINO_FILE_USES_STREAM | |||
class File : public FatFile, public Stream { | |||
#else // ARDUINO_FILE_USES_STREAM | |||
class File : public FatFile, public Print { | |||
#endif // ARDUINO_FILE_USES_STREAM | |||
public: | |||
File() {} | |||
/** Create a file object and open it in the current working directory. |
@@ -25,7 +25,6 @@ | |||
*/ | |||
#include "FatLibConfig.h" | |||
#if ENABLE_ARDUINO_FEATURES | |||
#include "SysCall.h" | |||
#include "bufstream.h" | |||
//============================================================================== | |||
/** | |||
@@ -51,7 +50,7 @@ class ArduinoInStream : public ibufstream { | |||
uint32_t t; | |||
m_line[0] = '\0'; | |||
while (!m_hw->available()) { | |||
SysCall::yield(); | |||
yield(); | |||
} | |||
while (1) { |
@@ -0,0 +1,56 @@ | |||
#ifndef BaseBlockDriver_h | |||
#define BaseBlockDriver_h | |||
#include "FatLibConfig.h" | |||
/** | |||
* \class BaseBlockDriver | |||
* \brief Base block driver. | |||
*/ | |||
class BaseBlockDriver { | |||
public: | |||
/** | |||
* Read a 512 byte block from an SD card. | |||
* | |||
* \param[in] block Logical block to be read. | |||
* \param[out] dst Pointer to the location that will receive the data. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
virtual bool readBlock(uint32_t block, uint8_t* dst) = 0; | |||
/** End multi-block transfer and go to idle state. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
virtual bool syncBlocks() = 0; | |||
/** | |||
* Writes a 512 byte block to an SD card. | |||
* | |||
* \param[in] block Logical block to be written. | |||
* \param[in] src Pointer to the location of the data to be written. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
virtual bool writeBlock(uint32_t block, const uint8_t* src) = 0; | |||
#if USE_MULTI_BLOCK_IO | |||
/** | |||
* Read multiple 512 byte blocks from an SD card. | |||
* | |||
* \param[in] block Logical block to be read. | |||
* \param[in] nb Number of blocks to be read. | |||
* \param[out] dst Pointer to the location that will receive the data. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0; | |||
/** | |||
* Write multiple 512 byte blocks to an SD card. | |||
* | |||
* \param[in] block Logical block to be written. | |||
* \param[in] nb Number of blocks to be written. | |||
* \param[in] src Pointer to the location of the data to be written. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0; | |||
#endif // USE_MULTI_BLOCK_IO | |||
}; | |||
#endif // BaseBlockDriver_h |
@@ -22,46 +22,46 @@ | |||
//------------------------------------------------------------------------------ | |||
// use the gnu style oflag in open() | |||
/** open() oflag for reading */ | |||
uint8_t const O_READ = 0X01; | |||
const uint8_t O_READ = 0X01; | |||
/** open() oflag - same as O_IN */ | |||
uint8_t const O_RDONLY = O_READ; | |||
const uint8_t O_RDONLY = O_READ; | |||
/** open() oflag for write */ | |||
uint8_t const O_WRITE = 0X02; | |||
const uint8_t O_WRITE = 0X02; | |||
/** open() oflag - same as O_WRITE */ | |||
uint8_t const O_WRONLY = O_WRITE; | |||
const uint8_t O_WRONLY = O_WRITE; | |||
/** open() oflag for reading and writing */ | |||
uint8_t const O_RDWR = (O_READ | O_WRITE); | |||
const uint8_t O_RDWR = (O_READ | O_WRITE); | |||
/** open() oflag mask for access modes */ | |||
uint8_t const O_ACCMODE = (O_READ | O_WRITE); | |||
const uint8_t O_ACCMODE = (O_READ | O_WRITE); | |||
/** The file offset shall be set to the end of the file prior to each write. */ | |||
uint8_t const O_APPEND = 0X04; | |||
const uint8_t O_APPEND = 0X04; | |||
/** synchronous writes - call sync() after each write */ | |||
uint8_t const O_SYNC = 0X08; | |||
const uint8_t O_SYNC = 0X08; | |||
/** truncate the file to zero length */ | |||
uint8_t const O_TRUNC = 0X10; | |||
const uint8_t O_TRUNC = 0X10; | |||
/** set the initial position at the end of the file */ | |||
uint8_t const O_AT_END = 0X20; | |||
const uint8_t O_AT_END = 0X20; | |||
/** create the file if nonexistent */ | |||
uint8_t const O_CREAT = 0X40; | |||
const uint8_t O_CREAT = 0X40; | |||
/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ | |||
uint8_t const O_EXCL = 0X80; | |||
const uint8_t O_EXCL = 0X80; | |||
// FatFile class static and const definitions | |||
// flags for ls() | |||
/** ls() flag for list all files including hidden. */ | |||
uint8_t const LS_A = 1; | |||
const uint8_t LS_A = 1; | |||
/** ls() flag to print modify. date */ | |||
uint8_t const LS_DATE = 2; | |||
const uint8_t LS_DATE = 2; | |||
/** ls() flag to print file size. */ | |||
uint8_t const LS_SIZE = 4; | |||
const uint8_t LS_SIZE = 4; | |||
/** ls() flag for recursive list of subdirectories */ | |||
uint8_t const LS_R = 8; | |||
const uint8_t LS_R = 8; | |||
// flags for timestamp | |||
/** set the file's last access date */ | |||
uint8_t const T_ACCESS = 1; | |||
const uint8_t T_ACCESS = 1; | |||
/** set the file's creation date and time */ | |||
uint8_t const T_CREATE = 2; | |||
const uint8_t T_CREATE = 2; | |||
/** Set the file's write date and time */ | |||
uint8_t const T_WRITE = 4; | |||
const uint8_t T_WRITE = 4; | |||
#endif // FatApiConstants_h |
@@ -50,7 +50,7 @@ bool FatFile::addDirCluster() { | |||
DBG_FAIL_MACRO; | |||
goto fail; | |||
} | |||
block = m_vol->clusterStartBlock(m_curCluster); | |||
block = m_vol->clusterFirstBlock(m_curCluster); | |||
pc = m_vol->cacheFetchData(block, FatCache::CACHE_RESERVE_FOR_WRITE); | |||
if (!pc) { | |||
DBG_FAIL_MACRO; | |||
@@ -113,8 +113,8 @@ bool FatFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { | |||
DBG_FAIL_MACRO; | |||
goto fail; | |||
} | |||
*bgnBlock = m_vol->clusterStartBlock(m_firstCluster); | |||
*endBlock = m_vol->clusterStartBlock(c) | |||
*bgnBlock = m_vol->clusterFirstBlock(m_firstCluster); | |||
*endBlock = m_vol->clusterFirstBlock(c) | |||
+ m_vol->blocksPerCluster() - 1; | |||
return true; | |||
} | |||
@@ -127,6 +127,7 @@ fail: | |||
bool FatFile::createContiguous(FatFile* dirFile, | |||
const char* path, uint32_t size) { | |||
uint32_t count; | |||
// don't allow zero length file | |||
if (size == 0) { | |||
DBG_FAIL_MACRO; | |||
@@ -149,7 +150,6 @@ bool FatFile::createContiguous(FatFile* dirFile, | |||
// insure sync() will update dir entry | |||
m_flags |= F_FILE_DIR_DIRTY; | |||
return sync(); | |||
fail: | |||
@@ -344,7 +344,7 @@ bool FatFile::mkdir(FatFile* parent, fname_t* fname) { | |||
} | |||
// cache block for '.' and '..' | |||
block = m_vol->clusterStartBlock(m_firstCluster); | |||
block = m_vol->clusterFirstBlock(m_firstCluster); | |||
pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); | |||
if (!pc) { | |||
DBG_FAIL_MACRO; | |||
@@ -612,7 +612,7 @@ bool FatFile::openParent(FatFile* dirFile) { | |||
if (dirFile->m_dirCluster == 0) { | |||
return openRoot(dirFile->m_vol); | |||
} | |||
lbn = dirFile->m_vol->clusterStartBlock(dirFile->m_dirCluster); | |||
lbn = dirFile->m_vol->clusterFirstBlock(dirFile->m_dirCluster); | |||
cb = dirFile->m_vol->cacheFetchData(lbn, FatCache::CACHE_FOR_READ); | |||
if (!cb) { | |||
DBG_FAIL_MACRO; | |||
@@ -748,7 +748,7 @@ int FatFile::read(void* buf, size_t nbyte) { | |||
} | |||
} | |||
} | |||
block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster; | |||
block = m_vol->clusterFirstBlock(m_curCluster) + blockOfCluster; | |||
} | |||
if (offset != 0 || toRead < 512 || block == m_vol->cacheBlockNumber()) { | |||
// amount to be read from current block | |||
@@ -941,7 +941,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) { | |||
// update dot dot if directory | |||
if (dirCluster) { | |||
// get new dot dot | |||
uint32_t block = m_vol->clusterStartBlock(dirCluster); | |||
uint32_t block = m_vol->clusterFirstBlock(dirCluster); | |||
pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ); | |||
if (!pc) { | |||
DBG_FAIL_MACRO; | |||
@@ -955,7 +955,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) { | |||
goto fail; | |||
} | |||
// store new dot dot | |||
block = m_vol->clusterStartBlock(m_firstCluster); | |||
block = m_vol->clusterFirstBlock(m_firstCluster); | |||
pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); | |||
if (!pc) { | |||
DBG_FAIL_MACRO; | |||
@@ -1404,7 +1404,7 @@ int FatFile::write(const void* buf, size_t nbyte) { | |||
} | |||
} | |||
// block for data write | |||
uint32_t block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster; | |||
uint32_t block = m_vol->clusterFirstBlock(m_curCluster) + blockOfCluster; | |||
if (blockOffset != 0 || nToWrite < 512) { | |||
// partial block - must use cache |
@@ -217,7 +217,7 @@ class FatFile { | |||
/** Create and open a new contiguous file of a specified size. | |||
* | |||
* \param[in] dirFile The directory where the file will be created. | |||
* \param[in] path A path with a valid DOS 8.3 file name. | |||
* \param[in] path A path with a validfile name. | |||
* \param[in] size The desired file size. | |||
* | |||
* \return The value true is returned for success and | |||
@@ -225,6 +225,17 @@ class FatFile { | |||
*/ | |||
bool createContiguous(FatFile* dirFile, | |||
const char* path, uint32_t size); | |||
/** Create and open a new contiguous file of a specified size. | |||
* | |||
* \param[in] path A path with a validfile name. | |||
* \param[in] size The desired file size. | |||
* | |||
* \return The value true is returned for success and | |||
* the value false, is returned for failure. | |||
*/ | |||
bool createContiguous(const char* path, uint32_t size) { | |||
return createContiguous(m_cwd, path, size); | |||
} | |||
/** \return The current cluster number for a file or directory. */ | |||
uint32_t curCluster() const { | |||
return m_curCluster; | |||
@@ -801,6 +812,13 @@ class FatFile { | |||
m_cwd = dir; | |||
return true; | |||
} | |||
/** \return first block of file or zero for empty file. */ | |||
uint32_t firstBlock() { | |||
if (m_firstCluster) { | |||
return m_vol->clusterFirstBlock(m_firstCluster); | |||
} | |||
return 0; | |||
} | |||
/** The sync() call causes all modified data and directory fields | |||
* to be written to the storage device. | |||
* | |||
@@ -956,9 +974,9 @@ class FatFile { | |||
// bits defined in m_flags | |||
// should be 0X0F | |||
static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); | |||
static const uint8_t F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); | |||
// sync of directory entry required | |||
static uint8_t const F_FILE_DIR_DIRTY = 0X80; | |||
static const uint8_t F_FILE_DIR_DIRTY = 0X80; | |||
// global pointer to cwd dir | |||
static FatFile* m_cwd; |
@@ -234,7 +234,6 @@ bool FatFile::printModifyDateTime(print_t* pr) { | |||
fail: | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
size_t FatFile::printFileSize(print_t* pr) { | |||
char buf[11]; |
@@ -21,6 +21,7 @@ | |||
#define FatFileSystem_h | |||
#include "FatVolume.h" | |||
#include "FatFile.h" | |||
#include "ArduinoStream.h" | |||
#include "ArduinoFiles.h" | |||
/** | |||
* \file | |||
@@ -35,11 +36,13 @@ class FatFileSystem : public FatVolume { | |||
public: | |||
/** | |||
* Initialize an FatFileSystem object. | |||
* \param[in] blockDev Device block driver. | |||
* \param[in] part partition to initialize. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool begin(uint8_t part = 0) { | |||
bool begin(BlockDriver* blockDev, uint8_t part = 0) { | |||
m_blockDev = blockDev; | |||
vwd()->close(); | |||
return (part ? init(part) : init(1) || init(0)) | |||
&& vwd()->openRoot(this) && FatFile::setCwd(vwd()); | |||
@@ -91,7 +94,7 @@ class FatFileSystem : public FatVolume { | |||
* \return a File object. | |||
*/ | |||
File open(const String &path, uint8_t mode = FILE_READ) { | |||
return open(path.c_str(), mode ); | |||
return open(path.c_str(), mode ); | |||
} | |||
#endif // ENABLE_ARDUINO_FEATURES | |||
/** Change a volume's working directory to root | |||
@@ -142,7 +145,6 @@ class FatFileSystem : public FatVolume { | |||
if (!dir.isDir()) { | |||
goto fail; | |||
} | |||
// *m_vwd = dir; | |||
m_vwd = dir; | |||
if (set_cwd) { | |||
FatFile::setCwd(vwd()); | |||
@@ -189,7 +191,7 @@ fail: | |||
* | |||
* LS_R - Recursive list of subdirectories. | |||
*/ | |||
void ls(print_t* pr, uint8_t flags) { | |||
void ls(print_t* pr, uint8_t flags = 0) { | |||
vwd()->ls(pr, flags); | |||
} | |||
//---------------------------------------------------------------------------- |
@@ -53,19 +53,6 @@ | |||
#define USE_LONG_FILE_NAMES 1 | |||
#endif // USE_LONG_FILE_NAMES | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Set ARDUINO_FILE_USES_STREAM nonzero to use Stream as the base class | |||
* for the Arduino File class. If ARDUINO_FILE_USES_STREAM is zero, Print | |||
* will be used as the base class for the Arduino File class. | |||
* | |||
* You can save some flash if you do not use Stream input functions such as | |||
* find(), findUntil(), readBytesUntil(), readString(), readStringUntil(), | |||
* parseInt(), and parsefloat(). | |||
*/ | |||
#ifndef ARDUINO_FILE_USES_STREAM | |||
#define ARDUINO_FILE_USES_STREAM 1 | |||
#endif // ARDUINO_FILE_USES_STREAM | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Set USE_SEPARATE_FAT_CACHE non-zero to use a second 512 byte cache | |||
* for FAT table entries. Improves performance for large writes that | |||
@@ -92,6 +79,15 @@ | |||
#endif // RAMEND | |||
#endif // USE_MULTI_BLOCK_IO | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters | |||
* updated. This will increase the speed of the freeClusterCount() call | |||
* after the first call. Extra flash will be required. | |||
*/ | |||
#ifndef MAINTAIN_FREE_CLUSTER_COUNT | |||
#define MAINTAIN_FREE_CLUSTER_COUNT 0 | |||
#endif // MAINTAIN_FREE_CLUSTER_COUNT | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Set DESTRUCTOR_CLOSES_FILE non-zero to close a file in its destructor. | |||
* | |||
@@ -133,7 +129,9 @@ | |||
/** | |||
* Enable Extra features for Arduino. | |||
*/ | |||
// #define ENABLE_ARDUINO_FEATURES 0 ////////////////////////FIX THIS ///////////////// | |||
#ifndef ENABLE_ARDUINO_FEATURES | |||
#include <Arduino.h> | |||
#if defined(ARDUINO) || defined(PLATFORM_ID) || defined(DOXYGEN) | |||
#define ENABLE_ARDUINO_FEATURES 1 | |||
#else // #if defined(ARDUINO) || defined(DOXYGEN) |
@@ -29,11 +29,11 @@ | |||
*/ | |||
//------------------------------------------------------------------------------ | |||
/** Value for byte 510 of boot block or MBR */ | |||
uint8_t const BOOTSIG0 = 0X55; | |||
const uint8_t BOOTSIG0 = 0X55; | |||
/** Value for byte 511 of boot block or MBR */ | |||
uint8_t const BOOTSIG1 = 0XAA; | |||
const uint8_t BOOTSIG1 = 0XAA; | |||
/** Value for bootSignature field int FAT/FAT32 boot sector */ | |||
uint8_t const EXTENDED_BOOT_SIG = 0X29; | |||
const uint8_t EXTENDED_BOOT_SIG = 0X29; | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* \struct partitionTable | |||
@@ -525,9 +525,9 @@ struct fat32_boot { | |||
typedef struct fat32_boot fat32_boot_t; | |||
//------------------------------------------------------------------------------ | |||
/** Lead signature for a FSINFO sector */ | |||
uint32_t const FSINFO_LEAD_SIG = 0x41615252; | |||
const uint32_t FSINFO_LEAD_SIG = 0x41615252; | |||
/** Struct signature for a FSINFO sector */ | |||
uint32_t const FSINFO_STRUCT_SIG = 0x61417272; | |||
const uint32_t FSINFO_STRUCT_SIG = 0x61417272; | |||
/** | |||
* \struct fat32_fsinfo | |||
* | |||
@@ -566,19 +566,19 @@ typedef struct fat32_fsinfo fat32_fsinfo_t; | |||
//------------------------------------------------------------------------------ | |||
// End Of Chain values for FAT entries | |||
/** FAT12 end of chain value used by Microsoft. */ | |||
uint16_t const FAT12EOC = 0XFFF; | |||
const uint16_t FAT12EOC = 0XFFF; | |||
/** Minimum value for FAT12 EOC. Use to test for EOC. */ | |||
uint16_t const FAT12EOC_MIN = 0XFF8; | |||
const uint16_t FAT12EOC_MIN = 0XFF8; | |||
/** FAT16 end of chain value used by Microsoft. */ | |||
uint16_t const FAT16EOC = 0XFFFF; | |||
const uint16_t FAT16EOC = 0XFFFF; | |||
/** Minimum value for FAT16 EOC. Use to test for EOC. */ | |||
uint16_t const FAT16EOC_MIN = 0XFFF8; | |||
const uint16_t FAT16EOC_MIN = 0XFFF8; | |||
/** FAT32 end of chain value used by Microsoft. */ | |||
uint32_t const FAT32EOC = 0X0FFFFFFF; | |||
const uint32_t FAT32EOC = 0X0FFFFFFF; | |||
/** Minimum value for FAT32 EOC. Use to test for EOC. */ | |||
uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; | |||
const uint32_t FAT32EOC_MIN = 0X0FFFFFF8; | |||
/** Mask a for FAT32 entry. Entries are 28 bits. */ | |||
uint32_t const FAT32MASK = 0X0FFFFFFF; | |||
const uint32_t FAT32MASK = 0X0FFFFFFF; | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* \struct directoryEntry | |||
@@ -665,33 +665,33 @@ typedef struct directoryEntry dir_t; | |||
// Definitions for directory entries | |||
// | |||
/** escape for name[0] = 0XE5 */ | |||
uint8_t const DIR_NAME_0XE5 = 0X05; | |||
const uint8_t DIR_NAME_0XE5 = 0X05; | |||
/** name[0] value for entry that is free after being "deleted" */ | |||
uint8_t const DIR_NAME_DELETED = 0XE5; | |||
const uint8_t DIR_NAME_DELETED = 0XE5; | |||
/** name[0] value for entry that is free and no allocated entries follow */ | |||
uint8_t const DIR_NAME_FREE = 0X00; | |||
const uint8_t DIR_NAME_FREE = 0X00; | |||
/** file is read-only */ | |||
uint8_t const DIR_ATT_READ_ONLY = 0X01; | |||
const uint8_t DIR_ATT_READ_ONLY = 0X01; | |||
/** File should e hidden in directory listings */ | |||
uint8_t const DIR_ATT_HIDDEN = 0X02; | |||
const uint8_t DIR_ATT_HIDDEN = 0X02; | |||
/** Entry is for a system file */ | |||
uint8_t const DIR_ATT_SYSTEM = 0X04; | |||
const uint8_t DIR_ATT_SYSTEM = 0X04; | |||
/** Directory entry contains the volume label */ | |||
uint8_t const DIR_ATT_VOLUME_ID = 0X08; | |||
const uint8_t DIR_ATT_VOLUME_ID = 0X08; | |||
/** Entry is for a directory */ | |||
uint8_t const DIR_ATT_DIRECTORY = 0X10; | |||
const uint8_t DIR_ATT_DIRECTORY = 0X10; | |||
/** Old DOS archive bit for backup support */ | |||
uint8_t const DIR_ATT_ARCHIVE = 0X20; | |||
const uint8_t DIR_ATT_ARCHIVE = 0X20; | |||
/** Test value for long name entry. Test is | |||
(d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ | |||
uint8_t const DIR_ATT_LONG_NAME = 0X0F; | |||
const uint8_t DIR_ATT_LONG_NAME = 0X0F; | |||
/** Test mask for long name entry */ | |||
uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; | |||
const uint8_t DIR_ATT_LONG_NAME_MASK = 0X3F; | |||
/** defined attribute bits */ | |||
uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; | |||
const uint8_t DIR_ATT_DEFINED_BITS = 0X3F; | |||
/** Mask for file/subdirectory tests */ | |||
uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); | |||
const uint8_t DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); | |||
/** Filename base-name is all lower case */ | |||
const uint8_t DIR_NT_LC_BASE = 0X08; | |||
@@ -818,9 +818,9 @@ static inline uint8_t FAT_SECOND(uint16_t fatTime) { | |||
return 2*(fatTime & 0X1F); | |||
} | |||
/** Default date for file timestamps is 1 Jan 2000 */ | |||
uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; | |||
const uint16_t FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; | |||
/** Default time for file timestamp is 1 am */ | |||
uint16_t const FAT_DEFAULT_TIME = (1 << 11); | |||
const uint16_t FAT_DEFAULT_TIME = (1 << 11); | |||
//------------------------------------------------------------------------------ | |||
/** Dimension of first name field in long directory entry */ | |||
const uint8_t LDIR_NAME1_DIM = 5; |
@@ -39,6 +39,7 @@ cache_t* FatCache::read(uint32_t lbn, uint8_t option) { | |||
return &m_block; | |||
fail: | |||
return 0; | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -183,7 +184,7 @@ fail: | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
uint32_t FatVolume::clusterStartBlock(uint32_t cluster) const { | |||
uint32_t FatVolume::clusterFirstBlock(uint32_t cluster) const { | |||
return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -196,7 +197,7 @@ int8_t FatVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||
// error if reserved cluster of beyond FAT | |||
DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | |||
if (m_fatType == 32) { | |||
if (fatType() == 32) { | |||
lba = m_fatStartBlock + (cluster >> 7); | |||
pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | |||
if (!pc) { | |||
@@ -206,8 +207,7 @@ int8_t FatVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||
next = pc->fat32[cluster & 0X7F] & FAT32MASK; | |||
goto done; | |||
} | |||
if (m_fatType == 16) { | |||
if (fatType() == 16) { | |||
lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | |||
pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); | |||
if (!pc) { | |||
@@ -217,7 +217,7 @@ int8_t FatVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||
next = pc->fat16[cluster & 0XFF]; | |||
goto done; | |||
} | |||
if (FAT12_SUPPORT && m_fatType == 12) { | |||
if (FAT12_SUPPORT && fatType() == 12) { | |||
uint16_t index = cluster; | |||
index += index >> 1; | |||
lba = m_fatStartBlock + (index >> 9); | |||
@@ -263,7 +263,7 @@ bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { | |||
// error if reserved cluster of beyond FAT | |||
DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); | |||
if (m_fatType == 32) { | |||
if (fatType() == 32) { | |||
lba = m_fatStartBlock + (cluster >> 7); | |||
pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | |||
if (!pc) { | |||
@@ -274,7 +274,7 @@ bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { | |||
return true; | |||
} | |||
if (m_fatType == 16) { | |||
if (fatType() == 16) { | |||
lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); | |||
pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); | |||
if (!pc) { | |||
@@ -285,7 +285,7 @@ bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { | |||
return true; | |||
} | |||
if (FAT12_SUPPORT && m_fatType == 12) { | |||
if (FAT12_SUPPORT && fatType() == 12) { | |||
uint16_t index = cluster; | |||
index += index >> 1; | |||
lba = m_fatStartBlock + (index >> 9); | |||
@@ -367,7 +367,7 @@ int32_t FatVolume::freeClusterCount() { | |||
uint32_t todo = m_lastCluster + 1; | |||
uint16_t n; | |||
if (FAT12_SUPPORT && m_fatType == 12) { | |||
if (FAT12_SUPPORT && fatType() == 12) { | |||
for (unsigned i = 2; i < todo; i++) { | |||
uint32_t c; | |||
int8_t fg = fatGet(i, &c); | |||
@@ -379,7 +379,7 @@ int32_t FatVolume::freeClusterCount() { | |||
free++; | |||
} | |||
} | |||
} else if (m_fatType == 16 || m_fatType == 32) { | |||
} else if (fatType() == 16 || fatType() == 32) { | |||
lba = m_fatStartBlock; | |||
while (todo) { | |||
cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ); | |||
@@ -387,11 +387,11 @@ int32_t FatVolume::freeClusterCount() { | |||
DBG_FAIL_MACRO; | |||
goto fail; | |||
} | |||
n = m_fatType == 16 ? 256 : 128; | |||
n = fatType() == 16 ? 256 : 128; | |||
if (todo < n) { | |||
n = todo; | |||
} | |||
if (m_fatType == 16) { | |||
if (fatType() == 16) { | |||
for (uint16_t i = 0; i < n; i++) { | |||
if (pc->fat16[i] == 0) { | |||
free++; | |||
@@ -427,12 +427,10 @@ bool FatVolume::init(uint8_t part) { | |||
uint8_t tmp; | |||
m_fatType = 0; | |||
m_allocSearchStart = 1; | |||
m_cache.init(this); | |||
#if USE_SEPARATE_FAT_CACHE | |||
m_fatCache.init(this); | |||
#endif // USE_SEPARATE_FAT_CACHE | |||
// if part == 0 assume super floppy with FAT boot sector in block zero | |||
// if part > 0 assume mbr volume with partition table | |||
if (part) { | |||
@@ -468,7 +466,6 @@ bool FatVolume::init(uint8_t part) { | |||
} | |||
m_blocksPerCluster = fbs->sectorsPerCluster; | |||
m_clusterBlockMask = m_blocksPerCluster - 1; | |||
// determine shift that is same as multiply by m_blocksPerCluster | |||
m_clusterSizeShift = 0; | |||
for (tmp = 1; m_blocksPerCluster != tmp; tmp <<= 1, m_clusterSizeShift++) { | |||
@@ -477,7 +474,6 @@ bool FatVolume::init(uint8_t part) { | |||
goto fail; | |||
} | |||
} | |||
m_blocksPerFat = fbs->sectorsPerFat16 ? | |||
fbs->sectorsPerFat16 : fbs->sectorsPerFat32; | |||
@@ -503,7 +499,6 @@ bool FatVolume::init(uint8_t part) { | |||
// Indicate unknown number of free clusters. | |||
setFreeClusterCount(-1); | |||
// FAT type is determined by cluster count | |||
if (clusterCount < 4085) { | |||
m_fatType = 12; | |||
@@ -527,7 +522,7 @@ bool FatVolume::wipe(print_t* pr) { | |||
cache_t* cache; | |||
uint16_t count; | |||
uint32_t lbn; | |||
if (!m_fatType) { | |||
if (!fatType()) { | |||
DBG_FAIL_MACRO; | |||
goto fail; | |||
} | |||
@@ -538,8 +533,8 @@ bool FatVolume::wipe(print_t* pr) { | |||
} | |||
memset(cache->data, 0, 512); | |||
// Zero root. | |||
if (m_fatType == 32) { | |||
lbn = clusterStartBlock(m_rootDirStart); | |||
if (fatType() == 32) { | |||
lbn = clusterFirstBlock(m_rootDirStart); | |||
count = m_blocksPerCluster; | |||
} else { | |||
lbn = m_rootDirStart; | |||
@@ -564,13 +559,13 @@ bool FatVolume::wipe(print_t* pr) { | |||
} | |||
} | |||
// Reserve first two clusters. | |||
if (m_fatType == 32) { | |||
if (fatType() == 32) { | |||
cache->fat32[0] = 0x0FFFFFF8; | |||
cache->fat32[1] = 0x0FFFFFFF; | |||
} else if (m_fatType == 16) { | |||
} else if (fatType() == 16) { | |||
cache->fat16[0] = 0XFFF8; | |||
cache->fat16[1] = 0XFFFF; | |||
} else if (FAT12_SUPPORT && m_fatType == 12) { | |||
} else if (FAT12_SUPPORT && fatType() == 12) { | |||
cache->fat32[0] = 0XFFFFF8; | |||
} else { | |||
DBG_FAIL_MACRO; | |||
@@ -581,7 +576,7 @@ bool FatVolume::wipe(print_t* pr) { | |||
DBG_FAIL_MACRO; | |||
goto fail; | |||
} | |||
if (m_fatType == 32) { | |||
if (fatType() == 32) { | |||
// Reserve root cluster. | |||
if (!fatPutEOC(m_rootDirStart) || !cacheSync()) { | |||
DBG_FAIL_MACRO; |
@@ -24,18 +24,18 @@ | |||
* \brief FatVolume class | |||
*/ | |||
#include <stddef.h> | |||
#include "SysCall.h" | |||
#include "FatLibConfig.h" | |||
#include "FatStructs.h" | |||
#include "BlockDriver.h" | |||
//------------------------------------------------------------------------------ | |||
#ifndef DOXYGEN_SHOULD_SKIP_THIS | |||
/** Macro for debug. */ | |||
#define DEBUG_MODE 0 | |||
#if DEBUG_MODE | |||
#define DBG_FAIL_MACRO Serial.print(F(__FILE__)); Serial.println(__LINE__) | |||
#define DBG_FAIL_MACRO Serial.print(F(__FILE__)); Serial.println(__LINE__); | |||
#define DBG_PRINT_IF(b) if (b) {Serial.println(F(#b)); DBG_FAIL_MACRO;} | |||
#define DBG_HALT_IF(b) if (b) {Serial.println(F(#b));\ | |||
DBG_FAIL_MACRO; SysCall::halt();} | |||
DBG_FAIL_MACRO; while (1);} | |||
#else // DEBUG_MODE | |||
#define DBG_FAIL_MACRO | |||
#define DBG_PRINT_IF(b) | |||
@@ -100,11 +100,11 @@ class FatCache { | |||
/** Sync existing block but do not read new block. */ | |||
static const uint8_t CACHE_OPTION_NO_READ = 4; | |||
/** Cache block for read. */ | |||
static uint8_t const CACHE_FOR_READ = 0; | |||
static const uint8_t CACHE_FOR_READ = 0; | |||
/** Cache block for write. */ | |||
static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; | |||
static const uint8_t CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; | |||
/** Reserve cache block for write - do not read from block device. */ | |||
static uint8_t const CACHE_RESERVE_FOR_WRITE | |||
static const uint8_t CACHE_RESERVE_FOR_WRITE | |||
= CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; | |||
/** \return Cache block address. */ | |||
cache_t* block() { | |||
@@ -256,7 +256,9 @@ class FatVolume { | |||
// Allow FatFile and FatCache access to FatVolume private functions. | |||
friend class FatCache; | |||
friend class FatFile; | |||
friend class FatFileSystem; | |||
//------------------------------------------------------------------------------ | |||
BlockDriver* m_blockDev; // block device | |||
uint8_t m_blocksPerCluster; // Cluster size in blocks. | |||
uint8_t m_clusterBlockMask; // Mask to extract block of cluster. | |||
uint8_t m_clusterSizeShift; // Cluster count to block count shift. | |||
@@ -269,6 +271,24 @@ class FatVolume { | |||
uint32_t m_lastCluster; // Last cluster number in FAT. | |||
uint32_t m_rootDirStart; // Start block for FAT16, cluster for FAT32. | |||
//------------------------------------------------------------------------------ | |||
// block I/O functions. | |||
bool readBlock(uint32_t block, uint8_t* dst) { | |||
return m_blockDev->readBlock(block, dst); | |||
} | |||
bool syncBlocks() { | |||
return m_blockDev->syncBlocks(); | |||
} | |||
bool writeBlock(uint32_t block, const uint8_t* src) { | |||
return m_blockDev->writeBlock(block, src); | |||
} | |||
#if USE_MULTI_BLOCK_IO | |||
bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) { | |||
return m_blockDev->readBlocks(block, dst, nb); | |||
} | |||
bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) { | |||
return m_blockDev->writeBlocks(block, src, nb); | |||
} | |||
#endif // USE_MULTI_BLOCK_IO | |||
#if MAINTAIN_FREE_CLUSTER_COUNT | |||
int32_t m_freeClusterCount; // Count of free clusters in volume. | |||
void setFreeClusterCount(int32_t value) { | |||
@@ -297,7 +317,7 @@ class FatVolume { | |||
options | FatCache::CACHE_STATUS_MIRROR_FAT); | |||
} | |||
bool cacheSync() { | |||
return m_cache.sync() && m_fatCache.sync(); | |||
return m_cache.sync() && m_fatCache.sync() && syncBlocks(); | |||
} | |||
#else // | |||
cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { | |||
@@ -305,7 +325,7 @@ class FatVolume { | |||
options | FatCache::CACHE_STATUS_MIRROR_FAT); | |||
} | |||
bool cacheSync() { | |||
return m_cache.sync(); | |||
return m_cache.sync() && syncBlocks(); | |||
} | |||
#endif // USE_SEPARATE_FAT_CACHE | |||
cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { | |||
@@ -332,7 +352,7 @@ class FatVolume { | |||
uint8_t blockOfCluster(uint32_t position) const { | |||
return (position >> 9) & m_clusterBlockMask; | |||
} | |||
uint32_t clusterStartBlock(uint32_t cluster) const; | |||
uint32_t clusterFirstBlock(uint32_t cluster) const; | |||
int8_t fatGet(uint32_t cluster, uint32_t* value); | |||
bool fatPut(uint32_t cluster, uint32_t value); | |||
bool fatPutEOC(uint32_t cluster) { | |||
@@ -342,13 +362,5 @@ class FatVolume { | |||
bool isEOC(uint32_t cluster) const { | |||
return cluster > m_lastCluster; | |||
} | |||
//---------------------------------------------------------------------------- | |||
// Virtual block I/O functions. | |||
virtual bool readBlock(uint32_t block, uint8_t* dst) = 0; | |||
virtual bool writeBlock(uint32_t block, const uint8_t* src) = 0; | |||
#if USE_MULTI_BLOCK_IO | |||
virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0; | |||
virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0; | |||
#endif // USE_MULTI_BLOCK_IO | |||
}; | |||
#endif // FatVolume |
@@ -32,8 +32,10 @@ extern char __bss_end; | |||
* \return The number of free bytes. | |||
*/ | |||
static int FreeStack() { | |||
char top; | |||
return __brkval ? &top - __brkval : &top - &__bss_end; | |||
char* sp = reinterpret_cast<char*>(SP); | |||
return __brkval ? sp - __brkval : sp - &__bss_end; | |||
// char top; | |||
// return __brkval ? &top - __brkval : &top - &__bss_end; | |||
} | |||
#elif defined(PLATFORM_ID) // Particle board | |||
static int FreeStack() { | |||
@@ -42,7 +44,7 @@ static int FreeStack() { | |||
#elif defined(__arm__) | |||
extern "C" char* sbrk(int incr); | |||
static int FreeStack() { | |||
char top; | |||
char top = 't'; | |||
return &top - reinterpret_cast<char*>(sbrk(0)); | |||
} | |||
#else |
@@ -17,11 +17,15 @@ | |||
* along with the Arduino SdFat Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "SystemInclude.h" | |||
#include "SysCall.h" | |||
#if defined(UDR0) || defined(DOXYGEN) | |||
#include "MinimumSerial.h" | |||
const uint16_t MIN_2X_BAUD = F_CPU/(4*(2*0XFFF + 1)) + 1; | |||
//------------------------------------------------------------------------------ | |||
int MinimumSerial::available() { | |||
return UCSR0A & (1 << RXC0) ? 1 : 0; | |||
} | |||
//------------------------------------------------------------------------------ | |||
void MinimumSerial::begin(uint32_t baud) { | |||
uint16_t baud_setting; | |||
// don't worry, the compiler will squeeze out F_CPU != 16000000UL | |||
@@ -43,6 +47,10 @@ void MinimumSerial::begin(uint32_t baud) { | |||
UCSR0B |= (1 << TXEN0) | (1 << RXEN0); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void MinimumSerial::flush() { | |||
while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} | |||
} | |||
//------------------------------------------------------------------------------ | |||
int MinimumSerial::read() { | |||
if (UCSR0A & (1 << RXC0)) { | |||
return UDR0; |
@@ -17,9 +17,13 @@ | |||
* along with the Arduino SdFat Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
/** | |||
* \file | |||
* \brief Minimal AVR Serial driver. | |||
*/ | |||
#ifndef MinimumSerial_h | |||
#define MinimumSerial_h | |||
#include "SystemInclude.h" | |||
#include "SysCall.h" | |||
//============================================================================== | |||
/** | |||
* \class MinimumSerial | |||
@@ -27,12 +31,20 @@ | |||
*/ | |||
class MinimumSerial : public Print { | |||
public: | |||
/** \return true for hardware serial */ | |||
operator bool() { return true; } | |||
/** | |||
* \return one if data is available. | |||
*/ | |||
int available(); | |||
/** | |||
* Set baud rate for serial port zero and enable in non interrupt mode. | |||
* Do not call this function if you use another serial library. | |||
* \param[in] baud rate | |||
*/ | |||
void begin(uint32_t baud); | |||
/** Wait for write done. */ | |||
void flush(); | |||
/** | |||
* Unbuffered read | |||
* \return -1 if no character is available or an available character. |
@@ -1,100 +0,0 @@ | |||
/* Arduino SdFat Library | |||
* Copyright (C) 2012 by William Greiman | |||
* | |||
* This file is part of the Arduino SdFat Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This Library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdFat Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "SdFat.h" | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::errorHalt(Print* pr) { | |||
errorPrint(pr); | |||
SysCall::halt(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::errorHalt(Print* pr, char const* msg) { | |||
errorPrint(pr, msg); | |||
SysCall::halt(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::errorPrint(Print* pr) { | |||
if (!cardErrorCode()) { | |||
return; | |||
} | |||
pr->print(F("SD errorCode: 0X")); | |||
pr->print(cardErrorCode(), HEX); | |||
pr->print(F(",0X")); | |||
pr->println(cardErrorData(), HEX); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::errorPrint(Print* pr, char const* msg) { | |||
pr->print(F("error: ")); | |||
pr->println(msg); | |||
errorPrint(pr); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::initErrorHalt(Print* pr) { | |||
initErrorPrint(pr); | |||
SysCall::halt(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::initErrorHalt(Print* pr, char const *msg) { | |||
pr->println(msg); | |||
initErrorHalt(pr); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::initErrorPrint(Print* pr) { | |||
if (cardErrorCode()) { | |||
pr->println(F("Can't access SD card. Do not reformat.")); | |||
if (cardErrorCode() == SD_CARD_ERROR_CMD0) { | |||
pr->println(F("No card, wrong chip select pin, or SPI problem?")); | |||
} | |||
errorPrint(pr); | |||
} else if (vol()->fatType() == 0) { | |||
pr->println(F("Invalid format, reformat SD.")); | |||
} else if (!vwd()->isOpen()) { | |||
pr->println(F("Can't open root directory.")); | |||
} else { | |||
pr->println(F("No error found.")); | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::initErrorPrint(Print* pr, char const *msg) { | |||
pr->println(msg); | |||
initErrorPrint(pr); | |||
} | |||
#if defined(ARDUINO) || defined(DOXYGEN) | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::errorPrint(Print* pr, const __FlashStringHelper* msg) { | |||
pr->print(F("error: ")); | |||
pr->println(msg); | |||
errorPrint(pr); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::errorHalt(Print* pr, const __FlashStringHelper* msg) { | |||
errorPrint(pr, msg); | |||
SysCall::halt(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::initErrorHalt(Print* pr, const __FlashStringHelper* msg) { | |||
pr->println(msg); | |||
initErrorHalt(pr); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdFatBase::initErrorPrint(Print* pr, const __FlashStringHelper* msg) { | |||
pr->println(msg); | |||
initErrorPrint(pr); | |||
} | |||
#endif // defined(ARDUINO) || defined(DOXYGEN) |
@@ -1,5 +1,5 @@ | |||
/* Arduino SdFat Library | |||
* Copyright (C) 2012 by William Greiman | |||
* Copyright (C) 2016 by William Greiman | |||
* | |||
* This file is part of the Arduino SdFat Library | |||
* | |||
@@ -23,16 +23,16 @@ | |||
* \file | |||
* \brief SdFat class | |||
*/ | |||
#include "SysCall.h" | |||
#include "BlockDriver.h" | |||
#ifdef ARDUINO | |||
#include "SdSpiCard/SdSpiCard.h" | |||
#include "FatLib/FatLib.h" | |||
#else // ARDUINO | |||
#include "SdSpiCard.h" | |||
#include "FatLib.h" | |||
#endif // ARDUINO | |||
//------------------------------------------------------------------------------ | |||
/** SdFat version YYYYMMDD */ | |||
#define SD_FAT_VERSION 20160430 | |||
#define SD_FAT_VERSION 20160801 | |||
//============================================================================== | |||
/** | |||
* \class SdBaseFile | |||
@@ -51,6 +51,7 @@ class SdBaseFile : public FatFile { | |||
*/ | |||
SdBaseFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {} | |||
}; | |||
//----------------------------------------------------------------------------- | |||
#if ENABLE_ARDUINO_FEATURES | |||
/** | |||
* \class SdFile | |||
@@ -70,25 +71,37 @@ class SdFile : public PrintFile { | |||
SdFile(const char* path, uint8_t oflag) : PrintFile(path, oflag) {} | |||
}; | |||
#endif // #if ENABLE_ARDUINO_FEATURES | |||
//----------------------------------------------------------------------------- | |||
/** | |||
* \class SdFatBase | |||
* \class SdFileSystem | |||
* \brief Virtual base class for %SdFat library. | |||
*/ | |||
class SdFatBase : public FatFileSystem { | |||
template<class SdDriverClass> | |||
class SdFileSystem : public FatFileSystem { | |||
public: | |||
/** Initialize SD card and file system. | |||
* \param[in] spi SPI object for the card. | |||
* \param[in] csPin SD card chip select pin. | |||
* \param[in] divisor SPI divisor. | |||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||
* \return true for success else false. | |||
*/ | |||
bool begin(SdSpiCard::m_spi_t* spi, uint8_t csPin = SS, uint8_t divisor = 2) { | |||
return m_sdCard.begin(spi, csPin, divisor) && | |||
FatFileSystem::begin(); | |||
bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||
return m_card.begin(spi, csPin, spiSettings) && | |||
FatFileSystem::begin(&m_card); | |||
} | |||
/** \return Pointer to SD card object */ | |||
SdSpiCard *card() { | |||
return &m_sdCard; | |||
SdDriverClass *card() { | |||
m_card.syncBlocks(); | |||
return &m_card; | |||
} | |||
/** Initialize SD card for diagnostic use. | |||
* \param[in] spi SPI object for the card. | |||
* \param[in] csPin SD card chip select pin. | |||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||
* \return true for success else false. | |||
*/ | |||
bool cardBegin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { | |||
return m_card.begin(spi, csPin, spiSettings); | |||
} | |||
/** %Print any SD error code to Serial and halt. */ | |||
void errorHalt() { | |||
@@ -98,7 +111,10 @@ class SdFatBase : public FatFileSystem { | |||
* | |||
* \param[in] pr Print destination. | |||
*/ | |||
void errorHalt(Print* pr); | |||
void errorHalt(Print* pr) { | |||
errorPrint(pr); | |||
SysCall::halt(); | |||
} | |||
/** %Print msg, any SD error code and halt. | |||
* | |||
* \param[in] msg Message to print. | |||
@@ -111,8 +127,10 @@ class SdFatBase : public FatFileSystem { | |||
* \param[in] pr Print destination. | |||
* \param[in] msg Message to print. | |||
*/ | |||
void errorHalt(Print* pr, char const* msg); | |||
void errorHalt(Print* pr, char const* msg) { | |||
errorPrint(pr, msg); | |||
SysCall::halt(); | |||
} | |||
/** %Print any SD error code to Serial */ | |||
void errorPrint() { | |||
errorPrint(&Serial); | |||
@@ -120,7 +138,15 @@ class SdFatBase : public FatFileSystem { | |||
/** %Print any SD error code. | |||
* \param[in] pr Print device. | |||
*/ | |||
void errorPrint(Print* pr); | |||
void errorPrint(Print* pr) { | |||
if (!cardErrorCode()) { | |||
return; | |||
} | |||
pr->print(F("SD errorCode: 0X")); | |||
pr->print(cardErrorCode(), HEX); | |||
pr->print(F(",0X")); | |||
pr->println(cardErrorData(), HEX); | |||
} | |||
/** %Print msg, any SD error code. | |||
* | |||
* \param[in] msg Message to print. | |||
@@ -133,14 +159,10 @@ class SdFatBase : public FatFileSystem { | |||
* \param[in] pr Print destination. | |||
* \param[in] msg Message to print. | |||
*/ | |||
void errorPrint(Print* pr, char const* msg); | |||
/** Diagnostic call to initialize FatFileSystem - use for | |||
* diagnostic purposes only. | |||
* \return true for success else false. | |||
*/ | |||
bool fsBegin() { | |||
return FatFileSystem::begin(); | |||
void errorPrint(Print* pr, char const* msg) { | |||
pr->print(F("error: ")); | |||
pr->println(msg); | |||
errorPrint(pr); | |||
} | |||
/** %Print any SD error code and halt. */ | |||
void initErrorHalt() { | |||
@@ -150,42 +172,65 @@ class SdFatBase : public FatFileSystem { | |||
* | |||
* \param[in] pr Print destination. | |||
*/ | |||
void initErrorHalt(Print* pr); | |||
/**Print message, error details, and halt after SdFat::init() fails. | |||
void initErrorHalt(Print* pr) { | |||
initErrorPrint(pr); | |||
SysCall::halt(); | |||
} | |||
/**Print message, error details, and halt after begin() fails. | |||
* | |||
* \param[in] msg Message to print. | |||
*/ | |||
void initErrorHalt(char const *msg) { | |||
initErrorHalt(&Serial, msg); | |||
} | |||
/**Print message, error details, and halt after SdFatBase::init() fails. | |||
/**Print message, error details, and halt after begin() fails. | |||
* \param[in] pr Print device. | |||
* \param[in] msg Message to print. | |||
*/ | |||
void initErrorHalt(Print* pr, char const *msg); | |||
void initErrorHalt(Print* pr, char const *msg) { | |||
pr->println(msg); | |||
initErrorHalt(pr); | |||
} | |||
/** Print error details after SdFat::init() fails. */ | |||
/** Print error details after begin() fails. */ | |||
void initErrorPrint() { | |||
initErrorPrint(&Serial); | |||
} | |||
/** Print error details after SdFatBase::init() fails. | |||
/** Print error details after begin() fails. | |||
* | |||
* \param[in] pr Print destination. | |||
*/ | |||
void initErrorPrint(Print* pr); | |||
/**Print message and error details and halt after SdFat::init() fails. | |||
void initErrorPrint(Print* pr) { | |||
if (cardErrorCode()) { | |||
pr->println(F("Can't access SD card. Do not reformat.")); | |||
if (cardErrorCode() == SD_CARD_ERROR_CMD0) { | |||
pr->println(F("No card, wrong chip select pin, or SPI problem?")); | |||
} | |||
errorPrint(pr); | |||
} else if (vol()->fatType() == 0) { | |||
pr->println(F("Invalid format, reformat SD.")); | |||
} else if (!vwd()->isOpen()) { | |||
pr->println(F("Can't open root directory.")); | |||
} else { | |||
pr->println(F("No error found.")); | |||
} | |||
} | |||
/**Print message and error details and halt after begin() fails. | |||
* | |||
* \param[in] msg Message to print. | |||
*/ | |||
void initErrorPrint(char const *msg) { | |||
initErrorPrint(&Serial, msg); | |||
} | |||
/**Print message and error details and halt after SdFatBase::init() fails. | |||
/**Print message and error details and halt after begin() fails. | |||
* | |||
* \param[in] pr Print destination. | |||
* \param[in] msg Message to print. | |||
*/ | |||
void initErrorPrint(Print* pr, char const *msg); | |||
void initErrorPrint(Print* pr, char const *msg) { | |||
pr->println(msg); | |||
initErrorPrint(pr); | |||
} | |||
#if defined(ARDUINO) || defined(DOXYGEN) | |||
/** %Print msg, any SD error code, and halt. | |||
* | |||
@@ -199,7 +244,11 @@ class SdFatBase : public FatFileSystem { | |||
* \param[in] pr Print destination. | |||
* \param[in] msg Message to print. | |||
*/ | |||
void errorHalt(Print* pr, const __FlashStringHelper* msg); | |||
void errorHalt(Print* pr, const __FlashStringHelper* msg) { | |||
errorPrint(pr, msg); | |||
SysCall::halt(); | |||
} | |||
/** %Print msg, any SD error code. | |||
* | |||
* \param[in] msg Message to print. | |||
@@ -212,149 +261,202 @@ class SdFatBase : public FatFileSystem { | |||
* \param[in] pr Print destination. | |||
* \param[in] msg Message to print. | |||
*/ | |||
void errorPrint(Print* pr, const __FlashStringHelper* msg); | |||
/**Print message, error details, and halt after SdFat::init() fails. | |||
void errorPrint(Print* pr, const __FlashStringHelper* msg) { | |||
pr->print(F("error: ")); | |||
pr->println(msg); | |||
errorPrint(pr); | |||
} | |||
/**Print message, error details, and halt after begin() fails. | |||
* | |||
* \param[in] msg Message to print. | |||
*/ | |||
void initErrorHalt(const __FlashStringHelper* msg) { | |||
initErrorHalt(&Serial, msg); | |||
} | |||
/**Print message, error details, and halt after SdFatBase::init() fails. | |||
/**Print message, error details, and halt after begin() fails. | |||
* \param[in] pr Print device for message. | |||
* \param[in] msg Message to print. | |||
*/ | |||
void initErrorHalt(Print* pr, const __FlashStringHelper* msg); | |||
/**Print message and error details and halt after SdFat::init() fails. | |||
void initErrorHalt(Print* pr, const __FlashStringHelper* msg) { | |||
pr->println(msg); | |||
initErrorHalt(pr); | |||
} | |||
/**Print message and error details and halt after begin() fails. | |||
* | |||
* \param[in] msg Message to print. | |||
*/ | |||
void initErrorPrint(const __FlashStringHelper* msg) { | |||
initErrorPrint(&Serial, msg); | |||
} | |||
/**Print message and error details and halt after SdFatBase::init() fails. | |||
/**Print message and error details and halt after begin() fails. | |||
* | |||
* \param[in] pr Print destination. | |||
* \param[in] msg Message to print. | |||
*/ | |||
void initErrorPrint(Print* pr, const __FlashStringHelper* msg); | |||
void initErrorPrint(Print* pr, const __FlashStringHelper* msg) { | |||
pr->println(msg); | |||
initErrorPrint(pr); | |||
} | |||
#endif // defined(ARDUINO) || defined(DOXYGEN) | |||
private: | |||
/** \return The card error code */ | |||
uint8_t cardErrorCode() { | |||
return m_sdCard.errorCode(); | |||
return m_card.errorCode(); | |||
} | |||
/** \return the card error data */ | |||
uint8_t cardErrorData() { | |||
return m_sdCard.errorData(); | |||
} | |||
bool readBlock(uint32_t block, uint8_t* dst) { | |||
return m_sdCard.readBlock(block, dst); | |||
} | |||
bool writeBlock(uint32_t block, const uint8_t* src) { | |||
return m_sdCard.writeBlock(block, src); | |||
} | |||
bool readBlocks(uint32_t block, uint8_t* dst, size_t n) { | |||
return m_sdCard.readBlocks(block, dst, n); | |||
} | |||
bool writeBlocks(uint32_t block, const uint8_t* src, size_t n) { | |||
return m_sdCard.writeBlocks(block, src, n); | |||
return m_card.errorData(); | |||
} | |||
SdSpiCard m_sdCard; | |||
private: | |||
SdDriverClass m_card; | |||
}; | |||
//============================================================================== | |||
/** | |||
* \class SdFat | |||
* \brief Main file system class for %SdFat library. | |||
*/ | |||
class SdFat : public SdFatBase { | |||
class SdFat : public SdFileSystem<SdBlockDriver> { | |||
public: | |||
#if IMPLEMENT_SPI_INTERFACE_SELECTION | |||
#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) | |||
SdFat() { | |||
m_spi.setSpiIf(0); | |||
m_spi.setPort(0); | |||
} | |||
explicit SdFat(uint8_t spiIf) { | |||
m_spi.setSpiIf(spiIf < SPI_INTERFACE_COUNT ? spiIf : 0); | |||
/** Constructor with SPI port selection. | |||
* \param[in] spiPort SPI port number. | |||
*/ | |||
explicit SdFat(uint8_t spiPort) { | |||
m_spi.setPort(spiPort); | |||
} | |||
#endif // IMPLEMENT_SPI_INTERFACE_SELECTION | |||
#endif // IMPLEMENT_SPI_PORT_SELECTION | |||
/** Initialize SD card and file system. | |||
* | |||
* \param[in] csPin SD card chip select pin. | |||
* \param[in] divisor SPI divisor. | |||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||
* \return true for success else false. | |||
*/ | |||
bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||
return SdFatBase::begin(&m_spi, csPin, divisor); | |||
*/ | |||
bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { | |||
return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||
} | |||
/** Diagnostic call to initialize SD card - use for diagnostic purposes only. | |||
/** Initialize SD card for diagnostic use only. | |||
* | |||
* \param[in] csPin SD card chip select pin. | |||
* \param[in] divisor SPI divisor. | |||
* \param[in] settings SPI speed, mode, and bit order. | |||
* \return true for success else false. | |||
*/ | |||
bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||
return card()->begin(&m_spi, csPin, divisor); | |||
*/ | |||
bool cardBegin(uint8_t csPin = SS, SPISettings settings = SPI_FULL_SPEED) { | |||
return SdFileSystem::cardBegin(&m_spi, csPin, settings); | |||
} | |||
/** Initialize file system for diagnostic use only. | |||
* \return true for success else false. | |||
*/ | |||
bool fsBegin() { | |||
return FatFileSystem::begin(card()); | |||
} | |||
private: | |||
SpiDefault_t m_spi; | |||
SdFatSpiDriver m_spi; | |||
}; | |||
//============================================================================== | |||
#if SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) | |||
#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||
/** | |||
* \class SdFatLibSpi | |||
* \brief SdFat class using the standard Arduino SPI library. | |||
* \class SdFatSoftSpi | |||
* \brief SdFat class using software SPI. | |||
*/ | |||
class SdFatLibSpi: public SdFatBase { | |||
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||
class SdFatSoftSpi : public SdFileSystem<SdBlockDriver> { | |||
public: | |||
/** Initialize SD card and file system. | |||
* | |||
* \param[in] csPin SD card chip select pin. | |||
* \param[in] divisor SPI divisor. | |||
* \return true for success else false. | |||
*/ | |||
bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||
return SdFatBase::begin(&m_spi, csPin, divisor); | |||
} | |||
/** Diagnostic call to initialize SD card - use for diagnostic purposes only. | |||
* | |||
* \param[in] csPin SD card chip select pin. | |||
* \param[in] divisor SPI divisor. | |||
* \param[in] spiSettings ignored for software SPI.. | |||
* \return true for success else false. | |||
*/ | |||
bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||
return card()->begin(&m_spi, csPin, divisor); | |||
bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { | |||
return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||
} | |||
private: | |||
SdSpiSoftDriver<MisoPin, MosiPin, SckPin> m_spi; | |||
}; | |||
#endif // #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||
#if ENABLE_EXTENDED_TRANSFER_CLASS || defined(DOXYGEN) | |||
//============================================================================== | |||
/** | |||
* \class SdFatEX | |||
* \brief SdFat class with extended SD I/O. | |||
*/ | |||
class SdFatEX : public SdFileSystem<SdBlockDriverEX> { | |||
public: | |||
#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) | |||
SdFatEX() { | |||
m_spi.setPort(0); | |||
} | |||
/** Constructor with SPI port selection. | |||
* \param[in] spiPort SPI port number. | |||
*/ | |||
explicit SdFatEX(uint8_t spiPort) { | |||
m_spi.setPort(spiPort); | |||
} | |||
#endif // IMPLEMENT_SPI_PORT_SELECTION | |||
/** Initialize SD card and file system. | |||
* | |||
* \param[in] csPin SD card chip select pin. | |||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||
* \return true for success else false. | |||
*/ | |||
bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { | |||
return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||
} | |||
private: | |||
SdSpiLib m_spi; | |||
SdFatSpiDriver m_spi; | |||
}; | |||
//============================================================================== | |||
#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||
/** | |||
* \class SdFatSoftSpi | |||
* \brief SdFat class using software SPI. | |||
* \class SdFatSoftSpiEX | |||
* \brief SdFat class using software SPI and extended SD I/O. | |||
*/ | |||
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||
class SdFatSoftSpi : public SdFatBase { | |||
class SdFatSoftSpiEX : public SdFileSystem<SdBlockDriverEX> { | |||
public: | |||
/** Initialize SD card and file system. | |||
* | |||
* \param[in] csPin SD card chip select pin. | |||
* \param[in] divisor SPI divisor. | |||
* \param[in] spiSettings ignored for software SPI. | |||
* \return true for success else false. | |||
*/ | |||
bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||
return SdFatBase::begin(&m_spi, csPin, divisor); | |||
bool begin(uint8_t csPin = SS, SPISettings spiSettings = 2) { | |||
return SdFileSystem::begin(&m_spi, csPin, spiSettings); | |||
} | |||
/** Diagnostic call to initialize SD card - use for diagnostic purposes only. | |||
* \param[in] csPin SD card chip select pin. | |||
* \param[in] divisor SPI divisor. | |||
private: | |||
SdSpiSoftDriver<MisoPin, MosiPin, SckPin> m_spi; | |||
}; | |||
#endif // #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||
#endif // ENABLE_EXTENDED_TRANSFER_CLASS || defined(DOXYGEN) | |||
//============================================================================= | |||
/** | |||
* \class Sd2Card | |||
* \brief Raw access to SD and SDHC card using default SPI library. | |||
*/ | |||
class Sd2Card : public SdSpiCard { | |||
public: | |||
/** Initialize the SD card. | |||
* \param[in] chipSelectPin SD chip select pin. | |||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||
* \return true for success else false. | |||
*/ | |||
bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { | |||
return card()->begin(&m_spi, csPin, divisor); | |||
bool begin(uint8_t chipSelectPin = SS, | |||
SPISettings spiSettings = SD_SCK_MHZ(50)) { | |||
m_spi.begin(chipSelectPin); | |||
m_spi.setSpiSettings(SD_SCK_HZ(250000)); | |||
if (!SdSpiCard::begin(&m_spi)) { | |||
return false; | |||
} | |||
m_spi.setSpiSettings(spiSettings); | |||
return true; | |||
} | |||
private: | |||
SdSpiSoft<MisoPin, MosiPin, SckPin> m_spi; | |||
SdFatSpiDriver m_spi; | |||
}; | |||
#endif /// SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) | |||
#endif // SdFat_h |
@@ -49,50 +49,28 @@ | |||
#define USE_LONG_FILE_NAMES 1 | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Set ARDUINO_FILE_USES_STREAM nonzero to use Stream as the base class | |||
* for the Arduino File class. If ARDUINO_FILE_USES_STREAM is zero, Print | |||
* will be used as the base class for the Arduino File class. | |||
* If the symbol ENABLE_EXTENDED_TRANSFER_CLASS is nonzero, the class SdFatEX | |||
* will be defined. If the symbol ENABLE_SOFTWARE_SPI_CLASS is also nonzero, | |||
* the class SdFatSoftSpiEX will be defined. | |||
* | |||
* You can save some flash if you do not use Stream input functions such as | |||
* find(), findUntil(), readBytesUntil(), readString(), readStringUntil(), | |||
* parseInt(), and parseFloat(). | |||
* These classes used extended multi-block SD I/O for better performance. | |||
* the SPI bus may not be shared with other devices in this mode. | |||
*/ | |||
#define ARDUINO_FILE_USES_STREAM 1 | |||
//------------------------------------------------------------------------------ | |||
#define ENABLE_EXTENDED_TRANSFER_CLASS 0 | |||
//----------------------------------------------------------------------------- | |||
/** | |||
* The symbol SD_SPI_CONFIGURATION defines SPI access to the SD card. | |||
* | |||
* IF SD_SPI_CONFIGUTATION is define to be zero, only the SdFat class | |||
* is define and SdFat uses a fast custom SPI implementation if avaiable. | |||
* If SD_HAS_CUSTOM_SPI is zero, the standard SPI library is used. | |||
* | |||
* If SD_SPI_CONFIGURATION is define to be one, only the SdFat class is | |||
* define and SdFat uses the standard Arduino SPI.h library. | |||
* | |||
* If SD_SPI_CONFIGURATION is define to be two, only the SdFat class is | |||
* define and SdFat uses software SPI on the pins defined below. | |||
* | |||
* If SD_SPI_CONFIGURATION is define to be three, the three classes, SdFat, | |||
* SdFatLibSpi, and SdFatSoftSpi are defined. SdFat uses the fast | |||
* custom SPI implementation. SdFatLibSpi uses the standard Arduino SPI | |||
* library. SdFatSoftSpi is a template class that uses Software SPI. The | |||
* template parameters define the software SPI pins. See the ThreeCard | |||
* example for simultaneous use of all three classes. | |||
*/ | |||
#define SD_SPI_CONFIGURATION 0 | |||
//------------------------------------------------------------------------------ | |||
* If the symbol USE_STANDARD_SPI_LIBRARY is nonzero, the classes SdFat and | |||
* SdFatEX use the standard Arduino SPI.h library. If USE_STANDARD_SPI_LIBRARY | |||
* is zero, an optimized custom SPI driver is used if it exists. | |||
*/ | |||
#define USE_STANDARD_SPI_LIBRARY 0 | |||
//----------------------------------------------------------------------------- | |||
/** | |||
* If SD_SPI_CONFIGURATION is defined to be two, these definitions | |||
* will define the pins used for software SPI. | |||
* | |||
* The default definition allows Uno shields to be used on other boards. | |||
* If the symbol ENABLE_SOFTWARE_SPI_CLASS is nonzero, the class SdFatSoftSpi | |||
* will be defined. If ENABLE_EXTENDED_TRANSFER_CLASS is also nonzero, | |||
* the class SdFatSoftSpiEX will be defined. | |||
*/ | |||
/** Software SPI Master Out Slave In pin */ | |||
uint8_t const SOFT_SPI_MOSI_PIN = 11; | |||
/** Software SPI Master In Slave Out pin */ | |||
uint8_t const SOFT_SPI_MISO_PIN = 12; | |||
/** Software SPI Clock pin */ | |||
uint8_t const SOFT_SPI_SCK_PIN = 13; | |||
#define ENABLE_SOFTWARE_SPI_CLASS 0 | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters | |||
@@ -104,19 +82,14 @@ uint8_t const SOFT_SPI_SCK_PIN = 13; | |||
/** | |||
* To enable SD card CRC checking set USE_SD_CRC nonzero. | |||
* | |||
* Set USE_SD_CRC to 1 to use a smaller slower CRC-CCITT function. | |||
* Set USE_SD_CRC to 1 to use a smaller CRC-CCITT function. This function | |||
* is slower for AVR but may be fast for ARM and other processors. | |||
* | |||
* Set USE_SD_CRC to 2 to used a larger faster table driven CRC-CCITT function. | |||
* Set USE_SD_CRC to 2 to used a larger table driven CRC-CCITT function. This | |||
* function is faster for AVR but may be slower for ARM and other processors. | |||
*/ | |||
#define USE_SD_CRC 0 | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Set ENABLE_SPI_TRANSACTIONS nonzero to enable the SPI transaction feature | |||
* of the standard Arduino SPI library. You must include SPI.h in your | |||
* programs when ENABLE_SPI_TRANSACTIONS is nonzero. | |||
*/ | |||
#define ENABLE_SPI_TRANSACTIONS 0 | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Handle Watchdog Timer for WiFi modules. | |||
* | |||
@@ -162,16 +135,6 @@ uint8_t const SOFT_SPI_SCK_PIN = 13; | |||
*/ | |||
#define ENDL_CALLS_FLUSH 0 | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* SPI SCK divisor for SD initialization commands. | |||
* or greater | |||
*/ | |||
#ifdef __AVR__ | |||
const uint8_t SPI_SCK_INIT_DIVISOR = 64; | |||
#else | |||
const uint8_t SPI_SCK_INIT_DIVISOR = 128; | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache | |||
* for FAT table entries. This improves performance for large writes | |||
@@ -197,42 +160,25 @@ const uint8_t SPI_SCK_INIT_DIVISOR = 128; | |||
/** | |||
* Determine the default SPI configuration. | |||
*/ | |||
#if defined(__AVR__)\ | |||
#if defined(__STM32F1__) | |||
// has multiple SPI ports | |||
#define SD_HAS_CUSTOM_SPI 2 | |||
#elif defined(__AVR__)\ | |||
|| defined(__SAM3X8E__) || defined(__SAM3X8H__)\ | |||
|| (defined(__arm__) && defined(CORE_TEENSY))\ | |||
|| defined(__STM32F1__)\ | |||
|| defined(PLATFORM_ID)\ | |||
|| defined(ESP8266)\ | |||
|| defined(DOXYGEN) | |||
// Use custom fast implementation. | |||
|| defined(ESP8266) | |||
#define SD_HAS_CUSTOM_SPI 1 | |||
#else // SD_HAS_CUSTOM_SPI | |||
// Use standard SPI library. | |||
#define SD_HAS_CUSTOM_SPI 0 | |||
#endif // SD_HAS_CUSTOM_SPI | |||
//----------------------------------------------------------------------------- | |||
/** | |||
* Number of hardware interfaces. | |||
*/ | |||
#if defined(PLATFORM_ID) | |||
#if Wiring_SPI1 && Wiring_SPI2 | |||
#define SPI_INTERFACE_COUNT 3 | |||
#elif Wiring_SPI1 | |||
#define SPI_INTERFACE_COUNT 2 | |||
#endif // Wiring_SPI1 && Wiring_SPI2 | |||
#endif // defined(PLATFORM_ID) | |||
// default is one | |||
#ifndef SPI_INTERFACE_COUNT | |||
#define SPI_INTERFACE_COUNT 1 | |||
#endif // SPI_INTERFACE_COUNT | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* Check if API to select HW SPI interface is needed. | |||
* Check if API to select HW SPI port is needed. | |||
*/ | |||
#if SPI_INTERFACE_COUNT > 1 && SD_HAS_CUSTOM_SPI\ | |||
&& SD_SPI_CONFIGURATION != 1 && SD_SPI_CONFIGURATION != 2 | |||
#define IMPLEMENT_SPI_INTERFACE_SELECTION 1 | |||
#else // SPI_INTERFACE_COUNT > 1 | |||
#define IMPLEMENT_SPI_INTERFACE_SELECTION 0 | |||
#endif // SPI_INTERFACE_COUNT > 1 | |||
#if (USE_STANDARD_SPI_LIBRARY || SD_HAS_CUSTOM_SPI < 2) | |||
#define IMPLEMENT_SPI_PORT_SELECTION 0 | |||
#else // USE_STANDARD_SPI_LIBRARY | |||
#define IMPLEMENT_SPI_PORT_SELECTION 1 | |||
#endif // USE_STANDARD_SPI_LIBRARY | |||
#endif // SdFatConfig_h |
@@ -1,41 +0,0 @@ | |||
/* Arduino SdFat Library | |||
* Copyright (C) 2015 by William Greiman | |||
* | |||
* This file is part of the Arduino SdFat Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This Library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdFat Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <stdlib.h> | |||
#include "SdFat.h" | |||
#include "SdFatUtil.h" | |||
//------------------------------------------------------------------------------ | |||
#ifdef __arm__ | |||
extern "C" char* sbrk(int incr); | |||
int SdFatUtil::FreeRam() { | |||
char top; | |||
return &top - reinterpret_cast<char*>(sbrk(0)); | |||
} | |||
#else // __arm__ | |||
extern char *__brkval; | |||
extern char __bss_end; | |||
/** Amount of free RAM | |||
* \return The number of free bytes. | |||
*/ | |||
int SdFatUtil::FreeRam() { | |||
char top; | |||
return __brkval ? &top - __brkval : &top - &__bss_end; | |||
} | |||
#endif // __arm | |||
@@ -1,35 +0,0 @@ | |||
/* Arduino SdFat Library | |||
* Copyright (C) 2012 by William Greiman | |||
* | |||
* This file is part of the Arduino SdFat Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This Library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdFat Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef SdFatUtil_h | |||
#define SdFatUtil_h | |||
/** | |||
* \file | |||
* \brief Useful utility functions. | |||
*/ | |||
#include "SdFat.h" | |||
namespace SdFatUtil { | |||
/** Amount of free RAM | |||
* \return The number of free bytes. | |||
*/ | |||
int FreeRam(); | |||
} // namespace SdFatUtil | |||
using namespace SdFatUtil; // NOLINT | |||
#endif // #define SdFatUtil_h |
@@ -33,157 +33,161 @@ | |||
//------------------------------------------------------------------------------ | |||
// SD card errors | |||
/** timeout error for command CMD0 (initialize card in SPI mode) */ | |||
uint8_t const SD_CARD_ERROR_CMD0 = 0X1; | |||
const uint8_t SD_CARD_ERROR_CMD0 = 0X1; | |||
/** CMD8 was not accepted - not a valid SD card*/ | |||
uint8_t const SD_CARD_ERROR_CMD8 = 0X2; | |||
const uint8_t SD_CARD_ERROR_CMD8 = 0X2; | |||
/** card returned an error response for CMD12 (stop multiblock read) */ | |||
uint8_t const SD_CARD_ERROR_CMD12 = 0X3; | |||
const uint8_t SD_CARD_ERROR_CMD12 = 0X3; | |||
/** card returned an error response for CMD17 (read block) */ | |||
uint8_t const SD_CARD_ERROR_CMD17 = 0X4; | |||
const uint8_t SD_CARD_ERROR_CMD17 = 0X4; | |||
/** card returned an error response for CMD18 (read multiple block) */ | |||
uint8_t const SD_CARD_ERROR_CMD18 = 0X5; | |||
const uint8_t SD_CARD_ERROR_CMD18 = 0X5; | |||
/** card returned an error response for CMD24 (write block) */ | |||
uint8_t const SD_CARD_ERROR_CMD24 = 0X6; | |||
const uint8_t SD_CARD_ERROR_CMD24 = 0X6; | |||
/** WRITE_MULTIPLE_BLOCKS command failed */ | |||
uint8_t const SD_CARD_ERROR_CMD25 = 0X7; | |||
const uint8_t SD_CARD_ERROR_CMD25 = 0X7; | |||
/** card returned an error response for CMD58 (read OCR) */ | |||
uint8_t const SD_CARD_ERROR_CMD58 = 0X8; | |||
const uint8_t SD_CARD_ERROR_CMD58 = 0X8; | |||
/** SET_WR_BLK_ERASE_COUNT failed */ | |||
uint8_t const SD_CARD_ERROR_ACMD23 = 0X9; | |||
const uint8_t SD_CARD_ERROR_ACMD23 = 0X9; | |||
/** ACMD41 initialization process timeout */ | |||
uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; | |||
const uint8_t SD_CARD_ERROR_ACMD41 = 0XA; | |||
/** card returned a bad CSR version field */ | |||
uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB; | |||
const uint8_t SD_CARD_ERROR_BAD_CSD = 0XB; | |||
/** erase block group command failed */ | |||
uint8_t const SD_CARD_ERROR_ERASE = 0XC; | |||
const uint8_t SD_CARD_ERROR_ERASE = 0XC; | |||
/** card not capable of single block erase */ | |||
uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; | |||
const uint8_t SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; | |||
/** Erase sequence timed out */ | |||
uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; | |||
const uint8_t SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; | |||
/** card returned an error token instead of read data */ | |||
uint8_t const SD_CARD_ERROR_READ = 0XF; | |||
const uint8_t SD_CARD_ERROR_READ = 0XF; | |||
/** read CID or CSD failed */ | |||
uint8_t const SD_CARD_ERROR_READ_REG = 0X10; | |||
const uint8_t SD_CARD_ERROR_READ_REG = 0X10; | |||
/** timeout while waiting for start of read data */ | |||
uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11; | |||
const uint8_t SD_CARD_ERROR_READ_TIMEOUT = 0X11; | |||
/** card did not accept STOP_TRAN_TOKEN */ | |||
uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12; | |||
const uint8_t SD_CARD_ERROR_STOP_TRAN = 0X12; | |||
/** card returned an error token as a response to a write operation */ | |||
uint8_t const SD_CARD_ERROR_WRITE = 0X13; | |||
/** attempt to write protected block zero */ | |||
uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X14; // REMOVE - not used | |||
const uint8_t SD_CARD_ERROR_WRITE = 0X13; | |||
/** card busy for command */ | |||
const uint8_t SD_CARD_ERROR_CMD_BUSY = 0X14; | |||
/** card did not go ready for a multiple block write */ | |||
uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; // Not used | |||
const uint8_t SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; // Not used | |||
/** card returned an error to a CMD13 status check after a write */ | |||
uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||
const uint8_t SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||
/** timeout occurred during write programming */ | |||
uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; | |||
const uint8_t SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; | |||
/** incorrect rate selected */ | |||
uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18; | |||
const uint8_t SD_CARD_ERROR_SCK_RATE = 0X18; | |||
/** init() not called */ | |||
uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; | |||
const uint8_t SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; | |||
/** card returned an error for CMD59 (CRC_ON_OFF) */ | |||
uint8_t const SD_CARD_ERROR_CMD59 = 0X1A; | |||
const uint8_t SD_CARD_ERROR_CMD59 = 0X1A; | |||
/** invalid read CRC */ | |||
uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B; | |||
const uint8_t SD_CARD_ERROR_READ_CRC = 0X1B; | |||
/** SPI DMA error */ | |||
uint8_t const SD_CARD_ERROR_SPI_DMA = 0X1C; | |||
/** CMD6 not accepted */ | |||
uint8_t const SD_CARD_ERROR_CMD6 = 0X1D; | |||
const uint8_t SD_CARD_ERROR_SPI_DMA = 0X1C; | |||
/** ACMD13 not accepted */ | |||
const uint8_t SD_CARD_ERROR_ACMD13 = 0X1D; | |||
//------------------------------------------------------------------------------ | |||
// card types | |||
/** Standard capacity V1 SD card */ | |||
uint8_t const SD_CARD_TYPE_SD1 = 1; | |||
const uint8_t SD_CARD_TYPE_SD1 = 1; | |||
/** Standard capacity V2 SD card */ | |||
uint8_t const SD_CARD_TYPE_SD2 = 2; | |||
const uint8_t SD_CARD_TYPE_SD2 = 2; | |||
/** High Capacity SD card */ | |||
uint8_t const SD_CARD_TYPE_SDHC = 3; | |||
const uint8_t SD_CARD_TYPE_SDHC = 3; | |||
//------------------------------------------------------------------------------ | |||
#define SD_SCK_HZ(maxSpeed) SPISettings(maxSpeed, MSBFIRST, SPI_MODE0) | |||
#define SD_SCK_MHZ(maxMhz) SPISettings(1000000UL*maxMhz, MSBFIRST, SPI_MODE0) | |||
// SPI divisor constants | |||
/** Set SCK to max rate of F_CPU/2. */ | |||
uint8_t const SPI_FULL_SPEED = 2; | |||
#define SPI_FULL_SPEED SD_SCK_MHZ(50) | |||
/** Set SCK rate to F_CPU/3 for Due */ | |||
uint8_t const SPI_DIV3_SPEED = 3; | |||
#define SPI_DIV3_SPEED SD_SCK_HZ(F_CPU/3) | |||
/** Set SCK rate to F_CPU/4. */ | |||
uint8_t const SPI_HALF_SPEED = 4; | |||
#define SPI_HALF_SPEED SD_SCK_HZ(F_CPU/4) | |||
/** Set SCK rate to F_CPU/6 for Due */ | |||
uint8_t const SPI_DIV6_SPEED = 6; | |||
#define SPI_DIV6_SPEED SD_SCK_HZ(F_CPU/6) | |||
/** Set SCK rate to F_CPU/8. */ | |||
uint8_t const SPI_QUARTER_SPEED = 8; | |||
#define SPI_QUARTER_SPEED SD_SCK_HZ(F_CPU/8) | |||
/** Set SCK rate to F_CPU/16. */ | |||
uint8_t const SPI_EIGHTH_SPEED = 16; | |||
#define SPI_EIGHTH_SPEED SD_SCK_HZ(F_CPU/16) | |||
/** Set SCK rate to F_CPU/32. */ | |||
uint8_t const SPI_SIXTEENTH_SPEED = 32; | |||
#define SPI_SIXTEENTH_SPEED SD_SCK_HZ(F_CPU/32) | |||
//------------------------------------------------------------------------------ | |||
// SD operation timeouts | |||
/** init timeout ms */ | |||
unsigned const SD_INIT_TIMEOUT = 2000; | |||
const uint16_t SD_INIT_TIMEOUT = 2000; | |||
/** erase timeout ms */ | |||
unsigned const SD_ERASE_TIMEOUT = 10000; | |||
const uint16_t SD_ERASE_TIMEOUT = 10000; | |||
/** read timeout ms */ | |||
unsigned const SD_READ_TIMEOUT = 300; | |||
const uint16_t SD_READ_TIMEOUT = 300; | |||
/** write time out ms */ | |||
unsigned const SD_WRITE_TIMEOUT = 600; | |||
const uint16_t SD_WRITE_TIMEOUT = 600; | |||
//------------------------------------------------------------------------------ | |||
// SD card commands | |||
/** GO_IDLE_STATE - init card in spi mode if CS low */ | |||
uint8_t const CMD0 = 0X00; | |||
const uint8_t CMD0 = 0X00; | |||
/** SWITCH_FUNC - Switch Function Command */ | |||
uint8_t const CMD6 = 0X06; | |||
const uint8_t CMD6 = 0X06; | |||
/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ | |||
uint8_t const CMD8 = 0X08; | |||
const uint8_t CMD8 = 0X08; | |||
/** SEND_CSD - read the Card Specific Data (CSD register) */ | |||
uint8_t const CMD9 = 0X09; | |||
const uint8_t CMD9 = 0X09; | |||
/** SEND_CID - read the card identification information (CID register) */ | |||
uint8_t const CMD10 = 0X0A; | |||
const uint8_t CMD10 = 0X0A; | |||
/** STOP_TRANSMISSION - end multiple block read sequence */ | |||
uint8_t const CMD12 = 0X0C; | |||
const uint8_t CMD12 = 0X0C; | |||
/** SEND_STATUS - read the card status register */ | |||
uint8_t const CMD13 = 0X0D; | |||
const uint8_t CMD13 = 0X0D; | |||
/** READ_SINGLE_BLOCK - read a single data block from the card */ | |||
uint8_t const CMD17 = 0X11; | |||
const uint8_t CMD17 = 0X11; | |||
/** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ | |||
uint8_t const CMD18 = 0X12; | |||
const uint8_t CMD18 = 0X12; | |||
/** WRITE_BLOCK - write a single data block to the card */ | |||
uint8_t const CMD24 = 0X18; | |||
const uint8_t CMD24 = 0X18; | |||
/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ | |||
uint8_t const CMD25 = 0X19; | |||
const uint8_t CMD25 = 0X19; | |||
/** ERASE_WR_BLK_START - sets the address of the first block to be erased */ | |||
uint8_t const CMD32 = 0X20; | |||
const uint8_t CMD32 = 0X20; | |||
/** ERASE_WR_BLK_END - sets the address of the last block of the continuous | |||
range to be erased*/ | |||
uint8_t const CMD33 = 0X21; | |||
const uint8_t CMD33 = 0X21; | |||
/** ERASE - erase all previously selected blocks */ | |||
uint8_t const CMD38 = 0X26; | |||
const uint8_t CMD38 = 0X26; | |||
/** APP_CMD - escape for application specific command */ | |||
uint8_t const CMD55 = 0X37; | |||
const uint8_t CMD55 = 0X37; | |||
/** READ_OCR - read the OCR register of a card */ | |||
uint8_t const CMD58 = 0X3A; | |||
const uint8_t CMD58 = 0X3A; | |||
/** CRC_ON_OFF - enable or disable CRC checking */ | |||
uint8_t const CMD59 = 0X3B; | |||
const uint8_t CMD59 = 0X3B; | |||
/** SD_STATUS - Send the SD Status. */ | |||
const uint8_t ACMD13 = 0X0D; | |||
/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be | |||
pre-erased before writing */ | |||
uint8_t const ACMD23 = 0X17; | |||
const uint8_t ACMD23 = 0X17; | |||
/** SD_SEND_OP_COMD - Sends host capacity support information and | |||
activates the card's initialization process */ | |||
uint8_t const ACMD41 = 0X29; | |||
const uint8_t ACMD41 = 0X29; | |||
//============================================================================== | |||
/** status for card in the ready state */ | |||
uint8_t const R1_READY_STATE = 0X00; | |||
const uint8_t R1_READY_STATE = 0X00; | |||
/** status for card in the idle state */ | |||
uint8_t const R1_IDLE_STATE = 0X01; | |||
const uint8_t R1_IDLE_STATE = 0X01; | |||
/** status bit for illegal command */ | |||
uint8_t const R1_ILLEGAL_COMMAND = 0X04; | |||
const uint8_t R1_ILLEGAL_COMMAND = 0X04; | |||
/** start data token for read or write single block*/ | |||
uint8_t const DATA_START_BLOCK = 0XFE; | |||
const uint8_t DATA_START_BLOCK = 0XFE; | |||
/** stop token for write multiple blocks*/ | |||
uint8_t const STOP_TRAN_TOKEN = 0XFD; | |||
const uint8_t STOP_TRAN_TOKEN = 0XFD; | |||
/** start data token for write multiple blocks*/ | |||
uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC; | |||
const uint8_t WRITE_MULTIPLE_TOKEN = 0XFC; | |||
/** mask for data response tokens after a write block operation */ | |||
uint8_t const DATA_RES_MASK = 0X1F; | |||
const uint8_t DATA_RES_MASK = 0X1F; | |||
/** write data accepted token */ | |||
uint8_t const DATA_RES_ACCEPTED = 0X05; | |||
const uint8_t DATA_RES_ACCEPTED = 0X05; | |||
//============================================================================== | |||
/** | |||
* \class CID |
@@ -1,411 +0,0 @@ | |||
/* Arduino SdSpi Library | |||
* Copyright (C) 2013 by William Greiman | |||
* | |||
* This file is part of the Arduino SdSpi Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This Library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdSpi Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
/** | |||
* \file | |||
* \brief SdSpi class for V2 SD/SDHC cards | |||
*/ | |||
#ifndef SdSpi_h | |||
#define SdSpi_h | |||
#include "SystemInclude.h" | |||
#include "SdFatConfig.h" | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* \class SdSpiBase | |||
* \brief Virtual SPI class for access to SD and SDHC flash memory cards. | |||
*/ | |||
class SdSpiBase { | |||
public: | |||
/** Initialize the SPI bus. | |||
* | |||
* \param[in] chipSelectPin SD card chip select pin. | |||
*/ | |||
virtual void begin(uint8_t chipSelectPin) = 0; | |||
/** Set SPI options for access to SD/SDHC cards. | |||
* | |||
* \param[in] divisor SCK clock divider relative to the system clock. | |||
*/ | |||
virtual void beginTransaction(uint8_t divisor); | |||
/** | |||
* End SPI transaction. | |||
*/ | |||
virtual void endTransaction(); | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
virtual uint8_t receive() = 0; | |||
/** Receive multiple bytes. | |||
* | |||
* \param[out] buf Buffer to receive the data. | |||
* \param[in] n Number of bytes to receive. | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
virtual uint8_t receive(uint8_t* buf, size_t n) = 0; | |||
/** Send a byte. | |||
* | |||
* \param[in] data Byte to send | |||
*/ | |||
virtual void send(uint8_t data) = 0; | |||
/** Send multiple bytes. | |||
* | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
virtual void send(const uint8_t* buf, size_t n) = 0; | |||
}; | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* \class SdSpi | |||
* \brief SPI class for access to SD and SDHC flash memory cards. | |||
*/ | |||
#if SD_SPI_CONFIGURATION >= 3 | |||
class SdSpi : public SdSpiBase { | |||
#else // SD_SPI_CONFIGURATION >= 3 | |||
class SdSpi { | |||
#endif // SD_SPI_CONFIGURATION >= 3 | |||
public: | |||
/** Initialize the SPI bus. | |||
* | |||
* \param[in] chipSelectPin SD card chip select pin. | |||
*/ | |||
void begin(uint8_t chipSelectPin); | |||
/** Set SPI options for access to SD/SDHC cards. | |||
* | |||
* \param[in] divisor SCK clock divider relative to the system clock. | |||
*/ | |||
void beginTransaction(uint8_t divisor); | |||
/** | |||
* End SPI transaction | |||
*/ | |||
void endTransaction(); | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
uint8_t receive(); | |||
/** Receive multiple bytes. | |||
* | |||
* \param[out] buf Buffer to receive the data. | |||
* \param[in] n Number of bytes to receive. | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
uint8_t receive(uint8_t* buf, size_t n); | |||
/** Send a byte. | |||
* | |||
* \param[in] data Byte to send | |||
*/ | |||
void send(uint8_t data); | |||
/** Send multiple bytes. | |||
* | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
void send(const uint8_t* buf, size_t n); | |||
/** \return true - uses SPI transactions */ | |||
#if IMPLEMENT_SPI_INTERFACE_SELECTION | |||
void setSpiIf(uint8_t spiIf) { | |||
m_spiIf = spiIf; | |||
} | |||
private: | |||
uint8_t m_spiIf; | |||
#endif // IMPLEMENT_SPI_INTERFACE_SELECTION | |||
}; | |||
//------------------------------------------------------------------------------ | |||
/** | |||
* \class SdSpiLib | |||
* \brief Arduino SPI library class for access to SD and SDHC flash | |||
* memory cards. | |||
*/ | |||
#if SD_SPI_CONFIGURATION >= 3 | |||
class SdSpiLib : public SdSpiBase { | |||
#else // SD_SPI_CONFIGURATION >= 3 | |||
class SdSpiLib { | |||
#endif // SD_SPI_CONFIGURATION >= 3 | |||
public: | |||
/** Initialize the SPI bus. | |||
* | |||
* \param[in] chipSelectPin SD card chip select pin. | |||
*/ | |||
void begin(uint8_t chipSelectPin) { | |||
pinMode(chipSelectPin, OUTPUT); | |||
digitalWrite(chipSelectPin, HIGH); | |||
SPI.begin(); | |||
} | |||
/** Set SPI options for access to SD/SDHC cards. | |||
* | |||
* \param[in] divisor SCK clock divider relative to the system clock. | |||
*/ | |||
void beginTransaction(uint8_t divisor) { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); | |||
#else // ENABLE_SPI_TRANSACTIONS | |||
SPI.setBitOrder(MSBFIRST); | |||
SPI.setDataMode(SPI_MODE0); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
#ifndef SPI_CLOCK_DIV128 | |||
SPI.setClockDivider(divisor); | |||
#else // SPI_CLOCK_DIV128 | |||
int v; | |||
if (divisor <= 2) { | |||
v = SPI_CLOCK_DIV2; | |||
} else if (divisor <= 4) { | |||
v = SPI_CLOCK_DIV4; | |||
} else if (divisor <= 8) { | |||
v = SPI_CLOCK_DIV8; | |||
} else if (divisor <= 16) { | |||
v = SPI_CLOCK_DIV16; | |||
} else if (divisor <= 32) { | |||
v = SPI_CLOCK_DIV32; | |||
} else if (divisor <= 64) { | |||
v = SPI_CLOCK_DIV64; | |||
} else { | |||
v = SPI_CLOCK_DIV128; | |||
} | |||
SPI.setClockDivider(v); | |||
#endif // SPI_CLOCK_DIV128 | |||
} | |||
/** | |||
* End SPI transaction. | |||
*/ | |||
void endTransaction() { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
SPI.endTransaction(); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
} | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
uint8_t receive() { | |||
return SPI.transfer(0XFF); | |||
} | |||
/** Receive multiple bytes. | |||
* | |||
* \param[out] buf Buffer to receive the data. | |||
* \param[in] n Number of bytes to receive. | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
uint8_t receive(uint8_t* buf, size_t n) { | |||
for (size_t i = 0; i < n; i++) { | |||
buf[i] = SPI.transfer(0XFF); | |||
} | |||
return 0; | |||
} | |||
/** Send a byte. | |||
* | |||
* \param[in] b Byte to send | |||
*/ | |||
void send(uint8_t b) { | |||
SPI.transfer(b); | |||
} | |||
/** Send multiple bytes. | |||
* | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
void send(const uint8_t* buf , size_t n) { | |||
for (size_t i = 0; i < n; i++) { | |||
SPI.transfer(buf[i]); | |||
} | |||
} | |||
}; | |||
//------------------------------------------------------------------------------ | |||
#if SD_SPI_CONFIGURATION > 1 || defined(DOXYGEN) | |||
#ifdef ARDUINO | |||
#include "SoftSPI.h" | |||
#elif defined(PLATFORM_ID) // Only defined if a Particle device | |||
#include "SoftSPIParticle.h" | |||
#endif // ARDUINO | |||
/** | |||
* \class SdSpiSoft | |||
* \brief Software SPI class for access to SD and SDHC flash memory cards. | |||
*/ | |||
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||
class SdSpiSoft : public SdSpiBase { | |||
public: | |||
/** Initialize the SPI bus. | |||
* | |||
* \param[in] chipSelectPin SD card chip select pin. | |||
*/ | |||
void begin(uint8_t chipSelectPin) { | |||
pinMode(chipSelectPin, OUTPUT); | |||
digitalWrite(chipSelectPin, HIGH); | |||
m_spi.begin(); | |||
} | |||
/** | |||
* Initialize hardware SPI - dummy for soft SPI | |||
* \param[in] divisor SCK divisor - ignored. | |||
*/ | |||
void beginTransaction(uint8_t divisor) { | |||
(void)divisor; | |||
} | |||
/** | |||
* End SPI transaction - dummy for soft SPI | |||
*/ | |||
void endTransaction() {} | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
uint8_t receive() { | |||
return m_spi.receive(); | |||
} | |||
/** Receive multiple bytes. | |||
* | |||
* \param[out] buf Buffer to receive the data. | |||
* \param[in] n Number of bytes to receive. | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
uint8_t receive(uint8_t* buf, size_t n) { | |||
for (size_t i = 0; i < n; i++) { | |||
buf[i] = receive(); | |||
} | |||
return 0; | |||
} | |||
/** Send a byte. | |||
* | |||
* \param[in] data Byte to send | |||
*/ | |||
void send(uint8_t data) { | |||
m_spi.send(data); | |||
} | |||
/** Send multiple bytes. | |||
* | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
void send(const uint8_t* buf , size_t n) { | |||
for (size_t i = 0; i < n; i++) { | |||
send(buf[i]); | |||
} | |||
} | |||
private: | |||
SoftSPI<MisoPin, MosiPin, SckPin, 0> m_spi; | |||
}; | |||
#endif // SD_SPI_CONFIGURATION > 1 || defined(DOXYGEN) | |||
//------------------------------------------------------------------------------ | |||
#if SD_SPI_CONFIGURATION == 2 | |||
/** Default is software SPI. */ | |||
typedef SdSpiSoft<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN> | |||
SpiDefault_t; | |||
#elif SD_SPI_CONFIGURATION == 1 || !SD_HAS_CUSTOM_SPI | |||
/** Default is Arduino library SPI. */ | |||
typedef SdSpiLib SpiDefault_t; | |||
#else // SpiDefault_t | |||
/** Default is custom fast SPI. */ | |||
typedef SdSpi SpiDefault_t; | |||
#endif // SpiDefault_t | |||
//------------------------------------------------------------------------------ | |||
// Use of in-line for AVR to save flash. | |||
#ifdef __AVR__ | |||
//------------------------------------------------------------------------------ | |||
inline void SdSpi::begin(uint8_t chipSelectPin) { | |||
#ifdef __AVR_ATmega328P__ | |||
// Save a few bytes for 328 CPU - gcc optimizes single bit '|' to sbi. | |||
PORTB |= 1 << 2; // SS high | |||
DDRB |= 1 << 2; // SS output mode | |||
DDRB |= 1 << 3; // MOSI output mode | |||
DDRB |= 1 << 5; // SCK output mode | |||
#else // __AVR_ATmega328P__ | |||
// set SS high - may be chip select for another SPI device | |||
digitalWrite(SS, HIGH); | |||
// SS must be in output mode even it is not chip select | |||
pinMode(SS, OUTPUT); | |||
pinMode(MOSI, OUTPUT); | |||
pinMode(SCK, OUTPUT); | |||
#endif // __AVR_ATmega328P__ | |||
pinMode(chipSelectPin, OUTPUT); | |||
digitalWrite(chipSelectPin, HIGH); | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline void SdSpi::beginTransaction(uint8_t divisor) { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
SPI.beginTransaction(SPISettings()); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
uint8_t b = 2; | |||
uint8_t r = 0; | |||
// See AVR processor documentation. | |||
for (; divisor > b && r < 7; b <<= 1, r += r < 5 ? 1 : 2) {} | |||
SPCR = (1 << SPE) | (1 << MSTR) | (r >> 1); | |||
SPSR = r & 1 ? 0 : 1 << SPI2X; | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline void SdSpi::endTransaction() { | |||
#if ENABLE_SPI_TRANSACTIONS | |||
SPI.endTransaction(); | |||
#endif // ENABLE_SPI_TRANSACTIONS | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline uint8_t SdSpi::receive() { | |||
SPDR = 0XFF; | |||
while (!(SPSR & (1 << SPIF))) {} | |||
return SPDR; | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
if (n-- == 0) { | |||
return 0; | |||
} | |||
SPDR = 0XFF; | |||
for (size_t i = 0; i < n; i++) { | |||
while (!(SPSR & (1 << SPIF))) {} | |||
uint8_t b = SPDR; | |||
SPDR = 0XFF; | |||
buf[i] = b; | |||
} | |||
while (!(SPSR & (1 << SPIF))) {} | |||
buf[n] = SPDR; | |||
return 0; | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline void SdSpi::send(uint8_t data) { | |||
SPDR = data; | |||
while (!(SPSR & (1 << SPIF))) {} | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline void SdSpi::send(const uint8_t* buf , size_t n) { | |||
if (n == 0) { | |||
return; | |||
} | |||
SPDR = buf[0]; | |||
if (n > 1) { | |||
uint8_t b = buf[1]; | |||
size_t i = 2; | |||
while (1) { | |||
while (!(SPSR & (1 << SPIF))) {} | |||
SPDR = b; | |||
if (i == n) { | |||
break; | |||
} | |||
b = buf[i++]; | |||
} | |||
} | |||
while (!(SPSR & (1 << SPIF))) {} | |||
} | |||
#endif // __AVR__ | |||
#endif // SdSpi_h |
@@ -0,0 +1,75 @@ | |||
/* Arduino SdSpiCard Library | |||
* Copyright (C) 2016 by William Greiman | |||
* | |||
* This file is part of the Arduino SdSpiCard Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This Library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdSpiCard Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef SdSpiBaseDriver_h | |||
#define SdSpiBaseDriver_h | |||
/** | |||
* \class SdSpiBaseDriver | |||
* \brief SPI base driver. | |||
*/ | |||
class SdSpiBaseDriver { | |||
public: | |||
/** Set SPI options for access to SD/SDHC cards. | |||
* | |||
*/ | |||
virtual void activate() = 0; | |||
/** Initialize the SPI bus. | |||
* | |||
* \param[in] chipSelectPin SD card chip select pin. | |||
*/ | |||
virtual void begin(uint8_t chipSelectPin) = 0; | |||
/** | |||
* End SPI transaction. | |||
*/ | |||
virtual void deactivate() = 0; | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
virtual uint8_t receive() = 0; | |||
/** Receive multiple bytes. | |||
* | |||
* \param[out] buf Buffer to receive the data. | |||
* \param[in] n Number of bytes to receive. | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
virtual uint8_t receive(uint8_t* buf, size_t n) = 0; | |||
/** Send a byte. | |||
* | |||
* \param[in] data Byte to send | |||
*/ | |||
virtual void send(uint8_t data) = 0; | |||
/** Send multiple bytes. | |||
* | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
virtual void send(const uint8_t* buf, size_t n) = 0; | |||
/** Set CS low. */ | |||
virtual void select() = 0; | |||
/** Save SPI settings. | |||
* \param[in] spiSettings SPI speed, mode, and bit order. | |||
*/ | |||
virtual void setSpiSettings(SPISettings spiSettings) = 0; | |||
/** Set CS high. */ | |||
virtual void unselect() = 0; | |||
}; | |||
#endif // SdSpiBaseDriver_h |
@@ -18,7 +18,6 @@ | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "SdSpiCard.h" | |||
#include "SdSpi.h" | |||
// debug trace macro | |||
#define SD_TRACE(m, b) | |||
// #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); | |||
@@ -44,7 +43,7 @@ static uint8_t CRC7(const uint8_t* data, uint8_t n) { | |||
} | |||
//------------------------------------------------------------------------------ | |||
#if USE_SD_CRC == 1 | |||
// slower CRC-CCITT | |||
// Shift based CRC-CCITT | |||
// uses the x^16,x^12,x^5,x^1 polynomial. | |||
static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { | |||
uint16_t crc = 0; | |||
@@ -59,7 +58,7 @@ static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { | |||
} | |||
#elif USE_SD_CRC > 1 // CRC_CCITT | |||
//------------------------------------------------------------------------------ | |||
// faster CRC-CCITT | |||
// Table based CRC-CCITT | |||
// uses the x^16,x^12,x^5,x^1 polynomial. | |||
#ifdef __AVR__ | |||
static const uint16_t crctab[] PROGMEM = { | |||
@@ -115,29 +114,24 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) { | |||
//============================================================================== | |||
// SdSpiCard member functions | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
bool SdSpiCard::begin(SdSpiDriver* spiDriver) { | |||
m_spiActive = false; | |||
m_errorCode = m_type = 0; | |||
m_spi = spi; | |||
m_chipSelectPin = chipSelectPin; | |||
// 16-bit init start time allows over a minute | |||
unsigned t0 = (unsigned)millis(); | |||
m_spiDriver = spiDriver; | |||
uint16_t t0 = curTimeMS(); | |||
uint32_t arg; | |||
// initialize SPI bus and chip select pin. | |||
spiBegin(m_chipSelectPin); | |||
// set SCK rate for initialization commands. | |||
m_sckDivisor = SPI_SCK_INIT_DIVISOR; | |||
spiBeginTransaction(m_sckDivisor); | |||
spiStart(); | |||
// must supply min of 74 clock cycles with CS high. | |||
spiUnselect(); | |||
for (uint8_t i = 0; i < 10; i++) { | |||
spiSend(0XFF); | |||
} | |||
spiEndTransaction(); | |||
spiSelect(); | |||
// command to go idle in SPI mode | |||
while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { | |||
if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||
if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||
error(SD_CARD_ERROR_CMD0); | |||
goto fail; | |||
} | |||
@@ -161,7 +155,7 @@ bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
type(SD_CARD_TYPE_SD2); | |||
break; | |||
} | |||
if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||
if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||
error(SD_CARD_ERROR_CMD8); | |||
goto fail; | |||
} | |||
@@ -171,7 +165,7 @@ bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { | |||
// check for timeout | |||
if (((unsigned)millis() - t0) > SD_INIT_TIMEOUT) { | |||
if (isTimedOut(t0, SD_INIT_TIMEOUT)) { | |||
error(SD_CARD_ERROR_ACMD41); | |||
goto fail; | |||
} | |||
@@ -190,43 +184,43 @@ bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
spiReceive(); | |||
} | |||
} | |||
chipSelectHigh(); | |||
m_sckDivisor = sckDivisor; | |||
spiStop(); | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
// send command and return error code. Return zero for OK | |||
uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { | |||
// select card | |||
if (!m_selected) { | |||
chipSelectLow(); | |||
if (!m_spiActive) { | |||
spiStart(); | |||
} | |||
// wait if busy | |||
waitNotBusy(SD_WRITE_TIMEOUT); | |||
uint8_t *pa = reinterpret_cast<uint8_t *>(&arg); | |||
#if USE_SD_CRC | |||
// form message | |||
uint8_t buf[6]; | |||
buf[0] = (uint8_t)0x40U | cmd; | |||
buf[1] = (uint8_t)(arg >> 24U); | |||
buf[2] = (uint8_t)(arg >> 16U); | |||
buf[3] = (uint8_t)(arg >> 8U); | |||
buf[4] = (uint8_t)arg; | |||
uint8_t d[6] = {cmd , pa[3], pa[2], pa[1], pa[0]}; | |||
d[0] |= 0X40; | |||
// add crc | |||
d[5] = CRC7(d, 5); | |||
// add CRC | |||
buf[5] = CRC7(buf, 5); | |||
// send message | |||
for (uint8_t k = 0; k < 6; k++) { | |||
spiSend(d[k]); | |||
} | |||
spiSend(buf, 6); | |||
#else // USE_SD_CRC | |||
// send command | |||
spiSend(cmd | 0x40); | |||
// send argument | |||
uint8_t *pa = reinterpret_cast<uint8_t *>(&arg); | |||
for (int8_t i = 3; i >= 0; i--) { | |||
spiSend(pa[i]); | |||
} | |||
@@ -268,35 +262,6 @@ uint32_t SdSpiCard::cardSize() { | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdSpiCard::chipSelectHigh() { | |||
if (!m_selected) { | |||
SD_CS_DBG("chipSelectHigh error"); | |||
return; | |||
} | |||
digitalWrite(m_chipSelectPin, HIGH); | |||
// insure MISO goes high impedance | |||
spiSend(0XFF); | |||
spiEndTransaction(); | |||
m_selected = false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdSpiCard::chipSelectLow() { | |||
#if WDT_YIELD_TIME_MICROS | |||
static uint32_t last; | |||
if ((micros() - last) > WDT_YIELD_TIME_MICROS) { | |||
SysCall::yield(); | |||
last = micros(); | |||
} | |||
#endif // WDT_YIELD_TIME_MICROS | |||
if (m_selected) { | |||
SD_CS_DBG("chipSelectLow error"); | |||
return; | |||
} | |||
spiBeginTransaction(m_sckDivisor); | |||
digitalWrite(m_chipSelectPin, LOW); | |||
m_selected = true; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | |||
csd_t csd; | |||
if (!readCSD(&csd)) { | |||
@@ -326,11 +291,11 @@ bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { | |||
error(SD_CARD_ERROR_ERASE_TIMEOUT); | |||
goto fail; | |||
} | |||
chipSelectHigh(); | |||
spiStop(); | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -341,21 +306,33 @@ bool SdSpiCard::eraseSingleBlockEnable() { | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::isBusy() { | |||
bool rtn = true; | |||
bool selected = m_selected; | |||
chipSelectLow(); | |||
bool spiActive = m_spiActive; | |||
if (!spiActive) { | |||
spiStart(); | |||
} | |||
for (uint8_t i = 0; i < 8; i++) { | |||
if (0XFF == spiReceive()) { | |||
rtn = false; | |||
break; | |||
} | |||
} | |||
if (!selected) { | |||
chipSelectHigh(); | |||
if (!spiActive) { | |||
spiStop(); | |||
} | |||
return rtn; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::isTimedOut(uint16_t startMS, uint16_t timeoutMS) { | |||
#if WDT_YIELD_TIME_MICROS | |||
static uint32_t last; | |||
if ((micros() - last) > WDT_YIELD_TIME_MICROS) { | |||
SysCall::yield(); | |||
last = micros(); | |||
} | |||
#endif // WDT_YIELD_TIME_MICROS | |||
return (curTimeMS() - startMS) > timeoutMS; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { | |||
SD_TRACE("RB", blockNumber); | |||
// use address if not SDHC card | |||
@@ -369,11 +346,11 @@ bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { | |||
if (!readData(dst, 512)) { | |||
goto fail; | |||
} | |||
chipSelectHigh(); | |||
spiStop(); | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -390,15 +367,7 @@ bool SdSpiCard::readBlocks(uint32_t block, uint8_t* dst, size_t count) { | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::readData(uint8_t *dst) { | |||
bool selected = m_selected; | |||
chipSelectLow(); | |||
if (!readData(dst, 512)) { | |||
return false; | |||
} | |||
if (!selected) { | |||
chipSelectHigh(); | |||
} | |||
return true; | |||
return readData(dst, 512); | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::readData(uint8_t* dst, size_t count) { | |||
@@ -406,9 +375,9 @@ bool SdSpiCard::readData(uint8_t* dst, size_t count) { | |||
uint16_t crc; | |||
#endif // USE_SD_CRC | |||
// wait for start block token | |||
unsigned t0 = millis(); | |||
uint16_t t0 = curTimeMS(); | |||
while ((m_status = spiReceive()) == 0XFF) { | |||
if (((unsigned)millis() - t0) > SD_READ_TIMEOUT) { | |||
if (isTimedOut(t0, SD_READ_TIMEOUT)) { | |||
error(SD_CARD_ERROR_READ_TIMEOUT); | |||
goto fail; | |||
} | |||
@@ -438,7 +407,7 @@ bool SdSpiCard::readData(uint8_t* dst, size_t count) { | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -452,11 +421,11 @@ bool SdSpiCard::readOCR(uint32_t* ocr) { | |||
p[3 - i] = spiReceive(); | |||
} | |||
chipSelectHigh(); | |||
spiStop(); | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -467,10 +436,14 @@ bool SdSpiCard::readRegister(uint8_t cmd, void* buf) { | |||
error(SD_CARD_ERROR_READ_REG); | |||
goto fail; | |||
} | |||
return readData(dst, 16); | |||
if (!readData(dst, 16)) { | |||
goto fail; | |||
} | |||
spiStop(); | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -483,67 +456,81 @@ bool SdSpiCard::readStart(uint32_t blockNumber) { | |||
error(SD_CARD_ERROR_CMD18); | |||
goto fail; | |||
} | |||
// spiStop(); | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::readStop() { | |||
if (cardCommand(CMD12, 0)) { | |||
error(SD_CARD_ERROR_CMD12); | |||
//----------------------------------------------------------------------------- | |||
bool SdSpiCard::readStatus(uint8_t* status) { | |||
// retrun is R2 so read extra status byte. | |||
if (cardAcmd(ACMD13, 0) || spiReceive()) { | |||
error(SD_CARD_ERROR_ACMD13); | |||
goto fail; | |||
} | |||
chipSelectHigh(); | |||
if (!readData(status, 64)) { | |||
goto fail; | |||
} | |||
spiStop(); | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::sendCmd6(uint32_t arg, uint8_t* status) { | |||
if (cardCommand(CMD6, arg)) { | |||
error(SD_CARD_ERROR_CMD6); | |||
goto fail; | |||
//----------------------------------------------------------------------------- | |||
void SdSpiCard::spiStart() { | |||
if (!m_spiActive) { | |||
spiActivate(); | |||
spiSelect(); | |||
m_spiActive = true; | |||
} | |||
if (!readData(status, 64)) { | |||
} | |||
//----------------------------------------------------------------------------- | |||
void SdSpiCard::spiStop() { | |||
if (m_spiActive) { | |||
spiUnselect(); | |||
spiSend(0XFF); | |||
spiDeactivate(); | |||
m_spiActive = false; | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::readStop() { | |||
if (cardCommand(CMD12, 0)) { | |||
error(SD_CARD_ERROR_CMD12); | |||
goto fail; | |||
} | |||
chipSelectHigh(); | |||
spiStop(); | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::setHighSpeedMode(uint8_t divisor) { | |||
uint8_t status[64]; | |||
uint8_t saveDivisor = m_sckDivisor; | |||
setSckDivisor(128); | |||
if (!sendCmd6(0X00FFFFFF, status) || (2 & status[13]) == 0 || | |||
!sendCmd6(0X80FFFFF1, status) || (status[16] & 0XF) != 1) { | |||
setSckDivisor(saveDivisor); | |||
return false; | |||
} | |||
setSckDivisor(divisor); | |||
return true; | |||
} | |||
//------------------------------------------------------------------------------ | |||
// wait for card to go not busy | |||
bool SdSpiCard::waitNotBusy(uint16_t timeoutMillis) { | |||
unsigned t0 = millis(); | |||
bool SdSpiCard::waitNotBusy(uint16_t timeoutMS) { | |||
uint16_t t0 = curTimeMS(); | |||
#if WDT_YIELD_TIME_MICROS | |||
// Call isTimedOut first to insure yield is called. | |||
while (!isTimedOut(t0, timeoutMS)) { | |||
if (spiReceive() == 0XFF) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
#else // WDT_YIELD_TIME_MICROS | |||
// Check not busy first since yield is not called in isTimedOut. | |||
while (spiReceive() != 0XFF) { | |||
if (((unsigned)millis() - t0) >= timeoutMillis) { | |||
goto fail; | |||
if (isTimedOut(t0, timeoutMS)) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
fail: | |||
return false; | |||
#endif // WDT_YIELD_TIME_MICROS | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | |||
@@ -574,16 +561,16 @@ bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { | |||
} | |||
#endif // CHECK_PROGRAMMING | |||
chipSelectHigh(); | |||
spiStop(); | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { | |||
if (!writeStart(block, count)) { | |||
if (!writeStart(block)) { | |||
goto fail; | |||
} | |||
for (size_t b = 0; b < count; b++, src += 512) { | |||
@@ -594,13 +581,11 @@ bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { | |||
return writeStop(); | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::writeData(const uint8_t* src) { | |||
bool selected = m_selected; | |||
chipSelectLow(); | |||
// wait for previous write to finish | |||
if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||
error(SD_CARD_ERROR_WRITE_TIMEOUT); | |||
@@ -609,13 +594,10 @@ bool SdSpiCard::writeData(const uint8_t* src) { | |||
if (!writeData(WRITE_MULTIPLE_TOKEN, src)) { | |||
goto fail; | |||
} | |||
if (!selected) { | |||
chipSelectHigh(); | |||
} | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -639,7 +621,23 @@ bool SdSpiCard::writeData(uint8_t token, const uint8_t* src) { | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::writeStart(uint32_t blockNumber) { | |||
// use address if not SDHC card | |||
if (type() != SD_CARD_TYPE_SDHC) { | |||
blockNumber <<= 9; | |||
} | |||
if (cardCommand(CMD25, blockNumber)) { | |||
error(SD_CARD_ERROR_CMD25); | |||
goto fail; | |||
} | |||
return true; | |||
fail: | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
@@ -661,24 +659,20 @@ bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) { | |||
return true; | |||
fail: | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool SdSpiCard::writeStop() { | |||
chipSelectLow(); | |||
if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||
goto fail; | |||
} | |||
spiSend(STOP_TRAN_TOKEN); | |||
if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||
goto fail; | |||
} | |||
chipSelectHigh(); | |||
spiStop(); | |||
return true; | |||
fail: | |||
error(SD_CARD_ERROR_STOP_TRAN); | |||
chipSelectHigh(); | |||
spiStop(); | |||
return false; | |||
} |
@@ -23,10 +23,10 @@ | |||
* \file | |||
* \brief SdSpiCard class for V2 SD/SDHC cards | |||
*/ | |||
#include "SystemInclude.h" | |||
#include "SdFatConfig.h" | |||
#include <stddef.h> | |||
#include "SysCall.h" | |||
#include "SdInfo.h" | |||
#include "SdSpi.h" | |||
#include "SdSpiDriver.h" | |||
//============================================================================== | |||
/** | |||
* \class SdSpiCard | |||
@@ -34,23 +34,13 @@ | |||
*/ | |||
class SdSpiCard { | |||
public: | |||
/** typedef for SPI class. */ | |||
#if SD_SPI_CONFIGURATION < 3 | |||
typedef SpiDefault_t m_spi_t; | |||
#else // SD_SPI_CONFIGURATION < 3 | |||
typedef SdSpiBase m_spi_t; | |||
#endif // SD_SPI_CONFIGURATION < 3 | |||
/** Construct an instance of SdSpiCard. */ | |||
SdSpiCard() : m_selected(false), | |||
m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} | |||
SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} | |||
/** Initialize the SD card. | |||
* \param[in] spi SPI object. | |||
* \param[in] chipSelectPin SD chip select pin. | |||
* \param[in] sckDivisor SPI clock divisor. | |||
* \param[in] spiDriver SPI driver for card. | |||
* \return true for success else false. | |||
*/ | |||
bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS, | |||
uint8_t sckDivisor = SPI_FULL_SPEED); | |||
bool begin(SdSpiDriver* spiDriver); | |||
/** | |||
* Determine the size of an SD flash memory card. | |||
* | |||
@@ -58,16 +48,6 @@ class SdSpiCard { | |||
* or zero if an error occurs. | |||
*/ | |||
uint32_t cardSize(); | |||
/** Set the SD chip select pin high, send a dummy byte, and call SPI endTransaction. | |||
* | |||
* This function should only be called by programs doing raw I/O to the SD. | |||
*/ | |||
void chipSelectHigh(); | |||
/** Set the SD chip select pin low and call SPI beginTransaction. | |||
* | |||
* This function should only be called by programs doing raw I/O to the SD. | |||
*/ | |||
void chipSelectLow(); | |||
/** Erase a range of blocks. | |||
* | |||
* \param[in] firstBlock The address of the first block in the range. | |||
@@ -178,40 +158,18 @@ class SdSpiCard { | |||
* the value false is returned for failure. | |||
*/ | |||
bool readStart(uint32_t blockNumber); | |||
/** Return the 64 byte card status | |||
* \param[out] status location for 64 status bytes. | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool readStatus(uint8_t* status); | |||
/** End a read multiple blocks sequence. | |||
* | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool readStop(); | |||
/** Return SCK divisor. | |||
* | |||
* \return Requested SCK divisor. | |||
*/ | |||
uint8_t sckDivisor() { | |||
return m_sckDivisor; | |||
} | |||
/** \return the SD chip select status, true if slected else false. */ | |||
bool selected() {return m_selected;} | |||
/** Send CMD6 - Switch Function Command | |||
* | |||
* param[in] arg 32-bit argument to CMD6. | |||
* param[out] status - 64 byte status returned by CMD6. | |||
* \return true if the command was accepted else false. | |||
*/ | |||
bool sendCmd6(uint32_t arg, uint8_t* status); | |||
/** Set High Speed Bus Mode. | |||
* | |||
* param[in] divisor new value for SPI SCK divisor. | |||
* \return true if successful else false. | |||
*/ | |||
bool setHighSpeedMode(uint8_t divisor); | |||
/** Set SCK divisor. | |||
* param[in] sckDivisor value for divisor. | |||
*/ | |||
void setSckDivisor(uint8_t sckDivisor) { | |||
m_sckDivisor = sckDivisor; | |||
} | |||
/** Return the card type: SD V1, SD V2 or SDHC | |||
* \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. | |||
*/ | |||
@@ -244,6 +202,18 @@ class SdSpiCard { | |||
*/ | |||
bool writeData(const uint8_t* src); | |||
/** Start a write multiple blocks sequence. | |||
* | |||
* \param[in] blockNumber Address of first block in sequence. | |||
* | |||
* \note This function is used with writeData() and writeStop() | |||
* for optimized multiple block writes. | |||
* | |||
* \return The value true is returned for success and | |||
* the value false is returned for failure. | |||
*/ | |||
bool writeStart(uint32_t blockNumber); | |||
/** Start a write multiple blocks sequence with pre-erase. | |||
* | |||
* \param[in] blockNumber Address of first block in sequence. | |||
* \param[in] eraseCount The number of blocks to be pre-erased. | |||
@@ -261,6 +231,10 @@ class SdSpiCard { | |||
* the value false is returned for failure. | |||
*/ | |||
bool writeStop(); | |||
/** Set CS low and activate the card. */ | |||
void spiStart(); | |||
/** Set CS high and deactivate the card. */ | |||
void spiStop(); | |||
private: | |||
// private functions | |||
@@ -269,74 +243,48 @@ class SdSpiCard { | |||
return cardCommand(cmd, arg); | |||
} | |||
uint8_t cardCommand(uint8_t cmd, uint32_t arg); | |||
bool isTimedOut(uint16_t startMS, uint16_t timeoutMS); | |||
bool readData(uint8_t* dst, size_t count); | |||
bool readRegister(uint8_t cmd, void* buf); | |||
void type(uint8_t value) { | |||
m_type = value; | |||
} | |||
bool waitNotBusy(uint16_t timeoutMillis); | |||
bool waitNotBusy(uint16_t timeoutMS); | |||
bool writeData(uint8_t token, const uint8_t* src); | |||
void spiBegin(uint8_t chipSelectPin) { | |||
m_spi->begin(chipSelectPin); | |||
} | |||
void spiBeginTransaction(uint8_t spiDivisor) { | |||
m_spi->beginTransaction(spiDivisor); | |||
//--------------------------------------------------------------------------- | |||
// functions defined in SdSpiDriver.h | |||
void spiActivate() { | |||
m_spiDriver->activate(); | |||
} | |||
void spiEndTransaction() { | |||
m_spi->endTransaction(); | |||
void spiDeactivate() { | |||
m_spiDriver->deactivate(); | |||
} | |||
uint8_t spiReceive() { | |||
return m_spi->receive(); | |||
return m_spiDriver->receive(); | |||
} | |||
uint8_t spiReceive(uint8_t* buf, size_t n) { | |||
return m_spi->receive(buf, n); | |||
return m_spiDriver->receive(buf, n); | |||
} | |||
void spiSend(uint8_t data) { | |||
m_spi->send(data); | |||
m_spiDriver->send(data); | |||
} | |||
void spiSend(const uint8_t* buf, size_t n) { | |||
m_spi->send(buf, n); | |||
m_spiDriver->send(buf, n); | |||
} | |||
void spiSelect() { | |||
m_spiDriver->select(); | |||
} | |||
void spiUnselect() { | |||
m_spiDriver->unselect(); | |||
} | |||
m_spi_t* m_spi; | |||
bool m_selected; | |||
uint8_t m_chipSelectPin; | |||
uint8_t m_errorCode; | |||
uint8_t m_sckDivisor; | |||
SdSpiDriver *m_spiDriver; | |||
bool m_spiActive; | |||
uint8_t m_status; | |||
uint8_t m_type; | |||
}; | |||
//============================================================================== | |||
/** | |||
* \class Sd2Card | |||
* \brief Raw access to SD and SDHC card using default SPI library. | |||
*/ | |||
class Sd2Card : public SdSpiCard { | |||
public: | |||
/** Initialize the SD card. | |||
* \param[in] chipSelectPin SD chip select pin. | |||
* \param[in] sckDivisor SPI clock divisor. | |||
* \return true for success else false. | |||
*/ | |||
bool begin(uint8_t chipSelectPin = SS, uint8_t sckDivisor = 2) { | |||
return SdSpiCard::begin(&m_spi, chipSelectPin, sckDivisor); | |||
} | |||
/** Initialize the SD card. Obsolete form. | |||
* \param[in] chipSelectPin SD chip select pin. | |||
* \param[in] sckDivisor SPI clock divisor. | |||
* \return true for success else false. | |||
*/ | |||
bool init(uint8_t sckDivisor = 2, uint8_t chipSelectPin = SS) { | |||
return begin(chipSelectPin, sckDivisor); | |||
} | |||
private: | |||
bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS, | |||
uint8_t sckDivisor = SPI_FULL_SPEED) { | |||
(void)spi; | |||
(void)chipSelectPin; | |||
(void)sckDivisor; | |||
return false; | |||
} | |||
SpiDefault_t m_spi; | |||
}; | |||
#endif // SpiCard_h |
@@ -1,110 +0,0 @@ | |||
/* Arduino SdFat Library | |||
* Copyright (C) 2016 by William Greiman | |||
* | |||
* This file is part of the Arduino SdFat Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This Library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdFat Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "SdSpi.h" | |||
#if defined(PLATFORM_ID) | |||
static uint32_t bugDelay = 0; // fix for SPI DMA bug. | |||
static volatile bool SPI_DMA_TransferCompleted = false; | |||
static SPIClass* const spiPtr[] = { | |||
&SPI | |||
#if Wiring_SPI1 | |||
, &SPI1 | |||
#if Wiring_SPI2 | |||
, &SPI2 | |||
#endif // Wiring_SPI2 | |||
#endif // Wiring_SPI1 | |||
}; | |||
#if SPI_INTERFACE_COUNT == 1 | |||
const uint8_t m_spiIf = 0; | |||
#endif | |||
//----------------------------------------------------------------------------- | |||
void SD_SPI_DMA_TransferComplete_Callback(void) { | |||
SPI_DMA_TransferCompleted = true; | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdSpi::begin(uint8_t chipSelectPin) { | |||
spiPtr[m_spiIf]->begin(chipSelectPin); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void SdSpi::beginTransaction(uint8_t divisor) { | |||
spiPtr[m_spiIf]->setBitOrder(MSBFIRST); | |||
spiPtr[m_spiIf]->setDataMode(SPI_MODE0); | |||
#ifndef SPI_CLOCK_DIV128 | |||
spiPtr[m_spiIf]->setClockDivider(divisor); | |||
#else // SPI_CLOCK_DIV128 | |||
int v; | |||
if (divisor <= 2) { | |||
v = SPI_CLOCK_DIV2; | |||
} else if (divisor <= 4) { | |||
v = SPI_CLOCK_DIV4; | |||
} else if (divisor <= 8) { | |||
v = SPI_CLOCK_DIV8; | |||
} else if (divisor <= 16) { | |||
v = SPI_CLOCK_DIV16; | |||
} else if (divisor <= 32) { | |||
v = SPI_CLOCK_DIV32; | |||
} else if (divisor <= 64) { | |||
v = SPI_CLOCK_DIV64; | |||
} else { | |||
v = SPI_CLOCK_DIV128; | |||
} | |||
spiPtr[m_spiIf]->setClockDivider(v); | |||
#endif // SPI_CLOCK_DIV128 | |||
// delay for SPI transfer done callback too soon bug. | |||
bugDelay = 24*divisor*(1 + m_spiIf)/60; | |||
} | |||
//----------------------------------------------------------------------------- | |||
void SdSpi::endTransaction() { | |||
} | |||
//----------------------------------------------------------------------------- | |||
/** SPI receive a byte */ | |||
uint8_t SdSpi::receive() { | |||
return spiPtr[m_spiIf]->transfer(0xFF); | |||
} | |||
//----------------------------------------------------------------------------- | |||
uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
SPI_DMA_TransferCompleted = false; | |||
spiPtr[m_spiIf]->transfer(0, buf, n, SD_SPI_DMA_TransferComplete_Callback); | |||
while (!SPI_DMA_TransferCompleted) {} | |||
if (bugDelay) { | |||
delayMicroseconds(bugDelay); | |||
} | |||
return 0; | |||
} | |||
//----------------------------------------------------------------------------- | |||
/** SPI send a byte */ | |||
void SdSpi::send(uint8_t b) { | |||
spiPtr[m_spiIf]->transfer(b); | |||
} | |||
//----------------------------------------------------------------------------- | |||
void SdSpi::send(const uint8_t* buf , size_t n) { | |||
SPI_DMA_TransferCompleted = false; | |||
spiPtr[m_spiIf]->transfer(const_cast<uint8_t*>(buf), 0, n, | |||
SD_SPI_DMA_TransferComplete_Callback); | |||
while (!SPI_DMA_TransferCompleted) {} | |||
if (bugDelay) { | |||
delayMicroseconds(bugDelay); | |||
} | |||
} | |||
#endif // defined(PLATFORM_ID) |
@@ -0,0 +1,359 @@ | |||
/* SdFat Library | |||
* Copyright (C) 2016 by William Greiman | |||
* | |||
* This file is part of the FatLib Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This Library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the FatLib Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
/** | |||
* \file | |||
* \brief SpiDriver classes | |||
*/ | |||
#ifndef SdSpiDriver_h | |||
#define SdSpiDriver_h | |||
#include <Arduino.h> | |||
#include "SPI.h" | |||
#include "SdSpiCard/SdSpiBaseDriver.h" | |||
#include "SdFatConfig.h" | |||
//----------------------------------------------------------------------------- | |||
/** | |||
* \class SdSpiLibDriver | |||
* \brief SdSpiLibDriver - use standard SPI library. | |||
*/ | |||
#if ENABLE_SOFTWARE_SPI_CLASS | |||
class SdSpiLibDriver : public SdSpiBaseDriver { | |||
#else // ENABLE_SOFTWARE_SPI_CLASS | |||
class SdSpiLibDriver { | |||
#endif // ENABLE_SOFTWARE_SPI_CLASS | |||
public: | |||
/** Activate SPI hardware. */ | |||
void activate() { | |||
SPI.beginTransaction(m_spiSettings); | |||
} | |||
/** Deactivate SPI hardware. */ | |||
void deactivate() { | |||
SPI.endTransaction(); | |||
} | |||
/** Initialize the SPI bus. | |||
* | |||
* \param[in] csPin SD card chip select pin. | |||
*/ | |||
void begin(uint8_t csPin) { | |||
m_csPin = csPin; | |||
digitalWrite(csPin, HIGH); | |||
pinMode(csPin, OUTPUT); | |||
SPI.begin(); | |||
} | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
uint8_t receive() { | |||
return SPI.transfer( 0XFF); | |||
} | |||
/** Receive multiple bytes. | |||
* | |||
* \param[out] buf Buffer to receive the data. | |||
* \param[in] n Number of bytes to receive. | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
uint8_t receive(uint8_t* buf, size_t n) { | |||
for (size_t i = 0; i < n; i++) { | |||
buf[i] = SPI.transfer(0XFF); | |||
} | |||
return 0; | |||
} | |||
/** Send a byte. | |||
* | |||
* \param[in] data Byte to send | |||
*/ | |||
void send(uint8_t data) { | |||
SPI.transfer(data); | |||
} | |||
/** Send multiple bytes. | |||
* | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
void send(const uint8_t* buf, size_t n) { | |||
for (size_t i = 0; i < n; i++) { | |||
SPI.transfer(buf[i]); | |||
} | |||
} | |||
/** Set CS low. */ | |||
void select() { | |||
digitalWrite(m_csPin, LOW); | |||
} | |||
/** Save SPISettings. | |||
* | |||
* \param[in] spiSettings SPI speed, mode, and byte order. | |||
*/ | |||
void setSpiSettings(SPISettings spiSettings) { | |||
m_spiSettings = spiSettings; | |||
} | |||
/** Set CS high. */ | |||
void unselect() { | |||
digitalWrite(m_csPin, HIGH); | |||
} | |||
private: | |||
SPISettings m_spiSettings; | |||
uint8_t m_csPin; | |||
}; | |||
//----------------------------------------------------------------------------- | |||
/** | |||
* \class SdSpiAltDriver | |||
* \brief Optimized SPI class for access to SD and SDHC flash memory cards. | |||
*/ | |||
#if ENABLE_SOFTWARE_SPI_CLASS | |||
class SdSpiAltDriver : public SdSpiBaseDriver { | |||
#else // ENABLE_SOFTWARE_SPI_CLASS | |||
class SdSpiAltDriver { | |||
#endif // ENABLE_SOFTWARE_SPI_CLASS | |||
public: | |||
/** Activate SPI hardware. */ | |||
void activate(); | |||
/** Deactivate SPI hardware. */ | |||
void deactivate(); | |||
/** Initialize the SPI bus. | |||
* | |||
* \param[in] csPin SD card chip select pin. | |||
*/ | |||
void begin(uint8_t csPin); | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
uint8_t receive(); | |||
/** Receive multiple bytes. | |||
* | |||
* \param[out] buf Buffer to receive the data. | |||
* \param[in] n Number of bytes to receive. | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
uint8_t receive(uint8_t* buf, size_t n); | |||
/** Send a byte. | |||
* | |||
* \param[in] data Byte to send | |||
*/ | |||
void send(uint8_t data); | |||
/** Send multiple bytes. | |||
* | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
void send(const uint8_t* buf, size_t n); | |||
/** Set CS low. */ | |||
void select() { | |||
digitalWrite(m_csPin, LOW); | |||
} | |||
/** Save SPISettings. | |||
* | |||
* \param[in] spiSettings SPI speed, mode, and byte order. | |||
*/ | |||
void setSpiSettings(SPISettings spiSettings) { | |||
m_spiSettings = spiSettings; | |||
} | |||
/** Set CS high. */ | |||
void unselect() { | |||
digitalWrite(m_csPin, HIGH); | |||
} | |||
#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) | |||
/** Set SPI port number. | |||
* \param[in] portNumber Hardware SPI port number. | |||
*/ | |||
void setPort(uint8_t portNumber); | |||
private: | |||
uint8_t m_spiPort; | |||
#else // IMPLEMENT_SPI_PORT_SELECTION | |||
private: | |||
#endif // IMPLEMENT_SPI_PORT_SELECTION | |||
SPISettings m_spiSettings; | |||
uint8_t m_csPin; | |||
}; | |||
//------------------------------------------------------------------------------ | |||
#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||
#ifdef ARDUINO | |||
#include "AltSpiDrivers/SoftSPI.h" | |||
#elif defined(PLATFORM_ID) // Only defined if a Particle device | |||
#include "SoftSPIParticle.h" | |||
#endif // ARDUINO | |||
/** | |||
* \class SdSpiSoftDriver | |||
* \brief Software SPI class for access to SD and SDHC flash memory cards. | |||
*/ | |||
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> | |||
class SdSpiSoftDriver : public SdSpiBaseDriver { | |||
public: | |||
/** Dummy activate SPI hardware for software SPI */ | |||
void activate() {} | |||
/** Dummy deactivate SPI hardware for software SPI */ | |||
void deactivate() {} | |||
/** Initialize the SPI bus. | |||
* | |||
* \param[in] csPin SD card chip select pin. | |||
*/ | |||
void begin(uint8_t csPin) { | |||
m_csPin = csPin; | |||
pinMode(m_csPin, OUTPUT); | |||
digitalWrite(m_csPin, HIGH); | |||
m_spi.begin(); | |||
} | |||
/** Receive a byte. | |||
* | |||
* \return The byte. | |||
*/ | |||
uint8_t receive() { | |||
return m_spi.receive(); | |||
} | |||
/** Receive multiple bytes. | |||
* | |||
* \param[out] buf Buffer to receive the data. | |||
* \param[in] n Number of bytes to receive. | |||
* | |||
* \return Zero for no error or nonzero error code. | |||
*/ | |||
uint8_t receive(uint8_t* buf, size_t n) { | |||
for (size_t i = 0; i < n; i++) { | |||
buf[i] = receive(); | |||
} | |||
return 0; | |||
} | |||
/** Send a byte. | |||
* | |||
* \param[in] data Byte to send | |||
*/ | |||
void send(uint8_t data) { | |||
m_spi.send(data); | |||
} | |||
/** Send multiple bytes. | |||
* | |||
* \param[in] buf Buffer for data to be sent. | |||
* \param[in] n Number of bytes to send. | |||
*/ | |||
void send(const uint8_t* buf , size_t n) { | |||
for (size_t i = 0; i < n; i++) { | |||
send(buf[i]); | |||
} | |||
} | |||
/** Set CS low. */ | |||
void select() { | |||
digitalWrite(m_csPin, LOW); | |||
} | |||
/** Save SPISettings. | |||
* | |||
* \param[in] spiSettings SPI speed, mode, and byte order. | |||
*/ | |||
void setSpiSettings(SPISettings spiSettings) { | |||
(void)spiSettings; | |||
} | |||
/** Set CS high. */ | |||
void unselect() { | |||
digitalWrite(m_csPin, HIGH); | |||
} | |||
private: | |||
uint8_t m_csPin; | |||
SoftSPI<MisoPin, MosiPin, SckPin, 0> m_spi; | |||
}; | |||
#endif // ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) | |||
//----------------------------------------------------------------------------- | |||
// Choose SPI driver for SdFat and SdFatEX classes. | |||
#if USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI | |||
/** SdFat uses Arduino library SPI. */ | |||
typedef SdSpiLibDriver SdFatSpiDriver; | |||
#else // USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI | |||
/** SdFat uses custom fast SPI. */ | |||
typedef SdSpiAltDriver SdFatSpiDriver; | |||
#endif // USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI | |||
/** typedef for for SdSpiCard class. */ | |||
#if ENABLE_SOFTWARE_SPI_CLASS | |||
// Need virtual driver. | |||
typedef SdSpiBaseDriver SdSpiDriver; | |||
#else // ENABLE_SOFTWARE_SPI_CLASS | |||
// Don't need virtual driver. | |||
typedef SdFatSpiDriver SdSpiDriver; | |||
#endif // ENABLE_SOFTWARE_SPI_CLASS | |||
//============================================================================= | |||
// Use of in-line for AVR to save flash. | |||
#ifdef __AVR__ | |||
//------------------------------------------------------------------------------ | |||
inline void SdSpiAltDriver::begin(uint8_t csPin) { | |||
m_csPin = csPin; | |||
pinMode(m_csPin, OUTPUT); | |||
digitalWrite(m_csPin, HIGH); | |||
SPI.begin(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline void SdSpiAltDriver::activate() { | |||
SPI.beginTransaction(m_spiSettings); | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline void SdSpiAltDriver::deactivate() { | |||
SPI.endTransaction(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline uint8_t SdSpiAltDriver::receive() { | |||
SPDR = 0XFF; | |||
while (!(SPSR & (1 << SPIF))) {} | |||
return SPDR; | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { | |||
if (n-- == 0) { | |||
return 0; | |||
} | |||
SPDR = 0XFF; | |||
for (size_t i = 0; i < n; i++) { | |||
while (!(SPSR & (1 << SPIF))) {} | |||
uint8_t b = SPDR; | |||
SPDR = 0XFF; | |||
buf[i] = b; | |||
} | |||
while (!(SPSR & (1 << SPIF))) {} | |||
buf[n] = SPDR; | |||
return 0; | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline void SdSpiAltDriver::send(uint8_t data) { | |||
SPDR = data; | |||
while (!(SPSR & (1 << SPIF))) {} | |||
} | |||
//------------------------------------------------------------------------------ | |||
inline void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { | |||
if (n == 0) { | |||
return; | |||
} | |||
SPDR = buf[0]; | |||
if (n > 1) { | |||
uint8_t b = buf[1]; | |||
size_t i = 2; | |||
while (1) { | |||
while (!(SPSR & (1 << SPIF))) {} | |||
SPDR = b; | |||
if (i == n) { | |||
break; | |||
} | |||
b = buf[i++]; | |||
} | |||
} | |||
while (!(SPSR & (1 << SPIF))) {} | |||
} | |||
#endif // __AVR__ | |||
#endif // SdSpiDriver_h |
@@ -31,10 +31,22 @@ | |||
#else // defined(ARDUINO) | |||
#error "Unknown system" | |||
#endif // defined(ARDUINO) | |||
//----------------------------------------------------------------------------- | |||
#ifdef ESP8266 | |||
// undefine F macro if ESP8266. | |||
#undef F | |||
#endif // ESP8266 | |||
//----------------------------------------------------------------------------- | |||
#ifndef F | |||
/** Define macro for strings stored in flash. */ | |||
#define F(str) (str) | |||
#endif // F | |||
//----------------------------------------------------------------------------- | |||
/** \return the time in milliseconds. */ | |||
inline uint16_t curTimeMS() { | |||
return millis(); | |||
} | |||
//----------------------------------------------------------------------------- | |||
/** | |||
* \class SysCall | |||
* \brief SysCall - Class to wrap system calls. | |||
@@ -51,7 +63,12 @@ class SysCall { | |||
static void yield(); | |||
}; | |||
#if defined(ARDUINO) | |||
#if defined(ESP8266) | |||
inline void SysCall::yield() { | |||
// Avoid ESP8266 bug | |||
delay(0); | |||
} | |||
#elif defined(ARDUINO) | |||
inline void SysCall::yield() { | |||
// Use the external Arduino yield() function. | |||
::yield(); | |||
@@ -60,8 +77,7 @@ inline void SysCall::yield() { | |||
inline void SysCall::yield() { | |||
Particle.process(); | |||
} | |||
#else // defined(ARDUINO) | |||
#else // ESP8266 | |||
inline void SysCall::yield() {} | |||
#endif // defined(ARDUINO) | |||
#endif // ESP8266 | |||
#endif // SysCall_h |
@@ -1,29 +0,0 @@ | |||
/* Arduino SdFat Library | |||
* Copyright (C) 2016 by William Greiman | |||
* | |||
* This file is part of the Arduino SdFat Library | |||
* | |||
* This Library is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This Library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with the Arduino SdFat Library. If not, see | |||
* <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef SystemInclude_h | |||
#define SystemInclude_h | |||
#if defined(ARDUINO) | |||
#include "FatLib/SysCall.h" | |||
#elif defined(PLATFORM_ID) // Only defined if a Particle device | |||
#include "SysCall.h" | |||
#else // System type | |||
#error Unknown System. | |||
#endif // System type | |||
#endif // SystemInclude_h |