您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

579 行
16KB

  1. /**
  2. * This program logs data to a binary file. Functions are included
  3. * to convert the binary file to a csv text file.
  4. *
  5. * Samples are logged at regular intervals. The maximum logging rate
  6. * depends on the quality of your SD card and the time required to
  7. * read sensor data. This example has been tested at 500 Hz with
  8. * good SD card on an Uno. 4000 HZ is possible on a Due.
  9. *
  10. * If your SD card has a long write latency, it may be necessary to use
  11. * slower sample rates. Using a Mega Arduino helps overcome latency
  12. * problems since 13 512 byte buffers will be used.
  13. *
  14. * Data is written to the file using a SD multiple block write command.
  15. */
  16. #include <SPI.h>
  17. #include "SdFat.h"
  18. #include "FreeStack.h"
  19. //------------------------------------------------------------------------------
  20. // User data functions. Modify these functions for your data items.
  21. #include "UserDataType.h" // Edit this include file to change data_t.
  22. // Set useSharedSpi true for use of an SPI sensor.
  23. const bool useSharedSpi = false;
  24. // Acquire a data record.
  25. void acquireData(data_t* data) {
  26. data->time = micros();
  27. for (int i = 0; i < ADC_DIM; i++) {
  28. data->adc[i] = analogRead(i);
  29. }
  30. }
  31. // Print a data record.
  32. void printData(Print* pr, data_t* data) {
  33. pr->print(data->time);
  34. for (int i = 0; i < ADC_DIM; i++) {
  35. pr->write(',');
  36. pr->print(data->adc[i]);
  37. }
  38. pr->println();
  39. }
  40. // Print data header.
  41. void printHeader(Print* pr) {
  42. pr->print(F("time"));
  43. for (int i = 0; i < ADC_DIM; i++) {
  44. pr->print(F(",adc"));
  45. pr->print(i);
  46. }
  47. pr->println();
  48. }
  49. //==============================================================================
  50. // Start of configuration constants.
  51. //==============================================================================
  52. //Interval between data records in microseconds.
  53. const uint32_t LOG_INTERVAL_USEC = 2000;
  54. //------------------------------------------------------------------------------
  55. // Pin definitions.
  56. //
  57. // SD chip select pin.
  58. const uint8_t SD_CS_PIN = SS;
  59. //
  60. // Digital pin to indicate an error, set to -1 if not used.
  61. // The led blinks for fatal errors. The led goes on solid for SD write
  62. // overrun errors and logging continues.
  63. const int8_t ERROR_LED_PIN = -1;
  64. //------------------------------------------------------------------------------
  65. // File definitions.
  66. //
  67. // Maximum file size in blocks.
  68. // The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks.
  69. // This file is flash erased using special SD commands. The file will be
  70. // truncated if logging is stopped early.
  71. const uint32_t FILE_BLOCK_COUNT = 256000;
  72. // log file base name. Must be six characters or less.
  73. #define FILE_BASE_NAME "data"
  74. //------------------------------------------------------------------------------
  75. // Buffer definitions.
  76. //
  77. // The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional
  78. // buffers.
  79. //
  80. #ifndef RAMEND
  81. // Assume ARM. Use total of nine 512 byte buffers.
  82. const uint8_t BUFFER_BLOCK_COUNT = 8;
  83. //
  84. #elif RAMEND < 0X8FF
  85. #error Too little SRAM
  86. //
  87. #elif RAMEND < 0X10FF
  88. // Use total of two 512 byte buffers.
  89. const uint8_t BUFFER_BLOCK_COUNT = 1;
  90. //
  91. #elif RAMEND < 0X20FF
  92. // Use total of five 512 byte buffers.
  93. const uint8_t BUFFER_BLOCK_COUNT = 4;
  94. //
  95. #else // RAMEND
  96. // Use total of 13 512 byte buffers.
  97. const uint8_t BUFFER_BLOCK_COUNT = 12;
  98. #endif // RAMEND
  99. //==============================================================================
  100. // End of configuration constants.
  101. //==============================================================================
  102. // Temporary log file. Will be deleted if a reset or power failure occurs.
  103. #define TMP_FILE_NAME "tmp_log.bin"
  104. // Size of file base name. Must not be larger than six.
  105. const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
  106. SdFat sd;
  107. SdBaseFile binFile;
  108. char binName[13] = FILE_BASE_NAME "00.bin";
  109. // Number of data records in a block.
  110. const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t);
  111. //Compute fill so block size is 512 bytes. FILL_DIM may be zero.
  112. const uint16_t FILL_DIM = 512 - 4 - DATA_DIM*sizeof(data_t);
  113. struct block_t {
  114. uint16_t count;
  115. uint16_t overrun;
  116. data_t data[DATA_DIM];
  117. uint8_t fill[FILL_DIM];
  118. };
  119. const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 2;
  120. block_t* emptyQueue[QUEUE_DIM];
  121. uint8_t emptyHead;
  122. uint8_t emptyTail;
  123. block_t* fullQueue[QUEUE_DIM];
  124. uint8_t fullHead;
  125. uint8_t fullTail;
  126. // Advance queue index.
  127. inline uint8_t queueNext(uint8_t ht) {
  128. return ht < (QUEUE_DIM - 1) ? ht + 1 : 0;
  129. }
  130. //==============================================================================
  131. // Error messages stored in flash.
  132. #define error(msg) errorFlash(F(msg))
  133. //------------------------------------------------------------------------------
  134. void errorFlash(const __FlashStringHelper* msg) {
  135. sd.errorPrint(msg);
  136. fatalBlink();
  137. }
  138. //------------------------------------------------------------------------------
  139. //
  140. void fatalBlink() {
  141. while (true) {
  142. if (ERROR_LED_PIN >= 0) {
  143. digitalWrite(ERROR_LED_PIN, HIGH);
  144. delay(200);
  145. digitalWrite(ERROR_LED_PIN, LOW);
  146. delay(200);
  147. }
  148. }
  149. }
  150. //==============================================================================
  151. // Convert binary file to csv file.
  152. void binaryToCsv() {
  153. uint8_t lastPct = 0;
  154. block_t block;
  155. uint32_t t0 = millis();
  156. uint32_t syncCluster = 0;
  157. SdFile csvFile;
  158. char csvName[13];
  159. if (!binFile.isOpen()) {
  160. Serial.println();
  161. Serial.println(F("No current binary file"));
  162. return;
  163. }
  164. binFile.rewind();
  165. // Create a new csvFile.
  166. strcpy(csvName, binName);
  167. strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");
  168. if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) {
  169. error("open csvFile failed");
  170. }
  171. Serial.println();
  172. Serial.print(F("Writing: "));
  173. Serial.print(csvName);
  174. Serial.println(F(" - type any character to stop"));
  175. printHeader(&csvFile);
  176. uint32_t tPct = millis();
  177. while (!Serial.available() && binFile.read(&block, 512) == 512) {
  178. uint16_t i;
  179. if (block.count == 0) {
  180. break;
  181. }
  182. if (block.overrun) {
  183. csvFile.print(F("OVERRUN,"));
  184. csvFile.println(block.overrun);
  185. }
  186. for (i = 0; i < block.count; i++) {
  187. printData(&csvFile, &block.data[i]);
  188. }
  189. if (csvFile.curCluster() != syncCluster) {
  190. csvFile.sync();
  191. syncCluster = csvFile.curCluster();
  192. }
  193. if ((millis() - tPct) > 1000) {
  194. uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100);
  195. if (pct != lastPct) {
  196. tPct = millis();
  197. lastPct = pct;
  198. Serial.print(pct, DEC);
  199. Serial.println('%');
  200. }
  201. }
  202. if (Serial.available()) {
  203. break;
  204. }
  205. }
  206. csvFile.close();
  207. Serial.print(F("Done: "));
  208. Serial.print(0.001*(millis() - t0));
  209. Serial.println(F(" Seconds"));
  210. }
  211. //------------------------------------------------------------------------------
  212. // read data file and check for overruns
  213. void checkOverrun() {
  214. bool headerPrinted = false;
  215. block_t block;
  216. uint32_t bgnBlock, endBlock;
  217. uint32_t bn = 0;
  218. if (!binFile.isOpen()) {
  219. Serial.println();
  220. Serial.println(F("No current binary file"));
  221. return;
  222. }
  223. if (!binFile.contiguousRange(&bgnBlock, &endBlock)) {
  224. error("contiguousRange failed");
  225. }
  226. binFile.rewind();
  227. Serial.println();
  228. Serial.println(F("Checking overrun errors - type any character to stop"));
  229. while (binFile.read(&block, 512) == 512) {
  230. if (block.count == 0) {
  231. break;
  232. }
  233. if (block.overrun) {
  234. if (!headerPrinted) {
  235. Serial.println();
  236. Serial.println(F("Overruns:"));
  237. Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount"));
  238. headerPrinted = true;
  239. }
  240. Serial.print(bn);
  241. Serial.print(',');
  242. Serial.print(bgnBlock + bn);
  243. Serial.print(',');
  244. Serial.println(block.overrun);
  245. }
  246. bn++;
  247. }
  248. if (!headerPrinted) {
  249. Serial.println(F("No errors found"));
  250. } else {
  251. Serial.println(F("Done"));
  252. }
  253. }
  254. //------------------------------------------------------------------------------
  255. // dump data file to Serial
  256. void dumpData() {
  257. block_t block;
  258. if (!binFile.isOpen()) {
  259. Serial.println();
  260. Serial.println(F("No current binary file"));
  261. return;
  262. }
  263. binFile.rewind();
  264. Serial.println();
  265. Serial.println(F("Type any character to stop"));
  266. delay(1000);
  267. printHeader(&Serial);
  268. while (!Serial.available() && binFile.read(&block , 512) == 512) {
  269. if (block.count == 0) {
  270. break;
  271. }
  272. if (block.overrun) {
  273. Serial.print(F("OVERRUN,"));
  274. Serial.println(block.overrun);
  275. }
  276. for (uint16_t i = 0; i < block.count; i++) {
  277. printData(&Serial, &block.data[i]);
  278. }
  279. }
  280. Serial.println(F("Done"));
  281. }
  282. //------------------------------------------------------------------------------
  283. // log data
  284. // max number of blocks to erase per erase call
  285. uint32_t const ERASE_SIZE = 262144L;
  286. void logData() {
  287. uint32_t bgnBlock, endBlock;
  288. // Allocate extra buffer space.
  289. block_t block[BUFFER_BLOCK_COUNT];
  290. block_t* curBlock = 0;
  291. Serial.println();
  292. // Find unused file name.
  293. if (BASE_NAME_SIZE > 6) {
  294. error("FILE_BASE_NAME too long");
  295. }
  296. while (sd.exists(binName)) {
  297. if (binName[BASE_NAME_SIZE + 1] != '9') {
  298. binName[BASE_NAME_SIZE + 1]++;
  299. } else {
  300. binName[BASE_NAME_SIZE + 1] = '0';
  301. if (binName[BASE_NAME_SIZE] == '9') {
  302. error("Can't create file name");
  303. }
  304. binName[BASE_NAME_SIZE]++;
  305. }
  306. }
  307. // Delete old tmp file.
  308. if (sd.exists(TMP_FILE_NAME)) {
  309. Serial.println(F("Deleting tmp file"));
  310. if (!sd.remove(TMP_FILE_NAME)) {
  311. error("Can't remove tmp file");
  312. }
  313. }
  314. // Create new file.
  315. Serial.println(F("Creating new file"));
  316. binFile.close();
  317. if (!binFile.createContiguous(sd.vwd(),
  318. TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
  319. error("createContiguous failed");
  320. }
  321. // Get the address of the file on the SD.
  322. if (!binFile.contiguousRange(&bgnBlock, &endBlock)) {
  323. error("contiguousRange failed");
  324. }
  325. // Use SdFat's internal buffer.
  326. uint8_t* cache = (uint8_t*)sd.vol()->cacheClear();
  327. if (cache == 0) {
  328. error("cacheClear failed");
  329. }
  330. // Flash erase all data in the file.
  331. Serial.println(F("Erasing all data"));
  332. uint32_t bgnErase = bgnBlock;
  333. uint32_t endErase;
  334. while (bgnErase < endBlock) {
  335. endErase = bgnErase + ERASE_SIZE;
  336. if (endErase > endBlock) {
  337. endErase = endBlock;
  338. }
  339. if (!sd.card()->erase(bgnErase, endErase)) {
  340. error("erase failed");
  341. }
  342. bgnErase = endErase + 1;
  343. }
  344. // Start a multiple block write.
  345. if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) {
  346. error("writeBegin failed");
  347. }
  348. // Set chip select high if other devices use SPI.
  349. if (useSharedSpi) {
  350. sd.card()->chipSelectHigh();
  351. }
  352. // Initialize queues.
  353. emptyHead = emptyTail = 0;
  354. fullHead = fullTail = 0;
  355. // Use SdFat buffer for one block.
  356. emptyQueue[emptyHead] = (block_t*)cache;
  357. emptyHead = queueNext(emptyHead);
  358. // Put rest of buffers in the empty queue.
  359. for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) {
  360. emptyQueue[emptyHead] = &block[i];
  361. emptyHead = queueNext(emptyHead);
  362. }
  363. Serial.println(F("Logging - type any character to stop"));
  364. // Wait for Serial Idle.
  365. Serial.flush();
  366. delay(10);
  367. uint32_t bn = 0;
  368. uint32_t t0 = millis();
  369. uint32_t t1 = t0;
  370. uint32_t overrun = 0;
  371. uint32_t overrunTotal = 0;
  372. uint32_t count = 0;
  373. uint32_t maxDelta = 0;
  374. uint32_t minDelta = 99999;
  375. uint32_t maxLatency = 0;
  376. // Start at a multiple of interval.
  377. uint32_t logTime = micros()/LOG_INTERVAL_USEC + 1;
  378. logTime *= LOG_INTERVAL_USEC;
  379. bool closeFile = false;
  380. while (1) {
  381. // Time for next data record.
  382. logTime += LOG_INTERVAL_USEC;
  383. if (Serial.available()) {
  384. closeFile = true;
  385. }
  386. if (closeFile) {
  387. if (curBlock != 0) {
  388. // Put buffer in full queue.
  389. fullQueue[fullHead] = curBlock;
  390. fullHead = queueNext(fullHead);
  391. curBlock = 0;
  392. }
  393. } else {
  394. if (curBlock == 0 && emptyTail != emptyHead) {
  395. curBlock = emptyQueue[emptyTail];
  396. emptyTail = queueNext(emptyTail);
  397. curBlock->count = 0;
  398. curBlock->overrun = overrun;
  399. overrun = 0;
  400. }
  401. if ((int32_t)(logTime - micros()) < 0) {
  402. error("Rate too fast");
  403. }
  404. int32_t delta;
  405. do {
  406. delta = micros() - logTime;
  407. } while (delta < 0);
  408. if (curBlock == 0) {
  409. overrun++;
  410. } else {
  411. acquireData(&curBlock->data[curBlock->count++]);
  412. if (curBlock->count == DATA_DIM) {
  413. fullQueue[fullHead] = curBlock;
  414. fullHead = queueNext(fullHead);
  415. curBlock = 0;
  416. }
  417. if ((uint32_t)delta > maxDelta) maxDelta = delta;
  418. if ((uint32_t)delta < minDelta) minDelta = delta;
  419. }
  420. }
  421. if (fullHead == fullTail) {
  422. // Exit loop if done.
  423. if (closeFile) {
  424. break;
  425. }
  426. } else if (!sd.card()->isBusy()) {
  427. // Get address of block to write.
  428. block_t* pBlock = fullQueue[fullTail];
  429. fullTail = queueNext(fullTail);
  430. // Write block to SD.
  431. uint32_t usec = micros();
  432. if (!sd.card()->writeData((uint8_t*)pBlock)) {
  433. error("write data failed");
  434. }
  435. usec = micros() - usec;
  436. t1 = millis();
  437. if (usec > maxLatency) {
  438. maxLatency = usec;
  439. }
  440. count += pBlock->count;
  441. // Add overruns and possibly light LED.
  442. if (pBlock->overrun) {
  443. overrunTotal += pBlock->overrun;
  444. if (ERROR_LED_PIN >= 0) {
  445. digitalWrite(ERROR_LED_PIN, HIGH);
  446. }
  447. }
  448. // Move block to empty queue.
  449. emptyQueue[emptyHead] = pBlock;
  450. emptyHead = queueNext(emptyHead);
  451. bn++;
  452. if (bn == FILE_BLOCK_COUNT) {
  453. // File full so stop
  454. break;
  455. }
  456. }
  457. }
  458. if (!sd.card()->writeStop()) {
  459. error("writeStop failed");
  460. }
  461. // Truncate file if recording stopped early.
  462. if (bn != FILE_BLOCK_COUNT) {
  463. Serial.println(F("Truncating file"));
  464. if (!binFile.truncate(512L * bn)) {
  465. error("Can't truncate file");
  466. }
  467. }
  468. if (!binFile.rename(sd.vwd(), binName)) {
  469. error("Can't rename file");
  470. }
  471. Serial.print(F("File renamed: "));
  472. Serial.println(binName);
  473. Serial.print(F("Max block write usec: "));
  474. Serial.println(maxLatency);
  475. Serial.print(F("Record time sec: "));
  476. Serial.println(0.001*(t1 - t0), 3);
  477. Serial.print(minDelta);
  478. Serial.print(F(" <= jitter microseconds <= "));
  479. Serial.println(maxDelta);
  480. Serial.print(F("Sample count: "));
  481. Serial.println(count);
  482. Serial.print(F("Samples/sec: "));
  483. Serial.println((1000.0)*count/(t1-t0));
  484. Serial.print(F("Overruns: "));
  485. Serial.println(overrunTotal);
  486. Serial.println(F("Done"));
  487. }
  488. //------------------------------------------------------------------------------
  489. void setup(void) {
  490. if (ERROR_LED_PIN >= 0) {
  491. pinMode(ERROR_LED_PIN, OUTPUT);
  492. }
  493. Serial.begin(9600);
  494. // Wait for USB Serial
  495. while (!Serial) {
  496. SysCall::yield();
  497. }
  498. Serial.print(F("FreeStack: "));
  499. Serial.println(FreeStack());
  500. Serial.print(F("Records/block: "));
  501. Serial.println(DATA_DIM);
  502. if (sizeof(block_t) != 512) {
  503. error("Invalid block size");
  504. }
  505. // initialize file system.
  506. if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) {
  507. sd.initErrorPrint();
  508. fatalBlink();
  509. }
  510. }
  511. //------------------------------------------------------------------------------
  512. void loop(void) {
  513. // discard any input
  514. do {
  515. delay(10);
  516. } while (Serial.read() >= 0);
  517. Serial.println();
  518. Serial.println(F("type:"));
  519. Serial.println(F("c - convert file to csv"));
  520. Serial.println(F("d - dump data to Serial"));
  521. Serial.println(F("e - overrun error details"));
  522. Serial.println(F("r - record data"));
  523. while(!Serial.available()) {
  524. SysCall::yield();
  525. }
  526. char c = tolower(Serial.read());
  527. // Discard extra Serial data.
  528. do {
  529. delay(10);
  530. } while (Serial.read() >= 0);
  531. if (ERROR_LED_PIN >= 0) {
  532. digitalWrite(ERROR_LED_PIN, LOW);
  533. }
  534. if (c == 'c') {
  535. binaryToCsv();
  536. } else if (c == 'd') {
  537. dumpData();
  538. } else if (c == 'e') {
  539. checkOverrun();
  540. } else if (c == 'r') {
  541. logData();
  542. } else {
  543. Serial.println(F("Invalid entry"));
  544. }
  545. }