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

530 行
15KB

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