|
@@ -9,66 +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.
|
|
|
-// May not work for some cards.
|
|
|
-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.
|
|
@@ -78,49 +59,51 @@ 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
|
|
|
//==============================================================================
|
|
|
// End of configuration constants.
|
|
|
//==============================================================================
|
|
|
// Temporary log file. Will be deleted if a reset or power failure occurs.
|
|
|
-#define TMP_FILE_NAME "tmp_log.bin"
|
|
|
+#define TMP_FILE_NAME FILE_BASE_NAME "##.bin"
|
|
|
|
|
|
-// Size of file base name. Must not be larger than six.
|
|
|
+// Size of file base name.
|
|
|
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);
|
|
|
|
|
@@ -133,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);
|
|
@@ -168,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;
|
|
@@ -176,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");
|
|
@@ -191,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"));
|
|
@@ -199,7 +208,7 @@ void binaryToCsv() {
|
|
|
uint32_t tPct = millis();
|
|
|
while (!Serial.available() && binFile.read(&block, 512) == 512) {
|
|
|
uint16_t i;
|
|
|
- if (block.count == 0) {
|
|
|
+ if (block.count == 0 || block.count > DATA_DIM) {
|
|
|
break;
|
|
|
}
|
|
|
if (block.overrun) {
|
|
@@ -231,48 +240,42 @@ 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;
|
|
|
+
|
|
|
+ // Delete old tmp file.
|
|
|
+ if (sd.exists(TMP_FILE_NAME)) {
|
|
|
+ Serial.println(F("Deleting tmp file " TMP_FILE_NAME));
|
|
|
+ if (!sd.remove(TMP_FILE_NAME)) {
|
|
|
+ error("Can't remove tmp file");
|
|
|
+ }
|
|
|
}
|
|
|
+ // Create new file.
|
|
|
+ Serial.println(F("\nCreating 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;
|
|
|
}
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
@@ -305,123 +308,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(F("\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");
|
|
|
- }
|
|
|
- // 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("writeStart 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;
|
|
@@ -435,24 +420,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 {
|
|
|
- if (useSharedSpi) {
|
|
|
- sd.card()->chipSelectHigh();
|
|
|
- }
|
|
|
+#if USE_SHARED_SPI
|
|
|
+ sd.card()->spiStop();
|
|
|
+#endif // USE_SHARED_SPI
|
|
|
acquireData(&curBlock->data[curBlock->count++]);
|
|
|
- if (useSharedSpi) {
|
|
|
- sd.card()->chipSelectLow();
|
|
|
- }
|
|
|
+#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) {
|
|
@@ -461,29 +451,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
|
|
@@ -494,6 +473,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"));
|
|
@@ -501,25 +486,76 @@ void logData() {
|
|
|
error("Can't truncate file");
|
|
|
}
|
|
|
}
|
|
|
+}
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+void recoverTmpFile() {
|
|
|
+ uint16_t count;
|
|
|
+ if (!binFile.open(TMP_FILE_NAME, O_RDWR)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (binFile.read(&count, 2) != 2 || count != DATA_DIM) {
|
|
|
+ error("Please delete existing " TMP_FILE_NAME);
|
|
|
+ }
|
|
|
+ Serial.println(F("\nRecovering data in tmp file " TMP_FILE_NAME));
|
|
|
+ uint32_t bgnBlock = 0;
|
|
|
+ uint32_t endBlock = binFile.fileSize()/512 - 1;
|
|
|
+ // find last used block.
|
|
|
+ while (bgnBlock < endBlock) {
|
|
|
+ uint32_t midBlock = (bgnBlock + endBlock + 1)/2;
|
|
|
+ binFile.seekSet(512*midBlock);
|
|
|
+ if (binFile.read(&count, 2) != 2) error("read");
|
|
|
+ if (count == 0 || count > DATA_DIM) {
|
|
|
+ endBlock = midBlock - 1;
|
|
|
+ } else {
|
|
|
+ bgnBlock = midBlock;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // truncate after last used block.
|
|
|
+ if (!binFile.truncate(512*(bgnBlock + 1))) {
|
|
|
+ error("Truncate " TMP_FILE_NAME " failed");
|
|
|
+ }
|
|
|
+ renameBinFile();
|
|
|
+}
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void renameBinFile() {
|
|
|
+ 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]++;
|
|
|
+ }
|
|
|
+ }
|
|
|
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(F("File size: "));
|
|
|
+ Serial.print(binFile.fileSize()/512);
|
|
|
+ Serial.println(F(" blocks"));
|
|
|
+}
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+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) {
|
|
@@ -532,19 +568,38 @@ void setup(void) {
|
|
|
while (!Serial) {
|
|
|
SysCall::yield();
|
|
|
}
|
|
|
-
|
|
|
- Serial.print(F("FreeStack: "));
|
|
|
+ Serial.print(F("\nFreeStack: "));
|
|
|
Serial.println(FreeStack());
|
|
|
Serial.print(F("Records/block: "));
|
|
|
Serial.println(DATA_DIM);
|
|
|
if (sizeof(block_t) != 512) {
|
|
|
error("Invalid block size");
|
|
|
}
|
|
|
- // initialize file system.
|
|
|
- if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) {
|
|
|
- sd.initErrorPrint();
|
|
|
+ // Allow userSetup access to SPI bus.
|
|
|
+ pinMode(SD_CS_PIN, OUTPUT);
|
|
|
+ digitalWrite(SD_CS_PIN, HIGH);
|
|
|
+
|
|
|
+ // Setup sensors.
|
|
|
+ userSetup();
|
|
|
+
|
|
|
+ // 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();
|
|
|
}
|
|
|
+ // recover existing tmp file.
|
|
|
+ if (sd.exists(TMP_FILE_NAME)) {
|
|
|
+ Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
|
|
|
+ while (!Serial.available()) {
|
|
|
+ SysCall::yield();
|
|
|
+ }
|
|
|
+ if (Serial.read() == 'Y') {
|
|
|
+ recoverTmpFile();
|
|
|
+ } else {
|
|
|
+ error("'Y' not typed, please manually delete " TMP_FILE_NAME);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
|
void loop(void) {
|
|
@@ -554,14 +609,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.
|
|
@@ -572,15 +634,22 @@ 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"));
|
|
|
}
|
|
|
-}
|
|
|
+}
|