TeensyDmaAdcLogger.ino 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // Test of Teensy exFAT DMA ADC logger.
  2. // This is mainly to test use of RingBuf in an ISR.
  3. // This example only supports pins on the first ADC.
  4. // it has only been tested on Teensy 3.6 and 4.1.
  5. // You should modify it for serious use as a data logger.
  6. //
  7. #include "ADC.h"
  8. #include "DMAChannel.h"
  9. #include "FreeStack.h"
  10. #include "RingBuf.h"
  11. #include "SdFat.h"
  12. // Pin must be on first ADC.
  13. #define ADC_PIN A0
  14. // 400 sector RingBuf - could be larger on Teensy 4.1.
  15. const size_t RING_BUF_SIZE = 400 * 512;
  16. // Preallocate 8GiB file.
  17. const uint64_t PRE_ALLOCATE_SIZE = 8ULL << 30;
  18. // Use FIFO SDIO.
  19. #define SD_CONFIG SdioConfig(FIFO_SDIO)
  20. ADC adc;
  21. DMAChannel dma(true);
  22. SdFs sd;
  23. FsFile file;
  24. // Ping-pong DMA buffer.
  25. DMAMEM static uint16_t __attribute__((aligned(32))) dmaBuf[2][256];
  26. // Count of DMA interrupts.
  27. volatile size_t dmaCount;
  28. // RingBuf for 512 byte sectors.
  29. RingBuf<FsFile, RING_BUF_SIZE> rb;
  30. // Shared between ISR and background.
  31. volatile size_t maxBytesUsed;
  32. // Overrun error for write to RingBuf.
  33. volatile bool overrun;
  34. //------------------------------------------------------------------------------
  35. // ISR for DMA.
  36. static void isr() {
  37. if (!overrun) {
  38. // Clear cache for buffer filled by DMA to insure read from DMA memory.
  39. arm_dcache_delete((void*)dmaBuf[dmaCount & 1], 512);
  40. // Enable RingBuf functions to be called in ISR.
  41. rb.beginISR();
  42. if (rb.write(dmaBuf[dmaCount & 1], 512) == 512) {
  43. dmaCount++;
  44. if (rb.bytesUsed() > maxBytesUsed) {
  45. maxBytesUsed = rb.bytesUsed();
  46. }
  47. } else {
  48. overrun = true;
  49. }
  50. // End use of RingBuf functions in ISR.
  51. rb.endISR();
  52. }
  53. dma.clearComplete();
  54. dma.clearInterrupt();
  55. #if defined(__IMXRT1062__)
  56. // Handle clear interrupt glitch in Teensy 4.x!
  57. asm("DSB");
  58. #endif // defined(__IMXRT1062__)
  59. }
  60. //------------------------------------------------------------------------------
  61. #if defined(__IMXRT1062__) // Teensy 4.x
  62. #define SOURCE_SADDR ADC1_R0
  63. #define SOURCE_EVENT DMAMUX_SOURCE_ADC1
  64. #else
  65. #define SOURCE_SADDR ADC0_RA
  66. #define SOURCE_EVENT DMAMUX_SOURCE_ADC0
  67. #endif
  68. //------------------------------------------------------------------------------
  69. static void init(uint8_t pin) {
  70. dma.begin();
  71. dma.attachInterrupt(isr);
  72. dma.source((volatile const signed short&)SOURCE_SADDR);
  73. dma.destinationBuffer((volatile uint16_t*)dmaBuf, sizeof(dmaBuf));
  74. dma.interruptAtHalf();
  75. dma.interruptAtCompletion();
  76. dma.triggerAtHardwareEvent(SOURCE_EVENT);
  77. dma.enable();
  78. adc.adc0->enableDMA();
  79. adc.adc0->startContinuous(pin);
  80. }
  81. //------------------------------------------------------------------------------
  82. void stopDma() {
  83. adc.adc0->disableDMA();
  84. dma.disable();
  85. }
  86. //------------------------------------------------------------------------------
  87. void printTest(Print* pr) {
  88. if (file.fileSize() < 1024 * 2) {
  89. return;
  90. }
  91. file.rewind();
  92. rb.begin(&file);
  93. // Could readIn RING_BUF_SIZE bytes and write to a csv file in a loop.
  94. if (rb.readIn(2048) != 2048) {
  95. sd.errorHalt("rb.readIn failed");
  96. }
  97. uint16_t data;
  98. for (size_t i = 0; i < 1024; i++) {
  99. pr->print(i);
  100. pr->print(',');
  101. // Test read with: template <typename Type>bool read(Type* data).
  102. rb.read(&data);
  103. pr->println(data);
  104. }
  105. }
  106. //------------------------------------------------------------------------------
  107. void runTest(uint8_t pin) {
  108. dmaCount = 0;
  109. maxBytesUsed = 0;
  110. overrun = false;
  111. do {
  112. delay(10);
  113. } while (Serial.read() >= 0);
  114. if (!file.open("IsrLoggerTest.bin", O_CREAT | O_TRUNC | O_RDWR)) {
  115. sd.errorHalt("file.open failed");
  116. }
  117. if (!file.preAllocate(PRE_ALLOCATE_SIZE)) {
  118. sd.errorHalt("file.preAllocate failed");
  119. }
  120. rb.begin(&file);
  121. Serial.println("Type any character to stop\n");
  122. init(pin);
  123. uint32_t samplingTime = micros();
  124. while (!overrun && !Serial.available()) {
  125. size_t n = rb.bytesUsed();
  126. if ((n + file.curPosition()) >= (PRE_ALLOCATE_SIZE - 512)) {
  127. Serial.println("File full - stopping");
  128. break;
  129. }
  130. if (n >= 512) {
  131. if (rb.writeOut(512) != 512) {
  132. Serial.println("writeOut() failed");
  133. file.close();
  134. return;
  135. }
  136. }
  137. }
  138. stopDma();
  139. samplingTime = micros() - samplingTime;
  140. if (!rb.sync()) {
  141. Serial.println("sync() failed");
  142. file.close();
  143. return;
  144. }
  145. if (!file.truncate()) {
  146. sd.errorHalt("truncate failed");
  147. }
  148. if (overrun) {
  149. Serial.println("Overrun ERROR!!");
  150. }
  151. Serial.print("dmsCount ");
  152. Serial.println(dmaCount);
  153. Serial.print("RingBufSize ");
  154. Serial.println(RING_BUF_SIZE);
  155. Serial.print("maxBytesUsed ");
  156. Serial.println(maxBytesUsed);
  157. Serial.print("fileSize ");
  158. file.printFileSize(&Serial);
  159. Serial.println();
  160. Serial.print(0.000001 * samplingTime);
  161. Serial.println(" seconds");
  162. Serial.print(1.0 * file.fileSize() / samplingTime, 3);
  163. Serial.println(" MB/sec\n");
  164. printTest(&Serial);
  165. file.close();
  166. }
  167. //------------------------------------------------------------------------------
  168. void waitSerial(const char* msg) {
  169. do {
  170. delay(10);
  171. } while (Serial.read() >= 0);
  172. Serial.println(msg);
  173. while (!Serial.available()) {
  174. }
  175. Serial.println();
  176. }
  177. //------------------------------------------------------------------------------
  178. void setup() {
  179. Serial.begin(9600);
  180. while (!Serial) {
  181. yield();
  182. }
  183. waitSerial("Type any character to begin");
  184. Serial.print("FreeStack: ");
  185. Serial.println(FreeStack());
  186. }
  187. //------------------------------------------------------------------------------
  188. void loop() {
  189. if (!sd.begin(SD_CONFIG)) {
  190. sd.initErrorHalt(&Serial);
  191. }
  192. // Try for max speed.
  193. adc.adc0->setAveraging(1);
  194. adc.adc0->setResolution(10);
  195. adc.adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED);
  196. adc.adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
  197. runTest(ADC_PIN);
  198. waitSerial("Type any character to run test again");
  199. }